/* * 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. * * 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 Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * https://www.gnu.org/licenses/gpl-3.0.html * or you may search the http://www.gnu.org website for the version 3 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using DEVICE_TYPE = SIM_MODEL::DEVICE_TYPE_; using TYPE = SIM_MODEL::TYPE; namespace SIM_MODEL_PARSER { using namespace SIM_MODEL_GRAMMAR; template struct fieldParamValuePairsSelector : std::false_type {}; template <> struct fieldParamValuePairsSelector : std::true_type {}; template <> struct fieldParamValuePairsSelector : std::true_type {}; template <> struct fieldParamValuePairsSelector : std::true_type {}; template struct pinSequenceSelector : std::false_type {}; template <> struct pinSequenceSelector : std::true_type {}; } namespace SIM_MODEL_SPICE_PARSER { using namespace SPICE_GRAMMAR; template struct spiceUnitSelector : std::false_type {}; template <> struct spiceUnitSelector : std::true_type {}; template <> struct spiceUnitSelector : std::true_type {}; template <> struct spiceUnitSelector : std::true_type {}; template <> struct spiceUnitSelector : std::true_type {}; template <> struct spiceUnitSelector : std::true_type {}; template <> struct spiceUnitSelector : std::true_type {}; } SIM_MODEL::DEVICE_INFO SIM_MODEL::DeviceTypeInfo( DEVICE_TYPE_ aDeviceType ) { switch( aDeviceType ) { case DEVICE_TYPE_::NONE: return { "", "" }; case DEVICE_TYPE_::R: return { "R", "Resistor" }; case DEVICE_TYPE_::C: return { "C", "Capacitor" }; case DEVICE_TYPE_::L: return { "L", "Inductor" }; case DEVICE_TYPE_::TLINE: return { "TLINE", "Transmission Line" }; case DEVICE_TYPE_::SW: return { "SW", "Switch" }; case DEVICE_TYPE_::D: return { "D", "Diode" }; case DEVICE_TYPE_::NPN: return { "NPN", "NPN BJT" }; case DEVICE_TYPE_::PNP: return { "PNP", "PNP BJT" }; case DEVICE_TYPE_::NJFET: return { "NJFET", "N-channel JFET" }; case DEVICE_TYPE_::PJFET: return { "PJFET", "P-channel JFET" }; case DEVICE_TYPE_::NMOS: return { "NMOS", "N-channel MOSFET" }; case DEVICE_TYPE_::PMOS: return { "PMOS", "P-channel MOSFET" }; case DEVICE_TYPE_::NMES: return { "NMES", "N-channel MESFET" }; case DEVICE_TYPE_::PMES: return { "PMES", "P-channel MESFET" }; case DEVICE_TYPE_::V: return { "V", "Voltage Source" }; case DEVICE_TYPE_::I: return { "I", "Current Source" }; case DEVICE_TYPE_::SUBCKT: return { "SUBCKT", "Subcircuit" }; case DEVICE_TYPE_::XSPICE: return { "XSPICE", "XSPICE Code Model" }; case DEVICE_TYPE_::SPICE: return { "SPICE", "Raw Spice Element" }; case DEVICE_TYPE_::_ENUM_END: break; } wxFAIL; return {}; } SIM_MODEL::INFO SIM_MODEL::TypeInfo( TYPE aType ) { switch( aType ) { case TYPE::NONE: return { DEVICE_TYPE_::NONE, "", "" }; case TYPE::R: return { DEVICE_TYPE_::R, "", "Ideal" }; //case TYPE::R_ADV: return { DEVICE_TYPE::R, "ADV", "Advanced" }; case TYPE::R_BEHAVIORAL: return { DEVICE_TYPE_::R, "=", "Behavioral" }; case TYPE::C: return { DEVICE_TYPE_::C, "", "Ideal" }; //case TYPE::C_ADV: return { DEVICE_TYPE::C, "ADV", "Advanced" }; case TYPE::C_BEHAVIORAL: return { DEVICE_TYPE_::C, "=", "Behavioral" }; case TYPE::L: return { DEVICE_TYPE_::L, "", "Ideal" }; //case TYPE::L_ADV: return { DEVICE_TYPE::L, "ADV", "Advanced" }; case TYPE::L_BEHAVIORAL: return { DEVICE_TYPE_::L, "=", "Behavioral" }; case TYPE::TLINE_Z0: return { DEVICE_TYPE_::TLINE, "Z0", "Characteristic impedance" }; case TYPE::TLINE_RLGC: return { DEVICE_TYPE_::TLINE, "RLGC", "RLGC" }; case TYPE::SW_V: return { DEVICE_TYPE_::SW, "V", "Voltage-controlled" }; case TYPE::SW_I: return { DEVICE_TYPE_::SW, "I", "Current-controlled" }; case TYPE::D: return { DEVICE_TYPE_::D, "", "" }; case TYPE::NPN_GUMMELPOON: return { DEVICE_TYPE_::NPN, "GUMMELPOON", "Gummel-Poon" }; case TYPE::PNP_GUMMELPOON: return { DEVICE_TYPE_::PNP, "GUMMELPOON", "Gummel-Poon" }; case TYPE::NPN_VBIC: return { DEVICE_TYPE_::NPN, "VBIC", "VBIC" }; case TYPE::PNP_VBIC: return { DEVICE_TYPE_::PNP, "VBIC", "VBIC" }; //case TYPE::BJT_MEXTRAM: return {}; case TYPE::NPN_HICUM2: return { DEVICE_TYPE_::NPN, "HICUML2", "HICUM level 2" }; case TYPE::PNP_HICUM2: return { DEVICE_TYPE_::PNP, "HICUML2", "HICUM level 2" }; //case TYPE::BJT_HICUM_L0: return {}; case TYPE::NJFET_SHICHMANHODGES: return { DEVICE_TYPE_::NJFET, "SHICHMANHODGES", "Shichman-Hodges" }; case TYPE::PJFET_SHICHMANHODGES: return { DEVICE_TYPE_::PJFET, "SHICHMANHODGES", "Shichman-Hodges" }; case TYPE::NJFET_PARKERSKELLERN: return { DEVICE_TYPE_::NJFET, "PARKERSKELLERN", "Parker-Skellern" }; case TYPE::PJFET_PARKERSKELLERN: return { DEVICE_TYPE_::PJFET, "PARKERSKELLERN", "Parker-Skellern" }; case TYPE::NMES_STATZ: return { DEVICE_TYPE_::NMES, "STATZ", "Statz" }; case TYPE::PMES_STATZ: return { DEVICE_TYPE_::PMES, "STATZ", "Statz" }; case TYPE::NMES_YTTERDAL: return { DEVICE_TYPE_::NMES, "YTTERDAL", "Ytterdal" }; case TYPE::PMES_YTTERDAL: return { DEVICE_TYPE_::PMES, "YTTERDAL", "Ytterdal" }; case TYPE::NMES_HFET1: return { DEVICE_TYPE_::NMES, "HFET1", "HFET1" }; case TYPE::PMES_HFET1: return { DEVICE_TYPE_::PMES, "HFET1", "HFET1" }; case TYPE::NMES_HFET2: return { DEVICE_TYPE_::NMES, "HFET2", "HFET2" }; case TYPE::PMES_HFET2: return { DEVICE_TYPE_::PMES, "HFET2", "HFET2" }; case TYPE::NMOS_MOS1: return { DEVICE_TYPE_::NMOS, "MOS1", "Classical quadratic (MOS1)" }; case TYPE::PMOS_MOS1: return { DEVICE_TYPE_::PMOS, "MOS1", "Classical quadratic (MOS1)" }; case TYPE::NMOS_MOS2: return { DEVICE_TYPE_::NMOS, "MOS2", "Grove-Frohman (MOS2)" }; case TYPE::PMOS_MOS2: return { DEVICE_TYPE_::PMOS, "MOS2", "Grove-Frohman (MOS2)" }; case TYPE::NMOS_MOS3: return { DEVICE_TYPE_::NMOS, "MOS3", "MOS3" }; case TYPE::PMOS_MOS3: return { DEVICE_TYPE_::PMOS, "MOS3", "MOS3" }; case TYPE::NMOS_BSIM1: return { DEVICE_TYPE_::NMOS, "BSIM1", "BSIM1" }; case TYPE::PMOS_BSIM1: return { DEVICE_TYPE_::PMOS, "BSIM1", "BSIM1" }; case TYPE::NMOS_BSIM2: return { DEVICE_TYPE_::NMOS, "BSIM2", "BSIM2" }; case TYPE::PMOS_BSIM2: return { DEVICE_TYPE_::PMOS, "BSIM2", "BSIM2" }; case TYPE::NMOS_MOS6: return { DEVICE_TYPE_::NMOS, "MOS6", "MOS6" }; case TYPE::PMOS_MOS6: return { DEVICE_TYPE_::PMOS, "MOS6", "MOS6" }; case TYPE::NMOS_BSIM3: return { DEVICE_TYPE_::NMOS, "BSIM3", "BSIM3" }; case TYPE::PMOS_BSIM3: return { DEVICE_TYPE_::PMOS, "BSIM3", "BSIM3" }; case TYPE::NMOS_MOS9: return { DEVICE_TYPE_::NMOS, "MOS9", "MOS9" }; case TYPE::PMOS_MOS9: return { DEVICE_TYPE_::PMOS, "MOS9", "MOS9" }; case TYPE::NMOS_B4SOI: return { DEVICE_TYPE_::NMOS, "B4SOI", "BSIM4 SOI (B4SOI)" }; case TYPE::PMOS_B4SOI: return { DEVICE_TYPE_::PMOS, "B4SOI", "BSIM4 SOI (B4SOI)" }; case TYPE::NMOS_BSIM4: return { DEVICE_TYPE_::NMOS, "BSIM4", "BSIM4" }; case TYPE::PMOS_BSIM4: return { DEVICE_TYPE_::PMOS, "BSIM4", "BSIM4" }; //case TYPE::NMOS_EKV2_6: return {}; //case TYPE::PMOS_EKV2_6: return {}; //case TYPE::NMOS_PSP: return {}; //case TYPE::PMOS_PSP: return {}; case TYPE::NMOS_B3SOIFD: return { DEVICE_TYPE_::NMOS, "B3SOIFD", "B3SOIFD (BSIM3 FD-SOI)" }; case TYPE::PMOS_B3SOIFD: return { DEVICE_TYPE_::PMOS, "B3SOIFD", "B3SOIFD (BSIM3 FD-SOI)" }; case TYPE::NMOS_B3SOIDD: return { DEVICE_TYPE_::NMOS, "B3SOIDD", "B3SOIDD (BSIM3 SOI)" }; case TYPE::PMOS_B3SOIDD: return { DEVICE_TYPE_::PMOS, "B3SOIDD", "B3SOIDD (BSIM3 SOI)" }; case TYPE::NMOS_B3SOIPD: return { DEVICE_TYPE_::NMOS, "B3SOIPD", "B3SOIPD (BSIM3 PD-SOI)" }; case TYPE::PMOS_B3SOIPD: return { DEVICE_TYPE_::PMOS, "B3SOIPD", "B3SOIPD (BSIM3 PD-SOI)" }; //case TYPE::NMOS_STAG: return {}; //case TYPE::PMOS_STAG: return {}; case TYPE::NMOS_HISIM2: return { DEVICE_TYPE_::NMOS, "HISIM2", "HiSIM2" }; case TYPE::PMOS_HISIM2: return { DEVICE_TYPE_::PMOS, "HISIM2", "HiSIM2" }; case TYPE::NMOS_HISIMHV1: return { DEVICE_TYPE_::NMOS, "HISIMHV1", "HiSIM_HV1" }; case TYPE::PMOS_HISIMHV1: return { DEVICE_TYPE_::PMOS, "HISIMHV1", "HiSIM_HV1" }; case TYPE::NMOS_HISIMHV2: return { DEVICE_TYPE_::NMOS, "HISIMHV2", "HiSIM_HV2" }; case TYPE::PMOS_HISIMHV2: return { DEVICE_TYPE_::PMOS, "HISIMHV2", "HiSIM_HV2" }; case TYPE::V: return { DEVICE_TYPE_::V, "DC", "DC", }; case TYPE::V_SIN: return { DEVICE_TYPE_::V, "SIN", "Sine" }; case TYPE::V_PULSE: return { DEVICE_TYPE_::V, "PULSE", "Pulse" }; case TYPE::V_EXP: return { DEVICE_TYPE_::V, "EXP", "Exponential" }; /*case TYPE::V_SFAM: return { DEVICE_TYPE::V, "SFAM", "Single-frequency AM" }; case TYPE::V_SFFM: return { DEVICE_TYPE::V, "SFFM", "Single-frequency FM" };*/ case TYPE::V_PWL: return { DEVICE_TYPE_::V, "PWL", "Piecewise linear" }; case TYPE::V_WHITENOISE: return { DEVICE_TYPE_::V, "WHITENOISE", "White noise" }; case TYPE::V_PINKNOISE: return { DEVICE_TYPE_::V, "PINKNOISE", "Pink noise (1/f)" }; case TYPE::V_BURSTNOISE: return { DEVICE_TYPE_::V, "BURSTNOISE", "Burst noise" }; case TYPE::V_RANDUNIFORM: return { DEVICE_TYPE_::V, "RANDUNIFORM", "Random uniform" }; case TYPE::V_RANDNORMAL: return { DEVICE_TYPE_::V, "RANDNORMAL", "Random normal" }; case TYPE::V_RANDEXP: return { DEVICE_TYPE_::V, "RANDEXP", "Random exponential" }; //case TYPE::V_RANDPOISSON: return { DEVICE_TYPE::V, "RANDPOISSON", "Random Poisson" }; case TYPE::V_BEHAVIORAL: return { DEVICE_TYPE_::V, "=", "Behavioral" }; case TYPE::I: return { DEVICE_TYPE_::I, "DC", "DC", }; case TYPE::I_SIN: return { DEVICE_TYPE_::I, "SIN", "Sine" }; case TYPE::I_PULSE: return { DEVICE_TYPE_::I, "PULSE", "Pulse" }; case TYPE::I_EXP: return { DEVICE_TYPE_::I, "EXP", "Exponential" }; /*case TYPE::I_SFAM: return { DEVICE_TYPE::I, "SFAM", "Single-frequency AM" }; case TYPE::I_SFFM: return { DEVICE_TYPE::I, "SFFM", "Single-frequency FM" };*/ case TYPE::I_PWL: return { DEVICE_TYPE_::I, "PWL", "Piecewise linear" }; case TYPE::I_WHITENOISE: return { DEVICE_TYPE_::I, "WHITENOISE", "White noise" }; case TYPE::I_PINKNOISE: return { DEVICE_TYPE_::I, "PINKNOISE", "Pink noise (1/f)" }; case TYPE::I_BURSTNOISE: return { DEVICE_TYPE_::I, "BURSTNOISE", "Burst noise" }; case TYPE::I_RANDUNIFORM: return { DEVICE_TYPE_::I, "RANDUNIFORM", "Random uniform" }; case TYPE::I_RANDNORMAL: return { DEVICE_TYPE_::I, "RANDNORMAL", "Random normal" }; case TYPE::I_RANDEXP: return { DEVICE_TYPE_::I, "RANDEXP", "Random exponential" }; //case TYPE::I_RANDPOISSON: return { DEVICE_TYPE::I, "RANDPOISSON", "Random Poisson" }; case TYPE::I_BEHAVIORAL: return { DEVICE_TYPE_::I, "=", "Behavioral" }; case TYPE::SUBCKT: return { DEVICE_TYPE_::SUBCKT, "", "" }; case TYPE::XSPICE: return { DEVICE_TYPE_::XSPICE, "", "" }; case TYPE::SPICE: return { DEVICE_TYPE_::SPICE, "", "" }; case TYPE::_ENUM_END: break; } wxFAIL; return {}; } SIM_MODEL::SPICE_INFO SIM_MODEL::SpiceInfo( TYPE aType ) { switch( aType ) { case TYPE::R: return { "R", "" }; //case TYPE::R_ADV: return { "R", "r" }; case TYPE::R_BEHAVIORAL: return { "R", "", "", "0", false, true }; case TYPE::C: return { "C", "" }; //case TYPE::C_ADV: return { "C", "c", }; case TYPE::C_BEHAVIORAL: return { "C", "", "", "0", false, true }; case TYPE::L: return { "L", "" }; //case TYPE::L_ADV: return { "L", "l" }; case TYPE::L_BEHAVIORAL: return { "L", "", "", "0", false, true }; case TYPE::TLINE_Z0: return { "T" }; case TYPE::TLINE_RLGC: return { "O", "ltra" }; case TYPE::SW_V: return { "S", "switch" }; case TYPE::SW_I: return { "W", "cswitch" }; case TYPE::D: return { "D", "d" }; case TYPE::NPN_GUMMELPOON: return { "Q", "npn", "", "1", true }; case TYPE::PNP_GUMMELPOON: return { "Q", "pnp", "", "1", true }; case TYPE::NPN_VBIC: return { "Q", "npn", "", "4" }; case TYPE::PNP_VBIC: return { "Q", "pnp", "", "4" }; case TYPE::NPN_HICUM2: return { "Q", "npn", "", "8" }; case TYPE::PNP_HICUM2: return { "Q", "pnp", "", "8" }; case TYPE::NJFET_SHICHMANHODGES: return { "M", "njf", "", "1" }; case TYPE::PJFET_SHICHMANHODGES: return { "M", "pjf", "", "1" }; case TYPE::NJFET_PARKERSKELLERN: return { "M", "njf", "", "2" }; case TYPE::PJFET_PARKERSKELLERN: return { "M", "pjf", "", "2" }; case TYPE::NMES_STATZ: return { "Z", "nmf", "", "1" }; case TYPE::PMES_STATZ: return { "Z", "pmf", "", "1" }; case TYPE::NMES_YTTERDAL: return { "Z", "nmf", "", "2" }; case TYPE::PMES_YTTERDAL: return { "Z", "pmf", "", "2" }; case TYPE::NMES_HFET1: return { "Z", "nmf", "", "5" }; case TYPE::PMES_HFET1: return { "Z", "pmf", "", "5" }; case TYPE::NMES_HFET2: return { "Z", "nmf", "", "6" }; case TYPE::PMES_HFET2: return { "Z", "pmf", "", "6" }; case TYPE::NMOS_MOS1: return { "M", "nmos", "", "1" }; case TYPE::PMOS_MOS1: return { "M", "pmos", "", "1" }; case TYPE::NMOS_MOS2: return { "M", "nmos", "", "2" }; case TYPE::PMOS_MOS2: return { "M", "pmos", "", "2" }; case TYPE::NMOS_MOS3: return { "M", "nmos", "", "3" }; case TYPE::PMOS_MOS3: return { "M", "pmos", "", "3" }; case TYPE::NMOS_BSIM1: return { "M", "nmos", "", "4" }; case TYPE::PMOS_BSIM1: return { "M", "pmos", "", "4" }; case TYPE::NMOS_BSIM2: return { "M", "nmos", "", "5" }; case TYPE::PMOS_BSIM2: return { "M", "pmos", "", "5" }; case TYPE::NMOS_MOS6: return { "M", "nmos", "", "6" }; case TYPE::PMOS_MOS6: return { "M", "pmos", "", "6" }; case TYPE::NMOS_BSIM3: return { "M", "nmos", "", "8" }; case TYPE::PMOS_BSIM3: return { "M", "pmos", "", "8" }; case TYPE::NMOS_MOS9: return { "M", "nmos", "", "9" }; case TYPE::PMOS_MOS9: return { "M", "pmos", "", "9" }; case TYPE::NMOS_B4SOI: return { "M", "nmos", "", "10" }; case TYPE::PMOS_B4SOI: return { "M", "pmos", "", "10" }; case TYPE::NMOS_BSIM4: return { "M", "nmos", "", "14" }; case TYPE::PMOS_BSIM4: return { "M", "pmos", "", "14" }; //case TYPE::NMOS_EKV2_6: return {}; //case TYPE::PMOS_EKV2_6: return {}; //case TYPE::NMOS_PSP: return {}; //case TYPE::PMOS_PSP: return {}; case TYPE::NMOS_B3SOIFD: return { "M", "nmos", "", "55" }; case TYPE::PMOS_B3SOIFD: return { "M", "pmos", "", "55" }; case TYPE::NMOS_B3SOIDD: return { "M", "nmos", "", "56" }; case TYPE::PMOS_B3SOIDD: return { "M", "pmos", "", "56" }; case TYPE::NMOS_B3SOIPD: return { "M", "nmos", "", "57" }; case TYPE::PMOS_B3SOIPD: return { "M", "pmos", "", "57" }; //case TYPE::NMOS_STAG: return {}; //case TYPE::PMOS_STAG: return {}; case TYPE::NMOS_HISIM2: return { "M", "nmos", "", "68" }; case TYPE::PMOS_HISIM2: return { "M", "pmos", "", "68" }; case TYPE::NMOS_HISIMHV1: return { "M", "nmos", "", "73", true, false, "1.2.4" }; case TYPE::PMOS_HISIMHV1: return { "M", "pmos", "", "73", true, false, "1.2.4" }; case TYPE::NMOS_HISIMHV2: return { "M", "nmos", "", "73", true, false, "2.2.0" }; case TYPE::PMOS_HISIMHV2: return { "M", "pmos", "", "73", true, false, "2.2.0" }; case TYPE::V: return { "V", "" }; case TYPE::V_SIN: return { "V", "", "SIN" }; case TYPE::V_PULSE: return { "V", "", "PULSE" }; case TYPE::V_EXP: return { "V", "", "EXP" }; /*case TYPE::V_SFAM: return { "V", "", "AM" }; case TYPE::V_SFFM: return { "V", "", "SFFM" };*/ case TYPE::V_PWL: return { "V", "", "PWL" }; case TYPE::V_WHITENOISE: return { "V", "", "TRNOISE" }; case TYPE::V_PINKNOISE: return { "V", "", "TRNOISE" }; case TYPE::V_BURSTNOISE: return { "V", "", "TRNOISE" }; case TYPE::V_RANDUNIFORM: return { "V", "", "TRRANDOM" }; case TYPE::V_RANDNORMAL: return { "V", "", "TRRANDOM" }; case TYPE::V_RANDEXP: return { "V", "", "TRRANDOM" }; //case TYPE::V_RANDPOISSON: return { "V", "", "TRRANDOM" }; case TYPE::V_BEHAVIORAL: return { "B" }; case TYPE::I: return { "I", "" }; case TYPE::I_PULSE: return { "I", "", "PULSE" }; case TYPE::I_SIN: return { "I", "", "SIN" }; case TYPE::I_EXP: return { "I", "", "EXP" }; /*case TYPE::I_SFAM: return { "V", "", "AM" }; case TYPE::I_SFFM: return { "V", "", "SFFM" };*/ case TYPE::I_PWL: return { "I", "", "PWL" }; case TYPE::I_WHITENOISE: return { "I", "", "TRNOISE" }; case TYPE::I_PINKNOISE: return { "I", "", "TRNOISE" }; case TYPE::I_BURSTNOISE: return { "I", "", "TRNOISE" }; case TYPE::I_RANDUNIFORM: return { "I", "", "TRRANDOM" }; case TYPE::I_RANDNORMAL: return { "I", "", "TRRANDOM" }; case TYPE::I_RANDEXP: return { "I", "", "TRRANDOM" }; //case TYPE::I_RANDPOISSON: return { "I", "", "TRRANDOM" }; case TYPE::I_BEHAVIORAL: return { "B" }; case TYPE::SUBCKT: return { "X" }; case TYPE::XSPICE: return { "A" }; case TYPE::NONE: case TYPE::SPICE: return {}; case TYPE::_ENUM_END: break; } wxFAIL; return {}; } TYPE SIM_MODEL::ReadTypeFromSpiceCode( const wxString& aSpiceCode ) { tao::pegtl::string_input<> in( aSpiceCode.ToUTF8(), "Spice_Code" ); std::unique_ptr root; try { root = tao::pegtl::parse_tree::parse ( in ); } catch( const tao::pegtl::parse_error& e ) { wxLogDebug( "%s", e.what() ); return TYPE::NONE; } for( const auto& node : root->children ) { if( node->is_type() ) { wxString paramName; wxString typeString; wxString level; wxString version; for( const auto& subnode : node->children ) { if( subnode->is_type() ) { // Do nothing. } else if( subnode->is_type() ) { typeString = subnode->string(); TYPE type = readTypeFromSpiceStrings( typeString ); if( type != TYPE::SPICE ) return type; } else if( subnode->is_type() ) { paramName = subnode->string(); } else if( subnode->is_type() ) { wxASSERT( paramName != "" ); if( paramName == "level" ) level = subnode->string(); else if( paramName == "version" ) version = subnode->string(); } else { wxFAIL_MSG( "Unhandled parse tree subnode" ); return TYPE::NONE; } } // Type was not determined from Spice type string alone, so now we take `level` and // `version` variables into account too. This is suboptimal since we read the model // twice this way, and moreover the code is now somewhat duplicated. return readTypeFromSpiceStrings( typeString, level, version, false ); } else if( node->is_type() ) return TYPE::SUBCKT; else { wxFAIL_MSG( "Unhandled parse tree node" ); return TYPE::NONE; } } wxFAIL_MSG( "Could not derive type from Spice code" ); return TYPE::NONE; } template TYPE SIM_MODEL::ReadTypeFromFields( const std::vector& aFields ); template TYPE SIM_MODEL::ReadTypeFromFields( const std::vector& aFields ); template TYPE SIM_MODEL::ReadTypeFromFields( const std::vector& aFields ) { wxString deviceTypeFieldValue = GetFieldValue( &aFields, DEVICE_TYPE_FIELD ); wxString typeFieldValue = GetFieldValue( &aFields, TYPE_FIELD ); if( deviceTypeFieldValue != "" ) { for( TYPE type : TYPE_ITERATOR() ) { if( typeFieldValue == TypeInfo( type ).fieldValue ) { if( deviceTypeFieldValue == DeviceTypeInfo( TypeInfo( type ).deviceType ).fieldValue ) return type; } } } if( !typeFieldValue.IsEmpty() ) return TYPE::NONE; // No type information. Look for legacy (pre-V7) fields. TYPE typeFromLegacyFields = InferTypeFromLegacyFields( aFields ); if( typeFromLegacyFields != TYPE::NONE ) return typeFromLegacyFields; // Still no type information. // For passives we infer the model from the mandatory fields in this case. return InferTypeFromRefAndValue( GetFieldValue( &aFields, REFERENCE_FIELD ), GetFieldValue( &aFields, VALUE_FIELD ) ); } TYPE SIM_MODEL::InferTypeFromRefAndValue( const wxString& aRef, const wxString& aValue ) { static std::map refPrefixToType = { { "R", TYPE::R }, { "C", TYPE::C }, { "L", TYPE::L }, { "TLINE", TYPE::TLINE_Z0 }, { "VSIN", TYPE::V_SIN }, { "VPULSE", TYPE::V_PULSE }, { "VEXP", TYPE::V_EXP }, /*{ "VSFAM", TYPE::V_SFAM }, { "VSFFM", TYPE::V_SFFM },*/ { "VPWL", TYPE::V_PWL }, { "VWHITENOISE", TYPE::V_WHITENOISE }, { "VPINKNOISE", TYPE::V_PINKNOISE }, { "VBURSTNOISE", TYPE::V_BURSTNOISE }, { "VRANDUNIFORM", TYPE::V_RANDUNIFORM }, { "VRANDNORMAL", TYPE::V_RANDNORMAL }, { "VRANDEXP", TYPE::V_RANDEXP }, //{ "VRANDPOISSON", TYPE::V_RANDPOISSON }, { "ISIN", TYPE::I_SIN }, { "IPULSE", TYPE::I_PULSE }, { "IEXP", TYPE::I_EXP }, /*{ "ISFAM", TYPE::I_SFAM }, { "ISFFM", TYPE::I_SFFM },*/ { "IPWL", TYPE::I_PWL }, { "IWHITENOISE", TYPE::I_WHITENOISE }, { "IPINKNOISE", TYPE::I_PINKNOISE }, { "IBURSTNOISE", TYPE::I_BURSTNOISE }, { "IRANDUNIFORM", TYPE::I_RANDUNIFORM }, { "IRANDNORMAL", TYPE::I_RANDNORMAL }, { "IRANDEXP", TYPE::I_RANDEXP }, //{ "IRANDPOISSON", TYPE::I_RANDPOISSON }, }; TYPE type = TYPE::NONE; for( auto&& [curPrefix, curType] : refPrefixToType ) { if( aRef.StartsWith( curPrefix ) ) { type = curType; break; } } // We handle "V" and "I" later because it collides and std::map is unordered. if( type == TYPE::NONE && aRef.StartsWith( "V" ) ) type = TYPE::V; if( type == TYPE::NONE && aRef.StartsWith( "I" ) ) type = TYPE::I; wxString value = aValue; // Some types have to be inferred from Value field. switch( type ) { case TYPE::R: if( value.Trim( false ).StartsWith( "=" ) ) type = TYPE::R_BEHAVIORAL; break; case TYPE::C: if( value.Trim( false ).StartsWith( "=" ) ) type = TYPE::C_BEHAVIORAL; break; case TYPE::L: if( value.Trim( false ).StartsWith( "=" ) ) type = TYPE::L_BEHAVIORAL; break; case TYPE::V: if( value.Trim( false ).StartsWith( "=" ) ) type = TYPE::V_BEHAVIORAL; break; case TYPE::I: if( value.Trim( false ).StartsWith( "=" ) ) type = TYPE::I_BEHAVIORAL; break; case TYPE::TLINE_Z0: try { tao::pegtl::string_input<> in( aValue.ToUTF8(), "Value" ); auto root = tao::pegtl::parse_tree::parse< SIM_MODEL_PARSER::fieldParamValuePairsGrammar, SIM_MODEL_PARSER::fieldParamValuePairsSelector> ( in ); for( const auto& node : root->children ) { if( node->is_type() && (node->string() == "r" || node->string() == "R" || node->string() == "c" || node->string() == "C" || node->string() == "l" || node->string() == "L" ) ) { type = TYPE::TLINE_RLGC; break; } } } catch( const tao::pegtl::parse_error& ) { } break; default: break; } return type; } template TYPE SIM_MODEL::InferTypeFromLegacyFields( const std::vector& aFields ) { if( GetFieldValue( &aFields, SIM_MODEL_SPICE::LEGACY_TYPE_FIELD ) != "" || GetFieldValue( &aFields, SIM_MODEL_SPICE::LEGACY_MODEL_FIELD ) != "" || GetFieldValue( &aFields, SIM_MODEL_SPICE::LEGACY_ENABLED_FIELD ) != "" || GetFieldValue( &aFields, SIM_MODEL_SPICE::LEGACY_LIB_FIELD ) != "" ) { return TYPE::SPICE; } else return TYPE::NONE; } template void SIM_MODEL::ReadDataFields( unsigned aSymbolPinCount, const std::vector* aFields ) { doReadDataFields( aSymbolPinCount, aFields ); } template <> void SIM_MODEL::ReadDataFields( unsigned aSymbolPinCount, const std::vector* aFields ) { ReadDataSchFields( aSymbolPinCount, aFields ); } template <> void SIM_MODEL::ReadDataFields( unsigned aSymbolPinCount, const std::vector* aFields ) { ReadDataLibFields( aSymbolPinCount, aFields ); } void SIM_MODEL::ReadDataSchFields( unsigned aSymbolPinCount, const std::vector* aFields ) { doReadDataFields( aSymbolPinCount, aFields ); } void SIM_MODEL::ReadDataLibFields( unsigned aSymbolPinCount, const std::vector* aFields ) { doReadDataFields( aSymbolPinCount, aFields ); } template <> void SIM_MODEL::WriteFields( std::vector& aFields ) const { WriteDataSchFields( aFields ); } template <> void SIM_MODEL::WriteFields( std::vector& aFields ) const { WriteDataLibFields( aFields ); } void SIM_MODEL::WriteDataSchFields( std::vector& aFields ) const { doWriteFields( aFields ); } void SIM_MODEL::WriteDataLibFields( std::vector& aFields ) const { doWriteFields( aFields ); } std::unique_ptr SIM_MODEL::Create( TYPE aType, unsigned aSymbolPinCount ) { std::unique_ptr model = create( aType ); // Passing nullptr to ReadDataFields will make it act as if all fields were empty. model->ReadDataFields( aSymbolPinCount, static_cast*>( nullptr ) ); return model; } std::unique_ptr SIM_MODEL::Create( const wxString& aSpiceCode ) { std::unique_ptr model = create( ReadTypeFromSpiceCode( aSpiceCode ) ); try { model->ReadSpiceCode( aSpiceCode ); } catch( const IO_ERROR& e ) { wxLogDebug( "%s", e.What() ); // Demote to raw Spice element and try again. std::unique_ptr rawSpiceModel = create( TYPE::SPICE ); rawSpiceModel->ReadSpiceCode( aSpiceCode ); return rawSpiceModel; } return model; } std::unique_ptr SIM_MODEL::Create( const SIM_MODEL& aBaseModel, unsigned aSymbolPinCount ) { std::unique_ptr model = create( aBaseModel.GetType() ); model->SetBaseModel( aBaseModel ); model->ReadDataFields( aSymbolPinCount, static_cast*>( nullptr ) ); return model; } template std::unique_ptr SIM_MODEL::Create( const SIM_MODEL& aBaseModel, unsigned aSymbolPinCount, const std::vector& aFields ) { std::unique_ptr model = create( aBaseModel.GetType() ); model->SetBaseModel( aBaseModel ); model->ReadDataFields( aSymbolPinCount, &aFields ); return model; } template std::unique_ptr SIM_MODEL::Create( const SIM_MODEL& aBaseModel, unsigned aSymbolPinCount, const std::vector& aFields ); template std::unique_ptr SIM_MODEL::Create( const SIM_MODEL& aBaseModel, unsigned aSymbolPinCount, const std::vector& aFields ); template std::unique_ptr SIM_MODEL::Create( unsigned aSymbolPinCount, const std::vector& aFields ) { std::unique_ptr model = SIM_MODEL::create( ReadTypeFromFields( aFields ) ); model->ReadDataFields( aSymbolPinCount, &aFields ); return model; } template std::unique_ptr SIM_MODEL::Create( unsigned aSymbolPinCount, const std::vector& aFields ); template std::unique_ptr SIM_MODEL::Create( unsigned aSymbolPinCount, const std::vector& aFields ); template wxString SIM_MODEL::GetFieldValue( const std::vector* aFields, const wxString& aFieldName ) { static_assert( std::is_same::value || std::is_same::value ); if( !aFields ) return wxEmptyString; // Should not happen, T=void specialization will be called instead. auto fieldIt = std::find_if( aFields->begin(), aFields->end(), [aFieldName]( const T& field ) { return field.GetName() == aFieldName; } ); if( fieldIt != aFields->end() ) return fieldIt->GetText(); return wxEmptyString; } // This specialization is used when no fields are passed. template <> wxString SIM_MODEL::GetFieldValue( const std::vector* aFields, const wxString& aFieldName ) { return wxEmptyString; } template void SIM_MODEL::SetFieldValue( std::vector& aFields, const wxString& aFieldName, const wxString& aValue ) { static_assert( std::is_same::value || std::is_same::value ); auto fieldIt = std::find_if( aFields.begin(), aFields.end(), [&]( const T& f ) { return f.GetName() == aFieldName; } ); if( fieldIt != aFields.end() ) { if( aValue.IsEmpty() ) aFields.erase( fieldIt ); else fieldIt->SetText( aValue ); return; } if( aValue.IsEmpty() ) return; if constexpr( std::is_same::value ) { wxASSERT( aFields.size() >= 1 ); SCH_ITEM* parent = static_cast( aFields.at( 0 ).GetParent() ); aFields.emplace_back( wxPoint(), aFields.size(), parent, aFieldName ); } else if constexpr( std::is_same::value ) aFields.emplace_back( aFields.size(), aFieldName ); aFields.back().SetText( aValue ); } void SIM_MODEL::ReadSpiceCode( const wxString& aSpiceCode ) { // The default behavior is to treat the Spice param=value pairs as the model parameters and // values (for many models the correspondence is not exact, so this function is overridden). tao::pegtl::string_input<> in( aSpiceCode.ToUTF8(), "Spice_Code" ); std::unique_ptr root; try { root = tao::pegtl::parse_tree::parse ( in ); } catch( tao::pegtl::parse_error& e ) { wxString msg; msg.Printf( _( "Error parsing spice code <%s>\n%s" ), aSpiceCode, e.what() ); THROW_IO_ERROR( e.what() ); } for( const auto& node : root->children ) { if( node->is_type() ) { wxString paramName = ""; for( const auto& subnode : node->children ) { if( subnode->is_type() ) { // Do nothing. } else if( subnode->is_type() ) { // Do nothing. } else if( subnode->is_type() ) { paramName = subnode->string(); } else if( subnode->is_type() ) { wxASSERT( !paramName.IsEmpty() ); if( !SetParamFromSpiceCode( paramName, subnode->string() ) ) { THROW_IO_ERROR( wxString::Format( _( "Failed to set parameter '%s' to '%s'" ), paramName, subnode->string() ) ); } } else { wxFAIL_MSG( "Unhandled parse tree subnode" ); } } } else { wxFAIL_MSG( "Unhandled parse tree node" ); } } m_spiceCode = aSpiceCode; } wxString SIM_MODEL::GenerateSpiceModelLine( const wxString& aModelName ) const { LOCALE_IO toggle; if( !HasSpiceNonInstanceOverrides() || !requiresSpiceModel() ) return ""; wxString result = ""; wxString line = ""; line << wxString::Format( ".model %s %s(\n+", aModelName, GetSpiceInfo().modelType ); for( const PARAM& param : GetParams() ) { if( param.info.isSpiceInstanceParam ) continue; wxString valueStr = param.value->ToSpiceString(); if( valueStr.IsEmpty() ) continue; wxString append = " " + param.info.name + "=" + valueStr; if( line.Length() + append.Length() > 60 ) { result << line + "\n"; line = "+" + append; } else line << append; } result << line + " )\n"; return result; } wxString SIM_MODEL::GenerateSpiceItemName( const wxString& aRefName ) const { if( aRefName != "" && aRefName.StartsWith( GetSpiceInfo().itemType ) ) return aRefName; else return GetSpiceInfo().itemType + aRefName; } wxString SIM_MODEL::GenerateSpiceItemLine( const wxString& aRefName, const wxString& aModelName ) const { std::vector pinNames; for( const PIN& pin : GetPins() ) pinNames.push_back( pin.name ); return GenerateSpiceItemLine( aRefName, aModelName, pinNames ); } wxString SIM_MODEL::GenerateSpiceItemLine( const wxString& aRefName, const wxString& aModelName, const std::vector& aPinNetNames ) const { wxString result = ""; result << GenerateSpiceItemName( aRefName ) << " "; for( const PIN& pin : GetPins() ) { for( unsigned i = 0; i < aPinNetNames.size(); ++i ) { unsigned symbolPinNumber = i + 1; if( symbolPinNumber == pin.symbolPinNumber ) result << aPinNetNames[i] << " "; } } if( requiresSpiceModel() ) result << aModelName << " "; for( const PARAM& param : GetParams() ) { if( !param.info.isSpiceInstanceParam ) continue; wxString name = ( param.info.spiceInstanceName == "" ) ? param.info.name : param.info.spiceInstanceName; wxString value = param.value->ToString( SIM_VALUE_GRAMMAR::NOTATION::SPICE ); if( value != "" ) result << name << "=" << value << " "; } result << "\n"; return result; } wxString SIM_MODEL::GenerateSpiceTuningLine( const wxString& aSymbol ) const { // TODO. return ""; } wxString SIM_MODEL::GenerateSpicePreview( const wxString& aModelName ) const { wxString spiceCode = GenerateSpiceModelLine( aModelName ); if( spiceCode == "" ) spiceCode = m_spiceCode; if( spiceCode == "" && GetBaseModel() ) spiceCode = GetBaseModel()->m_spiceCode; wxString itemLine = GenerateSpiceItemLine( "", aModelName ); if( spiceCode != "" ) spiceCode << "\n"; spiceCode << itemLine; return spiceCode.Trim(); } std::vector SIM_MODEL::GenerateSpiceCurrentNames( const wxString& aRefName ) const { LOCALE_IO toggle; return { wxString::Format( "I(%s)", GenerateSpiceItemName( aRefName ) ) }; } void SIM_MODEL::AddPin( const PIN& aPin ) { m_pins.push_back( aPin ); } unsigned SIM_MODEL::FindModelPinNumber( unsigned aSymbolPinNumber ) { for( unsigned i = 0; i < GetPinCount(); ++i ) { if( GetPin( i ).symbolPinNumber == aSymbolPinNumber ) return i + 1; } return 0; } void SIM_MODEL::AddParam( const PARAM::INFO& aInfo, bool aIsOtherVariant ) { m_params.emplace_back( aInfo, aIsOtherVariant ); } std::vector> SIM_MODEL::GetPins() const { std::vector> pins; for( unsigned i = 0; i < GetPinCount(); ++i ) pins.emplace_back( GetPin( i ) ); return pins; } const SIM_MODEL::PARAM& SIM_MODEL::GetParam( unsigned aParamIndex ) const { if( m_baseModel && m_params.at( aParamIndex ).value->ToString() == "" ) return m_baseModel->GetParam( aParamIndex ); else return m_params.at( aParamIndex ); } const SIM_MODEL::PARAM* SIM_MODEL::FindParam( const wxString& aParamName ) const { std::vector> params = GetParams(); auto it = std::find_if( params.begin(), params.end(), [aParamName]( const PARAM& param ) { return param.info.name == aParamName.Lower(); } ); if( it == params.end() ) return nullptr; return &it->get(); } std::vector> SIM_MODEL::GetParams() const { std::vector> params; for( unsigned i = 0; i < GetParamCount(); ++i ) params.emplace_back( GetParam( i ) ); return params; } const SIM_MODEL::PARAM& SIM_MODEL::GetUnderlyingParam( unsigned aParamIndex ) const { return m_params.at( aParamIndex ); } const SIM_MODEL::PARAM& SIM_MODEL::GetBaseParam( unsigned aParamIndex ) const { if( m_baseModel ) return m_baseModel->GetParam( aParamIndex ); else return m_params.at( aParamIndex ); } bool SIM_MODEL::SetParamValue( unsigned aParamIndex, const wxString& aValue, SIM_VALUE_GRAMMAR::NOTATION aNotation ) { // Models sourced from a library are immutable. if( m_spiceCode != "" ) return false; return m_params.at( aParamIndex ).value->FromString( aValue, aNotation ); } bool SIM_MODEL::SetParamValue( const wxString& aParamName, const wxString& aValue, SIM_VALUE_GRAMMAR::NOTATION aNotation ) { std::vector> params = GetParams(); auto it = std::find_if( params.begin(), params.end(), [aParamName]( const PARAM& param ) { return param.info.name == aParamName.Lower(); } ); if( it == params.end() ) return false; return SetParamValue( it - params.begin(), aValue, aNotation ); } bool SIM_MODEL::HasOverrides() const { for( const PARAM& param : m_params ) { if( param.value->ToString() != "" ) return true; } return false; } bool SIM_MODEL::HasNonInstanceOverrides() const { for( const PARAM& param : m_params ) { if( !param.info.isInstanceParam && param.value->ToString() != "" ) return true; } return false; } bool SIM_MODEL::HasSpiceNonInstanceOverrides() const { for( const PARAM& param : m_params ) { if( !param.info.isSpiceInstanceParam && param.value->ToString() != "" ) return true; } return false; } SIM_MODEL::SIM_MODEL( TYPE aType ) : m_baseModel( nullptr ), m_type( aType ), m_isEnabled( true ), m_isInferred( false ) { } void SIM_MODEL::CreatePins( unsigned aSymbolPinCount ) { // Default pin sequence: model pins are the same as symbol pins. // Excess model pins are set as Not Connected. // Note that intentionally nothing is added if `getPinNames()` returns an empty vector. for( unsigned i = 0; i < getPinNames().size(); ++i ) { if( i < aSymbolPinCount ) AddPin( { getPinNames().at( i ), i + 1 } ); else AddPin( { getPinNames().at( i ), PIN::NOT_CONNECTED } ); } } template void SIM_MODEL::WriteInferredDataFields( std::vector& aFields, const wxString& aValue ) const; template void SIM_MODEL::WriteInferredDataFields( std::vector& aFields, const wxString& aValue ) const; template void SIM_MODEL::WriteInferredDataFields( std::vector& aFields, const wxString& aValue ) const { if( GetPinCount() == 2 && GetPin( 0 ).symbolPinNumber == 1 && GetPin( 1 ).symbolPinNumber == 2 ) { SetFieldValue( aFields, PINS_FIELD, "" ); } SetFieldValue( aFields, VALUE_FIELD, aValue ); SetFieldValue( aFields, DEVICE_TYPE_FIELD, "" ); SetFieldValue( aFields, TYPE_FIELD, "" ); SetFieldValue( aFields, PARAMS_FIELD, "" ); SetFieldValue( aFields, DISABLED_FIELD, "" ); } wxString SIM_MODEL::GenerateParamValuePair( const PARAM& aParam, bool& aIsFirst ) const { wxString result = ""; if( aIsFirst ) aIsFirst = false; else result << " "; wxString name = aParam.info.name; // Because of collisions with instance parameters, we append some model parameters with "_". if( aParam.info.name.EndsWith( "_" ) ) name = aParam.info.name.BeforeLast( '_' ); wxString value = aParam.value->ToString(); if( value.Contains( " " ) ) value = "\"" + value + "\""; result << aParam.info.name + "=" + value; return result; } wxString SIM_MODEL::GenerateParamsField( const wxString& aPairSeparator ) const { bool isFirst = true; wxString result = ""; for( const PARAM& param : m_params ) { if( param.value->ToString() == "" ) continue; result << GenerateParamValuePair( param, isFirst ); } return result; } void SIM_MODEL::ParseParamsField( const wxString& aParamsField ) { LOCALE_IO toggle; tao::pegtl::string_input<> in( aParamsField.ToUTF8(), "Sim_Params" ); std::unique_ptr root; try { // Using parse tree instead of actions because we don't care about performance that much, // and having a tree greatly simplifies some things. root = tao::pegtl::parse_tree::parse< SIM_MODEL_PARSER::fieldParamValuePairsGrammar, SIM_MODEL_PARSER::fieldParamValuePairsSelector> ( in ); } catch( const tao::pegtl::parse_error& e ) { wxString msg; msg.Printf( _( "Error parsing param <%s>\n%s" ), aParamsField, e.what() ); THROW_IO_ERROR( msg ); } wxString paramName = ""; for( const auto& node : root->children ) { if( node->is_type() ) paramName = node->string(); // TODO: Do something with number. // It doesn't seem too useful? else if( node->is_type() || node->is_type() ) { wxASSERT( paramName != "" ); // TODO: Shouldn't be named "...fromSpiceCode" here... SetParamValue( paramName, node->string(), SIM_VALUE_GRAMMAR::NOTATION::SI ); } else if( node->is_type() ) { wxASSERT( !paramName.IsEmpty() ); wxString str = node->string(); // Unescape quotes. str.Replace( "\\\"", "\"" ); SetParamValue( paramName, str, SIM_VALUE_GRAMMAR::NOTATION::SI ); } else { wxFAIL; } } } void SIM_MODEL::ParsePinsField( unsigned aSymbolPinCount, const wxString& aPinsField ) { CreatePins( aSymbolPinCount ); if( aPinsField == "" ) return; tao::pegtl::string_input<> in( aPinsField.ToUTF8(), PINS_FIELD ); std::unique_ptr root; try { root = tao::pegtl::parse_tree::parse( in ); } catch( const tao::pegtl::parse_error& e ) { wxString msg; msg.Printf( _( "Error parsing pin field <%s>\n%s" ), aPinsField, e.what() ); THROW_IO_ERROR( e.what() ); } if( root->children.size() != GetPinCount() ) { THROW_IO_ERROR( wxString::Format( _( "%s describes %lu pins, expected %u" ), PINS_FIELD, root->children.size(), GetPinCount() ) ); } for( unsigned i = 0; i < root->children.size(); ++i ) { if( root->children.at( i )->string() == "X" ) SetPinSymbolPinNumber( static_cast( i ), PIN::NOT_CONNECTED ); else SetPinSymbolPinNumber( static_cast( i ), std::stoi( root->children.at( i )->string() ) ); } } void SIM_MODEL::ParseDisabledField( const wxString& aDisabledField ) { if( aDisabledField == "" ) return; char c = aDisabledField.Lower()[0]; if( c == 'y' || c == 't' || c == '1' ) m_isEnabled = false; } bool SIM_MODEL::SetParamFromSpiceCode( const wxString& aParamName, const wxString& aParamValue, SIM_VALUE_GRAMMAR::NOTATION aNotation ) { return SetParamValue( aParamName, aParamValue, aNotation ); } std::unique_ptr SIM_MODEL::create( TYPE aType ) { switch( aType ) { case TYPE::R: case TYPE::C: case TYPE::L: return std::make_unique( aType ); case TYPE::R_BEHAVIORAL: case TYPE::C_BEHAVIORAL: case TYPE::L_BEHAVIORAL: case TYPE::V_BEHAVIORAL: case TYPE::I_BEHAVIORAL: return std::make_unique( aType ); case TYPE::TLINE_Z0: case TYPE::TLINE_RLGC: return std::make_unique( aType ); case TYPE::V: case TYPE::I: case TYPE::V_SIN: case TYPE::I_SIN: case TYPE::V_PULSE: case TYPE::I_PULSE: case TYPE::V_EXP: case TYPE::I_EXP: /*case TYPE::V_SFAM: case TYPE::I_SFAM: case TYPE::V_SFFM: case TYPE::I_SFFM:*/ case TYPE::V_PWL: case TYPE::I_PWL: case TYPE::V_WHITENOISE: case TYPE::I_WHITENOISE: case TYPE::V_PINKNOISE: case TYPE::I_PINKNOISE: case TYPE::V_BURSTNOISE: case TYPE::I_BURSTNOISE: case TYPE::V_RANDUNIFORM: case TYPE::I_RANDUNIFORM: case TYPE::V_RANDNORMAL: case TYPE::I_RANDNORMAL: case TYPE::V_RANDEXP: case TYPE::I_RANDEXP: //case TYPE::V_RANDPOISSON: //case TYPE::I_RANDPOISSON: return std::make_unique( aType ); case TYPE::SUBCKT: return std::make_unique( aType ); case TYPE::XSPICE: return std::make_unique( aType ); case TYPE::SPICE: return std::make_unique( aType ); default: return std::make_unique( aType ); } } TYPE SIM_MODEL::readTypeFromSpiceStrings( const wxString& aTypeString, const wxString& aLevel, const wxString& aVersion, bool aSkipDefaultLevel ) { std::unique_ptr readLevel = SIM_VALUE::Create( SIM_VALUE::TYPE_INT, aLevel ); for( TYPE type : TYPE_ITERATOR() ) { wxString typePrefix = SpiceInfo( type ).modelType; wxString level = SpiceInfo( type ).level; wxString version = SpiceInfo( type ).version; bool isDefaultLevel = SpiceInfo( type ).isDefaultLevel; if( typePrefix == "" ) continue; // Check if `aTypeString` starts with `typePrefix`. if( aTypeString.Lower().StartsWith( typePrefix ) && ( level == readLevel->ToString() || ( !aSkipDefaultLevel && isDefaultLevel && aLevel == "" ) ) && version == aVersion ) { return type; } } // If the type string is not recognized, demote to a raw Spice element. This way the user won't // have an error if there is a type KiCad does not recognize. return TYPE::SPICE; } template void SIM_MODEL::doReadDataFields( unsigned aSymbolPinCount, const std::vector* aFields ) { ParsePinsField( aSymbolPinCount, GetFieldValue( aFields, PINS_FIELD ) ); ParseParamsField( GetFieldValue( aFields, PARAMS_FIELD ) ); ParseDisabledField( GetFieldValue( aFields, DISABLED_FIELD ) ); } template void SIM_MODEL::InferredReadDataFields( unsigned aSymbolPinCount, const std::vector* aFields ) { ParsePinsField( aSymbolPinCount, GetFieldValue( aFields, PINS_FIELD ) ); if( InferTypeFromRefAndValue( GetFieldValue( aFields, REFERENCE_FIELD ), GetFieldValue( aFields, VALUE_FIELD ) ) == GetType() ) { ParseParamsField( GetFieldValue( aFields, VALUE_FIELD ) ); m_isInferred = true; } else if( GetFieldValue( aFields, VALUE_FIELD ) == DeviceTypeInfo( GetDeviceType() ).fieldValue ) { m_isInferred = true; } } template void SIM_MODEL::InferredReadDataFields( unsigned aSymbolPinCount, const std::vector* aFields ); template void SIM_MODEL::InferredReadDataFields( unsigned aSymbolPinCount, const std::vector* aFields ); template void SIM_MODEL::doWriteFields( std::vector& aFields ) const { SetFieldValue( aFields, DEVICE_TYPE_FIELD, generateDeviceTypeField() ); SetFieldValue( aFields, TYPE_FIELD, generateTypeField() ); SetFieldValue( aFields, PINS_FIELD, generatePinsField() ); SetFieldValue( aFields, PARAMS_FIELD, GenerateParamsField( " " ) ); SetFieldValue( aFields, DISABLED_FIELD, generateDisabledField() ); } wxString SIM_MODEL::generateDeviceTypeField() const { return DeviceTypeInfo( TypeInfo( m_type ).deviceType ).fieldValue; } wxString SIM_MODEL::generateTypeField() const { return TypeInfo( m_type ).fieldValue; } wxString SIM_MODEL::generatePinsField() const { wxString result = ""; bool isFirst = true; for( unsigned i = 0; i < GetPinCount(); ++i ) { if( isFirst ) isFirst = false; else result << " "; if( GetPin( i ).symbolPinNumber == PIN::NOT_CONNECTED ) result << "X"; else result << GetPin( i ).symbolPinNumber; } return result; } wxString SIM_MODEL::generateDisabledField() const { return m_isEnabled ? "" : "1"; } bool SIM_MODEL::requiresSpiceModel() const { for( const PARAM& param : GetParams() ) { if( !param.info.isSpiceInstanceParam ) return true; } return false; }