<> <> <> <> <> <> <> <> <> <> <> DIRECTORY Atom USING [GetProp, PutProp, PutPropOnList], BSimModel, IO USING [int, PutR, PutF, PutFR, real, rope, STREAM], Process USING [Yield], Real USING [Round], RealFns USING [Exp, SqRt], Rope USING [Length, ROPE, Substr], ThymeGlobals USING [argList, EnterModels, MakeStringNB, RefModBody, ModelBody, Retreat], ThymeGlobalsExtras USING [CombineInstancesProc, IsUsefulProc]; BSimModelImplEval: CEDAR PROGRAM IMPORTS Atom, BSimModel, IO, Process, Real, RealFns, Rope, ThymeGlobals = BEGIN <> <<>> <> MCgbo: NAT= 22; Cgbo23rds: NAT= 23; <> maxFwdBias: NAT=24; NoCap: NAT= 25; nChannel: NAT= 26; -- -1: pType; else nType; BSim: PROC [args, oldArgs, parms, results: ThymeGlobals.argList] -- ThymeGlobals.model -- = { VgX: NAT= 0; -- arguments VsX: NAT= 1; VdX: NAT= 2; VbX: NAT= 3; <<>> <> 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: BOOL_ FALSE; realTwoPhiFMVbs, sqrt2PhiFMVbs, TwoPhiFMVbs: REAL; <> 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]}; <> 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}; <<>> <> { 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: ThymeGlobals.RefModBody => { prevVbs: REAL = parms[nChannel]*(rm.oldArgVector[VbX]-rm.oldArgVector[(IF reverse THEN VdX ELSE VsX)]); IF Vbs > prevVbs+0.1 THEN SIGNAL ThymeGlobals.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 <> { 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: ThymeGlobals.RefModBody _ NIL; newBSimModelBody: ThymeGlobals.RefModBody _ NIL; distinguishedBSimModelBody: ThymeGlobals.RefModBody _ NIL; nFavorites: NAT = 50; Favorites: TYPE = RECORD [ p: SEQUENCE size: NAT OF REF BSimModel.ProcessParams]; BSimInit: PROC [args, oldArgs, parms, results: ThymeGlobals.argList] -- ThymeGlobals.model -- = { L: NAT = 0; -- parameter indices W: NAT = 1; DUTZRatio: NAT = 2; channelType: INT = Real.Round[parms[nChannel]]; doTrap: BOOL _ FALSE; 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["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; <> 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; }; <> IF parms.modFunc.body # oldBSimModelBody THEN { oldBSimModelBody _ NARROW[parms.modFunc.body]; newBSimModelBody _ NEW[ThymeGlobals.ModelBody _ NARROW[parms.modFunc.body, ThymeGlobals.RefModBody]^]; distinguishedBSimModelBody _ NEW[ThymeGlobals.ModelBody _ NARROW[parms.modFunc.body, ThymeGlobals.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]; }; }; <> 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: ThymeGlobals.argList] -- ThymeGlobals.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: ThymeGlobals.RefModBody => { IF V > rm.oldArgVector[Va]-rm.oldArgVector[Vc]+0.1 THEN SIGNAL ThymeGlobals.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: ThymeGlobals.argList] RETURNS [isUseful: BOOL] -- ThymeGlobalsExtras.IsUsefulProc -- = { isUseful _ parms[Co]#0 OR parms[Io]#0; }; -- SDDiodeIsUseful SDDiodeCombineInstances: PROC [parmsA, parmsB: ThymeGlobals.argList] RETURNS [success: BOOL] -- ThymeGlobalsExtras.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: ThymeGlobals.argList, ix: NAT] RETURNS [r: Rope.ROPE] = { voltage: Rope.ROPE = IO.PutR[IO.real[args[ix]]]; r _ IO.PutFR["%g (%g V)", IO.rope[ThymeGlobals.MakeStringNB[ handle: parms.handle, n: parms.modFunc.arguments[ix], b: NIL]], IO.rope[Rope.Substr[base: voltage, len: MIN[voltage.Length, 5]]]]; Process.Yield[]; }; ThymeGlobals.EnterModels[[name: "BSimFromFile", proc: BSimInit, numArgs: 4, numParms: 27, numResults: 4]]; ThymeGlobals.EnterModels[[name: "BSim", proc: BSim, numArgs: 4, numParms: 27, numResults: 4]]; ThymeGlobals.EnterModels[[ name: "SDDiode", proc: SDDiode, numArgs: 2, numParms: 5, numResults: 2, data: Atom.PutPropOnList[ propList: Atom.PutPropOnList[ propList: NIL, prop: $IsUsefulProc, val: NEW[ThymeGlobalsExtras.IsUsefulProc _ SDDiodeIsUseful]], prop: $CombineInstancesProc, val: NEW[ThymeGlobalsExtras.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.