summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author couriersud <couriersud@gmx.org>2019-03-31 12:32:22 +0200
committer couriersud <couriersud@gmx.org>2019-03-31 22:34:06 +0200
commit980dbcc69342e436f1097aa4b0bd4bac8542f79f (patch)
tree8c5796db2a959094120775ff83dd386188e7b4fb
parentb8a104748ecd3ebe0d30038c78225b3a027aa225 (diff)
Netlist: Add global parameter to disable semi-conductor capacitance
modelling. [Couriersud] Added global NETLIST.DEFAULT_MOS_CAPMODEL parameter. Setting this to zero disables using capitance modelling in mos models. On a per mos device basis this can be achieved by adding CAPMODEL=0 to the model definition, e.g. MOSFET(X, "NMOS(CAPMODEL=0)") Improve MOSFET convergence by using log-stepping.
-rw-r--r--nl_examples/cmos_inverter_clk.cpp3
-rw-r--r--src/lib/netlist/analog/nld_mosfet.cpp114
-rw-r--r--src/lib/netlist/analog/nlid_twoterm.h81
-rw-r--r--src/lib/netlist/devices/nlid_system.h2
-rw-r--r--src/lib/netlist/macro/nlm_base.cpp12
-rw-r--r--src/lib/netlist/nl_base.h18
-rw-r--r--src/lib/netlist/nl_setup.cpp5
-rw-r--r--src/lib/netlist/plib/putil.h1
8 files changed, 160 insertions, 76 deletions
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<int> 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;
-
- m_cap_gb.timestep(m_Cgb, Ugb, step);
- m_cap_gs.timestep(m_Cgs, Ugs, step);
- m_cap_gd.timestep(m_Cgd, Ugd, step);
+ 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);
+ }
}
protected:
@@ -261,7 +270,6 @@ namespace analog
generic_capacitor<capacitor_e::VARIABLE_CAPACITY> m_cap_gs;
generic_capacitor<capacitor_e::VARIABLE_CAPACITY> 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<nl_double> m_Vgs;
+ state_var<nl_double> m_Vgd;
+
void set_cap(generic_capacitor<capacitor_e::VARIABLE_CAPACITY> 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;
}
}
+#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 double Is, const double n, double gmin, double temp)
+ 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<double> m_Vd;
- state_var<double> m_Id;
- state_var<double> m_G;
-
- double m_Vt;
- double m_Vmin;
- double m_Is;
- double m_logIs;
- double m_n;
- double m_gmin;
-
- double m_VtInv;
- double m_Vcrit;
+ state_var<nl_double> m_Vd;
+ state_var<nl_double> m_Id;
+ state_var<nl_double> m_G;
+
+ nl_double m_Vt;
+ nl_double m_Vmin;
+ nl_double m_Is;
+ nl_double m_logIs;
+ nl_double m_n;
+ nl_double m_gmin;
+
+ 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 <typename T>
+ class value_base_t
{
public:
- value_t(param_model_t &param, const pstring &name)
- : m_value(param.model_value(name))
+ value_base_t(param_model_t &param, const pstring &name)
+ : m_value(static_cast<T>(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<nl_double>;
+
+ template <typename T>
+ 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<devices::NETLIB_NAME(solver)>("solver");
m_netlist_params = m_nlstate.get_single_device<devices::NETLIB_NAME(netlistparams)>("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<T>(0); }
static constexpr T one() noexcept { return static_cast<T>(1); }
static constexpr T two() noexcept { return static_cast<T>(2); }
+ static constexpr T sqrt2() noexcept { return static_cast<T>(1.414213562373095048801688724209); }
/*!
* \brief Electric constant of vacuum