summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/netlist/analog/nld_opamps.cpp
blob: 5f55a26f5d077579ac2fbd3b6d2a49feb57dcf23 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
// license:GPL-2.0+
// copyright-holders:Couriersud
/*
 * nld_opamps.c
 *
 */

#include "nld_opamps.h"
#include "../nl_base.h"
#include "../nl_errstr.h"
#include "nlid_twoterm.h"
#include "nlid_fourterm.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)
			{
				register_sub("CP1", m_CP);
				register_sub("EBUF", m_EBUF);
				register_sub("DN", m_DN, "D(IS=1e-15 N=1)");
				register_sub("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.do_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->do_reset();
			m_DP->do_reset();
			m_DN->do_reset();
			m_CP->do_reset();
			m_RP.do_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)
	}
} // namespace netlist