diff --git a/.gitattributes b/.gitattributes index 7c2a204f708..b1e366f797c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2136,6 +2136,10 @@ src/emu/memarray.c svneol=native#text/plain src/emu/memarray.h svneol=native#text/plain src/emu/memory.c svneol=native#text/plain src/emu/memory.h svneol=native#text/plain +src/emu/netlist/analog/nld_bjt.c svneol=native#text/plain +src/emu/netlist/analog/nld_bjt.h svneol=native#text/plain +src/emu/netlist/analog/nld_fourterm.c svneol=native#text/plain +src/emu/netlist/analog/nld_fourterm.h svneol=native#text/plain src/emu/netlist/analog/nld_solver.c svneol=native#text/plain src/emu/netlist/analog/nld_solver.h svneol=native#text/plain src/emu/netlist/analog/nld_switches.c svneol=native#text/plain diff --git a/src/emu/netlist/analog/nld_bjt.c b/src/emu/netlist/analog/nld_bjt.c new file mode 100644 index 00000000000..ba59bb0b954 --- /dev/null +++ b/src/emu/netlist/analog/nld_bjt.c @@ -0,0 +1,170 @@ +/* + * nld_bjt.c + * + */ + +#include "nld_bjt.h" +#include "../nl_setup.h" +#include "nld_solver.h" + +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, ""); +} + +template +NETLIB_START(QBJT_switch<_type>) +{ + NETLIB_NAME(Q)::start(); + + register_sub(m_RB, "RB"); + register_sub(m_RC, "RC"); + register_input("BV", m_BV); + register_input("EV", m_EV); + + register_subalias("B", m_RB.m_P); + register_subalias("E", m_RB.m_N); + register_subalias("C", m_RC.m_P); + + setup().connect(m_RB.m_N, m_RC.m_N); + setup().connect(m_RB.m_P, m_BV); + setup().connect(m_RB.m_N, m_EV); + + save(NAME(m_state_on)); + + m_RB.set(NETLIST_GMIN, 0.0, 0.0); + m_RC.set(NETLIST_GMIN, 0.0, 0.0); + + m_state_on = 0; + +} + +NETLIB_UPDATE(Q) +{ + netlist().solver()->schedule(); +} + +template +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 + + m_V = d.V(0.005 / alpha); + + /* Base current is 0.005 / beta + * as a rough estimate, we just scale the conductance down */ + + m_gB = d.gI(0.005 / alpha) / BF; + + 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); + m_RB.set(NETLIST_GMIN, 0.0, 0.0); + m_RC.set(NETLIST_GMIN, 0.0, 0.0); +} + +template NETLIB_START(QBJT_switch); +template NETLIB_START(QBJT_switch); +template NETLIB_UPDATE_PARAM(QBJT_switch); +template NETLIB_UPDATE_PARAM(QBJT_switch); + +// ---------------------------------------------------------------------------------------- +// nld_Q - Ebers Moll +// ---------------------------------------------------------------------------------------- + +template +NETLIB_START(QBJT_EB<_type>) +{ + NETLIB_NAME(Q)::start(); + + register_terminal("B", m_D_BE.m_P); // Anode + register_terminal("E", m_D_BE.m_N); // Cathode + + register_terminal("_B1", m_D_BC.m_P); // Anode + register_terminal("C", m_D_BC.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_P, m_D_BC.m_P); + setup().connect(m_D_BE.m_P, m_I_BE.m_P); + setup().connect(m_D_BE.m_P, m_I_BC.m_P); + + setup().connect(m_D_BE.m_N, m_I_BE.m_N); + setup().connect(m_D_BC.m_N, m_I_BC.m_N); + + m_gD_BE.save("m_D_BE", *this); + m_gD_BC.save("m_D_BC", *this); + +} + + +template +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_gD_BE.set_param(IS / m_alpha_f, NF); + m_gD_BC.set_param(IS / m_alpha_r, NR); + +} + +template NETLIB_START(QBJT_EB); +template NETLIB_START(QBJT_EB); +template NETLIB_UPDATE_PARAM(QBJT_EB); +template NETLIB_UPDATE_PARAM(QBJT_EB); + diff --git a/src/emu/netlist/analog/nld_bjt.h b/src/emu/netlist/analog/nld_bjt.h new file mode 100644 index 00000000000..a0079895436 --- /dev/null +++ b/src/emu/netlist/analog/nld_bjt.h @@ -0,0 +1,207 @@ +/* + * nld_bjt.h + * + */ + +#ifndef NLD_BJT_H_ +#define NLD_BJT_H_ + +#include "../nl_base.h" +#include "nld_twoterm.h" + +// ---------------------------------------------------------------------------------------- +// Macros +// ---------------------------------------------------------------------------------------- + +#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_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) + + +// ---------------------------------------------------------------------------------------- +// 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 + * B ----VVV----+ | + * | | + * Rb Rc + * Rb Rc + * Rb Rc + * | | + * +----+----+ + * | + * E + */ + +template +class NETLIB_NAME(QBJT_switch) : public NETLIB_NAME(QBJT) +{ +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) { } + + NETLIB_UPDATEI() + { + double vE = INPANALOG(m_EV); + double vB = INPANALOG(m_BV); + double m = (_type == BJT_NPN) ? 1 : -1; + + int new_state = ((vB - vE) * m > m_V ) ? 1 : 0; + if (m_state_on ^ new_state) + { + double gb = m_gB; + double gc = m_gC; + double v = m_V * m; + if (!new_state ) + { + // not conducting + gb = NETLIST_GMIN; + v = 0; + gc = NETLIST_GMIN; + } + m_RB.set(gb, v, 0.0); + m_RC.set(gc, 0.0, 0.0); + m_state_on = new_state; + m_RB.update_dev(); + m_RC.update_dev(); + } + + } + + NETLIB_NAME(R) m_RB; + NETLIB_NAME(R) m_RC; + + netlist_analog_input_t m_BV; + netlist_analog_input_t m_EV; + +protected: + + ATTR_COLD virtual void start(); + ATTR_HOT void update_param(); + + double m_gB; // base conductance / switch on + double m_gC; // collector conductance / switch on + double m_V; // internal voltage source + UINT8 m_state_on; + +private: +}; + +typedef NETLIB_NAME(QBJT_switch) NETLIB_NAME(QPNP_switch); +typedef NETLIB_NAME(QBJT_switch) NETLIB_NAME(QNPN_switch); + +// ---------------------------------------------------------------------------------------- +// nld_QBJT_EB +// ---------------------------------------------------------------------------------------- + + + + +template +class NETLIB_NAME(QBJT_EB) : public NETLIB_NAME(QBJT) +{ +public: + ATTR_COLD NETLIB_NAME(QBJT_EB)() + : NETLIB_NAME(QBJT)(_type, BJT_EB), + m_D_BC(netlist_object_t::ANALOG), + m_D_BE(netlist_object_t::ANALOG), + m_I_BC(netlist_object_t::ANALOG), + m_I_BE(netlist_object_t::ANALOG) + { } + + NETLIB_UPDATE_TERMINALS() + { + m_gD_BE.update_diode(m_D_BE.deltaV()); + m_gD_BC.update_diode(m_D_BC.deltaV()); + + m_D_BE.set(m_gD_BE.G(), 0.0, m_gD_BE.Ieq()); + m_D_BC.set(m_gD_BC.G(), 0.0, m_gD_BC.Ieq()); + + m_I_BE.set(0.0, 0.0, - m_alpha_r * m_gD_BC.I()); + m_I_BC.set(0.0, 0.0, - m_alpha_f * m_gD_BE.I()); + } + +protected: + + ATTR_COLD virtual void start(); + ATTR_HOT void update_param(); + + netlist_generic_diode m_gD_BC; + netlist_generic_diode m_gD_BE; + + nld_twoterm m_D_BC; + nld_twoterm m_D_BE; + + 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(QPNP_EB); +typedef NETLIB_NAME(QBJT_EB) NETLIB_NAME(QNPN_EB); + + +#endif /* NLD_BJT_H_ */ diff --git a/src/emu/netlist/analog/nld_fourterm.c b/src/emu/netlist/analog/nld_fourterm.c new file mode 100644 index 00000000000..0c8a51e07cb --- /dev/null +++ b/src/emu/netlist/analog/nld_fourterm.c @@ -0,0 +1,88 @@ +/* + * nld_fourterm.c + * + */ + +#include "nld_fourterm.h" +#include "../nl_setup.h" +#include "nld_solver.h" + +// ---------------------------------------------------------------------------------------- +// 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, name() + ".OP1", netlist_core_terminal_t::STATE_INP_ACTIVE); + m_ON1.init_object(*this, name() + ".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; + + setup().connect(m_OP, m_OP1); + 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) +{ +} + diff --git a/src/emu/netlist/analog/nld_fourterm.h b/src/emu/netlist/analog/nld_fourterm.h new file mode 100644 index 00000000000..0520329a08d --- /dev/null +++ b/src/emu/netlist/analog/nld_fourterm.h @@ -0,0 +1,145 @@ +/* + * nld_fourterm.h + * + */ + +#ifndef NLD_FOURTERM_H_ +#define NLD_FOURTERM_H_ + + +#include "../nl_base.h" +#include "nld_twoterm.h" + +// ---------------------------------------------------------------------------------------- +// Macros +// ---------------------------------------------------------------------------------------- + +#define NETDEV_VCCS(_name) \ + NET_REGISTER_DEV(VCCS, _name) +//NETDEV_PARAMI(_name, model, _model) + +#define NETDEV_VCVS(_name) \ + NET_REGISTER_DEV(VCVS, _name) +//NETDEV_PARAMI(_name, model, _model) + +// ---------------------------------------------------------------------------------------- +// 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 +// ---------------------------------------------------------------------------------------- + +/* + * 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 + * + */ + +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 + * + */ + + +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; + + netlist_param_double_t m_RO; +}; + + +#endif /* NLD_FOURTERM_H_ */ diff --git a/src/emu/netlist/analog/nld_twoterm.c b/src/emu/netlist/analog/nld_twoterm.c index 5dc493a9b84..c2ebd93f0d8 100644 --- a/src/emu/netlist/analog/nld_twoterm.c +++ b/src/emu/netlist/analog/nld_twoterm.c @@ -127,23 +127,17 @@ NETLIB_START(D) register_terminal("K", m_N); register_param("model", m_model, ""); - m_Vd = 0.7; - - save(NAME(m_Vd)); + m_D.save("m_D", *this); } NETLIB_UPDATE_PARAM(D) { - m_Is = m_model.dValue("Is", 1e-15); - m_n = m_model.dValue("N", 1); + double Is = m_model.dValue("Is", 1e-15); + double n = m_model.dValue("N", 1); - m_Vt = 0.0258 * m_n; - - m_Vcrit = m_Vt * log(m_Vt / m_Is / sqrt(2.0)); - m_VtInv = 1.0 / m_Vt; - NL_VERBOSE_OUT(("VCutoff: %f\n", m_Vcrit)); + m_D.set_param(Is, n); } NETLIB_UPDATE(D) @@ -151,233 +145,4 @@ 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, ""); -} - -template -NETLIB_START(QBJT_switch<_type>) -{ - NETLIB_NAME(Q)::start(); - - register_sub(m_RB, "RB"); - register_sub(m_RC, "RC"); - register_input("BV", m_BV); - register_input("EV", m_EV); - - register_subalias("B", m_RB.m_P); - register_subalias("E", m_RB.m_N); - register_subalias("C", m_RC.m_P); - - setup().connect(m_RB.m_N, m_RC.m_N); - setup().connect(m_RB.m_P, m_BV); - setup().connect(m_RB.m_N, m_EV); - - save(NAME(m_state_on)); - - m_RB.set(NETLIST_GMIN, 0.0, 0.0); - m_RC.set(NETLIST_GMIN, 0.0, 0.0); - - m_state_on = 0; - -} - -NETLIB_UPDATE(Q) -{ - netlist().solver()->schedule(); -} - -template -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 - - m_V = d.V(0.005 / BF); - - m_gB = d.gI(0.005 / BF); - 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); - m_RB.set(NETLIST_GMIN, 0.0, 0.0); - m_RC.set(NETLIST_GMIN, 0.0, 0.0); -} - -template NETLIB_START(QBJT_switch); -template NETLIB_START(QBJT_switch); -template NETLIB_UPDATE_PARAM(QBJT_switch); -template NETLIB_UPDATE_PARAM(QBJT_switch); - -// ---------------------------------------------------------------------------------------- -// nld_Q - Ebers Moll -// ---------------------------------------------------------------------------------------- - -template -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_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); -template NETLIB_START(QBJT_EB); -template NETLIB_UPDATE_PARAM(QBJT_EB); -template NETLIB_UPDATE_PARAM(QBJT_EB); - -// ---------------------------------------------------------------------------------------- -// 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, name() + ".OP1", netlist_core_terminal_t::STATE_INP_ACTIVE); - m_ON1.init_object(*this, name() + ".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; - - setup().connect(m_OP, m_OP1); - 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) -{ -} diff --git a/src/emu/netlist/analog/nld_twoterm.h b/src/emu/netlist/analog/nld_twoterm.h index 894afdfbf9b..74a589ac233 100644 --- a/src/emu/netlist/analog/nld_twoterm.h +++ b/src/emu/netlist/analog/nld_twoterm.h @@ -57,23 +57,6 @@ NET_REGISTER_DEV(D, _name) \ NETDEV_PARAMI(_name, model, # _model) -#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_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 // ---------------------------------------------------------------------------------------- @@ -100,6 +83,9 @@ public: m_N.m_Idr = ( -V) * G + I; m_P.m_Idr = ( V) * G - I; } + + ATTR_HOT inline double deltaV() { return m_P.net().Q_Analog()- m_N.net().Q_Analog(); } + protected: ATTR_COLD virtual void start(); ATTR_HOT ATTR_ALIGN void update(); @@ -153,7 +139,7 @@ public: ATTR_HOT void step_time(const double st) { double G = m_C.Value() / st; - double I = -G * (m_P.net().Q_Analog()- m_N.net().Q_Analog()); + double I = -G * deltaV(); set(G, 0.0, I); } @@ -166,6 +152,89 @@ protected: }; + +// ---------------------------------------------------------------------------------------- +// A generic diode model to be used in other devices (Diode, BJT ...) +// ---------------------------------------------------------------------------------------- + +class netlist_generic_diode +{ +public: + netlist_generic_diode() {} + + ATTR_HOT inline void update_diode(const double nVd) + { + //FIXME: Optimize cutoff case + + if (nVd < -5.0 * m_Vt) + { + m_Vd = nVd; + m_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); + m_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); + + m_G = m_Is * m_VtInv * eVDVt; + } + + //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; } + ATTR_HOT inline double G() { return m_G; } + ATTR_HOT inline double Ieq() { return (m_Id - m_Vd * m_G); } + + /* owning object must save those ... */ + + void save(pstring name, netlist_object_t &parent) + { + parent.save(m_Vd, name + ".m_Vd"); + parent.save(m_Id, name + ".m_Id"); + parent.save(m_G, name + ".m_G"); + } + +private: + double m_Vd; + double m_Id; + double m_G; + + double m_Vt; + double m_Is; + double m_n; + + double m_VtInv; + double m_Vcrit; +}; + // ---------------------------------------------------------------------------------------- // nld_D // ---------------------------------------------------------------------------------------- @@ -206,42 +275,8 @@ public: NETLIB_UPDATE_TERMINALS() { - const double nVd = m_P.net().Q_Analog()- m_N.net().Q_Analog(); - - //FIXME: Optimize cutoff case - - double Id; - double G; - - if (nVd < -5.0 * m_Vt) - { - m_Vd = nVd; - G = NETLIST_GMIN; - Id = - m_Is; - } - else if (nVd < m_Vcrit) - { - m_Vd = nVd; - - const double eVDVt = exp(m_Vd * m_VtInv); - 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); - Id = m_Is * (eVDVt - 1.0); - - G = m_Is * m_VtInv * eVDVt; - } - - double I = (Id - m_Vd * G); - set(G, 0.0, I); + m_D.update_diode(deltaV()); + set(m_D.G(), 0.0, m_D.Ieq()); } protected: @@ -251,374 +286,9 @@ protected: netlist_param_model_t m_model; - double m_Vt; - double m_Is; - double m_n; - - double m_VtInv; - double m_Vcrit; - double m_Vd; - + netlist_generic_diode m_D; }; -// ---------------------------------------------------------------------------------------- -// 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 - * B ----VVV----+ | - * | | - * Rb Rc - * Rb Rc - * Rb Rc - * | | - * +----+----+ - * | - * E - */ - -template -class NETLIB_NAME(QBJT_switch) : public NETLIB_NAME(QBJT) -{ -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) { } - - NETLIB_UPDATEI() - { - double vE = INPANALOG(m_EV); - double vB = INPANALOG(m_BV); - double m = (_type == BJT_NPN) ? 1 : -1; - - int new_state = ((vB - vE) * m > m_V ) ? 1 : 0; - if (m_state_on ^ new_state) - { - double gb = m_gB; - double gc = m_gC; - double v = m_V * m; - if (!new_state ) - { - // not conducting - gb = NETLIST_GMIN; - v = 0; - gc = NETLIST_GMIN; - } - m_RB.set(gb, v, 0.0); - m_RC.set(gc, 0.0, 0.0); - m_state_on = new_state; - m_RB.update_dev(); - m_RC.update_dev(); - } - - } - - NETLIB_NAME(R) m_RB; - NETLIB_NAME(R) m_RC; - - netlist_analog_input_t m_BV; - netlist_analog_input_t m_EV; - -protected: - - ATTR_COLD virtual void start(); - ATTR_HOT void update_param(); - - double m_gB; // base conductance / switch on - double m_gC; // collector conductance / switch on - double m_V; // internal voltage source - UINT8 m_state_on; - -private: -}; - -typedef NETLIB_NAME(QBJT_switch) NETLIB_NAME(QPNP_switch); -typedef NETLIB_NAME(QBJT_switch) 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 -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(QPNP_EB); -typedef NETLIB_NAME(QBJT_EB) 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 -// ---------------------------------------------------------------------------------------- - -/* - * 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; - - netlist_param_double_t m_RO; -}; #endif /* NLD_TWOTERM_H_ */ diff --git a/src/emu/netlist/devices/net_lib.h b/src/emu/netlist/devices/net_lib.h index fbe953565fd..e1ac0f20f1e 100644 --- a/src/emu/netlist/devices/net_lib.h +++ b/src/emu/netlist/devices/net_lib.h @@ -75,9 +75,11 @@ #include "nld_log.h" -#include "../analog/nld_twoterm.h" +#include "../analog/nld_bjt.h" +#include "../analog/nld_fourterm.h" #include "../analog/nld_solver.h" #include "../analog/nld_switches.h" +#include "../analog/nld_twoterm.h" #include "nld_legacy.h" diff --git a/src/emu/netlist/netlist.mak b/src/emu/netlist/netlist.mak index 6b3644d6c81..1cea6f87e52 100644 --- a/src/emu/netlist/netlist.mak +++ b/src/emu/netlist/netlist.mak @@ -28,9 +28,11 @@ NETLISTOBJS+= \ $(NETLISTOBJ)/nl_setup.o \ $(NETLISTOBJ)/pstring.o \ $(NETLISTOBJ)/pstate.o \ + $(NETLISTOBJ)/analog/nld_bjt.o \ + $(NETLISTOBJ)/analog/nld_fourterm.o \ $(NETLISTOBJ)/analog/nld_solver.o \ - $(NETLISTOBJ)/analog/nld_twoterm.o \ $(NETLISTOBJ)/analog/nld_switches.o \ + $(NETLISTOBJ)/analog/nld_twoterm.o \ $(NETLISTOBJ)/devices/nld_7404.o \ $(NETLISTOBJ)/devices/nld_7474.o \ $(NETLISTOBJ)/devices/nld_7483.o \