summaryrefslogtreecommitdiffstatshomepage
path: root/src
diff options
context:
space:
mode:
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
commitf7856a86540f42232973d1000e74e22ed82a0c0e (patch)
tree069959d47741cd42349f00b10eb0e91edbc4b8e0 /src
parent45d9761cad18873ac1a92e06c2604f1b451f591a (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.c2
-rw-r--r--src/emu/netlist/devices/nld_solver.c67
-rw-r--r--src/emu/netlist/devices/nld_solver.h1
-rw-r--r--src/emu/netlist/devices/nld_twoterm.c54
-rw-r--r--src/emu/netlist/devices/nld_twoterm.h291
-rw-r--r--src/emu/netlist/nl_base.h1
-rw-r--r--src/emu/netlist/nl_parser.c4
-rw-r--r--src/mame/drivers/pong.c2
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)