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