KiCad PCB EDA Suite
Loading...
Searching...
No Matches
spice_model_parser.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-2024 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
25#include <wx/log.h>
27#include <sim/spice_grammar.h>
28#include <sim/sim_model_spice.h>
30
31#include <boost/algorithm/string/case_conv.hpp>
32#include <boost/algorithm/string/predicate.hpp>
33#include <pegtl.hpp>
34#include <pegtl/contrib/parse_tree.hpp>
35
36
42static const wxChar traceSpiceModelParser[] = wxT( "KICAD_SPICE_MODEL_PARSER" );
43
44
46{
47 using namespace SPICE_GRAMMAR;
48
49 template <typename Rule> struct spiceUnitSelector : std::false_type {};
50
51 template <> struct spiceUnitSelector<dotModelAko> : std::true_type {};
52 template <> struct spiceUnitSelector<dotModel> : std::true_type {};
53 template <> struct spiceUnitSelector<modelName> : std::true_type {};
54 template <> struct spiceUnitSelector<dotModelType> : std::true_type {};
55 template <> struct spiceUnitSelector<param> : std::true_type {};
56 template <> struct spiceUnitSelector<paramValue> : std::true_type {};
57
58 template <> struct spiceUnitSelector<dotSubckt> : std::true_type {};
59}
60
61
62SIM_MODEL::TYPE SPICE_MODEL_PARSER::ReadType( const SIM_LIBRARY_SPICE& aLibrary,
63 const std::string& aSpiceCode )
64{
65 tao::pegtl::string_input<> in( aSpiceCode, "Spice_Code" );
66 std::unique_ptr<tao::pegtl::parse_tree::node> root;
67
68 try
69 {
70 root = tao::pegtl::parse_tree::parse<SIM_MODEL_SPICE_PARSER::spiceUnitGrammar,
72 tao::pegtl::nothing,
74 ( in );
75 }
76 catch( const tao::pegtl::parse_error& e )
77 {
78 wxLogTrace( traceSpiceModelParser, wxS( "%s" ), e.what() );
79 return SIM_MODEL::TYPE::NONE;
80 }
81
82 for( const auto& node : root->children )
83 {
84 if( node->is_type<SIM_MODEL_SPICE_PARSER::dotModelAko>() )
85 {
86 std::string modelName = node->children.at( 0 )->string();
87 std::string akoName = node->children.at( 1 )->string();
88 const SIM_MODEL* sourceModel = aLibrary.FindModel( akoName );
89
90 if( !sourceModel )
91 {
92 THROW_IO_ERROR( wxString::Format(
93 _( "Could not find model '%s' to copy for \"A Kind Of\" model '%s'" ),
94 akoName,
95 modelName ) );
96 }
97
98 return sourceModel->GetType();
99 }
100 else if( node->is_type<SIM_MODEL_SPICE_PARSER::dotModel>() )
101 {
102 std::string paramName;
103 std::string typeString;
104 std::string level;
105 std::string version;
106
107 for( const auto& subnode : node->children )
108 {
109 if( subnode->is_type<SIM_MODEL_SPICE_PARSER::modelName>() )
110 {
111 // Do nothing.
112 }
113 else if( subnode->is_type<SIM_MODEL_SPICE_PARSER::dotModelType>() )
114 {
115 typeString = subnode->string();
116 SIM_MODEL::TYPE type = ReadTypeFromSpiceStrings( typeString );
117
118 if( type != SIM_MODEL::TYPE::RAWSPICE )
119 return type;
120 }
121 else if( subnode->is_type<SIM_MODEL_SPICE_PARSER::param>() )
122 {
123 paramName = subnode->string();
124 }
125 else if( subnode->is_type<SIM_MODEL_SPICE_PARSER::paramValue>() )
126 {
127 wxASSERT( paramName != "" );
128
129 if( paramName == "level" )
130 level = subnode->string();
131 else if( paramName == "version" )
132 version = subnode->string();
133 }
134 else
135 {
136 wxFAIL_MSG( "Unhandled parse tree subnode" );
137 return SIM_MODEL::TYPE::NONE;
138 }
139 }
140
141 // Type was not determined from Spice type string alone, so now we take `level` and
142 // `version` variables into account too. This is suboptimal since we read the model
143 // twice this way, and moreover the code is now somewhat duplicated.
144
145 return ReadTypeFromSpiceStrings( typeString, level, version, false );
146 }
147 else if( node->is_type<SIM_MODEL_SPICE_PARSER::dotSubckt>() )
148 {
149 return SIM_MODEL::TYPE::SUBCKT;
150 }
151 else
152 {
153 wxFAIL_MSG( "Unhandled parse tree node" );
154 return SIM_MODEL::TYPE::NONE;
155 }
156 }
157
158 wxFAIL_MSG( "Could not derive type from Spice code" );
159 return SIM_MODEL::TYPE::NONE;
160}
161
162
164 const std::string& aSpiceCode )
165{
166 // The default behavior is to treat the Spice param=value pairs as the model parameters and
167 // values (for many models the correspondence is not exact, so this function is overridden).
168
169 tao::pegtl::string_input<> in( aSpiceCode, "Spice_Code" );
170 std::unique_ptr<tao::pegtl::parse_tree::node> root;
171
172 try
173 {
174 root = tao::pegtl::parse_tree::parse<SIM_MODEL_SPICE_PARSER::spiceUnitGrammar,
176 tao::pegtl::nothing,
178 ( in );
179 }
180 catch( tao::pegtl::parse_error& e )
181 {
182 THROW_IO_ERROR( e.what() );
183 }
184
185 for( const auto& node : root->children )
186 {
187 if( node->is_type<SIM_MODEL_SPICE_PARSER::dotModelAko>() )
188 {
189 std::string modelName = node->children.at( 0 )->string();
190 std::string akoName = node->children.at( 1 )->string();
191
192 const SIM_MODEL* sourceModel = aLibrary.FindModel( akoName );
193
194 if( !sourceModel )
195 {
196 THROW_IO_ERROR( wxString::Format(
197 _( "Could not find model '%s' to copy for \"A Kind Of\" model '%s'" ),
198 akoName,
199 modelName ) );
200 }
201
202 for( int i = 0; i < static_cast<int>( sourceModel->GetParamCount() ); ++i )
203 m_model.SetParamValue( i, sourceModel->GetParam( i ).value );
204
205 std::string paramName;
206
207 for( const auto& subnode : node->children )
208 {
209 if( subnode->is_type<SIM_MODEL_SPICE_PARSER::modelName>() )
210 {
211 // Do nothing.
212 }
213 else if( subnode->is_type<SIM_MODEL_SPICE_PARSER::dotModelType>() )
214 {
215 // Do nothing.
216 }
217 else if( subnode->is_type<SIM_MODEL_SPICE_PARSER::param>() )
218 {
219 paramName = subnode->string();
220 }
221 else if( subnode->is_type<SIM_MODEL_SPICE_PARSER::paramValue>() )
222 {
223 wxASSERT( paramName != "" );
224
225 m_model.SetParamFromSpiceCode( paramName, subnode->string() );
226 }
227 else
228 {
229 wxFAIL_MSG( "Unhandled parse tree subnode" );
230 }
231 }
232 }
233 else if( node->is_type<SIM_MODEL_SPICE_PARSER::dotModel>() )
234 {
235 std::string modelName;
236 std::string paramName;
237
238 for( const auto& subnode : node->children )
239 {
240 if( subnode->is_type<SIM_MODEL_SPICE_PARSER::modelName>() )
241 {
242 modelName = subnode->string();
243 }
244 else if( subnode->is_type<SIM_MODEL_SPICE_PARSER::dotModelType>() )
245 {
246 // Do nothing.
247 }
248 else if( subnode->is_type<SIM_MODEL_SPICE_PARSER::param>() )
249 {
250 paramName = subnode->string();
251 }
252 else if( subnode->is_type<SIM_MODEL_SPICE_PARSER::paramValue>() )
253 {
254 wxASSERT( paramName != "" );
255
256 m_model.SetParamFromSpiceCode( paramName, subnode->string() );
257 }
258 else
259 {
260 wxFAIL_MSG( "Unhandled parse tree subnode" );
261 }
262 }
263 }
264 else
265 {
266 wxFAIL_MSG( "Unhandled parse tree node" );
267 }
268 }
269
270 m_model.m_spiceCode = aSpiceCode;
271}
272
273
274SIM_MODEL::TYPE SPICE_MODEL_PARSER::ReadTypeFromSpiceStrings( const std::string& aTypeString,
275 const std::string& aLevel,
276 const std::string& aVersion,
277 bool aSkipDefaultLevel )
278{
279 wxString input_level = wxString( aLevel ).BeforeFirst( '.' );
280 wxString input_type( aTypeString );
281 bool vdmos = false;
282 bool pchan = false;
283
284 input_type.UpperCase();
285
286 if( input_type.StartsWith( wxS( "VDMOS" ) ) )
287 {
288 vdmos = true;
289 pchan = input_type.Contains( wxS( "PCHAN" ) );
290 }
291
292 for( SIM_MODEL::TYPE candidate : SIM_MODEL::TYPE_ITERATOR() )
293 {
294 wxString candidate_type = SIM_MODEL::SpiceInfo( candidate ).modelType;
295 wxString candidate_level = SIM_MODEL::SpiceInfo( candidate ).level;
296 wxString candidate_version = SIM_MODEL::SpiceInfo( candidate ).version;
297 bool candidate_isDefaultLevel = SIM_MODEL::SpiceInfo( candidate ).isDefaultLevel;
298
299 if( candidate_type.IsEmpty() )
300 continue;
301
302 if( candidate_type.StartsWith( wxS( "VDMOS" ) ) && vdmos )
303 {
304 if( vdmos && pchan && candidate_type.EndsWith( wxS( "PCHAN" ) ) )
305 return candidate;
306 else if( vdmos && !pchan && candidate_type.EndsWith( wxS( "NCHAN" ) ) )
307 return candidate;
308 }
309 else if( input_type.StartsWith( candidate_type ) )
310 {
311 if( candidate_version != aVersion )
312 continue;
313
314 if( candidate_level == input_level )
315 return candidate;
316
317 if( aSkipDefaultLevel )
318 continue;
319
320 if( candidate_isDefaultLevel && aLevel == "" )
321 return candidate;
322 }
323 }
324
325 // If the type string is not recognized, demote to a raw Spice element. This way the user won't
326 // have an error if there is a type KiCad does not recognize.
327 return SIM_MODEL::TYPE::RAWSPICE;
328}
329
SIM_MODEL * FindModel(const std::string &aModelName) const
Definition: sim_library.cpp:56
std::string m_spiceCode
virtual void SetParamFromSpiceCode(const std::string &aParamName, const std::string &aParamValue, SIM_VALUE_GRAMMAR::NOTATION aNotation=SIM_VALUE_GRAMMAR::NOTATION::SPICE)
static SPICE_INFO SpiceInfo(TYPE aType)
Definition: sim_model.cpp:245
virtual const PARAM & GetParam(unsigned aParamIndex) const
Definition: sim_model.cpp:780
int GetParamCount() const
Definition: sim_model.h:481
void SetParamValue(int aParamIndex, const std::string &aValue, SIM_VALUE::NOTATION aNotation=SIM_VALUE::NOTATION::SI)
Definition: sim_model.cpp:849
TYPE GetType() const
Definition: sim_model.h:464
virtual void ReadModel(const SIM_LIBRARY_SPICE &aLibrary, const std::string &aSpiceCode)
static SIM_MODEL::TYPE ReadTypeFromSpiceStrings(const std::string &aTypeString, const std::string &aLevel="", const std::string &aVersion="", bool aSkipDefaultLevel=true)
SIM_MODEL_SPICE & m_model
static SIM_MODEL::TYPE ReadType(const SIM_LIBRARY_SPICE &aLibrary, const std::string &aSpiceCode)
#define _(s)
static const wxChar traceSpiceModelParser[]
Flag to enable SPICE model parser debugging output.
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:39
must_if< error >::control< Rule > control
std::string value
Definition: sim_model.h:400
std::string modelType
Definition: sim_model.h:305
std::string level
Definition: sim_model.h:307
std::string version
Definition: sim_model.h:310