Qucs-core  0.0.19
mosfet.cpp
Go to the documentation of this file.
00001 /*
00002  * mosfet.cpp - mosfet class implementation
00003  *
00004  * Copyright (C) 2004, 2005, 2006, 2008 Stefan Jahn <stefan@lkcc.org>
00005  *
00006  * This is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2, or (at your option)
00009  * any later version.
00010  *
00011  * This software is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this package; see the file COPYING.  If not, write to
00018  * the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
00019  * Boston, MA 02110-1301, USA.
00020  *
00021  * $Id$
00022  *
00023  */
00024 
00025 #if HAVE_CONFIG_H
00026 # include <config.h>
00027 #endif
00028 
00029 #include "component.h"
00030 #include "device.h"
00031 #include "mosfet.h"
00032 
00033 #define NODE_G 0 /* gate node   */
00034 #define NODE_D 1 /* drain node  */
00035 #define NODE_S 2 /* source node */
00036 #define NODE_B 3 /* bulk node   */
00037 
00038 using namespace qucs;
00039 using namespace qucs::device;
00040 
00041 mosfet::mosfet () : circuit (4) {
00042   transientMode = 0;
00043   rg = rs = rd = NULL;
00044   type = CIR_MOSFET;
00045 }
00046 
00047 void mosfet::calcSP (nr_double_t frequency) {
00048   setMatrixS (ytos (calcMatrixY (frequency)));
00049 }
00050 
00051 matrix mosfet::calcMatrixY (nr_double_t frequency) {
00052 
00053   // fetch computed operating points
00054   nr_double_t Cgd = getOperatingPoint ("Cgd");
00055   nr_double_t Cgs = getOperatingPoint ("Cgs");
00056   nr_double_t Cbd = getOperatingPoint ("Cbd");
00057   nr_double_t Cbs = getOperatingPoint ("Cbs");
00058   nr_double_t Cgb = getOperatingPoint ("Cgb");
00059   nr_double_t gbs = getOperatingPoint ("gbs");
00060   nr_double_t gbd = getOperatingPoint ("gbd");
00061   nr_double_t gds = getOperatingPoint ("gds");
00062   nr_double_t gm  = getOperatingPoint ("gm");
00063   nr_double_t gmb = getOperatingPoint ("gmb");
00064 
00065   // compute the models admittances
00066   nr_complex_t Ygd = nr_complex_t (0.0, 2.0 * pi * frequency * Cgd);
00067   nr_complex_t Ygs = nr_complex_t (0.0, 2.0 * pi * frequency * Cgs);
00068   nr_complex_t Yds = gds;
00069   nr_complex_t Ybd = nr_complex_t (gbd, 2.0 * pi * frequency * Cbd);
00070   nr_complex_t Ybs = nr_complex_t (gbs, 2.0 * pi * frequency * Cbs);
00071   nr_complex_t Ygb = nr_complex_t (0.0, 2.0 * pi * frequency * Cgb);
00072 
00073   // build admittance matrix and convert it to S-parameter matrix
00074   matrix y (4);
00075   y.set (NODE_G, NODE_G, Ygd + Ygs + Ygb);
00076   y.set (NODE_G, NODE_D, -Ygd);
00077   y.set (NODE_G, NODE_S, -Ygs);
00078   y.set (NODE_G, NODE_B, -Ygb);
00079   y.set (NODE_D, NODE_G, gm - Ygd);
00080   y.set (NODE_D, NODE_D, Ygd + Yds + Ybd - DrainControl);
00081   y.set (NODE_D, NODE_S, -Yds - SourceControl);
00082   y.set (NODE_D, NODE_B, -Ybd + gmb);
00083   y.set (NODE_S, NODE_G, -Ygs - gm);
00084   y.set (NODE_S, NODE_D, -Yds + DrainControl);
00085   y.set (NODE_S, NODE_S, Ygs + Yds + Ybs + SourceControl);
00086   y.set (NODE_S, NODE_B, -Ybs - gmb);
00087   y.set (NODE_B, NODE_G, -Ygb);
00088   y.set (NODE_B, NODE_D, -Ybd);
00089   y.set (NODE_B, NODE_S, -Ybs);
00090   y.set (NODE_B, NODE_B, Ybd + Ybs + Ygb);
00091 
00092   return y;
00093 }
00094 
00095 void mosfet::calcNoiseSP (nr_double_t frequency) {
00096   setMatrixN (cytocs (calcMatrixCy (frequency) * z0, getMatrixS ()));
00097 }
00098 
00099 matrix mosfet::calcMatrixCy (nr_double_t frequency) {
00100   /* get operating points and noise properties */
00101   nr_double_t Kf  = getPropertyDouble ("Kf");
00102   nr_double_t Af  = getPropertyDouble ("Af");
00103   nr_double_t Ffe = getPropertyDouble ("Ffe");
00104   nr_double_t gm  = fabs (getOperatingPoint ("gm"));
00105   nr_double_t Ids = fabs (getOperatingPoint ("Id"));
00106   nr_double_t T   = getPropertyDouble ("Temp");
00107 
00108   /* compute channel noise and flicker noise generated by the DC
00109      transconductance and current flow from drain to source */
00110   nr_double_t i = 8 * celsius2kelvin (T) / T0 * gm / 3 +
00111     Kf * qucs::pow (Ids, Af) / qucs::pow (frequency, Ffe) / kB / T0;
00112 
00113   /* build noise current correlation matrix and convert it to
00114      noise-wave correlation matrix */
00115   matrix cy = matrix (4);
00116   cy.set (NODE_D, NODE_D, +i);
00117   cy.set (NODE_S, NODE_S, +i);
00118   cy.set (NODE_D, NODE_S, -i);
00119   cy.set (NODE_S, NODE_D, -i);
00120   return cy;
00121 }
00122 
00123 void mosfet::restartDC (void) {
00124   // apply starting values to previous iteration values
00125   UgdPrev = real (getV (NODE_G) - getV (NODE_D));
00126   UgsPrev = real (getV (NODE_G) - getV (NODE_S));
00127   UbsPrev = real (getV (NODE_B) - getV (NODE_S));
00128   UbdPrev = real (getV (NODE_B) - getV (NODE_D));
00129   UdsPrev = UgsPrev - UgdPrev;
00130 }
00131 
00132 void mosfet::initDC (void) {
00133 
00134   // allocate MNA matrices
00135   allocMatrixMNA ();
00136 
00137   // initialize starting values
00138   restartDC ();
00139 
00140   // initialize the MOSFET
00141   initModel ();
00142 
00143   // get device temperature
00144   nr_double_t T = getPropertyDouble ("Temp");
00145 
00146   // possibly insert series resistance at source
00147   if (Rs != 0.0) {
00148     // create additional circuit if necessary and reassign nodes
00149     rs = splitResistor (this, rs, "Rs", "source", NODE_S);
00150     rs->setProperty ("Temp", T);
00151     rs->setProperty ("R", Rs);
00152     rs->setProperty ("Controlled", getName ());
00153     rs->initDC ();
00154   }
00155   // no series resistance at source
00156   else {
00157     disableResistor (this, rs, NODE_S);
00158   }
00159 
00160   // possibly insert series resistance at gate
00161   nr_double_t Rg = getPropertyDouble ("Rg");
00162   if (Rg != 0.0) {
00163     // create additional circuit if necessary and reassign nodes
00164     rg = splitResistor (this, rg, "Rg", "gate", NODE_G);
00165     rg->setProperty ("Temp", T);
00166     rg->setProperty ("R", Rg);
00167     rg->setProperty ("Controlled", getName ());
00168     rg->initDC ();
00169   }
00170   // no series resistance at source
00171   else {
00172     disableResistor (this, rg, NODE_G);
00173   }
00174 
00175   // possibly insert series resistance at drain
00176   if (Rd != 0.0) {
00177     // create additional circuit if necessary and reassign nodes
00178     rd = splitResistor (this, rd, "Rd", "drain", NODE_D);
00179     rd->setProperty ("Temp", T);
00180     rd->setProperty ("R", Rd);
00181     rd->setProperty ("Controlled", getName ());
00182     rd->initDC ();
00183   }
00184   // no series resistance at drain
00185   else {
00186     disableResistor (this, rd, NODE_D);
00187   }
00188 }
00189 
00190 void mosfet::initModel (void) {
00191 
00192   // get device temperature
00193   nr_double_t T  = getPropertyDouble ("Temp");
00194   nr_double_t T2 = celsius2kelvin (getPropertyDouble ("Temp"));
00195   nr_double_t T1 = celsius2kelvin (getPropertyDouble ("Tnom"));
00196 
00197   // apply polarity of MOSFET
00198   const char * const type = getPropertyString ("Type");
00199   pol = !strcmp (type, "pfet") ? -1 : 1;
00200 
00201   // calculate effective channel length
00202   nr_double_t L  = getPropertyDouble ("L");
00203   nr_double_t Ld = getPropertyDouble ("Ld");
00204   if ((Leff = L - 2 * Ld) <= 0) {
00205     logprint (LOG_STATUS, "WARNING: effective MOSFET channel length %g <= 0, "
00206               "set to L = %g\n", Leff, L);
00207     Leff = L;
00208   }
00209 
00210   // calculate gate oxide overlap capacitance
00211   nr_double_t W   = getPropertyDouble ("W");
00212   nr_double_t Tox = getPropertyDouble ("Tox");
00213   if (Tox <= 0) {
00214     logprint (LOG_STATUS, "WARNING: disabling gate oxide capacitance, "
00215               "Cox = 0\n");
00216     Cox = 0;
00217   } else {
00218     Cox = (ESiO2 * E0 / Tox);
00219   }
00220 
00221   // calculate DC transconductance coefficient
00222   nr_double_t Kp = getPropertyDouble ("Kp");
00223   nr_double_t Uo = getPropertyDouble ("Uo");
00224   nr_double_t F1 = qucs::exp (1.5 * qucs::log (T1 / T2));
00225   Kp = Kp * F1;
00226   Uo = Uo * F1;
00227   setScaledProperty ("Kp", Kp);
00228   setScaledProperty ("Uo", Uo);
00229   if (Kp > 0) {
00230     beta = Kp * W / Leff;
00231   } else {
00232     if (Cox > 0 && Uo > 0) {
00233       beta = Uo * 1e-4 * Cox * W / Leff;
00234     } else {
00235       logprint (LOG_STATUS, "WARNING: adjust Tox, Uo or Kp to get a valid "
00236                 "transconductance coefficient\n");
00237       beta = 2e-5 * W / Leff;
00238     }
00239   }
00240 
00241   // calculate surface potential
00242   nr_double_t P    = getPropertyDouble ("Phi");
00243   nr_double_t Nsub = getPropertyDouble ("Nsub");
00244   nr_double_t Ut   = T0 * kBoverQ;
00245   P = pnPotential_T (T1,T2, P);
00246   setScaledProperty ("Phi", P);
00247   if ((Phi = P) <= 0) {
00248     if (Nsub > 0) {
00249       if (Nsub * 1e6 >= NiSi) {
00250         Phi = 2 * Ut * qucs::log (Nsub * 1e6 / NiSi);
00251       } else {
00252         logprint (LOG_STATUS, "WARNING: substrate doping less than instrinsic "
00253                   "density, adjust Nsub >= %g\n", NiSi / 1e6);
00254         Phi = 0.6;
00255       }
00256     } else {
00257       logprint (LOG_STATUS, "WARNING: adjust Nsub or Phi to get a valid "
00258                 "surface potential\n");
00259       Phi = 0.6;
00260     }
00261   }
00262 
00263   // calculate bulk threshold
00264   nr_double_t G = getPropertyDouble ("Gamma");
00265   if ((Ga = G) < 0) {
00266     if (Cox > 0 && Nsub > 0) {
00267       Ga = qucs::sqrt (2 * Q_e * ESi * E0 * Nsub * 1e6) / Cox;
00268     } else {
00269       logprint (LOG_STATUS, "WARNING: adjust Tox, Nsub or Gamma to get a "
00270                 "valid bulk threshold\n");
00271       Ga = 0.0;
00272     }
00273   }
00274 
00275   // calculate threshold voltage
00276   nr_double_t Vt0 = getPropertyDouble ("Vt0");
00277   if ((Vto = Vt0) == 0.0) {
00278     nr_double_t Tpg = getPropertyDouble ("Tpg");
00279     nr_double_t Nss = getPropertyDouble ("Nss");
00280     nr_double_t PhiMS, PhiG, Eg;
00281     // bandgap for silicon
00282     Eg = Egap (celsius2kelvin (T));
00283     if (Tpg != 0.0) { // n-poly or p-poly
00284       PhiG = 4.15 + Eg / 2 - pol * Tpg * Eg / 2;
00285     } else {          // alumina
00286       PhiG = 4.1;
00287     }
00288     PhiMS = PhiG - (4.15 + Eg / 2 + pol * Phi / 2);
00289     if (Nss >= 0 && Cox > 0) {
00290       Vto = PhiMS - Q_e * Nss * 1e4 / Cox + pol * (Phi + Ga * qucs::sqrt (Phi));
00291     } else {
00292       logprint (LOG_STATUS, "WARNING: adjust Tox, Nss or Vt0 to get a "
00293                 "valid threshold voltage\n");
00294       Vto = 0.0;
00295     }
00296   }
00297 
00298   Cox = Cox * W * Leff;
00299 
00300   // calculate drain and source resistance if necessary
00301   nr_double_t Rsh = getPropertyDouble ("Rsh");
00302   nr_double_t Nrd = getPropertyDouble ("Nrd");
00303   nr_double_t Nrs = getPropertyDouble ("Nrs");
00304   Rd = getPropertyDouble ("Rd");
00305   Rs = getPropertyDouble ("Rs");
00306   if (Rsh > 0) {
00307     if (Nrd > 0) Rd += Rsh * Nrd;
00308     if (Nrs > 0) Rs += Rsh * Nrs;
00309   }
00310 
00311   // calculate zero-bias junction capacitance
00312   nr_double_t Cj  = getPropertyDouble ("Cj");
00313   nr_double_t Mj  = getPropertyDouble ("Mj");
00314   nr_double_t Mjs = getPropertyDouble ("Mjsw");
00315   nr_double_t Pb  = getPropertyDouble ("Pb");
00316   nr_double_t PbT, F2, F3;
00317   PbT = pnPotential_T (T1,T2, Pb);
00318   F2  = pnCapacitance_F (T1, T2, Mj, PbT / Pb);
00319   F3  = pnCapacitance_F (T1, T2, Mjs, PbT / Pb);
00320   Pb  = PbT;
00321   setScaledProperty ("Pb", Pb);
00322   if (Cj <= 0) {
00323     if (Pb > 0 && Nsub >= 0) {
00324       Cj = qucs::sqrt (ESi * E0 * Q_e * Nsub * 1e6 / 2 / Pb);
00325     }
00326     else {
00327       logprint (LOG_STATUS, "WARNING: adjust Pb, Nsub or Cj to get a "
00328                 "valid square junction capacitance\n");
00329       Cj = 0.0;
00330     }
00331   }
00332   Cj = Cj * F2;
00333   setScaledProperty ("Cj", Cj);
00334 
00335   // calculate junction capacitances
00336   nr_double_t Cbd0 = getPropertyDouble ("Cbd");
00337   nr_double_t Cbs0 = getPropertyDouble ("Cbs");
00338   nr_double_t Ad   = getPropertyDouble ("Ad");
00339   nr_double_t As   = getPropertyDouble ("As");
00340   Cbd0 = Cbd0 * F2;
00341   if (Cbd0 <= 0) {
00342     Cbd0 = Cj * Ad;
00343   }
00344   setScaledProperty ("Cbd", Cbd0);
00345   Cbs0 = Cbs0 * F2;
00346   if (Cbs0 <= 0) {
00347     Cbs0 = Cj * As;
00348   }
00349   setScaledProperty ("Cbs", Cbs0);
00350 
00351   // calculate periphery junction capacitances
00352   nr_double_t Cjs = getPropertyDouble ("Cjsw");
00353   nr_double_t Pd  = getPropertyDouble ("Pd");
00354   nr_double_t Ps  = getPropertyDouble ("Ps");
00355   Cjs = Cjs * F3;
00356   setProperty ("Cbds", Cjs * Pd);
00357   setProperty ("Cbss", Cjs * Ps);
00358 
00359   // calculate junction capacitances and saturation currents
00360   nr_double_t Js  = getPropertyDouble ("Js");
00361   nr_double_t Is  = getPropertyDouble ("Is");
00362   nr_double_t F4, E1, E2;
00363   E1 = Egap (T1);
00364   E2 = Egap (T2);
00365   F4 = qucs::exp (- QoverkB / T2 * (T2 / T1 * E1 - E2));
00366   Is = Is * F4;
00367   Js = Js * F4;
00368   nr_double_t Isd = (Ad > 0) ? Js * Ad : Is;
00369   nr_double_t Iss = (As > 0) ? Js * As : Is;
00370   setProperty ("Isd", Isd);
00371   setProperty ("Iss", Iss);
00372 
00373 #if DEBUG
00374   logprint (LOG_STATUS, "NOTIFY: Cox=%g, Beta=%g Ga=%g, Phi=%g, Vto=%g\n",
00375             Cox, beta, Ga, Phi, Vto);
00376 #endif /* DEBUG */
00377 }
00378 
00379 void mosfet::calcDC (void) {
00380 
00381   // fetch device model parameters
00382   nr_double_t Isd = getPropertyDouble ("Isd");
00383   nr_double_t Iss = getPropertyDouble ("Iss");
00384   nr_double_t n   = getPropertyDouble ("N");
00385   nr_double_t l   = getPropertyDouble ("Lambda");
00386   nr_double_t T   = getPropertyDouble ("Temp");
00387 
00388   nr_double_t Ut, IeqBS, IeqBD, IeqDS, UbsCrit, UbdCrit, gtiny;
00389 
00390   T = celsius2kelvin (T);
00391   Ut = T * kBoverQ;
00392   Ugd = real (getV (NODE_G) - getV (NODE_D)) * pol;
00393   Ugs = real (getV (NODE_G) - getV (NODE_S)) * pol;
00394   Ubs = real (getV (NODE_B) - getV (NODE_S)) * pol;
00395   Ubd = real (getV (NODE_B) - getV (NODE_D)) * pol;
00396   Uds = Ugs - Ugd;
00397 
00398   // critical voltage necessary for bad start values
00399   UbsCrit = pnCriticalVoltage (Iss, Ut * n);
00400   UbdCrit = pnCriticalVoltage (Isd, Ut * n);
00401 
00402   // for better convergence
00403   if (Uds >= 0) {
00404     Ugs = fetVoltage (Ugs, UgsPrev, Vto * pol);
00405     Uds = Ugs - Ugd;
00406     Uds = fetVoltageDS (Uds, UdsPrev);
00407     Ugd = Ugs - Uds;
00408   }
00409   else {
00410     Ugd = fetVoltage (Ugd, UgdPrev, Vto * pol);
00411     Uds = Ugs - Ugd;
00412     Uds = -fetVoltageDS (-Uds, -UdsPrev);
00413     Ugs = Ugd + Uds;
00414   }
00415   if (Uds >= 0) {
00416     Ubs = pnVoltage (Ubs, UbsPrev, Ut * n, UbsCrit);
00417     Ubd = Ubs - Uds;
00418   }
00419   else {
00420     Ubd = pnVoltage (Ubd, UbdPrev, Ut * n, UbdCrit);
00421     Ubs = Ubd + Uds;
00422   }
00423   UgsPrev = Ugs; UgdPrev = Ugd; UbdPrev = Ubd; UdsPrev = Uds; UbsPrev = Ubs;
00424 
00425   // parasitic bulk-source diode
00426   gtiny = Iss;
00427   pnJunctionMOS (Ubs, Iss, Ut * n, Ibs, gbs);
00428   Ibs += gtiny * Ubs;
00429   gbs += gtiny;
00430 
00431   // parasitic bulk-drain diode
00432   gtiny = Isd;
00433   pnJunctionMOS (Ubd, Isd, Ut * n, Ibd, gbd);
00434   Ibd += gtiny * Ubd;
00435   gbd += gtiny;
00436 
00437   // differentiate inverse and forward mode
00438   MOSdir = (Uds >= 0) ? +1 : -1;
00439 
00440   // first calculate sqrt (Upn - Phi)
00441   nr_double_t Upn = (MOSdir > 0) ? Ubs : Ubd;
00442   nr_double_t Sarg, Sphi = qucs::sqrt (Phi);
00443   if (Upn <= 0) {
00444     // take equation as is
00445     Sarg = qucs::sqrt (Phi - Upn);
00446   }
00447   else {
00448     // taylor series of "sqrt (x - 1)" -> continual at Ubs/Ubd = 0
00449     Sarg = Sphi - Upn / Sphi / 2;
00450     Sarg = std::max (Sarg, 0.0);
00451   }
00452 
00453   // calculate bias-dependent threshold voltage
00454   Uon = Vto * pol + Ga * (Sarg - Sphi);
00455   nr_double_t Utst = ((MOSdir > 0) ? Ugs : Ugd) - Uon;
00456   // no infinite backgate transconductance (if non-zero Ga)
00457   nr_double_t arg = (Sarg != 0.0) ? (Ga / Sarg / 2) : 0;
00458 
00459   // cutoff region
00460   if (Utst <= 0) {
00461     Ids = 0;
00462     gm  = 0;
00463     gds = 0;
00464     gmb = 0;
00465   }
00466   else {
00467     nr_double_t Vds = Uds * MOSdir;
00468     nr_double_t b   = beta * (1 + l * Vds);
00469     // saturation region
00470     if (Utst <= Vds) {
00471       Ids = b * Utst * Utst / 2;
00472       gm  = b * Utst;
00473       gds = l * beta * Utst * Utst / 2;
00474     }
00475     // linear region
00476     else {
00477       Ids = b * Vds * (Utst - Vds / 2);
00478       gm  = b * Vds;
00479       gds = b * (Utst - Vds) + l * beta * Vds * (Utst - Vds / 2);
00480     }
00481     gmb = gm * arg;
00482   }
00483   Udsat = pol * std::max (Utst, 0.0);
00484   Ids = MOSdir * Ids;
00485   Uon = pol * Uon;
00486 
00487   // compute autonomic current sources
00488   IeqBD = Ibd - gbd * Ubd;
00489   IeqBS = Ibs - gbs * Ubs;
00490 
00491   // exchange controlling nodes if necessary
00492   SourceControl = (MOSdir > 0) ? (gm + gmb) : 0;
00493   DrainControl  = (MOSdir < 0) ? (gm + gmb) : 0;
00494   if (MOSdir > 0) {
00495     IeqDS = Ids - gm * Ugs - gmb * Ubs - gds * Uds;
00496   } else {
00497     IeqDS = Ids - gm * Ugd - gmb * Ubd - gds * Uds;
00498   }
00499 
00500   setI (NODE_G, 0);
00501   setI (NODE_D, (+IeqBD - IeqDS) * pol);
00502   setI (NODE_S, (+IeqBS + IeqDS) * pol);
00503   setI (NODE_B, (-IeqBD - IeqBS) * pol);
00504 
00505   // apply admittance matrix elements
00506   setY (NODE_G, NODE_G, 0);
00507   setY (NODE_G, NODE_D, 0);
00508   setY (NODE_G, NODE_S, 0);
00509   setY (NODE_G, NODE_B, 0);
00510   setY (NODE_D, NODE_G, gm);
00511   setY (NODE_D, NODE_D, gds + gbd - DrainControl);
00512   setY (NODE_D, NODE_S, -gds - SourceControl);
00513   setY (NODE_D, NODE_B, gmb - gbd);
00514   setY (NODE_S, NODE_G, -gm);
00515   setY (NODE_S, NODE_D, -gds + DrainControl);
00516   setY (NODE_S, NODE_S, gbs + gds + SourceControl);
00517   setY (NODE_S, NODE_B, -gbs - gmb);
00518   setY (NODE_B, NODE_G, 0);
00519   setY (NODE_B, NODE_D, -gbd);
00520   setY (NODE_B, NODE_S, -gbs);
00521   setY (NODE_B, NODE_B, gbs + gbd);
00522 }
00523 
00524 /* Usual and additional state definitions. */
00525 
00526 #define qgdState  0 // gate-drain charge state
00527 #define igdState  1 // gate-drain current state
00528 #define vgdState  2 // gate-drain voltage state
00529 #define cgdState  3 // gate-drain capacitance state
00530 #define qgsState  4 // gate-source charge state
00531 #define igsState  5 // gate-source current state
00532 #define vgsState  6 // gate-source voltage state
00533 #define cgsState  7 // gate-source capacitance state
00534 #define qbdState  8 // bulk-drain charge state
00535 #define ibdState  9 // bulk-drain current state
00536 #define qbsState 10 // bulk-source charge state
00537 #define ibsState 11 // bulk-source current state
00538 #define qgbState 12 // gate-bulk charge state
00539 #define igbState 13 // gate-bulk current state
00540 #define vgbState 14 // gate-bulk voltage state
00541 #define cgbState 15 // gate-bulk capacitance state
00542 
00543 void mosfet::saveOperatingPoints (void) {
00544   nr_double_t Vgs, Vgd, Vbs, Vbd;
00545   Vgd = real (getV (NODE_G) - getV (NODE_D)) * pol;
00546   Vgs = real (getV (NODE_G) - getV (NODE_S)) * pol;
00547   Vbs = real (getV (NODE_B) - getV (NODE_S)) * pol;
00548   Vbd = real (getV (NODE_B) - getV (NODE_D)) * pol;
00549   setOperatingPoint ("Vgs", Vgs);
00550   setOperatingPoint ("Vgd", Vgd);
00551   setOperatingPoint ("Vbs", Vbs);
00552   setOperatingPoint ("Vbd", Vbd);
00553   setOperatingPoint ("Vds", Vgs - Vgd);
00554   setOperatingPoint ("Vgb", Vgs - Vbs);
00555 }
00556 
00557 void mosfet::loadOperatingPoints (void) {
00558   Ugs = getOperatingPoint ("Vgs");
00559   Ugd = getOperatingPoint ("Vgd");
00560   Ubs = getOperatingPoint ("Vbs");
00561   Ubd = getOperatingPoint ("Vbd");
00562   Uds = getOperatingPoint ("Vds");
00563   Ugb = getOperatingPoint ("Vgb");
00564 }
00565 
00566 void mosfet::calcOperatingPoints (void) {
00567 
00568   // fetch device model parameters
00569   nr_double_t Cbd0 = getScaledProperty ("Cbd");
00570   nr_double_t Cbs0 = getScaledProperty ("Cbs");
00571   nr_double_t Cbds = getPropertyDouble ("Cbds");
00572   nr_double_t Cbss = getPropertyDouble ("Cbss");
00573   nr_double_t Cgso = getPropertyDouble ("Cgso");
00574   nr_double_t Cgdo = getPropertyDouble ("Cgdo");
00575   nr_double_t Cgbo = getPropertyDouble ("Cgbo");
00576   nr_double_t Pb   = getScaledProperty ("Pb");
00577   nr_double_t M    = getPropertyDouble ("Mj");
00578   nr_double_t Ms   = getPropertyDouble ("Mjsw");
00579   nr_double_t Fc   = getPropertyDouble ("Fc");
00580   nr_double_t Tt   = getPropertyDouble ("Tt");
00581   nr_double_t W    = getPropertyDouble ("W");
00582 
00583   nr_double_t Cbs, Cbd, Cgd, Cgb, Cgs;
00584 
00585   // capacitance of bulk-drain diode
00586   Cbd = gbd * Tt + pnCapacitance (Ubd, Cbd0, Pb, M, Fc) +
00587     pnCapacitance (Ubd, Cbds, Pb, Ms, Fc);
00588   Qbd = Ibd * Tt + pnCharge (Ubd, Cbd0, Pb, M, Fc) +
00589     pnCharge (Ubd, Cbds, Pb, Ms, Fc);
00590 
00591   // capacitance of bulk-source diode
00592   Cbs = gbs * Tt + pnCapacitance (Ubs, Cbs0, Pb, M, Fc) +
00593     pnCapacitance (Ubs, Cbss, Pb, Ms, Fc);
00594   Qbs = Ibs * Tt + pnCharge (Ubs, Cbs0, Pb, M, Fc) +
00595     pnCharge (Ubs, Cbss, Pb, Ms, Fc);
00596 
00597   // calculate bias-dependent MOS overlap capacitances
00598   if (MOSdir > 0) {
00599     fetCapacitanceMeyer (Ugs, Ugd, Uon, Udsat, Phi, Cox, Cgs, Cgd, Cgb);
00600   } else {
00601     fetCapacitanceMeyer (Ugd, Ugs, Uon, Udsat, Phi, Cox, Cgd, Cgs, Cgb);
00602   }
00603 
00604   // charge approximation
00605   if (transientMode) {
00606     if (transientMode == 1) {      // by trapezoidal rule
00607       // gate-source charge
00608       Qgs = transientChargeTR (qgsState, Cgs, Ugs, Cgso * W);
00609       // gate-drain charge
00610       Qgd = transientChargeTR (qgdState, Cgd, Ugd, Cgdo * W);
00611       // gate-bulk charge
00612       Qgb = transientChargeTR (qgbState, Cgb, Ugb, Cgbo * Leff);
00613     }
00614     else if (transientMode == 2) { // by simpson's rule
00615       Qgs = transientChargeSR (qgsState, Cgs, Ugs, Cgso * W);
00616       Qgd = transientChargeSR (qgdState, Cgd, Ugd, Cgdo * W);
00617       Qgb = transientChargeSR (qgbState, Cgb, Ugb, Cgbo * Leff);
00618     }
00619   }
00620   // usual operating point
00621   else {
00622     Cgs += Cgso * W;
00623     Cgd += Cgdo * W;
00624     Cgb += Cgbo * Leff;
00625   }
00626 
00627   // save operating points
00628   setOperatingPoint ("Id", Ids);
00629   setOperatingPoint ("gm", gm);
00630   setOperatingPoint ("gmb", gmb);
00631   setOperatingPoint ("gds", gds);
00632   setOperatingPoint ("Vth", Vto);
00633   setOperatingPoint ("Vdsat", Udsat);
00634   setOperatingPoint ("gbs", gbs);
00635   setOperatingPoint ("gbd", gbd);
00636   setOperatingPoint ("Cbd", Cbd);
00637   setOperatingPoint ("Cbs", Cbs);
00638   setOperatingPoint ("Cgs", Cgs);
00639   setOperatingPoint ("Cgd", Cgd);
00640   setOperatingPoint ("Cgb", Cgb);
00641 }
00642 
00643 void mosfet::initAC (void) {
00644   allocMatrixMNA ();
00645 }
00646 
00647 void mosfet::calcAC (nr_double_t frequency) {
00648   setMatrixY (calcMatrixY (frequency));
00649 }
00650 
00651 void mosfet::calcNoiseAC (nr_double_t frequency) {
00652   setMatrixN (calcMatrixCy (frequency));
00653 }
00654 
00655 void mosfet::initTR (void) {
00656   setStates (16);
00657   initDC ();
00658 }
00659 
00660 void mosfet::calcTR (nr_double_t) {
00661   calcDC ();
00662   transientMode = getPropertyInteger ("capModel");
00663   saveOperatingPoints ();
00664   loadOperatingPoints ();
00665   calcOperatingPoints ();
00666   transientMode = 0;
00667 
00668   nr_double_t Cgd = getOperatingPoint ("Cgd");
00669   nr_double_t Cgs = getOperatingPoint ("Cgs");
00670   nr_double_t Cbd = getOperatingPoint ("Cbd");
00671   nr_double_t Cbs = getOperatingPoint ("Cbs");
00672   nr_double_t Cgb = getOperatingPoint ("Cgb");
00673 
00674   Uds = Ugs - Ugd;
00675   Ugb = Ugs - Ubs;
00676 
00677   transientCapacitance (qbdState, NODE_B, NODE_D, Cbd, Ubd, Qbd);
00678   transientCapacitance (qbsState, NODE_B, NODE_S, Cbs, Ubs, Qbs);
00679 
00680   // handle Meyer charges and capacitances
00681   transientCapacitance (qgdState, NODE_G, NODE_D, Cgd, Ugd, Qgd);
00682   transientCapacitance (qgsState, NODE_G, NODE_S, Cgs, Ugs, Qgs);
00683   transientCapacitance (qgbState, NODE_G, NODE_B, Cgb, Ugb, Qgb);
00684 }
00685 
00686 /* The function uses the trapezoidal rule to compute the current
00687    capacitance and charge.  The approximation is necessary because the
00688    Meyer model is a capacitance model and not a charge model. */
00689 nr_double_t mosfet::transientChargeTR (int qstate, nr_double_t& cap,
00690                                        nr_double_t voltage, nr_double_t ccap) {
00691   int vstate = qstate + 2, cstate = qstate + 3;
00692   setState (cstate, cap);
00693   cap = (cap + getState (cstate, 1)) / 2 + ccap;
00694   setState (vstate, voltage);
00695   return cap * (voltage - getState (vstate, 1)) + getState (qstate, 1);
00696 }
00697 
00698 /* The function uses Simpson's numerical integration rule to compute
00699    the current capacitance and charge. */
00700 nr_double_t mosfet::transientChargeSR (int qstate, nr_double_t& cap,
00701                                        nr_double_t voltage, nr_double_t ccap) {
00702   int vstate = qstate + 2, cstate = qstate + 3;
00703   setState (cstate, cap);
00704   cap = (cap + 4 * getState (cstate, 1) + getState (cstate, 2)) / 6 + ccap;
00705   setState (vstate, voltage);
00706   return cap * (voltage - getState (vstate, 1)) + getState (qstate, 1);
00707 }
00708 
00709 // properties
00710 PROP_REQ [] = {
00711   { "Is", PROP_REAL, { 1e-14, PROP_NO_STR }, PROP_POS_RANGE },
00712   { "N", PROP_REAL, { 1, PROP_NO_STR }, PROP_RNGII (0.1, 100) },
00713   { "Vt0", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
00714   { "Lambda", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
00715   { "Kp", PROP_REAL, { 2e-5, PROP_NO_STR }, PROP_POS_RANGE },
00716   { "Gamma", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
00717   { "Phi", PROP_REAL, { 0.6, PROP_NO_STR }, PROP_POS_RANGE },
00718   PROP_NO_PROP };
00719 PROP_OPT [] = {
00720   { "Rd", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
00721   { "Rs", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
00722   { "Rg", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
00723   { "L", PROP_REAL, { 100e-6, PROP_NO_STR }, PROP_RNG_X01I },
00724   { "Ld", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
00725   { "W", PROP_REAL, { 100e-6, PROP_NO_STR }, PROP_POS_RANGEX },
00726   { "Tox", PROP_REAL, { 1e-7, PROP_NO_STR }, PROP_RNG_X01I },
00727   { "Cgso", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
00728   { "Cgdo", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
00729   { "Cgbo", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
00730   { "Cbd", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
00731   { "Cbs", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
00732   { "Pb", PROP_REAL, { 0.8, PROP_NO_STR }, PROP_RNGXI (0, 10) },
00733   { "Mj", PROP_REAL, { 0.5, PROP_NO_STR }, PROP_RNGII (0, 1) },
00734   { "Fc", PROP_REAL, { 0.5, PROP_NO_STR }, PROP_RNGIX (0, 1) },
00735   { "Cjsw", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
00736   { "Mjsw", PROP_REAL, { 0.33, PROP_NO_STR }, PROP_RNGII (0, 1) },
00737   { "Tt", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
00738   { "Kf", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
00739   { "Af", PROP_REAL, { 1, PROP_NO_STR }, PROP_POS_RANGE },
00740   { "Ffe", PROP_REAL, { 1, PROP_NO_STR }, PROP_POS_RANGE },
00741   { "Nsub", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
00742   { "Nss", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
00743   { "Tpg", PROP_INT, { 1, PROP_NO_STR }, PROP_RNGII (-1 , 1) },
00744   { "Uo", PROP_REAL, { 600, PROP_NO_STR }, PROP_POS_RANGE },
00745   { "Rsh", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
00746   { "Nrd", PROP_REAL, { 1, PROP_NO_STR }, PROP_POS_RANGE },
00747   { "Nrs", PROP_REAL, { 1, PROP_NO_STR }, PROP_POS_RANGE },
00748   { "Cj", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
00749   { "Js", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
00750   { "Ad", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
00751   { "As", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
00752   { "Pd", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
00753   { "Ps", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
00754   { "Temp", PROP_REAL, { 26.85, PROP_NO_STR }, PROP_MIN_VAL (K) },
00755   { "Tnom", PROP_REAL, { 26.85, PROP_NO_STR }, PROP_MIN_VAL (K) },
00756   { "Type", PROP_STR, { PROP_NO_VAL, "nfet" }, PROP_RNG_FET },
00757   { "capModel", PROP_INT, { 2, PROP_NO_STR }, PROP_RNGII (1 , 2) },
00758   PROP_NO_PROP };
00759 struct define_t mosfet::cirdef =
00760   { "MOSFET", 4, PROP_COMPONENT, PROP_NO_SUBSTRATE, PROP_NONLINEAR, PROP_DEF };