summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/netlist/analog/nld_opamps.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/netlist/analog/nld_opamps.cpp')
-rw-r--r--src/lib/netlist/analog/nld_opamps.cpp314
1 files changed, 172 insertions, 142 deletions
diff --git a/src/lib/netlist/analog/nld_opamps.cpp b/src/lib/netlist/analog/nld_opamps.cpp
index 24f239e9455..b7e4ebfa762 100644
--- a/src/lib/netlist/analog/nld_opamps.cpp
+++ b/src/lib/netlist/analog/nld_opamps.cpp
@@ -1,128 +1,133 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Couriersud
-/*
- * nld_opamps.c
- *
- */
-
-#include "nld_opamps.h"
-#include "netlist/nl_base.h"
-#include "netlist/nl_errstr.h"
+
+#include "nl_base.h"
+#include "nl_errstr.h"
#include "nlid_fourterm.h"
#include "nlid_twoterm.h"
-#include <cmath>
+//
+// Set to 1 to model output impedance as a series resistor.
+// The default is that the VCVS already has an internal impedance.
+// This needs more investigation.
+//
+#define TEST_ALT_OUTPUT (0)
namespace netlist
{
namespace analog
{
- /*
- * Type = 0: Impedance changer
- * 1; Idealized opamp
- * 2; opamp with first pole
- * 3: opamp with first pole + output limit
- * 4: opamp with input stage, first pole + output limit
- *
- * Type 1 parameters:
- * FPF = frequency of first pole in Hz (ony used for open-loop gain)
- * UGF = unity gain frequency in Hz (only used for open-loop gain)
- * RI = input resistance in Ohms
- * RO = output resistance in Ohms
- *
- * Type 3 parameters:
- * VLH = high supply rail minus high output swing in V
- * VLL = low output swing minus low supply rail in V
- * FPF = frequency of first pole in Hz
- * UGF = unity gain frequency (transition frequency) in Hz
- * SLEW = unity gain slew rate in V/s
- * RI = input resistance in Ohms
- * RO = output resistance in Ohms
- * DAB = Differential Amp Bias ~ op amp's total quiescent current.
- *
- * .model abc OPAMP(VLH=2.0 VLL=0.2 FPF=5 UGF=10k SLEW=0.6u RI=1000k RO=50 DAB=0.002)
- *
- * http://www.ecircuitcenter.com/Circuits/opmodel1/opmodel1.htm
- *
- * */
-
- /*! Class representing the opamp model parameters.
- * The opamp model was designed based on designs from
- * http://www.ecircuitcenter.com/Circuits/opmodel1/opmodel1.htm.
- * Currently 2 different types are supported: Type 1 and Type 3. Type 1
- * is less complex and should run faster than Type 3.
- *
- * This is an extension to the traditional SPICE approach which
- * assumes that you will be using an manufacturer model. These models may
- * have copyrights incompatible with the netlist license. Thus they may not
- * be suitable for certain implementations of netlist.
- *
- * For the typical use cases in low frequency (< 100 KHz) applications at
- * which netlist is targeted, this model is certainly suitable. All parameters
- * can be determined from a typical opamp datasheet.
- *
- * |Type|name |parameter |units|default| example|
- * |:--:|:-----|:----------------------------------------------|:----|------:|-------:|
- * | 3 |TYPE |Model Type, 1 and 3 are supported | | | |
- * |1,3 |FPF |frequency of first pole |Hz | |100 |
- * | 3 |SLEW |unity gain slew rate |V/s | | 1|
- * |1,3 |RI |input resistance |Ohm | |1M |
- * |1,3 |RO |output resistance |Ohm | |50 |
- * |1,3 |UGF |unity gain frequency (transition frequency) |Hz | |1000 |
- * | 3 |VLL |low output swing minus low supply rail |V | |1.5 |
- * | 3 |VLH |high supply rail minus high output swing |V | |1.5 |
- * | 3 |DAB |Differential Amp Bias - total quiescent current|A | |0.001 |
- */
-
- class opamp_model_t : public param_model_t
+
+ /// \brief Class representing the opamp model parameters.
+ ///
+ /// The opamp model was designed based on designs from
+ /// http://www.ecircuitcenter.com/Circuits/opmodel1/opmodel1.htm.
+ /// Currently 2 different types are supported: Type 1 and Type 3. Type 1
+ /// is less complex and should run faster than Type 3.
+ ///
+ /// This is an extension to the traditional SPICE approach which
+ /// assumes that you will be using an manufacturer model. These models may
+ /// have copyrights incompatible with the netlist license. Thus they may not
+ /// be suitable for certain implementations of netlist.
+ ///
+ /// For the typical use cases in low frequency (< 100 KHz) applications at
+ /// which netlist is targeted, this model is certainly suitable. All parameters
+ /// can be determined from a typical opamp datasheet.
+ ///
+ /// |Type|name |parameter |units|default| example|
+ /// |:--:|:-----|:----------------------------------------------|:----|------:|-------:|
+ /// | 3 |TYPE |Model Type, 1 and 3 are supported | | | |
+ /// |1,3 |FPF |frequency of first pole |Hz | |100 |
+ /// | 3 |SLEW |unity gain slew rate |V/s | | 1|
+ /// |1,3 |RI |input resistance |Ohm | |1M |
+ /// |1,3 |RO |output resistance |Ohm | |50 |
+ /// |1,3 |UGF |unity gain frequency (transition frequency) |Hz | |1000 |
+ /// | 3 |VLL |low output swing minus low supply rail |V | |1.5 |
+ /// | 3 |VLH |high supply rail minus high output swing |V | |1.5 |
+ /// | 3 |DAB |Differential Amp Bias - total quiescent current|A | |0.001 |
+ ///
+ ///
+ /// Type = 0: Impedance changer
+ /// 1; Idealized opamp
+ /// 2; opamp with first pole
+ /// 3: opamp with first pole + output limit
+ /// 4: opamp with input stage, first pole + output limit
+ ///
+ /// Type 1 parameters:
+ /// FPF = frequency of first pole in Hz (ony used for open-loop gain)
+ /// UGF = unity gain frequency in Hz (only used for open-loop gain)
+ /// RI = input resistance in Ohms
+ /// RO = output resistance in Ohms
+ ///
+ /// Type 3 parameters:
+ /// VLH = high supply rail minus high output swing in V
+ /// VLL = low output swing minus low supply rail in V
+ /// FPF = frequency of first pole in Hz
+ /// UGF = unity gain frequency (transition frequency) in Hz
+ /// SLEW = unity gain slew rate in V/s
+ /// RI = input resistance in Ohms
+ /// RO = output resistance in Ohms
+ /// DAB = Differential Amp Bias ~ op amp's total quiescent current.
+ ///
+ /// .model abc OPAMP(VLH=2.0 VLL=0.2 FPF=5 UGF=10k SLEW=0.6u RI=1000k RO=50 DAB=0.002)
+ ///
+ /// http://www.ecircuitcenter.com/Circuits/opmodel1/opmodel1.htm
+ ///
+ ///
+ class opamp_model_t
{
public:
- opamp_model_t(device_t &device, const pstring &name, const pstring &val)
- : param_model_t(device, name, val)
- , m_TYPE(*this, "TYPE")
- , m_FPF(*this, "FPF")
- , m_SLEW(*this, "SLEW")
- , m_RI(*this, "RI")
- , m_RO(*this, "RO")
- , m_UGF(*this, "UGF")
- , m_VLL(*this, "VLL")
- , m_VLH(*this, "VLH")
- , m_DAB(*this, "DAB")
+ opamp_model_t(param_model_t &model)
+ : m_TYPE(model, "TYPE")
+ , m_FPF(model, "FPF")
+ , m_SLEW(model, "SLEW")
+ , m_RI(model, "RI")
+ , m_RO(model, "RO")
+ , m_UGF(model, "UGF")
+ , m_VLL(model, "VLL")
+ , m_VLH(model, "VLH")
+ , m_DAB(model, "DAB")
{}
- value_t m_TYPE; //!< Model Type, 1 and 3 are supported
- value_t m_FPF; //!< frequency of first pole
- value_t m_SLEW; //!< unity gain slew rate
- value_t m_RI; //!< input resistance
- value_t m_RO; //!< output resistance
- value_t m_UGF; //!< unity gain frequency (transition frequency)
- value_t m_VLL; //!< low output swing minus low supply rail
- value_t m_VLH; //!< high supply rail minus high output swing
- value_t m_DAB; //!< Differential Amp Bias - total quiescent current
+ param_model_t::value_t m_TYPE; //!< Model Type, 1 and 3 are supported
+ param_model_t::value_t m_FPF; //!< frequency of first pole
+ param_model_t::value_t m_SLEW; //!< unity gain slew rate
+ param_model_t::value_t m_RI; //!< input resistance
+ param_model_t::value_t m_RO; //!< output resistance
+ param_model_t::value_t m_UGF; //!< unity gain frequency (transition frequency)
+ param_model_t::value_t m_VLL; //!< low output swing minus low supply rail
+ param_model_t::value_t m_VLH; //!< high supply rail minus high output swing
+ param_model_t::value_t m_DAB; //!< Differential Amp Bias - total quiescent current
};
- NETLIB_OBJECT(opamp)
+ class nld_opamp : public base_device_t
{
- NETLIB_CONSTRUCTOR(opamp)
+ public:
+ nld_opamp(constructor_param_t data)
+ : base_device_t(data)
, m_RP(*this, "RP1")
, m_G1(*this, "G1")
- , m_VCC(*this, "VCC")
- , m_GND(*this, "GND")
+ , m_VCC(*this, "VCC", NETLIB_DELEGATE(supply))
+ , m_GND(*this, "GND", NETLIB_DELEGATE(supply))
, m_model(*this, "MODEL", "LM324")
+ , m_modacc(m_model)
, m_VH(*this, "VH")
, m_VL(*this, "VL")
, m_VREF(*this, "VREF")
+ , m_type(plib::narrow_cast<int>(m_modacc.m_TYPE))
{
- m_type = static_cast<int>(m_model.m_TYPE);
if (m_type < 1 || m_type > 3)
+ {
log().fatal(MF_OPAMP_UNKNOWN_TYPE(m_type));
+ throw nl_exception(MF_OPAMP_UNKNOWN_TYPE(m_type));
+ }
if (m_type == 1)
{
- register_subalias("PLUS", "G1.IP");
- register_subalias("MINUS", "G1.IN");
- register_subalias("OUT", "G1.OP");
+ register_sub_alias("PLUS", "G1.IP");
+ register_sub_alias("MINUS", "G1.IN");
+ register_sub_alias("OUT", "G1.OP");
connect("G1.ON", "VREF");
connect("RP1.2", "VREF");
@@ -131,11 +136,13 @@ namespace netlist
}
if (m_type == 2 || m_type == 3)
{
- create_and_register_subdevice("CP1", m_CP);
- create_and_register_subdevice("EBUF", m_EBUF);
-
- register_subalias("PLUS", "G1.IP");
- register_subalias("MINUS", "G1.IN");
+ create_and_register_sub_device(*this, "CP1", m_CP);
+ create_and_register_sub_device(*this, "EBUF", m_EBUF);
+#if TEST_ALT_OUTPUT
+ create_and_register_sub_device("RO", m_RO);
+#endif
+ register_sub_alias("PLUS", "G1.IP");
+ register_sub_alias("MINUS", "G1.IN");
connect("G1.ON", "VREF");
connect("RP1.2", "VREF");
@@ -150,97 +157,120 @@ namespace netlist
}
if (m_type == 2)
{
- register_subalias("OUT", "EBUF.OP");
+#if TEST_ALT_OUTPUT
+ connect("EBUF.OP", "RO.1");
+ register_sub_alias("OUT", "RO.2");
+#else
+ register_sub_alias("OUT", "EBUF.OP");
+#endif
}
if (m_type == 3)
{
- create_and_register_subdevice("DN", m_DN, "D(IS=1e-15 N=1)");
- create_and_register_subdevice("DP", m_DP, "D(IS=1e-15 N=1)");
+ create_and_register_sub_device(*this, "DN", m_DN, "D(IS=1e-15 N=1)");
+ create_and_register_sub_device(*this, "DP", m_DP, "D(IS=1e-15 N=1)");
connect("DP.K", "VH");
connect("VL", "DN.A");
connect("DP.A", "DN.K");
connect("DN.K", "RP1.1");
-
- register_subalias("OUT", "EBUF.OP");
+#if TEST_ALT_OUTPUT
+ connect("EBUF.OP", "RO.1");
+ register_sub_alias("OUT", "RO.2");
+#else
+ register_sub_alias("OUT", "EBUF.OP");
+#endif
}
}
- NETLIB_UPDATEI();
+ NETLIB_HANDLERI(supply)
+ {
+ const nl_fptype cVt = nlconst::np_VT(nlconst::one()); // * m_n;
+ const nl_fptype cId = m_modacc.m_DAB; // 3 mA
+ const nl_fptype cVd = cVt * plib::log(cId / nlconst::np_Is() + nlconst::one());
+
+ m_VH.push(m_VCC() - m_modacc.m_VLH - cVd);
+ m_VL.push(m_GND() + m_modacc.m_VLL + cVd);
+ m_VREF.push((m_VCC() + m_GND()) / nlconst::two());
+ }
+
NETLIB_RESETI()
{
}
+
NETLIB_UPDATE_PARAMI();
private:
- analog::NETLIB_SUB(R_base) m_RP;
- analog::NETLIB_SUB(VCCS) m_G1;
- NETLIB_SUBXX(analog, C) m_CP;
- NETLIB_SUBXX(analog, VCVS) m_EBUF;
- NETLIB_SUBXX(analog, D) m_DP;
- NETLIB_SUBXX(analog, D) m_DN;
+ NETLIB_SUB_NS(analog, R_base) m_RP;
+ NETLIB_SUB_NS(analog, VCCS) m_G1;
+ NETLIB_SUB_UPTR(analog, C) m_CP;
+#if TEST_ALT_OUTPUT
+ NETLIB_SUB_UPTR(analog, R_base) m_RO;
+#endif
+ NETLIB_SUB_UPTR(analog, VCVS) m_EBUF;
+ NETLIB_SUB_UPTR(analog, D) m_DP;
+ NETLIB_SUB_UPTR(analog, D) m_DN;
analog_input_t m_VCC;
analog_input_t m_GND;
- opamp_model_t m_model;
+ param_model_t m_model;
+ opamp_model_t m_modacc;
analog_output_t m_VH;
analog_output_t m_VL;
analog_output_t m_VREF;
- /* state */
+ // state
int m_type;
};
- NETLIB_UPDATE(opamp)
- {
- const double cVt = 0.0258 * 1.0; // * m_n;
- const double cId = m_model.m_DAB; // 3 mA
- const double cVd = cVt * std::log(cId / 1e-15 + 1.0);
-
- m_VH.push(m_VCC() - m_model.m_VLH - cVd);
- m_VL.push(m_GND() + m_model.m_VLL + cVd);
- m_VREF.push((m_VCC() + m_GND()) / 2.0);
- }
-
NETLIB_UPDATE_PARAM(opamp)
{
- m_G1.m_RI.setTo(m_model.m_RI);
+ m_G1().m_RI.set(m_modacc.m_RI);
if (m_type == 1)
{
- double RO = m_model.m_RO;
- double G = m_model.m_UGF / m_model.m_FPF / RO;
- m_RP.set_R(RO);
- m_G1.m_G.setTo(G);
+ nl_fptype RO = m_modacc.m_RO;
+ nl_fptype G = m_modacc.m_UGF / m_modacc.m_FPF / RO;
+ m_RP().set_R(RO);
+ m_G1().m_G.set(G);
}
if (m_type == 3 || m_type == 2)
{
- double CP = m_model.m_DAB / m_model.m_SLEW;
- double RP = 0.5 / constants::pi() / CP / m_model.m_FPF;
- double G = m_model.m_UGF / m_model.m_FPF / RP;
+ nl_fptype CP = m_modacc.m_DAB / m_modacc.m_SLEW;
+ nl_fptype RP = nlconst::half() / nlconst::pi() / CP / m_modacc.m_FPF;
+ nl_fptype G = m_modacc.m_UGF / m_modacc.m_FPF / RP;
//printf("OPAMP %s: %g %g %g\n", name().c_str(), CP, RP, G);
- if (m_model.m_SLEW / (4.0 * constants::pi() * 0.0258) < m_model.m_UGF)
+ if (m_modacc.m_SLEW / (nlconst::four() * nlconst::pi() * nlconst::np_VT()) < m_modacc.m_UGF)
log().warning(MW_OPAMP_FAIL_CONVERGENCE(this->name()));
- m_CP->m_C.setTo(CP);
- m_RP.set_R(RP);
- m_G1.m_G.setTo(G);
+ m_CP->set_cap_embedded(CP);
+ m_RP().set_R(RP);
+ m_G1().m_G.set(G);
}
if (m_type == 2)
{
- m_EBUF->m_G.setTo(1.0);
- m_EBUF->m_RO.setTo(m_model.m_RO);
+ m_EBUF->m_G.set(nlconst::one());
+#if TEST_ALT_OUTPUT
+ m_EBUF->m_RO.set(0.001);
+ m_RO->set_R(m_modacc.m_RO);
+#else
+ m_EBUF->m_RO.set(m_modacc.m_RO);
+#endif
}
if (m_type == 3)
{
- m_EBUF->m_G.setTo(1.0);
- m_EBUF->m_RO.setTo(m_model.m_RO);
+ m_EBUF->m_G.set(nlconst::one());
+#if TEST_ALT_OUTPUT
+ m_EBUF->m_RO.set(0.001);
+ m_RO->set_R(m_modacc.m_RO);
+#else
+ m_EBUF->m_RO.set(m_modacc.m_RO);
+#endif
}
}