From fef64ab241973fb5b937777c904649a31b52d6c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81rp=C3=A1d=20B=C5=B1rmen?= Date: Fri, 19 Sep 2025 08:18:51 +0200 Subject: [PATCH] Diode soft recovery demo. --- src/spicelib/devices/dio/dio.c | 1 + src/spicelib/devices/dio/diodefs.h | 31 +++++-- src/spicelib/devices/dio/dioload.c | 132 +++++++++++++++++++++++++--- src/spicelib/devices/dio/diomask.c | 3 + src/spicelib/devices/dio/diompar.c | 4 + src/spicelib/devices/dio/diosetup.c | 31 ++++++- 6 files changed, 185 insertions(+), 17 deletions(-) diff --git a/src/spicelib/devices/dio/dio.c b/src/spicelib/devices/dio/dio.c index 3c4ff131c..49293e248 100644 --- a/src/spicelib/devices/dio/dio.c +++ b/src/spicelib/devices/dio/dio.c @@ -120,6 +120,7 @@ IFparm DIOmPTable[] = { /* model parameters */ OPU( "cond", DIO_MOD_COND,IF_REAL, "Ohmic conductance"), IOP( "isr", DIO_MOD_ISR, IF_REAL, "Recombination saturation current"), IOP( "nr", DIO_MOD_NR, IF_REAL, "Recombination current emission coefficient"), + IOP( "vp", DIO_MOD_VP, IF_REAL, "Soft reverse recovery parameter"), /* SOA parameters */ IOP( "fv_max", DIO_MOD_FV_MAX, IF_REAL, "maximum voltage in forward direction"), diff --git a/src/spicelib/devices/dio/diodefs.h b/src/spicelib/devices/dio/diodefs.h index 80f49a59d..85dff9e44 100644 --- a/src/spicelib/devices/dio/diodefs.h +++ b/src/spicelib/devices/dio/diodefs.h @@ -40,6 +40,7 @@ typedef struct sDIOinstance { const int DIOnegNode; /* number of negative node of diode */ const int DIOtempNode; /* number of the temperature node of the diode */ int DIOposPrimeNode; /* number of positive prime node of diode */ + int DIOqpNode; /* number of soft recovery charge node */ double *DIOposPosPrimePtr; /* pointer to sparse matrix at * (positive,positive prime) */ @@ -65,6 +66,13 @@ typedef struct sDIOinstance { double *DIOposPrimeTempPtr; double *DIOnegTempPtr; + /* rev-rec */ + double *DIOqpQpPtr; + double *DIOqpPosPrimePtr; + double *DIOqpNegPtr; + double *DIOposPrimeQpPtr; + double *DIOnegQpPtr; + double DIOcap; /* stores the diode capacitance */ double *DIOsens; /* stores the perturbed values of geq and ceq in ac @@ -218,16 +226,24 @@ typedef struct sDIOinstance { #define DIOconduct DIOstate+2 #define DIOcapCharge DIOstate+3 #define DIOcapCurrent DIOstate+4 +#define DIOsrcapCharge DIOstate+5 +#define DIOsrcapCurrent DIOstate+6 +#define DIOqp DIOstate+7 +#define DIOresCurrent DIOstate+8 +#define DIOresConduct DIOstate+9 +#define DIOcqcsr DIOstate+10 +#define DIOgqcsr DIOstate+11 -#define DIOqth DIOstate+5 /* thermal capacitor charge */ -#define DIOcqth DIOstate+6 /* thermal capacitor current */ -#define DIOdeltemp DIOstate+7 /* thermal voltage over rth0 */ -#define DIOdIdio_dT DIOstate+8 +#define DIOqth DIOstate+10 /* thermal capacitor charge */ +#define DIOcqth DIOstate+11 /* thermal capacitor current */ -#define DIOnumStates 9 +#define DIOdeltemp DIOstate+12 /* thermal voltage over rth0 */ +#define DIOdIdio_dT DIOstate+13 -#define DIOsensxp DIOstate+9 /* charge sensitivities and their derivatives. +#define DIOnumStates 14 + +#define DIOsensxp DIOstate+14 /* charge sensitivities and their derivatives. * +10 for the derivatives - pointer to the * beginning of the array */ @@ -302,6 +318,7 @@ typedef struct sDIOmodel { /* model structure for a diode */ unsigned DIOte_maxGiven : 1; unsigned DIOrecSatCurGiven : 1; unsigned DIOrecEmissionCoeffGiven : 1; + unsigned DIOsoftRevRecParamGiven : 1; unsigned DIOrth0Given :1; unsigned DIOcth0Given :1; @@ -375,6 +392,7 @@ typedef struct sDIOmodel { /* model structure for a diode */ double DIOte_max; /* maximum temperature */ double DIOrecSatCur; /* Recombination saturation current */ double DIOrecEmissionCoeff; /* Recombination emission coefficient */ + double DIOsoftRevRecParam; /* Soft reverse recovery parameter */ double DIOrth0; double DIOcth0; @@ -481,6 +499,7 @@ enum { DIO_MOD_PD_MAX, DIO_MOD_ISR, DIO_MOD_NR, + DIO_MOD_VP, DIO_MOD_RTH0, DIO_MOD_CTH0, diff --git a/src/spicelib/devices/dio/dioload.c b/src/spicelib/devices/dio/dioload.c index 65a88b82a..b46014f8f 100644 --- a/src/spicelib/devices/dio/dioload.c +++ b/src/spicelib/devices/dio/dioload.c @@ -25,7 +25,7 @@ DIOload(GENmodel *inModel, CKTcircuit *ckt) double arg; double argsw; double capd; - double cd, cdb, cdsw, cdb_dT, cdsw_dT; + double cd, cdres, gdres, cdb, cdsw, cdb_dT, cdsw_dT; double cdeq; double cdhat; double ceq; @@ -46,6 +46,7 @@ DIOload(GENmodel *inModel, CKTcircuit *ckt) double evd; double evrev; double gd, gdb, gdsw, gen_fac, gen_fac_vd; + double capsr, gqcsr, cqcsr; double t1, evd_rec, cdb_rec, gdb_rec, cdb_rec_dT; double geq; double gspr; /* area-scaled conductance */ @@ -54,6 +55,7 @@ DIOload(GENmodel *inModel, CKTcircuit *ckt) double tol; /* temporary for tolerence calculations */ #endif double vd; /* current diode voltage */ + double vqp; double vdtemp; double vt; /* K t / Q */ double vte, vtesw, vtetun, vtebrk; @@ -116,9 +118,11 @@ DIOload(GENmodel *inModel, CKTcircuit *ckt) if((ckt->CKTsenInfo->SENmode == TRANSEN)&& (ckt->CKTmode & MODEINITTRAN)) { vd = *(ckt->CKTstate1 + here->DIOvoltage); + vqp = *(ckt->CKTstate1 + here->DIOqp); delTemp = *(ckt->CKTstate1 + here->DIOdeltemp); } else{ vd = *(ckt->CKTstate0 + here->DIOvoltage); + vqp = *(ckt->CKTstate0 + here->DIOqp); delTemp = *(ckt->CKTstate0 + here->DIOdeltemp); } @@ -131,21 +135,27 @@ DIOload(GENmodel *inModel, CKTcircuit *ckt) Check_dio=1; if(ckt->CKTmode & MODEINITSMSIG) { vd= *(ckt->CKTstate0 + here->DIOvoltage); + vqp= *(ckt->CKTstate0 + here->DIOqp); delTemp = *(ckt->CKTstate0 + here->DIOdeltemp); } else if (ckt->CKTmode & MODEINITTRAN) { vd= *(ckt->CKTstate1 + here->DIOvoltage); + vqp= *(ckt->CKTstate1 + here->DIOqp); delTemp = *(ckt->CKTstate1 + here->DIOdeltemp); } else if ( (ckt->CKTmode & MODEINITJCT) && (ckt->CKTmode & MODETRANOP) && (ckt->CKTmode & MODEUIC) ) { vd=here->DIOinitCond; + vqp=0; } else if ( (ckt->CKTmode & MODEINITJCT) && here->DIOoff) { vd=0; + vqp=0; delTemp = 0.0; } else if ( ckt->CKTmode & MODEINITJCT) { vd=here->DIOtVcrit; + vqp=0; delTemp = 0.0; } else if ( ckt->CKTmode & MODEINITFIX && here->DIOoff) { vd=0; + vqp=0; delTemp = 0.0; } else { #ifndef PREDICTOR @@ -153,10 +163,19 @@ DIOload(GENmodel *inModel, CKTcircuit *ckt) *(ckt->CKTstate0 + here->DIOvoltage) = *(ckt->CKTstate1 + here->DIOvoltage); vd = DEVpred(ckt,here->DIOvoltage); + vqp = DEVpred(ckt,here->DIOqp); *(ckt->CKTstate0 + here->DIOcurrent) = *(ckt->CKTstate1 + here->DIOcurrent); *(ckt->CKTstate0 + here->DIOconduct) = *(ckt->CKTstate1 + here->DIOconduct); + *(ckt->CKTstate0 + here->DIOresCurrent) = + *(ckt->CKTstate1 + here->DIOresCurrent); + *(ckt->CKTstate0 + here->DIOresConduct) = + *(ckt->CKTstate1 + here->DIOresConduct); + *(ckt->CKTstate0 + here->DIOcqcsr) = + *(ckt->CKTstate1 + here->DIOcqcsr); + *(ckt->CKTstate0 + here->DIOgqcsr) = + *(ckt->CKTstate1 + here->DIOgqcsr); *(ckt->CKTstate0 + here->DIOdeltemp) = *(ckt->CKTstate1 + here->DIOdeltemp); delTemp = DEVpred(ckt,here->DIOdeltemp); @@ -168,6 +187,7 @@ DIOload(GENmodel *inModel, CKTcircuit *ckt) #endif /* PREDICTOR */ vd = *(ckt->CKTrhsOld+here->DIOposPrimeNode)- *(ckt->CKTrhsOld + here->DIOnegNode); + vqp = *(ckt->CKTrhsOld+here->DIOqpNode); if (selfheat) delTemp = *(ckt->CKTrhsOld + here->DIOtempNode); else @@ -203,8 +223,13 @@ DIOload(GENmodel *inModel, CKTcircuit *ckt) fabs(*(ckt->CKTstate0+here->DIOdeltemp)))+ ckt->CKTvoltTol*1e4))) { vd= *(ckt->CKTstate0 + here->DIOvoltage); + vqp= *(ckt->CKTstate0 + here->DIOqp); cd= *(ckt->CKTstate0 + here->DIOcurrent); gd= *(ckt->CKTstate0 + here->DIOconduct); + cdres= *(ckt->CKTstate0 + here->DIOresCurrent); + gdres= *(ckt->CKTstate0 + here->DIOresConduct); + cqcsr= *(ckt->CKTstate0 + here->DIOcqcsr); + gqcsr= *(ckt->CKTstate0 + here->DIOgqcsr); delTemp = *(ckt->CKTstate0 + here->DIOdeltemp); dIdio_dT= *(ckt->CKTstate0 + here->DIOdIdio_dT); goto load; @@ -400,9 +425,12 @@ next1: gd = gd + ckt->CKTgmin; cd = cd + ckt->CKTgmin*vd; } - } - + gdres = gd; + cdres = cd; + gqcsr = 0; + cqcsr = 0; + if ((ckt->CKTmode & (MODEDCTRANCURVE | MODETRAN | MODEAC | MODEINITSMSIG)) || ((ckt->CKTmode & MODETRANOP) && (ckt->CKTmode & MODEUIC))) { /* @@ -433,15 +461,44 @@ next1: deplcapSW = czof2SW*(here->DIOtF3SW+model->DIOgradingSWCoeff*vd/here->DIOtJctSWPot); } - diffcharge = here->DIOtTransitTime*cd; - *(ckt->CKTstate0 + here->DIOcapCharge) = - diffcharge + deplcharge + deplchargeSW; + /* + Dietmar: should not we also add a term (DIOcmetal+DIOcpoly)*vd to DIOcapCharge? + */ + + if (model->DIOsoftRevRecParam!=0 && here->DIOtTransitTime!=0) { + /* + soft recovery with TT!=0 + add only depletion capacitance. + + */ + *(ckt->CKTstate0 + here->DIOcapCharge) = + deplcharge + deplchargeSW; + + capd = deplcap + deplcapSW + here->DIOcmetal + here->DIOcpoly; + here->DIOcap = capd; + /* + DIOcap is now equal only to depletion capacitance + overlap capacitance. + Diffusion capacitance is modelled via Qp so there is no clear way to define it. + Situation is similar to the one when we have an NQS model for the charge. + */ - diffcap = here->DIOtTransitTime*gd; - capd = diffcap + deplcap + deplcapSW + here->DIOcmetal + here->DIOcpoly; - - here->DIOcap = capd; + /* Now prepare the charge for the capacitor connected to the QP node */ + *(ckt->CKTstate0 + here->DIOsrcapCharge) = here->DIOtTransitTime * vqp; + capsr = here->DIOtTransitTime; + } else { + /* no soft recovery of soft recovery with TT=0 (i.e. no soft recovery due to TT=0) */ + diffcharge = here->DIOtTransitTime*cd; + *(ckt->CKTstate0 + here->DIOcapCharge) = + diffcharge + deplcharge + deplchargeSW; + + diffcap = here->DIOtTransitTime*gd; + capd = diffcap + deplcap + deplcapSW + here->DIOcmetal + here->DIOcpoly; + here->DIOcap = capd; + + *(ckt->CKTstate0 + here->DIOsrcapCharge) = 0; + capsr = 0; + } /* * store small-signal parameters @@ -454,6 +511,10 @@ next1: if(SenCond){ *(ckt->CKTstate0 + here->DIOcurrent) = cd; *(ckt->CKTstate0 + here->DIOconduct) = gd; + *(ckt->CKTstate0 + here->DIOresCurrent) = cdres; + *(ckt->CKTstate0 + here->DIOresConduct) = gdres; + *(ckt->CKTstate0 + here->DIOcqcsr) = cqcsr; + *(ckt->CKTstate0 + here->DIOgqcsr) = gqcsr; *(ckt->CKTstate0 + here->DIOdIdio_dT) = dIdio_dT; #ifdef SENSDEBUG printf("storing small signal parameters\n"); @@ -469,6 +530,7 @@ next1: */ if(SenCond && (ckt->CKTsenInfo->SENmode == TRANSEN)){ *(ckt->CKTstate0 + here->DIOcurrent) = cd; + *(ckt->CKTstate0 + here->DIOresCurrent) = cdres; #ifdef SENSDEBUG printf("storing parameters for transient sensitivity\n" ); @@ -490,6 +552,22 @@ next1: *(ckt->CKTstate1 + here->DIOcapCurrent) = *(ckt->CKTstate0 + here->DIOcapCurrent); } + if (model->DIOsoftRevRecParam!=0 && here->DIOtTransitTime!=0) { + /* soft recovery subcircuit */ + if (ckt->CKTmode & MODEINITTRAN) { + *(ckt->CKTstate1 + here->DIOsrcapCharge) = + *(ckt->CKTstate0 + here->DIOsrcapCharge); + } + error = NIintegrate(ckt,&geq,&ceq,capsr,here->DIOsrcapCharge); + if(error) return(error); + gqcsr = geq; + cqcsr = *(ckt->CKTstate0 + here->DIOsrcapCurrent); + if (ckt->CKTmode & MODEINITTRAN) { + *(ckt->CKTstate1 + here->DIOsrcapCurrent) = + *(ckt->CKTstate0 + here->DIOsrcapCurrent); + } + } + if (selfheat) { error = NIintegrate(ckt, &gcTt, &ceqqth, model->DIOcth0, here->DIOqth); @@ -514,8 +592,13 @@ next1: } } next2: *(ckt->CKTstate0 + here->DIOvoltage) = vd; + *(ckt->CKTstate0 + here->DIOqp) = vqp; *(ckt->CKTstate0 + here->DIOcurrent) = cd; *(ckt->CKTstate0 + here->DIOconduct) = gd; + *(ckt->CKTstate0 + here->DIOresCurrent) = cdres; + *(ckt->CKTstate0 + here->DIOresConduct) = gdres; + *(ckt->CKTstate0 + here->DIOcqcsr) = cqcsr; + *(ckt->CKTstate0 + here->DIOgqcsr) = gqcsr; *(ckt->CKTstate0 + here->DIOdeltemp) = delTemp; *(ckt->CKTstate0 + here->DIOdIdio_dT) = dIdio_dT; @@ -573,6 +656,35 @@ next2: *(ckt->CKTstate0 + here->DIOvoltage) = vd; (*(here->DIOposPrimeTempPtr) += dIdio_dT - dIrs_dT); (*(here->DIOnegTempPtr) += -dIdio_dT); } + + if (model->DIOsoftRevRecParam!=0 && here->DIOtTransitTime!=0) { + // Compute + } else { + + } + + if (model->DIOsoftRevRecParam!=0 && here->DIOtTransitTime!=0) { + double fac, ceqrr, dcrrdvd, grr; + double gain, ceqrrd, geqrrd; + /* QP subcircuit */ + fac = here->DIOtTransitTime / model->DIOsoftRevRecParam; + dcrrdvd = fac*gdres; + ceqrr = -fac*cdres + cqcsr + dcrrdvd*vd - gqcsr*vqp; + grr = 1/model->DIOsoftRevRecParam; + *(ckt->CKTrhs + here->DIOqpNode) -= ceqrr; + *(here->DIOqpQpPtr) += grr + gqcsr; + *(here->DIOqpPosPrimePtr) += -dcrrdvd; + *(here->DIOqpNegPtr) += dcrrdvd; + /* Contribution to diode current */ + gain = (1 - model->DIOsoftRevRecParam) / here->DIOtTransitTime; + /* Linear contribution -(1-vp)/tau*ddt(Qp) */ + geqrrd = gain*gqcsr; + ceqrrd = gain*cqcsr - geqrrd*vqp; + *(ckt->CKTrhs + here->DIOposPrimeNode) -= ceqrrd; + *(ckt->CKTrhs + here->DIOnegNode) += ceqrrd; + *(here->DIOposPrimeQpPtr) += geqrrd; + *(here->DIOnegQpPtr) += -geqrrd; + } } } return(OK); diff --git a/src/spicelib/devices/dio/diomask.c b/src/spicelib/devices/dio/diomask.c index dcbdfffc0..c9f408655 100644 --- a/src/spicelib/devices/dio/diomask.c +++ b/src/spicelib/devices/dio/diomask.c @@ -193,6 +193,9 @@ DIOmAsk (CKTcircuit *ckt, GENmodel *inModel, int which, IFvalue *value) case DIO_MOD_NR: value->rValue = model->DIOrecEmissionCoeff; return(OK); + case DIO_MOD_VP: + value->rValue = model->DIOsoftRevRecParam; + return(OK); case DIO_MOD_RTH0: value->rValue = model->DIOrth0; return(OK); diff --git a/src/spicelib/devices/dio/diompar.c b/src/spicelib/devices/dio/diompar.c index 8c06b4bfc..43fac8c03 100644 --- a/src/spicelib/devices/dio/diompar.c +++ b/src/spicelib/devices/dio/diompar.c @@ -237,6 +237,10 @@ DIOmParam(int param, IFvalue *value, GENmodel *inModel) model->DIOrecEmissionCoeff = value->rValue; model->DIOrecEmissionCoeffGiven = TRUE; break; + case DIO_MOD_VP: + model->DIOsoftRevRecParam = value->rValue; + model->DIOsoftRevRecParamGiven = TRUE; + break; case DIO_MOD_RTH0: model->DIOrth0 = value->rValue; model->DIOrth0Given = TRUE; diff --git a/src/spicelib/devices/dio/diosetup.c b/src/spicelib/devices/dio/diosetup.c index d67449313..91da9097a 100644 --- a/src/spicelib/devices/dio/diosetup.c +++ b/src/spicelib/devices/dio/diosetup.c @@ -211,6 +211,9 @@ DIOsetup(SMPmatrix *matrix, GENmodel *inModel, CKTcircuit *ckt, int *states) if(!model->DIOrecSatCurGiven) { model->DIOrecSatCur = 1e-14; } + if (!model->DIOsoftRevRecParamGiven) { + model->DIOsoftRevRecParam = 0.0; + } /* set lower limit of saturation current */ if (model->DIOsatCur < ckt->CKTepsmin) @@ -360,6 +363,18 @@ DIOsetup(SMPmatrix *matrix, GENmodel *inModel, CKTcircuit *ckt, int *states) } } + /* rev-rec */ + if (model->DIOsoftRevRecParamGiven && model->DIOsoftRevRecParam!=0 && model->DIOtransitTime!=0) { + if(here->DIOqpNode == 0) { + error = CKTmkVolt(ckt, &tmp, here->DIOname, "qp"); + if(error) return(error); + here->DIOqpNode = tmp->number; + } + } else { + here->DIOqpNode = 0; + } + + int selfheat = ((here->DIOtempNode > 0) && (here->DIOthermal) && (model->DIOrth0Given)); /* macro to make elements with built in test for out of memory */ @@ -385,7 +400,15 @@ do { if((here->ptr = SMPmakeElt(matrix, here->first, here->second)) == NULL){\ TSTALLOC(DIOposPrimeTempPtr, DIOposPrimeNode, DIOtempNode); TSTALLOC(DIOnegTempPtr, DIOnegNode, DIOtempNode); } - + + /* rev-rec */ + if (model->DIOsoftRevRecParamGiven && model->DIOsoftRevRecParam!=0 && model->DIOtransitTime!=0) { + TSTALLOC(DIOqpQpPtr , DIOqpNode, DIOqpNode); + TSTALLOC(DIOqpPosPrimePtr, DIOqpNode, DIOposPrimeNode); + TSTALLOC(DIOqpNegPtr , DIOqpNode, DIOnegNode); + TSTALLOC(DIOposPrimeQpPtr, DIOposPrimeNode, DIOqpNode); + TSTALLOC(DIOnegQpPtr, DIOnegNode, DIOqpNode); + } } } return(OK); @@ -410,6 +433,12 @@ DIOunsetup( && here->DIOposPrimeNode != here->DIOposNode) CKTdltNNum(ckt, here->DIOposPrimeNode); here->DIOposPrimeNode = 0; + + /* rev-rec */ + if (here->DIOqpNode > 0) + CKTdltNNum(ckt, here->DIOqpNode); + here->DIOqpNode = 0; + } } return OK;