Sim: Move Spice model parsing to a separate class hierarchy
Roughly analogous to the SPICE_GENERATOR hierarchy.
This commit is contained in:
parent
42005d9678
commit
8599323dce
|
@ -340,6 +340,7 @@ if( KICAD_SPICE )
|
||||||
sim/sim_plot_panel.cpp
|
sim/sim_plot_panel.cpp
|
||||||
sim/sim_property.cpp
|
sim/sim_property.cpp
|
||||||
sim/sim_workbook.cpp
|
sim/sim_workbook.cpp
|
||||||
|
sim/spice_model_parser.cpp
|
||||||
sim/spice_simulator.cpp
|
sim/spice_simulator.cpp
|
||||||
sim/spice_value.cpp
|
sim/spice_value.cpp
|
||||||
widgets/tuner_slider.cpp
|
widgets/tuner_slider.cpp
|
||||||
|
|
|
@ -931,7 +931,7 @@ std::unique_ptr<SIM_MODEL> SIM_MODEL::Create( TYPE aType )
|
||||||
return std::make_unique<SIM_MODEL_SOURCE>( aType );
|
return std::make_unique<SIM_MODEL_SOURCE>( aType );
|
||||||
|
|
||||||
case TYPE::SUBCKT:
|
case TYPE::SUBCKT:
|
||||||
return std::make_unique<SIM_MODEL_SUBCKT>( aType );
|
return std::make_unique<SIM_MODEL_SUBCKT>();
|
||||||
|
|
||||||
case TYPE::XSPICE:
|
case TYPE::XSPICE:
|
||||||
return std::make_unique<SIM_MODEL_XSPICE>( aType );
|
return std::make_unique<SIM_MODEL_XSPICE>( aType );
|
||||||
|
|
|
@ -24,27 +24,9 @@
|
||||||
|
|
||||||
#include <sim/sim_model_spice.h>
|
#include <sim/sim_model_spice.h>
|
||||||
#include <sim/sim_model_raw_spice.h>
|
#include <sim/sim_model_raw_spice.h>
|
||||||
|
#include <sim/spice_model_parser.h>
|
||||||
|
|
||||||
#include <sim/spice_grammar.h>
|
|
||||||
#include <confirm.h>
|
#include <confirm.h>
|
||||||
#include <pegtl.hpp>
|
|
||||||
#include <pegtl/contrib/parse_tree.hpp>
|
|
||||||
|
|
||||||
|
|
||||||
namespace SIM_MODEL_SPICE_PARSER
|
|
||||||
{
|
|
||||||
using namespace SPICE_GRAMMAR;
|
|
||||||
|
|
||||||
template <typename Rule> struct spiceUnitSelector : std::false_type {};
|
|
||||||
|
|
||||||
template <> struct spiceUnitSelector<dotModel> : std::true_type {};
|
|
||||||
template <> struct spiceUnitSelector<modelName> : std::true_type {};
|
|
||||||
template <> struct spiceUnitSelector<dotModelType> : std::true_type {};
|
|
||||||
template <> struct spiceUnitSelector<param> : std::true_type {};
|
|
||||||
template <> struct spiceUnitSelector<paramValue> : std::true_type {};
|
|
||||||
|
|
||||||
template <> struct spiceUnitSelector<dotSubckt> : std::true_type {};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
wxString SPICE_GENERATOR_SPICE::Preview( const wxString& aModelName ) const
|
wxString SPICE_GENERATOR_SPICE::Preview( const wxString& aModelName ) const
|
||||||
|
@ -66,96 +48,14 @@ wxString SPICE_GENERATOR_SPICE::Preview( const wxString& aModelName ) const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
SIM_MODEL::TYPE SIM_MODEL_SPICE::ReadTypeFromSpiceCode( const wxString& aSpiceCode )
|
|
||||||
{
|
|
||||||
tao::pegtl::string_input<> in( aSpiceCode.ToUTF8(), "Spice_Code" );
|
|
||||||
std::unique_ptr<tao::pegtl::parse_tree::node> root;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
root = tao::pegtl::parse_tree::parse<SIM_MODEL_SPICE_PARSER::spiceUnitGrammar,
|
|
||||||
SIM_MODEL_SPICE_PARSER::spiceUnitSelector,
|
|
||||||
tao::pegtl::nothing,
|
|
||||||
SIM_MODEL_SPICE_PARSER::control>
|
|
||||||
( in );
|
|
||||||
}
|
|
||||||
catch( const tao::pegtl::parse_error& e )
|
|
||||||
{
|
|
||||||
wxLogDebug( "%s", e.what() );
|
|
||||||
return TYPE::NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
for( const auto& node : root->children )
|
|
||||||
{
|
|
||||||
if( node->is_type<SIM_MODEL_SPICE_PARSER::dotModel>() )
|
|
||||||
{
|
|
||||||
wxString paramName;
|
|
||||||
wxString typeString;
|
|
||||||
wxString level;
|
|
||||||
wxString version;
|
|
||||||
|
|
||||||
for( const auto& subnode : node->children )
|
|
||||||
{
|
|
||||||
if( subnode->is_type<SIM_MODEL_SPICE_PARSER::modelName>() )
|
|
||||||
{
|
|
||||||
// Do nothing.
|
|
||||||
}
|
|
||||||
else if( subnode->is_type<SIM_MODEL_SPICE_PARSER::dotModelType>() )
|
|
||||||
{
|
|
||||||
typeString = subnode->string();
|
|
||||||
TYPE type = readTypeFromSpiceStrings( typeString );
|
|
||||||
|
|
||||||
if( type != TYPE::RAWSPICE )
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
else if( subnode->is_type<SIM_MODEL_SPICE_PARSER::param>() )
|
|
||||||
{
|
|
||||||
paramName = subnode->string();
|
|
||||||
}
|
|
||||||
else if( subnode->is_type<SIM_MODEL_SPICE_PARSER::paramValue>() )
|
|
||||||
{
|
|
||||||
wxASSERT( paramName != "" );
|
|
||||||
|
|
||||||
if( paramName == "level" )
|
|
||||||
level = subnode->string();
|
|
||||||
else if( paramName == "version" )
|
|
||||||
version = subnode->string();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
wxFAIL_MSG( "Unhandled parse tree subnode" );
|
|
||||||
return TYPE::NONE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type was not determined from Spice type string alone, so now we take `level` and
|
|
||||||
// `version` variables into account too. This is suboptimal since we read the model
|
|
||||||
// twice this way, and moreover the code is now somewhat duplicated.
|
|
||||||
|
|
||||||
return readTypeFromSpiceStrings( typeString, level, version, false );
|
|
||||||
}
|
|
||||||
else if( node->is_type<SIM_MODEL_SPICE_PARSER::dotSubckt>() )
|
|
||||||
return TYPE::SUBCKT;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
wxFAIL_MSG( "Unhandled parse tree node" );
|
|
||||||
return TYPE::NONE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wxFAIL_MSG( "Could not derive type from Spice code" );
|
|
||||||
return TYPE::NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
std::unique_ptr<SIM_MODEL_SPICE> SIM_MODEL_SPICE::Create( const wxString& aSpiceCode )
|
std::unique_ptr<SIM_MODEL_SPICE> SIM_MODEL_SPICE::Create( const wxString& aSpiceCode )
|
||||||
{
|
{
|
||||||
auto model = static_cast<SIM_MODEL_SPICE*>(
|
auto model = static_cast<SIM_MODEL_SPICE*>(
|
||||||
SIM_MODEL::Create( ReadTypeFromSpiceCode( aSpiceCode ) ).release() );
|
SIM_MODEL::Create( SPICE_MODEL_PARSER::ReadType( aSpiceCode ) ).release() );
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
model->ReadSpiceCode( aSpiceCode );
|
model->m_spiceModelParser->ReadModel( aSpiceCode );
|
||||||
}
|
}
|
||||||
catch( const IO_ERROR& e )
|
catch( const IO_ERROR& e )
|
||||||
{
|
{
|
||||||
|
@ -166,72 +66,20 @@ std::unique_ptr<SIM_MODEL_SPICE> SIM_MODEL_SPICE::Create( const wxString& aSpice
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SIM_MODEL_SPICE::ReadSpiceCode( const wxString& aSpiceCode )
|
SIM_MODEL_SPICE::SIM_MODEL_SPICE( TYPE aType,
|
||||||
|
std::unique_ptr<SPICE_GENERATOR> aSpiceGenerator ) :
|
||||||
|
SIM_MODEL( aType, std::move( aSpiceGenerator ) ),
|
||||||
|
m_spiceModelParser( std::make_unique<SPICE_MODEL_PARSER>( *this ) )
|
||||||
{
|
{
|
||||||
// The default behavior is to treat the Spice param=value pairs as the model parameters and
|
}
|
||||||
// values (for many models the correspondence is not exact, so this function is overridden).
|
|
||||||
|
|
||||||
tao::pegtl::string_input<> in( aSpiceCode.ToUTF8(), "Spice_Code" );
|
|
||||||
std::unique_ptr<tao::pegtl::parse_tree::node> root;
|
|
||||||
|
|
||||||
try
|
SIM_MODEL_SPICE::SIM_MODEL_SPICE( TYPE aType,
|
||||||
{
|
std::unique_ptr<SPICE_GENERATOR> aSpiceGenerator,
|
||||||
root = tao::pegtl::parse_tree::parse<SIM_MODEL_SPICE_PARSER::spiceUnitGrammar,
|
std::unique_ptr<SPICE_MODEL_PARSER> aSpiceModelParser ) :
|
||||||
SIM_MODEL_SPICE_PARSER::spiceUnitSelector,
|
SIM_MODEL( aType, std::move( aSpiceGenerator ) ),
|
||||||
tao::pegtl::nothing,
|
m_spiceModelParser( std::move( aSpiceModelParser ) )
|
||||||
SIM_MODEL_SPICE_PARSER::control>
|
{
|
||||||
( in );
|
|
||||||
}
|
|
||||||
catch( tao::pegtl::parse_error& e )
|
|
||||||
{
|
|
||||||
THROW_IO_ERROR( e.what() );
|
|
||||||
}
|
|
||||||
|
|
||||||
for( const auto& node : root->children )
|
|
||||||
{
|
|
||||||
if( node->is_type<SIM_MODEL_SPICE_PARSER::dotModel>() )
|
|
||||||
{
|
|
||||||
wxString paramName = "";
|
|
||||||
|
|
||||||
for( const auto& subnode : node->children )
|
|
||||||
{
|
|
||||||
if( subnode->is_type<SIM_MODEL_SPICE_PARSER::modelName>() )
|
|
||||||
{
|
|
||||||
// Do nothing.
|
|
||||||
}
|
|
||||||
else if( subnode->is_type<SIM_MODEL_SPICE_PARSER::dotModelType>() )
|
|
||||||
{
|
|
||||||
// Do nothing.
|
|
||||||
}
|
|
||||||
else if( subnode->is_type<SIM_MODEL_SPICE_PARSER::param>() )
|
|
||||||
{
|
|
||||||
paramName = subnode->string();
|
|
||||||
}
|
|
||||||
else if( subnode->is_type<SIM_MODEL_SPICE_PARSER::paramValue>() )
|
|
||||||
{
|
|
||||||
wxASSERT( !paramName.IsEmpty() );
|
|
||||||
|
|
||||||
if( !SetParamFromSpiceCode( paramName, subnode->string() ) )
|
|
||||||
{
|
|
||||||
THROW_IO_ERROR( wxString::Format(
|
|
||||||
_( "Failed to set parameter '%s' to '%s'" ),
|
|
||||||
paramName,
|
|
||||||
subnode->string() ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
wxFAIL_MSG( "Unhandled parse tree subnode" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
wxFAIL_MSG( "Unhandled parse tree node" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_spiceCode = aSpiceCode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -252,37 +100,3 @@ bool SIM_MODEL_SPICE::SetParamFromSpiceCode( const wxString& aParamName,
|
||||||
{
|
{
|
||||||
return SIM_MODEL::SetParamValue( aParamName, aParamValue, aNotation );
|
return SIM_MODEL::SetParamValue( aParamName, aParamValue, aNotation );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
SIM_MODEL::TYPE SIM_MODEL_SPICE::readTypeFromSpiceStrings( const wxString& aTypeString,
|
|
||||||
const wxString& aLevel,
|
|
||||||
const wxString& aVersion,
|
|
||||||
bool aSkipDefaultLevel )
|
|
||||||
{
|
|
||||||
std::unique_ptr<SIM_VALUE> readLevel = SIM_VALUE::Create( SIM_VALUE::TYPE_INT,
|
|
||||||
aLevel.ToStdString() );
|
|
||||||
|
|
||||||
for( TYPE type : TYPE_ITERATOR() )
|
|
||||||
{
|
|
||||||
wxString typePrefix = SpiceInfo( type ).modelType;
|
|
||||||
wxString level = SpiceInfo( type ).level;
|
|
||||||
wxString version = SpiceInfo( type ).version;
|
|
||||||
bool isDefaultLevel = SpiceInfo( type ).isDefaultLevel;
|
|
||||||
|
|
||||||
if( typePrefix == "" )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Check if `aTypeString` starts with `typePrefix`.
|
|
||||||
if( aTypeString.Upper().StartsWith( typePrefix )
|
|
||||||
&& ( level == readLevel->ToString()
|
|
||||||
|| ( !aSkipDefaultLevel && isDefaultLevel && aLevel == "" ) )
|
|
||||||
&& version == aVersion )
|
|
||||||
{
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the type string is not recognized, demote to a raw Spice element. This way the user won't
|
|
||||||
// have an error if there is a type KiCad does not recognize.
|
|
||||||
return TYPE::RAWSPICE;
|
|
||||||
}
|
|
||||||
|
|
|
@ -27,10 +27,12 @@
|
||||||
|
|
||||||
#include <sim/sim_model.h>
|
#include <sim/sim_model.h>
|
||||||
#include <sim/spice_generator.h>
|
#include <sim/spice_generator.h>
|
||||||
|
#include <sim/spice_model_parser.h>
|
||||||
|
|
||||||
|
|
||||||
class SPICE_GENERATOR_SPICE : public SPICE_GENERATOR
|
class SPICE_GENERATOR_SPICE : public SPICE_GENERATOR
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
using SPICE_GENERATOR::SPICE_GENERATOR;
|
using SPICE_GENERATOR::SPICE_GENERATOR;
|
||||||
|
|
||||||
wxString Preview( const wxString& aModelName ) const override;
|
wxString Preview( const wxString& aModelName ) const override;
|
||||||
|
@ -41,15 +43,18 @@ class SIM_MODEL_SPICE : public SIM_MODEL
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
friend class SPICE_GENERATOR_SPICE;
|
friend class SPICE_GENERATOR_SPICE;
|
||||||
|
friend class SPICE_MODEL_PARSER;
|
||||||
|
|
||||||
static TYPE ReadTypeFromSpiceCode( const wxString& aSpiceCode );
|
|
||||||
static std::unique_ptr<SIM_MODEL_SPICE> Create( const wxString& aSpiceCode );
|
static std::unique_ptr<SIM_MODEL_SPICE> Create( const wxString& aSpiceCode );
|
||||||
|
|
||||||
virtual void ReadSpiceCode( const wxString& aSpiceCode );
|
SIM_MODEL_SPICE( TYPE aType,
|
||||||
|
std::unique_ptr<SPICE_GENERATOR> aSpiceGenerator );
|
||||||
|
|
||||||
|
SIM_MODEL_SPICE( TYPE aType,
|
||||||
|
std::unique_ptr<SPICE_GENERATOR> aSpiceGenerator,
|
||||||
|
std::unique_ptr<SPICE_MODEL_PARSER> aSpiceModelParser );
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
using SIM_MODEL::SIM_MODEL;
|
|
||||||
|
|
||||||
bool SetParamValue( unsigned aParamIndex, const wxString& aParamValue,
|
bool SetParamValue( unsigned aParamIndex, const wxString& aParamValue,
|
||||||
SIM_VALUE_GRAMMAR::NOTATION aNotation
|
SIM_VALUE_GRAMMAR::NOTATION aNotation
|
||||||
= SIM_VALUE_GRAMMAR::NOTATION::SI ) override;
|
= SIM_VALUE_GRAMMAR::NOTATION::SI ) override;
|
||||||
|
@ -61,10 +66,7 @@ protected:
|
||||||
wxString m_spiceCode;
|
wxString m_spiceCode;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static TYPE readTypeFromSpiceStrings( const wxString& aTypeString,
|
std::unique_ptr<SPICE_MODEL_PARSER> m_spiceModelParser;
|
||||||
const wxString& aLevel = "",
|
|
||||||
const wxString& aVersion = "",
|
|
||||||
bool aSkipDefaultLevel = true );
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SIM_MODEL_SPICE_H
|
#endif // SIM_MODEL_SPICE_H
|
||||||
|
|
|
@ -67,13 +67,7 @@ std::vector<wxString> SPICE_GENERATOR_SUBCKT::CurrentNames( const wxString& aRef
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
SIM_MODEL_SUBCKT::SIM_MODEL_SUBCKT( TYPE aType ) :
|
void SPICE_MODEL_PARSER_SUBCKT::ReadModel( const wxString& aSpiceCode )
|
||||||
SIM_MODEL_SPICE( aType, std::make_unique<SPICE_GENERATOR_SUBCKT>( *this ) )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void SIM_MODEL_SUBCKT::ReadSpiceCode( const wxString& aSpiceCode )
|
|
||||||
{
|
{
|
||||||
tao::pegtl::string_input<> in( aSpiceCode.ToUTF8(), "from_content" );
|
tao::pegtl::string_input<> in( aSpiceCode.ToUTF8(), "from_content" );
|
||||||
std::unique_ptr<tao::pegtl::parse_tree::node> root;
|
std::unique_ptr<tao::pegtl::parse_tree::node> root;
|
||||||
|
@ -91,6 +85,8 @@ void SIM_MODEL_SUBCKT::ReadSpiceCode( const wxString& aSpiceCode )
|
||||||
THROW_IO_ERROR( e.what() );
|
THROW_IO_ERROR( e.what() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SIM_MODEL_SUBCKT& model = static_cast<SIM_MODEL_SUBCKT&>( m_model );
|
||||||
|
|
||||||
for( const auto& node : root->children )
|
for( const auto& node : root->children )
|
||||||
{
|
{
|
||||||
if( node->is_type<SIM_MODEL_SUBCKT_SPICE_PARSER::dotSubckt>() )
|
if( node->is_type<SIM_MODEL_SUBCKT_SPICE_PARSER::dotSubckt>() )
|
||||||
|
@ -102,7 +98,7 @@ void SIM_MODEL_SUBCKT::ReadSpiceCode( const wxString& aSpiceCode )
|
||||||
}
|
}
|
||||||
else if( subnode->is_type<SIM_MODEL_SUBCKT_SPICE_PARSER::dotSubcktPinName>() )
|
else if( subnode->is_type<SIM_MODEL_SUBCKT_SPICE_PARSER::dotSubcktPinName>() )
|
||||||
{
|
{
|
||||||
AddPin( { subnode->string(), wxString::FromCDouble( GetPinCount() + 1 ) } );
|
model.AddPin( { subnode->string(), wxString::FromCDouble( model.GetPinCount() + 1 ) } );
|
||||||
}
|
}
|
||||||
else if( subnode->is_type<SIM_MODEL_SUBCKT_SPICE_PARSER::dotSubcktParams>() )
|
else if( subnode->is_type<SIM_MODEL_SUBCKT_SPICE_PARSER::dotSubcktParams>() )
|
||||||
{
|
{
|
||||||
|
@ -110,12 +106,12 @@ void SIM_MODEL_SUBCKT::ReadSpiceCode( const wxString& aSpiceCode )
|
||||||
{
|
{
|
||||||
if( subsubnode->is_type<SIM_MODEL_SUBCKT_SPICE_PARSER::param>() )
|
if( subsubnode->is_type<SIM_MODEL_SUBCKT_SPICE_PARSER::param>() )
|
||||||
{
|
{
|
||||||
m_paramInfos.push_back( std::make_unique<PARAM::INFO>() );
|
model.m_paramInfos.push_back( std::make_unique<SIM_MODEL::PARAM::INFO>() );
|
||||||
m_paramInfos.back()->name = subsubnode->string();
|
model.m_paramInfos.back()->name = subsubnode->string();
|
||||||
m_paramInfos.back()->isInstanceParam = true;
|
model.m_paramInfos.back()->isInstanceParam = true;
|
||||||
m_paramInfos.back()->isSpiceInstanceParam = true;
|
model.m_paramInfos.back()->isSpiceInstanceParam = true;
|
||||||
|
|
||||||
AddParam( *m_paramInfos.back() );
|
model.AddParam( *model.m_paramInfos.back() );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -130,8 +126,8 @@ void SIM_MODEL_SUBCKT::ReadSpiceCode( const wxString& aSpiceCode )
|
||||||
SIM_MODEL_SUBCKT_SPICE_PARSER::number<SIM_VALUE::TYPE_FLOAT,
|
SIM_MODEL_SUBCKT_SPICE_PARSER::number<SIM_VALUE::TYPE_FLOAT,
|
||||||
SIM_MODEL_SUBCKT_SPICE_PARSER::NOTATION::SPICE>>() )
|
SIM_MODEL_SUBCKT_SPICE_PARSER::NOTATION::SPICE>>() )
|
||||||
{
|
{
|
||||||
wxASSERT( m_paramInfos.size() > 0 );
|
wxASSERT( model.m_paramInfos.size() > 0 );
|
||||||
m_paramInfos.back()->defaultValue = subnode->string();
|
model.m_paramInfos.back()->defaultValue = subnode->string();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,7 +137,15 @@ void SIM_MODEL_SUBCKT::ReadSpiceCode( const wxString& aSpiceCode )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_spiceCode = aSpiceCode;
|
model.m_spiceCode = aSpiceCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SIM_MODEL_SUBCKT::SIM_MODEL_SUBCKT() :
|
||||||
|
SIM_MODEL_SPICE( TYPE::SUBCKT,
|
||||||
|
std::make_unique<SPICE_GENERATOR_SUBCKT>( *this ),
|
||||||
|
std::make_unique<SPICE_MODEL_PARSER_SUBCKT>( *this ) )
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -39,12 +39,22 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class SPICE_MODEL_PARSER_SUBCKT : public SPICE_MODEL_PARSER
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using SPICE_MODEL_PARSER::SPICE_MODEL_PARSER;
|
||||||
|
|
||||||
|
void ReadModel( const wxString& aSpiceCode ) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class SIM_MODEL_SUBCKT : public SIM_MODEL_SPICE
|
class SIM_MODEL_SUBCKT : public SIM_MODEL_SPICE
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SIM_MODEL_SUBCKT( SIM_MODEL::TYPE aType );
|
friend class SPICE_MODEL_PARSER_SUBCKT;
|
||||||
|
|
||||||
|
SIM_MODEL_SUBCKT();
|
||||||
|
|
||||||
void ReadSpiceCode( const wxString& aSpiceCode ) override;
|
|
||||||
void SetBaseModel( const SIM_MODEL& aBaseModel ) override;
|
void SetBaseModel( const SIM_MODEL& aBaseModel ) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -53,7 +63,6 @@ protected:
|
||||||
private:
|
private:
|
||||||
bool requiresSpiceModelLine() const override { return true; }
|
bool requiresSpiceModelLine() const override { return true; }
|
||||||
|
|
||||||
|
|
||||||
std::vector<std::unique_ptr<PARAM::INFO>> m_paramInfos;
|
std::vector<std::unique_ptr<PARAM::INFO>> m_paramInfos;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,231 @@
|
||||||
|
/*
|
||||||
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2022 Mikolaj Wielgus
|
||||||
|
* Copyright (C) 2022 KiCad Developers, see AUTHORS.txt for contributors.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 3
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, you may find one here:
|
||||||
|
* https://www.gnu.org/licenses/gpl-3.0.html
|
||||||
|
* or you may search the http://www.gnu.org website for the version 3 license,
|
||||||
|
* or you may write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sim/spice_model_parser.h>
|
||||||
|
#include <sim/spice_grammar.h>
|
||||||
|
#include <sim/sim_model_spice.h>
|
||||||
|
|
||||||
|
#include <pegtl.hpp>
|
||||||
|
#include <pegtl/contrib/parse_tree.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
namespace SIM_MODEL_SPICE_PARSER
|
||||||
|
{
|
||||||
|
using namespace SPICE_GRAMMAR;
|
||||||
|
|
||||||
|
template <typename Rule> struct spiceUnitSelector : std::false_type {};
|
||||||
|
|
||||||
|
template <> struct spiceUnitSelector<dotModel> : std::true_type {};
|
||||||
|
template <> struct spiceUnitSelector<modelName> : std::true_type {};
|
||||||
|
template <> struct spiceUnitSelector<dotModelType> : std::true_type {};
|
||||||
|
template <> struct spiceUnitSelector<param> : std::true_type {};
|
||||||
|
template <> struct spiceUnitSelector<paramValue> : std::true_type {};
|
||||||
|
|
||||||
|
template <> struct spiceUnitSelector<dotSubckt> : std::true_type {};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SIM_MODEL::TYPE SPICE_MODEL_PARSER::ReadType( const wxString& aSpiceCode )
|
||||||
|
{
|
||||||
|
tao::pegtl::string_input<> in( aSpiceCode.ToUTF8(), "Spice_Code" );
|
||||||
|
std::unique_ptr<tao::pegtl::parse_tree::node> root;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
root = tao::pegtl::parse_tree::parse<SIM_MODEL_SPICE_PARSER::spiceUnitGrammar,
|
||||||
|
SIM_MODEL_SPICE_PARSER::spiceUnitSelector,
|
||||||
|
tao::pegtl::nothing,
|
||||||
|
SIM_MODEL_SPICE_PARSER::control>
|
||||||
|
( in );
|
||||||
|
}
|
||||||
|
catch( const tao::pegtl::parse_error& e )
|
||||||
|
{
|
||||||
|
wxLogDebug( "%s", e.what() );
|
||||||
|
return SIM_MODEL::TYPE::NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
for( const auto& node : root->children )
|
||||||
|
{
|
||||||
|
if( node->is_type<SIM_MODEL_SPICE_PARSER::dotModel>() )
|
||||||
|
{
|
||||||
|
wxString paramName;
|
||||||
|
wxString typeString;
|
||||||
|
wxString level;
|
||||||
|
wxString version;
|
||||||
|
|
||||||
|
for( const auto& subnode : node->children )
|
||||||
|
{
|
||||||
|
if( subnode->is_type<SIM_MODEL_SPICE_PARSER::modelName>() )
|
||||||
|
{
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
else if( subnode->is_type<SIM_MODEL_SPICE_PARSER::dotModelType>() )
|
||||||
|
{
|
||||||
|
typeString = subnode->string();
|
||||||
|
SIM_MODEL::TYPE type = ReadTypeFromSpiceStrings( typeString );
|
||||||
|
|
||||||
|
if( type != SIM_MODEL::TYPE::RAWSPICE )
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
else if( subnode->is_type<SIM_MODEL_SPICE_PARSER::param>() )
|
||||||
|
{
|
||||||
|
paramName = subnode->string();
|
||||||
|
}
|
||||||
|
else if( subnode->is_type<SIM_MODEL_SPICE_PARSER::paramValue>() )
|
||||||
|
{
|
||||||
|
wxASSERT( paramName != "" );
|
||||||
|
|
||||||
|
if( paramName == "level" )
|
||||||
|
level = subnode->string();
|
||||||
|
else if( paramName == "version" )
|
||||||
|
version = subnode->string();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wxFAIL_MSG( "Unhandled parse tree subnode" );
|
||||||
|
return SIM_MODEL::TYPE::NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type was not determined from Spice type string alone, so now we take `level` and
|
||||||
|
// `version` variables into account too. This is suboptimal since we read the model
|
||||||
|
// twice this way, and moreover the code is now somewhat duplicated.
|
||||||
|
|
||||||
|
return ReadTypeFromSpiceStrings( typeString, level, version, false );
|
||||||
|
}
|
||||||
|
else if( node->is_type<SIM_MODEL_SPICE_PARSER::dotSubckt>() )
|
||||||
|
return SIM_MODEL::TYPE::SUBCKT;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wxFAIL_MSG( "Unhandled parse tree node" );
|
||||||
|
return SIM_MODEL::TYPE::NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wxFAIL_MSG( "Could not derive type from Spice code" );
|
||||||
|
return SIM_MODEL::TYPE::NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SPICE_MODEL_PARSER::ReadModel( const wxString& aSpiceCode )
|
||||||
|
{
|
||||||
|
// The default behavior is to treat the Spice param=value pairs as the model parameters and
|
||||||
|
// values (for many models the correspondence is not exact, so this function is overridden).
|
||||||
|
|
||||||
|
tao::pegtl::string_input<> in( aSpiceCode.ToUTF8(), "Spice_Code" );
|
||||||
|
std::unique_ptr<tao::pegtl::parse_tree::node> root;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
root = tao::pegtl::parse_tree::parse<SIM_MODEL_SPICE_PARSER::spiceUnitGrammar,
|
||||||
|
SIM_MODEL_SPICE_PARSER::spiceUnitSelector,
|
||||||
|
tao::pegtl::nothing,
|
||||||
|
SIM_MODEL_SPICE_PARSER::control>
|
||||||
|
( in );
|
||||||
|
}
|
||||||
|
catch( tao::pegtl::parse_error& e )
|
||||||
|
{
|
||||||
|
THROW_IO_ERROR( e.what() );
|
||||||
|
}
|
||||||
|
|
||||||
|
for( const auto& node : root->children )
|
||||||
|
{
|
||||||
|
if( node->is_type<SIM_MODEL_SPICE_PARSER::dotModel>() )
|
||||||
|
{
|
||||||
|
wxString paramName = "";
|
||||||
|
|
||||||
|
for( const auto& subnode : node->children )
|
||||||
|
{
|
||||||
|
if( subnode->is_type<SIM_MODEL_SPICE_PARSER::modelName>() )
|
||||||
|
{
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
else if( subnode->is_type<SIM_MODEL_SPICE_PARSER::dotModelType>() )
|
||||||
|
{
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
else if( subnode->is_type<SIM_MODEL_SPICE_PARSER::param>() )
|
||||||
|
{
|
||||||
|
paramName = subnode->string();
|
||||||
|
}
|
||||||
|
else if( subnode->is_type<SIM_MODEL_SPICE_PARSER::paramValue>() )
|
||||||
|
{
|
||||||
|
wxASSERT( !paramName.IsEmpty() );
|
||||||
|
|
||||||
|
if( !m_model.SetParamFromSpiceCode( paramName, subnode->string() ) )
|
||||||
|
{
|
||||||
|
THROW_IO_ERROR( wxString::Format(
|
||||||
|
_( "Failed to set parameter '%s' to '%s'" ),
|
||||||
|
paramName,
|
||||||
|
subnode->string() ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wxFAIL_MSG( "Unhandled parse tree subnode" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wxFAIL_MSG( "Unhandled parse tree node" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_model.m_spiceCode = aSpiceCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SIM_MODEL::TYPE SPICE_MODEL_PARSER::ReadTypeFromSpiceStrings( const wxString& aTypeString,
|
||||||
|
const wxString& aLevel,
|
||||||
|
const wxString& aVersion,
|
||||||
|
bool aSkipDefaultLevel )
|
||||||
|
{
|
||||||
|
std::unique_ptr<SIM_VALUE> readLevel = SIM_VALUE::Create( SIM_VALUE::TYPE_INT,
|
||||||
|
aLevel.ToStdString() );
|
||||||
|
|
||||||
|
for( SIM_MODEL::TYPE type : SIM_MODEL::TYPE_ITERATOR() )
|
||||||
|
{
|
||||||
|
wxString typePrefix = SIM_MODEL::SpiceInfo( type ).modelType;
|
||||||
|
wxString level = SIM_MODEL::SpiceInfo( type ).level;
|
||||||
|
wxString version = SIM_MODEL::SpiceInfo( type ).version;
|
||||||
|
bool isDefaultLevel = SIM_MODEL::SpiceInfo( type ).isDefaultLevel;
|
||||||
|
|
||||||
|
if( typePrefix == "" )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Check if `aTypeString` starts with `typePrefix`.
|
||||||
|
if( aTypeString.Upper().StartsWith( typePrefix )
|
||||||
|
&& ( level == readLevel->ToString()
|
||||||
|
|| ( !aSkipDefaultLevel && isDefaultLevel && aLevel == "" ) )
|
||||||
|
&& version == aVersion )
|
||||||
|
{
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the type string is not recognized, demote to a raw Spice element. This way the user won't
|
||||||
|
// have an error if there is a type KiCad does not recognize.
|
||||||
|
return SIM_MODEL::TYPE::RAWSPICE;
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2022 Mikolaj Wielgus
|
||||||
|
* Copyright (C) 2022 KiCad Developers, see AUTHORS.txt for contributors.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 3
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, you may find one here:
|
||||||
|
* https://www.gnu.org/licenses/gpl-3.0.html
|
||||||
|
* or you may search the http://www.gnu.org website for the version 3 license,
|
||||||
|
* or you may write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SPICE_MODEL_PARSER_H
|
||||||
|
#define SPICE_MODEL_PARSER_H
|
||||||
|
|
||||||
|
#include <sim/sim_model.h>
|
||||||
|
|
||||||
|
class SIM_MODEL_SPICE;
|
||||||
|
|
||||||
|
|
||||||
|
class SPICE_MODEL_PARSER
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static SIM_MODEL::TYPE ReadType( const wxString& aSpiceCode );
|
||||||
|
|
||||||
|
SPICE_MODEL_PARSER( SIM_MODEL_SPICE& aModel ) : m_model( aModel ) {}
|
||||||
|
|
||||||
|
virtual void ReadModel( const wxString& aSpiceCode );
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static SIM_MODEL::TYPE ReadTypeFromSpiceStrings( const wxString& aTypeString,
|
||||||
|
const wxString& aLevel = "",
|
||||||
|
const wxString& aVersion = "",
|
||||||
|
bool aSkipDefaultLevel = true );
|
||||||
|
|
||||||
|
SIM_MODEL_SPICE& m_model;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SPICE_MODEL_PARSER
|
Loading…
Reference in New Issue