Qucs-core
0.0.19
|
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 };