diff options
author | Couriersud <couriersud@users.noreply.github.com> | 2014-01-06 14:39:15 +0000 |
---|---|---|
committer | Couriersud <couriersud@users.noreply.github.com> | 2014-01-06 14:39:15 +0000 |
commit | f7856a86540f42232973d1000e74e22ed82a0c0e (patch) | |
tree | 069959d47741cd42349f00b10eb0e91edbc4b8e0 /src | |
parent | 45d9761cad18873ac1a92e06c2604f1b451f591a (diff) |
Netlist:
- Added Ebers-Moll BJT model. Currently only for NPN.
- Fixed convergence issue in the solver.
- Accuracy for Ebers-Moll must be better than 1e-5 to not generate nano-second spikes.
Typical sound applications should be able to run with less since the spikes are
not audible.
Diffstat (limited to 'src')
-rw-r--r-- | src/emu/netlist/devices/net_lib.c | 2 | ||||
-rw-r--r-- | src/emu/netlist/devices/nld_solver.c | 67 | ||||
-rw-r--r-- | src/emu/netlist/devices/nld_solver.h | 1 | ||||
-rw-r--r-- | src/emu/netlist/devices/nld_twoterm.c | 54 | ||||
-rw-r--r-- | src/emu/netlist/devices/nld_twoterm.h | 291 | ||||
-rw-r--r-- | src/emu/netlist/nl_base.h | 1 | ||||
-rw-r--r-- | src/emu/netlist/nl_parser.c | 4 | ||||
-rw-r--r-- | src/mame/drivers/pong.c | 2 |
8 files changed, 337 insertions, 85 deletions
diff --git a/src/emu/netlist/devices/net_lib.c b/src/emu/netlist/devices/net_lib.c index 83eab69bb69..23cc5ce07c5 100644 --- a/src/emu/netlist/devices/net_lib.c +++ b/src/emu/netlist/devices/net_lib.c @@ -244,6 +244,8 @@ void netlist_factory::initialize() ENTRY(D, NETDEV_D) ENTRY(VCVS, NETDEV_VCVS) ENTRY(VCCS, NETDEV_VCCS) + ENTRY(QPNP_EB, NETDEV_QPNP_EB) + ENTRY(QNPN_EB, NETDEV_QNPN_EB) ENTRY(QPNP_switch, NETDEV_QPNP) ENTRY(QNPN_switch, NETDEV_QNPN) ENTRY(ttl_const, NETDEV_TTL_CONST) diff --git a/src/emu/netlist/devices/nld_solver.c b/src/emu/netlist/devices/nld_solver.c index 9055d5f1781..f78ef10c88e 100644 --- a/src/emu/netlist/devices/nld_solver.c +++ b/src/emu/netlist/devices/nld_solver.c @@ -34,7 +34,9 @@ ATTR_COLD void netlist_matrix_solver_t::setup(netlist_net_t::list_t &nets, NETLI if (!m_steps.contains(&p->netdev())) m_steps.add(&p->netdev()); break; - case netlist_device_t::DIODE: + case netlist_device_t::BJT_EB: + printf("Found ebers moll\n"); + case netlist_device_t::DIODE: //case netlist_device_t::VCVS: //case netlist_device_t::BJT_SWITCH: if (!m_dynamic.contains(&p->netdev())) @@ -82,28 +84,15 @@ ATTR_HOT inline void netlist_matrix_solver_t::update_inputs() } - -ATTR_HOT inline bool netlist_matrix_solver_t::solve() +ATTR_HOT inline int netlist_matrix_solver_t::solve_non_dynamic() { - bool resched; - // FIXME: There may be situations where we *could* need more than one iteration for dynamic elements + bool resched; - int resched_cnt = (is_dynamic() ? /* 0 */ 1 : 1); + int resched_cnt = 0; ATTR_UNUSED netlist_net_t *last_resched_net = NULL; do { resched = false; - /* update all non-linear devices */ - for (dev_list_t::entry_t *p = m_dynamic.first(); p != NULL; p = m_dynamic.next(p)) - switch (p->object()->family()) - { - case netlist_device_t::DIODE: - static_cast<NETLIB_NAME(D) *>(p->object())->update_terminals(); - break; - default: - p->object()->update_terminals(); - break; - } for (netlist_net_t::list_t::entry_t *pn = m_nets.first(); pn != NULL; pn = m_nets.next(pn)) { @@ -140,7 +129,46 @@ ATTR_HOT inline bool netlist_matrix_solver_t::solve() //NL_VERBOSE_OUT(("New: %lld %f %f\n", netlist().time().as_raw(), netlist().time().as_double(), new_val)); } resched_cnt++; - } while ((resched && (resched_cnt < m_resched_loops)) || (resched_cnt <= 1)); + } while (resched && (resched_cnt < m_resched_loops / 2 )); + + return resched_cnt; +} + + +ATTR_HOT inline bool netlist_matrix_solver_t::solve() +{ + bool resched = false; + // FIXME: There may be situations where we *could* need more than one iteration for dynamic elements + + int resched_cnt = 0; + ATTR_UNUSED netlist_net_t *last_resched_net = NULL; + + if (is_dynamic()) + { + int this_resched; + do + { + /* update all non-linear devices */ + for (dev_list_t::entry_t *p = m_dynamic.first(); p != NULL; p = m_dynamic.next(p)) + switch (p->object()->family()) + { + case netlist_device_t::DIODE: + static_cast<NETLIB_NAME(D) *>(p->object())->update_terminals(); + break; + default: + p->object()->update_terminals(); + break; + } + this_resched = solve_non_dynamic(); + resched_cnt += this_resched; + } while (this_resched > 1 && resched_cnt < m_resched_loops); + } + else + { + resched_cnt = solve_non_dynamic(); + } + if (resched_cnt >= m_resched_loops) + resched = true; if (!resched) update_inputs(); @@ -199,7 +227,8 @@ NETLIB_START(solver) register_param("FREQ", m_freq, 48000.0); m_inc = netlist_time::from_hz(m_freq.Value()); - register_param("ACCURACY", m_accuracy, 1e-3); + //register_param("ACCURACY", m_accuracy, 1e-3); + register_param("ACCURACY", m_accuracy, 1e-6); register_param("CONVERG", m_convergence, 0.3); register_param("RESCHED_LOOPS", m_resched_loops, 15); diff --git a/src/emu/netlist/devices/nld_solver.h b/src/emu/netlist/devices/nld_solver.h index c937e42386b..62af5d9ef9e 100644 --- a/src/emu/netlist/devices/nld_solver.h +++ b/src/emu/netlist/devices/nld_solver.h @@ -32,6 +32,7 @@ public: // return true if a reschedule is needed ... ATTR_HOT bool solve(); + ATTR_HOT int solve_non_dynamic(); ATTR_HOT void step(const netlist_time delta); ATTR_HOT void update_inputs(); diff --git a/src/emu/netlist/devices/nld_twoterm.c b/src/emu/netlist/devices/nld_twoterm.c index 92ea07e6ea3..c37bc42e6a1 100644 --- a/src/emu/netlist/devices/nld_twoterm.c +++ b/src/emu/netlist/devices/nld_twoterm.c @@ -251,6 +251,60 @@ template NETLIB_UPDATE_PARAM(QBJT_switch<NETLIB_NAME(Q)::BJT_NPN>); template NETLIB_UPDATE_PARAM(QBJT_switch<NETLIB_NAME(Q)::BJT_PNP>); // ---------------------------------------------------------------------------------------- +// nld_Q - Ebers Moll +// ---------------------------------------------------------------------------------------- + +template <NETLIB_NAME(Q)::q_type _type> +NETLIB_START(QBJT_EB<_type>) +{ + NETLIB_NAME(Q)::start(); + + register_terminal("B", m_D_BE.m_tt.m_P); // Anode + register_terminal("E", m_D_BE.m_tt.m_N); // Cathode + + register_terminal("_B1", m_D_BC.m_tt.m_P); // Anode + register_terminal("C", m_D_BC.m_tt.m_N); // Cathode + + register_terminal("_B2", m_I_BE.m_P); + register_terminal("_E2", m_I_BE.m_N); + + register_terminal("_B3", m_I_BC.m_P); + register_terminal("_C1", m_I_BC.m_N); + + setup().connect(m_D_BE.m_tt.m_P, m_D_BC.m_tt.m_P); + setup().connect(m_D_BE.m_tt.m_P, m_I_BE.m_P); + setup().connect(m_D_BE.m_tt.m_P, m_I_BC.m_P); + + setup().connect(m_D_BE.m_tt.m_N, m_I_BE.m_N); + setup().connect(m_D_BC.m_tt.m_N, m_I_BC.m_N); + +} + + +template <NETLIB_NAME(Q)::q_type _type> +NETLIB_UPDATE_PARAM(QBJT_EB<_type>) +{ + double IS = m_model.dValue("IS", 1e-15); + double BF = m_model.dValue("BF", 100); + double NF = m_model.dValue("NF", 1); + double BR = m_model.dValue("BR", 1); + double NR = m_model.dValue("NR", 1); + //double VJE = m_model.dValue("VJE", 0.75); + + m_alpha_f = BF / (1.0 + BF); + m_alpha_r = BR / (1.0 + BR); + + m_D_BE.set_param(IS / m_alpha_f, NF); + m_D_BC.set_param(IS / m_alpha_r, NR); + +} + +template NETLIB_START(QBJT_EB<NETLIB_NAME(Q)::BJT_NPN>); +template NETLIB_START(QBJT_EB<NETLIB_NAME(Q)::BJT_PNP>); +template NETLIB_UPDATE_PARAM(QBJT_EB<NETLIB_NAME(Q)::BJT_NPN>); +template NETLIB_UPDATE_PARAM(QBJT_EB<NETLIB_NAME(Q)::BJT_PNP>); + +// ---------------------------------------------------------------------------------------- // nld_VCCS // ---------------------------------------------------------------------------------------- diff --git a/src/emu/netlist/devices/nld_twoterm.h b/src/emu/netlist/devices/nld_twoterm.h index c104b6dc266..894afdfbf9b 100644 --- a/src/emu/netlist/devices/nld_twoterm.h +++ b/src/emu/netlist/devices/nld_twoterm.h @@ -65,6 +65,15 @@ NET_REGISTER_DEV(QNPN_switch, _name) \ NETDEV_PARAMI(_name, model, # _model) +#define NETDEV_QPNP_EB(_name, _model) \ + NET_REGISTER_DEV(QPNP_EB, _name) \ + NETDEV_PARAMI(_name, model, # _model) + +#define NETDEV_QNPN_EB(_name, _model) \ + NET_REGISTER_DEV(QNPN_switch, _name) \ + NETDEV_PARAMI(_name, model, # _model) + + // ---------------------------------------------------------------------------------------- // Implementation // ---------------------------------------------------------------------------------------- @@ -252,6 +261,52 @@ protected: }; +// ---------------------------------------------------------------------------------------- +// nld_Q - Base classes +// ---------------------------------------------------------------------------------------- + +// Have a common start for transistors + +class NETLIB_NAME(Q) : public netlist_device_t +{ +public: + enum q_type { + BJT_NPN, + BJT_PNP + }; + + ATTR_COLD NETLIB_NAME(Q)(const q_type atype, const family_t afamily) + : netlist_device_t(afamily) + , m_qtype(atype) { } + + inline q_type qtype() const { return m_qtype; } + inline bool is_qtype(q_type atype) const { return m_qtype == atype; } +protected: + ATTR_COLD virtual void start(); + ATTR_HOT ATTR_ALIGN void update(); + + netlist_param_model_t m_model; +private: + q_type m_qtype; +}; + +class NETLIB_NAME(QBJT) : public NETLIB_NAME(Q) +{ +public: + + ATTR_COLD NETLIB_NAME(QBJT)(const q_type atype, const family_t afamily) + : NETLIB_NAME(Q)(atype, afamily) { } + +protected: + +private: +}; + +// ---------------------------------------------------------------------------------------- +// nld_QBJT_switch +// ---------------------------------------------------------------------------------------- + + /* * + - C * B ----VVV----+ | @@ -265,100 +320,204 @@ protected: * E */ -// Have a common start for transistors - -class NETLIB_NAME(Q) : public netlist_device_t +template <NETLIB_NAME(Q)::q_type _type> +class NETLIB_NAME(QBJT_switch) : public NETLIB_NAME(QBJT) { public: - enum q_type { - BJT_NPN, - BJT_PNP - }; - - ATTR_COLD NETLIB_NAME(Q)(const q_type atype, const family_t afamily) - : netlist_device_t(afamily) - , m_qtype(atype) { } + ATTR_COLD NETLIB_NAME(QBJT_switch)() + : NETLIB_NAME(QBJT)(_type, BJT_SWITCH), m_gB(NETLIST_GMIN), m_gC(NETLIST_GMIN), m_V(0.0), m_state_on(0) { } + + NETLIB_UPDATEI() + { + double vE = INPANALOG(m_EV); + double vB = INPANALOG(m_BV); + double m = (_type == BJT_NPN) ? 1 : -1; + + int new_state = ((vB - vE) * m > m_V ) ? 1 : 0; + if (m_state_on ^ new_state) + { + double gb = m_gB; + double gc = m_gC; + double v = m_V * m; + if (!new_state ) + { + // not conducting + gb = NETLIST_GMIN; + v = 0; + gc = NETLIST_GMIN; + } + m_RB.set(gb, v, 0.0); + m_RC.set(gc, 0.0, 0.0); + m_state_on = new_state; + m_RB.update_dev(); + m_RC.update_dev(); + } + + } + + NETLIB_NAME(R) m_RB; + NETLIB_NAME(R) m_RC; + + netlist_analog_input_t m_BV; + netlist_analog_input_t m_EV; - inline q_type qtype() const { return m_qtype; } - inline bool is_qtype(q_type atype) const { return m_qtype == atype; } protected: - ATTR_COLD virtual void start(); - ATTR_HOT ATTR_ALIGN void update(); - netlist_param_model_t m_model; + ATTR_COLD virtual void start(); + ATTR_HOT void update_param(); + + double m_gB; // base conductance / switch on + double m_gC; // collector conductance / switch on + double m_V; // internal voltage source + UINT8 m_state_on; + private: - q_type m_qtype; }; -class NETLIB_NAME(QBJT) : public NETLIB_NAME(Q) +typedef NETLIB_NAME(QBJT_switch)<NETLIB_NAME(Q)::BJT_PNP> NETLIB_NAME(QPNP_switch); +typedef NETLIB_NAME(QBJT_switch)<NETLIB_NAME(Q)::BJT_NPN> NETLIB_NAME(QNPN_switch); + +// ---------------------------------------------------------------------------------------- +// nld_QBJT_EB +// ---------------------------------------------------------------------------------------- + +struct generic_diode { -public: + generic_diode() : m_tt(netlist_object_t::ANALOG) {} + + ATTR_HOT inline void update_diode() + { + //FIXME: Optimize cutoff case + + const double nVd = m_tt.m_P.net().Q_Analog()- m_tt.m_N.net().Q_Analog(); + + double G; + + if (nVd < -5.0 * m_Vt) + { + m_Vd = nVd; + G = NETLIST_GMIN; + m_Id = - m_Is; + } + else if (nVd < m_Vcrit) + { + m_Vd = nVd; + + const double eVDVt = exp(m_Vd * m_VtInv); + m_Id = m_Is * (eVDVt - 1.0); + G = m_Is * m_VtInv * eVDVt; + } + else + { +#if defined(_MSC_VER) && _MSC_VER < 1800 + m_Vd = m_Vd + log((nVd - m_Vd) * m_VtInv + 1.0) * m_Vt; +#else + m_Vd = m_Vd + log1p((nVd - m_Vd) * m_VtInv) * m_Vt; +#endif + const double eVDVt = exp(m_Vd * m_VtInv); + m_Id = m_Is * (eVDVt - 1.0); - ATTR_COLD NETLIB_NAME(QBJT)(const q_type atype, const family_t afamily) - : NETLIB_NAME(Q)(atype, afamily) { } + G = m_Is * m_VtInv * eVDVt; + } -protected: + double I = (m_Id - m_Vd * G); + m_tt.set(G, 0.0, I); + //printf("nVd %f m_Vd %f Vcrit %f\n", nVd, m_Vd, m_Vcrit); + } + + void set_param(const double Is, const double n) + { + m_Is = Is; + m_n = n; + + m_Vt = 0.0258 * m_n; + + m_Vcrit = m_Vt * log(m_Vt / m_Is / sqrt(2.0)); + m_VtInv = 1.0 / m_Vt; + + m_Vd = 0.7; + } + + ATTR_HOT inline double I() { return m_Id; } + + nld_twoterm m_tt; private: + double m_Id; + + double m_Vt; + double m_Is; + double m_n; + + double m_VtInv; + double m_Vcrit; + double m_Vd; }; -//NETLIB_NAME(Q) nld_Q::q_type + template <NETLIB_NAME(Q)::q_type _type> -class NETLIB_NAME(QBJT_switch) : public NETLIB_NAME(QBJT) +class NETLIB_NAME(QBJT_EB) : public NETLIB_NAME(QBJT) { public: - ATTR_COLD NETLIB_NAME(QBJT_switch)() - : NETLIB_NAME(QBJT)(_type, BJT_SWITCH), m_gB(NETLIST_GMIN), m_gC(NETLIST_GMIN), m_V(0.0), m_state_on(0) { } + ATTR_COLD NETLIB_NAME(QBJT_EB)() + : NETLIB_NAME(QBJT)(_type, BJT_EB), + m_I_BC(netlist_object_t::ANALOG), + m_I_BE(netlist_object_t::ANALOG) + { } + + NETLIB_UPDATE_TERMINALS() + { + m_D_BE.update_diode(); + m_D_BC.update_diode(); + m_I_BC.set(0.0, 0.0, - m_alpha_f * m_D_BE.I()); + m_I_BE.set(0.0, 0.0, - m_alpha_r * m_D_BC.I()); + } - NETLIB_UPDATEI() - { - double vE = INPANALOG(m_EV); - double vB = INPANALOG(m_BV); - double m = (_type == BJT_NPN) ? 1 : -1; +protected: - int new_state = ((vB - vE) * m > m_V ) ? 1 : 0; - if (m_state_on ^ new_state) - { - double gb = m_gB; - double gc = m_gC; - double v = m_V * m; - if (!new_state ) - { - // not conducting - gb = NETLIST_GMIN; - v = 0; - gc = NETLIST_GMIN; - } - m_RB.set(gb, v, 0.0); - m_RC.set(gc, 0.0, 0.0); - m_state_on = new_state; - m_RB.update_dev(); - m_RC.update_dev(); - } + ATTR_COLD virtual void start(); + ATTR_HOT void update_param(); - } + generic_diode m_D_BE; + generic_diode m_D_BC; - NETLIB_NAME(R) m_RB; - NETLIB_NAME(R) m_RC; + nld_twoterm m_I_BC; + nld_twoterm m_I_BE; - netlist_analog_input_t m_BV; - netlist_analog_input_t m_EV; + double m_alpha_f; + double m_alpha_r; -protected: +private: +}; - ATTR_COLD virtual void start(); - ATTR_HOT void update_param(); +typedef NETLIB_NAME(QBJT_EB)<NETLIB_NAME(Q)::BJT_PNP> NETLIB_NAME(QPNP_EB); +typedef NETLIB_NAME(QBJT_EB)<NETLIB_NAME(Q)::BJT_NPN> NETLIB_NAME(QNPN_EB); - double m_gB; // base conductance / switch on - double m_gC; // collector conductance / switch on - double m_V; // internal voltage source - UINT8 m_state_on; +// ---------------------------------------------------------------------------------------- +// nld_CCCS +// ---------------------------------------------------------------------------------------- -private: -}; +/* + * Current controlled current source + * + * IP ---+ +------> OP + * | | + * RI I + * RI => G => I IOut = (V(IP)-V(IN)) / RI * G + * RI I + * | | + * IN ---+ +------< ON + * + * G=1 ==> 1A ==> 1A + * + * RI = 1 + * + * FIXME: This needs extremely high levels of accuracy to work + * With the current default of 1mv we can only measure + * currents of 1mA. Therefore not yet implemented. + * + */ -typedef NETLIB_NAME(QBJT_switch)<NETLIB_NAME(Q)::BJT_PNP> NETLIB_NAME(QPNP_switch); -typedef NETLIB_NAME(QBJT_switch)<NETLIB_NAME(Q)::BJT_NPN> NETLIB_NAME(QNPN_switch); // ---------------------------------------------------------------------------------------- // nld_VCCS diff --git a/src/emu/netlist/nl_base.h b/src/emu/netlist/nl_base.h index 92087f91bff..11d900bc744 100644 --- a/src/emu/netlist/nl_base.h +++ b/src/emu/netlist/nl_base.h @@ -272,6 +272,7 @@ public: BJT_SWITCH = 7, // BJT(Switch) VCVS = 8, // Voltage controlled voltage source VCCS = 9, // Voltage controlled voltage source + BJT_EB = 10, // BJT(Ebers-Moll) }; ATTR_COLD netlist_object_t(const type_t atype, const family_t afamily); diff --git a/src/emu/netlist/nl_parser.c b/src/emu/netlist/nl_parser.c index 31fff8b530c..2edef087784 100644 --- a/src/emu/netlist/nl_parser.c +++ b/src/emu/netlist/nl_parser.c @@ -66,6 +66,10 @@ void netlist_parser::parse(const char *buf) netdev_device(n, "model", true); else if (n == "NETDEV_QPNP") netdev_device(n, "model", true); + else if (n == "NETDEV_QNPN_EB") + netdev_device(n, "model", true); + else if (n == "NETDEV_QPNP_EB") + netdev_device(n, "model", true); else if ((n == "NETDEV_TTL_CONST") || (n == "NETDEV_ANALOG_CONST")) netdev_const(n); else if ((n == "NET_MODEL")) diff --git a/src/mame/drivers/pong.c b/src/mame/drivers/pong.c index 4977ad871ea..64973d51e45 100644 --- a/src/mame/drivers/pong.c +++ b/src/mame/drivers/pong.c @@ -84,6 +84,8 @@ enum input_changed_enum static NETLIST_START(pong_schematics) NETDEV_SOLVER(Solver) NETDEV_PARAM(Solver.FREQ, 48000) + NETDEV_PARAM(Solver.ACCURACY, 1e-4) // works and is sufficient + NETDEV_ANALOG_CONST(V5, 5) NETDEV_TTL_CONST(high, 1) |