summaryrefslogtreecommitdiffstats
path: root/src/lib/netlist/analog
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/netlist/analog')
-rw-r--r--src/lib/netlist/analog/nld_bjt.cpp632
-rw-r--r--src/lib/netlist/analog/nld_bjt.h2
-rw-r--r--src/lib/netlist/analog/nld_fourterm.h2
-rw-r--r--src/lib/netlist/analog/nld_mosfet.cpp488
-rw-r--r--src/lib/netlist/analog/nld_mosfet.h21
-rw-r--r--src/lib/netlist/analog/nld_opamps.cpp30
-rw-r--r--src/lib/netlist/analog/nld_opamps.h2
-rw-r--r--src/lib/netlist/analog/nld_switches.cpp49
-rw-r--r--src/lib/netlist/analog/nld_switches.h2
-rw-r--r--src/lib/netlist/analog/nld_twoterm.h2
-rw-r--r--src/lib/netlist/analog/nlid_fourterm.cpp52
-rw-r--r--src/lib/netlist/analog/nlid_fourterm.h60
-rw-r--r--src/lib/netlist/analog/nlid_twoterm.cpp233
-rw-r--r--src/lib/netlist/analog/nlid_twoterm.h338
14 files changed, 1262 insertions, 651 deletions
diff --git a/src/lib/netlist/analog/nld_bjt.cpp b/src/lib/netlist/analog/nld_bjt.cpp
index 6f4a2db86bf..aef920635fe 100644
--- a/src/lib/netlist/analog/nld_bjt.cpp
+++ b/src/lib/netlist/analog/nld_bjt.cpp
@@ -5,47 +5,49 @@
*
*/
-#include "../solver/nld_solver.h"
+#include "netlist/solver/nld_solver.h"
+#include "netlist/nl_setup.h"
#include "nlid_twoterm.h"
-#include "../nl_setup.h"
+
#include <cmath>
namespace netlist
{
- namespace analog
- {
-
-class diode
+namespace analog
{
-public:
- diode() : m_Is(1e-15), m_VT(0.0258), m_VT_inv(1.0 / m_VT) {}
- diode(const nl_double Is, const nl_double n)
- {
- m_Is = Is;
- m_VT = 0.0258 * n;
- m_VT_inv = 1.0 / m_VT;
- }
- void set(const nl_double Is, const nl_double n)
- {
- m_Is = Is;
- m_VT = 0.0258 * n;
- m_VT_inv = 1.0 / m_VT;
- }
- nl_double I(const nl_double V) const { return m_Is * std::exp(V * m_VT_inv) - m_Is; }
- nl_double g(const nl_double V) const { return m_Is * m_VT_inv * std::exp(V * m_VT_inv); }
- nl_double V(const nl_double I) const { return std::log1p(I / m_Is) * m_VT; } // log1p(x)=log(1.0 + x)
- nl_double gI(const nl_double I) const { return m_VT_inv * (I + m_Is); }
+ using constants = plib::constants<nl_double>;
-private:
- nl_double m_Is;
- nl_double m_VT;
- nl_double m_VT_inv;
-};
+ class diode
+ {
+ public:
+ diode() : m_Is(1e-15), m_VT(0.0258), m_VT_inv(1.0 / m_VT) {}
+ diode(const nl_double Is, const nl_double n)
+ {
+ m_Is = Is;
+ m_VT = 0.0258 * n;
+ m_VT_inv = 1.0 / m_VT;
+ }
+ void set(const nl_double Is, const nl_double n)
+ {
+ m_Is = Is;
+ m_VT = 0.0258 * n;
+ m_VT_inv = 1.0 / m_VT;
+ }
+ nl_double I(const nl_double V) const { return m_Is * std::exp(V * m_VT_inv) - m_Is; }
+ nl_double g(const nl_double V) const { return m_Is * m_VT_inv * std::exp(V * m_VT_inv); }
+ nl_double V(const nl_double I) const { return std::log1p(I / m_Is) * m_VT; } // log1p(x)=log(1.0 + x)
+ nl_double gI(const nl_double I) const { return m_VT_inv * (I + m_Is); }
+
+ private:
+ nl_double m_Is;
+ nl_double m_VT;
+ nl_double m_VT_inv;
+ };
-// -----------------------------------------------------------------------------
-// nld_Q - Base classes
-// -----------------------------------------------------------------------------
+ // -----------------------------------------------------------------------------
+ // nld_Q - Base classes
+ // -----------------------------------------------------------------------------
/*! Class representing the bjt model paramers.
*
@@ -73,7 +75,7 @@ private:
* | | RBM | minimum base resistance at high currents | | RB | 10 | * |
* | | RE | emitter resistance | | 0 | 1 | * |
* | | RC | collector resistance | | 0 | 10 | * |
- * | | CJE | B-E zero-bias depletion capacitance | F | 0 | 2pF | * |
+ * | Y | CJE | B-E zero-bias depletion capacitance | F | 0 | 2pF | * |
* | | VJE | B-E built-in potential | V | 0.75 | 0.6 | |
* | | MJE | B-E junction exponential factor | - | 0.33 | 0.33 | |
* | | TF | ideal forward transit time | sec | 0 | 0.1ns | |
@@ -81,7 +83,7 @@ private:
* | | VTF | voltage describing VBC dependence of TF | V | infinite | | |
* | | ITF | high-current parameter for effect on TF | A | 0 | | * |
* | | PTF | excess phase at freq=1.0/(TF*2PI) Hz | deg | 0 | | |
- * | | CJC | B-C zero-bias depletion capacitance | F | 0 | 2pF | * |
+ * | Y | CJC | B-C zero-bias depletion capacitance | F | 0 | 2pF | * |
* | | VJC | B-C built-in potential | V | 0.75 | 0.5 | |
* | | MJC | B-C junction exponential factor | - | 0.33 | 0.5 | |
* | | XCJC | fraction of B-C depletion capacitance connected to internal base node | - | 1 | | |
@@ -95,343 +97,369 @@ private:
* | | KF | flicker-noise coefficient | - | 0 | | |
* | | AF | flicker-noise exponent | - | 1 | | |
* | | FC | coefficient for forward-bias depletion capacitance formula | - | 0.5 | | |
- * | | TNOM | Parameter measurement temperature | C | 27 | 50 | | */
+ * | | TNOM | Parameter measurement temperature | C | 27 | 50 | |
+ * */
class bjt_model_t : public param_model_t
{
public:
bjt_model_t(device_t &device, const pstring &name, const pstring &val)
: param_model_t(device, name, val)
- , m_IS(*this, "IS")
- , m_BF(*this, "BF")
- , m_NF(*this, "NF")
- , m_BR(*this, "BR")
- , m_NR(*this, "NR")
+ , m_IS (*this, "IS")
+ , m_BF (*this, "BF")
+ , m_NF (*this, "NF")
+ , m_BR (*this, "BR")
+ , m_NR (*this, "NR")
+ , m_CJE(*this, "CJE")
+ , m_CJC(*this, "CJC")
{}
- value_t m_IS; //!< transport saturation current
- value_t m_BF; //!< ideal maximum forward beta
- value_t m_NF; //!< forward current emission coefficient
- value_t m_BR; //!< ideal maximum reverse beta
- value_t m_NR; //!< reverse current emission coefficient
+ value_t m_IS; //!< transport saturation current
+ value_t m_BF; //!< ideal maximum forward beta
+ value_t m_NF; //!< forward current emission coefficient
+ value_t m_BR; //!< ideal maximum reverse beta
+ value_t m_NR; //!< reverse current emission coefficient
+ value_t m_CJE; //!< B-E zero-bias depletion capacitance
+ value_t m_CJC; //!< B-C zero-bias depletion capacitance
+
};
// Have a common start for transistors
-NETLIB_OBJECT(Q)
-{
-public:
- enum q_type {
- BJT_NPN,
- BJT_PNP
+ NETLIB_OBJECT(Q)
+ {
+ public:
+ enum q_type {
+ BJT_NPN,
+ BJT_PNP
+ };
+
+ NETLIB_CONSTRUCTOR(Q)
+ , m_model(*this, "MODEL", "NPN")
+ , m_qtype(BJT_NPN)
+ {
+ }
+
+ NETLIB_IS_DYNAMIC(true)
+
+ //NETLIB_RESETI();
+ NETLIB_UPDATEI();
+
+ q_type qtype() const { return m_qtype; }
+ bool is_qtype(q_type atype) const { return m_qtype == atype; }
+ void set_qtype(q_type atype) { m_qtype = atype; }
+ protected:
+
+ bjt_model_t m_model;
+ private:
+ q_type m_qtype;
};
- NETLIB_CONSTRUCTOR(Q)
- , m_model(*this, "MODEL", "NPN")
- , m_qtype(BJT_NPN)
+ NETLIB_OBJECT_DERIVED(QBJT, Q)
{
- }
-
- NETLIB_IS_DYNAMIC(true)
-
- //NETLIB_RESETI();
- NETLIB_UPDATEI();
+ public:
+ NETLIB_CONSTRUCTOR_DERIVED(QBJT, Q)
+ { }
- inline q_type qtype() const { return m_qtype; }
- inline bool is_qtype(q_type atype) const { return m_qtype == atype; }
- inline void set_qtype(q_type atype) { m_qtype = atype; }
-protected:
+ protected:
- bjt_model_t m_model;
-private:
- q_type m_qtype;
-};
+ private:
+ };
-NETLIB_OBJECT_DERIVED(QBJT, Q)
-{
-public:
- NETLIB_CONSTRUCTOR_DERIVED(QBJT, Q)
- { }
-protected:
-private:
-};
+ // -----------------------------------------------------------------------------
+ // nld_QBJT_switch
+ // -----------------------------------------------------------------------------
+ /*
+ * + - C
+ * B ----VVV----+ |
+ * | |
+ * Rb Rc
+ * Rb Rc
+ * Rb Rc
+ * | |
+ * +----+----+
+ * |
+ * E
+ */
-// -----------------------------------------------------------------------------
-// nld_QBJT_switch
-// -----------------------------------------------------------------------------
+ NETLIB_OBJECT_DERIVED(QBJT_switch, QBJT)
+ {
+ NETLIB_CONSTRUCTOR_DERIVED(QBJT_switch, QBJT)
+ , m_RB(*this, "m_RB", true)
+ , m_RC(*this, "m_RC", true)
+ , m_BC_dummy(*this, "m_BC", true)
+ , m_gB(1e-9)
+ , m_gC(1e-9)
+ , m_V(0.0)
+ , m_state_on(*this, "m_state_on", 0)
+ {
+ register_subalias("B", m_RB.m_P);
+ register_subalias("E", m_RB.m_N);
+ register_subalias("C", m_RC.m_P);
+ //register_term("_E1", m_RC.m_N);
+
+ //register_term("_B1", m_BC_dummy.m_P);
+ //register_term("_C1", m_BC_dummy.m_N);
+
+ connect(m_RB.m_N, m_RC.m_N);
+
+ connect(m_RB.m_P, m_BC_dummy.m_P);
+ connect(m_RC.m_P, m_BC_dummy.m_N);
+ }
+
+ NETLIB_RESETI();
+ NETLIB_UPDATEI();
+ NETLIB_UPDATE_PARAMI();
+ NETLIB_UPDATE_TERMINALSI();
+
+ private:
+ nld_twoterm m_RB;
+ nld_twoterm m_RC;
+
+ // FIXME: this is needed so we have all terminals belong to one net list
+
+ nld_twoterm m_BC_dummy;
+
+ nl_double m_gB; // base conductance / switch on
+ nl_double m_gC; // collector conductance / switch on
+ nl_double m_V; // internal voltage source
+ state_var<unsigned> m_state_on;
+
+ private:
+ };
+ // -----------------------------------------------------------------------------
+ // nld_QBJT_EB
+ // -----------------------------------------------------------------------------
-/*
- * + - C
- * B ----VVV----+ |
- * | |
- * Rb Rc
- * Rb Rc
- * Rb Rc
- * | |
- * +----+----+
- * |
- * E
- */
-NETLIB_OBJECT_DERIVED(QBJT_switch, QBJT)
-{
- NETLIB_CONSTRUCTOR_DERIVED(QBJT_switch, QBJT)
- , m_RB(*this, "m_RB", true)
- , m_RC(*this, "m_RC", true)
- , m_BC_dummy(*this, "m_BC", true)
- , m_gB(NETLIST_GMIN_DEFAULT)
- , m_gC(NETLIST_GMIN_DEFAULT)
- , m_V(0.0)
- , m_state_on(*this, "m_state_on", 0)
+ NETLIB_OBJECT_DERIVED(QBJT_EB, QBJT)
{
- register_subalias("B", m_RB.m_P);
- register_subalias("E", m_RB.m_N);
- register_subalias("C", m_RC.m_P);
- //register_term("_E1", m_RC.m_N);
+ public:
+ NETLIB_CONSTRUCTOR_DERIVED(QBJT_EB, QBJT)
+ , m_gD_BC(*this, "m_D_BC")
+ , m_gD_BE(*this, "m_D_BE")
+ , m_D_CB(*this, "m_D_CB", true)
+ , m_D_EB(*this, "m_D_EB", true)
+ , m_D_EC(*this, "m_D_EC", true)
+ , m_alpha_f(0)
+ , m_alpha_r(0)
+ {
+ register_subalias("E", m_D_EB.m_P); // Cathode
+ register_subalias("B", m_D_EB.m_N); // Anode
+
+ register_subalias("C", m_D_CB.m_P); // Cathode
+
+ connect(m_D_EB.m_P, m_D_EC.m_P);
+ connect(m_D_EB.m_N, m_D_CB.m_N);
+ connect(m_D_CB.m_P, m_D_EC.m_N);
+
+ 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");
+ }
+ }
+
+ protected:
+
+ NETLIB_RESETI();
+ NETLIB_UPDATEI();
+ NETLIB_UPDATE_PARAMI();
+ NETLIB_UPDATE_TERMINALSI();
+
+ private:
+ generic_diode<diode_e::BIPOLAR> m_gD_BC;
+ generic_diode<diode_e::BIPOLAR> m_gD_BE;
+
+ nld_twoterm m_D_CB; // gcc, gce - gcc, gec - gcc, gcc - gce | Ic
+ nld_twoterm m_D_EB; // gee, gec - gee, gce - gee, gee - gec | Ie
+ nld_twoterm m_D_EC; // 0, -gec, -gcc, 0 | 0
+
+ nl_double m_alpha_f;
+ nl_double m_alpha_r;
+
+ NETLIB_SUBXX(analog, C) m_CJE;
+ NETLIB_SUBXX(analog, C) m_CJC;
+ };
- //register_term("_B1", m_BC_dummy.m_P);
- //register_term("_C1", m_BC_dummy.m_N);
- connect(m_RB.m_N, m_RC.m_N);
+ // ----------------------------------------------------------------------------------------
+ // nld_Q
+ // ----------------------------------------------------------------------------------------
- connect(m_RB.m_P, m_BC_dummy.m_P);
- connect(m_RC.m_P, m_BC_dummy.m_N);
+ NETLIB_UPDATE(Q)
+ {
+ // netlist().solver()->schedule1();
}
- NETLIB_RESETI();
- NETLIB_UPDATEI();
- NETLIB_UPDATE_PARAMI();
- NETLIB_UPDATE_TERMINALSI();
-
- nld_twoterm m_RB;
- nld_twoterm m_RC;
-
- // FIXME: this is needed so we have all terminals belong to one net list
+ // ----------------------------------------------------------------------------------------
+ // nld_QBJT_switch
+ // ----------------------------------------------------------------------------------------
- nld_twoterm m_BC_dummy;
-
-protected:
+ NETLIB_RESET(QBJT_switch)
+ {
+ NETLIB_NAME(Q)::reset();
- nl_double m_gB; // base conductance / switch on
- nl_double m_gC; // collector conductance / switch on
- nl_double m_V; // internal voltage source
- state_var<unsigned> m_state_on;
+ m_state_on = 0;
-private:
-};
+ m_RB.set_G_V_I(exec().gmin(), 0.0, 0.0);
+ m_RC.set_G_V_I(exec().gmin(), 0.0, 0.0);
-// -----------------------------------------------------------------------------
-// nld_QBJT_EB
-// -----------------------------------------------------------------------------
+ m_BC_dummy.set_G_V_I(exec().gmin() / 10.0, 0.0, 0.0);
+ }
-NETLIB_OBJECT_DERIVED(QBJT_EB, QBJT)
-{
-public:
- NETLIB_CONSTRUCTOR_DERIVED(QBJT_EB, QBJT)
- , m_gD_BC(*this, "m_D_BC")
- , m_gD_BE(*this, "m_D_BE")
- , m_D_CB(*this, "m_D_CB", true)
- , m_D_EB(*this, "m_D_EB", true)
- , m_D_EC(*this, "m_D_EC", true)
- , m_alpha_f(0)
- , m_alpha_r(0)
+ NETLIB_UPDATE(QBJT_switch)
{
- register_subalias("E", m_D_EB.m_P); // Cathode
- register_subalias("B", m_D_EB.m_N); // Anode
-
- register_subalias("C", m_D_CB.m_P); // Cathode
- //register_term("_B1", m_D_CB.m_N); // Anode
-
- //register_term("_E1", m_D_EC.m_P);
- //register_term("_C1", m_D_EC.m_N);
-
- connect(m_D_EB.m_P, m_D_EC.m_P);
- connect(m_D_EB.m_N, m_D_CB.m_N);
- connect(m_D_CB.m_P, m_D_EC.m_N);
+ if (!m_RB.m_P.net().isRailNet())
+ m_RB.m_P.solve_now(); // Basis
+ else if (!m_RB.m_N.net().isRailNet())
+ m_RB.m_N.solve_now(); // Emitter
+ else if (!m_RC.m_P.net().isRailNet())
+ m_RC.m_P.solve_now(); // Collector
}
-protected:
-
- NETLIB_RESETI();
- NETLIB_UPDATEI();
- NETLIB_UPDATE_PARAMI();
- NETLIB_UPDATE_TERMINALSI();
-
- generic_diode m_gD_BC;
- generic_diode m_gD_BE;
-
-private:
- nld_twoterm m_D_CB; // gcc, gce - gcc, gec - gcc, gcc - gce | Ic
- nld_twoterm m_D_EB; // gee, gec - gee, gce - gee, gee - gec | Ie
- nld_twoterm m_D_EC; // 0, -gec, -gcc, 0 | 0
-
- nl_double m_alpha_f;
- nl_double m_alpha_r;
-
-};
-
-
-// ----------------------------------------------------------------------------------------
-// nld_Q
-// ----------------------------------------------------------------------------------------
-
-NETLIB_UPDATE(Q)
-{
-// netlist().solver()->schedule1();
-}
-
-// ----------------------------------------------------------------------------------------
-// nld_QBJT_switch
-// ----------------------------------------------------------------------------------------
-
-
-NETLIB_RESET(QBJT_switch)
-{
- NETLIB_NAME(Q)::reset();
- m_state_on = 0;
-
- m_RB.set(netlist().gmin(), 0.0, 0.0);
- m_RC.set(netlist().gmin(), 0.0, 0.0);
-
- m_BC_dummy.set(netlist().gmin() / 10.0, 0.0, 0.0);
-
-}
+ NETLIB_UPDATE_PARAM(QBJT_switch)
+ {
+ nl_double IS = m_model.m_IS;
+ nl_double BF = m_model.m_BF;
+ nl_double NF = m_model.m_NF;
+ //nl_double VJE = m_model.dValue("VJE", 0.75);
-NETLIB_UPDATE(QBJT_switch)
-{
- if (!m_RB.m_P.net().isRailNet())
- m_RB.m_P.solve_now(); // Basis
- else if (!m_RB.m_N.net().isRailNet())
- m_RB.m_N.solve_now(); // Emitter
- else if (!m_RC.m_P.net().isRailNet())
- m_RC.m_P.solve_now(); // Collector
-}
+ set_qtype((m_model.model_type() == "NPN") ? BJT_NPN : BJT_PNP);
+ nl_double alpha = BF / (1.0 + BF);
-NETLIB_UPDATE_PARAM(QBJT_switch)
-{
- nl_double IS = m_model.m_IS;
- nl_double BF = m_model.m_BF;
- nl_double NF = m_model.m_NF;
- //nl_double VJE = m_model.dValue("VJE", 0.75);
+ diode d(IS, NF);
- set_qtype((m_model.model_type() == "NPN") ? BJT_NPN : BJT_PNP);
+ // Assume 5mA Collector current for switch operation
- nl_double alpha = BF / (1.0 + BF);
+ m_V = d.V(0.005 / alpha);
- diode d(IS, NF);
+ /* Base current is 0.005 / beta
+ * as a rough estimate, we just scale the conductance down */
- // Assume 5mA Collector current for switch operation
+ m_gB = 1.0 / (m_V/(0.005 / BF));
- m_V = d.V(0.005 / alpha);
+ //m_gB = d.gI(0.005 / alpha);
- /* Base current is 0.005 / beta
- * as a rough estimate, we just scale the conductance down */
+ if (m_gB < exec().gmin())
+ m_gB = exec().gmin();
+ m_gC = d.gI(0.005); // very rough estimate
+ }
- m_gB = 1.0 / (m_V/(0.005 / BF));
+ NETLIB_UPDATE_TERMINALS(QBJT_switch)
+ {
+ const nl_double m = (is_qtype( BJT_NPN) ? 1 : -1);
+
+ const unsigned new_state = (m_RB.deltaV() * m > m_V ) ? 1 : 0;
+ if (m_state_on ^ new_state)
+ {
+ const nl_double gb = new_state ? m_gB : exec().gmin();
+ const nl_double gc = new_state ? m_gC : exec().gmin();
+ const nl_double v = new_state ? m_V * m : 0;
+
+ m_RB.set_G_V_I(gb, v, 0.0);
+ m_RC.set_G_V_I(gc, 0.0, 0.0);
+ m_state_on = new_state;
+ }
+ }
- //m_gB = d.gI(0.005 / alpha);
- if (m_gB < netlist().gmin())
- m_gB = netlist().gmin();
- m_gC = d.gI(0.005); // very rough estimate
-}
+ // ----------------------------------------------------------------------------------------
+ // nld_Q - Ebers Moll
+ // ----------------------------------------------------------------------------------------
-NETLIB_UPDATE_TERMINALS(QBJT_switch)
-{
- const nl_double m = (is_qtype( BJT_NPN) ? 1 : -1);
- const unsigned new_state = (m_RB.deltaV() * m > m_V ) ? 1 : 0;
- if (m_state_on ^ new_state)
+ NETLIB_UPDATE(QBJT_EB)
{
- const nl_double gb = new_state ? m_gB : netlist().gmin();
- const nl_double gc = new_state ? m_gC : netlist().gmin();
- const nl_double v = new_state ? m_V * m : 0;
-
- m_RB.set(gb, v, 0.0);
- m_RC.set(gc, 0.0, 0.0);
- m_state_on = new_state;
+ if (!m_D_EB.m_P.net().isRailNet())
+ m_D_EB.m_P.solve_now(); // Basis
+ else if (!m_D_EB.m_N.net().isRailNet())
+ m_D_EB.m_N.solve_now(); // Emitter
+ else
+ m_D_CB.m_N.solve_now(); // Collector
}
-}
+ NETLIB_RESET(QBJT_EB)
+ {
+ NETLIB_NAME(Q)::reset();
+ if (m_CJE)
+ {
+ m_CJE->reset();
+ m_CJE->m_C.setTo(m_model.m_CJE);
+ }
+ if (m_CJC)
+ {
+ m_CJC->reset();
+ m_CJC->m_C.setTo(m_model.m_CJC);
+ }
-// ----------------------------------------------------------------------------------------
-// nld_Q - Ebers Moll
-// ----------------------------------------------------------------------------------------
+ }
+ NETLIB_UPDATE_TERMINALS(QBJT_EB)
+ {
+ const nl_double polarity = (qtype() == BJT_NPN ? 1.0 : -1.0);
+
+ m_gD_BE.update_diode(-m_D_EB.deltaV() * polarity);
+ m_gD_BC.update_diode(-m_D_CB.deltaV() * polarity);
+
+ const nl_double gee = m_gD_BE.G();
+ const nl_double gcc = m_gD_BC.G();
+ const nl_double gec = m_alpha_r * gcc;
+ const nl_double gce = m_alpha_f * gee;
+ const nl_double sIe = -m_gD_BE.I() + m_alpha_r * m_gD_BC.I();
+ const nl_double sIc = m_alpha_f * m_gD_BE.I() - m_gD_BC.I();
+ const nl_double Ie = (sIe + gee * m_gD_BE.Vd() - gec * m_gD_BC.Vd()) * polarity;
+ const nl_double Ic = (sIc - gce * m_gD_BE.Vd() + gcc * m_gD_BC.Vd()) * polarity;
+
+ m_D_EB.set_mat( gee, gec - gee, -Ie,
+ gce - gee, gee - gec, Ie);
+ m_D_CB.set_mat( gcc, gce - gcc, -Ic,
+ gec - gcc, gcc - gce, Ic);
+ m_D_EC.set_mat( 0, -gec, 0,
+ -gce, 0, 0);
+ }
-NETLIB_UPDATE(QBJT_EB)
-{
- if (!m_D_EB.m_P.net().isRailNet())
- m_D_EB.m_P.solve_now(); // Basis
- else if (!m_D_EB.m_N.net().isRailNet())
- m_D_EB.m_N.solve_now(); // Emitter
- else
- m_D_CB.m_N.solve_now(); // Collector
-}
-
-NETLIB_RESET(QBJT_EB)
-{
- NETLIB_NAME(Q)::reset();
-}
-NETLIB_UPDATE_TERMINALS(QBJT_EB)
-{
- const nl_double polarity = (qtype() == BJT_NPN ? 1.0 : -1.0);
-
- m_gD_BE.update_diode(-m_D_EB.deltaV() * polarity);
- m_gD_BC.update_diode(-m_D_CB.deltaV() * polarity);
-
- const nl_double gee = m_gD_BE.G();
- const nl_double gcc = m_gD_BC.G();
- const nl_double gec = m_alpha_r * gcc;
- const nl_double gce = m_alpha_f * gee;
- const nl_double sIe = -m_gD_BE.I() + m_alpha_r * m_gD_BC.I();
- const nl_double sIc = m_alpha_f * m_gD_BE.I() - m_gD_BC.I();
- const nl_double Ie = (sIe + gee * m_gD_BE.Vd() - gec * m_gD_BC.Vd()) * polarity;
- const nl_double Ic = (sIc - gce * m_gD_BE.Vd() + gcc * m_gD_BC.Vd()) * polarity;
-
- m_D_EB.set_mat( gee, gec - gee, -Ie,
- gce - gee, gee - gec, Ie);
- m_D_CB.set_mat( gcc, gce - gcc, -Ic,
- gec - gcc, gcc - gce, Ic);
- m_D_EC.set_mat( 0, -gec, 0,
- -gce, 0, 0);
-}
-
-
-NETLIB_UPDATE_PARAM(QBJT_EB)
-{
- nl_double IS = m_model.m_IS;
- nl_double BF = m_model.m_BF;
- nl_double NF = m_model.m_NF;
- nl_double BR = m_model.m_BR;
- nl_double NR = m_model.m_NR;
- //nl_double VJE = m_model.dValue("VJE", 0.75);
+ NETLIB_UPDATE_PARAM(QBJT_EB)
+ {
+ nl_double IS = m_model.m_IS;
+ nl_double BF = m_model.m_BF;
+ nl_double NF = m_model.m_NF;
+ nl_double BR = m_model.m_BR;
+ nl_double NR = m_model.m_NR;
+ //nl_double VJE = m_model.dValue("VJE", 0.75);
- set_qtype((m_model.model_type() == "NPN") ? BJT_NPN : BJT_PNP);
+ set_qtype((m_model.model_type() == "NPN") ? BJT_NPN : BJT_PNP);
- m_alpha_f = BF / (1.0 + BF);
- m_alpha_r = BR / (1.0 + BR);
+ m_alpha_f = BF / (1.0 + BF);
+ m_alpha_r = BR / (1.0 + BR);
- m_gD_BE.set_param(IS / m_alpha_f, NF, netlist().gmin());
- m_gD_BC.set_param(IS / m_alpha_r, NR, netlist().gmin());
-}
+ m_gD_BE.set_param(IS / m_alpha_f, NF, exec().gmin(), constants::T0());
+ m_gD_BC.set_param(IS / m_alpha_r, NR, exec().gmin(), constants::T0());
+ }
- } //namespace analog
+} // namespace analog
- namespace devices {
- NETLIB_DEVICE_IMPL_NS(analog, QBJT_EB)
- NETLIB_DEVICE_IMPL_NS(analog, QBJT_switch)
- }
+namespace devices {
+ NETLIB_DEVICE_IMPL_NS(analog, QBJT_EB, "QBJT_EB", "MODEL")
+ NETLIB_DEVICE_IMPL_NS(analog, QBJT_switch, "QBJT_SW", "MODEL")
+} // namespace devices
} // namespace netlist
diff --git a/src/lib/netlist/analog/nld_bjt.h b/src/lib/netlist/analog/nld_bjt.h
index 9e1f82afbaa..3a8478f19f7 100644
--- a/src/lib/netlist/analog/nld_bjt.h
+++ b/src/lib/netlist/analog/nld_bjt.h
@@ -8,7 +8,7 @@
#ifndef NLD_BJT_H_
#define NLD_BJT_H_
-#include "../nl_setup.h"
+#include "netlist/nl_setup.h"
// -----------------------------------------------------------------------------
// Macros
diff --git a/src/lib/netlist/analog/nld_fourterm.h b/src/lib/netlist/analog/nld_fourterm.h
index b4bb5db1059..66c47c0be29 100644
--- a/src/lib/netlist/analog/nld_fourterm.h
+++ b/src/lib/netlist/analog/nld_fourterm.h
@@ -9,7 +9,7 @@
#define NLD_FOURTERM_H_
-#include "../nl_setup.h"
+#include "netlist/nl_setup.h"
// ----------------------------------------------------------------------------------------
// Macros
diff --git a/src/lib/netlist/analog/nld_mosfet.cpp b/src/lib/netlist/analog/nld_mosfet.cpp
new file mode 100644
index 00000000000..ea717fdefee
--- /dev/null
+++ b/src/lib/netlist/analog/nld_mosfet.cpp
@@ -0,0 +1,488 @@
+// license:GPL-2.0+
+// copyright-holders:Couriersud
+/*
+ * nld_mosfet.cpp
+ *
+ * Formulas in here based on the following Sources:
+ *
+ * https://www.imperial.ac.uk/pls/portallive/docs/1/7292573.PDF
+ * http://www3.imperial.ac.uk/pls/portallive/docs/1/56133736.PDF
+ * https://people.rit.edu/lffeee/SPICE_MOSFET_Model_Intro.pdf
+ * https://people.rit.edu/lffeee/SPICE.pdf
+ * http://web.mit.edu/course/6/6.012/SPR98/www/lectures/S98_Lecture10.pdf
+ * http://homepages.rpi.edu/~sawyes/Models_review.pdf
+ * http://jaco.ec.t.kanazawa-u.ac.jp/edu/mix/pdf/3.pdf
+ *
+ * Farid N. Naim, Circuit Simulation (Wiley-IEEE Press, 2010).
+ * Stefan Jahn, Michael Margraf, Vincent Habchi and Raimund Jacob, "Qucs Technical Papers" (2007)
+ *
+ */
+
+#include "netlist/solver/nld_solver.h"
+#include "netlist/nl_setup.h"
+#include "nlid_twoterm.h"
+
+#include <cmath>
+
+#define BODY_CONNECTED_TO_SOURCE (1)
+
+namespace netlist
+{
+namespace analog
+{
+
+ using constants = plib::constants<nl_double>;
+
+ // -----------------------------------------------------------------------------
+ // nld_FET - Base classes
+ // -----------------------------------------------------------------------------
+
+ /*! Class representing the nmos 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.
+ *
+ * | NL? |Name | Description|Units |Default |Example |
+ * |:---:|------|-----------------------------------------------------------------------|-------|---------:|----------------:|
+ * | Y |Vto | Zero-bias threshold voltage | V | 0 | 1 |
+ * | Y |Kp | Transconductance parameter | A/V² | 0.00002 | 0.00003 |
+ * | Y |Gamma | Bulk threshold parameter | V^½ | 0 | 0.37 |
+ * | Y |Phi | Surface inversion potential | V | 0.6 | 0.65 |
+ * | Y |Lambda| Channel-length modulation (level 1 and 2 only) | 1/V | 0 | 0.02 |
+ * | |Rd | Drain ohmic resistance |W|0|1|
+ * | |Rs | Source ohmic resistance |W|0|1|
+ * | |Cbd | Zero-bias B-D junction capacitance |F|0|20f|
+ * | |Cbs | Zero-bias B-S junction capacitance |F|0|20f|
+ * | Y |Is | Bulk junction saturation current |A|0.00000000000001|1E-015|
+ * | Y |N | Bulk diode emission coefficient |-|1|*
+ * | |Pb | Bulk junction potential |V|0.8|0.87|8|
+ * | |Cgso | Gate-source overlap capacitance per meter channel width |F/m|0|0.00000000004|
+ * | |Cgdo | Gate-drain overlap capacitance per meter channel width |F/m|0|0.00000000004|*
+ * | |Cgbo | Gate-bulk overlap capacitance per meter channel width |F/m|0|0.0000000002|*
+ * | |Rsh | Drain and source diffusion sheet resistance |W|0|10|*
+ * | |Cj | Zero-bias bulk junction bottom capacitance per square meter of junction area|F/m²|0|0.0002|*
+ * | |Mj | Bulk junction bottom grading coefficient |-|0.5|0.5|*
+ * | |Cjsw | Zero-bias bulk junction sidewall capacitance per meter of junction perimeter|F/m|0|1p|*
+ * | |Mjsw | Bulk junction sidewall grading coefficient |-|.50 level 1 .33 level 2,3||
+ * | |Js | Bulk junction saturation current per square-meter of junction area|A/m|0|0.00000001|
+ * | Y |Tox | Oxide thickness |m|0.0000001|0.0000001|
+ * | Y |Nsub | Substrate doping |1/cm³|0|4000000000000000|
+ * | |Nss | Surface state density |1/cm²|0|10000000000|
+ * | |Nfs | Fast surface state |1/cm²|0|10000000000|*
+ * | |TPG | Type of gate material: +1 opp. to substrate -1 same as substrate 0 Al gate|-|1|
+ * | |Xj | Metallurgical junction depth |m|0|1µ|*
+ * | Y |Ld | Lateral diffusion |m|0|0.8µ|
+ * | Y |Uo | Surface mobility |cm²/V/s|600|700|
+ * | |Ucrit | Critical field for mobility degradation (level 2 only) |V/cm|10000|10000|
+ * | |Uexp | Critical field exponent in mobility degradation (level 2 only) |-|0|0.1|
+ * | |Utra | Transverse field coefficient (level 2 only) |-|0|0.3|*
+ * | |Vmax | Maximum carrier drift velocity (levels 2 & 3 only) |m/s|0|50000|
+ * | |Neff | Total channel-charge exponent (level 2 only) |-|1|5|
+ * | |Kf | Flicker noise coefficient |-|0|1E-026|
+ * | |Af | Flicker noise exponent |-|1|1.2|
+ * | |Fc | Coefficient for forward-bias depletion capacitance formula |-|0.5|
+ * | |Delta | Width effect on threshold voltage(levels 2 and 3) |-|0|1|
+ * | |Theta | Mobility modulation (level 3 only) |-|0|0.1|
+ * | |Eta | Static feedback (level 3 only) |-|0|1|
+ * | |Kappa | Saturation field (level 3 only) |0.2|0.5|
+ * | |Tnom | Parameter measurement temperature |ºC|27|50||
+ * | Y |L | Length scaling |-|1.0||
+ * | Y |W | Width scaling |-|1.0||
+ * */
+
+ class fet_model_t : public param_model_t
+ {
+ public:
+ fet_model_t(device_t &device, const pstring &name, const pstring &val)
+ : param_model_t(device, name, val)
+ , m_VTO(*this, "VTO")
+ , m_N(*this, "N")
+ , m_ISS(*this, "IS") // Haven't seen a model using ISS / ISD
+ , m_ISD(*this, "IS")
+ , m_LD(*this, "LD")
+ , m_L(*this, "L")
+ , m_W(*this, "W")
+ , m_TOX(*this, "TOX")
+ , m_KP(*this, "KP")
+ , m_UO(*this, "UO")
+ , m_PHI(*this, "PHI")
+ , m_NSUB(*this, "NSUB")
+ , m_GAMMA(*this, "GAMMA")
+ , m_LAMBDA(*this, "LAMBDA")
+ , m_RD(*this, "RD")
+ , m_RS(*this, "RS")
+ {}
+
+ value_t m_VTO; //!< Threshold voltage [V]
+ value_t m_N; //!< Bulk diode emission coefficient
+ value_t m_ISS; //!< Body diode saturation current
+ value_t m_ISD; //!< Body diode saturation current
+ value_t m_LD; //!< Lateral diffusion [m]
+ value_t m_L; //!< Length scaling
+ value_t m_W; //!< Width scaling
+ value_t m_TOX; //!< Oxide thickness
+ value_t m_KP; //!< Transconductance parameter [A/V²]
+ value_t m_UO; //!< Surface mobility [cm²/V/s]
+ value_t m_PHI; //!< Surface inversion potential [V]
+ value_t m_NSUB;//!< Substrate doping [1/cm³]
+ value_t m_GAMMA; //!< Bulk threshold parameter [V^½]
+ value_t m_LAMBDA; //!< Channel-length modulation [1/V]
+ value_t m_RD; //!< Drain ohmic resistance
+ value_t m_RS; //!< Source ohmic resistance
+ };
+
+ // Have a common start for mosfets
+
+ NETLIB_OBJECT(FET)
+ {
+ public:
+ enum q_type {
+ FET_NMOS,
+ FET_PMOS
+ };
+
+ NETLIB_CONSTRUCTOR(FET)
+ , m_model(*this, "MODEL", "NMOS")
+ , m_qtype(FET_NMOS)
+ {
+ }
+
+ NETLIB_IS_DYNAMIC(true)
+
+ //NETLIB_RESETI();
+ NETLIB_UPDATEI() { }
+
+ q_type qtype() const { return m_qtype; }
+ bool is_qtype(q_type atype) const { return m_qtype == atype; }
+ void set_qtype(q_type atype) { m_qtype = atype; }
+ protected:
+
+ fet_model_t m_model;
+ private:
+ q_type m_qtype;
+ };
+
+ // -----------------------------------------------------------------------------
+ // nld_QBJT_EB
+ // -----------------------------------------------------------------------------
+
+
+ NETLIB_OBJECT_DERIVED(MOSFET, FET)
+ {
+ public:
+ NETLIB_CONSTRUCTOR_DERIVED(MOSFET, FET)
+ , m_DG(*this, "m_DG", true)
+ , m_SG(*this, "m_SG", true)
+ , m_SD(*this, "m_SD", true)
+ , m_D_BD(*this, "m_D_BD")
+#if (!BODY_CONNECTED_TO_SOURCE)
+ , m_D_BS(*this, "m_D_BS")
+#endif
+ , m_phi(0.0)
+ , m_gamma(0.0)
+ , m_vto(0.0)
+ , m_beta(0.0)
+ , m_lambda(0.0)
+ , m_Leff(0.0)
+ , m_Cox(0.0)
+ {
+ register_subalias("S", m_SG.m_P); // Source
+ register_subalias("G", m_SG.m_N); // Gate
+
+ register_subalias("D", m_DG.m_P); // Drain
+
+ connect(m_SG.m_P, m_SD.m_P);
+ 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
+ }
+
+ protected:
+
+ NETLIB_RESETI();
+ NETLIB_UPDATEI();
+ NETLIB_UPDATE_PARAMI();
+ NETLIB_UPDATE_TERMINALSI();
+
+ private:
+
+ nld_twoterm m_DG;
+ nld_twoterm m_SG;
+ nld_twoterm m_SD;
+
+ generic_diode<diode_e::MOS> m_D_BD;
+#if (!BODY_CONNECTED_TO_SOURCE)
+ generic_diode<diode_e::MOS> m_D_BS;
+#endif
+
+ nl_double m_phi;
+ nl_double m_gamma;
+ nl_double m_vto;
+ nl_double m_beta;
+ nl_double m_lambda;
+
+ /* used in capacitance calculation */
+ nl_double m_Leff;
+ nl_double m_Cox;
+
+ //NETLIB_SUBXX(analog, C) m_CJE;
+ //NETLIB_SUBXX(analog, C) m_CJC;
+ };
+
+
+
+ // ----------------------------------------------------------------------------------------
+ // nld_Q - Ebers Moll
+ // ----------------------------------------------------------------------------------------
+
+
+ NETLIB_UPDATE(MOSFET)
+ {
+ if (!m_SG.m_P.net().isRailNet())
+ m_SG.m_P.solve_now(); // Basis
+ else if (!m_SG.m_N.net().isRailNet())
+ m_SG.m_N.solve_now(); // Emitter
+ else
+ m_DG.m_N.solve_now(); // Collector
+ }
+
+ NETLIB_RESET(MOSFET)
+ {
+ NETLIB_NAME(FET)::reset();
+#if 0
+ if (m_CJE)
+ {
+ m_CJE->reset();
+ m_CJE->m_C.setTo(m_model.m_CJE);
+ }
+ if (m_CJC)
+ {
+ m_CJC->reset();
+ m_CJC->m_C.setTo(m_model.m_CJC);
+ }
+#endif
+ }
+
+ NETLIB_UPDATE_TERMINALS(MOSFET)
+ {
+ const nl_double polarity = (qtype() == FET_NMOS ? 1.0 : -1.0);
+
+ const nl_double Ugd = -m_DG.deltaV() * polarity; // Gate - Drain
+ const nl_double Ugs = -m_SG.deltaV() * polarity; // Gate - Source
+ const nl_double Ubs = 0.0; // Bulk - Source == 0 if connected
+ const nl_double Ubd = m_SD.deltaV() * polarity; // Bulk - Drain = Source - Drain
+ const nl_double Uds = Ugs - Ugd;
+
+#if (!BODY_CONNECTED_TO_SOURCE)
+ m_D_BS.update_diode(Ubs);
+#endif
+ m_D_BD.update_diode(Ubd);
+
+ // Are we in forward mode ?
+ const bool is_forward = Uds >= 0;
+
+ // calculate Vth
+ const nl_double Vbulk = is_forward ? Ubs : Ubd;
+ const nl_double phi_m_Vbulk = (m_phi > Vbulk) ? std::sqrt(m_phi - Vbulk) : 0.0;
+ const nl_double Vth = m_vto * polarity + m_gamma * (phi_m_Vbulk - std::sqrt(m_phi));
+
+ const nl_double Vctrl = (is_forward ? Ugs : Ugd) - Vth;
+
+ nl_double Ids, gm, gds, gmb;
+
+ if (Vctrl <= 0.0)
+ {
+ // cutoff region
+ Ids = 0.0;
+ gm = 0.0;
+ gds = 0.0;
+ gmb = 0.0;
+ }
+ else
+ {
+ const nl_double Vds = std::abs(Uds);
+ const nl_double b = m_beta * (1.0 + m_lambda * Vds);
+ if (Vctrl <= Vds)
+ {
+ // saturation region
+ Ids = b * Vctrl * Vctrl / 2.0;
+ gm = b * Vctrl;
+ gds = m_lambda * m_beta * Vctrl * Vctrl / 2.0;
+ }
+ else
+ {
+ // linear region
+ Ids = b * Vds * (Vctrl - Vds / 2);
+ gm = b * Vds;
+ gds = b * (Vctrl - Vds) + m_lambda * m_beta * Vds * (Vctrl - Vds / 2.0);
+ }
+
+ // backgate transconductance
+ const nl_double bgtc = (phi_m_Vbulk != 0.0) ? (m_gamma / phi_m_Vbulk / 2.0) : 0.0;
+ gmb = gm * bgtc;
+ }
+
+ // FIXME: these are needed to compute capacitance
+ // nl_double Udsat = pol * std::max (Utst, 0.0);
+ // Uon = pol * Vth;
+
+ // compute bulk diode equivalent currents
+
+ const nl_double IeqBD = m_D_BD.Ieq();
+ const nl_double gbd = m_D_BD.G();
+#if 0
+ const nl_double IeqBS = m_D_BS.Ieq();
+ const nl_double gbs = m_D_BS.G();
+#else
+ const nl_double IeqBS = 0.0;
+ const nl_double gbs = 0.0;
+#endif
+ // exchange controlling nodes if necessary
+ const nl_double gsource = is_forward ? (gm + gmb) : 0;
+ const nl_double gdrain = is_forward ? 0.0 : (gm + gmb);
+
+ const nl_double IeqDS = (is_forward) ?
+ Ids - gm * Ugs - gmb * Ubs - gds * Uds
+ : -Ids - gm * Ugd - gmb * Ubd - gds * Uds;
+
+ // IG = 0
+ const nl_double IG = 0.0;
+ const nl_double ID = (+IeqBD - IeqDS) * polarity;
+ const nl_double IS = (+IeqBS + IeqDS) * polarity;
+ const nl_double IB = (-IeqBD - IeqBS) * polarity;
+
+ const nl_double gGG = 0.0; // ok
+ const nl_double gGD = 0.0; // ok
+ const nl_double gGS = 0.0; // ok
+ const nl_double gGB = 0.0; // ok
+
+ const nl_double gDG = gm; // ok
+ const nl_double gDD = gds + gbd - gdrain; // ok
+ const nl_double gDS = -gds - gsource; // ok
+ const nl_double gDB = gmb - gbd; // ok
+
+ const nl_double gSG = -gm; // ok
+ const nl_double gSD = -gds + gdrain; // ok
+ const nl_double gSS = gbs + gds + gsource; // ok
+ const nl_double gSB = -gbs - gmb;
+
+ const nl_double gBG = 0.0; // ok
+ const nl_double gBD = -gbd; // ok
+ const nl_double gBS = -gbs;
+ const nl_double gBB = gbs + gbd; // ok
+
+ // Source connected to body, Diode S-B shorted!
+ const nl_double gSSBB = gSS + gBB + gBS + gSB;
+
+ // S G
+ m_SG.set_mat( gSSBB, gSG + gBG, +(IS + IB), // S
+ gGS + gGB, gGG, IG ); // G
+ // D G
+ m_DG.set_mat( gDD, gDG, +ID, // D
+ gGD, 0.0, 0.0 ); // G
+ // S D
+ m_SD.set_mat( 0.0, gSD + gBD, 0.0, // S
+ gDS + gDB, 0.0, 0.0); // D
+
+ }
+
+
+ NETLIB_UPDATE_PARAM(MOSFET)
+ {
+ set_qtype((m_model.model_type() == "NMOS") ? FET_NMOS : FET_PMOS);
+
+ /*
+ * From http://ltwiki.org/LTspiceHelp/LTspiceHelp/M_MOSFET.htm :
+ *
+ * VTO, KP, LAMBDA, PHI and GAMMA. These parameters are computed
+ * if the process parameters(NSUB, TOX,...) are given, but
+ * user-specified values always override.
+ *
+ * But couldn't find a formula for lambda anywhere
+ *
+ */
+
+ m_lambda = m_model.m_LAMBDA; // FIXME: m_lambda only set once
+
+ // calculate effective channel length
+ m_Leff = m_model.m_L - 2 * m_model.m_LD;
+ nl_assert_always(m_Leff > 0.0, "Effective Lateral diffusion would be negative for model " + m_model.name());
+ if (m_model.m_TOX > 0.0)
+ m_Cox = (constants::eps_SiO2() * constants::eps_0() / m_model.m_TOX);
+ else
+ m_Cox = 0.0;
+
+ // calculate DC transconductance coefficient
+ if (m_model.m_KP > 0)
+ m_beta = m_model.m_KP * m_model.m_W / m_Leff;
+ else if (m_Cox > 0 && m_model.m_UO > 0)
+ m_beta = m_model.m_UO * 1e-4 * m_Cox * m_model.m_W / m_Leff;
+ else
+ m_beta = 2e-5 * m_model.m_W / m_Leff;
+
+ // Bulk diodes
+
+ m_D_BD.set_param(m_model.m_ISD, m_model.m_N, exec().gmin(), constants::T0());
+#if (!BODY_CONNECTED_TO_SOURCE)
+ m_D_BS.set_param(m_model.m_ISS, m_model.m_N, exec().gmin(), constants::T0());
+#endif
+
+ //FIXME::UT can disappear
+ const double Vt = constants::T0() * constants::k_b() / constants::Q_e();
+
+ // calculate surface potential if not given
+
+ if (m_model.m_PHI > 0.0)
+ m_phi = m_model.m_PHI;
+ else if (m_model.m_NSUB > 0.0)
+ {
+ nl_assert_always(m_model.m_NSUB * 1e6 >= constants::NiSi(), "Error calculating phi for model " + m_model.name());
+ m_phi = 2 * Vt * std::log (m_model.m_NSUB * 1e6 / constants::NiSi());
+ }
+ else
+ m_phi = 0.6;
+
+ // calculate bulk threshold if not given
+ if (m_model.m_GAMMA > 0.0)
+ m_gamma = m_model.m_GAMMA;
+ else
+ {
+ if (m_Cox > 0 && m_model.m_NSUB > 0)
+ m_gamma = std::sqrt (2.0 * constants::Q_e() * constants::eps_Si() * constants::eps_0() * m_model.m_NSUB * 1e6) / m_Cox;
+ else
+ m_gamma = 0.0;
+ }
+
+ m_vto = m_model.m_VTO;
+ nl_assert_always(m_vto != 0.0, "Threshold voltage not specified for " + m_model.name());
+
+ /* FIXME: VTO if missing may be calculated from TPG, NSS and temperature. Usually models
+ * specify VTO so skip this here.
+ */
+
+ m_Cox = m_Cox * m_model.m_W * m_Leff;
+
+ }
+
+} // namespace analog
+
+namespace devices {
+ NETLIB_DEVICE_IMPL_NS(analog, MOSFET, "MOSFET", "MODEL")
+} // namespace devices
+
+} // namespace netlist
diff --git a/src/lib/netlist/analog/nld_mosfet.h b/src/lib/netlist/analog/nld_mosfet.h
new file mode 100644
index 00000000000..32011d709d8
--- /dev/null
+++ b/src/lib/netlist/analog/nld_mosfet.h
@@ -0,0 +1,21 @@
+// license:GPL-2.0+
+// copyright-holders:Couriersud
+/*
+ * nld_mosfet.h
+ *
+ */
+
+#ifndef NLD_MOSFET_H_
+#define NLD_MOSFET_H_
+
+#include "netlist/nl_setup.h"
+
+// -----------------------------------------------------------------------------
+// Macros
+// -----------------------------------------------------------------------------
+
+#define MOSFET(name, model) \
+ NET_REGISTER_DEV(MOSFET, name) \
+ NETDEV_PARAMI(name, MODEL, model)
+
+#endif /* NLD_MOSFET_H_ */
diff --git a/src/lib/netlist/analog/nld_opamps.cpp b/src/lib/netlist/analog/nld_opamps.cpp
index 5f55a26f5d0..8c3c655bfa9 100644
--- a/src/lib/netlist/analog/nld_opamps.cpp
+++ b/src/lib/netlist/analog/nld_opamps.cpp
@@ -6,10 +6,10 @@
*/
#include "nld_opamps.h"
-#include "../nl_base.h"
-#include "../nl_errstr.h"
-#include "nlid_twoterm.h"
+#include "netlist/nl_base.h"
+#include "netlist/nl_errstr.h"
#include "nlid_fourterm.h"
+#include "nlid_twoterm.h"
#include <cmath>
@@ -129,10 +129,10 @@ namespace netlist
}
else if (m_type == 3)
{
- register_sub("CP1", m_CP);
- register_sub("EBUF", m_EBUF);
- register_sub("DN", m_DN, "D(IS=1e-15 N=1)");
- register_sub("DP", m_DP, "D(IS=1e-15 N=1)");
+ create_and_register_subdevice("CP1", m_CP);
+ create_and_register_subdevice("EBUF", m_EBUF);
+ create_and_register_subdevice("DN", m_DN, "D(IS=1e-15 N=1)");
+ create_and_register_subdevice("DP", m_DP, "D(IS=1e-15 N=1)");
//m_DP->m_model.setTo("D(IS=1e-15 N=1)");
//m_DN->m_model.setTo("D(IS=1e-15 N=1)");
@@ -201,7 +201,7 @@ namespace netlist
NETLIB_RESET(opamp)
{
- m_G1.do_reset();
+ m_G1.reset();
m_G1.m_RI.setTo(m_model.m_RI);
if (m_type == 1)
@@ -213,11 +213,11 @@ namespace netlist
}
else if (m_type == 3)
{
- m_EBUF->do_reset();
- m_DP->do_reset();
- m_DN->do_reset();
- m_CP->do_reset();
- m_RP.do_reset();
+ m_EBUF->reset();
+ m_DP->reset();
+ m_DN->reset();
+ m_CP->reset();
+ m_RP.reset();
m_EBUF->m_G.setTo(1.0);
m_EBUF->m_RO.setTo(m_model.m_RO);
@@ -239,6 +239,6 @@ namespace netlist
} //namespace analog
namespace devices {
- NETLIB_DEVICE_IMPL_NS(analog, opamp)
- }
+ NETLIB_DEVICE_IMPL_NS(analog, opamp, "OPAMP", "MODEL")
+ } // namespace devices
} // namespace netlist
diff --git a/src/lib/netlist/analog/nld_opamps.h b/src/lib/netlist/analog/nld_opamps.h
index d05a19aa3dc..4a07f646587 100644
--- a/src/lib/netlist/analog/nld_opamps.h
+++ b/src/lib/netlist/analog/nld_opamps.h
@@ -10,7 +10,7 @@
#ifndef NLD_OPAMPS_H_
#define NLD_OPAMPS_H_
-#include "../nl_setup.h"
+#include "netlist/nl_setup.h"
// ----------------------------------------------------------------------------------------
// Macros
diff --git a/src/lib/netlist/analog/nld_switches.cpp b/src/lib/netlist/analog/nld_switches.cpp
index 1575551f260..0e62fe13952 100644
--- a/src/lib/netlist/analog/nld_switches.cpp
+++ b/src/lib/netlist/analog/nld_switches.cpp
@@ -6,10 +6,13 @@
*/
#include "nlid_twoterm.h"
-#include "../nl_base.h"
-#include "../nl_factory.h"
+#include "netlist/nl_base.h"
+#include "netlist/nl_factory.h"
+#include "netlist/solver/nld_solver.h"
-#define R_OFF (1.0 / netlist().gmin())
+/* FIXME : convert to parameters */
+
+#define R_OFF (1.0 / exec().gmin())
#define R_ON 0.01
namespace netlist
@@ -24,7 +27,7 @@ namespace netlist
{
NETLIB_CONSTRUCTOR(switch1)
, m_R(*this, "R")
- , m_POS(*this, "POS", 0)
+ , m_POS(*this, "POS", false)
{
register_subalias("1", m_R.m_P);
register_subalias("2", m_R.m_N);
@@ -46,6 +49,11 @@ namespace netlist
NETLIB_UPDATE(switch1)
{
+ }
+
+ NETLIB_UPDATE_PARAM(switch1)
+ {
+ m_R.solve_now();
if (!m_POS())
{
m_R.set_R(R_OFF);
@@ -54,13 +62,8 @@ namespace netlist
{
m_R.set_R(R_ON);
}
+ m_R.solve_later();
- m_R.update_dev();
- }
-
- NETLIB_UPDATE_PARAM(switch1)
- {
- update();
}
// ----------------------------------------------------------------------------------------
@@ -72,7 +75,7 @@ namespace netlist
NETLIB_CONSTRUCTOR(switch2)
, m_R1(*this, "R1")
, m_R2(*this, "R2")
- , m_POS(*this, "POS", 0)
+ , m_POS(*this, "POS", false)
{
connect(m_R1.m_N, m_R2.m_N);
@@ -110,19 +113,31 @@ namespace netlist
m_R2.set_R(R_ON);
}
- m_R1.update_dev();
- m_R2.update_dev();
+ //m_R1.update_dev(time);
+ //m_R2.update_dev(time);
}
NETLIB_UPDATE_PARAM(switch2)
{
- update();
+ if (!m_POS())
+ {
+ m_R1.set_R(R_ON);
+ m_R2.set_R(R_OFF);
+ }
+ else
+ {
+ m_R1.set_R(R_OFF);
+ m_R2.set_R(R_ON);
+ }
+
+ m_R1.solve_now();
+ m_R2.solve_now();
}
} //namespace analog
namespace devices {
- NETLIB_DEVICE_IMPL_NS(analog, switch1)
- NETLIB_DEVICE_IMPL_NS(analog, switch2)
- }
+ NETLIB_DEVICE_IMPL_NS(analog, switch1, "SWITCH", "")
+ NETLIB_DEVICE_IMPL_NS(analog, switch2, "SWITCH2", "")
+ } // namespace devices
} // namespace netlist
diff --git a/src/lib/netlist/analog/nld_switches.h b/src/lib/netlist/analog/nld_switches.h
index 3cd561f84b1..7aa31c925ae 100644
--- a/src/lib/netlist/analog/nld_switches.h
+++ b/src/lib/netlist/analog/nld_switches.h
@@ -10,7 +10,7 @@
#ifndef NLD_SWITCHES_H_
#define NLD_SWITCHES_H_
-#include "../nl_setup.h"
+#include "netlist/nl_setup.h"
// ----------------------------------------------------------------------------------------
// Macros
diff --git a/src/lib/netlist/analog/nld_twoterm.h b/src/lib/netlist/analog/nld_twoterm.h
index d5cf6f6d228..b175f40af97 100644
--- a/src/lib/netlist/analog/nld_twoterm.h
+++ b/src/lib/netlist/analog/nld_twoterm.h
@@ -4,7 +4,7 @@
#ifndef NLD_TWOTERM_H_
#define NLD_TWOTERM_H_
-#include "../nl_setup.h"
+#include "netlist/nl_setup.h"
// -----------------------------------------------------------------------------
// Macros
diff --git a/src/lib/netlist/analog/nlid_fourterm.cpp b/src/lib/netlist/analog/nlid_fourterm.cpp
index aa2723fe8e1..7fa6a2f3809 100644
--- a/src/lib/netlist/analog/nlid_fourterm.cpp
+++ b/src/lib/netlist/analog/nlid_fourterm.cpp
@@ -5,8 +5,8 @@
*
*/
-#include "../solver/nld_solver.h"
-#include "../nl_factory.h"
+#include "netlist/solver/nld_solver.h"
+#include "netlist/nl_factory.h"
#include "nlid_fourterm.h"
#include <cmath>
@@ -24,16 +24,16 @@ namespace netlist
NETLIB_RESET(VCCS)
{
const nl_double m_mult = m_G() * m_gfac; // 1.0 ==> 1V ==> 1A
- const nl_double GI = NL_FCONST(1.0) / m_RI();
+ const nl_double GI = plib::constants<nl_double>::one() / m_RI();
- m_IP.set(GI);
- m_IN.set(GI);
+ m_IP.set_conductivity(GI);
+ m_IN.set_conductivity(GI);
- m_OP.set(m_mult, NL_FCONST(0.0));
- m_OP1.set(-m_mult, NL_FCONST(0.0));
+ m_OP.set_go_gt(-m_mult, plib::constants<nl_double>::zero());
+ m_OP1.set_go_gt(m_mult, plib::constants<nl_double>::zero());
- m_ON.set(-m_mult, NL_FCONST(0.0));
- m_ON1.set(m_mult, NL_FCONST(0.0));
+ m_ON.set_go_gt(m_mult, plib::constants<nl_double>::zero());
+ m_ON1.set_go_gt(-m_mult, plib::constants<nl_double>::zero());
}
NETLIB_UPDATE(VCCS)
@@ -63,11 +63,6 @@ NETLIB_UPDATE_PARAM(LVCCS)
NETLIB_NAME(VCCS)::update_param();
}
-NETLIB_UPDATE(LVCCS)
-{
- NETLIB_NAME(VCCS)::update();
-}
-
NETLIB_UPDATE_TERMINALS(LVCCS)
{
const nl_double m_mult = m_G() * m_gfac; // 1.0 ==> 1V ==> 1A
@@ -84,11 +79,11 @@ NETLIB_UPDATE_TERMINALS(LVCCS)
const nl_double beta = m_mult * (1.0 - X*X);
const nl_double I = m_cur_limit() * X - beta * m_vi;
- m_OP.set(beta, NL_FCONST(0.0), I);
- m_OP1.set(-beta, NL_FCONST(0.0));
+ m_OP.set_go_gt_I(-beta, plib::constants<nl_double>::zero(), I);
+ m_OP1.set_go_gt(beta, plib::constants<nl_double>::zero());
- m_ON.set(-beta, NL_FCONST(0.0), -I);
- m_ON1.set(beta, NL_FCONST(0.0));
+ m_ON.set_go_gt_I(beta, plib::constants<nl_double>::zero(), -I);
+ m_ON1.set_go_gt(-beta, plib::constants<nl_double>::zero());
}
// ----------------------------------------------------------------------------------------
@@ -105,30 +100,25 @@ NETLIB_UPDATE_PARAM(CCCS)
NETLIB_NAME(VCCS)::update_param();
}
-NETLIB_UPDATE(CCCS)
-{
- NETLIB_NAME(VCCS)::update();
-}
-
// ----------------------------------------------------------------------------------------
// nld_VCVS
// ----------------------------------------------------------------------------------------
NETLIB_RESET(VCVS)
{
- m_gfac = NL_FCONST(1.0) / m_RO();
+ m_gfac = plib::constants<nl_double>::one() / m_RO();
NETLIB_NAME(VCCS)::reset();
- m_OP2.set(NL_FCONST(1.0) / m_RO());
- m_ON2.set(NL_FCONST(1.0) / m_RO());
+ m_OP2.set_conductivity(plib::constants<nl_double>::one() / m_RO());
+ m_ON2.set_conductivity(plib::constants<nl_double>::one() / m_RO());
}
} //namespace analog
namespace devices {
- NETLIB_DEVICE_IMPL_NS(analog, VCVS)
- NETLIB_DEVICE_IMPL_NS(analog, VCCS)
- NETLIB_DEVICE_IMPL_NS(analog, CCCS)
- NETLIB_DEVICE_IMPL_NS(analog, LVCCS)
- }
+ NETLIB_DEVICE_IMPL_NS(analog, VCVS, "VCVS", "")
+ NETLIB_DEVICE_IMPL_NS(analog, VCCS, "VCCS", "")
+ NETLIB_DEVICE_IMPL_NS(analog, CCCS, "CCCS", "")
+ NETLIB_DEVICE_IMPL_NS(analog, LVCCS, "LVCCS", "")
+ } // namespace devices
} // namespace netlist
diff --git a/src/lib/netlist/analog/nlid_fourterm.h b/src/lib/netlist/analog/nlid_fourterm.h
index 641fc72427b..5ca6a747116 100644
--- a/src/lib/netlist/analog/nlid_fourterm.h
+++ b/src/lib/netlist/analog/nlid_fourterm.h
@@ -8,7 +8,8 @@
#ifndef NLID_FOURTERM_H_
#define NLID_FOURTERM_H_
-#include "../nl_base.h"
+#include "netlist/nl_base.h"
+#include "plib/putil.h"
namespace netlist {
namespace analog {
@@ -40,39 +41,32 @@ namespace netlist {
NETLIB_CONSTRUCTOR(VCCS)
, m_G(*this, "G", 1.0)
, m_RI(*this, "RI", 1e9)
- , m_OP(*this, "OP")
- , m_ON(*this, "ON")
- , m_IP(*this, "IP")
- , m_IN(*this, "IN")
- , m_OP1(*this, "_OP1")
- , m_ON1(*this, "_ON1")
+ , m_OP(*this, "OP", &m_IP)
+ , m_ON(*this, "ON", &m_IP)
+ , m_IP(*this, "IP", &m_IN) // <= this should be NULL and terminal be filtered out prior to solving...
+ , m_IN(*this, "IN", &m_IP) // <= this should be NULL and terminal be filtered out prior to solving...
+ , m_OP1(*this, "_OP1", &m_IN)
+ , m_ON1(*this, "_ON1", &m_IN)
, m_gfac(1.0)
{
- m_IP.m_otherterm = &m_IN; // <= this should be NULL and terminal be filtered out prior to solving...
- m_IN.m_otherterm = &m_IP; // <= this should be NULL and terminal be filtered out prior to solving...
-
- m_OP.m_otherterm = &m_IP;
- m_OP1.m_otherterm = &m_IN;
-
- m_ON.m_otherterm = &m_IP;
- m_ON1.m_otherterm = &m_IN;
-
connect(m_OP, m_OP1);
connect(m_ON, m_ON1);
- m_gfac = NL_FCONST(1.0);
+ m_gfac = plib::constants<nl_double>::one();
}
+ NETLIB_RESETI();
+
param_double_t m_G;
param_double_t m_RI;
protected:
- NETLIB_RESETI();
NETLIB_UPDATEI();
NETLIB_UPDATE_PARAMI()
{
NETLIB_NAME(VCCS)::reset();
}
+
terminal_t m_OP;
terminal_t m_ON;
@@ -98,14 +92,14 @@ namespace netlist {
NETLIB_IS_DYNAMIC(true)
- param_double_t m_cur_limit; /* current limit */
-
protected:
- NETLIB_UPDATEI();
+ //NETLIB_UPDATEI();
NETLIB_RESETI();
NETLIB_UPDATE_PARAMI();
NETLIB_UPDATE_TERMINALSI();
+ private:
+ param_double_t m_cur_limit; /* current limit */
nl_double m_vi;
};
@@ -137,12 +131,13 @@ namespace netlist {
public:
NETLIB_CONSTRUCTOR_DERIVED(CCCS, VCCS)
{
- m_gfac = NL_FCONST(1.0) / m_RI();
+ m_gfac = plib::constants<nl_double>::one() / m_RI();
}
- protected:
- NETLIB_UPDATEI();
NETLIB_RESETI();
+
+ protected:
+ //NETLIB_UPDATEI();
NETLIB_UPDATE_PARAMI();
};
@@ -180,29 +175,28 @@ namespace netlist {
public:
NETLIB_CONSTRUCTOR_DERIVED(VCVS, VCCS)
, m_RO(*this, "RO", 1.0)
- , m_OP2(*this, "_OP2")
- , m_ON2(*this, "_ON2")
+ , m_OP2(*this, "_OP2", &m_ON2)
+ , m_ON2(*this, "_ON2", &m_OP2)
{
- m_OP2.m_otherterm = &m_ON2;
- m_ON2.m_otherterm = &m_OP2;
-
connect(m_OP2, m_OP1);
connect(m_ON2, m_ON1);
}
+ NETLIB_RESETI();
+
param_double_t m_RO;
- protected:
+ private:
//NETLIB_UPDATEI();
- NETLIB_RESETI();
//NETLIB_UPDATE_PARAMI();
terminal_t m_OP2;
terminal_t m_ON2;
+
};
- }
-}
+ } // namespace analog
+} // namespace netlist
#endif /* NLD_FOURTERM_H_ */
diff --git a/src/lib/netlist/analog/nlid_twoterm.cpp b/src/lib/netlist/analog/nlid_twoterm.cpp
index f54c2b3e68f..5019c98c0e4 100644
--- a/src/lib/netlist/analog/nlid_twoterm.cpp
+++ b/src/lib/netlist/analog/nlid_twoterm.cpp
@@ -5,10 +5,10 @@
*
*/
-#include "../solver/nld_solver.h"
+#include "netlist/solver/nld_solver.h"
+#include "netlist/nl_factory.h"
#include "nlid_twoterm.h"
-#include "../nl_factory.h"
#include <cmath>
@@ -16,81 +16,34 @@ namespace netlist
{
namespace analog
{
+
// ----------------------------------------------------------------------------------------
-// generic_diode
+// nld_twoterm
// ----------------------------------------------------------------------------------------
-generic_diode::generic_diode(device_t &dev, pstring name)
- : m_Vd(dev, name + ".m_Vd", 0.7)
- , m_Id(dev, name + ".m_Id", 0.0)
- , m_G(dev, name + ".m_G", 1e-15)
- , m_Vt(0.0)
- , m_Vmin(0.0)
- , m_Is(0.0)
- , m_logIs(0.0)
- , m_n(0.0)
- , m_gmin(1e-15)
- , m_VtInv(0.0)
- , m_Vcrit(0.0)
-{
- set_param(1e-15, 1, 1e-15);
-}
-
-void generic_diode::set_param(const nl_double Is, const nl_double n, nl_double gmin)
+void NETLIB_NAME(twoterm)::solve_now()
{
- static constexpr double csqrt2 = 1.414213562373095048801688724209; //std::sqrt(2.0);
- m_Is = Is;
- m_logIs = std::log(Is);
- m_n = n;
- m_gmin = gmin;
-
- m_Vt = 0.0258 * m_n;
- m_Vmin = -5.0 * m_Vt;
-
- m_Vcrit = m_Vt * std::log(m_Vt / m_Is / csqrt2);
- m_VtInv = 1.0 / m_Vt;
+ /* we only need to call the non-rail terminal */
+ if (m_P.has_net() && !m_P.net().isRailNet())
+ m_P.solve_now();
+ else if (m_N.has_net() && !m_N.net().isRailNet())
+ m_N.solve_now();
}
-void generic_diode::update_diode(const nl_double nVd)
+void NETLIB_NAME(twoterm)::solve_later(netlist_time delay)
{
- if (nVd < m_Vmin)
- {
- m_Vd = nVd;
- m_G = m_gmin;
- m_Id = - m_Is;
- }
- else if (nVd < m_Vcrit)
- {
- m_Vd = nVd;
- //m_Vd = m_Vd + 10.0 * m_Vt * std::tanh((nVd - m_Vd) / 10.0 / m_Vt);
- //const double IseVDVt = m_Is * std::exp(m_Vd * m_VtInv);
- const double IseVDVt = std::exp(m_logIs + m_Vd * m_VtInv);
- m_Id = IseVDVt - m_Is;
- m_G = IseVDVt * m_VtInv + m_gmin;
- }
- else
- {
- const double a = std::max((nVd - m_Vd) * m_VtInv, NL_FCONST(-0.99));
- m_Vd = m_Vd + std::log1p(a) * m_Vt;
- //const double IseVDVt = m_Is * std::exp(m_Vd * m_VtInv);
- const double IseVDVt = std::exp(m_logIs + m_Vd * m_VtInv);
- m_Id = IseVDVt - m_Is;
- m_G = IseVDVt * m_VtInv + m_gmin;
- }
+ /* we only need to call the non-rail terminal */
+ if (m_P.has_net() && !m_P.net().isRailNet())
+ m_P.schedule_solve_after(delay);
+ else if (m_N.has_net() && !m_N.net().isRailNet())
+ m_N.schedule_solve_after(delay);
}
-// ----------------------------------------------------------------------------------------
-// nld_twoterm
-// ----------------------------------------------------------------------------------------
NETLIB_UPDATE(twoterm)
{
/* only called if connected to a rail net ==> notify the solver to recalculate */
- /* we only need to call the non-rail terminal */
- if (m_P.has_net() && !m_P.net().isRailNet())
- m_P.solve_now();
- else if (m_N.has_net() && !m_N.net().isRailNet())
- m_N.solve_now();
+ solve_now();
}
// ----------------------------------------------------------------------------------------
@@ -100,12 +53,7 @@ NETLIB_UPDATE(twoterm)
NETLIB_RESET(R_base)
{
NETLIB_NAME(twoterm)::reset();
- set_R(1.0 / netlist().gmin());
-}
-
-NETLIB_UPDATE(R_base)
-{
- NETLIB_NAME(twoterm)::update();
+ set_R(1.0 / exec().gmin());
}
// ----------------------------------------------------------------------------------------
@@ -114,14 +62,14 @@ NETLIB_UPDATE(R_base)
NETLIB_UPDATE_PARAM(R)
{
- update_dev();
- set_R(std::max(m_R(), netlist().gmin()));
+ solve_now();
+ set_R(std::max(m_R(), exec().gmin()));
}
NETLIB_RESET(R)
{
NETLIB_NAME(twoterm)::reset();
- set_R(std::max(m_R(), netlist().gmin()));
+ set_R(std::max(m_R(), exec().gmin()));
}
// ----------------------------------------------------------------------------------------
@@ -134,21 +82,22 @@ NETLIB_RESET(POT)
if (m_DialIsLog())
v = (std::exp(v) - 1.0) / (std::exp(1.0) - 1.0);
- m_R1.set_R(std::max(m_R() * v, netlist().gmin()));
- m_R2.set_R(std::max(m_R() * (NL_FCONST(1.0) - v), netlist().gmin()));
+ m_R1.set_R(std::max(m_R() * v, exec().gmin()));
+ m_R2.set_R(std::max(m_R() * (plib::constants<nl_double>::one() - v), exec().gmin()));
}
NETLIB_UPDATE_PARAM(POT)
{
- m_R1.update_dev();
- m_R2.update_dev();
+ m_R1.solve_now();
+ m_R2.solve_now();
nl_double v = m_Dial();
if (m_DialIsLog())
v = (std::exp(v) - 1.0) / (std::exp(1.0) - 1.0);
-
- m_R1.set_R(std::max(m_R() * v, netlist().gmin()));
- m_R2.set_R(std::max(m_R() * (NL_FCONST(1.0) - v), netlist().gmin()));
+ if (m_Reverse())
+ v = 1.0 - v;
+ m_R1.set_R(std::max(m_R() * v, exec().gmin()));
+ m_R2.set_R(std::max(m_R() * (plib::constants<nl_double>::one() - v), exec().gmin()));
}
@@ -164,13 +113,13 @@ NETLIB_RESET(POT2)
v = (std::exp(v) - 1.0) / (std::exp(1.0) - 1.0);
if (m_Reverse())
v = 1.0 - v;
- m_R1.set_R(std::max(m_R() * v, netlist().gmin()));
+ m_R1.set_R(std::max(m_R() * v, exec().gmin()));
}
NETLIB_UPDATE_PARAM(POT2)
{
- m_R1.update_dev();
+ m_R1.solve_now();
nl_double v = m_Dial();
@@ -178,38 +127,7 @@ NETLIB_UPDATE_PARAM(POT2)
v = (std::exp(v) - 1.0) / (std::exp(1.0) - 1.0);
if (m_Reverse())
v = 1.0 - v;
- m_R1.set_R(std::max(m_R() * v, netlist().gmin()));
-}
-
-// ----------------------------------------------------------------------------------------
-// nld_C
-// ----------------------------------------------------------------------------------------
-
-NETLIB_RESET(C)
-{
- // FIXME: Startup conditions
- set(netlist().gmin(), 0.0, -5.0 / netlist().gmin());
- //set(netlist().gmin(), 0.0, 0.0);
-}
-
-NETLIB_UPDATE_PARAM(C)
-{
- m_GParallel = netlist().gmin();
-}
-
-NETLIB_UPDATE(C)
-{
- NETLIB_NAME(twoterm)::update();
-}
-
-NETLIB_TIMESTEP(C)
-{
- /* Gpar should support convergence */
- const nl_double G = m_C() / step + m_GParallel;
- const nl_double I = -G * deltaV();
- set_mat( G, -G, -I,
- -G, G, I);
- //set(G, 0.0, I);
+ m_R1.set_R(std::max(m_R() * v, exec().gmin()));
}
// ----------------------------------------------------------------------------------------
@@ -218,9 +136,9 @@ NETLIB_TIMESTEP(C)
NETLIB_RESET(L)
{
- m_GParallel = netlist().gmin();
+ m_gmin = exec().gmin();
m_I = 0.0;
- m_G = m_GParallel;
+ m_G = m_gmin;
set_mat( m_G, -m_G, -m_I,
-m_G, m_G, m_I);
//set(1.0/NETLIST_GMIN, 0.0, -5.0 * NETLIST_GMIN);
@@ -230,16 +148,11 @@ NETLIB_UPDATE_PARAM(L)
{
}
-NETLIB_UPDATE(L)
-{
- NETLIB_NAME(twoterm)::update();
-}
-
NETLIB_TIMESTEP(L)
{
/* Gpar should support convergence */
m_I += m_I + m_G * deltaV();
- m_G = step / m_L() + m_GParallel;
+ m_G = step / m_L() + m_gmin;
set_mat( m_G, -m_G, -m_I,
-m_G, m_G, m_I);
//set(m_G, 0.0, m_I);
@@ -254,8 +167,8 @@ NETLIB_RESET(D)
nl_double Is = m_model.m_IS;
nl_double n = m_model.m_N;
- m_D.set_param(Is, n, netlist().gmin());
- set(m_D.G(), 0.0, m_D.Ieq());
+ m_D.set_param(Is, n, exec().gmin(), constants::T0());
+ set_G_V_I(m_D.G(), 0.0, m_D.Ieq());
}
NETLIB_UPDATE_PARAM(D)
@@ -263,12 +176,7 @@ NETLIB_UPDATE_PARAM(D)
nl_double Is = m_model.m_IS;
nl_double n = m_model.m_N;
- m_D.set_param(Is, n, netlist().gmin());
-}
-
-NETLIB_UPDATE(D)
-{
- NETLIB_NAME(twoterm)::update();
+ m_D.set_param(Is, n, exec().gmin(), constants::T0());
}
NETLIB_UPDATE_TERMINALS(D)
@@ -281,65 +189,18 @@ NETLIB_UPDATE_TERMINALS(D)
//set(m_D.G(), 0.0, m_D.Ieq());
}
-// ----------------------------------------------------------------------------------------
-// nld_VS
-// ----------------------------------------------------------------------------------------
-
-NETLIB_RESET(VS)
-{
- NETLIB_NAME(twoterm)::reset();
- this->set(1.0 / m_R(), m_V(), 0.0);
-}
-
-NETLIB_UPDATE(VS)
-{
- NETLIB_NAME(twoterm)::update();
-}
-
-NETLIB_TIMESTEP(VS)
-{
- this->set(1.0 / m_R(),
- m_compiled.evaluate(std::vector<double>({netlist().time().as_double()})),
- 0.0);
-}
-
-// ----------------------------------------------------------------------------------------
-// nld_CS
-// ----------------------------------------------------------------------------------------
-
-NETLIB_RESET(CS)
-{
- NETLIB_NAME(twoterm)::reset();
- const nl_double I = m_I();
-
- set_mat(0.0, 0.0, -I,
- 0.0, 0.0, I);
- //this->set(0.0, 0.0, m_I());
-}
-
-NETLIB_UPDATE(CS)
-{
- NETLIB_NAME(twoterm)::update();
-}
-
-NETLIB_TIMESTEP(CS)
-{
- const double I = m_compiled.evaluate(std::vector<double>({netlist().time().as_double()}));
- set_mat(0.0, 0.0, -I,
- 0.0, 0.0, I);
-}
} //namespace analog
namespace devices {
- NETLIB_DEVICE_IMPL_NS(analog, R)
- NETLIB_DEVICE_IMPL_NS(analog, POT)
- NETLIB_DEVICE_IMPL_NS(analog, POT2)
- NETLIB_DEVICE_IMPL_NS(analog, C)
- NETLIB_DEVICE_IMPL_NS(analog, L)
- NETLIB_DEVICE_IMPL_NS(analog, D)
- NETLIB_DEVICE_IMPL_NS(analog, VS)
- NETLIB_DEVICE_IMPL_NS(analog, CS)
- }
+ NETLIB_DEVICE_IMPL_NS(analog, R, "RES", "R")
+ NETLIB_DEVICE_IMPL_NS(analog, POT, "POT", "R")
+ NETLIB_DEVICE_IMPL_NS(analog, POT2, "POT2", "R")
+ NETLIB_DEVICE_IMPL_NS(analog, C, "CAP", "C")
+ NETLIB_DEVICE_IMPL_NS(analog, L, "IND", "L")
+ NETLIB_DEVICE_IMPL_NS(analog, D, "DIODE", "MODEL")
+ NETLIB_DEVICE_IMPL_NS(analog, VS, "VS", "V")
+ NETLIB_DEVICE_IMPL_NS(analog, CS, "CS", "I")
+ } // namespace devices
} // namespace netlist
diff --git a/src/lib/netlist/analog/nlid_twoterm.h b/src/lib/netlist/analog/nlid_twoterm.h
index 7778f5f4585..470fb35c96e 100644
--- a/src/lib/netlist/analog/nlid_twoterm.h
+++ b/src/lib/netlist/analog/nlid_twoterm.h
@@ -33,8 +33,12 @@
#ifndef NLID_TWOTERM_H_
#define NLID_TWOTERM_H_
-#include "../nl_base.h"
-#include "../plib/pfunction.h"
+#include "netlist/nl_base.h"
+#include "netlist/nl_setup.h"
+#include "netlist/solver/nld_solver.h"
+#include "plib/pfunction.h"
+
+#include <cmath>
// -----------------------------------------------------------------------------
// Implementation
@@ -48,14 +52,27 @@ namespace netlist
// nld_twoterm
// -----------------------------------------------------------------------------
+ template <class C>
+ inline core_device_t &bselect(bool b, C &d1, core_device_t &d2)
+ {
+ auto *h = dynamic_cast<core_device_t *>(&d1);
+ return b ? *h : d2;
+ }
+ template<>
+ inline core_device_t &bselect(bool b, netlist_state_t &d1, core_device_t &d2)
+ {
+ plib::unused_var(d1);
+ if (b)
+ throw nl_exception("bselect with netlist and b==true");
+ return d2;
+ }
+
NETLIB_OBJECT(twoterm)
{
NETLIB_CONSTRUCTOR_EX(twoterm, bool terminals_owned = false)
- , m_P(bselect(terminals_owned, owner, *this), (terminals_owned ? name + "." : "") + "1")
- , m_N(bselect(terminals_owned, owner, *this), (terminals_owned ? name + "." : "") + "2")
+ , m_P(bselect(terminals_owned, owner, *this), (terminals_owned ? name + "." : "") + "1", &m_N)
+ , m_N(bselect(terminals_owned, owner, *this), (terminals_owned ? name + "." : "") + "2", &m_P)
{
- m_P.m_otherterm = &m_N;
- m_N.m_otherterm = &m_P;
}
terminal_t m_P;
@@ -63,36 +80,36 @@ NETLIB_OBJECT(twoterm)
//NETLIB_UPDATE_TERMINALSI() { }
//NETLIB_RESETI() { }
- NETLIB_UPDATEI();
public:
- /* inline */ void set(const nl_double G, const nl_double V, const nl_double I)
+
+ NETLIB_UPDATEI();
+
+ void solve_now();
+
+ void solve_later(netlist_time delay = netlist_time::from_nsec(1));
+
+ void set_G_V_I(const nl_double G, const nl_double V, const nl_double I)
{
/* GO, GT, I */
- m_P.set( G, G, ( V) * G - I);
- m_N.set( G, G, ( -V) * G + I);
+ m_P.set_go_gt_I( -G, G, ( V) * G - I);
+ m_N.set_go_gt_I( -G, G, ( -V) * G + I);
}
- /* inline */ nl_double deltaV() const
+ nl_double deltaV() const
{
return m_P.net().Q_Analog() - m_N.net().Q_Analog();
}
- void set_mat(const nl_double a11, const nl_double a12, const nl_double r1,
- const nl_double a21, const nl_double a22, const nl_double r2)
+ void set_mat(const nl_double a11, const nl_double a12, const nl_double rhs1,
+ const nl_double a21, const nl_double a22, const nl_double rhs2)
{
/* GO, GT, I */
- m_P.set(-a12, a11, r1);
- m_N.set(-a21, a22, r2);
+ m_P.set_go_gt_I(a12, a11, rhs1);
+ m_N.set_go_gt_I(a21, a22, rhs2);
}
private:
- template <class C>
- static core_device_t &bselect(bool b, C &d1, core_device_t &d2)
- {
- core_device_t *h = dynamic_cast<core_device_t *>(&d1);
- return b ? *h : d2;
- }
};
@@ -106,17 +123,17 @@ NETLIB_OBJECT_DERIVED(R_base, twoterm)
{
}
-public:
- inline void set_R(const nl_double R)
+ void set_R(const nl_double R)
{
- const nl_double G = NL_FCONST(1.0) / R;
+ const nl_double G = plib::constants<nl_double>::one() / R;
set_mat( G, -G, 0.0,
-G, G, 0.0);
}
-protected:
NETLIB_RESETI();
- NETLIB_UPDATEI();
+
+protected:
+ //NETLIB_UPDATEI();
};
@@ -127,7 +144,6 @@ NETLIB_OBJECT_DERIVED(R, R_base)
{
}
- param_double_t m_R;
protected:
@@ -136,6 +152,7 @@ protected:
NETLIB_UPDATE_PARAMI();
private:
+ param_double_t m_R;
/* protect set_R ... it's a recipe to desaster when used to bypass the parameter */
using NETLIB_NAME(R_base)::set_R;
};
@@ -151,7 +168,8 @@ NETLIB_OBJECT(POT)
, m_R2(*this, "_R2")
, m_R(*this, "R", 10000)
, m_Dial(*this, "DIAL", 0.5)
- , m_DialIsLog(*this, "DIALLOG", 0)
+ , m_DialIsLog(*this, "DIALLOG", false)
+ , m_Reverse(*this, "REVERSE", false)
{
register_subalias("1", m_R1.m_P);
register_subalias("2", m_R1.m_N);
@@ -172,6 +190,7 @@ private:
param_double_t m_R;
param_double_t m_Dial;
param_logic_t m_DialIsLog;
+ param_logic_t m_Reverse;
};
NETLIB_OBJECT(POT2)
@@ -180,8 +199,8 @@ NETLIB_OBJECT(POT2)
, m_R1(*this, "_R1")
, m_R(*this, "R", 10000)
, m_Dial(*this, "DIAL", 0.5)
- , m_DialIsLog(*this, "DIALLOG", 0)
- , m_Reverse(*this, "REVERSE", 0)
+ , m_DialIsLog(*this, "DIALLOG", false)
+ , m_Reverse(*this, "REVERSE", false)
{
register_subalias("1", m_R1.m_P);
register_subalias("2", m_R1.m_N);
@@ -201,6 +220,83 @@ private:
param_logic_t m_Reverse;
};
+// -----------------------------------------------------------------------------
+// A generic capacitor model
+// -----------------------------------------------------------------------------
+
+enum class capacitor_e
+{
+ CHARGE_CONSERVING,
+ CONSTANT_CAPACITY
+};
+
+template <capacitor_e TYPE>
+class generic_capacitor
+{
+};
+
+
+template <>
+class generic_capacitor<capacitor_e::CHARGE_CONSERVING>
+{
+public:
+ generic_capacitor(device_t &dev, const pstring &name)
+ : m_h(dev, name + ".m_h", 0.0)
+ , m_charge(dev, name + ".m_charge", 0.0)
+ , m_gmin(0.0)
+ {
+ }
+
+ constexpr capacitor_e type() const { return capacitor_e::CHARGE_CONSERVING; }
+
+ constexpr nl_double G(nl_double cap) const
+ {
+ return cap * m_h + m_gmin;
+ }
+
+ constexpr nl_double Ieq(nl_double cap, nl_double v) const
+ {
+ return m_h * (cap * v - m_charge) - G(cap) * v;
+ }
+
+ void timestep(nl_double cap, nl_double v, nl_double step)
+ {
+ m_h = 1.0 / step;
+ m_charge = cap * v;
+ }
+
+ void setparams(nl_double gmin) { m_gmin = gmin; }
+
+private:
+ state_var<double> m_h;
+ state_var<double> m_charge;
+ nl_double m_gmin;
+};
+
+template <>
+class generic_capacitor<capacitor_e::CONSTANT_CAPACITY>
+{
+public:
+ generic_capacitor(device_t &dev, const pstring &name)
+ : m_h(dev, name + ".m_h", 0.0)
+ , m_gmin(0.0)
+ {
+ }
+
+ constexpr capacitor_e type() const { return capacitor_e::CONSTANT_CAPACITY; }
+ constexpr nl_double G(nl_double cap) const { return cap * m_h + m_gmin; }
+ constexpr nl_double Ieq(nl_double cap, nl_double v) const { return - G(cap) * v; }
+
+ void timestep(nl_double cap, nl_double v, nl_double step)
+ {
+ plib::unused_var(cap, v);
+ m_h = 1.0 / step;
+ }
+ void setparams(nl_double gmin) { m_gmin = gmin; }
+private:
+ state_var<nl_double> m_h;
+ nl_double m_gmin;
+};
// -----------------------------------------------------------------------------
// nld_C
@@ -211,25 +307,45 @@ NETLIB_OBJECT_DERIVED(C, twoterm)
public:
NETLIB_CONSTRUCTOR_DERIVED(C, twoterm)
, m_C(*this, "C", 1e-6)
- , m_GParallel(0.0)
+ , m_cap(*this, "m_cap")
{
- //register_term("1", m_P);
- //register_term("2", m_N);
}
NETLIB_IS_TIMESTEP(true)
- NETLIB_TIMESTEPI();
+ NETLIB_TIMESTEPI()
+ {
+ m_cap.timestep(m_C(), deltaV(), step);
+ if (m_cap.type() == capacitor_e::CONSTANT_CAPACITY)
+ {
+ const nl_double I = m_cap.Ieq(m_C(), deltaV());
+ const nl_double G = m_cap.G(m_C());
+ set_mat( G, -G, -I,
+ -G, G, I);
+ }
+ }
+
+ NETLIB_IS_DYNAMIC(m_cap.type() == capacitor_e::CHARGE_CONSERVING)
+ NETLIB_UPDATE_TERMINALSI()
+ {
+ const nl_double I = m_cap.Ieq(m_C(), deltaV());
+ const nl_double G = m_cap.G(m_C());
+ set_mat( G, -G, -I,
+ -G, G, I);
+ }
param_double_t m_C;
+ NETLIB_RESETI()
+ {
+ m_cap.setparams(exec().gmin());
+ }
protected:
- NETLIB_RESETI();
- NETLIB_UPDATEI();
- NETLIB_UPDATE_PARAMI();
+ //NETLIB_UPDATEI();
+ NETLIB_UPDATE_PARAMI() { }
private:
- nl_double m_GParallel;
-
+ generic_capacitor<capacitor_e::CHARGE_CONSERVING> m_cap;
+ //generic_capacitor<capacitor_e::CONSTANT_CAPACITY> m_cap;
};
// -----------------------------------------------------------------------------
@@ -241,7 +357,7 @@ NETLIB_OBJECT_DERIVED(L, twoterm)
public:
NETLIB_CONSTRUCTOR_DERIVED(L, twoterm)
, m_L(*this, "L", 1e-6)
- , m_GParallel(0.0)
+ , m_gmin(0.0)
, m_G(0.0)
, m_I(0.0)
{
@@ -251,16 +367,16 @@ public:
NETLIB_IS_TIMESTEP(true)
NETLIB_TIMESTEPI();
-
- param_double_t m_L;
+ NETLIB_RESETI();
protected:
- NETLIB_RESETI();
- NETLIB_UPDATEI();
+ //NETLIB_UPDATEI();
NETLIB_UPDATE_PARAMI();
private:
- nl_double m_GParallel;
+ param_double_t m_L;
+
+ nl_double m_gmin;
nl_double m_G;
nl_double m_I;
};
@@ -269,14 +385,82 @@ private:
// A generic diode model to be used in other devices (Diode, BJT ...)
// -----------------------------------------------------------------------------
+enum class diode_e
+{
+ BIPOLAR,
+ MOS
+};
+
+template <diode_e TYPE>
class generic_diode
{
public:
- generic_diode(device_t &dev, pstring name);
+ generic_diode(device_t &dev, const pstring &name)
+ : m_Vd(dev, name + ".m_Vd", 0.7)
+ , m_Id(dev, name + ".m_Id", 0.0)
+ , m_G(dev, name + ".m_G", 1e-15)
+ , m_Vt(0.0)
+ , m_Vmin(0.0) // not used in MOS model
+ , m_Is(0.0)
+ , m_logIs(0.0)
+ , m_n(0.0)
+ , m_gmin(1e-15)
+ , m_VtInv(0.0)
+ , m_Vcrit(0.0)
+ {
+ set_param(1e-15, 1, 1e-15, 300.0);
+ }
+
+ void update_diode(const double nVd)
+ {
+ 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;
+ }
+ // FIXME: For MOS, stop here, the critical code path will not converge
+ else if (TYPE == diode_e::MOS || nVd < m_Vcrit)
+ {
+ m_Vd = nVd;
+ const double IseVDVt = std::exp(std::min(700.0, m_logIs + m_Vd * m_VtInv));
+ m_Id = IseVDVt - m_Is;
+ m_G = IseVDVt * m_VtInv + m_gmin;
+ }
+ else
+ {
+ const double a = std::max((nVd - m_Vd) * m_VtInv, constants::cast(-0.99));
+ m_Vd = m_Vd + std::log1p(a) * m_Vt;
+ //const double IseVDVt = m_Is * std::exp(m_Vd * m_VtInv);
+ const double IseVDVt = std::exp(m_logIs + m_Vd * m_VtInv);
+ m_Id = IseVDVt - m_Is;
+ m_G = IseVDVt * m_VtInv + m_gmin;
+ }
+ }
+
+
+ void set_param(const double Is, const double n, double gmin, double temp)
+ {
+ static constexpr double csqrt2 = 1.414213562373095048801688724209; //std::sqrt(2.0);
+ m_Is = Is;
+ m_logIs = std::log(Is);
+ m_n = n;
+ m_gmin = gmin;
- void update_diode(const double nVd);
+ m_Vt = m_n * temp * constants::k_b() / constants::Q_e();
+
+ m_Vmin = -5.0 * m_Vt;
+
+ m_Vcrit = m_Vt * std::log(m_Vt / m_Is / csqrt2);
+ m_VtInv = 1.0 / m_Vt;
+ }
- void set_param(const double Is, const double n, double gmin);
double I() const { return m_Id; }
double G() const { return m_G; }
@@ -367,15 +551,15 @@ public:
NETLIB_IS_DYNAMIC(true)
NETLIB_UPDATE_TERMINALSI();
-
- diode_model_t m_model;
+ NETLIB_RESETI();
protected:
- NETLIB_RESETI();
- NETLIB_UPDATEI();
+ //NETLIB_UPDATEI();
NETLIB_UPDATE_PARAMI();
- generic_diode m_D;
+private:
+ diode_model_t m_model;
+ generic_diode<diode_e::BIPOLAR> m_D;
};
@@ -389,10 +573,11 @@ NETLIB_OBJECT_DERIVED(VS, twoterm)
{
public:
NETLIB_CONSTRUCTOR_DERIVED(VS, twoterm)
+ , m_t(*this, "m_t", 0.0)
, m_R(*this, "R", 0.1)
, m_V(*this, "V", 0.0)
, m_func(*this,"FUNC", "")
- , m_compiled(this->name() + ".FUNCC", this, this->netlist().state())
+ , m_compiled(this->name() + ".FUNCC", this, this->state().run_state_manager())
{
register_subalias("P", m_P);
register_subalias("N", m_N);
@@ -401,12 +586,26 @@ public:
}
NETLIB_IS_TIMESTEP(m_func() != "")
- NETLIB_TIMESTEPI();
+
+ NETLIB_TIMESTEPI()
+ {
+ m_t += step;
+ this->set_G_V_I(1.0 / m_R(),
+ m_compiled.evaluate(std::vector<double>({m_t})),
+ 0.0);
+ }
protected:
- NETLIB_UPDATEI();
- NETLIB_RESETI();
+ // NETLIB_UPDATEI() { NETLIB_NAME(twoterm)::update(time); }
+
+ NETLIB_RESETI()
+ {
+ NETLIB_NAME(twoterm)::reset();
+ this->set_G_V_I(1.0 / m_R(), m_V(), 0.0);
+ }
+private:
+ state_var<double> m_t;
param_double_t m_R;
param_double_t m_V;
param_str_t m_func;
@@ -421,9 +620,10 @@ NETLIB_OBJECT_DERIVED(CS, twoterm)
{
public:
NETLIB_CONSTRUCTOR_DERIVED(CS, twoterm)
+ , m_t(*this, "m_t", 0.0)
, m_I(*this, "I", 1.0)
, m_func(*this,"FUNC", "")
- , m_compiled(this->name() + ".FUNCC", this, this->netlist().state())
+ , m_compiled(this->name() + ".FUNCC", this, this->state().run_state_manager())
{
register_subalias("P", m_P);
register_subalias("N", m_N);
@@ -432,19 +632,33 @@ public:
}
NETLIB_IS_TIMESTEP(m_func() != "")
- NETLIB_TIMESTEPI();
+ NETLIB_TIMESTEPI()
+ {
+ m_t += step;
+ const double I = m_compiled.evaluate(std::vector<double>({m_t}));
+ set_mat(0.0, 0.0, -I,
+ 0.0, 0.0, I);
+ }
+
protected:
- NETLIB_UPDATEI();
- NETLIB_RESETI();
+ //NETLIB_UPDATEI() { NETLIB_NAME(twoterm)::update(time); }
+ NETLIB_RESETI()
+ {
+ NETLIB_NAME(twoterm)::reset();
+ set_mat(0.0, 0.0, -m_I(),
+ 0.0, 0.0, m_I());
+ }
+private:
+ state_var<double> m_t;
param_double_t m_I;
param_str_t m_func;
plib::pfunction m_compiled;
};
- } //namespace devices
+ } // namespace analog
} // namespace netlist
#endif /* NLD_TWOTERM_H_ */