mirror of
https://github.com/holub/mame
synced 2025-10-04 16:34:53 +03:00
netlist: add MOSFET model. [Couriersud]
- added MOSFET model. Currently capacitances are not modelled. This is a 3-pin model (Bulk connected to Source) with provisions to extend it to 4-pin at a later stage. - Add a capacitor generic model which is charge conserving. Switch netlist to use this model instead of constant capacity model. - Start putting constants into a central place. Please expect minor timing differences due to a different numerical path. The cmos inverter example illustrates the analog implementation of a cmos inverter gate. These were used a lot back in the 70s/80s to generate sinus waves. The model should also be able to better emulate 4066 analog switches. The addition of a relatively simple capacitor model is planned at a later stage. Expect everything from the MOSFET model at the current stage. Wrong results as well as convergence issues and crashes.
This commit is contained in:
parent
a105f9f456
commit
39a7c420b1
42
nl_examples/cmos_inverter.cpp
Normal file
42
nl_examples/cmos_inverter.cpp
Normal file
@ -0,0 +1,42 @@
|
||||
// license:GPL-2.0+
|
||||
// copyright-holders:Couriersud
|
||||
/*
|
||||
* bjt.c
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "netlist/devices/net_lib.h"
|
||||
#include "netlist/analog/nld_twoterm.h"
|
||||
|
||||
NETLIST_START(cmos_inverter)
|
||||
/* Standard stuff */
|
||||
|
||||
SOLVER(Solver, 48000)
|
||||
PARAM(Solver.ACCURACY, 1e-7)
|
||||
PARAM(Solver.NR_LOOPS, 5000)
|
||||
PARAM(Solver.METHOD, "MAT_CR")
|
||||
ANALOG_INPUT(V5, 5)
|
||||
|
||||
VS(IN, 5)
|
||||
PARAM(IN.FUNC, "T 5 *")
|
||||
|
||||
MOSFET(P, "PMOS(VTO=-1.0 KP=2e-3 LAMBDA=2E-2)")
|
||||
MOSFET(M, "NMOS(VTO=1.0 KP=2e-3 LAMBDA=2E-2)")
|
||||
|
||||
NET_C(P.S, V5)
|
||||
NET_C(P.D, M.D)
|
||||
NET_C(GND, M.S, IN.N)
|
||||
|
||||
NET_C(IN.P, M.G, P.G)
|
||||
|
||||
// capacitance over D - S
|
||||
#if 0
|
||||
CAP(C, CAP_N(1))
|
||||
NET_C(M.D, C.1)
|
||||
NET_C(M.S, C.2)
|
||||
#endif
|
||||
LOG(log_G, M.G)
|
||||
LOG(log_D, M.D)
|
||||
|
||||
NETLIST_END()
|
52
nl_examples/nmos_fet.cpp
Normal file
52
nl_examples/nmos_fet.cpp
Normal file
@ -0,0 +1,52 @@
|
||||
// license:GPL-2.0+
|
||||
// copyright-holders:Couriersud
|
||||
/*
|
||||
* bjt.c
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "netlist/devices/net_lib.h"
|
||||
#include "netlist/analog/nld_twoterm.h"
|
||||
|
||||
NETLIST_START(bjt)
|
||||
/* Standard stuff */
|
||||
|
||||
CLOCK(clk, 100) // 100 Hz
|
||||
SOLVER(Solver, 48000)
|
||||
PARAM(Solver.ACCURACY, 1e-7)
|
||||
PARAM(Solver.NR_LOOPS, 5000)
|
||||
ANALOG_INPUT(V5, 5)
|
||||
ANALOG_INPUT(V3, 3.5)
|
||||
|
||||
/* NMOS - example */
|
||||
|
||||
NET_MODEL("MM NMOS(VTO=1.0 KP=2e-3 LAMBDA=2E-2)")
|
||||
MOSFET(M, "MM")
|
||||
|
||||
RES(RB, 1000)
|
||||
RES(RC, 10000)
|
||||
|
||||
NET_C(RC.1, V5)
|
||||
NET_C(RC.2, M.D)
|
||||
NET_C(RB.1, clk)
|
||||
//NET_C(RB.1, V3)
|
||||
NET_C(RB.2, M.G)
|
||||
NET_C(M.S, GND)
|
||||
|
||||
// put some load on M.D
|
||||
|
||||
RES(RCE, 150000)
|
||||
NET_C(RCE.1, M.D)
|
||||
NET_C(RCE.2, GND)
|
||||
|
||||
// capacitance over D - S
|
||||
|
||||
CAP(C, CAP_N(1))
|
||||
NET_C(M.D, C.1)
|
||||
NET_C(M.S, C.2)
|
||||
|
||||
LOG(log_G, M.G)
|
||||
LOG(log_D, M.D)
|
||||
|
||||
NETLIST_END()
|
@ -85,6 +85,8 @@ project "netlist"
|
||||
MAME_DIR .. "src/lib/netlist/tools/nl_convert.h",
|
||||
MAME_DIR .. "src/lib/netlist/analog/nld_bjt.cpp",
|
||||
MAME_DIR .. "src/lib/netlist/analog/nld_bjt.h",
|
||||
MAME_DIR .. "src/lib/netlist/analog/nld_mosfet.cpp",
|
||||
MAME_DIR .. "src/lib/netlist/analog/nld_mosfet.h",
|
||||
MAME_DIR .. "src/lib/netlist/analog/nlid_fourterm.cpp",
|
||||
MAME_DIR .. "src/lib/netlist/analog/nlid_fourterm.h",
|
||||
MAME_DIR .. "src/lib/netlist/analog/nld_fourterm.h",
|
||||
|
@ -9,12 +9,14 @@
|
||||
#include "netlist/nl_setup.h"
|
||||
#include "nlid_twoterm.h"
|
||||
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace netlist
|
||||
{
|
||||
namespace analog
|
||||
{
|
||||
using constants = plib::constants<nl_double>;
|
||||
|
||||
class diode
|
||||
{
|
||||
@ -279,8 +281,8 @@ namespace analog
|
||||
NETLIB_UPDATE_TERMINALSI();
|
||||
|
||||
private:
|
||||
generic_diode m_gD_BC;
|
||||
generic_diode m_gD_BE;
|
||||
generic_diode<diode_e::BIPOLAR> m_gD_BC;
|
||||
generic_diode<diode_e::BIPOLAR> m_gD_BE;
|
||||
|
||||
nld_twoterm m_D_CB; // gcc, gce - gcc, gec - gcc, gcc - gce | Ic
|
||||
nld_twoterm m_D_EB; // gee, gec - gee, gce - gee, gee - gec | Ie
|
||||
@ -449,8 +451,8 @@ namespace analog
|
||||
m_alpha_f = BF / (1.0 + BF);
|
||||
m_alpha_r = BR / (1.0 + BR);
|
||||
|
||||
m_gD_BE.set_param(IS / m_alpha_f, NF, exec().gmin());
|
||||
m_gD_BC.set_param(IS / m_alpha_r, NR, exec().gmin());
|
||||
m_gD_BE.set_param(IS / m_alpha_f, NF, exec().gmin(), constants::T0());
|
||||
m_gD_BC.set_param(IS / m_alpha_r, NR, exec().gmin(), constants::T0());
|
||||
}
|
||||
|
||||
} // namespace analog
|
||||
|
488
src/lib/netlist/analog/nld_mosfet.cpp
Normal file
488
src/lib/netlist/analog/nld_mosfet.cpp
Normal file
@ -0,0 +1,488 @@
|
||||
// license:GPL-2.0+
|
||||
// copyright-holders:Couriersud
|
||||
/*
|
||||
* nld_mosfet.cpp
|
||||
*
|
||||
* Formulas in here based on the following Sources:
|
||||
*
|
||||
* https://www.imperial.ac.uk/pls/portallive/docs/1/7292573.PDF
|
||||
* http://www3.imperial.ac.uk/pls/portallive/docs/1/56133736.PDF
|
||||
* https://people.rit.edu/lffeee/SPICE_MOSFET_Model_Intro.pdf
|
||||
* https://people.rit.edu/lffeee/SPICE.pdf
|
||||
* http://web.mit.edu/course/6/6.012/SPR98/www/lectures/S98_Lecture10.pdf
|
||||
* http://homepages.rpi.edu/~sawyes/Models_review.pdf
|
||||
* http://jaco.ec.t.kanazawa-u.ac.jp/edu/mix/pdf/3.pdf
|
||||
*
|
||||
* Farid N. Naim, Circuit Simulation (Wiley-IEEE Press, 2010).
|
||||
* Stefan Jahn, Michael Margraf, Vincent Habchi and Raimund Jacob, "Qucs Technical Papers" (2007)
|
||||
*
|
||||
*/
|
||||
|
||||
#include "netlist/solver/nld_solver.h"
|
||||
#include "netlist/nl_setup.h"
|
||||
#include "nlid_twoterm.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#define BODY_CONNECTED_TO_SOURCE (1)
|
||||
|
||||
namespace netlist
|
||||
{
|
||||
namespace analog
|
||||
{
|
||||
|
||||
using constants = plib::constants<nl_double>;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// nld_FET - Base classes
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/*! Class representing the nmos model paramers.
|
||||
*
|
||||
* This is the model representation of the nmos model. Typically, SPICE uses
|
||||
* the following parameters. A "Y" in the first column indicates that the
|
||||
* parameter is actually used in netlist.
|
||||
*
|
||||
* | NL? |Name | Description|Units |Default |Example |
|
||||
* |:---:|------|-----------------------------------------------------------------------|-------|---------:|----------------:|
|
||||
* | Y |Vto | Zero-bias threshold voltage | V | 0 | 1 |
|
||||
* | Y |Kp | Transconductance parameter | A/V² | 0.00002 | 0.00003 |
|
||||
* | Y |Gamma | Bulk threshold parameter | V^½ | 0 | 0.37 |
|
||||
* | Y |Phi | Surface inversion potential | V | 0.6 | 0.65 |
|
||||
* | Y |Lambda| Channel-length modulation (level 1 and 2 only) | 1/V | 0 | 0.02 |
|
||||
* | |Rd | Drain ohmic resistance |W|0|1|
|
||||
* | |Rs | Source ohmic resistance |W|0|1|
|
||||
* | |Cbd | Zero-bias B-D junction capacitance |F|0|20f|
|
||||
* | |Cbs | Zero-bias B-S junction capacitance |F|0|20f|
|
||||
* | Y |Is | Bulk junction saturation current |A|0.00000000000001|1E-015|
|
||||
* | Y |N | Bulk diode emission coefficient |-|1|*
|
||||
* | |Pb | Bulk junction potential |V|0.8|0.87|8|
|
||||
* | |Cgso | Gate-source overlap capacitance per meter channel width |F/m|0|0.00000000004|
|
||||
* | |Cgdo | Gate-drain overlap capacitance per meter channel width |F/m|0|0.00000000004|*
|
||||
* | |Cgbo | Gate-bulk overlap capacitance per meter channel width |F/m|0|0.0000000002|*
|
||||
* | |Rsh | Drain and source diffusion sheet resistance |W|0|10|*
|
||||
* | |Cj | Zero-bias bulk junction bottom capacitance per square meter of junction area|F/m²|0|0.0002|*
|
||||
* | |Mj | Bulk junction bottom grading coefficient |-|0.5|0.5|*
|
||||
* | |Cjsw | Zero-bias bulk junction sidewall capacitance per meter of junction perimeter|F/m|0|1p|*
|
||||
* | |Mjsw | Bulk junction sidewall grading coefficient |-|.50 level 1 .33 level 2,3||
|
||||
* | |Js | Bulk junction saturation current per square-meter of junction area|A/m|0|0.00000001|
|
||||
* | Y |Tox | Oxide thickness |m|0.0000001|0.0000001|
|
||||
* | Y |Nsub | Substrate doping |1/cm³|0|4000000000000000|
|
||||
* | |Nss | Surface state density |1/cm²|0|10000000000|
|
||||
* | |Nfs | Fast surface state |1/cm²|0|10000000000|*
|
||||
* | |TPG | Type of gate material: +1 opp. to substrate -1 same as substrate 0 Al gate|-|1|
|
||||
* | |Xj | Metallurgical junction depth |m|0|1µ|*
|
||||
* | Y |Ld | Lateral diffusion |m|0|0.8µ|
|
||||
* | Y |Uo | Surface mobility |cm²/V/s|600|700|
|
||||
* | |Ucrit | Critical field for mobility degradation (level 2 only) |V/cm|10000|10000|
|
||||
* | |Uexp | Critical field exponent in mobility degradation (level 2 only) |-|0|0.1|
|
||||
* | |Utra | Transverse field coefficient (level 2 only) |-|0|0.3|*
|
||||
* | |Vmax | Maximum carrier drift velocity (levels 2 & 3 only) |m/s|0|50000|
|
||||
* | |Neff | Total channel-charge exponent (level 2 only) |-|1|5|
|
||||
* | |Kf | Flicker noise coefficient |-|0|1E-026|
|
||||
* | |Af | Flicker noise exponent |-|1|1.2|
|
||||
* | |Fc | Coefficient for forward-bias depletion capacitance formula |-|0.5|
|
||||
* | |Delta | Width effect on threshold voltage(levels 2 and 3) |-|0|1|
|
||||
* | |Theta | Mobility modulation (level 3 only) |-|0|0.1|
|
||||
* | |Eta | Static feedback (level 3 only) |-|0|1|
|
||||
* | |Kappa | Saturation field (level 3 only) |0.2|0.5|
|
||||
* | |Tnom | Parameter measurement temperature |ºC|27|50||
|
||||
* | Y |L | Length scaling |-|1.0||
|
||||
* | Y |W | Width scaling |-|1.0||
|
||||
* */
|
||||
|
||||
class fet_model_t : public param_model_t
|
||||
{
|
||||
public:
|
||||
fet_model_t(device_t &device, const pstring &name, const pstring &val)
|
||||
: param_model_t(device, name, val)
|
||||
, m_VTO(*this, "VTO")
|
||||
, m_N(*this, "N")
|
||||
, m_ISS(*this, "IS") // Haven't seen a model using ISS / ISD
|
||||
, m_ISD(*this, "IS")
|
||||
, m_LD(*this, "LD")
|
||||
, m_L(*this, "L")
|
||||
, m_W(*this, "W")
|
||||
, m_TOX(*this, "TOX")
|
||||
, m_KP(*this, "KP")
|
||||
, m_UO(*this, "UO")
|
||||
, m_PHI(*this, "PHI")
|
||||
, m_NSUB(*this, "NSUB")
|
||||
, m_GAMMA(*this, "GAMMA")
|
||||
, m_LAMBDA(*this, "LAMBDA")
|
||||
, m_RD(*this, "RD")
|
||||
, m_RS(*this, "RS")
|
||||
{}
|
||||
|
||||
value_t m_VTO; //!< Threshold voltage [V]
|
||||
value_t m_N; //!< Bulk diode emission coefficient
|
||||
value_t m_ISS; //!< Body diode saturation current
|
||||
value_t m_ISD; //!< Body diode saturation current
|
||||
value_t m_LD; //!< Lateral diffusion [m]
|
||||
value_t m_L; //!< Length scaling
|
||||
value_t m_W; //!< Width scaling
|
||||
value_t m_TOX; //!< Oxide thickness
|
||||
value_t m_KP; //!< Transconductance parameter [A/V²]
|
||||
value_t m_UO; //!< Surface mobility [cm²/V/s]
|
||||
value_t m_PHI; //!< Surface inversion potential [V]
|
||||
value_t m_NSUB;//!< Substrate doping [1/cm³]
|
||||
value_t m_GAMMA; //!< Bulk threshold parameter [V^½]
|
||||
value_t m_LAMBDA; //!< Channel-length modulation [1/V]
|
||||
value_t m_RD; //!< Drain ohmic resistance
|
||||
value_t m_RS; //!< Source ohmic resistance
|
||||
};
|
||||
|
||||
// Have a common start for mosfets
|
||||
|
||||
NETLIB_OBJECT(FET)
|
||||
{
|
||||
public:
|
||||
enum q_type {
|
||||
FET_NMOS,
|
||||
FET_PMOS
|
||||
};
|
||||
|
||||
NETLIB_CONSTRUCTOR(FET)
|
||||
, m_model(*this, "MODEL", "NMOS")
|
||||
, m_qtype(FET_NMOS)
|
||||
{
|
||||
}
|
||||
|
||||
NETLIB_IS_DYNAMIC(true)
|
||||
|
||||
//NETLIB_RESETI();
|
||||
NETLIB_UPDATEI() { }
|
||||
|
||||
q_type qtype() const { return m_qtype; }
|
||||
bool is_qtype(q_type atype) const { return m_qtype == atype; }
|
||||
void set_qtype(q_type atype) { m_qtype = atype; }
|
||||
protected:
|
||||
|
||||
fet_model_t m_model;
|
||||
private:
|
||||
q_type m_qtype;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// nld_QBJT_EB
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
NETLIB_OBJECT_DERIVED(MOSFET, FET)
|
||||
{
|
||||
public:
|
||||
NETLIB_CONSTRUCTOR_DERIVED(MOSFET, FET)
|
||||
, m_DG(*this, "m_DG", true)
|
||||
, m_SG(*this, "m_SG", true)
|
||||
, m_SD(*this, "m_SD", true)
|
||||
, m_D_BD(*this, "m_D_BD")
|
||||
#if (!BODY_CONNECTED_TO_SOURCE)
|
||||
, m_D_BS(*this, "m_D_BS")
|
||||
#endif
|
||||
, m_phi(0.0)
|
||||
, m_gamma(0.0)
|
||||
, m_vto(0.0)
|
||||
, m_beta(0.0)
|
||||
, m_lambda(0.0)
|
||||
, m_Leff(0.0)
|
||||
, m_Cox(0.0)
|
||||
{
|
||||
register_subalias("S", m_SG.m_P); // Source
|
||||
register_subalias("G", m_SG.m_N); // Gate
|
||||
|
||||
register_subalias("D", m_DG.m_P); // Drain
|
||||
|
||||
connect(m_SG.m_P, m_SD.m_P);
|
||||
connect(m_SG.m_N, m_DG.m_N);
|
||||
connect(m_DG.m_P, m_SD.m_N);
|
||||
|
||||
#if 0
|
||||
if (m_model.m_CJE > 0.0)
|
||||
{
|
||||
create_and_register_subdevice("m_CJE", m_CJE);
|
||||
connect("B", "m_CJE.1");
|
||||
connect("E", "m_CJE.2");
|
||||
}
|
||||
if (m_model.m_CJC > 0.0)
|
||||
{
|
||||
create_and_register_subdevice("m_CJC", m_CJC);
|
||||
connect("B", "m_CJC.1");
|
||||
connect("C", "m_CJC.2");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
NETLIB_RESETI();
|
||||
NETLIB_UPDATEI();
|
||||
NETLIB_UPDATE_PARAMI();
|
||||
NETLIB_UPDATE_TERMINALSI();
|
||||
|
||||
private:
|
||||
|
||||
nld_twoterm m_DG;
|
||||
nld_twoterm m_SG;
|
||||
nld_twoterm m_SD;
|
||||
|
||||
generic_diode<diode_e::MOS> m_D_BD;
|
||||
#if (!BODY_CONNECTED_TO_SOURCE)
|
||||
generic_diode<diode_e::MOS> m_D_BS;
|
||||
#endif
|
||||
|
||||
nl_double m_phi;
|
||||
nl_double m_gamma;
|
||||
nl_double m_vto;
|
||||
nl_double m_beta;
|
||||
nl_double m_lambda;
|
||||
|
||||
/* used in capacitance calculation */
|
||||
nl_double m_Leff;
|
||||
nl_double m_Cox;
|
||||
|
||||
//NETLIB_SUBXX(analog, C) m_CJE;
|
||||
//NETLIB_SUBXX(analog, C) m_CJC;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// nld_Q - Ebers Moll
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
NETLIB_UPDATE(MOSFET)
|
||||
{
|
||||
if (!m_SG.m_P.net().isRailNet())
|
||||
m_SG.m_P.solve_now(); // Basis
|
||||
else if (!m_SG.m_N.net().isRailNet())
|
||||
m_SG.m_N.solve_now(); // Emitter
|
||||
else
|
||||
m_DG.m_N.solve_now(); // Collector
|
||||
}
|
||||
|
||||
NETLIB_RESET(MOSFET)
|
||||
{
|
||||
NETLIB_NAME(FET)::reset();
|
||||
#if 0
|
||||
if (m_CJE)
|
||||
{
|
||||
m_CJE->reset();
|
||||
m_CJE->m_C.setTo(m_model.m_CJE);
|
||||
}
|
||||
if (m_CJC)
|
||||
{
|
||||
m_CJC->reset();
|
||||
m_CJC->m_C.setTo(m_model.m_CJC);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
NETLIB_UPDATE_TERMINALS(MOSFET)
|
||||
{
|
||||
const nl_double polarity = (qtype() == FET_NMOS ? 1.0 : -1.0);
|
||||
|
||||
const nl_double Ugd = -m_DG.deltaV() * polarity; // Gate - Drain
|
||||
const nl_double Ugs = -m_SG.deltaV() * polarity; // Gate - Source
|
||||
const nl_double Ubs = 0.0; // Bulk - Source == 0 if connected
|
||||
const nl_double Ubd = m_SD.deltaV() * polarity; // Bulk - Drain = Source - Drain
|
||||
const nl_double Uds = Ugs - Ugd;
|
||||
|
||||
#if (!BODY_CONNECTED_TO_SOURCE)
|
||||
m_D_BS.update_diode(Ubs);
|
||||
#endif
|
||||
m_D_BD.update_diode(Ubd);
|
||||
|
||||
// Are we in forward mode ?
|
||||
const bool is_forward = Uds >= 0;
|
||||
|
||||
// calculate Vth
|
||||
const nl_double Vbulk = is_forward ? Ubs : Ubd;
|
||||
const nl_double phi_m_Vbulk = (m_phi > Vbulk) ? std::sqrt(m_phi - Vbulk) : 0.0;
|
||||
const nl_double Vth = m_vto * polarity + m_gamma * (phi_m_Vbulk - std::sqrt(m_phi));
|
||||
|
||||
const nl_double Vctrl = (is_forward ? Ugs : Ugd) - Vth;
|
||||
|
||||
nl_double Ids, gm, gds, gmb;
|
||||
|
||||
if (Vctrl <= 0.0)
|
||||
{
|
||||
// cutoff region
|
||||
Ids = 0.0;
|
||||
gm = 0.0;
|
||||
gds = 0.0;
|
||||
gmb = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
const nl_double Vds = std::abs(Uds);
|
||||
const nl_double b = m_beta * (1.0 + m_lambda * Vds);
|
||||
if (Vctrl <= Vds)
|
||||
{
|
||||
// saturation region
|
||||
Ids = b * Vctrl * Vctrl / 2.0;
|
||||
gm = b * Vctrl;
|
||||
gds = m_lambda * m_beta * Vctrl * Vctrl / 2.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// linear region
|
||||
Ids = b * Vds * (Vctrl - Vds / 2);
|
||||
gm = b * Vds;
|
||||
gds = b * (Vctrl - Vds) + m_lambda * m_beta * Vds * (Vctrl - Vds / 2.0);
|
||||
}
|
||||
|
||||
// backgate transconductance
|
||||
const nl_double bgtc = (phi_m_Vbulk != 0.0) ? (m_gamma / phi_m_Vbulk / 2.0) : 0.0;
|
||||
gmb = gm * bgtc;
|
||||
}
|
||||
|
||||
// FIXME: these are needed to compute capacitance
|
||||
// nl_double Udsat = pol * std::max (Utst, 0.0);
|
||||
// Uon = pol * Vth;
|
||||
|
||||
// compute bulk diode equivalent currents
|
||||
|
||||
const nl_double IeqBD = m_D_BD.Ieq();
|
||||
const nl_double gbd = m_D_BD.G();
|
||||
#if 0
|
||||
const nl_double IeqBS = m_D_BS.Ieq();
|
||||
const nl_double gbs = m_D_BS.G();
|
||||
#else
|
||||
const nl_double IeqBS = 0.0;
|
||||
const nl_double gbs = 0.0;
|
||||
#endif
|
||||
// exchange controlling nodes if necessary
|
||||
const nl_double gsource = is_forward ? (gm + gmb) : 0;
|
||||
const nl_double gdrain = is_forward ? 0.0 : (gm + gmb);
|
||||
|
||||
const nl_double IeqDS = (is_forward) ?
|
||||
Ids - gm * Ugs - gmb * Ubs - gds * Uds
|
||||
: -Ids - gm * Ugd - gmb * Ubd - gds * Uds;
|
||||
|
||||
// IG = 0
|
||||
const nl_double IG = 0.0;
|
||||
const nl_double ID = (+IeqBD - IeqDS) * polarity;
|
||||
const nl_double IS = (+IeqBS + IeqDS) * polarity;
|
||||
const nl_double IB = (-IeqBD - IeqBS) * polarity;
|
||||
|
||||
const nl_double gGG = 0.0; // ok
|
||||
const nl_double gGD = 0.0; // ok
|
||||
const nl_double gGS = 0.0; // ok
|
||||
const nl_double gGB = 0.0; // ok
|
||||
|
||||
const nl_double gDG = gm; // ok
|
||||
const nl_double gDD = gds + gbd - gdrain; // ok
|
||||
const nl_double gDS = -gds - gsource; // ok
|
||||
const nl_double gDB = gmb - gbd; // ok
|
||||
|
||||
const nl_double gSG = -gm; // ok
|
||||
const nl_double gSD = -gds + gdrain; // ok
|
||||
const nl_double gSS = gbs + gds + gsource; // ok
|
||||
const nl_double gSB = -gbs - gmb;
|
||||
|
||||
const nl_double gBG = 0.0; // ok
|
||||
const nl_double gBD = -gbd; // ok
|
||||
const nl_double gBS = -gbs;
|
||||
const nl_double gBB = gbs + gbd; // ok
|
||||
|
||||
// Source connected to body, Diode S-B shorted!
|
||||
const nl_double gSSBB = gSS + gBB + gBS + gSB;
|
||||
|
||||
// S G
|
||||
m_SG.set_mat( gSSBB, gSG + gBG, +(IS + IB), // S
|
||||
gGS + gGB, gGG, IG ); // G
|
||||
// D G
|
||||
m_DG.set_mat( gDD, gDG, +ID, // D
|
||||
gGD, 0.0, 0.0 ); // G
|
||||
// S D
|
||||
m_SD.set_mat( 0.0, gSD + gBD, 0.0, // S
|
||||
gDS + gDB, 0.0, 0.0); // D
|
||||
|
||||
}
|
||||
|
||||
|
||||
NETLIB_UPDATE_PARAM(MOSFET)
|
||||
{
|
||||
set_qtype((m_model.model_type() == "NMOS") ? FET_NMOS : FET_PMOS);
|
||||
|
||||
/*
|
||||
* From http://ltwiki.org/LTspiceHelp/LTspiceHelp/M_MOSFET.htm :
|
||||
*
|
||||
* VTO, KP, LAMBDA, PHI and GAMMA. These parameters are computed
|
||||
* if the process parameters(NSUB, TOX,...) are given, but
|
||||
* user-specified values always override.
|
||||
*
|
||||
* But couldn't find a formula for lambda anywhere
|
||||
*
|
||||
*/
|
||||
|
||||
m_lambda = m_model.m_LAMBDA; // FIXME: m_lambda only set once
|
||||
|
||||
// calculate effective channel length
|
||||
m_Leff = m_model.m_L - 2 * m_model.m_LD;
|
||||
nl_assert_always(m_Leff > 0.0, "Effective Lateral diffusion would be negative for model " + m_model.name());
|
||||
if (m_model.m_TOX > 0.0)
|
||||
m_Cox = (constants::eps_SiO2() * constants::eps_0() / m_model.m_TOX);
|
||||
else
|
||||
m_Cox = 0.0;
|
||||
|
||||
// calculate DC transconductance coefficient
|
||||
if (m_model.m_KP > 0)
|
||||
m_beta = m_model.m_KP * m_model.m_W / m_Leff;
|
||||
else if (m_Cox > 0 && m_model.m_UO > 0)
|
||||
m_beta = m_model.m_UO * 1e-4 * m_Cox * m_model.m_W / m_Leff;
|
||||
else
|
||||
m_beta = 2e-5 * m_model.m_W / m_Leff;
|
||||
|
||||
// Bulk diodes
|
||||
|
||||
m_D_BD.set_param(m_model.m_ISD, m_model.m_N, exec().gmin(), constants::T0());
|
||||
#if (!BODY_CONNECTED_TO_SOURCE)
|
||||
m_D_BS.set_param(m_model.m_ISS, m_model.m_N, exec().gmin(), constants::T0());
|
||||
#endif
|
||||
|
||||
//FIXME::UT can disappear
|
||||
const double Vt = constants::T0() * constants::k_b() / constants::Q_e();
|
||||
|
||||
// calculate surface potential if not given
|
||||
|
||||
if (m_model.m_PHI > 0.0)
|
||||
m_phi = m_model.m_PHI;
|
||||
else if (m_model.m_NSUB > 0.0)
|
||||
{
|
||||
nl_assert_always(m_model.m_NSUB * 1e6 >= constants::NiSi(), "Error calculating phi for model " + m_model.name());
|
||||
m_phi = 2 * Vt * std::log (m_model.m_NSUB * 1e6 / constants::NiSi());
|
||||
}
|
||||
else
|
||||
m_phi = 0.6;
|
||||
|
||||
// calculate bulk threshold if not given
|
||||
if (m_model.m_GAMMA > 0.0)
|
||||
m_gamma = m_model.m_GAMMA;
|
||||
else
|
||||
{
|
||||
if (m_Cox > 0 && m_model.m_NSUB > 0)
|
||||
m_gamma = std::sqrt (2.0 * constants::Q_e() * constants::eps_Si() * constants::eps_0() * m_model.m_NSUB * 1e6) / m_Cox;
|
||||
else
|
||||
m_gamma = 0.0;
|
||||
}
|
||||
|
||||
m_vto = m_model.m_VTO;
|
||||
nl_assert_always(m_vto != 0.0, "Threshold voltage not specified for " + m_model.name());
|
||||
|
||||
/* FIXME: VTO if missing may be calculated from TPG, NSS and temperature. Usually models
|
||||
* specify VTO so skip this here.
|
||||
*/
|
||||
|
||||
m_Cox = m_Cox * m_model.m_W * m_Leff;
|
||||
|
||||
}
|
||||
|
||||
} // namespace analog
|
||||
|
||||
namespace devices {
|
||||
NETLIB_DEVICE_IMPL_NS(analog, MOSFET, "MOSFET", "MODEL")
|
||||
} // namespace devices
|
||||
|
||||
} // namespace netlist
|
21
src/lib/netlist/analog/nld_mosfet.h
Normal file
21
src/lib/netlist/analog/nld_mosfet.h
Normal file
@ -0,0 +1,21 @@
|
||||
// license:GPL-2.0+
|
||||
// copyright-holders:Couriersud
|
||||
/*
|
||||
* nld_mosfet.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NLD_MOSFET_H_
|
||||
#define NLD_MOSFET_H_
|
||||
|
||||
#include "netlist/nl_setup.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Macros
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#define MOSFET(name, model) \
|
||||
NET_REGISTER_DEV(MOSFET, name) \
|
||||
NETDEV_PARAMI(name, MODEL, model)
|
||||
|
||||
#endif /* NLD_MOSFET_H_ */
|
@ -16,68 +16,6 @@ namespace netlist
|
||||
{
|
||||
namespace analog
|
||||
{
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// generic_diode
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
generic_diode::generic_diode(device_t &dev, const pstring &name)
|
||||
: m_Vd(dev, name + ".m_Vd", 0.7)
|
||||
, m_Id(dev, name + ".m_Id", 0.0)
|
||||
, m_G(dev, name + ".m_G", 1e-15)
|
||||
, m_Vt(0.0)
|
||||
, m_Vmin(0.0)
|
||||
, m_Is(0.0)
|
||||
, m_logIs(0.0)
|
||||
, m_n(0.0)
|
||||
, m_gmin(1e-15)
|
||||
, m_VtInv(0.0)
|
||||
, m_Vcrit(0.0)
|
||||
{
|
||||
set_param(1e-15, 1, 1e-15);
|
||||
}
|
||||
|
||||
void generic_diode::set_param(const nl_double Is, const nl_double n, nl_double gmin)
|
||||
{
|
||||
static constexpr double csqrt2 = 1.414213562373095048801688724209; //std::sqrt(2.0);
|
||||
m_Is = Is;
|
||||
m_logIs = std::log(Is);
|
||||
m_n = n;
|
||||
m_gmin = gmin;
|
||||
|
||||
m_Vt = 0.0258 * m_n;
|
||||
m_Vmin = -5.0 * m_Vt;
|
||||
|
||||
m_Vcrit = m_Vt * std::log(m_Vt / m_Is / csqrt2);
|
||||
m_VtInv = 1.0 / m_Vt;
|
||||
}
|
||||
|
||||
void generic_diode::update_diode(const nl_double nVd)
|
||||
{
|
||||
if (nVd < m_Vmin)
|
||||
{
|
||||
m_Vd = nVd;
|
||||
m_G = m_gmin;
|
||||
m_Id = - m_Is;
|
||||
}
|
||||
else if (nVd < m_Vcrit)
|
||||
{
|
||||
m_Vd = nVd;
|
||||
//m_Vd = m_Vd + 10.0 * m_Vt * std::tanh((nVd - m_Vd) / 10.0 / m_Vt);
|
||||
//const double IseVDVt = m_Is * std::exp(m_Vd * m_VtInv);
|
||||
const double IseVDVt = std::exp(m_logIs + m_Vd * m_VtInv);
|
||||
m_Id = IseVDVt - m_Is;
|
||||
m_G = IseVDVt * m_VtInv + m_gmin;
|
||||
}
|
||||
else
|
||||
{
|
||||
const double a = std::max((nVd - m_Vd) * m_VtInv, plib::constants<nl_double>::cast(-0.99));
|
||||
m_Vd = m_Vd + std::log1p(a) * m_Vt;
|
||||
//const double IseVDVt = m_Is * std::exp(m_Vd * m_VtInv);
|
||||
const double IseVDVt = std::exp(m_logIs + m_Vd * m_VtInv);
|
||||
m_Id = IseVDVt - m_Is;
|
||||
m_G = IseVDVt * m_VtInv + m_gmin;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// nld_twoterm
|
||||
@ -192,41 +130,15 @@ NETLIB_UPDATE_PARAM(POT2)
|
||||
m_R1.set_R(std::max(m_R() * v, exec().gmin()));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// nld_C
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
NETLIB_RESET(C)
|
||||
{
|
||||
// FIXME: Startup conditions
|
||||
set_G_V_I(exec().gmin(), 0.0, -5.0 / exec().gmin());
|
||||
//set(exec().gmin(), 0.0, 0.0);
|
||||
}
|
||||
|
||||
NETLIB_UPDATE_PARAM(C)
|
||||
{
|
||||
m_GParallel = exec().gmin();
|
||||
}
|
||||
|
||||
NETLIB_TIMESTEP(C)
|
||||
{
|
||||
/* Gpar should support convergence */
|
||||
const nl_double G = m_C() / step + m_GParallel;
|
||||
const nl_double I = -G * deltaV();
|
||||
set_mat( G, -G, -I,
|
||||
-G, G, I);
|
||||
//set(G, 0.0, I);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// nld_L
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
NETLIB_RESET(L)
|
||||
{
|
||||
m_GParallel = exec().gmin();
|
||||
m_gmin = exec().gmin();
|
||||
m_I = 0.0;
|
||||
m_G = m_GParallel;
|
||||
m_G = m_gmin;
|
||||
set_mat( m_G, -m_G, -m_I,
|
||||
-m_G, m_G, m_I);
|
||||
//set(1.0/NETLIST_GMIN, 0.0, -5.0 * NETLIST_GMIN);
|
||||
@ -240,7 +152,7 @@ NETLIB_TIMESTEP(L)
|
||||
{
|
||||
/* Gpar should support convergence */
|
||||
m_I += m_I + m_G * deltaV();
|
||||
m_G = step / m_L() + m_GParallel;
|
||||
m_G = step / m_L() + m_gmin;
|
||||
set_mat( m_G, -m_G, -m_I,
|
||||
-m_G, m_G, m_I);
|
||||
//set(m_G, 0.0, m_I);
|
||||
@ -255,7 +167,7 @@ NETLIB_RESET(D)
|
||||
nl_double Is = m_model.m_IS;
|
||||
nl_double n = m_model.m_N;
|
||||
|
||||
m_D.set_param(Is, n, exec().gmin());
|
||||
m_D.set_param(Is, n, exec().gmin(), constants::T0());
|
||||
set_G_V_I(m_D.G(), 0.0, m_D.Ieq());
|
||||
}
|
||||
|
||||
@ -264,7 +176,7 @@ NETLIB_UPDATE_PARAM(D)
|
||||
nl_double Is = m_model.m_IS;
|
||||
nl_double n = m_model.m_N;
|
||||
|
||||
m_D.set_param(Is, n, exec().gmin());
|
||||
m_D.set_param(Is, n, exec().gmin(), constants::T0());
|
||||
}
|
||||
|
||||
NETLIB_UPDATE_TERMINALS(D)
|
||||
|
@ -35,8 +35,11 @@
|
||||
|
||||
#include "netlist/nl_base.h"
|
||||
#include "netlist/nl_setup.h"
|
||||
#include "netlist/solver/nld_solver.h"
|
||||
#include "plib/pfunction.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Implementation
|
||||
// -----------------------------------------------------------------------------
|
||||
@ -217,6 +220,83 @@ private:
|
||||
param_logic_t m_Reverse;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// A generic capacitor model
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
enum class capacitor_e
|
||||
{
|
||||
CHARGE_CONSERVING,
|
||||
CONSTANT_CAPACITY
|
||||
};
|
||||
|
||||
template <capacitor_e TYPE>
|
||||
class generic_capacitor
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
template <>
|
||||
class generic_capacitor<capacitor_e::CHARGE_CONSERVING>
|
||||
{
|
||||
public:
|
||||
generic_capacitor(device_t &dev, const pstring &name)
|
||||
: m_h(dev, name + ".m_h", 0.0)
|
||||
, m_charge(dev, name + ".m_charge", 0.0)
|
||||
, m_gmin(0.0)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr capacitor_e type() const { return capacitor_e::CHARGE_CONSERVING; }
|
||||
|
||||
constexpr nl_double G(nl_double cap) const
|
||||
{
|
||||
return cap * m_h + m_gmin;
|
||||
}
|
||||
|
||||
constexpr nl_double Ieq(nl_double cap, nl_double v) const
|
||||
{
|
||||
return m_h * (cap * v - m_charge) - G(cap) * v;
|
||||
}
|
||||
|
||||
void timestep(nl_double cap, nl_double v, nl_double step)
|
||||
{
|
||||
m_h = 1.0 / step;
|
||||
m_charge = cap * v;
|
||||
}
|
||||
|
||||
void setparams(nl_double gmin) { m_gmin = gmin; }
|
||||
|
||||
private:
|
||||
state_var<double> m_h;
|
||||
state_var<double> m_charge;
|
||||
nl_double m_gmin;
|
||||
};
|
||||
|
||||
template <>
|
||||
class generic_capacitor<capacitor_e::CONSTANT_CAPACITY>
|
||||
{
|
||||
public:
|
||||
generic_capacitor(device_t &dev, const pstring &name)
|
||||
: m_h(dev, name + ".m_h", 0.0)
|
||||
, m_gmin(0.0)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr capacitor_e type() const { return capacitor_e::CONSTANT_CAPACITY; }
|
||||
constexpr nl_double G(nl_double cap) const { return cap * m_h + m_gmin; }
|
||||
constexpr nl_double Ieq(nl_double cap, nl_double v) const { return - G(cap) * v; }
|
||||
|
||||
void timestep(nl_double cap, nl_double v, nl_double step)
|
||||
{
|
||||
plib::unused_var(cap, v);
|
||||
m_h = 1.0 / step;
|
||||
}
|
||||
void setparams(nl_double gmin) { m_gmin = gmin; }
|
||||
private:
|
||||
state_var<nl_double> m_h;
|
||||
nl_double m_gmin;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// nld_C
|
||||
@ -227,25 +307,45 @@ NETLIB_OBJECT_DERIVED(C, twoterm)
|
||||
public:
|
||||
NETLIB_CONSTRUCTOR_DERIVED(C, twoterm)
|
||||
, m_C(*this, "C", 1e-6)
|
||||
, m_GParallel(0.0)
|
||||
, m_cap(*this, "m_cap")
|
||||
{
|
||||
//register_term("1", m_P);
|
||||
//register_term("2", m_N);
|
||||
}
|
||||
|
||||
NETLIB_IS_TIMESTEP(true)
|
||||
NETLIB_TIMESTEPI();
|
||||
NETLIB_TIMESTEPI()
|
||||
{
|
||||
m_cap.timestep(m_C(), deltaV(), step);
|
||||
if (m_cap.type() == capacitor_e::CONSTANT_CAPACITY)
|
||||
{
|
||||
const nl_double I = m_cap.Ieq(m_C(), deltaV());
|
||||
const nl_double G = m_cap.G(m_C());
|
||||
set_mat( G, -G, -I,
|
||||
-G, G, I);
|
||||
}
|
||||
}
|
||||
|
||||
NETLIB_IS_DYNAMIC(m_cap.type() == capacitor_e::CHARGE_CONSERVING)
|
||||
NETLIB_UPDATE_TERMINALSI()
|
||||
{
|
||||
const nl_double I = m_cap.Ieq(m_C(), deltaV());
|
||||
const nl_double G = m_cap.G(m_C());
|
||||
set_mat( G, -G, -I,
|
||||
-G, G, I);
|
||||
}
|
||||
|
||||
param_double_t m_C;
|
||||
NETLIB_RESETI();
|
||||
NETLIB_RESETI()
|
||||
{
|
||||
m_cap.setparams(exec().gmin());
|
||||
}
|
||||
|
||||
protected:
|
||||
//NETLIB_UPDATEI();
|
||||
NETLIB_UPDATE_PARAMI();
|
||||
NETLIB_UPDATE_PARAMI() { }
|
||||
|
||||
private:
|
||||
nl_double m_GParallel;
|
||||
|
||||
generic_capacitor<capacitor_e::CHARGE_CONSERVING> m_cap;
|
||||
//generic_capacitor<capacitor_e::CONSTANT_CAPACITY> m_cap;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@ -257,7 +357,7 @@ NETLIB_OBJECT_DERIVED(L, twoterm)
|
||||
public:
|
||||
NETLIB_CONSTRUCTOR_DERIVED(L, twoterm)
|
||||
, m_L(*this, "L", 1e-6)
|
||||
, m_GParallel(0.0)
|
||||
, m_gmin(0.0)
|
||||
, m_G(0.0)
|
||||
, m_I(0.0)
|
||||
{
|
||||
@ -276,7 +376,7 @@ protected:
|
||||
private:
|
||||
param_double_t m_L;
|
||||
|
||||
nl_double m_GParallel;
|
||||
nl_double m_gmin;
|
||||
nl_double m_G;
|
||||
nl_double m_I;
|
||||
};
|
||||
@ -285,14 +385,82 @@ private:
|
||||
// A generic diode model to be used in other devices (Diode, BJT ...)
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
enum class diode_e
|
||||
{
|
||||
BIPOLAR,
|
||||
MOS
|
||||
};
|
||||
|
||||
template <diode_e TYPE>
|
||||
class generic_diode
|
||||
{
|
||||
public:
|
||||
generic_diode(device_t &dev, const pstring &name);
|
||||
generic_diode(device_t &dev, const pstring &name)
|
||||
: m_Vd(dev, name + ".m_Vd", 0.7)
|
||||
, m_Id(dev, name + ".m_Id", 0.0)
|
||||
, m_G(dev, name + ".m_G", 1e-15)
|
||||
, m_Vt(0.0)
|
||||
, m_Vmin(0.0) // not used in MOS model
|
||||
, m_Is(0.0)
|
||||
, m_logIs(0.0)
|
||||
, m_n(0.0)
|
||||
, m_gmin(1e-15)
|
||||
, m_VtInv(0.0)
|
||||
, m_Vcrit(0.0)
|
||||
{
|
||||
set_param(1e-15, 1, 1e-15, 300.0);
|
||||
}
|
||||
|
||||
void update_diode(const double nVd);
|
||||
void update_diode(const double nVd)
|
||||
{
|
||||
if (TYPE == diode_e::BIPOLAR && nVd < m_Vmin)
|
||||
{
|
||||
m_Vd = nVd;
|
||||
m_G = m_gmin;
|
||||
m_Id = - m_Is;
|
||||
}
|
||||
else if (TYPE == diode_e::MOS && nVd < constants::zero())
|
||||
{
|
||||
m_Vd = nVd;
|
||||
m_G = m_Is * m_VtInv + m_gmin;
|
||||
m_Id = m_G * m_Vd;
|
||||
}
|
||||
// FIXME: For MOS, stop here, the critical code path will not converge
|
||||
else if (TYPE == diode_e::MOS || nVd < m_Vcrit)
|
||||
{
|
||||
m_Vd = nVd;
|
||||
const double IseVDVt = std::exp(std::min(700.0, m_logIs + m_Vd * m_VtInv));
|
||||
m_Id = IseVDVt - m_Is;
|
||||
m_G = IseVDVt * m_VtInv + m_gmin;
|
||||
}
|
||||
else
|
||||
{
|
||||
const double a = std::max((nVd - m_Vd) * m_VtInv, constants::cast(-0.99));
|
||||
m_Vd = m_Vd + std::log1p(a) * m_Vt;
|
||||
//const double IseVDVt = m_Is * std::exp(m_Vd * m_VtInv);
|
||||
const double IseVDVt = std::exp(m_logIs + m_Vd * m_VtInv);
|
||||
m_Id = IseVDVt - m_Is;
|
||||
m_G = IseVDVt * m_VtInv + m_gmin;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void set_param(const double Is, const double n, double gmin, double temp)
|
||||
{
|
||||
static constexpr double csqrt2 = 1.414213562373095048801688724209; //std::sqrt(2.0);
|
||||
m_Is = Is;
|
||||
m_logIs = std::log(Is);
|
||||
m_n = n;
|
||||
m_gmin = gmin;
|
||||
|
||||
m_Vt = m_n * temp * constants::k_b() / constants::Q_e();
|
||||
|
||||
m_Vmin = -5.0 * m_Vt;
|
||||
|
||||
m_Vcrit = m_Vt * std::log(m_Vt / m_Is / csqrt2);
|
||||
m_VtInv = 1.0 / m_Vt;
|
||||
}
|
||||
|
||||
void set_param(const double Is, const double n, double gmin);
|
||||
|
||||
double I() const { return m_Id; }
|
||||
double G() const { return m_G; }
|
||||
@ -391,7 +559,7 @@ protected:
|
||||
|
||||
private:
|
||||
diode_model_t m_model;
|
||||
generic_diode m_D;
|
||||
generic_diode<diode_e::BIPOLAR> m_D;
|
||||
};
|
||||
|
||||
|
||||
|
@ -28,7 +28,7 @@ space :=
|
||||
space +=
|
||||
TIDY_FLAGS = $(subst $(space),,$(TIDY_FLAGSX))
|
||||
|
||||
#TIDY_FLAGS = -checks=llvm-include-order,llvm-namespace-comment,modernize-use-override,modernize-use-using -fix
|
||||
TIDY_FLAGS = -checks=llvm-include-order,llvm-namespace-comment,modernize-use-override,modernize-use-using -fix
|
||||
#TIDY_FLAGS = -checks=llvm-include-order -fix
|
||||
#TIDY_FLAGS = -checks=llvm-namespace-comment -fix
|
||||
#TIDY_FLAGS = -checks=modernize-use-override -fix
|
||||
@ -94,6 +94,7 @@ NLOBJS := \
|
||||
$(NLOBJ)/nl_setup.o \
|
||||
$(NLOBJ)/nl_factory.o \
|
||||
$(NLOBJ)/analog/nld_bjt.o \
|
||||
$(NLOBJ)/analog/nld_mosfet.o \
|
||||
$(NLOBJ)/analog/nlid_fourterm.o \
|
||||
$(NLOBJ)/analog/nld_switches.o \
|
||||
$(NLOBJ)/analog/nlid_twoterm.o \
|
||||
|
@ -45,6 +45,7 @@ namespace devices
|
||||
LIB_ENTRY(function) // only for macro devices - NO FEEDBACK loops
|
||||
LIB_ENTRY(QBJT_EB)
|
||||
LIB_ENTRY(QBJT_switch)
|
||||
LIB_ENTRY(MOSFET)
|
||||
LIB_ENTRY(logic_input_ttl)
|
||||
LIB_ENTRY(logic_input)
|
||||
LIB_ENTRY(analog_input)
|
||||
|
@ -92,6 +92,7 @@
|
||||
|
||||
#include "netlist/analog/nld_bjt.h"
|
||||
#include "netlist/analog/nld_fourterm.h"
|
||||
#include "netlist/analog/nld_mosfet.h"
|
||||
#include "netlist/analog/nld_opamps.h"
|
||||
#include "netlist/analog/nld_switches.h"
|
||||
#include "netlist/analog/nld_twoterm.h"
|
||||
|
@ -31,6 +31,13 @@ NETLIST_END()
|
||||
* BJT Models
|
||||
* ---------------------------------------------------------------------------*/
|
||||
|
||||
static NETLIST_START(mosfet_models)
|
||||
//NET_MODEL("NMOS _(VTO=0.0 N=1.0 IS=1E-14 KP=2E-5 UO=600 PHI=0.6 LD=0.0 L=1.0 TOX=1E-7 W=1.0 NSUB=0.0 GAMMA=0.0 RD=0.0 RS=0.0 LAMBDA=0.0)")
|
||||
//NET_MODEL("PMOS _(VTO=0.0 N=1.0 IS=1E-14 KP=2E-5 UO=600 PHI=0.6 LD=0.0 L=1.0 TOX=1E-7 W=1.0 NSUB=0.0 GAMMA=0.0 RD=0.0 RS=0.0 LAMBDA=0.0)")
|
||||
NET_MODEL("NMOS _(VTO=0.0 N=1.0 IS=1E-14 KP=0.0 UO=600 PHI=0.0 LD=0.0 L=1.0 TOX=1E-7 W=1.0 NSUB=0.0 GAMMA=0.0 RD=0.0 RS=0.0 LAMBDA=0.0)")
|
||||
NET_MODEL("PMOS _(VTO=0.0 N=1.0 IS=1E-14 KP=0.0 UO=600 PHI=0.0 LD=0.0 L=1.0 TOX=1E-7 W=1.0 NSUB=0.0 GAMMA=0.0 RD=0.0 RS=0.0 LAMBDA=0.0)")
|
||||
NETLIST_END()
|
||||
|
||||
static NETLIST_START(bjt_models)
|
||||
NET_MODEL("NPN _(IS=1e-15 BF=100 NF=1 BR=1 NR=1 CJE=0 CJC=0)")
|
||||
NET_MODEL("PNP _(IS=1e-15 BF=100 NF=1 BR=1 NR=1 CJE=0 CJC=0)")
|
||||
@ -81,7 +88,9 @@ NETLIST_START(base)
|
||||
|
||||
LOCAL_SOURCE(diode_models)
|
||||
LOCAL_SOURCE(bjt_models)
|
||||
LOCAL_SOURCE(mosfet_models)
|
||||
LOCAL_SOURCE(family_models)
|
||||
|
||||
LOCAL_SOURCE(TTL74XX_lib)
|
||||
LOCAL_SOURCE(CD4XXX_lib)
|
||||
LOCAL_SOURCE(OPAMP_lib)
|
||||
@ -89,6 +98,7 @@ NETLIST_START(base)
|
||||
|
||||
INCLUDE(diode_models)
|
||||
INCLUDE(bjt_models)
|
||||
INCLUDE(mosfet_models)
|
||||
INCLUDE(family_models)
|
||||
INCLUDE(TTL74XX_lib)
|
||||
INCLUDE(CD4XXX_lib)
|
||||
|
@ -1024,6 +1024,7 @@ namespace netlist
|
||||
virtual void changed();
|
||||
const pstring &value() const NL_NOEXCEPT { return m_param; }
|
||||
private:
|
||||
PALIGNAS_CACHELINE()
|
||||
pstring m_param;
|
||||
};
|
||||
|
||||
@ -1042,8 +1043,8 @@ namespace netlist
|
||||
: m_value(param.model_value(name))
|
||||
{
|
||||
}
|
||||
const double &operator()() const noexcept { return m_value; }
|
||||
operator const double&() const noexcept { return m_value; }
|
||||
double operator()() const noexcept { return m_value; }
|
||||
operator double() const noexcept { return m_value; }
|
||||
private:
|
||||
const double m_value;
|
||||
};
|
||||
@ -1091,7 +1092,7 @@ namespace netlist
|
||||
|
||||
param_rom_t(device_t &device, const pstring &name);
|
||||
|
||||
const ST & operator[] (std::size_t n) NL_NOEXCEPT { return m_data[n]; }
|
||||
ST operator[] (std::size_t n) const NL_NOEXCEPT { return m_data[n]; }
|
||||
protected:
|
||||
void changed() override
|
||||
{
|
||||
|
@ -20,12 +20,20 @@
|
||||
#include "plib/pstate.h"
|
||||
#include "plib/pstring.h"
|
||||
#include "plib/ptime.h"
|
||||
#include "plib/putil.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace netlist
|
||||
{
|
||||
/*! @brief plib::constants struct specialized for nl_double
|
||||
*
|
||||
* This may be any of bool, uint8_t, uint16_t, uin32_t and uint64_t.
|
||||
* The choice has little to no impact on performance.
|
||||
*/
|
||||
using constants = plib::constants<nl_double>;
|
||||
|
||||
/*! @brief netlist_sig_t is the type used for logic signals.
|
||||
*
|
||||
* This may be any of bool, uint8_t, uint16_t, uin32_t and uint64_t.
|
||||
|
@ -15,9 +15,9 @@
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <type_traits>
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64) || defined(_MSC_VER)
|
||||
#include <malloc.h>
|
||||
|
@ -66,6 +66,35 @@ namespace plib
|
||||
static constexpr T one() noexcept { return static_cast<T>(1); }
|
||||
static constexpr T two() noexcept { return static_cast<T>(2); }
|
||||
|
||||
/*!
|
||||
* \brief Electric constant of vacuum
|
||||
*/
|
||||
static constexpr T eps_0() noexcept { return static_cast<T>(8.854187817e-12); }
|
||||
/*!
|
||||
* \brief Relative permittivity of Silicon dioxide
|
||||
*/
|
||||
static constexpr T eps_SiO2() noexcept { return static_cast<T>(3.9); }
|
||||
/*!
|
||||
* \brief Relative permittivity of Silicon
|
||||
*/
|
||||
static constexpr T eps_Si() noexcept { return static_cast<T>(11.7); }
|
||||
/*!
|
||||
* \brief Boltzmann constant
|
||||
*/
|
||||
static constexpr T k_b() noexcept { return static_cast<T>(1.38064852e-23); }
|
||||
/*!
|
||||
* \brief room temperature (gives VT = 0.02585 at T=300)
|
||||
*/
|
||||
static constexpr T T0() noexcept { return static_cast<T>(300); }
|
||||
/*!
|
||||
* \brief Elementary charge
|
||||
*/
|
||||
static constexpr T Q_e() noexcept { return static_cast<T>(1.6021765314e-19); }
|
||||
/*!
|
||||
* \brief Intrinsic carrier concentration in 1/m^3 of Silicon
|
||||
*/
|
||||
static constexpr T NiSi() noexcept { return static_cast<T>(1.45e16); }
|
||||
|
||||
template <typename V>
|
||||
static constexpr const T cast(V &&v) noexcept { return static_cast<T>(v); }
|
||||
};
|
||||
|
@ -44,6 +44,7 @@ namespace devices
|
||||
const float_type v0 = (this->RHS(0) - b * v1) / a;
|
||||
std::array<float_type, 2> new_V = {v0, v1};
|
||||
|
||||
this->m_stat_calculations++;
|
||||
const float_type err = (newton_raphson ? this->delta(new_V) : 0.0);
|
||||
this->store(new_V);
|
||||
return (err > this->m_params.m_accuracy) ? 2 : 1;
|
||||
|
Loading…
Reference in New Issue
Block a user