mirror of
https://github.com/holub/mame
synced 2025-07-05 01:48:29 +03:00
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.
This commit is contained in:
parent
45d9761cad
commit
f7856a8654
@ -244,6 +244,8 @@ void netlist_factory::initialize()
|
|||||||
ENTRY(D, NETDEV_D)
|
ENTRY(D, NETDEV_D)
|
||||||
ENTRY(VCVS, NETDEV_VCVS)
|
ENTRY(VCVS, NETDEV_VCVS)
|
||||||
ENTRY(VCCS, NETDEV_VCCS)
|
ENTRY(VCCS, NETDEV_VCCS)
|
||||||
|
ENTRY(QPNP_EB, NETDEV_QPNP_EB)
|
||||||
|
ENTRY(QNPN_EB, NETDEV_QNPN_EB)
|
||||||
ENTRY(QPNP_switch, NETDEV_QPNP)
|
ENTRY(QPNP_switch, NETDEV_QPNP)
|
||||||
ENTRY(QNPN_switch, NETDEV_QNPN)
|
ENTRY(QNPN_switch, NETDEV_QNPN)
|
||||||
ENTRY(ttl_const, NETDEV_TTL_CONST)
|
ENTRY(ttl_const, NETDEV_TTL_CONST)
|
||||||
|
@ -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()))
|
if (!m_steps.contains(&p->netdev()))
|
||||||
m_steps.add(&p->netdev());
|
m_steps.add(&p->netdev());
|
||||||
break;
|
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::VCVS:
|
||||||
//case netlist_device_t::BJT_SWITCH:
|
//case netlist_device_t::BJT_SWITCH:
|
||||||
if (!m_dynamic.contains(&p->netdev()))
|
if (!m_dynamic.contains(&p->netdev()))
|
||||||
@ -82,28 +84,15 @@ ATTR_HOT inline void netlist_matrix_solver_t::update_inputs()
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ATTR_HOT inline int netlist_matrix_solver_t::solve_non_dynamic()
|
||||||
ATTR_HOT inline bool netlist_matrix_solver_t::solve()
|
|
||||||
{
|
{
|
||||||
bool resched;
|
bool resched;
|
||||||
// FIXME: There may be situations where we *could* need more than one iteration for dynamic elements
|
|
||||||
|
|
||||||
int resched_cnt = (is_dynamic() ? /* 0 */ 1 : 1);
|
int resched_cnt = 0;
|
||||||
ATTR_UNUSED netlist_net_t *last_resched_net = NULL;
|
ATTR_UNUSED netlist_net_t *last_resched_net = NULL;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
resched = false;
|
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))
|
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));
|
//NL_VERBOSE_OUT(("New: %lld %f %f\n", netlist().time().as_raw(), netlist().time().as_double(), new_val));
|
||||||
}
|
}
|
||||||
resched_cnt++;
|
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)
|
if (!resched)
|
||||||
update_inputs();
|
update_inputs();
|
||||||
@ -199,7 +227,8 @@ NETLIB_START(solver)
|
|||||||
register_param("FREQ", m_freq, 48000.0);
|
register_param("FREQ", m_freq, 48000.0);
|
||||||
m_inc = netlist_time::from_hz(m_freq.Value());
|
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("CONVERG", m_convergence, 0.3);
|
||||||
register_param("RESCHED_LOOPS", m_resched_loops, 15);
|
register_param("RESCHED_LOOPS", m_resched_loops, 15);
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ public:
|
|||||||
|
|
||||||
// return true if a reschedule is needed ...
|
// return true if a reschedule is needed ...
|
||||||
ATTR_HOT bool solve();
|
ATTR_HOT bool solve();
|
||||||
|
ATTR_HOT int solve_non_dynamic();
|
||||||
ATTR_HOT void step(const netlist_time delta);
|
ATTR_HOT void step(const netlist_time delta);
|
||||||
ATTR_HOT void update_inputs();
|
ATTR_HOT void update_inputs();
|
||||||
|
|
||||||
|
@ -250,6 +250,60 @@ 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_NPN>);
|
||||||
template NETLIB_UPDATE_PARAM(QBJT_switch<NETLIB_NAME(Q)::BJT_PNP>);
|
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
|
// nld_VCCS
|
||||||
// ----------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------
|
||||||
|
@ -65,6 +65,15 @@
|
|||||||
NET_REGISTER_DEV(QNPN_switch, _name) \
|
NET_REGISTER_DEV(QNPN_switch, _name) \
|
||||||
NETDEV_PARAMI(_name, model, # _model)
|
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
|
// 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
|
* + - C
|
||||||
* B ----VVV----+ |
|
* B ----VVV----+ |
|
||||||
@ -265,94 +320,56 @@ protected:
|
|||||||
* E
|
* E
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// 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:
|
|
||||||
};
|
|
||||||
|
|
||||||
//NETLIB_NAME(Q) nld_Q::q_type
|
|
||||||
template <NETLIB_NAME(Q)::q_type _type>
|
template <NETLIB_NAME(Q)::q_type _type>
|
||||||
class NETLIB_NAME(QBJT_switch) : public NETLIB_NAME(QBJT)
|
class NETLIB_NAME(QBJT_switch) : public NETLIB_NAME(QBJT)
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ATTR_COLD NETLIB_NAME(QBJT_switch)()
|
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_NAME(QBJT)(_type, BJT_SWITCH), m_gB(NETLIST_GMIN), m_gC(NETLIST_GMIN), m_V(0.0), m_state_on(0) { }
|
||||||
|
|
||||||
NETLIB_UPDATEI()
|
NETLIB_UPDATEI()
|
||||||
{
|
{
|
||||||
double vE = INPANALOG(m_EV);
|
double vE = INPANALOG(m_EV);
|
||||||
double vB = INPANALOG(m_BV);
|
double vB = INPANALOG(m_BV);
|
||||||
double m = (_type == BJT_NPN) ? 1 : -1;
|
double m = (_type == BJT_NPN) ? 1 : -1;
|
||||||
|
|
||||||
int new_state = ((vB - vE) * m > m_V ) ? 1 : 0;
|
int new_state = ((vB - vE) * m > m_V ) ? 1 : 0;
|
||||||
if (m_state_on ^ new_state)
|
if (m_state_on ^ new_state)
|
||||||
{
|
{
|
||||||
double gb = m_gB;
|
double gb = m_gB;
|
||||||
double gc = m_gC;
|
double gc = m_gC;
|
||||||
double v = m_V * m;
|
double v = m_V * m;
|
||||||
if (!new_state )
|
if (!new_state )
|
||||||
{
|
{
|
||||||
// not conducting
|
// not conducting
|
||||||
gb = NETLIST_GMIN;
|
gb = NETLIST_GMIN;
|
||||||
v = 0;
|
v = 0;
|
||||||
gc = NETLIST_GMIN;
|
gc = NETLIST_GMIN;
|
||||||
}
|
}
|
||||||
m_RB.set(gb, v, 0.0);
|
m_RB.set(gb, v, 0.0);
|
||||||
m_RC.set(gc, 0.0, 0.0);
|
m_RC.set(gc, 0.0, 0.0);
|
||||||
m_state_on = new_state;
|
m_state_on = new_state;
|
||||||
m_RB.update_dev();
|
m_RB.update_dev();
|
||||||
m_RC.update_dev();
|
m_RC.update_dev();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NETLIB_NAME(R) m_RB;
|
NETLIB_NAME(R) m_RB;
|
||||||
NETLIB_NAME(R) m_RC;
|
NETLIB_NAME(R) m_RC;
|
||||||
|
|
||||||
netlist_analog_input_t m_BV;
|
netlist_analog_input_t m_BV;
|
||||||
netlist_analog_input_t m_EV;
|
netlist_analog_input_t m_EV;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
ATTR_COLD virtual void start();
|
ATTR_COLD virtual void start();
|
||||||
ATTR_HOT void update_param();
|
ATTR_HOT void update_param();
|
||||||
|
|
||||||
double m_gB; // base conductance / switch on
|
double m_gB; // base conductance / switch on
|
||||||
double m_gC; // collector conductance / switch on
|
double m_gC; // collector conductance / switch on
|
||||||
double m_V; // internal voltage source
|
double m_V; // internal voltage source
|
||||||
UINT8 m_state_on;
|
UINT8 m_state_on;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
};
|
};
|
||||||
@ -360,6 +377,148 @@ private:
|
|||||||
typedef NETLIB_NAME(QBJT_switch)<NETLIB_NAME(Q)::BJT_PNP> NETLIB_NAME(QPNP_switch);
|
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);
|
typedef NETLIB_NAME(QBJT_switch)<NETLIB_NAME(Q)::BJT_NPN> NETLIB_NAME(QNPN_switch);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------------------
|
||||||
|
// nld_QBJT_EB
|
||||||
|
// ----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
struct generic_diode
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
|
||||||
|
G = m_Is * m_VtInv * eVDVt;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <NETLIB_NAME(Q)::q_type _type>
|
||||||
|
class NETLIB_NAME(QBJT_EB) : public NETLIB_NAME(QBJT)
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
ATTR_COLD virtual void start();
|
||||||
|
ATTR_HOT void update_param();
|
||||||
|
|
||||||
|
generic_diode m_D_BE;
|
||||||
|
generic_diode m_D_BC;
|
||||||
|
|
||||||
|
nld_twoterm m_I_BC;
|
||||||
|
nld_twoterm m_I_BE;
|
||||||
|
|
||||||
|
double m_alpha_f;
|
||||||
|
double m_alpha_r;
|
||||||
|
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------------------
|
||||||
|
// nld_CCCS
|
||||||
|
// ----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------
|
||||||
// nld_VCCS
|
// nld_VCCS
|
||||||
// ----------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------
|
||||||
|
@ -272,6 +272,7 @@ public:
|
|||||||
BJT_SWITCH = 7, // BJT(Switch)
|
BJT_SWITCH = 7, // BJT(Switch)
|
||||||
VCVS = 8, // Voltage controlled voltage source
|
VCVS = 8, // Voltage controlled voltage source
|
||||||
VCCS = 9, // 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);
|
ATTR_COLD netlist_object_t(const type_t atype, const family_t afamily);
|
||||||
|
@ -65,6 +65,10 @@ void netlist_parser::parse(const char *buf)
|
|||||||
else if (n == "NETDEV_QNPN")
|
else if (n == "NETDEV_QNPN")
|
||||||
netdev_device(n, "model", true);
|
netdev_device(n, "model", true);
|
||||||
else if (n == "NETDEV_QPNP")
|
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);
|
netdev_device(n, "model", true);
|
||||||
else if ((n == "NETDEV_TTL_CONST") || (n == "NETDEV_ANALOG_CONST"))
|
else if ((n == "NETDEV_TTL_CONST") || (n == "NETDEV_ANALOG_CONST"))
|
||||||
netdev_const(n);
|
netdev_const(n);
|
||||||
|
@ -84,6 +84,8 @@ enum input_changed_enum
|
|||||||
static NETLIST_START(pong_schematics)
|
static NETLIST_START(pong_schematics)
|
||||||
NETDEV_SOLVER(Solver)
|
NETDEV_SOLVER(Solver)
|
||||||
NETDEV_PARAM(Solver.FREQ, 48000)
|
NETDEV_PARAM(Solver.FREQ, 48000)
|
||||||
|
NETDEV_PARAM(Solver.ACCURACY, 1e-4) // works and is sufficient
|
||||||
|
|
||||||
NETDEV_ANALOG_CONST(V5, 5)
|
NETDEV_ANALOG_CONST(V5, 5)
|
||||||
|
|
||||||
NETDEV_TTL_CONST(high, 1)
|
NETDEV_TTL_CONST(high, 1)
|
||||||
|
Loading…
Reference in New Issue
Block a user