mame/src/lib/netlist/analog/nlid_twoterm.h
couriersud db0dbeaea5 netlist: improve readability. (nw)
Renamed cast member of the constants struct to magic to clearly identify
magic numbers.

Introduced nlconst struct inheriting from plib::constants<nl_fptype> to
make code better understandable.
2019-11-02 23:39:24 +01:00

501 lines
12 KiB
C++

// license:GPL-2.0+
// copyright-holders:Couriersud
/*
* nld_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.
*
*/
#ifndef NLID_TWOTERM_H_
#define NLID_TWOTERM_H_
#include "netlist/nl_base.h"
#include "netlist/nl_setup.h"
#include "netlist/solver/nld_solver.h"
#include "nld_generic_models.h"
#include "plib/pfunction.h"
#include <cmath>
// -----------------------------------------------------------------------------
// 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)
throw 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());
void set_G_V_I(const nl_fptype G, const nl_fptype V, const nl_fptype I)
{
/* 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
{
return m_P.net().Q_Analog() - m_N.net().Q_Analog();
}
void set_mat(const nl_fptype a11, const nl_fptype a12, const nl_fptype rhs1,
const nl_fptype a21, const nl_fptype a22, const nl_fptype rhs2)
{
/* 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(const nl_fptype R)
{
const nl_fptype G = nlconst::one() / 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;
};
/*! 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(std::vector<pstring>({{pstring("T")}}), m_func());
}
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(std::vector<pstring>({{pstring("T")}}), m_func());
}
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_ */