- added VCCS and VCVS components.
- added over-relaxation on demand to the iterative solver.
- added a slightly more complex opamp model example to pong (commented out)
This commit is contained in:
Couriersud 2013-12-11 23:50:28 +00:00
parent 55c1991174
commit 3710459410
8 changed files with 366 additions and 23 deletions

View File

@ -786,6 +786,8 @@ void netlist_factory::initialize()
ENTRY(R, NETDEV_R)
ENTRY(C, NETDEV_C)
ENTRY(D, NETDEV_D)
ENTRY(VCVS, NETDEV_VCVS)
ENTRY(VCCS, NETDEV_VCCS)
ENTRY(QPNP_switch, NETDEV_QPNP)
ENTRY(QNPN_switch, NETDEV_QNPN)
ENTRY(ttl_const, NETDEV_TTL_CONST)

View File

@ -103,6 +103,7 @@ ATTR_COLD void netlist_matrix_solver_t::setup(netlist_net_t::list_t &nets)
m_steps.add(&p->netdev());
break;
case netlist_device_t::DIODE:
//case netlist_device_t::VCVS:
//case netlist_device_t::BJT_SWITCH:
if (!m_dynamic.contains(&p->netdev()))
m_dynamic.add(&p->netdev());
@ -153,6 +154,7 @@ ATTR_HOT inline bool netlist_matrix_solver_t::solve()
netlist_net_t *net = pn->object();
double gtot = 0;
double gabs = 0;
double iIdr = 0;
const netlist_net_t::terminal_list_t &terms = net->m_terms;
#if 1
@ -161,17 +163,19 @@ ATTR_HOT inline bool netlist_matrix_solver_t::solve()
case 1:
{
const netlist_terminal_t *pt = terms.first()->object();
gtot = pt->m_g;
iIdr = pt->m_Idr + pt->m_g * pt->m_otherterm->net().Q_Analog();
gtot = pt->m_gt;
gabs = fabs(pt->m_go);
iIdr = pt->m_Idr + pt->m_go * pt->m_otherterm->net().Q_Analog();
}
break;
case 2:
{
const netlist_terminal_t *pt1 = terms.first()->object();
const netlist_terminal_t *pt2 = terms.item(1)->object();
gtot = pt1->m_g + pt2->m_g;
iIdr = pt1->m_Idr + pt1->m_g * pt1->m_otherterm->net().Q_Analog()
+ pt2->m_Idr + pt2->m_g * pt2->m_otherterm->net().Q_Analog();
gtot = pt1->m_gt + pt2->m_gt;
gabs = fabs(pt1->m_go) + fabs(pt2->m_go);
iIdr = pt1->m_Idr + pt1->m_go * pt1->m_otherterm->net().Q_Analog()
+ pt2->m_Idr + pt2->m_go * pt2->m_otherterm->net().Q_Analog();
}
break;
case 3:
@ -179,18 +183,20 @@ ATTR_HOT inline bool netlist_matrix_solver_t::solve()
const netlist_terminal_t *pt1 = terms.first()->object();
const netlist_terminal_t *pt2 = terms.item(1)->object();
const netlist_terminal_t *pt3 = terms.item(2)->object();
gtot = pt1->m_g + pt2->m_g + pt3->m_g;
iIdr = pt1->m_Idr + pt1->m_g * pt1->m_otherterm->net().Q_Analog()
+ pt2->m_Idr + pt2->m_g * pt2->m_otherterm->net().Q_Analog()
+ pt3->m_Idr + pt3->m_g * pt3->m_otherterm->net().Q_Analog();
gtot = pt1->m_gt + pt2->m_gt + pt3->m_gt;
gabs = fabs(pt1->m_go) + fabs(pt2->m_go) + fabs(pt3->m_go);
iIdr = pt1->m_Idr + pt1->m_go * pt1->m_otherterm->net().Q_Analog()
+ pt2->m_Idr + pt2->m_go * pt2->m_otherterm->net().Q_Analog()
+ pt3->m_Idr + pt3->m_go * pt3->m_otherterm->net().Q_Analog();
}
break;
default:
for (netlist_net_t::terminal_list_t::entry_t *e = terms.first(); e != NULL; e = terms.next(e))
{
netlist_terminal_t *pt = e->object();
gtot += pt->m_g;
iIdr += pt->m_Idr + pt->m_g * pt->m_otherterm->net().Q_Analog();
gtot += pt->m_gt;
gabs += fabs(pt->m_go);
iIdr += pt->m_Idr + pt->m_go * pt->m_otherterm->net().Q_Analog();
}
break;
}
@ -198,11 +204,18 @@ ATTR_HOT inline bool netlist_matrix_solver_t::solve()
for (netlist_net_t::terminal_list_t::entry_t *e = terms.first(); e != NULL; e = terms.next(e))
{
netlist_terminal_t *pt = e->object();
gtot += pt->m_g;
iIdr += pt->m_Idr + pt->m_g * pt->m_otherterm->net().Q_Analog();
gtot += pt->m_gt;
gabs += fabs(pt->m_go);
iIdr += pt->m_Idr + pt->m_go * pt->m_otherterm->net().Q_Analog();
}
#endif
double new_val = iIdr / gtot;
double new_val;
gabs *= m_convergence_factor;
if (gabs > gtot)
new_val = (net->m_cur.Analog * gabs + iIdr) / (gtot + gabs);
else
new_val = iIdr / gtot;
if (fabs(new_val - net->m_cur.Analog) > m_accuracy)
resched = true;
net->m_cur.Analog = net->m_new.Analog = new_val;
@ -261,6 +274,7 @@ NETLIB_START(solver)
m_inc = netlist_time::from_hz(m_freq.Value());
register_param("ACCURACY", m_accuracy, 1e-3);
register_param("CONVERG", m_convergence, 0.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);
@ -329,6 +343,7 @@ NETLIB_FUNC_VOID(solver, post_start, ())
{
netlist_matrix_solver_t *ms = new netlist_matrix_solver_t;
ms->m_accuracy = m_accuracy.Value();
ms->m_convergence_factor = m_convergence.Value();
ms->setup(groups[i]);
m_mat_solvers.add(ms);
printf("%d ==> %d nets %s\n", i, groups[i].count(), groups[i].first()->object()->m_head->name().cstr());
@ -367,8 +382,8 @@ NETLIB_UPDATE(solver)
} while ((resched && (resched_cnt < 5)) || (resched_cnt <= 1));
global_resched = global_resched || resched;
}
if (global_resched)
printf("rescheduled\n");
//if (global_resched)
// printf("rescheduled\n");
if (global_resched)
{
schedule();

View File

@ -94,6 +94,7 @@ public:
ATTR_HOT inline bool is_dynamic() { return m_dynamic.count() > 0; }
double m_accuracy;
double m_convergence_factor;
private:
netlist_net_t::list_t m_nets;
@ -114,6 +115,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_param_double_t m_convergence;
netlist_time m_inc;
netlist_time m_last_step;

View File

@ -195,3 +195,84 @@ template NETLIB_START(QBJT_switch<NETLIB_NAME(Q)::BJT_NPN>);
template NETLIB_START(QBJT_switch<NETLIB_NAME(Q)::BJT_PNP>);
template NETLIB_UPDATE_PARAM(QBJT_switch<NETLIB_NAME(Q)::BJT_NPN>);
template NETLIB_UPDATE_PARAM(QBJT_switch<NETLIB_NAME(Q)::BJT_PNP>);
// ----------------------------------------------------------------------------------------
// nld_VCCS
// ----------------------------------------------------------------------------------------
NETLIB_START(VCCS)
{
configure(1.0, NETLIST_GMIN);
}
ATTR_COLD void NETLIB_NAME(VCCS)::configure(const double Gfac, const double GI)
{
register_param("G", m_G, 1.0);
register_terminal("IP", m_IP);
register_terminal("IN", m_IN);
register_terminal("OP", m_OP);
register_terminal("ON", m_ON);
m_OP1.init_object(*this, "OP1", netlist_core_terminal_t::STATE_INP_ACTIVE);
m_ON1.init_object(*this, "ON1", netlist_core_terminal_t::STATE_INP_ACTIVE);
const double m_mult = m_G.Value() * Gfac; // 1.0 ==> 1V ==> 1A
m_IP.set(GI);
m_IP.m_otherterm = &m_IN; // <= this should be NULL and terminal be filtered out prior to solving...
m_IN.set(GI);
m_IN.m_otherterm = &m_IP; // <= this should be NULL and terminal be filtered out prior to solving...
m_OP.set(m_mult, 0.0);
m_OP.m_otherterm = &m_IP;
m_OP1.set(-m_mult, 0.0);
m_OP1.m_otherterm = &m_IN;
m_ON.set(-m_mult, 0.0);
m_ON.m_otherterm = &m_IP;
m_ON1.set(m_mult, 0.0);
m_ON1.m_otherterm = &m_IN;
m_setup->connect(m_OP, m_OP1);
m_setup->connect(m_ON, m_ON1);
}
NETLIB_UPDATE_PARAM(VCCS)
{
}
NETLIB_UPDATE(VCCS)
{
/* only called if connected to a rail net ==> notify the solver to recalculate */
netlist().solver()->schedule();
}
// ----------------------------------------------------------------------------------------
// nld_VCVS
// ----------------------------------------------------------------------------------------
NETLIB_START(VCVS)
{
register_param("RO", m_RO, 1.0);
const double gRO = 1.0 / m_RO.Value();
configure(gRO, NETLIST_GMIN);
m_OP2.init_object(*this, "OP2", netlist_core_terminal_t::STATE_INP_ACTIVE);
m_ON2.init_object(*this, "ON2", netlist_core_terminal_t::STATE_INP_ACTIVE);
m_OP2.set(gRO);
m_ON2.set(gRO);
m_OP2.m_otherterm = &m_ON2;
m_ON2.m_otherterm = &m_OP2;
setup()->connect(m_OP2, m_OP1);
setup()->connect(m_ON2, m_ON1);
}
NETLIB_UPDATE_PARAM(VCVS)
{
}

View File

@ -76,7 +76,7 @@ public:
ATTR_HOT inline void set(const double G, const double V, const double I)
{
m_P.m_g = m_N.m_g = G;
m_P.m_go = m_N.m_go = m_P.m_gt = m_N.m_gt = G;
m_N.m_Idr = ( -V) * G + I;
m_P.m_Idr = ( V) * G - I;
}
@ -243,7 +243,6 @@ 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) { }
#if 1
NETLIB_UPDATEI()
{
double vE = INPANALOG(m_EV);
@ -271,7 +270,6 @@ public:
}
}
#endif
NETLIB_NAME(R) m_RB;
NETLIB_NAME(R) m_RC;
@ -295,4 +293,110 @@ 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);
// ----------------------------------------------------------------------------------------
// nld_VCCS
// ----------------------------------------------------------------------------------------
/*
* Voltage controlled current source
*
* IP ---+ +------> OP
* | |
* RI I
* RI => G => I IOut = (V(IP)-V(IN)) * G
* RI I
* | |
* IN ---+ +------< ON
*
* G=1 ==> 1V ==> 1A
*
* RI = 1 / NETLIST_GMIN
*
*/
#define NETDEV_VCCS(_name) \
NET_REGISTER_DEV(VCCS, _name) \
//NETDEV_PARAMI(_name, model, _model)
class NETLIB_NAME(VCCS) : public netlist_device_t
{
public:
ATTR_COLD NETLIB_NAME(VCCS)()
: netlist_device_t(VCCS) { }
ATTR_COLD NETLIB_NAME(VCCS)(const family_t afamily)
: netlist_device_t(afamily) { }
protected:
ATTR_COLD virtual void start();
ATTR_COLD virtual void update_param();
ATTR_HOT ATTR_ALIGN void update();
ATTR_COLD void configure(const double Gfac, const double GI);
netlist_terminal_t m_OP;
netlist_terminal_t m_ON;
netlist_terminal_t m_IP;
netlist_terminal_t m_IN;
netlist_terminal_t m_OP1;
netlist_terminal_t m_ON1;
netlist_param_double_t m_G;
};
// ----------------------------------------------------------------------------------------
// nld_VCVS
// ----------------------------------------------------------------------------------------
/*
* Voltage controlled voltage source
*
* Parameters:
* G Default: 1
* RO Default: 1 (would be typically 50 for an op-amp
*
* IP ---+ +--+---- OP
* | | |
* RI I RO
* RI => G => I RO V(OP) - V(ON) = (V(IP)-V(IN)) * G
* RI I RO
* | | |
* IN ---+ +--+---- ON
*
* G=1 ==> 1V ==> 1V
*
* RI = 1 / NETLIST_GMIN
*
* Internal GI = G / RO
*
*/
#define NETDEV_VCVS(_name) \
NET_REGISTER_DEV(VCVS, _name) \
//NETDEV_PARAMI(_name, model, _model)
class NETLIB_NAME(VCVS) : public NETLIB_NAME(VCCS)
{
public:
ATTR_COLD NETLIB_NAME(VCVS)()
: NETLIB_NAME(VCCS)(VCVS) { }
protected:
ATTR_COLD virtual void start();
ATTR_COLD virtual void update_param();
//ATTR_HOT ATTR_ALIGN void update();
netlist_terminal_t m_OP2;
netlist_terminal_t m_ON2;
double m_mult;
netlist_param_double_t m_RO;
};
#endif /* NLD_TWOTERM_H_ */

View File

@ -484,7 +484,8 @@ ATTR_COLD netlist_core_terminal_t::netlist_core_terminal_t(const type_t atype, c
ATTR_COLD netlist_terminal_t::netlist_terminal_t()
: netlist_core_terminal_t(TERMINAL, ANALOG)
, m_Idr(0.0)
, m_g(NETLIST_GMIN)
, m_go(NETLIST_GMIN)
, m_gt(NETLIST_GMIN)
{
}

View File

@ -270,6 +270,8 @@ public:
CAPACITOR = 5, // Capacitor
DIODE = 6, // Diode
BJT_SWITCH = 7, // BJT(Switch)
VCVS = 8, // Voltage controlled voltage source
VCCS = 9, // Voltage controlled voltage source
};
ATTR_COLD netlist_object_t(const type_t atype, const family_t afamily);
@ -362,7 +364,29 @@ public:
ATTR_COLD netlist_terminal_t();
double m_Idr; // drive current
double m_g; // conductance
double m_go; // conductance for Voltage from other term
double m_gt; // conductance for total conductance
ATTR_HOT inline void set(const double G)
{
m_Idr = 0;
m_go = m_gt = G;
}
ATTR_HOT inline void set(const double GO, const double GT)
{
m_Idr = 0;
m_go = GO;
m_gt = GT;
}
ATTR_HOT inline void set(const double GO, const double GT, const double I)
{
m_Idr = I;
m_go = GO;
m_gt = GT;
}
netlist_terminal_t *m_otherterm;
};
@ -834,7 +858,7 @@ public:
ATTR_COLD virtual void init(netlist_setup_t &setup, const pstring &name);
ATTR_COLD const netlist_setup_t *setup() const { return m_setup; }
ATTR_COLD netlist_setup_t *setup() const { return m_setup; }
ATTR_COLD bool variable_input_count() { return m_variable_input_count; }

View File

@ -521,7 +521,6 @@ static NETLIST_START(pong_schematics)
//NETDEV_LOG(log1, C1.1)
#endif
#define tt(_n) \
NETDEV_R(R ## _n, 1000) \
NETDEV_D(D ## _n) \
@ -602,7 +601,122 @@ static NETLIST_START(pong_schematics)
//NETDEV_LOG(logC, Q.C)
#endif
#if 0
NETDEV_VCVS(VV)
NETDEV_R(R1, 1000)
NETDEV_R(R2, 10000)
NET_C(V5, R1.1)
NET_C(R1.2, VV.IN)
NET_C(R2.1, VV.OP)
NET_C(R2.2, VV.IN)
NET_C(VV.ON, GND)
NET_C(VV.IP, GND)
NETDEV_LOG(logX, VV.OP)
#endif
#if 0
NETDEV_VCCS(VV)
NETDEV_PARAM(VV.G, 100000) // typical OP-AMP amplification
NETDEV_R(R1, 1000)
NETDEV_R(R2, 1)
NETDEV_R(R3, 10000)
NET_C(4V, R1.1)
NET_C(R1.2, VV.IN)
NET_C(R2.1, VV.OP)
NET_C(R3.1, VV.IN)
NET_C(R3.2, VV.OP)
NET_C(R2.2, GND)
NET_C(VV.ON, GND)
NET_C(VV.IP, GND)
//NETDEV_LOG(logX, VV.OP)
//NETDEV_LOG(logY, 4V)
#endif
#if 0
NETDEV_VCVS(VV)
NETDEV_PARAM(VV.G, 100000) // typical OP-AMP amplification
NETDEV_PARAM(VV.RO, 50) // typical OP-AMP amplification
NETDEV_R(R1, 1000)
NETDEV_R(R3, 10000) // ==> 10x amplification (inverting)
NET_C(4V, R1.1)
NET_C(R1.2, VV.IN)
NET_C(R3.1, VV.IN)
NET_C(R3.2, VV.OP)
NET_C(VV.ON, GND)
NET_C(VV.IP, GND)
NETDEV_LOG(logX, VV.OP)
NETDEV_LOG(logY, 4V)
#endif
#if 0
// Impedance converter with resistor
NETDEV_VCVS(VV)
NETDEV_PARAM(VV.G, 100000) // typical OP-AMP amplification
NETDEV_PARAM(VV.RO, 50) // typical OP-AMP amplification
NETDEV_R(R3, 10000)
NET_C(4V, VV.IP)
NET_C(R3.1, VV.IN)
NET_C(R3.2, VV.OP)
NET_C(VV.ON, GND)
NETDEV_LOG(logX, VV.OP)
NETDEV_LOG(logY, 4V)
#endif
#if 0
// Impedance converter without resistor
NETDEV_VCVS(VV)
NETDEV_PARAM(VV.G, 100000) // typical OP-AMP amplification
NETDEV_PARAM(VV.RO, 50) // typical OP-AMP amplification
NET_C(4V, VV.IP)
NET_C(VV.IN, VV.OP)
NET_C(VV.ON, GND)
NETDEV_LOG(logX, VV.OP)
NETDEV_LOG(logY, 4V)
#endif
#if 0
/* Impedance converter current source opamp model from
*
* http://www.ecircuitcenter.com/Circuits/opmodel1/opmodel1.htm
*
* Bandwidth 10Mhz
*
*/
NETDEV_VCCS(G1)
NETDEV_PARAM(G1.G, 100) // typical OP-AMP amplification 100 * 1000 = 100000
NETDEV_R(RP1, 1000)
NETDEV_C(CP1, 1.59e-6) // <== change to 1.59e-3 for 10Khz bandwidth
NETDEV_VCVS(EBUF)
NETDEV_PARAM(EBUF.RO, 50)
NETDEV_PARAM(EBUF.G, 1)
NET_C(G1.IP, 4V)
NET_C(G1.IN, EBUF.OP)
NET_C(EBUF.ON, GND)
NET_C(G1.ON, GND)
NET_C(RP1.2, GND)
NET_C(CP1.2, GND)
NET_C(EBUF.IN, GND)
NET_C(RP1.1, G1.OP)
NET_C(CP1.1, RP1.1)
NET_C(EBUF.IP, RP1.1)
NETDEV_LOG(logX, EBUF.OP)
NETDEV_LOG(logY, 4V)
#endif
NETLIST_END