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