mirror of
https://github.com/holub/mame
synced 2025-04-24 01:11:11 +03:00
Netlist: Math helps ... In the end the solution of the analog subsystem boils down to
(G - D) * V = I with G being the conductance matrix, D a diagonal matrix with total conductance on the diagonal elements, V the net voltage vector and I the current vector. By using solely two terminal devices, we can simplify the whole calculation significantly. A BJT now is a four terminal device with two terminals being connected internally. The system is solved using an iterative approach: G * V - D * V = I assuming V=Vn=Vo Vn = D-1 * (I - G * Vo) The above was already used in the code. However, with a pure two terminal device approach going forward we can e.g. further optimize. G typically is a sparse matrix.
This commit is contained in:
parent
63026847a2
commit
ecc3ce1934
@ -129,14 +129,26 @@ NETLIB_FUNC_VOID(solver, post_start, ())
|
||||
switch (p->type())
|
||||
{
|
||||
case netlist_terminal_t::TERMINAL:
|
||||
m_terms.add(p);
|
||||
switch (p->netdev().family())
|
||||
{
|
||||
case CAPACITOR:
|
||||
if (!m_steps.contains(&p->netdev()))
|
||||
m_steps.add(&p->netdev());
|
||||
break;
|
||||
case DIODE:
|
||||
case BJT_SWITCH:
|
||||
if (!m_dynamic.contains(&p->netdev()))
|
||||
m_dynamic.add(&p->netdev());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
pn->object()->m_terms.add(static_cast<netlist_terminal_t *>(p));
|
||||
NL_VERBOSE_OUT(("Added terminal\n"));
|
||||
if (p->netdev().isFamily(CAPACITOR))
|
||||
if (!m_steps.contains(&p->netdev()))
|
||||
m_steps.add(&p->netdev());
|
||||
break;
|
||||
case netlist_terminal_t::INPUT:
|
||||
m_inps.add(p);
|
||||
if (!m_inps.contains(&p->netdev()))
|
||||
m_inps.add(&p->netdev());
|
||||
NL_VERBOSE_OUT(("Added input\n"));
|
||||
break;
|
||||
default:
|
||||
@ -174,6 +186,10 @@ NETLIB_UPDATE(solver)
|
||||
p->object()->step_time(delta.as_double());
|
||||
}
|
||||
do {
|
||||
/* update all non-linear devices */
|
||||
for (dev_list_t::entry_t *p = m_dynamic.first(); p != NULL; p = m_dynamic.next(p))
|
||||
p->object()->update_terminals();
|
||||
|
||||
resched = false;
|
||||
|
||||
for (net_list_t::entry_t *pn = m_nets.first(); pn != NULL; pn = m_nets.next(pn))
|
||||
@ -183,39 +199,11 @@ NETLIB_UPDATE(solver)
|
||||
double gtot = 0;
|
||||
double iIdr = 0;
|
||||
|
||||
for (netlist_core_terminal_t *p = net->m_head; p != NULL; p = p->m_update_list_next)
|
||||
for (netlist_net_t::terminal_list_t::entry_t *e = net->m_terms.first(); e != NULL; e = net->m_terms.next(e))
|
||||
{
|
||||
if (p->isType(netlist_core_terminal_t::TERMINAL))
|
||||
{
|
||||
netlist_terminal_t *pt = static_cast<netlist_terminal_t *>(p);
|
||||
netlist_core_device_t &dev = pt->netdev();
|
||||
#if 0
|
||||
switch (pt->family())
|
||||
{
|
||||
case RESISTOR:
|
||||
static_cast<NETLIB_NAME(R) &>(dev).update_terminals();
|
||||
break;
|
||||
case CAPACITOR:
|
||||
static_cast<NETLIB_NAME(C) &>(dev).update_terminals();
|
||||
break;
|
||||
#if 1
|
||||
case DIODE:
|
||||
static_cast<NETLIB_NAME(D) &>(dev).update_terminals();
|
||||
break;
|
||||
case BJT_SWITCH_NPN:
|
||||
static_cast<NETLIB_NAME(QNPN_switch) &>(dev).update_terminals();
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
dev.update_terminals();
|
||||
break;
|
||||
}
|
||||
#else
|
||||
dev.update_terminals();
|
||||
#endif
|
||||
gtot += pt->m_g;
|
||||
iIdr += pt->m_Idr;
|
||||
}
|
||||
netlist_terminal_t *pt = e->object();
|
||||
gtot += pt->m_g;
|
||||
iIdr += pt->m_Idr + pt->m_g * pt->m_otherterm->net().Q_Analog();
|
||||
}
|
||||
|
||||
double new_val = iIdr / gtot;
|
||||
@ -227,35 +215,19 @@ NETLIB_UPDATE(solver)
|
||||
NL_VERBOSE_OUT(("Info: %d\n", pn->object()->m_num_cons));
|
||||
NL_VERBOSE_OUT(("New: %lld %f %f\n", netlist().time().as_raw(), netlist().time().as_double(), new_val));
|
||||
}
|
||||
} while (resched && (resched_cnt < 1));
|
||||
} while (resched && (resched_cnt < 5));
|
||||
//if (resched_cnt >= 5)
|
||||
// printf("rescheduled\n");
|
||||
if (resched)
|
||||
{
|
||||
schedule();
|
||||
}
|
||||
#if 1
|
||||
else
|
||||
#endif
|
||||
{
|
||||
/* update all inputs connected */
|
||||
#if 0
|
||||
for (net_list_t::entry_t *pn = m_nets.first(); pn != NULL; pn = m_nets.next(pn))
|
||||
{
|
||||
if (pn->object()->m_cur.Analog != pn->object()->m_last.Analog)
|
||||
{
|
||||
for (netlist_core_terminal_t *p = pn->object()->m_head; p != NULL; p = p->m_update_list_next)
|
||||
{
|
||||
if (p->isType(netlist_terminal_t::INPUT))
|
||||
p->netdev().update_dev();
|
||||
}
|
||||
}
|
||||
pn->object()->m_last.Analog = pn->object()->m_cur.Analog;
|
||||
}
|
||||
#else
|
||||
for (terminal_list_t::entry_t *p = m_inps.first(); p != NULL; p = m_inps.next(p))
|
||||
p->object()->netdev().update_dev();
|
||||
#endif
|
||||
for (dev_list_t::entry_t *p = m_inps.first(); p != NULL; p = m_inps.next(p))
|
||||
p->object()->update_dev();
|
||||
|
||||
/* step circuit */
|
||||
if (!m_Q_step.net().is_queued())
|
||||
m_Q_step.net().push_to_queue(m_inc);
|
||||
|
@ -80,7 +80,6 @@ NETLIB_DEVICE_WITH_PARAMS(clock,
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
NETLIB_DEVICE_WITH_PARAMS(solver,
|
||||
typedef netlist_list_t<netlist_core_terminal_t *> terminal_list_t;
|
||||
typedef netlist_list_t<netlist_net_t *> net_list_t;
|
||||
typedef netlist_list_t<netlist_core_device_t *> dev_list_t;
|
||||
|
||||
@ -98,8 +97,8 @@ NETLIB_DEVICE_WITH_PARAMS(solver,
|
||||
netlist_time m_last_step;
|
||||
netlist_time m_nt_sync_delay;
|
||||
|
||||
terminal_list_t m_terms;
|
||||
terminal_list_t m_inps;
|
||||
dev_list_t m_dynamic;
|
||||
dev_list_t m_inps;
|
||||
dev_list_t m_steps;
|
||||
|
||||
public:
|
||||
|
@ -10,6 +10,13 @@
|
||||
// nld_twoterm
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
ATTR_COLD NETLIB_NAME(twoterm)::NETLIB_NAME(twoterm)(const family_t afamily) :
|
||||
netlist_device_t(afamily)
|
||||
{
|
||||
m_P.m_otherterm = &m_N;
|
||||
m_N.m_otherterm = &m_P;
|
||||
}
|
||||
|
||||
NETLIB_START(twoterm)
|
||||
{
|
||||
}
|
||||
@ -34,7 +41,7 @@ NETLIB_START(R)
|
||||
|
||||
NETLIB_UPDATE_PARAM(R)
|
||||
{
|
||||
m_g = 1.0 / m_R.Value();
|
||||
set_R(m_R.Value());
|
||||
}
|
||||
|
||||
NETLIB_UPDATE(R)
|
||||
@ -138,7 +145,14 @@ NETLIB_START(QBJT)
|
||||
register_terminal("B", m_B);
|
||||
register_terminal("C", m_C);
|
||||
register_terminal("E", m_E);
|
||||
register_terminal("EB", m_EB);
|
||||
|
||||
m_setup->connect(m_E, m_EB);
|
||||
|
||||
m_B.m_otherterm = &m_EB;
|
||||
m_EB.m_otherterm = &m_B;
|
||||
m_C.m_otherterm = &m_E;
|
||||
m_E.m_otherterm = &m_C;
|
||||
}
|
||||
|
||||
NETLIB_UPDATE(Q)
|
||||
|
@ -65,26 +65,25 @@
|
||||
class NETLIB_NAME(twoterm) : public netlist_device_t
|
||||
{
|
||||
public:
|
||||
ATTR_COLD NETLIB_NAME(twoterm)(const family_t afamily)
|
||||
: netlist_device_t(afamily), m_g(0.0), m_V(0.0), m_I(0.0) { }
|
||||
ATTR_COLD NETLIB_NAME(twoterm)(const family_t afamily);
|
||||
|
||||
netlist_terminal_t m_P;
|
||||
netlist_terminal_t m_N;
|
||||
|
||||
virtual NETLIB_UPDATE_TERMINALS()
|
||||
{
|
||||
m_P.m_g = m_N.m_g = m_g;
|
||||
m_N.m_Idr = (m_P.net().Q_Analog() - m_V) * m_g + m_I;
|
||||
m_P.m_Idr = (m_N.net().Q_Analog() + m_V) * m_g - m_I;
|
||||
//printf("%f %f %f %f\n", m_N.m_Idr, m_P.m_Idr, m_N.net().Q_Analog(), m_P.net().Q_Analog());
|
||||
}
|
||||
|
||||
ATTR_HOT inline void set(const double G, const double V, const double I)
|
||||
{
|
||||
m_P.m_g = m_N.m_g = G;
|
||||
m_N.m_Idr = ( -V) * G + I;
|
||||
m_P.m_Idr = ( V) * G - I;
|
||||
}
|
||||
protected:
|
||||
ATTR_COLD virtual void start();
|
||||
ATTR_HOT ATTR_ALIGN void update();
|
||||
|
||||
double m_g; // conductance
|
||||
double m_V; // internal voltage source
|
||||
double m_I; // internal current source
|
||||
private:
|
||||
};
|
||||
|
||||
@ -97,7 +96,7 @@ class NETLIB_NAME(R) : public NETLIB_NAME(twoterm)
|
||||
public:
|
||||
ATTR_COLD NETLIB_NAME(R)() : NETLIB_NAME(twoterm)(RESISTOR) { }
|
||||
|
||||
inline void set_R(const double R) { m_g = 1.0 / R; }
|
||||
inline void set_R(const double R) { set(1.0 / R, 0.0, 0.0); }
|
||||
|
||||
protected:
|
||||
ATTR_COLD virtual void start();
|
||||
@ -119,8 +118,9 @@ public:
|
||||
|
||||
ATTR_HOT void step_time(const double st)
|
||||
{
|
||||
m_g = m_P.m_g = m_N.m_g = m_C.Value() / st;
|
||||
m_I = -m_g * (m_P.net().Q_Analog()- m_N.net().Q_Analog());
|
||||
double G = m_C.Value() / st;
|
||||
double I = -G * (m_P.net().Q_Analog()- m_N.net().Q_Analog());
|
||||
set(G, 0.0, I);
|
||||
}
|
||||
|
||||
protected:
|
||||
@ -151,12 +151,11 @@ public:
|
||||
const double eVDVt = exp(m_Vd * m_VtInv);
|
||||
const double Id = m_Is * (eVDVt - 1.0);
|
||||
|
||||
m_g = m_Is * m_VtInv * eVDVt;
|
||||
double G = m_Is * m_VtInv * eVDVt;
|
||||
|
||||
m_I = (Id - m_Vd * m_g);
|
||||
//printf("Vd: %f %f %f %f\n", m_Vd, m_g, Id, m_I);
|
||||
double I = (Id - m_Vd * G);
|
||||
|
||||
NETLIB_NAME(twoterm)::update_terminals();
|
||||
set(G, 0.0, I);
|
||||
}
|
||||
|
||||
protected:
|
||||
@ -235,6 +234,8 @@ public:
|
||||
netlist_terminal_t m_C;
|
||||
netlist_terminal_t m_E;
|
||||
|
||||
netlist_terminal_t m_EB;
|
||||
|
||||
protected:
|
||||
ATTR_COLD virtual void start();
|
||||
|
||||
@ -247,7 +248,7 @@ class NETLIB_NAME(QBJT_switch) : public NETLIB_NAME(QBJT)
|
||||
{
|
||||
public:
|
||||
ATTR_COLD NETLIB_NAME(QBJT_switch)()
|
||||
: NETLIB_NAME(QBJT)(_type, BJT_SWITCH_NPN), m_gB(NETLIST_GMIN), m_gC(NETLIST_GMIN), m_V(0.0) { }
|
||||
: NETLIB_NAME(QBJT)(_type, BJT_SWITCH), m_gB(NETLIST_GMIN), m_gC(NETLIST_GMIN), m_V(0.0) { }
|
||||
|
||||
NETLIB_UPDATE_TERMINALS()
|
||||
{
|
||||
@ -257,6 +258,7 @@ public:
|
||||
double vE = m_E.net().Q_Analog();
|
||||
double vB = m_B.net().Q_Analog();
|
||||
|
||||
//printf("diff %f = %f - %f\n", vB - vE, vB, vE);
|
||||
if (vB - vE < m_V )
|
||||
{
|
||||
// not conducting
|
||||
@ -265,21 +267,23 @@ public:
|
||||
gc = NETLIST_GMIN;
|
||||
}
|
||||
|
||||
m_B.m_g = m_E.m_g = gb;
|
||||
m_C.m_g = gc;
|
||||
m_B.m_g = m_EB.m_g = gb;
|
||||
m_C.m_g = m_E.m_g = gc;
|
||||
|
||||
m_B.m_Idr = (vE + v) * gb;
|
||||
m_C.m_Idr = (vE) * gc;
|
||||
m_E.m_Idr = (vB - v) * gb + m_C.net().Q_Analog() * gc;
|
||||
m_B.m_Idr = ( v) * gb;
|
||||
m_EB.m_Idr = ( -v) * gb;
|
||||
m_C.m_Idr = 0.0;
|
||||
m_E.m_Idr = 0.0;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
ATTR_COLD void update_param();
|
||||
|
||||
double m_gB; // conductance
|
||||
double m_gC; // conductance
|
||||
double m_gB; // base conductance / switch on
|
||||
double m_gC; // collector conductance / switch on
|
||||
double m_V; // internal voltage source
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
|
@ -251,7 +251,7 @@ public:
|
||||
RESISTOR = 4, // Resistor
|
||||
CAPACITOR = 5, // Capacitor
|
||||
DIODE = 6, // Diode
|
||||
BJT_SWITCH_NPN = 7, // BJT(Switch)
|
||||
BJT_SWITCH = 7, // BJT(Switch)
|
||||
};
|
||||
|
||||
ATTR_COLD netlist_object_t(const type_t atype, const family_t afamily);
|
||||
@ -346,6 +346,7 @@ public:
|
||||
double m_Idr; // drive current
|
||||
double m_g; // conductance
|
||||
|
||||
netlist_terminal_t *m_otherterm;
|
||||
};
|
||||
|
||||
|
||||
@ -489,6 +490,10 @@ public:
|
||||
ATTR_HOT inline void push_to_queue(const netlist_time &delay);
|
||||
ATTR_HOT bool is_queued() { return m_in_queue == 1; }
|
||||
|
||||
// m_terms is only used by analog subsystem
|
||||
typedef netlist_list_t<netlist_terminal_t *> terminal_list_t;
|
||||
terminal_list_t m_terms;
|
||||
|
||||
protected:
|
||||
|
||||
/* prohibit use in device functions
|
||||
|
Loading…
Reference in New Issue
Block a user