<> <> <> <> <> <<>> 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 { <> IF deltaV > 0 THEN { vTemp: REAL_ vto + 0.5; IF vgsNew > vTemp THEN vgsNew_ vTemp; }; } ELSE IF vgsOld < vtoPlus35 THEN { <> vgsNew_ IF deltaV > 0 THEN MIN[vgsNew, vto + 4.0] -- increasing ELSE MAX[vgsNew, vto - 0.5]; } ELSE { -- decreasing <> 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]; <> 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]; < 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.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 = { <> VdX: NAT= 0; VgX: NAT= 1; VsX: NAT= 2; VbX: NAT= 3; <> oldVdX: NAT= 0; oldVgX: NAT= 1; oldVsX: NAT= 2; oldVbX: NAT= 3; <> 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); }; <> 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; <> 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>> <> 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> beta1_ parms[BetaX]*UFactor/clFactor; <> gammaD_ gamaSD; IF Vds <= 1.0E-8 THEN GOTO done; -- (id=0) 1050 <<730>> IF VgsT <= 0.0 THEN { -- else goto 900 <> 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 ); <> 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; }; <> VgdT_ Vgd - parms[VonX]; <> Cgb_ IF VgsT <= 0 THEN parms[CgbMX] ELSE 0; <> 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); <> 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] }; <> 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. <<>>