diff options
Diffstat (limited to 'src/lib/netlist/analog/nlid_twoterm.h')
-rw-r--r-- | src/lib/netlist/analog/nlid_twoterm.h | 338 |
1 files changed, 276 insertions, 62 deletions
diff --git a/src/lib/netlist/analog/nlid_twoterm.h b/src/lib/netlist/analog/nlid_twoterm.h index 7778f5f4585..470fb35c96e 100644 --- a/src/lib/netlist/analog/nlid_twoterm.h +++ b/src/lib/netlist/analog/nlid_twoterm.h @@ -33,8 +33,12 @@ #ifndef NLID_TWOTERM_H_ #define NLID_TWOTERM_H_ -#include "../nl_base.h" -#include "../plib/pfunction.h" +#include "netlist/nl_base.h" +#include "netlist/nl_setup.h" +#include "netlist/solver/nld_solver.h" +#include "plib/pfunction.h" + +#include <cmath> // ----------------------------------------------------------------------------- // Implementation @@ -48,14 +52,27 @@ namespace netlist // nld_twoterm // ----------------------------------------------------------------------------- + template <class C> + inline core_device_t &bselect(bool b, C &d1, core_device_t &d2) + { + auto *h = dynamic_cast<core_device_t *>(&d1); + return b ? *h : d2; + } + template<> + inline core_device_t &bselect(bool b, netlist_state_t &d1, core_device_t &d2) + { + plib::unused_var(d1); + if (b) + throw nl_exception("bselect with netlist and b==true"); + return d2; + } + NETLIB_OBJECT(twoterm) { NETLIB_CONSTRUCTOR_EX(twoterm, bool terminals_owned = false) - , m_P(bselect(terminals_owned, owner, *this), (terminals_owned ? name + "." : "") + "1") - , m_N(bselect(terminals_owned, owner, *this), (terminals_owned ? name + "." : "") + "2") + , m_P(bselect(terminals_owned, owner, *this), (terminals_owned ? name + "." : "") + "1", &m_N) + , m_N(bselect(terminals_owned, owner, *this), (terminals_owned ? name + "." : "") + "2", &m_P) { - m_P.m_otherterm = &m_N; - m_N.m_otherterm = &m_P; } terminal_t m_P; @@ -63,36 +80,36 @@ NETLIB_OBJECT(twoterm) //NETLIB_UPDATE_TERMINALSI() { } //NETLIB_RESETI() { } - NETLIB_UPDATEI(); public: - /* inline */ void set(const nl_double G, const nl_double V, const nl_double I) + + NETLIB_UPDATEI(); + + void solve_now(); + + void solve_later(netlist_time delay = netlist_time::from_nsec(1)); + + void set_G_V_I(const nl_double G, const nl_double V, const nl_double I) { /* GO, GT, I */ - m_P.set( G, G, ( V) * G - I); - m_N.set( G, G, ( -V) * G + I); + m_P.set_go_gt_I( -G, G, ( V) * G - I); + m_N.set_go_gt_I( -G, G, ( -V) * G + I); } - /* inline */ nl_double deltaV() const + nl_double deltaV() const { return m_P.net().Q_Analog() - m_N.net().Q_Analog(); } - void set_mat(const nl_double a11, const nl_double a12, const nl_double r1, - const nl_double a21, const nl_double a22, const nl_double r2) + void set_mat(const nl_double a11, const nl_double a12, const nl_double rhs1, + const nl_double a21, const nl_double a22, const nl_double rhs2) { /* GO, GT, I */ - m_P.set(-a12, a11, r1); - m_N.set(-a21, a22, r2); + m_P.set_go_gt_I(a12, a11, rhs1); + m_N.set_go_gt_I(a21, a22, rhs2); } private: - template <class C> - static core_device_t &bselect(bool b, C &d1, core_device_t &d2) - { - core_device_t *h = dynamic_cast<core_device_t *>(&d1); - return b ? *h : d2; - } }; @@ -106,17 +123,17 @@ NETLIB_OBJECT_DERIVED(R_base, twoterm) { } -public: - inline void set_R(const nl_double R) + void set_R(const nl_double R) { - const nl_double G = NL_FCONST(1.0) / R; + const nl_double G = plib::constants<nl_double>::one() / R; set_mat( G, -G, 0.0, -G, G, 0.0); } -protected: NETLIB_RESETI(); - NETLIB_UPDATEI(); + +protected: + //NETLIB_UPDATEI(); }; @@ -127,7 +144,6 @@ NETLIB_OBJECT_DERIVED(R, R_base) { } - param_double_t m_R; protected: @@ -136,6 +152,7 @@ protected: NETLIB_UPDATE_PARAMI(); private: + param_double_t m_R; /* protect set_R ... it's a recipe to desaster when used to bypass the parameter */ using NETLIB_NAME(R_base)::set_R; }; @@ -151,7 +168,8 @@ NETLIB_OBJECT(POT) , m_R2(*this, "_R2") , m_R(*this, "R", 10000) , m_Dial(*this, "DIAL", 0.5) - , m_DialIsLog(*this, "DIALLOG", 0) + , m_DialIsLog(*this, "DIALLOG", false) + , m_Reverse(*this, "REVERSE", false) { register_subalias("1", m_R1.m_P); register_subalias("2", m_R1.m_N); @@ -172,6 +190,7 @@ private: param_double_t m_R; param_double_t m_Dial; param_logic_t m_DialIsLog; + param_logic_t m_Reverse; }; NETLIB_OBJECT(POT2) @@ -180,8 +199,8 @@ NETLIB_OBJECT(POT2) , m_R1(*this, "_R1") , m_R(*this, "R", 10000) , m_Dial(*this, "DIAL", 0.5) - , m_DialIsLog(*this, "DIALLOG", 0) - , m_Reverse(*this, "REVERSE", 0) + , m_DialIsLog(*this, "DIALLOG", false) + , m_Reverse(*this, "REVERSE", false) { register_subalias("1", m_R1.m_P); register_subalias("2", m_R1.m_N); @@ -201,6 +220,83 @@ private: param_logic_t m_Reverse; }; +// ----------------------------------------------------------------------------- +// A generic capacitor model +// ----------------------------------------------------------------------------- + +enum class capacitor_e +{ + CHARGE_CONSERVING, + CONSTANT_CAPACITY +}; + +template <capacitor_e TYPE> +class generic_capacitor +{ +}; + + +template <> +class generic_capacitor<capacitor_e::CHARGE_CONSERVING> +{ +public: + generic_capacitor(device_t &dev, const pstring &name) + : m_h(dev, name + ".m_h", 0.0) + , m_charge(dev, name + ".m_charge", 0.0) + , m_gmin(0.0) + { + } + + constexpr capacitor_e type() const { return capacitor_e::CHARGE_CONSERVING; } + + constexpr nl_double G(nl_double cap) const + { + return cap * m_h + m_gmin; + } + + constexpr nl_double Ieq(nl_double cap, nl_double v) const + { + return m_h * (cap * v - m_charge) - G(cap) * v; + } + + void timestep(nl_double cap, nl_double v, nl_double step) + { + m_h = 1.0 / step; + m_charge = cap * v; + } + + void setparams(nl_double gmin) { m_gmin = gmin; } + +private: + state_var<double> m_h; + state_var<double> m_charge; + nl_double m_gmin; +}; + +template <> +class generic_capacitor<capacitor_e::CONSTANT_CAPACITY> +{ +public: + generic_capacitor(device_t &dev, const pstring &name) + : m_h(dev, name + ".m_h", 0.0) + , m_gmin(0.0) + { + } + + constexpr capacitor_e type() const { return capacitor_e::CONSTANT_CAPACITY; } + constexpr nl_double G(nl_double cap) const { return cap * m_h + m_gmin; } + constexpr nl_double Ieq(nl_double cap, nl_double v) const { return - G(cap) * v; } + + void timestep(nl_double cap, nl_double v, nl_double step) + { + plib::unused_var(cap, v); + m_h = 1.0 / step; + } + void setparams(nl_double gmin) { m_gmin = gmin; } +private: + state_var<nl_double> m_h; + nl_double m_gmin; +}; // ----------------------------------------------------------------------------- // nld_C @@ -211,25 +307,45 @@ NETLIB_OBJECT_DERIVED(C, twoterm) public: NETLIB_CONSTRUCTOR_DERIVED(C, twoterm) , m_C(*this, "C", 1e-6) - , m_GParallel(0.0) + , m_cap(*this, "m_cap") { - //register_term("1", m_P); - //register_term("2", m_N); } NETLIB_IS_TIMESTEP(true) - NETLIB_TIMESTEPI(); + NETLIB_TIMESTEPI() + { + m_cap.timestep(m_C(), deltaV(), step); + if (m_cap.type() == capacitor_e::CONSTANT_CAPACITY) + { + const nl_double I = m_cap.Ieq(m_C(), deltaV()); + const nl_double G = m_cap.G(m_C()); + set_mat( G, -G, -I, + -G, G, I); + } + } + + NETLIB_IS_DYNAMIC(m_cap.type() == capacitor_e::CHARGE_CONSERVING) + NETLIB_UPDATE_TERMINALSI() + { + const nl_double I = m_cap.Ieq(m_C(), deltaV()); + const nl_double G = m_cap.G(m_C()); + set_mat( G, -G, -I, + -G, G, I); + } param_double_t m_C; + NETLIB_RESETI() + { + m_cap.setparams(exec().gmin()); + } protected: - NETLIB_RESETI(); - NETLIB_UPDATEI(); - NETLIB_UPDATE_PARAMI(); + //NETLIB_UPDATEI(); + NETLIB_UPDATE_PARAMI() { } private: - nl_double m_GParallel; - + generic_capacitor<capacitor_e::CHARGE_CONSERVING> m_cap; + //generic_capacitor<capacitor_e::CONSTANT_CAPACITY> m_cap; }; // ----------------------------------------------------------------------------- @@ -241,7 +357,7 @@ NETLIB_OBJECT_DERIVED(L, twoterm) public: NETLIB_CONSTRUCTOR_DERIVED(L, twoterm) , m_L(*this, "L", 1e-6) - , m_GParallel(0.0) + , m_gmin(0.0) , m_G(0.0) , m_I(0.0) { @@ -251,16 +367,16 @@ public: NETLIB_IS_TIMESTEP(true) NETLIB_TIMESTEPI(); - - param_double_t m_L; + NETLIB_RESETI(); protected: - NETLIB_RESETI(); - NETLIB_UPDATEI(); + //NETLIB_UPDATEI(); NETLIB_UPDATE_PARAMI(); private: - nl_double m_GParallel; + param_double_t m_L; + + nl_double m_gmin; nl_double m_G; nl_double m_I; }; @@ -269,14 +385,82 @@ private: // A generic diode model to be used in other devices (Diode, BJT ...) // ----------------------------------------------------------------------------- +enum class diode_e +{ + BIPOLAR, + MOS +}; + +template <diode_e TYPE> class generic_diode { public: - generic_diode(device_t &dev, pstring name); + generic_diode(device_t &dev, const pstring &name) + : m_Vd(dev, name + ".m_Vd", 0.7) + , m_Id(dev, name + ".m_Id", 0.0) + , m_G(dev, name + ".m_G", 1e-15) + , m_Vt(0.0) + , m_Vmin(0.0) // not used in MOS model + , m_Is(0.0) + , m_logIs(0.0) + , m_n(0.0) + , m_gmin(1e-15) + , m_VtInv(0.0) + , m_Vcrit(0.0) + { + set_param(1e-15, 1, 1e-15, 300.0); + } + + void update_diode(const double nVd) + { + if (TYPE == diode_e::BIPOLAR && nVd < m_Vmin) + { + m_Vd = nVd; + m_G = m_gmin; + m_Id = - m_Is; + } + else if (TYPE == diode_e::MOS && nVd < constants::zero()) + { + m_Vd = nVd; + m_G = m_Is * m_VtInv + m_gmin; + m_Id = m_G * m_Vd; + } + // FIXME: For MOS, stop here, the critical code path will not converge + else if (TYPE == diode_e::MOS || nVd < m_Vcrit) + { + m_Vd = nVd; + const double IseVDVt = std::exp(std::min(700.0, m_logIs + m_Vd * m_VtInv)); + m_Id = IseVDVt - m_Is; + m_G = IseVDVt * m_VtInv + m_gmin; + } + else + { + const double a = std::max((nVd - m_Vd) * m_VtInv, constants::cast(-0.99)); + m_Vd = m_Vd + std::log1p(a) * m_Vt; + //const double IseVDVt = m_Is * std::exp(m_Vd * m_VtInv); + const double IseVDVt = std::exp(m_logIs + m_Vd * m_VtInv); + m_Id = IseVDVt - m_Is; + m_G = IseVDVt * m_VtInv + m_gmin; + } + } + + + void set_param(const double Is, const double n, double gmin, double temp) + { + static constexpr double csqrt2 = 1.414213562373095048801688724209; //std::sqrt(2.0); + m_Is = Is; + m_logIs = std::log(Is); + m_n = n; + m_gmin = gmin; - void update_diode(const double nVd); + m_Vt = m_n * temp * constants::k_b() / constants::Q_e(); + + m_Vmin = -5.0 * m_Vt; + + m_Vcrit = m_Vt * std::log(m_Vt / m_Is / csqrt2); + m_VtInv = 1.0 / m_Vt; + } - void set_param(const double Is, const double n, double gmin); double I() const { return m_Id; } double G() const { return m_G; } @@ -367,15 +551,15 @@ public: NETLIB_IS_DYNAMIC(true) NETLIB_UPDATE_TERMINALSI(); - - diode_model_t m_model; + NETLIB_RESETI(); protected: - NETLIB_RESETI(); - NETLIB_UPDATEI(); + //NETLIB_UPDATEI(); NETLIB_UPDATE_PARAMI(); - generic_diode m_D; +private: + diode_model_t m_model; + generic_diode<diode_e::BIPOLAR> m_D; }; @@ -389,10 +573,11 @@ NETLIB_OBJECT_DERIVED(VS, twoterm) { public: NETLIB_CONSTRUCTOR_DERIVED(VS, twoterm) + , m_t(*this, "m_t", 0.0) , m_R(*this, "R", 0.1) , m_V(*this, "V", 0.0) , m_func(*this,"FUNC", "") - , m_compiled(this->name() + ".FUNCC", this, this->netlist().state()) + , m_compiled(this->name() + ".FUNCC", this, this->state().run_state_manager()) { register_subalias("P", m_P); register_subalias("N", m_N); @@ -401,12 +586,26 @@ public: } NETLIB_IS_TIMESTEP(m_func() != "") - NETLIB_TIMESTEPI(); + + NETLIB_TIMESTEPI() + { + m_t += step; + this->set_G_V_I(1.0 / m_R(), + m_compiled.evaluate(std::vector<double>({m_t})), + 0.0); + } protected: - NETLIB_UPDATEI(); - NETLIB_RESETI(); + // NETLIB_UPDATEI() { NETLIB_NAME(twoterm)::update(time); } + + NETLIB_RESETI() + { + NETLIB_NAME(twoterm)::reset(); + this->set_G_V_I(1.0 / m_R(), m_V(), 0.0); + } +private: + state_var<double> m_t; param_double_t m_R; param_double_t m_V; param_str_t m_func; @@ -421,9 +620,10 @@ NETLIB_OBJECT_DERIVED(CS, twoterm) { public: NETLIB_CONSTRUCTOR_DERIVED(CS, twoterm) + , m_t(*this, "m_t", 0.0) , m_I(*this, "I", 1.0) , m_func(*this,"FUNC", "") - , m_compiled(this->name() + ".FUNCC", this, this->netlist().state()) + , m_compiled(this->name() + ".FUNCC", this, this->state().run_state_manager()) { register_subalias("P", m_P); register_subalias("N", m_N); @@ -432,19 +632,33 @@ public: } NETLIB_IS_TIMESTEP(m_func() != "") - NETLIB_TIMESTEPI(); + NETLIB_TIMESTEPI() + { + m_t += step; + const double I = m_compiled.evaluate(std::vector<double>({m_t})); + set_mat(0.0, 0.0, -I, + 0.0, 0.0, I); + } + protected: - NETLIB_UPDATEI(); - NETLIB_RESETI(); + //NETLIB_UPDATEI() { NETLIB_NAME(twoterm)::update(time); } + NETLIB_RESETI() + { + NETLIB_NAME(twoterm)::reset(); + set_mat(0.0, 0.0, -m_I(), + 0.0, 0.0, m_I()); + } +private: + state_var<double> m_t; param_double_t m_I; param_str_t m_func; plib::pfunction m_compiled; }; - } //namespace devices + } // namespace analog } // namespace netlist #endif /* NLD_TWOTERM_H_ */ |