mirror of
https://github.com/holub/mame
synced 2025-06-02 02:49:44 +03:00

- Removed DUMMY_INPUT. NC (not connected) pins should now use NC_PIN. If a NC_PIN is actually connected, an error will be logged and validation will fail. - Enabled "extended" validation. This will catch now if power terminals are not connected. - Added const and noexcept where appropriate. - Removed dead code. - Fixed the 7414 Schmitt-Trigger device to use nld_power_pins
506 lines
12 KiB
C++
506 lines
12 KiB
C++
// license:GPL-2.0+
|
|
// copyright-holders:Couriersud
|
|
|
|
#ifndef NLID_TWOTERM_H_
|
|
#define NLID_TWOTERM_H_
|
|
|
|
///
|
|
/// \file nlid_twoterm.h
|
|
///
|
|
/// Devices with two terminals ...
|
|
///
|
|
///
|
|
/// (k)
|
|
/// +-----T-----+
|
|
/// | | |
|
|
/// | +--+--+ |
|
|
/// | | | |
|
|
/// | R | |
|
|
/// | R | |
|
|
/// | R I |
|
|
/// | | I | Device n
|
|
/// | V+ I |
|
|
/// | V | |
|
|
/// | V- | |
|
|
/// | | | |
|
|
/// | +--+--+ |
|
|
/// | | |
|
|
/// +-----T-----+
|
|
/// (l)
|
|
///
|
|
/// This is a resistance in series to a voltage source and paralleled by a
|
|
/// current source. This is suitable to model voltage sources, current sources,
|
|
/// resistors, capacitors, inductances and diodes.
|
|
///
|
|
////
|
|
|
|
#include "netlist/nl_base.h"
|
|
#include "netlist/nl_setup.h"
|
|
#include "netlist/solver/nld_solver.h"
|
|
#include "nld_generic_models.h"
|
|
#include "netlist/plib/pfunction.h"
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Implementation
|
|
// -----------------------------------------------------------------------------
|
|
|
|
namespace netlist
|
|
{
|
|
namespace analog
|
|
{
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// nld_twoterm
|
|
// -----------------------------------------------------------------------------
|
|
|
|
template <class C>
|
|
inline core_device_t &bselect(bool b, C &d1, core_device_t &d2)
|
|
{
|
|
auto *h = dynamic_cast<core_device_t *>(&d1);
|
|
return b ? *h : d2;
|
|
}
|
|
template<>
|
|
inline core_device_t &bselect(bool b, netlist_state_t &d1, core_device_t &d2)
|
|
{
|
|
plib::unused_var(d1);
|
|
if (b)
|
|
plib::pthrow<nl_exception>("bselect with netlist and b==true");
|
|
return d2;
|
|
}
|
|
|
|
NETLIB_OBJECT(twoterm)
|
|
{
|
|
NETLIB_CONSTRUCTOR_EX(twoterm, bool terminals_owned = false)
|
|
, m_P(bselect(terminals_owned, owner, *this), (terminals_owned ? name + "." : "") + "1", &m_N)
|
|
, m_N(bselect(terminals_owned, owner, *this), (terminals_owned ? name + "." : "") + "2", &m_P)
|
|
{
|
|
}
|
|
|
|
terminal_t m_P;
|
|
terminal_t m_N;
|
|
|
|
//NETLIB_UPDATE_TERMINALSI() { }
|
|
//NETLIB_RESETI() { }
|
|
|
|
public:
|
|
|
|
NETLIB_UPDATEI();
|
|
|
|
void solve_now();
|
|
|
|
void solve_later(netlist_time delay = netlist_time::quantum()) noexcept;
|
|
|
|
void set_G_V_I(nl_fptype G, nl_fptype V, nl_fptype I) const noexcept
|
|
{
|
|
// GO, GT, I
|
|
m_P.set_go_gt_I( -G, G, ( V) * G - I);
|
|
m_N.set_go_gt_I( -G, G, ( -V) * G + I);
|
|
}
|
|
|
|
nl_fptype deltaV() const noexcept
|
|
{
|
|
return m_P.net().Q_Analog() - m_N.net().Q_Analog();
|
|
}
|
|
|
|
nl_fptype V1P() const noexcept
|
|
{
|
|
return m_P.net().Q_Analog();
|
|
}
|
|
|
|
nl_fptype V2N() const noexcept
|
|
{
|
|
return m_N.net().Q_Analog();
|
|
}
|
|
|
|
void set_mat(nl_fptype a11, nl_fptype a12, nl_fptype rhs1,
|
|
nl_fptype a21, nl_fptype a22, nl_fptype rhs2) const noexcept
|
|
{
|
|
// GO, GT, I
|
|
m_P.set_go_gt_I(a12, a11, rhs1);
|
|
m_N.set_go_gt_I(a21, a22, rhs2);
|
|
}
|
|
|
|
private:
|
|
};
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// nld_R
|
|
// -----------------------------------------------------------------------------
|
|
|
|
NETLIB_OBJECT_DERIVED(R_base, twoterm)
|
|
{
|
|
NETLIB_CONSTRUCTOR_DERIVED(R_base, twoterm)
|
|
{
|
|
}
|
|
|
|
void set_R(nl_fptype R) const noexcept
|
|
{
|
|
const nl_fptype G = plib::reciprocal(R);
|
|
set_mat( G, -G, nlconst::zero(),
|
|
-G, G, nlconst::zero());
|
|
}
|
|
|
|
NETLIB_RESETI();
|
|
|
|
protected:
|
|
//NETLIB_UPDATEI();
|
|
|
|
};
|
|
|
|
NETLIB_OBJECT_DERIVED(R, R_base)
|
|
{
|
|
NETLIB_CONSTRUCTOR_DERIVED(R, R_base)
|
|
, m_R(*this, "R", nlconst::magic(1e9))
|
|
{
|
|
}
|
|
|
|
|
|
protected:
|
|
|
|
//NETLIB_UPDATEI() { }
|
|
NETLIB_RESETI()
|
|
{
|
|
NETLIB_NAME(twoterm)::reset();
|
|
set_R(std::max(m_R(), exec().gmin()));
|
|
}
|
|
|
|
NETLIB_UPDATE_PARAMI()
|
|
{
|
|
solve_now();
|
|
set_R(std::max(m_R(), exec().gmin()));
|
|
}
|
|
|
|
private:
|
|
param_fp_t m_R;
|
|
// protect set_R ... it's a recipe to desaster when used to bypass the parameter
|
|
using NETLIB_NAME(R_base)::set_R;
|
|
};
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// nld_POT
|
|
// -----------------------------------------------------------------------------
|
|
|
|
NETLIB_OBJECT(POT)
|
|
{
|
|
NETLIB_CONSTRUCTOR(POT)
|
|
, m_R1(*this, "_R1")
|
|
, m_R2(*this, "_R2")
|
|
, m_R(*this, "R", 10000)
|
|
, m_Dial(*this, "DIAL", nlconst::half())
|
|
, m_DialIsLog(*this, "DIALLOG", false)
|
|
, m_Reverse(*this, "REVERSE", false)
|
|
{
|
|
register_subalias("1", m_R1.m_P);
|
|
register_subalias("2", m_R1.m_N);
|
|
register_subalias("3", m_R2.m_N);
|
|
|
|
connect(m_R2.m_P, m_R1.m_N);
|
|
|
|
}
|
|
|
|
//NETLIB_UPDATEI();
|
|
NETLIB_RESETI();
|
|
NETLIB_UPDATE_PARAMI();
|
|
|
|
private:
|
|
NETLIB_SUB(R_base) m_R1;
|
|
NETLIB_SUB(R_base) m_R2;
|
|
|
|
param_fp_t m_R;
|
|
param_fp_t m_Dial;
|
|
param_logic_t m_DialIsLog;
|
|
param_logic_t m_Reverse;
|
|
};
|
|
|
|
NETLIB_OBJECT(POT2)
|
|
{
|
|
NETLIB_CONSTRUCTOR(POT2)
|
|
, m_R1(*this, "_R1")
|
|
, m_R(*this, "R", nlconst::magic(10000.0))
|
|
, m_Dial(*this, "DIAL", nlconst::half())
|
|
, m_DialIsLog(*this, "DIALLOG", false)
|
|
, m_Reverse(*this, "REVERSE", false)
|
|
{
|
|
register_subalias("1", m_R1.m_P);
|
|
register_subalias("2", m_R1.m_N);
|
|
|
|
}
|
|
|
|
//NETLIB_UPDATEI();
|
|
NETLIB_RESETI();
|
|
NETLIB_UPDATE_PARAMI();
|
|
|
|
private:
|
|
NETLIB_SUB(R_base) m_R1;
|
|
|
|
param_fp_t m_R;
|
|
param_fp_t m_Dial;
|
|
param_logic_t m_DialIsLog;
|
|
param_logic_t m_Reverse;
|
|
};
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// nld_C
|
|
// -----------------------------------------------------------------------------
|
|
|
|
NETLIB_OBJECT_DERIVED(C, twoterm)
|
|
{
|
|
public:
|
|
NETLIB_CONSTRUCTOR_DERIVED(C, twoterm)
|
|
, m_C(*this, "C", nlconst::magic(1e-6))
|
|
, m_cap(*this, "m_cap")
|
|
{
|
|
}
|
|
|
|
NETLIB_IS_TIMESTEP(true)
|
|
NETLIB_TIMESTEPI()
|
|
{
|
|
m_cap.timestep(m_C(), deltaV(), step);
|
|
if (m_cap.type() == capacitor_e::CONSTANT_CAPACITY)
|
|
{
|
|
const nl_fptype I = m_cap.Ieq(m_C(), deltaV());
|
|
const nl_fptype G = m_cap.G(m_C());
|
|
set_mat( G, -G, -I,
|
|
-G, G, I);
|
|
}
|
|
}
|
|
|
|
NETLIB_IS_DYNAMIC(m_cap.type() == capacitor_e::VARIABLE_CAPACITY)
|
|
NETLIB_UPDATE_TERMINALSI()
|
|
{
|
|
const nl_fptype I = m_cap.Ieq(m_C(), deltaV());
|
|
const nl_fptype G = m_cap.G(m_C());
|
|
set_mat( G, -G, -I,
|
|
-G, G, I);
|
|
}
|
|
|
|
param_fp_t m_C;
|
|
NETLIB_RESETI()
|
|
{
|
|
m_cap.setparams(exec().gmin());
|
|
}
|
|
|
|
protected:
|
|
//NETLIB_UPDATEI();
|
|
NETLIB_UPDATE_PARAMI() { }
|
|
|
|
private:
|
|
//generic_capacitor<capacitor_e::VARIABLE_CAPACITY> m_cap;
|
|
generic_capacitor<capacitor_e::CONSTANT_CAPACITY> m_cap;
|
|
};
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// nld_L
|
|
// -----------------------------------------------------------------------------
|
|
|
|
NETLIB_OBJECT_DERIVED(L, twoterm)
|
|
{
|
|
public:
|
|
NETLIB_CONSTRUCTOR_DERIVED(L, twoterm)
|
|
, m_L(*this, "L", nlconst::magic(1e-6))
|
|
, m_gmin(nlconst::zero())
|
|
, m_G(nlconst::zero())
|
|
, m_I(nlconst::zero())
|
|
{
|
|
//register_term("1", m_P);
|
|
//register_term("2", m_N);
|
|
}
|
|
|
|
NETLIB_IS_TIMESTEP(true)
|
|
NETLIB_TIMESTEPI();
|
|
NETLIB_RESETI();
|
|
|
|
protected:
|
|
//NETLIB_UPDATEI();
|
|
NETLIB_UPDATE_PARAMI();
|
|
|
|
private:
|
|
param_fp_t m_L;
|
|
|
|
nl_fptype m_gmin;
|
|
nl_fptype m_G;
|
|
nl_fptype m_I;
|
|
};
|
|
|
|
/// \brief Class representing the diode model paramers.
|
|
///
|
|
/// This is the model representation of the diode model. Typically, SPICE uses
|
|
/// the following parameters. A "Y" in the first column indicates that the
|
|
/// parameter is actually used in netlist.
|
|
///
|
|
/// |NL? |name |parameter |units|default| example|area |
|
|
/// |:--:|:-----|:--------------------------------|:----|------:|-------:|:----:|
|
|
/// | Y |IS |saturation current |A |1.0e-14| 1.0e-14| * |
|
|
/// | |RS |ohmic resistance |Ohm | 0| 10| * |
|
|
/// | Y |N |emission coefficient |- | 1| 1| |
|
|
/// | |TT |transit-time |sec | 0| 0.1ns| |
|
|
/// | |CJO |zero-bias junction capacitance |F | 0| 2pF| * |
|
|
/// | |VJ |junction potential |V | 1| 0.6| |
|
|
/// | |M |grading coefficient |- | 0.5| 0.5| |
|
|
/// | |EG |band-gap energy |eV | 1.11| 1.11 Si| |
|
|
/// | |XTI |saturation-current temp.exp |- | 3|3.0 pn. 2.0 Schottky| |
|
|
/// | |KF |flicker noise coefficient |- | 0| | |
|
|
/// | |AF |flicker noise exponent |- | 1| | |
|
|
/// | |FC |coefficient for forward-bias depletion capacitance formula|-|0.5|| |
|
|
/// | |BV |reverse breakdown voltage |V |infinite| 40| |
|
|
/// | |IBV |current at breakdown voltage |V | 0.001| | |
|
|
/// | |TNOM |parameter measurement temperature|deg C| 27| 50| |
|
|
///
|
|
class diode_model_t : public param_model_t
|
|
{
|
|
public:
|
|
diode_model_t(device_t &device, const pstring &name, const pstring &val)
|
|
: param_model_t(device, name, val)
|
|
, m_IS(*this, "IS")
|
|
, m_N(*this, "N")
|
|
{}
|
|
|
|
value_t m_IS; //!< saturation current.
|
|
value_t m_N; //!< emission coefficient.
|
|
};
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// nld_D
|
|
// -----------------------------------------------------------------------------
|
|
|
|
NETLIB_OBJECT_DERIVED(D, twoterm)
|
|
{
|
|
public:
|
|
NETLIB_CONSTRUCTOR_DERIVED_EX(D, twoterm, const pstring &model = "D")
|
|
, m_model(*this, "MODEL", model)
|
|
, m_D(*this, "m_D")
|
|
{
|
|
register_subalias("A", m_P);
|
|
register_subalias("K", m_N);
|
|
}
|
|
|
|
NETLIB_IS_DYNAMIC(true)
|
|
NETLIB_UPDATE_TERMINALSI();
|
|
NETLIB_RESETI();
|
|
|
|
protected:
|
|
//NETLIB_UPDATEI();
|
|
NETLIB_UPDATE_PARAMI();
|
|
|
|
private:
|
|
diode_model_t m_model;
|
|
generic_diode<diode_e::BIPOLAR> m_D;
|
|
};
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// nld_VS - Voltage source
|
|
//
|
|
// netlist voltage source must have inner resistance
|
|
// -----------------------------------------------------------------------------
|
|
|
|
NETLIB_OBJECT_DERIVED(VS, twoterm)
|
|
{
|
|
public:
|
|
NETLIB_CONSTRUCTOR_DERIVED(VS, twoterm)
|
|
, m_t(*this, "m_t", nlconst::zero())
|
|
, m_R(*this, "R", nlconst::magic(0.1))
|
|
, m_V(*this, "V", nlconst::zero())
|
|
, m_func(*this,"FUNC", "")
|
|
, m_compiled(this->name() + ".FUNCC", this, this->state().run_state_manager())
|
|
, m_funcparam({nlconst::zero()})
|
|
{
|
|
register_subalias("P", m_P);
|
|
register_subalias("N", m_N);
|
|
if (m_func() != "")
|
|
m_compiled.compile(m_func(), std::vector<pstring>({{pstring("T")}}));
|
|
}
|
|
|
|
NETLIB_IS_TIMESTEP(m_func() != "")
|
|
|
|
NETLIB_TIMESTEPI()
|
|
{
|
|
m_t += step;
|
|
m_funcparam[0] = m_t;
|
|
this->set_G_V_I(plib::reciprocal(m_R()),
|
|
m_compiled.evaluate(m_funcparam),
|
|
nlconst::zero());
|
|
}
|
|
|
|
protected:
|
|
// NETLIB_UPDATEI() { NETLIB_NAME(twoterm)::update(time); }
|
|
|
|
NETLIB_RESETI()
|
|
{
|
|
NETLIB_NAME(twoterm)::reset();
|
|
this->set_G_V_I(plib::reciprocal(m_R()), m_V(), nlconst::zero());
|
|
}
|
|
|
|
private:
|
|
state_var<nl_fptype> m_t;
|
|
param_fp_t m_R;
|
|
param_fp_t m_V;
|
|
param_str_t m_func;
|
|
plib::pfunction<nl_fptype> m_compiled;
|
|
std::vector<nl_fptype> m_funcparam;
|
|
};
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// nld_CS - Current source
|
|
// -----------------------------------------------------------------------------
|
|
|
|
NETLIB_OBJECT_DERIVED(CS, twoterm)
|
|
{
|
|
public:
|
|
NETLIB_CONSTRUCTOR_DERIVED(CS, twoterm)
|
|
, m_t(*this, "m_t", nlconst::zero())
|
|
, m_I(*this, "I", nlconst::one())
|
|
, m_func(*this,"FUNC", "")
|
|
, m_compiled(this->name() + ".FUNCC", this, this->state().run_state_manager())
|
|
, m_funcparam({nlconst::zero()})
|
|
{
|
|
register_subalias("P", m_P);
|
|
register_subalias("N", m_N);
|
|
if (m_func() != "")
|
|
m_compiled.compile(m_func(), std::vector<pstring>({{pstring("T")}}));
|
|
}
|
|
|
|
NETLIB_IS_TIMESTEP(m_func() != "")
|
|
NETLIB_TIMESTEPI()
|
|
{
|
|
m_t += step;
|
|
m_funcparam[0] = m_t;
|
|
const nl_fptype I = m_compiled.evaluate(m_funcparam);
|
|
const auto zero(nlconst::zero());
|
|
set_mat(zero, zero, -I,
|
|
zero, zero, I);
|
|
}
|
|
|
|
protected:
|
|
|
|
//NETLIB_UPDATEI() { NETLIB_NAME(twoterm)::update(time); }
|
|
NETLIB_RESETI()
|
|
{
|
|
NETLIB_NAME(twoterm)::reset();
|
|
const auto zero(nlconst::zero());
|
|
set_mat(zero, zero, -m_I(),
|
|
zero, zero, m_I());
|
|
}
|
|
|
|
NETLIB_UPDATE_PARAMI()
|
|
{
|
|
solve_now();
|
|
const auto zero(nlconst::zero());
|
|
set_mat(zero, zero, -m_I(),
|
|
zero, zero, m_I());
|
|
}
|
|
|
|
|
|
private:
|
|
state_var<nl_fptype> m_t;
|
|
param_fp_t m_I;
|
|
param_str_t m_func;
|
|
plib::pfunction<nl_fptype> m_compiled;
|
|
std::vector<nl_fptype> m_funcparam;
|
|
};
|
|
|
|
} // namespace analog
|
|
} // namespace netlist
|
|
|
|
#endif // NLD_TWOTERM_H_
|