/* * 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 using DEVICE_TYPE = SIM_MODEL::DEVICE_TYPE; using TYPE = SIM_MODEL::TYPE; namespace SIM_MODEL_PARSER { using namespace SIM_MODEL_GRAMMAR; template struct paramValuePairsSelector : std::false_type {}; template <> struct paramValuePairsSelector : std::true_type {}; template <> struct paramValuePairsSelector> : std::true_type {}; template <> struct paramValuePairsSelector> : std::true_type {}; template <> struct paramValuePairsSelector> : std::true_type {}; template <> struct paramValuePairsSelector> : std::true_type {}; 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 {}; template <> struct spiceUnitSelector> : std::true_type {}; template <> struct spiceUnitSelector> : std::true_type {}; template <> struct spiceUnitSelector : std::true_type {}; template struct pinSequenceSelector : std::false_type {}; template <> struct pinSequenceSelector : std::true_type {}; } SIM_MODEL::DEVICE_INFO SIM_MODEL::DeviceTypeInfo( DEVICE_TYPE aDeviceType ) { switch( aDeviceType ) { case DEVICE_TYPE::NONE: return { "", "" }; case DEVICE_TYPE::RESISTOR: return { "RESISTOR", "Resistor" }; case DEVICE_TYPE::CAPACITOR: return { "CAPACITOR", "Capacitor" }; case DEVICE_TYPE::INDUCTOR: return { "INDUCTOR", "Inductor" }; case DEVICE_TYPE::TLINE: return { "TLINE", "Transmission Line" }; case DEVICE_TYPE::SWITCH: return { "SWITCH", "Switch" }; case DEVICE_TYPE::DIODE: return { "DIODE", "Diode" }; case DEVICE_TYPE::NPN: return { "NPN", "NPN BJT" }; case DEVICE_TYPE::PNP: return { "PNP", "PNP BJT" }; case DEVICE_TYPE::NJF: return { "NJF", "N-Channel JFET" }; case DEVICE_TYPE::PJF: return { "PJF", "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::VSOURCE: return { "VSOURCE", "Voltage Source" }; case DEVICE_TYPE::ISOURCE: return { "ISOURCE", "Current Source" }; case DEVICE_TYPE::SUBCIRCUIT: return { "SUBCIRCUIT", "Subcircuit" }; case DEVICE_TYPE::CODEMODEL: return { "CODEMODEL", "Code Model" }; case DEVICE_TYPE::RAWSPICE: return { "RAWSPICE", "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::RESISTOR_IDEAL: return { DEVICE_TYPE::RESISTOR, "IDEAL", "Ideal" }; case TYPE::RESISTOR_ADVANCED: return { DEVICE_TYPE::RESISTOR, "ADVANCED", "Advanced" }; case TYPE::RESISTOR_BEHAVIORAL: return { DEVICE_TYPE::RESISTOR, "BEHAVIORAL", "Behavioral" }; case TYPE::CAPACITOR_IDEAL: return { DEVICE_TYPE::CAPACITOR, "IDEAL", "Ideal" }; case TYPE::CAPACITOR_ADVANCED: return { DEVICE_TYPE::CAPACITOR, "ADVANCED", "Advanced" }; case TYPE::CAPACITOR_BEHAVIORAL: return { DEVICE_TYPE::CAPACITOR, "BEHAVIORAL", "Behavioral" }; case TYPE::INDUCTOR_IDEAL: return { DEVICE_TYPE::INDUCTOR, "IDEAL", "Ideal" }; case TYPE::INDUCTOR_ADVANCED: return { DEVICE_TYPE::INDUCTOR, "ADVANCED", "Advanced" }; case TYPE::INDUCTOR_BEHAVIORAL: return { DEVICE_TYPE::INDUCTOR, "BEHAVIORAL", "Behavioral" }; case TYPE::TLINE_LOSSY: return { DEVICE_TYPE::TLINE, "LOSSY", "Lossy" }; case TYPE::TLINE_LOSSLESS: return { DEVICE_TYPE::TLINE, "LOSSLESS", "Lossless" }; case TYPE::TLINE_UNIFORM_RC: return { DEVICE_TYPE::TLINE, "UNIFORM_RC", "Uniform RC" }; case TYPE::TLINE_KSPICE: return { DEVICE_TYPE::TLINE, "KSPICE", "KSPICE" }; case TYPE::SWITCH_VCTRL: return { DEVICE_TYPE::SWITCH, "VCTRL", "Voltage-controlled" }; case TYPE::SWITCH_ICTRL: return { DEVICE_TYPE::SWITCH, "ICTRL", "Current-controlled" }; case TYPE::DIODE: return { DEVICE_TYPE::DIODE, "", "" }; case TYPE::NPN_GUMMEL_POON: return { DEVICE_TYPE::NPN, "GUMMEL_POON", "Gummel-Poon" }; case TYPE::PNP_GUMMEL_POON: return { DEVICE_TYPE::PNP, "GUMMEL_POON", "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_HICUM_L2: return { DEVICE_TYPE::NPN, "HICUM_L2", "HICUM Level 2" }; case TYPE::PNP_HICUM_L2: return { DEVICE_TYPE::PNP, "HICUM_L2", "HICUM Level 2" }; //case TYPE::BJT_HICUM_L0: return {}; case TYPE::NJF_SHICHMAN_HODGES: return { DEVICE_TYPE::NJF, "SHICHMAN_HODGES", "Shichman-Hodges" }; case TYPE::PJF_SHICHMAN_HODGES: return { DEVICE_TYPE::PJF, "SHICHMAN_HODGES", "Shichman-Hodges" }; case TYPE::NJF_PARKER_SKELLERN: return { DEVICE_TYPE::NJF, "PARKER_SKELLERN", "Parker-Skellern" }; case TYPE::PJF_PARKER_SKELLERN: return { DEVICE_TYPE::PJF, "PARKER_SKELLERN", "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::PMES_HFET2: return { DEVICE_TYPE::NMES, "HFET2", "HFET2" }; case TYPE::NMES_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_HISIM_HV1: return { DEVICE_TYPE::NMOS, "HISIM_HV1", "HiSIM_HV1" }; case TYPE::PMOS_HISIM_HV1: return { DEVICE_TYPE::PMOS, "HISIM_HV1", "HiSIM_HV1" }; case TYPE::NMOS_HISIM_HV2: return { DEVICE_TYPE::NMOS, "HISIM_HV2", "HiSIM_HV2" }; case TYPE::PMOS_HISIM_HV2: return { DEVICE_TYPE::PMOS, "HISIM_HV2", "HiSIM_HV2" }; case TYPE::VSOURCE_PULSE: return { DEVICE_TYPE::VSOURCE, "PULSE", "Pulse" }; case TYPE::VSOURCE_SIN: return { DEVICE_TYPE::VSOURCE, "SINCE", "Sine" }; case TYPE::VSOURCE_EXP: return { DEVICE_TYPE::VSOURCE, "EXP", "Exponential" }; case TYPE::VSOURCE_SFAM: return { DEVICE_TYPE::VSOURCE, "SFAM", "Single-frequency AM" }; case TYPE::VSOURCE_SFFM: return { DEVICE_TYPE::VSOURCE, "SFFM", "Single-frequency FM" }; case TYPE::VSOURCE_PWL: return { DEVICE_TYPE::VSOURCE, "PWL", "Piecewise linear" }; case TYPE::VSOURCE_WHITE_NOISE: return { DEVICE_TYPE::VSOURCE, "WHITE_NOISE", "White Noise" }; case TYPE::VSOURCE_PINK_NOISE: return { DEVICE_TYPE::VSOURCE, "PINK_NOISE", "Pink Noise (1/f)" }; case TYPE::VSOURCE_BURST_NOISE: return { DEVICE_TYPE::VSOURCE, "BURST_NOISE", "Burst Noise" }; case TYPE::VSOURCE_RANDOM_UNIFORM: return { DEVICE_TYPE::VSOURCE, "RANDOM_UNIFORM", "Random uniform" }; case TYPE::VSOURCE_RANDOM_NORMAL: return { DEVICE_TYPE::VSOURCE, "RANDOM_NORMAL", "Random normal" }; case TYPE::VSOURCE_RANDOM_EXP: return { DEVICE_TYPE::VSOURCE, "RANDOM_EXP", "Random exponential" }; case TYPE::VSOURCE_RANDOM_POISSON: return { DEVICE_TYPE::VSOURCE, "RANDOM_POISSON", "Random Poisson" }; case TYPE::VSOURCE_BEHAVIORAL: return { DEVICE_TYPE::VSOURCE, "BEHAVIORAL", "Behavioral" }; case TYPE::ISOURCE_PULSE: return { DEVICE_TYPE::ISOURCE, "PULSE", "Pulse" }; case TYPE::ISOURCE_SIN: return { DEVICE_TYPE::ISOURCE, "SINCE", "Sine" }; case TYPE::ISOURCE_EXP: return { DEVICE_TYPE::ISOURCE, "EXP", "Exponential" }; case TYPE::ISOURCE_SFAM: return { DEVICE_TYPE::ISOURCE, "SFAM", "Single-frequency AM" }; case TYPE::ISOURCE_SFFM: return { DEVICE_TYPE::ISOURCE, "SFFM", "Single-frequency FM" }; case TYPE::ISOURCE_PWL: return { DEVICE_TYPE::ISOURCE, "PWL", "Piecewise linear" }; case TYPE::ISOURCE_WHITE_NOISE: return { DEVICE_TYPE::ISOURCE, "WHITE_NOISE", "White Noise" }; case TYPE::ISOURCE_PINK_NOISE: return { DEVICE_TYPE::ISOURCE, "PINK_NOISE", "Pink Noise (1/f)" }; case TYPE::ISOURCE_BURST_NOISE: return { DEVICE_TYPE::ISOURCE, "BURST_NOISE", "Burst Noise" }; case TYPE::ISOURCE_RANDOM_UNIFORM: return { DEVICE_TYPE::ISOURCE, "RANDOM_UNIFORM", "Random uniform" }; case TYPE::ISOURCE_RANDOM_NORMAL: return { DEVICE_TYPE::ISOURCE, "RANDOM_NORMAL", "Random normal" }; case TYPE::ISOURCE_RANDOM_EXP: return { DEVICE_TYPE::ISOURCE, "RANDOM_EXP", "Random exponential" }; case TYPE::ISOURCE_RANDOM_POISSON: return { DEVICE_TYPE::ISOURCE, "RANDOM_POISSON", "Random Poisson" }; case TYPE::ISOURCE_BEHAVIORAL: return { DEVICE_TYPE::ISOURCE, "BEHAVIORAL", "Behavioral" }; case TYPE::SUBCIRCUIT: return { DEVICE_TYPE::SUBCIRCUIT, "", "" }; case TYPE::CODEMODEL: return { DEVICE_TYPE::CODEMODEL, "", "" }; case TYPE::RAWSPICE: return { DEVICE_TYPE::RAWSPICE, "", "" }; case TYPE::_ENUM_END: break; } wxFAIL; return {}; } SIM_MODEL::SPICE_INFO SIM_MODEL::SpiceInfo( TYPE aType ) { switch( aType ) { case TYPE::RESISTOR_IDEAL: return { "R", "" }; case TYPE::RESISTOR_ADVANCED: return { "R", "R" }; case TYPE::RESISTOR_BEHAVIORAL: return { "R", "", "", 0, true }; case TYPE::CAPACITOR_IDEAL: return { "C", "" }; case TYPE::CAPACITOR_ADVANCED: return { "C", "C", }; case TYPE::CAPACITOR_BEHAVIORAL: return { "C", "", "", 0, true }; case TYPE::INDUCTOR_IDEAL: return { "L", "" }; case TYPE::INDUCTOR_ADVANCED: return { "L", "L" }; case TYPE::INDUCTOR_BEHAVIORAL: return { "L", "", "", 0, true }; case TYPE::TLINE_LOSSY: return { "O", "LTRA" }; case TYPE::TLINE_LOSSLESS: return { "T" }; case TYPE::TLINE_UNIFORM_RC: return { "U" }; case TYPE::TLINE_KSPICE: return { "Y" }; case TYPE::SWITCH_VCTRL: return { "S", "switch" }; case TYPE::SWITCH_ICTRL: return { "W", "cswitch" }; case TYPE::DIODE: return { "D", "D" }; case TYPE::NPN_GUMMEL_POON: return { "Q", "NPN", "", 1 }; case TYPE::PNP_GUMMEL_POON: return { "Q", "PNP", "", 1 }; case TYPE::NPN_VBIC: return { "Q", "NPN", "", 4 }; case TYPE::PNP_VBIC: return { "Q", "PNP", "", 4 }; case TYPE::NPN_HICUM_L2: return { "Q", "NPN", "", 8 }; case TYPE::PNP_HICUM_L2: return { "Q", "PNP", "", 8 }; case TYPE::NJF_SHICHMAN_HODGES: return { "M", "NJF", "", 1 }; case TYPE::PJF_SHICHMAN_HODGES: return { "M", "PJF", "", 1 }; case TYPE::NJF_PARKER_SKELLERN: return { "M", "NJF", "", 2 }; case TYPE::PJF_PARKER_SKELLERN: 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::PMES_HFET2: return { "Z", "NMF", "", 6 }; case TYPE::NMES_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_HISIM_HV1: return { "M", "NMOS", "", 73, false, "1.2.4" }; case TYPE::PMOS_HISIM_HV1: return { "M", "PMOS", "", 73, false, "1.2.4" }; case TYPE::NMOS_HISIM_HV2: return { "M", "NMOS", "", 73, false, "2.2.0" }; case TYPE::PMOS_HISIM_HV2: return { "M", "PMOS", "", 73, false, "2.2.0" }; case TYPE::VSOURCE_PULSE: return { "V", "", "PULSE" }; case TYPE::VSOURCE_SIN: return { "V", "", "SIN" }; case TYPE::VSOURCE_EXP: return { "V", "", "EXP" }; case TYPE::VSOURCE_SFAM: return { "V", "", "AM" }; case TYPE::VSOURCE_SFFM: return { "V", "", "SFFM" }; case TYPE::VSOURCE_PWL: return { "V", "", "PWL" }; case TYPE::VSOURCE_WHITE_NOISE: return { "V", "", "TRNOISE" }; case TYPE::VSOURCE_PINK_NOISE: return { "V", "", "TRNOISE" }; case TYPE::VSOURCE_BURST_NOISE: return { "V", "", "TRNOISE" }; case TYPE::VSOURCE_RANDOM_UNIFORM: return { "V", "", "TRRANDOM" }; case TYPE::VSOURCE_RANDOM_NORMAL: return { "V", "", "TRRANDOM" }; case TYPE::VSOURCE_RANDOM_EXP: return { "V", "", "TRRANDOM" }; case TYPE::VSOURCE_RANDOM_POISSON: return { "V", "", "TRRANDOM" }; case TYPE::VSOURCE_BEHAVIORAL: return { "B" }; case TYPE::ISOURCE_PULSE: return { "V", "", "PULSE" }; case TYPE::ISOURCE_SIN: return { "V", "", "SIN" }; case TYPE::ISOURCE_EXP: return { "V", "", "EXP" }; case TYPE::ISOURCE_SFAM: return { "V", "", "AM" }; case TYPE::ISOURCE_SFFM: return { "V", "", "SFFM" }; case TYPE::ISOURCE_PWL: return { "V", "", "PWL" }; case TYPE::ISOURCE_WHITE_NOISE: return { "V", "", "TRNOISE" }; case TYPE::ISOURCE_PINK_NOISE: return { "V", "", "TRNOISE" }; case TYPE::ISOURCE_BURST_NOISE: return { "V", "", "TRNOISE" }; case TYPE::ISOURCE_RANDOM_UNIFORM: return { "V", "", "TRRANDOM" }; case TYPE::ISOURCE_RANDOM_NORMAL: return { "V", "", "TRRANDOM" }; case TYPE::ISOURCE_RANDOM_EXP: return { "V", "", "TRRANDOM" }; case TYPE::ISOURCE_RANDOM_POISSON: return { "V", "", "TRRANDOM" }; case TYPE::ISOURCE_BEHAVIORAL: return { "B" }; case TYPE::SUBCIRCUIT: return { "X" }; case TYPE::CODEMODEL: return { "A" }; case TYPE::NONE: case TYPE::RAWSPICE: return {}; case TYPE::_ENUM_END: break; } wxFAIL; return {}; } TYPE SIM_MODEL::ReadTypeFromSpiceCode( const std::string& aSpiceCode ) { tao::pegtl::string_input<> in( aSpiceCode, "from_input" ); std::unique_ptr root; try { root = tao::pegtl::parse_tree::parse ( in ); } catch( tao::pegtl::parse_error& e ) { throw KI_PARAM_ERROR( wxString::Format( _( "Failed to parse '%s': %s" ), aSpiceCode, e.what() ) ); } wxASSERT( root ); for( const auto& node : root->children ) { if( node->is_type() ) { for( const auto& subnode : node->children ) { if( subnode->is_type() ) { // Do nothing. } else if( subnode->is_type() ) return readTypeFromSpiceTypeString( subnode->string() ); else { wxFAIL_MSG( "Unhandled parse tree subnode" ); return TYPE::NONE; } } } else if( node->is_type() ) return TYPE::SUBCIRCUIT; 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 typeFieldValue = GetFieldValue( &aFields, TYPE_FIELD ); wxString deviceTypeFieldValue = GetFieldValue( &aFields, DEVICE_TYPE_FIELD ); bool typeFound = false; for( TYPE type : TYPE_ITERATOR() ) { if( typeFieldValue == TypeInfo( type ).fieldValue ) { typeFound = true; if( deviceTypeFieldValue == DeviceTypeInfo( TypeInfo( type ).deviceType ).fieldValue ) return type; } } // TODO: Return TYPE::NONE instead of throwing an exception. if( !typeFound ) throw KI_PARAM_ERROR( wxString::Format( _( "Invalid '%s' field value: '%s'" ), TYPE_FIELD, typeFieldValue ) ); throw KI_PARAM_ERROR( wxString::Format( _( "Invalid '%s' field value: '%s'" ), DEVICE_TYPE_FIELD, deviceTypeFieldValue ) ); } std::unique_ptr SIM_MODEL::Create( TYPE aType, int 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 std::string& aSpiceCode ) { std::unique_ptr model = create( ReadTypeFromSpiceCode( aSpiceCode ) ); if( !model->ReadSpiceCode( aSpiceCode ) ) { // Demote to raw Spice element and try again. std::unique_ptr rawSpiceModel = create( TYPE::RAWSPICE ); rawSpiceModel->ReadSpiceCode( aSpiceCode ); return rawSpiceModel; } return model; } std::unique_ptr SIM_MODEL::Create( const SIM_MODEL& aBaseModel ) { std::unique_ptr model = create( aBaseModel.GetType() ); model->SetBaseModel( aBaseModel ); return model; } template std::unique_ptr SIM_MODEL::Create( int aSymbolPinCount, const std::vector& aFields ); template std::unique_ptr SIM_MODEL::Create( int aSymbolPinCount, const std::vector& aFields ); template std::unique_ptr SIM_MODEL::Create( int aSymbolPinCount, const std::vector& aFields ) { std::unique_ptr model = SIM_MODEL::create( ReadTypeFromFields( aFields ) ); model->ReadDataFields( aSymbolPinCount, &aFields ); return model; } 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 ); } bool SIM_MODEL::ReadSpiceCode( const std::string& 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, "from_input" ); std::unique_ptr root; try { root = tao::pegtl::parse_tree::parse ( in ); } catch( tao::pegtl::parse_error& e ) { return false; } wxASSERT( root ); std::cout << "BEGIN" << std::endl; // DEBUG TRACE for( const auto& node : root->children ) { std::cout << "node: " << node->string() << std::endl; // DEBUG TRACE if( node->is_type() || 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(); } // TODO: Do something with number. // It doesn't seem too useful? else if( subnode->is_type< SIM_MODEL_PARSER::number>() ) { wxASSERT( !paramName.IsEmpty() ); if( !setParamFromSpiceCode( paramName, subnode->string() ) ) return false; } else { wxFAIL_MSG( "Unhandled parse tree subnode" ); return false; } } } else { wxFAIL_MSG( "Unhandled parse tree node" ); return false; } } std::cout << "END" << std::endl; // DEBUG TRACE m_spiceCode = aSpiceCode; return true; } template void SIM_MODEL::ReadDataFields( int aSymbolPinCount, const std::vector* aFields ) { doReadDataFields( aSymbolPinCount, aFields ); } template <> void SIM_MODEL::ReadDataFields( int aSymbolPinCount, const std::vector* aFields ) { ReadDataSchFields( aSymbolPinCount, aFields ); } template <> void SIM_MODEL::ReadDataFields( int aSymbolPinCount, const std::vector* aFields ) { ReadDataLibFields( aSymbolPinCount, aFields ); } void SIM_MODEL::ReadDataSchFields( int aSymbolPinCount, const std::vector* aFields ) { doReadDataFields( aSymbolPinCount, aFields ); } void SIM_MODEL::ReadDataLibFields( int aSymbolPinCount, const std::vector* aFields ) { doReadDataFields( aSymbolPinCount, aFields ); } template <> void SIM_MODEL::WriteFields( std::vector& aFields ) { WriteDataSchFields( aFields ); } template <> void SIM_MODEL::WriteFields( std::vector& aFields ) { WriteDataLibFields( aFields ); } void SIM_MODEL::WriteDataSchFields( std::vector& aFields ) { doWriteFields( aFields ); } void SIM_MODEL::WriteDataLibFields( std::vector& aFields ) { doWriteFields( aFields ); } wxString SIM_MODEL::GenerateSpiceIncludeLine( const wxString& aLibraryFilename ) const { LOCALE_IO toggle; if( GetBaseModel() && !HasOverrides() ) return wxString::Format( ".include \"%s\"\n", aLibraryFilename ); return ""; } wxString SIM_MODEL::GenerateSpiceModelLine( const wxString& aModelName ) const { LOCALE_IO toggle; wxString result = ""; wxString line = ""; line << wxString::Format( ".model %s %s(\n+", aModelName, GetSpiceInfo().typeString ); for( int paramIndex = 0; paramIndex < GetParamCount(); ++paramIndex ) { const PARAM& param = GetParam( paramIndex ); wxString valueStr = param.value->ToString(); if( valueStr.IsEmpty() ) continue; wxString append = ""; append << " "; append << param.info.name; append << "="; append << param.value->ToString(); if( line.Length() + append.Length() > 60 ) { result << line + "\n"; line = "+" + append; } else line << append; } result << line + ")\n"; return result; } SIM_MODEL::SPICE_INFO SIM_MODEL::GetSpiceInfo() const { return SpiceInfo( GetType() ); } wxString SIM_MODEL::GenerateSpiceItemLine( const wxString& aRefName, const wxString& aModelName ) const { return GenerateSpiceItemLine( aRefName, aModelName, getPinNames() ); } wxString SIM_MODEL::GenerateSpiceItemLine( const wxString& aRefName, const wxString& aModelName, const std::vector& aPinNetNames ) const { wxString result = ""; if( aRefName.Length() >= 1 && aRefName.StartsWith( GetSpiceInfo().itemType ) ) result << aRefName << " "; else result << GetSpiceInfo().itemType << aRefName << " "; for( const wxString& pinNetName : aPinNetNames ) result << pinNetName << " "; result << aModelName; return result; } wxString SIM_MODEL::GenerateSpicePreview( const wxString& aModelName ) const { if( !m_spiceCode.IsEmpty() ) return m_spiceCode; // `aModelName` is ignored in this case. if( GetBaseModel() && !HasOverrides() ) return GetBaseModel()->GenerateSpicePreview( aModelName ); wxString modelLine = GenerateSpiceModelLine( aModelName ); if( !modelLine.IsEmpty() ) return modelLine; return GenerateSpiceItemLine( "", aModelName ); } void SIM_MODEL::AddParam( const PARAM::INFO& aInfo, bool aIsOtherVariant ) { m_params.emplace_back( aInfo ); } const SIM_MODEL::PARAM& SIM_MODEL::GetParam( int aParamIndex ) const { if( m_baseModel && m_params.at( aParamIndex ).value->ToString().IsEmpty() ) return m_baseModel->GetParam( aParamIndex ); else return m_params.at( aParamIndex ); } const SIM_MODEL::PARAM& SIM_MODEL::GetUnderlyingParam( int aParamIndex ) const { return m_params.at( aParamIndex ); } const SIM_MODEL::PARAM& SIM_MODEL::GetBaseParam( int aParamIndex ) const { if( m_baseModel ) return m_baseModel->GetParam( aParamIndex ); else return m_params.at( aParamIndex ); } bool SIM_MODEL::SetParamValue( int aParamIndex, const wxString& aValue ) { // Models sourced from a library are immutable. if( !m_spiceCode.IsEmpty() ) return false; m_params.at( aParamIndex ).value->FromString( aValue ); return true; } bool SIM_MODEL::HasOverrides() const { for( const PARAM& param : m_params ) { if( !param.value->ToString().IsEmpty() ) return true; } return false; } bool SIM_MODEL::HasNonPrincipalOverrides() const { for( const PARAM& param : m_params ) { if( param.info.category != PARAM::CATEGORY::PRINCIPAL && !param.value->ToString().IsEmpty() ) { return true; } } return false; } SIM_MODEL::SIM_MODEL( TYPE aType ) : m_baseModel( nullptr ), m_type( aType ) { } std::unique_ptr SIM_MODEL::create( TYPE aType ) { switch( aType ) { case TYPE::RESISTOR_IDEAL: case TYPE::CAPACITOR_IDEAL: case TYPE::INDUCTOR_IDEAL: return std::make_unique( aType ); case TYPE::RESISTOR_BEHAVIORAL: case TYPE::CAPACITOR_BEHAVIORAL: case TYPE::INDUCTOR_BEHAVIORAL: case TYPE::VSOURCE_BEHAVIORAL: case TYPE::ISOURCE_BEHAVIORAL: return std::make_unique( aType ); case TYPE::VSOURCE_PULSE: case TYPE::ISOURCE_PULSE: case TYPE::VSOURCE_SIN: case TYPE::ISOURCE_SIN: case TYPE::VSOURCE_EXP: case TYPE::ISOURCE_EXP: case TYPE::VSOURCE_SFAM: case TYPE::ISOURCE_SFAM: case TYPE::VSOURCE_SFFM: case TYPE::ISOURCE_SFFM: case TYPE::VSOURCE_PWL: case TYPE::ISOURCE_PWL: case TYPE::VSOURCE_WHITE_NOISE: case TYPE::ISOURCE_WHITE_NOISE: case TYPE::VSOURCE_PINK_NOISE: case TYPE::ISOURCE_PINK_NOISE: case TYPE::VSOURCE_BURST_NOISE: case TYPE::ISOURCE_BURST_NOISE: case TYPE::VSOURCE_RANDOM_UNIFORM: case TYPE::ISOURCE_RANDOM_UNIFORM: case TYPE::VSOURCE_RANDOM_NORMAL: case TYPE::ISOURCE_RANDOM_NORMAL: case TYPE::VSOURCE_RANDOM_EXP: case TYPE::ISOURCE_RANDOM_EXP: case TYPE::VSOURCE_RANDOM_POISSON: case TYPE::ISOURCE_RANDOM_POISSON: return std::make_unique( aType ); case TYPE::SUBCIRCUIT: return std::make_unique( aType ); case TYPE::CODEMODEL: return std::make_unique( aType ); case TYPE::RAWSPICE: return std::make_unique( aType ); default: return std::make_unique( aType ); } } TYPE SIM_MODEL::readTypeFromSpiceTypeString( const std::string& aTypeString ) { for( TYPE type : TYPE_ITERATOR() ) { if( SpiceInfo( type ).typeString == aTypeString ) return type; } // If the type string is not recognized, demote to raw Spice element. This way the user won't // have an error if there is a type KiCad does not recognize. return TYPE::RAWSPICE; } template void SIM_MODEL::doReadDataFields( int aSymbolPinCount, const std::vector* aFields ) { parsePinsField( aSymbolPinCount, GetFieldValue( aFields, PINS_FIELD ) ); parseParamsField( GetFieldValue( aFields, PARAMS_FIELD ) ); } template void SIM_MODEL::doWriteFields( std::vector& aFields ) { SetFieldValue( aFields, DEVICE_TYPE_FIELD, generateDeviceTypeField() ); SetFieldValue( aFields, TYPE_FIELD, generateTypeField() ); SetFieldValue( aFields, PINS_FIELD, generatePinsField() ); SetFieldValue( aFields, PARAMS_FIELD, generateParamsField( " " ) ); } 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( int 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; } void SIM_MODEL::parsePinsField( int aSymbolPinCount, const wxString& aPinsField ) { // Default pin sequence: model pins are the same as symbol pins. // Excess model pins are set as Not Connected. for( int i = 0; i < static_cast( getPinNames().size() ); ++i ) { if( i < aSymbolPinCount ) m_pins.push_back( { i + 1, getPinNames().at( i ) } ); else m_pins.push_back( { PIN::NOT_CONNECTED, getPinNames().at( i ) } ); } if( aPinsField.IsEmpty() ) return; LOCALE_IO toggle; tao::pegtl::string_input<> in( aPinsField.ToStdString(), "from_input" ); std::unique_ptr root; try { root = tao::pegtl::parse_tree::parse( in ); } catch( tao::pegtl::parse_error& e ) { throw KI_PARAM_ERROR( wxString::Format( _( "Failed to parse model pin sequence: %s" ), e.what() ) ); } wxASSERT( root ); if( static_cast( root->children.size() ) != GetPinCount() ) throw KI_PARAM_ERROR( wxString::Format( _( "The model pin sequence has a different number of values (%d) " "than the number of model pins (%d)" ) ) ); for( unsigned int 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() ) ); } } wxString SIM_MODEL::generateParamsField( const wxString& aPairSeparator ) const { bool isFirst = true; wxString result = ""; for( const PARAM& param : m_params ) { wxString valueStr = param.value->ToString(); if( valueStr.IsEmpty() ) continue; if( isFirst ) isFirst = false; else result << " "; result << param.info.name; result << "="; result << param.value->ToString(); } return result; } void SIM_MODEL::parseParamsField( const wxString& aParamsField ) { LOCALE_IO toggle; tao::pegtl::string_input<> in( aParamsField.ToStdString(), "from_input" ); 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::paramValuePairsGrammar, SIM_MODEL_PARSER::paramValuePairsSelector> ( in ); } catch( tao::pegtl::parse_error& e ) { throw KI_PARAM_ERROR( wxString::Format( _( "Failed to parse model parameters: %s" ), e.what() ) ); } wxASSERT( root ); 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>() ) { wxASSERT( !paramName.IsEmpty() ); // TODO: Shouldn't be named "...fromSpiceCode" here... setParamFromSpiceCode( paramName, node->string() ); } else { wxFAIL; return; } } } bool SIM_MODEL::setParamFromSpiceCode( const wxString& aParamName, const wxString& aParamValue ) { int i = 0; for(; i < GetParamCount(); ++i ) { if( GetParam( i ).info.name == aParamName.Lower() ) break; } if( i == GetParamCount() ) return false; // No parameter with this name exists. try { SetParamValue( i, wxString( aParamValue ) ); } catch( KI_PARAM_ERROR& e ) { m_params.clear(); return false; } return true; }