Fix issue converting legacy SPICE models.

1) if a legacy model references a library then we need to see if said
   libraray exists and read model from it if so
2) legacy node ordering is by index, not pin name
3) we can't auto-generate a pin map when we don't know the pin names,
   so don't try
This commit is contained in:
Jeff Young 2022-12-14 13:00:25 +00:00
parent 19a5a3ae16
commit 50ccc4e6da
14 changed files with 127 additions and 73 deletions

View File

@ -51,8 +51,8 @@ DIALOG_SIM_MODEL<T_symbol, T_field>::DIALOG_SIM_MODEL( wxWindow* aParent, T_symb
: DIALOG_SIM_MODEL_BASE( aParent ),
m_symbol( aSymbol ),
m_fields( aFields ),
m_libraryModelsMgr( Prj() ),
m_builtinModelsMgr( Prj() ),
m_libraryModelsMgr( &Prj() ),
m_builtinModelsMgr( &Prj() ),
m_prevModel( nullptr ),
m_curModelType( SIM_MODEL::TYPE::NONE ),
m_scintillaTricks( nullptr ),

View File

@ -99,7 +99,7 @@ std::string NAME_GENERATOR::Generate( const std::string& aProposedName )
NETLIST_EXPORTER_SPICE::NETLIST_EXPORTER_SPICE( SCHEMATIC_IFACE* aSchematic ) :
NETLIST_EXPORTER_BASE( aSchematic ),
m_libMgr( aSchematic->Prj() )
m_libMgr( &aSchematic->Prj() )
{
}

View File

@ -1856,6 +1856,6 @@ void SCH_SCREEN::MigrateSimModels()
for( SCH_ITEM* item : Items().OfType( SCH_SYMBOL_T ) )
{
SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
SIM_MODEL::MigrateSimModel<SCH_SYMBOL, SCH_FIELD>( *symbol );
SIM_MODEL::MigrateSimModel<SCH_SYMBOL, SCH_FIELD>( *symbol, &Schematic()->Prj() );
}
}

View File

@ -34,7 +34,8 @@
#include <sim/sim_model.h>
#include <sim/sim_model_ideal.h>
SIM_LIB_MGR::SIM_LIB_MGR( const PROJECT& aPrj ) : m_project( aPrj )
SIM_LIB_MGR::SIM_LIB_MGR( const PROJECT* aPrj ) :
m_project( aPrj )
{
}
@ -46,15 +47,15 @@ void SIM_LIB_MGR::Clear()
}
wxString SIM_LIB_MGR::ResolveLibraryPath( const wxString& aLibraryPath, const PROJECT& aProject )
wxString SIM_LIB_MGR::ResolveLibraryPath( const wxString& aLibraryPath, const PROJECT* aProject )
{
wxString expandedPath = ExpandEnvVarSubstitutions( aLibraryPath, &aProject );
wxString expandedPath = ExpandEnvVarSubstitutions( aLibraryPath, aProject );
wxFileName fn( expandedPath );
if( fn.IsAbsolute() )
return fn.GetFullPath();
wxFileName projectFn( aProject.AbsolutePath( expandedPath ) );
wxFileName projectFn( aProject ? aProject->AbsolutePath( expandedPath ) : expandedPath );
if( projectFn.Exists() )
return projectFn.GetFullPath();

View File

@ -40,7 +40,7 @@ class SCH_SYMBOL;
class SIM_LIB_MGR
{
public:
SIM_LIB_MGR( const PROJECT& aPrj );
SIM_LIB_MGR( const PROJECT* aPrj );
virtual ~SIM_LIB_MGR() = default;
void Clear();
@ -71,10 +71,10 @@ public:
std::map<wxString, std::reference_wrapper<const SIM_LIBRARY>> GetLibraries() const;
std::vector<std::reference_wrapper<SIM_MODEL>> GetModels() const;
static wxString ResolveLibraryPath( const wxString& aLibraryPath, const PROJECT& aProject );
static wxString ResolveLibraryPath( const wxString& aLibraryPath, const PROJECT* aProject );
private:
const PROJECT& m_project;
const PROJECT* m_project;
std::map<wxString, std::unique_ptr<SIM_LIBRARY>> m_libraries;
std::vector<std::unique_ptr<SIM_MODEL>> m_models;
};

View File

@ -41,7 +41,7 @@
#include <sim/sim_model_switch.h>
#include <sim/sim_model_tline.h>
#include <sim/sim_model_xspice.h>
#include <sim/sim_lib_mgr.h>
#include <sim/sim_library_kibis.h>
#include <boost/algorithm/string/case_conv.hpp>
@ -665,22 +665,39 @@ void SIM_MODEL::SetPinSymbolPinNumber( int aPinIndex, const std::string& aSymbol
void SIM_MODEL::SetPinSymbolPinNumber( const std::string& aPinName,
const std::string& aSymbolPinNumber )
{
int aPinIndex = -1;
const std::vector<std::reference_wrapper<const PIN>> pins = GetPins();
auto it = std::find_if( pins.begin(), pins.end(),
[aPinName]( const PIN& aPin )
{
return aPin.name == aPinName;
} );
if( it == pins.end() )
for( int ii = 0; ii < (int) pins.size(); ++ii )
{
THROW_IO_ERROR( wxString::Format( _( "Could not find a pin named '%s' in simulation model of type '%s'" ),
if( pins.at( ii ).get().name == aPinName )
{
aPinIndex = ii;
break;
}
}
if( aPinIndex < 0 )
{
// If aPinName wasn't in fact a name, see if it's a raw (1-based) index. This is
// required for legacy files which didn't use pin names.
aPinIndex = (int) strtol( aPinName.c_str(), nullptr, 10 );
// Convert to 0-based. (Note that this will also convert the error state to -1, which
// means we don't have to check for it separately.)
aPinIndex--;
}
if( aPinIndex < 0 )
{
THROW_IO_ERROR( wxString::Format( _( "Could not find a pin named '%s' in "
"simulation model of type '%s'" ),
aPinName,
GetTypeInfo().fieldValue ) );
}
SetPinSymbolPinNumber( static_cast<int>( it - pins.begin() ), aSymbolPinNumber );
SetPinSymbolPinNumber( aPinIndex, aSymbolPinNumber );
}
@ -721,7 +738,7 @@ std::vector<std::reference_wrapper<const SIM_MODEL::PARAM>> SIM_MODEL::GetParams
}
const SIM_MODEL::PARAM& SIM_MODEL::GetUnderlyingParam( unsigned aParamIndex ) const
const SIM_MODEL::PARAM& SIM_MODEL::GetParamOverride( unsigned aParamIndex ) const
{
return m_params.at( aParamIndex );
}
@ -762,7 +779,8 @@ void SIM_MODEL::SetParamValue( const std::string& aParamName, const SIM_VALUE& a
if( it == params.end() )
{
THROW_IO_ERROR( wxString::Format( _( "Could not find a parameter named '%s' in simulation model of type '%s'" ),
THROW_IO_ERROR( wxString::Format( _( "Could not find a parameter named '%s' in "
"simulation model of type '%s'" ),
aParamName,
GetTypeInfo().fieldValue ) );
}
@ -778,7 +796,8 @@ void SIM_MODEL::SetParamValue( const std::string& aParamName, const std::string&
if( !param )
{
THROW_IO_ERROR( wxString::Format( _( "Could not find a parameter named '%s' in simulation model of type '%s'" ),
THROW_IO_ERROR( wxString::Format( _( "Could not find a parameter named '%s' in "
"simulation model of type '%s'" ),
aParamName,
GetTypeInfo().fieldValue ) );
}
@ -1055,7 +1074,7 @@ std::pair<wxString, wxString> SIM_MODEL::InferSimModel( const wxString& aPrefix,
template <typename T_symbol, typename T_field>
void SIM_MODEL::MigrateSimModel( T_symbol& aSymbol )
void SIM_MODEL::MigrateSimModel( T_symbol& aSymbol, const PROJECT* aProject )
{
if( aSymbol.FindField( SIM_MODEL::DEVICE_TYPE_FIELD )
|| aSymbol.FindField( SIM_MODEL::TYPE_FIELD )
@ -1085,6 +1104,9 @@ void SIM_MODEL::MigrateSimModel( T_symbol& aSymbol )
wxString spiceModel;
wxString spiceLib;
wxString pinMap;
wxString spiceParams;
bool modelFromValueField = false;
bool modelFromLib = false;
if( aSymbol.FindField( wxT( "Spice_Primitive" ) )
|| aSymbol.FindField( wxT( "Spice_Node_Sequence" ) )
@ -1130,7 +1152,7 @@ void SIM_MODEL::MigrateSimModel( T_symbol& aSymbol )
else
{
spiceModel = getSIValue( valueField );
valueField->SetText( wxT( "${SIM.PARAMS}" ) );
modelFromValueField = true;
}
if( T_field* netlistEnabledField = aSymbol.FindField( wxT( "Spice_Netlist_Enabled" ) ) )
@ -1149,12 +1171,13 @@ void SIM_MODEL::MigrateSimModel( T_symbol& aSymbol )
{
spiceLib = libFileField->GetText();
aSymbol.RemoveField( libFileField );
modelFromLib = true;
}
}
else if( prefix == wxT( "V" ) || prefix == wxT( "I" ) )
{
spiceModel = getSIValue( valueField );
valueField->SetText( wxT( "${SIM.PARAMS}" ) );
modelFromValueField = true;
}
else
{
@ -1205,60 +1228,87 @@ void SIM_MODEL::MigrateSimModel( T_symbol& aSymbol )
legacyPins->SetText( pins );
}
if( T_field* legacyPins = aSymbol.FindField( wxT( "Sim_Params" ) ) )
if( T_field* legacyParams = aSymbol.FindField( wxT( "Sim_Params" ) ) )
{
legacyPins->SetName( SIM_MODEL::PARAMS_FIELD );
legacyParams->SetName( SIM_MODEL::PARAMS_FIELD );
}
return;
}
// Insert a plaintext model as a substitute.
T_field deviceTypeField( &aSymbol, -1, SIM_MODEL::DEVICE_TYPE_FIELD );
deviceTypeField.SetText( SIM_MODEL::DeviceInfo( SIM_MODEL::DEVICE_T::SPICE ).fieldValue );
aSymbol.AddField( deviceTypeField );
T_field paramsField( &aSymbol, -1, SIM_MODEL::PARAMS_FIELD );
if( spiceType.IsEmpty() && spiceLib.IsEmpty() )
if( modelFromLib )
{
paramsField.SetText( spiceModel );
SIM_LIB_MGR libMgr( aProject );
try
{
std::vector<T_field> emptyFields;
SIM_LIBRARY::MODEL model = libMgr.CreateModel( spiceLib, spiceModel.ToStdString(),
emptyFields, aSymbol.GetPinCount() );
spiceParams = wxString( model.model.GetBaseModel()->Serde().GenerateParams() );
}
catch( ... )
{
// Fall back to raw spice model
modelFromLib = false;
}
}
if( modelFromLib )
{
T_field libraryField( &aSymbol, -1, SIM_MODEL::LIBRARY_FIELD );
libraryField.SetText( spiceLib );
aSymbol.AddField( libraryField );
T_field nameField( &aSymbol, -1, SIM_MODEL::NAME_FIELD );
nameField.SetText( spiceModel );
aSymbol.AddField( nameField );
T_field paramsField( &aSymbol, -1, SIM_MODEL::PARAMS_FIELD );
paramsField.SetText( spiceParams );
aSymbol.AddField( paramsField );
if( modelFromValueField )
valueField->SetText( wxT( "${SIM.NAME}" ) );
}
else
{
paramsField.SetText( wxString::Format( "type=\"%s\" model=\"%s\" lib=\"%s\"",
spiceType, spiceModel, spiceLib ) );
// Insert a raw spice model as a substitute.
if( spiceType.IsEmpty() && spiceLib.IsEmpty() )
{
spiceParams = spiceModel;
}
else
{
spiceParams.Printf( wxT( "type=\"%s\" model=\"%s\" lib=\"%s\"" ),
spiceType, spiceModel, spiceLib );
}
T_field deviceTypeField( &aSymbol, -1, SIM_MODEL::DEVICE_TYPE_FIELD );
deviceTypeField.SetText( SIM_MODEL::DeviceInfo( SIM_MODEL::DEVICE_T::SPICE ).fieldValue );
aSymbol.AddField( deviceTypeField );
T_field paramsField( &aSymbol, -1, SIM_MODEL::PARAMS_FIELD );
paramsField.SetText( spiceParams );
aSymbol.AddField( paramsField );
if( modelFromValueField )
valueField->SetText( wxT( "${SIM.PARAMS}" ) );
}
aSymbol.AddField( paramsField );
// Legacy models by default get linear pin mapping.
if( pinMap != "" )
if( !pinMap.IsEmpty() )
{
T_field pinsField( &aSymbol, -1, SIM_MODEL::PINS_FIELD );
pinsField.SetText( pinMap );
aSymbol.AddField( pinsField );
}
else
{
wxString pins;
for( unsigned ii = 0; ii < aSymbol.GetPinCount(); ++ii )
{
if( ii > 0 )
pins.Append( wxS( " " ) );
pins.Append( wxString::Format( wxT( "%u=%u" ), ii + 1, ii + 1 ) );
}
T_field pinsField( &aSymbol, aSymbol.GetFieldCount(), SIM_MODEL::PINS_FIELD );
pinsField.SetText( pins );
aSymbol.AddField( pinsField );
}
}
template void SIM_MODEL::MigrateSimModel<SCH_SYMBOL, SCH_FIELD>( SCH_SYMBOL& aSymbol );
template void SIM_MODEL::MigrateSimModel<LIB_SYMBOL, LIB_FIELD>( LIB_SYMBOL& aSymbol );
template void SIM_MODEL::MigrateSimModel<SCH_SYMBOL, SCH_FIELD>( SCH_SYMBOL& aSymbol,
const PROJECT* aProject );
template void SIM_MODEL::MigrateSimModel<LIB_SYMBOL, LIB_FIELD>( LIB_SYMBOL& aSymbol,
const PROJECT* aProject );

View File

@ -41,6 +41,7 @@
class SIM_LIBRARY;
class SPICE_GENERATOR;
class SIM_SERDE;
class PROJECT;
class SIM_MODEL
@ -60,6 +61,8 @@ public:
static constexpr auto PINS_FIELD = "Sim.Pins";
static constexpr auto PARAMS_FIELD = "Sim.Params";
static constexpr auto ENABLE_FIELD = "Sim.Enable";
static constexpr auto LIBRARY_FIELD = "Sim.Library";
static constexpr auto NAME_FIELD = "Sim.Name";
// There's a trailing '_' because `DEVICE_TYPE` collides with something in Windows headers.
@ -490,7 +493,7 @@ public:
std::vector<std::reference_wrapper<const PARAM>> GetParams() const;
const PARAM& GetUnderlyingParam( unsigned aParamIndex ) const; // Return the actual parameter.
const PARAM& GetParamOverride( unsigned aParamIndex ) const; // Return the actual parameter.
const PARAM& GetBaseParam( unsigned aParamIndex ) const; // Always return base parameter if it exists.
@ -527,7 +530,7 @@ public:
SIM_VALUE_GRAMMAR::NOTATION aNotation );
template <class T_symbol, class T_field>
static void MigrateSimModel( T_symbol& aSymbol );
static void MigrateSimModel( T_symbol& aSymbol, const PROJECT* aProject );
protected:
static std::unique_ptr<SIM_MODEL> Create( TYPE aType );

View File

@ -68,7 +68,7 @@ std::string SPICE_GENERATOR_KIBIS::IbisDevice( const SPICE_ITEM& aItem, const PR
std::string ibisModelName = SIM_MODEL::GetFieldValue( &aItem.fields, SIM_LIBRARY_KIBIS::MODEL_FIELD );
bool diffMode = SIM_MODEL::GetFieldValue( &aItem.fields, SIM_LIBRARY_KIBIS::DIFF_FIELD ) == "1";
wxString path = SIM_LIB_MGR::ResolveLibraryPath( ibisLibFilename, aProject );
wxString path = SIM_LIB_MGR::ResolveLibraryPath( ibisLibFilename, &aProject );
KIBIS kibis( std::string( path.c_str() ) );
kibis.m_cacheDir = std::string( aCacheDir.c_str() );

View File

@ -607,7 +607,7 @@ void SIM_PLOT_FRAME::UpdateTunerValue( SCH_SYMBOL* aSymbol, const wxString& aVal
{
if( item == aSymbol )
{
SIM_LIB_MGR mgr( Prj() );
SIM_LIB_MGR mgr( &Prj() );
SIM_MODEL& model = mgr.CreateModel( &m_schematicFrame->GetCurrentSheet(),
*aSymbol ).model;

View File

@ -66,7 +66,7 @@ std::string SIM_SERDE::GenerateType() const
std::string SIM_SERDE::GenerateValue() const
{
const SIM_MODEL::PARAM& param = m_model.GetUnderlyingParam( 0 );
const SIM_MODEL::PARAM& param = m_model.GetParamOverride( 0 );
std::string result = param.value->ToString();
if( result == "" )
@ -86,7 +86,7 @@ std::string SIM_SERDE::GenerateParams() const
if( i == 0 && m_model.IsStoredInValue() )
continue;
const SIM_MODEL::PARAM& param = m_model.GetUnderlyingParam( i );
const SIM_MODEL::PARAM& param = m_model.GetParamOverride( i );
if( param.value->ToString() == ""
&& !( i == 0 && m_model.HasPrimaryValue() && !m_model.IsStoredInValue() ) )

View File

@ -406,7 +406,7 @@ LIB_SYMBOL* SYMBOL_LIB_TABLE::LoadSymbol( const wxString& aNickname, const wxStr
id.SetLibNickname( row->GetNickName() );
symbol->SetLibId( id );
SIM_MODEL::MigrateSimModel<LIB_SYMBOL, LIB_FIELD>( *symbol );
SIM_MODEL::MigrateSimModel<LIB_SYMBOL, LIB_FIELD>( *symbol, nullptr );
}
return symbol;

View File

@ -169,7 +169,7 @@ LIB_SYMBOL* SYMBOL_LIB::FindSymbol( const wxString& aName ) const
if( !symbol->GetLib() )
symbol->SetLib( const_cast<SYMBOL_LIB*>( this ) );
SIM_MODEL::MigrateSimModel<LIB_SYMBOL, LIB_FIELD>( *symbol );
SIM_MODEL::MigrateSimModel<LIB_SYMBOL, LIB_FIELD>( *symbol, nullptr );
}
return symbol;

View File

@ -876,7 +876,7 @@ int SCH_EDITOR_CONTROL::SimProbe( const TOOL_EVENT& aEvent )
SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item->GetParent() );
std::vector<LIB_PIN*> pins = symbol->GetLibPins();
SIM_LIB_MGR mgr( m_frame->Prj() );
SIM_LIB_MGR mgr( &m_frame->Prj() );
SIM_MODEL& model = mgr.CreateModel( &sheet, *symbol ).model;
SPICE_ITEM spiceItem;

View File

@ -79,7 +79,7 @@ public:
{
BOOST_TEST_CONTEXT( "Param name: " << aModel.GetParam( i ).info.name )
{
BOOST_CHECK_EQUAL( aModel.GetUnderlyingParam( i ).value->ToString(), "" );
BOOST_CHECK_EQUAL( aModel.GetParamOverride( i ).value->ToString(), "" );
}
}
}