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