diff --git a/examples/soa/bjt-soa.cir b/examples/soa/bjt-soa.cir new file mode 100644 index 000000000..56e8c16f9 --- /dev/null +++ b/examples/soa/bjt-soa.cir @@ -0,0 +1,19 @@ +SOA check for bipolar + +Vce c 0 -1 +Ib b 0 -1u +Ve e 0 0 + +Q1 c b e MPSA92 + +.MODEL MPSA92 PNP(IS=9.53E-14 ISE=8.37E-13 ISC=9.99E-11 XTI=3.00 BF=9.80E1 BR=4.78 IKF=3.49E-2 IKR=1.00 XTB=1.5 VAF=2.60E2 VAR=1.40E2 VJE=3.00E-1 VJC=3.00E-1 RE=1.00E-2 RC=1.00E-2 RB=2.76E1 RBM=6.66E-2 IRB=7.02E-4 CJE=9.54E-11 CJC=4.66E-11 .00 FC=5.00E-1 NF=1.00 NR=1.55 NE=1.49 NC=1.50 MJE=4.26E-1 MJC=7.00E-1 TF=9.52E-10 TR=516.9p ITF=4.12E-1 VTF=9.99E5 XTF=1.03 EG=1.11 VCEO=300 ICRATING=500m MFG=SIEMENS pow_max=0.625 rth0=200 tnom=25) + +.option warn=1 maxwarns=2 +.temp 50 + +.control +dc Vce 0 -200 -0.1 Ib 0 80u 8u +plot i(Ve) +.endc + +.end diff --git a/src/spicelib/devices/bjt/bjt.c b/src/spicelib/devices/bjt/bjt.c index 7c0fb832f..02678c1dc 100644 --- a/src/spicelib/devices/bjt/bjt.c +++ b/src/spicelib/devices/bjt/bjt.c @@ -229,7 +229,12 @@ IFparm BJTmPTable[] = { /* model parameters */ IOP("d", BJT_MOD_XD, IF_REAL, "Temperature exponent of VO"), IOP("vbe_max", BJT_MOD_VBE_MAX, IF_REAL, "maximum voltage B-E junction"), IOP("vbc_max", BJT_MOD_VBC_MAX, IF_REAL, "maximum voltage B-C junction"), - IOP("vce_max", BJT_MOD_VCE_MAX, IF_REAL, "maximum voltage C-E branch") + IOP("vce_max", BJT_MOD_VCE_MAX, IF_REAL, "maximum voltage C-E branch"), + IOP("pow_max", BJT_MOD_POW_MAX, IF_REAL, "maximum device power dissipation"), + IOP("ic_max", BJT_MOD_IC_MAX, IF_REAL, "maximum collector current"), + IOP("ib_max", BJT_MOD_IB_MAX, IF_REAL, "maximum base current"), + IOP("te_max", BJT_MOD_TE_MAX, IF_REAL, "maximum temperature"), + IOP("rth0", BJT_MOD_RTH0, IF_REAL, "thermal resistance juntion to ambient"), }; char *BJTnames[] = { diff --git a/src/spicelib/devices/bjt/bjtdefs.h b/src/spicelib/devices/bjt/bjtdefs.h index 9064f7a8c..5850cf9cc 100644 --- a/src/spicelib/devices/bjt/bjtdefs.h +++ b/src/spicelib/devices/bjt/bjtdefs.h @@ -498,6 +498,11 @@ typedef struct sBJTmodel { /* model structure for a bjt */ double BJTvbeMax; /* maximum voltage over B-E junction */ double BJTvbcMax; /* maximum voltage over B-C junction */ double BJTvceMax; /* maximum voltage over C-E branch */ + double BJTicMax; /* maximum collector current */ + double BJTibMax; /* maximum base current */ + double BJTpowMax; /* maximum device power dissipation */ + double BJTteMax; /* maximum device temperature */ + double BJTrth0; /* thermal resistance juntion to ambient */ unsigned BJTsubsGiven : 1; unsigned BJTtnomGiven : 1; @@ -617,6 +622,11 @@ typedef struct sBJTmodel { /* model structure for a bjt */ unsigned BJTvbeMaxGiven : 1; unsigned BJTvbcMaxGiven : 1; unsigned BJTvceMaxGiven : 1; + unsigned BJTpowMaxGiven : 1; + unsigned BJTicMaxGiven : 1; + unsigned BJTibMaxGiven : 1; + unsigned BJTteMaxGiven : 1; + unsigned BJTrth0Given : 1; } BJTmodel; #ifndef NPN @@ -770,6 +780,11 @@ enum { BJT_MOD_VBE_MAX, BJT_MOD_VBC_MAX, BJT_MOD_VCE_MAX, + BJT_MOD_POW_MAX, + BJT_MOD_IC_MAX, + BJT_MOD_IB_MAX, + BJT_MOD_TE_MAX, + BJT_MOD_RTH0, }; /* device questions */ diff --git a/src/spicelib/devices/bjt/bjtmask.c b/src/spicelib/devices/bjt/bjtmask.c index b68464d02..8dfcfa9e6 100644 --- a/src/spicelib/devices/bjt/bjtmask.c +++ b/src/spicelib/devices/bjt/bjtmask.c @@ -416,6 +416,19 @@ BJTmAsk(CKTcircuit *ckt, GENmodel *instPtr, int which, IFvalue *value) return(OK); case BJT_MOD_VCE_MAX: value->rValue = here->BJTvceMax; + case BJT_MOD_IC_MAX: + value->rValue = here->BJTicMax; + return(OK); + case BJT_MOD_IB_MAX: + value->rValue = here->BJTibMax; + return(OK); + case BJT_MOD_POW_MAX: + value->rValue = here->BJTpowMax; + case BJT_MOD_RTH0: + value->rValue = here->BJTrth0; + return(OK); + case BJT_MOD_TE_MAX: + value->rValue = here->BJTteMax; return(OK); default: return(E_BADPARM); diff --git a/src/spicelib/devices/bjt/bjtmpar.c b/src/spicelib/devices/bjt/bjtmpar.c index 63a369bb6..33b7f7e2e 100644 --- a/src/spicelib/devices/bjt/bjtmpar.c +++ b/src/spicelib/devices/bjt/bjtmpar.c @@ -506,6 +506,27 @@ BJTmParam(int param, IFvalue *value, GENmodel *inModel) mods->BJTvceMax = value->rValue; mods->BJTvceMaxGiven = TRUE; break; + case BJT_MOD_IC_MAX: + mods->BJTicMax = value->rValue; + mods->BJTicMaxGiven = TRUE; + break; + case BJT_MOD_IB_MAX: + mods->BJTibMax = value->rValue; + mods->BJTibMaxGiven = TRUE; + break; + case BJT_MOD_POW_MAX: + mods->BJTpowMax = value->rValue; + mods->BJTpowMaxGiven = TRUE; + break; + case BJT_MOD_TE_MAX: + mods->BJTteMax = value->rValue; + mods->BJTteMaxGiven = TRUE; + break; + case BJT_MOD_RTH0: + mods->BJTrth0 = value->rValue; + mods->BJTrth0Given = TRUE; + break; + default: return(E_BADPARM); } diff --git a/src/spicelib/devices/bjt/bjtsetup.c b/src/spicelib/devices/bjt/bjtsetup.c index 9b8d900cb..3265d1af3 100644 --- a/src/spicelib/devices/bjt/bjtsetup.c +++ b/src/spicelib/devices/bjt/bjtsetup.c @@ -369,6 +369,20 @@ BJTsetup(SMPmatrix *matrix, GENmodel *inModel, CKTcircuit *ckt, int *states) model->BJTvceMax = 1e99; } + if(!model->BJTicMaxGiven) { + model->BJTicMax = 1e99; + } + if(!model->BJTibMaxGiven) { + model->BJTibMax = 1e99; + } + if(!model->BJTpowMaxGiven) { + model->BJTpowMax = 1e99; + } + if(!model->BJTteMaxGiven) { + model->BJTteMax = 1e99; + } + + /* * COMPATABILITY WARNING! * special note: for backward compatability to much older models, spice 2G diff --git a/src/spicelib/devices/bjt/bjtsoachk.c b/src/spicelib/devices/bjt/bjtsoachk.c index b13d85d27..2ec046ab5 100644 --- a/src/spicelib/devices/bjt/bjtsoachk.c +++ b/src/spicelib/devices/bjt/bjtsoachk.c @@ -18,13 +18,20 @@ BJTsoaCheck(CKTcircuit *ckt, GENmodel *inModel) BJTmodel *model = (BJTmodel *) inModel; BJTinstance *here; double vbe, vbc, vce; /* actual bjt voltages */ + double pow; + double ic, ib; /* actual bjt currents */ int maxwarns; static int warns_vbe = 0, warns_vbc = 0, warns_vce = 0; + static int warns_pow = 0, warns_ic = 0, warns_ib = 0; + if (!ckt) { warns_vbe = 0; warns_vbc = 0; warns_vce = 0; + warns_pow = 0; + warns_ic = 0; + warns_ib = 0; return OK; } @@ -32,40 +39,105 @@ BJTsoaCheck(CKTcircuit *ckt, GENmodel *inModel) for (; model; model = BJTnextModel(model)) { - for (here = BJTinstances(model); here; here=BJTnextInstance(here)) { + for (here = BJTinstances(model); here; here = BJTnextInstance(here)) { - vbe = fabs(ckt->CKTrhsOld [here->BJTbasePrimeNode] - - ckt->CKTrhsOld [here->BJTemitPrimeNode]); - vbc = fabs(ckt->CKTrhsOld [here->BJTbasePrimeNode] - - ckt->CKTrhsOld [here->BJTcolPrimeNode]); - vce = fabs(ckt->CKTrhsOld [here->BJTcolPrimeNode] - - ckt->CKTrhsOld [here->BJTemitPrimeNode]); + vbe = fabs(ckt->CKTrhsOld[here->BJTbasePrimeNode] - + ckt->CKTrhsOld[here->BJTemitPrimeNode]); + vbc = fabs(ckt->CKTrhsOld[here->BJTbasePrimeNode] - + ckt->CKTrhsOld[here->BJTcolPrimeNode]); + vce = fabs(ckt->CKTrhsOld[here->BJTcolPrimeNode] - + ckt->CKTrhsOld[here->BJTemitPrimeNode]); if (vbe > model->BJTvbeMax) if (warns_vbe < maxwarns) { - soa_printf(ckt, (GENinstance*) here, - "|Vbe|=%g has exceeded Vbe_max=%g\n", - vbe, model->BJTvbeMax); + soa_printf(ckt, (GENinstance*)here, + "|Vbe|=%g has exceeded Vbe_max=%g\n", + vbe, model->BJTvbeMax); warns_vbe++; } if (vbc > model->BJTvbcMax) if (warns_vbc < maxwarns) { - soa_printf(ckt, (GENinstance*) here, - "|Vbc|=%g has exceeded Vbc_max=%g\n", - vbc, model->BJTvbcMax); + soa_printf(ckt, (GENinstance*)here, + "|Vbc|=%g has exceeded Vbc_max=%g\n", + vbc, model->BJTvbcMax); warns_vbc++; } if (vce > model->BJTvceMax) if (warns_vce < maxwarns) { - soa_printf(ckt, (GENinstance*) here, - "|Vce|=%g has exceeded Vce_max=%g\n", - vce, model->BJTvceMax); + soa_printf(ckt, (GENinstance*)here, + "|Vce|=%g has exceeded Vce_max=%g\n", + vce, model->BJTvceMax); warns_vce++; } + ic = fabs(*(ckt->CKTstate0 + here->BJTcc)); + if (ic > fabs(model->BJTicMax)) + if (warns_ic < maxwarns) { + soa_printf(ckt, (GENinstance*)here, + "Ic=%.4g A at Vce=%.4g V has exceeded Ic_max=%.4g A\n", + ic, vce, model->BJTicMax); + warns_ic++; + } + + ib = fabs(*(ckt->CKTstate0 + here->BJTcb)); + if (ib > fabs(model->BJTibMax)) + if (warns_ib < maxwarns) { + soa_printf(ckt, (GENinstance*)here, + "Ib=%.4g A at Vbe=%.4g V has exceeded Ib_max=%.4g A\n", + ib, vbe, model->BJTibMax); + warns_ib++; + } + + if (warns_pow < maxwarns) { + pow = fabs(*(ckt->CKTstate0 + here->BJTcc) * + (*(ckt->CKTrhsOld + here->BJTcolNode) - + *(ckt->CKTrhsOld + here->BJTemitNode)) + ); + pow += fabs(*(ckt->CKTstate0 + here->BJTcb) * + (*(ckt->CKTrhsOld + here->BJTbaseNode) - + *(ckt->CKTrhsOld + here->BJTemitNode)) + ); + pow += fabs(*(ckt->CKTstate0 + here->BJTcdsub) * + (*(ckt->CKTrhsOld + here->BJTsubstConNode) - + *(ckt->CKTrhsOld + here->BJTsubstNode)) + ); + if ((ckt->CKTcurrentAnalysis & DOING_TRAN) && !(ckt->CKTmode & + MODETRANOP)) { + pow += *(ckt->CKTstate0 + here->BJTcqsub) * + fabs(*(ckt->CKTrhsOld + here->BJTsubstConNode) - + *(ckt->CKTrhsOld + here->BJTsubstNode)); + } + pow *= here->BJTm; + /* derating without self-heating, external temp and model tnom given */ + if (model->BJTrth0Given && model->BJTpowMaxGiven && model->BJTtnomGiven) { + double pow_max; + if (here->BJTtemp < model->BJTtnom) + pow_max = model->BJTpowMax; + else { + pow_max = model->BJTpowMax - (here->BJTtemp - model->BJTtnom) / model->BJTrth0; + pow_max = (pow_max > 0) ? pow_max : 0.; + } + if (pow > pow_max) { + soa_printf(ckt, (GENinstance*)here, + "Pow=%.4g W has exceeded Pow_max=%.4g W\n at Vce=%.4g V, Ib=%.4g A, Ic=%.4g A, and Te=%.4g C\n", + pow, pow_max, vce, ib, ic, here->BJTtemp - CONSTCtoK); + warns_pow++; + } + } + /* no derating */ + else { + if (pow > model->BJTpowMax) { + soa_printf(ckt, (GENinstance*)here, + "Pow=%.4g W has exceeded Pow_max=%.4g W\n at Vce=%.4g V, Ib=%.4g A, and Ic=%.4g A\n", + pow, model->BJTpowMax, vce, ib, ic); + warns_pow++; + } + } + } } + } return OK;