KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sim_model.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2022 Mikolaj Wielgus
5 * Copyright (C) 2022 CERN
6 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 3
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * https://www.gnu.org/licenses/gpl-3.0.html
21 * or you may search the http://www.gnu.org website for the version 3 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26#include <ki_exception.h>
27#include <lib_symbol.h>
28#include <sch_symbol.h>
29#include <string_utils.h>
30#include <wx/regex.h>
31
32#include <iterator>
33
34#include <sim/sim_model.h>
36#include <sim/sim_model_ideal.h>
39#include <sim/sim_model_r_pot.h>
40#include <sim/sim_model_ibis.h>
45#include <sim/sim_model_tline.h>
47#include <sim/sim_lib_mgr.h>
49
50#include <boost/algorithm/string.hpp>
51#include <fmt/core.h>
52#include <pegtl/contrib/parse_tree.hpp>
53
54
56
57using TYPE = SIM_MODEL::TYPE;
58
59
60SIM_MODEL::DEVICE_INFO SIM_MODEL::DeviceInfo( DEVICE_T aDeviceType )
61{
62 switch( aDeviceType )
63 {
64 // | fieldValue | description | showInMenu |
65 // -------------------------------------------------------
66 //
67 case DEVICE_T::NONE: return { "", "", true };
68 case DEVICE_T::R: return { "R", "Resistor", true };
69 case DEVICE_T::C: return { "C", "Capacitor", true };
70 case DEVICE_T::L: return { "L", "Inductor", true };
71 case DEVICE_T::K: return { "K", "Mutual Inductance Statement", true };
72 case DEVICE_T::TLINE: return { "TLINE", "Transmission Line", true };
73 case DEVICE_T::SW: return { "SW", "Switch", true };
74
75 case DEVICE_T::D: return { "D", "Diode", true };
76 case DEVICE_T::NPN: return { "NPN", "NPN BJT", true };
77 case DEVICE_T::PNP: return { "PNP", "PNP BJT", true };
78
79 case DEVICE_T::NJFET: return { "NJFET", "N-channel JFET", true };
80 case DEVICE_T::PJFET: return { "PJFET", "P-channel JFET", true };
81
82 case DEVICE_T::NMOS: return { "NMOS", "N-channel MOSFET", true };
83 case DEVICE_T::PMOS: return { "PMOS", "P-channel MOSFET", true };
84 case DEVICE_T::NMES: return { "NMES", "N-channel MESFET", true };
85 case DEVICE_T::PMES: return { "PMES", "P-channel MESFET", true };
86
87 case DEVICE_T::V: return { "V", "Voltage Source", true };
88 case DEVICE_T::I: return { "I", "Current Source", true };
89 case DEVICE_T::E: return { "E", "Voltage Source", false };
90 case DEVICE_T::F: return { "F", "Current Source", false };
91 case DEVICE_T::G: return { "G", "Current Source", false };
92 case DEVICE_T::H: return { "H", "Voltage Source", false };
93
94 case DEVICE_T::KIBIS: return { "IBIS", "IBIS Model", false };
95
96 case DEVICE_T::SUBCKT: return { "SUBCKT", "Subcircuit", false };
97 case DEVICE_T::XSPICE: return { "XSPICE", "XSPICE Code Model", true };
98 case DEVICE_T::SPICE: return { "SPICE", "Raw SPICE Element", true };
99
100 default: wxFAIL; return {};
101 }
102}
103
104
105SIM_MODEL::INFO SIM_MODEL::TypeInfo( TYPE aType )
106{
107 switch( aType )
108 {
109 // | deviceType | fieldValue | description |
110 // ---------------------------------------------------------------------
111 //
112 case TYPE::NONE: return { DEVICE_T::NONE, "", "" };
113
114 case TYPE::R: return { DEVICE_T::R, "", "Ideal" };
115 case TYPE::R_POT: return { DEVICE_T::R, "POT", "Potentiometer" };
116 case TYPE::R_BEHAVIORAL: return { DEVICE_T::R, "=", "Behavioral" };
117
118 case TYPE::C: return { DEVICE_T::C, "", "Ideal" };
119 case TYPE::C_BEHAVIORAL: return { DEVICE_T::C, "=", "Behavioral" };
120
121 case TYPE::L: return { DEVICE_T::L, "", "Ideal" };
122 case TYPE::L_BEHAVIORAL: return { DEVICE_T::L, "=", "Behavioral" };
123
124 case TYPE::K: return { DEVICE_T::K, "", "Mutual Inductance Statement" };
125
126 case TYPE::TLINE_Z0: return { DEVICE_T::TLINE, "", "Characteristic impedance" };
127 case TYPE::TLINE_RLGC: return { DEVICE_T::TLINE, "RLGC", "RLGC" };
128
129 case TYPE::SW_V: return { DEVICE_T::SW, "V", "Voltage-controlled" };
130 case TYPE::SW_I: return { DEVICE_T::SW, "I", "Current-controlled" };
131
132 case TYPE::D: return { DEVICE_T::D, "", "" };
133
134 case TYPE::NPN_VBIC: return { DEVICE_T::NPN, "VBIC", "VBIC" };
135 case TYPE::PNP_VBIC: return { DEVICE_T::PNP, "VBIC", "VBIC" };
136 case TYPE::NPN_GUMMELPOON: return { DEVICE_T::NPN, "GUMMELPOON", "Gummel-Poon" };
137 case TYPE::PNP_GUMMELPOON: return { DEVICE_T::PNP, "GUMMELPOON", "Gummel-Poon" };
138 //case TYPE::BJT_MEXTRAM: return {};
139 case TYPE::NPN_HICUM2: return { DEVICE_T::NPN, "HICUML2", "HICUM level 2" };
140 case TYPE::PNP_HICUM2: return { DEVICE_T::PNP, "HICUML2", "HICUM level 2" };
141 //case TYPE::BJT_HICUM_L0: return {};
142
143 case TYPE::NJFET_SHICHMANHODGES: return { DEVICE_T::NJFET, "SHICHMANHODGES", "Shichman-Hodges" };
144 case TYPE::PJFET_SHICHMANHODGES: return { DEVICE_T::PJFET, "SHICHMANHODGES", "Shichman-Hodges" };
145 case TYPE::NJFET_PARKERSKELLERN: return { DEVICE_T::NJFET, "PARKERSKELLERN", "Parker-Skellern" };
146 case TYPE::PJFET_PARKERSKELLERN: return { DEVICE_T::PJFET, "PARKERSKELLERN", "Parker-Skellern" };
147
148 case TYPE::NMES_STATZ: return { DEVICE_T::NMES, "STATZ", "Statz" };
149 case TYPE::PMES_STATZ: return { DEVICE_T::PMES, "STATZ", "Statz" };
150 case TYPE::NMES_YTTERDAL: return { DEVICE_T::NMES, "YTTERDAL", "Ytterdal" };
151 case TYPE::PMES_YTTERDAL: return { DEVICE_T::PMES, "YTTERDAL", "Ytterdal" };
152 case TYPE::NMES_HFET1: return { DEVICE_T::NMES, "HFET1", "HFET1" };
153 case TYPE::PMES_HFET1: return { DEVICE_T::PMES, "HFET1", "HFET1" };
154 case TYPE::NMES_HFET2: return { DEVICE_T::NMES, "HFET2", "HFET2" };
155 case TYPE::PMES_HFET2: return { DEVICE_T::PMES, "HFET2", "HFET2" };
156
157 case TYPE::NMOS_VDMOS: return { DEVICE_T::NMOS, "VDMOS", "VDMOS" };
158 case TYPE::PMOS_VDMOS: return { DEVICE_T::PMOS, "VDMOS", "VDMOS" };
159 case TYPE::NMOS_MOS1: return { DEVICE_T::NMOS, "MOS1", "Classical quadratic (MOS1)" };
160 case TYPE::PMOS_MOS1: return { DEVICE_T::PMOS, "MOS1", "Classical quadratic (MOS1)" };
161 case TYPE::NMOS_MOS2: return { DEVICE_T::NMOS, "MOS2", "Grove-Frohman (MOS2)" };
162 case TYPE::PMOS_MOS2: return { DEVICE_T::PMOS, "MOS2", "Grove-Frohman (MOS2)" };
163 case TYPE::NMOS_MOS3: return { DEVICE_T::NMOS, "MOS3", "MOS3" };
164 case TYPE::PMOS_MOS3: return { DEVICE_T::PMOS, "MOS3", "MOS3" };
165 case TYPE::NMOS_BSIM1: return { DEVICE_T::NMOS, "BSIM1", "BSIM1" };
166 case TYPE::PMOS_BSIM1: return { DEVICE_T::PMOS, "BSIM1", "BSIM1" };
167 case TYPE::NMOS_BSIM2: return { DEVICE_T::NMOS, "BSIM2", "BSIM2" };
168 case TYPE::PMOS_BSIM2: return { DEVICE_T::PMOS, "BSIM2", "BSIM2" };
169 case TYPE::NMOS_MOS6: return { DEVICE_T::NMOS, "MOS6", "MOS6" };
170 case TYPE::PMOS_MOS6: return { DEVICE_T::PMOS, "MOS6", "MOS6" };
171 case TYPE::NMOS_BSIM3: return { DEVICE_T::NMOS, "BSIM3", "BSIM3" };
172 case TYPE::PMOS_BSIM3: return { DEVICE_T::PMOS, "BSIM3", "BSIM3" };
173 case TYPE::NMOS_MOS9: return { DEVICE_T::NMOS, "MOS9", "MOS9" };
174 case TYPE::PMOS_MOS9: return { DEVICE_T::PMOS, "MOS9", "MOS9" };
175 case TYPE::NMOS_B4SOI: return { DEVICE_T::NMOS, "B4SOI", "BSIM4 SOI (B4SOI)" };
176 case TYPE::PMOS_B4SOI: return { DEVICE_T::PMOS, "B4SOI", "BSIM4 SOI (B4SOI)" };
177 case TYPE::NMOS_BSIM4: return { DEVICE_T::NMOS, "BSIM4", "BSIM4" };
178 case TYPE::PMOS_BSIM4: return { DEVICE_T::PMOS, "BSIM4", "BSIM4" };
179 //case TYPE::NMOS_EKV2_6: return {};
180 //case TYPE::PMOS_EKV2_6: return {};
181 //case TYPE::NMOS_PSP: return {};
182 //case TYPE::PMOS_PSP: return {};
183 case TYPE::NMOS_B3SOIFD: return { DEVICE_T::NMOS, "B3SOIFD", "B3SOIFD (BSIM3 FD-SOI)" };
184 case TYPE::PMOS_B3SOIFD: return { DEVICE_T::PMOS, "B3SOIFD", "B3SOIFD (BSIM3 FD-SOI)" };
185 case TYPE::NMOS_B3SOIDD: return { DEVICE_T::NMOS, "B3SOIDD", "B3SOIDD (BSIM3 SOI)" };
186 case TYPE::PMOS_B3SOIDD: return { DEVICE_T::PMOS, "B3SOIDD", "B3SOIDD (BSIM3 SOI)" };
187 case TYPE::NMOS_B3SOIPD: return { DEVICE_T::NMOS, "B3SOIPD", "B3SOIPD (BSIM3 PD-SOI)" };
188 case TYPE::PMOS_B3SOIPD: return { DEVICE_T::PMOS, "B3SOIPD", "B3SOIPD (BSIM3 PD-SOI)" };
189 //case TYPE::NMOS_STAG: return {};
190 //case TYPE::PMOS_STAG: return {};
191 case TYPE::NMOS_HISIM2: return { DEVICE_T::NMOS, "HISIM2", "HiSIM2" };
192 case TYPE::PMOS_HISIM2: return { DEVICE_T::PMOS, "HISIM2", "HiSIM2" };
193 case TYPE::NMOS_HISIMHV1: return { DEVICE_T::NMOS, "HISIMHV1", "HiSIM_HV1" };
194 case TYPE::PMOS_HISIMHV1: return { DEVICE_T::PMOS, "HISIMHV1", "HiSIM_HV1" };
195 case TYPE::NMOS_HISIMHV2: return { DEVICE_T::NMOS, "HISIMHV2", "HiSIM_HV2" };
196 case TYPE::PMOS_HISIMHV2: return { DEVICE_T::PMOS, "HISIMHV2", "HiSIM_HV2" };
197
198 case TYPE::V: return { DEVICE_T::V, "DC", "DC", };
199 case TYPE::V_SIN: return { DEVICE_T::V, "SIN", "Sine" };
200 case TYPE::V_PULSE: return { DEVICE_T::V, "PULSE", "Pulse" };
201 case TYPE::V_EXP: return { DEVICE_T::V, "EXP", "Exponential" };
202 case TYPE::V_AM: return { DEVICE_T::V, "AM", "Amplitude modulated" };
203 case TYPE::V_SFFM: return { DEVICE_T::V, "SFFM", "Single-frequency FM" };
204 case TYPE::V_VCL: return { DEVICE_T::E, "", "Voltage-controlled" };
205 case TYPE::V_CCL: return { DEVICE_T::H, "", "Current-controlled" };
206 case TYPE::V_PWL: return { DEVICE_T::V, "PWL", "Piecewise linear" };
207 case TYPE::V_WHITENOISE: return { DEVICE_T::V, "WHITENOISE", "White noise" };
208 case TYPE::V_PINKNOISE: return { DEVICE_T::V, "PINKNOISE", "Pink noise (1/f)" };
209 case TYPE::V_BURSTNOISE: return { DEVICE_T::V, "BURSTNOISE", "Burst noise" };
210 case TYPE::V_RANDUNIFORM: return { DEVICE_T::V, "RANDUNIFORM", "Random uniform" };
211 case TYPE::V_RANDGAUSSIAN: return { DEVICE_T::V, "RANDGAUSSIAN", "Random Gaussian" };
212 case TYPE::V_RANDEXP: return { DEVICE_T::V, "RANDEXP", "Random exponential" };
213 case TYPE::V_RANDPOISSON: return { DEVICE_T::V, "RANDPOISSON", "Random Poisson" };
214 case TYPE::V_BEHAVIORAL: return { DEVICE_T::V, "=", "Behavioral" };
215
216 case TYPE::I: return { DEVICE_T::I, "DC", "DC", };
217 case TYPE::I_SIN: return { DEVICE_T::I, "SIN", "Sine" };
218 case TYPE::I_PULSE: return { DEVICE_T::I, "PULSE", "Pulse" };
219 case TYPE::I_EXP: return { DEVICE_T::I, "EXP", "Exponential" };
220 case TYPE::I_AM: return { DEVICE_T::I, "AM", "Amplitude modulated" };
221 case TYPE::I_SFFM: return { DEVICE_T::I, "SFFM", "Single-frequency FM" };
222 case TYPE::I_VCL: return { DEVICE_T::G, "", "Voltage-controlled" };
223 case TYPE::I_CCL: return { DEVICE_T::F, "", "Current-controlled" };
224 case TYPE::I_PWL: return { DEVICE_T::I, "PWL", "Piecewise linear" };
225 case TYPE::I_WHITENOISE: return { DEVICE_T::I, "WHITENOISE", "White noise" };
226 case TYPE::I_PINKNOISE: return { DEVICE_T::I, "PINKNOISE", "Pink noise (1/f)" };
227 case TYPE::I_BURSTNOISE: return { DEVICE_T::I, "BURSTNOISE", "Burst noise" };
228 case TYPE::I_RANDUNIFORM: return { DEVICE_T::I, "RANDUNIFORM", "Random uniform" };
229 case TYPE::I_RANDGAUSSIAN: return { DEVICE_T::I, "RANDGAUSSIAN", "Random Gaussian" };
230 case TYPE::I_RANDEXP: return { DEVICE_T::I, "RANDEXP", "Random exponential" };
231 case TYPE::I_RANDPOISSON: return { DEVICE_T::I, "RANDPOISSON", "Random Poisson" };
232 case TYPE::I_BEHAVIORAL: return { DEVICE_T::I, "=", "Behavioral" };
233
234 case TYPE::SUBCKT: return { DEVICE_T::SUBCKT, "", "Subcircuit" };
235 case TYPE::XSPICE: return { DEVICE_T::XSPICE, "", "" };
236
237 case TYPE::KIBIS_DEVICE: return { DEVICE_T::KIBIS, "DEVICE", "Device" };
238 case TYPE::KIBIS_DRIVER_DC: return { DEVICE_T::KIBIS, "DCDRIVER", "DC driver" };
239 case TYPE::KIBIS_DRIVER_RECT: return { DEVICE_T::KIBIS, "RECTDRIVER", "Rectangular wave driver" };
240 case TYPE::KIBIS_DRIVER_PRBS: return { DEVICE_T::KIBIS, "PRBSDRIVER", "PRBS driver" };
241
242 case TYPE::RAWSPICE: return { DEVICE_T::SPICE, "", "" };
243
244 default: wxFAIL; return {};
245 }
246}
247
248
250{
251 switch( aType )
252 {
253 // | itemType | modelType | fnName | level |isDefaultLvl|hasExpr|version|
254 // -------------------------------------------------------------------------
255 case TYPE::R: return { "R", "" };
256 case TYPE::R_POT: return { "A", "" };
257 case TYPE::R_BEHAVIORAL: return { "R", "", "", "0", false, true };
258
259 case TYPE::C: return { "C", "" };
260 case TYPE::C_BEHAVIORAL: return { "C", "", "", "0", false, true };
261
262 case TYPE::L: return { "L", "" };
263 case TYPE::L_BEHAVIORAL: return { "L", "", "", "0", false, true };
264
265 case TYPE::K: return { "K", "" };
266
267 //case TYPE::TLINE_Z0: return { "T" };
268 case TYPE::TLINE_Z0: return { "O", "LTRA" };
269 case TYPE::TLINE_RLGC: return { "O", "LTRA" };
270
271 case TYPE::SW_V: return { "S", "SW" };
272 case TYPE::SW_I: return { "W", "CSW" };
273
274 case TYPE::D: return { "D", "D" };
275
276 case TYPE::NPN_VBIC: return { "Q", "NPN", "", "4" };
277 case TYPE::PNP_VBIC: return { "Q", "PNP", "", "4" };
278 case TYPE::NPN_GUMMELPOON: return { "Q", "NPN", "", "1", true };
279 case TYPE::PNP_GUMMELPOON: return { "Q", "PNP", "", "1", true };
280 case TYPE::NPN_HICUM2: return { "Q", "NPN", "", "8" };
281 case TYPE::PNP_HICUM2: return { "Q", "PNP", "", "8" };
282
283 case TYPE::NJFET_SHICHMANHODGES: return { "J", "NJF", "", "1", true };
284 case TYPE::PJFET_SHICHMANHODGES: return { "J", "PJF", "", "1", true };
285 case TYPE::NJFET_PARKERSKELLERN: return { "J", "NJF", "", "2" };
286 case TYPE::PJFET_PARKERSKELLERN: return { "J", "PJF", "", "2" };
287
288 case TYPE::NMES_STATZ: return { "Z", "NMF", "", "1", true };
289 case TYPE::PMES_STATZ: return { "Z", "PMF", "", "1", true };
290 case TYPE::NMES_YTTERDAL: return { "Z", "NMF", "", "2" };
291 case TYPE::PMES_YTTERDAL: return { "Z", "PMF", "", "2" };
292 case TYPE::NMES_HFET1: return { "Z", "NMF", "", "5" };
293 case TYPE::PMES_HFET1: return { "Z", "PMF", "", "5" };
294 case TYPE::NMES_HFET2: return { "Z", "NMF", "", "6" };
295 case TYPE::PMES_HFET2: return { "Z", "PMF", "", "6" };
296
297 case TYPE::NMOS_VDMOS: return { "M", "VDMOS NCHAN" };
298 case TYPE::PMOS_VDMOS: return { "M", "VDMOS PCHAN" };
299 case TYPE::NMOS_MOS1: return { "M", "NMOS", "", "1", true };
300 case TYPE::PMOS_MOS1: return { "M", "PMOS", "", "1", true };
301 case TYPE::NMOS_MOS2: return { "M", "NMOS", "", "2" };
302 case TYPE::PMOS_MOS2: return { "M", "PMOS", "", "2" };
303 case TYPE::NMOS_MOS3: return { "M", "NMOS", "", "3" };
304 case TYPE::PMOS_MOS3: return { "M", "PMOS", "", "3" };
305 case TYPE::NMOS_BSIM1: return { "M", "NMOS", "", "4" };
306 case TYPE::PMOS_BSIM1: return { "M", "PMOS", "", "4" };
307 case TYPE::NMOS_BSIM2: return { "M", "NMOS", "", "5" };
308 case TYPE::PMOS_BSIM2: return { "M", "PMOS", "", "5" };
309 case TYPE::NMOS_MOS6: return { "M", "NMOS", "", "6" };
310 case TYPE::PMOS_MOS6: return { "M", "PMOS", "", "6" };
311 case TYPE::NMOS_BSIM3: return { "M", "NMOS", "", "8" };
312 case TYPE::PMOS_BSIM3: return { "M", "PMOS", "", "8" };
313 case TYPE::NMOS_MOS9: return { "M", "NMOS", "", "9" };
314 case TYPE::PMOS_MOS9: return { "M", "PMOS", "", "9" };
315 case TYPE::NMOS_B4SOI: return { "M", "NMOS", "", "10" };
316 case TYPE::PMOS_B4SOI: return { "M", "PMOS", "", "10" };
317 case TYPE::NMOS_BSIM4: return { "M", "NMOS", "", "14" };
318 case TYPE::PMOS_BSIM4: return { "M", "PMOS", "", "14" };
319 //case TYPE::NMOS_EKV2_6: return {};
320 //case TYPE::PMOS_EKV2_6: return {};
321 //case TYPE::NMOS_PSP: return {};
322 //case TYPE::PMOS_PSP: return {};
323 case TYPE::NMOS_B3SOIFD: return { "M", "NMOS", "", "55" };
324 case TYPE::PMOS_B3SOIFD: return { "M", "PMOS", "", "55" };
325 case TYPE::NMOS_B3SOIDD: return { "M", "NMOS", "", "56" };
326 case TYPE::PMOS_B3SOIDD: return { "M", "PMOS", "", "56" };
327 case TYPE::NMOS_B3SOIPD: return { "M", "NMOS", "", "57" };
328 case TYPE::PMOS_B3SOIPD: return { "M", "PMOS", "", "57" };
329 //case TYPE::NMOS_STAG: return {};
330 //case TYPE::PMOS_STAG: return {};
331 case TYPE::NMOS_HISIM2: return { "M", "NMOS", "", "68" };
332 case TYPE::PMOS_HISIM2: return { "M", "PMOS", "", "68" };
333 case TYPE::NMOS_HISIMHV1: return { "M", "NMOS", "", "73", false, false, "1.2.4" };
334 case TYPE::PMOS_HISIMHV1: return { "M", "PMOS", "", "73", false, false, "1.2.4" };
335 case TYPE::NMOS_HISIMHV2: return { "M", "NMOS", "", "73", false, false, "2.2.0" };
336 case TYPE::PMOS_HISIMHV2: return { "M", "PMOS", "", "73", false, false, "2.2.0" };
337
338 case TYPE::V: return { "V", "", "DC" };
339 case TYPE::V_SIN: return { "V", "", "SIN" };
340 case TYPE::V_PULSE: return { "V", "", "PULSE" };
341 case TYPE::V_EXP: return { "V", "", "EXP" };
342 case TYPE::V_AM: return { "V", "", "AM" };
343 case TYPE::V_SFFM: return { "V", "", "SFFM" };
344 case TYPE::V_VCL: return { "E", "", "" };
345 case TYPE::V_CCL: return { "H", "", "" };
346 case TYPE::V_PWL: return { "V", "", "PWL" };
347 case TYPE::V_WHITENOISE: return { "V", "", "TRNOISE" };
348 case TYPE::V_PINKNOISE: return { "V", "", "TRNOISE" };
349 case TYPE::V_BURSTNOISE: return { "V", "", "TRNOISE" };
350 case TYPE::V_RANDUNIFORM: return { "V", "", "TRRANDOM" };
351 case TYPE::V_RANDGAUSSIAN: return { "V", "", "TRRANDOM" };
352 case TYPE::V_RANDEXP: return { "V", "", "TRRANDOM" };
353 case TYPE::V_RANDPOISSON: return { "V", "", "TRRANDOM" };
354 case TYPE::V_BEHAVIORAL: return { "B" };
355
356 case TYPE::I: return { "I", "", "DC" };
357 case TYPE::I_PULSE: return { "I", "", "PULSE" };
358 case TYPE::I_SIN: return { "I", "", "SIN" };
359 case TYPE::I_EXP: return { "I", "", "EXP" };
360 case TYPE::I_AM: return { "V", "", "AM" };
361 case TYPE::I_SFFM: return { "V", "", "SFFM" };
362 case TYPE::I_VCL: return { "G", "", "" };
363 case TYPE::I_CCL: return { "F", "", "" };
364 case TYPE::I_PWL: return { "I", "", "PWL" };
365 case TYPE::I_WHITENOISE: return { "I", "", "TRNOISE" };
366 case TYPE::I_PINKNOISE: return { "I", "", "TRNOISE" };
367 case TYPE::I_BURSTNOISE: return { "I", "", "TRNOISE" };
368 case TYPE::I_RANDUNIFORM: return { "I", "", "TRRANDOM" };
369 case TYPE::I_RANDGAUSSIAN: return { "I", "", "TRRANDOM" };
370 case TYPE::I_RANDEXP: return { "I", "", "TRRANDOM" };
371 case TYPE::I_RANDPOISSON: return { "I", "", "TRRANDOM" };
372 case TYPE::I_BEHAVIORAL: return { "B" };
373
374 case TYPE::SUBCKT: return { "X" };
375 case TYPE::XSPICE: return { "A" };
376
377 case TYPE::KIBIS_DEVICE: return { "X" };
378 case TYPE::KIBIS_DRIVER_DC: return { "X" };
379 case TYPE::KIBIS_DRIVER_RECT: return { "X" };
380 case TYPE::KIBIS_DRIVER_PRBS: return { "X" };
381
382 case TYPE::NONE:
383 case TYPE::RAWSPICE: return {};
384
385 default: wxFAIL; return {};
386 }
387}
388
389
390TYPE SIM_MODEL::ReadTypeFromFields( const std::vector<SCH_FIELD>& aFields, REPORTER& aReporter )
391{
392 std::string deviceTypeFieldValue = GetFieldValue( &aFields, SIM_DEVICE_FIELD );
393 std::string typeFieldValue = GetFieldValue( &aFields, SIM_DEVICE_SUBTYPE_FIELD );
394
395 if( !deviceTypeFieldValue.empty() )
396 {
397 for( TYPE type : TYPE_ITERATOR() )
398 {
399 if( typeFieldValue == TypeInfo( type ).fieldValue )
400 {
401 if( deviceTypeFieldValue == DeviceInfo( TypeInfo( type ).deviceType ).fieldValue )
402 return type;
403 }
404 }
405 }
406
407 if( typeFieldValue.empty() )
408 return TYPE::NONE;
409
410 if( aFields.size() > REFERENCE_FIELD )
411 {
412 aReporter.Report( wxString::Format( _( "No simulation model definition found for "
413 "symbol '%s'." ),
414 aFields[REFERENCE_FIELD].GetText() ),
416 }
417 else
418 {
419 aReporter.Report( _( "No simulation model definition found." ),
421 }
422
423 return TYPE::NONE;
424}
425
426
427void SIM_MODEL::ReadDataFields( const std::vector<SCH_FIELD>* aFields,
428 const std::vector<SCH_PIN*>& aPins )
429{
430 bool diffMode = GetFieldValue( aFields, SIM_LIBRARY_IBIS::DIFF_FIELD ) == "1";
431 SwitchSingleEndedDiff( diffMode );
432
433 m_serializer->ParseEnable( GetFieldValue( aFields, SIM_LEGACY_ENABLE_FIELD_V7 ) );
434
435 createPins( aPins );
436 m_serializer->ParsePins( GetFieldValue( aFields, SIM_PINS_FIELD ) );
437
438 std::string paramsField = GetFieldValue( aFields, SIM_PARAMS_FIELD );
439
440 if( !m_serializer->ParseParams( paramsField ) )
441 m_serializer->ParseValue( GetFieldValue( aFields, SIM_VALUE_FIELD ) );
442}
443
444
445void SIM_MODEL::WriteFields( std::vector<SCH_FIELD>& aFields ) const
446{
447 // Remove duplicate fields: they are at the end of list
448 for( size_t ii = aFields.size() - 1; ii > 0; ii-- )
449 {
450 wxString currFieldName = aFields[ii].GetName();
451
452 auto end_candidate_list = aFields.begin() + ii - 1;
453
454 auto fieldIt = std::find_if( aFields.begin(), end_candidate_list,
455 [&]( const SCH_FIELD& f )
456 {
457 return f.GetName() == currFieldName;
458 } );
459
460 // If duplicate field found: remove current checked item
461 if( fieldIt != end_candidate_list )
462 aFields.erase( aFields.begin() + ii );
463 }
464
465 SetFieldValue( aFields, SIM_DEVICE_FIELD, m_serializer->GenerateDevice(), false );
466 SetFieldValue( aFields, SIM_DEVICE_SUBTYPE_FIELD, m_serializer->GenerateDeviceSubtype(),
467 false );
468
469 SetFieldValue( aFields, SIM_LEGACY_ENABLE_FIELD_V7, m_serializer->GenerateEnable(), false );
470 SetFieldValue( aFields, SIM_PINS_FIELD, m_serializer->GeneratePins(), false );
471
472 SetFieldValue( aFields, SIM_PARAMS_FIELD, m_serializer->GenerateParams(), false );
473
474 if( IsStoredInValue() )
475 SetFieldValue( aFields, SIM_VALUE_FIELD, m_serializer->GenerateValue(), false );
476
477 // New fields have a ID = -1 (undefined). so replace the undefined ID
478 // by a degined ID
479 int lastFreeId = MANDATORY_FIELDS;
480
481 // Search for the first available value:
482 for( auto& fld : aFields )
483 {
484 if( fld.GetId() >= lastFreeId )
485 lastFreeId = fld.GetId() + 1;
486 }
487
488 // Set undefined IDs to a better value
489 for( auto& fld : aFields )
490 {
491 if( fld.GetId() < 0 )
492 fld.SetId( lastFreeId++ );
493 }
494}
495
496
497std::unique_ptr<SIM_MODEL> SIM_MODEL::Create( TYPE aType, const std::vector<SCH_PIN*>& aPins,
498 REPORTER& aReporter )
499{
500 std::unique_ptr<SIM_MODEL> model = Create( aType );
501
502 try
503 {
504 // Passing nullptr to ReadDataFields will make it act as if all fields were empty.
505 model->ReadDataFields( static_cast<const std::vector<SCH_FIELD>*>( nullptr ), aPins );
506 }
507 catch( IO_ERROR& )
508 {
509 wxFAIL_MSG( "Shouldn't throw reading empty fields!" );
510 }
511
512 return model;
513}
514
515
516std::unique_ptr<SIM_MODEL> SIM_MODEL::Create( const SIM_MODEL* aBaseModel,
517 const std::vector<SCH_PIN*>& aPins,
518 REPORTER& aReporter )
519{
520 std::unique_ptr<SIM_MODEL> model;
521
522 if( aBaseModel )
523 {
524 TYPE type = aBaseModel->GetType();
525
526 if( dynamic_cast<const SIM_MODEL_SPICE_FALLBACK*>( aBaseModel ) )
527 model = std::make_unique<SIM_MODEL_SPICE_FALLBACK>( type );
528 else if( dynamic_cast< const SIM_MODEL_RAW_SPICE*>( aBaseModel ) )
529 model = std::make_unique<SIM_MODEL_RAW_SPICE>();
530 else
531 model = Create( type );
532
533 model->SetBaseModel( *aBaseModel );
534 }
535 else // No base model means the model wasn't found in the library, so create a fallback
536 {
537 model = std::make_unique<SIM_MODEL_SPICE_FALLBACK>( TYPE::NONE );
538 }
539
540 try
541 {
542 model->ReadDataFields( static_cast<const std::vector<SCH_FIELD>*>( nullptr ), aPins );
543 }
544 catch( IO_ERROR& )
545 {
546 wxFAIL_MSG( "Shouldn't throw reading empty fields!" );
547 }
548
549 return model;
550}
551
552
553std::unique_ptr<SIM_MODEL> SIM_MODEL::Create( const SIM_MODEL* aBaseModel,
554 const std::vector<SCH_PIN*>& aPins,
555 const std::vector<SCH_FIELD>& aFields,
556 REPORTER& aReporter )
557{
558 std::unique_ptr<SIM_MODEL> model;
559
560 if( aBaseModel )
561 {
562 NULL_REPORTER devnull;
563 TYPE type = aBaseModel->GetType();
564 TYPE type_override = ReadTypeFromFields( aFields, devnull );
565
566 // Check for an override in the case of IBIS models.
567 // The other models require type to be set from the base model.
568 if( dynamic_cast<const SIM_MODEL_IBIS*>( aBaseModel ) &&
569 type_override != TYPE::NONE )
570 {
571 type = type_override;
572 }
573
574 if( dynamic_cast<const SIM_MODEL_SPICE_FALLBACK*>( aBaseModel ) )
575 model = std::make_unique<SIM_MODEL_SPICE_FALLBACK>( type );
576 else if( dynamic_cast< const SIM_MODEL_RAW_SPICE*>( aBaseModel ) )
577 model = std::make_unique<SIM_MODEL_RAW_SPICE>();
578 else
579 model = Create( type );
580
581 model->SetBaseModel( *aBaseModel );
582 }
583 else // No base model means the model wasn't found in the library, so create a fallback
584 {
585 TYPE type = ReadTypeFromFields( aFields, aReporter );
586 model = std::make_unique<SIM_MODEL_SPICE_FALLBACK>( type );
587 }
588
589 try
590 {
591 model->ReadDataFields( &aFields, aPins );
592 }
593 catch( IO_ERROR& err )
594 {
595 aReporter.Report( wxString::Format( _( "Error reading simulation model from "
596 "symbol '%s':\n%s" ),
597 aFields[REFERENCE_FIELD].GetText(),
598 err.Problem() ),
600 }
601
602 return model;
603}
604
605
606std::unique_ptr<SIM_MODEL> SIM_MODEL::Create( const std::vector<SCH_FIELD>& aFields,
607 const std::vector<SCH_PIN*>& aPins,
608 bool aResolved, REPORTER& aReporter )
609{
610 TYPE type = ReadTypeFromFields( aFields, aReporter );
611 std::unique_ptr<SIM_MODEL> model = SIM_MODEL::Create( type );
612
613 try
614 {
615 model->ReadDataFields( &aFields, aPins );
616 }
617 catch( const IO_ERROR& parse_err )
618 {
619 if( !aResolved )
620 {
621 aReporter.Report( parse_err.What(), RPT_SEVERITY_ERROR );
622 return model;
623 }
624
625 // Just because we can't parse it doesn't mean that a SPICE interpreter can't. Fall
626 // back to a raw spice code model.
627
628 std::string modelData = GetFieldValue( &aFields, SIM_PARAMS_FIELD );
629
630 if( modelData.empty() )
631 modelData = GetFieldValue( &aFields, SIM_VALUE_FIELD );
632
633 model = std::make_unique<SIM_MODEL_RAW_SPICE>( modelData );
634
635 try
636 {
637 model->createPins( aPins );
638 model->m_serializer->ParsePins( GetFieldValue( &aFields, SIM_PINS_FIELD ) );
639 }
640 catch( const IO_ERROR& err )
641 {
642 // We own the pin syntax, so if we can't parse it then there's an error.
643 aReporter.Report( wxString::Format( _( "Error reading simulation model from "
644 "symbol '%s':\n%s" ),
645 aFields[REFERENCE_FIELD].GetText(),
646 err.Problem() ),
648 }
649 }
650
651 return model;
652}
653
654
655std::string SIM_MODEL::GetFieldValue( const std::vector<SCH_FIELD>* aFields,
656 const wxString& aFieldName, bool aResolve )
657{
658 if( !aFields )
659 return ""; // Should not happen, T=void specialization will be called instead.
660
661 for( const SCH_FIELD& field : *aFields )
662 {
663 if( field.GetName() == aFieldName )
664 {
665 return aResolve ? field.GetShownText( false ).ToStdString()
666 : field.GetText().ToStdString();
667 }
668 }
669
670 return "";
671}
672
673
674void SIM_MODEL::SetFieldValue( std::vector<SCH_FIELD>& aFields, const wxString& aFieldName,
675 const std::string& aValue, bool aIsVisible )
676{
677 auto fieldIt = std::find_if( aFields.begin(), aFields.end(),
678 [&]( const SCH_FIELD& f )
679 {
680 return f.GetName() == aFieldName;
681 } );
682
683 if( fieldIt != aFields.end() )
684 {
685 if( aValue == "" )
686 {
687 aFields.erase( fieldIt );
688 }
689 else
690 {
691 fieldIt->SetText( aValue );
692 }
693
694 return;
695 }
696
697 if( aValue == "" )
698 return;
699
700 SCH_ITEM* parent = static_cast<SCH_ITEM*>( aFields.at( 0 ).GetParent() );
701 aFields.emplace_back( VECTOR2I(), aFields.size(), parent, aFieldName );
702
703 aFields.back().SetText( aValue );
704 aFields.back().SetVisible( aIsVisible );
705}
706
707
708SIM_MODEL::~SIM_MODEL() = default;
709
710
712{
713 m_modelPins.push_back( aPin );
714}
715
716
718{
719 m_modelPins.clear();
720}
721
722
723int SIM_MODEL::FindModelPinIndex( const std::string& aSymbolPinNumber )
724{
725 for( int modelPinIndex = 0; modelPinIndex < GetPinCount(); ++modelPinIndex )
726 {
727 if( GetPin( modelPinIndex ).symbolPinNumber == aSymbolPinNumber )
728 return modelPinIndex;
729 }
730
732}
733
734
736{
737 m_params.emplace_back( aInfo );
738
739 // Enums are initialized with their default values.
740 if( aInfo.enumValues.size() >= 1 )
741 m_params.back().value = aInfo.defaultValue;
742}
743
744
745void SIM_MODEL::SetBaseModel( const SIM_MODEL& aBaseModel )
746{
747 wxASSERT_MSG( GetType() == aBaseModel.GetType(),
748 wxS( "Simulation model type must be the same as its base class!" ) );
749
750 m_baseModel = &aBaseModel;
751}
752
753
754std::vector<std::reference_wrapper<const SIM_MODEL_PIN>> SIM_MODEL::GetPins() const
755{
756 std::vector<std::reference_wrapper<const SIM_MODEL_PIN>> pins;
757
758 for( int modelPinIndex = 0; modelPinIndex < GetPinCount(); ++modelPinIndex )
759 pins.emplace_back( GetPin( modelPinIndex ) );
760
761 return pins;
762}
763
765 const wxString& aSymbolPinNumber )
766{
767 if( aModelPinIndex >= 0 && aModelPinIndex < (int) m_modelPins.size() )
768 m_modelPins.at( aModelPinIndex ).symbolPinNumber = aSymbolPinNumber;
769}
770
771
772void SIM_MODEL::AssignSymbolPinNumberToModelPin( const std::string& aModelPinName,
773 const wxString& aSymbolPinNumber )
774{
776 {
777 if( pin.modelPinName == aModelPinName )
778 {
779 pin.symbolPinNumber = aSymbolPinNumber;
780 return;
781 }
782 }
783
784 // If aPinName wasn't in fact a name, see if it's a raw (1-based) index. This is required
785 // for legacy files which didn't use pin names.
786 int pinIndex = (int) strtol( aModelPinName.c_str(), nullptr, 10 );
787
788 if( pinIndex < 1 || pinIndex > (int) m_modelPins.size() )
789 THROW_IO_ERROR( wxString::Format( _( "Unknown simulation model pin '%s'" ), aModelPinName ) );
790
791 m_modelPins[ --pinIndex /* convert to 0-based */ ].symbolPinNumber = aSymbolPinNumber;
792}
793
794
795const SIM_MODEL::PARAM& SIM_MODEL::GetParam( unsigned aParamIndex ) const
796{
797 if( m_baseModel && m_params.at( aParamIndex ).value == "" )
798 return m_baseModel->GetParam( aParamIndex );
799 else
800 return m_params.at( aParamIndex );
801}
802
803
804bool SIM_MODEL::PARAM::INFO::Matches( const std::string& aParamName ) const
805{
806 return boost::iequals( name, aParamName );
807}
808
809
810int SIM_MODEL::doFindParam( const std::string& aParamName ) const
811{
812 for( int ii = 0; ii < (int) GetParamCount(); ++ii )
813 {
814 if( GetParam( ii ).Matches( aParamName ) )
815 return ii;
816 }
817
818 return -1;
819}
820
821
822const SIM_MODEL::PARAM* SIM_MODEL::FindParam( const std::string& aParamName ) const
823{
824 int idx = doFindParam( aParamName );
825
826 return idx >= 0 ? &GetParam( idx ) : nullptr;
827}
828
829
830const SIM_MODEL::PARAM& SIM_MODEL::GetParamOverride( unsigned aParamIndex ) const
831{
832 return m_params.at( aParamIndex );
833}
834
835
836const SIM_MODEL::PARAM& SIM_MODEL::GetBaseParam( unsigned aParamIndex ) const
837{
838 if( m_baseModel )
839 return m_baseModel->GetParam( aParamIndex );
840 else
841 return m_params.at( aParamIndex );
842}
843
844
845void SIM_MODEL::doSetParamValue( int aParamIndex, const std::string& aValue )
846{
847 m_params.at( aParamIndex ).value = aValue;
848}
849
850
851void SIM_MODEL::SetParamValue( int aParamIndex, const std::string& aValue,
852 SIM_VALUE::NOTATION aNotation )
853{
854 // Notation conversion is very slow. Avoid if possible.
855
856 auto plainNumber =
857 []( const std::string& aString )
858 {
859 for( char c : aString )
860 {
861 if( c != '.' && ( c < '0' || c > '9' ) )
862 return false;
863 }
864
865 return true;
866 };
867
868
869 if( aValue.find( ',' ) != std::string::npos )
870 {
871 doSetParamValue( aParamIndex, SIM_VALUE::ConvertNotation( aValue, aNotation,
872 SIM_VALUE::NOTATION::SI ) );
873 }
874 else if( aNotation != SIM_VALUE::NOTATION::SI && !plainNumber( aValue ) )
875 {
876 doSetParamValue( aParamIndex, SIM_VALUE::ConvertNotation( aValue, aNotation,
877 SIM_VALUE::NOTATION::SI ) );
878 }
879 else
880 {
881 doSetParamValue( aParamIndex, aValue );
882 }
883}
884
885
886void SIM_MODEL::SetParamValue( const std::string& aParamName, const std::string& aValue,
887 SIM_VALUE::NOTATION aNotation )
888{
889 int idx = doFindParam( aParamName );
890
891 if( idx < 0 )
892 THROW_IO_ERROR( wxString::Format( "Unknown simulation model parameter '%s'", aParamName ) );
893
894 SetParamValue( idx, aValue, aNotation );
895}
896
897
898std::unique_ptr<SIM_MODEL> SIM_MODEL::Create( TYPE aType )
899{
900 switch( aType )
901 {
902 case TYPE::R:
903 case TYPE::C:
904 case TYPE::L:
905 return std::make_unique<SIM_MODEL_IDEAL>( aType );
906
907 case TYPE::R_POT:
908 return std::make_unique<SIM_MODEL_R_POT>();
909
910 case TYPE::K:
911 return std::make_unique<SIM_MODEL_L_MUTUAL>();
912
913 case TYPE::R_BEHAVIORAL:
914 case TYPE::C_BEHAVIORAL:
915 case TYPE::L_BEHAVIORAL:
916 case TYPE::V_BEHAVIORAL:
917 case TYPE::I_BEHAVIORAL:
918 return std::make_unique<SIM_MODEL_BEHAVIORAL>( aType );
919
920 case TYPE::TLINE_Z0:
921 case TYPE::TLINE_RLGC:
922 return std::make_unique<SIM_MODEL_TLINE>( aType );
923
924 case TYPE::SW_V:
925 case TYPE::SW_I:
926 return std::make_unique<SIM_MODEL_SWITCH>( aType );
927
928 case TYPE::V:
929 case TYPE::I:
930 case TYPE::V_SIN:
931 case TYPE::I_SIN:
932 case TYPE::V_PULSE:
933 case TYPE::I_PULSE:
934 case TYPE::V_EXP:
935 case TYPE::I_EXP:
936 case TYPE::V_AM:
937 case TYPE::I_AM:
938 case TYPE::V_SFFM:
939 case TYPE::I_SFFM:
940 case TYPE::V_VCL:
941 case TYPE::V_CCL:
942 case TYPE::V_PWL:
943 case TYPE::I_VCL:
944 case TYPE::I_CCL:
945 case TYPE::I_PWL:
946 case TYPE::V_WHITENOISE:
947 case TYPE::I_WHITENOISE:
948 case TYPE::V_PINKNOISE:
949 case TYPE::I_PINKNOISE:
950 case TYPE::V_BURSTNOISE:
951 case TYPE::I_BURSTNOISE:
952 case TYPE::V_RANDUNIFORM:
953 case TYPE::I_RANDUNIFORM:
954 case TYPE::V_RANDGAUSSIAN:
955 case TYPE::I_RANDGAUSSIAN:
956 case TYPE::V_RANDEXP:
957 case TYPE::I_RANDEXP:
958 case TYPE::V_RANDPOISSON:
959 case TYPE::I_RANDPOISSON:
960 return std::make_unique<SIM_MODEL_SOURCE>( aType );
961
962 case TYPE::SUBCKT:
963 return std::make_unique<SIM_MODEL_SUBCKT>();
964
965 case TYPE::XSPICE:
966 return std::make_unique<SIM_MODEL_XSPICE>( aType );
967
968 case TYPE::KIBIS_DEVICE:
969 case TYPE::KIBIS_DRIVER_DC:
970 case TYPE::KIBIS_DRIVER_RECT:
971 case TYPE::KIBIS_DRIVER_PRBS:
972 return std::make_unique<SIM_MODEL_IBIS>( aType );
973
974 case TYPE::RAWSPICE:
975 return std::make_unique<SIM_MODEL_RAW_SPICE>();
976
977 default:
978 return std::make_unique<SIM_MODEL_NGSPICE>( aType );
979 }
980}
981
982
984 SIM_MODEL( aType, std::make_unique<SPICE_GENERATOR>( *this ),
985 std::make_unique<SIM_MODEL_SERIALIZER>( *this ) )
986{
987}
988
989
990SIM_MODEL::SIM_MODEL( TYPE aType, std::unique_ptr<SPICE_GENERATOR> aSpiceGenerator ) :
991 SIM_MODEL( aType, std::move( aSpiceGenerator ),
992 std::make_unique<SIM_MODEL_SERIALIZER>( *this ) )
993{
994}
995
996
997SIM_MODEL::SIM_MODEL( TYPE aType, std::unique_ptr<SPICE_GENERATOR> aSpiceGenerator,
998 std::unique_ptr<SIM_MODEL_SERIALIZER> aSerializer ) :
999 m_baseModel( nullptr ),
1000 m_serializer( std::move( aSerializer ) ),
1001 m_spiceGenerator( std::move( aSpiceGenerator ) ),
1002 m_type( aType ),
1003 m_isEnabled( true ),
1004 m_isStoredInValue( false )
1005{
1006}
1007
1008
1009void SIM_MODEL::createPins( const std::vector<SCH_PIN*>& aSymbolPins )
1010{
1011 // Default pin sequence: model pins are the same as symbol pins.
1012 // Excess model pins are set as Not Connected.
1013 // Note that intentionally nothing is added if `GetPinNames()` returns an empty vector.
1014
1015 // SIM_MODEL pins must be ordered by symbol pin numbers -- this is assumed by the code that
1016 // accesses them.
1017
1018 std::vector<std::string> pinNames = GetPinNames();
1019
1020 for( unsigned modelPinIndex = 0; modelPinIndex < pinNames.size(); ++modelPinIndex )
1021 {
1022 wxString pinName = pinNames[ modelPinIndex ];
1023 bool optional = false;
1024
1025 if( pinName.StartsWith( '<' ) && pinName.EndsWith( '>' ) )
1026 {
1027 pinName = pinName.Mid( 1, pinName.Length() - 2 );
1028 optional = true;
1029 }
1030
1031 if( modelPinIndex < aSymbolPins.size() )
1032 {
1033 AddPin( { pinNames.at( modelPinIndex ),
1034 aSymbolPins[ modelPinIndex ]->GetNumber().ToStdString() } );
1035 }
1036 else if( !optional )
1037 {
1038 AddPin( { pinNames.at( modelPinIndex ), "" } );
1039 }
1040 }
1041}
1042
1043
1045{
1046 // SUBCKTs are a single level; there's never a baseModel.
1047 if( m_type == TYPE::SUBCKT )
1048 return false;
1049
1050 // Model must be written if there's no base model or the base model is an internal model
1051 if( !m_baseModel || aItem.baseModelName == "" )
1052 return true;
1053
1054 for( int ii = 0; ii < GetParamCount(); ++ii )
1055 {
1056 const PARAM& param = m_params[ii];
1057
1058 // Instance parameters are written in item lines
1059 if( param.info.isSpiceInstanceParam )
1060 continue;
1061
1062 // Empty parameters are interpreted as default-value
1063 if ( param.value == "" )
1064 continue;
1065
1066 if( const SIM_MODEL* baseModel = dynamic_cast<const SIM_MODEL*>( m_baseModel ) )
1067 {
1068 const std::string& baseValue = baseModel->m_params[ii].value;
1069
1070 if( param.value == baseValue )
1071 continue;
1072
1073 // One more check for equivalence, mostly for early 7.0 files which wrote all
1074 // parameters to the Sim.Params field in normalized format
1075 if( param.value == SIM_VALUE::Normalize( SIM_VALUE::ToDouble( baseValue ) ) )
1076 continue;
1077
1078 // Overrides must be written
1079 return true;
1080 }
1081 }
1082
1083 return false;
1084}
1085
1086
1087template <class T>
1088bool SIM_MODEL::InferSimModel( T& aSymbol, std::vector<SCH_FIELD>* aFields, bool aResolve,
1089 SIM_VALUE_GRAMMAR::NOTATION aNotation, wxString* aDeviceType,
1090 wxString* aModelType, wxString* aModelParams, wxString* aPinMap )
1091{
1092 // SPICE notation is case-insensitive and locale-insensitve. This means it uses "Meg" for
1093 // mega (as both 'M' and 'm' must mean milli), and "." (always) for a decimal separator.
1094 //
1095 // KiCad's GUI uses the SI-standard 'M' for mega and 'm' for milli, and a locale-dependent
1096 // decimal separator.
1097 //
1098 // KiCad's Sim.* fields are in-between, using SI notation but a fixed decimal separator.
1099 //
1100 // So where does that leave inferred value fields? Behavioural models must be passed in
1101 // straight, because we don't (at present) know how to parse them.
1102 //
1103 // However, behavioural models _look_ like SPICE code, so it's not a stretch to expect them
1104 // to _be_ SPICE code. A passive capacitor model on the other hand, just looks like a
1105 // capacitance. Some users might expect 3,3u to work, while others might expect 3,300uF to
1106 // work.
1107 //
1108 // Checking the locale isn't reliable because it assumes the current computer's locale is
1109 // the same as the locale the schematic was authored in -- something that isn't true, for
1110 // instance, when sharing designs over DIYAudio.com.
1111 //
1112 // However, even the E192 series of preferred values uses only 3 significant digits, so a ','
1113 // or '.' followed by 3 digits _could_ reasonably-reliably be interpreted as a thousands
1114 // separator.
1115 //
1116 // Or we could just say inferred values are locale-independent, with "." used as a decimal
1117 // separator and "," used as a thousands separator. 3,300uF works, but 3,3 does not.
1118
1119 auto convertNotation =
1120 [&]( const wxString& units ) -> wxString
1121 {
1124 if( units == wxS( "µ" ) || units == wxS( "μ" ) )
1125 return wxS( "u" );
1126
1127 if( aNotation == SIM_VALUE_GRAMMAR::NOTATION::SPICE )
1128 {
1129 if( units == wxT( "M" ) )
1130 return wxT( "Meg" );
1131 }
1132 else if( aNotation == SIM_VALUE_GRAMMAR::NOTATION::SI )
1133 {
1134 if( units.Capitalize() == wxT( "Meg" ) )
1135 return wxT( "M" );
1136 }
1137
1138 return units;
1139 };
1140
1141 auto convertSeparators =
1142 []( wxString* mantissa )
1143 {
1144 mantissa->Replace( wxS( " " ), wxEmptyString );
1145
1146 wxChar ambiguousSeparator = '?';
1147 wxChar thousandsSeparator = '?';
1148 bool thousandsSeparatorFound = false;
1149 wxChar decimalSeparator = '?';
1150 bool decimalSeparatorFound = false;
1151 int digits = 0;
1152
1153 for( int ii = (int) mantissa->length() - 1; ii >= 0; --ii )
1154 {
1155 wxChar c = mantissa->GetChar( ii );
1156
1157 if( c >= '0' && c <= '9' )
1158 {
1159 digits += 1;
1160 }
1161 else if( c == '.' || c == ',' )
1162 {
1163 if( decimalSeparator != '?' || thousandsSeparator != '?' )
1164 {
1165 // We've previously found a non-ambiguous separator...
1166
1167 if( c == decimalSeparator )
1168 {
1169 if( thousandsSeparatorFound )
1170 return false; // decimal before thousands
1171 else if( decimalSeparatorFound )
1172 return false; // more than one decimal
1173 else
1174 decimalSeparatorFound = true;
1175 }
1176 else if( c == thousandsSeparator )
1177 {
1178 if( digits != 3 )
1179 return false; // thousands not followed by 3 digits
1180 else
1181 thousandsSeparatorFound = true;
1182 }
1183 }
1184 else if( ambiguousSeparator != '?' )
1185 {
1186 // We've previously found a separator, but we don't know for sure
1187 // which...
1188
1189 if( c == ambiguousSeparator )
1190 {
1191 // They both must be thousands separators
1192 thousandsSeparator = ambiguousSeparator;
1193 thousandsSeparatorFound = true;
1194 decimalSeparator = c == '.' ? ',' : '.';
1195 }
1196 else
1197 {
1198 // The first must have been a decimal, and this must be a
1199 // thousands.
1200 decimalSeparator = ambiguousSeparator;
1201 decimalSeparatorFound = true;
1202 thousandsSeparator = c;
1203 thousandsSeparatorFound = true;
1204 }
1205 }
1206 else
1207 {
1208 // This is the first separator...
1209
1210 // If it's preceeded by a '0' (only), or if it's followed by some
1211 // number of digits not equal to 3, then it -must- be a decimal
1212 // separator.
1213 //
1214 // In all other cases we don't really know what it is yet.
1215
1216 if( ( ii == 1 && mantissa->GetChar( 0 ) == '0' ) || digits != 3 )
1217 {
1218 decimalSeparator = c;
1219 decimalSeparatorFound = true;
1220 thousandsSeparator = c == '.' ? ',' : '.';
1221 }
1222 else
1223 {
1224 ambiguousSeparator = c;
1225 }
1226 }
1227
1228 digits = 0;
1229 }
1230 else
1231 {
1232 digits = 0;
1233 }
1234 }
1235
1236 // If we found nothing difinitive then we have to assume SPICE-native syntax
1237 if( decimalSeparator == '?' && thousandsSeparator == '?' )
1238 {
1239 decimalSeparator = '.';
1240 thousandsSeparator = ',';
1241 }
1242
1243 mantissa->Replace( thousandsSeparator, wxEmptyString );
1244 mantissa->Replace( decimalSeparator, '.' );
1245
1246 return true;
1247 };
1248
1249 wxString prefix = aSymbol.GetPrefix();
1250 wxString library = GetFieldValue( aFields, SIM_LIBRARY_FIELD, aResolve );
1251 wxString modelName = GetFieldValue( aFields, SIM_NAME_FIELD, aResolve );
1252 wxString value = GetFieldValue( aFields, SIM_VALUE_FIELD, aResolve );
1253 std::vector<SCH_PIN*> pins = aSymbol.GetPins();
1254
1255 *aDeviceType = GetFieldValue( aFields, SIM_DEVICE_FIELD, aResolve );
1256 *aModelType = GetFieldValue( aFields, SIM_DEVICE_SUBTYPE_FIELD, aResolve );
1257 *aModelParams = GetFieldValue( aFields, SIM_PARAMS_FIELD, aResolve );
1258 *aPinMap = GetFieldValue( aFields, SIM_PINS_FIELD, aResolve );
1259
1260 if( pins.size() != 2 )
1261 return false;
1262
1263 if( ( ( *aDeviceType == "R" || *aDeviceType == "L" || *aDeviceType == "C" )
1264 && aModelType->IsEmpty() )
1265 ||
1266 ( library.IsEmpty() && modelName.IsEmpty()
1267 && aDeviceType->IsEmpty()
1268 && aModelType->IsEmpty()
1269 && !value.IsEmpty()
1270 && ( prefix.StartsWith( "R" ) || prefix.StartsWith( "L" ) || prefix.StartsWith( "C" ) ) ) )
1271 {
1272 if( aModelParams->IsEmpty() )
1273 {
1274 wxRegEx idealVal( wxT( "^"
1275 "([0-9\\,\\. ]+)"
1276 "([fFpPnNuUmMkKgGtTμµ𝛍𝜇𝝁 ]|M(e|E)(g|G))?"
1277 "([fFhHΩΩ𝛀𝛺𝝮rR]|ohm)?"
1278 "([-1-9 ]*)"
1279 "([fFhHΩΩ𝛀𝛺𝝮rR]|ohm)?"
1280 "$" ) );
1281
1282 if( idealVal.Matches( value ) ) // Ideal
1283 {
1284 wxString valueMantissa( idealVal.GetMatch( value, 1 ) );
1285 wxString valueExponent( idealVal.GetMatch( value, 2 ) );
1286 wxString valueFraction( idealVal.GetMatch( value, 6 ) );
1287
1288 if( !convertSeparators( &valueMantissa ) )
1289 return false;
1290
1291 if( valueMantissa.Contains( wxT( "." ) ) || valueFraction.IsEmpty() )
1292 {
1293 aModelParams->Printf( wxT( "%s=\"%s%s\"" ),
1294 prefix.Left(1).Lower(),
1295 valueMantissa,
1296 convertNotation( valueExponent ) );
1297 }
1298 else
1299 {
1300 aModelParams->Printf( wxT( "%s=\"%s.%s%s\"" ),
1301 prefix.Left(1).Lower(),
1302 valueMantissa,
1303 valueFraction,
1304 convertNotation( valueExponent ) );
1305 }
1306 }
1307 else // Behavioral
1308 {
1309 *aModelType = wxT( "=" );
1310 aModelParams->Printf( wxT( "%s=\"%s\"" ), prefix.Left(1).Lower(), value );
1311 }
1312 }
1313
1314 if( aDeviceType->IsEmpty() )
1315 *aDeviceType = prefix.Left( 1 );
1316
1317 if( aPinMap->IsEmpty() )
1318 aPinMap->Printf( wxT( "%s=+ %s=-" ), pins[0]->GetNumber(), pins[1]->GetNumber() );
1319
1320 return true;
1321 }
1322
1323 if( ( ( *aDeviceType == wxT( "V" ) || *aDeviceType == wxT( "I" ) )
1324 && ( aModelType->IsEmpty() || *aModelType == wxT( "DC" ) ) )
1325 ||
1326 ( aDeviceType->IsEmpty()
1327 && aModelType->IsEmpty()
1328 && !value.IsEmpty()
1329 && ( prefix.StartsWith( "V" ) || prefix.StartsWith( "I" ) ) ) )
1330 {
1331 if( !value.IsEmpty() )
1332 {
1333 wxString param = "dc";
1334
1335 if( value.StartsWith( wxT( "DC " ) ) )
1336 {
1337 value = value.Right( value.Length() - 3 );
1338 }
1339 else if( value.StartsWith( wxT( "AC " ) ) )
1340 {
1341 value = value.Right( value.Length() - 3 );
1342 param = "ac";
1343 }
1344
1345 wxRegEx sourceVal( wxT( "^"
1346 "([0-9\\,\\. ]+)"
1347 "([fFpPnNuUmMkKgGtTμµ𝛍𝜇𝝁 ]|M(e|E)(g|G))?"
1348 "([vVaA])?"
1349 "([-1-9 ]*)"
1350 "([vVaA])?"
1351 "$" ) );
1352
1353 if( sourceVal.Matches( value ) )
1354 {
1355 wxString valueMantissa( sourceVal.GetMatch( value, 1 ) );
1356 wxString valueExponent( sourceVal.GetMatch( value, 2 ) );
1357 wxString valueFraction( sourceVal.GetMatch( value, 6 ) );
1358
1359 if( !convertSeparators( &valueMantissa ) )
1360 return false;
1361
1362 if( valueMantissa.Contains( wxT( "." ) ) || valueFraction.IsEmpty() )
1363 {
1364 aModelParams->Printf( wxT( "%s=\"%s%s\" %s" ),
1365 param,
1366 valueMantissa,
1367 convertNotation( valueExponent ),
1368 *aModelParams );
1369 }
1370 else
1371 {
1372 aModelParams->Printf( wxT( "%s=\"%s.%s%s\" %s" ),
1373 param,
1374 valueMantissa,
1375 valueFraction,
1376 convertNotation( valueExponent ),
1377 *aModelParams );
1378 }
1379 }
1380 else
1381 {
1382 aModelParams->Printf( wxT( "%s=\"%s\" %s" ),
1383 param,
1384 value,
1385 *aModelParams );
1386 }
1387 }
1388
1389 if( aDeviceType->IsEmpty() )
1390 *aDeviceType = prefix.Left( 1 );
1391
1392 if( aModelType->IsEmpty() )
1393 *aModelType = wxT( "DC" );
1394
1395 if( aPinMap->IsEmpty() )
1396 aPinMap->Printf( wxT( "%s=+ %s=-" ), pins[0]->GetNumber(), pins[1]->GetNumber() );
1397
1398 return true;
1399 }
1400
1401 return false;
1402}
1403
1404
1405template bool SIM_MODEL::InferSimModel<SCH_SYMBOL>( SCH_SYMBOL& aSymbol,
1406 std::vector<SCH_FIELD>* aFields, bool aResolve,
1408 wxString* aDeviceType, wxString* aModelType,
1409 wxString* aModelParams, wxString* aPinMap );
1410template bool SIM_MODEL::InferSimModel<LIB_SYMBOL>( LIB_SYMBOL& aSymbol,
1411 std::vector<SCH_FIELD>* aFields, bool aResolve,
1413 wxString* aDeviceType, wxString* aModelType,
1414 wxString* aModelParams, wxString* aPinMap );
1415
1416
1417template <typename T>
1418void SIM_MODEL::MigrateSimModel( T& aSymbol, const PROJECT* aProject )
1419{
1420 class FIELD_INFO
1421 {
1422 public:
1423 FIELD_INFO()
1424 {
1425 m_Attributes.m_Visible = false;
1426 m_Attributes.m_Size = VECTOR2I( DEFAULT_SIZE_TEXT * schIUScale.IU_PER_MILS,
1428 };
1429
1430 FIELD_INFO( const wxString& aText, SCH_FIELD* aField ) :
1431 m_Text( aText ),
1432 m_Attributes( aField->GetAttributes() ),
1433 m_Pos( aField->GetPosition() )
1434 {}
1435
1436 bool IsEmpty() const { return m_Text.IsEmpty(); }
1437
1438 SCH_FIELD CreateField( T* aSymbol, const wxString& aFieldName )
1439 {
1440 SCH_FIELD field( aSymbol, -1, aFieldName );
1441
1442 field.SetText( m_Text );
1443 field.SetAttributes( m_Attributes );
1444 field.SetPosition( m_Pos );
1445
1446 return field;
1447 }
1448
1449 public:
1450 wxString m_Text;
1451 TEXT_ATTRIBUTES m_Attributes;
1452 VECTOR2I m_Pos;
1453 };
1454
1455 SCH_FIELD* existing_deviceField = aSymbol.FindField( SIM_DEVICE_FIELD );
1456 SCH_FIELD* existing_deviceSubtypeField = aSymbol.FindField( SIM_DEVICE_SUBTYPE_FIELD );
1457 SCH_FIELD* existing_pinsField = aSymbol.FindField( SIM_PINS_FIELD );
1458 SCH_FIELD* existing_paramsField = aSymbol.FindField( SIM_PARAMS_FIELD );
1459
1460 wxString existing_deviceSubtype;
1461
1462 if( existing_deviceSubtypeField )
1463 existing_deviceSubtype = existing_deviceSubtypeField->GetShownText( false ).Upper();
1464
1465 if( existing_deviceField
1466 || existing_deviceSubtypeField
1467 || existing_pinsField
1468 || existing_paramsField )
1469 {
1470 // Has a current (V7+) model field.
1471
1472 // Up until 7.0RC2 we used '+' and '-' for potentiometer pins, which doesn't match
1473 // SPICE. Here we remap them to 'r0' and 'r1'.
1474 if( existing_deviceSubtype == wxS( "POT" ) )
1475 {
1476 if( existing_pinsField )
1477 {
1478 wxString pinMap = existing_pinsField->GetText();
1479 pinMap.Replace( wxS( "=+" ), wxS( "=r1" ) );
1480 pinMap.Replace( wxS( "=-" ), wxS( "=r0" ) );
1481 existing_pinsField->SetText( pinMap );
1482 }
1483 }
1484
1485 // Up until 8.0RC1 random voltage/current sources were a bit of a mess.
1486 if( existing_deviceSubtype.StartsWith( wxS( "RAND" ) ) )
1487 {
1488 // Re-fetch value without resolving references. If it's an indirect value then we
1489 // can't migrate it.
1490 existing_deviceSubtype = existing_deviceSubtypeField->GetText().Upper();
1491
1492 if( existing_deviceSubtype.Replace( wxS( "NORMAL" ), wxS( "GAUSSIAN" ) ) )
1493 existing_deviceSubtypeField->SetText( existing_deviceSubtype );
1494
1495 if( existing_paramsField )
1496 {
1497 wxString params = existing_paramsField->GetText().Lower();
1498 size_t count = 0;
1499
1500 // We used to support 'min' and 'max' instead of 'range' and 'offset', but we
1501 // wrote all 4 to the netlist which would cause ngspice to barf, so no one has
1502 // working documents with min and max specified. Just delete them if they're
1503 // uninitialized.
1504 count += params.Replace( wxS( "min=0 " ), wxEmptyString );
1505 count += params.Replace( wxS( "max=0 " ), wxEmptyString );
1506
1507 // We used to use 'dt', but the correct ngspice name is 'ts'.
1508 count += params.Replace( wxS( "dt=" ), wxS( "ts=" ) );
1509
1510 if( count )
1511 existing_paramsField->SetText( params );
1512 }
1513 }
1514
1515 // Up until 8.0.1 we treated a mutual inductance statement as a type of inductor --
1516 // which is confusing because it doesn't represent a device at all.
1517 if( existing_deviceSubtype == wxS( "MUTUAL" ) )
1518 {
1519 if( existing_deviceSubtypeField ) // Can't be null, but Coverity doesn't know that
1520 aSymbol.RemoveField( existing_deviceSubtypeField );
1521
1522 if( existing_deviceField )
1523 {
1524 existing_deviceField->SetText( wxS( "K" ) );
1525 }
1526 else
1527 {
1528 FIELD_INFO deviceFieldInfo;
1529 deviceFieldInfo.m_Text = wxS( "K" );
1530
1531 SCH_FIELD deviceField = deviceFieldInfo.CreateField( &aSymbol, SIM_DEVICE_FIELD );
1532 aSymbol.AddField( deviceField );
1533 }
1534 }
1535
1536 return;
1537 }
1538
1539 auto getSIValue =
1540 []( SCH_FIELD* aField )
1541 {
1542 if( !aField ) // no, not really, but it keeps Coverity happy
1543 return wxString( wxEmptyString );
1544
1545 wxRegEx regex( wxT( "([^a-z])(M)(e|E)(g|G)($|[^a-z])" ) );
1546 wxString value = aField->GetText();
1547
1548 // Keep prefix, M, and suffix, but drop e|E and g|G
1549 regex.ReplaceAll( &value, wxT( "\\1\\2\\5" ) );
1550
1551 return value;
1552 };
1553
1554 auto generateDefaultPinMapFromSymbol =
1555 []( const std::vector<SCH_PIN*>& sourcePins )
1556 {
1557 wxString pinMap;
1558
1559 // If we're creating the pinMap from the symbol it means we don't know what the
1560 // SIM_MODEL's pin names are, so just use indexes.
1561
1562 for( unsigned ii = 0; ii < sourcePins.size(); ++ii )
1563 {
1564 if( ii > 0 )
1565 pinMap.Append( wxS( " " ) );
1566
1567 pinMap.Append( wxString::Format( wxT( "%s=%u" ),
1568 sourcePins[ii]->GetNumber(),
1569 ii + 1 ) );
1570 }
1571
1572 return pinMap;
1573 };
1574
1575 wxString prefix = aSymbol.GetPrefix();
1576 SCH_FIELD* valueField = aSymbol.FindField( wxT( "Value" ) );
1577 std::vector<SCH_PIN*> sourcePins = aSymbol.GetPins();
1578 bool sourcePinsSorted = false;
1579
1580 auto lazySortSourcePins =
1581 [&sourcePins, &sourcePinsSorted]()
1582 {
1583 if( !sourcePinsSorted )
1584 {
1585 std::sort( sourcePins.begin(), sourcePins.end(),
1586 []( const SCH_PIN* lhs, const SCH_PIN* rhs )
1587 {
1588 return StrNumCmp( lhs->GetNumber(), rhs->GetNumber(), true ) < 0;
1589 } );
1590 }
1591
1592 sourcePinsSorted = true;
1593 };
1594
1595 FIELD_INFO deviceInfo;
1596 FIELD_INFO modelInfo;
1597 FIELD_INFO deviceSubtypeInfo;
1598 FIELD_INFO libInfo;
1599 FIELD_INFO spiceParamsInfo;
1600 FIELD_INFO pinMapInfo;
1601 bool modelFromValueField = false;
1602
1603 if( aSymbol.FindField( SIM_LEGACY_PRIMITIVE_FIELD )
1604 || aSymbol.FindField( SIM_LEGACY_PINS_FIELD )
1605 || aSymbol.FindField( SIM_LEGACY_MODEL_FIELD )
1606 || aSymbol.FindField( SIM_LEGACY_ENABLE_FIELD )
1607 || aSymbol.FindField( SIM_LEGACY_LIBRARY_FIELD ) )
1608 {
1609 if( SCH_FIELD* primitiveField = aSymbol.FindField( SIM_LEGACY_PRIMITIVE_FIELD ) )
1610 {
1611 deviceInfo = FIELD_INFO( primitiveField->GetText(), primitiveField );
1612 aSymbol.RemoveField( primitiveField );
1613 }
1614
1615 if( SCH_FIELD* nodeSequenceField = aSymbol.FindField( SIM_LEGACY_PINS_FIELD ) )
1616 {
1617 const wxString delimiters( "{:,; }" );
1618 const wxString& nodeSequence = nodeSequenceField->GetText();
1619 wxString pinMap;
1620
1621 if( nodeSequence != "" )
1622 {
1623 wxStringTokenizer tkz( nodeSequence, delimiters );
1624
1625 for( long modelPinNumber = 1; tkz.HasMoreTokens(); ++modelPinNumber )
1626 {
1627 long symbolPinNumber = 1;
1628 tkz.GetNextToken().ToLong( &symbolPinNumber );
1629
1630 if( modelPinNumber != 1 )
1631 pinMap.Append( " " );
1632
1633 pinMap.Append( wxString::Format( "%ld=%ld", symbolPinNumber, modelPinNumber ) );
1634 }
1635 }
1636
1637 pinMapInfo = FIELD_INFO( pinMap, nodeSequenceField );
1638 aSymbol.RemoveField( nodeSequenceField );
1639 }
1640
1641 if( SCH_FIELD* modelField = aSymbol.FindField( SIM_LEGACY_MODEL_FIELD ) )
1642 {
1643 modelInfo = FIELD_INFO( getSIValue( modelField ), modelField );
1644 aSymbol.RemoveField( modelField );
1645 }
1646 else if( valueField )
1647 {
1648 modelInfo = FIELD_INFO( getSIValue( valueField ), valueField );
1649 modelFromValueField = true;
1650 }
1651
1652 if( SCH_FIELD* libFileField = aSymbol.FindField( SIM_LEGACY_LIBRARY_FIELD ) )
1653 {
1654 libInfo = FIELD_INFO( libFileField->GetText(), libFileField );
1655 aSymbol.RemoveField( libFileField );
1656 }
1657 }
1658 else
1659 {
1660 // Auto convert some legacy fields used in the middle of 7.0 development...
1661
1662 if( SCH_FIELD* legacyType = aSymbol.FindField( wxT( "Sim_Type" ) ) )
1663 {
1664 legacyType->SetName( SIM_DEVICE_SUBTYPE_FIELD );
1665 }
1666
1667 if( SCH_FIELD* legacyDevice = aSymbol.FindField( wxT( "Sim_Device" ) ) )
1668 {
1669 legacyDevice->SetName( SIM_DEVICE_FIELD );
1670 }
1671
1672 if( SCH_FIELD* legacyPins = aSymbol.FindField( wxT( "Sim_Pins" ) ) )
1673 {
1674 bool isPassive = prefix.StartsWith( wxT( "R" ) )
1675 || prefix.StartsWith( wxT( "L" ) )
1676 || prefix.StartsWith( wxT( "C" ) );
1677
1678 // Migrate pins from array of indexes to name-value-pairs
1679 wxString pinMap;
1680 wxArrayString pinIndexes;
1681
1682 wxStringSplit( legacyPins->GetText(), pinIndexes, ' ' );
1683
1684 lazySortSourcePins();
1685
1686 if( isPassive && pinIndexes.size() == 2 && sourcePins.size() == 2 )
1687 {
1688 if( pinIndexes[0] == wxT( "2" ) )
1689 {
1690 pinMap.Printf( wxT( "%s=- %s=+" ),
1691 sourcePins[0]->GetNumber(),
1692 sourcePins[1]->GetNumber() );
1693 }
1694 else
1695 {
1696 pinMap.Printf( wxT( "%s=+ %s=-" ),
1697 sourcePins[0]->GetNumber(),
1698 sourcePins[1]->GetNumber() );
1699 }
1700 }
1701 else
1702 {
1703 for( unsigned ii = 0; ii < pinIndexes.size() && ii < sourcePins.size(); ++ii )
1704 {
1705 if( ii > 0 )
1706 pinMap.Append( wxS( " " ) );
1707
1708 pinMap.Append( wxString::Format( wxT( "%s=%s" ),
1709 sourcePins[ii]->GetNumber(),
1710 pinIndexes[ ii ] ) );
1711 }
1712 }
1713
1714 legacyPins->SetName( SIM_PINS_FIELD );
1715 legacyPins->SetText( pinMap );
1716 }
1717
1718 if( SCH_FIELD* legacyParams = aSymbol.FindField( wxT( "Sim_Params" ) ) )
1719 {
1720 legacyParams->SetName( SIM_PARAMS_FIELD );
1721 }
1722
1723 return;
1724 }
1725
1726 wxString device = deviceInfo.m_Text.Trim( true ).Trim( false );
1727 wxString lib = libInfo.m_Text.Trim( true ).Trim( false );
1728 wxString model = modelInfo.m_Text.Trim( true ).Trim( false );
1729 wxString modelLineParams;
1730
1731 bool libraryModel = false;
1732 bool inferredModel = false;
1733 bool internalModel = false;
1734
1735 if( !lib.IsEmpty() )
1736 {
1737 WX_STRING_REPORTER reporter;
1738 SIM_LIB_MGR libMgr( aProject );
1739 std::vector<SCH_FIELD> emptyFields;
1740
1741 // Pull out any following parameters from model name
1742 model = model.BeforeFirst( ' ', &modelLineParams );
1743 modelInfo.m_Text = model;
1744
1745 lazySortSourcePins();
1746
1747 SIM_LIBRARY::MODEL simModel = libMgr.CreateModel( lib, model.ToStdString(),
1748 emptyFields, sourcePins, reporter );
1749
1750 if( reporter.HasMessage() )
1751 libraryModel = false; // Fall back to raw spice model
1752 else
1753 libraryModel = true;
1754
1755 if( pinMapInfo.IsEmpty() )
1756 {
1757 // Try to generate a default pin map from the SIM_MODEL's pins; if that fails,
1758 // generate one from the symbol's pins
1759 pinMapInfo.m_Text = wxString( simModel.model.Serializer().GeneratePins() );
1760
1761 if( pinMapInfo.IsEmpty() )
1762 pinMapInfo.m_Text = generateDefaultPinMapFromSymbol( sourcePins );
1763 }
1764 }
1765 else if( ( device == wxS( "R" )
1766 || device == wxS( "L" )
1767 || device == wxS( "C" )
1768 || device == wxS( "V" )
1769 || device == wxS( "I" ) )
1770 && prefix.StartsWith( device )
1771 && modelFromValueField )
1772 {
1773 inferredModel = true;
1774 }
1775 else if( device == wxS( "V" ) || device == wxS( "I" ) )
1776 {
1777 // See if we have a SPICE time-dependent function such as "sin(0 1 60)" or "sin 0 1 60"
1778 // that can be handled by a built-in SIM_MODEL_SOURCE.
1779
1780 wxStringTokenizer tokenizer( model, wxT( "() " ), wxTOKEN_STRTOK );
1781
1782 if( tokenizer.HasMoreTokens() )
1783 {
1784 deviceSubtypeInfo.m_Text = tokenizer.GetNextToken();
1785 deviceSubtypeInfo.m_Text.MakeUpper();
1786
1787 for( SIM_MODEL::TYPE type : SIM_MODEL::TYPE_ITERATOR() )
1788 {
1789 if( device == SIM_MODEL::SpiceInfo( type ).itemType
1790 && deviceSubtypeInfo.m_Text == SIM_MODEL::SpiceInfo( type ).functionName )
1791 {
1792 try
1793 {
1794 std::unique_ptr<SIM_MODEL> simModel = SIM_MODEL::Create( type );
1795
1796 if( deviceSubtypeInfo.m_Text == wxT( "DC" ) && tokenizer.CountTokens() == 1 )
1797 {
1798 wxCHECK( valueField, /* void */ );
1799 valueField->SetText( tokenizer.GetNextToken() );
1800 modelFromValueField = false;
1801 }
1802 else
1803 {
1804 for( int ii = 0; tokenizer.HasMoreTokens(); ++ii )
1805 {
1806 simModel->SetParamValue( ii, tokenizer.GetNextToken().ToStdString(),
1808 }
1809
1810 deviceSubtypeInfo.m_Text = SIM_MODEL::TypeInfo( type ).fieldValue;
1811
1812 spiceParamsInfo = modelInfo;
1813 spiceParamsInfo.m_Text = wxString( simModel->Serializer().GenerateParams() );
1814 }
1815
1816 internalModel = true;
1817
1818 if( pinMapInfo.IsEmpty() )
1819 {
1820 lazySortSourcePins();
1821
1822 // Generate a default pin map from the SIM_MODEL's pins
1823 simModel->createPins( sourcePins );
1824 pinMapInfo.m_Text = wxString( simModel->Serializer().GeneratePins() );
1825 }
1826 }
1827 catch( ... )
1828 {
1829 // Fall back to raw spice model
1830 }
1831
1832 break;
1833 }
1834 }
1835 }
1836 }
1837
1838 if( libraryModel )
1839 {
1840 SCH_FIELD libField = libInfo.CreateField( &aSymbol, SIM_LIBRARY_FIELD );
1841 aSymbol.AddField( libField );
1842
1843 SCH_FIELD nameField = modelInfo.CreateField( &aSymbol, SIM_NAME_FIELD );
1844 aSymbol.AddField( nameField );
1845
1846 if( !modelLineParams.IsEmpty() )
1847 {
1848 spiceParamsInfo = modelInfo;
1849 spiceParamsInfo.m_Pos.x += nameField.GetBoundingBox().GetWidth();
1850 spiceParamsInfo.m_Text = modelLineParams;
1851
1852 BOX2I nameBBox = nameField.GetBoundingBox();
1853 int nameWidth = nameBBox.GetWidth();
1854
1855 // Add space between model name and additional parameters
1856 nameWidth += KiROUND( nameBBox.GetHeight() * 1.25 );
1857
1858 if( nameField.GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
1859 spiceParamsInfo.m_Pos.x -= nameWidth;
1860 else
1861 spiceParamsInfo.m_Pos.x += nameWidth;
1862
1863 SCH_FIELD paramsField = spiceParamsInfo.CreateField( &aSymbol, SIM_PARAMS_FIELD );
1864 aSymbol.AddField( paramsField );
1865 }
1866
1867 if( modelFromValueField )
1868 valueField->SetText( wxT( "${SIM.NAME}" ) );
1869 }
1870 else if( inferredModel )
1871 {
1872 // DeviceType is left in the reference designator and Model is left in the value field,
1873 // so there's nothing to do here....
1874 }
1875 else if( internalModel )
1876 {
1877 SCH_FIELD deviceField = deviceInfo.CreateField( &aSymbol, SIM_DEVICE_FIELD );
1878 aSymbol.AddField( deviceField );
1879
1880 if( !deviceSubtypeInfo.m_Text.IsEmpty() )
1881 {
1882 SCH_FIELD subtypeField = deviceSubtypeInfo.CreateField( &aSymbol, SIM_DEVICE_SUBTYPE_FIELD );
1883 aSymbol.AddField( subtypeField );
1884 }
1885
1886 if( !spiceParamsInfo.IsEmpty() )
1887 {
1888 SCH_FIELD paramsField = spiceParamsInfo.CreateField( &aSymbol, SIM_PARAMS_FIELD );
1889 aSymbol.AddField( paramsField );
1890 }
1891
1892 if( modelFromValueField )
1893 valueField->SetText( wxT( "${SIM.PARAMS}" ) );
1894 }
1895 else // Insert a raw spice model as a substitute.
1896 {
1897 if( device.IsEmpty() && lib.IsEmpty() )
1898 {
1899 spiceParamsInfo = modelInfo;
1900 }
1901 else
1902 {
1903 spiceParamsInfo.m_Text.Printf( wxT( "type=\"%s\" model=\"%s\" lib=\"%s\"" ), device,
1904 model, lib );
1905 }
1906
1907 deviceInfo.m_Text = SIM_MODEL::DeviceInfo( SIM_MODEL::DEVICE_T::SPICE ).fieldValue;
1908
1909 SCH_FIELD deviceField = deviceInfo.CreateField( &aSymbol, SIM_DEVICE_FIELD );
1910 aSymbol.AddField( deviceField );
1911
1912 SCH_FIELD paramsField = spiceParamsInfo.CreateField( &aSymbol, SIM_PARAMS_FIELD );
1913 aSymbol.AddField( paramsField );
1914
1915 if( modelFromValueField )
1916 {
1917 // Get the current Value field, after previous changes.
1918 valueField = aSymbol.FindField( wxT( "Value" ) );
1919
1920 if( valueField )
1921 valueField->SetText( wxT( "${SIM.PARAMS}" ) );
1922 }
1923
1924 // We know nothing about the SPICE model here, so we've got no choice but to generate
1925 // the default pin map from the symbol's pins.
1926
1927 if( pinMapInfo.IsEmpty() )
1928 {
1929 lazySortSourcePins();
1930 pinMapInfo.m_Text = generateDefaultPinMapFromSymbol( sourcePins );
1931 }
1932 }
1933
1934 if( !pinMapInfo.IsEmpty() )
1935 {
1936 SCH_FIELD pinsField = pinMapInfo.CreateField( &aSymbol, SIM_PINS_FIELD );
1937 aSymbol.AddField( pinsField );
1938 }
1939}
1940
1941
1942template void SIM_MODEL::MigrateSimModel<SCH_SYMBOL>( SCH_SYMBOL& aSymbol,
1943 const PROJECT* aProject );
1944template void SIM_MODEL::MigrateSimModel<LIB_SYMBOL>( LIB_SYMBOL& aSymbol,
1945 const PROJECT* aProject );
constexpr EDA_IU_SCALE schIUScale
Definition: base_units.h:110
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition: box2.h:990
constexpr size_type GetWidth() const
Definition: box2.h:214
constexpr size_type GetHeight() const
Definition: box2.h:215
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:98
void SetAttributes(const EDA_TEXT &aSrc, bool aSetPosition=true)
Set the text attributes from another instance.
Definition: eda_text.cpp:424
GR_TEXT_H_ALIGN_T GetHorizJustify() const
Definition: eda_text.h:187
const TEXT_ATTRIBUTES & GetAttributes() const
Definition: eda_text.h:218
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Definition: ki_exception.h:77
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:30
virtual const wxString Problem() const
what was the problem?
Definition: exceptions.cpp:46
Define a library symbol object.
Definition: lib_symbol.h:84
A singleton reporter that reports to nowhere.
Definition: reporter.h:203
Container for project specific data.
Definition: project.h:64
A pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:72
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)=0
Report a string with a given severity.
Instances are attached to a symbol or sheet and provide a place for the symbol's value,...
Definition: sch_field.h:51
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition: sch_field.cpp:616
VECTOR2I GetPosition() const override
Definition: sch_field.cpp:1485
void SetPosition(const VECTOR2I &aPosition) override
Definition: sch_field.cpp:1465
wxString GetShownText(const SCH_SHEET_PATH *aPath, bool aAllowExtraText, int aDepth=0) const
Definition: sch_field.cpp:211
void SetText(const wxString &aText) override
Definition: sch_field.cpp:1212
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:167
Schematic symbol object.
Definition: sch_symbol.h:77
static constexpr auto DIFF_FIELD
SIM_MODEL & CreateModel(SIM_MODEL::TYPE aType, const std::vector< SCH_PIN * > &aPins, REPORTER &aReporter)
Serializes/deserializes a SIM_MODEL for storage in LIB_FIELDs/SCH_FIELDs.
std::string GeneratePins() const
int FindModelPinIndex(const std::string &aSymbolPinNumber)
Definition: sim_model.cpp:723
static void MigrateSimModel(T &aSymbol, const PROJECT *aProject)
Definition: sim_model.cpp:1418
const PARAM & GetBaseParam(unsigned aParamIndex) const
Definition: sim_model.cpp:836
void AddParam(const PARAM::INFO &aInfo)
Definition: sim_model.cpp:735
bool IsStoredInValue() const
Definition: sim_model.h:509
virtual std::vector< std::string > GetPinNames() const
Definition: sim_model.h:469
virtual bool requiresSpiceModelLine(const SPICE_ITEM &aItem) const
Definition: sim_model.cpp:1044
void ClearPins()
Definition: sim_model.cpp:717
static TYPE ReadTypeFromFields(const std::vector< SCH_FIELD > &aFields, REPORTER &aReporter)
Definition: sim_model.cpp:390
std::vector< SIM_MODEL_PIN > m_modelPins
Definition: sim_model.h:539
static INFO TypeInfo(TYPE aType)
Definition: sim_model.cpp:105
int GetPinCount() const
Definition: sim_model.h:471
void AddPin(const SIM_MODEL_PIN &aPin)
Definition: sim_model.cpp:711
static SPICE_INFO SpiceInfo(TYPE aType)
Definition: sim_model.cpp:249
static void SetFieldValue(std::vector< SCH_FIELD > &aFields, const wxString &aFieldName, const std::string &aValue, bool aIsVisible=true)
Definition: sim_model.cpp:674
const SIM_MODEL_SERIALIZER & Serializer() const
Definition: sim_model.h:436
void ReadDataFields(const std::vector< SCH_FIELD > *aFields, const std::vector< SCH_PIN * > &aPins)
Definition: sim_model.cpp:427
virtual const PARAM & GetParam(unsigned aParamIndex) const
Definition: sim_model.cpp:795
static bool InferSimModel(T &aSymbol, std::vector< SCH_FIELD > *aFields, bool aResolve, SIM_VALUE_GRAMMAR::NOTATION aNotation, wxString *aDeviceType, wxString *aModelType, wxString *aModelParams, wxString *aPinMap)
Definition: sim_model.cpp:1088
void createPins(const std::vector< SCH_PIN * > &aSymbolPins)
Definition: sim_model.cpp:1009
void WriteFields(std::vector< SCH_FIELD > &aFields) const
Definition: sim_model.cpp:445
virtual void SetBaseModel(const SIM_MODEL &aBaseModel)
Definition: sim_model.cpp:745
SIM_MODEL()=delete
static std::string GetFieldValue(const std::vector< SCH_FIELD > *aFields, const wxString &aFieldName, bool aResolve=true)
Definition: sim_model.cpp:655
int GetParamCount() const
Definition: sim_model.h:481
void AssignSymbolPinNumberToModelPin(int aPinIndex, const wxString &aSymbolPinNumber)
Definition: sim_model.cpp:764
static DEVICE_INFO DeviceInfo(DEVICE_T aDeviceType)
Definition: sim_model.cpp:60
const PARAM * FindParam(const std::string &aParamName) const
Definition: sim_model.cpp:822
virtual void doSetParamValue(int aParamIndex, const std::string &aValue)
Definition: sim_model.cpp:845
std::vector< PARAM > m_params
Definition: sim_model.h:538
const PARAM & GetParamOverride(unsigned aParamIndex) const
Definition: sim_model.cpp:830
virtual ~SIM_MODEL()
static std::unique_ptr< SIM_MODEL > Create(TYPE aType, const std::vector< SCH_PIN * > &aPins, REPORTER &aReporter)
Definition: sim_model.cpp:497
void SetParamValue(int aParamIndex, const std::string &aValue, SIM_VALUE::NOTATION aNotation=SIM_VALUE::NOTATION::SI)
Definition: sim_model.cpp:851
virtual void SwitchSingleEndedDiff(bool aDiff)
Definition: sim_model.h:511
const SIM_MODEL_PIN & GetPin(unsigned aIndex) const
Definition: sim_model.h:472
std::unique_ptr< SIM_MODEL_SERIALIZER > m_serializer
Definition: sim_model.h:541
virtual int doFindParam(const std::string &aParamName) const
Definition: sim_model.cpp:810
TYPE GetType() const
Definition: sim_model.h:464
std::vector< std::reference_wrapper< const SIM_MODEL_PIN > > GetPins() const
Definition: sim_model.cpp:754
const TYPE m_type
Definition: sim_model.h:546
const SIM_MODEL * m_baseModel
Definition: sim_model.h:540
static std::string Normalize(double aValue)
Definition: sim_value.cpp:406
static std::string ConvertNotation(const std::string &aString, NOTATION aFromNotation, NOTATION aToNotation)
Definition: sim_value.cpp:370
static double ToDouble(const std::string &aString, double aDefault=NAN)
Definition: sim_value.cpp:442
A wrapper for reporting to a wxString object.
Definition: reporter.h:171
bool HasMessage() const override
Returns true if the reporter client is non-empty.
Definition: reporter.cpp:97
#define _(s)
#define DEFAULT_SIZE_TEXT
This is the "default-of-the-default" hardcoded text size; individual application define their own def...
Definition: eda_text.h:70
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:39
STL namespace.
@ RPT_SEVERITY_ERROR
SIM_MODEL::TYPE TYPE
Definition: sim_model.cpp:57
#define SIM_PINS_FIELD
Definition: sim_model.h:54
#define SIM_DEVICE_FIELD
Definition: sim_model.h:52
#define SIM_NAME_FIELD
Definition: sim_model.h:57
#define SIM_LIBRARY_FIELD
Definition: sim_model.h:56
#define SIM_LEGACY_ENABLE_FIELD
Definition: sim_model.h:64
#define SIM_LEGACY_ENABLE_FIELD_V7
Definition: sim_model.h:60
#define SIM_LEGACY_MODEL_FIELD
Definition: sim_model.h:62
#define SIM_LEGACY_PINS_FIELD
Definition: sim_model.h:63
#define SIM_LEGACY_LIBRARY_FIELD
Definition: sim_model.h:65
#define SIM_PARAMS_FIELD
Definition: sim_model.h:55
#define SIM_VALUE_FIELD
Definition: sim_model.h:50
#define SIM_LEGACY_PRIMITIVE_FIELD
Definition: sim_model.h:61
#define SIM_DEVICE_SUBTYPE_FIELD
Definition: sim_model.h:53
bool convertSeparators(wxString *value)
void wxStringSplit(const wxString &aText, wxArrayString &aStrings, wxChar aSplitter)
Split aString to a string list separated at aSplitter.
const double IU_PER_MILS
Definition: base_units.h:77
SIM_MODEL & model
Definition: sim_library.h:41
std::vector< std::string > enumValues
Definition: sim_model.h:388
std::string defaultValue
Definition: sim_model.h:382
bool Matches(const std::string &aName) const
Definition: sim_model.h:395
std::string value
Definition: sim_model.h:400
const INFO & info
Definition: sim_model.h:401
std::string functionName
Definition: sim_model.h:306
static constexpr auto NOT_CONNECTED
Definition: sim_model.h:73
wxString symbolPinNumber
Definition: sim_model.h:71
std::string baseModelName
@ MANDATORY_FIELDS
The first 5 are mandatory, and must be instantiated in SCH_COMPONENT and LIB_PART constructors.
@ REFERENCE_FIELD
Field Reference of part, i.e. "IC21".
@ GR_TEXT_H_ALIGN_RIGHT
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:695