diff options
Diffstat (limited to 'src/lib/netlist/analog/nld_bjt.cpp')
-rw-r--r-- | src/lib/netlist/analog/nld_bjt.cpp | 708 |
1 files changed, 418 insertions, 290 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 |