/* * jfet.cpp - jfet class implementation * * Copyright (C) 2004, 2005, 2006, 2008 Stefan Jahn * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, * Boston, MA 02110-1301, USA. * * $Id$ * */ #if HAVE_CONFIG_H # include #endif #include "component.h" #include "device.h" #include "jfet.h" #define NODE_G 0 /* gate node */ #define NODE_D 1 /* drain node */ #define NODE_S 2 /* source node */ using namespace qucs; using namespace qucs::device; jfet::jfet () : circuit (3) { rs = rd = NULL; type = CIR_JFET; } void jfet::calcSP (nr_double_t frequency) { setMatrixS (ytos (calcMatrixY (frequency))); } matrix jfet::calcMatrixY (nr_double_t frequency) { // fetch computed operating points nr_double_t Cgd = getOperatingPoint ("Cgd"); nr_double_t Cgs = getOperatingPoint ("Cgs"); nr_double_t ggs = getOperatingPoint ("ggs"); nr_double_t ggd = getOperatingPoint ("ggd"); nr_double_t gds = getOperatingPoint ("gds"); nr_double_t gm = getOperatingPoint ("gm"); // compute the models admittances nr_complex_t Ygd = nr_complex_t (ggd, 2.0 * pi * frequency * Cgd); nr_complex_t Ygs = nr_complex_t (ggs, 2.0 * pi * frequency * Cgs); nr_complex_t Yds = gds; // build admittance matrix and convert it to S-parameter matrix matrix y (3); y.set (NODE_G, NODE_G, Ygd + Ygs); y.set (NODE_G, NODE_D, -Ygd); y.set (NODE_G, NODE_S, -Ygs); y.set (NODE_D, NODE_G, gm - Ygd); y.set (NODE_D, NODE_D, Ygd + Yds); y.set (NODE_D, NODE_S, -Yds - gm); y.set (NODE_S, NODE_G, -Ygs - gm); y.set (NODE_S, NODE_D, -Yds); y.set (NODE_S, NODE_S, Ygs + Yds + gm); return y; } void jfet::calcNoiseSP (nr_double_t frequency) { setMatrixN (cytocs (calcMatrixCy (frequency) * z0, getMatrixS ())); } matrix jfet::calcMatrixCy (nr_double_t frequency) { /* get operating points and noise properties */ nr_double_t Kf = getPropertyDouble ("Kf"); nr_double_t Af = getPropertyDouble ("Af"); nr_double_t Ffe = getPropertyDouble ("Ffe"); nr_double_t gm = fabs (getOperatingPoint ("gm")); nr_double_t Ids = fabs (getOperatingPoint ("Id")); nr_double_t T = getPropertyDouble ("Temp"); /* compute channel noise and flicker noise generated by the DC transconductance and current flow from drain to source */ nr_double_t i = 8 * celsius2kelvin (T) / T0 * gm / 3 + Kf * qucs::pow (Ids, Af) / qucs::pow (frequency, Ffe) / kB / T0; /* build noise current correlation matrix and convert it to noise-wave correlation matrix */ matrix cy = matrix (3); cy.set (NODE_D, NODE_D, +i); cy.set (NODE_S, NODE_S, +i); cy.set (NODE_D, NODE_S, -i); cy.set (NODE_S, NODE_D, -i); return cy; } void jfet::initModel (void) { // fetch necessary device properties nr_double_t T = getPropertyDouble ("Temp"); nr_double_t Tn = getPropertyDouble ("Tnom"); nr_double_t A = getPropertyDouble ("Area"); // compute Is temperature and area dependency nr_double_t Is = getPropertyDouble ("Is"); nr_double_t N = getPropertyDouble ("N"); nr_double_t Xti = getPropertyDouble ("Xti"); nr_double_t T1, T2, Eg; T2 = celsius2kelvin (T); T1 = celsius2kelvin (Tn); Eg = Egap (300); Is = pnCurrent_T (T1, T2, Is, Eg, N, Xti); setScaledProperty ("Is", Is * A); // compute Isr temperature and area dependency nr_double_t Isr = getPropertyDouble ("Isr"); nr_double_t Nr = getPropertyDouble ("Nr"); Isr = pnCurrent_T (T1, T2, Isr, Eg, Nr, Xti); setScaledProperty ("Isr", Isr * A); // compute Pb temperature dependency nr_double_t Pb = getPropertyDouble ("Pb"); nr_double_t PbT; PbT = pnPotential_T (T1,T2, Pb); setScaledProperty ("Pb", PbT); // compute Cgs and Cgd temperature and area dependency nr_double_t Cgs = getPropertyDouble ("Cgs"); nr_double_t Cgd = getPropertyDouble ("Cgd"); nr_double_t M = getPropertyDouble ("M"); nr_double_t F; F = A * pnCapacitance_F (T1, T2, M, PbT / Pb); setScaledProperty ("Cgs", Cgs * F); setScaledProperty ("Cgd", Cgd * F); // compute Vth temperature dependency nr_double_t Vt0 = getPropertyDouble ("Vt0"); nr_double_t Vt0tc = getPropertyDouble ("Vt0tc"); nr_double_t DT = T2 - T1; Vt0 = Vt0 + Vt0tc * DT; setScaledProperty ("Vt0", Vt0); // compute Beta temperature and area dependency nr_double_t Beta = getPropertyDouble ("Beta"); nr_double_t Betatce = getPropertyDouble ("Betatce"); Beta = Beta * qucs::exp (Betatce * DT * qucs::log (1.01)); setScaledProperty ("Beta", Beta * A); // compute Rs and Rd area dependency nr_double_t Rs = getPropertyDouble ("Rs"); nr_double_t Rd = getPropertyDouble ("Rd"); setScaledProperty ("Rs", Rs / A); setScaledProperty ("Rd", Rd / A); } void jfet::restartDC (void) { // apply starting values to previous iteration values UgdPrev = real (getV (NODE_G) - getV (NODE_D)); UgsPrev = real (getV (NODE_G) - getV (NODE_S)); } void jfet::initDC (void) { // allocate MNA matrices allocMatrixMNA (); // initialize scalability initModel (); // initialize starting values restartDC (); // apply polarity of JFET const char * const type = getPropertyString ("Type"); pol = !strcmp (type, "pfet") ? -1 : 1; // get device temperature nr_double_t T = getPropertyDouble ("Temp"); // possibly insert series resistance at source nr_double_t Rs = getScaledProperty ("Rs"); if (Rs != 0.0) { // create additional circuit if necessary and reassign nodes rs = splitResistor (this, rs, "Rs", "source", NODE_S); rs->setProperty ("Temp", T); rs->setProperty ("R", Rs); rs->setProperty ("Controlled", getName ()); rs->initDC (); } // no series resistance at source else { disableResistor (this, rs, NODE_S); } // possibly insert series resistance at drain nr_double_t Rd = getScaledProperty ("Rd"); if (Rd != 0.0) { // create additional circuit if necessary and reassign nodes rd = splitResistor (this, rd, "Rd", "drain", NODE_D); rd->setProperty ("Temp", T); rd->setProperty ("R", Rd); rd->setProperty ("Controlled", getName ()); rd->initDC (); } // no series resistance at drain else { disableResistor (this, rd, NODE_D); } } void jfet::calcDC (void) { // fetch device model parameters nr_double_t Is = getScaledProperty ("Is"); nr_double_t n = getPropertyDouble ("N"); nr_double_t Isr = getScaledProperty ("Isr"); nr_double_t nr = getPropertyDouble ("Nr"); nr_double_t Vt0 = getScaledProperty ("Vt0"); nr_double_t l = getPropertyDouble ("Lambda"); nr_double_t beta = getScaledProperty ("Beta"); nr_double_t T = getPropertyDouble ("Temp"); nr_double_t Ut, IeqG, IeqD, IeqS, UgsCrit, UgdCrit; nr_double_t Igs, Igd, gtiny; T = celsius2kelvin (T); Ut = T * kBoverQ; Ugd = real (getV (NODE_G) - getV (NODE_D)) * pol; Ugs = real (getV (NODE_G) - getV (NODE_S)) * pol; // critical voltage necessary for bad start values UgsCrit = pnCriticalVoltage (Is, Ut * n); UgdCrit = pnCriticalVoltage (Is, Ut * n); UgsPrev = Ugs = pnVoltage (Ugs, UgsPrev, Ut * n, UgsCrit); UgdPrev = Ugd = pnVoltage (Ugd, UgdPrev, Ut * n, UgdCrit); Uds = Ugs - Ugd; // gate-source diode gtiny = Ugs < - 10 * Ut * n ? (Is + Isr) : 0; ggs = pnConductance (Ugs, Is, Ut * n) + pnConductance (Ugs, Isr, Ut * nr) + gtiny; Igs = pnCurrent (Ugs, Is, Ut * n) + pnCurrent (Ugs, Isr, Ut * nr) + gtiny * Ugs; // gate-drain diode gtiny = Ugd < - 10 * Ut * n ? (Is + Isr) : 0; ggd = pnConductance (Ugd, Is, Ut * n) + pnConductance (Ugd, Isr, Ut * nr) + gtiny; Igd = pnCurrent (Ugd, Is, Ut * n) + pnCurrent (Ugd, Isr, Ut * nr) + gtiny * Ugd; // normal (forward) mode of operation if (Uds >= 0) { nr_double_t Ugst = Ugs - Vt0; // normal mode, cutoff region if (Ugst <= 0) { Ids = 0; gm = 0; gds = 0; } else { nr_double_t b = beta * (1 + l * Uds); // normal mode, saturation region if (Ugst <= Uds) { Ids = b * Ugst * Ugst; gm = b * 2 * Ugst; gds = l * beta * Ugst * Ugst; } // normal mode, linear region else { Ids = b * Uds * (2 * Ugst - Uds); gm = b * 2 * Uds; gds = b * 2 * (Ugst - Uds) + l * beta * Uds * (2 * Ugst - Uds); } } } // inverse (backward) mode of operation else { nr_double_t Ugdt = Ugd - Vt0; // inverse mode, cutoff region if (Ugdt <= 0) { Ids = 0; gm = 0; gds = 0; } else { nr_double_t b = beta * (1 - l * Uds); // inverse mode, saturation region if (Ugdt <= -Uds) { Ids = - b * Ugdt * Ugdt; gm = - b * 2 * Ugdt; gds = beta * l * Ugdt * Ugdt + b * 2 * Ugdt; } // inverse mode, linear region else { Ids = b * Uds * (2 * Ugdt + Uds); gm = b * 2 * Uds; gds = 2 * b * Ugdt - beta * l * Uds * (2 * Ugdt + Uds); } } } // compute autonomic current sources IeqG = Igs - ggs * Ugs; IeqD = Igd - ggd * Ugd; IeqS = Ids - gm * Ugs - gds * Uds; setI (NODE_G, (-IeqG - IeqD) * pol); setI (NODE_D, (+IeqD - IeqS) * pol); setI (NODE_S, (+IeqG + IeqS) * pol); // apply admittance matrix elements setY (NODE_G, NODE_G, ggs + ggd); setY (NODE_G, NODE_D, -ggd); setY (NODE_G, NODE_S, -ggs); setY (NODE_D, NODE_G, -ggd + gm); setY (NODE_D, NODE_D, gds + ggd); setY (NODE_D, NODE_S, -gm - gds); setY (NODE_S, NODE_G, -ggs - gm); setY (NODE_S, NODE_D, -gds); setY (NODE_S, NODE_S, ggs + gds + gm); } void jfet::loadOperatingPoints (void) { Ugs = getOperatingPoint ("Vgs"); Ugd = getOperatingPoint ("Vgd"); Uds = getOperatingPoint ("Vds"); } void jfet::saveOperatingPoints (void) { nr_double_t Vgs, Vgd; Vgd = real (getV (NODE_G) - getV (NODE_D)) * pol; Vgs = real (getV (NODE_G) - getV (NODE_S)) * pol; setOperatingPoint ("Vgs", Vgs); setOperatingPoint ("Vgd", Vgd); setOperatingPoint ("Vds", Vgs - Vgd); } void jfet::calcOperatingPoints (void) { // fetch device model parameters nr_double_t z = getPropertyDouble ("M"); nr_double_t Cgd0 = getScaledProperty ("Cgd"); nr_double_t Cgs0 = getScaledProperty ("Cgs"); nr_double_t Pb = getScaledProperty ("Pb"); nr_double_t Fc = getPropertyDouble ("Fc"); nr_double_t Cgs, Cgd; // capacitance of gate-drain diode Cgd = pnCapacitance (Ugd, Cgd0, Pb, z, Fc); Qgd = pnCharge (Ugd, Cgd0, Pb, z, Fc); // capacitance of gate-source diode Cgs = pnCapacitance (Ugs, Cgs0, Pb, z, Fc); Qgs = pnCharge (Ugs, Cgs0, Pb, z, Fc); // save operating points setOperatingPoint ("ggs", ggs); setOperatingPoint ("ggd", ggd); setOperatingPoint ("gds", gds); setOperatingPoint ("gm", gm); setOperatingPoint ("Id", Ids); setOperatingPoint ("Cgd", Cgd); setOperatingPoint ("Cgs", Cgs); } void jfet::initAC (void) { allocMatrixMNA (); clearI (); } void jfet::calcAC (nr_double_t frequency) { setMatrixY (calcMatrixY (frequency)); } void jfet::calcNoiseAC (nr_double_t frequency) { setMatrixN (calcMatrixCy (frequency)); } #define qgdState 0 // gate-drain charge state #define cgdState 1 // gate-drain current state #define qgsState 2 // gate-source charge state #define cgsState 3 // gate-source current state void jfet::initTR (void) { setStates (4); initDC (); } void jfet::calcTR (nr_double_t) { calcDC (); saveOperatingPoints (); loadOperatingPoints (); calcOperatingPoints (); nr_double_t Cgs = getOperatingPoint ("Cgs"); nr_double_t Cgd = getOperatingPoint ("Cgd"); transientCapacitance (qgsState, NODE_G, NODE_S, Cgs, Ugs, Qgs); transientCapacitance (qgdState, NODE_G, NODE_D, Cgd, Ugd, Qgd); } // properties PROP_REQ [] = { { "Is", PROP_REAL, { 1e-14, PROP_NO_STR }, PROP_POS_RANGE }, { "N", PROP_REAL, { 1, PROP_NO_STR }, PROP_RNGII (1, 100) }, { "Vt0", PROP_REAL, { -2, PROP_NO_STR }, PROP_NEG_RANGE }, { "Lambda", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE }, { "Beta", PROP_REAL, { 1e-4, PROP_NO_STR }, PROP_POS_RANGE }, { "M", PROP_REAL, { 0.5, PROP_NO_STR }, PROP_RNGII (0, 1) }, { "Pb", PROP_REAL, { 1.0, PROP_NO_STR }, PROP_RNGXI (0, 10) }, { "Fc", PROP_REAL, { 0.5, PROP_NO_STR }, PROP_RNGIX (0, 1) }, { "Cgs", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE }, { "Cgd", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE }, PROP_NO_PROP }; PROP_OPT [] = { { "Rd", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE }, { "Rs", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE }, { "Isr", PROP_REAL, { 1e-14, PROP_NO_STR }, PROP_POS_RANGE }, { "Nr", PROP_REAL, { 2, PROP_NO_STR }, PROP_RNGII (1, 100) }, { "Kf", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE }, { "Af", PROP_REAL, { 1, PROP_NO_STR }, PROP_POS_RANGE }, { "Ffe", PROP_REAL, { 1, PROP_NO_STR }, PROP_POS_RANGE }, { "Temp", PROP_REAL, { 26.85, PROP_NO_STR }, PROP_MIN_VAL (K) }, { "Type", PROP_STR, { PROP_NO_VAL, "nfet" }, PROP_RNG_FET }, { "Xti", PROP_REAL, { 3, PROP_NO_STR }, PROP_POS_RANGE }, { "Vt0tc", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE }, { "Betatce", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE }, { "Tnom", PROP_REAL, { 26.85, PROP_NO_STR }, PROP_MIN_VAL (K) }, { "Area", PROP_REAL, { 1, PROP_NO_STR }, PROP_POS_RANGEX }, PROP_NO_PROP }; struct define_t jfet::cirdef = { "JFET", 3, PROP_COMPONENT, PROP_NO_SUBSTRATE, PROP_NONLINEAR, PROP_DEF };