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