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