diff options
Diffstat (limited to 'src/lib/netlist/analog/nld_opamps.cpp')
-rw-r--r-- | src/lib/netlist/analog/nld_opamps.cpp | 314 |
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 } } |