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