/* $Id: d_bjt.model,v 26.14 2007/02/07 09:06:48 al Exp $ -*- C++ -*- * Copyright (C) 2002 Albert Davis * Author: Albert Davis * * This file is part of "Gnucap", the Gnu Circuit Analysis Package * * This program 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 program 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 program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. *------------------------------------------------------------------ * Berkeley BJT model * Derived from Spice code, both from 2g6 and 3f4 * Recoded for Gnucap model compiler, Al Davis, 2002 *------------------------------------------------------------------ * data structures and defaults for bjt model. * * netlist syntax: * device: qxxxx c b e s mname * model: .model mname NPN * or .model mname PNP * * known BUGs * 1. excess phase partially implemented, disabled, as if PTF == 0. */ /*--------------------------------------------------------------------------*/ h_headers { #include "d_diode.h" } cc_headers { #include "u_limit.h" } /*--------------------------------------------------------------------------*/ device BJT { parse_name bjt; model_type BJT; id_letter Q; circuit { sync; ports {col base emit} {sub}; local_nodes { icol short_to=col short_if="!OPT::rstray || m->rc == 0."; ibase short_to=base short_if="!OPT::rstray || (m->rb == 0. && m->rbm == 0.)"; iemit short_to=emit short_if="!OPT::rstray || m->re == 0."; } // basics cpoly_g Ice {icol,iemit ibase,iemit} state=ccexxx; // cce, go, gm cpoly_g Ipi {ibase iemit} state=cpixxx; // cpi, gpi cpoly_g Imu {ibase icol} state=cmuxxx; // cmu, gmu // junction charge effects fpoly_cap Cbx {base icol} state=qbx; // qbx, cqbx fpoly_cap Cbc {ibase icol} state=qbc; // qbc, cqbc fpoly_cap Ccs {sub icol} state=qcs // qcs, cqcs omit="!(_n[n_sub].n_())"; fpoly_cap Cbe {ibase iemit ibase icol} state=qbe; // qbe, cqbe, geqbc // parasitics resistor Rc {col icol} value="m->rc / (c->area * c->m)" omit="!OPT::rstray || m->rc == 0."; // gcpr resistor Re {emit iemit} value="m->re / (c->area * c->m)" omit="!OPT::rstray || m->re == 0."; // gepr cpoly_g Yb {base ibase} state=ixxxx // gx. should be "eval" omit="!OPT::rstray || (m->rb == 0. && m->rbm == 0.)"; capacitor Cbcp {base col} value="m->cbcp * c->area * c->m" omit="!OPT::cstray || m->cbcp == 0."; capacitor Cbep {base emit} value="m->cbep * c->area * c->m" omit="!OPT::cstray || m->cbep == 0."; capacitor Cbs {base sub} value="(m->cbsp + m->ccsp) * c->area * c->m" omit="!OPT::cstray || ((m->cbsp + m->ccsp) == 0.)"; } tr_probe { VBEInt = "vbe"; VBCInt = "vbc"; VBXInt = "vbx"; VCSInt = "vcs"; VBS = "@n_base[V] - @n_sub[V]"; VBE = "@n_base[V] - @n_emit[V]"; VBC = "@n_base[V] - @n_col[V]"; VBX = "@n_base[V] - @n_ibase[V]"; VCS = "@n_col[V] - @n_sub[V]"; VCB = "@n_col[V] - @n_base[V]"; VCE = "@n_col[V] - @n_emit[V]"; VES = "@n_emit[V] - @n_sub[V]"; VEB = "@n_emit[V] - @n_base[V]"; VEC = "@n_emit[V] - @n_col[V]"; VB = "@n_base[V]"; VC = "@n_col[V]"; VE = "@n_emit[V]"; VS = "@n_sub[V]"; VBI = "@n_ibase[V]"; VCI = "@n_icol[V]"; VEI = "@n_iemit[V]"; ICE = "cce"; ICEOffset = "ccexxx"; HOE = "go"; ROe = "(go != 0.) ? 1/go : BIGBIG"; IPI = "cpi"; IPIOffset = "cpixxx"; RPI = "(gpi != 0.) ? 1/gpi : BIGBIG"; HIE = "(gpi != 0.) ? 1/gpi : BIGBIG"; IMU = "cmu"; IMUOffset = "cmuxxx"; RMU = "(gmu != 0.) ? 1/gmu : BIGBIG"; IB = "cpi + cmu"; RX = "(gx != NA) ? 1/gx : 0."; IC = "cce - cmu"; IE = "-cce -cpi"; CBX = "cqbx"; CBC = "cqbc"; CMU = "cqbc"; CCS = "cqcs"; CBE = "cqbe"; CPI = "cqbe"; P = "@Ice[P] + @Ipi[P] + @Imu[P] + @Rc[P] + @Re[P] + @Yb[P] + @Cbx[P] + @Cbc[P] + @Ccs[P] + @Cbe[P]"; PD = "@Ice[PD] + @Ipi[PD] + @Imu[PD] + @Rc[PD] + @Re[PD] + @Yb[PD] + @Cbx[PD] + @Cbc[PD] + @Ccs[PD] + @Cbe[PD]"; PS = "@Ice[PS] + @Ipi[PS] + @Imu[PS] + @Rc[PS] + @Re[PS] + @Yb[PS] + @Cbx[PS] + @Cbc[PS] + @Ccs[PS] + @Cbe[PS]"; Status = "static_cast(converged() * 2)"; } device { calculated_parameters { double vbe "B-E voltage"; // gather double vbc "B-C voltage"; double vbx "B-C voltage (ext base)"; double vcs "C-S voltage"; double cce "collector-emitter current"; double ccexxx; double go "Small signal output conductance"; double gm "Small signal transconductance"; double cpi "emitter-base current"; double cpixxx; double gpi "Small signal input conductance - pi"; double cmu "collector-base current"; double cmuxxx; double gmu "Small signal conductance - mu"; double ixxxx "Current offset at base node, constant" default=0.; double gx "dix/dvbb Conductance from base to internal base"; double qbx "Charge storage B-X junction"; double cqbx "Cap. due to charge storage in B-X jct."; // cbx, capbx double qbc "Charge storage B-C junction"; double cqbc "Cap. due to charge storage in B-C jct."; // cmu, capbc double qcs "Charge storage C-S junction"; double cqcs "Cap. due to charge storage in C-S jct."; // ccs, capcs double qbe "Charge storage B-E junction"; double cqbe "Cap. due to charge storage in B-E jct."; // cpi, capbe double geqcb "d(Ieb)/d(Vcb)"; double cexbc_0 "Total Capacitance in B-X junction"; // ?? double cexbc_1 ""; double cexbc_2 ""; double _dt_0 "time step"; double _dt_1 ""; } } common { unnamed area; raw_parameters { double m "device multiplier" name=M default=1. positive print_test="m != 1."; double area "area factor" name=Area positive default=1.; bool off "device initially off" name=OFF default=false print_test=off; double icvbe "Initial B-E voltage" name="ICVBE" default=NA; double icvce "Initial C-E voltage" name="ICVCE" default=NA; double temp_c "instance temperature" name="TEMP" default=NA; } calculated_parameters { double oik "" calculate="m->invrollofff / c->area"; double oikr "" calculate="m->invrolloffr / c->area"; } } tr_eval { int foo=3; } } /*--------------------------------------------------------------------------*/ model BJT { level 1; dev_type BJT; hide_base; inherit DIODE; public_keys { NPN polarity=pN; PNP polarity=pP; } independent { override { double kf "Flicker Noise Coefficient" name=KF default=0.; double af "Flicker Noise Exponent" name=AF default=1.; } raw_parameters { int level "dummy" name=LEVEL default=1 print_test=false; // basic double bf "Ideal forward beta" name=BF alt_name=BFM default=100.; double br "Ideal reverse beta" name=BR alt_name=BRM default=1.; double ibc "bc Saturation Current" name=IBC default=NA print_test="ibe != ibc" final_default="((has_hard_value(is)) ? is : 1e-16)"; double ibe "be Saturation Current" name=IBE default=NA print_test="ibe != ibc" final_default="((has_hard_value(is)) ? is : 1e-16)"; double is "Saturation Current" name=IS default=NA print_test="ibe == ibc" final_default="((ibe == ibc) ? ibe : NA)"; //double iss "bulk to collector or base sat current (ignored)" //name=ISS default=0; //double expli "current explosion factor (ignored)" //name=EXPLI default=1e15; double nf "Forward emission coefficient" name=NF default=1.; double nr "Reverse emission coefficient" name=NR default=1.; //double ns "substrate leakage emission coefficient (ignored)" //name=NS default=1; //int subs "geometry, +1=vertical, -1=lateral (ignored)", name=SUBS //default="(polarity==pP) ? -1 : 1"; // base width modulation double vaf "Forward Early voltage" name=VAf alt_name=VBF default=NA; double var "Reverse Early voltage" name=VAR alt_name=VB default=NA; // low current beta degeneration double isc "B-C leakage saturation current" name=ISC default=NA final_default="(c4*is)"; double c4 "obsolete, don't use" name=C4 alt_name=JLC default=0.; double nc "B-C leakage emission coefficient" name=NC default=2.; double ise "B-E leakage saturation current" name=ISE default=NA final_default="(c2*is)"; double c2 "obsolete, don't use" name=C2 alt_name=JLE default=0.; double ne "B-E leakage emission coefficient" name=NE default=1.5; // high current beta degeneration double ikf "Forward beta roll-off corner current" name=IKf alt_name=JBF default=NA; double ikr "reverse beta roll-off corner current" name=IKR alt_name=JBR default=NA; // parasitic resistance double irb "Current for base resistance=(rb+rbm)/2" name=IRB alt_name=JRB default=NA; // alt name IOB double rb "Zero bias base resistance" name=RB default=0.; double rbm "Minimum base resistance" name=RBM default=NA final_default=rb; double re "Emitter resistance" name=RE default=0.; double rc "Collector resistance" name=RC default=0.; // parasitic capacitance double cbcp "external BC capacitance" name=CBCP print_test="cbcp!=0." default=0.; double cbep "external BE capacitance" name=CBEP print_test="cbep!=0." default=0.; double cbsp "external BS capacitance (lateral)" name=CBSP print_test="cbsp!=0." default=0.; double ccsp "external BS capacitance (vertical)" name=CCSP print_test="ccsp!=0." default=0.; // junction capacitance double cjc "Zero bias B-C depletion capacitance" name=CJC default=0.; double cje "Zero bias B-E depletion capacitance" name=CJE default=0.; double cjs "Zero bias C-S capacitance" name=CJS alt_name=CCS default=0.; // alt name CSUB double fc "Forward bias junction fit parameter" name=FC default=NA final_default=.5 quiet_max=.9999; double mjc "B-C junction grading coefficient" name=MJc alt_name=MC default=.33; double mje "B-E junction grading coefficient" name=MJE alt_name=ME default=.33; double mjs "Substrate junction grading coefficient" name=MJS alt_name=MSub default=0.; // alt name ESUB?? double vjc "B-C built in potential" name=VJC alt_name=PC default=.75; double vje "B-E built in potential" name=VJE alt_name=PE default=.75; double vjs "Substrate junction built in potential" name=VJS alt_name=PSub default=.75; double xcjc "Fraction of B-C cap to internal base" name=XCJC alt_name=CDIS default=1.; // transit time double itf "High current dependence of TF" name=ITF alt_name=JTF default=0.; double ptf "Excess phase" name=PTf default=0.; double tf "Ideal forward transit time" name=TF default=0.; double tr "Ideal reverse transit time" name=TR default=0.; double vtf "Voltage giving VBC dependence of TF" name=VTF default=NA; double xtf "Coefficient for bias dependence of TF" name=XTF default=0.; // temperature effects double xtb "Forward and reverse beta temp. exp." name=XTB alt_name=TB default=0.; // alt name TCB double xti "Temp. exponent for IS" name=XTI default=3.; double eg "Energy gap for IS temp. dependency" name=EG default=1.11; } calculated_parameters { double tnom_k "nominal temperature, kelvin" calculate="_tnom_c + P_CELSIUS0"; polarity_t polarity "" default=pN; double invearlyvoltf "Inverse early voltage:forward" name=INVEARLYVOLTF calculate="(has_nz_value(vaf)) ? 1./vaf : 0."; double invearlyvoltr "Inverse early voltage:reverse" name=INVEARLYVOLTR calculate="(has_nz_value(var)) ? 1./var : 0."; double invrollofff "Inverse roll off - forward" name=INVROLLOFFF calculate="(has_nz_value(ikf)) ? 1./ikf : 0."; double invrolloffr "Inverse roll off - reverse" name=INVROLLOFFR calculate="(has_nz_value(ikr)) ? 1./ikr : 0."; double transtimevbcfact "Transit time VBC factor" name=TRANSTIMEVBCFACT calculate="(has_nz_value(vtf)) ? 1./(vtf*1.44) : 0."; double excessphasefactor "Excess phase fact." name=EXCESSPHASEFACTOR calculate="(ptf * DTOR) * tf"; double xfc "" calculate="log(1 - fc)"; double f2 "" calculate="exp((1 + mje) * xfc)"; double f3 "" calculate="1 - fc * (1 + mje)"; double f6 "" calculate="exp((1 + mjc) * xfc)"; double f7 "" calculate="1 - fc * (1 + mjc)"; } } temperature_dependent { calculated_parameters { double vt ""; //double is "BJTtSatCur" calculate = "m->is * factor"; double ibc "BJTtSatCur" calculate = "m->ibc * factor"; double ibe "BJTtSatCur" calculate = "m->ibe * factor"; double BetaF "BJTtBetaF" calculate = "m->bf * bfactor"; double BetaR "BJTtBetaR" calculate = "m->br * bfactor"; double BEleakCur "BJTtBEleakCur" calculate = "m->ise * exp(factlog/m->ne) / bfactor"; double BCleakCur "BJTtBCleakCur" calculate = "m->isc * exp(factlog/m->nc) / bfactor"; double BEpot "BJTtBEpot"; double BEcap "BJTtBEcap"; double DepCap ""; double f1 ""; double BCpot "BJTtBCpot"; double BCcap "BJTtBCcap"; double f4 ""; double f5 ""; double Vcrit "" calculate="vt * log(vt / (M_SQRT2 * m->ibe))"; } code_pre { const double reftemp = 300.15; double temp_k = ((has_hard_value(c->temp_c)) ? c->temp_c : SIM::temp_c) + P_CELSIUS0; double fact1 = m->tnom_k / reftemp; double fact2 = temp_k / reftemp; double tempratio = temp_k / m->tnom_k; // fact2/fact1 double kt = temp_k * P_K; vt = temp_k * P_K_Q; double egap = 1.16 - (7.02e-4*temp_k*temp_k) / (temp_k+1108.); // egfet // double arg = (m->eg*tempratio - egap) / (2*kt); double arg = -egap/(2 * kt) + 1.1150877 / (P_K * (reftemp+reftemp)); double pbfact = -2 * vt * (1.5 * log(fact2) + P_Q * arg); double ratlog = log(tempratio); double ratio1 = tempratio - 1; double factlog = ratio1 * m->eg / vt + m->xti * ratlog; double factor = exp(factlog); double bfactor = exp(ratlog * m->xtb); } code_post { { double pbo = (m->vje - pbfact) / fact1; BEpot = fact2 * pbo + pbfact; double gmaold = (m->vje - pbo) / pbo; double gmanew = (BEpot - pbo) / pbo; BEcap = (m->cje / (1 + m->mje * (4e-4*(m->tnom_k-reftemp)-gmaold))) * (1 + m->mje * (4e-4*(temp_k-reftemp)-gmanew)); DepCap = m->fc * BEpot; f1 = BEpot * (1 - exp((1 - m->mje) * m->xfc)) / (1 - m->mje); } { double pbo = (m->vjc - pbfact) / fact1; BCpot = fact2 * pbo + pbfact; double gmaold = (m->vjc - pbo) / pbo; double gmanew = (BCpot - pbo) / pbo; BCcap = (m->cjc / (1 + m->mjc * (4e-4*(m->tnom_k-reftemp)-gmaold))) * (1 + m->mjc * (4e-4*(temp_k-reftemp)-gmanew)); f4 = m->fc * BCpot; f5 = BCpot * (1 - exp((1 - m->mjc) * m->xfc)) / (1 - m->mjc); } } } tr_eval { // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - trace0("--------------------------"); trace1(d->long_label().c_str(), d->evaliter()); trace4("", d->vbe, d->vbc, d->vbx, d->vcs); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - double cbe, gbe; { // d->cpi, d->gpi, d->cpixxx // uses: d->vbe double cben, gben; double vtn = t->vt * m->nf; double csat = t->ibe * c->area; double c2 = t->BEleakCur * c->area; if (d->vbe > -5 * vtn) { double evbe = exp(d->vbe / vtn); cbe = csat * (evbe-1) + OPT::gmin * d->vbe; gbe = csat * evbe/vtn + OPT::gmin; if (c2 == 0.) { cben = 0.; gben = 0.; }else{ double vte = m->ne * t->vt; double evben = exp(d->vbe / vte); cben = c2 * (evben-1); gben = c2 * evben/vte; } trace4("vbe on", cbe, gbe, cben, gben); }else{ gbe = -csat/d->vbe + OPT::gmin; cbe = gbe * d->vbe; gben = -c2 / d->vbe; cben = gben * d->vbe; trace4("vbe off", cbe, gbe, cben, gben); } d->cpi = cbe / t->BetaF + cben; d->gpi = gbe / t->BetaF + gben; d->cpixxx = d->cpi - d->vbe * d->gpi; trace3("", t->BetaF, d->cpi, d->gpi); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - double cbc, gbc, cbcn; { // d->cmu, d->gmu, d->cmuxxx // uses: d->vbc double gbcn; double vtn = t->vt * m->nr; double csat = t->ibc * c->area; double c4 = t->BCleakCur * c->area; if (d->vbc > -5 * vtn) { double evbc = exp(d->vbc / vtn); cbc = csat * (evbc-1) + OPT::gmin * d->vbc; gbc = csat * evbc/vtn + OPT::gmin; if (c4 == 0.) { cbcn = 0.; gbcn = 0.; }else{ double vtc = m->nc * t->vt; double evbcn = exp(d->vbc / vtc); cbcn = c4 * (evbcn-1); gbcn = c4 * evbcn/vtc; } trace4("vbc on", cbc, gbc, cbcn, gbcn); }else{ gbc = -csat/d->vbc + OPT::gmin; cbc = gbc * d->vbc; gbcn = -c4 / d->vbc; cbcn = gbcn * d->vbc; trace4("vbc off", cbc, gbc, cbcn, gbcn); } d->cmu = cbc / t->BetaR + cbcn; d->gmu = gbc / t->BetaR + gbcn; d->cmuxxx = d->cmu - d->vbc * d->gmu; trace3("", t->BetaR, d->cmu, d->gmu); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // determine base charge terms double qb, dqbdve, dqbdvc; { double q1 = 1 / (1 - m->invearlyvoltf*d->vbc - m->invearlyvoltr*d->vbe); if(c->oik == 0. && c->oikr == 0.) { qb = q1; dqbdve = q1 * qb * m->invearlyvoltr; dqbdvc = q1 * qb * m->invearlyvoltf; trace4("!oik", q1, qb, dqbdve, dqbdvc); }else{ double q2 = c->oik * cbe + c->oikr * cbc; double arg = std::max(0., 1+4*q2); double sqarg = (arg == 0.) ? 1 : sqrt(arg); qb = q1 * (1+sqarg) / 2; dqbdve = q1 * (qb * m->invearlyvoltr + c->oik * gbe / sqarg); dqbdvc = q1 * (qb * m->invearlyvoltf + c->oikr * gbc / sqarg); trace2("", c->oik, c->oikr); trace4("oik", q1, qb, dqbdve, dqbdvc); } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // weil's approx. for excess phase applied with backward-euler integration { double cc = 0.; double cex = cbe; double gex = gbe; if (0 && m->excessphasefactor != 0.) { unreachable(); incomplete(); // doesn't save old values of cexbc, so disabled double arg1 = d->_dt_0 / m->excessphasefactor; double arg2 = 3 * arg1; arg1 *= arg2; double denom = 1 + arg1 + arg2; double arg3 = arg1 / denom; if (initial_step()) { d->cexbc_2 = d->cexbc_1 = cbe / qb; } cc = (d->cexbc_1 * (1 + d->_dt_0/d->_dt_1 + arg2) - d->cexbc_2 * d->_dt_0/d->_dt_1) / denom; cex *= arg3; gex *= arg3; } d->cexbc_0 = cc + cex / qb; d->cce = cc + (cex-cbc)/qb - cbc/t->BetaR - cbcn; d->go = (gbc + (cex-cbc)*dqbdvc / qb) / qb; d->gm = (gex - (cex-cbc)*dqbdve / qb) / qb - d->go; d->ccexxx = d->cce - ((d->vbe - d->vbc) * d->go + d->vbe * d->gm); trace4("", d->cce, d->go, d->gm, d->cce/t->vt); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // d->gx // should be moved to a private eval // may use d->cpi, d->cmu, qb { if (!OPT::rstray || (!has_nz_value(m->rb) && !has_nz_value(m->rbm))) { trace3("", m->rb, m->irb, d->gx); assert(d->gx == NA); }else{ double rx = NA; double rbpr = m->rbm / c->area; double rbpi = m->rb / c->area - rbpr; if (has_nz_value(m->irb)) {itested();//554 // base resistance lowering at high current double cb = d->cpi + d->cmu; double xjrb = m->irb * c->area; double arg1 = std::max(cb/xjrb, 1e-9); double arg2 = (-1 + sqrt(1+14.59025*arg1)) / 2.4317 / sqrt(arg1); arg1 = tan(arg2); rx = rbpr + 3 * rbpi * (arg1-arg2) / arg2 / arg1 / arg1; }else{ rx = rbpr + rbpi / qb; } trace3("", m->rb, m->irb, rx); assert(rx != NA); assert(rx != 0.); d->gx = 1 / rx; trace1("", d->gx); } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - const bool charge_computation_needed = OPT::cstray; if (charge_computation_needed) { if (has_nz_value(m->tf) && d->vbe > 0.) { double argtf = NA; double arg2 = NA; double arg3 = NA; if (has_nz_value(m->xtf)) {itested();//579 if (m->transtimevbcfact != 0.) { argtf = m->xtf * exp(d->vbc * m->transtimevbcfact); }else{untested(); argtf = m->xtf; } arg2 = argtf; if(has_nz_value(m->itf)) { double temp = cbe / (cbe + m->itf * c->area); argtf *= temp*temp; arg2 *= (3-temp-temp); }else{ } arg3 = cbe * argtf * m->transtimevbcfact; }else{ arg3 = arg2 = argtf = 0.; } assert(argtf != NA); assert(arg2 != NA); assert(arg3 != NA); cbe *= (1+argtf) / qb; gbe = (gbe * (1+arg2) - cbe * dqbdve) / qb; d->geqcb=m->tf*(arg3-cbe*dqbdvc)/qb; }else{ d->geqcb=0.; } { // d->qbe, d->cqbe // uses: d->vbe, cbe, gbe double czbe = t->BEcap * c->area; if (d->vbe < t->DepCap) { double arg = 1 - d->vbe / t->BEpot; double sarg = pow(arg, -m->mje); d->qbe = m->tf * cbe + t->BEpot * czbe * (1-arg*sarg) / (1 - m->mje); d->cqbe = m->tf * gbe + czbe * sarg; }else{ double czbef2 = czbe / m->f2; d->qbe = m->tf * cbe + czbe * t->f1 + czbef2 * (m->f3 * (d->vbe - t->DepCap) + (m->mje / (2. * t->BEpot)) * (d->vbe * d->vbe - t->DepCap * t->DepCap)); d->cqbe = m->tf * gbe + czbef2 * (m->f3 + m->mje*d->vbe / t->BEpot); } } { // d->qbc, d->cqbc // uses: d->vbc, cbc, gbc double czbc = t->BCcap * c->area * m->xcjc; if (d->vbc < t->f4) { double arg = 1 - d->vbc / t->BCpot; double sarg = pow(arg, -m->mjc); d->qbc = m->tr *cbc + t->BCpot *czbc * (1 - arg*sarg) / (1 - m->mjc); d->cqbc = m->tr * gbc + czbc * sarg; }else{ double czbcf2 = czbc / m->f6; d->qbc = m->tr * cbc + czbc * t->f5 + czbcf2 * (m->f7 * (d->vbc-t->f4) + (m->mjc/(t->BCpot+t->BCpot)) * (d->vbc*d->vbc-t->f4*t->f4)); d->cqbc = m->tr * gbc + czbcf2 * (m->f7 + m->mjc * d->vbc/t->BCpot); } } { // d->qbx, d->cqbx // uses: d->vbx double czbx = t->BCcap * c->area * (1 - m->xcjc); if (d->vbx < t->f4) { double arg = 1 - d->vbx / t->BCpot; double sarg = pow(arg, -m->mjc); d->qbx = t->BCpot * czbx * (1 - arg*sarg) / (1 - m->mjc); d->cqbx = czbx * sarg; }else{ double czbxf2 = czbx / m->f6; d->qbx = czbx * t->f5 + czbxf2 * (m->f7 * (d->vbx-t->f4) + (m->mjc / (t->BCpot+t->BCpot)) * (d->vbx*d->vbx-t->f4*t->f4)); d->cqbx = czbxf2 * (m->f7 + m->mjc * d->vbx / t->BCpot); } } { // d->qcs, d->cqcs // uses: d->vcs double czcs = m->cjs * c->area; if (d->vcs < 0.) { double arg = 1 - d->vcs / m->vjs; double sarg = pow(arg, -m->mjs); d->qcs = m->vjs * czcs * (1 - arg*sarg) / (1 - m->mjs); d->cqcs = czcs * sarg; }else{ d->qcs = d->vcs * czcs * (1 + m->mjs * d->vcs / (2 * m->vjs)); d->cqcs = czcs * (1 + m->mjs * d->vcs / m->vjs); } } } } } /*--------------------------------------------------------------------------*/ cc_direct { /*--------------------------------------------------------------------------*/ bool DEV_BJT::tr_needs_eval()const { if (is_q_for_eval()) { untested(); return false; }else if (!converged()) { return true; }else{ const COMMON_BJT* c = prechecked_cast(common()); assert(c); const MODEL_BJT* m=prechecked_cast(c->model()); assert(m); polarity_t polarity = m->polarity; return !(conchk(vbc, polarity*(_n[n_ibase].v0()-_n[n_icol].v0()), OPT::vntol) && conchk(vbe, polarity*(_n[n_ibase].v0()-_n[n_iemit].v0()), OPT::vntol) && conchk(vcs, polarity*(_n[n_icol].v0()-_n[n_sub].v0()), OPT::vntol)); } } /*--------------------------------------------------------------------------*/ bool DEV_BJT::do_tr() { const COMMON_BJT* c = prechecked_cast(common()); assert(c); const MODEL_BJT* m = prechecked_cast(c->model()); assert(m); const TDP_BJT T(this); const TDP_BJT* t = &T; if(initial_step()) { // initial guess if (c->off) { vbe = 0.; }else{ double vt = (SIM::temp_c + P_CELSIUS0) * P_K_Q; vbe = vt * log(vt / (M_SQRT2 * m->ibe)); } vbc = 0.; /* ERROR: need to initialize VCS, VBX here */ vcs = vbx = 0.; }else{ // normal gather vbe = pnj_limit((m->polarity * volts_limited(_n[n_ibase], _n[n_iemit])), vbe, t->vt, t->Vcrit); vbc = pnj_limit((m->polarity * volts_limited(_n[n_ibase], _n[n_icol])), vbc, t->vt, t->Vcrit); vbx = m->polarity * volts_limited(_n[n_base], _n[n_icol]); vcs = m->polarity * volts_limited(_n[n_sub], _n[n_icol]); } if (SIM::uic_now()) {untested();//736 if (has_good_value(c->icvbe)) {untested();//737 vbe = m->polarity * c->icvbe; }else{untested();//739 } if (has_good_value(c->icvce)) {untested();//741 vbc = vbe - m->polarity * c->icvce; vbx = vbc; }else{untested();//744 } }else{ } m->tr_eval(this); switch (m->polarity) { case pP: cce = -cce; ccexxx = -ccexxx; cpi = -cpi; cpixxx = -cpixxx; cmu = -cmu; cmuxxx = -cmuxxx; assert(ixxxx == 0.); qbx = -qbx; qbc = -qbc; qcs = -qcs; qbe = -qbe; break; case pN: // leave it as is break; } if (c->m != 1.) { cce *= c->m; ccexxx *= c->m; go *= c->m; gm *= c->m; cpi *= c->m; cpixxx *= c->m; gpi *= c->m; cmu *= c->m; cmuxxx *= c->m; gmu *= c->m; ixxxx *= c->m; gx *= c->m; qbx *= c->m; cqbx *= c->m; qbc *= c->m; cqbc *= c->m; qcs *= c->m; cqcs *= c->m; qbe *= c->m; cqbe *= c->m; geqcb *= c->m; } assert(subckt()); set_converged(subckt()->do_tr()); return converged(); } } /*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/