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]; }; 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["/DATools/DATools6.1/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; 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]; }; 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. เ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 Christian Le Cocq April 29, 1987 10:19:34 am PDT Curry, September 23, 1986 5:17:25 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 CSIM2 Parameters -- in addition to the ones in BSimModel capacitance parameters for MOSAID model. cf. MOSAID documentation by Dick Foss. other parameters: results: Invert voltages if transistor is p-type. Switch source and drain if biases are reversed Calculate Ids .. otherwise retreating won't do much good.. Calculate capacitances From this point on, refill the parms vector with calculated parameters as a function of lMicrons and wMicrons Don't call this procedure any more, go directly to BSim SDDiode parameters .. otherwise retreating won't do much good.. ส – "cedar" style˜Icodešœ™K™>™Kšœ0ฯk™3K™0Kšœ%™(Kšœ)™,Kšœ#™&Kšœ(™+Kšœ+™.—K˜š ˜ Kšœœ#˜-Kšœ ˜ Kšœœ&œ˜6Kšœœ ˜Kšœœ ˜Kšœœ ˜Kšœœ œ ˜"Kšœ œF˜XKšœœ&˜>K˜—š ฯnœœœœœ.˜jK˜—Kš˜K˜Kšฯtะct'™8K™Kšœœ œ™OKšœœ˜Kšœ œ˜K˜Kšœ™Kšœ œ˜Kšœœ˜Kšœ œฯc˜,K˜K˜šžœœ7กœ˜]K˜Kšœœก ˜Kšœœ˜ Kšœœ˜ Kšœœ˜ K™Kšœ™Kšœœ˜ Kšœœ˜ Kšœœ˜ Kšœœ˜ K˜K˜Kšœ$œ˜)Kšœœ˜Kšœ œ˜Kšœœ˜!Kšœ œœ˜šœ-œ˜2K˜—Kšœ(™(šœœ˜K˜ K˜—šœ˜K˜K˜K˜—Kšœ.™.Kšœ œœ œ˜?K˜˜7K˜—Kšœ-˜-šœ ˜Kšœ˜Kšœ*˜*—š˜Kšœ[˜[Kšœ+˜+K™—šœ ™ Kšœ˜Kšœ œ ˜Kšœœœ^˜mK˜šœ˜šœœ˜#šœ ˜ Kš œ œ:œ œœ˜gšœ˜Kšœœ˜!Kšœ,™,—Kšœ˜—Kšœœ˜—K˜—šœ%œ˜-Kšœ)ก*˜Sšœs˜sšœœ œœ˜Kšœœ ˜Kšœœ3˜=K˜Kšœœ'ก˜@K˜Kšœกะbcกœœ/˜Qšœœ˜'K˜—Kšœœ˜"Kšœœ)˜0Kšœœ$˜0K˜Kšœ œก˜,šœœœœ˜UKšœ œK˜ZKšœœ*˜3Kšœœ ˜Kšœ œP˜^Kšœ œ ˜Kšœ2˜2Kšœ˜—K˜šœœ ˜+Kšœ2˜6Kšœ+˜/K˜—šœ ก˜K˜—Kšœก˜—Kšœก˜—K˜šœ™Kšœ˜Kšœ œ˜K˜šœœ˜Kšœ(˜(šœก ˜Kšœ˜—šœ$ก/˜SKš˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜—šœก4˜?Kš˜Kšœœ˜Kšœœ ก˜*Kšœ˜Kšœ2˜2Kšœ2˜2Kšœ˜—K˜—K˜—šœ œ˜K˜K˜K˜—šœ˜K˜K˜K˜K˜—Kšœก˜ K˜K˜—K˜Kšœ,œ˜0Kšœ,œ˜0Kšœ6œ˜:K˜Kšœ œ˜šœ œœ˜Kš œœœœœ˜6K˜—K˜Kšžœœ7กœ˜a˜Kšœœก˜ Kšœœ˜ Kšœ œ˜K˜Kšœ œ˜/Kšœœœ˜K˜šœœ œ˜"Kšœœ˜$Kšœก œœ˜)K˜Kšœœ˜$Kšœœœ˜%Kšœœœ˜ Kšœ œ ˜K˜šœ.œ˜8Kšœ*œ˜G—Kšœ œ˜šœ œ&œœ˜TKšœ#œ;œœ˜{Kšœœœ"˜MK˜K˜—Kšœœœœ˜2K˜Kšœm™mK˜Kšœ˜Kšœ˜Kšœ˜Kšœ œœœ˜WK˜Kšœ2˜2Kšœ2˜2Kšœ*˜*Kšœ'ก˜6K˜šœœœ!˜0KšœœM˜Tšœ˜ šœ˜Kšœ5œ'œœ˜{—Kšœm˜mKšœB˜Bšœ:˜:Kš œœ/œœœ˜E—Kšœ˜—Kšœ˜—Kšœ-˜-Kšœ'˜'K˜—K˜Kšœ7™7K˜šœ'œ˜/Kšœœ˜.šœœ˜0Kšœ0˜6—šœœ˜:Kšœ0˜6—Kšœ"˜"Kšœ,กœ˜HK˜—šœ˜Kšœ1˜1Kšœ$˜$Kšœ˜—š˜Kšœ'˜'Kšœ$˜$K˜—Kšœ˜K˜K˜—Kšœ™K˜Kšœœก!˜-Kšœ œก;˜PKšœ œก ˜Kšœœก-˜9Kšœœก˜-K˜šžœœ7กœ˜`Kšœœก˜(Kšœœก˜&K˜Kšœœก˜Kšœœ˜ K˜Kšœœ˜Kšœ œ˜&šœœก˜šœœ˜#šœ ˜ šœ1˜7Kšœœ˜!Kšœ,™,—Kšœ˜—Kšœœ˜K˜—šœœ˜Kšœก"˜8šœj˜jšœ ˜"Kšœ ˜"Kšœ ˜ ——Kšœ˜Kšœ˜—Kšœ@˜@šœœ˜)Kšœ!˜%Kšœก(œ˜G——šœก˜Kšœ˜Kšœ4˜4K˜—Kšœก ˜ K˜K˜—š žœœœ œก%œ˜vKšœœ ˜&šœก˜K˜—K˜—š žœœ(œ œก-œ˜Žšœ6˜8Kšœ&˜)Kšœ˜!šœ˜Kšœ%˜%Kšœ%˜%Kšœ˜——Kšœก˜K˜K˜K˜K˜—š žœœ)œœ œ˜UKšœœœœ˜0šœœ˜šœ ˜"K˜K˜Kšœœ˜ —Kšœ&œ˜B—Kšœ˜Kšœ˜K˜K˜K˜—K˜jK˜^šœ˜Kšœ˜Kšœ˜Kšœ'˜'šœ˜šœ˜Kšœ œ˜Kšœ˜Kšœœ5˜=—Kšœ˜KšœœD˜L—Kšœ˜—K˜Kšœ˜K˜šœœ˜ Kšœ#œ ˜/Kšœœ˜