From 1af4b9f870002c1756678dba95b95b9be9487351 Mon Sep 17 00:00:00 2001 From: couriersud Date: Fri, 17 Apr 2020 23:08:31 +0200 Subject: [PATCH] netlist: Document alternative approach to CD4016. (nw) The CD4016 is an analog switch IC. Document an experimental approach to implement the analog switch as a 3 terminal element which is completely being dealt with as part as the linear system. The intention was to improve convergence when the switch is in a feedback loop. One example are two-opamp tridiagonal wave generators. Unfortunately the approach did not work out and in addition was performing far worse than the net-separating original code. Also updated comment in nld_generic_models.h. --- src/lib/netlist/analog/nld_generic_models.h | 4 +- src/lib/netlist/devices/nld_4066.cpp | 144 ++++++++++++++------ 2 files changed, 109 insertions(+), 39 deletions(-) diff --git a/src/lib/netlist/analog/nld_generic_models.h b/src/lib/netlist/analog/nld_generic_models.h index 59ba3455966..ef5d733e1f9 100644 --- a/src/lib/netlist/analog/nld_generic_models.h +++ b/src/lib/netlist/analog/nld_generic_models.h @@ -14,7 +14,9 @@ // // Set to 0 to use a linearized diode model in the range exceeding // maximum dissipation. The intention is to have a faster -// convergence but this yet not really is observable +// convergence. On selected circuits (LM3900 trapezoidal) this is +// observable and has a 10% impact. +// FIXME: More research needed // #define USE_TEXTBOOK_DIODE (1) diff --git a/src/lib/netlist/devices/nld_4066.cpp b/src/lib/netlist/devices/nld_4066.cpp index 018ae5e12bd..38171b33e50 100644 --- a/src/lib/netlist/devices/nld_4066.cpp +++ b/src/lib/netlist/devices/nld_4066.cpp @@ -11,6 +11,20 @@ #include "netlist/solver/nld_solver.h" #include "nlid_system.h" +// This is an experimental approach to implement the analog switch. +// This will make the switch a 3 terminal element which is completely +// being dealt with as part as the linear system. +// +// The intention was to improve convergence when the switch is in a feedback +// loop. One example are two-opamp tridiagonal wave generators. +// Unfortunately the approach did not work out and in addition was performing +// far worse than the net-separating original code. +// +// FIXME: The transfer function needs review +// + +#define USE_DYNAMIC_APPROACH (0) + namespace netlist { namespace devices @@ -27,56 +41,110 @@ namespace netlist { } - NETLIB_RESETI(); - NETLIB_UPDATEI(); + NETLIB_RESETI() + { + // Start in off condition + // FIXME: is ROFF correct? + m_R.set_R(plib::reciprocal(exec().gmin())); + } + + NETLIB_UPDATEI() + { + nl_fptype sup = (m_supply.VCC().Q_Analog() - m_supply.GND().Q_Analog()); + nl_fptype in = m_control() - m_supply.GND().Q_Analog(); + nl_fptype rON = m_base_r() * nlconst::magic(5.0) / sup; + nl_fptype R = -nlconst::one(); + nl_fptype low = nlconst::magic(0.45) * sup; + nl_fptype high = nlconst::magic(0.55) * sup; + bool new_state(false); + if (in < low) + { + R = plib::reciprocal(exec().gmin()); + } + else if (in > high) + { + R = rON; + new_state = true; + } + if (R > nlconst::zero() && (m_last != new_state)) + { + m_last = new_state; + m_R.update(); + m_R.set_R(R); + m_R.solve_later(); + } + } private: nld_power_pins m_supply; analog::NETLIB_SUB(R_base) m_R; - analog_input_t m_control; param_fp_t m_base_r; state_var m_last; }; - NETLIB_RESET(CD4066_GATE) + + NETLIB_OBJECT(CD4066_GATE_DYNAMIC) { - // Start in off condition - // FIXME: is ROFF correct? - m_R.set_R(plib::reciprocal(exec().gmin())); - - } - - NETLIB_UPDATE(CD4066_GATE) - { - nl_fptype sup = (m_supply.VCC().Q_Analog() - m_supply.GND().Q_Analog()); - nl_fptype low = nlconst::magic(0.45) * sup; - nl_fptype high = nlconst::magic(0.55) * sup; - nl_fptype in = m_control() - m_supply.GND().Q_Analog(); - nl_fptype rON = m_base_r() * nlconst::magic(5.0) / sup; - nl_fptype R = -nlconst::one(); - bool new_state(false); - - if (in < low) + NETLIB_CONSTRUCTOR(CD4066_GATE_DYNAMIC) + NETLIB_FAMILY("CD4XXX") + , m_supply(*this, "VDD", "VSS") + , m_R(*this, "R", true) + , m_DUM1(*this, "_DUM1", true) + , m_DUM2(*this, "_DUM2", true) + , m_base_r(*this, "BASER", nlconst::magic(270.0)) + , m_last(*this, "m_last", false) { - R = plib::reciprocal(exec().gmin()); - } - else if (in > high) - { - R = rON; - new_state = true; - } - //printf("%s %f %f %g\n", name().c_str(), sup, in, R); - if (R > nlconst::zero() && (m_last != new_state)) - { - m_last = new_state; - m_R.update(); - m_R.set_R(R); - m_R.solve_later(); - } - } + register_subalias("CTL", m_DUM1.m_P); // Cathode + connect(m_DUM1.m_P, m_DUM2.m_P); + connect(m_DUM1.m_N, m_R.m_P); + connect(m_DUM2.m_N, m_R.m_N); + } + + NETLIB_RESETI() + { + // Start in off condition + // FIXME: is ROFF correct? + } + + NETLIB_UPDATEI() + { + } + + NETLIB_UPDATE_TERMINALSI() + { + nl_fptype sup = (m_supply.VCC().Q_Analog() - m_supply.GND().Q_Analog()); + nl_fptype in = m_DUM1.m_P.net().Q_Analog() - m_supply.GND().Q_Analog(); + nl_fptype rON = m_base_r() * nlconst::magic(5.0) / sup; + nl_fptype R = std::exp(-(in / sup - 0.55) * 25.0) + rON; + nl_fptype G = plib::reciprocal(R); + // dI/dVin = (VR1-VR2)*(1.0/sup*b) * exp((Vin/sup-a) * b) + const auto dfdz = 25.0/(R*sup) * m_R.deltaV(); + const auto Ieq = dfdz * in; + m_R.set_mat( G, -G, 0.0, + -G, G, 0.0); + //VIN VR1 + m_DUM1.set_mat( 0.0, 0.0, 0.0, // IIN + dfdz, 0.0, Ieq); // IR1 + m_DUM2.set_mat( 0.0, 0.0, 0.0, // IIN + -dfdz, 0.0, -Ieq); // IR2 + } + NETLIB_IS_DYNAMIC(true) + + private: + nld_power_pins m_supply; + analog::nld_twoterm m_R; + analog::nld_twoterm m_DUM1; + analog::nld_twoterm m_DUM2; + param_fp_t m_base_r; + state_var m_last; + }; + +#if !USE_DYNAMIC_APPROACH NETLIB_DEVICE_IMPL(CD4066_GATE, "CD4066_GATE", "") - +#else + NETLIB_DEVICE_IMPL_ALIAS(CD4066_GATE, CD4066_GATE_DYNAMIC, "CD4066_GATE", "") +#endif } //namespace devices } // namespace netlist