Branch data Line data Source code
1 : : /*
2 : : * mosfet.cpp - mosfet class implementation
3 : : *
4 : : * Copyright (C) 2004, 2005, 2006, 2008 Stefan Jahn <stefan@lkcc.org>
5 : : *
6 : : * This is free software; you can redistribute it and/or modify
7 : : * it under the terms of the GNU General Public License as published by
8 : : * the Free Software Foundation; either version 2, or (at your option)
9 : : * any later version.
10 : : *
11 : : * This software is distributed in the hope that it will be useful,
12 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : : * GNU General Public License for more details.
15 : : *
16 : : * You should have received a copy of the GNU General Public License
17 : : * along with this package; see the file COPYING. If not, write to
18 : : * the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
19 : : * Boston, MA 02110-1301, USA.
20 : : *
21 : : * $Id$
22 : : *
23 : : */
24 : :
25 : : #if HAVE_CONFIG_H
26 : : # include <config.h>
27 : : #endif
28 : :
29 : : #include "component.h"
30 : : #include "device.h"
31 : : #include "mosfet.h"
32 : :
33 : : #define NODE_G 0 /* gate node */
34 : : #define NODE_D 1 /* drain node */
35 : : #define NODE_S 2 /* source node */
36 : : #define NODE_B 3 /* bulk node */
37 : :
38 : : using namespace qucs;
39 : : using namespace qucs::device;
40 : :
41 : 19 : mosfet::mosfet () : circuit (4) {
42 : 19 : transientMode = 0;
43 : 19 : rg = rs = rd = NULL;
44 : 19 : type = CIR_MOSFET;
45 : 19 : }
46 : :
47 : 0 : void mosfet::calcSP (nr_double_t frequency) {
48 [ # # ][ # # ]: 0 : setMatrixS (ytos (calcMatrixY (frequency)));
[ # # ][ # # ]
[ # # ]
49 : 0 : }
50 : :
51 : 1600 : matrix mosfet::calcMatrixY (nr_double_t frequency) {
52 : :
53 : : // fetch computed operating points
54 [ + - ]: 1600 : nr_double_t Cgd = getOperatingPoint ("Cgd");
55 [ + - ]: 1600 : nr_double_t Cgs = getOperatingPoint ("Cgs");
56 [ + - ]: 1600 : nr_double_t Cbd = getOperatingPoint ("Cbd");
57 [ + - ]: 1600 : nr_double_t Cbs = getOperatingPoint ("Cbs");
58 [ + - ]: 1600 : nr_double_t Cgb = getOperatingPoint ("Cgb");
59 [ + - ]: 1600 : nr_double_t gbs = getOperatingPoint ("gbs");
60 [ + - ]: 1600 : nr_double_t gbd = getOperatingPoint ("gbd");
61 [ + - ]: 1600 : nr_double_t gds = getOperatingPoint ("gds");
62 [ + - ]: 1600 : nr_double_t gm = getOperatingPoint ("gm");
63 [ + - ]: 1600 : nr_double_t gmb = getOperatingPoint ("gmb");
64 : :
65 : : // compute the models admittances
66 : 1600 : nr_complex_t Ygd = nr_complex_t (0.0, 2.0 * M_PI * frequency * Cgd);
67 : 1600 : nr_complex_t Ygs = nr_complex_t (0.0, 2.0 * M_PI * frequency * Cgs);
68 : 1600 : nr_complex_t Yds = gds;
69 : 1600 : nr_complex_t Ybd = nr_complex_t (gbd, 2.0 * M_PI * frequency * Cbd);
70 : 1600 : nr_complex_t Ybs = nr_complex_t (gbs, 2.0 * M_PI * frequency * Cbs);
71 : 1600 : nr_complex_t Ygb = nr_complex_t (0.0, 2.0 * M_PI * frequency * Cgb);
72 : :
73 : : // build admittance matrix and convert it to S-parameter matrix
74 [ + - ]: 1600 : matrix y (4);
75 [ + - ][ + - ]: 1600 : y.set (NODE_G, NODE_G, Ygd + Ygs + Ygb);
[ + - ]
76 [ + - ]: 1600 : y.set (NODE_G, NODE_D, -Ygd);
77 [ + - ]: 1600 : y.set (NODE_G, NODE_S, -Ygs);
78 [ + - ]: 1600 : y.set (NODE_G, NODE_B, -Ygb);
79 [ + - ]: 1600 : y.set (NODE_D, NODE_G, gm - Ygd);
80 [ + - ][ + - ]: 1600 : y.set (NODE_D, NODE_D, Ygd + Yds + Ybd - DrainControl);
[ + - ]
81 [ + - ]: 1600 : y.set (NODE_D, NODE_S, -Yds - SourceControl);
82 [ + - ]: 1600 : y.set (NODE_D, NODE_B, -Ybd + gmb);
83 [ + - ]: 1600 : y.set (NODE_S, NODE_G, -Ygs - gm);
84 [ + - ]: 1600 : y.set (NODE_S, NODE_D, -Yds + DrainControl);
85 [ + - ][ + - ]: 1600 : y.set (NODE_S, NODE_S, Ygs + Yds + Ybs + SourceControl);
[ + - ]
86 [ + - ]: 1600 : y.set (NODE_S, NODE_B, -Ybs - gmb);
87 [ + - ]: 1600 : y.set (NODE_B, NODE_G, -Ygb);
88 [ + - ]: 1600 : y.set (NODE_B, NODE_D, -Ybd);
89 [ + - ]: 1600 : y.set (NODE_B, NODE_S, -Ybs);
90 [ + - ][ + - ]: 1600 : y.set (NODE_B, NODE_B, Ybd + Ybs + Ygb);
[ + - ]
91 : :
92 : 1600 : return y;
93 : : }
94 : :
95 : 0 : void mosfet::calcNoiseSP (nr_double_t frequency) {
96 [ # # ][ # # ]: 0 : setMatrixN (cytocs (calcMatrixCy (frequency) * z0, getMatrixS ()));
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
97 : 0 : }
98 : :
99 : 0 : matrix mosfet::calcMatrixCy (nr_double_t frequency) {
100 : : /* get operating points and noise properties */
101 : 0 : nr_double_t Kf = getPropertyDouble ("Kf");
102 : 0 : nr_double_t Af = getPropertyDouble ("Af");
103 : 0 : nr_double_t Ffe = getPropertyDouble ("Ffe");
104 : 0 : nr_double_t gm = fabs (getOperatingPoint ("gm"));
105 : 0 : nr_double_t Ids = fabs (getOperatingPoint ("Id"));
106 : 0 : nr_double_t T = getPropertyDouble ("Temp");
107 : :
108 : : /* compute channel noise and flicker noise generated by the DC
109 : : transconductance and current flow from drain to source */
110 : : nr_double_t i = 8 * kelvin (T) / T0 * gm / 3 +
111 : 0 : Kf * qucs::pow (Ids, Af) / qucs::pow (frequency, Ffe) / kB / T0;
112 : :
113 : : /* build noise current correlation matrix and convert it to
114 : : noise-wave correlation matrix */
115 : 0 : matrix cy = matrix (4);
116 [ # # ]: 0 : cy.set (NODE_D, NODE_D, +i);
117 [ # # ]: 0 : cy.set (NODE_S, NODE_S, +i);
118 [ # # ]: 0 : cy.set (NODE_D, NODE_S, -i);
119 [ # # ]: 0 : cy.set (NODE_S, NODE_D, -i);
120 : 0 : return cy;
121 : : }
122 : :
123 : 3898 : void mosfet::restartDC (void) {
124 : : // apply starting values to previous iteration values
125 [ + - ][ + - ]: 3898 : UgdPrev = real (getV (NODE_G) - getV (NODE_D));
126 [ + - ][ + - ]: 3898 : UgsPrev = real (getV (NODE_G) - getV (NODE_S));
127 [ + - ][ + - ]: 3898 : UbsPrev = real (getV (NODE_B) - getV (NODE_S));
128 [ + - ][ + - ]: 3898 : UbdPrev = real (getV (NODE_B) - getV (NODE_D));
129 : 3898 : UdsPrev = UgsPrev - UgdPrev;
130 : 3898 : }
131 : :
132 : 30 : void mosfet::initDC (void) {
133 : :
134 : : // allocate MNA matrices
135 : 30 : allocMatrixMNA ();
136 : :
137 : : // initialize starting values
138 : 30 : restartDC ();
139 : :
140 : : // initialize the MOSFET
141 : 30 : initModel ();
142 : :
143 : : // get device temperature
144 : 30 : nr_double_t T = getPropertyDouble ("Temp");
145 : :
146 : : // possibly insert series resistance at source
147 [ + + ]: 30 : if (Rs != 0.0) {
148 : : // create additional circuit if necessary and reassign nodes
149 : 10 : rs = splitResistor (this, rs, "Rs", "source", NODE_S);
150 : 10 : rs->setProperty ("Temp", T);
151 : 10 : rs->setProperty ("R", Rs);
152 : 10 : rs->setProperty ("Controlled", getName ());
153 : 10 : rs->initDC ();
154 : : }
155 : : // no series resistance at source
156 : : else {
157 : 20 : disableResistor (this, rs, NODE_S);
158 : : }
159 : :
160 : : // possibly insert series resistance at gate
161 : 30 : nr_double_t Rg = getPropertyDouble ("Rg");
162 [ - + ]: 30 : if (Rg != 0.0) {
163 : : // create additional circuit if necessary and reassign nodes
164 : 0 : rg = splitResistor (this, rg, "Rg", "gate", NODE_G);
165 : 0 : rg->setProperty ("Temp", T);
166 : 0 : rg->setProperty ("R", Rg);
167 : 0 : rg->setProperty ("Controlled", getName ());
168 : 0 : rg->initDC ();
169 : : }
170 : : // no series resistance at source
171 : : else {
172 : 30 : disableResistor (this, rg, NODE_G);
173 : : }
174 : :
175 : : // possibly insert series resistance at drain
176 [ + + ]: 30 : if (Rd != 0.0) {
177 : : // create additional circuit if necessary and reassign nodes
178 : 10 : rd = splitResistor (this, rd, "Rd", "drain", NODE_D);
179 : 10 : rd->setProperty ("Temp", T);
180 : 10 : rd->setProperty ("R", Rd);
181 : 10 : rd->setProperty ("Controlled", getName ());
182 : 10 : rd->initDC ();
183 : : }
184 : : // no series resistance at drain
185 : : else {
186 : 20 : disableResistor (this, rd, NODE_D);
187 : : }
188 : 30 : }
189 : :
190 : 30 : void mosfet::initModel (void) {
191 : :
192 : : // get device temperature
193 : 30 : nr_double_t T = getPropertyDouble ("Temp");
194 : 30 : nr_double_t T2 = kelvin (getPropertyDouble ("Temp"));
195 : 30 : nr_double_t T1 = kelvin (getPropertyDouble ("Tnom"));
196 : :
197 : : // apply polarity of MOSFET
198 : 30 : const char * const type = getPropertyString ("Type");
199 [ + + ]: 30 : pol = !strcmp (type, "pfet") ? -1 : 1;
200 : :
201 : : // calculate effective channel length
202 : 30 : nr_double_t L = getPropertyDouble ("L");
203 : 30 : nr_double_t Ld = getPropertyDouble ("Ld");
204 [ - + ]: 30 : if ((Leff = L - 2 * Ld) <= 0) {
205 : : logprint (LOG_STATUS, "WARNING: effective MOSFET channel length %g <= 0, "
206 : 0 : "set to L = %g\n", Leff, L);
207 : 0 : Leff = L;
208 : : }
209 : :
210 : : // calculate gate oxide overlap capacitance
211 : 30 : nr_double_t W = getPropertyDouble ("W");
212 : 30 : nr_double_t Tox = getPropertyDouble ("Tox");
213 [ - + ]: 30 : if (Tox <= 0) {
214 : : logprint (LOG_STATUS, "WARNING: disabling gate oxide capacitance, "
215 : 0 : "Cox = 0\n");
216 : 0 : Cox = 0;
217 : : } else {
218 : 30 : Cox = (ESiO2 * E0 / Tox);
219 : : }
220 : :
221 : : // calculate DC transconductance coefficient
222 : 30 : nr_double_t Kp = getPropertyDouble ("Kp");
223 : 30 : nr_double_t Uo = getPropertyDouble ("Uo");
224 : 30 : nr_double_t F1 = qucs::exp (1.5 * qucs::log (T1 / T2));
225 : 30 : Kp = Kp * F1;
226 : 30 : Uo = Uo * F1;
227 : 30 : setScaledProperty ("Kp", Kp);
228 : 30 : setScaledProperty ("Uo", Uo);
229 [ + - ]: 30 : if (Kp > 0) {
230 : 30 : beta = Kp * W / Leff;
231 : : } else {
232 [ # # ][ # # ]: 0 : if (Cox > 0 && Uo > 0) {
233 : 0 : beta = Uo * 1e-4 * Cox * W / Leff;
234 : : } else {
235 : : logprint (LOG_STATUS, "WARNING: adjust Tox, Uo or Kp to get a valid "
236 : 0 : "transconductance coefficient\n");
237 : 0 : beta = 2e-5 * W / Leff;
238 : : }
239 : : }
240 : :
241 : : // calculate surface potential
242 : 30 : nr_double_t P = getPropertyDouble ("Phi");
243 : 30 : nr_double_t Nsub = getPropertyDouble ("Nsub");
244 : 30 : nr_double_t Ut = T0 * kBoverQ;
245 : 30 : P = pnPotential_T (T1,T2, P);
246 : 30 : setScaledProperty ("Phi", P);
247 [ - + ]: 30 : if ((Phi = P) <= 0) {
248 [ # # ]: 0 : if (Nsub > 0) {
249 [ # # ]: 0 : if (Nsub * 1e6 >= NiSi) {
250 : 0 : Phi = 2 * Ut * qucs::log (Nsub * 1e6 / NiSi);
251 : : } else {
252 : : logprint (LOG_STATUS, "WARNING: substrate doping less than instrinsic "
253 : 0 : "density, adjust Nsub >= %g\n", NiSi / 1e6);
254 : 0 : Phi = 0.6;
255 : : }
256 : : } else {
257 : : logprint (LOG_STATUS, "WARNING: adjust Nsub or Phi to get a valid "
258 : 0 : "surface potential\n");
259 : 0 : Phi = 0.6;
260 : : }
261 : : }
262 : :
263 : : // calculate bulk threshold
264 : 30 : nr_double_t G = getPropertyDouble ("Gamma");
265 [ - + ]: 30 : if ((Ga = G) < 0) {
266 [ # # ][ # # ]: 0 : if (Cox > 0 && Nsub > 0) {
267 : 0 : Ga = qucs::sqrt (2 * Q_e * ESi * E0 * Nsub * 1e6) / Cox;
268 : : } else {
269 : : logprint (LOG_STATUS, "WARNING: adjust Tox, Nsub or Gamma to get a "
270 : 0 : "valid bulk threshold\n");
271 : 0 : Ga = 0.0;
272 : : }
273 : : }
274 : :
275 : : // calculate threshold voltage
276 : 30 : nr_double_t Vt0 = getPropertyDouble ("Vt0");
277 [ - + ]: 30 : if ((Vto = Vt0) == 0.0) {
278 : 0 : nr_double_t Tpg = getPropertyDouble ("Tpg");
279 : 0 : nr_double_t Nss = getPropertyDouble ("Nss");
280 : : nr_double_t PhiMS, PhiG, Eg;
281 : : // bandgap for silicon
282 : 0 : Eg = Egap (kelvin (T));
283 [ # # ]: 0 : if (Tpg != 0.0) { // n-poly or p-poly
284 : 0 : PhiG = 4.15 + Eg / 2 - pol * Tpg * Eg / 2;
285 : : } else { // alumina
286 : 0 : PhiG = 4.1;
287 : : }
288 : 0 : PhiMS = PhiG - (4.15 + Eg / 2 + pol * Phi / 2);
289 [ # # ][ # # ]: 0 : if (Nss >= 0 && Cox > 0) {
290 : 0 : Vto = PhiMS - Q_e * Nss * 1e4 / Cox + pol * (Phi + Ga * qucs::sqrt (Phi));
291 : : } else {
292 : : logprint (LOG_STATUS, "WARNING: adjust Tox, Nss or Vt0 to get a "
293 : 0 : "valid threshold voltage\n");
294 : 0 : Vto = 0.0;
295 : : }
296 : : }
297 : :
298 : 30 : Cox = Cox * W * Leff;
299 : :
300 : : // calculate drain and source resistance if necessary
301 : 30 : nr_double_t Rsh = getPropertyDouble ("Rsh");
302 : 30 : nr_double_t Nrd = getPropertyDouble ("Nrd");
303 : 30 : nr_double_t Nrs = getPropertyDouble ("Nrs");
304 : 30 : Rd = getPropertyDouble ("Rd");
305 : 30 : Rs = getPropertyDouble ("Rs");
306 [ + + ]: 30 : if (Rsh > 0) {
307 [ + - ]: 10 : if (Nrd > 0) Rd += Rsh * Nrd;
308 [ + - ]: 10 : if (Nrs > 0) Rs += Rsh * Nrs;
309 : : }
310 : :
311 : : // calculate zero-bias junction capacitance
312 : 30 : nr_double_t Cj = getPropertyDouble ("Cj");
313 : 30 : nr_double_t Mj = getPropertyDouble ("Mj");
314 : 30 : nr_double_t Mjs = getPropertyDouble ("Mjsw");
315 : 30 : nr_double_t Pb = getPropertyDouble ("Pb");
316 : : nr_double_t PbT, F2, F3;
317 : 30 : PbT = pnPotential_T (T1,T2, Pb);
318 : 30 : F2 = pnCapacitance_F (T1, T2, Mj, PbT / Pb);
319 : 30 : F3 = pnCapacitance_F (T1, T2, Mjs, PbT / Pb);
320 : 30 : Pb = PbT;
321 : 30 : setScaledProperty ("Pb", Pb);
322 [ + + ]: 30 : if (Cj <= 0) {
323 [ + - ][ + - ]: 14 : if (Pb > 0 && Nsub >= 0) {
324 : 14 : Cj = qucs::sqrt (ESi * E0 * Q_e * Nsub * 1e6 / 2 / Pb);
325 : : }
326 : : else {
327 : : logprint (LOG_STATUS, "WARNING: adjust Pb, Nsub or Cj to get a "
328 : 0 : "valid square junction capacitance\n");
329 : 14 : Cj = 0.0;
330 : : }
331 : : }
332 : 30 : Cj = Cj * F2;
333 : 30 : setScaledProperty ("Cj", Cj);
334 : :
335 : : // calculate junction capacitances
336 : 30 : nr_double_t Cbd0 = getPropertyDouble ("Cbd");
337 : 30 : nr_double_t Cbs0 = getPropertyDouble ("Cbs");
338 : 30 : nr_double_t Ad = getPropertyDouble ("Ad");
339 : 30 : nr_double_t As = getPropertyDouble ("As");
340 : 30 : Cbd0 = Cbd0 * F2;
341 [ + + ]: 30 : if (Cbd0 <= 0) {
342 : 22 : Cbd0 = Cj * Ad;
343 : : }
344 : 30 : setScaledProperty ("Cbd", Cbd0);
345 : 30 : Cbs0 = Cbs0 * F2;
346 [ + + ]: 30 : if (Cbs0 <= 0) {
347 : 22 : Cbs0 = Cj * As;
348 : : }
349 : 30 : setScaledProperty ("Cbs", Cbs0);
350 : :
351 : : // calculate periphery junction capacitances
352 : 30 : nr_double_t Cjs = getPropertyDouble ("Cjsw");
353 : 30 : nr_double_t Pd = getPropertyDouble ("Pd");
354 : 30 : nr_double_t Ps = getPropertyDouble ("Ps");
355 : 30 : Cjs = Cjs * F3;
356 : 30 : setProperty ("Cbds", Cjs * Pd);
357 : 30 : setProperty ("Cbss", Cjs * Ps);
358 : :
359 : : // calculate junction capacitances and saturation currents
360 : 30 : nr_double_t Js = getPropertyDouble ("Js");
361 : 30 : nr_double_t Is = getPropertyDouble ("Is");
362 : : nr_double_t F4, E1, E2;
363 : 30 : E1 = Egap (T1);
364 : 30 : E2 = Egap (T2);
365 : 30 : F4 = qucs::exp (- QoverkB / T2 * (T2 / T1 * E1 - E2));
366 : 30 : Is = Is * F4;
367 : 30 : Js = Js * F4;
368 [ + + ]: 30 : nr_double_t Isd = (Ad > 0) ? Js * Ad : Is;
369 [ + + ]: 30 : nr_double_t Iss = (As > 0) ? Js * As : Is;
370 : 30 : setProperty ("Isd", Isd);
371 : 30 : setProperty ("Iss", Iss);
372 : :
373 : : #if DEBUG
374 : : logprint (LOG_STATUS, "NOTIFY: Cox=%g, Beta=%g Ga=%g, Phi=%g, Vto=%g\n",
375 : 30 : Cox, beta, Ga, Phi, Vto);
376 : : #endif /* DEBUG */
377 : 30 : }
378 : :
379 : 83645 : void mosfet::calcDC (void) {
380 : :
381 : : // fetch device model parameters
382 : 83645 : nr_double_t Isd = getPropertyDouble ("Isd");
383 : 83645 : nr_double_t Iss = getPropertyDouble ("Iss");
384 : 83645 : nr_double_t n = getPropertyDouble ("N");
385 : 83645 : nr_double_t l = getPropertyDouble ("Lambda");
386 : 83645 : nr_double_t T = getPropertyDouble ("Temp");
387 : :
388 : : nr_double_t Ut, IeqBS, IeqBD, IeqDS, UbsCrit, UbdCrit, gtiny;
389 : :
390 : 83645 : T = kelvin (T);
391 : 83645 : Ut = T * kBoverQ;
392 [ + - ][ + - ]: 83645 : Ugd = real (getV (NODE_G) - getV (NODE_D)) * pol;
393 [ + - ][ + - ]: 83645 : Ugs = real (getV (NODE_G) - getV (NODE_S)) * pol;
394 [ + - ][ + - ]: 83645 : Ubs = real (getV (NODE_B) - getV (NODE_S)) * pol;
395 [ + - ][ + - ]: 83645 : Ubd = real (getV (NODE_B) - getV (NODE_D)) * pol;
396 : 83645 : Uds = Ugs - Ugd;
397 : :
398 : : // critical voltage necessary for bad start values
399 : 83645 : UbsCrit = pnCriticalVoltage (Iss, Ut * n);
400 : 83645 : UbdCrit = pnCriticalVoltage (Isd, Ut * n);
401 : :
402 : : // for better convergence
403 [ + + ]: 83645 : if (Uds >= 0) {
404 : 74705 : Ugs = fetVoltage (Ugs, UgsPrev, Vto * pol);
405 : 74705 : Uds = Ugs - Ugd;
406 : 74705 : Uds = fetVoltageDS (Uds, UdsPrev);
407 : 74705 : Ugd = Ugs - Uds;
408 : : }
409 : : else {
410 : 8940 : Ugd = fetVoltage (Ugd, UgdPrev, Vto * pol);
411 : 8940 : Uds = Ugs - Ugd;
412 : 8940 : Uds = -fetVoltageDS (-Uds, -UdsPrev);
413 : 8940 : Ugs = Ugd + Uds;
414 : : }
415 [ + + ]: 83645 : if (Uds >= 0) {
416 : 74425 : Ubs = pnVoltage (Ubs, UbsPrev, Ut * n, UbsCrit);
417 : 74425 : Ubd = Ubs - Uds;
418 : : }
419 : : else {
420 : 9220 : Ubd = pnVoltage (Ubd, UbdPrev, Ut * n, UbdCrit);
421 : 9220 : Ubs = Ubd + Uds;
422 : : }
423 : 83645 : UgsPrev = Ugs; UgdPrev = Ugd; UbdPrev = Ubd; UdsPrev = Uds; UbsPrev = Ubs;
424 : :
425 : : // parasitic bulk-source diode
426 : 83645 : gtiny = Iss;
427 : 83645 : pnJunctionMOS (Ubs, Iss, Ut * n, Ibs, gbs);
428 : 83645 : Ibs += gtiny * Ubs;
429 : 83645 : gbs += gtiny;
430 : :
431 : : // parasitic bulk-drain diode
432 : 83645 : gtiny = Isd;
433 : 83645 : pnJunctionMOS (Ubd, Isd, Ut * n, Ibd, gbd);
434 : 83645 : Ibd += gtiny * Ubd;
435 : 83645 : gbd += gtiny;
436 : :
437 : : // differentiate inverse and forward mode
438 [ + + ]: 83645 : MOSdir = (Uds >= 0) ? +1 : -1;
439 : :
440 : : // first calculate sqrt (Upn - Phi)
441 [ + + ]: 83645 : nr_double_t Upn = (MOSdir > 0) ? Ubs : Ubd;
442 : 83645 : nr_double_t Sarg, Sphi = qucs::sqrt (Phi);
443 [ + + ]: 83645 : if (Upn <= 0) {
444 : : // take equation as is
445 : 75987 : Sarg = qucs::sqrt (Phi - Upn);
446 : : }
447 : : else {
448 : : // taylor series of "sqrt (x - 1)" -> continual at Ubs/Ubd = 0
449 : 7658 : Sarg = Sphi - Upn / Sphi / 2;
450 [ + - ]: 7658 : Sarg = MAX (Sarg, 0);
451 : : }
452 : :
453 : : // calculate bias-dependent threshold voltage
454 : 83645 : Uon = Vto * pol + Ga * (Sarg - Sphi);
455 [ + + ]: 83645 : nr_double_t Utst = ((MOSdir > 0) ? Ugs : Ugd) - Uon;
456 : : // no infinite backgate transconductance (if non-zero Ga)
457 [ + - ]: 83645 : nr_double_t arg = (Sarg != 0.0) ? (Ga / Sarg / 2) : 0;
458 : :
459 : : // cutoff region
460 [ + + ]: 83645 : if (Utst <= 0) {
461 : 17520 : Ids = 0;
462 : 17520 : gm = 0;
463 : 17520 : gds = 0;
464 : 17520 : gmb = 0;
465 : : }
466 : : else {
467 : 66125 : nr_double_t Vds = Uds * MOSdir;
468 : 66125 : nr_double_t b = beta * (1 + l * Vds);
469 : : // saturation region
470 [ + + ]: 66125 : if (Utst <= Vds) {
471 : 39742 : Ids = b * Utst * Utst / 2;
472 : 39742 : gm = b * Utst;
473 : 39742 : gds = l * beta * Utst * Utst / 2;
474 : : }
475 : : // linear region
476 : : else {
477 : 26383 : Ids = b * Vds * (Utst - Vds / 2);
478 : 26383 : gm = b * Vds;
479 : 26383 : gds = b * (Utst - Vds) + l * beta * Vds * (Utst - Vds / 2);
480 : : }
481 : 66125 : gmb = gm * arg;
482 : : }
483 [ + + ]: 83645 : Udsat = pol * MAX (Utst, 0);
484 : 83645 : Ids = MOSdir * Ids;
485 : 83645 : Uon = pol * Uon;
486 : :
487 : : // compute autonomic current sources
488 : 83645 : IeqBD = Ibd - gbd * Ubd;
489 : 83645 : IeqBS = Ibs - gbs * Ubs;
490 : :
491 : : // exchange controlling nodes if necessary
492 [ + + ]: 83645 : SourceControl = (MOSdir > 0) ? (gm + gmb) : 0;
493 [ + + ]: 83645 : DrainControl = (MOSdir < 0) ? (gm + gmb) : 0;
494 [ + + ]: 83645 : if (MOSdir > 0) {
495 : 74425 : IeqDS = Ids - gm * Ugs - gmb * Ubs - gds * Uds;
496 : : } else {
497 : 9220 : IeqDS = Ids - gm * Ugd - gmb * Ubd - gds * Uds;
498 : : }
499 : :
500 [ + - ]: 83645 : setI (NODE_G, 0);
501 [ + - ]: 83645 : setI (NODE_D, (+IeqBD - IeqDS) * pol);
502 [ + - ]: 83645 : setI (NODE_S, (+IeqBS + IeqDS) * pol);
503 [ + - ]: 83645 : setI (NODE_B, (-IeqBD - IeqBS) * pol);
504 : :
505 : : // apply admittance matrix elements
506 [ + - ]: 83645 : setY (NODE_G, NODE_G, 0);
507 [ + - ]: 83645 : setY (NODE_G, NODE_D, 0);
508 [ + - ]: 83645 : setY (NODE_G, NODE_S, 0);
509 [ + - ]: 83645 : setY (NODE_G, NODE_B, 0);
510 [ + - ]: 83645 : setY (NODE_D, NODE_G, gm);
511 [ + - ]: 83645 : setY (NODE_D, NODE_D, gds + gbd - DrainControl);
512 [ + - ]: 83645 : setY (NODE_D, NODE_S, -gds - SourceControl);
513 [ + - ]: 83645 : setY (NODE_D, NODE_B, gmb - gbd);
514 [ + - ]: 83645 : setY (NODE_S, NODE_G, -gm);
515 [ + - ]: 83645 : setY (NODE_S, NODE_D, -gds + DrainControl);
516 [ + - ]: 83645 : setY (NODE_S, NODE_S, gbs + gds + SourceControl);
517 [ + - ]: 83645 : setY (NODE_S, NODE_B, -gbs - gmb);
518 [ + - ]: 83645 : setY (NODE_B, NODE_G, 0);
519 [ + - ]: 83645 : setY (NODE_B, NODE_D, -gbd);
520 [ + - ]: 83645 : setY (NODE_B, NODE_S, -gbs);
521 [ + - ]: 83645 : setY (NODE_B, NODE_B, gbs + gbd);
522 : 83645 : }
523 : :
524 : : /* Usual and additional state definitions. */
525 : :
526 : : #define qgdState 0 // gate-drain charge state
527 : : #define igdState 1 // gate-drain current state
528 : : #define vgdState 2 // gate-drain voltage state
529 : : #define cgdState 3 // gate-drain capacitance state
530 : : #define qgsState 4 // gate-source charge state
531 : : #define igsState 5 // gate-source current state
532 : : #define vgsState 6 // gate-source voltage state
533 : : #define cgsState 7 // gate-source capacitance state
534 : : #define qbdState 8 // bulk-drain charge state
535 : : #define ibdState 9 // bulk-drain current state
536 : : #define qbsState 10 // bulk-source charge state
537 : : #define ibsState 11 // bulk-source current state
538 : : #define qgbState 12 // gate-bulk charge state
539 : : #define igbState 13 // gate-bulk current state
540 : : #define vgbState 14 // gate-bulk voltage state
541 : : #define cgbState 15 // gate-bulk capacitance state
542 : :
543 : 83504 : void mosfet::saveOperatingPoints (void) {
544 : : nr_double_t Vgs, Vgd, Vbs, Vbd;
545 [ + - ][ + - ]: 83504 : Vgd = real (getV (NODE_G) - getV (NODE_D)) * pol;
546 [ + - ][ + - ]: 83504 : Vgs = real (getV (NODE_G) - getV (NODE_S)) * pol;
547 [ + - ][ + - ]: 83504 : Vbs = real (getV (NODE_B) - getV (NODE_S)) * pol;
548 [ + - ][ + - ]: 83504 : Vbd = real (getV (NODE_B) - getV (NODE_D)) * pol;
549 : 83504 : setOperatingPoint ("Vgs", Vgs);
550 : 83504 : setOperatingPoint ("Vgd", Vgd);
551 : 83504 : setOperatingPoint ("Vbs", Vbs);
552 : 83504 : setOperatingPoint ("Vbd", Vbd);
553 : 83504 : setOperatingPoint ("Vds", Vgs - Vgd);
554 : 83504 : setOperatingPoint ("Vgb", Vgs - Vbs);
555 : 83504 : }
556 : :
557 : 83496 : void mosfet::loadOperatingPoints (void) {
558 : 83496 : Ugs = getOperatingPoint ("Vgs");
559 : 83496 : Ugd = getOperatingPoint ("Vgd");
560 : 83496 : Ubs = getOperatingPoint ("Vbs");
561 : 83496 : Ubd = getOperatingPoint ("Vbd");
562 : 83496 : Uds = getOperatingPoint ("Vds");
563 : 83496 : Ugb = getOperatingPoint ("Vgb");
564 : 83496 : }
565 : :
566 : 83504 : void mosfet::calcOperatingPoints (void) {
567 : :
568 : : // fetch device model parameters
569 [ + - ]: 83504 : nr_double_t Cbd0 = getScaledProperty ("Cbd");
570 [ + - ]: 83504 : nr_double_t Cbs0 = getScaledProperty ("Cbs");
571 [ + - ]: 83504 : nr_double_t Cbds = getPropertyDouble ("Cbds");
572 [ + - ]: 83504 : nr_double_t Cbss = getPropertyDouble ("Cbss");
573 [ + - ]: 83504 : nr_double_t Cgso = getPropertyDouble ("Cgso");
574 [ + - ]: 83504 : nr_double_t Cgdo = getPropertyDouble ("Cgdo");
575 [ + - ]: 83504 : nr_double_t Cgbo = getPropertyDouble ("Cgbo");
576 [ + - ]: 83504 : nr_double_t Pb = getScaledProperty ("Pb");
577 [ + - ]: 83504 : nr_double_t M = getPropertyDouble ("Mj");
578 [ + - ]: 83504 : nr_double_t Ms = getPropertyDouble ("Mjsw");
579 [ + - ]: 83504 : nr_double_t Fc = getPropertyDouble ("Fc");
580 [ + - ]: 83504 : nr_double_t Tt = getPropertyDouble ("Tt");
581 [ + - ]: 83504 : nr_double_t W = getPropertyDouble ("W");
582 : :
583 : : nr_double_t Cbs, Cbd, Cgd, Cgb, Cgs;
584 : :
585 : : // capacitance of bulk-drain diode
586 [ + - ]: 83504 : Cbd = gbd * Tt + pnCapacitance (Ubd, Cbd0, Pb, M, Fc) +
587 [ + - ]: 83504 : pnCapacitance (Ubd, Cbds, Pb, Ms, Fc);
588 [ + - ]: 83504 : Qbd = Ibd * Tt + pnCharge (Ubd, Cbd0, Pb, M, Fc) +
589 [ + - ]: 83504 : pnCharge (Ubd, Cbds, Pb, Ms, Fc);
590 : :
591 : : // capacitance of bulk-source diode
592 [ + - ]: 83504 : Cbs = gbs * Tt + pnCapacitance (Ubs, Cbs0, Pb, M, Fc) +
593 [ + - ]: 83504 : pnCapacitance (Ubs, Cbss, Pb, Ms, Fc);
594 [ + - ]: 83504 : Qbs = Ibs * Tt + pnCharge (Ubs, Cbs0, Pb, M, Fc) +
595 [ + - ]: 83504 : pnCharge (Ubs, Cbss, Pb, Ms, Fc);
596 : :
597 : : // calculate bias-dependent MOS overlap capacitances
598 [ + + ]: 83504 : if (MOSdir > 0) {
599 [ + - ]: 74286 : fetCapacitanceMeyer (Ugs, Ugd, Uon, Udsat, Phi, Cox, Cgs, Cgd, Cgb);
600 : : } else {
601 [ + - ]: 9218 : fetCapacitanceMeyer (Ugd, Ugs, Uon, Udsat, Phi, Cox, Cgd, Cgs, Cgb);
602 : : }
603 : :
604 : : // charge approximation
605 [ + + ]: 83504 : if (transientMode) {
606 [ - + ]: 83496 : if (transientMode == 1) { // by trapezoidal rule
607 : : // gate-source charge
608 [ # # ]: 0 : Qgs = transientChargeTR (qgsState, Cgs, Ugs, Cgso * W);
609 : : // gate-drain charge
610 [ # # ]: 0 : Qgd = transientChargeTR (qgdState, Cgd, Ugd, Cgdo * W);
611 : : // gate-bulk charge
612 [ # # ]: 0 : Qgb = transientChargeTR (qgbState, Cgb, Ugb, Cgbo * Leff);
613 : : }
614 [ + - ]: 83496 : else if (transientMode == 2) { // by simpson's rule
615 [ + - ]: 83496 : Qgs = transientChargeSR (qgsState, Cgs, Ugs, Cgso * W);
616 [ + - ]: 83496 : Qgd = transientChargeSR (qgdState, Cgd, Ugd, Cgdo * W);
617 [ + - ]: 83496 : Qgb = transientChargeSR (qgbState, Cgb, Ugb, Cgbo * Leff);
618 : : }
619 : : }
620 : : // usual operating point
621 : : else {
622 : 8 : Cgs += Cgso * W;
623 : 8 : Cgd += Cgdo * W;
624 : 8 : Cgb += Cgbo * Leff;
625 : : }
626 : :
627 : : // save operating points
628 [ + - ]: 83504 : setOperatingPoint ("Id", Ids);
629 [ + - ]: 83504 : setOperatingPoint ("gm", gm);
630 [ + - ]: 83504 : setOperatingPoint ("gmb", gmb);
631 [ + - ]: 83504 : setOperatingPoint ("gds", gds);
632 [ + - ]: 83504 : setOperatingPoint ("Vth", Vto);
633 [ + - ]: 83504 : setOperatingPoint ("Vdsat", Udsat);
634 [ + - ]: 83504 : setOperatingPoint ("gbs", gbs);
635 [ + - ]: 83504 : setOperatingPoint ("gbd", gbd);
636 [ + - ]: 83504 : setOperatingPoint ("Cbd", Cbd);
637 [ + - ]: 83504 : setOperatingPoint ("Cbs", Cbs);
638 [ + - ]: 83504 : setOperatingPoint ("Cgs", Cgs);
639 [ + - ]: 83504 : setOperatingPoint ("Cgd", Cgd);
640 [ + - ]: 83504 : setOperatingPoint ("Cgb", Cgb);
641 : 83504 : }
642 : :
643 : 8 : void mosfet::initAC (void) {
644 : 8 : allocMatrixMNA ();
645 : 8 : }
646 : :
647 : 1600 : void mosfet::calcAC (nr_double_t frequency) {
648 [ + - ]: 1600 : setMatrixY (calcMatrixY (frequency));
649 : 1600 : }
650 : :
651 : 0 : void mosfet::calcNoiseAC (nr_double_t frequency) {
652 [ # # ]: 0 : setMatrixN (calcMatrixCy (frequency));
653 : 0 : }
654 : :
655 : 11 : void mosfet::initTR (void) {
656 : 11 : setStates (16);
657 : 11 : initDC ();
658 : 11 : }
659 : :
660 : 83496 : void mosfet::calcTR (nr_double_t) {
661 : 83496 : calcDC ();
662 : 83496 : transientMode = getPropertyInteger ("capModel");
663 : 83496 : saveOperatingPoints ();
664 : 83496 : loadOperatingPoints ();
665 : 83496 : calcOperatingPoints ();
666 : 83496 : transientMode = 0;
667 : :
668 : 83496 : nr_double_t Cgd = getOperatingPoint ("Cgd");
669 : 83496 : nr_double_t Cgs = getOperatingPoint ("Cgs");
670 : 83496 : nr_double_t Cbd = getOperatingPoint ("Cbd");
671 : 83496 : nr_double_t Cbs = getOperatingPoint ("Cbs");
672 : 83496 : nr_double_t Cgb = getOperatingPoint ("Cgb");
673 : :
674 : 83496 : Uds = Ugs - Ugd;
675 : 83496 : Ugb = Ugs - Ubs;
676 : :
677 : 83496 : transientCapacitance (qbdState, NODE_B, NODE_D, Cbd, Ubd, Qbd);
678 : 83496 : transientCapacitance (qbsState, NODE_B, NODE_S, Cbs, Ubs, Qbs);
679 : :
680 : : // handle Meyer charges and capacitances
681 : 83496 : transientCapacitance (qgdState, NODE_G, NODE_D, Cgd, Ugd, Qgd);
682 : 83496 : transientCapacitance (qgsState, NODE_G, NODE_S, Cgs, Ugs, Qgs);
683 : 83496 : transientCapacitance (qgbState, NODE_G, NODE_B, Cgb, Ugb, Qgb);
684 : 83496 : }
685 : :
686 : : /* The function uses the trapezoidal rule to compute the current
687 : : capacitance and charge. The approximation is necessary because the
688 : : Meyer model is a capacitance model and not a charge model. */
689 : 0 : nr_double_t mosfet::transientChargeTR (int qstate, nr_double_t& cap,
690 : : nr_double_t voltage, nr_double_t ccap) {
691 : 0 : int vstate = qstate + 2, cstate = qstate + 3;
692 : 0 : setState (cstate, cap);
693 : 0 : cap = (cap + getState (cstate, 1)) / 2 + ccap;
694 : 0 : setState (vstate, voltage);
695 : 0 : return cap * (voltage - getState (vstate, 1)) + getState (qstate, 1);
696 : : }
697 : :
698 : : /* The function uses Simpson's numerical integration rule to compute
699 : : the current capacitance and charge. */
700 : 250488 : nr_double_t mosfet::transientChargeSR (int qstate, nr_double_t& cap,
701 : : nr_double_t voltage, nr_double_t ccap) {
702 : 250488 : int vstate = qstate + 2, cstate = qstate + 3;
703 : 250488 : setState (cstate, cap);
704 : 250488 : cap = (cap + 4 * getState (cstate, 1) + getState (cstate, 2)) / 6 + ccap;
705 : 250488 : setState (vstate, voltage);
706 : 250488 : return cap * (voltage - getState (vstate, 1)) + getState (qstate, 1);
707 : : }
708 : :
709 : : // properties
710 : : PROP_REQ [] = {
711 : : { "Is", PROP_REAL, { 1e-14, PROP_NO_STR }, PROP_POS_RANGE },
712 : : { "N", PROP_REAL, { 1, PROP_NO_STR }, PROP_RNGII (0.1, 100) },
713 : : { "Vt0", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
714 : : { "Lambda", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
715 : : { "Kp", PROP_REAL, { 2e-5, PROP_NO_STR }, PROP_POS_RANGE },
716 : : { "Gamma", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
717 : : { "Phi", PROP_REAL, { 0.6, PROP_NO_STR }, PROP_POS_RANGE },
718 : : PROP_NO_PROP };
719 : : PROP_OPT [] = {
720 : : { "Rd", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
721 : : { "Rs", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
722 : : { "Rg", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
723 : : { "L", PROP_REAL, { 100e-6, PROP_NO_STR }, PROP_RNG_X01I },
724 : : { "Ld", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
725 : : { "W", PROP_REAL, { 100e-6, PROP_NO_STR }, PROP_POS_RANGEX },
726 : : { "Tox", PROP_REAL, { 1e-7, PROP_NO_STR }, PROP_RNG_X01I },
727 : : { "Cgso", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
728 : : { "Cgdo", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
729 : : { "Cgbo", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
730 : : { "Cbd", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
731 : : { "Cbs", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
732 : : { "Pb", PROP_REAL, { 0.8, PROP_NO_STR }, PROP_RNGXI (0, 10) },
733 : : { "Mj", PROP_REAL, { 0.5, PROP_NO_STR }, PROP_RNGII (0, 1) },
734 : : { "Fc", PROP_REAL, { 0.5, PROP_NO_STR }, PROP_RNGIX (0, 1) },
735 : : { "Cjsw", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
736 : : { "Mjsw", PROP_REAL, { 0.33, PROP_NO_STR }, PROP_RNGII (0, 1) },
737 : : { "Tt", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
738 : : { "Kf", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
739 : : { "Af", PROP_REAL, { 1, PROP_NO_STR }, PROP_POS_RANGE },
740 : : { "Ffe", PROP_REAL, { 1, PROP_NO_STR }, PROP_POS_RANGE },
741 : : { "Nsub", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
742 : : { "Nss", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
743 : : { "Tpg", PROP_INT, { 1, PROP_NO_STR }, PROP_RNGII (-1 , 1) },
744 : : { "Uo", PROP_REAL, { 600, PROP_NO_STR }, PROP_POS_RANGE },
745 : : { "Rsh", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
746 : : { "Nrd", PROP_REAL, { 1, PROP_NO_STR }, PROP_POS_RANGE },
747 : : { "Nrs", PROP_REAL, { 1, PROP_NO_STR }, PROP_POS_RANGE },
748 : : { "Cj", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
749 : : { "Js", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
750 : : { "Ad", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
751 : : { "As", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
752 : : { "Pd", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
753 : : { "Ps", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
754 : : { "Temp", PROP_REAL, { 26.85, PROP_NO_STR }, PROP_MIN_VAL (K) },
755 : : { "Tnom", PROP_REAL, { 26.85, PROP_NO_STR }, PROP_MIN_VAL (K) },
756 : : { "Type", PROP_STR, { PROP_NO_VAL, "nfet" }, PROP_RNG_FET },
757 : : { "capModel", PROP_INT, { 2, PROP_NO_STR }, PROP_RNGII (1 , 2) },
758 : : PROP_NO_PROP };
759 : : struct define_t mosfet::cirdef =
760 : : { "MOSFET", 4, PROP_COMPONENT, PROP_NO_SUBSTRATE, PROP_NONLINEAR, PROP_DEF };
|