<Cedar5.1>System>spCSIM2Model.mesa>> <> 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 <> <<>> <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; <> vdd: NAT = 20; tempK: NAT = 21; <> MCgbo: NAT= 22; Cgbo23rds: NAT= 23; <> 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; <<>> <> 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: BOOL_ FALSE; <> 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; <> { 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 <> { 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; <> 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; }; <> 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[]; }; <> 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.