diff --git a/nl_examples/cmos_inverter_clk.cpp b/nl_examples/cmos_inverter_clk.cpp index e869a478a6c..09064a59d19 100644 --- a/nl_examples/cmos_inverter_clk.cpp +++ b/nl_examples/cmos_inverter_clk.cpp @@ -19,7 +19,7 @@ NETLIST_START(cmos_inverter_clk) //SOLVER(Solver, 100000000000) PARAM(Solver.ACCURACY, 1e-7 ) PARAM(Solver.NR_LOOPS, 500000) - PARAM(Solver.DYNAMIC_TS, 1) + PARAM(Solver.DYNAMIC_TS, 0) PARAM(Solver.DYNAMIC_LTE, 1e-5) PARAM(Solver.DYNAMIC_MIN_TIMESTEP, 2e-8) ANALOG_INPUT(V5, 5) @@ -28,6 +28,7 @@ NETLIST_START(cmos_inverter_clk) #if (USE_CLOCK) CLOCK(V, 5000) + PARAM(NETLIST.DEFAULT_MOS_CAPMODEL, 0) // Disable capacitance modeling //CLOCK(V, 500000) #else VS(V, 5) diff --git a/src/lib/netlist/analog/nld_mosfet.cpp b/src/lib/netlist/analog/nld_mosfet.cpp index f941d06076a..07c6ca2ba78 100644 --- a/src/lib/netlist/analog/nld_mosfet.cpp +++ b/src/lib/netlist/analog/nld_mosfet.cpp @@ -37,11 +37,17 @@ namespace analog // nld_FET - Base classes // ----------------------------------------------------------------------------- - /*! Class representing the nmos model paramers. + /*! Class representing the nmos/pmos model paramers. * - * This is the model representation of the nmos model. Typically, SPICE uses - * the following parameters. A "Y" in the first column indicates that the - * parameter is actually used in netlist. + * This is the model representation of the nmos model. + * + * Netlist has an additional parameter caller CAPMOD: + * + * CAPMOD=0: Capacitance model disabled + * CAPMOD=2: Meyer capacitance model + * + * Typically, SPICE uses the following parameters. A "Y" in the first + * column indicates that the parameter is actually used in netlist. * * | NL? |Name | Description|Units |Default |Example | * |:---:|------|-----------------------------------------------------------------------|-------|---------:|----------------:| @@ -115,6 +121,7 @@ namespace analog , m_CGSO(*this, "CGSO") , m_CGDO(*this, "CGDO") , m_CGBO(*this, "CGBO") + , m_CAPMOD(*this, "CAPMOD") {} value_t m_VTO; //!< Threshold voltage [V] @@ -136,6 +143,7 @@ namespace analog value_t m_CGSO; //!< Gate-source overlap capacitance per meter channel width value_t m_CGDO; //!< Gate-drain overlap capacitance per meter channel width value_t m_CGBO; //!< Gate-bulk overlap capacitance per meter channel width + value_base_t m_CAPMOD; //!< Capacitance model (0=no model 2=Meyer) }; // Have a common start for mosfets @@ -198,7 +206,9 @@ namespace analog , m_Cgb(0.0) , m_Cgs(0.0) , m_Cgd(0.0) - + , m_capmod(2) + , m_Vgs(*this, "m_Vgs", 0.0) + , m_Vgd(*this, "m_Vgd", 0.0) { register_subalias("S", m_SG.m_P); // Source register_subalias("G", m_SG.m_N); // Gate @@ -209,34 +219,33 @@ namespace analog connect(m_SG.m_N, m_DG.m_N); connect(m_DG.m_P, m_SD.m_N); -#if 0 - if (m_model.m_CJE > 0.0) - { - create_and_register_subdevice("m_CJE", m_CJE); - connect("B", "m_CJE.1"); - connect("E", "m_CJE.2"); - } - if (m_model.m_CJC > 0.0) - { - create_and_register_subdevice("m_CJC", m_CJC); - connect("B", "m_CJC.1"); - connect("C", "m_CJC.2"); - } -#endif + set_qtype((m_model.model_type() == "NMOS_DEFAULT") ? FET_NMOS : FET_PMOS); + m_polarity = qtype() == FET_NMOS ? 1.0 : -1.0; + + m_capmod = m_model.m_CAPMOD; + // printf("capmod %d %g %g\n", m_capmod, (double)m_model.m_VTO, m_polarity); + nl_assert_always(m_capmod == 0 || m_capmod == 2, "Error: CAPMODEL invalid value for " + m_model.name()); + + //printf("%g\n", exec().solver()->gmin()); } - NETLIB_IS_TIMESTEP(true) + NETLIB_IS_TIMESTEP(true || m_capmod != 0) NETLIB_TIMESTEPI() { - const nl_double Ugd = -m_DG.deltaV() * m_polarity; // Gate - Drain - const nl_double Ugs = -m_SG.deltaV() * m_polarity; // Gate - Source - const nl_double Ubs = 0.0; // Bulk - Source == 0 if connected - const nl_double Ugb = Ugs - Ubs; + if (m_capmod != 0) + { + //const nl_double Ugd = -m_DG.deltaV() * m_polarity; // Gate - Drain + //const nl_double Ugs = -m_SG.deltaV() * m_polarity; // Gate - Source + const nl_double Ugd = m_Vgd; // Gate - Drain + const nl_double Ugs = m_Vgs; // Gate - Source + const nl_double Ubs = 0.0; // Bulk - Source == 0 if connected + const nl_double Ugb = Ugs - Ubs; - m_cap_gb.timestep(m_Cgb, Ugb, step); - m_cap_gs.timestep(m_Cgs, Ugs, step); - m_cap_gd.timestep(m_Cgd, Ugd, step); + m_cap_gb.timestep(m_Cgb, Ugb, step); + m_cap_gs.timestep(m_Cgs, Ugs, step); + m_cap_gd.timestep(m_Cgd, Ugd, step); + } } protected: @@ -261,7 +270,6 @@ namespace analog generic_capacitor m_cap_gs; generic_capacitor m_cap_gd; - nl_double m_phi; nl_double m_gamma; nl_double m_vto; @@ -279,6 +287,10 @@ namespace analog nl_double m_Cgs; nl_double m_Cgd; + int m_capmod; + state_var m_Vgs; + state_var m_Vgd; + void set_cap(generic_capacitor cap, nl_double capval, nl_double V, nl_double &g11, nl_double &g12, nl_double &g21, nl_double &g22, @@ -313,7 +325,7 @@ namespace analog else if (Vctrl <= 0) { Cgb = -Vctrl * m_CoxWL / m_phi; - Cgs = (Vctrl * m_CoxWL * (4.0 / 3.0) / m_phi + (2.0 / 3.0) * m_CoxWL); + Cgs = Vctrl * m_CoxWL * (4.0 / 3.0) / m_phi + (2.0 / 3.0) * m_CoxWL; Cgd = 0.0; } else @@ -361,11 +373,24 @@ namespace analog NETLIB_UPDATE_TERMINALS(MOSFET) { - const nl_double Vgd = -m_DG.deltaV() * m_polarity; // Gate - Drain - const nl_double Vgs = -m_SG.deltaV() * m_polarity; // Gate - Source + nl_double Vgd = -m_DG.deltaV() * m_polarity; // Gate - Drain + nl_double Vgs = -m_SG.deltaV() * m_polarity; // Gate - Source + + // limit step sizes + + const nl_double k = 3.5; // see "Circuit Simulation", page 185 + nl_double d = (Vgs - m_Vgs); + Vgs = m_Vgs + 1.0/k * (d < 0 ? -1.0 : 1.0) * std::log1p(k * std::abs(d)); + d = (Vgd - m_Vgd); + Vgd = m_Vgd + 1.0/k * (d < 0 ? -1.0 : 1.0) * std::log1p(k * std::abs(d)); + + m_Vgs = Vgs; + m_Vgd = Vgd; + const nl_double Vbs = 0.0; // Bulk - Source == 0 if connected - const nl_double Vbd = m_SD.deltaV() * m_polarity; // Bulk - Drain = Source - Drain + //const nl_double Vbd = m_SD.deltaV() * m_polarity; // Bulk - Drain = Source - Drain const nl_double Vds = Vgs - Vgd; + const nl_double Vbd = -Vds; // Bulk - Drain = Source - Drain #if (!BODY_CONNECTED_TO_SOURCE) m_D_BS.update_diode(Vbs); @@ -425,6 +450,7 @@ namespace analog const nl_double IeqBD = m_D_BD.Ieq(); const nl_double gbd = m_D_BD.G(); + #if (!BODY_CONNECTED_TO_SOURCE) const nl_double IeqBS = m_D_BS.Ieq(); const nl_double gbs = m_D_BS.G(); @@ -466,16 +492,19 @@ namespace analog const nl_double gBS = -gbs; nl_double gBB = gbs + gbd; - const nl_double Vgb = Vgs - Vbs; + if (m_capmod != 0) + { + const nl_double Vgb = Vgs - Vbs; - if (is_forward) - calculate_caps(Vgs, Vgd, Vth, m_Cgs, m_Cgd, m_Cgb); - else - calculate_caps(Vgd, Vgs, Vth, m_Cgd, m_Cgs, m_Cgb); + if (is_forward) + calculate_caps(Vgs, Vgd, Vth, m_Cgs, m_Cgd, m_Cgb); + else + calculate_caps(Vgd, Vgs, Vth, m_Cgd, m_Cgs, m_Cgb); - set_cap(m_cap_gb, m_Cgb + m_model.m_CGBO * m_Leff, Vgb, gGG, gGB, gBG, gBB, IG, IB); - set_cap(m_cap_gs, m_Cgs + m_model.m_CGSO * m_model.m_W, Vgs, gGG, gGS, gSG, gSS, IG, IS); - set_cap(m_cap_gd, m_Cgd + m_model.m_CGDO * m_model.m_W, Vgd, gGG, gGD, gDG, gDD, IG, ID); + set_cap(m_cap_gb, m_Cgb + m_model.m_CGBO * m_Leff, Vgb, gGG, gGB, gBG, gBB, IG, IB); + set_cap(m_cap_gs, m_Cgs + m_model.m_CGSO * m_model.m_W, Vgs, gGG, gGS, gSG, gSS, IG, IS); + set_cap(m_cap_gd, m_Cgd + m_model.m_CGDO * m_model.m_W, Vgd, gGG, gGD, gDG, gDD, IG, ID); + } // Source connected to body, Diode S-B shorted! const nl_double gSSBB = gSS + gBB + gBS + gSB; @@ -493,9 +522,6 @@ namespace analog NETLIB_UPDATE_PARAM(MOSFET) { - set_qtype((m_model.model_type() == "NMOS") ? FET_NMOS : FET_PMOS); - m_polarity = qtype() == FET_NMOS ? 1.0 : -1.0; - /* * From http://ltwiki.org/LTspiceHelp/LTspiceHelp/M_MOSFET.htm : * diff --git a/src/lib/netlist/analog/nlid_twoterm.h b/src/lib/netlist/analog/nlid_twoterm.h index aaf35fe9e28..9ce5702e5de 100644 --- a/src/lib/netlist/analog/nlid_twoterm.h +++ b/src/lib/netlist/analog/nlid_twoterm.h @@ -459,6 +459,7 @@ public: set_param(1e-15, 1, 1e-15, 300.0); } +#if 0 void update_diode(const double nVd) { if (TYPE == diode_e::BIPOLAR && nVd < m_Vmin) @@ -491,11 +492,48 @@ public: m_G = IseVDVt * m_VtInv + m_gmin; } } - - - void set_param(const double Is, const double n, double gmin, double temp) +#else + void update_diode(const nl_double nVd) + { + nl_double IseVDVt(0.0); + + if (TYPE == diode_e::BIPOLAR && nVd < m_Vmin) + { + m_Vd = nVd; + m_G = m_gmin; + m_Id = - m_Is; + } + else if (TYPE == diode_e::MOS && nVd < constants::zero()) + { + m_Vd = nVd; + m_G = m_Is * m_VtInv + m_gmin; + m_Id = m_G * m_Vd; + } + else if (/*TYPE == diode_e::MOS || */nVd < m_Vcrit) + { + m_Vd = nVd; + IseVDVt = std::exp(std::min(300.0, m_logIs + m_Vd * m_VtInv)); + m_Id = IseVDVt - m_Is; + m_G = IseVDVt * m_VtInv + m_gmin; + } + else + { + if (TYPE == diode_e::MOS && m_Vd < constants::zero()) + m_Vd = std::min(m_Vmin, nVd); + + const nl_double d = (nVd - m_Vd); + const nl_double a = std::abs(nVd - m_Vd) * m_VtInv; + m_Vd = m_Vd + (d < 0 ? -1.0 : 1.0) * std::log1p(a) * m_Vt; + IseVDVt = std::exp(m_logIs + m_Vd * m_VtInv); + //const double IseVDVt = m_Is * std::exp(m_Vd * m_VtInv); + m_Id = IseVDVt - m_Is; + m_G = IseVDVt * m_VtInv + m_gmin; + } + } +#endif + + void set_param(const nl_double Is, const nl_double n, nl_double gmin, nl_double temp) { - static constexpr double csqrt2 = 1.414213562373095048801688724209; //std::sqrt(2.0); m_Is = Is; m_logIs = std::log(Is); m_n = n; @@ -505,32 +543,33 @@ public: m_Vmin = -5.0 * m_Vt; - m_Vcrit = m_Vt * std::log(m_Vt / m_Is / csqrt2); - m_VtInv = 1.0 / m_Vt; + m_Vcrit = m_Vt * std::log(m_Vt / m_Is / constants::sqrt2()); + m_VtInv = constants::one() / m_Vt; + //printf("%g %g\n", m_Vmin, m_Vcrit); } - double I() const { return m_Id; } - double G() const { return m_G; } - double Ieq() const { return (m_Id - m_Vd * m_G); } - double Vd() const { return m_Vd; } + nl_double I() const { return m_Id; } + nl_double G() const { return m_G; } + nl_double Ieq() const { return (m_Id - m_Vd * m_G); } + nl_double Vd() const { return m_Vd; } /* owning object must save those ... */ private: - state_var m_Vd; - state_var m_Id; - state_var m_G; + state_var m_Vd; + state_var m_Id; + state_var m_G; - double m_Vt; - double m_Vmin; - double m_Is; - double m_logIs; - double m_n; - double m_gmin; + nl_double m_Vt; + nl_double m_Vmin; + nl_double m_Is; + nl_double m_logIs; + nl_double m_n; + nl_double m_gmin; - double m_VtInv; - double m_Vcrit; + nl_double m_VtInv; + nl_double m_Vcrit; }; /*! Class representing the diode model paramers. diff --git a/src/lib/netlist/devices/nlid_system.h b/src/lib/netlist/devices/nlid_system.h index f1f2af1b31e..11b207a1103 100644 --- a/src/lib/netlist/devices/nlid_system.h +++ b/src/lib/netlist/devices/nlid_system.h @@ -29,6 +29,7 @@ namespace netlist NETLIB_CONSTRUCTOR(netlistparams) , m_use_deactivate(*this, "USE_DEACTIVATE", false) , m_startup_strategy(*this, "STARTUP_STRATEGY", 1) + , m_mos_capmodel(*this, "DEFAULT_MOS_CAPMODEL", 2) { } NETLIB_UPDATEI() { } @@ -37,6 +38,7 @@ namespace netlist public: param_logic_t m_use_deactivate; param_int_t m_startup_strategy; + param_int_t m_mos_capmodel; }; // ----------------------------------------------------------------------------- diff --git a/src/lib/netlist/macro/nlm_base.cpp b/src/lib/netlist/macro/nlm_base.cpp index 2e4542de4b6..b876cd51c94 100644 --- a/src/lib/netlist/macro/nlm_base.cpp +++ b/src/lib/netlist/macro/nlm_base.cpp @@ -28,16 +28,22 @@ static NETLIST_START(diode_models) NETLIST_END() /* ---------------------------------------------------------------------------- - * BJT Models + * Mosfet Models * ---------------------------------------------------------------------------*/ static NETLIST_START(mosfet_models) //NET_MODEL("NMOS _(VTO=0.0 N=1.0 IS=1E-14 KP=2E-5 UO=600 PHI=0.6 LD=0.0 L=1.0 TOX=1E-7 W=1.0 NSUB=0.0 GAMMA=0.0 RD=0.0 RS=0.0 LAMBDA=0.0)") //NET_MODEL("PMOS _(VTO=0.0 N=1.0 IS=1E-14 KP=2E-5 UO=600 PHI=0.6 LD=0.0 L=1.0 TOX=1E-7 W=1.0 NSUB=0.0 GAMMA=0.0 RD=0.0 RS=0.0 LAMBDA=0.0)") - NET_MODEL("NMOS _(VTO=0.0 N=1.0 IS=1E-14 KP=0.0 UO=600 PHI=0.0 LD=0.0 L=100e-6 TOX=1E-7 W=100e-6 NSUB=0.0 GAMMA=0.0 RD=0.0 RS=0.0 LAMBDA=0.0 CGSO=0 CGDO=0 CGBO=0)") - NET_MODEL("PMOS _(VTO=0.0 N=1.0 IS=1E-14 KP=0.0 UO=600 PHI=0.0 LD=0.0 L=100e-6 TOX=1E-7 W=100e-6 NSUB=0.0 GAMMA=0.0 RD=0.0 RS=0.0 LAMBDA=0.0 CGSO=0 CGDO=0 CGBO=0)") + + // NMOS_DEFAULT and PMOS_DEFAULT are created in nl_setup.cpp + NET_MODEL("NMOS NMOS_DEFAULT(VTO=0.0 N=1.0 IS=1E-14 KP=0.0 UO=600 PHI=0.0 LD=0.0 L=100e-6 TOX=1E-7 W=100e-6 NSUB=0.0 GAMMA=0.0 RD=0.0 RS=0.0 LAMBDA=0.0 CGSO=0 CGDO=0 CGBO=0)") + NET_MODEL("PMOS PMOS_DEFAULT(VTO=0.0 N=1.0 IS=1E-14 KP=0.0 UO=600 PHI=0.0 LD=0.0 L=100e-6 TOX=1E-7 W=100e-6 NSUB=0.0 GAMMA=0.0 RD=0.0 RS=0.0 LAMBDA=0.0 CGSO=0 CGDO=0 CGBO=0)") NETLIST_END() +/* ---------------------------------------------------------------------------- + * BJT Models + * ---------------------------------------------------------------------------*/ + static NETLIST_START(bjt_models) NET_MODEL("NPN _(IS=1e-15 BF=100 NF=1 BR=1 NR=1 CJE=0 CJC=0)") NET_MODEL("PNP _(IS=1e-15 BF=100 NF=1 BR=1 NR=1 CJE=0 CJC=0)") diff --git a/src/lib/netlist/nl_base.h b/src/lib/netlist/nl_base.h index c1f07a55af2..c5d0c0896fd 100644 --- a/src/lib/netlist/nl_base.h +++ b/src/lib/netlist/nl_base.h @@ -1036,20 +1036,24 @@ namespace netlist { public: - class value_t + template + class value_base_t { public: - value_t(param_model_t ¶m, const pstring &name) - : m_value(param.model_value(name)) + value_base_t(param_model_t ¶m, const pstring &name) + : m_value(static_cast(param.model_value(name))) { } - double operator()() const noexcept { return m_value; } - operator double() const noexcept { return m_value; } + T operator()() const noexcept { return m_value; } + operator T() const noexcept { return m_value; } private: - const double m_value; + const T m_value; }; - friend class value_t; + using value_t = value_base_t; + + template + friend class value_base_t; param_model_t(device_t &device, const pstring &name, const pstring &val) : param_str_t(device, name, val) { } diff --git a/src/lib/netlist/nl_setup.cpp b/src/lib/netlist/nl_setup.cpp index 3ae73b50f6f..ddbd8c678db 100644 --- a/src/lib/netlist/nl_setup.cpp +++ b/src/lib/netlist/nl_setup.cpp @@ -1043,6 +1043,11 @@ void setup_t::prepare_to_run() auto solver = m_nlstate.get_single_device("solver"); m_netlist_params = m_nlstate.get_single_device("parameter"); + /* set default model parameters */ + + m_models.register_model(plib::pfmt("NMOS_DEFAULT _(CAPMOD={1})")(m_netlist_params->m_mos_capmodel())); + m_models.register_model(plib::pfmt("PMOS_DEFAULT _(CAPMOD={1})")(m_netlist_params->m_mos_capmodel())); + /* create devices */ log().debug("Creating devices ...\n"); diff --git a/src/lib/netlist/plib/putil.h b/src/lib/netlist/plib/putil.h index e8145361d9d..5cc4fc53187 100644 --- a/src/lib/netlist/plib/putil.h +++ b/src/lib/netlist/plib/putil.h @@ -65,6 +65,7 @@ namespace plib static constexpr T zero() noexcept { return static_cast(0); } static constexpr T one() noexcept { return static_cast(1); } static constexpr T two() noexcept { return static_cast(2); } + static constexpr T sqrt2() noexcept { return static_cast(1.414213562373095048801688724209); } /*! * \brief Electric constant of vacuum