Add XSPICE node/port modifiers: %d [ ~ ] etc.
This commit is contained in:
parent
64812d3fe6
commit
25abba7432
|
@ -36,6 +36,7 @@
|
|||
#include <sim/sim_library.h>
|
||||
#include <sim/sim_library_kibis.h>
|
||||
#include <sim/sim_model_kibis.h>
|
||||
#include <sim/sim_xspice_parser.h>
|
||||
#include <sch_screen.h>
|
||||
#include <sch_text.h>
|
||||
#include <sch_textbox.h>
|
||||
|
@ -227,7 +228,7 @@ bool NETLIST_EXPORTER_SPICE::ReadSchematicAndLibraries( unsigned aNetlistOptions
|
|||
readModel( sheet, *symbol, spiceItem, aReporter );
|
||||
readPinNumbers( *symbol, spiceItem, pins );
|
||||
readPinNetNames( *symbol, spiceItem, pins, ncCounter );
|
||||
|
||||
readNodePattern( spiceItem );
|
||||
// TODO: transmission line handling?
|
||||
|
||||
m_items.push_back( std::move( spiceItem ) );
|
||||
|
@ -506,7 +507,76 @@ void NETLIST_EXPORTER_SPICE::readPinNetNames( SCH_SYMBOL& aSymbol, SPICE_ITEM& a
|
|||
m_nets.insert( netName );
|
||||
}
|
||||
}
|
||||
void NETLIST_EXPORTER_SPICE::getNodePattern( SPICE_ITEM& aItem,
|
||||
std::vector<std::string>& aModifiers )
|
||||
{
|
||||
std::string input = SIM_MODEL::GetFieldValue( &aItem.fields, SIM_NODES_FORMAT_FIELD, true );
|
||||
|
||||
if( input == "" )
|
||||
return;
|
||||
|
||||
tao::pegtl::string_input<> in( input, "Sim.NodesFormat field" );
|
||||
std::unique_ptr<tao::pegtl::parse_tree::node> root;
|
||||
std::string singleNodeModifier;
|
||||
|
||||
try
|
||||
{
|
||||
root = tao::pegtl::parse_tree::parse<SIM_XSPICE_PARSER_GRAMMAR::nodeSequenceGrammar,
|
||||
SIM_XSPICE_PARSER_GRAMMAR::spiceUnitSelector,
|
||||
tao::pegtl::nothing,
|
||||
SIM_XSPICE_PARSER_GRAMMAR::control>( in );
|
||||
for( const auto& node : root->children )
|
||||
{
|
||||
if( node->is_type<SIM_XSPICE_PARSER_GRAMMAR::squareBracketC>() )
|
||||
{
|
||||
//we want ']' to close previous ?
|
||||
aModifiers.back().append( node->string() );
|
||||
}
|
||||
else
|
||||
{ //rest goes to the new singleNodeModifier
|
||||
singleNodeModifier.append( node->string() );
|
||||
}
|
||||
|
||||
if( node->is_type<SIM_XSPICE_PARSER_GRAMMAR::nodeName>() )
|
||||
{
|
||||
aModifiers.push_back( singleNodeModifier );
|
||||
singleNodeModifier.erase( singleNodeModifier.begin(), singleNodeModifier.end() );
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( const tao::pegtl::parse_error& e )
|
||||
{
|
||||
THROW_IO_ERROR( wxString::Format( _( "Error in parsing model '%s', error: '%s'" ),
|
||||
aItem.refName, e.what() ) );
|
||||
}
|
||||
}
|
||||
void NETLIST_EXPORTER_SPICE::readNodePattern( SPICE_ITEM& aItem )
|
||||
{
|
||||
std::vector<std::string> xspicePattern;
|
||||
NETLIST_EXPORTER_SPICE::getNodePattern( aItem, xspicePattern );
|
||||
|
||||
if( xspicePattern.empty() )
|
||||
return;
|
||||
|
||||
if( xspicePattern.size() != aItem.pinNetNames.size() )
|
||||
{
|
||||
THROW_IO_ERROR( wxString::Format( _( "Error in parsing model '%s', wrong number of nodes "
|
||||
"'?' in Sim.NodesFormat compared to connections" ),
|
||||
aItem.refName ) );
|
||||
return;
|
||||
}
|
||||
|
||||
auto itNetNames = aItem.pinNetNames.begin();
|
||||
|
||||
for( std::string& pattern : xspicePattern )
|
||||
{
|
||||
// ngspice does not care about aditional spaces, and we make sure that "%d?" is separated
|
||||
const std::string netName = " " + *itNetNames + " ";
|
||||
pattern.replace( pattern.find( "?" ), 1, netName );
|
||||
*itNetNames = pattern;
|
||||
++itNetNames;
|
||||
}
|
||||
}
|
||||
|
||||
void NETLIST_EXPORTER_SPICE::writeInclude( OUTPUTFORMATTER& aFormatter, unsigned aNetlistOptions,
|
||||
const wxString& aPath )
|
||||
|
|
|
@ -152,6 +152,8 @@ private:
|
|||
const std::vector<PIN_INFO>& aPins );
|
||||
void readPinNetNames( SCH_SYMBOL& aSymbol, SPICE_ITEM& aItem,
|
||||
const std::vector<PIN_INFO>& aPins, int& aNcCounter );
|
||||
void getNodePattern( SPICE_ITEM& aItem, std::vector<std::string>& aModifiers );
|
||||
void readNodePattern( SPICE_ITEM& aItem );
|
||||
|
||||
void writeInclude( OUTPUTFORMATTER& aFormatter, unsigned aNetlistOptions,
|
||||
const wxString& aPath );
|
||||
|
|
|
@ -1486,6 +1486,80 @@ template bool SIM_MODEL::InferSimModel<LIB_SYMBOL, LIB_FIELD>( LIB_SYMBOL& aSymb
|
|||
template <typename T_symbol, typename T_field>
|
||||
void SIM_MODEL::MigrateSimModel( T_symbol& aSymbol, const PROJECT* aProject )
|
||||
{
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
T_field* existing_deviceField = aSymbol.FindField( SIM_DEVICE_FIELD );
|
||||
T_field* existing_deviceSubtypeField = aSymbol.FindField( SIM_DEVICE_SUBTYPE_FIELD );
|
||||
T_field* existing_pinsField = aSymbol.FindField( SIM_PINS_FIELD );
|
||||
T_field* existing_paramsField = aSymbol.FindField( SIM_PARAMS_FIELD );
|
||||
|
||||
wxString existing_deviceSubtype;
|
||||
|
||||
if( existing_deviceSubtypeField )
|
||||
existing_deviceSubtype = existing_deviceSubtypeField->GetShownText( false ).Upper();
|
||||
|
||||
if( existing_deviceField
|
||||
|| existing_deviceSubtypeField
|
||||
|| existing_pinsField
|
||||
|| existing_paramsField )
|
||||
{
|
||||
// Has a current (V7+) model field.
|
||||
|
||||
// Up until 7.0RC2 we used '+' and '-' for potentiometer pins, which doesn't match
|
||||
// SPICE. Here we remap them to 'r0' and 'r1'.
|
||||
if( existing_deviceSubtype == wxS( "POT" ) )
|
||||
{
|
||||
if( existing_pinsField )
|
||||
{
|
||||
wxString pinMap = existing_pinsField->GetText();
|
||||
pinMap.Replace( wxS( "=+" ), wxS( "=r1" ) );
|
||||
pinMap.Replace( wxS( "=-" ), wxS( "=r0" ) );
|
||||
existing_pinsField->SetText( pinMap );
|
||||
}
|
||||
}
|
||||
|
||||
// Up until 8.0RC1 random voltage/current sources were a bit of a mess.
|
||||
if( existing_deviceSubtype.StartsWith( wxS( "RAND" ) ) )
|
||||
{
|
||||
// Re-fetch value without resolving references. If it's an indirect value then we
|
||||
// can't migrate it.
|
||||
existing_deviceSubtype = existing_deviceSubtypeField->GetText().Upper();
|
||||
|
||||
if( existing_deviceSubtype.Replace( wxS( "NORMAL" ), wxS( "GAUSSIAN" ) ) )
|
||||
existing_deviceSubtypeField->SetText( existing_deviceSubtype );
|
||||
|
||||
if( existing_paramsField )
|
||||
{
|
||||
wxString params = existing_paramsField->GetText().Lower();
|
||||
size_t count = 0;
|
||||
|
||||
// We used to support 'min' and 'max' instead of 'range' and 'offset', but we
|
||||
// wrote all 4 to the netlist which would cause ngspice to barf, so no one has
|
||||
// working documents with min and max specified. Just delete them if they're
|
||||
// uninitialized.
|
||||
count += params.Replace( wxS( "min=0 " ), wxEmptyString );
|
||||
count += params.Replace( wxS( "max=0 " ), wxEmptyString );
|
||||
|
||||
// We used to use 'dt', but the correct ngspice name is 'ts'.
|
||||
count += params.Replace( wxS( "dt=" ), wxS( "ts=" ) );
|
||||
|
||||
if( count )
|
||||
existing_paramsField->SetText( params );
|
||||
}
|
||||
}
|
||||
|
||||
// Up until 8.0.1 we treated a mutual inductance statement as a type of inductor --
|
||||
// which is confusing because it doesn't represent a device at all.
|
||||
if( existing_deviceSubtype == wxS( "MUTUAL" ) )
|
||||
{
|
||||
aSymbol.RemoveField( existing_deviceSubtypeField );
|
||||
existing_deviceField->SetText( "K" );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
>>>>>>> a529ae9e3d (Mutual Inductor isn't an inductor.)
|
||||
class FIELD_INFO
|
||||
{
|
||||
public:
|
||||
|
@ -1657,9 +1731,12 @@ void SIM_MODEL::MigrateSimModel( T_symbol& aSymbol, const PROJECT* aProject )
|
|||
} );
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
sourcePinsSorted = true;
|
||||
};
|
||||
|
||||
=======
|
||||
>>>>>>> a529ae9e3d (Mutual Inductor isn't an inductor.)
|
||||
FIELD_INFO deviceInfo;
|
||||
FIELD_INFO modelInfo;
|
||||
FIELD_INFO deviceSubtypeInfo;
|
||||
|
@ -1811,8 +1888,11 @@ void SIM_MODEL::MigrateSimModel( T_symbol& aSymbol, const PROJECT* aProject )
|
|||
model = model.BeforeFirst( ' ', &modelLineParams );
|
||||
modelInfo.m_Text = model;
|
||||
|
||||
<<<<<<< HEAD
|
||||
lazySortSourcePins();
|
||||
|
||||
=======
|
||||
>>>>>>> a529ae9e3d (Mutual Inductor isn't an inductor.)
|
||||
SIM_LIBRARY::MODEL simModel = libMgr.CreateModel( lib, model.ToStdString(),
|
||||
emptyFields, sourcePins, reporter );
|
||||
|
||||
|
|
|
@ -49,19 +49,20 @@ class PROJECT;
|
|||
#define SIM_REFERENCE_FIELD wxT( "Reference" )
|
||||
#define SIM_VALUE_FIELD wxT( "Value" )
|
||||
|
||||
#define SIM_DEVICE_FIELD wxT( "Sim.Device" )
|
||||
#define SIM_DEVICE_SUBTYPE_FIELD wxT( "Sim.Type" )
|
||||
#define SIM_PINS_FIELD wxT( "Sim.Pins" )
|
||||
#define SIM_PARAMS_FIELD wxT( "Sim.Params" )
|
||||
#define SIM_LIBRARY_FIELD wxT( "Sim.Library" )
|
||||
#define SIM_NAME_FIELD wxT( "Sim.Name" )
|
||||
#define SIM_DEVICE_FIELD wxT( "Sim.Device" )
|
||||
#define SIM_DEVICE_SUBTYPE_FIELD wxT( "Sim.Type" )
|
||||
#define SIM_PINS_FIELD wxT( "Sim.Pins" )
|
||||
#define SIM_PARAMS_FIELD wxT( "Sim.Params" )
|
||||
#define SIM_LIBRARY_FIELD wxT( "Sim.Library" )
|
||||
#define SIM_NAME_FIELD wxT( "Sim.Name" )
|
||||
#define SIM_NODES_FORMAT_FIELD wxT( "Sim.NodesFormat" )
|
||||
|
||||
#define SIM_LEGACY_ENABLE_FIELD_V7 wxT( "Sim.Enable" )
|
||||
#define SIM_LEGACY_PRIMITIVE_FIELD wxS( "Spice_Primitive" )
|
||||
#define SIM_LEGACY_MODEL_FIELD wxS( "Spice_Model" )
|
||||
#define SIM_LEGACY_PINS_FIELD wxS( "Spice_Node_Sequence" )
|
||||
#define SIM_LEGACY_ENABLE_FIELD wxS( "Spice_Netlist_Enabled" )
|
||||
#define SIM_LEGACY_LIBRARY_FIELD wxS( "Spice_Lib_File" )
|
||||
#define SIM_LEGACY_MODEL_FIELD wxS( "Spice_Model" )
|
||||
#define SIM_LEGACY_PINS_FIELD wxS( "Spice_Node_Sequence" )
|
||||
#define SIM_LEGACY_ENABLE_FIELD wxS( "Spice_Netlist_Enabled" )
|
||||
#define SIM_LEGACY_LIBRARY_FIELD wxS( "Spice_Lib_File" )
|
||||
|
||||
|
||||
class SIM_MODEL
|
||||
|
|
|
@ -0,0 +1,223 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2024 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 2
|
||||
* 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:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
#ifndef SIM_XSPCIE_PARSER_H_
|
||||
#define SIM_XSPCIE_PARSER_H_
|
||||
|
||||
#include "sim/sim_value.h"
|
||||
#include <tao/pegtl.hpp>
|
||||
#include <tao/pegtl/eol.hpp>
|
||||
#include <tao/pegtl/rules.hpp>
|
||||
|
||||
namespace SIM_XSPICE_PARSER_GRAMMAR
|
||||
{
|
||||
using namespace SIM_VALUE_GRAMMAR;
|
||||
|
||||
/**
|
||||
* Notes:
|
||||
* spaces are allowed everywhere in any number
|
||||
* ~ can only be before ?
|
||||
* ~~ is not allowed
|
||||
* [] can enclose as many '?' with modifiers as we want
|
||||
* nested vectors are not allowed [ [?] ? ]
|
||||
* () and spaces are allowed and treated as separators
|
||||
* we want at least one node '?'
|
||||
**/
|
||||
struct nodeName : one<'?'>
|
||||
{
|
||||
};
|
||||
struct squareBracketO : one<'['>
|
||||
{
|
||||
};
|
||||
struct squareBracketC : one<']'>
|
||||
{
|
||||
};
|
||||
struct invertionDigital : one<'~'>
|
||||
{
|
||||
};
|
||||
struct sep : opt<plus<sor<space,
|
||||
one<'('>,
|
||||
one<')'>>>>
|
||||
{
|
||||
};
|
||||
|
||||
struct invertionSeparated : seq<sep, invertionDigital, sep>
|
||||
{
|
||||
};
|
||||
struct portInversionDouble : if_must<invertionSeparated, not_at<invertionSeparated>>
|
||||
{
|
||||
};
|
||||
struct portInversionVector : if_must<portInversionDouble, not_at<squareBracketO>>
|
||||
{
|
||||
};
|
||||
struct portInversion : if_must<portInversionVector, not_at<one<'%'>>>
|
||||
{
|
||||
};
|
||||
|
||||
struct portModifiersSingleNames : sor<istring<'v', 'n', 'a', 'm'>,
|
||||
string<'v'>,
|
||||
istring<'i'>,
|
||||
istring<'g'>,
|
||||
istring<'h'>,
|
||||
istring<'d'>>
|
||||
{
|
||||
};
|
||||
struct portModifierDifferentialNames
|
||||
: sor<istring<'v', 'd'>,
|
||||
istring<'i', 'd'>,
|
||||
istring<'g', 'd'>,
|
||||
istring<'h', 'd'>>
|
||||
{
|
||||
};
|
||||
|
||||
struct portModifierDigital : seq<one<'%'>, sep, istring<'d'>>
|
||||
{
|
||||
};
|
||||
struct portModifiersSingle : seq<one<'%'>, sep, portModifiersSingleNames>
|
||||
{
|
||||
};
|
||||
struct portModifiersDifferential : seq<one<'%'>, sep, portModifierDifferentialNames>
|
||||
{
|
||||
};
|
||||
struct validPortTypes
|
||||
: until<if_must<one<'%'>, sep,
|
||||
sor<portModifierDifferentialNames,
|
||||
portModifiersSingleNames,
|
||||
istring<'d'>>>>
|
||||
{
|
||||
};
|
||||
|
||||
struct nodeNameSeparated : seq<sep, nodeName, sep>
|
||||
{
|
||||
};
|
||||
struct nodeDigital
|
||||
: seq<sep, opt<portModifierDigital>, opt<portInversion>, rep_min<1, nodeNameSeparated>>
|
||||
{
|
||||
};
|
||||
struct nodeSingle : seq<sep, if_must<portModifiersSingle, sep, rep_min<1, nodeNameSeparated>>>
|
||||
{
|
||||
};
|
||||
struct nodeDifferential
|
||||
: seq<sep, if_must<portModifiersDifferential, sep, rep_min<2, nodeNameSeparated>>>
|
||||
{
|
||||
};
|
||||
struct nodeSequence : sor<nodeDifferential,
|
||||
nodeDigital,
|
||||
nodeSingle>
|
||||
{
|
||||
};
|
||||
|
||||
struct vectorPattern : if_must<squareBracketO, until<squareBracketC, nodeSequence>>
|
||||
{
|
||||
};
|
||||
struct vectorExpr : seq<opt<portModifierDigital>, opt<portModifiersDifferential>,
|
||||
opt<portModifiersSingle>, sep, vectorPattern>
|
||||
{
|
||||
};
|
||||
struct nodeSequenceGrammar : must<at<rep_min<0, validPortTypes>>, sep,
|
||||
plus<sor<vectorExpr,
|
||||
nodeSequence>>,
|
||||
not_at<squareBracketC>>
|
||||
{
|
||||
};
|
||||
|
||||
template <typename>
|
||||
inline constexpr const char* errorMessage = nullptr;
|
||||
template <>
|
||||
inline constexpr auto errorMessage<plus<sor<vectorExpr, nodeSequence>>> =
|
||||
"Expected at least one '?', are all modifiers and vectors correct?";
|
||||
template <>
|
||||
inline constexpr auto errorMessage<until<squareBracketC, nodeSequence>> =
|
||||
"Vectors [ must be closed ] and not nested.";
|
||||
template <>
|
||||
inline constexpr auto
|
||||
errorMessage<sor<portModifierDifferentialNames, portModifiersSingleNames, istring<'d'>>> =
|
||||
"Port type is invalid. '%%' needs to be followed by a valid name.";
|
||||
template <>
|
||||
inline constexpr auto errorMessage<at<rep_min<0, validPortTypes>>> = "";
|
||||
template <>
|
||||
inline constexpr auto errorMessage<rep_min<1, nodeNameSeparated>> =
|
||||
"Port type is invalid. '%%' needs to be followed by a valid name and a '?'.";
|
||||
template <>
|
||||
inline constexpr auto errorMessage<not_at<invertionSeparated>> = "'~~' is not supported.";
|
||||
template <>
|
||||
inline constexpr auto errorMessage<not_at<one<'%'>>> =
|
||||
"'~ %%d' not supported, consider changing to '%%d ~'.";
|
||||
template <>
|
||||
inline constexpr auto errorMessage<rep_min<2, nodeNameSeparated>> =
|
||||
"Differential ports need two nodes, and '~' is not supported for those nodes. Also check "
|
||||
"if port modifier name is valid.";
|
||||
template <>
|
||||
inline constexpr auto errorMessage<not_at<squareBracketO>> = "'~[' not supported.";
|
||||
template <>
|
||||
inline constexpr auto errorMessage<not_at<squareBracketC>> =
|
||||
"Vector is either empty, open or nested.";
|
||||
template <>
|
||||
inline constexpr auto errorMessage<sep> = "";
|
||||
|
||||
|
||||
struct error
|
||||
{
|
||||
template <typename Rule>
|
||||
static constexpr bool raise_on_failure = false;
|
||||
template <typename Rule>
|
||||
static constexpr auto message = errorMessage<Rule>;
|
||||
};
|
||||
template <typename Rule>
|
||||
using control = must_if<error>::control<Rule>;
|
||||
|
||||
|
||||
template <typename Rule>
|
||||
struct spiceUnitSelector : std::false_type
|
||||
{
|
||||
};
|
||||
template <>
|
||||
struct spiceUnitSelector<squareBracketO> : std::true_type
|
||||
{
|
||||
};
|
||||
template <>
|
||||
struct spiceUnitSelector<portModifierDigital> : std::true_type
|
||||
{
|
||||
};
|
||||
template <>
|
||||
struct spiceUnitSelector<portModifiersSingle> : std::true_type
|
||||
{
|
||||
};
|
||||
template <>
|
||||
struct spiceUnitSelector<portModifiersDifferential> : std::true_type
|
||||
{
|
||||
};
|
||||
template <>
|
||||
struct spiceUnitSelector<invertionDigital> : std::true_type
|
||||
{
|
||||
};
|
||||
template <>
|
||||
struct spiceUnitSelector<squareBracketC> : std::true_type
|
||||
{
|
||||
};
|
||||
template <>
|
||||
struct spiceUnitSelector<nodeName> : std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
} // namespace SIM_XSPICE_PARSER_GRAMMAR
|
||||
#endif // SIM_XSPCIE_PARSER_H_
|
Loading…
Reference in New Issue