56#include <pegtl/contrib/parse_tree.hpp>
58namespace NETLIST_EXPORTER_SPICE_PARSER
62 struct textGrammar : must<spiceSource> {};
64 template <
typename Rule>
struct textSelector : std::false_type {};
65 template <>
struct textSelector<
modelUnit> : std::true_type {};
67 template <>
struct textSelector<
dotControl> : std::true_type {};
69 template <>
struct textSelector<
dotTitle> : std::true_type {};
70 template <>
struct textSelector<
dotTitleTitle> : std::true_type {};
72 template <>
struct textSelector<
dotInclude> : std::true_type {};
75 template <>
struct textSelector<
dotIncludePath> : std::true_type {};
77 template <>
struct textSelector<
kLine> : std::true_type {};
79 template <>
struct textSelector<
dotLine> : std::true_type {};
86 std::string
name = aProposedName;
90 name = fmt::format(
"{}#{}", aProposedName, ii++ );
97 wxWindow* aDialogParent ) :
99 m_libMgr( &aSchematic->
Prj() ),
100 m_dialogParent( aDialogParent )
109 return DoWriteNetlist( wxEmptyString, aNetlistOptions, formatter, aReporter );
142 aFormatter.
Print( 0,
".title KiCad schematic\n" );
148 aFormatter.
Print( 0,
".end\n" );
156 std::set<std::string> refNames;
168 cacheDir.AppendDir( wxT(
"ibis" ) );
170 if( !cacheDir.DirExists() )
172 cacheDir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
174 if( !cacheDir.DirExists() )
176 wxLogTrace( wxT(
"IBIS_CACHE:" ),
177 wxT(
"%s:%s:%d\n * failed to create ibis cache directory '%s'" ),
178 __FILE__, __FUNCTION__, __LINE__, cacheDir.GetPath() );
185 wxString dirName = cacheDir.GetFullPath();
187 if( !dir.Open( dirName ) )
191 wxArrayString fileList;
192 wxString fileSpec = wxT(
"*.cache" );
194 thisFile.SetPath( dirName );
196 size_t numFilesFound = wxDir::GetAllFiles( dirName, &fileList, fileSpec );
198 for(
size_t ii = 0; ii < numFilesFound; ii++ )
201 thisFile.SetFullName( fileList[ii] );
202 wxRemoveFile( thisFile.GetFullPath() );
215 std::vector<PIN_INFO> pins =
CreatePinList( symbol, &sheet,
true );
219 spiceItem.
fields.emplace_back(
VECTOR2I(), -1, symbol, field.GetName() );
222 spiceItem.
fields.back().SetText( symbol->
GetRef( &sheet ) );
224 spiceItem.
fields.back().SetText( field.GetShownText( &sheet,
false ) );
227 readRefName( sheet, *symbol, spiceItem, refNames );
228 readModel( sheet, *symbol, spiceItem, aReporter );
234 m_items.push_back( std::move( spiceItem ) );
245 std::unique_ptr<MARKUP::NODE> root = markupParser.
Parse();
246 std::string converted;
248 std::function<void(
const std::unique_ptr<MARKUP::NODE>&)> convertMarkup =
249 [&](
const std::unique_ptr<MARKUP::NODE>& aNode )
253 if( !aNode->is_root() )
255 if( aNode->isOverbar() )
260 else if( aNode->isSubscript() || aNode->isSuperscript() )
265 if( aNode->has_content() )
266 converted += aNode->string();
269 for(
const std::unique_ptr<MARKUP::NODE>& child : aNode->children )
270 convertMarkup( child );
274 convertMarkup( root );
277 std::replace( converted.begin(), converted.end(),
'%',
'_' );
278 std::replace( converted.begin(), converted.end(),
'(',
'_' );
279 std::replace( converted.begin(), converted.end(),
')',
'_' );
280 std::replace( converted.begin(), converted.end(),
',',
'_' );
281 std::replace( converted.begin(), converted.end(),
'[',
'_' );
282 std::replace( converted.begin(), converted.end(),
']',
'_' );
283 std::replace( converted.begin(), converted.end(),
'<',
'_' );
284 std::replace( converted.begin(), converted.end(),
'>',
'_' );
285 std::replace( converted.begin(), converted.end(),
'~',
'_' );
286 std::replace( converted.begin(), converted.end(),
' ',
'_' );
288 aNetName = converted;
305 const std::list<SPICE_ITEM>& spiceItems =
GetItems();
307 auto it = std::find_if( spiceItems.begin(), spiceItems.end(),
310 return item.refName == aRefName;
313 if( it != spiceItems.end() )
329 for(
SCH_ITEM* item : sheet.LastScreen()->Items() )
331 if( item->GetExcludedFromSim() )
335 text =
static_cast<SCH_TEXT*
>( item )->GetShownText( &sheet,
false );
342 wxStringTokenizer tokenizer(
text, wxT(
"\r\n" ), wxTOKEN_STRTOK );
343 bool foundDirective =
false;
346 [](
const wxString& line,
const wxString& dir )
348 return line == dir || line.StartsWith( dir + wxS(
" " ) );
351 while( tokenizer.HasMoreTokens() )
353 wxString line = tokenizer.GetNextToken().Upper();
355 if( line.StartsWith( wxT(
"." ) ) )
357 if( isDirective( line, wxS(
".AC" ) )
358 || isDirective( line, wxS(
".CONTROL" ) )
359 || isDirective( line, wxS(
".CSPARAM" ) )
360 || isDirective( line, wxS(
".DISTO" ) )
361 || isDirective( line, wxS(
".DC" ) )
362 || isDirective( line, wxS(
".ELSE" ) )
363 || isDirective( line, wxS(
".ELSEIF" ) )
364 || isDirective( line, wxS(
".END" ) )
365 || isDirective( line, wxS(
".ENDC" ) )
366 || isDirective( line, wxS(
".ENDIF" ) )
367 || isDirective( line, wxS(
".ENDS" ) )
368 || isDirective( line, wxS(
".FOUR" ) )
369 || isDirective( line, wxS(
".FUNC" ) )
370 || isDirective( line, wxS(
".GLOBAL" ) )
371 || isDirective( line, wxS(
".IC" ) )
372 || isDirective( line, wxS(
".IF" ) )
373 || isDirective( line, wxS(
".INCLUDE" ) )
374 || isDirective( line, wxS(
".LIB" ) )
375 || isDirective( line, wxS(
".MEAS" ) )
376 || isDirective( line, wxS(
".MODEL" ) )
377 || isDirective( line, wxS(
".NODESET" ) )
378 || isDirective( line, wxS(
".NOISE" ) )
379 || isDirective( line, wxS(
".OP" ) )
380 || isDirective( line, wxS(
".OPTIONS" ) )
381 || isDirective( line, wxS(
".PARAM" ) )
382 || isDirective( line, wxS(
".PLOT" ) )
383 || isDirective( line, wxS(
".PRINT" ) )
384 || isDirective( line, wxS(
".PROBE" ) )
385 || isDirective( line, wxS(
".PZ" ) )
386 || isDirective( line, wxS(
".SAVE" ) )
387 || isDirective( line, wxS(
".SENS" ) )
388 || isDirective( line, wxS(
".SP" ) )
389 || isDirective( line, wxS(
".SUBCKT" ) )
390 || isDirective( line, wxS(
".TEMP" ) )
391 || isDirective( line, wxS(
".TF" ) )
392 || isDirective( line, wxS(
".TITLE" ) )
393 || isDirective( line, wxS(
".TRAN" ) )
394 || isDirective( line, wxS(
".WIDTH" ) ) )
396 foundDirective =
true;
400 else if( line.StartsWith( wxT(
"K" ) ) )
403 wxStringTokenizer line_t( line, wxT(
" \t" ), wxTOKEN_STRTOK );
406 if( !line_t.HasMoreTokens() || !line_t.GetNextToken().StartsWith( wxT(
"K" ) ) )
410 if( !line_t.HasMoreTokens() || !line_t.GetNextToken().StartsWith( wxT(
"L" ) ) )
414 if( !line_t.HasMoreTokens() || !line_t.GetNextToken().StartsWith( wxT(
"L" ) ) )
419 if( line_t.HasMoreTokens() )
421 foundDirective =
true;
435 SPICE_ITEM& aItem, std::set<std::string>& aRefNames )
439 if( !aRefNames.insert( aItem.
refName ).second )
440 wxASSERT( wxT(
"Duplicate refdes encountered; what happened to ReadyToNetlist()?" ) );
459 int libParamIndex =
static_cast<int>( SIM_MODEL_RAW_SPICE::SPICE_PARAM::LIB );
460 wxString
path = rawSpiceModel->GetParam( libParamIndex ).value;
462 if( !
path.IsEmpty() )
469 cacheFn.AppendDir( wxT(
"ibis" ) );
470 cacheFn.SetFullName( aSymbol.
GetRef( &aSheet ) + wxT(
".cache" ) );
472 wxFile cacheFile( cacheFn.GetFullPath(), wxFile::write );
474 if( !cacheFile.IsOpened() )
477 "to write IBIS model" ),
478 cacheFn.GetFullPath() ) );
482 std::string modelData = spiceGenerator.
IbisDevice(
483 aItem,
m_schematic->
Prj(), cacheFn.GetPath( wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR ) );
485 cacheFile.Write( wxString( modelData ) );
492 const std::vector<PIN_INFO>& aPins )
500 const std::vector<PIN_INFO>& aPins,
int& aNcCounter )
502 for(
const PIN_INFO& pinInfo : aPins )
511 std::vector<std::string>& aModifiers )
518 tao::pegtl::string_input<> in( input,
"Sim.NodesFormat field" );
519 std::unique_ptr<tao::pegtl::parse_tree::node> root;
520 std::string singleNodeModifier;
528 for(
const auto& node : root->children )
533 aModifiers.back().append( node->string() );
537 singleNodeModifier.append( node->string() );
542 aModifiers.push_back( singleNodeModifier );
543 singleNodeModifier.erase( singleNodeModifier.begin(), singleNodeModifier.end() );
547 catch(
const tao::pegtl::parse_error& e )
549 THROW_IO_ERROR( wxString::Format(
_(
"Error in parsing model '%s', error: '%s'" ),
555 std::vector<std::string> xspicePattern;
558 if( xspicePattern.empty() )
561 if( xspicePattern.size() != aItem.
pinNetNames.size() )
563 THROW_IO_ERROR( wxString::Format(
_(
"Error in parsing model '%s', wrong number of nodes "
564 "'?' in Sim.NodesFormat compared to connections" ),
571 for( std::string& pattern : xspicePattern )
574 const std::string netName =
" " + *itNetNames +
" ";
575 pattern.replace( pattern.find(
"?" ), 1, netName );
576 *itNetNames = pattern;
582 const wxString& aPath )
588 expandedPath.Replace(
'\\',
'/' );
597 if( fullPath.IsEmpty() )
602 fullPath = expandedPath;
604 else if( wxFileName::GetPathSeparator() ==
'\\' )
607 fullPath.Replace(
'\\',
'/' );
612 fullPath = expandedPath;
615 aFormatter.
Print( 0,
".include \"%s\"\n",
TO_UTF8( fullPath ) );
636 if( !item.model->IsEnabled() )
639 aFormatter.
Print( 0,
"%s", item.model->SpiceGenerator().ModelLine( item ).c_str() );
648 if( !item.model->IsEnabled() )
651 aFormatter.
Print( 0,
"%s", item.model->SpiceGenerator().ItemLine( item ).c_str() );
660 aFormatter.
Print( 0,
".save all\n" );
663 aFormatter.
Print( 0,
".probe alli\n" );
671 std::string itemName = item.model->SpiceGenerator().ItemName( item );
673 if( ( item.model->GetPinCount() >= 2 ) && ( itemName.size() > 0 )
674 && ( itemName.c_str()[0] !=
'A' ) )
676 aFormatter.
Print( 0,
".probe p(%s)\n", itemName.c_str() );
682 [](
const wxString& candidate,
const wxString& dir )
684 return candidate == dir || candidate.StartsWith( dir + wxS(
" " ) );
689 bool simCommand =
false;
691 if( directive.StartsWith(
"." ) )
693 wxString candidate = directive.Upper();
695 simCommand = ( isSimCommand( candidate, wxS(
".AC" ) )
696 || isSimCommand( candidate, wxS(
".DC" ) )
697 || isSimCommand( candidate, wxS(
".TRAN" ) )
698 || isSimCommand( candidate, wxS(
".OP" ) )
699 || isSimCommand( candidate, wxS(
".DISTO" ) )
700 || isSimCommand( candidate, wxS(
".NOISE" ) )
701 || isSimCommand( candidate, wxS(
".PZ" ) )
702 || isSimCommand( candidate, wxS(
".SENS" ) )
703 || isSimCommand( candidate, wxS(
".TF" ) ) );
713 int& aNcCounter )
const
715 std::string netName = aNetName;
721 netName = fmt::format(
"NC-{}", aNcCounter++ );
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
std::unique_ptr< NODE > Parse()
std::unordered_set< std::string > m_names
std::string Generate(const std::string &aProposedName)
An abstract class used for the netlist exporters that Eeschema supports.
std::set< LIB_SYMBOL *, LIB_SYMBOL_LESS_THAN > m_libParts
unique library symbols used. LIB_SYMBOL items are sorted by names
std::vector< PIN_INFO > CreatePinList(SCH_SYMBOL *aSymbol, SCH_SHEET_PATH *aSheetPath, bool aKeepUnconnectedPins)
Find a symbol from the DrawList and builds its pin list.
UNIQUE_STRINGS m_referencesAlreadyFound
Used for "multiple symbols per package" symbols to avoid processing a lib symbol more than once.
SCH_SYMBOL * findNextSymbol(EDA_ITEM *aItem, SCH_SHEET_PATH *aSheetPath)
Check if the given symbol should be processed for netlisting.
SCHEMATIC_IFACE * m_schematic
The schematic we're generating a netlist for.
void readPinNetNames(SCH_SYMBOL &aSymbol, SPICE_ITEM &aItem, const std::vector< PIN_INFO > &aPins, int &aNcCounter)
void writeModels(OUTPUTFORMATTER &aFormatter)
void writeIncludes(OUTPUTFORMATTER &aFormatter, unsigned aNetlistOptions)
NETLIST_EXPORTER_SPICE(SCHEMATIC_IFACE *aSchematic, wxWindow *aDialogParent=nullptr)
std::list< SPICE_ITEM > m_items
void getNodePattern(SPICE_ITEM &aItem, std::vector< std::string > &aModifiers)
void ReadDirectives(unsigned aNetlistOptions)
std::string GetItemName(const std::string &aRefName) const
Return name of Spice device corresponding to a schematic symbol.
@ OPTION_SAVE_ALL_CURRENTS
@ OPTION_SAVE_ALL_VOLTAGES
@ OPTION_SAVE_ALL_DISSIPATIONS
@ OPTION_ADJUST_INCLUDE_PATHS
@ OPTION_CUR_SHEET_AS_ROOT
void readModel(SCH_SHEET_PATH &aSheet, SCH_SYMBOL &aSymbol, SPICE_ITEM &aItem, REPORTER &aReporter)
SCH_SHEET_LIST GetSheets(unsigned aNetlistOptions=0) const
Return the paths of exported sheets (either all or the current one).
void readRefName(SCH_SHEET_PATH &aSheet, SCH_SYMBOL &aSymbol, SPICE_ITEM &aItem, std::set< std::string > &aRefNames)
static void ConvertToSpiceMarkup(std::string &aNetName)
Remove formatting wrappers and replace illegal spice net name characters with underscores.
void writeItems(OUTPUTFORMATTER &aFormatter)
std::vector< wxString > m_directives
Spice directives found in the schematic sheet.
virtual void WriteHead(OUTPUTFORMATTER &aFormatter, unsigned aNetlistOptions)
Write the netlist head (title and so on).
virtual std::string GenerateItemPinNetName(const std::string &aNetName, int &aNcCounter) const
const SPICE_ITEM * FindItem(const std::string &aRefName) const
Find and return the item corresponding to aRefName.
void writeInclude(OUTPUTFORMATTER &aFormatter, unsigned aNetlistOptions, const wxString &aPath)
virtual void WriteDirectives(const wxString &aSimCommand, unsigned aSimOptions, OUTPUTFORMATTER &candidate) const
SIM_LIB_MGR m_libMgr
Holds libraries and models.
wxWindow * m_dialogParent
virtual bool ReadSchematicAndLibraries(unsigned aNetlistOptions, REPORTER &aReporter)
Process the schematic and Spice libraries to create net mapping and a list of SPICE_ITEMs.
bool DoWriteNetlist(const wxString &aSimCommand, unsigned aSimOptions, OUTPUTFORMATTER &aFormatter, REPORTER &aReporter)
Write the netlist in aFormatter.
std::set< std::string > m_nets
Items representing schematic symbols in Spice world.
void readPinNumbers(SCH_SYMBOL &aSymbol, SPICE_ITEM &aItem, const std::vector< PIN_INFO > &aPins)
const std::list< SPICE_ITEM > & GetItems() const
Return the list of items representing schematic symbols in the Spice world.
void readNodePattern(SPICE_ITEM &aItem)
std::set< wxString > m_rawIncludes
include directives found in symbols
virtual void WriteTail(OUTPUTFORMATTER &aFormatter, unsigned aNetlistOptions)
Write the tail (.end).
NAME_GENERATOR m_modelNameGenerator
Generates unique model names.
bool WriteNetlist(const wxString &aOutFileName, unsigned aNetlistOptions, REPORTER &aReporter) override
Write to specified output file.
static wxString GetUserCachePath()
Gets the stock (install) 3d viewer plugins path.
A pure virtual class used to derive REPORTER objects from.
virtual bool HasMessage() const =0
Returns true if the reporter client is non-empty.
virtual SCH_SHEET_LIST GetSheets() const =0
virtual SCH_SHEET_PATH & CurrentSheet() const =0
virtual PROJECT & Prj() const =0
Instances are attached to a symbol or sheet and provide a place for the symbol's value,...
Base class for any item which can be embedded within the SCHEMATIC container class,...
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
SCH_SHEET * Last() const
Return a pointer to the last SCH_SHEET of the list.
bool GetExcludedFromSim() const override
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly)
Populate a std::vector with SCH_FIELDs.
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const override
virtual wxString GetShownText(const SCH_SHEET_PATH *aPath, bool aAllowExtraText, int aDepth=0) const
SIM_MODEL & CreateModel(SIM_MODEL::TYPE aType, const std::vector< LIB_PIN * > &aPins, REPORTER &aReporter)
std::map< wxString, std::reference_wrapper< const SIM_LIBRARY > > GetLibraries() const
const SPICE_GENERATOR & SpiceGenerator() const
static std::string GetFieldValue(const std::vector< SCH_FIELD > *aFields, const wxString &aFieldName, bool aResolve=true)
std::string IbisDevice(const SPICE_ITEM &aItem, const PROJECT &aProject, const wxString &aCacheDir) const
virtual std::string ItemName(const SPICE_ITEM &aItem) const
virtual std::string ModelName(const SPICE_ITEM &aItem) const
void Clear()
Erase the record.
An 8 bit string that is assuredly encoded in UTF8, and supplies special conversion support to and fro...
const char * c_str() const
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
This file is part of the common library.
wxString ResolveFile(const wxString &aFileName, const ENV_VAR_MAP *aEnvVars, const PROJECT *aProject)
Search the default paths trying to find one with the requested file.
#define THROW_IO_ERROR(msg)
must_if< error >::control< Rule > control
PGM_BASE & Pgm()
The global Program "get" accessor.
#define SIM_NODES_FORMAT_FIELD
std::vector< FAB_LAYER_COLOR > dummy
wxString UnescapeString(const wxString &aSource)
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Notes: spaces are allowed everywhere in any number ~ can only be before ? ~~ is not allowed [] can en...
std::vector< SCH_FIELD > fields
std::vector< std::string > pinNetNames
std::string baseModelName
std::vector< std::string > pinNumbers
@ REFERENCE_FIELD
Field Reference of part, i.e. "IC21".