Netlist: added a simplified BJT Switch Model. This should be sufficient for audio purposes in >> 90% of all cases I have seen so far.

The performance of the analog subsystem has quite some room for improvement :-(
This commit is contained in:
Couriersud 2013-12-07 23:41:12 +00:00
parent dcd3d5fd03
commit 3c13417a92
9 changed files with 380 additions and 79 deletions

View File

@ -786,6 +786,8 @@ void netlist_factory::initialize()
ENTRY(R, NETDEV_R)
ENTRY(C, NETDEV_C)
ENTRY(D, NETDEV_D)
ENTRY(QPNP_switch, NETDEV_QPNP)
ENTRY(QNPN_switch, NETDEV_QNPN)
ENTRY(ttl_const, NETDEV_TTL_CONST)
ENTRY(analog_const, NETDEV_ANALOG_CONST)
ENTRY(logic_input, NETDEV_LOGIC_INPUT)

View File

@ -4,6 +4,7 @@
*/
#include "nld_system.h"
#include "nld_twoterm.h"
// ----------------------------------------------------------------------------------------
// netdev_const
@ -92,6 +93,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_link_internal(m_fb_sync, m_Q_sync, netlist_input_t::STATE_INP_ACTIVE);
register_link_internal(m_fb_step, m_Q_step, netlist_input_t::STATE_INP_ACTIVE);
m_last_step = netlist_time::zero;
@ -116,6 +119,7 @@ NETLIB_NAME(solver)::~NETLIB_NAME(solver)()
NETLIB_FUNC_VOID(solver, post_start, ())
{
NL_VERBOSE_OUT(("post start solver ...\n"));
for (net_list_t::entry_t *pn = m_nets.first(); pn != NULL; pn = m_nets.next(pn))
{
@ -127,6 +131,9 @@ NETLIB_FUNC_VOID(solver, post_start, ())
case netlist_terminal_t::TERMINAL:
m_terms.add(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);
@ -154,6 +161,7 @@ NETLIB_UPDATE(solver)
//OUTLOGIC(m_Q, !m_Q.net().new_Q(), m_inc );
bool resched = false;
int resched_cnt = 0;
netlist_time now = netlist().time();
netlist_time delta = now - m_last_step;
@ -162,38 +170,73 @@ NETLIB_UPDATE(solver)
NL_VERBOSE_OUT(("Step!\n"));
/* update all terminals for new time step */
m_last_step = now;
for (terminal_list_t::entry_t *p = m_terms.first(); p != NULL; p = m_terms.next(p))
p->object()->netdev().step_time(delta.as_double());
for (dev_list_t::entry_t *p = m_steps.first(); p != NULL; p = m_steps.next(p))
p->object()->step_time(delta.as_double());
}
for (net_list_t::entry_t *pn = m_nets.first(); pn != NULL; pn = m_nets.next(pn))
{
double gtot = 0;
double iIdr = 0;
do {
resched = false;
for (netlist_core_terminal_t *p = pn->object()->m_head; p != NULL; p = p->m_update_list_next)
for (net_list_t::entry_t *pn = m_nets.first(); pn != NULL; pn = m_nets.next(pn))
{
if (p->isType(netlist_core_terminal_t::TERMINAL))
netlist_net_t *net = pn->object();
double gtot = 0;
double iIdr = 0;
for (netlist_core_terminal_t *p = net->m_head; p != NULL; p = p->m_update_list_next)
{
netlist_terminal_t *pt = static_cast<netlist_terminal_t *>(p);
pt->netdev().update_terminals();
gtot += pt->m_g;
iIdr += pt->m_Idr;
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;
}
}
double new_val = iIdr / gtot;
if (fabs(new_val - net->m_cur.Analog) > m_accuracy.Value())
resched = true;
resched_cnt++;
net->m_cur.Analog = net->m_new.Analog = new_val;
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));
}
double new_val = iIdr / gtot;
if (fabs(new_val - pn->object()->m_cur.Analog) > 1e-4)
resched = true;
pn->object()->m_cur.Analog = pn->object()->m_new.Analog = new_val;
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));
//if (resched_cnt >= 5)
// printf("rescheduled\n");
if (resched)
{
schedule();
}
#if 1
else
#endif
{
/* update all inputs connected */
#if 0
@ -201,7 +244,7 @@ NETLIB_UPDATE(solver)
{
if (pn->object()->m_cur.Analog != pn->object()->m_last.Analog)
{
for (netlist_terminal_t *p = pn->object()->m_head; p != NULL; p = p->m_update_list_next)
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();

View File

@ -82,6 +82,7 @@ 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;
netlist_ttl_input_t m_fb_sync;
netlist_ttl_output_t m_Q_sync;
@ -91,6 +92,7 @@ NETLIB_DEVICE_WITH_PARAMS(solver,
netlist_param_double_t m_freq;
netlist_param_double_t m_sync_delay;
netlist_param_double_t m_accuracy;
netlist_time m_inc;
netlist_time m_last_step;
@ -98,6 +100,7 @@ NETLIB_DEVICE_WITH_PARAMS(solver,
terminal_list_t m_terms;
terminal_list_t m_inps;
dev_list_t m_steps;
public:

View File

@ -94,3 +94,83 @@ NETLIB_UPDATE(D)
{
NETLIB_NAME(twoterm)::update();
}
class diode
{
public:
diode() : m_Is(1e-15), m_VT(0.0258), m_VT_inv(1.0 / m_VT) {}
diode(const double Is, const double n)
{
m_Is = Is;
m_VT = 0.0258 * n;
m_VT_inv = 1.0 / m_VT;
}
void set(const double Is, const double n)
{
m_Is = Is;
m_VT = 0.0258 * n;
m_VT_inv = 1.0 / m_VT;
}
double I(const double V) const { return m_Is * exp(V * m_VT_inv) - m_Is; }
double g(const double V) const { return m_Is * m_VT_inv * exp(V * m_VT_inv); }
double V(const double I) const { return log(1.0 + I / m_Is) * m_VT; }
double gI(const double I) const { return m_VT_inv * (I + m_Is); }
private:
double m_Is;
double m_VT;
double m_VT_inv;
};
// ----------------------------------------------------------------------------------------
// nld_Q
// ----------------------------------------------------------------------------------------
NETLIB_START(Q)
{
register_param("model", m_model, "");
}
NETLIB_START(QBJT)
{
NETLIB_NAME(Q)::start();
register_terminal("B", m_B);
register_terminal("C", m_C);
register_terminal("E", m_E);
}
NETLIB_UPDATE(Q)
{
netlist().solver()->schedule();
}
template <NETLIB_NAME(Q)::q_type _type>
NETLIB_UPDATE_PARAM(QBJT_switch<_type>)
{
double IS = m_model.dValue("IS", 1e-15);
double BF = m_model.dValue("BF", 100);
double NF = m_model.dValue("NF", 1);
//double VJE = m_model.dValue("VJE", 0.75);
double alpha = BF / (1.0 + BF);
diode d(IS, NF);
// Assume 5mA Collector current for switch operation
if (_type == BJT_NPN)
m_V = d.V(0.005 / alpha);
else
m_V = - d.V(0.005 / alpha);
m_gB = d.gI(0.005 / alpha);
if (m_gB < NETLIST_GMIN)
m_gB = NETLIST_GMIN;
m_gC = BF * m_gB; // very rough estimate
printf("%f %f \n", m_V, m_gB);
}
template NETLIB_UPDATE_PARAM(QBJT_switch<NETLIB_NAME(Q)::BJT_NPN>);
template NETLIB_UPDATE_PARAM(QBJT_switch<NETLIB_NAME(Q)::BJT_PNP>);

View File

@ -62,18 +62,15 @@
// nld_twoterm
// ----------------------------------------------------------------------------------------
class nld_twoterm : public netlist_device_t
class NETLIB_NAME(twoterm) : public netlist_device_t
{
public:
nld_twoterm()
: netlist_device_t(), m_g(0.0), m_V(0.0), m_I(0.0) { }
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) { }
netlist_terminal_t m_P;
netlist_terminal_t m_N;
protected:
virtual void start();
virtual NETLIB_UPDATE_TERMINALS()
{
m_P.m_g = m_N.m_g = m_g;
@ -81,7 +78,8 @@ protected:
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());
}
protected:
ATTR_COLD virtual void start();
ATTR_HOT ATTR_ALIGN void update();
double m_g; // conductance
@ -94,23 +92,30 @@ private:
// nld_R
// ----------------------------------------------------------------------------------------
NETLIB_DEVICE_WITH_PARAMS_DERIVED(R, twoterm,
netlist_param_double_t m_R;
NETLIB_UPDATE_TERMINALS() { NETLIB_NAME(twoterm)::update_terminals(); }
class NETLIB_NAME(R) : public NETLIB_NAME(twoterm)
{
public:
inline void set_R(double R) { m_g = 1.0 / R; }
ATTR_COLD NETLIB_NAME(R)() : NETLIB_NAME(twoterm)(RESISTOR) { }
);
inline void set_R(const double R) { m_g = 1.0 / R; }
protected:
ATTR_COLD virtual void start();
ATTR_COLD virtual void update_param();
ATTR_HOT ATTR_ALIGN void update();
netlist_param_double_t m_R;
};
// ----------------------------------------------------------------------------------------
// nld_C
// ----------------------------------------------------------------------------------------
NETLIB_DEVICE_WITH_PARAMS_DERIVED(C, twoterm,
netlist_param_double_t m_C;
class NETLIB_NAME(C) : public NETLIB_NAME(twoterm)
{
public:
ATTR_COLD NETLIB_NAME(C)() : NETLIB_NAME(twoterm)(CAPACITOR) { }
ATTR_HOT void step_time(const double st)
{
@ -118,23 +123,23 @@ NETLIB_DEVICE_WITH_PARAMS_DERIVED(C, twoterm,
m_I = -m_g * (m_P.net().Q_Analog()- m_N.net().Q_Analog());
}
);
protected:
ATTR_COLD virtual void start();
ATTR_COLD virtual void update_param();
ATTR_HOT ATTR_ALIGN void update();
netlist_param_double_t m_C;
};
// ----------------------------------------------------------------------------------------
// nld_D
// ----------------------------------------------------------------------------------------
NETLIB_DEVICE_WITH_PARAMS_DERIVED(D, twoterm,
netlist_param_multi_t m_model;
double m_Vt;
double m_Is;
double m_n;
double m_VtInv;
double m_Vcrit;
double m_Vd;
class NETLIB_NAME(D) : public NETLIB_NAME(twoterm)
{
public:
ATTR_COLD NETLIB_NAME(D)() : NETLIB_NAME(twoterm)(DIODE) { }
NETLIB_UPDATE_TERMINALS()
{
@ -151,11 +156,134 @@ NETLIB_DEVICE_WITH_PARAMS_DERIVED(D, twoterm,
m_I = (Id - m_Vd * m_g);
//printf("Vd: %f %f %f %f\n", m_Vd, m_g, Id, m_I);
nld_twoterm::update_terminals();
NETLIB_NAME(twoterm)::update_terminals();
}
private:
);
protected:
ATTR_COLD virtual void start();
ATTR_COLD virtual void update_param();
ATTR_HOT ATTR_ALIGN void update();
netlist_param_multi_t m_model;
double m_Vt;
double m_Is;
double m_n;
double m_VtInv;
double m_Vcrit;
double m_Vd;
};
/*
* + - C
* B ----VVV----+ |
* | |
* Rb Rc
* Rb Rc
* Rb Rc
* | |
* +----+----+
* |
* E
*/
#define NETDEV_QPNP(_name, _model) \
NET_REGISTER_DEV(QPNP_switch, _name) \
NETDEV_PARAMI(_name, model, _model)
#define NETDEV_QNPN(_name, _model) \
NET_REGISTER_DEV(QNPN_switch, _name) \
NETDEV_PARAMI(_name, model, _model)
#define NETDEV_BC238B(_name) NETDEV_QNPN(_name, "IS=1.8E-14 ISE=5.0E-14 ISC=1.72E-13 XTI=3 BF=400 BR=35.5 IKF=0.14 IKR=0.03 XTB=1.5 VAF=80 VAR=12.5 VJE=0.58 VJC=0.54 RE=0.6 RC=0.25 RB=0.56 CJE=13E-12 CJC=4E-12 XCJC=0.75 FC=0.5 NF=0.9955 NR=1.005 NE=1.46 NC=1.27 MJE=0.33 MJC=0.33 TF=0.64E-9 TR=50.72E-9 EG=1.11 KF=0 AF=1 VCEO=45V ICRATING=100M MFG=ZETEX")
// 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_multi_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) { }
netlist_terminal_t m_B;
netlist_terminal_t m_C;
netlist_terminal_t m_E;
protected:
ATTR_COLD virtual void start();
private:
};
//NETLIB_NAME(Q) nld_Q::q_type
template <NETLIB_NAME(Q)::q_type _type>
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_UPDATE_TERMINALS()
{
double gb = m_gB;
double gc = m_gC;
double v = m_V;
double vE = m_E.net().Q_Analog();
double vB = m_B.net().Q_Analog();
if (vB - vE < m_V )
{
// not conducting
gb = NETLIST_GMIN;
v = 0;
gc = NETLIST_GMIN;
}
m_B.m_g = m_E.m_g = gb;
m_C.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;
}
protected:
ATTR_COLD void update_param();
double m_gB; // conductance
double m_gC; // conductance
double m_V; // internal voltage source
private:
};
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);
#endif /* NLD_TWOTERM_H_ */

View File

@ -197,8 +197,13 @@ ATTR_HOT ATTR_ALIGN void netlist_base_t::process_queue(INT32 &atime)
// net_core_device_t
// ----------------------------------------------------------------------------------------
netlist_core_device_t::netlist_core_device_t()
: netlist_object_t(DEVICE, ALL)
ATTR_COLD netlist_core_device_t::netlist_core_device_t()
: netlist_object_t(DEVICE, GENERIC)
{
}
ATTR_COLD netlist_core_device_t::netlist_core_device_t(const family_t afamily)
: netlist_object_t(DEVICE, afamily)
{
}
@ -218,7 +223,7 @@ ATTR_COLD void netlist_core_device_t::init(netlist_setup_t &setup, const pstring
}
netlist_core_device_t::~netlist_core_device_t()
ATTR_COLD netlist_core_device_t::~netlist_core_device_t()
{
}
@ -226,6 +231,28 @@ netlist_core_device_t::~netlist_core_device_t()
// net_device_t
// ----------------------------------------------------------------------------------------
netlist_device_t::netlist_device_t()
: netlist_core_device_t(),
m_terminals(20),
m_setup(NULL),
m_variable_input_count(false)
{
}
netlist_device_t::netlist_device_t(const family_t afamily)
: netlist_core_device_t(afamily),
m_terminals(20),
m_setup(NULL),
m_variable_input_count(false)
{
}
netlist_device_t::~netlist_device_t()
{
//NL_VERBOSE_OUT(("~net_device_t\n");
}
ATTR_HOT ATTR_ALIGN const netlist_sig_t netlist_core_device_t::INPLOGIC_PASSIVE(netlist_logic_input_t &inp)
{
if (inp.state() == netlist_input_t::STATE_INP_PASSIVE)
@ -240,19 +267,6 @@ ATTR_HOT ATTR_ALIGN const netlist_sig_t netlist_core_device_t::INPLOGIC_PASSIVE(
}
netlist_device_t::netlist_device_t()
: netlist_core_device_t(),
m_terminals(20),
m_setup(NULL),
m_variable_input_count(false)
{
}
netlist_device_t::~netlist_device_t()
{
//NL_VERBOSE_OUT(("~net_device_t\n");
}
ATTR_COLD void netlist_device_t::init(netlist_setup_t &setup, const pstring &name)
{
netlist_core_device_t::init(setup, name);
@ -538,9 +552,9 @@ ATTR_COLD void netlist_logic_output_t::set_levels(const double low, const double
ATTR_COLD double netlist_param_multi_t::dValue(const pstring &entity, const double defval) const
{
pstring tmp = this->Value();
pstring tmp = this->Value().ucase();
// .model 1N914 D(Is=2.52n Rs=.568 N=1.752 Cjo=4p M=.4 tt=20n Iave=200m Vpk=75 mfg=OnSemi type=silicon)
int p = tmp.find(entity);
int p = tmp.find(entity.ucase() + "=");
if (p>=0)
{
int pblank = tmp.find(" ", p);

View File

@ -238,15 +238,20 @@ public:
TERMINAL = 0,
INPUT = 1,
OUTPUT = 2,
DEVICE = 3,
PARAM = 4,
NET = 5
PARAM = 3,
NET = 4,
DEVICE = 5,
};
enum family_t {
LOGIC = 1,
ANALOG = 2,
CURRENT = 3,
ALL = 4 // <== devices usually fall into this category
// Terminal families
LOGIC = 1,
ANALOG = 2,
// Device families
GENERIC = 3, // <== devices usually fall into this category
RESISTOR = 4, // Resistor
CAPACITOR = 5, // Capacitor
DIODE = 6, // Diode
BJT_SWITCH_NPN = 7, // BJT(Switch)
};
ATTR_COLD netlist_object_t(const type_t atype, const family_t afamily);
@ -708,6 +713,7 @@ class netlist_core_device_t : public netlist_object_t
public:
ATTR_COLD netlist_core_device_t();
ATTR_COLD netlist_core_device_t(const family_t afamily);
ATTR_COLD virtual ~netlist_core_device_t();
@ -792,6 +798,7 @@ class netlist_device_t : public netlist_core_device_t
public:
ATTR_COLD netlist_device_t();
ATTR_COLD netlist_device_t(const family_t afamily);
ATTR_COLD virtual ~netlist_device_t();

View File

@ -74,6 +74,15 @@ public:
return;
}
}
}
ATTR_HOT inline bool contains(const _ListClass elem) const
{
for (entry_t *i = m_list; i <= m_ptr; i++)
{
if (i->object() == elem)
return true;
}
return false;
}
ATTR_HOT inline entry_t *first() const { return (m_ptr >= m_list ? &m_list[0] : NULL ); }
ATTR_HOT inline entry_t *next(entry_t *lc) const { return (lc < last() ? lc + 1 : NULL ); }

View File

@ -588,6 +588,21 @@ static NETLIST_START(pong_schematics)
//NETDEV_LOG(log3, 555.OUT)
#endif
#if 0
NETDEV_BC238B(Q)
NETDEV_R(RB, 1000)
NETDEV_R(RC, 1000)
NET_C(RC.1, V5)
NET_C(RC.2, Q.C)
NET_C(RB.1, 128H)
NET_C(RB.2, Q.B)
NET_C(Q.E, GND)
//NETDEV_LOG(logB, Q.B)
//NETDEV_LOG(logC, Q.C)
#endif
NETLIST_END