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:
Couriersud 2013-12-08 15:38:46 +00:00
parent 63026847a2
commit ecc3ce1934
5 changed files with 80 additions and 86 deletions

View File

@ -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);

View File

@ -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:

View File

@ -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)

View File

@ -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:
};

View File

@ -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