mame/src/lib/netlist/analog/nld_opamps.cpp
2019-03-18 00:01:41 +01:00

245 lines
7.0 KiB
C++

// license:GPL-2.0+
// copyright-holders:Couriersud
/*
* nld_opamps.c
*
*/
#include "nld_opamps.h"
#include "netlist/nl_base.h"
#include "netlist/nl_errstr.h"
#include "nlid_fourterm.h"
#include "nlid_twoterm.h"
#include <cmath>
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
{
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")
{}
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
};
NETLIB_OBJECT(opamp)
{
NETLIB_CONSTRUCTOR(opamp)
, m_RP(*this, "RP1")
, m_G1(*this, "G1")
, m_VCC(*this, "VCC")
, m_GND(*this, "GND")
, m_model(*this, "MODEL", "LM324")
, m_VH(*this, "VH")
, m_VL(*this, "VL")
, m_VREF(*this, "VREF")
{
m_type = static_cast<int>(m_model.m_TYPE);
if (m_type == 1)
{
register_subalias("PLUS", "G1.IP");
register_subalias("MINUS", "G1.IN");
register_subalias("OUT", "G1.OP");
connect("G1.ON", "VREF");
connect("RP1.2", "VREF");
connect("RP1.1", "G1.OP");
}
else if (m_type == 3)
{
create_and_register_subdevice("CP1", m_CP);
create_and_register_subdevice("EBUF", m_EBUF);
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)");
//m_DP->m_model.setTo("D(IS=1e-15 N=1)");
//m_DN->m_model.setTo("D(IS=1e-15 N=1)");
register_subalias("PLUS", "G1.IP");
register_subalias("MINUS", "G1.IN");
register_subalias("OUT", "EBUF.OP");
connect("EBUF.ON", "VREF");
connect("G1.ON", "VREF");
connect("RP1.2", "VREF");
connect("CP1.2", "VREF");
connect("EBUF.IN", "VREF");
connect("RP1.1", "G1.OP");
connect("CP1.1", "RP1.1");
connect("DP.K", "VH");
connect("VL", "DN.A");
connect("DP.A", "DN.K");
connect("DN.K", "RP1.1");
connect("EBUF.IP", "RP1.1");
}
else
log().fatal(MF_1_UNKNOWN_OPAMP_TYPE, m_type);
}
NETLIB_UPDATEI();
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;
analog_input_t m_VCC;
analog_input_t m_GND;
opamp_model_t m_model;
analog_output_t m_VH;
analog_output_t m_VL;
analog_output_t m_VREF;
/* 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_RESET(opamp)
{
m_G1.reset();
m_G1.m_RI.setTo(m_model.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);
}
else if (m_type == 3)
{
m_EBUF->reset();
m_DP->reset();
m_DN->reset();
m_CP->reset();
m_RP.reset();
m_EBUF->m_G.setTo(1.0);
m_EBUF->m_RO.setTo(m_model.m_RO);
double CP = m_model.m_DAB / m_model.m_SLEW;
double RP = 0.5 / 3.1459 / CP / m_model.m_FPF;
double G = m_model.m_UGF / m_model.m_FPF / RP;
//printf("Min Freq %s: %f\n", name().c_str(), 1.0 / (CP*RP / (G*RP)));
m_CP->m_C.setTo(CP);
m_RP.set_R(RP);
m_G1.m_G.setTo(G);
}
}
} //namespace analog
namespace devices {
NETLIB_DEVICE_IMPL_NS(analog, opamp, "OPAMP", "MODEL")
} // namespace devices
} // namespace netlist