diff --git a/demos/simulation/sallen_key/sallen_key.kicad_pro b/demos/simulation/sallen_key/sallen_key.kicad_pro index d59f7569e8..cdf16f378a 100644 --- a/demos/simulation/sallen_key/sallen_key.kicad_pro +++ b/demos/simulation/sallen_key/sallen_key.kicad_pro @@ -123,6 +123,7 @@ "equivalence_files": [] }, "erc": { + "erc_exclusions": [], "meta": { "version": 0 }, @@ -133,6 +134,7 @@ 0, 0, 0, + 0, 1, 0, 0, @@ -146,6 +148,7 @@ 0, 1, 0, + 0, 1, 0, 2, @@ -159,6 +162,7 @@ 0, 0, 0, + 0, 1, 0, 1, @@ -172,6 +176,7 @@ 0, 0, 0, + 0, 1, 1, 2, @@ -185,6 +190,7 @@ 0, 0, 0, + 0, 1, 0, 0, @@ -192,12 +198,27 @@ 0, 2 ], + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2 + ], [ 1, 1, 1, 1, 1, + 0, 1, 1, 1, @@ -211,6 +232,7 @@ 0, 1, 0, + 0, 1, 0, 0, @@ -224,6 +246,7 @@ 1, 2, 0, + 0, 1, 0, 2, @@ -237,6 +260,7 @@ 0, 1, 0, + 0, 1, 0, 2, @@ -250,6 +274,7 @@ 1, 1, 0, + 0, 1, 0, 2, @@ -268,6 +293,7 @@ 2, 2, 2, + 2, 2 ] ], @@ -278,7 +304,9 @@ "bus_to_net_conflict": "error", "different_unit_footprint": "error", "different_unit_net": "error", + "duplicate_reference": "error", "duplicate_sheet_names": "error", + "extra_units": "error", "global_label_dangling": "warning", "hier_label_mismatch": "error", "label_dangling": "error", @@ -292,6 +320,8 @@ "pin_to_pin": "warning", "power_pin_not_driven": "error", "similar_labels": "warning", + "unannotated": "error", + "unit_value_mismatch": "error", "unresolved_variable": "error", "wire_dangling": "error" } @@ -348,6 +378,7 @@ "default_text_size": 60.0, "default_wire_thickness": 6.0, "field_names": [], + "intersheets_ref_own_page": false, "intersheets_ref_prefix": "", "intersheets_ref_short": false, "intersheets_ref_show": false, @@ -362,6 +393,12 @@ "version": 0 }, "net_format_name": "", + "ngspice": { + "meta": { + "version": 0 + }, + "model_mode": 0 + }, "page_layout_descr_file": "", "plot_directory": "", "spice_adjust_passive_values": false, diff --git a/eeschema/dialogs/dialog_sim_settings.cpp b/eeschema/dialogs/dialog_sim_settings.cpp index 0e58b02c1f..173f721424 100644 --- a/eeschema/dialogs/dialog_sim_settings.cpp +++ b/eeschema/dialogs/dialog_sim_settings.cpp @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2016-2021 CERN - * Copyright (C) 2016-2021 KiCad Developers, see CHANGELOG.TXT for contributors. + * Copyright (C) 2016-2021 KiCad Developers, see AUTHORS.txt for contributors. * @author Maciej Suminski * * This program is free software; you can redistribute it and/or @@ -25,6 +25,8 @@ #include "dialog_sim_settings.h" #include +#include + #include #include @@ -54,9 +56,11 @@ static wxString getStringSelection( const wxRadioBox* aCtrl ) } -DIALOG_SIM_SETTINGS::DIALOG_SIM_SETTINGS( wxWindow* aParent ) : +DIALOG_SIM_SETTINGS::DIALOG_SIM_SETTINGS( wxWindow* aParent, + std::shared_ptr& aSettings ) : DIALOG_SIM_SETTINGS_BASE( aParent ), m_exporter( nullptr ), + m_settings( aSettings ), m_spiceEmptyValidator( true ) { m_posIntValidator.SetMin( 1 ); @@ -91,6 +95,9 @@ DIALOG_SIM_SETTINGS::DIALOG_SIM_SETTINGS( wxWindow* aParent ) : m_simPages->RemovePage( m_simPages->FindPage( m_pgSensitivity ) ); m_simPages->RemovePage( m_simPages->FindPage( m_pgTransferFunction ) ); + if( dynamic_cast( aSettings.get() ) == nullptr ) + m_simPages->RemovePage( m_simPages->FindPage( m_pgNgspice ) ); + m_sdbSizerOK->SetDefault(); updateNetlistOpts(); @@ -155,6 +162,26 @@ bool DIALOG_SIM_SETTINGS::TransferDataFromWindow() if( !wxDialog::TransferDataFromWindow() ) return false; + // The simulator dependent settings always get transferred. + NGSPICE_SIMULATOR_SETTINGS* ngspiceSettings = + dynamic_cast( m_settings.get() ); + + if( ngspiceSettings ) + { + if( m_rbNgspiceDefaultModelMode->GetValue() ) + ngspiceSettings->SetModelMode( NGSPICE_MODEL_MODE::USER_CONFIG ); + else if( m_rbNgspiceSpiceModelMode->GetValue() ) + ngspiceSettings->SetModelMode( NGSPICE_MODEL_MODE::NGSPICE ); + else if( m_rbNgspicePSpiceModelMode->GetValue() ) + ngspiceSettings->SetModelMode( NGSPICE_MODEL_MODE::PSPICE ); + else if( m_rbNgspiceLTSpiceModelMode->GetValue() ) + ngspiceSettings->SetModelMode( NGSPICE_MODEL_MODE::LTSPICE ); + else if( m_rbNgspicePLTSpiceModelMode->GetValue() ) + ngspiceSettings->SetModelMode( NGSPICE_MODEL_MODE::LT_PSPICE ); + else if( m_rbNgspiceHSpiceModelMode->GetValue() ) + ngspiceSettings->SetModelMode( NGSPICE_MODEL_MODE::HSPICE ); + } + wxWindow* page = m_simPages->GetCurrentPage(); // AC analysis @@ -279,10 +306,48 @@ bool DIALOG_SIM_SETTINGS::TransferDataFromWindow() bool DIALOG_SIM_SETTINGS::TransferDataToWindow() { - /// @todo one day it could interpret the sim command and fill out appropriate fields.. + /// @todo one day it could interpret the sim command and fill out appropriate fields. if( empty( m_customTxt ) ) loadDirectives(); + NGSPICE_SIMULATOR_SETTINGS* ngspiceSettings = + dynamic_cast( m_settings.get() ); + + if( ngspiceSettings ) + { + switch( ngspiceSettings->GetModelMode() ) + { + case NGSPICE_MODEL_MODE::USER_CONFIG: + m_rbNgspiceDefaultModelMode->SetValue( true ); + break; + + case NGSPICE_MODEL_MODE::NGSPICE: + m_rbNgspiceSpiceModelMode->SetValue( true ); + break; + + case NGSPICE_MODEL_MODE::PSPICE: + m_rbNgspicePSpiceModelMode->SetValue( true ); + break; + + case NGSPICE_MODEL_MODE::LTSPICE: + m_rbNgspiceLTSpiceModelMode->SetValue( true ); + break; + + case NGSPICE_MODEL_MODE::LT_PSPICE: + m_rbNgspicePLTSpiceModelMode->SetValue( true ); + break; + + case NGSPICE_MODEL_MODE::HSPICE: + m_rbNgspiceHSpiceModelMode->SetValue( true ); + break; + + default: + wxFAIL_MSG( wxString::Format( "Unkown NGSPICE_MODEL_MODE %d.", + ngspiceSettings->GetModelMode() ) ); + break; + } + } + if( m_simCommand.IsEmpty() && !empty( m_customTxt ) ) return parseCommand( m_customTxt->GetValue() ); @@ -320,6 +385,7 @@ int DIALOG_SIM_SETTINGS::ShowModal() return DIALOG_SIM_SETTINGS_BASE::ShowModal(); } + void DIALOG_SIM_SETTINGS::updateDCSources( wxChar aType, wxChoice* aSource ) { wxString prevSelection; @@ -396,7 +462,6 @@ bool DIALOG_SIM_SETTINGS::parseCommand( const wxString& aCommand ) m_acFreqStart->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() ); m_acFreqStop->SetValue( SPICE_VALUE( tokenizer.GetNextToken() ).ToSpiceString() ); } - else if( tkn == ".dc" ) { SPICE_DC_PARAMS src1, src2; @@ -436,7 +501,6 @@ bool DIALOG_SIM_SETTINGS::parseCommand( const wxString& aCommand ) refreshUIControls(); } - else if( tkn == ".tran" ) { m_simPages->SetSelection( m_simPages->FindPage( m_pgTransient ) ); @@ -451,14 +515,11 @@ bool DIALOG_SIM_SETTINGS::parseCommand( const wxString& aCommand ) if( !tkn.IsEmpty() ) m_transInitial->SetValue( SPICE_VALUE( tkn ).ToSpiceString() ); } - else if( tkn == ".op" ) { m_simPages->SetSelection( m_simPages->FindPage( m_pgOP ) ); } - - // Custom directives - else if( !empty( m_customTxt ) ) + else if( !empty( m_customTxt ) ) // Custom directives { m_simPages->SetSelection( m_simPages->FindPage( m_pgCustom ) ); } diff --git a/eeschema/dialogs/dialog_sim_settings.h b/eeschema/dialogs/dialog_sim_settings.h index 87af776937..c78ee41600 100644 --- a/eeschema/dialogs/dialog_sim_settings.h +++ b/eeschema/dialogs/dialog_sim_settings.h @@ -2,6 +2,8 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2016 CERN + * Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors. + * * @author Maciej Suminski * * This program is free software; you can redistribute it and/or @@ -31,11 +33,13 @@ #include class NETLIST_EXPORTER_PSPICE_SIM; +class SPICE_SIMULATOR_SETTINGS; + class DIALOG_SIM_SETTINGS : public DIALOG_SIM_SETTINGS_BASE { public: - DIALOG_SIM_SETTINGS( wxWindow* aParent ); + DIALOG_SIM_SETTINGS( wxWindow* aParent, std::shared_ptr& aSettings ); const wxString& GetSimCommand() const { @@ -67,7 +71,7 @@ public: // The default dialog Validate() calls the validators of all widgets. // This is not what we want; We want only validators of the selected page - // of the notbooks. So disable the wxDialog::Validate(), and let our + // of the notebooks. So disable the wxDialog::Validate(), and let our // TransferDataFromWindow doing the job. virtual bool Validate() override { @@ -84,7 +88,7 @@ private: LINEAR }; - ///!> Generates events to update UI state + ///< Generate events to update UI state. void refreshUIControls() { wxQueueEvent( m_dcEnable2, new wxCommandEvent( wxEVT_CHECKBOX ) ); @@ -93,19 +97,20 @@ private: } /** - * @brief Reads values from one DC sweep source to form a part of sim command + * Read values from one DC sweep source to form a part of simulation command. + * * @return string of four SPICE values if values are correct, empty string upon error. */ wxString evaluateDCControls( wxChoice* aDcSource, wxTextCtrl* aDcStart, wxTextCtrl* aDcStop, wxTextCtrl* aDcIncr ); /** - * @brief Updates DC sweep source with components from schematic + * Update DC sweep source with components from schematic. */ void updateDCSources( wxChar aType, wxChoice* aSource ); /** - * @brief Updates units on labels depending on selected source + * Update units on labels depending on selected source. */ void updateDCUnits( wxChar aType, wxChoice* aSource, wxStaticText* aStartValUnit, wxStaticText* aEndValUnit, wxStaticText* aStepUnit ); @@ -120,9 +125,10 @@ private: } /** - * @brief Parses a Spice directive. + * Parse a Spice directive. + * * @param aCommand is the directive to be parsed (e.g. ".tran 10n 1000n"). - * @return bool if the directive was parsed correctly. + * @return true if the directive was parsed correctly. */ bool parseCommand( const wxString& aCommand ); @@ -155,14 +161,14 @@ private: { switch( aOption ) { - case DECADE: - return wxString( "dec" ); + case DECADE: + return wxString( "dec" ); - case OCTAVE: - return wxString( "oct" ); + case OCTAVE: + return wxString( "oct" ); - case LINEAR: - return wxString( "lin" ); + case LINEAR: + return wxString( "lin" ); } wxASSERT_MSG( false, "Unhandled scale type" ); @@ -176,7 +182,7 @@ private: wxString m_simCommand; int m_netlistOpts; NETLIST_EXPORTER_PSPICE_SIM* m_exporter; - + std::shared_ptr m_settings; SPICE_VALIDATOR m_spiceValidator; SPICE_VALIDATOR m_spiceEmptyValidator; wxIntegerValidator m_posIntValidator; diff --git a/eeschema/dialogs/dialog_sim_settings_base.cpp b/eeschema/dialogs/dialog_sim_settings_base.cpp index 237f7d46e9..fa925800c8 100644 --- a/eeschema/dialogs/dialog_sim_settings_base.cpp +++ b/eeschema/dialogs/dialog_sim_settings_base.cpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version Oct 26 2018) +// C++ code generated with wxFormBuilder (version 3.9.0 Jun 18 2020) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! @@ -17,8 +17,6 @@ DIALOG_SIM_SETTINGS_BASE::DIALOG_SIM_SETTINGS_BASE( wxWindow* parent, wxWindowID bSizer1 = new wxBoxSizer( wxVERTICAL ); m_simPages = new wxNotebook( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); - m_simPages->SetMinSize( wxSize( 650,-1 ) ); - m_pgAC = new wxPanel( m_simPages, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); wxBoxSizer* bSizer3; bSizer3 = new wxBoxSizer( wxVERTICAL ); @@ -199,7 +197,7 @@ DIALOG_SIM_SETTINGS_BASE::DIALOG_SIM_SETTINGS_BASE( wxWindow* parent, wxWindowID m_pgDC->SetSizer( bSizer82 ); m_pgDC->Layout(); bSizer82->Fit( m_pgDC ); - m_simPages->AddPage( m_pgDC, _("DC Transfer"), true ); + m_simPages->AddPage( m_pgDC, _("DC Transfer"), false ); m_pgDistortion = new wxPanel( m_simPages, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); m_simPages->AddPage( m_pgDistortion, _("Distortion"), false ); m_pgNoise = new wxPanel( m_simPages, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); @@ -417,6 +415,51 @@ DIALOG_SIM_SETTINGS_BASE::DIALOG_SIM_SETTINGS_BASE( wxWindow* parent, wxWindowID m_pgCustom->Layout(); bSizer2->Fit( m_pgCustom ); m_simPages->AddPage( m_pgCustom, _("Custom"), false ); + m_pgNgspice = new wxPanel( m_simPages, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + wxBoxSizer* bSizer21; + bSizer21 = new wxBoxSizer( wxVERTICAL ); + + wxStaticBoxSizer* sbSizer4; + sbSizer4 = new wxStaticBoxSizer( new wxStaticBox( m_pgNgspice, wxID_ANY, _("Model Mode") ), wxVERTICAL ); + + m_rbNgspiceDefaultModelMode = new wxRadioButton( sbSizer4->GetStaticBox(), wxID_ANY, _("User configuration"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP ); + m_rbNgspiceDefaultModelMode->SetToolTip( _("Use the settings in the Ngspice configuration file or any settings defined in the schematic") ); + + sbSizer4->Add( m_rbNgspiceDefaultModelMode, 0, wxLEFT|wxRIGHT, 5 ); + + m_rbNgspiceSpiceModelMode = new wxRadioButton( sbSizer4->GetStaticBox(), wxID_ANY, _("Spice"), wxDefaultPosition, wxDefaultSize, 0 ); + m_rbNgspiceSpiceModelMode->SetToolTip( _("Enable default spice model simulation") ); + + sbSizer4->Add( m_rbNgspiceSpiceModelMode, 0, wxALL, 5 ); + + m_rbNgspicePSpiceModelMode = new wxRadioButton( sbSizer4->GetStaticBox(), wxID_ANY, _("PSpice"), wxDefaultPosition, wxDefaultSize, 0 ); + m_rbNgspicePSpiceModelMode->SetToolTip( _("Enable PSpice model simulation") ); + + sbSizer4->Add( m_rbNgspicePSpiceModelMode, 0, wxALL, 5 ); + + m_rbNgspiceLTSpiceModelMode = new wxRadioButton( sbSizer4->GetStaticBox(), wxID_ANY, _("LTSpice"), wxDefaultPosition, wxDefaultSize, 0 ); + m_rbNgspiceLTSpiceModelMode->SetToolTip( _("Enable LTSpice model simulation") ); + + sbSizer4->Add( m_rbNgspiceLTSpiceModelMode, 0, wxBOTTOM|wxLEFT|wxRIGHT, 5 ); + + m_rbNgspicePLTSpiceModelMode = new wxRadioButton( sbSizer4->GetStaticBox(), wxID_ANY, _("PSpice and LTSpice"), wxDefaultPosition, wxDefaultSize, 0 ); + m_rbNgspicePLTSpiceModelMode->SetToolTip( _("Enable PSpice and LTSpice model simulation") ); + + sbSizer4->Add( m_rbNgspicePLTSpiceModelMode, 0, wxBOTTOM|wxLEFT|wxRIGHT, 5 ); + + m_rbNgspiceHSpiceModelMode = new wxRadioButton( sbSizer4->GetStaticBox(), wxID_ANY, _("HSpice"), wxDefaultPosition, wxDefaultSize, 0 ); + m_rbNgspiceHSpiceModelMode->SetToolTip( _("Enable HSpice model simulation") ); + + sbSizer4->Add( m_rbNgspiceHSpiceModelMode, 0, wxBOTTOM|wxLEFT|wxRIGHT, 5 ); + + + bSizer21->Add( sbSizer4, 0, wxALL, 5 ); + + + m_pgNgspice->SetSizer( bSizer21 ); + m_pgNgspice->Layout(); + bSizer21->Fit( m_pgNgspice ); + m_simPages->AddPage( m_pgNgspice, _("Ngspice"), true ); bSizer1->Add( m_simPages, 1, wxEXPAND | wxALL, 5 ); diff --git a/eeschema/dialogs/dialog_sim_settings_base.fbp b/eeschema/dialogs/dialog_sim_settings_base.fbp index 3905ebd67a..b67f6c3c23 100644 --- a/eeschema/dialogs/dialog_sim_settings_base.fbp +++ b/eeschema/dialogs/dialog_sim_settings_base.fbp @@ -14,6 +14,7 @@ dialog_sim_settings_base 1000 none + 1 DIALOG_SIM_SETTINGS_BASE @@ -25,6 +26,7 @@ 1 1 UI + 0 0 0 @@ -97,7 +99,7 @@ 0 - 650,-1 + -1,-1 1 m_simPages 1 @@ -803,7 +805,7 @@ DC Transfer - 1 + 0 1 1 @@ -879,7 +881,7 @@ bSizer151 wxHORIZONTAL none - + 5 wxEXPAND 1 @@ -2294,6 +2296,7 @@ + 0 @@ -3467,7 +3470,7 @@ - + Operating Point 0 @@ -4522,7 +4525,7 @@ - + Custom 0 @@ -4720,6 +4723,7 @@ + 0 @@ -4783,13 +4787,474 @@ + + + Ngspice + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_pgNgspice + 1 + + + protected + 1 + + Resizable + 1 + + + 0 + + + + wxTAB_TRAVERSAL + + + bSizer21 + wxVERTICAL + none + + 5 + wxALL + 0 + + wxID_ANY + Model Mode + + sbSizer4 + wxVERTICAL + 1 + none + + 5 + wxLEFT|wxRIGHT + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + User configuration + + 0 + + + 0 + + 1 + m_rbNgspiceDefaultModelMode + 1 + + + protected + 1 + + Resizable + 1 + + wxRB_GROUP + ; ; forward_declare + 0 + Use the settings in the Ngspice configuration file or any settings defined in the schematic + + wxFILTER_NONE + wxDefaultValidator + + 0 + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Spice + + 0 + + + 0 + + 1 + m_rbNgspiceSpiceModelMode + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + Enable default spice model simulation + + wxFILTER_NONE + wxDefaultValidator + + 0 + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + PSpice + + 0 + + + 0 + + 1 + m_rbNgspicePSpiceModelMode + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + Enable PSpice model simulation + + wxFILTER_NONE + wxDefaultValidator + + 0 + + + + + + + 5 + wxBOTTOM|wxLEFT|wxRIGHT + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + LTSpice + + 0 + + + 0 + + 1 + m_rbNgspiceLTSpiceModelMode + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + Enable LTSpice model simulation + + wxFILTER_NONE + wxDefaultValidator + + 0 + + + + + + + 5 + wxBOTTOM|wxLEFT|wxRIGHT + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + PSpice and LTSpice + + 0 + + + 0 + + 1 + m_rbNgspicePLTSpiceModelMode + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + Enable PSpice and LTSpice model simulation + + wxFILTER_NONE + wxDefaultValidator + + 0 + + + + + + + 5 + wxBOTTOM|wxLEFT|wxRIGHT + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + HSpice + + 0 + + + 0 + + 1 + m_rbNgspiceHSpiceModelMode + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + Enable HSpice model simulation + + wxFILTER_NONE + wxDefaultValidator + + 0 + + + + + + + + + + - + 5 wxEXPAND|wxALL 0 - + bSizer88 wxVERTICAL diff --git a/eeschema/dialogs/dialog_sim_settings_base.h b/eeschema/dialogs/dialog_sim_settings_base.h index b9fe3ddde3..9c2b941e16 100644 --- a/eeschema/dialogs/dialog_sim_settings_base.h +++ b/eeschema/dialogs/dialog_sim_settings_base.h @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version Oct 26 2018) +// C++ code generated with wxFormBuilder (version 3.9.0 Jun 18 2020) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include #include @@ -114,6 +116,13 @@ class DIALOG_SIM_SETTINGS_BASE : public DIALOG_SHIM wxStaticText* m_staticText18; wxTextCtrl* m_customTxt; wxButton* m_loadDirectives; + wxPanel* m_pgNgspice; + wxRadioButton* m_rbNgspiceDefaultModelMode; + wxRadioButton* m_rbNgspiceSpiceModelMode; + wxRadioButton* m_rbNgspicePSpiceModelMode; + wxRadioButton* m_rbNgspiceLTSpiceModelMode; + wxRadioButton* m_rbNgspicePLTSpiceModelMode; + wxRadioButton* m_rbNgspiceHSpiceModelMode; wxCheckBox* m_fixPassiveVals; wxCheckBox* m_fixIncludePaths; wxStdDialogButtonSizer* m_sdbSizer; diff --git a/eeschema/schematic.cpp b/eeschema/schematic.cpp index 47af7ed8c7..f4898e3881 100644 --- a/eeschema/schematic.cpp +++ b/eeschema/schematic.cpp @@ -1,7 +1,7 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2020-2021 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 @@ -26,6 +26,7 @@ #include #include #include +#include SCHEMATIC::SCHEMATIC( PROJECT* aPrj ) : @@ -92,6 +93,7 @@ void SCHEMATIC::SetProject( PROJECT* aPrj ) project.m_SchematicSettings = new SCHEMATIC_SETTINGS( &project, "schematic" ); project.m_SchematicSettings->LoadFromFile(); + project.m_SchematicSettings->m_NgspiceSimulatorSettings->LoadFromFile(); project.m_ErcSettings->LoadFromFile(); } } diff --git a/eeschema/schematic_settings.cpp b/eeschema/schematic_settings.cpp index 9b10f19131..aa8d28fa6f 100644 --- a/eeschema/schematic_settings.cpp +++ b/eeschema/schematic_settings.cpp @@ -2,6 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2020 CERN + * Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors. * @author Jon Evans * * This program is free software: you can redistribute it and/or modify it @@ -27,6 +28,7 @@ #include #include #include +#include const int schSettingsSchemaVersion = 0; @@ -47,7 +49,8 @@ SCHEMATIC_SETTINGS::SCHEMATIC_SETTINGS( JSON_SETTINGS* aParent, const std::strin m_IntersheetRefsFormatShort( false ), m_IntersheetRefsPrefix( DEFAULT_IREF_PREFIX ), m_IntersheetRefsSuffix( DEFAULT_IREF_SUFFIX ), - m_SpiceAdjustPassiveValues( false ) + m_SpiceAdjustPassiveValues( false ), + m_NgspiceSimulatorSettings( nullptr ) { EESCHEMA_SETTINGS* appSettings = dynamic_cast( Kiface().KifaceSettings() ); @@ -206,6 +209,9 @@ SCHEMATIC_SETTINGS::SCHEMATIC_SETTINGS( JSON_SETTINGS* aParent, const std::strin m_params.emplace_back( new PARAM( "subpart_first_id", LIB_PART::SubpartFirstIdPtr(), 'A', '1', 'z' ) ); + + m_NgspiceSimulatorSettings = + std::make_shared( this, "ngspice" ); } diff --git a/eeschema/schematic_settings.h b/eeschema/schematic_settings.h index 11e4f3c7e3..0573370d75 100644 --- a/eeschema/schematic_settings.h +++ b/eeschema/schematic_settings.h @@ -1,7 +1,7 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2020-2021 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 @@ -25,12 +25,14 @@ #include #include +class NGSPICE_SIMULATOR_SETTINGS; + /** * These settings were stored in SCH_BASE_FRAME previously. * The backing store is currently the project file. * They should likely move to a project settings file (JSON) once that framework exists. * - * These are loaded from eeschema settings but then overwritten by the project settings. + * These are loaded from Eeschema settings but then overwritten by the project settings. * All of the values are stored in IU, but the backing file stores in mils. */ class SCHEMATIC_SETTINGS : public NESTED_SETTINGS @@ -64,10 +66,16 @@ public: wxString m_NetFormatName; + ///< @todo These should probably be moved to the "schematic.simulator" path. bool m_SpiceAdjustPassiveValues; wxString m_SpiceCommandString; // A command string to run external spice TEMPLATES m_TemplateFieldNames; + + /** + * Ngspice simulator settings. + */ + std::shared_ptr m_NgspiceSimulatorSettings; }; #endif diff --git a/eeschema/sim/ngspice.cpp b/eeschema/sim/ngspice.cpp index 1329b5062a..6feb100ed8 100644 --- a/eeschema/sim/ngspice.cpp +++ b/eeschema/sim/ngspice.cpp @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2016-2018 CERN - * Copyright (C) 2018 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2018-2021 KiCad Developers, see AUTHORS.txt for contributors. * * @author Tomasz Wlostowski * @author Maciej Suminski @@ -31,6 +31,9 @@ #include "ngspice.h" #include "spice_reporter.h" +#include +#include + #include #include @@ -45,18 +48,50 @@ using namespace std; + +/** + * Flag to enable debug output of Ngspice simulator. + * + * Use "KICAD_NGSPICE" to enable Ngspice simulator tracing. + * + * @ingroup trace_env_vars + */ static const wxChar* const traceNgspice = wxT( "KICAD_NGSPICE" ); -NGSPICE::NGSPICE() - : m_ngSpice_Init( nullptr ), - m_ngSpice_Circ( nullptr ), - m_ngSpice_Command( nullptr ), - m_ngGet_Vec_Info( nullptr ), - m_ngSpice_CurPlot( nullptr ), - m_ngSpice_AllPlots( nullptr ), - m_ngSpice_AllVecs( nullptr ), - m_ngSpice_Running( nullptr ), - m_error( false ) + +NGSPICE_SIMULATOR_SETTINGS::NGSPICE_SIMULATOR_SETTINGS( + JSON_SETTINGS* aParent, const std::string& aPath ) : + SPICE_SIMULATOR_SETTINGS( aParent, aPath ), + m_modelMode( NGSPICE_MODEL_MODE::USER_CONFIG ) +{ + m_params.emplace_back( new PARAM_ENUM( "model_mode", &m_modelMode, + NGSPICE_MODEL_MODE::USER_CONFIG, + NGSPICE_MODEL_MODE::USER_CONFIG, + NGSPICE_MODEL_MODE::HSPICE ) ); +} + + +bool NGSPICE_SIMULATOR_SETTINGS::operator==( const SPICE_SIMULATOR_SETTINGS& aRhs ) const +{ + const NGSPICE_SIMULATOR_SETTINGS* settings = + dynamic_cast( &aRhs ); + + wxCHECK( settings, false ); + + return m_modelMode == settings->m_modelMode; +} + + +NGSPICE::NGSPICE() : + m_ngSpice_Init( nullptr ), + m_ngSpice_Circ( nullptr ), + m_ngSpice_Command( nullptr ), + m_ngGet_Vec_Info( nullptr ), + m_ngSpice_CurPlot( nullptr ), + m_ngSpice_AllPlots( nullptr ), + m_ngSpice_AllVecs( nullptr ), + m_ngSpice_Running( nullptr ), + m_error( false ) { init_dll(); } @@ -67,9 +102,15 @@ NGSPICE::~NGSPICE() } -void NGSPICE::Init() +void NGSPICE::Init( const SPICE_SIMULATOR_SETTINGS* aSettings ) { Command( "reset" ); + + for( const std::string& command : GetSettingCommands() ) + { + wxLogTrace( traceNgspice, "Sending Ngspice configuration command '%s'.", command ); + Command( command ); + } } @@ -81,6 +122,7 @@ vector NGSPICE::AllPlots() const int noOfPlots = 0; vector retVal; + if( allPlots != nullptr ) { for( char** plot = allPlots; *plot != nullptr; plot++ ) @@ -297,35 +339,86 @@ string NGSPICE::GetXAxis( SIM_TYPE aType ) const { switch( aType ) { - case ST_AC: - case ST_NOISE: - return string( "frequency" ); + case ST_AC: + case ST_NOISE: + return string( "frequency" ); - case ST_DC: - // find plot, which ends with "-sweep" - for( auto& plot : AllPlots() ) + case ST_DC: + // find plot, which ends with "-sweep" + for( auto& plot : AllPlots() ) + { + const string sweepEnding = "-sweep"; + unsigned int len = sweepEnding.length(); + + if( plot.length() > len + && plot.substr( plot.length() - len, len ).compare( sweepEnding ) == 0 ) { - const string sweepEnding = "-sweep"; - unsigned int len = sweepEnding.length(); - - if( plot.length() > len - && plot.substr( plot.length() - len, len ).compare( sweepEnding ) == 0 ) - { - return string( plot ); - } + return string( plot ); } - break; + } + break; - case ST_TRANSIENT: - return string( "time" ); + case ST_TRANSIENT: + return string( "time" ); - default: - break; + default: + break; } + return string( "" ); } +std::vector NGSPICE::GetSettingCommands() const +{ + const NGSPICE_SIMULATOR_SETTINGS* settings = + dynamic_cast( Settings().get() ); + + std::vector commands; + + wxCHECK( settings, commands ); + + switch( settings->GetModelMode() ) + { + case NGSPICE_MODEL_MODE::USER_CONFIG: + break; + + case NGSPICE_MODEL_MODE::NGSPICE: + commands.emplace_back( "unset ngbehavior" ); + break; + + case NGSPICE_MODEL_MODE::PSPICE: + commands.emplace_back( "set ngbehavior=ps" ); + break; + + case NGSPICE_MODEL_MODE::LTSPICE: + commands.emplace_back( "set ngbehavior=lt" ); + break; + + case NGSPICE_MODEL_MODE::LT_PSPICE: + commands.emplace_back( "set ngbehavior=ltps" ); + break; + + case NGSPICE_MODEL_MODE::HSPICE: + commands.emplace_back( "set ngbehavior=hs" ); + break; + + default: + wxFAIL_MSG( wxString::Format( "Undefined NGSPICE_MODEL_MODE %d.", + settings->GetModelMode() ) ); + break; + } + + return commands; +} + + +const std::string NGSPICE::GetNetlist() const +{ + return m_netlist; +} + + void NGSPICE::init_dll() { if( m_initialized ) @@ -337,22 +430,26 @@ void NGSPICE::init_dll() if( m_dll.IsLoaded() ) // enable force reload m_dll.Unload(); -// Extra effort to find libngspice + // Extra effort to find libngspice + // @todo Shouldn't we be using the normal KiCad path searching mechanism here? wxFileName dllFile( "", NGSPICE_DLL_FILE ); #if defined(__WINDOWS__) - #if defined( _MSC_VER ) + #if defined( _MSC_VER ) const vector dllPaths = { "" }; - #else + #else const vector dllPaths = { "", "/mingw64/bin", "/mingw32/bin" }; - #endif + #endif #elif defined(__WXMAC__) const vector dllPaths = { PATHS::GetOSXKicadUserDataDir().ToStdString() + "/PlugIns/ngspice", PATHS::GetOSXKicadMachineDataDir().ToStdString() + "/PlugIns/ngspice", + // when running kicad.app stdPaths.GetPluginsDir().ToStdString() + "/sim", + // when running eeschema.app - wxFileName( stdPaths.GetExecutablePath() ).GetPath().ToStdString() + "/../../../../../Contents/PlugIns/sim" + wxFileName( stdPaths.GetExecutablePath() ).GetPath().ToStdString() + + "/../../../../../Contents/PlugIns/sim" }; #else // Unix systems const vector dllPaths = { "/usr/local/lib" }; @@ -374,8 +471,7 @@ void NGSPICE::init_dll() if( !m_dll.IsLoaded() ) // try also the system libraries m_dll.Load( wxDynamicLibrary::CanonicalizeName( "ngspice" ) ); - -#else // here: __LINUX__ +#else // First, try the system libraries m_dll.Load( NGSPICE_DLL_FILE, wxDL_VERBATIM | wxDL_QUIET | wxDL_NOW ); @@ -412,7 +508,8 @@ void NGSPICE::init_dll() m_ngSpice_AllVecs = (ngSpice_AllVecs) m_dll.GetSymbol( "ngSpice_AllVecs" ); m_ngSpice_Running = (ngSpice_Running) m_dll.GetSymbol( "ngSpice_running" ); // it is not a typo - m_ngSpice_Init( &cbSendChar, &cbSendStat, &cbControlledExit, NULL, NULL, &cbBGThreadRunning, this ); + m_ngSpice_Init( &cbSendChar, &cbSendStat, &cbControlledExit, NULL, NULL, + &cbBGThreadRunning, this ); // Load a custom spinit file, to fix the problem with loading .cm files // Switch to the executable directory, so the relative paths are correct @@ -433,8 +530,9 @@ void NGSPICE::init_dll() ".", #ifdef __WXMAC__ stdPaths.GetPluginsDir().ToStdString() + "/sim/ngspice/scripts", - wxFileName( stdPaths.GetExecutablePath() ).GetPath().ToStdString() + "/../../../../../Contents/PlugIns/sim/ngspice/scripts" -#endif /* __WXMAC__ */ + wxFileName( stdPaths.GetExecutablePath() ).GetPath().ToStdString() + + "/../../../../../Contents/PlugIns/sim/ngspice/scripts" +#endif "../share/kicad", "../share", "../../share/kicad", @@ -498,9 +596,10 @@ string NGSPICE::findCmPath() const "/Applications/ngspice/lib/ngspice", "Contents/Frameworks", wxStandardPaths::Get().GetPluginsDir().ToStdString() + "/sim/ngspice", - wxFileName( wxStandardPaths::Get().GetExecutablePath() ).GetPath().ToStdString() + "/../../../../../Contents/PlugIns/sim/ngspice", + wxFileName( wxStandardPaths::Get().GetExecutablePath() ).GetPath().ToStdString() + + "/../../../../../Contents/PlugIns/sim/ngspice", "../Plugins/sim/ngspice", -#endif /* __WXMAC__ */ +#endif "../lib/ngspice", "../../lib/ngspice", "lib/ngspice", @@ -554,10 +653,6 @@ int NGSPICE::cbSendChar( char* what, int id, void* user ) int NGSPICE::cbSendStat( char* what, int id, void* user ) { -/* NGSPICE* sim = reinterpret_cast( user ); - if( sim->m_consoleReporter ) - sim->m_consoleReporter->Report( what );*/ - return 0; } @@ -594,10 +689,4 @@ void NGSPICE::validate() } -const std::string NGSPICE::GetNetlist() const -{ - return m_netlist; -} - - bool NGSPICE::m_initialized = false; diff --git a/eeschema/sim/ngspice.h b/eeschema/sim/ngspice.h index 037552ce93..dfc3bd9f31 100644 --- a/eeschema/sim/ngspice.h +++ b/eeschema/sim/ngspice.h @@ -34,14 +34,47 @@ class wxDynamicLibrary; -class NGSPICE : public SPICE_SIMULATOR { +/** + * Spice model compatibility modes. + * + * @note The ngspice model modes are mutually exclusive. + */ +enum class NGSPICE_MODEL_MODE { + USER_CONFIG, + NGSPICE, + PSPICE, + LTSPICE, + LT_PSPICE, + HSPICE +}; + +/** + * Container for Ngspice simulator settings. + */ +class NGSPICE_SIMULATOR_SETTINGS : public SPICE_SIMULATOR_SETTINGS +{ +public: + NGSPICE_SIMULATOR_SETTINGS( JSON_SETTINGS* aParent, const std::string& aPath ); + + bool operator==( const SPICE_SIMULATOR_SETTINGS& aRhs ) const override; + + NGSPICE_MODEL_MODE GetModelMode() const { return m_modelMode; } + void SetModelMode( NGSPICE_MODEL_MODE aMode ) { m_modelMode = aMode; } + +private: + NGSPICE_MODEL_MODE m_modelMode; +}; + + +class NGSPICE : public SPICE_SIMULATOR +{ public: NGSPICE(); virtual ~NGSPICE(); ///< @copydoc SPICE_SIMULATOR::Init() - void Init() override; + void Init( const SPICE_SIMULATOR_SETTINGS* aSettings = nullptr ) override; ///< @copydoc SPICE_SIMULATOR::LoadNetlist() bool LoadNetlist( const std::string& aNetlist ) override; @@ -79,6 +112,8 @@ public: ///< @copydoc SPICE_SIMULATOR::GetPhasePlot() std::vector GetPhasePlot( const std::string& aName, int aMaxLen = -1 ) override; + std::vector GetSettingCommands() const override; + ///< @copydoc SPICE_SIMULATOR::GetNetlist() virtual const std::string GetNetlist() const override; @@ -89,15 +124,15 @@ private: void init_dll(); // ngspice library functions - typedef void (*ngSpice_Init)( SendChar*, SendStat*, ControlledExit*, - SendData*, SendInitData*, BGThreadRunning*, void* ); - typedef int (*ngSpice_Circ)( char** circarray ); - typedef int (*ngSpice_Command)( char* command ); - typedef pvector_info (*ngGet_Vec_Info)( char* vecname ); + typedef void ( *ngSpice_Init )( SendChar*, SendStat*, ControlledExit*, SendData*, SendInitData*, + BGThreadRunning*, void* ); + typedef int ( *ngSpice_Circ )( char** circarray ); + typedef int ( *ngSpice_Command )( char* command ); + typedef pvector_info ( *ngGet_Vec_Info )( char* vecname ); typedef char* ( *ngSpice_CurPlot )( void ); - typedef char** (*ngSpice_AllPlots)( void ); - typedef char** (*ngSpice_AllVecs)( char* plotname ); - typedef bool (*ngSpice_Running)( void ); + typedef char** ( *ngSpice_AllPlots )( void ); + typedef char** ( *ngSpice_AllVecs )( char* plotname ); + typedef bool ( *ngSpice_Running )( void ); ///< Handle to DLL functions ngSpice_Init m_ngSpice_Init; @@ -133,7 +168,7 @@ private: ///< Error flag indicating that ngspice needs to be reloaded bool m_error; - ///< NGspice should be initialized only once + ///< Ngspice should be initialized only once static bool m_initialized; ///< current netlist diff --git a/eeschema/sim/sim_plot_frame.cpp b/eeschema/sim/sim_plot_frame.cpp index 6b48b48d12..f525395d88 100644 --- a/eeschema/sim/sim_plot_frame.cpp +++ b/eeschema/sim/sim_plot_frame.cpp @@ -26,6 +26,7 @@ #include +#include #include #include #include @@ -36,6 +37,7 @@ #include #include "netlist_exporter_pspice_sim.h" #include +#include "ngspice.h" #include "sim_plot_colors.h" #include "sim_plot_frame.h" #include "sim_plot_panel.h" @@ -59,8 +61,8 @@ SIM_PLOT_TYPE operator|( SIM_PLOT_TYPE aFirst, SIM_PLOT_TYPE aSecond ) class SIM_THREAD_REPORTER : public SPICE_REPORTER { public: - SIM_THREAD_REPORTER( SIM_PLOT_FRAME* aParent ) - : m_parent( aParent ) + SIM_THREAD_REPORTER( SIM_PLOT_FRAME* aParent ) : + m_parent( aParent ) { } @@ -83,17 +85,17 @@ public: switch( aNewState ) { - case SIM_IDLE: - event = new wxCommandEvent( EVT_SIM_FINISHED ); - break; + case SIM_IDLE: + event = new wxCommandEvent( EVT_SIM_FINISHED ); + break; - case SIM_RUNNING: - event = new wxCommandEvent( EVT_SIM_STARTED ); - break; + case SIM_RUNNING: + event = new wxCommandEvent( EVT_SIM_STARTED ); + break; - default: - wxFAIL; - return; + default: + wxFAIL; + return; } wxQueueEvent( m_parent, event ); @@ -105,8 +107,10 @@ private: TRACE_DESC::TRACE_DESC( const NETLIST_EXPORTER_PSPICE_SIM& aExporter, const wxString& aName, - SIM_PLOT_TYPE aType, const wxString& aParam ) - : m_name( aName ), m_type( aType ), m_param( aParam ) + SIM_PLOT_TYPE aType, const wxString& aParam ) : + m_name( aName ), + m_type( aType ), + m_param( aParam ) { // Title generation m_title = wxString::Format( "%s(%s)", aParam, aName ); @@ -117,14 +121,16 @@ TRACE_DESC::TRACE_DESC( const NETLIST_EXPORTER_PSPICE_SIM& aExporter, const wxSt m_title += " (phase)"; } + // Store the path of saved workbooks during the session wxString SIM_PLOT_FRAME::m_savedWorkbooksPath; -SIM_PLOT_FRAME::SIM_PLOT_FRAME( KIWAY* aKiway, wxWindow* aParent ) - : SIM_PLOT_FRAME_BASE( aParent ), - m_lastSimPlot( nullptr ), - m_welcomePanel( nullptr ), - m_plotNumber( 0 ) + +SIM_PLOT_FRAME::SIM_PLOT_FRAME( KIWAY* aKiway, wxWindow* aParent ) : + SIM_PLOT_FRAME_BASE( aParent ), + m_lastSimPlot( nullptr ), + m_welcomePanel( nullptr ), + m_plotNumber( 0 ) { SetKiway( this, aKiway ); m_signalsIconColorList = NULL; @@ -139,6 +145,14 @@ SIM_PLOT_FRAME::SIM_PLOT_FRAME( KIWAY* aKiway, wxWindow* aParent ) icon.CopyFromBitmap( KiBitmap( BITMAPS::simulator ) ); SetIcon( icon ); + m_simulator = SPICE_SIMULATOR::CreateInstance( "ngspice" ); + + if( !m_simulator ) + { + throw std::runtime_error( "Could not create simulator instance" ); + return; + } + // Get the previous size and position of windows: LoadSettings( config() ); @@ -148,14 +162,6 @@ SIM_PLOT_FRAME::SIM_PLOT_FRAME( KIWAY* aKiway, wxWindow* aParent ) // Give icons to menuitems setIconsForMenuItems(); - m_simulator = SPICE_SIMULATOR::CreateInstance( "ngspice" ); - - if( !m_simulator ) - { - throw std::runtime_error( "Could not create simulator instance" ); - return; - } - m_simulator->Init(); if( m_savedWorkbooksPath.IsEmpty() ) @@ -178,7 +184,8 @@ SIM_PLOT_FRAME::SIM_PLOT_FRAME( KIWAY* aKiway, wxWindow* aParent ) Connect( EVT_SIM_REPORT, wxCommandEventHandler( SIM_PLOT_FRAME::onSimReport ), NULL, this ); Connect( EVT_SIM_STARTED, wxCommandEventHandler( SIM_PLOT_FRAME::onSimStarted ), NULL, this ); Connect( EVT_SIM_FINISHED, wxCommandEventHandler( SIM_PLOT_FRAME::onSimFinished ), NULL, this ); - Connect( EVT_SIM_CURSOR_UPDATE, wxCommandEventHandler( SIM_PLOT_FRAME::onCursorUpdate ), NULL, this ); + Connect( EVT_SIM_CURSOR_UPDATE, wxCommandEventHandler( SIM_PLOT_FRAME::onCursorUpdate ), + NULL, this ); // Toolbar buttons m_toolSimulate = m_toolBar->AddTool( ID_SIM_RUN, _( "Run/Stop Simulation" ), @@ -204,12 +211,14 @@ SIM_PLOT_FRAME::SIM_PLOT_FRAME( KIWAY* aKiway, wxWindow* aParent ) wxCommandEventHandler( SIM_PLOT_FRAME::onSettings ), NULL, this ); // Bind toolbar buttons event to existing menu event handlers, so they behave the same - Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onSimulate, this, m_runSimulation->GetId() ); - Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onAddSignal, this, m_addSignals->GetId() ); - Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onProbe, this, m_probeSignals->GetId() ); - Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onTune, this, m_tuneValue->GetId() ); - Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onShowNetlist, this, m_showNetlist->GetId() ); - Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onSettings, this, m_settings->GetId() ); + Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onSimulate, this, + m_runSimulation->GetId() ); + Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onAddSignal, this, m_addSignals->GetId() ); + Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onProbe, this, m_probeSignals->GetId() ); + Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onTune, this, m_tuneValue->GetId() ); + Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onShowNetlist, this, + m_showNetlist->GetId() ); + Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onSettings, this, m_settings->GetId() ); m_toolBar->Realize(); @@ -258,6 +267,14 @@ void SIM_PLOT_FRAME::SaveSettings( APP_SETTINGS_BASE* aCfg ) cfg->m_Simulator.cursors_panel_height = m_splitterTuneValues->GetSashPosition(); cfg->m_Simulator.white_background = m_plotUseWhiteBg; } + + if( m_schematicFrame ) + { + PROJECT_FILE& project = Prj().GetProjectFile(); + + project.m_SchematicSettings->m_NgspiceSimulatorSettings->SaveToFile(); + m_schematicFrame->SaveProjectSettings(); + } } @@ -277,6 +294,13 @@ void SIM_PLOT_FRAME::LoadSettings( APP_SETTINGS_BASE* aCfg ) m_splitterTuneValuesSashPosition = cfg->m_Simulator.cursors_panel_height; m_plotUseWhiteBg = cfg->m_Simulator.white_background; } + + PROJECT_FILE& project = Prj().GetProjectFile(); + + NGSPICE* currentSim = dynamic_cast( m_simulator.get() ); + + if( currentSim ) + m_simulator->Settings() = project.m_SchematicSettings->m_NgspiceSimulatorSettings; } @@ -345,7 +369,8 @@ void SIM_PLOT_FRAME::setIconsForMenuItems() continue; wxMenu* menu = item->GetMenu(); - // Calculate the initial index of item inside the wxMenu parent + + // Calculate the initial index of item inside the wxMenu parent. wxMenuItemList& mlist = menu->GetMenuItems(); int mpos = mlist.IndexOf( item ); @@ -354,6 +379,7 @@ void SIM_PLOT_FRAME::setIconsForMenuItems() // Modify the bitmap menu->Remove( item ); AddBitmapToMenuItem( item, KiBitmap( bm_list[ii].m_Bitmap ) ); + // Insert item to its the initial index menu->Insert( mpos, item ); } @@ -382,7 +408,7 @@ void SIM_PLOT_FRAME::StartSimulation( const wxString& aSimCommand ) STRING_FORMATTER formatter; if( !m_settingsDlg ) - m_settingsDlg = new DIALOG_SIM_SETTINGS( this ); + m_settingsDlg = new DIALOG_SIM_SETTINGS( this, m_simulator->Settings() ); m_simConsole->Clear(); updateNetlistExporter(); @@ -490,7 +516,8 @@ void SIM_PLOT_FRAME::AddTuner( SCH_COMPONENT* aComponent ) // For now limit the tuner tool to RLC components char primitiveType = NETLIST_EXPORTER_PSPICE::GetSpiceField( SF_PRIMITIVE, aComponent, 0 )[0]; - if( primitiveType != SP_RESISTOR && primitiveType != SP_CAPACITOR && primitiveType != SP_INDUCTOR ) + if( primitiveType != SP_RESISTOR && primitiveType != SP_CAPACITOR + && primitiveType != SP_INDUCTOR ) return; const wxString componentName = aComponent->GetField( REFERENCE_FIELD )->GetText(); @@ -546,6 +573,14 @@ const NETLIST_EXPORTER_PSPICE_SIM* SIM_PLOT_FRAME::GetExporter() const } +std::shared_ptr& SIM_PLOT_FRAME::GetSimulatorSettings() +{ + wxASSERT( m_simulator->Settings() ); + + return m_simulator->Settings(); +} + + void SIM_PLOT_FRAME::addPlot( const wxString& aName, SIM_PLOT_TYPE aType, const wxString& aParam ) { SIM_TYPE simType = m_exporter->GetSimType(); @@ -586,7 +621,8 @@ void SIM_PLOT_FRAME::addPlot( const wxString& aName, SIM_PLOT_TYPE aType, const // Add two plots: magnitude & phase TRACE_DESC mag_desc( *m_exporter, descriptor, (SIM_PLOT_TYPE)( baseType | SPT_AC_MAG ) ); - TRACE_DESC phase_desc( *m_exporter, descriptor, (SIM_PLOT_TYPE)( baseType | SPT_AC_PHASE ) ); + TRACE_DESC phase_desc( *m_exporter, descriptor, + (SIM_PLOT_TYPE)( baseType | SPT_AC_PHASE ) ); updated |= updatePlot( mag_desc, plotPanel ); updated |= updatePlot( phase_desc, plotPanel ); @@ -631,6 +667,7 @@ void SIM_PLOT_FRAME::removePlot( const wxString& aPlotName, bool aErase ) void SIM_PLOT_FRAME::updateNetlistExporter() { m_exporter.reset( new NETLIST_EXPORTER_PSPICE_SIM( &m_schematicFrame->Schematic() ) ); + if( m_settingsDlg ) m_settingsDlg->SetNetlistExporter( m_exporter.get() ); } @@ -668,31 +705,28 @@ bool SIM_PLOT_FRAME::updatePlot( const TRACE_DESC& aDescriptor, SIM_PLOT_PANEL* // Now, Y axis data switch( m_exporter->GetSimType() ) { - case ST_AC: - { - wxASSERT_MSG( !( ( plotType & SPT_AC_MAG ) && ( plotType & SPT_AC_PHASE ) ), - "Cannot set both AC_PHASE and AC_MAG bits" ); + case ST_AC: + wxASSERT_MSG( !( ( plotType & SPT_AC_MAG ) && ( plotType & SPT_AC_PHASE ) ), + "Cannot set both AC_PHASE and AC_MAG bits" ); - if( plotType & SPT_AC_MAG ) - data_y = m_simulator->GetMagPlot( (const char*) spiceVector.c_str() ); - else if( plotType & SPT_AC_PHASE ) - data_y = m_simulator->GetPhasePlot( (const char*) spiceVector.c_str() ); - else - wxASSERT_MSG( false, "Plot type missing AC_PHASE or AC_MAG bit" ); - } - break; - - case ST_NOISE: - case ST_DC: - case ST_TRANSIENT: - { + if( plotType & SPT_AC_MAG ) data_y = m_simulator->GetMagPlot( (const char*) spiceVector.c_str() ); - } + else if( plotType & SPT_AC_PHASE ) + data_y = m_simulator->GetPhasePlot( (const char*) spiceVector.c_str() ); + else + wxASSERT_MSG( false, "Plot type missing AC_PHASE or AC_MAG bit" ); + break; - default: - wxASSERT_MSG( false, "Unhandled plot type" ); - return false; + case ST_NOISE: + case ST_DC: + case ST_TRANSIENT: + data_y = m_simulator->GetMagPlot( (const char*) spiceVector.c_str() ); + break; + + default: + wxASSERT_MSG( false, "Unhandled plot type" ); + return false; } if( data_y.size() != size ) @@ -1177,7 +1211,7 @@ void SIM_PLOT_FRAME::menuWhiteBackground( wxCommandEvent& event ) if( curPage == m_welcomePanel ) continue; - // ensure it is truely a plot panel and not the (zero plots) placeholder + // ensure it is truly a plot panel and not the (zero plots) placeholder // which is only SIM_PLOT_PANEL_BASE SIM_PLOT_PANEL* panel = dynamic_cast( curPage ); @@ -1256,7 +1290,7 @@ void SIM_PLOT_FRAME::onSettings( wxCommandEvent& event ) SIM_PANEL_BASE* plotPanelWindow = currentPlotWindow(); if( !m_settingsDlg ) - m_settingsDlg = new DIALOG_SIM_SETTINGS( this ); + m_settingsDlg = new DIALOG_SIM_SETTINGS( this, m_simulator->Settings() ); // Initial processing is required to e.g. display a list of power sources updateNetlistExporter(); @@ -1277,7 +1311,8 @@ void SIM_PLOT_FRAME::onSettings( wxCommandEvent& event ) SIM_TYPE newSimType = NETLIST_EXPORTER_PSPICE_SIM::CommandToSimType( newCommand ); // If it is a new simulation type, open a new plot - // For the DC sim, check if sweep source type has changed (char 4 will contain 'v', 'i', 'r' or 't' + // For the DC sim, check if sweep source type has changed (char 4 will contain 'v', + // 'i', 'r' or 't'. if( !plotPanelWindow || ( plotPanelWindow && plotPanelWindow->GetType() != newSimType ) || ( newSimType == ST_DC @@ -1287,6 +1322,7 @@ void SIM_PLOT_FRAME::onSettings( wxCommandEvent& event ) } m_plots[plotPanelWindow].m_simCommand = newCommand; + m_simulator->Init(); } } @@ -1325,6 +1361,7 @@ void SIM_PLOT_FRAME::onTune( wxCommandEvent& event ) m_schematicFrame->Raise(); } + void SIM_PLOT_FRAME::onShowNetlist( wxCommandEvent& event ) { class NETLIST_VIEW_DIALOG : public DIALOG_SHIM @@ -1369,7 +1406,7 @@ void SIM_PLOT_FRAME::onShowNetlist( wxCommandEvent& event ) SetSizer( sizer ); Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( NETLIST_VIEW_DIALOG::onClose ), NULL, - this ); + this ); finishDialogSettings(); } @@ -1411,7 +1448,8 @@ void SIM_PLOT_FRAME::onCursorUpdate( wxCommandEvent& event ) // Fill the signals listctrl m_cursors->AppendColumn( _( "Signal" ), wxLIST_FORMAT_LEFT, size.x / 2 ); - const long X_COL = m_cursors->AppendColumn( plotPanel->GetLabelX(), wxLIST_FORMAT_LEFT, size.x / 4 ); + const long X_COL = m_cursors->AppendColumn( plotPanel->GetLabelX(), wxLIST_FORMAT_LEFT, + size.x / 4 ); wxString labelY1 = plotPanel->GetLabelY1(); wxString labelY2 = plotPanel->GetLabelY2(); @@ -1570,7 +1608,8 @@ SIM_PLOT_FRAME::SIGNAL_CONTEXT_MENU::SIGNAL_CONTEXT_MENU( const wxString& aSigna else AddMenuItem( this, SHOW_CURSOR, _( "Show Cursor" ), KiBitmap( BITMAPS::pcb_target ) ); - Connect( wxEVT_COMMAND_MENU_SELECTED, wxMenuEventHandler( SIGNAL_CONTEXT_MENU::onMenuEvent ), NULL, this ); + Connect( wxEVT_COMMAND_MENU_SELECTED, wxMenuEventHandler( SIGNAL_CONTEXT_MENU::onMenuEvent ), + NULL, this ); } @@ -1580,20 +1619,21 @@ void SIM_PLOT_FRAME::SIGNAL_CONTEXT_MENU::onMenuEvent( wxMenuEvent& aEvent ) switch( aEvent.GetId() ) { - case HIDE_SIGNAL: - m_plotFrame->removePlot( m_signal ); - break; + case HIDE_SIGNAL: + m_plotFrame->removePlot( m_signal ); + break; - case SHOW_CURSOR: - plot->EnableCursor( m_signal, true ); - break; + case SHOW_CURSOR: + plot->EnableCursor( m_signal, true ); + break; - case HIDE_CURSOR: - plot->EnableCursor( m_signal, false ); - break; + case HIDE_CURSOR: + plot->EnableCursor( m_signal, false ); + break; } } + wxDEFINE_EVENT( EVT_SIM_UPDATE, wxCommandEvent ); wxDEFINE_EVENT( EVT_SIM_REPORT, wxCommandEvent ); diff --git a/eeschema/sim/sim_plot_frame.h b/eeschema/sim/sim_plot_frame.h index 115c2bc582..c1a879c8f7 100644 --- a/eeschema/sim/sim_plot_frame.h +++ b/eeschema/sim/sim_plot_frame.h @@ -50,6 +50,7 @@ class SCH_EDIT_FRAME; class SCH_COMPONENT; class SPICE_SIMULATOR; +class SPICE_SIMULATOR_SETTINGS; class NETLIST_EXPORTER_PSPICE_SIM; #include "sim_plot_panel.h" @@ -59,7 +60,7 @@ class SIM_THREAD_REPORTER; class TUNER_SLIDER; -///> Trace descriptor class +///< Trace descriptor class class TRACE_DESC { public: @@ -110,11 +111,12 @@ private: }; -/** Implementing SIM_PLOT_FRAME_BASE */ +/** + * Implementing SIM_PLOT_FRAME_BASE + */ class SIM_PLOT_FRAME : public SIM_PLOT_FRAME_BASE { public: - /** Constructor */ SIM_PLOT_FRAME( KIWAY* aKiway, wxWindow* aParent ); ~SIM_PLOT_FRAME(); @@ -147,7 +149,6 @@ public: /** * Add a tuner for a component. - * */ void AddTuner( SCH_COMPONENT* aComponent ); @@ -185,6 +186,8 @@ public: // Simulator doesn't host a tool framework wxWindow* GetToolCanvas() const override { return nullptr; } + std::shared_ptr& GetSimulatorSettings(); + private: /** * Give icons to menuitems of the main menubar @@ -238,7 +241,8 @@ private: /** * Filter out tuners for components that do not exist anymore. - * Decisions are based on the current NETLIST_EXPORTER_BASE data. + * + * Decisions are based on the current #NETLIST_EXPORTER_BASE data. */ void updateTuners(); diff --git a/eeschema/sim/spice_simulator.cpp b/eeschema/sim/spice_simulator.cpp index 5b97e89aec..bcbbcd8eae 100644 --- a/eeschema/sim/spice_simulator.cpp +++ b/eeschema/sim/spice_simulator.cpp @@ -2,6 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2016 CERN + * Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors. * @author Tomasz Wlostowski * * This program is free software; you can redistribute it and/or @@ -26,6 +27,19 @@ #include +#include + + +const int ngspiceSettingsSchemaVersion = 0; + + +SPICE_SIMULATOR_SETTINGS::SPICE_SIMULATOR_SETTINGS( JSON_SETTINGS* aParent, + const std::string& aPath ) : + NESTED_SETTINGS( "simulator", ngspiceSettingsSchemaVersion, aParent, aPath ) +{ +} + + std::shared_ptr SPICE_SIMULATOR::CreateInstance( const std::string& ) { try @@ -39,12 +53,13 @@ std::shared_ptr SPICE_SIMULATOR::CreateInstance( const std::str } catch( std::exception& e ) { - DisplayError( NULL, e.what() ); + DisplayError( nullptr, e.what() ); } - return NULL; + return nullptr; } + wxString SPICE_SIMULATOR::TypeToName( SIM_TYPE aType, bool aShortName ) { switch( aType ) diff --git a/eeschema/sim/spice_simulator.h b/eeschema/sim/spice_simulator.h index a84987d4cc..37adba9f8b 100644 --- a/eeschema/sim/spice_simulator.h +++ b/eeschema/sim/spice_simulator.h @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2016 CERN - * Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2020-2021 KiCad Developers, see AUTHORS.txt for contributors. * * @author Tomasz Wlostowski * @@ -27,6 +27,8 @@ #ifndef SPICE_SIMULATOR_H #define SPICE_SIMULATOR_H +#include + #include "sim_types.h" #include @@ -37,23 +39,48 @@ #include class SPICE_REPORTER; -class SPICE_SIMULATOR; typedef std::complex COMPLEX; + +/** + * Storage for simulator specific settings. + */ +class SPICE_SIMULATOR_SETTINGS : public NESTED_SETTINGS +{ +public: + SPICE_SIMULATOR_SETTINGS( JSON_SETTINGS* aParent, const std::string& aPath ); + + virtual ~SPICE_SIMULATOR_SETTINGS() {} + + virtual bool operator==( const SPICE_SIMULATOR_SETTINGS& aRhs ) const = 0; + + bool operator!=( const SPICE_SIMULATOR_SETTINGS& aRhs ) const { return !( *this == aRhs ); } +}; + + class SPICE_SIMULATOR { public: - SPICE_SIMULATOR() : m_reporter( NULL ) {} + SPICE_SIMULATOR() : + m_reporter( nullptr ), + m_settings( nullptr ) + {} + virtual ~SPICE_SIMULATOR() {} ///< Create a simulator instance of particular type (currently only ngspice is handled) static std::shared_ptr CreateInstance( const std::string& aName ); - ///< Initialize the simulator - virtual void Init() = 0; - /* + * Initialize the simulator using the optional \a aSettings. + * + * @param aSettings [in] are the simulator specific settings. Can be null if no settings need + * to be initialized. + */ + virtual void Init( const SPICE_SIMULATOR_SETTINGS* aSettings = nullptr ) = 0; + + /** * Load a netlist for the simulation. * * @return True in case of success, false otherwise. @@ -91,7 +118,7 @@ public: ///< Return X axis name for a given simulation type virtual std::string GetXAxis( SIM_TYPE aType ) const = 0; - ///< Set a SPICE_REPORTER object to receive the simulation log. + ///< Set a #SPICE_REPORTER object to receive the simulation log. virtual void SetReporter( SPICE_REPORTER* aReporter ) { m_reporter = aReporter; @@ -99,7 +126,7 @@ public: /** * Return a list with all vectors generated in current simulation. - * @param none + * * @return List of vector names. ?May not match to the net name elements. */ virtual std::vector AllPlots() const = 0; @@ -164,6 +191,20 @@ public: */ virtual const std::string GetNetlist() const = 0; + /** + * @return a list of simulator setting command strings. + */ + virtual std::vector GetSettingCommands() const = 0; + + /** + * Return the simulator configuration settings. + * + * @return the simulator specific settings. + */ + std::shared_ptr& Settings() { return m_settings; } + + const std::shared_ptr& Settings() const { return m_settings; } + /** * Return a string with simulation name based on enum. * @@ -177,6 +218,9 @@ public: protected: ///< Reporter object to receive simulation log. SPICE_REPORTER* m_reporter; + + ///< We don't own this. We are just borrowing it from the #SCHEMATIC_SETTINGS. + std::shared_ptr m_settings; }; #endif /* SPICE_SIMULATOR_H */ diff --git a/include/project/project_file.h b/include/project/project_file.h index ef2137e597..5ba8edfecc 100644 --- a/include/project/project_file.h +++ b/include/project/project_file.h @@ -2,6 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2020 CERN + * Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors. * @author Jon Evans * * This program is free software: you can redistribute it and/or modify it @@ -22,6 +23,7 @@ #define KICAD_PROJECT_FILE_H #include // needed for wxstring hash template +#include #include #include #include @@ -54,7 +56,7 @@ enum LAST_PATH_TYPE : unsigned int }; /** - * PROJECT_FILE is the backing store for a PROJECT, in JSON format. + * The backing store for a PROJECT, in JSON format. * * There is either zero or one PROJECT_FILE for every PROJECT * (you can have a dummy PROJECT that has no file) @@ -63,7 +65,7 @@ class PROJECT_FILE : public JSON_SETTINGS { public: /** - * Constructs the project file for a project + * Construct the project file for a project * @param aFullPath is the full disk path to the project */ PROJECT_FILE( const wxString& aFullPath ); @@ -168,11 +170,12 @@ public: /** * Net settings for this project (owned here) - * NOTE: If we go multi-board in the future, we have to decide whether to use a global - * NET_SETTINGS or have one per board. Right now I think global makes more sense (one set of - * schematics, one netlist partitioned into multiple boards) + * + * @note If we go multi-board in the future, we have to decide whether to use a global + * NET_SETTINGS or have one per board. Right now I think global makes more sense + * (one set of schematics, one netlist partitioned into multiple boards) */ - std::shared_ptr m_NetSettings; + std::shared_ptr m_NetSettings; /// List of stored layer presets std::vector m_LayerPresets;