From a08e6a0676cffb72e16054f5cf51e06b4b55f619 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Fri, 20 Aug 2021 12:23:38 +0200 Subject: [PATCH] Add new SOA parameters (safe operating area) for BJT. We now scan for max of Ic, Ib, power, taking into account the max allowed temperature, using the thermal resistance rth0 model parameter between juntion and ambient. Two user options are there: If rth0 is given, we calculate a derating of the max power allowed. Without a constant max power according to model param pow_max is assumed. The new model parameters for the bipolar model are: pow_max, ic_max, ib_max, te_max, and rth0, in addition to the already existing vbe_max, vbc_max, adn vce_max. --- examples/soa/bjt-soa.cir | 19 +++++ src/spicelib/devices/bjt/bjt.c | 7 +- src/spicelib/devices/bjt/bjtdefs.h | 15 ++++ src/spicelib/devices/bjt/bjtmask.c | 13 ++++ src/spicelib/devices/bjt/bjtmpar.c | 21 ++++++ src/spicelib/devices/bjt/bjtsetup.c | 14 ++++ src/spicelib/devices/bjt/bjtsoachk.c | 104 ++++++++++++++++++++++----- 7 files changed, 176 insertions(+), 17 deletions(-) create mode 100644 examples/soa/bjt-soa.cir 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;