Qucs-core  0.0.19
jfet.cpp
Go to the documentation of this file.
00001 /*
00002  * jfet.cpp - jfet 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 "jfet.h"
00032 
00033 #define NODE_G 0 /* gate node   */
00034 #define NODE_D 1 /* drain node  */
00035 #define NODE_S 2 /* source node */
00036 
00037 using namespace qucs;
00038 using namespace qucs::device;
00039 
00040 jfet::jfet () : circuit (3) {
00041   rs = rd = NULL;
00042   type = CIR_JFET;
00043 }
00044 
00045 void jfet::calcSP (nr_double_t frequency) {
00046   setMatrixS (ytos (calcMatrixY (frequency)));
00047 }
00048 
00049 matrix jfet::calcMatrixY (nr_double_t frequency) {
00050 
00051   // fetch computed operating points
00052   nr_double_t Cgd = getOperatingPoint ("Cgd");
00053   nr_double_t Cgs = getOperatingPoint ("Cgs");
00054   nr_double_t ggs = getOperatingPoint ("ggs");
00055   nr_double_t ggd = getOperatingPoint ("ggd");
00056   nr_double_t gds = getOperatingPoint ("gds");
00057   nr_double_t gm  = getOperatingPoint ("gm");
00058 
00059   // compute the models admittances
00060   nr_complex_t Ygd = nr_complex_t (ggd, 2.0 * pi * frequency * Cgd);
00061   nr_complex_t Ygs = nr_complex_t (ggs, 2.0 * pi * frequency * Cgs);
00062   nr_complex_t Yds = gds;
00063 
00064   // build admittance matrix and convert it to S-parameter matrix
00065   matrix y (3);
00066   y.set (NODE_G, NODE_G, Ygd + Ygs);
00067   y.set (NODE_G, NODE_D, -Ygd);
00068   y.set (NODE_G, NODE_S, -Ygs);
00069   y.set (NODE_D, NODE_G, gm - Ygd);
00070   y.set (NODE_D, NODE_D, Ygd + Yds);
00071   y.set (NODE_D, NODE_S, -Yds - gm);
00072   y.set (NODE_S, NODE_G, -Ygs - gm);
00073   y.set (NODE_S, NODE_D, -Yds);
00074   y.set (NODE_S, NODE_S, Ygs + Yds + gm);
00075   return y;
00076 }
00077 
00078 void jfet::calcNoiseSP (nr_double_t frequency) {
00079   setMatrixN (cytocs (calcMatrixCy (frequency) * z0, getMatrixS ()));
00080 }
00081 
00082 matrix jfet::calcMatrixCy (nr_double_t frequency) {
00083   /* get operating points and noise properties */
00084   nr_double_t Kf  = getPropertyDouble ("Kf");
00085   nr_double_t Af  = getPropertyDouble ("Af");
00086   nr_double_t Ffe = getPropertyDouble ("Ffe");
00087   nr_double_t gm  = fabs (getOperatingPoint ("gm"));
00088   nr_double_t Ids = fabs (getOperatingPoint ("Id"));
00089   nr_double_t T   = getPropertyDouble ("Temp");
00090 
00091   /* compute channel noise and flicker noise generated by the DC
00092      transconductance and current flow from drain to source */
00093   nr_double_t i = 8 * celsius2kelvin (T) / T0 * gm / 3 +
00094     Kf * qucs::pow (Ids, Af) / qucs::pow (frequency, Ffe) / kB / T0;
00095 
00096   /* build noise current correlation matrix and convert it to
00097      noise-wave correlation matrix */
00098   matrix cy = matrix (3);
00099   cy.set (NODE_D, NODE_D, +i);
00100   cy.set (NODE_S, NODE_S, +i);
00101   cy.set (NODE_D, NODE_S, -i);
00102   cy.set (NODE_S, NODE_D, -i);
00103   return cy;
00104 }
00105 
00106 void jfet::initModel (void) {
00107   // fetch necessary device properties
00108   nr_double_t T  = getPropertyDouble ("Temp");
00109   nr_double_t Tn = getPropertyDouble ("Tnom");
00110   nr_double_t A  = getPropertyDouble ("Area");
00111 
00112   // compute Is temperature and area dependency
00113   nr_double_t Is  = getPropertyDouble ("Is");
00114   nr_double_t N   = getPropertyDouble ("N");
00115   nr_double_t Xti = getPropertyDouble ("Xti");
00116   nr_double_t T1, T2, Eg;
00117   T2 = celsius2kelvin (T);
00118   T1 = celsius2kelvin (Tn);
00119   Eg = Egap (300);
00120   Is = pnCurrent_T (T1, T2, Is, Eg, N, Xti);
00121   setScaledProperty ("Is", Is * A);
00122 
00123   // compute Isr temperature and area dependency
00124   nr_double_t Isr = getPropertyDouble ("Isr");
00125   nr_double_t Nr  = getPropertyDouble ("Nr");
00126   Isr = pnCurrent_T (T1, T2, Isr, Eg, Nr, Xti);
00127   setScaledProperty ("Isr", Isr * A);
00128 
00129   // compute Pb temperature dependency
00130   nr_double_t Pb = getPropertyDouble ("Pb");
00131   nr_double_t PbT;
00132   PbT = pnPotential_T (T1,T2, Pb);
00133   setScaledProperty ("Pb", PbT);
00134 
00135   // compute Cgs and Cgd temperature and area dependency
00136   nr_double_t Cgs = getPropertyDouble ("Cgs");
00137   nr_double_t Cgd = getPropertyDouble ("Cgd");
00138   nr_double_t M   = getPropertyDouble ("M");
00139   nr_double_t F;
00140   F = A * pnCapacitance_F (T1, T2, M, PbT / Pb);
00141   setScaledProperty ("Cgs", Cgs * F);
00142   setScaledProperty ("Cgd", Cgd * F);
00143 
00144   // compute Vth temperature dependency
00145   nr_double_t Vt0   = getPropertyDouble ("Vt0");
00146   nr_double_t Vt0tc = getPropertyDouble ("Vt0tc");
00147   nr_double_t DT    = T2 - T1;
00148   Vt0 = Vt0 + Vt0tc * DT;
00149   setScaledProperty ("Vt0", Vt0);
00150 
00151   // compute Beta temperature and area dependency
00152   nr_double_t Beta    = getPropertyDouble ("Beta");
00153   nr_double_t Betatce = getPropertyDouble ("Betatce");
00154   Beta = Beta * qucs::exp (Betatce * DT * qucs::log (1.01));
00155   setScaledProperty ("Beta", Beta * A);
00156 
00157   // compute Rs and Rd area dependency
00158   nr_double_t Rs = getPropertyDouble ("Rs");
00159   nr_double_t Rd = getPropertyDouble ("Rd");
00160   setScaledProperty ("Rs", Rs / A);
00161   setScaledProperty ("Rd", Rd / A);
00162 }
00163 
00164 void jfet::restartDC (void) {
00165   // apply starting values to previous iteration values
00166   UgdPrev = real (getV (NODE_G) - getV (NODE_D));
00167   UgsPrev = real (getV (NODE_G) - getV (NODE_S));
00168 }
00169 
00170 void jfet::initDC (void) {
00171 
00172   // allocate MNA matrices
00173   allocMatrixMNA ();
00174 
00175   // initialize scalability
00176   initModel ();
00177 
00178   // initialize starting values
00179   restartDC ();
00180 
00181   // apply polarity of JFET
00182   const char * const type = getPropertyString ("Type");
00183   pol = !strcmp (type, "pfet") ? -1 : 1;
00184 
00185   // get device temperature
00186   nr_double_t T = getPropertyDouble ("Temp");
00187 
00188   // possibly insert series resistance at source
00189   nr_double_t Rs = getScaledProperty ("Rs");
00190   if (Rs != 0.0) {
00191     // create additional circuit if necessary and reassign nodes
00192     rs = splitResistor (this, rs, "Rs", "source", NODE_S);
00193     rs->setProperty ("Temp", T);
00194     rs->setProperty ("R", Rs);
00195     rs->setProperty ("Controlled", getName ());
00196     rs->initDC ();
00197   }
00198   // no series resistance at source
00199   else {
00200     disableResistor (this, rs, NODE_S);
00201   }
00202 
00203   // possibly insert series resistance at drain
00204   nr_double_t Rd = getScaledProperty ("Rd");
00205   if (Rd != 0.0) {
00206     // create additional circuit if necessary and reassign nodes
00207     rd = splitResistor (this, rd, "Rd", "drain", NODE_D);
00208     rd->setProperty ("Temp", T);
00209     rd->setProperty ("R", Rd);
00210     rd->setProperty ("Controlled", getName ());
00211     rd->initDC ();
00212   }
00213   // no series resistance at drain
00214   else {
00215     disableResistor (this, rd, NODE_D);
00216   }
00217 }
00218 
00219 void jfet::calcDC (void) {
00220 
00221   // fetch device model parameters
00222   nr_double_t Is   = getScaledProperty ("Is");
00223   nr_double_t n    = getPropertyDouble ("N");
00224   nr_double_t Isr  = getScaledProperty ("Isr");
00225   nr_double_t nr   = getPropertyDouble ("Nr");
00226   nr_double_t Vt0  = getScaledProperty ("Vt0");
00227   nr_double_t l    = getPropertyDouble ("Lambda");
00228   nr_double_t beta = getScaledProperty ("Beta");
00229   nr_double_t T    = getPropertyDouble ("Temp");
00230 
00231   nr_double_t Ut, IeqG, IeqD, IeqS, UgsCrit, UgdCrit;
00232   nr_double_t Igs, Igd, gtiny;
00233 
00234   T = celsius2kelvin (T);
00235   Ut = T * kBoverQ;
00236   Ugd = real (getV (NODE_G) - getV (NODE_D)) * pol;
00237   Ugs = real (getV (NODE_G) - getV (NODE_S)) * pol;
00238 
00239   // critical voltage necessary for bad start values
00240   UgsCrit = pnCriticalVoltage (Is, Ut * n);
00241   UgdCrit = pnCriticalVoltage (Is, Ut * n);
00242   UgsPrev = Ugs = pnVoltage (Ugs, UgsPrev, Ut * n, UgsCrit);
00243   UgdPrev = Ugd = pnVoltage (Ugd, UgdPrev, Ut * n, UgdCrit);
00244 
00245   Uds = Ugs - Ugd;
00246 
00247   // gate-source diode
00248   gtiny = Ugs < - 10 * Ut * n ? (Is + Isr) : 0;
00249   ggs = pnConductance (Ugs, Is, Ut * n) +
00250     pnConductance (Ugs, Isr, Ut * nr) + gtiny;
00251   Igs = pnCurrent (Ugs, Is, Ut * n) +
00252     pnCurrent (Ugs, Isr, Ut * nr) + gtiny * Ugs;
00253 
00254   // gate-drain diode
00255   gtiny = Ugd < - 10 * Ut * n ? (Is + Isr) : 0;
00256   ggd = pnConductance (Ugd, Is, Ut * n) +
00257     pnConductance (Ugd, Isr, Ut * nr) + gtiny;
00258   Igd = pnCurrent (Ugd, Is, Ut * n) +
00259     pnCurrent (Ugd, Isr, Ut * nr) + gtiny * Ugd;
00260 
00261   // normal (forward) mode of operation
00262   if (Uds >= 0) {
00263     nr_double_t Ugst = Ugs - Vt0;
00264     // normal mode, cutoff region
00265     if (Ugst <= 0) {
00266       Ids = 0;
00267       gm  = 0;
00268       gds = 0;
00269     }
00270     else {
00271       nr_double_t b = beta * (1 + l * Uds);
00272       // normal mode, saturation region
00273       if (Ugst <= Uds) {
00274         Ids = b * Ugst * Ugst;
00275         gm  = b * 2 * Ugst;
00276         gds = l * beta * Ugst * Ugst;
00277       }
00278       // normal mode, linear region
00279       else {
00280         Ids = b * Uds * (2 * Ugst - Uds);
00281         gm  = b * 2 * Uds;
00282         gds = b * 2 * (Ugst - Uds) + l * beta * Uds * (2 * Ugst - Uds);
00283       }
00284     }
00285   }
00286   // inverse (backward) mode of operation
00287   else {
00288     nr_double_t Ugdt = Ugd - Vt0;
00289     // inverse mode, cutoff region
00290     if (Ugdt <= 0) {
00291       Ids = 0;
00292       gm  = 0;
00293       gds = 0;
00294     }
00295     else {
00296       nr_double_t b = beta * (1 - l * Uds);
00297       // inverse mode, saturation region
00298       if (Ugdt <= -Uds) {
00299         Ids = - b * Ugdt * Ugdt;
00300         gm  = - b * 2 * Ugdt;
00301         gds = beta * l * Ugdt * Ugdt + b * 2 * Ugdt;
00302       }
00303       // inverse mode, linear region
00304       else {
00305         Ids = b * Uds * (2 * Ugdt + Uds);
00306         gm  = b * 2 * Uds;
00307         gds = 2 * b * Ugdt - beta * l * Uds * (2 * Ugdt + Uds);
00308       }
00309     }
00310   }
00311 
00312   // compute autonomic current sources
00313   IeqG = Igs - ggs * Ugs;
00314   IeqD = Igd - ggd * Ugd;
00315   IeqS = Ids - gm * Ugs - gds * Uds;
00316   setI (NODE_G, (-IeqG - IeqD) * pol);
00317   setI (NODE_D, (+IeqD - IeqS) * pol);
00318   setI (NODE_S, (+IeqG + IeqS) * pol);
00319 
00320   // apply admittance matrix elements
00321   setY (NODE_G, NODE_G, ggs + ggd);
00322   setY (NODE_G, NODE_D, -ggd);
00323   setY (NODE_G, NODE_S, -ggs);
00324   setY (NODE_D, NODE_G, -ggd + gm);
00325   setY (NODE_D, NODE_D, gds + ggd);
00326   setY (NODE_D, NODE_S, -gm - gds);
00327   setY (NODE_S, NODE_G, -ggs - gm);
00328   setY (NODE_S, NODE_D, -gds);
00329   setY (NODE_S, NODE_S, ggs + gds + gm);
00330 }
00331 
00332 void jfet::loadOperatingPoints (void) {
00333   Ugs = getOperatingPoint ("Vgs");
00334   Ugd = getOperatingPoint ("Vgd");
00335   Uds = getOperatingPoint ("Vds");
00336 }
00337 
00338 void jfet::saveOperatingPoints (void) {
00339   nr_double_t Vgs, Vgd;
00340   Vgd = real (getV (NODE_G) - getV (NODE_D)) * pol;
00341   Vgs = real (getV (NODE_G) - getV (NODE_S)) * pol;
00342   setOperatingPoint ("Vgs", Vgs);
00343   setOperatingPoint ("Vgd", Vgd);
00344   setOperatingPoint ("Vds", Vgs - Vgd);
00345 }
00346 
00347 void jfet::calcOperatingPoints (void) {
00348 
00349   // fetch device model parameters
00350   nr_double_t z    = getPropertyDouble ("M");
00351   nr_double_t Cgd0 = getScaledProperty ("Cgd");
00352   nr_double_t Cgs0 = getScaledProperty ("Cgs");
00353   nr_double_t Pb   = getScaledProperty ("Pb");
00354   nr_double_t Fc   = getPropertyDouble ("Fc");
00355 
00356   nr_double_t Cgs, Cgd;
00357 
00358   // capacitance of gate-drain diode
00359   Cgd = pnCapacitance (Ugd, Cgd0, Pb, z, Fc);
00360   Qgd = pnCharge (Ugd, Cgd0, Pb, z, Fc);
00361 
00362   // capacitance of gate-source diode
00363   Cgs = pnCapacitance (Ugs, Cgs0, Pb, z, Fc);
00364   Qgs = pnCharge (Ugs, Cgs0, Pb, z, Fc);
00365 
00366   // save operating points
00367   setOperatingPoint ("ggs", ggs);
00368   setOperatingPoint ("ggd", ggd);
00369   setOperatingPoint ("gds", gds);
00370   setOperatingPoint ("gm", gm);
00371   setOperatingPoint ("Id", Ids);
00372   setOperatingPoint ("Cgd", Cgd);
00373   setOperatingPoint ("Cgs", Cgs);
00374 }
00375 
00376 void jfet::initAC (void) {
00377   allocMatrixMNA ();
00378   clearI ();
00379 }
00380 
00381 void jfet::calcAC (nr_double_t frequency) {
00382   setMatrixY (calcMatrixY (frequency));
00383 }
00384 
00385 void jfet::calcNoiseAC (nr_double_t frequency) {
00386   setMatrixN (calcMatrixCy (frequency));
00387 }
00388 
00389 #define qgdState 0 // gate-drain charge state
00390 #define cgdState 1 // gate-drain current state
00391 #define qgsState 2 // gate-source charge state
00392 #define cgsState 3 // gate-source current state
00393 
00394 void jfet::initTR (void) {
00395   setStates (4);
00396   initDC ();
00397 }
00398 
00399 void jfet::calcTR (nr_double_t) {
00400   calcDC ();
00401   saveOperatingPoints ();
00402   loadOperatingPoints ();
00403   calcOperatingPoints ();
00404 
00405   nr_double_t Cgs = getOperatingPoint ("Cgs");
00406   nr_double_t Cgd = getOperatingPoint ("Cgd");
00407 
00408   transientCapacitance (qgsState, NODE_G, NODE_S, Cgs, Ugs, Qgs);
00409   transientCapacitance (qgdState, NODE_G, NODE_D, Cgd, Ugd, Qgd);
00410 }
00411 
00412 // properties
00413 PROP_REQ [] = {
00414   { "Is", PROP_REAL, { 1e-14, PROP_NO_STR }, PROP_POS_RANGE },
00415   { "N", PROP_REAL, { 1, PROP_NO_STR }, PROP_RNGII (1, 100) },
00416   { "Vt0", PROP_REAL, { -2, PROP_NO_STR }, PROP_NEG_RANGE },
00417   { "Lambda", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
00418   { "Beta", PROP_REAL, { 1e-4, PROP_NO_STR }, PROP_POS_RANGE },
00419   { "M", PROP_REAL, { 0.5, PROP_NO_STR }, PROP_RNGII (0, 1) },
00420   { "Pb", PROP_REAL, { 1.0, PROP_NO_STR }, PROP_RNGXI (0, 10) },
00421   { "Fc", PROP_REAL, { 0.5, PROP_NO_STR }, PROP_RNGIX (0, 1) },
00422   { "Cgs", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
00423   { "Cgd", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
00424   PROP_NO_PROP };
00425 PROP_OPT [] = {
00426   { "Rd", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
00427   { "Rs", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
00428   { "Isr", PROP_REAL, { 1e-14, PROP_NO_STR }, PROP_POS_RANGE },
00429   { "Nr", PROP_REAL, { 2, PROP_NO_STR }, PROP_RNGII (1, 100) },
00430   { "Kf", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
00431   { "Af", PROP_REAL, { 1, PROP_NO_STR }, PROP_POS_RANGE },
00432   { "Ffe", PROP_REAL, { 1, PROP_NO_STR }, PROP_POS_RANGE },
00433   { "Temp", PROP_REAL, { 26.85, PROP_NO_STR }, PROP_MIN_VAL (K) },
00434   { "Type", PROP_STR, { PROP_NO_VAL, "nfet" }, PROP_RNG_FET },
00435   { "Xti", PROP_REAL, { 3, PROP_NO_STR }, PROP_POS_RANGE },
00436   { "Vt0tc", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
00437   { "Betatce", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
00438   { "Tnom", PROP_REAL, { 26.85, PROP_NO_STR }, PROP_MIN_VAL (K) },
00439   { "Area", PROP_REAL, { 1, PROP_NO_STR }, PROP_POS_RANGEX },
00440   PROP_NO_PROP };
00441 struct define_t jfet::cirdef =
00442   { "JFET", 3, PROP_COMPONENT, PROP_NO_SUBSTRATE, PROP_NONLINEAR, PROP_DEF };