49#include <wx/tokenzr.h>
56 std::string
name = aProposedName;
60 name = fmt::format(
"{}#{}", aProposedName, ii++ );
70 std::vector<EMBEDDED_FILES*> embeddedFilesStack;
72 m_libMgr.SetFilesStack( std::move( embeddedFilesStack ) );
80 return DoWriteNetlist( wxEmptyString, aNetlistOptions, formatter, aReporter );
113 aFormatter.
Print( 0,
".title KiCad schematic\n" );
119 aFormatter.
Print( 0,
".end\n" );
126 std::set<std::string> refNames;
128 wxString variant =
m_schematic->GetCurrentVariant();
139 cacheDir.AppendDir( wxT(
"ibis" ) );
141 if( !cacheDir.DirExists() )
143 cacheDir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
145 if( !cacheDir.DirExists() )
147 wxLogTrace( wxT(
"IBIS_CACHE:" ),
148 wxT(
"%s:%s:%d\n * failed to create ibis cache directory '%s'" ),
149 __FILE__, __FUNCTION__, __LINE__, cacheDir.GetPath() );
156 wxString dirName = cacheDir.GetFullPath();
158 if( !dir.Open( dirName ) )
162 wxArrayString fileList;
163 wxString fileSpec = wxT(
"*.cache" );
165 thisFile.SetPath( dirName );
167 size_t numFilesFound = wxDir::GetAllFiles( dirName, &fileList, fileSpec );
169 for(
size_t ii = 0; ii < numFilesFound; ii++ )
172 thisFile.SetFullName( fileList[ii] );
173 wxRemoveFile( thisFile.GetFullPath() );
188 std::vector<PIN_INFO> pins =
CreatePinList( symbol, sheet,
true );
195 spiceItem.
fields.back().SetText( symbol->
GetRef( &sheet ) );
197 spiceItem.
fields.back().SetText( field.GetShownText( &sheet,
false, 0, variant ) );
200 readRefName( sheet, *symbol, spiceItem, refNames );
201 readModel( sheet, *symbol, spiceItem, variant, aReporter );
207 m_items.push_back( std::move( spiceItem ) );
223 std::unique_ptr<MARKUP::NODE> root = markupParser.
Parse();
225 std::function<void(
const std::unique_ptr<MARKUP::NODE>&)> convertMarkup =
226 [&](
const std::unique_ptr<MARKUP::NODE>& aNode )
230 if( !aNode->is_root() )
232 if( aNode->isOverbar() )
237 else if( aNode->isSubscript() || aNode->isSuperscript() )
242 if( aNode->has_content() )
243 *aNetName += aNode->string();
246 for(
const std::unique_ptr<MARKUP::NODE>& child : aNode->children )
247 convertMarkup( child );
251 *aNetName = wxEmptyString;
252 convertMarkup( root );
255 aNetName->Replace(
'%',
'_' );
256 aNetName->Replace(
'(',
'_' );
257 aNetName->Replace(
')',
'_' );
258 aNetName->Replace(
',',
'_' );
259 aNetName->Replace(
'[',
'_' );
260 aNetName->Replace(
']',
'_' );
261 aNetName->Replace(
'<',
'_' );
262 aNetName->Replace(
'>',
'_' );
263 aNetName->Replace(
'~',
'_' );
264 aNetName->Replace(
' ',
'_' );
268 if( aNetName->StartsWith( wxS(
"//" ) ) )
269 aNetName->Replace( wxS(
"//" ), wxS(
"/root/" ),
false );
276 return item->model->SpiceGenerator().ItemName( *item );
278 return wxEmptyString;
284 const std::string refName = aRefName.ToStdString();
285 const std::list<SPICE_ITEM>& spiceItems =
GetItems();
287 auto it = std::find_if( spiceItems.begin(), spiceItems.end(),
290 return item.refName == refName;
293 if( it != spiceItems.end() )
308 for(
SCH_ITEM* item : sheet.LastScreen()->Items() )
310 if( item->ResolveExcludedFromSim() )
314 text =
static_cast<SCH_TEXT*
>( item )->GetShownText( &sheet,
false );
316 text =
static_cast<SCH_TEXTBOX*
>( item )->GetShownText(
nullptr, &sheet,
false );
321 wxStringTokenizer tokenizer(
text,
"\r\n", wxTOKEN_STRTOK );
322 bool foundDirective =
false;
325 [](
const wxString& line,
const wxString& dir )
327 return line == dir || line.StartsWith( dir + wxS(
" " ) );
330 while( tokenizer.HasMoreTokens() )
332 wxString line = tokenizer.GetNextToken().Upper();
334 if( line.StartsWith( wxT(
"." ) ) )
336 if( isDirective( line, wxS(
".AC" ) )
337 || isDirective( line, wxS(
".CONTROL" ) )
338 || isDirective( line, wxS(
".CSPARAM" ) )
339 || isDirective( line, wxS(
".DISTO" ) )
340 || isDirective( line, wxS(
".DC" ) )
341 || isDirective( line, wxS(
".ELSE" ) )
342 || isDirective( line, wxS(
".ELSEIF" ) )
343 || isDirective( line, wxS(
".END" ) )
344 || isDirective( line, wxS(
".ENDC" ) )
345 || isDirective( line, wxS(
".ENDIF" ) )
346 || isDirective( line, wxS(
".ENDS" ) )
347 || isDirective( line, wxS(
".FOUR" ) )
348 || isDirective( line, wxS(
".FUNC" ) )
349 || isDirective( line, wxS(
".GLOBAL" ) )
350 || isDirective( line, wxS(
".IC" ) )
351 || isDirective( line, wxS(
".IF" ) )
352 || isDirective( line, wxS(
".INCLUDE" ) )
353 || isDirective( line, wxS(
".LIB" ) )
354 || isDirective( line, wxS(
".MEAS" ) )
355 || isDirective( line, wxS(
".MODEL" ) )
356 || isDirective( line, wxS(
".NODESET" ) )
357 || isDirective( line, wxS(
".NOISE" ) )
358 || isDirective( line, wxS(
".OP" ) )
359 || isDirective( line, wxS(
".OPTIONS" ) )
360 || isDirective( line, wxS(
".PARAM" ) )
361 || isDirective( line, wxS(
".PLOT" ) )
362 || isDirective( line, wxS(
".PRINT" ) )
363 || isDirective( line, wxS(
".PROBE" ) )
364 || isDirective( line, wxS(
".PZ" ) )
365 || isDirective( line, wxS(
".SAVE" ) )
366 || isDirective( line, wxS(
".SENS" ) )
367 || isDirective( line, wxS(
".SP" ) )
368 || isDirective( line, wxS(
".SUBCKT" ) )
369 || isDirective( line, wxS(
".TEMP" ) )
370 || isDirective( line, wxS(
".TF" ) )
371 || isDirective( line, wxS(
".TITLE" ) )
372 || isDirective( line, wxS(
".TRAN" ) )
373 || isDirective( line, wxS(
".WIDTH" ) ) )
375 foundDirective =
true;
379 else if( line.StartsWith( wxT(
"K" ) ) )
382 wxStringTokenizer line_t( line,
" \t", wxTOKEN_STRTOK );
385 if( !line_t.HasMoreTokens() || !line_t.GetNextToken().StartsWith( wxT(
"K" ) ) )
389 if( !line_t.HasMoreTokens() || !line_t.GetNextToken().StartsWith( wxT(
"L" ) ) )
393 if( !line_t.HasMoreTokens() || !line_t.GetNextToken().StartsWith( wxT(
"L" ) ) )
398 if( line_t.HasMoreTokens() )
400 foundDirective =
true;
418 return wxEmptyString;
420 wxString ref = aSymbol.
GetRef( &aSheet );
421 std::vector<std::pair<wxString, wxString>> pinList;
422 std::set<wxString> pinNumbers;
425 auto parsePins = [&](
const wxString& aPins )
427 wxStringTokenizer tokenizer( aPins, wxS(
" \t\r\n" ), wxTOKEN_STRTOK );
429 while( tokenizer.HasMoreTokens() )
431 wxString token = tokenizer.GetNextToken();
432 int pos = token.Find( wxS(
'=' ) );
434 if( pos == wxNOT_FOUND )
437 wxString pinNumber = token.Left( pos );
438 wxString modelPin = token.Mid( pos + 1 );
441 if( pinNumbers.insert( pinNumber ).second )
442 pinList.emplace_back( pinNumber, modelPin );
448 parsePins( pinsField->GetShownText( &aSheet,
false ) );
457 if( other == &aSymbol )
460 if( other->
GetRef( &sheet ) != ref )
464 parsePins( pinsField->GetShownText( &sheet,
false ) );
470 if( pinList.empty() )
471 return wxEmptyString;
476 for(
const auto& [pinNumber, modelPin] : pinList )
478 if( !merged.IsEmpty() )
479 merged += wxS(
" " );
481 merged += pinNumber + wxS(
"=" ) + modelPin;
489 SPICE_ITEM& aItem, std::set<std::string>& aRefNames )
493 if( !aRefNames.insert( aItem.
refName ).second )
494 wxASSERT( wxT(
"Duplicate refdes encountered; what happened to ReadyToNetlist()?" ) );
499 const wxString& aVariantName,
REPORTER& aReporter )
505 aReporter, mergedSimPins );
517 int libParamIndex =
static_cast<int>( SIM_MODEL_RAW_SPICE::SPICE_PARAM::LIB );
518 wxString
path = rawSpiceModel->GetParam( libParamIndex ).value;
520 if( !
path.IsEmpty() )
527 cacheFn.AppendDir( wxT(
"ibis" ) );
528 cacheFn.SetFullName( aSymbol.
GetRef( &aSheet ) + wxT(
".cache" ) );
530 wxFile cacheFile( cacheFn.GetFullPath(), wxFile::write );
532 if( !cacheFile.IsOpened() )
534 wxLogError(
_(
"Could not open file '%s' to write IBIS model" ),
535 cacheFn.GetFullPath() );
540 wxString cacheFilepath = cacheFn.GetPath( wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR );
542 cacheFilepath, aReporter );
544 cacheFile.Write( wxString( modelData ) );
551 const std::vector<PIN_INFO>& aPins )
559 const std::vector<PIN_INFO>& aPins,
int& aNcCounter )
561 for(
const PIN_INFO& pinInfo : aPins )
565 aItem.
pinNetNames.push_back( netName.ToStdString() );
572 std::vector<std::string>& aModifiers )
579 tao::pegtl::string_input<> in( input,
"Sim.NodesFormat field" );
580 std::unique_ptr<tao::pegtl::parse_tree::node> root;
581 std::string singleNodeModifier;
589 for(
const auto& node : root->children )
594 aModifiers.back().append( node->string() );
598 singleNodeModifier.append( node->string() );
603 aModifiers.push_back( singleNodeModifier );
604 singleNodeModifier.erase( singleNodeModifier.begin(), singleNodeModifier.end() );
608 catch(
const tao::pegtl::parse_error& e )
610 THROW_IO_ERROR( wxString::Format(
_(
"Error in parsing model '%s', error: '%s'" ),
616 std::vector<std::string> xspicePattern;
619 if( xspicePattern.empty() )
622 if( xspicePattern.size() != aItem.
pinNetNames.size() )
624 THROW_IO_ERROR( wxString::Format(
_(
"Error in parsing model '%s', wrong number of nodes "
625 "'?' in Sim.NodesFormat compared to connections" ),
632 for( std::string& pattern : xspicePattern )
635 const std::string netName =
" " + *itNetNames +
" ";
636 pattern.replace( pattern.find(
"?" ), 1, netName );
637 *itNetNames = pattern;
643 const wxString& aPath )
649 expandedPath.Replace(
'\\',
'/' );
658 if( fullPath.IsEmpty() )
660 wxLogError(
_(
"Could not find library file '%s'" ), expandedPath );
661 fullPath = expandedPath;
663 else if( wxFileName::GetPathSeparator() ==
'\\' )
666 fullPath.Replace(
'\\',
'/' );
671 fullPath = expandedPath;
674 aFormatter.
Print( 0,
".include \"%s\"\n",
TO_UTF8( fullPath ) );
695 if( !item.model->IsEnabled() )
698 aFormatter.
Print( 0,
"%s", item.model->SpiceGenerator().ModelLine( item ).c_str() );
707 if( !item.model->IsEnabled() )
710 aFormatter.
Print( 0,
"%s", item.model->SpiceGenerator().ItemLine( item ).c_str() );
719 aFormatter.
Print( 0,
".save all\n" );
722 aFormatter.
Print( 0,
".probe alli\n" );
730 std::string itemName = item.model->SpiceGenerator().ItemName( item );
732 if( ( item.model->GetPinCount() >= 2 ) && ( itemName.size() > 0 )
733 && ( itemName.c_str()[0] !=
'A' ) )
735 aFormatter.
Print( 0,
".probe p(%s)\n", itemName.c_str() );
741 [](
const wxString& candidate,
const wxString& dir )
743 return candidate == dir || candidate.StartsWith( dir + wxS(
" " ) );
748 bool simCommand =
false;
750 if( directive.StartsWith(
"." ) )
752 wxString candidate = directive.Upper();
754 simCommand = ( isSimCommand( candidate, wxS(
".AC" ) )
755 || isSimCommand( candidate, wxS(
".DC" ) )
756 || isSimCommand( candidate, wxS(
".TRAN" ) )
757 || isSimCommand( candidate, wxS(
".OP" ) )
758 || isSimCommand( candidate, wxS(
".DISTO" ) )
759 || isSimCommand( candidate, wxS(
".NOISE" ) )
760 || isSimCommand( candidate, wxS(
".PZ" ) )
761 || isSimCommand( candidate, wxS(
".SENS" ) )
762 || isSimCommand( candidate, wxS(
".TF" ) ) );
766 aFormatter.
Print( 0,
"%s\n",
UTF8( directive ).c_str() );
772 int& aNcCounter )
const
778 if( netName.IsEmpty() )
779 netName.Printf( wxS(
"NC-%d" ), aNcCounter++ );
794 std::erase_if( sheets,
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
int GetUnitCount() const override
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)
SCHEMATIC * m_schematic
The schematic we're generating a netlist for.
std::vector< PIN_INFO > CreatePinList(SCH_SYMBOL *aSymbol, const SCH_SHEET_PATH &aSheetPath, bool aKeepUnconnectedPins)
Find a symbol from the DrawList and builds its pin list.
SCH_SYMBOL * findNextSymbol(EDA_ITEM *aItem, const SCH_SHEET_PATH &aSheetPath)
Check if the given symbol should be processed for netlisting.
std::set< LIB_SYMBOL *, LIB_SYMBOL_LESS_THAN > m_libParts
unique library symbols used. LIB_SYMBOL items are sorted by names
NETLIST_EXPORTER_BASE(SCHEMATIC *aSchematic)
UNIQUE_STRINGS m_referencesAlreadyFound
Used for "multiple symbols per package" symbols to avoid processing a lib symbol more than once.
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)
std::list< SPICE_ITEM > m_items
void getNodePattern(SPICE_ITEM &aItem, std::vector< std::string > &aModifiers)
static void ConvertToSpiceMarkup(wxString *aNetName)
Remove formatting wrappers and replace illegal spice net name characters with underscores.
void ReadDirectives(unsigned aNetlistOptions)
@ OPTION_SAVE_ALL_CURRENTS
@ OPTION_SAVE_ALL_VOLTAGES
@ OPTION_SAVE_ALL_DISSIPATIONS
@ OPTION_ADJUST_INCLUDE_PATHS
@ OPTION_CUR_SHEET_AS_ROOT
SCH_SHEET_LIST BuildSheetList(unsigned aNetlistOptions=0) const
Return the paths of exported sheets (either all or the current one).
virtual wxString GenerateItemPinNetName(const wxString &aNetName, int &aNcCounter) const
std::set< wxString > m_nets
Items representing schematic symbols in Spice world.
void readRefName(SCH_SHEET_PATH &aSheet, SCH_SYMBOL &aSymbol, SPICE_ITEM &aItem, std::set< std::string > &aRefNames)
wxString collectMergedSimPins(SCH_SYMBOL &aSymbol, const SCH_SHEET_PATH &aSheet)
Collect merged Sim.Pins from all units of a multi-unit symbol.
wxString GetItemName(const wxString &aRefName) const
Return name of Spice device corresponding to a schematic symbol.
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).
void writeInclude(OUTPUTFORMATTER &aFormatter, unsigned aNetlistOptions, const wxString &aPath)
virtual void WriteDirectives(const wxString &aSimCommand, unsigned aSimOptions, OUTPUTFORMATTER &candidate) const
NETLIST_EXPORTER_SPICE(SCHEMATIC *aSchematic)
SIM_LIB_MGR m_libMgr
Holds libraries and models.
const SPICE_ITEM * FindItem(const wxString &aRefName) const
Find and return the item corresponding to aRefName.
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.
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
void readModel(SCH_SHEET_PATH &aSheet, SCH_SYMBOL &aSymbol, SPICE_ITEM &aItem, const wxString &aVariantName, REPORTER &aReporter)
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 REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Report a string with a given severity.
virtual bool HasMessageOfSeverity(int aSeverityMask) const
Returns true if the reporter has one or more messages matching the specified severity mask.
Holds all the data relating to one schematic.
EMBEDDED_FILES * GetEmbeddedFiles() override
Base class for any item which can be embedded within the SCHEMATIC container class,...
bool ResolveExcludedFromSim(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const
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...
bool GetExcludedFromSim() const
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly) const override
Populate a std::vector with SCH_FIELDs, sorted in ordinal order.
std::unique_ptr< LIB_SYMBOL > & GetLibSymbolRef()
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const override
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this symbol.
const SPICE_GENERATOR & SpiceGenerator() const
std::string IbisDevice(const SPICE_ITEM &aItem, SCHEMATIC *aSchematic, const wxString &aCacheDir, REPORTER &aReporter) const
virtual std::string ModelName(const SPICE_ITEM &aItem) const
An 8 bit string that is assuredly encoded in UTF8, and supplies special conversion support to and fro...
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
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.
Helper functions to substitute paths with environmental variables.
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
must_if< error >::control< Rule > control
PGM_BASE & Pgm()
The global program "get" accessor.
wxString GetFieldValue(const std::vector< SCH_FIELD > *aFields, FIELD_T aFieldType)
#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 ?
std::vector< SCH_FIELD > fields
std::vector< std::string > pinNetNames
std::string baseModelName
std::vector< std::string > pinNumbers
@ USER
The field ID hasn't been set yet; field is invalid.
@ REFERENCE
Field Reference of part, i.e. "IC21".
wxString result
Test unit parsing edge cases and error handling.