You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
490 lines
18 KiB
490 lines
18 KiB
/**********
|
|
Copyright 1990 Regents of the University of California. All rights reserved.
|
|
Author: 1985 Thomas L. Quarles
|
|
Modified: 2000 AlansFixes
|
|
Modified by Paolo Nenzi 2003 and Dietmar Warning 2012
|
|
**********/
|
|
|
|
/* load the diode structure with those pointers needed later
|
|
* for fast matrix loading
|
|
*/
|
|
|
|
#include "ngspice/ngspice.h"
|
|
#include "ngspice/smpdefs.h"
|
|
#include "ngspice/cktdefs.h"
|
|
#include "diodefs.h"
|
|
#include "ngspice/sperror.h"
|
|
#include "ngspice/suffix.h"
|
|
#include "ngspice/fteext.h"
|
|
#include "ngspice/compatmode.h"
|
|
|
|
int
|
|
DIOsetup(SMPmatrix *matrix, GENmodel *inModel, CKTcircuit *ckt, int *states)
|
|
{
|
|
DIOmodel *model = (DIOmodel*)inModel;
|
|
DIOinstance *here;
|
|
int error;
|
|
CKTnode *tmp;
|
|
double scale;
|
|
|
|
if (!cp_getvar("scale", CP_REAL, &scale, 0))
|
|
scale = 1;
|
|
|
|
/* loop through all the diode models */
|
|
for( ; model != NULL; model = DIOnextModel(model)) {
|
|
|
|
if(!model->DIOlevelGiven) {
|
|
model->DIOlevel = 1;
|
|
} else if (model->DIOlevel == 2) {
|
|
SPfrontEnd->IFerrorf(ERR_FATAL,
|
|
"%s: Diode model level 2 is not supported.", model->DIOmodName);
|
|
return(E_BADPARM);
|
|
}
|
|
if(!model->DIOemissionCoeffGiven) {
|
|
model->DIOemissionCoeff = 1;
|
|
}
|
|
if(!model->DIOsatCurGiven) {
|
|
model->DIOsatCur = 1e-14;
|
|
}
|
|
if(!model->DIOsatSWCurGiven) {
|
|
model->DIOsatSWCur = 0.0;
|
|
}
|
|
if(!model->DIOswEmissionCoeffGiven) {
|
|
model->DIOswEmissionCoeff = 1;
|
|
}
|
|
if(!model->DIObreakdownCurrentGiven) {
|
|
model->DIObreakdownCurrent = 1e-3;
|
|
}
|
|
if(!model->DIOjunctionPotGiven){
|
|
model->DIOjunctionPot = 1;
|
|
}
|
|
if(!model->DIOgradingCoeffGiven) {
|
|
model->DIOgradingCoeff = .5;
|
|
}
|
|
if(!model->DIOgradCoeffTemp1Given) {
|
|
model->DIOgradCoeffTemp1 = 0.0;
|
|
}
|
|
if(!model->DIOgradCoeffTemp2Given) {
|
|
model->DIOgradCoeffTemp2 = 0.0;
|
|
}
|
|
if(!model->DIOdepletionCapCoeffGiven) {
|
|
model->DIOdepletionCapCoeff = .5;
|
|
}
|
|
if(!model->DIOdepletionSWcapCoeffGiven) {
|
|
model->DIOdepletionSWcapCoeff = .5;
|
|
}
|
|
if(!model->DIOtransitTimeGiven) {
|
|
model->DIOtransitTime = 0;
|
|
}
|
|
if(!model->DIOtranTimeTemp1Given) {
|
|
model->DIOtranTimeTemp1 = 0.0;
|
|
}
|
|
if(!model->DIOtranTimeTemp2Given) {
|
|
model->DIOtranTimeTemp2 = 0.0;
|
|
}
|
|
if(!model->DIOjunctionCapGiven) {
|
|
if (newcompat.ps || newcompat.lt) {
|
|
double cdiode = 0.;
|
|
/* to improve convergence (sometimes) */
|
|
if (cp_getvar("diode_cj0", CP_REAL, &cdiode, 0) && cdiode > 0) {
|
|
model->DIOjunctionCap = cdiode;
|
|
if (ft_ngdebug)
|
|
fprintf(stderr, "Diode junction capacitance in model %s set to %e F\n", model->gen.GENmodName, cdiode);
|
|
}
|
|
else
|
|
model->DIOjunctionCap = 0.0;
|
|
}
|
|
else {
|
|
model->DIOjunctionCap = 0.0;
|
|
}
|
|
}
|
|
if(!model->DIOjunctionSWCapGiven) {
|
|
model->DIOjunctionSWCap = 0;
|
|
}
|
|
if(!model->DIOjunctionSWPotGiven){
|
|
model->DIOjunctionSWPot = 1;
|
|
}
|
|
if(!model->DIOgradingSWCoeffGiven) {
|
|
model->DIOgradingSWCoeff = .33;
|
|
}
|
|
if(model->DIOforwardKneeCurrentGiven) {
|
|
if (model->DIOforwardKneeCurrent < ckt->CKTepsmin) {
|
|
model->DIOforwardKneeCurrentGiven = FALSE;
|
|
fprintf(stderr, "Warning: %s: IKF too small - model effect disabled!\n",
|
|
model->DIOmodName);
|
|
}
|
|
}
|
|
if(model->DIOreverseKneeCurrentGiven) {
|
|
if (model->DIOreverseKneeCurrent < ckt->CKTepsmin) {
|
|
model->DIOreverseKneeCurrentGiven = FALSE;
|
|
fprintf(stderr, "Warning: %s: IKR too small - model effect disabled!\n",
|
|
model->DIOmodName);
|
|
}
|
|
}
|
|
if(model->DIOforwardSWKneeCurrentGiven) {
|
|
if (model->DIOforwardSWKneeCurrent < ckt->CKTepsmin) {
|
|
model->DIOforwardSWKneeCurrentGiven = FALSE;
|
|
fprintf(stderr, "Warning: %s: IKP too small - model effect disabled!\n",
|
|
model->DIOmodName);
|
|
}
|
|
}
|
|
if(!model->DIObrkdEmissionCoeffGiven) {
|
|
model->DIObrkdEmissionCoeff = model->DIOemissionCoeff;
|
|
}
|
|
if(!model->DIOtlevGiven) {
|
|
model->DIOtlev = 0;
|
|
}
|
|
if(!model->DIOtlevcGiven) {
|
|
model->DIOtlevc = 0;
|
|
}
|
|
if(!model->DIOactivationEnergyGiven) {
|
|
if(model->DIOtlev == 2) {
|
|
model->DIOactivationEnergy = 1.16;
|
|
} else {
|
|
model->DIOactivationEnergy = 1.11;
|
|
}
|
|
}
|
|
if(!model->DIOfirstBGcorrFactorGiven) {
|
|
model->DIOfirstBGcorrFactor = 7.02e-4;
|
|
}
|
|
if(!model->DIOsecndBGcorrFactorGiven) {
|
|
model->DIOsecndBGcorrFactor = 1108.0;
|
|
}
|
|
if(!model->DIOsaturationCurrentExpGiven) {
|
|
model->DIOsaturationCurrentExp = 3;
|
|
}
|
|
if(!model->DIOctaGiven) {
|
|
model->DIOcta = 0.0;
|
|
}
|
|
if(!model->DIOctpGiven) {
|
|
model->DIOctp = 0.0;
|
|
}
|
|
if(!model->DIOtpbGiven) {
|
|
model->DIOtpb = 0.0;
|
|
}
|
|
if(!model->DIOtphpGiven) {
|
|
model->DIOtphp = 0.0;
|
|
}
|
|
if(!model->DIOfNcoefGiven) {
|
|
model->DIOfNcoef = 0.0;
|
|
}
|
|
if(!model->DIOfNexpGiven) {
|
|
model->DIOfNexp = 1.0;
|
|
}
|
|
if(!model->DIOresistTemp1Given) {
|
|
model->DIOresistTemp1 = 0.0;
|
|
}
|
|
if(!model->DIOresistTemp2Given) {
|
|
model->DIOresistTemp2 = 0.0;
|
|
}
|
|
if(!model->DIOtcvGiven) {
|
|
model->DIOtcv = 0.0;
|
|
}
|
|
if(!model->DIOareaGiven) {
|
|
model->DIOarea = 1.0;
|
|
}
|
|
if(!model->DIOpjGiven) {
|
|
model->DIOpj = 0.0;
|
|
}
|
|
if(!model->DIOtunSatCurGiven) {
|
|
model->DIOtunSatCur = 0.0;
|
|
}
|
|
if(!model->DIOtunSatSWCurGiven) {
|
|
model->DIOtunSatSWCur = 0.0;
|
|
}
|
|
if(!model->DIOtunEmissionCoeffGiven) {
|
|
model->DIOtunEmissionCoeff = 30.0;
|
|
}
|
|
if(!model->DIOtunSaturationCurrentExpGiven) {
|
|
model->DIOtunSaturationCurrentExp = 3.0;
|
|
}
|
|
if(!model->DIOtunEGcorrectionFactorGiven) {
|
|
model->DIOtunEGcorrectionFactor = 1.0;
|
|
}
|
|
if(!model->DIOfv_maxGiven) {
|
|
model->DIOfv_max = 1e99;
|
|
}
|
|
if(!model->DIObv_maxGiven) {
|
|
model->DIObv_max = 1e99;
|
|
}
|
|
if(!model->DIOid_maxGiven) {
|
|
model->DIOid_max = 1e99;
|
|
}
|
|
if(!model->DIOpd_maxGiven) {
|
|
model->DIOpd_max = 1e99;
|
|
}
|
|
if(!model->DIOte_maxGiven) {
|
|
model->DIOte_max = 1e99;
|
|
}
|
|
if(!model->DIOrecEmissionCoeffGiven) {
|
|
model->DIOrecEmissionCoeff = 2;
|
|
}
|
|
if(!model->DIOrecSatCurGiven) {
|
|
model->DIOrecSatCur = 1e-14;
|
|
}
|
|
|
|
/* set lower limit of saturation current */
|
|
if (model->DIOsatCur < ckt->CKTepsmin)
|
|
model->DIOsatCur = ckt->CKTepsmin;
|
|
|
|
if(!model->DIOnomTempGiven) {
|
|
model->DIOnomTemp = ckt->CKTnomTemp;
|
|
}
|
|
|
|
if((!model->DIOresistGiven) || (model->DIOresist==0)) {
|
|
if (newcompat.ps || newcompat.lt) {
|
|
double rsdiode = 0.;
|
|
/* to improve convergence (sometimes) */
|
|
if (cp_getvar("diode_rser", CP_REAL, &rsdiode, 0) && rsdiode > 0) {
|
|
model->DIOconductance = 1./rsdiode;
|
|
model->DIOresist = rsdiode;
|
|
if (ft_ngdebug)
|
|
fprintf(stderr, "Diode series resistance in model %s set to %e Ohm\n", model->gen.GENmodName, rsdiode);
|
|
}
|
|
else
|
|
model->DIOconductance = 0.0;
|
|
}
|
|
else
|
|
model->DIOconductance = 0.0;
|
|
} else {
|
|
model->DIOconductance = 1/model->DIOresist;
|
|
}
|
|
if((!model->DIOresistSWGiven) || (model->DIOresistSW==0)) {
|
|
if (newcompat.ps || newcompat.lt) {
|
|
double rsdiode = 0.;
|
|
/* to improve convergence (sometimes) */
|
|
if (cp_getvar("diode_rser", CP_REAL, &rsdiode, 0) && rsdiode > 0) {
|
|
model->DIOconductanceSW = 1./rsdiode;
|
|
model->DIOresistSW = rsdiode;
|
|
if (ft_ngdebug)
|
|
fprintf(stderr, "Diode sidewall series resistance in model %s set to %e Ohm\n", model->gen.GENmodName, rsdiode);
|
|
}
|
|
else
|
|
model->DIOconductanceSW = 0.0;
|
|
}
|
|
else
|
|
model->DIOconductanceSW = 0.0;
|
|
} else {
|
|
model->DIOconductanceSW = 1/model->DIOresistSW;
|
|
}
|
|
|
|
if (!model->DIOrth0Given) {
|
|
model->DIOrth0 = 0;
|
|
}
|
|
if (!model->DIOcth0Given) {
|
|
model->DIOcth0 = 1e-5;
|
|
}
|
|
|
|
if(!model->DIOlengthMetalGiven) {
|
|
model->DIOlengthMetal = 0.0;
|
|
}
|
|
if(!model->DIOlengthPolyGiven) {
|
|
model->DIOlengthPoly = 0.0;
|
|
}
|
|
if(!model->DIOwidthMetalGiven) {
|
|
model->DIOwidthMetal = 0.0;
|
|
}
|
|
if(!model->DIOwidthPolyGiven) {
|
|
model->DIOwidthPoly = 0.0;
|
|
}
|
|
if(!model->DIOmetalOxideThickGiven) {
|
|
model->DIOmetalOxideThick = 1e4; /* 10k Angstrom */
|
|
}
|
|
if(!model->DIOpolyOxideThickGiven) {
|
|
model->DIOpolyOxideThick = 1e4; /* 10k Angstrom */
|
|
}
|
|
if(!model->DIOmetalMaskOffsetGiven) {
|
|
model->DIOmetalMaskOffset = 0.0;
|
|
}
|
|
if(!model->DIOpolyMaskOffsetGiven) {
|
|
model->DIOpolyMaskOffset = 0.0;
|
|
}
|
|
if(!model->DIOmaskOffsetGiven) {
|
|
model->DIOmaskOffset = 0.0;
|
|
}
|
|
|
|
/* loop through all the instances of the model */
|
|
for (here = DIOinstances(model); here != NULL ;
|
|
here=DIOnextInstance(here)) {
|
|
|
|
if(!here->DIOareaGiven) {
|
|
if((!here->DIOwGiven) && (!here->DIOlGiven)) {
|
|
here->DIOarea = model->DIOarea;
|
|
} else {
|
|
here->DIOarea = 1;
|
|
}
|
|
}
|
|
if(!here->DIOpjGiven) {
|
|
if((!here->DIOwGiven) && (!here->DIOlGiven)) {
|
|
here->DIOpj = model->DIOpj;
|
|
} else {
|
|
here->DIOpj = 0;
|
|
}
|
|
}
|
|
if(!here->DIOmGiven) {
|
|
here->DIOm = 1;
|
|
}
|
|
|
|
here->DIOcmetal = 0.0;
|
|
here->DIOcpoly = 0.0;
|
|
if (model->DIOlevel == 3) {
|
|
double wm, lm, wp, lp;
|
|
if((here->DIOwGiven) && (here->DIOlGiven)) {
|
|
here->DIOarea = (here->DIOw+model->DIOmaskOffset) * (here->DIOl+model->DIOmaskOffset) * here->DIOm * scale * scale;
|
|
here->DIOpj = (2 * (here->DIOw+model->DIOmaskOffset) + 2 * (here->DIOl+model->DIOmaskOffset)) * here->DIOm * scale;
|
|
}
|
|
if (here->DIOwidthMetalGiven)
|
|
wm = here->DIOwidthMetal;
|
|
else
|
|
wm = model->DIOwidthMetal;
|
|
if (here->DIOlengthMetalGiven)
|
|
lm = here->DIOlengthMetal;
|
|
else
|
|
lm = model->DIOlengthMetal;
|
|
if (here->DIOwidthPolyGiven)
|
|
wp = here->DIOwidthPoly;
|
|
else
|
|
wp = model->DIOwidthPoly;
|
|
if (here->DIOlengthPolyGiven)
|
|
lp = here->DIOlengthPoly;
|
|
else
|
|
lp = model->DIOlengthPoly;
|
|
here->DIOcmetal = CONSTepsSiO2 / model->DIOmetalOxideThick * here->DIOm
|
|
* (wm * scale + model->DIOmetalMaskOffset)
|
|
* (lm * scale + model->DIOmetalMaskOffset);
|
|
here->DIOcpoly = CONSTepsSiO2 / model->DIOpolyOxideThick * here->DIOm
|
|
* (wp * scale + model->DIOpolyMaskOffset)
|
|
* (lp * scale + model->DIOpolyMaskOffset);
|
|
}
|
|
here->DIOforwardKneeCurrent = model->DIOforwardKneeCurrent * here->DIOarea * here->DIOm;
|
|
here->DIOreverseKneeCurrent = model->DIOreverseKneeCurrent * here->DIOarea * here->DIOm;
|
|
here->DIOforwardSWKneeCurrent = model->DIOforwardSWKneeCurrent * here->DIOpj * here->DIOm;
|
|
here->DIOjunctionCap = model->DIOjunctionCap * here->DIOarea * here->DIOm;
|
|
here->DIOjunctionSWCap = model->DIOjunctionSWCap * here->DIOpj * here->DIOm;
|
|
|
|
here->DIOstate = *states;
|
|
*states += DIOnumStates;
|
|
if(ckt->CKTsenInfo && (ckt->CKTsenInfo->SENmode & TRANSEN) ){
|
|
*states += DIOnumSenStates * (ckt->CKTsenInfo->SENparms);
|
|
}
|
|
|
|
if(model->DIOresist == 0) {
|
|
|
|
here->DIOposPrimeNode = here->DIOposNode;
|
|
|
|
} else if(here->DIOposPrimeNode == 0) {
|
|
|
|
CKTnode *tmpNode;
|
|
IFuid tmpName;
|
|
|
|
error = CKTmkVolt(ckt,&tmp,here->DIOname,"internal");
|
|
if(error) return(error);
|
|
here->DIOposPrimeNode = tmp->number;
|
|
if (ckt->CKTcopyNodesets) {
|
|
if (CKTinst2Node(ckt,here,1,&tmpNode,&tmpName)==OK) {
|
|
if (tmpNode->nsGiven) {
|
|
tmp->nodeset=tmpNode->nodeset;
|
|
tmp->nsGiven=tmpNode->nsGiven;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!model->DIOresistSWGiven) {
|
|
|
|
here->DIOposSwPrimeNode = here->DIOposPrimeNode;
|
|
|
|
} else if(here->DIOposSwPrimeNode == 0) {
|
|
|
|
CKTnode *tmpNode;
|
|
IFuid tmpName;
|
|
|
|
error = CKTmkVolt(ckt,&tmp,here->DIOname,"internal_sw");
|
|
if(error) return(error);
|
|
here->DIOposSwPrimeNode = tmp->number;
|
|
if (ckt->CKTcopyNodesets) {
|
|
if (CKTinst2Node(ckt,here,1,&tmpNode,&tmpName)==OK) {
|
|
if (tmpNode->nsGiven) {
|
|
tmp->nodeset=tmpNode->nodeset;
|
|
tmp->nsGiven=tmpNode->nsGiven;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int selfheat = ((here->DIOtempNode > 0) && (here->DIOthermal) && (model->DIOrth0Given));
|
|
|
|
/* macro to make elements with built in test for out of memory */
|
|
#define TSTALLOC(ptr,first,second) \
|
|
do { if((here->ptr = SMPmakeElt(matrix, here->first, here->second)) == NULL){\
|
|
return(E_NOMEM);\
|
|
} } while(0)
|
|
|
|
TSTALLOC(DIOposPosPrimePtr,DIOposNode,DIOposPrimeNode);
|
|
TSTALLOC(DIOnegPosPrimePtr,DIOnegNode,DIOposPrimeNode);
|
|
TSTALLOC(DIOposPrimePosPtr,DIOposPrimeNode,DIOposNode);
|
|
TSTALLOC(DIOposPrimeNegPtr,DIOposPrimeNode,DIOnegNode);
|
|
TSTALLOC(DIOposPosPtr,DIOposNode,DIOposNode);
|
|
TSTALLOC(DIOnegNegPtr,DIOnegNode,DIOnegNode);
|
|
TSTALLOC(DIOposPrimePosPrimePtr,DIOposPrimeNode,DIOposPrimeNode);
|
|
if(model->DIOresistSWGiven) {
|
|
/* separate sidewall */
|
|
TSTALLOC(DIOposPosSwPrimePtr,DIOposNode,DIOposSwPrimeNode);
|
|
TSTALLOC(DIOnegPosSwPrimePtr,DIOnegNode,DIOposSwPrimeNode);
|
|
TSTALLOC(DIOposSwPrimePosPtr,DIOposSwPrimeNode,DIOposNode);
|
|
TSTALLOC(DIOposSwPrimeNegPtr,DIOposSwPrimeNode,DIOnegNode);
|
|
TSTALLOC(DIOposSwPrimePosSwPrimePtr,DIOposSwPrimeNode,DIOposSwPrimeNode);
|
|
}
|
|
|
|
if (selfheat) {
|
|
TSTALLOC(DIOtempPosPtr, DIOtempNode, DIOposNode);
|
|
TSTALLOC(DIOtempPosPrimePtr, DIOtempNode, DIOposPrimeNode);
|
|
TSTALLOC(DIOtempNegPtr, DIOtempNode, DIOnegNode);
|
|
TSTALLOC(DIOtempTempPtr, DIOtempNode, DIOtempNode);
|
|
TSTALLOC(DIOposTempPtr, DIOposNode, DIOtempNode);
|
|
TSTALLOC(DIOposPrimeTempPtr, DIOposPrimeNode, DIOtempNode);
|
|
TSTALLOC(DIOnegTempPtr, DIOnegNode, DIOtempNode);
|
|
if(model->DIOresistSWGiven) {
|
|
/* separate sidewall */
|
|
TSTALLOC(DIOtempPosSwPrimePtr, DIOtempNode, DIOposSwPrimeNode);
|
|
TSTALLOC(DIOposSwPrimeTempPtr, DIOposSwPrimeNode, DIOtempNode);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
return(OK);
|
|
}
|
|
|
|
int
|
|
DIOunsetup(
|
|
GENmodel *inModel,
|
|
CKTcircuit *ckt)
|
|
{
|
|
DIOmodel *model;
|
|
DIOinstance *here;
|
|
|
|
for (model = (DIOmodel *)inModel; model != NULL;
|
|
model = DIOnextModel(model))
|
|
{
|
|
for (here = DIOinstances(model); here != NULL;
|
|
here=DIOnextInstance(here))
|
|
{
|
|
|
|
if (here->DIOposPrimeNode > 0
|
|
&& here->DIOposPrimeNode != here->DIOposNode)
|
|
CKTdltNNum(ckt, here->DIOposPrimeNode);
|
|
here->DIOposPrimeNode = 0;
|
|
|
|
if(model->DIOresistSWGiven) {
|
|
/* separate sidewall */
|
|
if (here->DIOposSwPrimeNode > 0
|
|
&& here->DIOposSwPrimeNode != here->DIOposNode)
|
|
CKTdltNNum(ckt, here->DIOposSwPrimeNode);
|
|
here->DIOposSwPrimeNode = 0;
|
|
}
|
|
|
|
}
|
|
}
|
|
return OK;
|
|
}
|