File: [Cherry]<Thyme>Cedar5.2>System>spModels.mesa
Last Edited by: SChen, May 10, 1985 1:00:56 pm PDT
DIRECTORY
Atom USING [PutPropOnList],
IO USING [PutFR, PutR, real, rope],
Rope USING [ROPE, Length, Substr],
RealFns USING [Exp, SqRt],
spGlobals USING [argList, EnterModels, makeStringNB, model, Retreat],
spGlobalsExtras USING [CombineInstancesProc, IsUsefulProc];
spModels: CEDAR PROGRAM
IMPORTS Atom, IO, RealFns, Rope, spGlobals
EXPORTS spGlobals=
BEGIN
modelError: SIGNAL[s: Rope.ROPE]= CODE;
NFET: spGlobals.model= {
VgX: NAT= 0;
VsX: NAT= 1;
VdX: NAT= 2;
VbX: NAT= 3;
VtX: NAT= 0;
hKp: NAT= 1;
Phi2: NAT= 2;
Phi2fb: NAT= 3;
Kb: NAT= 4;
ftKb: NAT= 5;
hKbsq: NAT= 6;
qKbsq: NAT= 7;
Vfb: NAT= 8;
Cox: NAT= 9;
Cgsd0: NAT= 10;
Cgsdp: NAT= 11;
Ioff: NAT= 12;
Io: NAT= 13;
kTq: NAT= 14;
expMax: NAT= 15;
IdX: NAT= 0;
CgbX: NAT= 1;
CgsX: NAT= 2;
CgdX: NAT= 3;
Vg, Vs, Vd, Vb, Id: REAL;
Cgs, Cgd: REAL;
Vt, Vgst, Vgdt, VdSAT, Vsbphi2, Vdx, SqrtV: REAL;
t1, t2: REAL;
reverse: BOOLFALSE;
Vg← args[VgX];
Vb← args[VbX];
Vd← args[VdX];
Vs← args[VsX];
t1← Vb - Vs;
IF t1 > 0.0 THEN {
t1← t1/parms[kTq];
IF t1 >= parms[expMax] THEN SIGNAL spGlobals.Retreat["NFET -- Source."];
results[4]← parms[Io]*(RealFns.Exp[t1] - 1.0);
}
ELSE results[4]← -parms[Io];
t2← Vb - Vd;
IF t2 > 0.0 THEN {
t2← t2/parms[kTq];
IF t2 >= parms[expMax] THEN SIGNAL spGlobals.Retreat["NFET -- Drain."];
results[5]← parms[Io]*(RealFns.Exp[t2] - 1.0);
}
ELSE results[5]← -parms[Io];
IF Vs > Vd THEN {
reverse← TRUE;
t1← Vs;
Vs← Vd;
Vd← t1;
};
Vsbphi2← MAX[Vs - Vb + parms[Phi2], 0.0];
SqrtV← RealFns.SqRt[MAX[0.0, parms[qKbsq] + Vg - parms[Vfb] - Vb]];
Vt← IF Vs= Vb THEN parms[VtX]
ELSE parms[Phi2fb] + parms[Kb]*RealFns.SqRt[Vsbphi2];
Vgst← Vg - Vs - Vt;
IF Vgst > 0 THEN {
VdSAT← Vg - parms[Phi2fb] + parms[hKbsq] - parms[Kb]*SqrtV;
IF VdSAT < Vs THEN SIGNAL modelError["Negative VdSAT"];
Vdx← IF Vd > VdSAT THEN VdSAT ELSE Vd;
t1← Vsbphi2*RealFns.SqRt[Vsbphi2];
t2← Vdx - Vb + parms[Phi2];
t2← t2*RealFns.SqRt[t2];
Id← parms[hKp]*((2.0*(Vg - parms[Phi2fb]) - Vs - Vdx)*
(Vdx - Vs) + parms[ftKb]*(t1 - t2));
}
ELSE Id← parms[Ioff];
IF Id < 0.0 THEN
IF Id < -parms[Ioff] THEN SIGNAL modelError["Negative drain current"]
ELSE Id← parms[Ioff];
t1← Vg - Vb - parms[Vfb];
IF t1 <= 0.0 THEN results[CgbX]← parms[Cox]
ELSE results[CgbX]← 0.5*parms[Kb]*parms[Cox]/SqrtV;
Vgdt← Vg - Vd - Vt;
t1← Vgst + Vgdt;
t1← t1*t1;
IF Vgst <= 0.0 THEN Cgs← parms[Cgsdp]
ELSE
IF Vgdt < 0.0 THEN Cgs← parms[Cgsd0] + parms[Cgsdp]
ELSE Cgs← parms[Cgsdp] + parms[Cgsd0]*(1.0 - Vgdt*Vgdt/t1);
IF Vgdt <= 0.0 THEN Cgd← parms[Cgsdp]
ELSE Cgd← parms[Cgsdp] + parms[Cgsd0]*(1.0 - Vgst*Vgst/t1);
IF reverse THEN {
results[IdX] ← - Id;
results[CgsX]← Cgd;
results[CgdX]← Cgs;
}
ELSE {
results[IdX] ← Id;
results[CgsX]← Cgs;
results[CgdX]← Cgd;
};
}; -- NFET
PFET: spGlobals.model= {
VgX: NAT= 0;
VsX: NAT= 1;
VdX: NAT= 2;
VbX: NAT= 3;
VtX: NAT= 0;
hKp: NAT= 1;
Phi2: NAT= 2;
Phi2fb: NAT= 3;
Kb: NAT= 4;
ftKb: NAT= 5;
hKbsq: NAT= 6;
qKbsq: NAT= 7;
Vfb: NAT= 8;
Cox: NAT= 9;
Cgsd0: NAT= 10;
Cgsdp: NAT= 11;
Ioff: NAT= 12;
Io: NAT= 13;
kTq: NAT= 14;
expMax: NAT= 15;
IdX: NAT= 0;
CgbX: NAT= 1;
CgsX: NAT= 2;
CgdX: NAT= 3;
Vg, Vs, Vd, Vb, Id: REAL;
Cgs, Cgd: REAL;
Vt, Vgst, Vgdt, VdSAT, Vsbphi2, Vdx, SqrtV: REAL;
t1, t2: REAL;
reverse: BOOLFALSE;
Vg← args[VgX];
Vb← args[VbX];
Vd← args[VdX];
Vs← args[VsX];
t1← Vs - Vb;
IF t1 > 0.0 THEN {
t1← t1/parms[kTq];
IF t1 >= parms[expMax] THEN SIGNAL spGlobals.Retreat["PFET -- Source."];
results[4]← parms[Io]*(RealFns.Exp[t1] - 1.0);
}
ELSE results[4]← -parms[Io];
t2← Vd - Vb;
IF t2 > 0.0 THEN {
t2← t2/parms[kTq];
IF t2 >= parms[expMax] THEN SIGNAL spGlobals.Retreat["PFET -- Drain."];
results[5]← parms[Io]*(RealFns.Exp[t2] - 1.0);
}
ELSE results[5]← -parms[Io];
IF Vs < Vd THEN {
reverse← TRUE;
t1← Vs;
Vs← Vd;
Vd← t1;
};
Vsbphi2← MIN[Vs - Vb + parms[Phi2], 0.0];
SqrtV← RealFns.SqRt[-MIN[0.0, -parms[qKbsq] + Vg - parms[Vfb] - Vb]];
Vt← IF Vs= Vb THEN parms[VtX]
ELSE parms[Phi2fb] - parms[Kb]*RealFns.SqRt[-Vsbphi2];
Vgst← Vg - Vs - Vt;
IF Vgst < 0 THEN {
VdSAT← Vg - parms[Phi2fb] - parms[hKbsq] + parms[Kb]*SqrtV;
IF VdSAT > Vs THEN SIGNAL modelError["Positive VdSAT"];
Vdx← IF Vd < VdSAT THEN VdSAT ELSE Vd;
t1← Vsbphi2*RealFns.SqRt[-Vsbphi2];
t2← Vdx - Vb + parms[Phi2];
t2← t2*RealFns.SqRt[-t2];
Id← parms[hKp]*((2.0*(Vg - parms[Phi2fb]) - Vs - Vdx)*(Vdx - Vs) - parms[ftKb]*(t1 - t2));
}
ELSE Id← parms[Ioff];
IF Id < 0.0 THEN
IF Id < -parms[Ioff]THEN SIGNAL modelError["Negative drain current"]
ELSE Id← parms[Ioff];
t1← Vg - Vb - parms[Vfb];
IF t1 >= 0.0 THEN results[CgbX]← parms[Cox]
ELSE results[CgbX]← 0.5*parms[Kb]*parms[Cox]/SqrtV;
Vgdt← Vg - Vd - Vt;
t1← Vgst + Vgdt;
t1← t1*t1;
IF Vgst >= 0.0 THEN Cgs← parms[Cgsdp]
ELSE IF Vgdt > 0.0 THEN Cgs← parms[Cgsd0] + parms[Cgsdp]
ELSE Cgs← parms[Cgsdp] + parms[Cgsd0]*(1.0 - Vgdt*Vgdt/t1);
IF Vgdt >= 0.0 THEN Cgd← parms[Cgsdp]
ELSE Cgd← parms[Cgsdp] + parms[Cgsd0]*(1.0 - Vgst*Vgst/t1);
IF reverse THEN {
results[IdX] ← -Id;
results[CgsX]← Cgd;
results[CgdX]← Cgs;
}
ELSE {
results[IdX] ← Id;
results[CgsX]← Cgs;
results[CgdX]← Cgd;
};
}; -- PFET
dcDiode: spGlobals.model= {
Vd, e: REAL;
Va: NAT= 0;
Vc: NAT= 1;
Io: NAT= 0;
kTq: NAT= 1;
expMax: NAT= 2;
Vd← args[Va] - args[Vc];
IF Vd + parms[kTq] <= 0.0 THEN results[0]← -parms[Io]
ELSE {
e← Vd/parms[kTq];
IF e >= parms[expMax] THEN SIGNAL spGlobals.Retreat["dcDiode."];
results[0]← parms[Io]*(RealFns.Exp[e] - 1.0);
};
}; -- dcDiode
CSIM: spGlobals.model= {
args:
VgX: NAT= 0;
VsX: NAT= 1;
VdX: NAT= 2;
VbX: NAT= 3;
The 8 electrical parameters of CSIM model. cf. Scharfetter's memo of 12/30/82.
vfb: NAT= 0;
twoPhiF: NAT= 1;
k1: NAT= 2;
k2: NAT= 3;
eta: NAT= 4;
beta0: NAT= 5;
u0: NAT= 6;
u1: NAT= 7;
The 6 capacitance parameters for MOSAID model. cf. MOSAID documentation by Dick Foss.
MCgbo: NAT= 8;
Cgbo23rds: NAT= 9;
Cov: NAT= 10;
Cbso: NAT= 11;
Cbdo: NAT= 12;
phiB: NAT= 13;
other parameters:
Ios: NAT= 14;
Iod: NAT= 15;
kTq: NAT= 16;
expMax: NAT= 17;
NoCap: NAT= 18;
nChannel: NAT= 19; -- -1: pType; else nType;
results:
IdX : NAT= 0;
CgbX: NAT= 1;
CgsX: NAT= 2;
CgdX: NAT= 3;
CbsX: NAT= 4;
CbdX: NAT= 5;
Ibs: NAT= 6;
Ibd: NAT= 7;
Vg, Vs, Vd, Vb, Vds, Vgs, Vbs, Vbd: REAL;
Id, Cgs, Cgd, Cbs, Cbd: REAL;
reverse: BOOLFALSE;
t1, TwoPhiFMVbs, Sqrt2PhiFMVbs, EtaVds, VgsMVth, VgdMVth, Vth: REAL;
ArgRope: PROC [ix: NAT] RETURNS [r: Rope.ROPE] = {
voltage: Rope.ROPE = IO.PutR[IO.real[args[ix]]];
r ← IO.PutFR["%g (%g V)",
IO.rope[spGlobals.makeStringNB[
handle: parms.handle,
n: parms.modFunc.arguments[ix],
b: NIL]],
IO.rope[Rope.Substr[base: voltage, len: MIN[voltage.Length, 5]]]];
}; -- ArgRope
IF parms[nChannel]= -1 THEN {
Vg← -args[VgX]; Vb← -args[VbX];
Vd← -args[VdX]; Vs← -args[VsX];
}
ELSE {
parms[nChannel]← 1;
Vg← args[VgX]; Vb← args[VbX]; Vd← args[VdX]; Vs← args[VsX];
};
get Ibs:
IF Vb > Vs THEN {
t1← (Vb-Vs)/parms[kTq];
IF t1 > parms[expMax] THEN SIGNAL spGlobals.Retreat[
IO.PutFR["CSIM -> Vbs forward biased too much: b = %g, s = %g",
IO.rope[ArgRope[VbX]], IO.rope[ArgRope[VsX]]]];
IF (Vb-Vs) >= parms[twoPhiF] THEN {
Vb ← Vs+parms[twoPhiF]-0.001; -- recover
t1← (Vb-Vs)/parms[kTq]
};
results[Ibs]← parms[nChannel]*parms[Ios]*(RealFns.Exp[t1]- 1.0);
}
ELSE results[Ibs]← -parms[nChannel]*parms[Ios];
get Ibd:
IF Vb > Vd THEN {
t1← (Vb-Vd)/parms[kTq];
IF t1 > parms[expMax] THEN SIGNAL spGlobals.Retreat[
IO.PutFR["CSIM -> Vbd forward biased too much: b = %g, d = %g",
IO.rope[ArgRope[VbX]], IO.rope[ArgRope[VdX]]]];
results[Ibd]← parms[nChannel]*parms[Iod]*(RealFns.Exp[t1]- 1.0);
}
ELSE results[Ibd]← -parms[nChannel]*parms[Iod];
IF Vs > Vd THEN {reverse← TRUE; t1← Vs; Vs← Vd; Vd← t1; };
Vds← Vd - Vs; Vgs← Vg - Vs; Vbs← Vb - Vs; Vbd← Vb - Vd;
If it ever gets here, Vbs must be less than 2phif. cf. expMax calculation in ThymeBasics.thy
TwoPhiFMVbs← parms[twoPhiF]-Vbs;
Sqrt2PhiFMVbs← RealFns.SqRt[TwoPhiFMVbs];
EtaVds← parms[eta]*Vds;
Vth← parms[vfb]+parms[twoPhiF]+parms[k1]*Sqrt2PhiFMVbs-EtaVds
-parms[k2]*TwoPhiFMVbs;
VgsMVth← Vgs - Vth;
IF VgsMVth <= 0.0 THEN Id← 0
ELSE { -- Vgs>Vth:
g: REAL← 1.0-1.0/(1.744+0.8364*TwoPhiFMVbs);
a: REAL← 1.0 + 0.5*g*parms[k1]/Sqrt2PhiFMVbs;
alpha: REAL← a*(1.0 + parms[u1]*VgsMVth); -- a(1+u1*(Vgs-Vth))
VdSat: REAL← VgsMVth/alpha;
BetaP: REAL← parms[nChannel]*parms[beta0]/(1.0+parms[u0]*VgsMVth); -- beta0/(1+u0(Vgs-Vth))
Id← IF Vds<VdSat THEN BetaP*Vds*(VgsMVth - 0.5*alpha*Vds)
ELSE BetaP*VgsMVth*VgsMVth/(2.0*alpha);
}; -- end of Vgs>Vth
IF parms[NoCap]#0 THEN {
results[CgbX]← results[CgsX]← results[CgdX]← results[CbsX]← results[CbdX]← 0;
results[IdX]← IF reverse THEN -Id ELSE Id;
RETURN;
};
VgdMVth← Vg - Vd - Vth;
Cgb:
results[CgbX]← IF VgsMVth <= 0 THEN parms[MCgbo] ELSE 0;
Cgs and Cgd:
t1← VgsMVth+VgdMVth;
t1← t1*t1; -- (Vgs + Vgd - 2Vth)^2
Cgs←
IF VgsMVth <= 0.0 THEN parms[Cov]
ELSE
IF VgdMVth <= 0.0 THEN parms[Cgbo23rds] + parms[Cov]
ELSE parms[Cov] + parms[Cgbo23rds]*(1.0 - VgdMVth*VgdMVth/t1);
Cgd←
IF VgdMVth <= 0.0 THEN parms[Cov]
ELSE parms[Cov] + parms[Cgbo23rds]*(1.0 - VgsMVth*VgsMVth/t1);
Cbs and Cbd: Since phiB>2phif, the following should never blow up.
Cbs← parms[Cbso]/RealFns.SqRt[1.0 - Vbs/parms[phiB]];
Cbd← parms[Cbdo]/RealFns.SqRt[1.0 - Vbd/parms[phiB]];
IF reverse THEN {
results[IdX]← -Id;
results[CgsX]← Cgd;
results[CgdX]← Cgs;
results[CbsX]← Cbd;
results[CbdX]← Cbs;
}
ELSE {
results[IdX]← Id;
results[CgsX]← Cgs;
results[CgdX]← Cgd;
results[CbsX]← Cbs;
results[CbdX]← Cbd;
};
}; -- CSIM
acDiode: spGlobals.model= {
Vc: NAT= 0; -- cathode voltage index
Va: NAT= 1; -- anode voltage index
Co: NAT= 0;
Pb: NAT= 1;
Vt: NAT= 2;
Io: NAT= 3;
expMax: NAT= 4;
C: NAT= 0;
I: NAT= 1;
t1: REAL;
V: REAL← args[Va] - args[Vc];
IF V > 0 THEN { -- forward biased
t1← V/parms[Vt];
IF t1 >= parms[expMax] THEN SIGNAL spGlobals.Retreat[
"acDiode forward biased too much"];
results[I]← -parms[Io]*(RealFns.Exp[t1]- 1.0);
}
ELSE results[I]← parms[Io];
results[C]← parms[Co]/RealFns.SqRt[1.0 - V/parms[Pb]];
}; -- acDiode
acDiodeIsUseful: PROC [parms: spGlobals.argList] RETURNS [isUseful: BOOL] = {
Co: NAT= 0;
Pb: NAT= 1;
Vt: NAT= 2;
Io: NAT= 3;
expMax: NAT= 4;
isUseful ← parms[Co]#0 OR parms[Io]#0;
}; -- acDiodeIsUseful
acDiodeCombineInstances: PROC [parmsA, parmsB: spGlobals.argList]
RETURNS [ success: BOOL ] = {
Co: NAT= 0;
Pb: NAT= 1;
Vt: NAT= 2;
Io: NAT= 3;
expMax: NAT= 4;
IF (success ← (parmsA[Pb] = parmsB[Pb] AND parmsA[Vt] = parmsB[Vt]
AND parmsA[expMax] = parmsB[expMax])) THEN {
parmsA[Co] ← parmsA[Co]+parmsB[Co];
parmsA[Io] ← parmsA[Io]+parmsB[Io];
};
}; -- acDiodeCombineInstances
spGlobals.EnterModels[["CSIM", CSIM, 4, 20, 8]];
spGlobals.EnterModels[["acDiode", acDiode, 2, 5, 2, Atom.PutPropOnList[
Atom.PutPropOnList[NIL,
$IsUsefulProc,
NEW[spGlobalsExtras.IsUsefulProc ← acDiodeIsUseful]],
$CombineInstancesProc,
NEW[spGlobalsExtras.CombineInstancesProc ← acDiodeCombineInstances]]]];
spGlobals.EnterModels[["dcDiode", dcDiode, 2, 3, 1]];
spGlobals.EnterModels[["NFET", NFET, 4, 16, 6]];
spGlobals.EnterModels[["PFET", PFET, 4, 16, 6]];
END.
CHANGE LOG
Wilhelm, April 12, 1982 9:28 AM
Barth, 7-May-82 10:43:16 PDT
Chen, April 29, 1983 12:03 PM, added and "enter"ed CSIM and acDiode model.
Chen, June 7, 1984 8:14:49 pm PDT, cedarized.
McCreight, April 2, 1985 5:04:05 pm PST,
1. added acDiodeIsUseful, acDiodeCombineInstances, and related stuff.
2. added more information for the user when bs or bd junctions are forward biased too much.
Chen, May 9, 1985 6:21:28 pm PDT, RealOps.SqRt => RealFns.SqRt .