diff options
Diffstat (limited to 'src/lib/netlist/analog/nld_generic_models.h')
-rw-r--r-- | src/lib/netlist/analog/nld_generic_models.h | 348 |
1 files changed, 243 insertions, 105 deletions
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_ |