File: BSimModelImplEval.mesa
Copyright (C) 1985 by Xerox Corporation. All rights reserved.
Last Edited by:
Last Edited by: Jacobi June 10, 1986 7:17:27 pm PDT
Pradeep Sindhu March 19, 1986 9:44:46 pm PST
McCreight, May 31, 1985 2:44:17 pm PDT
Sweetsun Chen, July 22, 1985 8:14:02 pm PDT
Christian Jacobi, June 10, 1986 7:15:54 pm PDT
DIRECTORY
Atom USING [GetProp, PutProp, PutPropOnList],
BSimModel,
IO USING [int, PutR, PutF, PutFR, real, rope, STREAM],
Process USING [Yield],
Real USING [RoundLI],
RealFns USING [Exp, SqRt],
Rope USING [Length, ROPE, Substr],
spGlobals USING [argList, EnterModels, makeStringNB, RefModBody, ModelBody, Retreat],
spGlobalsExtras USING [CombineInstancesProc, IsUsefulProc];
BSimModelImplEval: CEDAR PROGRAM IMPORTS Atom, BSimModel, IO, Process, Real, RealFns, Rope, spGlobals =
BEGIN
CSIM2 Parameters -- in addition to the ones in BSimModel
capacitance parameters for MOSAID model. cf. MOSAID documentation by Dick Foss.
MCgbo: NAT= 22;
Cgbo23rds: NAT= 23;
other parameters:
maxFwdBias: NAT=24;
NoCap: NAT= 25;
nChannel: NAT= 26; -- -1: pType; else nType;
BSim: PROC [args, oldArgs, parms, results: spGlobals.argList] -- spGlobals.model -- = {
VgX: NAT= 0; -- arguments
VsX: NAT= 1;
VdX: NAT= 2;
VbX: NAT= 3;
results:
IdX : NAT= 0;
CgbX: NAT= 1;
CgsX: NAT= 2;
CgdX: NAT= 3;
Vg, Vs, Vd, Vb, Vds, Vgs, Vbs, Vbd: REAL;
Vth, VgsMVth, Id: REAL;
Cgs, Cgd: REAL ← 0.0;
Vdd: REAL = parms[BSimModel.vdd];
reverse: BOOLFALSE;
realTwoPhiFMVbs, sqrt2PhiFMVbs, TwoPhiFMVbs: REAL;
Invert voltages if transistor is p-type.
IF parms[nChannel]= -1 THEN {
Vg← -args[VgX]; Vb← -args[VbX];
Vd← -args[VdX]; Vs← -args[VsX]}
ELSE {
Vg← args[VgX]; Vb← args[VbX];
Vd← args[VdX]; Vs← args[VsX]};
Switch source and drain if biases are reversed
IF Vs > Vd THEN {t1: REAL = Vs; Vs← Vd; Vd← t1; reverse← TRUE};
Vds← Vd - Vs; Vgs← Vg - Vs; Vbs← Vb - Vs; Vbd← Vb - Vd;
realTwoPhiFMVbs ← parms[BSimModel.phif2]-Vbs;
IF Vbs <= 0 THEN
{TwoPhiFMVbs ← realTwoPhiFMVbs;
sqrt2PhiFMVbs ← RealFns.SqRt[TwoPhiFMVbs]}
ELSE
{sqrt2PhiFMVbs ← RealFns.SqRt[parms[BSimModel.phif2]]/(1.0+0.5*Vbs/parms[BSimModel.phif2]);
TwoPhiFMVbs ← sqrt2PhiFMVbs*sqrt2PhiFMVbs};
Calculate Ids
{
VdsMVdd: REAL = Vds-Vdd;
Eta: REAL = MAX[0.0, parms[BSimModel.eta]+parms[BSimModel.x2eta]*TwoPhiFMVbs+parms[BSimModel.x3eta]*VdsMVdd];
IF realTwoPhiFMVbs < 0.0 THEN
WITH parms.modFunc.body SELECT FROM
rm: spGlobals.RefModBody => {
prevVbs: REAL = parms[nChannel]*(rm.oldArgVector[VbX]-rm.oldArgVector[(IF reverse THEN VdX ELSE VsX)]);
IF Vbs > prevVbs+0.1 THEN
SIGNAL spGlobals.Retreat[NIL];
.. otherwise retreating won't do much good..
};
ENDCASE => ERROR;
IF realTwoPhiFMVbs < parms[maxFwdBias] THEN {
parms[maxFwdBias] ← realTwoPhiFMVbs+0.1; -- don't complain again for another 100 mV
parms.handle.msgStream.PutF["\nBSIM inaccurate because source(%g)-body(%g) diode is forward-biased to %d volts.\n",
IO.rope[ArgRope[args, parms, IF reverse THEN VdX ELSE VsX]],
IO.rope[ArgRope[args, parms, VbX]],
IO.real[Vbs]];
Process.Yield[];
};
Vth ← parms[BSimModel.vfb]+parms[BSimModel.phif2]+parms[BSimModel.k1]*sqrt2PhiFMVbs
-parms[BSimModel.k2]*TwoPhiFMVbs-Eta*Vds; -- calculate threshold voltage
IF (VgsMVth ← MAX[0, Vgs - Vth]) <= 0.0 THEN Id ← 0.0
ELSE { -- Vgs>Vth:
u: REAL = 1.0+MAX[0.0, parms[BSimModel.u0]+parms[BSimModel.x2u0]*Vbs]*VgsMVth;
u1Sum: REAL = MAX[0.0, parms[BSimModel.u1]+parms[BSimModel.x2u1]*Vbs+parms[BSimModel.x3u1]*VdsMVdd];
temp: REAL = parms[BSimModel.beta0]+parms[BSimModel.x2beta0]*Vbs; -- calculate beta
tempA: REAL = (parms[BSimModel.beta0sat]+parms[BSimModel.x2beta0sat]*Vbs)/temp-1.0;
x3vddvdd: REAL = parms[BSimModel.x3beta0sat]*Vdd/temp - tempA;
vRatio: REAL = Vds/Vdd;
beta: REAL = temp*(1.0+(tempA-x3vddvdd*(1.0-vRatio))*vRatio);
g: REAL = 1.0-1.0/(1.744+0.8364*TwoPhiFMVbs); -- calculate alpha
alpha -- also known as a -- : REAL = 1.0+0.5*g*parms[BSimModel.k1]/sqrt2PhiFMVbs;
VgsMVthOverAlpha: REAL = VgsMVth/alpha;
vc: REAL = u1Sum*VgsMVthOverAlpha;
k: REAL = 0.5*(1.0+vc+RealFns.SqRt[1.0+2.0*vc]);
VdsSat: REAL = VgsMVthOverAlpha/RealFns.SqRt[k];
IdsDiff: REAL ← 0.0; -- subthreshold current
IF parms[BSimModel.n0]#0 OR parms[BSimModel.x2nb]#0 OR parms[BSimModel.x3nd]#0 THEN {
nSubThPar: REAL = parms[BSimModel.n0]+parms[BSimModel.x3nd]*Vds+parms[BSimModel.x2nb]*Vbs;
Vtm: REAL = 0.0258512*parms[BSimModel.tempK]/300.0;
VtmSq: REAL = Vtm*Vtm;
IdsSubTh: REAL = VtmSq*RealFns.Exp[(1.0-RealFns.Exp[-Vds-Vtm])*(Vgs-Vth)/(Vtm*nSubThPar)+1.8];
IdsDiffLim: REAL = 4.5*VtmSq;
IdsDiff ← IdsSubTh*IdsSubTh/(IdsSubTh+IdsDiffLim);
};
Id ← parms[nChannel]*beta*((IF Vds < VdsSat
THEN Vds*(VgsMVth - 0.5*alpha*Vds)/(u*(1.0+u1Sum*Vds))
ELSE VgsMVth*VgsMVth/(2.0*u*alpha*k))+IdsDiff);
Id ← Id; -- for breakpoints
}; -- end of Vgs>Vth
}; --end of calculating Ids
Calculate capacitances
{
VgdMVth: REAL;
SELECT TRUE FROM
parms[NoCap] # 0 => results[CgbX] ← 0.0;
VgsMVth <= 0.0 => -- no channel
results[CgbX]← parms[MCgbo];
(VgdMVth ← Vg - Vd - Vth) <= 0.0 => -- partial channel (source inverted, drain not)
BEGIN
results[CgbX]← 0.0;
Cgs ← parms[Cgbo23rds];
Cgd ← 0;
END;
ENDCASE => -- channel complete (both source and drain inverted)
BEGIN
t1: REAL = VgsMVth+VgdMVth;
t2: REAL = t1*t1; -- (Vgs + Vgd - 2Vth)^2
results[CgbX]← 0.0;
Cgs ← parms[Cgbo23rds]*(1.0 - VgdMVth*VgdMVth/t2);
Cgd ← parms[Cgbo23rds]*(1.0 - VgsMVth*VgsMVth/t2);
END;
};
IF reverse THEN {
results[IdX]← -Id;
results[CgsX]← Cgd;
results[CgdX]← Cgs}
ELSE {
results[IdX]← Id;
results[CgsX]← Cgs;
results[CgdX]← Cgd};
}; -- BSim
oldBSimModelBody: spGlobals.RefModBody ← NIL;
newBSimModelBody: spGlobals.RefModBody ← NIL;
distinguishedBSimModelBody: spGlobals.RefModBody ← NIL;
nFavorites: NAT = 50;
Favorites: TYPE = RECORD [
p: SEQUENCE size: NAT OF REF BSimModel.ProcessParams];
BSimInit: PROC [args, oldArgs, parms, results: spGlobals.argList] -- spGlobals.model -- = {
L: NAT = 0; -- parameter indices
W: NAT = 1;
DUTZRatio: NAT = 2;
channelType: INT = Real.RoundLI[parms[nChannel]];
doTrap: BOOLFALSE;
IF ~ channelType IN [-1..1] THEN {
lMicrons, wMicrons, dutZRatio: REAL;
lEff, wEff, cox, zRatio -- w/l -- : REAL;
bsimpp: REF BSimModel.ProcessParams;
processIndex: INT = ABS[channelType];
f: REF ANY;
favorites: REF Favorites;
IF (f ← Atom.GetProp[$BSimModel, $Favorites]) = NIL THEN
Atom.PutProp[$BSimModel, $Favorites, (f ← NEW[Favorites[nFavorites]])];
favorites ← NARROW[f];
IF processIndex >= favorites.size OR (bsimpp ← favorites[processIndex]) = NIL THEN {
bsimpp ← BSimModel.ReadProcessFile[IO.PutFR["/DATools/DATools6.0/Thyme/BSimProcess-%d.process", IO.int[ABS[channelType]]]];
IF processIndex IN [2..favorites.size) THEN favorites[processIndex] ← bsimpp;
};
parms[nChannel] ← IF channelType<0 THEN -1 ELSE 1;
From this point on, refill the parms vector with calculated parameters as a function of lMicrons and wMicrons
lMicrons ← parms[L];
wMicrons ← parms[W];
doTrap ← parms[DUTZRatio]<0;
dutZRatio ← (IF parms[DUTZRatio]>0 THEN parms[DUTZRatio] ELSE bsimpp.dutW/bsimpp.dutL);
lEff ← lMicrons+bsimpp.params[BSimModel.beta0][2];
wEff ← wMicrons+bsimpp.params[BSimModel.beta0][3];
zRatio ← bsimpp.betaFudgeFactor*wEff/lEff;
cox ← 3.9*8.854E-10/bsimpp.toxMicrons; -- farads/sq cm
FOR i: INT IN [0..BSimModel.nBasicBSimParams) DO
t: REAL = bsimpp.params[i][1]+(bsimpp.params[i][2]/lEff)+(bsimpp.params[i][3]/wEff);
SELECT i FROM
BSimModel.beta0 =>
parms[BSimModel.beta0] ← bsimpp.params[i][1]*zRatio*(IF bsimpp.params[BSimModel.beta0][1]<1.0 THEN 1.0/dutZRatio ELSE cox);
BSimModel.x2beta0, BSimModel.beta0sat, BSimModel.x2beta0sat, BSimModel.x3beta0sat => parms[i] ← t*cox*zRatio;
BSimModel.u1, BSimModel.x2u1, BSimModel.x3u1 => parms[i] ← t/lEff;
BSimModel.n0, BSimModel.x2nb, BSimModel.x3nd => parms[i] ←
(IF Atom.GetProp[$BSimModel, $TurnOffSubThresh] # NIL THEN 0 ELSE t);
ENDCASE => parms[i] ← t;
ENDLOOP;
parms[BSimModel.tempK] ← bsimpp.tempC+273.15;
parms[BSimModel.vdd] ← bsimpp.vddVolts;
};
Don't call this procedure any more, go directly to BSim
IF parms.modFunc.body # oldBSimModelBody THEN {
oldBSimModelBody ← NARROW[parms.modFunc.body];
newBSimModelBody ← NEW[spGlobals.ModelBody ←
NARROW[parms.modFunc.body, spGlobals.RefModBody]^];
distinguishedBSimModelBody ← NEW[spGlobals.ModelBody ←
NARROW[parms.modFunc.body, spGlobals.RefModBody]^];
newBSimModelBody.modelProc ← BSim;
distinguishedBSimModelBody.modelProc ← BSim -- or DistinguishedBSim -- ;
};
IF doTrap THEN
{parms.modFunc.body ← distinguishedBSimModelBody;
BSim[args, oldArgs, parms, results];
}
ELSE
{parms.modFunc.body ← newBSimModelBody;
BSim[args, oldArgs, parms, results];
};
};
SDDiode parameters
Co: NAT= 0; -- zero-bias capacitance (farads)
OneOverPhi0: NAT= 1; -- 1/built-in potential (=kT/2*ln[Na*Nd/(Ni*Ni)]) (1/volts)
OneOverVT: NAT= 2; -- (1/volts)
Io: NAT= 3; -- reverse current (amps, should be negative)
VMax: NAT= 4; -- maximum forward bias (volts)
SDDiode: PROC [args, oldArgs, parms, results: spGlobals.argList] -- spGlobals.model -- = {
Vc: NAT= 0; -- cathode voltage arg index
Va: NAT= 1; -- anode voltage arg index
C: NAT= 0; -- results index
I: NAT= 1;
V: REAL ← args[Va] - args[Vc];
phiRatio: REAL = V*parms[OneOverPhi0];
IF V > 0 THEN { -- forward bias
WITH parms.modFunc.body SELECT FROM
rm: spGlobals.RefModBody => {
IF V > rm.oldArgVector[Va]-rm.oldArgVector[Vc]+0.1 THEN
SIGNAL spGlobals.Retreat[NIL];
.. otherwise retreating won't do much good..
};
ENDCASE => ERROR;
IF V >= parms[VMax] THEN {
parms[VMax] ← V+0.1; -- don't complain again for 100 mV
parms.handle.msgStream.PutF["\nSource-drain diode (anode(%g), cathode(%g)) forward biased to %d volts.\n",
IO.rope[ArgRope[args, parms, Va]],
IO.rope[ArgRope[args, parms, Vc]],
IO.real[V]];
Process.Yield[];
};
results[I] ← -parms[Io]*(RealFns.Exp[V*parms[OneOverVT]] - 1.0);
results[C] ← parms[Co]*(IF phiRatio < 0.5
THEN 1.0/RealFns.SqRt[1.0 - phiRatio]
ELSE 1.4142*(0.5+phiRatio) -- extrapolate linearly from V=Phi0/2 -- ) }
ELSE { -- reverse bias
results[I] ← parms[Io];
results[C] ← parms[Co]/RealFns.SqRt[1.0 - phiRatio];
};
}; -- SDDiode
SDDiodeIsUseful: PROC [parms: spGlobals.argList] RETURNS [isUseful: BOOL] -- spGlobalsExtras.IsUsefulProc -- = {
isUseful ← parms[Co]#0 OR parms[Io]#0;
}; -- SDDiodeIsUseful
SDDiodeCombineInstances: PROC [parmsA, parmsB: spGlobals.argList] RETURNS [success: BOOL] -- spGlobalsExtras.CombineInstancesProc -- = {
IF (success ← (parmsA[OneOverPhi0] = parmsB[OneOverPhi0]
AND parmsA[OneOverVT] = parmsB[OneOverVT]
AND parmsA[VMax] = parmsB[VMax]))
THEN {
parmsA[Co] ← parmsA[Co] + parmsB[Co];
parmsA[Io] ← parmsA[Io] + parmsB[Io];
};
}; -- SDDiodeCombineInstances
ArgRope: PROC [args, parms: spGlobals.argList, 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]]]];
Process.Yield[];
};
spGlobals.EnterModels[[name: "BSimFromFile", proc: BSimInit, numArgs: 4, numParms: 27, numResults: 4]];
spGlobals.EnterModels[[name: "BSim", proc: BSim, numArgs: 4, numParms: 27, numResults: 4]];
spGlobals.EnterModels[[
name: "SDDiode",
proc: SDDiode,
numArgs: 2, numParms: 5, numResults: 2,
data: Atom.PutPropOnList[
propList: Atom.PutPropOnList[
propList: NIL,
prop: $IsUsefulProc,
val: NEW[spGlobalsExtras.IsUsefulProc ← SDDiodeIsUseful]],
prop: $CombineInstancesProc,
val: NEW[spGlobalsExtras.CombineInstancesProc ← SDDiodeCombineInstances]]
]];
END.
CHANGE LOG.
McCreight, May 13, 1985 4:22:00 pm PDT, created
Chen, May 17, 1985 2:54:28 pm PDT, Real.SqRt -> RealFns.SqRt
Chen, July 17, 1985 11:12:00 am PDT, recompiled in Cedar6.0.
Christian Jacobi, June 10, 1986 7:16:09 pm PDT, Yield to avoid monopolizing cpu.