Don't allow writing "//" to spice netlist.

Fixes https://gitlab.com/kicad/code/kicad/-/issues/18161
This commit is contained in:
Jeff Young 2024-06-10 15:35:27 +01:00
parent 0d2838518b
commit 2e38fa84bf
9 changed files with 76 additions and 85 deletions

View File

@ -108,7 +108,7 @@ DIALOG_SIM_COMMAND::DIALOG_SIM_COMMAND( SIMULATOR_FRAME* aParent,
// NoiseRef is optional
m_noiseRef->Append( wxEmptyString );
for( const std::string& net : m_circuitModel->GetNets() )
for( const wxString& net : m_circuitModel->GetNets() )
{
m_pzInput->Append( net );
m_pzInputRef->Append( net );
@ -287,7 +287,7 @@ wxString DIALOG_SIM_COMMAND::evaluateDCControls( wxChoice* aDcSource, wxTextCtrl
// pick device name from exporter when something different than temperature is selected
if( dcSource.Cmp( "TEMP" ) )
dcSource = m_circuitModel->GetItemName( std::string( dcSource.ToUTF8() ) );
dcSource = m_circuitModel->GetItemName( dcSource );
return wxString::Format( "%s %s %s %s", dcSource,
SPICE_VALUE( aDcStart->GetValue() ).ToSpiceString(),
@ -423,10 +423,7 @@ bool DIALOG_SIM_COMMAND::TransferDataFromWindow()
}
if( !ref.IsEmpty() )
{
ref = wxS( "," )
+ wxString( m_circuitModel->GetItemName( std::string( ref.ToUTF8() ) ) );
}
ref = wxS( "," ) + m_circuitModel->GetItemName( ref );
m_simCommand.Printf( ".noise v(%s%s) %s %s %s %s %s %s",
output,

View File

@ -3,7 +3,7 @@
*
* Copyright (C) 1992-2013 jp.charras at wanadoo.fr
* Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* Copyright (C) 1992-2023 KiCad Developers, see AUTHORS.TXT for contributors.
* Copyright (C) 1992-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
@ -239,11 +239,10 @@ bool NETLIST_EXPORTER_SPICE::ReadSchematicAndLibraries( unsigned aNetlistOptions
}
void NETLIST_EXPORTER_SPICE::ConvertToSpiceMarkup( std::string& aNetName )
void NETLIST_EXPORTER_SPICE::ConvertToSpiceMarkup( wxString* aNetName )
{
MARKUP::MARKUP_PARSER markupParser( aNetName );
MARKUP::MARKUP_PARSER markupParser( aNetName->ToStdString() );
std::unique_ptr<MARKUP::NODE> root = markupParser.Parse();
std::string converted;
std::function<void( const std::unique_ptr<MARKUP::NODE>&)> convertMarkup =
[&]( const std::unique_ptr<MARKUP::NODE>& aNode )
@ -255,7 +254,7 @@ void NETLIST_EXPORTER_SPICE::ConvertToSpiceMarkup( std::string& aNetName )
if( aNode->isOverbar() )
{
// ~{CLK} is a different signal than CLK
converted += '~';
*aNetName += '~';
}
else if( aNode->isSubscript() || aNode->isSuperscript() )
{
@ -263,7 +262,7 @@ void NETLIST_EXPORTER_SPICE::ConvertToSpiceMarkup( std::string& aNetName )
}
if( aNode->has_content() )
converted += aNode->string();
*aNetName += aNode->string();
}
for( const std::unique_ptr<MARKUP::NODE>& child : aNode->children )
@ -271,43 +270,46 @@ void NETLIST_EXPORTER_SPICE::ConvertToSpiceMarkup( std::string& aNetName )
}
};
*aNetName = wxEmptyString;
convertMarkup( root );
// Replace all ngspice-disallowed chars in netnames by a '_'
std::replace( converted.begin(), converted.end(), '%', '_' );
std::replace( converted.begin(), converted.end(), '(', '_' );
std::replace( converted.begin(), converted.end(), ')', '_' );
std::replace( converted.begin(), converted.end(), ',', '_' );
std::replace( converted.begin(), converted.end(), '[', '_' );
std::replace( converted.begin(), converted.end(), ']', '_' );
std::replace( converted.begin(), converted.end(), '<', '_' );
std::replace( converted.begin(), converted.end(), '>', '_' );
std::replace( converted.begin(), converted.end(), '~', '_' );
std::replace( converted.begin(), converted.end(), ' ', '_' );
aNetName->Replace( '%', '_' );
aNetName->Replace( '(', '_' );
aNetName->Replace( ')', '_' );
aNetName->Replace( ',', '_' );
aNetName->Replace( '[', '_' );
aNetName->Replace( ']', '_' );
aNetName->Replace( '<', '_' );
aNetName->Replace( '>', '_' );
aNetName->Replace( '~', '_' );
aNetName->Replace( ' ', '_' );
aNetName = converted;
// A net name on the root sheet with a label '/foo' is going to get titled "//foo". This
// will trip up ngspice as "//" opens a line comment.
if( aNetName->StartsWith( wxS( "//" ) ) )
aNetName->Replace( wxS( "//" ), wxS( "/root/" ), false /* replace all */ );
}
std::string NETLIST_EXPORTER_SPICE::GetItemName( const std::string& aRefName ) const
wxString NETLIST_EXPORTER_SPICE::GetItemName( const wxString& aRefName ) const
{
const SPICE_ITEM* item = FindItem( aRefName );
if( !item )
return "";
if( const SPICE_ITEM* item = FindItem( aRefName ) )
return item->model->SpiceGenerator().ItemName( *item );
return wxEmptyString;
}
const SPICE_ITEM* NETLIST_EXPORTER_SPICE::FindItem( const std::string& aRefName ) const
const SPICE_ITEM* NETLIST_EXPORTER_SPICE::FindItem( const wxString& aRefName ) const
{
const std::string refName = aRefName.ToStdString();
const std::list<SPICE_ITEM>& spiceItems = GetItems();
auto it = std::find_if( spiceItems.begin(), spiceItems.end(),
[aRefName]( const SPICE_ITEM& item )
[refName]( const SPICE_ITEM& item )
{
return item.refName == aRefName;
return item.refName == refName;
} );
if( it != spiceItems.end() )
@ -502,12 +504,14 @@ void NETLIST_EXPORTER_SPICE::readPinNetNames( SCH_SYMBOL& aSymbol, SPICE_ITEM& a
{
for( const PIN_INFO& pinInfo : aPins )
{
std::string netName = GenerateItemPinNetName( pinInfo.netName.ToStdString(), aNcCounter );
wxString netName = GenerateItemPinNetName( pinInfo.netName, aNcCounter );
aItem.pinNetNames.push_back( netName );
aItem.pinNetNames.push_back( netName.ToStdString() );
m_nets.insert( netName );
}
}
void NETLIST_EXPORTER_SPICE::getNodePattern( SPICE_ITEM& aItem,
std::vector<std::string>& aModifiers )
{
@ -710,16 +714,15 @@ void NETLIST_EXPORTER_SPICE::WriteDirectives( const wxString& aSimCommand, unsig
}
std::string NETLIST_EXPORTER_SPICE::GenerateItemPinNetName( const std::string& aNetName,
wxString NETLIST_EXPORTER_SPICE::GenerateItemPinNetName( const wxString& aNetName,
int& aNcCounter ) const
{
std::string netName = aNetName;
wxString netName = UnescapeString( aNetName );
ConvertToSpiceMarkup( netName );
netName = std::string( UnescapeString( netName ).ToUTF8() );
ConvertToSpiceMarkup( &netName );
if( netName == "" )
netName = fmt::format( "NC-{}", aNcCounter++ );
if( netName.IsEmpty() )
netName.Printf( wxS( "NC-%d" ), aNcCounter++ );
return netName;
}

View File

@ -102,12 +102,12 @@ public:
/**
* Remove formatting wrappers and replace illegal spice net name characters with underscores.
*/
static void ConvertToSpiceMarkup( std::string& aNetName );
static void ConvertToSpiceMarkup( wxString* aNetName );
/**
* Return the list of nets.
*/
std::set<std::string> GetNets() const { return m_nets; }
std::set<wxString> GetNets() const { return m_nets; }
/**
* Return name of Spice device corresponding to a schematic symbol.
@ -118,7 +118,7 @@ public:
* corresponds to the assigned device model type or a reference prefixed with a character
* defining the device model type.
*/
std::string GetItemName( const std::string& aRefName ) const;
wxString GetItemName( const wxString& aRefName ) const;
/**
* Return the list of items representing schematic symbols in the Spice world.
@ -128,7 +128,7 @@ public:
/**
* Find and return the item corresponding to \a aRefName.
*/
const SPICE_ITEM* FindItem( const std::string& aRefName ) const;
const SPICE_ITEM* FindItem( const wxString& aRefName ) const;
const std::vector<wxString>& GetDirectives() { return m_directives; }
@ -137,8 +137,7 @@ protected:
virtual void WriteDirectives( const wxString& aSimCommand, unsigned aSimOptions,
OUTPUTFORMATTER& candidate ) const;
virtual std::string GenerateItemPinNetName( const std::string& aNetName,
int& aNcCounter ) const;
virtual wxString GenerateItemPinNetName( const wxString& aNetName, int& aNcCounter ) const;
/**
* Return the paths of exported sheets (either all or the current one).
@ -167,11 +166,9 @@ private:
SIM_LIB_MGR m_libMgr; ///< Holds libraries and models
NAME_GENERATOR m_modelNameGenerator; ///< Generates unique model names
///< Generates unique net names (only unique for NC nets for now)
NAME_GENERATOR m_netNameGenerator;
std::vector<wxString> m_directives; ///< Spice directives found in the schematic sheet
std::set<wxString> m_rawIncludes; ///< include directives found in symbols
std::set<std::string> m_nets;
std::set<wxString> m_nets;
///< Items representing schematic symbols in Spice world.
std::list<SPICE_ITEM> m_items;

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2022 Mikolaj Wielgus.
* Copyright (C) 2022 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2022-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
@ -24,7 +24,6 @@
#include "netlist_exporter_spice_model.h"
#include <sch_screen.h>
#include <sch_label.h>
#include <string_utils.h>
@ -37,22 +36,19 @@ void NETLIST_EXPORTER_SPICE_MODEL::WriteHead( OUTPUTFORMATTER& aFormatter,
for( auto const& [key, port] : m_ports )
{
std::string portDir;
wxString portDir;
switch( port.dir )
switch( port.m_dir )
{
case L_INPUT: portDir = "input"; break;
case L_OUTPUT: portDir = "output"; break;
case L_BIDI: portDir = "inout"; break;
case L_TRISTATE: portDir = "tristate"; break;
case L_UNSPECIFIED: portDir = "passive"; break;
default:
wxFAIL_MSG( "Invalid port direction" );
break;
default: wxFAIL_MSG( "Invalid port direction" ); break;
}
aFormatter.Print( 0, "+ %s ; %s\n", port.name.c_str(), portDir.c_str() );
aFormatter.Print( 0, "+ %s ; %s\n", TO_UTF8( port.m_name ), TO_UTF8( portDir ) );
}
aFormatter.Print( 0, "\n\n" );
@ -75,13 +71,13 @@ bool NETLIST_EXPORTER_SPICE_MODEL::ReadSchematicAndLibraries( unsigned aNetlistO
}
std::string NETLIST_EXPORTER_SPICE_MODEL::GenerateItemPinNetName( const std::string& aNetName,
wxString NETLIST_EXPORTER_SPICE_MODEL::GenerateItemPinNetName( const wxString& aNetName,
int& aNcCounter ) const
{
std::string netName = aNetName;
wxString netName = aNetName;
if( m_ports.count( netName ) )
netName = m_ports.at( netName ).name;
netName = m_ports.at( netName).m_name;
return NETLIST_EXPORTER_SPICE::GenerateItemPinNetName( netName, aNcCounter );
}
@ -98,10 +94,7 @@ void NETLIST_EXPORTER_SPICE_MODEL::readPorts( unsigned aNetlistOptions )
if( SCH_CONNECTION* conn = label->Connection( &sheet ) )
{
wxString labelText = label->GetShownText( &sheet, false );
m_ports.insert( {
std::string( conn->Name().ToUTF8() ),
PORT_INFO{ std::string( labelText.ToUTF8() ), label->GetShape() }
} );
m_ports.insert( { conn->Name(), PORT_INFO{ labelText, label->GetShape() } } );
}
}
}

View File

@ -42,18 +42,18 @@ public:
bool ReadSchematicAndLibraries( unsigned aNetlistOptions, REPORTER& aReporter ) override;
protected:
std::string GenerateItemPinNetName( const std::string& aNetName, int& aNcCounter ) const override;
wxString GenerateItemPinNetName( const wxString& aNetName, int& aNcCounter ) const override;
private:
struct PORT_INFO
{
std::string name;
LABEL_FLAG_SHAPE dir;
wxString m_name;
LABEL_FLAG_SHAPE m_dir;
};
void readPorts( unsigned aNetlistOptions );
std::map<std::string, PORT_INFO> m_ports;
std::map<wxString, PORT_INFO> m_ports;
};
#endif // NETLIST_EXPORTER_SPICE_MODEL_H

View File

@ -706,10 +706,10 @@ void SCHEMATIC::RecomputeIntersheetRefs( const std::function<void( SCH_GLOBALLAB
wxString SCHEMATIC::GetOperatingPoint( const wxString& aNetName, int aPrecision,
const wxString& aRange )
{
std::string spiceNetName( aNetName.Lower().ToStdString() );
NETLIST_EXPORTER_SPICE::ConvertToSpiceMarkup( spiceNetName );
wxString spiceNetName( aNetName.Lower() );
NETLIST_EXPORTER_SPICE::ConvertToSpiceMarkup( &spiceNetName );
if( spiceNetName == "gnd" || spiceNetName == "0" )
if( spiceNetName == wxS( "gnd" ) || spiceNetName == wxS( "0" ) )
return wxEmptyString;
auto it = m_operatingPoints.find( spiceNetName );

View File

@ -884,10 +884,11 @@ void SIMULATOR_FRAME_UI::rebuildSignalsList()
if( ( options & NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_VOLTAGES )
&& ( simType == ST_TRAN || simType == ST_DC || simType == ST_AC || simType == ST_FFT) )
{
for( const std::string& net : circuitModel()->GetNets() )
for( const wxString& net : circuitModel()->GetNets() )
{
// netnames are escaped (can contain "{slash}" for '/') Unscape them:
wxString netname = UnescapeString( net );
NETLIST_EXPORTER_SPICE::ConvertToSpiceMarkup( &netname );
if( netname == "GND" || netname == "0" || netname.StartsWith( unconnected ) )
continue;
@ -1425,7 +1426,7 @@ void SIMULATOR_FRAME_UI::AddTuner( const SCH_SHEET_PATH& aSheetPath, SCH_SYMBOL*
return;
}
const SPICE_ITEM* item = GetExporter()->FindItem( std::string( ref.ToUTF8() ) );
const SPICE_ITEM* item = GetExporter()->FindItem( ref );
// Do nothing if the symbol is not tunable.
if( !item || !item->model->GetTunerParam() )
@ -1874,7 +1875,7 @@ void SIMULATOR_FRAME_UI::applyTuners()
continue;
}
const SPICE_ITEM* item = GetExporter()->FindItem( tuner->GetSymbolRef().ToStdString() );
const SPICE_ITEM* item = GetExporter()->FindItem( tuner->GetSymbolRef() );
if( !item || !item->model->GetTunerParam() )
{

View File

@ -618,8 +618,8 @@ int SCH_EDITOR_CONTROL::SimProbe( const TOOL_EVENT& aEvent )
{
if( SCH_CONNECTION* conn = static_cast<SCH_ITEM*>( item )->Connection() )
{
std::string spiceNet = UnescapeString( conn->Name() ).ToStdString();
NETLIST_EXPORTER_SPICE::ConvertToSpiceMarkup( spiceNet );
wxString spiceNet = UnescapeString( conn->Name() );
NETLIST_EXPORTER_SPICE::ConvertToSpiceMarkup( &spiceNet );
simFrame->AddVoltageTrace( wxString::Format( "V(%s)", spiceNet ) );
}

View File

@ -50,7 +50,7 @@ TUNER_SLIDER::TUNER_SLIDER( SIMULATOR_FRAME_UI* aFrame, wxWindow* aParent,
m_value( 0.0 ),
m_frame( aFrame )
{
const SPICE_ITEM* item = m_frame->GetExporter()->FindItem( std::string( m_ref.ToUTF8() ) );
const SPICE_ITEM* item = m_frame->GetExporter()->FindItem( m_ref );
if( !item )
throw KI_PARAM_ERROR( wxString::Format( _( "%s not found" ), m_ref ) );