KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sim_model_ngspice.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 The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 3
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
22
23#include <boost/algorithm/string.hpp>
24#include <fmt/core.h>
25#include <ki_exception.h>
26
27
28std::vector<std::string> SPICE_GENERATOR_NGSPICE::CurrentNames( const SPICE_ITEM& aItem ) const
29{
30 switch( m_model.GetTypeInfo().deviceType )
31 {
32 case SIM_MODEL::DEVICE_T::NPN:
33 case SIM_MODEL::DEVICE_T::PNP:
34 return { fmt::format( "I({}:c)", ItemName( aItem ) ),
35 fmt::format( "I({}:b)", ItemName( aItem ) ),
36 fmt::format( "I({}:e)", ItemName( aItem ) ) };
37
38 case SIM_MODEL::DEVICE_T::NJFET:
39 case SIM_MODEL::DEVICE_T::PJFET:
40 case SIM_MODEL::DEVICE_T::NMES:
41 case SIM_MODEL::DEVICE_T::PMES:
42 case SIM_MODEL::DEVICE_T::NMOS:
43 case SIM_MODEL::DEVICE_T::PMOS:
44 return { fmt::format( "I({}:d)", ItemName( aItem ) ),
45 fmt::format( "I({}:g)", ItemName( aItem ) ),
46 fmt::format( "I({}:s)", ItemName( aItem ) ) };
47
48 case SIM_MODEL::DEVICE_T::R:
49 case SIM_MODEL::DEVICE_T::C:
50 case SIM_MODEL::DEVICE_T::L:
51 case SIM_MODEL::DEVICE_T::D:
52 return SPICE_GENERATOR::CurrentNames( aItem );
53
54 default:
55 return {};
56 }
57}
58
59
61 SIM_MODEL_SPICE( aType, std::make_unique<SPICE_GENERATOR_NGSPICE>( *this ) )
62{
63 const MODEL_INFO& modelInfo = ModelInfo( getModelType() );
64
65 for( const SIM_MODEL::PARAM::INFO& paramInfo : modelInfo.instanceParams )
66 {
67 // For now, only the geometry and flags parameters.
68 if( paramInfo.category == SIM_MODEL::PARAM::CATEGORY::PRINCIPAL
69 || paramInfo.category == SIM_MODEL::PARAM::CATEGORY::GEOMETRY
70 || paramInfo.category == SIM_MODEL::PARAM::CATEGORY::FLAGS )
71 {
72 AddParam( paramInfo );
73 }
74 }
75
76 for( const SIM_MODEL::PARAM::INFO& paramInfo : modelInfo.modelParams )
77 AddParam( paramInfo );
78}
79
80
81int SIM_MODEL_NGSPICE::doFindParam( const std::string& aParamName ) const
82{
83 for( int ii = 0; ii < (int) GetParamCount(); ++ii )
84 {
85 const PARAM& param = GetParam( ii );
86
87 if( param.Matches( aParamName ) )
88 return ii;
89 }
90
91 // Look for escaped param names as a second pass (as they're less common)
92 for( int ii = 0; ii < (int) GetParamCount(); ++ii )
93 {
94 const PARAM& param = GetParam( ii );
95
96 if( !param.info.name.ends_with( '_' ) )
97 continue;
98
99 if( param.Matches( aParamName + "_" ) )
100 return ii;
101 }
102
103 return -1;
104}
105
106
107void SIM_MODEL_NGSPICE::SetParamFromSpiceCode( const std::string& aParamName,
108 const std::string& aValue,
110{
111 // "level" and "version" are not really parameters - they're part of the type - so silently
112 // ignore them.
113 if( boost::iequals( aParamName, "level" ) || boost::iequals( aParamName, "version" ) )
114 return;
115
116 // First we try to use the name as is. Note that you can't set instance parameters from this
117 // function, it's for ".model" cards, not for instantiations.
118
119 for( int ii = 0; ii < (int) GetParamCount(); ++ii )
120 {
121 const PARAM& param = GetParam( ii );
122
123 if( param.info.isSpiceInstanceParam || param.info.category == PARAM::CATEGORY::SUPERFLUOUS )
124 continue;
125
126 if( param.Matches( aParamName ) )
127 {
128 SetParamValue( ii, aValue, aNotation );
129 return;
130 }
131 }
132
133 // Look for escaped param names as a second pass (as they're less common)
134 for( int ii = 0; ii < (int) GetParamCount(); ++ii )
135 {
136 const PARAM& param = GetParam( ii );
137
138 if( param.info.isSpiceInstanceParam || param.info.category == PARAM::CATEGORY::SUPERFLUOUS )
139 continue;
140
141 if( !param.info.name.ends_with( '_' ) )
142 continue;
143
144 if( param.Matches( aParamName + "_" ) )
145 {
146 SetParamValue( ii, aValue, aNotation );
147 return;
148 }
149 }
150
151 // One Spice param can have multiple names, we need to take this into account.
152
153 // Now we search the base model parameters without excluding superfluous parameters (which
154 // may be aliases to non-superfluous parameters).
155
156 for( const PARAM::INFO& ngspiceParamInfo : ModelInfo( getModelType() ).modelParams )
157 {
158 if( ngspiceParamInfo.Matches( aParamName ) )
159 {
160 // Find an actual parameter with the same id. Even if the ngspiceParam was
161 // superfluous, its alias target might not be.
162 for( int ii = 0; ii < (int) GetParamCount(); ++ii )
163 {
164 const PARAM::INFO& paramInfo = GetParam( ii ).info;
165
166 if( paramInfo.category == PARAM::CATEGORY::SUPERFLUOUS )
167 continue;
168
169 if( paramInfo.id == ngspiceParamInfo.id )
170 {
171 SetParamValue( ii, aValue, aNotation );
172 return;
173 }
174 }
175
176 break;
177 }
178 }
179
180 if( !canSilentlyIgnoreParam( aParamName ) )
181 THROW_IO_ERROR( wxString::Format( "Unknown simulation model parameter '%s'", aParamName ) );
182}
183
184
185bool SIM_MODEL_NGSPICE::canSilentlyIgnoreParam( const std::string& aParamName )
186{
187 // Ignore the purely informative LTspice-specific parameters "mfg" and "type".
188 if( boost::iequals( aParamName, "mfg" ) || boost::iequals( aParamName, "type" ) )
189 return true;
190
191 if( GetDeviceType() == DEVICE_T::D )
192 {
193 if( boost::iequals( aParamName, "perim" )
194 || boost::iequals( aParamName, "isw" )
195 || boost::iequals( aParamName, "ns" )
196 || boost::iequals( aParamName, "rsw" )
197 || boost::iequals( aParamName, "cjsw" )
198 || boost::iequals( aParamName, "vjsw" )
199 || boost::iequals( aParamName, "mjsw" )
200 || boost::iequals( aParamName, "fcs" ) )
201 {
202 return true;
203 }
204 }
205
206 if( GetDeviceType() == DEVICE_T::NPN || GetDeviceType() == DEVICE_T::PNP )
207 {
208 // Ignore the purely informative LTspice-specific parameters "icrating" and "vceo".
209 if( boost::iequals( aParamName, "icrating" ) || boost::iequals( aParamName, "vceo" ) )
210 return true;
211 }
212
213 if( GetType() == TYPE::NPN_GUMMELPOON || GetType() == TYPE::PNP_GUMMELPOON )
214 {
215 // Ignore unused parameters.
216 if( boost::iequals( aParamName, "bvcbo" )
217 || boost::iequals( aParamName, "nbvcbo" )
218 || boost::iequals( aParamName, "tbvcbo1" )
219 || boost::iequals( aParamName, "tbvcbo2" )
220 || boost::iequals( aParamName, "bvbe" )
221 || boost::iequals( aParamName, "ibvbe" )
222 || boost::iequals( aParamName, "nbvbe" ) )
223 {
224 return true;
225 }
226 }
227
228 if( GetType() == TYPE::NMOS_VDMOS || GetType() == TYPE::PMOS_VDMOS )
229 {
230 // Ignore the purely informative LTspice-specific parameters "Vds", "Ron" and "Qg".
231 if( boost::iequals( aParamName, "vds" )
232 || boost::iequals( aParamName, "ron" )
233 || boost::iequals( aParamName, "qg" ) )
234 {
235 return true;
236 }
237 }
238
239 return false;
240}
241
242
243std::vector<std::string> SIM_MODEL_NGSPICE::GetPinNames() const
244{
245 return ModelInfo( getModelType() ).pinNames;
246}
247
248
249SIM_MODEL_NGSPICE::MODEL_TYPE SIM_MODEL_NGSPICE::getModelType() const
250{
251 switch( GetType() )
252 {
253 case TYPE::NONE: return MODEL_TYPE::NONE;
254 case TYPE::D: return MODEL_TYPE::DIODE;
255
256 case TYPE::NPN_VBIC:
257 case TYPE::PNP_VBIC: return MODEL_TYPE::VBIC;
258 case TYPE::NPN_GUMMELPOON:
259 case TYPE::PNP_GUMMELPOON: return MODEL_TYPE::BJT;
260 case TYPE::NPN_HICUM2:
261 case TYPE::PNP_HICUM2: return MODEL_TYPE::HICUM2;
262
263 case TYPE::NJFET_SHICHMANHODGES:
264 case TYPE::PJFET_SHICHMANHODGES: return MODEL_TYPE::JFET;
265 case TYPE::NJFET_PARKERSKELLERN:
266 case TYPE::PJFET_PARKERSKELLERN: return MODEL_TYPE::JFET2;
267
268 case TYPE::NMES_STATZ:
269 case TYPE::PMES_STATZ: return MODEL_TYPE::MES;
270 case TYPE::NMES_YTTERDAL:
271 case TYPE::PMES_YTTERDAL: return MODEL_TYPE::MESA;
272 case TYPE::NMES_HFET1:
273 case TYPE::PMES_HFET1: return MODEL_TYPE::HFET1;
274 case TYPE::NMES_HFET2:
275 case TYPE::PMES_HFET2: return MODEL_TYPE::HFET2;
276
277 case TYPE::NMOS_VDMOS:
278 case TYPE::PMOS_VDMOS: return MODEL_TYPE::VDMOS;
279 case TYPE::NMOS_MOS1:
280 case TYPE::PMOS_MOS1: return MODEL_TYPE::MOS1;
281 case TYPE::NMOS_MOS2:
282 case TYPE::PMOS_MOS2: return MODEL_TYPE::MOS2;
283 case TYPE::NMOS_MOS3:
284 case TYPE::PMOS_MOS3: return MODEL_TYPE::MOS3;
285 case TYPE::NMOS_BSIM1:
286 case TYPE::PMOS_BSIM1: return MODEL_TYPE::BSIM1;
287 case TYPE::NMOS_BSIM2:
288 case TYPE::PMOS_BSIM2: return MODEL_TYPE::BSIM2;
289 case TYPE::NMOS_MOS6:
290 case TYPE::PMOS_MOS6: return MODEL_TYPE::MOS6;
291 case TYPE::NMOS_BSIM3:
292 case TYPE::PMOS_BSIM3: return MODEL_TYPE::BSIM3;
293 case TYPE::NMOS_MOS9:
294 case TYPE::PMOS_MOS9: return MODEL_TYPE::MOS9;
295 case TYPE::NMOS_B4SOI:
296 case TYPE::PMOS_B4SOI: return MODEL_TYPE::B4SOI;
297 case TYPE::NMOS_BSIM4:
298 case TYPE::PMOS_BSIM4: return MODEL_TYPE::BSIM4;
299 case TYPE::NMOS_B3SOIFD:
300 case TYPE::PMOS_B3SOIFD: return MODEL_TYPE::B3SOIFD;
301 case TYPE::NMOS_B3SOIDD:
302 case TYPE::PMOS_B3SOIDD: return MODEL_TYPE::B3SOIDD;
303 case TYPE::NMOS_B3SOIPD:
304 case TYPE::PMOS_B3SOIPD: return MODEL_TYPE::B3SOIPD;
305 case TYPE::NMOS_HISIM2:
306 case TYPE::PMOS_HISIM2: return MODEL_TYPE::HISIM2;
307 case TYPE::NMOS_HISIMHV1:
308 case TYPE::PMOS_HISIMHV1: return MODEL_TYPE::HISIMHV1;
309 case TYPE::NMOS_HISIMHV2:
310 case TYPE::PMOS_HISIMHV2: return MODEL_TYPE::HISIMHV2;
311
312 default:
313 wxFAIL_MSG( "Unhandled SIM_MODEL type in SIM_MODEL_NGSPICE" );
314 return MODEL_TYPE::NONE;
315 }
316}
317
318
319const SIM_MODEL_NGSPICE::MODEL_INFO& SIM_MODEL_NGSPICE::ModelInfo( MODEL_TYPE aType )
320{
321 // Lazy first-touch init. Spice library parsing parallelizes via the thread pool,
322 // so multiple threads can race to construct this; rely on the C++11 thread-safe
323 // function-local static instead of a hand-rolled double-checked unique_ptr.
324 // The map is large, so we still want lazy init when the user never sims.
325 static const NGSPICE_MODEL_INFO_MAP s_ModelInfoMap;
326
327 return s_ModelInfoMap.modelInfos.at( aType );
328}
329
330
332{
333 modelInfos[SIM_MODEL_NGSPICE::MODEL_TYPE::NONE] = {};
334 addBJT();
335 addBSIM1();
336 addBSIM2();
337 addBSIM3();
338 addBSIM4();
339 addB3SOI();
340 addB4SOI();
341 addDIODE();
342 addHFET();
343 addHICUM2();
344 addHSIM();
345 addJFET();
346 addMES();
347 addMOS();
348 addMOS6();
349 addMOS9();
350 addVBIC();
351}
bool canSilentlyIgnoreParam(const std::string &aParamName)
static const MODEL_INFO & ModelInfo(MODEL_TYPE aType)
std::vector< std::string > GetPinNames() const override
MODEL_TYPE getModelType() const
void SetParamFromSpiceCode(const std::string &aParamName, const std::string &aValue, SIM_VALUE_GRAMMAR::NOTATION aNotation) override
int doFindParam(const std::string &aParamName) const override
SIM_MODEL_NGSPICE(TYPE aType)
friend struct NGSPICE_MODEL_INFO_MAP
SIM_MODEL_SPICE(TYPE aType, std::unique_ptr< SPICE_GENERATOR > aSpiceGenerator)
void AddParam(const PARAM::INFO &aInfo)
virtual const PARAM & GetParam(unsigned aParamIndex) const
int GetParamCount() const
Definition sim_model.h:475
DEVICE_T GetDeviceType() const
Definition sim_model.h:457
void SetParamValue(int aParamIndex, const std::string &aValue, SIM_VALUE::NOTATION aNotation=SIM_VALUE::NOTATION::SI)
TYPE GetType() const
Definition sim_model.h:458
std::vector< std::string > CurrentNames(const SPICE_ITEM &aItem) const override
virtual std::string ItemName(const SPICE_ITEM &aItem) const
const SIM_MODEL & m_model
virtual std::vector< std::string > CurrentNames(const SPICE_ITEM &aItem) const
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
STL namespace.
SIM_MODEL::TYPE TYPE
Definition sim_model.cpp:54
std::unordered_map< MODEL_TYPE, MODEL_INFO > modelInfos
bool Matches(const std::string &aName) const
Definition sim_model.h:392
const INFO & info
Definition sim_model.h:398