File: [Cherry]<Thyme>Cedar5.1>System>spCSIM2Model.mesa
Last Edited by: McCreight, April 11, 1985 4:33:21 pm PST
DIRECTORY
Atom USING [PutPropOnList],
FS USING [StreamOpen],
IO USING [Backup, Close, GetChar, GetLineRope, GetReal, int, PutR, PutF, PutFR, real, RIS, rope, STREAM],
Real USING [RoundLI, SqRt],
RealFns USING [Exp],
Rope USING [Cat, Equal, Fetch, Find, Length, ROPE, Substr],
spGlobals USING [argList, EnterModels, makeStringNB, RefModBody, ModelBody, Retreat],
spGlobalsExtras USING [CombineInstancesProc, IsUsefulProc];
spCSIM2Model: CEDAR PROGRAM IMPORTS Atom, FS, IO, Real, RealFns, Rope, spGlobals =
BEGIN
CSIM2 Parameters
The 17 electrical parameters of CSIM 3.2 model of Scharfetter and Berkeley. cf. [cherry]<akis>BSim>BSim.pas.
vfb: NAT= 0;
phif2: NAT= 1;
k1: NAT= 2;
k2: NAT= 3;
eta: NAT= 4;
beta0: NAT= 5;
u0: NAT= 6;
u1: NAT= 7;
x2beta0: NAT = 8;
x2eta: NAT = 9;
x3eta: NAT = 10;
x2u0: NAT = 11;
x2u1: NAT = 12;
beta0sat: NAT = 13;
x2beta0sat: NAT = 14;
x3beta0sat: NAT = 15;
x3u1: NAT = 16;
n0: NAT = 17; -- who knows what these last three are for or in what order they appear!!
x2nb: NAT = 18;
x3nd: NAT = 19;
nBasicBSIMParams: NAT = 20;
additional CSIM parameters
vdd: NAT = 20;
tempK: NAT = 21;
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;
CSIM2: 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[vdd];
reverse: BOOLFALSE;
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;
Calculate Ids
{
TwoPhiFMVbs: REAL = parms[phif2]-Vbs;
sqrt2PhiFMVbs: REAL = Real.SqRt[MAX[0.0, TwoPhiFMVbs]];
VdsMVdd: REAL = Vds-Vdd;
Eta: REAL = MAX[0.0, parms[eta]+parms[x2eta]*TwoPhiFMVbs+parms[x3eta]*VdsMVdd];
IF TwoPhiFMVbs < 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 TwoPhiFMVbs < parms[maxFwdBias] THEN {
parms[maxFwdBias] ← TwoPhiFMVbs+0.1; -- don't complain again for another 100 mV
parms.handle.msgStream.PutF["\nCSIM2 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]];
};
Vth ← parms[vfb]+parms[phif2]+parms[k1]*sqrt2PhiFMVbs
-parms[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[u0]+parms[x2u0]*Vbs]*VgsMVth;
u1Sum: REAL = MAX[0.0, parms[u1]+parms[x2u1]*Vbs+parms[x3u1]*VdsMVdd];
temp: REAL = parms[beta0]+parms[x2beta0]*Vbs; -- calculate beta
tempA: REAL = (parms[beta0sat]+parms[x2beta0sat]*Vbs)/temp-1.0;
x3vddvdd: REAL = parms[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[k1]/sqrt2PhiFMVbs;
VgsMVthOverAlpha: REAL = VgsMVth/alpha;
vc: REAL = u1Sum*VgsMVthOverAlpha;
k: REAL = 0.5*(1.0+vc+Real.SqRt[1.0+2.0*vc]);
VdsSat: REAL = VgsMVthOverAlpha/Real.SqRt[k];
IdsDiff: REAL ← 0.0; -- subthreshold current
IF parms[n0]#0 OR parms[x2nb]#0 OR parms[x3nd]#0 THEN {
nSubThPar: REAL = parms[n0]+parms[x3nd]*Vds+parms[x2nb]*Vbs;
Vtm: REAL = 0.0258512*parms[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);
NULL; -- 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};
}; -- CSIM2
oldCSIM2ModelBody: spGlobals.RefModBody ← NIL;
newCSIM2ModelBody: spGlobals.RefModBody ← NIL;
nFavorites: NAT = 30;
favoriteProcessFiles: ARRAY [2..nFavorites] OF REF CSIM2ProcessParams ← ALL[NIL];
CSIM2Init: 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]];
IF ~ channelType IN [-1..1] THEN {
lMicrons, wMicrons, dutZRatio: REAL;
lEff, wEff, cox, zRatio -- w/l -- : REAL;
csim2pp: REF CSIM2ProcessParams;
processIndex: INT = ABS[channelType];
IF processIndex > nFavorites OR (csim2pp ← favoriteProcessFiles[processIndex]) = NIL THEN {
csim2pp ← ReadProcessFile[IO.PutFR["BSIMProcess-%d.process", IO.int[ABS[channelType]]]];
IF processIndex IN [2..nFavorites] THEN favoriteProcessFiles[processIndex] ← csim2pp;
};
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];
dutZRatio ← (IF parms[DUTZRatio]>0 THEN parms[DUTZRatio] ELSE csim2pp.dutW/csim2pp.dutL);
lEff ← lMicrons+csim2pp.params[beta0][2];
wEff ← wMicrons+csim2pp.params[beta0][3];
zRatio ← csim2pp.betaFudgeFactor*wEff/lEff;
cox ← 3.9*8.854E-10/csim2pp.toxMicrons; -- farads/sq cm
FOR i: INT IN [0..nBasicBSIMParams) DO
t: REAL = csim2pp.params[i][1]+(csim2pp.params[i][2]/lEff)+(csim2pp.params[i][3]/wEff);
SELECT i FROM
beta0 =>
parms[beta0] ← t*zRatio*(IF csim2pp.params[beta0][1]<1.0 THEN 1.0/dutZRatio ELSE cox);
x2beta0, beta0sat, x2beta0sat, x3beta0sat => parms[i] ← t*cox*zRatio;
u1, x2u1, x3u1 => parms[i] ← t/lEff;
ENDCASE => parms[i] ← t;
ENDLOOP;
parms[tempK] ← csim2pp.tempC+273.15;
parms[vdd] ← csim2pp.vddVolts;
};
Don't call this procedure any more, go directly to CSIM2
IF parms.modFunc.body # oldCSIM2ModelBody THEN {
oldCSIM2ModelBody ← NARROW[parms.modFunc.body];
newCSIM2ModelBody ← NEW[spGlobals.ModelBody ←
NARROW[parms.modFunc.body, spGlobals.RefModBody]^];
newCSIM2ModelBody.modelProc ← CSIM2;
};
parms.modFunc.body ← newCSIM2ModelBody;
CSIM2[args, oldArgs, parms, results];
};
CSIM2ProcessParams: TYPE = RECORD [
info: Rope.ROPE,
params: ARRAY[0..nBasicBSIMParams) OF ARRAY[1..6] OF REAL,
toxMicrons, tempC, vddVolts: REAL,
dutL: REAL ← 2.0,
dutW: REAL ← 20.0,
betaFudgeFactor: REAL ← 1.0
];
ReadProcessFile: PROC [ fileName: Rope.ROPE ] RETURNS [ p: REF CSIM2ProcessParams ] = {
s: IO.STREAM = FS.StreamOpen[fileName];
ReadReal: PROC [ s: IO.STREAM ] RETURNS [ r: REAL ] =
BEGIN
DO
SELECT (c ← s.GetChar[]) FROM
',, ' , '/, '\n => NULL;
ENDCASE => {s.Backup[c]; RETURN[s.GetReal[]]};
ENDLOOP;
END;
c: CHAR;
p ← NEW[CSIM2ProcessParams];
p.info ← NIL;
WHILE (c ← s.GetChar[]) = '* DO
line: Rope.ROPE = s.GetLineRope[];
eqPos: INT = line.Find["="];
p.info ← Rope.Cat[p.info, "*", line, "\n"];
IF line.Length>1 AND line.Fetch[0]#' AND eqPos>0 THEN {
label: Rope.ROPE = line.Substr[len: eqPos];
rest: IO.STREAM = IO.RIS[line.Substr[start: eqPos+1]];
SELECT TRUE FROM
label.Equal["W/L", FALSE] => {
p.dutW ← ReadReal[rest]; p.dutL ← ReadReal[rest]};
label.Equal["BetaFudgeFactor", FALSE] =>
p.betaFudgeFactor ← ReadReal[rest];
ENDCASE => NULL;
};
ENDLOOP;
s.Backup[c];
FOR i: INT IN [0..nBasicBSIMParams) DO
FOR j: INT IN [1..6] DO
p.params[i][j] ← ReadReal[s];
ENDLOOP;
ENDLOOP;
p.toxMicrons ← ReadReal[s];
p.tempC ← ReadReal[s];
p.vddVolts ← ReadReal[s];
s.Close[];
};


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]];
};
results[I] ← -parms[Io]*(RealFns.Exp[V*parms[OneOverVT]] - 1.0);
results[C] ← parms[Co]*(IF phiRatio < 0.5
THEN 1.0/Real.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]/Real.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]]]];
};
spGlobals.EnterModels[[name: "CSIM2FromFile", proc: CSIM2Init, numArgs: 4, numParms: 27, numResults: 4]];
spGlobals.EnterModels[[name: "CSIM2", proc: CSIM2, 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.