Level2Model.mesa
Copyright (C) 1985 by Xerox Corporation. All rights reserved.
Last Edited by:
Christian LeCocq January 27, 1987 3:43:30 pm PST
Sweetsun Chen, July 22, 1985 8:29:38 pm PDT
DIRECTORY
RealFns USING [ArcTan, Cos, Exp, Ln, SqRt],
Rope USING [ROPE],
ThymeGlobals USING [EnterModels, model, Retreat];
Level2Model: CEDAR PROGRAM
IMPORTS
RealFns,
ThymeGlobals
= BEGIN
LimitVgs: PROC [vgs, vgsOld, vto: REAL] RETURNS [vgsNew: REAL] = {
vtstHigh, vtstLow, vtoPlus35, deltaV: REAL;
vtstHigh← 2.0 + 2.0*ABS[vgsOld-vto];
vtstLow← vtstHigh/2.0 + 2.0;
vtoPlus35← vto + 3.5;
deltaV← vgs-vgsOld;
vgsNew← vgs;
IF vgsOld < vto THEN {
OFF
IF deltaV > 0 THEN {
vTemp: REAL← vto + 0.5;
IF vgsNew > vTemp THEN vgsNew← vTemp;
};
}
ELSE IF vgsOld < vtoPlus35 THEN {
middle
vgsNew←
IF deltaV > 0 THEN MIN[vgsNew, vto + 4.0] -- increasing
ELSE MAX[vgsNew, vto - 0.5];
}
ELSE { -- decreasing
on
IF deltaV > 0 THEN { -- staying on
IF deltaV > vtstHigh THEN vgsNew← vgsOld + vtstHigh; }
ELSE { -- going off
IF vgsNew < vtoPlus35 THEN vgsNew← MAX[vgsNew, vto + 2.0]
ELSE IF -deltaV > vtstLow THEN vgsNew ← vgsOld - vtstLow;
};
};
}; -- LimitVgs
LimitVds: PROC [vds, vdsOld: REAL] RETURNS [vdsNew: REAL] = {
vdsNew← vds;
IF vdsOld < 3.5 THEN {
vdsNew←
IF vdsNew <= vdsOld THEN MAX[vdsNew, -5]
ELSE MIN[vdsNew, 4]; }
ELSE {
IF vdsNew <= vdsOld THEN
{IF vdsNew < 3.5 THEN vdsNew← MAX[vdsNew, 2]}
ELSE vdsNew← MIN[vdsNew, 2.0 + 3.0*vdsOld];
};
}; -- LimitVds
LimitVbs: PROC [vbs, vbsOld, vt, vcrit: REAL] RETURNS [vbsNew: REAL] = {
vbsNew← vbs;
IF vbsNew > vcrit THEN {
vLimit: REAL← vt + vt;
deltaV: REAL← vbsNew - vbsOld;
IF ABS[deltaV] > vLimit THEN {
IF vbsOld <= 0 THEN vbsNew← vt*RealFns.Ln[vbsNew/vt]
ELSE {
arg: REAL← 1.0 + deltaV/vt;
vbsNew← IF arg <= 0 THEN vcrit
ELSE vbsOld + vt*RealFns.Ln[arg];
};
};
};
}; -- LimitVbs
Baum: PROC [VgsX, Vbin, Eta, Phi, Vbs, Vmax, L, UEffective, gammaD, SArg3: REAL]
RETURNS [Vdsat: REAL] = {
v1, v2, xv, a1, b1, c1, d1, a3, b3, A, B, C, R, S,
r3, s2, P, p0, p2, fi, y3, delta4, xValid: REAL← 0.0;
x4, poly4: ARRAY[1..8] OF REAL;
a4, b4, Sig1, Sig2: ARRAY[1..4] OF REAL;
Icount, Jcount: NAT← 0;
Icount ← Jcount← 0;
x4← poly4← ALL[0.0];
a4← b4← ALL[0.0];
Sig1← [1.0, -1.0, 1.0, -1.0];
Sig2← [1.0, 1.0, -1.0, -1.0];
v1← (VgsX-Vbin)/Eta+Phi-Vbs;
v2← Phi-Vbs;
xv← Vmax*L/UEffective;
a1← gammaD/0.75; b1← -2.0*(v1+xv); c1← -2.0*gammaD*xv;
d1← 2.0*v1*(v2+xv)-v2*v2-4.0/3.0*gammaD*SArg3;
A← -b1; B← a1*c1-4.0*d1; C← -d1*(a1*a1-4.0*b1)-c1*c1;
R← -A*A/3.0+B; r3← R*R*R;
S← 2.0*A*A*A/27.0-A*B/3.0+C; s2← S*S;
P← s2/4.0+r3/27.0; p0← ABS[P]; p2← RealFns.SqRt[p0];
get y3 value.
IF P < 0.0 THEN {
ro: REAL;
ro← RealFns.SqRt[s2/4.0+p0];
ro← RealFns.Ln[ro]/3.0;
ro← RealFns.Exp[ro];
fi← RealFns.ArcTan[-2.0*p2/S, 1]; -- arcTan(-2.0*p2/S);
y3← 2.0*ro*RealFns.Cos[fi/3.0]-A/3.0; }
ELSE { -- 550
p3, p4: REAL;
p3← RealFns.Exp[ RealFns.Ln[ABS[-S/2.0+p2]]/3.0 ];
p4← RealFns.Exp[ RealFns.Ln[ABS[-S/2.0-p2]]/3.0 ];
y3← p3+p4-A/3.0;
};
a3← RealFns.SqRt[a1*a1/4.0-b1+y3];
b3← RealFns.SqRt[y3*y3/4.0-d1];
from a3, b3, Sig1, Sig2, y3 -> a4, b4 -> delta4 -> x4
FOR I: NAT IN[1..4] DO
a4[I]← a1/2.0+Sig1[I]*a3;
b4[I]← y3/2.0+Sig2[I]*b3;
delta4← a4[I]*a4[I]/4.0-b4[I];
IF delta4 >= 0.0 THEN {
sqrtd4: REAL;
sqrtd4← RealFns.SqRt[delta4];
Icount← Icount+1;
x4[Icount]← a4[I]/(-2.0)+sqrtd4;
Icount← Icount+1;
x4[Icount]← a4[I]/(-2.0)-sqrtd4;
};
ENDLOOP;
FOR J: NAT IN [1..Icount] DO
tmp: REAL;
IF x4[J] <= 0.0 AND J < Icount THEN LOOP;
tmp← (x4[J]+a1);
tmp← (tmp*x4[J]+b1);
tmp← (tmp*x4[J]+c1);
poly4[J]← (tmp*x4[J]+d1);
IF ABS[poly4[J]] > 1.0E-6 AND J < Icount THEN LOOP;
Jcount← Jcount+1;
IF Jcount <= 1 THEN xValid← x4[J];
IF x4[J] <= xValid THEN xValid← x4[J];
ENDLOOP;
IF Jcount > 0 THEN Vdsat← xValid*xValid+Vbs-Phi
ELSE SIGNAL ThymeGlobals.Retreat["Error in immediate velocity saturation model"];
}; -- Baum
Grove: PROC [gammaD, VgsX, Vbin, Eta, Phi, Vbs: REAL]
RETURNS [Vdsat: REAL] = {
gammaD2, ArgV: REAL;
gammaD2← gammaD*gammaD;
ArgV← (VgsX-Vbin)/Eta+Phi-Vbs;
IF ArgV <= 0.0 THEN Vdsat← 0.0
ELSE {
Arg: REAL;
Arg ← RealFns.SqRt[1.0+4.0*ArgV/gammaD2];
Vdsat ← (VgsX-Vbin)/Eta+gammaD2*(1.0-Arg)/2.0;
IF Vdsat < 0.0 THEN Vdsat𡤀.0;
};
}; -- Grove
JcnCurrent: PROC [vt, v, isat: REAL] RETURNS[i: REAL] = {
t1: REAL← v/vt;
IF v > 0 THEN {
t1 ← MIN[t1, 85.0];
i ← isat*(RealFns.Exp[t1]- 1.0);
}
ELSE i← IF t1 <= -1.0 THEN -isat ELSE isat*t1;
}; -- JcnCurrent
Pair: TYPE= RECORD[f, df: REAL];
FandDF: PROC [v, p, sp, sp3: REAL] RETURNS[pair: Pair] = {
IF v <= 0 THEN {
pair.f← RealFns.SqRt[p-v];
pair.df← -0.5/pair.f;
}
ELSE { -- 130
pair.f← sp/(1.0+0.5*v/p);
pair.df← -0.5*pair.f*pair.f/sp3;
};
}; -- FandDF
SpiceLevel2: ThymeGlobals.model = {
args
VdX: NAT= 0;
VgX: NAT= 1;
VsX: NAT= 2;
VbX: NAT= 3;
oldArgs
oldVdX: NAT= 0;
oldVgX: NAT= 1;
oldVsX: NAT= 2;
oldVbX: NAT= 3;
parms
LX: NAT= 0;
WX: NAT= 1;
XdX: NAT= 2;
XjX: NAT= 3;
IssatX: NAT= 4;
IdsatX: NAT= 5;
BetaX: NAT= 6;
PhiX: NAT= 7;
SqRtPhiX: NAT= 8;
SqRtPhi3X: NAT= 9;
PbX: NAT= 10;
SqRtPbX: NAT= 11;
VbpX: NAT= 12;
VbiX: NAT= 13;
GammaX: NAT= 14;
LambdaX: NAT= 15;
VtX: NAT= 16;
UoX: NAT= 17;
VmaxX: NAT= 18;
QNfsX: NAT= 19;
NsubX: NAT= 20;
UexpX: NAT= 21;
NeffX: NAT= 22;
EtaX: NAT= 23;
FactorX: NAT= 24;
CoxX: NAT= 25;
CovGSX: NAT= 26;
CovGDX: NAT= 27;
CgbMX: NAT= 28;
Cgb23rdsX: NAT= 29;
CbsBottomX: NAT= 30;
CbdBottomX: NAT= 31;
CbsSWX: NAT= 32;
CbdSWX: NAT= 33;
CbsFwd1X: NAT= 34;
CbsFwd2X: NAT= 35;
CbdFwd1X: NAT= 36;
CbdFwd2X: NAT= 37;
MjX: NAT= 38;
MjswX: NAT= 39;
FcPbX: NAT= 40;
VonX: NAT= 41;
minVX: NAT= 42;
maxVX: NAT= 43;
SignX: NAT= 44;
reverse: BOOL;
sign, Vd, Vg, Vs, Vb, oldVd, oldVg, oldVs, oldVb, Vds, Vgs, Vbs, Vgd, Vbd, oldVds, oldVgs, oldVbs, oldVgd, oldVbd, Id, Cgb, Cgs, Cgd, Cbs, Cbd, t1, Vbin, XdBarg, XdSarg, ArgSS, ArgSD, DBArgS, DBArgD, ArgD, ArgS, gamaSD, DBXWD, DBXWS, gammaD, DgdDvb, VgsT, VgdT, SArg3, body, UFactor, UEffective, VgsX, bodyS, clFactor, Vdsat, ArgG, beta1, Vcrit: REAL;
SArg, BArg: Pair;
LimitNodeVoltage: PROC[oldV: REAL]
RETURNS[newV: REAL]= {
newV← MIN[parms[maxVX], MAX[parms[minVX], oldV]];
}; -- LimitNodeVoltage
Id← 0;
sign← parms[SignX];
Vd← LimitNodeVoltage[sign*args[VdX]];
Vg← LimitNodeVoltage[sign*args[VgX]];
Vs← LimitNodeVoltage[sign*args[VsX]];
Vb← LimitNodeVoltage[sign*args[VbX]];
oldVd← LimitNodeVoltage[sign*args[oldVdX]];
oldVg← LimitNodeVoltage[sign*args[oldVgX]];
oldVs← LimitNodeVoltage[sign*args[oldVsX]];
oldVb← LimitNodeVoltage[sign*args[oldVbX]];
Vds ← Vd - Vs; Vgs← Vg - Vs; Vbs← Vb - Vs;
Vgd ← Vg - Vd; Vbd← Vb - Vd;
oldVds ← oldVd - oldVs; oldVgs ← oldVg - oldVs; oldVbs← oldVb - oldVs;
oldVgd ← oldVg - oldVd; oldVbd ← oldVb - oldVd;
IF oldVds >= 0 THEN {
Vgs← LimitVgs[Vgs, oldVgs, parms[VonX]];
Vds← Vgs - Vgd;
Vds← LimitVds[Vds, oldVds];
Vgd← Vgs - Vds; }
ELSE {
Vgd← LimitVgs[Vgd, oldVgd, parms[VonX]];
Vds← Vgs - Vgd;
Vds← -LimitVds[-Vds, -oldVds];
Vgs← Vgd + Vds;
};
IF Vds >= 0 THEN {
Vcrit← parms[VtX]*RealFns.Ln[parms[VtX]/(1.414*parms[IssatX])];
Vbs← LimitVbs[Vbs, oldVbs, parms[VtX], Vcrit];
Vbd← Vbs - Vds;
args[VdX]← sign*(Vs + Vds);
args[VgX]← sign*(Vs + Vgs);
args[VbX]← sign*(Vs + Vbs); }
ELSE {
Vcrit← parms[VtX]*RealFns.Ln[parms[VtX]/(1.414*parms[IdsatX])];
Vbd← LimitVbs[Vbd, oldVbd, parms[VtX], Vcrit];
Vbs← Vbd + Vds;
args[VsX]← sign*(Vd - Vds);
args[VgX]← sign*(Vd + Vgd);
args[VbX]← sign*(Vd + Vbd);
};
get Ibs and Ibd
results[6]← sign*JcnCurrent[parms[VtX], Vbs, parms[IssatX]];
results[7]← sign*JcnCurrent[parms[VtX], Vbd, parms[IdsatX]];
IF Vds >= 0 THEN reverse← FALSE
ELSE {
reverse← TRUE; Vds← -Vds;
t1← Vbs; Vbs← Vbd; Vbd← t1;
t1← Vgs; Vgs← Vgd; Vgd← t1;
};
100
SArg← FandDF[Vbs, parms[PhiX], parms[SqRtPhiX], parms[SqRtPhi3X]];
BArg← FandDF[Vbd, parms[PhiX], parms[SqRtPhiX], parms[SqRtPhi3X]];
200, Calculate parms[VonX], narrow channel effect
Vbin← parms[VbiX]+parms[FactorX]*(parms[PhiX]-Vbs);
XdBarg← parms[XdX]*BArg.f;
XdSarg← parms[XdX]*SArg.f;
short channel effect with Vds # 0
ArgSS← ArgSD← DBArgS← DBArgD← 0.0;
IF parms[XjX] > 0.0 THEN {
ArgXS, ArgXD: REAL;
ArgXS← 1.0+2.0*XdSarg/parms[XjX];
ArgS← RealFns.SqRt[ArgXS];
ArgSS← 0.5*parms[XjX]/parms[LX]*(ArgS-1.0);
ArgXD← 1.0+2.0*XdBarg/parms[XjX];
ArgD← RealFns.SqRt[ArgXD];
ArgSD← 0.5*parms[XjX]/parms[LX]*(ArgD-1.0);
};
(205)
gamaSD← parms[GammaX]*(1.0-ArgSS-ArgSD);
DBXWD← parms[XdX]*BArg.df;
DBXWS← parms[XdX]*SArg.df;
IF parms[XjX] > 0.0 THEN {
DBArgS← 0.5/parms[LX]*DBXWS/ArgS;
DBArgD← 0.5/parms[LX]*DBXWD/ArgD;
};
DgdDvb← -parms[GammaX]*(DBArgS+DBArgD);
220
parms[VonX]← Vbin+gamaSD*SArg.f;
Vdsat← 0.0;
225
{
IF parms[QNfsX] = 0.0 THEN { -- 230
VgsT← Vgs-parms[VonX];
IF VgsT<=0.0 THEN GOTO done;
} -- (Id=0) 1050
ELSE {
CdOnCo, Xn: REAL;
CdOnCo← -(gamaSD*SArg.df+DgdDvb*SArg.f)+parms[FactorX];
Xn← 1.0+parms[QNfsX]/parms[CoxX]+CdOnCo;
parms[VonX]← parms[VonX]+parms[VtX]*Xn;
ArgG← 1.0/(parms[VtX]*Xn);
VgsT← Vgs-parms[VonX];
};
300
SArg3← SArg.f*SArg.f*SArg.f;
gammaD← gamaSD;
body← BArg.f*BArg.f*BArg.f-SArg3;
400, evaluate effective mobility and its derivatives
IF VgsT > parms[VbpX] THEN {
UFactor← RealFns.Exp[ parms[UexpX]*RealFns.Ln[parms[VbpX]/VgsT] ];
UEffective← parms[UoX]*UFactor; }
ELSE { -- 410
UFactor← 1.0;
UEffective← parms[UoX];
};
500 evaluate saturation voltage and its derivatives
according to grove-frohman equation
VgsX← Vgs;
gammaD← gamaSD/parms[EtaX];
IF parms[QNfsX]#0.0 THEN VgsX← MAX[Vgs, parms[VonX]];
Vdsat← IF gammaD <= 0.0 THEN MAX[0.0, (VgsX-Vbin)/parms[EtaX]]
ELSE Grove[gammaD, VgsX, Vbin, parms[EtaX], parms[PhiX], Vbs];
545
IF parms[VmaxX] > 0.0 THEN Vdsat← Baum[
VgsX, Vbin, parms[EtaX], parms[PhiX], Vbs, parms[VmaxX],
parms[LX], UEffective, gammaD, SArg3];
600, evaluate effective channel length and its derivatives
IF Vds # 0.0 THEN {
lFactor, ArgV, xdv, lv, ls, SArgV, BSArg: REAL;
gammaD← gamaSD;
BSArg← IF Vbs <= Vdsat THEN RealFns.SqRt[Vdsat-Vbs+parms[PhiX]]
ELSE parms[SqRtPhiX]/(1.0+0.5*(Vbs-Vdsat)/parms[PhiX]);
bodyS← BSArg*BSArg*BSArg-SArg3;
IF parms[VmaxX] <=0.0 THEN {
IF parms[NsubX] # 0.0 AND parms[LambdaX] <= 0.0 THEN {
Arg: REAL;
ArgV← (Vds-Vdsat)/4.0;
SArgV← RealFns.SqRt[1.0+ArgV*ArgV];
Arg← RealFns.SqRt[ArgV+SArgV];
lFactor← parms[XdX]/(parms[LX]*Vds);
parms[LambdaX]← lFactor*Arg;
};
}
ELSE { -- 603
ArgV← (VgsX-Vbin)/parms[EtaX]-Vdsat;
xdv← parms[XdX]/RealFns.SqRt[parms[NeffX]];
lv← parms[VmaxX]*xdv/(2.0*UEffective);
IF parms[NsubX] # 0.0 AND parms[LambdaX] <= 0.0 THEN {
ArgV← MAX[Vds-Vdsat, 0.0];
ls← RealFns.SqRt[lv*lv+ArgV];
lFactor← xdv/(parms[LX]*Vds);
parms[LambdaX]← lFactor*(ls-lv);
};
};
}; -- block 600
620, limit channel shortening at punch-through
{
leffective, deltaL, xwb, ld: REAL;
xwb← parms[XdX]*parms[SqRtPbX];
ld← parms[LX]-xwb;
clFactor← 1.0-parms[LambdaX]*Vds;
leffective← parms[LX]*clFactor;
deltaL← parms[LambdaX]*Vds*parms[LX];
IF parms[NsubX]=0.0 THEN xwb← 0.25E-6;
IF leffective<xwb THEN {
leffective← xwb/(1.0+(deltaL-ld)/xwb);
clFactor← leffective/parms[LX];
};
}; -- block 620
700 evaluate effective beta (effective kp)
beta1← parms[BetaX]*UFactor/clFactor;
test for mode of operation and branch appropriately
gammaD← gamaSD;
IF Vds <= 1.0E-8 THEN GOTO done; -- (id=0) 1050
730
IF VgsT <= 0.0 THEN { -- else goto 900
subthreshold region, if vdsat<=0.0 then id← 0.0
IF Vdsat > 0.0 THEN { -- 830
VdsOn, IdsOn, ArgGVgsT: REAL;
VdsOn← MIN[Vdsat,Vds];
ArgGVgsT← ArgG*VgsT;
IF Vds > Vdsat THEN body← bodyS;
IdsOn← beta1*( (parms[VonX] - Vbin -
parms[EtaX]*VdsOn*0.5)*VdsOn - gammaD*body/1.5 );
if arggvgst < -ln(idson) - 34.539 then id < 1e-15 ~ 0
Id← IF ArgGVgsT < (- 34.539 - RealFns.Ln[IdsOn]) THEN 0
ELSE sign*IdsOn*RealFns.Exp[ArgGVgsT];
};
}
ELSE
Id ←
IF Vds <= Vdsat THEN -- 900 LINEAR REGION
sign*beta1*((Vgs-Vbin-parms[EtaX]*Vds/2.0)*Vds-gammaD*body/1.5)
ELSE -- 1000 saturation region
sign*beta1*((Vgs-Vbin-parms[EtaX]*Vdsat/2.0)*
Vdsat-gammaD*bodyS/1.5);
EXITS
done=> NULL;
};
capacitances, and results
VgdT← Vgd - parms[VonX];
cgb:
Cgb← IF VgsT <= 0 THEN parms[CgbMX] ELSE 0;
cgs and cgd:
t1← VgsT+VgdT;
t1← t1*t1; -- (Vgs + Vgd - 2Von)^2
Cgs← IF VgsT <= 0.0 THEN parms[CovGSX] ELSE
IF VgdT <= 0.0 THEN parms[Cgb23rdsX] + parms[CovGSX]
ELSE parms[CovGSX] + parms[Cgb23rdsX]*(1.0 - VgdT*VgdT/t1);
Cgd← IF VgdT <= 0.0 THEN parms[CovGDX]
ELSE parms[CovGDX] + parms[Cgb23rdsX]*(1.0 - VgsT*VgsT/t1);
cbs and cbd
IF Vbs >= parms[FcPbX] THEN -- 520
Cbs← parms[CbsFwd1X] + Vbs*parms[CbsFwd2X]
ELSE {
t1← RealFns.Ln[1.0-Vbs/parms[PbX]];
Cbs← parms[CbsBottomX] * RealFns.Exp[-parms[MjX] * t1] + parms[CbsSWX] * RealFns.Exp[-parms[MjswX] * t1]
};
IF Vbd >= parms[FcPbX] THEN -- 520
Cbd← parms[CbdFwd1X] + Vbd*parms[CbdFwd2X]
ELSE {
t1← RealFns.Ln[1.0-Vbd/parms[PbX]];
Cbd← parms[CbdBottomX] * RealFns.Exp[-parms[MjX] * t1] + parms[CbdSWX] * RealFns.Exp[-parms[MjswX] * t1]
};
results order: id, cgb, cgs, cgd, cbs, cbd, ibs, ibd.
IF reverse THEN {
results[0]← -Id;
results[1]← Cgb;
results[2]← Cgd;
results[3]← Cgs;
results[4]← Cbd;
results[5]← Cbs;
}
ELSE {
results[0]← Id;
results[1]← Cgb;
results[2]← Cgs;
results[3]← Cgd;
results[4]← Cbs;
results[5]← Cbd;
};
}; -- SpiceLevel2
ThymeGlobals.EnterModels[["SpiceLevel2", SpiceLevel2, 4, 45, 8]];
END.
CHANGE LOG
Chen, 2/12/84, created to support Spice level2 model.
Chen, 2/19/84, added voltage limiting statements; modified Poly4 calculation.
Chen, 2/22/84, added protection for weak inversion current calculation to insure that Exp(ArgG*VgsT) doesn't blow up.
Chen, June 7, 1984 8:15:37 pm PDT, cedarized.
Chen, May 8, 1985 3:36:44 pm PDT, RealOps.SqRt => RealFns.SqRt, plus some minor changes.
Chen, July 22, 1985 8:29:18 pm PDT, => Cedar6.0.