summaryrefslogtreecommitdiffstatshomepage
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.cpp708
-rw-r--r--src/lib/netlist/analog/nld_bjt.h25
-rw-r--r--src/lib/netlist/analog/nld_fourterm.h30
-rw-r--r--src/lib/netlist/analog/nld_generic_models.h348
-rw-r--r--src/lib/netlist/analog/nld_mosfet.cpp865
-rw-r--r--src/lib/netlist/analog/nld_mosfet.h21
-rw-r--r--src/lib/netlist/analog/nld_opamps.cpp314
-rw-r--r--src/lib/netlist/analog/nld_opamps.h24
-rw-r--r--src/lib/netlist/analog/nld_switches.cpp118
-rw-r--r--src/lib/netlist/analog/nld_switches.h25
-rw-r--r--src/lib/netlist/analog/nld_twoterm.h67
-rw-r--r--src/lib/netlist/analog/nlid_fourterm.cpp199
-rw-r--r--src/lib/netlist/analog/nlid_fourterm.h282
-rw-r--r--src/lib/netlist/analog/nlid_twoterm.cpp361
-rw-r--r--src/lib/netlist/analog/nlid_twoterm.h735
15 files changed, 2282 insertions, 1840 deletions
diff --git a/src/lib/netlist/analog/nld_bjt.cpp b/src/lib/netlist/analog/nld_bjt.cpp
index 3d954504264..2152f8f431a 100644
--- a/src/lib/netlist/analog/nld_bjt.cpp
+++ b/src/lib/netlist/analog/nld_bjt.cpp
@@ -1,446 +1,574 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * nld_bjt.c
- *
- */
-#include "netlist/solver/nld_solver.h"
-#include "netlist/nl_setup.h"
+#include "nl_base.h"
#include "nlid_twoterm.h"
+#include "solver/nld_solver.h"
-#include <cmath>
+// Names
+// spell-checker: words Ebers, Moll
-namespace netlist
-{
-namespace analog
-{
- using constants = plib::constants<nl_double>;
+// FIXME: Remove QBJT_switch - no more use
+namespace netlist::analog
+{
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)
+ diode()
+ : m_Is(nlconst::np_Is())
+ , m_VT(nlconst::np_VT())
+ , m_VT_inv(plib::reciprocal(m_VT))
+ {
+ }
+
+ diode(nl_fptype Is, nl_fptype n)
+ : m_Is(Is)
+ , m_VT(nlconst::np_VT(n))
+ , m_VT_inv(plib::reciprocal(m_VT))
{
- 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)
+ void set(nl_fptype Is, nl_fptype n) noexcept
{
m_Is = Is;
- m_VT = 0.0258 * n;
- m_VT_inv = 1.0 / m_VT;
+ m_VT = nlconst::np_VT(n);
+ m_VT_inv = plib::reciprocal(m_VT);
+ }
+ nl_fptype I(nl_fptype V) const noexcept
+ {
+ return m_Is * plib::exp(V * m_VT_inv) - m_Is;
+ }
+ nl_fptype g(nl_fptype V) const noexcept
+ {
+ return m_Is * m_VT_inv * plib::exp(V * m_VT_inv);
+ }
+ nl_fptype V(nl_fptype I) const noexcept
+ {
+ return plib::log1p(I / m_Is) * m_VT;
+ } // log1p(x)=log(1.0 + x)
+ nl_fptype gI(nl_fptype I) const noexcept
+ {
+ return m_VT_inv * (I + m_Is);
}
- 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;
+ nl_fptype m_Is;
+ nl_fptype m_VT;
+ nl_fptype m_VT_inv;
};
// -----------------------------------------------------------------------------
// nld_Q - Base classes
// -----------------------------------------------------------------------------
- /*! Class representing the bjt model parameters.
- *
- * This is the model representation of the bjt model. Typically, SPICE uses
- * the following parameters. A "Y" in the first column indicates that the
- * parameter is actually used in netlist.
- *
- * | NL? | name | parameter | units | default | example | area |
- * |:---:|------|-----------------------------------------------------------------------|-------|---------:|----------------:|:----:|
- * | Y | IS | transport saturation current | A | 1E-016 | 1E-015 | * |
- * | Y | BF | ideal maximum forward beta | - | 100 | 100 | |
- * | Y | NF | forward current emission coefficient | - | 1 | 1 | |
- * | | VAF | forward Early voltage | V | infinite | 200 | |
- * | | IKF | corner for forward beta high current roll-off | A | infinite | 0.01 | * |
- * | | ISE | B-E leakage saturation current | A | 0 | 0.0000000000001 | * |
- * | | NE | B-E leakage emission coefficient | - | 1.5 | 2 | |
- * | Y | BR | ideal maximum reverse beta | - | 1 | 0.1 | |
- * | Y | NR | reverse current emission coefficient | - | 1 | 1 | |
- * | | VAR | reverse Early voltage | V | infinite | 200 | |
- * | | IKR | corner for reverse beta high current roll-off | A | infinite | 0.01 | * |
- * | | ISC | leakage saturation current | A | 0 | 8 | |
- * | | NC | leakage emission coefficient | - | 2 | 1.5 | |
- * | | RB | zero bias base resistance | | 0 | 100 | * |
- * | | IRB | current where base resistance falls halfway to its min value | A | infinite | 0.1 | * |
- * | | RBM | minimum base resistance at high currents | | RB | 10 | * |
- * | | RE | emitter resistance | | 0 | 1 | * |
- * | | RC | collector resistance | | 0 | 10 | * |
- * | 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 | |
- * | | XTF | coefficient for bias dependence of TF | - | 0 | | |
- * | | 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 | | |
- * | 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 | | |
- * | | TR | ideal reverse transit time | sec | 0 | 10ns | |
- * | | CJS | zero-bias collector-substrate capacitance | F | 0 | 2pF | * |
- * | | VJS | substrate junction built-in potential | V | 0.75 | | |
- * | | MJS | substrate junction exponential factor | - | 0 | 0.5 | |
- * | | XTB | forward and reverse beta temperature exponent | - | 0 | | |
- * | | EG | energy gap for temperature effect on IS | eV | 1.11 | | |
- * | | XTI | temperature exponent for effect on IS | - | 3 | | |
- * | | 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 | |
- * */
-
- class bjt_model_t : public param_model_t
+ enum class bjt_type
+ {
+ BJT_NPN,
+ BJT_PNP
+ };
+
+ /// \brief Class representing the bjt model parameters
+ ///
+ /// This is the model representation of the bjt model. Typically, SPICE
+ /// uses the following parameters. A "Y" in the first column indicates that
+ /// the parameter is actually used in netlist.
+ ///
+ /// | NL? | name | parameter | units | default | example | area | xxx
+ /// |:---:|------|-----------------------------------------------------------------------|-------|---------:|----------------:|:----:| xxx
+ /// | Y | IS | transport saturation current | A | 1E-016 | 1E-015 | * | xxx
+ /// | Y | BF | ideal maximum forward beta | - | 100 | 100 | |
+ /// | Y | NF | forward current emission coefficient | - | 1 | 1 | |
+ /// | | VAF | forward Early voltage | V | infinite | 200 | |
+ /// | | IKF | corner for forward beta high current roll-off | A | infinite | 0.01 | * |
+ /// | | ISE | B-E leakage saturation current | A | 0 | 0.0000000000001 | * |
+ /// | | NE | B-E leakage emission coefficient | - | 1.5 | 2 | |
+ /// | Y | BR | ideal maximum reverse beta | - | 1 | 0.1 | |
+ /// | Y | NR | reverse current emission coefficient | - | 1 | 1 | |
+ /// | | VAR | reverse Early voltage | V | infinite | 200 | |
+ /// | | IKR | corner for reverse beta high current roll-off | A | infinite | 0.01 | * |
+ /// | | ISC | leakage saturation current | A | 0 | 8 | |
+ /// | | NC | leakage emission coefficient | - | 2 | 1.5 | |
+ /// | | RB | zero bias base resistance | | 0 | 100 | * |
+ /// | | IRB | current where base resistance falls halfway to its min value | A | infinite | 0.1 | * |
+ /// | | RBM | minimum base resistance at high currents | | RB | 10 | * |
+ /// | | RE | emitter resistance | | 0 | 1 | * |
+ /// | | RC | collector resistance | | 0 | 10 | * |
+ /// | 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 | |
+ /// | | XTF | coefficient for bias dependence of TF | - | 0 | | |
+ /// | | 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 | | |
+ /// | 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 | | |
+ /// | | TR | ideal reverse transit time | sec | 0 | 10ns | |
+ /// | | CJS | zero-bias collector-substrate capacitance | F | 0 | 2pF | * |
+ /// | | VJS | substrate junction built-in potential | V | 0.75 | | |
+ /// | | MJS | substrate junction exponential factor | - | 0 | 0.5 | |
+ /// | | XTB | forward and reverse beta temperature exponent | - | 0 | | |
+ /// | | EG | energy gap for temperature effect on IS | eV | 1.11 | | |
+ /// | | XTI | temperature exponent for effect on IS | - | 3 | | |
+ /// | | 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 | |
+ ///
+
+ class bjt_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_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_CJE; //!< B-E zero-bias depletion capacitance
- value_t m_CJC; //!< B-C zero-bias depletion capacitance
+ bjt_model_t(param_model_t &model)
+ : m_type((model.type() == "NPN") ? bjt_type::BJT_NPN
+ : bjt_type::BJT_PNP)
+ , m_IS(model, "IS")
+ , m_BF(model, "BF")
+ , m_NF(model, "NF")
+ , m_BR(model, "BR")
+ , m_NR(model, "NR")
+ , m_CJE(model, "CJE")
+ , m_CJC(model, "CJC")
+ {
+ }
+ bjt_type m_type;
+ param_model_t::value_t m_IS; //!< transport saturation current
+ param_model_t::value_t m_BF; //!< ideal maximum forward beta
+ param_model_t::value_t m_NF; //!< forward current emission coefficient
+ param_model_t::value_t m_BR; //!< ideal maximum reverse beta
+ param_model_t::value_t m_NR; //!< reverse current emission coefficient
+ param_model_t::value_t m_CJE; //!< B-E zero-bias depletion capacitance
+ param_model_t::value_t m_CJC; //!< B-C zero-bias depletion capacitance
};
- // Have a common start for transistors
+ // -----------------------------------------------------------------------------
+ // nld_QBJT_switch
+ // -----------------------------------------------------------------------------
- NETLIB_OBJECT(QBJT)
+ //
+ // + - C
+ // B ----VVV----+ |
+ // | |
+ // Rb Rc
+ // Rb Rc
+ // Rb Rc
+ // | |
+ // +----+----+
+ // |
+ // E
+ //
+
+ class nld_QBJT_switch : public base_device_t
{
public:
- enum q_type {
- BJT_NPN,
- BJT_PNP
- };
+ nld_QBJT_switch(constructor_param_t data)
+ : base_device_t(data)
+ , m_model(*this, "MODEL", "NPN")
+ , m_bjt_model(m_model)
+ , m_RB(*this, "m_RB", NETLIB_DELEGATE(terminal_handler))
+ , m_RC(*this, "m_RC", NETLIB_DELEGATE(terminal_handler))
+ , m_BC(*this, "m_BC", NETLIB_DELEGATE(terminal_handler))
+ , m_gB(nlconst::cgmin())
+ , m_gC(nlconst::cgmin())
+ , m_V(nlconst::zero())
+ , m_state_on(*this, "m_state_on", 0U)
+ {
+ register_sub_alias("B", m_RB.P());
+ register_sub_alias("E", m_RB.N());
+ register_sub_alias("C", m_RC.P());
- NETLIB_CONSTRUCTOR_EX(QBJT, pstring model = "NPN")
- , m_model(*this, "MODEL", model)
- , m_qtype(BJT_NPN)
+ connect(m_RB.N(), m_RC.N());
+ connect(m_RB.P(), m_BC.P());
+ connect(m_RC.P(), m_BC.N());
+ }
+
+ NETLIB_RESETI();
+ NETLIB_HANDLERI(terminal_handler)
{
+ auto *solver(m_RB.solver());
+ if (solver != nullptr)
+ solver->solve_now();
+ else
+ m_RC.solver()->solve_now();
}
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:
+ NETLIB_UPDATE_PARAMI();
+ NETLIB_UPDATE_TERMINALSI();
- bjt_model_t m_model;
private:
- q_type m_qtype;
+ param_model_t m_model;
+ bjt_model_t m_bjt_model;
+ NETLIB_NAME(two_terminal) m_RB;
+ NETLIB_NAME(two_terminal) m_RC;
+ NETLIB_NAME(two_terminal) m_BC;
+
+ nl_fptype m_gB; // base conductance / switch on
+ nl_fptype m_gC; // collector conductance / switch on
+ nl_fptype m_V; // internal voltage source
+ state_var<unsigned> m_state_on;
};
// -----------------------------------------------------------------------------
- // nld_QBJT_switch
+ // nld_three_terminal
// -----------------------------------------------------------------------------
+ //
+ // PIN1 C
+ // P1_P2 |
+ // +----N 3T P----+
+ // | |
+ // | N
+ // Pin2 --+ 3T P0_P1
+ // B | P
+ // | |
+ // +----N 3T P----+
+ // P0_P2 |
+ // Pin0 E
+ //
+
+ struct mna2
+ {
+ using row = std::array<nl_fptype, 3>;
+ std::array<row, 2> arr;
+ };
+
+ struct mna3
+ {
+ using row = std::array<nl_fptype, 4>;
+ std::array<row, 3> arr;
+ const row &operator[](std::size_t i) const { return arr[i]; }
+ };
- /*
- * + - C
- * B ----VVV----+ |
- * | |
- * Rb Rc
- * Rb Rc
- * Rb Rc
- * | |
- * +----+----+
- * |
- * E
- */
-
- NETLIB_OBJECT_DERIVED(QBJT_switch, QBJT)
+ class nld_three_terminal : public base_device_t
{
- NETLIB_CONSTRUCTOR_DERIVED(QBJT_switch, QBJT)
- , m_RB(*this, "m_RB", true)
- , m_RC(*this, "m_RC", true)
- , m_BC(*this, "m_BC", true)
- , m_gB(1e-9)
- , m_gC(1e-9)
- , m_V(0.0)
- , m_state_on(*this, "m_state_on", 0)
+ public:
+ nld_three_terminal(constructor_param_t data,
+ std::array<pstring, 3> pins)
+ : base_device_t(data)
+ , m_P0_P2(*this, "m_P1_P3", NETLIB_DELEGATE(terminal_handler))
+ , m_P1_P2(*this, "m_P2_P3", NETLIB_DELEGATE(terminal_handler))
+ , m_P0_P1(*this, "m_P1_P2", NETLIB_DELEGATE(terminal_handler))
{
- register_subalias("B", m_RB.m_P);
- register_subalias("E", m_RB.m_N);
- register_subalias("C", m_RC.m_P);
+ register_sub_alias(pins[0], m_P0_P2.P()); // Emitter - row 1
+ register_sub_alias(pins[1], m_P1_P2.P()); // Collector- row 2
+ register_sub_alias(pins[2], m_P0_P2.N()); // Base -row 3
- connect(m_RB.m_N, m_RC.m_N);
- connect(m_RB.m_P, m_BC.m_P);
- connect(m_RC.m_P, m_BC.m_N);
+ connect(m_P0_P2.P(), m_P0_P1.P());
+ connect(m_P0_P2.N(), m_P1_P2.N());
+ connect(m_P1_P2.P(), m_P0_P1.N());
}
- NETLIB_RESETI();
- NETLIB_UPDATEI();
- NETLIB_UPDATE_PARAMI();
- NETLIB_UPDATE_TERMINALSI();
+ NETLIB_RESETI()
+ {
+ if (m_P0_P2.solver() == nullptr && m_P1_P2.solver() == nullptr)
+ throw nl_exception(MF_DEVICE_FRY_1(this->name()));
+ }
- private:
- nld_twoterm m_RB;
- nld_twoterm m_RC;
- nld_twoterm m_BC;
+ NETLIB_HANDLERI(terminal_handler)
+ {
+ auto *solver(m_P0_P2.solver());
+ if (solver != nullptr)
+ solver->solve_now();
+ else
+ m_P1_P2.solver()->solve_now();
+ }
- 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;
+ template <int PIN1, int PIN2>
+ nl_fptype delta_V() const noexcept
+ {
+ static_assert(PIN1 >= 0 && PIN2 >= 0 && PIN1 <= 2 && PIN2 <= 2,
+ "out of bounds pin number");
+ static constexpr const int sel = PIN1 * 10 + PIN2;
+ if constexpr (sel == 0)
+ return 0.0;
+ else if constexpr (sel == 1) // P0 P1
+ return m_P0_P1.deltaV();
+ else if constexpr (sel == 2) // P0 P2
+ return m_P0_P2.deltaV();
+ else if constexpr (sel == 10) // P1 P0
+ return -m_P0_P1.deltaV();
+ else if constexpr (sel == 11) // P1 P1
+ return 0.0;
+ else if constexpr (sel == 12) // P1 P2
+ return m_P1_P2.deltaV();
+ else if constexpr (sel == 20) // P2 P0
+ return -m_P0_P2.deltaV();
+ else if constexpr (sel == 21) // P2 P1
+ return -m_P1_P2.deltaV();
+ else if constexpr (sel == 22) // P2 P2
+ return 0.0;
+ }
+
+ void set_mat_ex(double xee, double xec, double xeb, double xIe,
+ double xce, double xcc, double xcb, double xIc,
+ double xbe, double xbc, double xbb, double xIb)
+ {
+ using row2 = std::array<nl_fptype, 3>;
+ // rows 0 and 2
+ m_P0_P2.set_mat({
+ row2{xee, xeb, xIe},
+ row2{xbe, xbb, xIb}
+ });
+ // rows 1 and 2
+ m_P1_P2.set_mat({
+ row2{xcc, xcb, xIc},
+ row2{xbc, 0, 0 }
+ });
+ // rows 0 and 1
+ m_P0_P1.set_mat({
+ row2{0, xec, 0},
+ row2{xce, 0, 0}
+ });
+ }
+
+ void set_mat_ex(const mna3 &m)
+ {
+ using row2 = std::array<nl_fptype, 3>;
+ // rows 0 and 2
+ m_P0_P2.set_mat({
+ row2{m[0][0], m[0][2], m[0][3]},
+ row2{m[2][0], m[2][2], m[2][3]}
+ });
+ // rows 1 and 2
+ m_P1_P2.set_mat({
+ row2{m[1][1], m[1][2], m[1][3]},
+ row2{m[2][1], 0, 0 }
+ });
+ // rows 0 and 1
+ m_P0_P1.set_mat({
+ row2{0, m[0][1], 0},
+ row2{m[1][0], 0, 0}
+ });
+ }
private:
+ nld_two_terminal m_P0_P2; // gee, gec - gee, gce - gee, gee - gec | Ie
+ nld_two_terminal m_P1_P2; // gcc, gce - gcc, gec - gcc, gcc - gce | Ic
+ nld_two_terminal m_P0_P1; // 0, -gec, -gcc, 0 | 0
};
// -----------------------------------------------------------------------------
// nld_QBJT_EB
// -----------------------------------------------------------------------------
-
- NETLIB_OBJECT_DERIVED(QBJT_EB, QBJT)
+ class nld_QBJT_EB : public nld_three_terminal
{
+ enum pins
+ {
+ E = 0,
+ C = 1,
+ B = 2
+ };
+
public:
- NETLIB_CONSTRUCTOR_DERIVED(QBJT_EB, QBJT)
+ nld_QBJT_EB(constructor_param_t data)
+ : nld_three_terminal(data, {"E", "C", "B"})
+ , m_model(*this, "MODEL", "NPN")
+ , m_bjt_model(m_model)
, 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)
+ if (m_bjt_model.m_CJE > nlconst::zero())
{
- create_and_register_subdevice("m_CJE", m_CJE);
+ create_and_register_sub_device(*this, "m_CJE", m_CJE);
connect("B", "m_CJE.1");
connect("E", "m_CJE.2");
}
- if (m_model.m_CJC > 0.0)
+ if (m_bjt_model.m_CJC > nlconst::zero())
{
- create_and_register_subdevice("m_CJC", m_CJC);
+ create_and_register_sub_device(*this, "m_CJC", m_CJC);
connect("B", "m_CJC.1");
connect("C", "m_CJC.2");
}
-
}
protected:
-
NETLIB_RESETI();
- NETLIB_UPDATEI();
+
+ NETLIB_IS_DYNAMIC(true)
+
NETLIB_UPDATE_PARAMI();
NETLIB_UPDATE_TERMINALSI();
private:
+ param_model_t m_model;
+ bjt_model_t m_bjt_model;
generic_diode<diode_e::BIPOLAR> m_gD_BC;
generic_diode<diode_e::BIPOLAR> m_gD_BE;
+ nl_fptype m_alpha_f;
+ nl_fptype m_alpha_r;
- 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;
+ NETLIB_SUB_UPTR(analog, C) m_CJE;
+ NETLIB_SUB_UPTR(analog, C) m_CJC;
};
-
- // ----------------------------------------------------------------------------------------
- // nld_Q
- // ----------------------------------------------------------------------------------------
-
- NETLIB_UPDATE(QBJT)
- {
- // netlist().solver()->schedule1();
- }
-
// ----------------------------------------------------------------------------------------
// nld_QBJT_switch
// ----------------------------------------------------------------------------------------
-
NETLIB_RESET(QBJT_switch)
{
- NETLIB_NAME(QBJT)::reset();
+ if (m_RB.solver() == nullptr && m_RC.solver() == nullptr)
+ throw nl_exception(MF_DEVICE_FRY_1(this->name()));
- m_state_on = 0;
+ static constexpr const auto zero(nlconst::zero());
- m_RB.set_G_V_I(exec().gmin(), 0.0, 0.0);
- m_RC.set_G_V_I(exec().gmin(), 0.0, 0.0);
+ m_state_on = 0;
- m_BC.set_G_V_I(exec().gmin() / 10.0, 0.0, 0.0);
+ m_RB.set_G_V_I(exec().gmin(), zero, zero);
+ m_RC.set_G_V_I(exec().gmin(), zero, zero);
+ m_BC.set_G_V_I(exec().gmin() / nlconst::magic(10.0), zero, zero);
}
- NETLIB_UPDATE(QBJT_switch)
+ NETLIB_UPDATE_PARAM(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
- }
+ nl_fptype IS = m_bjt_model.m_IS;
+ nl_fptype BF = m_bjt_model.m_BF;
+ nl_fptype NF = m_bjt_model.m_NF;
+ // nl_fptype VJE = m_bjt_model.dValue("VJE", 0.75);
+ nl_fptype alpha = BF / (nlconst::one() + BF);
+#if 0
+ diode d(IS, NF);
- 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);
+ // Assume 5mA Collector current for switch operation
- set_qtype((m_model.type() == "NPN") ? BJT_NPN : BJT_PNP);
+ const auto cc(nlconst::magic(0.005));
+ m_V = d.V(cc / alpha);
- nl_double alpha = BF / (1.0 + BF);
+ // Base current is 0.005 / beta
+ // as a rough estimate, we just scale the conductance down
- diode d(IS, NF);
+ m_gB = plib::reciprocal((m_V / (cc / BF)));
+
+ // m_gB = d.gI(0.005 / alpha);
+
+ if (m_gB < exec().gmin())
+ m_gB = exec().gmin();
+ m_gC = d.gI(cc); // very rough estimate
+#else
+ // diode d(IS, NF);
// Assume 5mA Collector current for switch operation
- m_V = d.V(0.005 / alpha);
+ const auto cc(nlconst::magic(0.005));
+ // Get voltage across diode
+ // m_V = d.V(cc / alpha);
+ m_V = plib::log1p((cc / alpha) / IS) * nlconst::np_VT(NF);
- /* Base current is 0.005 / beta
- * as a rough estimate, we just scale the conductance down */
+ // Base current is 0.005 / beta
+ // as a rough estimate, we just scale the conductance down
- m_gB = 1.0 / (m_V/(0.005 / BF));
+ m_gB = plib::reciprocal((m_V / (cc / BF)));
- //m_gB = d.gI(0.005 / alpha);
+ // m_gB = d.gI(0.005 / alpha);
if (m_gB < exec().gmin())
m_gB = exec().gmin();
- m_gC = d.gI(0.005); // very rough estimate
+
+ // m_gC = d.gI(cc); // very rough estimate
+ m_gC = plib::reciprocal(nlconst::np_VT(NF)) * (cc + IS);
+#endif
}
NETLIB_UPDATE_TERMINALS(QBJT_switch)
{
- const nl_double m = (is_qtype( BJT_NPN) ? 1 : -1);
+ const nl_fptype m = (m_bjt_model.m_type == bjt_type::BJT_NPN)
+ ? nlconst::one()
+ : -nlconst::one();
- const unsigned new_state = (m_RB.deltaV() * m > m_V ) ? 1 : 0;
+ 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;
+ const auto zero(nlconst::zero());
+ const nl_fptype gb = new_state ? m_gB : exec().gmin();
+ const nl_fptype gc = new_state ? m_gC : exec().gmin();
+ const nl_fptype v = new_state ? m_V * m : zero;
- m_RB.set_G_V_I(gb, v, 0.0);
- m_RC.set_G_V_I(gc, 0.0, 0.0);
+ m_RB.set_G_V_I(gb, v, zero);
+ m_RC.set_G_V_I(gc, zero, zero);
m_state_on = new_state;
}
}
-
// ----------------------------------------------------------------------------------------
// nld_Q - Ebers Moll
// ----------------------------------------------------------------------------------------
-
- 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(QBJT)::reset();
+ nld_three_terminal::reset();
+
if (m_CJE)
{
m_CJE->reset();
- m_CJE->m_C.setTo(m_model.m_CJE);
+ m_CJE->set_cap_embedded(m_bjt_model.m_CJE);
}
if (m_CJC)
{
m_CJC->reset();
- m_CJC->m_C.setTo(m_model.m_CJC);
+ m_CJC->set_cap_embedded(m_bjt_model.m_CJC);
}
-
}
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;
+ const nl_fptype polarity(m_bjt_model.m_type == bjt_type::BJT_NPN
+ ? nlconst::one()
+ : -nlconst::one());
+
+ m_gD_BE.update_diode(delta_V<pins::B, pins::E>() * polarity);
+ m_gD_BC.update_diode(delta_V<pins::B, pins::C>() * polarity);
+
+ const nl_fptype gee = m_gD_BE.G();
+ const nl_fptype gcc = m_gD_BC.G();
+ const nl_fptype gec = m_alpha_r * gcc;
+ const nl_fptype gce = m_alpha_f * gee;
+ const nl_fptype sIe = -m_gD_BE.I() + m_alpha_r * m_gD_BC.I();
+ const nl_fptype sIc = m_alpha_f * m_gD_BE.I() - m_gD_BC.I();
+ const nl_fptype Ie = (sIe + gee * m_gD_BE.Vd() - gec * m_gD_BC.Vd())
+ * polarity;
+ const nl_fptype Ic = (sIc - gce * m_gD_BE.Vd() + gcc * m_gD_BC.Vd())
+ * polarity;
// "Circuit Design", page 174
-
- 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);
+ using r = mna3::row;
+ set_mat_ex(mna3{
+ r{gee, -gec, gec - gee, -Ie },
+ r{-gce, gcc, gce - gcc, -Ic },
+ r{gce - gee, gec - gcc, gcc + gee - gce - gec, Ie + Ic}
+ });
}
-
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.type() == "NPN") ? BJT_NPN : BJT_PNP);
-
- m_alpha_f = BF / (1.0 + BF);
- m_alpha_r = BR / (1.0 + BR);
-
- 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());
+ nl_fptype IS = m_bjt_model.m_IS;
+ nl_fptype BF = m_bjt_model.m_BF;
+ nl_fptype NF = m_bjt_model.m_NF;
+ nl_fptype BR = m_bjt_model.m_BR;
+ nl_fptype NR = m_bjt_model.m_NR;
+ // nl_fptype VJE = m_m_bjt_model.dValue("VJE", 0.75);
+
+ m_alpha_f = BF / (nlconst::one() + BF);
+ m_alpha_r = BR / (nlconst::one() + BR);
+
+ m_gD_BE.set_param(IS / m_alpha_f, NF, exec().gmin(), nlconst::T0());
+ m_gD_BC.set_param(IS / m_alpha_r, NR, exec().gmin(), nlconst::T0());
}
-} // namespace analog
+} // namespace netlist::analog
-namespace devices {
+namespace netlist::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
+} // namespace netlist::devices
diff --git a/src/lib/netlist/analog/nld_bjt.h b/src/lib/netlist/analog/nld_bjt.h
deleted file mode 100644
index 3a8478f19f7..00000000000
--- a/src/lib/netlist/analog/nld_bjt.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// license:GPL-2.0+
-// copyright-holders:Couriersud
-/*
- * nld_bjt.h
- *
- */
-
-#ifndef NLD_BJT_H_
-#define NLD_BJT_H_
-
-#include "netlist/nl_setup.h"
-
-// -----------------------------------------------------------------------------
-// Macros
-// -----------------------------------------------------------------------------
-
-#define QBJT_SW(name, model) \
- NET_REGISTER_DEV(QBJT_SW, name) \
- NETDEV_PARAMI(name, MODEL, model)
-
-#define QBJT_EB(name, model) \
- NET_REGISTER_DEV(QBJT_EB, name) \
- NETDEV_PARAMI(name, MODEL, model)
-
-#endif /* NLD_BJT_H_ */
diff --git a/src/lib/netlist/analog/nld_fourterm.h b/src/lib/netlist/analog/nld_fourterm.h
deleted file mode 100644
index 66c47c0be29..00000000000
--- a/src/lib/netlist/analog/nld_fourterm.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// license:GPL-2.0+
-// copyright-holders:Couriersud
-/*
- * nld_fourterm.h
- *
- */
-
-#ifndef NLD_FOURTERM_H_
-#define NLD_FOURTERM_H_
-
-
-#include "netlist/nl_setup.h"
-
-// ----------------------------------------------------------------------------------------
-// Macros
-// ----------------------------------------------------------------------------------------
-
-#define VCCS(name) \
- NET_REGISTER_DEV(VCCS, name)
-
-#define CCCS(name) \
- NET_REGISTER_DEV(CCCS, name)
-
-#define VCVS(name) \
- NET_REGISTER_DEV(VCVS, name)
-
-#define LVCCS(name) \
- NET_REGISTER_DEV(LVCCS, name)
-
-#endif /* NLD_FOURTERM_H_ */
diff --git a/src/lib/netlist/analog/nld_generic_models.h b/src/lib/netlist/analog/nld_generic_models.h
index 1edc4abd320..d8df599a511 100644
--- a/src/lib/netlist/analog/nld_generic_models.h
+++ b/src/lib/netlist/analog/nld_generic_models.h
@@ -1,21 +1,27 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * nl_generic_models.h
- *
- */
#ifndef NLD_GENERIC_MODELS_H_
#define NLD_GENERIC_MODELS_H_
-#include "netlist/nl_base.h"
-#include "netlist/nl_setup.h"
+///
+/// \file nld_generic_models.h
+///
-#include <cmath>
+#include "core/state_var.h"
+#include "nl_base.h"
-namespace netlist
-{
-namespace analog
+//
+// Set to 0 to use a linearized diode model in the range exceeding
+// maximum dissipation. The intention is to have a faster
+// convergence. On selected circuits (LM3900 trapezoidal) this is
+// observable and has a 10% impact.
+// FIXME: More research needed
+//
+
+#define USE_TEXTBOOK_DIODE (1)
+
+namespace netlist::analog
{
// -----------------------------------------------------------------------------
@@ -37,15 +43,15 @@ namespace analog
class generic_capacitor<capacitor_e::VARIABLE_CAPACITY>
{
public:
- generic_capacitor(device_t &dev, const pstring &name)
- : m_h(dev, name + ".m_h", 0.0)
- , m_c(dev, name + ".m_c", 0.0)
- , m_v(dev, name + ".m_v", 0.0)
- , m_gmin(0.0)
+ generic_capacitor(core_device_t &dev, const pstring &name)
+ : m_h(dev, name + ".m_h", nlconst::zero())
+ , m_c(dev, name + ".m_c", nlconst::zero())
+ , m_v(dev, name + ".m_v", nlconst::zero())
+ , m_gmin(nlconst::zero())
{
}
- capacitor_e type() const { return capacitor_e::VARIABLE_CAPACITY; }
+ static capacitor_e type() noexcept { return capacitor_e::VARIABLE_CAPACITY; }
// Circuit Simulation, page 284, 5.360
// q(un+1) - q(un) = int(un, un+1, C(U)) = (C0+C1)/2 * (un+1-un)
@@ -54,35 +60,39 @@ namespace analog
// so that G depends on un+1 only and Ieq on un only.
// In both cases, i = G * un+1 + Ieq
- nl_double G(nl_double cap) const
+ nl_fptype G(nl_fptype cap) const noexcept
{
//return m_h * cap + m_gmin;
- return m_h * 0.5 * (cap + m_c) + m_gmin;
+ return m_h * nlconst::half() * (cap + m_c) + m_gmin;
//return m_h * cap + m_gmin;
}
- nl_double Ieq(nl_double cap, nl_double v) const
+ nl_fptype Ieq(nl_fptype cap, [[maybe_unused]] nl_fptype v) const noexcept
{
- plib::unused_var(v);
//return -m_h * 0.5 * ((cap + m_c) * m_v + (cap - m_c) * v) ;
- return -m_h * 0.5 * (cap + m_c) * m_v;
+ return -m_h * nlconst::half() * (cap + m_c) * m_v;
//return -m_h * cap * m_v;
}
- void timestep(nl_double cap, nl_double v, nl_double step)
+ void time_step(nl_fptype cap, nl_fptype v, nl_fptype step) noexcept
{
- m_h = 1.0 / step;
+ m_h = plib::reciprocal(step);
m_c = cap;
m_v = v;
}
- void setparams(nl_double gmin) { m_gmin = gmin; }
+ void restore_state() noexcept
+ {
+ // no state used
+ }
+
+ void set_params(nl_fptype gmin) noexcept { m_gmin = gmin; }
private:
- state_var<double> m_h;
- state_var<double> m_c;
- state_var<double> m_v;
- nl_double m_gmin;
+ state_var<nl_fptype> m_h;
+ state_var<nl_fptype> m_c;
+ state_var<nl_fptype> m_v;
+ nl_fptype m_gmin;
};
// "Circuit simulation", page 274
@@ -91,33 +101,107 @@ namespace analog
{
public:
generic_capacitor(device_t &dev, const pstring &name)
- : m_h(dev, name + ".m_h", 0.0)
- , m_v(dev, name + ".m_v", 0.0)
- , m_gmin(0.0)
+ : m_h(dev, name + ".m_h", nlconst::zero())
+ , m_v(dev, name + ".m_v", nlconst::zero())
+ , m_gmin(nlconst::zero())
{
}
- capacitor_e type() const { return capacitor_e::CONSTANT_CAPACITY; }
- nl_double G(nl_double cap) const { return cap * m_h + m_gmin; }
- nl_double Ieq(nl_double cap, nl_double v) const
+ static capacitor_e type() noexcept { return capacitor_e::CONSTANT_CAPACITY; }
+ nl_fptype G(nl_fptype cap) const noexcept { return cap * m_h + m_gmin; }
+ nl_fptype Ieq(nl_fptype cap, [[maybe_unused]] nl_fptype v) const noexcept
{
- plib::unused_var(v);
return - G(cap) * m_v;
}
- void timestep(nl_double cap, nl_double v, nl_double step)
+ void time_step([[maybe_unused]] nl_fptype cap, nl_fptype v, nl_fptype step) noexcept
{
- plib::unused_var(cap);
- m_h = 1.0 / step;
+ m_h = plib::reciprocal(step);
m_v = v;
}
- void setparams(nl_double gmin) { m_gmin = gmin; }
+ void setparams(nl_fptype gmin) noexcept { m_gmin = gmin; }
+ private:
+ state_var<nl_fptype> m_h;
+ state_var<nl_fptype> m_v;
+ nl_fptype m_gmin;
+ };
+
+#if (NL_USE_BACKWARD_EULER)
+ // Constant model for constant capacitor model
+ // Backward Euler
+ // "Circuit simulation", page 274
+ struct generic_capacitor_const
+ {
+ public:
+ generic_capacitor_const( /*[[maybe_unused]]*/ core_device_t &dev, /*[[maybe_unused]]*/ const pstring &name)
+ : m_gmin(nlconst::zero())
+ {
+ // gcc 7.2 (mingw) and 7.5 (ubuntu) don't accept maybe_unused here
+ plib::unused_var(dev, name);
+ }
+
+ // Returns { G, Ieq }
+ std::pair<nl_fptype, nl_fptype> time_step(nl_fptype cap, nl_fptype v, nl_fptype step) const noexcept
+ {
+ const nl_fptype h(plib::reciprocal(step));
+ const nl_fptype G(cap * h + m_gmin);
+ return { G, - G * v };
+ }
+ void restore_state() noexcept
+ {
+ // this one has no state
+ }
+ void set_parameters(nl_fptype gmin) noexcept { m_gmin = gmin; }
private:
- state_var<nl_double> m_h;
- state_var<double> m_v;
- nl_double m_gmin;
+ nl_fptype m_gmin;
};
+#else
+ // Constant model for constant capacitor model
+ // Trapezoidal
+ // "Circuit simulation", page 278
+ struct generic_capacitor_const
+ {
+ public:
+ generic_capacitor_const([[maybe_unused]] core_device_t &dev, [[maybe_unused]] const pstring &name)
+ : m_gmin(nlconst::zero())
+ , m_vn(0)
+ , m_in(0)
+ , m_trn(0.0)
+ {
+ }
+ // Returns { G, Ieq }
+ std::pair<nl_fptype, nl_fptype> time_step(nl_fptype cap, nl_fptype v, nl_fptype step) noexcept
+ {
+ const nl_fptype h(plib::reciprocal(step));
+ if (m_trn == 0.0)
+ {
+ const nl_fptype G(cap * h + m_gmin);
+ m_vn = v;
+ m_trn = h;
+ return { G, - G * v };
+ }
+ const nl_fptype Gn = nlconst::two() * cap * m_trn;
+ const nl_fptype inp1 = Gn * v - (m_in + Gn * m_vn);
+ const nl_fptype G(nlconst::two() * cap * h);
+ const nl_fptype Ieq(inp1 + G * v);
+ m_in = inp1;
+ m_vn = v;
+ m_trn = h;
+ return { G + m_gmin, -Ieq };
+ }
+ void restore_state() noexcept
+ {
+ // this one has no state
+ }
+ void set_parameters(nl_fptype gmin) noexcept { m_gmin = gmin; }
+ private:
+ nl_fptype m_gmin;
+ nl_fptype m_vn;
+ nl_fptype m_in;
+ nl_fptype m_trn;
+ };
+#endif
// -----------------------------------------------------------------------------
// A generic diode model to be used in other devices (Diode, BJT ...)
// -----------------------------------------------------------------------------
@@ -132,39 +216,58 @@ namespace analog
class generic_diode
{
public:
- 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)
- , m_name(name)
+ generic_diode()
+ : m_Vd(nlconst::diode_start_voltage())
+ , m_Id(nlconst::zero())
+ , m_G(nlconst::cgminalt())
+ , m_Vt(nlconst::zero())
+ , m_Vmin(nlconst::zero()) // not used in MOS model
+ , m_Is(nlconst::zero())
+ , m_logIs(nlconst::zero())
+ , m_gmin(nlconst::cgminalt())
+ , m_VtInv(nlconst::zero())
+ , m_Vcrit(nlconst::zero())
{
- set_param(1e-15, 1, 1e-15, 300.0);
+ set_param(
+ nlconst::np_Is()
+ , nlconst::one()
+ , nlconst::cgminalt()
+ , nlconst::T0());
}
- void update_diode(const nl_double nVd)
+ generic_diode(core_device_t &dev, const pstring &name)
+ : generic_diode()
+ {
+ dev.state().save(dev, m_Vd, dev.name(), name + ".m_Vd");
+ dev.state().save(dev, m_Id, dev.name(), name + ".m_Id");
+ dev.state().save(dev, m_G, dev.name(), name + ".m_G");
+ }
+ // Basic math
+ //
+ // I(V) = f(V)
+ //
+ // G(V) = df/dV(V)
+ //
+ // Ieq(V) = I(V) - V * G(V)
+ //
+ //
+ void update_diode(nl_fptype nVd) noexcept
{
- nl_double IseVDVt(0.0);
-
if (TYPE == diode_e::BIPOLAR)
{
- //printf("%s: %g %g\n", m_name.c_str(), nVd, (double) m_Vd);
+#if USE_TEXTBOOK_DIODE
if (nVd > m_Vcrit)
{
- const nl_double d = std::min(1e100, nVd - m_Vd);
- const nl_double a = std::abs(d) * m_VtInv;
- m_Vd = m_Vd + (d < 0 ? -1.0 : 1.0) * std::log1p(a) * m_Vt;
+ // if the old voltage is less than zero and new is above
+ // make sure we move enough so that matrix and current
+ // changes.
+ const nl_fptype old = std::max(nlconst::zero(), m_Vd);
+ const nl_fptype d = std::min(+fp_constants<nl_fptype>::DIODE_MAXDIFF(), nVd - old);
+ const nl_fptype a = plib::abs(d) * m_VtInv;
+ m_Vd = old + plib::signum(d) * plib::log1p(a) * m_Vt;
}
else
- m_Vd = std::max(-1e100, nVd);
- //m_Vd = nVd;
+ m_Vd = std::max(-fp_constants<nl_fptype>::DIODE_MAXDIFF(), nVd);
if (m_Vd < m_Vmin)
{
@@ -173,73 +276,108 @@ namespace analog
}
else
{
- IseVDVt = std::exp(m_logIs + m_Vd * m_VtInv);
+ const auto IseVDVt = plib::exp(m_logIs + m_Vd * m_VtInv);
m_Id = IseVDVt - m_Is;
m_G = IseVDVt * m_VtInv + m_gmin;
}
+#else
+ //printf("%s: %g %g\n", m_name.c_str(), nVd, (nl_fptype) m_Vd);
+ m_Vd = nVd;
+ if (nVd > m_Vcrit)
+ {
+ m_Id = m_Icrit_p_Is - m_Is + (m_Vd - m_Vcrit) * m_Icrit_p_Is * m_VtInv;
+ m_G = m_Icrit_p_Is * m_VtInv + m_gmin;
+ }
+ else if (m_Vd < m_Vmin)
+ {
+ m_G = m_gmin;
+ //m_Id = m_Imin + (m_Vd - m_Vmin) * m_gmin;
+ //m_Imin = m_gmin * m_Vt - m_Is;
+ m_Id = (m_Vd - m_Vmin + m_Vt) * m_gmin - m_Is;
+ }
+ else
+ {
+ const auto IseVDVt = plib::exp(m_logIs + m_Vd * m_VtInv);
+ m_Id = IseVDVt - m_Is;
+ m_G = IseVDVt * m_VtInv + m_gmin;
+ }
+#endif
}
else if (TYPE == diode_e::MOS)
{
- if (nVd < constants::zero())
+ m_Vd = nVd;
+ if (nVd < nlconst::zero())
{
- m_Vd = nVd;
m_G = m_Is * m_VtInv + m_gmin;
m_Id = m_G * m_Vd;
}
- else /* log stepping should already be done in mosfet */
+ else // log stepping should already be done in mosfet
{
- m_Vd = nVd;
- IseVDVt = std::exp(std::min(300.0, m_logIs + m_Vd * m_VtInv));
+ const auto IseVDVt = plib::exp(std::min(+fp_constants<nl_fptype>::DIODE_MAXVOLT(), m_logIs + m_Vd * m_VtInv));
m_Id = IseVDVt - m_Is;
m_G = IseVDVt * m_VtInv + m_gmin;
}
}
}
- void set_param(const nl_double Is, const nl_double n, nl_double gmin, nl_double temp)
+ void set_param(nl_fptype Is, nl_fptype n, nl_fptype gmin, nl_fptype temp) noexcept
{
m_Is = Is;
- m_logIs = std::log(Is);
- m_n = n;
+ m_logIs = plib::log(Is);
m_gmin = gmin;
- m_Vt = m_n * temp * constants::k_b() / constants::Q_e();
-
- m_Vmin = -5.0 * m_Vt;
+ m_Vt = nlconst::np_VT(n, temp);
+ m_VtInv = plib::reciprocal(m_Vt);
+
+#if USE_TEXTBOOK_DIODE
+ m_Vmin = nlconst::diode_min_cutoff_mult() * m_Vt;
+ // Vcrit : f(V) has smallest radius of curvature rho(V) == min(rho(v))
+ m_Vcrit = m_Vt * plib::log(m_Vt / m_Is / nlconst::sqrt2());
+#else
+ m_Vmin = plib::log(m_gmin * m_Vt / m_Is) * m_Vt;
+ //m_Imin = plib::exp(m_logIs + m_Vmin * m_VtInv) - m_Is;
+ //m_Imin = m_gmin * m_Vt - m_Is;
+ // Fixme: calculate max dissipation voltage - use use 0.5 (500mW) here for typical diode
+ // P = V * I = V * (Is*exp(V/Vt) - Is)
+ // P ~= V * I = V * Is*exp(V/Vt)
+ // ln(P/Is) = ln(V)+V/Vt ~= V - 1 + V/vt
+ // V = (1+ln(P/Is))/(1 + 1/Vt)
+
+ m_Vcrit = (nlconst::one() + plib::log(nlconst::half() / m_Is)) / (nlconst::one() + m_VtInv);
+ //printf("Vcrit: %f\n", m_Vcrit);
+ m_Icrit_p_Is = plib::exp(m_logIs + m_Vcrit * m_VtInv);
+ //m_Icrit = plib::exp(m_logIs + m_Vcrit * m_VtInv) - m_Is;
+#endif
- 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);
}
+ nl_fptype I() const noexcept { return m_Id; }
+ nl_fptype G() const noexcept { return m_G; }
+ nl_fptype Ieq() const noexcept { return (m_Id - m_Vd * m_G); }
+ nl_fptype Vd() const noexcept { 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 ... */
+ // owning object must save those ...
private:
- 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;
-
- pstring m_name;
+ nl_fptype m_Vd;
+ nl_fptype m_Id;
+ nl_fptype m_G;
+
+ nl_fptype m_Vt;
+ nl_fptype m_Vmin;
+ nl_fptype m_Is;
+ nl_fptype m_logIs;
+ nl_fptype m_gmin;
+
+ nl_fptype m_VtInv;
+ nl_fptype m_Vcrit;
+#if !USE_TEXTBOOK_DIODE
+ //nl_fptype m_Imin;
+ nl_fptype m_Icrit_p_Is;
+#endif
};
-} // namespace analog
-} // namespace netlist
+} // namespace netlist::analog
-#endif /* NLD_GENERIC_MODELS_H_ */
+#endif // NLD_GENERIC_MODELS_H_
diff --git a/src/lib/netlist/analog/nld_mosfet.cpp b/src/lib/netlist/analog/nld_mosfet.cpp
index c66cec291f6..63911ceb02f 100644
--- a/src/lib/netlist/analog/nld_mosfet.cpp
+++ b/src/lib/netlist/analog/nld_mosfet.cpp
@@ -1,193 +1,168 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// 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"
+
+// Names
+// spell-checker: words Farid, Naim, Jahn, Margraf, Habchi, Raimund, Qucs
+//
+// Specific technical terms
+// spell-checker: words Cgso, Cgdo, Cgbo, Cjsw, Mjsw, Ucrit, Uexp, Utra, Neff, Tnom, capval, Udsat, Utst
+
+///
+/// \file 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 "../nl_setup.h"
#include "nlid_twoterm.h"
-#include <cmath>
+#include "solver/nld_solver.h"
-#define BODY_CONNECTED_TO_SOURCE (1)
+#define BODY_CONNECTED_TO_SOURCE (1)
-namespace netlist
-{
-namespace analog
+namespace netlist::analog
{
- using constants = plib::constants<nl_double>;
-
- // -----------------------------------------------------------------------------
- // nld_FET - Base classes
- // -----------------------------------------------------------------------------
-
- /*! Class representing the nmos/pmos model paramers.
- *
- * 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 |
- * |:---:|------|-----------------------------------------------------------------------|-------|---------:|----------------:|
- * | 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|
- * | Y |Cgso | Gate-source overlap capacitance per meter channel width |F/m|0|0.00000000004|
- * | Y |Cgdo | Gate-drain overlap capacitance per meter channel width |F/m|0|0.00000000004|*
- * | Y |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 |-|100e-6||
- * | Y |W | Width scaling |-|100e-6||
- * */
-
- class fet_model_t : public param_model_t
+ using constants = plib::constants<nl_fptype>;
+
+ /// \brief Class representing the nmos/pmos model parameters.
+ ///
+ /// 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 |
+ /// |:---:|------|-----------------------------------------------------------------------|-------|---------:|----------------:|
+ /// | 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|
+ /// | Y |Cgso | Gate-source overlap capacitance per meter channel width |F/m|0|0.00000000004|
+ /// | Y |Cgdo | Gate-drain overlap capacitance per meter channel width |F/m|0|0.00000000004*|
+ /// | Y |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 |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 |-|100e-6||
+ /// | Y |W | Width scaling |-|100e-6||
+ ///
+
+ class fet_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")
- , m_CGSO(*this, "CGSO")
- , m_CGDO(*this, "CGDO")
- , m_CGBO(*this, "CGBO")
- , m_CAPMOD(*this, "CAPMOD")
- {}
-
- 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
- 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
-
- NETLIB_OBJECT(FET)
- {
- public:
- enum q_type {
- FET_NMOS,
- FET_PMOS
- };
-
- NETLIB_CONSTRUCTOR(FET)
- , m_model(*this, "MODEL", "NMOS")
- , m_qtype(FET_NMOS)
+ fet_model_t(param_model_t &model)
+ : m_VTO(model, "VTO")
+ , m_N(model, "N")
+ , m_ISS(model, "IS") // Haven't seen a model using ISS / ISD
+ , m_ISD(model, "IS")
+ , m_LD(model, "LD")
+ , m_L(model, "L")
+ , m_W(model, "W")
+ , m_TOX(model, "TOX")
+ , m_KP(model, "KP")
+ , m_UO(model, "UO")
+ , m_PHI(model, "PHI")
+ , m_NSUB(model, "NSUB")
+ , m_GAMMA(model, "GAMMA")
+ , m_LAMBDA(model, "LAMBDA")
+ , m_RD(model, "RD")
+ , m_RS(model, "RS")
+ , m_CGSO(model, "CGSO")
+ , m_CGDO(model, "CGDO")
+ , m_CGBO(model, "CGBO")
+ , m_CAPMOD(model, "CAPMOD")
{
}
- 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;
+ param_model_t::value_t m_VTO; //!< Threshold voltage [V]
+ param_model_t::value_t m_N; //!< Bulk diode emission coefficient
+ param_model_t::value_t m_ISS; //!< Body diode saturation current
+ param_model_t::value_t m_ISD; //!< Body diode saturation current
+ param_model_t::value_t m_LD; //!< Lateral diffusion [m]
+ param_model_t::value_t m_L; //!< Length scaling
+ param_model_t::value_t m_W; //!< Width scaling
+ param_model_t::value_t m_TOX; //!< Oxide thickness
+ param_model_t::value_t m_KP; //!< Transconductance parameter [A/V²]
+ param_model_t::value_t m_UO; //!< Surface mobility [cm²/V/s]
+ param_model_t::value_t m_PHI; //!< Surface inversion potential [V]
+ param_model_t::value_t m_NSUB; //!< Substrate doping [1/cm³]
+ param_model_t::value_t m_GAMMA; //!< Bulk threshold parameter [V^½]
+ param_model_t::value_t m_LAMBDA; //!< Channel-length modulation [1/V]
+ param_model_t::value_t m_RD; //!< Drain ohmic resistance
+ param_model_t::value_t m_RS; //!< Source ohmic resistance
+ param_model_t::value_t m_CGSO; //!< Gate-source overlap capacitance per
+ //!< meter channel width
+ param_model_t::value_t m_CGDO; //!< Gate-drain overlap capacitance per
+ //!< meter channel width
+ param_model_t::value_t m_CGBO; //!< Gate-bulk overlap capacitance per
+ //!< meter channel width
+ param_model_t::value_base_t<int> m_CAPMOD; //!< Capacitance model (0=no
+ //!< model 2=Meyer)
};
// -----------------------------------------------------------------------------
- // nld_QBJT_EB
+ // nld_MOSFET
// -----------------------------------------------------------------------------
- NETLIB_OBJECT_DERIVED(MOSFET, FET)
+ class nld_MOSFET : public base_device_t
{
public:
- NETLIB_CONSTRUCTOR_DERIVED(MOSFET, FET)
- , m_DG(*this, "m_DG", true)
- , m_SG(*this, "m_SG", true)
- , m_SD(*this, "m_SD", true)
+ nld_MOSFET(constructor_param_t data)
+ : base_device_t(data)
+ , m_model(*this, "MODEL", "NMOS")
+ , m_DG(*this, "m_DG", NETLIB_DELEGATE(terminal_handler))
+ , m_SG(*this, "m_SG", NETLIB_DELEGATE(terminal_handler))
+ , m_SD(*this, "m_SD", NETLIB_DELEGATE(terminal_handler))
, m_D_BD(*this, "m_D_BD")
#if (!BODY_CONNECTED_TO_SOURCE)
, m_D_BS(*this, "m_D_BS")
@@ -195,144 +170,183 @@ namespace analog
, m_cap_gb(*this, "m_cap_gb")
, m_cap_gs(*this, "m_cap_gs")
, m_cap_gd(*this, "m_cap_gd")
- , 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_CoxWL(0.0)
- , m_polarity(qtype() == FET_NMOS ? 1.0 : -1.0)
- , 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
+ , m_phi(nlconst::zero())
+ , m_gamma(nlconst::zero())
+ , m_vto(nlconst::zero())
+ , m_beta(nlconst::zero())
+ , m_lambda(nlconst::zero())
+ , m_Leff(nlconst::zero())
+ , m_CoxWL(nlconst::zero())
+ // S, m_polarity(qtype() == FET_NMOS ? nlconst::one() : -nlconst::one())
+ , m_Cgb(nlconst::zero())
+ , m_Cgs(nlconst::zero())
+ , m_Cgd(nlconst::zero())
+ , m_capacitor_model(2)
+ , m_Vgs(*this, "m_Vgs", nlconst::zero())
+ , m_Vgd(*this, "m_Vgd", nlconst::zero())
+ , m_model_acc(m_model)
+ {
+ register_sub_alias("S", m_SG.P()); // Source
+ register_sub_alias("G", m_SG.N()); // Gate
- register_subalias("D", m_DG.m_P); // Drain
+ register_sub_alias("D", m_DG.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);
+ connect(m_SG.P(), m_SD.P());
+ connect(m_SG.N(), m_DG.N());
+ connect(m_DG.P(), m_SD.N());
- set_qtype((m_model.type() == "NMOS_DEFAULT") ? FET_NMOS : FET_PMOS);
- m_polarity = qtype() == FET_NMOS ? 1.0 : -1.0;
+ m_polarity = (m_model.type() == "NMOS_DEFAULT" ? nlconst::one()
+ : -nlconst::one());
- 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());
+ m_capacitor_model = m_model_acc.m_CAPMOD;
+ //# printf("capmod %d %g %g\n", m_capacitor_model, (nl_fptype)m_model_acc.m_VTO, m_polarity);
+ nl_assert_always(m_capacitor_model == 0 || m_capacitor_model == 2,
+ "Error: CAPMODEL invalid value");
- /*
- * 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
- *
- */
+ //
+ // 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
+ m_lambda = m_model_acc.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());
+ m_Leff = m_model_acc.m_L - 2 * m_model_acc.m_LD;
+ nl_assert_always(
+ m_Leff > nlconst::zero(),
+ "Effective Lateral diffusion would be negative for model");
- nl_double Cox = (m_model.m_TOX > 0.0) ? (constants::eps_SiO2() * constants::eps_0() / m_model.m_TOX) : 0.0;
+ nl_fptype Cox = (m_model_acc.m_TOX > nlconst::zero())
+ ? (constants::eps_SiO2() * constants::eps_0()
+ / m_model_acc.m_TOX)
+ : nlconst::zero();
// calculate DC transconductance coefficient
- if (m_model.m_KP > 0)
- m_beta = m_model.m_KP * m_model.m_W / m_Leff;
- else if (Cox > 0 && m_model.m_UO > 0)
- m_beta = m_model.m_UO * 1e-4 * Cox * m_model.m_W / m_Leff;
+ if (m_model_acc.m_KP > nlconst::zero())
+ m_beta = m_model_acc.m_KP * m_model_acc.m_W / m_Leff;
+ else if (Cox > nlconst::zero()
+ && m_model_acc.m_UO > nlconst::zero())
+ m_beta = m_model_acc.m_UO * nlconst::magic(1e-4) * Cox
+ * m_model_acc.m_W / m_Leff;
else
- m_beta = 2e-5 * m_model.m_W / m_Leff;
+ m_beta = nlconst::magic(2e-5) * m_model_acc.m_W / m_Leff;
- //FIXME::UT can disappear
- const double Vt = constants::T0() * constants::k_b() / constants::Q_e();
+ // FIXME::UT can disappear
+ const nl_fptype 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)
+ if (m_model_acc.m_PHI > nlconst::zero())
+ m_phi = m_model_acc.m_PHI;
+ else if (m_model_acc.m_NSUB > nlconst::zero())
{
- 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());
+ nl_assert_always(m_model_acc.m_NSUB * nlconst::magic(1e6)
+ >= constants::NiSi(),
+ "Error calculating phi for model");
+ m_phi = nlconst::two() * Vt
+ * plib::log(m_model_acc.m_NSUB * nlconst::magic(1e6)
+ / constants::NiSi());
}
else
- m_phi = 0.6;
+ m_phi = nlconst::magic(0.6);
// calculate bulk threshold if not given
- if (m_model.m_GAMMA > 0.0)
- m_gamma = m_model.m_GAMMA;
+ if (m_model_acc.m_GAMMA > nlconst::zero())
+ m_gamma = m_model_acc.m_GAMMA;
else
{
- if (Cox > 0.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) / Cox;
+ if (Cox > nlconst::zero()
+ && m_model_acc.m_NSUB > nlconst::zero())
+ m_gamma = plib::sqrt(
+ nlconst::two() * constants::Q_e()
+ * constants::eps_Si() * constants::eps_0()
+ * m_model_acc.m_NSUB * nlconst::magic(1e6))
+ / Cox;
else
- m_gamma = 0.0;
+ m_gamma = nlconst::zero();
}
- m_vto = m_model.m_VTO;
- if(m_vto != 0.0)
+ m_vto = m_model_acc.m_VTO;
+ // FIXME zero conversion
+ if (m_vto == nlconst::zero())
log().warning(MW_MOSFET_THRESHOLD_VOLTAGE(m_model.name()));
- /* FIXME: VTO if missing may be calculated from TPG, NSS and temperature. Usually models
- * specify VTO so skip this here.
- */
+ // FIXME: VTO if missing may be calculated from TPG, NSS and
+ // temperature. Usually models specify VTO so skip this here.
- m_CoxWL = Cox * m_model.m_W * m_Leff;
+ m_CoxWL = Cox * m_model_acc.m_W * m_Leff;
- //printf("Cox: %g\n", m_Cox);
+ //#printf("Cox: %g\n", m_Cox);
}
- NETLIB_IS_TIMESTEP(true || m_capmod != 0)
+ NETLIB_IS_DYNAMIC(true)
+ NETLIB_IS_TIMESTEP(true || m_capacitor_model != 0)
NETLIB_TIMESTEPI()
{
- if (m_capmod != 0)
+ if (m_capacitor_model != 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);
+ if (ts_type == detail::time_step_type::FORWARD)
+ {
+ //#const nl_nl_fptype Ugd = -m_DG.deltaV() * m_polarity; // Gate - Drain
+ //#const nl_nl_fptype Ugs = -m_SG.deltaV() * m_polarity; // Gate - Source
+ const nl_fptype Ugd = m_Vgd; // Gate - Drain
+ const nl_fptype Ugs = m_Vgs; // Gate - Source
+ const nl_fptype Ubs = nlconst::zero(); // Bulk - Source == 0
+ // if connected
+ const nl_fptype Ugb = Ugs - Ubs;
+
+ m_cap_gb.time_step(m_Cgb, Ugb, step);
+ m_cap_gs.time_step(m_Cgs, Ugs, step);
+ m_cap_gd.time_step(m_Cgd, Ugd, step);
+ }
+ else
+ {
+ m_cap_gb.restore_state();
+ m_cap_gs.restore_state();
+ m_cap_gd.restore_state();
+ }
}
}
protected:
-
NETLIB_RESETI()
{
- NETLIB_NAME(FET)::reset();
// 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
+ m_D_BD.set_param(m_model_acc.m_ISD, m_model_acc.m_N, exec().gmin(),
+ constants::T0());
+#if (!BODY_CONNECTED_TO_SOURCE)
+ m_D_BS.set_param(m_model_acc.m_ISS, m_model_acc.m_N, exec().gmin(),
+ constants::T0());
+#endif
}
- NETLIB_UPDATEI();
+ NETLIB_HANDLERI(terminal_handler)
+ {
+ // only called if connected to a rail net ==> notify the solver to
+ // recalculate
+ auto *solv(m_SG.solver());
+ if (solv != nullptr)
+ solv->solve_now();
+ else
+ m_DG.solver()->solve_now();
+ }
NETLIB_UPDATE_PARAMI();
NETLIB_UPDATE_TERMINALSI();
private:
+ param_model_t m_model;
- nld_twoterm m_DG;
- nld_twoterm m_SG;
- nld_twoterm m_SD;
+ NETLIB_NAME(two_terminal) m_DG;
+ NETLIB_NAME(two_terminal) m_SG;
+ NETLIB_NAME(two_terminal) m_SD;
generic_diode<diode_e::MOS> m_D_BD;
#if (!BODY_CONNECTED_TO_SOURCE)
@@ -343,83 +357,94 @@ 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;
- nl_double m_beta;
- nl_double m_lambda;
+ nl_fptype m_phi;
+ nl_fptype m_gamma;
+ nl_fptype m_vto;
+ nl_fptype m_beta;
+ nl_fptype m_lambda;
- /* used in capacitance calculation */
- nl_double m_Leff;
- nl_double m_CoxWL;
- nl_double m_polarity;
+ // used in capacitance calculation
+ nl_fptype m_Leff;
+ nl_fptype m_CoxWL;
+ nl_fptype m_polarity;
- /* capacitance values */
+ // capacitance values
- nl_double m_Cgb;
- nl_double m_Cgs;
- nl_double m_Cgd;
+ nl_fptype m_Cgb;
+ nl_fptype m_Cgs;
+ nl_fptype m_Cgd;
- int m_capmod;
- state_var<nl_double> m_Vgs;
- state_var<nl_double> m_Vgd;
+ int m_capacitor_model;
+ state_var<nl_fptype> m_Vgs;
+ state_var<nl_fptype> m_Vgd;
+ fet_model_t m_model_acc;
- 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,
- nl_double &I1, nl_double &I2)
+ void set_cap(generic_capacitor<capacitor_e::VARIABLE_CAPACITY> &cap,
+ nl_fptype capval, nl_fptype V, nl_fptype &g11,
+ nl_fptype &g12, nl_fptype &g21, nl_fptype &g22,
+ nl_fptype &I1, nl_fptype &I2) const
{
- const nl_double I = cap.Ieq(capval, V) * m_polarity;
- const nl_double G = cap.G(capval);
- g11 += G; g12 -= G; g21 -= G; g22 += G;
- I1 -= I; I2 += I;
- //printf("Cap: %g\n", capval);
+ const nl_fptype I = cap.Ieq(capval, V) * m_polarity;
+ const nl_fptype G = cap.G(capval);
+ g11 += G;
+ g12 -= G;
+ g21 -= G;
+ g22 += G;
+ I1 -= I;
+ I2 += I;
+ // printf("Cap: %g\n", capval);
}
- void calculate_caps(nl_double Vgs, nl_double Vgd, nl_double Vth,
- nl_double &Cgs, nl_double &Cgd, nl_double &Cgb)
+ void
+ calculate_caps(nl_fptype Vgs, nl_fptype Vgd, nl_fptype Vth,
+ nl_fptype &Cgs, nl_fptype &Cgd, nl_fptype &Cgb) const
{
- nl_double Vctrl = Vgs - Vth * m_polarity;
+ nl_fptype Vctrl = Vgs - Vth * m_polarity;
// Cut off - now further differentiated into 3 different formulas
// Accumulation
if (Vctrl <= -m_phi)
{
Cgb = m_CoxWL;
- Cgs = 0.0;
- Cgd = 0.0;
+ Cgs = nlconst::zero();
+ Cgd = nlconst::zero();
}
- else if (Vctrl <= -m_phi / 2.0)
+ else if (Vctrl <= -m_phi / nlconst::two())
{
Cgb = -Vctrl * m_CoxWL / m_phi;
- Cgs = 0.0;
- Cgd = 0.0;
+ Cgs = nlconst::zero();
+ Cgd = nlconst::zero();
}
// Depletion
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;
- Cgd = 0.0;
+ Cgs = Vctrl * m_CoxWL * nlconst::fraction(4.0, 3.0) / m_phi
+ + nlconst::two_thirds() * m_CoxWL;
+ Cgd = nlconst::zero();
}
else
{
- const nl_double Vdsat = Vctrl;
- const nl_double Vds = Vgs - Vgd;
+ const nl_fptype Vdsat = Vctrl;
+ const nl_fptype Vds = Vgs - Vgd;
// saturation
if (Vdsat <= Vds)
{
- Cgb = 0;
- Cgs = (2.0 / 3.0) * m_CoxWL;
- Cgd = 0;
+ Cgb = nlconst::zero();
+ Cgs = nlconst::two_thirds() * m_CoxWL;
+ Cgd = nlconst::zero();
}
else
{
// linear
- const nl_double Sqr1 = std::pow(Vdsat - Vds, 2);
- const nl_double Sqr2 = std::pow(2.0 * Vdsat - Vds, 2);
+ const auto Sqr1(plib::narrow_cast<nl_fptype>(
+ plib::pow(Vdsat - Vds, 2)));
+ const auto Sqr2(plib::narrow_cast<nl_fptype>(
+ plib::pow(nlconst::two() * Vdsat - Vds, 2)));
Cgb = 0;
- Cgs = m_CoxWL * (1.0 - Sqr1 / Sqr2) * (2.0 / 3.0);
- Cgd = m_CoxWL * (1.0 - Vdsat * Vdsat / Sqr2) * (2.0 / 3.0);
+ Cgs = m_CoxWL * (nlconst::one() - Sqr1 / Sqr2)
+ * nlconst::two_thirds();
+ Cgd = m_CoxWL * (nlconst::one() - Vdsat * Vdsat / Sqr2)
+ * nlconst::two_thirds();
}
}
}
@@ -429,36 +454,33 @@ namespace analog
// MOSFET
// ----------------------------------------------------------------------------------------
- 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_UPDATE_TERMINALS(MOSFET)
{
- nl_double Vgd = -m_DG.deltaV() * m_polarity; // Gate - Drain
- nl_double Vgs = -m_SG.deltaV() * m_polarity; // Gate - Source
+ nl_fptype Vgd = -m_DG.deltaV() * m_polarity; // Gate - Drain
+ nl_fptype 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));
+ const nl_fptype k = nlconst::magic(3.5); // see "Circuit Simulation",
+ // page 185
+ nl_fptype d = (Vgs - m_Vgs);
+ Vgs = m_Vgs
+ + plib::reciprocal(k) * plib::signum(d)
+ * plib::log1p(k * plib::abs(d));
d = (Vgd - m_Vgd);
- Vgd = m_Vgd + 1.0/k * (d < 0 ? -1.0 : 1.0) * std::log1p(k * std::abs(d));
+ Vgd = m_Vgd
+ + plib::reciprocal(k) * plib::signum(d)
+ * plib::log1p(k * plib::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 Vds = Vgs - Vgd;
- const nl_double Vbd = -Vds; // Bulk - Drain = Source - Drain
+ const nl_fptype Vbs = nlconst::zero(); // Bulk - Source == 0 if
+ // connected
+ // const nl_nl_fptype Vbd = m_SD.deltaV() * m_polarity; // Bulk - Drain
+ // = Source - Drain
+ const nl_fptype Vds = Vgs - Vgd;
+ const nl_fptype Vbd = -Vds; // Bulk - Drain = Source - Drain
#if (!BODY_CONNECTED_TO_SOURCE)
m_D_BS.update_diode(Vbs);
@@ -467,135 +489,176 @@ namespace analog
// Are we in forward mode ?
// in backward mode, just swap source and drain
- const bool is_forward = Vds >= 0;
+ const bool is_forward = Vds >= nlconst::zero();
// calculate Vth
- const nl_double Vbulk = is_forward ? Vbs : Vbd;
- const nl_double phi_m_Vbulk = (m_phi > Vbulk) ? std::sqrt(m_phi - Vbulk) : 0.0;
- const nl_double Vth = m_vto * m_polarity + m_gamma * (phi_m_Vbulk - std::sqrt(m_phi));
+ const nl_fptype Vbulk = is_forward ? Vbs : Vbd;
+ const nl_fptype phi_m_Vbulk = (m_phi > Vbulk)
+ ? plib::sqrt(m_phi - Vbulk)
+ : nlconst::zero();
+ const nl_fptype Vth = m_vto * m_polarity
+ + m_gamma * (phi_m_Vbulk - plib::sqrt(m_phi));
+
+ const nl_fptype Vctrl = (is_forward ? Vgs : Vgd) - Vth;
- const nl_double Vctrl = (is_forward ? Vgs : Vgd) - Vth;
+ nl_fptype Ids(0);
+ nl_fptype gm(0);
+ nl_fptype gds(0);
+ nl_fptype gmb(0);
- nl_double Ids, gm, gds, gmb;
- const nl_double absVds = std::abs(Vds);
+ const nl_fptype absVds = plib::abs(Vds);
- if (Vctrl <= 0.0)
+ if (Vctrl <= nlconst::zero())
{
// cutoff region
- Ids = 0.0;
- gm = 0.0;
- gds = 0.0;
- gmb = 0.0;
+ Ids = nlconst::zero();
+ gm = nlconst::zero();
+ gds = nlconst::zero();
+ gmb = nlconst::zero();
}
else
{
- const nl_double beta = m_beta * (1.0 + m_lambda * absVds);
+ const nl_fptype beta = m_beta
+ * (nlconst::one() + m_lambda * absVds);
if (Vctrl <= absVds)
{
// saturation region
- Ids = beta * Vctrl * Vctrl / 2.0;
- gm = beta * Vctrl;
- gds = m_lambda * m_beta * Vctrl * Vctrl / 2.0;
+ Ids = beta * Vctrl * Vctrl / nlconst::two();
+ gm = beta * Vctrl;
+ gds = m_lambda * m_beta * Vctrl * Vctrl / nlconst::two();
}
else
{
// linear region
- Ids = beta * absVds * (Vctrl - absVds / 2);
- gm = beta * absVds;
- gds = beta * (Vctrl - absVds) + m_lambda * m_beta * absVds * (Vctrl - absVds / 2.0);
+ Ids = beta * absVds * (Vctrl - absVds / nlconst::two());
+ gm = beta * absVds;
+ gds = beta * (Vctrl - absVds)
+ + m_lambda * m_beta * absVds
+ * (Vctrl - absVds / nlconst::two());
}
- // backgate transconductance
- const nl_double bgtc = (phi_m_Vbulk != 0.0) ? (m_gamma / phi_m_Vbulk / 2.0) : 0.0;
+ // back gate transconductance
+ const nl_fptype bgtc = (phi_m_Vbulk != nlconst::zero())
+ ? (m_gamma / phi_m_Vbulk
+ / nlconst::two())
+ : nlconst::zero();
gmb = gm * bgtc;
}
// FIXME: these are needed to compute capacitance
- // nl_double Udsat = pol * std::max (Utst, 0.0);
+ // nl_fptype 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();
+ const nl_fptype IeqBD = m_D_BD.Ieq();
+ const nl_fptype 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();
+ const nl_fptype IeqBS = m_D_BS.Ieq();
+ const nl_fptype gbs = m_D_BS.G();
#else
- const nl_double IeqBS = 0.0;
- const nl_double gbs = 0.0;
+ const nl_fptype IeqBS = nlconst::zero();
+ const nl_fptype gbs = nlconst::zero();
#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_fptype gate_source = is_forward ? (gm + gmb) : nlconst::zero();
+ const nl_fptype gate_drain = is_forward ? nlconst::zero() : (gm + gmb);
- const nl_double IeqDS = (is_forward) ?
- Ids - gm * Vgs - gmb * Vbs - gds * Vds
- : -Ids - gm * Vgd - gmb * Vbd - gds * Vds;
+ const nl_fptype IeqDS = (is_forward)
+ ? Ids - gm * Vgs - gmb * Vbs - gds * Vds
+ : -Ids - gm * Vgd - gmb * Vbd - gds * Vds;
// IG = 0
- nl_double IG = 0.0;
- nl_double ID = (+IeqBD - IeqDS) * m_polarity;
- nl_double IS = (+IeqBS + IeqDS) * m_polarity;
- nl_double IB = (-IeqBD - IeqBS) * m_polarity;
-
- nl_double gGG = 0.0;
- nl_double gGD = 0.0;
- nl_double gGS = 0.0;
- nl_double gGB = 0.0;
-
- nl_double gDG = gm;
- nl_double gDD = gds + gbd - gdrain;
- const nl_double gDS = -gds - gsource;
- const nl_double gDB = gmb - gbd;
-
- nl_double gSG = -gm;
- const nl_double gSD = -gds + gdrain;
- nl_double gSS = gbs + gds + gsource;
- const nl_double gSB = -gbs - gmb;
-
- nl_double gBG = 0.0;
- const nl_double gBD = -gbd;
- const nl_double gBS = -gbs;
- nl_double gBB = gbs + gbd;
-
- if (m_capmod != 0)
+ nl_fptype IG = nlconst::zero();
+ nl_fptype ID = (+IeqBD - IeqDS) * m_polarity;
+ nl_fptype IS = (+IeqBS + IeqDS) * m_polarity;
+ nl_fptype IB = (-IeqBD - IeqBS) * m_polarity;
+
+ nl_fptype gGG = nlconst::zero();
+ nl_fptype gGD = nlconst::zero();
+ nl_fptype gGS = nlconst::zero();
+ nl_fptype gGB = nlconst::zero();
+
+ nl_fptype gDG = gm;
+ nl_fptype gDD = gds + gbd - gate_drain;
+ const nl_fptype gDS = -gds - gate_source;
+ const nl_fptype gDB = gmb - gbd;
+
+ nl_fptype gSG = -gm;
+ const nl_fptype gSD = -gds + gate_drain;
+ nl_fptype gSS = gbs + gds + gate_source;
+ const nl_fptype gSB = -gbs - gmb;
+
+ nl_fptype gBG = nlconst::zero();
+ const nl_fptype gBD = -gbd;
+ const nl_fptype gBS = -gbs;
+ nl_fptype gBB = gbs + gbd;
+
+ if (m_capacitor_model != 0)
{
- const nl_double Vgb = Vgs - Vbs;
+ const nl_fptype 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);
- 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_acc.m_CGBO * m_Leff, Vgb, gGG,
+ gGB, gBG, gBB, IG, IB);
+ set_cap(m_cap_gs, m_Cgs + m_model_acc.m_CGSO * m_model_acc.m_W, Vgs,
+ gGG, gGS, gSG, gSS, IG, IS);
+ set_cap(m_cap_gd, m_Cgd + m_model_acc.m_CGDO * m_model_acc.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;
-
+ const nl_fptype gSSBB = gSS + gBB + gBS + gSB;
+ const auto zero(nlconst::zero());
// S G
- m_SG.set_mat( gSSBB, gSG + gBG, +(IS + IB), // S
- gGS + gGB, gGG, IG ); // 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
+ m_DG.set_mat(gDD, gDG, +ID, // D
+ gGD, zero, zero); // G
// S D
- m_SD.set_mat( 0.0, gSD + gBD, 0.0, // S
- gDS + gDB, 0.0, 0.0); // D
+ m_SD.set_mat(zero, gSD + gBD, zero, // S
+ gDS + gDB, zero, zero); // D
+
+ /// |
+ /// | D S G I
+ /// |
+ /// | D gDD gDS + gDB gDG ID
+ ///
+ /// | S gSD + gBD gSSBB gSG + gBG IS+IB
+ ///
+ /// | G gGD gGS + gGB gGG IG
+ /// |
+ /// | forward=yes, bulk diode=no, backgate transconductance=no
+ /// | IG = 0, gGG = 0, gGS, gGB =0, gGD=0l
+ /// gDD=gds + gbd=gds
+ /// gSD+gBD=-gds + gmb - gbd = -gds
+ ///
+ /// | D S G I
+ /// |
+ /// | D gDD gDS + gDB gDG ID
+ ///
+ /// | S gSD + gBD gSSBB gSG + gBG IS+IB
+ ///
+ /// | G 0 0 0 0
+ /// |
+ /// |
+ /// |
+ /// |
+ /// |
+ /// |
}
- NETLIB_UPDATE_PARAM(MOSFET)
- {
- }
+ NETLIB_UPDATE_PARAM(MOSFET) {}
-} // namespace analog
+} // namespace netlist::analog
-namespace devices {
+namespace netlist::devices
+{
NETLIB_DEVICE_IMPL_NS(analog, MOSFET, "MOSFET", "MODEL")
-} // namespace devices
-
-} // namespace netlist
+} // namespace netlist::devices
diff --git a/src/lib/netlist/analog/nld_mosfet.h b/src/lib/netlist/analog/nld_mosfet.h
deleted file mode 100644
index 32011d709d8..00000000000
--- a/src/lib/netlist/analog/nld_mosfet.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// 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 24f239e9455..b7e4ebfa762 100644
--- a/src/lib/netlist/analog/nld_opamps.cpp
+++ b/src/lib/netlist/analog/nld_opamps.cpp
@@ -1,128 +1,133 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * nld_opamps.c
- *
- */
-
-#include "nld_opamps.h"
-#include "netlist/nl_base.h"
-#include "netlist/nl_errstr.h"
+
+#include "nl_base.h"
+#include "nl_errstr.h"
#include "nlid_fourterm.h"
#include "nlid_twoterm.h"
-#include <cmath>
+//
+// Set to 1 to model output impedance as a series resistor.
+// The default is that the VCVS already has an internal impedance.
+// This needs more investigation.
+//
+#define TEST_ALT_OUTPUT (0)
namespace netlist
{
namespace analog
{
- /*
- * Type = 0: Impedance changer
- * 1; Idealized opamp
- * 2; opamp with first pole
- * 3: opamp with first pole + output limit
- * 4: opamp with input stage, first pole + output limit
- *
- * Type 1 parameters:
- * FPF = frequency of first pole in Hz (ony used for open-loop gain)
- * UGF = unity gain frequency in Hz (only used for open-loop gain)
- * RI = input resistance in Ohms
- * RO = output resistance in Ohms
- *
- * Type 3 parameters:
- * VLH = high supply rail minus high output swing in V
- * VLL = low output swing minus low supply rail in V
- * FPF = frequency of first pole in Hz
- * UGF = unity gain frequency (transition frequency) in Hz
- * SLEW = unity gain slew rate in V/s
- * RI = input resistance in Ohms
- * RO = output resistance in Ohms
- * DAB = Differential Amp Bias ~ op amp's total quiescent current.
- *
- * .model abc OPAMP(VLH=2.0 VLL=0.2 FPF=5 UGF=10k SLEW=0.6u RI=1000k RO=50 DAB=0.002)
- *
- * http://www.ecircuitcenter.com/Circuits/opmodel1/opmodel1.htm
- *
- * */
-
- /*! Class representing the opamp model parameters.
- * The opamp model was designed based on designs from
- * http://www.ecircuitcenter.com/Circuits/opmodel1/opmodel1.htm.
- * Currently 2 different types are supported: Type 1 and Type 3. Type 1
- * is less complex and should run faster than Type 3.
- *
- * This is an extension to the traditional SPICE approach which
- * assumes that you will be using an manufacturer model. These models may
- * have copyrights incompatible with the netlist license. Thus they may not
- * be suitable for certain implementations of netlist.
- *
- * For the typical use cases in low frequency (< 100 KHz) applications at
- * which netlist is targeted, this model is certainly suitable. All parameters
- * can be determined from a typical opamp datasheet.
- *
- * |Type|name |parameter |units|default| example|
- * |:--:|:-----|:----------------------------------------------|:----|------:|-------:|
- * | 3 |TYPE |Model Type, 1 and 3 are supported | | | |
- * |1,3 |FPF |frequency of first pole |Hz | |100 |
- * | 3 |SLEW |unity gain slew rate |V/s | | 1|
- * |1,3 |RI |input resistance |Ohm | |1M |
- * |1,3 |RO |output resistance |Ohm | |50 |
- * |1,3 |UGF |unity gain frequency (transition frequency) |Hz | |1000 |
- * | 3 |VLL |low output swing minus low supply rail |V | |1.5 |
- * | 3 |VLH |high supply rail minus high output swing |V | |1.5 |
- * | 3 |DAB |Differential Amp Bias - total quiescent current|A | |0.001 |
- */
-
- class opamp_model_t : public param_model_t
+
+ /// \brief Class representing the opamp model parameters.
+ ///
+ /// The opamp model was designed based on designs from
+ /// http://www.ecircuitcenter.com/Circuits/opmodel1/opmodel1.htm.
+ /// Currently 2 different types are supported: Type 1 and Type 3. Type 1
+ /// is less complex and should run faster than Type 3.
+ ///
+ /// This is an extension to the traditional SPICE approach which
+ /// assumes that you will be using an manufacturer model. These models may
+ /// have copyrights incompatible with the netlist license. Thus they may not
+ /// be suitable for certain implementations of netlist.
+ ///
+ /// For the typical use cases in low frequency (< 100 KHz) applications at
+ /// which netlist is targeted, this model is certainly suitable. All parameters
+ /// can be determined from a typical opamp datasheet.
+ ///
+ /// |Type|name |parameter |units|default| example|
+ /// |:--:|:-----|:----------------------------------------------|:----|------:|-------:|
+ /// | 3 |TYPE |Model Type, 1 and 3 are supported | | | |
+ /// |1,3 |FPF |frequency of first pole |Hz | |100 |
+ /// | 3 |SLEW |unity gain slew rate |V/s | | 1|
+ /// |1,3 |RI |input resistance |Ohm | |1M |
+ /// |1,3 |RO |output resistance |Ohm | |50 |
+ /// |1,3 |UGF |unity gain frequency (transition frequency) |Hz | |1000 |
+ /// | 3 |VLL |low output swing minus low supply rail |V | |1.5 |
+ /// | 3 |VLH |high supply rail minus high output swing |V | |1.5 |
+ /// | 3 |DAB |Differential Amp Bias - total quiescent current|A | |0.001 |
+ ///
+ ///
+ /// Type = 0: Impedance changer
+ /// 1; Idealized opamp
+ /// 2; opamp with first pole
+ /// 3: opamp with first pole + output limit
+ /// 4: opamp with input stage, first pole + output limit
+ ///
+ /// Type 1 parameters:
+ /// FPF = frequency of first pole in Hz (ony used for open-loop gain)
+ /// UGF = unity gain frequency in Hz (only used for open-loop gain)
+ /// RI = input resistance in Ohms
+ /// RO = output resistance in Ohms
+ ///
+ /// Type 3 parameters:
+ /// VLH = high supply rail minus high output swing in V
+ /// VLL = low output swing minus low supply rail in V
+ /// FPF = frequency of first pole in Hz
+ /// UGF = unity gain frequency (transition frequency) in Hz
+ /// SLEW = unity gain slew rate in V/s
+ /// RI = input resistance in Ohms
+ /// RO = output resistance in Ohms
+ /// DAB = Differential Amp Bias ~ op amp's total quiescent current.
+ ///
+ /// .model abc OPAMP(VLH=2.0 VLL=0.2 FPF=5 UGF=10k SLEW=0.6u RI=1000k RO=50 DAB=0.002)
+ ///
+ /// http://www.ecircuitcenter.com/Circuits/opmodel1/opmodel1.htm
+ ///
+ ///
+ class opamp_model_t
{
public:
- opamp_model_t(device_t &device, const pstring &name, const pstring &val)
- : param_model_t(device, name, val)
- , m_TYPE(*this, "TYPE")
- , m_FPF(*this, "FPF")
- , m_SLEW(*this, "SLEW")
- , m_RI(*this, "RI")
- , m_RO(*this, "RO")
- , m_UGF(*this, "UGF")
- , m_VLL(*this, "VLL")
- , m_VLH(*this, "VLH")
- , m_DAB(*this, "DAB")
+ opamp_model_t(param_model_t &model)
+ : m_TYPE(model, "TYPE")
+ , m_FPF(model, "FPF")
+ , m_SLEW(model, "SLEW")
+ , m_RI(model, "RI")
+ , m_RO(model, "RO")
+ , m_UGF(model, "UGF")
+ , m_VLL(model, "VLL")
+ , m_VLH(model, "VLH")
+ , m_DAB(model, "DAB")
{}
- value_t m_TYPE; //!< Model Type, 1 and 3 are supported
- value_t m_FPF; //!< frequency of first pole
- value_t m_SLEW; //!< unity gain slew rate
- value_t m_RI; //!< input resistance
- value_t m_RO; //!< output resistance
- value_t m_UGF; //!< unity gain frequency (transition frequency)
- value_t m_VLL; //!< low output swing minus low supply rail
- value_t m_VLH; //!< high supply rail minus high output swing
- value_t m_DAB; //!< Differential Amp Bias - total quiescent current
+ param_model_t::value_t m_TYPE; //!< Model Type, 1 and 3 are supported
+ param_model_t::value_t m_FPF; //!< frequency of first pole
+ param_model_t::value_t m_SLEW; //!< unity gain slew rate
+ param_model_t::value_t m_RI; //!< input resistance
+ param_model_t::value_t m_RO; //!< output resistance
+ param_model_t::value_t m_UGF; //!< unity gain frequency (transition frequency)
+ param_model_t::value_t m_VLL; //!< low output swing minus low supply rail
+ param_model_t::value_t m_VLH; //!< high supply rail minus high output swing
+ param_model_t::value_t m_DAB; //!< Differential Amp Bias - total quiescent current
};
- NETLIB_OBJECT(opamp)
+ class nld_opamp : public base_device_t
{
- NETLIB_CONSTRUCTOR(opamp)
+ public:
+ nld_opamp(constructor_param_t data)
+ : base_device_t(data)
, m_RP(*this, "RP1")
, m_G1(*this, "G1")
- , m_VCC(*this, "VCC")
- , m_GND(*this, "GND")
+ , m_VCC(*this, "VCC", NETLIB_DELEGATE(supply))
+ , m_GND(*this, "GND", NETLIB_DELEGATE(supply))
, m_model(*this, "MODEL", "LM324")
+ , m_modacc(m_model)
, m_VH(*this, "VH")
, m_VL(*this, "VL")
, m_VREF(*this, "VREF")
+ , m_type(plib::narrow_cast<int>(m_modacc.m_TYPE))
{
- m_type = static_cast<int>(m_model.m_TYPE);
if (m_type < 1 || m_type > 3)
+ {
log().fatal(MF_OPAMP_UNKNOWN_TYPE(m_type));
+ throw nl_exception(MF_OPAMP_UNKNOWN_TYPE(m_type));
+ }
if (m_type == 1)
{
- register_subalias("PLUS", "G1.IP");
- register_subalias("MINUS", "G1.IN");
- register_subalias("OUT", "G1.OP");
+ register_sub_alias("PLUS", "G1.IP");
+ register_sub_alias("MINUS", "G1.IN");
+ register_sub_alias("OUT", "G1.OP");
connect("G1.ON", "VREF");
connect("RP1.2", "VREF");
@@ -131,11 +136,13 @@ namespace netlist
}
if (m_type == 2 || m_type == 3)
{
- create_and_register_subdevice("CP1", m_CP);
- create_and_register_subdevice("EBUF", m_EBUF);
-
- register_subalias("PLUS", "G1.IP");
- register_subalias("MINUS", "G1.IN");
+ create_and_register_sub_device(*this, "CP1", m_CP);
+ create_and_register_sub_device(*this, "EBUF", m_EBUF);
+#if TEST_ALT_OUTPUT
+ create_and_register_sub_device("RO", m_RO);
+#endif
+ register_sub_alias("PLUS", "G1.IP");
+ register_sub_alias("MINUS", "G1.IN");
connect("G1.ON", "VREF");
connect("RP1.2", "VREF");
@@ -150,97 +157,120 @@ namespace netlist
}
if (m_type == 2)
{
- register_subalias("OUT", "EBUF.OP");
+#if TEST_ALT_OUTPUT
+ connect("EBUF.OP", "RO.1");
+ register_sub_alias("OUT", "RO.2");
+#else
+ register_sub_alias("OUT", "EBUF.OP");
+#endif
}
if (m_type == 3)
{
- 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)");
+ create_and_register_sub_device(*this, "DN", m_DN, "D(IS=1e-15 N=1)");
+ create_and_register_sub_device(*this, "DP", m_DP, "D(IS=1e-15 N=1)");
connect("DP.K", "VH");
connect("VL", "DN.A");
connect("DP.A", "DN.K");
connect("DN.K", "RP1.1");
-
- register_subalias("OUT", "EBUF.OP");
+#if TEST_ALT_OUTPUT
+ connect("EBUF.OP", "RO.1");
+ register_sub_alias("OUT", "RO.2");
+#else
+ register_sub_alias("OUT", "EBUF.OP");
+#endif
}
}
- NETLIB_UPDATEI();
+ NETLIB_HANDLERI(supply)
+ {
+ const nl_fptype cVt = nlconst::np_VT(nlconst::one()); // * m_n;
+ const nl_fptype cId = m_modacc.m_DAB; // 3 mA
+ const nl_fptype cVd = cVt * plib::log(cId / nlconst::np_Is() + nlconst::one());
+
+ m_VH.push(m_VCC() - m_modacc.m_VLH - cVd);
+ m_VL.push(m_GND() + m_modacc.m_VLL + cVd);
+ m_VREF.push((m_VCC() + m_GND()) / nlconst::two());
+ }
+
NETLIB_RESETI()
{
}
+
NETLIB_UPDATE_PARAMI();
private:
- analog::NETLIB_SUB(R_base) m_RP;
- analog::NETLIB_SUB(VCCS) m_G1;
- NETLIB_SUBXX(analog, C) m_CP;
- NETLIB_SUBXX(analog, VCVS) m_EBUF;
- NETLIB_SUBXX(analog, D) m_DP;
- NETLIB_SUBXX(analog, D) m_DN;
+ NETLIB_SUB_NS(analog, R_base) m_RP;
+ NETLIB_SUB_NS(analog, VCCS) m_G1;
+ NETLIB_SUB_UPTR(analog, C) m_CP;
+#if TEST_ALT_OUTPUT
+ NETLIB_SUB_UPTR(analog, R_base) m_RO;
+#endif
+ NETLIB_SUB_UPTR(analog, VCVS) m_EBUF;
+ NETLIB_SUB_UPTR(analog, D) m_DP;
+ NETLIB_SUB_UPTR(analog, D) m_DN;
analog_input_t m_VCC;
analog_input_t m_GND;
- opamp_model_t m_model;
+ param_model_t m_model;
+ opamp_model_t m_modacc;
analog_output_t m_VH;
analog_output_t m_VL;
analog_output_t m_VREF;
- /* state */
+ // state
int m_type;
};
- NETLIB_UPDATE(opamp)
- {
- const double cVt = 0.0258 * 1.0; // * m_n;
- const double cId = m_model.m_DAB; // 3 mA
- const double cVd = cVt * std::log(cId / 1e-15 + 1.0);
-
- m_VH.push(m_VCC() - m_model.m_VLH - cVd);
- m_VL.push(m_GND() + m_model.m_VLL + cVd);
- m_VREF.push((m_VCC() + m_GND()) / 2.0);
- }
-
NETLIB_UPDATE_PARAM(opamp)
{
- m_G1.m_RI.setTo(m_model.m_RI);
+ m_G1().m_RI.set(m_modacc.m_RI);
if (m_type == 1)
{
- double RO = m_model.m_RO;
- double G = m_model.m_UGF / m_model.m_FPF / RO;
- m_RP.set_R(RO);
- m_G1.m_G.setTo(G);
+ nl_fptype RO = m_modacc.m_RO;
+ nl_fptype G = m_modacc.m_UGF / m_modacc.m_FPF / RO;
+ m_RP().set_R(RO);
+ m_G1().m_G.set(G);
}
if (m_type == 3 || m_type == 2)
{
- double CP = m_model.m_DAB / m_model.m_SLEW;
- double RP = 0.5 / constants::pi() / CP / m_model.m_FPF;
- double G = m_model.m_UGF / m_model.m_FPF / RP;
+ nl_fptype CP = m_modacc.m_DAB / m_modacc.m_SLEW;
+ nl_fptype RP = nlconst::half() / nlconst::pi() / CP / m_modacc.m_FPF;
+ nl_fptype G = m_modacc.m_UGF / m_modacc.m_FPF / RP;
//printf("OPAMP %s: %g %g %g\n", name().c_str(), CP, RP, G);
- if (m_model.m_SLEW / (4.0 * constants::pi() * 0.0258) < m_model.m_UGF)
+ if (m_modacc.m_SLEW / (nlconst::four() * nlconst::pi() * nlconst::np_VT()) < m_modacc.m_UGF)
log().warning(MW_OPAMP_FAIL_CONVERGENCE(this->name()));
- m_CP->m_C.setTo(CP);
- m_RP.set_R(RP);
- m_G1.m_G.setTo(G);
+ m_CP->set_cap_embedded(CP);
+ m_RP().set_R(RP);
+ m_G1().m_G.set(G);
}
if (m_type == 2)
{
- m_EBUF->m_G.setTo(1.0);
- m_EBUF->m_RO.setTo(m_model.m_RO);
+ m_EBUF->m_G.set(nlconst::one());
+#if TEST_ALT_OUTPUT
+ m_EBUF->m_RO.set(0.001);
+ m_RO->set_R(m_modacc.m_RO);
+#else
+ m_EBUF->m_RO.set(m_modacc.m_RO);
+#endif
}
if (m_type == 3)
{
- m_EBUF->m_G.setTo(1.0);
- m_EBUF->m_RO.setTo(m_model.m_RO);
+ m_EBUF->m_G.set(nlconst::one());
+#if TEST_ALT_OUTPUT
+ m_EBUF->m_RO.set(0.001);
+ m_RO->set_R(m_modacc.m_RO);
+#else
+ m_EBUF->m_RO.set(m_modacc.m_RO);
+#endif
}
}
diff --git a/src/lib/netlist/analog/nld_opamps.h b/src/lib/netlist/analog/nld_opamps.h
deleted file mode 100644
index 4a07f646587..00000000000
--- a/src/lib/netlist/analog/nld_opamps.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// license:GPL-2.0+
-// copyright-holders:Couriersud
-/*
- * nld_opamps.h
- *
- */
-
-//#pragma once
-
-#ifndef NLD_OPAMPS_H_
-#define NLD_OPAMPS_H_
-
-#include "netlist/nl_setup.h"
-
-// ----------------------------------------------------------------------------------------
-// Macros
-// ----------------------------------------------------------------------------------------
-
-#define OPAMP(name, model) \
- NET_REGISTER_DEV(OPAMP, name) \
- NETDEV_PARAMI(name, MODEL, model)
-
-
-#endif /* NLD_OPAMPS_H_ */
diff --git a/src/lib/netlist/analog/nld_switches.cpp b/src/lib/netlist/analog/nld_switches.cpp
index 0e62fe13952..6b87279e398 100644
--- a/src/lib/netlist/analog/nld_switches.cpp
+++ b/src/lib/netlist/analog/nld_switches.cpp
@@ -1,19 +1,15 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * nld_legacy.c
- *
- */
#include "nlid_twoterm.h"
-#include "netlist/nl_base.h"
-#include "netlist/nl_factory.h"
-#include "netlist/solver/nld_solver.h"
+#include "nl_base.h"
+#include "nl_factory.h"
+#include "solver/nld_solver.h"
-/* FIXME : convert to parameters */
+// FIXME : convert to parameters
-#define R_OFF (1.0 / exec().gmin())
-#define R_ON 0.01
+#define R_OFF (plib::reciprocal(exec().gmin()))
+#define R_ON nlconst::magic(0.01)
namespace netlist
{
@@ -23,83 +19,72 @@ namespace netlist
// SWITCH
// ----------------------------------------------------------------------------------------
- NETLIB_OBJECT(switch1)
+ class nld_switch1 : public base_device_t
{
- NETLIB_CONSTRUCTOR(switch1)
+ public:
+ nld_switch1(constructor_param_t data)
+ : base_device_t(data)
, m_R(*this, "R")
, m_POS(*this, "POS", false)
{
- register_subalias("1", m_R.m_P);
- register_subalias("2", m_R.m_N);
+ register_sub_alias("1", m_R().P());
+ register_sub_alias("2", m_R().N());
}
- NETLIB_RESETI();
- NETLIB_UPDATEI();
- NETLIB_UPDATE_PARAMI();
-
- analog::NETLIB_SUB(R_base) m_R;
- param_logic_t m_POS;
- };
-
-
- NETLIB_RESET(switch1)
- {
- m_R.set_R(R_OFF);
- }
-
- NETLIB_UPDATE(switch1)
- {
- }
-
- NETLIB_UPDATE_PARAM(switch1)
- {
- m_R.solve_now();
- if (!m_POS())
+ NETLIB_RESETI()
{
- m_R.set_R(R_OFF);
+ m_R().set_R(R_OFF);
}
- else
+ NETLIB_UPDATE_PARAMI()
{
- m_R.set_R(R_ON);
+ m_R().change_state([this]()
+ {
+ m_R().set_R(m_POS() ? R_ON : R_OFF);
+ });
}
- m_R.solve_later();
- }
+ private:
+ NETLIB_SUB_NS(analog, R_base) m_R;
+ param_logic_t m_POS;
+ };
// ----------------------------------------------------------------------------------------
// SWITCH2
// ----------------------------------------------------------------------------------------
- NETLIB_OBJECT(switch2)
+ class nld_switch2 : public base_device_t
{
- NETLIB_CONSTRUCTOR(switch2)
+ public:
+ nld_switch2(constructor_param_t data)
+ : base_device_t(data)
, m_R1(*this, "R1")
, m_R2(*this, "R2")
, m_POS(*this, "POS", false)
{
- connect(m_R1.m_N, m_R2.m_N);
+ connect(m_R1().N(), m_R2().N());
- register_subalias("1", m_R1.m_P);
- register_subalias("2", m_R2.m_P);
+ register_sub_alias("1", m_R1().P());
+ register_sub_alias("2", m_R2().P());
- register_subalias("Q", m_R1.m_N);
+ register_sub_alias("Q", m_R1().N());
}
NETLIB_RESETI();
- NETLIB_UPDATEI();
NETLIB_UPDATE_PARAMI();
- analog::NETLIB_SUB(R_base) m_R1;
- analog::NETLIB_SUB(R_base) m_R2;
- param_logic_t m_POS;
+ private:
+ NETLIB_SUB_NS(analog, R_base) m_R1;
+ NETLIB_SUB_NS(analog, R_base) m_R2;
+ param_logic_t m_POS;
};
NETLIB_RESET(switch2)
{
- m_R1.set_R(R_ON);
- m_R2.set_R(R_OFF);
+ m_R1().set_R(R_ON);
+ m_R2().set_R(R_OFF);
}
+#ifdef FIXMELATER
NETLIB_UPDATE(switch2)
{
if (!m_POS())
@@ -112,26 +97,23 @@ namespace netlist
m_R1.set_R(R_OFF);
m_R2.set_R(R_ON);
}
-
- //m_R1.update_dev(time);
- //m_R2.update_dev(time);
}
-
+#endif
NETLIB_UPDATE_PARAM(switch2)
{
- if (!m_POS())
- {
- m_R1.set_R(R_ON);
- m_R2.set_R(R_OFF);
- }
+ // R1 and R2 are connected. However this net may be a rail net.
+ // The code here thus is a bit more complex.
+
+ nl_fptype r1 = m_POS() ? R_OFF : R_ON;
+ nl_fptype r2 = m_POS() ? R_ON : R_OFF;
+
+ if (m_R1().solver() == m_R2().solver())
+ m_R1().change_state([this, &r1, &r2]() { m_R1().set_R(r1); m_R2().set_R(r2); });
else
{
- m_R1.set_R(R_OFF);
- m_R2.set_R(R_ON);
+ m_R1().change_state([this, &r1]() { m_R1().set_R(r1); });
+ m_R2().change_state([this, &r2]() { m_R2().set_R(r2); });
}
-
- m_R1.solve_now();
- m_R2.solve_now();
}
} //namespace analog
diff --git a/src/lib/netlist/analog/nld_switches.h b/src/lib/netlist/analog/nld_switches.h
deleted file mode 100644
index 7aa31c925ae..00000000000
--- a/src/lib/netlist/analog/nld_switches.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// license:GPL-2.0+
-// copyright-holders:Couriersud
-/*
- * nld_switches.h
- *
- */
-
-#pragma once
-
-#ifndef NLD_SWITCHES_H_
-#define NLD_SWITCHES_H_
-
-#include "netlist/nl_setup.h"
-
-// ----------------------------------------------------------------------------------------
-// Macros
-// ----------------------------------------------------------------------------------------
-
-#define SWITCH(name) \
- NET_REGISTER_DEV(SWITCH, name)
-
-#define SWITCH2(name) \
- NET_REGISTER_DEV(SWITCH2, name)
-
-#endif /* NLD_SWITCHES_H_ */
diff --git a/src/lib/netlist/analog/nld_twoterm.h b/src/lib/netlist/analog/nld_twoterm.h
deleted file mode 100644
index b175f40af97..00000000000
--- a/src/lib/netlist/analog/nld_twoterm.h
+++ /dev/null
@@ -1,67 +0,0 @@
-// license:GPL-2.0+
-// copyright-holders:Couriersud
-
-#ifndef NLD_TWOTERM_H_
-#define NLD_TWOTERM_H_
-
-#include "netlist/nl_setup.h"
-
-// -----------------------------------------------------------------------------
-// Macros
-// -----------------------------------------------------------------------------
-
-#define RES(name, p_R) \
- NET_REGISTER_DEV(RES, name) \
- NETDEV_PARAMI(name, R, p_R)
-
-#define POT(name, p_R) \
- NET_REGISTER_DEV(POT, name) \
- NETDEV_PARAMI(name, R, p_R)
-
-/* Does not have pin 3 connected */
-#define POT2(name, p_R) \
- NET_REGISTER_DEV(POT2, name) \
- NETDEV_PARAMI(name, R, p_R)
-
-
-#define CAP(name, p_C) \
- NET_REGISTER_DEV(CAP, name) \
- NETDEV_PARAMI(name, C, p_C)
-
-#define IND(name, p_L) \
- NET_REGISTER_DEV(IND, name) \
- NETDEV_PARAMI(name, L, p_L)
-
-/* Generic Diode */
-#define DIODE(name, model) \
- NET_REGISTER_DEV(DIODE, name) \
- NETDEV_PARAMI(name, MODEL, model)
-
-#define VS(name, pV) \
- NET_REGISTER_DEV(VS, name) \
- NETDEV_PARAMI(name, V, pV)
-
-#define CS(name, pI) \
- NET_REGISTER_DEV(CS, name) \
- NETDEV_PARAMI(name, I, pI)
-
-// -----------------------------------------------------------------------------
-// Generic macros
-// -----------------------------------------------------------------------------
-
-#ifdef RES_R
-#warning "Do not include rescap.h in a netlist environment"
-#endif
-#ifndef RES_R
-#define RES_R(res) (static_cast<double>(res))
-#define RES_K(res) (static_cast<double>(res) * 1e3)
-#define RES_M(res) (static_cast<double>(res) * 1e6)
-#define CAP_U(cap) (static_cast<double>(cap) * 1e-6)
-#define CAP_N(cap) (static_cast<double>(cap) * 1e-9)
-#define CAP_P(cap) (static_cast<double>(cap) * 1e-12)
-#define IND_U(ind) (static_cast<double>(ind) * 1e-6)
-#define IND_N(ind) (static_cast<double>(ind) * 1e-9)
-#define IND_P(ind) (static_cast<double>(ind) * 1e-12)
-#endif
-
-#endif /* NLD_TWOTERM_H_ */
diff --git a/src/lib/netlist/analog/nlid_fourterm.cpp b/src/lib/netlist/analog/nlid_fourterm.cpp
index 7fa6a2f3809..e12a931c142 100644
--- a/src/lib/netlist/analog/nlid_fourterm.cpp
+++ b/src/lib/netlist/analog/nlid_fourterm.cpp
@@ -1,124 +1,129 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * nld_fourterm.c
- *
- */
-#include "netlist/solver/nld_solver.h"
-#include "netlist/nl_factory.h"
#include "nlid_fourterm.h"
-#include <cmath>
+#include "nl_factory.h"
-namespace netlist
+#include "solver/nld_solver.h"
+
+namespace netlist::analog
{
- namespace analog
+
+ // -------------------------------------------------------------------------
+ // nld_VCCS
+ // -------------------------------------------------------------------------
+
+ NETLIB_RESET(VCCS)
{
+ const nl_fptype m_mult = m_G() * m_gfac; // 1.0 ==> 1V ==> 1A
+ const nl_fptype GI = plib::reciprocal(m_RI());
+ m_IP.set_conductivity(GI);
+ m_IN.set_conductivity(GI);
-// ----------------------------------------------------------------------------------------
-// nld_VCCS
-// ----------------------------------------------------------------------------------------
+ m_OP.set_go_gt(-m_mult, nlconst::zero());
+ m_OP1.set_go_gt(m_mult, nlconst::zero());
-NETLIB_RESET(VCCS)
-{
- const nl_double m_mult = m_G() * m_gfac; // 1.0 ==> 1V ==> 1A
- const nl_double GI = plib::constants<nl_double>::one() / m_RI();
+ m_ON.set_go_gt(m_mult, nlconst::zero());
+ m_ON1.set_go_gt(-m_mult, nlconst::zero());
+ }
- m_IP.set_conductivity(GI);
- m_IN.set_conductivity(GI);
+ NETLIB_HANDLER(VCCS, terminal_handler)
+ {
+ solver::matrix_solver_t *solv = nullptr;
+ // only called if connected to a rail net ==> notify the solver to
+ // recalculate
+ // NOLINTNEXTLINE(bugprone-branch-clone)
+ if ((solv = m_IP.solver()) != nullptr)
+ solv->solve_now();
+ else if ((solv = m_IN.solver()) != nullptr)
+ solv->solve_now();
+ else if ((solv = m_OP.solver()) != nullptr)
+ solv->solve_now();
+ else if ((solv = m_ON.solver()) != nullptr)
+ solv->solve_now();
+ }
+
+ // -------------------------------------------------------------------------
+ // nld_LVCCS
+ // -------------------------------------------------------------------------
+
+ NETLIB_RESET(LVCCS) { NETLIB_NAME(VCCS)::reset(); }
+
+ NETLIB_UPDATE_PARAM(LVCCS) { NETLIB_NAME(VCCS)::update_param(); }
+
+ NETLIB_UPDATE_TERMINALS(LVCCS)
+ {
+ const nl_fptype m_mult = m_G() * get_gfac(); // 1.0 ==> 1V ==> 1A
+ const nl_fptype vi = m_IP.net().Q_Analog() - m_IN.net().Q_Analog();
+ const auto c1(nlconst::magic(0.2));
- m_OP.set_go_gt(-m_mult, plib::constants<nl_double>::zero());
- m_OP1.set_go_gt(m_mult, plib::constants<nl_double>::zero());
+ if (plib::abs(m_mult / m_cur_limit() * vi) > nlconst::half())
+ m_vi = m_vi + c1 * plib::tanh((vi - m_vi) / c1);
+ else
+ m_vi = vi;
- m_ON.set_go_gt(m_mult, plib::constants<nl_double>::zero());
- m_ON1.set_go_gt(-m_mult, plib::constants<nl_double>::zero());
-}
+ const nl_fptype x = m_mult / m_cur_limit() * m_vi;
+ const nl_fptype tanhx = plib::tanh(x);
-NETLIB_UPDATE(VCCS)
-{
- /* only called if connected to a rail net ==> notify the solver to recalculate */
- if (!m_IP.net().isRailNet())
- m_IP.solve_now();
- else if (!m_IN.net().isRailNet())
- m_IN.solve_now();
- else if (!m_OP.net().isRailNet())
- m_OP.solve_now();
- else if (!m_ON.net().isRailNet())
- m_ON.solve_now();
-}
-
-// ----------------------------------------------------------------------------------------
-// nld_LVCCS
-// ----------------------------------------------------------------------------------------
-
-NETLIB_RESET(LVCCS)
-{
- NETLIB_NAME(VCCS)::reset();
-}
+ const nl_fptype beta = m_mult * (nlconst::one() - tanhx * tanhx);
+ const nl_fptype I = m_cur_limit() * tanhx - beta * m_vi;
-NETLIB_UPDATE_PARAM(LVCCS)
-{
- NETLIB_NAME(VCCS)::update_param();
-}
+ m_OP.set_go_gt_I(-beta, nlconst::zero(), I);
+ m_OP1.set_go_gt(beta, nlconst::zero());
-NETLIB_UPDATE_TERMINALS(LVCCS)
-{
- const nl_double m_mult = m_G() * m_gfac; // 1.0 ==> 1V ==> 1A
- const nl_double vi = m_IP.net().Q_Analog() - m_IN.net().Q_Analog();
+ m_ON.set_go_gt_I(beta, nlconst::zero(), -I);
+ m_ON1.set_go_gt(-beta, nlconst::zero());
+ }
- if (std::abs(m_mult / m_cur_limit() * vi) > 0.5)
- m_vi = m_vi + 0.2*std::tanh((vi - m_vi)/0.2);
- else
- m_vi = vi;
+ // -------------------------------------------------------------------------
+ // nld_CCCS
+ // -------------------------------------------------------------------------
- const nl_double x = m_mult / m_cur_limit() * m_vi;
- const nl_double X = std::tanh(x);
+ NETLIB_RESET(CCCS) { NETLIB_NAME(VCCS)::reset(); }
- const nl_double beta = m_mult * (1.0 - X*X);
- const nl_double I = m_cur_limit() * X - beta * m_vi;
+ NETLIB_UPDATE_PARAM(CCCS) { NETLIB_NAME(VCCS)::update_param(); }
- m_OP.set_go_gt_I(-beta, plib::constants<nl_double>::zero(), I);
- m_OP1.set_go_gt(beta, plib::constants<nl_double>::zero());
+ // -------------------------------------------------------------------------
+ // nld_VCVS
+ // -------------------------------------------------------------------------
- m_ON.set_go_gt_I(beta, plib::constants<nl_double>::zero(), -I);
- m_ON1.set_go_gt(-beta, plib::constants<nl_double>::zero());
-}
+ NETLIB_RESET(VCVS)
+ {
+ const auto gfac(plib::reciprocal(m_RO()));
+ set_gfac(gfac);
-// ----------------------------------------------------------------------------------------
-// nld_CCCS
-// ----------------------------------------------------------------------------------------
+ NETLIB_NAME(VCCS)::reset();
-NETLIB_RESET(CCCS)
-{
- NETLIB_NAME(VCCS)::reset();
-}
+ m_OP2.set_conductivity(gfac);
+ m_ON2.set_conductivity(gfac);
+ }
-NETLIB_UPDATE_PARAM(CCCS)
-{
- NETLIB_NAME(VCCS)::update_param();
-}
+ // -------------------------------------------------------------------------
+ // nld_CCVS
+ // -------------------------------------------------------------------------
+
+ NETLIB_RESET(CCVS)
+ {
+ const auto gfac(plib::reciprocal(m_RO()));
+ set_gfac(gfac);
+
+ NETLIB_NAME(VCCS)::reset();
+
+ m_OP2.set_conductivity(gfac);
+ m_ON2.set_conductivity(gfac);
+ }
-// ----------------------------------------------------------------------------------------
-// nld_VCVS
-// ----------------------------------------------------------------------------------------
+} // namespace netlist::analog
-NETLIB_RESET(VCVS)
+namespace netlist::devices
{
- m_gfac = plib::constants<nl_double>::one() / m_RO();
- NETLIB_NAME(VCCS)::reset();
-
- 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, "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
+ // clang-format off
+ NETLIB_DEVICE_IMPL_NS(analog, VCVS, "VCVS", "G")
+ NETLIB_DEVICE_IMPL_NS(analog, VCCS, "VCCS", "G")
+ NETLIB_DEVICE_IMPL_NS(analog, CCCS, "CCCS", "G")
+ NETLIB_DEVICE_IMPL_NS(analog, CCVS, "CCVS", "G")
+ NETLIB_DEVICE_IMPL_NS(analog, LVCCS, "LVCCS", "")
+ // clang-format on
+} // namespace netlist::devices
diff --git a/src/lib/netlist/analog/nlid_fourterm.h b/src/lib/netlist/analog/nlid_fourterm.h
index 5ca6a747116..1320f97984b 100644
--- a/src/lib/netlist/analog/nlid_fourterm.h
+++ b/src/lib/netlist/analog/nlid_fourterm.h
@@ -1,71 +1,74 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * nlid_fourterm.h
- *
- */
#ifndef NLID_FOURTERM_H_
#define NLID_FOURTERM_H_
-#include "netlist/nl_base.h"
+///
+/// \file nlid_fourterm.h
+///
+
+#include "nl_base.h"
+
#include "plib/putil.h"
-namespace netlist {
- namespace analog {
+namespace netlist::analog
+{
// ----------------------------------------------------------------------------------------
// nld_VCCS
// ----------------------------------------------------------------------------------------
- /*
- * Voltage controlled current source
- *
- * IP ---+ +------> OP
- * | |
- * RI I
- * RI => G => I IOut = (V(IP)-V(IN)) * G
- * RI I
- * | |
- * IN ---+ +------< ON
- *
- * G=1 ==> 1V ==> 1A
- *
- * RI = 1 / NETLIST_GMIN
- *
- */
-
- NETLIB_OBJECT(VCCS)
+ //
+ // Voltage controlled current source
+ //
+ // IP ---+ +------> OP
+ // | |
+ // RI I
+ // RI => G => I IOut = (V(IP)-V(IN)) * G
+ // RI I
+ // | |
+ // IN ---+ +------< ON
+ //
+ // G=1 ==> 1V ==> 1A
+ //
+ // RI = 1 / NETLIST_GMIN
+ //
+ class nld_VCCS : public base_device_t
{
public:
- NETLIB_CONSTRUCTOR(VCCS)
- , m_G(*this, "G", 1.0)
- , m_RI(*this, "RI", 1e9)
- , 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)
+ nld_VCCS(constructor_param_t data, nl_fptype ri = nlconst::magic(1e9))
+ : base_device_t(data)
+ , m_G(*this, "G", nlconst::one())
+ , m_RI(*this, "RI", ri)
+ , m_OP(*this, "OP", &m_IP, {&m_ON, &m_IN},
+ NETLIB_DELEGATE(terminal_handler))
+ , m_ON(*this, "ON", &m_IP, {&m_OP, &m_IN},
+ NETLIB_DELEGATE(terminal_handler))
+ , m_IP(*this, "IP", &m_IN, {&m_OP, &m_ON},
+ NETLIB_DELEGATE(terminal_handler))
+ , m_IN(*this, "IN", &m_IP, {&m_OP, &m_ON},
+ NETLIB_DELEGATE(terminal_handler))
+ , m_OP1(*this, "_OP1", &m_IN, NETLIB_DELEGATE(terminal_handler))
+ , m_ON1(*this, "_ON1", &m_IN, NETLIB_DELEGATE(terminal_handler))
+ , m_gfac(nlconst::one())
{
connect(m_OP, m_OP1);
connect(m_ON, m_ON1);
- m_gfac = plib::constants<nl_double>::one();
}
NETLIB_RESETI();
- param_double_t m_G;
- param_double_t m_RI;
+ param_fp_t m_G;
+ param_fp_t m_RI;
protected:
- NETLIB_UPDATEI();
- NETLIB_UPDATE_PARAMI()
- {
- NETLIB_NAME(VCCS)::reset();
- }
+ NETLIB_HANDLERI(terminal_handler);
+ NETLIB_UPDATE_PARAMI() { NETLIB_NAME(VCCS)::reset(); }
+ void set_gfac(nl_fptype g) noexcept { m_gfac = g; }
+
+ nl_fptype get_gfac() const noexcept { return m_gfac; }
terminal_t m_OP;
terminal_t m_ON;
@@ -76,107 +79,107 @@ namespace netlist {
terminal_t m_OP1;
terminal_t m_ON1;
- nl_double m_gfac;
+ private:
+ nl_fptype m_gfac;
};
- /* Limited Current source*/
+ // Limited Current source
- NETLIB_OBJECT_DERIVED(LVCCS, VCCS)
+ class nld_LVCCS : public nld_VCCS
{
public:
- NETLIB_CONSTRUCTOR_DERIVED(LVCCS, VCCS)
- , m_cur_limit(*this, "CURLIM", 1000.0)
- , m_vi(0.0)
+ nld_LVCCS(constructor_param_t data)
+ : nld_VCCS(data)
+ , m_cur_limit(*this, "CURLIM", nlconst::magic(1000.0))
+ , m_vi(nlconst::zero())
{
}
NETLIB_IS_DYNAMIC(true)
protected:
- //NETLIB_UPDATEI();
NETLIB_RESETI();
NETLIB_UPDATE_PARAMI();
NETLIB_UPDATE_TERMINALSI();
private:
- param_double_t m_cur_limit; /* current limit */
- nl_double m_vi;
+ param_fp_t m_cur_limit; // current limit
+ nl_fptype m_vi;
};
// ----------------------------------------------------------------------------------------
// nld_CCCS
// ----------------------------------------------------------------------------------------
- /*
- * Current controlled current source
- *
- * IP ---+ +------> OP
- * | |
- * RI I
- * RI => G => I IOut = (V(IP)-V(IN)) / RI * G
- * RI I
- * | |
- * IN ---+ +------< ON
- *
- * G=1 ==> 1A ==> 1A
- *
- * RI = 1
- *
- * This needs high levels of accuracy to work with 1 Ohm RI.
- *
- */
-
- NETLIB_OBJECT_DERIVED(CCCS, VCCS)
+ //
+ // Current controlled current source
+ //
+ // IP ---+ +------> OP
+ // | |
+ // RI I
+ // RI => G => I IOut = -(V(IP)-V(IN)) / RI * G
+ // RI I
+ // | |
+ // IN ---+ +------< ON
+ //
+ // G=1 ==> 1A ==> 1A
+ //
+ // RI = 1
+ //
+ // If current flows from IP to IN than output current flows from OP to ON
+ //
+ // This needs high levels of accuracy to work with 1 Ohm RI.
+ //
+
+ class nld_CCCS : public nld_VCCS
{
public:
- NETLIB_CONSTRUCTOR_DERIVED(CCCS, VCCS)
+ nld_CCCS(constructor_param_t data)
+ : nld_VCCS(data, nlconst::one())
{
- m_gfac = plib::constants<nl_double>::one() / m_RI();
+ set_gfac(-plib::reciprocal(m_RI()));
}
NETLIB_RESETI();
protected:
- //NETLIB_UPDATEI();
NETLIB_UPDATE_PARAMI();
};
-
// ----------------------------------------------------------------------------------------
// nld_VCVS
// ----------------------------------------------------------------------------------------
- /*
- * Voltage controlled voltage source
- *
- * Parameters:
- * G Default: 1
- * RO Default: 1 (would be typically 50 for an op-amp
- *
- * IP ---+ +--+---- OP
- * | | |
- * RI I RO
- * RI => G => I RO V(OP) - V(ON) = (V(IP)-V(IN)) * G
- * RI I RO
- * | | |
- * IN ---+ +--+---- ON
- *
- * G=1 ==> 1V ==> 1V
- *
- * RI = 1 / NETLIST_GMIN
- *
- * Internal GI = G / RO
- *
- */
-
-
- NETLIB_OBJECT_DERIVED(VCVS, VCCS)
+ //
+ // Voltage controlled voltage source
+ //
+ // Parameters:
+ // G Default: 1
+ // RO Default: 1 (would be typically 50 for an op-amp
+ //
+ // IP ---+ +--+---- OP
+ // | | |
+ // RI I RO
+ // RI => G => I RO V(OP) - V(ON) = (V(IP)-V(IN)) * G
+ // RI I RO
+ // | | |
+ // IN ---+ +--+---- ON
+ //
+ // G=1 ==> 1V ==> 1V
+ //
+ // RI = 1 / NETLIST_GMIN
+ //
+ // Internal GI = G / RO
+ //
+
+ class nld_VCVS : public nld_VCCS
{
public:
- NETLIB_CONSTRUCTOR_DERIVED(VCVS, VCCS)
- , m_RO(*this, "RO", 1.0)
- , m_OP2(*this, "_OP2", &m_ON2)
- , m_ON2(*this, "_ON2", &m_OP2)
+ nld_VCVS(constructor_param_t data)
+ : nld_VCCS(data)
+ , m_RO(*this, "RO", nlconst::one())
+ , m_OP2(*this, "_OP2", &m_ON2, NETLIB_DELEGATE(terminal_handler))
+ , m_ON2(*this, "_ON2", &m_OP2, NETLIB_DELEGATE(terminal_handler))
{
connect(m_OP2, m_OP1);
connect(m_ON2, m_ON1);
@@ -184,19 +187,74 @@ namespace netlist {
NETLIB_RESETI();
- param_double_t m_RO;
+ param_fp_t m_RO;
private:
- //NETLIB_UPDATEI();
- //NETLIB_UPDATE_PARAMI();
+ // NETLIB_UPDATE_PARAMI();
+ NETLIB_HANDLERI(terminal_handler)
+ {
+ NETLIB_NAME(VCCS)::terminal_handler();
+ }
terminal_t m_OP2;
terminal_t m_ON2;
+ };
+ // ----------------------------------------------------------------------------------------
+ // nld_CCVS
+ // ----------------------------------------------------------------------------------------
+ //
+ // Voltage controlled voltage source
+ //
+ // Parameters:
+ // G Default: 1
+ // RO Default: 1 (would be typically 50 for an op-amp
+ //
+ // IP ---+ +--+---- OP
+ // | | |
+ // RI I RO
+ // RI => G => I RO V(OP) - V(ON) = (V(IP)-V(IN)) / RI * G
+ // RI I RO
+ // | | |
+ // IN ---+ +--+---- ON
+ //
+ // G=1 ==> 1A ==> 1V
+ //
+ // RI = 1
+ //
+ // Internal GI = G / RO
+ //
+
+ class nld_CCVS : public nld_VCCS
+ {
+ public:
+ nld_CCVS(constructor_param_t data)
+ : nld_VCCS(data, nlconst::one())
+ , m_RO(*this, "RO", nlconst::one())
+ , m_OP2(*this, "_OP2", &m_ON2, NETLIB_DELEGATE(terminal_handler))
+ , m_ON2(*this, "_ON2", &m_OP2, NETLIB_DELEGATE(terminal_handler))
+ {
+ connect(m_OP2, m_OP1);
+ connect(m_ON2, m_ON1);
+ }
+
+ NETLIB_RESETI();
+
+ param_fp_t m_RO;
+
+ private:
+ // NETLIB_UPDATE_PARAMI();
+
+ NETLIB_HANDLERI(terminal_handler)
+ {
+ NETLIB_NAME(VCCS)::terminal_handler();
+ }
+
+ terminal_t m_OP2;
+ terminal_t m_ON2;
};
- } // namespace analog
-} // namespace netlist
+} // namespace netlist::analog
-#endif /* NLD_FOURTERM_H_ */
+#endif // NLD_FOURTERM_H_
diff --git a/src/lib/netlist/analog/nlid_twoterm.cpp b/src/lib/netlist/analog/nlid_twoterm.cpp
index aaddb65387a..1a3c79e713f 100644
--- a/src/lib/netlist/analog/nlid_twoterm.cpp
+++ b/src/lib/netlist/analog/nlid_twoterm.cpp
@@ -1,190 +1,231 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * nld_twoterm.c
- *
- */
-#include "netlist/solver/nld_solver.h"
-
-#include "netlist/nl_factory.h"
#include "nlid_twoterm.h"
-#include <cmath>
+#include "nl_factory.h"
-namespace netlist
-{
- namespace analog
- {
+#include "solver/nld_solver.h"
-// ----------------------------------------------------------------------------------------
-// nld_twoterm
-// ----------------------------------------------------------------------------------------
-
-void NETLIB_NAME(twoterm)::solve_now()
-{
- /* 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 NETLIB_NAME(twoterm)::solve_later(netlist_time delay)
+namespace netlist::analog
{
- /* 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 */
- solve_now();
-}
-
-// ----------------------------------------------------------------------------------------
-// nld_R_base
-// ----------------------------------------------------------------------------------------
-
-NETLIB_RESET(R_base)
-{
- NETLIB_NAME(twoterm)::reset();
- set_R(1.0 / exec().gmin());
-}
+ solver::matrix_solver_t *nld_two_terminal::solver() const noexcept
+ {
+ auto *solv(m_P.solver());
+ if (solv != nullptr)
+ return solv;
+ return m_N.solver();
+ }
-// ----------------------------------------------------------------------------------------
-// nld_POT
-// ----------------------------------------------------------------------------------------
+ void nld_two_terminal::solve_now() const
+ {
+ auto *solv(solver());
+ if (solv != nullptr)
+ solv->solve_now();
+ }
-NETLIB_RESET(POT)
-{
- nl_double v = m_Dial();
- if (m_DialIsLog())
- v = (std::exp(v) - 1.0) / (std::exp(1.0) - 1.0);
+ NETLIB_HANDLER(two_terminal, terminal_handler)
+ {
+ // only called if connected to a rail net ==> notify the solver to
+ // recalculate
+ // printf("%s update\n", this->name().c_str());
+ solve_now();
+ }
- 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()));
-}
+ // -------------------------------------------------------------------------
+ // nld_POT
+ // -------------------------------------------------------------------------
-NETLIB_UPDATE_PARAM(POT)
-{
- m_R1.solve_now();
- m_R2.solve_now();
+ NETLIB_RESET(POT)
+ {
+ nl_fptype v = m_Dial();
+ if (m_DialIsLog())
+ v = (plib::exp(v) - nlconst::one())
+ / (plib::exp(nlconst::one()) - nlconst::one());
- nl_double v = m_Dial();
- if (m_DialIsLog())
- 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, exec().gmin()));
- m_R2.set_R(std::max(m_R() * (plib::constants<nl_double>::one() - v), exec().gmin()));
+ m_R1().set_R(std::max(m_R() * v, exec().gmin()));
+ m_R2().set_R(std::max(m_R() * (nlconst::one() - v), exec().gmin()));
+ }
-}
+ NETLIB_UPDATE_PARAM(POT)
+ {
+ nl_fptype v = m_Dial();
+ if (m_DialIsLog())
+ v = (plib::exp(v) - nlconst::one())
+ / (plib::exp(nlconst::one()) - nlconst::one());
+ if (m_Reverse())
+ v = nlconst::one() - v;
+
+ nl_fptype r1(std::max(m_R() * v, exec().gmin()));
+ nl_fptype r2(std::max(m_R() * (nlconst::one() - v), exec().gmin()));
+
+ if (m_R1().solver() == m_R2().solver())
+ m_R1().change_state(
+ [this, &r1, &r2]()
+ {
+ m_R1().set_R(r1);
+ m_R2().set_R(r2);
+ });
+ else
+ {
+ m_R1().change_state([this, &r1]() { m_R1().set_R(r1); });
+ m_R2().change_state([this, &r2]() { m_R2().set_R(r2); });
+ }
+ }
+
+ // -------------------------------------------------------------------------
+ // nld_POT2
+ // -------------------------------------------------------------------------
+
+ NETLIB_RESET(POT2)
+ {
+ nl_fptype v = m_Dial();
-// ----------------------------------------------------------------------------------------
-// nld_POT2
-// ----------------------------------------------------------------------------------------
+ if (m_DialIsLog())
+ v = (plib::exp(v) - nlconst::one())
+ / (plib::exp(nlconst::one()) - nlconst::one());
+ if (m_Reverse())
+ v = nlconst::one() - v;
+ m_R1().set_R(std::max(m_R() * v, exec().gmin()));
+ }
-NETLIB_RESET(POT2)
-{
- nl_double v = m_Dial();
+ NETLIB_UPDATE_PARAM(POT2)
+ {
+ nl_fptype v = m_Dial();
- if (m_DialIsLog())
- 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, exec().gmin()));
-}
+ if (m_DialIsLog())
+ v = (plib::exp(v) - nlconst::one())
+ / (plib::exp(nlconst::one()) - nlconst::one());
+ if (m_Reverse())
+ v = nlconst::one() - v;
+ m_R1().change_state(
+ [this, &v]() { m_R1().set_R(std::max(m_R() * v, exec().gmin())); });
+ }
-NETLIB_UPDATE_PARAM(POT2)
-{
- m_R1.solve_now();
+ // -------------------------------------------------------------------------
+ // nld_L
+ // -------------------------------------------------------------------------
- nl_double v = m_Dial();
+ NETLIB_RESET(L)
+ {
+ m_gmin = exec().gmin();
+ m_I = nlconst::zero();
+ m_G = m_gmin;
+ set_mat(m_G, -m_G, -m_I, //
+ -m_G, m_G, m_I);
+ }
- if (m_DialIsLog())
- 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, exec().gmin()));
-}
+ NETLIB_UPDATE_PARAM(L) {}
-// ----------------------------------------------------------------------------------------
-// nld_L
-// ----------------------------------------------------------------------------------------
+ NETLIB_TIMESTEP(L)
+ {
+ if (ts_type == detail::time_step_type::FORWARD)
+ {
+ m_last_I = m_I;
+ m_last_G = m_G;
+ // Gpar should support convergence
+ m_I += m_G * deltaV();
+ m_G = step / m_L() + m_gmin;
+ set_mat(m_G, -m_G, -m_I, //
+ -m_G, m_G, m_I);
+ }
+ else
+ {
+ m_I = m_last_I;
+ m_G = m_last_G;
+ }
+ }
+
+ // -------------------------------------------------------------------------
+ // nld_D
+ // -------------------------------------------------------------------------
+
+ NETLIB_RESET(D)
+ {
+ nl_fptype Is = m_modacc.m_IS;
+ nl_fptype n = m_modacc.m_N;
-NETLIB_RESET(L)
-{
- m_gmin = exec().gmin();
- m_I = 0.0;
- 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);
-}
-
-NETLIB_UPDATE_PARAM(L)
-{
-}
+ m_D.set_param(Is, n, exec().gmin(), nlconst::T0());
+ set_G_V_I(m_D.G(), nlconst::zero(), m_D.Ieq());
+ }
-NETLIB_TIMESTEP(L)
-{
- /* Gpar should support convergence */
- m_I += m_I + m_G * deltaV();
- 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);
-}
-
-// ----------------------------------------------------------------------------------------
-// nld_D
-// ----------------------------------------------------------------------------------------
-
-NETLIB_RESET(D)
-{
- nl_double Is = m_model.m_IS;
- nl_double n = m_model.m_N;
+ NETLIB_UPDATE_PARAM(D)
+ {
+ nl_fptype Is = m_modacc.m_IS;
+ nl_fptype n = m_modacc.m_N;
- m_D.set_param(Is, n, exec().gmin(), constants::T0());
- set_G_V_I(m_D.G(), 0.0, m_D.Ieq());
-}
+ m_D.set_param(Is, n, exec().gmin(), nlconst::T0());
+ }
-NETLIB_UPDATE_PARAM(D)
-{
- nl_double Is = m_model.m_IS;
- nl_double n = m_model.m_N;
+ NETLIB_UPDATE_TERMINALS(D)
+ {
+ m_D.update_diode(deltaV());
+ const nl_fptype G(m_D.G());
+ const nl_fptype I(m_D.Ieq());
+ set_mat(G, -G, -I, //
+ -G, G, I);
+ // set(m_D.G(), 0.0, m_D.Ieq());
+ }
+
+ // -------------------------------------------------------------------------
+ // nld_Z
+ // -------------------------------------------------------------------------
+
+ NETLIB_RESET(Z)
+ {
+ nl_fptype IsBV = m_modacc.m_IBV
+ / (plib::exp(m_modacc.m_BV
+ / nlconst::np_VT(m_modacc.m_NBV))
+ - nlconst::one());
+
+ m_D.set_param(m_modacc.m_IS, m_modacc.m_N, exec().gmin(),
+ nlconst::T0());
+ m_R.set_param(IsBV, m_modacc.m_NBV, exec().gmin(), nlconst::T0());
+ set_G_V_I(m_D.G(), nlconst::zero(), m_D.Ieq());
+ }
+
+ NETLIB_UPDATE_PARAM(Z)
+ {
+ nl_fptype IsBV = m_modacc.m_IBV
+ / (plib::exp(m_modacc.m_BV
+ / nlconst::np_VT(m_modacc.m_NBV))
+ - nlconst::one());
+
+ m_D.set_param(m_modacc.m_IS, m_modacc.m_N, exec().gmin(),
+ nlconst::T0());
+ m_R.set_param(IsBV, m_modacc.m_NBV, exec().gmin(), nlconst::T0());
+ set_G_V_I(m_D.G(), nlconst::zero(), m_D.Ieq());
+ }
+
+ NETLIB_UPDATE_TERMINALS(Z)
+ {
+ m_D.update_diode(deltaV());
+ m_R.update_diode(-deltaV());
+ const nl_fptype G(m_D.G() + m_R.G());
+ const nl_fptype I(m_D.Ieq() - m_R.Ieq());
+ set_mat(G, -G, -I, //
+ -G, G, I);
+ }
- m_D.set_param(Is, n, exec().gmin(), constants::T0());
-}
+} // namespace netlist::analog
-NETLIB_UPDATE_TERMINALS(D)
+namespace netlist::devices
{
- m_D.update_diode(deltaV());
- const nl_double G = m_D.G();
- const nl_double I = m_D.Ieq();
- set_mat( G, -G, -I,
- -G, G, I);
- //set(m_D.G(), 0.0, m_D.Ieq());
-}
-
-
- } //namespace analog
-
- namespace devices {
- 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
+ // clang-format off
+ 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, Z, "ZDIODE", "MODEL")
+ NETLIB_DEVICE_IMPL_NS(analog, VS, "VS", "V")
+ NETLIB_DEVICE_IMPL_NS(analog, CS, "CS", "I")
+ // clang-format on
+} // namespace netlist::devices
diff --git a/src/lib/netlist/analog/nlid_twoterm.h b/src/lib/netlist/analog/nlid_twoterm.h
index 1dc639008c4..d84356fe82d 100644
--- a/src/lib/netlist/analog/nlid_twoterm.h
+++ b/src/lib/netlist/analog/nlid_twoterm.h
@@ -1,245 +1,360 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * nld_twoterm.h
- *
- * Devices with two terminals ...
- *
- *
- * (k)
- * +-----T-----+
- * | | |
- * | +--+--+ |
- * | | | |
- * | R | |
- * | R | |
- * | R I |
- * | | I | Device n
- * | V+ I |
- * | V | |
- * | V- | |
- * | | | |
- * | +--+--+ |
- * | | |
- * +-----T-----+
- * (l)
- *
- * This is a resistance in series to a voltage source and paralleled by a
- * current source. This is suitable to model voltage sources, current sources,
- * resistors, capacitors, inductances and diodes.
- *
- */
#ifndef NLID_TWOTERM_H_
#define NLID_TWOTERM_H_
-#include "netlist/nl_base.h"
-#include "netlist/nl_setup.h"
-#include "netlist/solver/nld_solver.h"
+///
+/// \file nlid_twoterm.h
+///
+/// Devices with two terminals ...
+///
+///
+/// (k)
+/// +-----T-----+
+/// | | |
+/// | +--+--+ |
+/// | | | |
+/// | R | |
+/// | R | |
+/// | R I |
+/// | | I | Device n
+/// | V+ I |
+/// | V | |
+/// | V- | |
+/// | | | |
+/// | +--+--+ |
+/// | | |
+/// +-----T-----+
+/// (l)
+///
+/// This is a resistance in series to a voltage source and paralleled by a
+/// current source. This is suitable to model voltage sources, current sources,
+/// resistors, capacitors, inductances and diodes.
+///
+//
+
+#include "../nl_setup.h"
+#include "nl_base.h"
#include "nld_generic_models.h"
-#include "plib/pfunction.h"
-#include <cmath>
+#include "solver/nld_solver.h"
+
+#include "plib/pfunction.h"
// -----------------------------------------------------------------------------
// Implementation
// -----------------------------------------------------------------------------
-namespace netlist
-{
-namespace analog
+namespace netlist::analog
{
- // -----------------------------------------------------------------------------
- // 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;
- }
+ // -------------------------------------------------------------------------
+ // nld_two_terminal
+ // -------------------------------------------------------------------------
- NETLIB_OBJECT(twoterm)
+ class nld_two_terminal : public base_device_t
{
- NETLIB_CONSTRUCTOR_EX(twoterm, bool terminals_owned = false)
- , 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)
+ public:
+ nld_two_terminal(constructor_param_t data)
+ : base_device_t(data)
+ , m_P(*this, "1", &m_N, NETLIB_DELEGATE(terminal_handler))
+ , m_N(*this, "2", &m_P, NETLIB_DELEGATE(terminal_handler))
{
}
- terminal_t m_P;
- terminal_t m_N;
+ // This constructor covers the case in which the terminals are "owned"
+ // by the device using a two_terminal. In this case it passes
+ // the terminal handler on to the terminals.
+
+ nld_two_terminal(base_device_t &owner, const pstring &name,
+ nl_delegate owner_delegate)
+ : base_device_t(
+ constructor_data_t{owner.state(), owner.name() + "." + name})
+ , m_P(owner, name + ".1", &m_N, owner_delegate)
+ , m_N(owner, name + ".2", &m_P, owner_delegate)
+ {
+ }
- //NETLIB_UPDATE_TERMINALSI() { }
- //NETLIB_RESETI() { }
+ // NETLIB_UPDATE_TERMINALSI() { }
+ // NETLIB_RESETI() {}
public:
+ NETLIB_HANDLERI(terminal_handler);
- NETLIB_UPDATEI();
+ solver::matrix_solver_t *solver() const noexcept;
- void solve_now();
+ void solve_now() const;
- void solve_later(netlist_time delay = netlist_time::quantum());
+ template <typename F>
+ void change_state(F f) const
+ {
+ auto *solv(solver());
+ if (solv)
+ solv->change_state(f);
+ }
- void set_G_V_I(const nl_double G, const nl_double V, const nl_double I)
+ void set_G_V_I(nl_fptype G, nl_fptype V, nl_fptype I) const noexcept
{
- /* GO, GT, I */
+ // GO, GT, I
m_P.set_go_gt_I( -G, G, ( V) * G - I);
m_N.set_go_gt_I( -G, G, ( -V) * G + I);
}
- nl_double deltaV() const
+ nl_fptype deltaV() const noexcept
{
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 rhs1,
- const nl_double a21, const nl_double a22, const nl_double rhs2)
+ nl_fptype V1P() const noexcept { return m_P.net().Q_Analog(); }
+
+ nl_fptype V2N() const noexcept { return m_N.net().Q_Analog(); }
+
+ void set_mat(nl_fptype a11, nl_fptype a12, nl_fptype rhs1, //
+ nl_fptype a21, nl_fptype a22, nl_fptype rhs2 //
+ ) const noexcept
{
- /* GO, GT, I */
+ // GO, GT, I
m_P.set_go_gt_I(a12, a11, rhs1);
m_N.set_go_gt_I(a21, a22, rhs2);
}
+ void set_mat(const std::array<std::array<nl_fptype,3>,2> &a) const noexcept
+ {
+ // GO, GT, I
+ m_P.set_go_gt_I(a[0][1], a[0][0], a[0][2]);
+ m_N.set_go_gt_I(a[1][0], a[1][1], a[1][2]);
+ }
+
+ void clear_mat() const noexcept
+ {
+ const auto z = nlconst::zero();
+ // GO, GT, I
+ m_P.set_go_gt_I(z, z, z);
+ m_N.set_go_gt_I(z, z, z);
+ }
+
+ /// \brief Get a const reference to the m_P terminal
+ ///
+ /// This is typically called during initialization to connect
+ /// terminals.
+ ///
+ /// \returns Reference to m_P terminal.
+ const terminal_t &P() const noexcept { return m_P; }
+
+ /// \brief Get a const reference to the m_N terminal
+ ///
+ /// This is typically called during initialization to connect
+ /// terminals.
+ ///
+ /// \returns Reference to m_N terminal.
+ const terminal_t &N() const noexcept { return m_N; }
+
+ /// \brief Get a reference to the m_P terminal
+ ///
+ /// This call is only allowed from the core. Device code should never
+ /// need to call this.
+ ///
+ /// \returns Reference to m_P terminal.
+ terminal_t &setup_P() noexcept { return m_P; }
+
+ /// \brief Get a reference to the m_N terminal
+ ///
+ /// This call is only allowed from the core. Device code should never
+ /// need to call this.
+ ///
+ /// \returns Reference to m_P terminal.
+ terminal_t &setup_N() noexcept { return m_N; }
+
private:
+ terminal_t m_P;
+ terminal_t m_N;
};
-
- // -----------------------------------------------------------------------------
+ // -------------------------------------------------------------------------
// nld_R
- // -----------------------------------------------------------------------------
+ // -------------------------------------------------------------------------
- NETLIB_OBJECT_DERIVED(R_base, twoterm)
+ class nld_R_base : public nld_two_terminal
{
- NETLIB_CONSTRUCTOR_DERIVED(R_base, twoterm)
+ public:
+ nld_R_base(constructor_param_t data)
+ : nld_two_terminal(data)
{
}
- void set_R(const nl_double R)
+ void set_R(nl_fptype R) const noexcept
{
- const nl_double G = plib::constants<nl_double>::one() / R;
- set_mat( G, -G, 0.0,
- -G, G, 0.0);
+ const nl_fptype G = plib::reciprocal(R);
+ set_mat(G, -G, nlconst::zero(), //
+ -G, G, nlconst::zero());
}
- NETLIB_RESETI();
+ void set_G(nl_fptype G) const noexcept
+ {
+ set_mat(G, -G, nlconst::zero(), //
+ -G, G, nlconst::zero());
+ }
- protected:
- //NETLIB_UPDATEI();
+ // NETLIB_RESETI();
+ protected:
+ // NETLIB_UPDATEI();
};
- NETLIB_OBJECT_DERIVED(R, R_base)
+ class nld_R : public nld_R_base
{
- NETLIB_CONSTRUCTOR_DERIVED(R, R_base)
- , m_R(*this, "R", 1e9)
+ public:
+ nld_R(constructor_param_t data)
+ : nld_R_base(data)
+ , m_R(*this, "R", nlconst::magic(1e9))
{
}
-
protected:
-
- //NETLIB_UPDATEI() { }
- NETLIB_RESETI()
- {
- NETLIB_NAME(twoterm)::reset();
- set_R(std::max(m_R(), exec().gmin()));
- }
+ NETLIB_RESETI() { set_R(std::max(m_R(), exec().gmin())); }
NETLIB_UPDATE_PARAMI()
{
- solve_now();
- set_R(std::max(m_R(), exec().gmin()));
+ // FIXME: We only need to update the net first if this is a time
+ // stepping net
+ change_state([this]() { set_R(std::max(m_R(), exec().gmin())); });
}
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;
+ param_fp_t m_R;
+ // protect set_R ... it's a recipe to disaster when used to bypass the
+ // parameter
+ using nld_R_base::set_G;
+ using nld_R_base::set_R;
};
- // -----------------------------------------------------------------------------
+ // -------------------------------------------------------------------------
// nld_POT
- // -----------------------------------------------------------------------------
+ // -------------------------------------------------------------------------
- NETLIB_OBJECT(POT)
+ class nld_POT : public base_device_t
{
- NETLIB_CONSTRUCTOR(POT)
+ public:
+ nld_POT(constructor_param_t data)
+ : base_device_t(data)
, m_R1(*this, "_R1")
, m_R2(*this, "_R2")
, m_R(*this, "R", 10000)
- , m_Dial(*this, "DIAL", 0.5)
+ , m_Dial(*this, "DIAL", nlconst::half())
, m_DialIsLog(*this, "DIALLOG", false)
, m_Reverse(*this, "REVERSE", false)
{
- register_subalias("1", m_R1.m_P);
- register_subalias("2", m_R1.m_N);
- register_subalias("3", m_R2.m_N);
-
- connect(m_R2.m_P, m_R1.m_N);
+ register_sub_alias("1", m_R1().P());
+ register_sub_alias("2", m_R1().N());
+ register_sub_alias("3", m_R2().N());
+ connect(m_R2().P(), m_R1().N());
}
- //NETLIB_UPDATEI();
+ // NETLIB_UPDATEI();
NETLIB_RESETI();
NETLIB_UPDATE_PARAMI();
private:
- NETLIB_SUB(R_base) m_R1;
- NETLIB_SUB(R_base) m_R2;
+ NETLIB_SUB_NS(analog, R_base) m_R1;
+ NETLIB_SUB_NS(analog, R_base) m_R2;
- param_double_t m_R;
- param_double_t m_Dial;
+ param_fp_t m_R;
+ param_fp_t m_Dial;
param_logic_t m_DialIsLog;
param_logic_t m_Reverse;
};
- NETLIB_OBJECT(POT2)
+ class nld_POT2 : public base_device_t
{
- NETLIB_CONSTRUCTOR(POT2)
+ public:
+ nld_POT2(constructor_param_t data)
+ : base_device_t(data)
, m_R1(*this, "_R1")
- , m_R(*this, "R", 10000)
- , m_Dial(*this, "DIAL", 0.5)
+ , m_R(*this, "R", nlconst::magic(10000.0))
+ , m_Dial(*this, "DIAL", nlconst::half())
, m_DialIsLog(*this, "DIALLOG", false)
, m_Reverse(*this, "REVERSE", false)
{
- register_subalias("1", m_R1.m_P);
- register_subalias("2", m_R1.m_N);
-
+ register_sub_alias("1", m_R1().P());
+ register_sub_alias("2", m_R1().N());
}
- //NETLIB_UPDATEI();
+ // NETLIB_UPDATEI();
NETLIB_RESETI();
NETLIB_UPDATE_PARAMI();
private:
- NETLIB_SUB(R_base) m_R1;
+ NETLIB_SUB_NS(analog, R_base) m_R1;
- param_double_t m_R;
- param_double_t m_Dial;
+ param_fp_t m_R;
+ param_fp_t m_Dial;
param_logic_t m_DialIsLog;
param_logic_t m_Reverse;
};
- // -----------------------------------------------------------------------------
+ // -------------------------------------------------------------------------
// nld_C
- // -----------------------------------------------------------------------------
+ // -------------------------------------------------------------------------
+
+#if 1
+ class nld_C : public nld_two_terminal
+ {
+ public:
+ nld_C(constructor_param_t data)
+ : nld_two_terminal(data)
+ , m_C(*this, "C", nlconst::magic(1e-6))
+ , m_cap(*this, "m_cap")
+ {
+ }
+
+ NETLIB_IS_TIMESTEP(true)
+ NETLIB_TIMESTEPI()
+ {
+ if (ts_type == detail::time_step_type::FORWARD)
+ {
+ // G, Ieq
+ const auto res(m_cap.time_step(m_C(), deltaV(), step));
+ const nl_fptype G = res.first;
+ const nl_fptype I = res.second;
+ set_mat(G, -G, -I, //
+ -G, G, I);
+ }
+ else
+ m_cap.restore_state();
+ }
+
+ NETLIB_RESETI() { m_cap.set_parameters(exec().gmin()); }
+
+ /// \brief Set capacitance
+ ///
+ /// This call will set the capacitance. The typical use case are
+ /// are components like BJTs which use this component to model
+ /// internal capacitances. Typically called during initialization.
+ ///
+ /// \param val Capacitance value
+ ///
+ void set_cap_embedded(nl_fptype val) { m_C.set(val); }
+
+ protected:
+ // NETLIB_UPDATEI();
+ // FIXME: should be able to change
+ NETLIB_UPDATE_PARAMI() {}
+
+ private:
+ param_fp_t m_C;
+ generic_capacitor_const m_cap;
+ };
- NETLIB_OBJECT_DERIVED(C, twoterm)
+#else
+ // Code preserved as a basis for a current/voltage controlled capacitor
+ class nld_C : public nld_two_terminal)
{
public:
- NETLIB_CONSTRUCTOR_DERIVED(C, twoterm)
- , m_C(*this, "C", 1e-6)
+ nld_C(constructor_param_t data)
+ : nld_two_terminal(data)
+ , m_C(*this, "C", nlconst::magic(1e-6))
, m_cap(*this, "m_cap")
{
}
@@ -247,55 +362,57 @@ namespace analog
NETLIB_IS_TIMESTEP(true)
NETLIB_TIMESTEPI()
{
- m_cap.timestep(m_C(), deltaV(), step);
+ m_cap.time_step(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);
+ const nl_fptype I = m_cap.Ieq(m_C(), deltaV());
+ const nl_fptype G = m_cap.G(m_C());
+ set_mat(G, -G, -I, //
+ -G, G, I);
}
}
NETLIB_IS_DYNAMIC(m_cap.type() == capacitor_e::VARIABLE_CAPACITY)
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);
+ const nl_fptype I = m_cap.Ieq(m_C(), deltaV());
+ const nl_fptype 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());
- }
+ param_fp_t m_C;
+ NETLIB_RESETI() { m_cap.set_parameters(exec().gmin()); }
protected:
- //NETLIB_UPDATEI();
- NETLIB_UPDATE_PARAMI() { }
+ // NETLIB_UPDATEI();
+ // FIXME: should be able to change
+ NETLIB_UPDATE_PARAMI() {}
private:
- //generic_capacitor<capacitor_e::VARIABLE_CAPACITY> m_cap;
+ // generic_capacitor<capacitor_e::VARIABLE_CAPACITY> m_cap;
generic_capacitor<capacitor_e::CONSTANT_CAPACITY> m_cap;
};
+#endif
- // -----------------------------------------------------------------------------
+ // -------------------------------------------------------------------------
// nld_L
- // -----------------------------------------------------------------------------
+ // -------------------------------------------------------------------------
- NETLIB_OBJECT_DERIVED(L, twoterm)
+ class nld_L : public nld_two_terminal
{
public:
- NETLIB_CONSTRUCTOR_DERIVED(L, twoterm)
- , m_L(*this, "L", 1e-6)
- , m_gmin(0.0)
- , m_G(0.0)
- , m_I(0.0)
+ nld_L(constructor_param_t data)
+ : nld_two_terminal(data)
+ , m_L(*this, "L", nlconst::magic(1e-6))
+ , m_gmin(nlconst::zero())
+ , m_G(*this, "m_G", nlconst::zero())
+ , m_I(*this, "m_I", nlconst::zero())
+ , m_last_I(*this, "m_last_I", nlconst::zero())
+ , m_last_G(*this, "m_last_G", nlconst::zero())
{
- //register_term("1", m_P);
- //register_term("2", m_N);
+ // register_term("1", m_P);
+ // register_term("2", m_N);
}
NETLIB_IS_TIMESTEP(true)
@@ -303,69 +420,91 @@ namespace analog
NETLIB_RESETI();
protected:
- //NETLIB_UPDATEI();
+ // NETLIB_UPDATEI();
NETLIB_UPDATE_PARAMI();
private:
- param_double_t m_L;
+ param_fp_t m_L;
- nl_double m_gmin;
- nl_double m_G;
- nl_double m_I;
+ nl_fptype m_gmin;
+ state_var<nl_fptype> m_G;
+ state_var<nl_fptype> m_I;
+ state_var<nl_fptype> m_last_I;
+ state_var<nl_fptype> m_last_G;
};
- /*! Class representing the diode model paramers.
- * This is the model representation of the diode model. Typically, SPICE uses
- * the following parameters. A "Y" in the first column indicates that the
- * parameter is actually used in netlist.
- *
- * |NL? |name |parameter |units|default| example|area |
- * |:--:|:-----|:--------------------------------|:----|------:|-------:|:----:|
- * | Y |IS |saturation current |A |1.0e-14| 1.0e-14| * |
- * | |RS |ohmic resistance |Ohm | 0| 10| * |
- * | Y |N |emission coefficient |- | 1| 1| |
- * | |TT |transit-time |sec | 0| 0.1ns| |
- * | |CJO |zero-bias junction capacitance |F | 0| 2pF| * |
- * | |VJ |junction potential |V | 1| 0.6| |
- * | |M |grading coefficient |- | 0.5| 0.5| |
- * | |EG |band-gap energy |eV | 1.11| 1.11 Si| |
- * | |XTI |saturation-current temp.exp |- | 3|3.0 pn. 2.0 Schottky| |
- * | |KF |flicker noise coefficient |- | 0| | |
- * | |AF |flicker noise exponent |- | 1| | |
- * | |FC |coefficient for forward-bias depletion capacitance formula|-|0.5|| |
- * | |BV |reverse breakdown voltage |V |infinite| 40| |
- * | |IBV |current at breakdown voltage |V | 0.001| | |
- * | |TNOM |parameter measurement temperature|deg C| 27| 50| |
- *
- */
-
- class diode_model_t : public param_model_t
+ /// \brief Class representing the diode model parameters.
+ ///
+ /// This is the model representation of the diode model. Typically, SPICE
+ /// uses the following parameters. A "Y" in the first column indicates that
+ /// the parameter is actually used in netlist.
+ ///
+ /// NBV, BV and IBV are only used in the ZDIODE model. It is assumed
+ /// that DIODEs are not modeled up to their breakdown voltage.
+ ///
+ /// |NL? |name |parameter |units|default| example|area |
+ /// |:--:|:-----|:--------------------------------|:----|------:|-------:|:----:|
+ /// | Y |IS |saturation current |A |1.0e-14| 1.0e-14| * |
+ /// | |RS |ohmic resistance |Ohm | 0| 10| * |
+ /// | Y |N |emission coefficient |- | 1| 1| |
+ /// | |TT |transit-time |sec | 0| 0.1ns| |
+ /// | |CJO |zero-bias junction capacitance |F | 0| 2pF| * |
+ /// | |VJ |junction potential |V | 1| 0.6| |
+ /// | |M |grading coefficient |- | 0.5| 0.5| |
+ /// | |EG |band-gap energy |eV | 1.11| 1.11 Si| |
+ /// | |XTI |saturation-current temp.exp |- | 3|3.0 pn. 2.0 Schottky| |
+ /// | |KF |flicker noise coefficient |- | 0| | |
+ /// | |AF |flicker noise exponent |- | 1| | |
+ /// | |FC |coefficient for forward-bias depletion capacitance formula|-|0.5|| |
+ /// | Y |NBV |reverse emission coefficient |- | 3| 1| |
+ /// | Y |BV |reverse breakdown voltage |V |infinite| 40| |
+ /// | Y |IBV |current at breakdown voltage |A | 0.001| | |
+ /// | |TNOM |parameter measurement temperature|deg C| 27| 50| |
+ ///
+ class diode_model_t
{
public:
- diode_model_t(device_t &device, const pstring &name, const pstring &val)
- : param_model_t(device, name, val)
- , m_IS(*this, "IS")
- , m_N(*this, "N")
- {}
-
- value_t m_IS; //!< saturation current.
- value_t m_N; //!< emission coefficient.
+ diode_model_t(param_model_t &model)
+ : m_IS(model, "IS")
+ , m_N(model, "N")
+ {
+ }
+
+ param_model_t::value_t m_IS; //!< saturation current.
+ param_model_t::value_t m_N; //!< emission coefficient.
};
+ class zdiode_model_t : public diode_model_t
+ {
+ public:
+ zdiode_model_t(param_model_t &model)
+ : diode_model_t(model)
+ , m_NBV(model, "NBV")
+ , m_BV(model, "BV")
+ , m_IBV(model, "IBV")
+ {
+ }
- // -----------------------------------------------------------------------------
+ param_model_t::value_t m_NBV; //!< reverse emission coefficient.
+ param_model_t::value_t m_BV; //!< reverse breakdown voltage.
+ param_model_t::value_t m_IBV; //!< current at breakdown voltage.
+ };
+
+ // -------------------------------------------------------------------------
// nld_D
- // -----------------------------------------------------------------------------
+ // -------------------------------------------------------------------------
- NETLIB_OBJECT_DERIVED(D, twoterm)
+ class nld_D : public nld_two_terminal
{
public:
- NETLIB_CONSTRUCTOR_DERIVED_EX(D, twoterm, pstring model = "D")
+ nld_D(constructor_param_t data, const pstring &model = "D")
+ : nld_two_terminal(data)
, m_model(*this, "MODEL", model)
+ , m_modacc(m_model)
, m_D(*this, "m_D")
{
- register_subalias("A", m_P);
- register_subalias("K", m_N);
+ register_sub_alias("A", P());
+ register_sub_alias("K", N());
}
NETLIB_IS_DYNAMIC(true)
@@ -373,125 +512,175 @@ namespace analog
NETLIB_RESETI();
protected:
- //NETLIB_UPDATEI();
+ // NETLIB_UPDATEI();
NETLIB_UPDATE_PARAMI();
private:
- diode_model_t m_model;
+ param_model_t m_model;
+ diode_model_t m_modacc;
generic_diode<diode_e::BIPOLAR> m_D;
};
+ // -------------------------------------------------------------------------
+ // nld_Z - Zener Diode
+ // -------------------------------------------------------------------------
- // -----------------------------------------------------------------------------
+ class nld_Z : public nld_two_terminal
+ {
+ public:
+ nld_Z(constructor_param_t data, const pstring &model = "D")
+ : nld_two_terminal(data)
+ , m_model(*this, "MODEL", model)
+ , m_modacc(m_model)
+ , m_D(*this, "m_D")
+ , m_R(*this, "m_R")
+ {
+ register_sub_alias("A", P());
+ register_sub_alias("K", N());
+ }
+
+ NETLIB_IS_DYNAMIC(true)
+ NETLIB_UPDATE_TERMINALSI();
+ NETLIB_RESETI();
+
+ protected:
+ // NETLIB_UPDATEI();
+ NETLIB_UPDATE_PARAMI();
+
+ private:
+ param_model_t m_model;
+ zdiode_model_t m_modacc;
+ generic_diode<diode_e::BIPOLAR> m_D;
+ // REVERSE diode
+ generic_diode<diode_e::BIPOLAR> m_R;
+ };
+
+ // -------------------------------------------------------------------------
// nld_VS - Voltage source
//
// netlist voltage source must have inner resistance
- // -----------------------------------------------------------------------------
+ // -------------------------------------------------------------------------
- NETLIB_OBJECT_DERIVED(VS, twoterm)
+ class nld_VS : public nld_two_terminal
{
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->state().run_state_manager())
- , m_funcparam({0.0})
+ nld_VS(constructor_param_t data)
+ : nld_two_terminal(data)
+ , m_t(*this, "m_t", nlconst::zero())
+ , m_R(*this, "RI", nlconst::magic(0.1))
+ , m_V(*this, "V", nlconst::zero())
+ , m_func(*this, "FUNC", "")
+ , m_compiled(*this, "m_compiled")
+ , m_funcparam({nlconst::zero()})
{
- register_subalias("P", m_P);
- register_subalias("N", m_N);
- if (m_func() != "")
- m_compiled.compile(std::vector<pstring>({{"T"}}), m_func());
+ register_sub_alias("P", P());
+ register_sub_alias("N", N());
+ if (!m_func().empty())
+ m_compiled->compile(m_func(),
+ std::vector<pstring>({{pstring("T")}}));
}
- NETLIB_IS_TIMESTEP(m_func() != "")
+ NETLIB_IS_TIMESTEP(!m_func().empty())
NETLIB_TIMESTEPI()
{
- m_t += step;
- m_funcparam[0] = m_t;
- this->set_G_V_I(1.0 / m_R(),
- m_compiled.evaluate(m_funcparam),
- 0.0);
+ if (ts_type == detail::time_step_type::FORWARD)
+ {
+ m_t += step;
+ m_funcparam[0] = m_t;
+ this->set_G_V_I(plib::reciprocal(m_R()),
+ m_compiled->evaluate(m_funcparam),
+ nlconst::zero());
+ }
+ else
+ m_t -= step; // only need to restore state, will be called again
}
protected:
- // 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);
+ nld_two_terminal::reset();
+ this->set_G_V_I(plib::reciprocal(m_R()), m_V(), nlconst::zero());
}
private:
- state_var<double> m_t;
- param_double_t m_R;
- param_double_t m_V;
- param_str_t m_func;
- plib::pfunction m_compiled;
- std::vector<double> m_funcparam;
+ state_var<nl_fptype> m_t;
+ param_fp_t m_R;
+ param_fp_t m_V;
+ param_str_t m_func;
+ state_var<plib::pfunction<nl_fptype>> m_compiled;
+ std::vector<nl_fptype> m_funcparam;
};
- // -----------------------------------------------------------------------------
+ // -------------------------------------------------------------------------
// nld_CS - Current source
- // -----------------------------------------------------------------------------
+ // -------------------------------------------------------------------------
- NETLIB_OBJECT_DERIVED(CS, twoterm)
+ class nld_CS : public nld_two_terminal
{
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->state().run_state_manager())
- , m_funcparam({0.0})
+ nld_CS(constructor_param_t data)
+ : nld_two_terminal(data)
+ , m_t(*this, "m_t", nlconst::zero())
+ , m_I(*this, "I", nlconst::one())
+ , m_func(*this, "FUNC", "")
+ , m_compiled(*this, "m_compiled")
+ , m_funcparam({nlconst::zero()})
{
- register_subalias("P", m_P);
- register_subalias("N", m_N);
- if (m_func() != "")
- m_compiled.compile(std::vector<pstring>({{"T"}}), m_func());
+ register_sub_alias("P", "1");
+ register_sub_alias("N", "2");
+ if (!m_func().empty())
+ m_compiled->compile(m_func(),
+ std::vector<pstring>({{pstring("T")}}));
}
- NETLIB_IS_TIMESTEP(m_func() != "")
+ NETLIB_IS_TIMESTEP(!m_func().empty())
NETLIB_TIMESTEPI()
{
- m_t += step;
- m_funcparam[0] = m_t;
- const double I = m_compiled.evaluate(m_funcparam);
- set_mat(0.0, 0.0, -I,
- 0.0, 0.0, I);
+ if (ts_type == detail::time_step_type::FORWARD)
+ {
+ m_t += step;
+ m_funcparam[0] = m_t;
+ const nl_fptype I = m_compiled->evaluate(m_funcparam);
+ const auto zero(nlconst::zero());
+ set_mat(zero, zero, -I, //
+ zero, zero, I);
+ }
+ else
+ m_t -= step;
}
protected:
-
- //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());
+ nld_two_terminal::reset();
+ const auto zero(nlconst::zero());
+ set_mat(zero, zero, -m_I(), //
+ zero, zero, m_I());
}
NETLIB_UPDATE_PARAMI()
{
- solve_now();
- set_mat(0.0, 0.0, -m_I(),
- 0.0, 0.0, m_I());
+ // FIXME: We only need to update the net first if this is a time
+ // stepping net
+ // FIXME: works only for CS without function
+ change_state(
+ [this]()
+ {
+ const auto zero(nlconst::zero());
+ set_mat(zero, zero, -m_I(), //
+ zero, zero, m_I());
+ });
}
-
private:
- state_var<double> m_t;
- param_double_t m_I;
- param_str_t m_func;
- plib::pfunction m_compiled;
- std::vector<double> m_funcparam;
+ state_var<nl_fptype> m_t;
+ param_fp_t m_I;
+ param_str_t m_func;
+ state_var<plib::pfunction<nl_fptype>> m_compiled;
+ std::vector<nl_fptype> m_funcparam;
};
+} // namespace netlist::analog
-} // namespace analog
-} // namespace netlist
-
-#endif /* NLD_TWOTERM_H_ */
+#endif // NLD_TWOTERM_H_