summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/netlist/analog/nld_generic_models.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/netlist/analog/nld_generic_models.h')
-rw-r--r--src/lib/netlist/analog/nld_generic_models.h348
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_