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