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;
138 cacheDir.AppendDir( wxT(
"ibis" ) );
140 if( !cacheDir.DirExists() )
142 cacheDir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
144 if( !cacheDir.DirExists() )
146 wxLogTrace( wxT(
"IBIS_CACHE:" ),
147 wxT(
"%s:%s:%d\n * failed to create ibis cache directory '%s'" ),
148 __FILE__, __FUNCTION__, __LINE__, cacheDir.GetPath() );
155 wxString dirName = cacheDir.GetFullPath();
157 if( !dir.Open( dirName ) )
161 wxArrayString fileList;
162 wxString fileSpec = wxT(
"*.cache" );
164 thisFile.SetPath( dirName );
166 size_t numFilesFound = wxDir::GetAllFiles( dirName, &fileList, fileSpec );
168 for(
size_t ii = 0; ii < numFilesFound; ii++ )
171 thisFile.SetFullName( fileList[ii] );
172 wxRemoveFile( thisFile.GetFullPath() );
187 std::vector<PIN_INFO> pins =
CreatePinList( symbol, sheet,
true );
194 spiceItem.
fields.back().SetText( symbol->
GetRef( &sheet ) );
196 spiceItem.
fields.back().SetText( field.GetShownText( &sheet,
false ) );
199 readRefName( sheet, *symbol, spiceItem, refNames );
200 readModel( sheet, *symbol, spiceItem, aReporter );
206 m_items.push_back( std::move( spiceItem ) );
222 std::unique_ptr<MARKUP::NODE> root = markupParser.
Parse();
224 std::function<void(
const std::unique_ptr<MARKUP::NODE>&)> convertMarkup =
225 [&](
const std::unique_ptr<MARKUP::NODE>& aNode )
229 if( !aNode->is_root() )
231 if( aNode->isOverbar() )
236 else if( aNode->isSubscript() || aNode->isSuperscript() )
241 if( aNode->has_content() )
242 *aNetName += aNode->string();
245 for(
const std::unique_ptr<MARKUP::NODE>& child : aNode->children )
246 convertMarkup( child );
250 *aNetName = wxEmptyString;
251 convertMarkup( root );
254 aNetName->Replace(
'%',
'_' );
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(
' ',
'_' );
267 if( aNetName->StartsWith( wxS(
"//" ) ) )
268 aNetName->Replace( wxS(
"//" ), wxS(
"/root/" ),
false );
275 return item->model->SpiceGenerator().ItemName( *item );
277 return wxEmptyString;
283 const std::string refName = aRefName.ToStdString();
284 const std::list<SPICE_ITEM>& spiceItems =
GetItems();
286 auto it = std::find_if( spiceItems.begin(), spiceItems.end(),
289 return item.refName == refName;
292 if( it != spiceItems.end() )
307 for(
SCH_ITEM* item : sheet.LastScreen()->Items() )
309 if( item->ResolveExcludedFromSim() )
313 text =
static_cast<SCH_TEXT*
>( item )->GetShownText( &sheet,
false );
315 text =
static_cast<SCH_TEXTBOX*
>( item )->GetShownText(
nullptr, &sheet,
false );
320 wxStringTokenizer tokenizer(
text,
"\r\n", wxTOKEN_STRTOK );
321 bool foundDirective =
false;
324 [](
const wxString& line,
const wxString& dir )
326 return line == dir || line.StartsWith( dir + wxS(
" " ) );
329 while( tokenizer.HasMoreTokens() )
331 wxString line = tokenizer.GetNextToken().Upper();
333 if( line.StartsWith( wxT(
"." ) ) )
335 if( isDirective( line, wxS(
".AC" ) )
336 || isDirective( line, wxS(
".CONTROL" ) )
337 || isDirective( line, wxS(
".CSPARAM" ) )
338 || isDirective( line, wxS(
".DISTO" ) )
339 || isDirective( line, wxS(
".DC" ) )
340 || isDirective( line, wxS(
".ELSE" ) )
341 || isDirective( line, wxS(
".ELSEIF" ) )
342 || isDirective( line, wxS(
".END" ) )
343 || isDirective( line, wxS(
".ENDC" ) )
344 || isDirective( line, wxS(
".ENDIF" ) )
345 || isDirective( line, wxS(
".ENDS" ) )
346 || isDirective( line, wxS(
".FOUR" ) )
347 || isDirective( line, wxS(
".FUNC" ) )
348 || isDirective( line, wxS(
".GLOBAL" ) )
349 || isDirective( line, wxS(
".IC" ) )
350 || isDirective( line, wxS(
".IF" ) )
351 || isDirective( line, wxS(
".INCLUDE" ) )
352 || isDirective( line, wxS(
".LIB" ) )
353 || isDirective( line, wxS(
".MEAS" ) )
354 || isDirective( line, wxS(
".MODEL" ) )
355 || isDirective( line, wxS(
".NODESET" ) )
356 || isDirective( line, wxS(
".NOISE" ) )
357 || isDirective( line, wxS(
".OP" ) )
358 || isDirective( line, wxS(
".OPTIONS" ) )
359 || isDirective( line, wxS(
".PARAM" ) )
360 || isDirective( line, wxS(
".PLOT" ) )
361 || isDirective( line, wxS(
".PRINT" ) )
362 || isDirective( line, wxS(
".PROBE" ) )
363 || isDirective( line, wxS(
".PZ" ) )
364 || isDirective( line, wxS(
".SAVE" ) )
365 || isDirective( line, wxS(
".SENS" ) )
366 || isDirective( line, wxS(
".SP" ) )
367 || isDirective( line, wxS(
".SUBCKT" ) )
368 || isDirective( line, wxS(
".TEMP" ) )
369 || isDirective( line, wxS(
".TF" ) )
370 || isDirective( line, wxS(
".TITLE" ) )
371 || isDirective( line, wxS(
".TRAN" ) )
372 || isDirective( line, wxS(
".WIDTH" ) ) )
374 foundDirective =
true;
378 else if( line.StartsWith( wxT(
"K" ) ) )
381 wxStringTokenizer line_t( line,
" \t", wxTOKEN_STRTOK );
384 if( !line_t.HasMoreTokens() || !line_t.GetNextToken().StartsWith( wxT(
"K" ) ) )
388 if( !line_t.HasMoreTokens() || !line_t.GetNextToken().StartsWith( wxT(
"L" ) ) )
392 if( !line_t.HasMoreTokens() || !line_t.GetNextToken().StartsWith( wxT(
"L" ) ) )
397 if( line_t.HasMoreTokens() )
399 foundDirective =
true;
417 return wxEmptyString;
419 wxString ref = aSymbol.
GetRef( &aSheet );
420 std::vector<std::pair<wxString, wxString>> pinList;
421 std::set<wxString> pinNumbers;
424 auto parsePins = [&](
const wxString& aPins )
426 wxStringTokenizer tokenizer( aPins, wxS(
" \t\r\n" ), wxTOKEN_STRTOK );
428 while( tokenizer.HasMoreTokens() )
430 wxString token = tokenizer.GetNextToken();
431 int pos = token.Find( wxS(
'=' ) );
433 if( pos == wxNOT_FOUND )
436 wxString pinNumber = token.Left( pos );
437 wxString modelPin = token.Mid( pos + 1 );
440 if( pinNumbers.insert( pinNumber ).second )
441 pinList.emplace_back( pinNumber, modelPin );
447 parsePins( pinsField->GetShownText( &aSheet,
false ) );
456 if( other == &aSymbol )
459 if( other->
GetRef( &sheet ) != ref )
463 parsePins( pinsField->GetShownText( &sheet,
false ) );
469 if( pinList.empty() )
470 return wxEmptyString;
475 for(
const auto& [pinNumber, modelPin] : pinList )
477 if( !merged.IsEmpty() )
478 merged += wxS(
" " );
480 merged += pinNumber + wxS(
"=" ) + modelPin;
488 SPICE_ITEM& aItem, std::set<std::string>& aRefNames )
492 if( !aRefNames.insert( aItem.
refName ).second )
493 wxASSERT( wxT(
"Duplicate refdes encountered; what happened to ReadyToNetlist()?" ) );
516 int libParamIndex =
static_cast<int>( SIM_MODEL_RAW_SPICE::SPICE_PARAM::LIB );
517 wxString
path = rawSpiceModel->GetParam( libParamIndex ).value;
519 if( !
path.IsEmpty() )
526 cacheFn.AppendDir( wxT(
"ibis" ) );
527 cacheFn.SetFullName( aSymbol.
GetRef( &aSheet ) + wxT(
".cache" ) );
529 wxFile cacheFile( cacheFn.GetFullPath(), wxFile::write );
531 if( !cacheFile.IsOpened() )
533 wxLogError(
_(
"Could not open file '%s' to write IBIS model" ),
534 cacheFn.GetFullPath() );
539 wxString cacheFilepath = cacheFn.GetPath( wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR );
541 cacheFilepath, aReporter );
543 cacheFile.Write( wxString( modelData ) );
550 const std::vector<PIN_INFO>& aPins )
558 const std::vector<PIN_INFO>& aPins,
int& aNcCounter )
560 for(
const PIN_INFO& pinInfo : aPins )
564 aItem.
pinNetNames.push_back( netName.ToStdString() );
571 std::vector<std::string>& aModifiers )
578 tao::pegtl::string_input<> in( input,
"Sim.NodesFormat field" );
579 std::unique_ptr<tao::pegtl::parse_tree::node> root;
580 std::string singleNodeModifier;
588 for(
const auto& node : root->children )
593 aModifiers.back().append( node->string() );
597 singleNodeModifier.append( node->string() );
602 aModifiers.push_back( singleNodeModifier );
603 singleNodeModifier.erase( singleNodeModifier.begin(), singleNodeModifier.end() );
607 catch(
const tao::pegtl::parse_error& e )
609 THROW_IO_ERROR( wxString::Format(
_(
"Error in parsing model '%s', error: '%s'" ),
615 std::vector<std::string> xspicePattern;
618 if( xspicePattern.empty() )
621 if( xspicePattern.size() != aItem.
pinNetNames.size() )
623 THROW_IO_ERROR( wxString::Format(
_(
"Error in parsing model '%s', wrong number of nodes "
624 "'?' in Sim.NodesFormat compared to connections" ),
631 for( std::string& pattern : xspicePattern )
634 const std::string netName =
" " + *itNetNames +
" ";
635 pattern.replace( pattern.find(
"?" ), 1, netName );
636 *itNetNames = pattern;
642 const wxString& aPath )
648 expandedPath.Replace(
'\\',
'/' );
657 if( fullPath.IsEmpty() )
659 wxLogError(
_(
"Could not find library file '%s'" ), expandedPath );
660 fullPath = expandedPath;
662 else if( wxFileName::GetPathSeparator() ==
'\\' )
665 fullPath.Replace(
'\\',
'/' );
670 fullPath = expandedPath;
673 aFormatter.
Print( 0,
".include \"%s\"\n",
TO_UTF8( fullPath ) );
694 if( !item.model->IsEnabled() )
697 aFormatter.
Print( 0,
"%s", item.model->SpiceGenerator().ModelLine( item ).c_str() );
706 if( !item.model->IsEnabled() )
709 aFormatter.
Print( 0,
"%s", item.model->SpiceGenerator().ItemLine( item ).c_str() );
718 aFormatter.
Print( 0,
".save all\n" );
721 aFormatter.
Print( 0,
".probe alli\n" );
729 std::string itemName = item.model->SpiceGenerator().ItemName( item );
731 if( ( item.model->GetPinCount() >= 2 ) && ( itemName.size() > 0 )
732 && ( itemName.c_str()[0] !=
'A' ) )
734 aFormatter.
Print( 0,
".probe p(%s)\n", itemName.c_str() );
740 [](
const wxString& candidate,
const wxString& dir )
742 return candidate == dir || candidate.StartsWith( dir + wxS(
" " ) );
747 bool simCommand =
false;
749 if( directive.StartsWith(
"." ) )
751 wxString candidate = directive.Upper();
753 simCommand = ( isSimCommand( candidate, wxS(
".AC" ) )
754 || isSimCommand( candidate, wxS(
".DC" ) )
755 || isSimCommand( candidate, wxS(
".TRAN" ) )
756 || isSimCommand( candidate, wxS(
".OP" ) )
757 || isSimCommand( candidate, wxS(
".DISTO" ) )
758 || isSimCommand( candidate, wxS(
".NOISE" ) )
759 || isSimCommand( candidate, wxS(
".PZ" ) )
760 || isSimCommand( candidate, wxS(
".SENS" ) )
761 || isSimCommand( candidate, wxS(
".TF" ) ) );
765 aFormatter.
Print( 0,
"%s\n",
UTF8( directive ).c_str() );
771 int& aNcCounter )
const
777 if( netName.IsEmpty() )
778 netName.Printf( wxS(
"NC-%d" ), aNcCounter++ );
793 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).
void readModel(SCH_SHEET_PATH &aSheet, SCH_SYMBOL &aSymbol, SPICE_ITEM &aItem, REPORTER &aReporter)
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
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.