Implement fallback models for SPICE .lib models that we can't parse.

This commit is contained in:
Jeff Young 2022-12-22 20:21:10 +00:00
parent d0d6dc6ce0
commit 7f60b87d1d
18 changed files with 190 additions and 79 deletions

View File

@ -299,6 +299,7 @@ set( EESCHEMA_SRCS
sim/sim_model_raw_spice.cpp
sim/sim_model_source.cpp
sim/sim_model_spice.cpp
sim/sim_model_spice_fallback.cpp
sim/sim_model_subckt.cpp
sim/sim_model_switch.cpp
sim/sim_model_tline.cpp

View File

@ -31,6 +31,7 @@
#include <sim/sim_model.h>
#include <sim/sim_model_kibis.h>
#include <sim/sim_model_raw_spice.h>
#include <sim/sim_model_spice_fallback.h>
#include <grid_tricks.h>
#include <widgets/grid_icon_text_helpers.h>
#include <widgets/std_bitmap_button.h>
@ -40,6 +41,7 @@
#include <locale_io.h>
#include <wx/filedlg.h>
#include <wx/textfile.h>
#include "fmt/format.h"
using CATEGORY = SIM_MODEL::PARAM::CATEGORY;
@ -227,6 +229,8 @@ bool DIALOG_SIM_MODEL<T_symbol, T_field>::TransferDataToWindow()
}
}
}
m_curModelType = curModel().GetType();
}
else
{
@ -413,10 +417,15 @@ void DIALOG_SIM_MODEL<T_symbol, T_field>::updateInstanceWidgets()
m_typeChoice->Enable( !m_useLibraryModelRadioButton->GetValue() || isIbisLoaded() );
if( dynamic_cast<SIM_MODEL_RAW_SPICE*>( &curModel() ) )
if( dynamic_cast<SIM_MODEL_RAW_SPICE*>( &curModel() )
|| dynamic_cast<SIM_MODEL_SPICE_FALLBACK*>( &curModel() ) )
{
m_modelNotebook->SetSelection( 1 );
}
else
{
m_modelNotebook->SetSelection( 0 );
}
if( curModel().HasPrimaryValue() )
{
@ -535,9 +544,16 @@ template <typename T_symbol, typename T_field>
void DIALOG_SIM_MODEL<T_symbol, T_field>::updateModelCodeTab()
{
wxString text;
wxString pin( _( "Pin" ) );
SPICE_ITEM item;
item.modelName = m_modelNameChoice->GetStringSelection();
for( size_t ii = 1; ii <= m_symbol.GetFullPinCount(); ++ii )
{
item.pinNumbers.push_back( fmt::format( "{}", ii ) );
item.pinNetNames.push_back( pin.ToStdString() + fmt::format( "{}", ii ) );
}
if( m_useInstanceModelRadioButton->GetValue() || item.modelName == "" )
item.modelName = m_fields.at( REFERENCE_FIELD ).GetText();

View File

@ -140,7 +140,7 @@ DIALOG_SIM_MODEL_BASE::DIALOG_SIM_MODEL_BASE( wxWindow* parent, wxWindowID id, c
m_paramGridMgr = new wxPropertyGridManager(m_parametersPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxPGMAN_DEFAULT_STYLE|wxPG_SPLITTER_AUTO_CENTER);
m_paramGridMgr->SetExtraStyle( wxPG_EX_MODE_BUTTONS|wxPG_EX_NATIVE_DOUBLE_BUFFERING );
m_paramGridMgr->SetMinSize( wxSize( 440,-1 ) );
m_paramGridMgr->SetMinSize( wxSize( 500,-1 ) );
m_paramGrid = m_paramGridMgr->AddPage( _("Page"), wxNullBitmap );

View File

@ -1493,7 +1493,7 @@
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size">440,-1</property>
<property name="minimum_size">500,-1</property>
<property name="moveable">1</property>
<property name="name">m_paramGridMgr</property>
<property name="pane_border">1</property>

View File

@ -25,6 +25,7 @@
#include <sim/sim_library_kibis.h>
#include <sim/sim_library.h>
#include <sim/sim_library_spice.h>
#include <boost/algorithm/string/case_conv.hpp>
std::unique_ptr<SIM_LIBRARY> SIM_LIBRARY::Create( const wxString &aFilePath, REPORTER *aReporter,
@ -52,11 +53,11 @@ void SIM_LIBRARY::ReadFile( const std::string& aFilePath )
SIM_MODEL* SIM_LIBRARY::FindModel( const std::string& aModelName ) const
{
std::string lowerName = boost::to_lower_copy( aModelName );
for( int i = 0; i < static_cast<int>( m_modelNames.size() ); ++i )
{
const std::string& modelName = m_modelNames.at( i );
if( modelName == aModelName )
if( boost::to_lower_copy( m_modelNames.at( i ) ) == lowerName )
return m_models.at( i ).get();
}

View File

@ -49,6 +49,7 @@
#include <pegtl/contrib/parse_tree.hpp>
#include <iterator>
#include "sim_model_spice_fallback.h"
using TYPE = SIM_MODEL::TYPE;
@ -442,6 +443,13 @@ void SIM_MODEL::WriteFields( std::vector<LIB_FIELD>& aFields ) const
}
std::unique_ptr<SIM_MODEL> SIM_MODEL::CreateFallback( TYPE aType, const std::string& aSpiceCode )
{
std::unique_ptr<SIM_MODEL> model( new SIM_MODEL_SPICE_FALLBACK( aType, aSpiceCode ) );
return model;
}
std::unique_ptr<SIM_MODEL> SIM_MODEL::Create( TYPE aType, const std::vector<LIB_PIN*>& aPins )
{
std::unique_ptr<SIM_MODEL> model = Create( aType );
@ -455,7 +463,12 @@ std::unique_ptr<SIM_MODEL> SIM_MODEL::Create( TYPE aType, const std::vector<LIB_
std::unique_ptr<SIM_MODEL> SIM_MODEL::Create( const SIM_MODEL& aBaseModel,
const std::vector<LIB_PIN*>& aPins)
{
std::unique_ptr<SIM_MODEL> model = Create( aBaseModel.GetType() );
std::unique_ptr<SIM_MODEL> model;
if( dynamic_cast<const SIM_MODEL_SPICE_FALLBACK*>( &aBaseModel ) )
model = CreateFallback( aBaseModel.GetType() );
else
model = Create( aBaseModel.GetType() );
try
{
@ -482,7 +495,12 @@ std::unique_ptr<SIM_MODEL> SIM_MODEL::Create( const SIM_MODEL& aBaseModel,
if( type == TYPE::NONE )
type = aBaseModel.GetType();
std::unique_ptr<SIM_MODEL> model = Create( type );
std::unique_ptr<SIM_MODEL> model;
if( dynamic_cast<const SIM_MODEL_SPICE_FALLBACK*>( &aBaseModel ) )
model = CreateFallback( type );
else
model = Create( type );
try
{
@ -655,13 +673,11 @@ void SIM_MODEL::SetPinSymbolPinNumber( int aPinIndex, const std::string& aSymbol
void SIM_MODEL::SetPinSymbolPinNumber( const std::string& aPinName,
const std::string& aSymbolPinNumber )
{
const std::vector<std::reference_wrapper<const PIN>> pins = GetPins();
for( int ii = 0; ii < (int) pins.size(); ++ii )
for( PIN& pin : m_pins )
{
if( pins.at( ii ).get().name == aPinName )
if( pin.name == aPinName )
{
SetPinSymbolPinNumber( ii, aSymbolPinNumber );
pin.symbolPinNumber = aSymbolPinNumber;
return;
}
}
@ -678,7 +694,7 @@ void SIM_MODEL::SetPinSymbolPinNumber( const std::string& aPinName,
GetTypeInfo().fieldValue ) );
}
SetPinSymbolPinNumber( --aPinIndex /* convert to 0-based */, aSymbolPinNumber );
m_pins[ --aPinIndex /* convert to 0-based */ ].symbolPinNumber = aSymbolPinNumber;
}
@ -915,7 +931,7 @@ SIM_MODEL::SIM_MODEL( TYPE aType, std::unique_ptr<SPICE_GENERATOR> aSpiceGenerat
}
void SIM_MODEL::CreatePins( const std::vector<LIB_PIN*>& aSymbolPins )
void SIM_MODEL::createPins( const std::vector<LIB_PIN*>& aSymbolPins )
{
// Default pin sequence: model pins are the same as symbol pins.
// Excess model pins are set as Not Connected.
@ -952,7 +968,7 @@ void SIM_MODEL::doReadDataFields( const std::vector<T>* aFields,
{
m_serializer->ParseEnable( GetFieldValue( aFields, ENABLE_FIELD ) );
CreatePins( aPins );
createPins( aPins );
m_serializer->ParsePins( GetFieldValue( aFields, PINS_FIELD ) );
std::string paramsField = GetFieldValue( aFields, PARAMS_FIELD );
@ -1476,7 +1492,7 @@ void SIM_MODEL::MigrateSimModel( T_symbol& aSymbol, const PROJECT* aProject )
// Try to generate a default pin map from the SIM_MODEL's pins; if that fails,
// generate one from the symbol's pins
model.model.SIM_MODEL::CreatePins( sourcePins );
model.model.SIM_MODEL::createPins( sourcePins );
pinMap = wxString( model.model.Serializer().GeneratePins() );
if( pinMap.IsEmpty() )
@ -1536,7 +1552,7 @@ void SIM_MODEL::MigrateSimModel( T_symbol& aSymbol, const PROJECT* aProject )
if( pinMap.IsEmpty() )
{
// Generate a default pin map from the SIM_MODEL's pins
model->CreatePins( sourcePins );
model->createPins( sourcePins );
pinMap = wxString( model->Serializer().GeneratePins() );
}
}

View File

@ -414,6 +414,9 @@ public:
static std::unique_ptr<SIM_MODEL> Create( const std::vector<T>& aFields,
const std::vector<LIB_PIN*>& aPins );
static std::unique_ptr<SIM_MODEL> CreateFallback( TYPE aType,
const std::string& aSpiceCode = "" );
template <typename T>
static std::string GetFieldValue( const std::vector<T>* aFields, const std::string& aFieldName,
bool aResolve = true );
@ -476,7 +479,8 @@ public:
std::vector<std::reference_wrapper<const PIN>> GetPins() const;
void SetPinSymbolPinNumber( int aPinIndex, const std::string& aSymbolPinNumber );
void SetPinSymbolPinNumber( const std::string& aPinName, const std::string& aSymbolPinNumber );
virtual void SetPinSymbolPinNumber( const std::string& aPinName,
const std::string& aSymbolPinNumber );
int GetParamCount() const { return static_cast<int>( m_params.size() ); }
@ -534,7 +538,7 @@ protected:
SIM_MODEL( TYPE aType, std::unique_ptr<SPICE_GENERATOR> aSpiceGenerator,
std::unique_ptr<SIM_MODEL_SERIALIZER> aSerializer );
virtual void CreatePins( const std::vector<LIB_PIN*>& aSymbolPins );
void createPins( const std::vector<LIB_PIN*>& aSymbolPins );
virtual int doFindParam( const std::string& aParamName ) const;
@ -551,6 +555,7 @@ private:
protected:
std::vector<PARAM> m_params;
std::vector<PIN> m_pins;
const SIM_MODEL* m_baseModel;
std::unique_ptr<SIM_MODEL_SERIALIZER> m_serializer;
@ -558,7 +563,6 @@ private:
std::unique_ptr<SPICE_GENERATOR> m_spiceGenerator;
const TYPE m_type;
std::vector<PIN> m_pins;
bool m_isEnabled;
bool m_isStoredInValue;
};

View File

@ -24,7 +24,6 @@
#include <sim/kibis/kibis.h>
#include <sim/sim_model_kibis.h>
#include <sim/sim_library_kibis.h>
#include <common.h>
#include <fmt/core.h>
#include <wx/filename.h>
#include <kiway.h>
@ -297,17 +296,6 @@ SIM_MODEL_KIBIS::SIM_MODEL_KIBIS( TYPE aType, SIM_MODEL_KIBIS& aSource,
}
void SIM_MODEL_KIBIS::CreatePins( const std::vector<LIB_PIN*>& aSymbolPins )
{
SIM_MODEL::CreatePins( aSymbolPins );
// Reset the pins to Not Connected. Linear order is not as common, and reordering the pins is
// more effort in the GUI than assigning them from scratch.
for( int pinIndex = 0; pinIndex < GetPinCount(); ++pinIndex )
SetPinSymbolPinNumber( pinIndex, "" );
}
bool SIM_MODEL_KIBIS::ChangePin( const SIM_LIBRARY_KIBIS& aLib, std::string aPinNumber )
{
KIBIS_COMPONENT* kcomp = aLib.m_kibis.GetComponent( std::string( GetComponentName() ) );

View File

@ -93,9 +93,6 @@ public:
bool CanDifferential() const { return m_enableDiff; } ;
bool m_enableDiff;
protected:
void CreatePins( const std::vector<LIB_PIN*>& aSymbolPins ) override;
private:
bool requiresSpiceModelLine() const override { return true; }

View File

@ -122,10 +122,19 @@ SIM_MODEL_RAW_SPICE::SIM_MODEL_RAW_SPICE() :
}
void SIM_MODEL_RAW_SPICE::CreatePins( const std::vector<LIB_PIN*>& aSymbolPins )
void SIM_MODEL_RAW_SPICE::SetPinSymbolPinNumber( const std::string& aPinName,
const std::string& aSymbolPinNumber )
{
for( unsigned symbolPinIndex = 0; symbolPinIndex < aSymbolPins.size(); ++symbolPinIndex )
AddPin( { fmt::format( "{}", symbolPinIndex + 1 ), "" } );
for( PIN& pin : m_pins )
{
if( pin.name == aPinName )
{
pin.symbolPinNumber = aSymbolPinNumber;
return;
}
}
m_pins.push_back( { aPinName, aSymbolPinNumber } );
}

View File

@ -74,8 +74,8 @@ public:
return m_spiceCode;
}
protected:
void CreatePins( const std::vector<LIB_PIN*>& aSymbolPins ) override;
void SetPinSymbolPinNumber( const std::string& aPinName,
const std::string& aSymbolPinNumber ) override;
private:
static std::vector<PARAM::INFO> makeParamInfos();

View File

@ -25,7 +25,7 @@
// Include simulator headers after wxWidgets headers to avoid conflicts with Windows headers
// (especially on msys2 + wxWidgets 3.0.x)
#include <sim/sim_model_spice.h>
#include <sim/sim_model_raw_spice.h>
#include <sim/sim_model_spice_fallback.h>
#include <sim/spice_model_parser.h>
#include <sim/sim_library_spice.h>
@ -46,47 +46,49 @@ std::string SPICE_GENERATOR_SPICE::Preview( const SPICE_ITEM& aItem ) const
item.refName = "";
std::string itemLine = ItemLine( item );
if( spiceCode != "" )
spiceCode.append( "\n" );
if( spiceCode != "" && itemLine != "" )
spiceCode.append( "\n\n" );
spiceCode.append( itemLine );
return boost::trim_copy( spiceCode );
}
std::unique_ptr<SIM_MODEL> SIM_MODEL_SPICE::Create( const SIM_LIBRARY_SPICE& aLibrary,
const std::string& aSpiceCode )
std::unique_ptr<SIM_MODEL_SPICE> SIM_MODEL_SPICE::Create( const SIM_LIBRARY_SPICE& aLibrary,
const std::string& aSpiceCode )
{
SIM_MODEL::TYPE modelType = SPICE_MODEL_PARSER::ReadType( aLibrary, aSpiceCode );
SIM_MODEL* model = SIM_MODEL::Create( modelType ).release();
SIM_MODEL::TYPE modelType = SPICE_MODEL_PARSER::ReadType( aLibrary, aSpiceCode );
std::unique_ptr<SIM_MODEL> model = SIM_MODEL::Create( modelType );
if( SIM_MODEL_SPICE* spiceModel = dynamic_cast<SIM_MODEL_SPICE*>( model ) )
if( SIM_MODEL_SPICE* spiceModel = dynamic_cast<SIM_MODEL_SPICE*>( model.release() ) )
{
spiceModel->m_spiceModelParser->ReadModel( aLibrary, aSpiceCode );
return std::unique_ptr<SIM_MODEL_SPICE>( spiceModel );
}
else if( SIM_MODEL_RAW_SPICE* rawSpice = dynamic_cast<SIM_MODEL_RAW_SPICE*>( model ) )
{
rawSpice->SetSource( aSpiceCode );
return std::unique_ptr<SIM_MODEL_RAW_SPICE>( rawSpice );
try
{
spiceModel->m_spiceModelParser->ReadModel( aLibrary, aSpiceCode );
return std::unique_ptr<SIM_MODEL_SPICE>( spiceModel );
}
catch( const IO_ERROR& e )
{
// Fall back to raw spice code
}
}
delete model;
THROW_IO_ERROR( "Could not determine Spice model modelType" );
// Fall back to raw spice code
return std::make_unique<SIM_MODEL_SPICE_FALLBACK>( modelType, 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 ) )
SIM_MODEL( aType, std::move( aSpiceGenerator ) ),
m_spiceModelParser( std::make_unique<SPICE_MODEL_PARSER>( *this ) )
{
}
SIM_MODEL_SPICE::SIM_MODEL_SPICE( TYPE aType, std::unique_ptr<SPICE_GENERATOR> aSpiceGenerator,
std::unique_ptr<SPICE_MODEL_PARSER> aSpiceModelParser ) :
SIM_MODEL( aType, std::move( aSpiceGenerator ) ),
m_spiceModelParser( std::move( aSpiceModelParser ) )
SIM_MODEL( aType, std::move( aSpiceGenerator ) ),
m_spiceModelParser( std::move( aSpiceModelParser ) )
{
}

View File

@ -47,8 +47,8 @@ public:
friend class SPICE_GENERATOR_SPICE;
friend class SPICE_MODEL_PARSER;
static std::unique_ptr<SIM_MODEL> Create( const SIM_LIBRARY_SPICE& aLibrary,
const std::string& aSpiceCode );
static std::unique_ptr<SIM_MODEL_SPICE> Create( const SIM_LIBRARY_SPICE& aLibrary,
const std::string& aSpiceCode );
SIM_MODEL_SPICE( TYPE aType,
std::unique_ptr<SPICE_GENERATOR> aSpiceGenerator );

View File

@ -0,0 +1,50 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* 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/sim_model_spice_fallback.h>
#include <fmt/format.h>
SIM_MODEL_SPICE_FALLBACK::SIM_MODEL_SPICE_FALLBACK( TYPE aType, const std::string& aRawSpiceCode ) :
SIM_MODEL_SPICE( aType, std::make_unique<SPICE_GENERATOR_SPICE>( *this ) )
{
m_spiceCode = aRawSpiceCode;
}
void SIM_MODEL_SPICE_FALLBACK::SetPinSymbolPinNumber( const std::string& aPinName,
const std::string& aSymbolPinNumber )
{
for( PIN& pin : m_pins )
{
if( pin.name == aPinName )
{
pin.symbolPinNumber = aSymbolPinNumber;
return;
}
}
m_pins.push_back( { aPinName, aSymbolPinNumber } );
}

View File

@ -0,0 +1,39 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* 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 SIM_MODEL_SPICE_FALLBACK_H
#define SIM_MODEL_SPICE_FALLBACK_H
#include <sim/sim_model_spice.h>
class SIM_MODEL_SPICE_FALLBACK : public SIM_MODEL_SPICE
{
public:
SIM_MODEL_SPICE_FALLBACK( TYPE aType, const std::string& aRawSpiceCode = "" );
void SetPinSymbolPinNumber( const std::string& aPinName,
const std::string& aSymbolPinNumber ) override;
};
#endif // SIM_MODEL_SPICE_FALLBACK_H

View File

@ -136,8 +136,7 @@ void SPICE_MODEL_PARSER_SUBCKT::ReadModel( const SIM_LIBRARY_SPICE& aLibrary,
SIM_MODEL_SUBCKT::SIM_MODEL_SUBCKT() :
SIM_MODEL_SPICE( TYPE::SUBCKT,
std::make_unique<SPICE_GENERATOR_SUBCKT>( *this ),
SIM_MODEL_SPICE( TYPE::SUBCKT, std::make_unique<SPICE_GENERATOR_SUBCKT>( *this ),
std::make_unique<SPICE_MODEL_PARSER_SUBCKT>( *this ) )
{
}
@ -156,13 +155,3 @@ void SIM_MODEL_SUBCKT::SetBaseModel( const SIM_MODEL& aBaseModel )
AddParam( param.info );
}
void SIM_MODEL_SUBCKT::CreatePins( const std::vector<LIB_PIN*>& aSymbolPins )
{
SIM_MODEL::CreatePins( aSymbolPins );
// Reset the pins to Not Connected. Linear order is not as common, and reordering the pins is
// more effort in the GUI than assigning them from scratch.
for( int pinIndex = 0; pinIndex < GetPinCount(); ++pinIndex )
SetPinSymbolPinNumber( pinIndex, "" );
}

View File

@ -57,9 +57,6 @@ public:
void SetBaseModel( const SIM_MODEL& aBaseModel ) override;
protected:
void CreatePins( const std::vector<LIB_PIN*>& aSymbolPins ) override;
private:
bool requiresSpiceModelLine() const override { return true; }

View File

@ -137,7 +137,9 @@ std::string SPICE_GENERATOR::ItemPins( const SPICE_ITEM& aItem ) const
pin.symbolPinNumber );
if( it == aItem.pinNumbers.end() )
{
result.append( fmt::format( " NC-{}-{}", aItem.refName, ncCounter++ ) );
}
else
{
long symbolPinIndex = std::distance( aItem.pinNumbers.begin(), it );