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 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]; }; 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]; }; 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. File: [Cherry]Cedar5.1>System>spCSIM2Model.mesa Last Edited by: McCreight, April 11, 1985 4:33:21 pm PST CSIM2 Parameters The 17 electrical parameters of CSIM 3.2 model of Scharfetter and Berkeley. cf. [cherry]BSim>BSim.pas. additional CSIM parameters 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 CSIM2 SDDiode parameters .. otherwise retreating won't do much good.. Êç˜JšÏc6™6J™8J˜šÏk ˜ Jšœžœ˜Jšžœžœ˜JšžœžœNžœžœ˜iJšœžœ˜Jšœžœ˜Jšœžœ#žœ ˜;Jšœ žœF˜UJšœžœ&˜;J˜—š œžœžœžœžœžœ"˜RJ˜—Jšž˜J˜JšÐct™J™JšŸm™mJšœžœ˜ Jšœžœ˜Jšœžœ˜ Jšœžœ˜ Jšœžœ˜ Jšœžœ˜Jšœžœ˜ Jšœžœ˜ Jšœ žœ˜Jšœžœ˜Jšœžœ˜Jšœžœ˜Jšœžœ˜Jšœ žœ˜Jšœ žœ˜Jšœ žœ˜Jšœžœ˜JšœžœI˜WJšœžœ˜Jšœžœ˜Jšœžœ˜J˜Jšœ™Jšœžœ˜Jšœžœ˜J˜JšœO™OJšœžœ˜Jšœ žœ˜J˜Jšœ™Jšœ žœ˜Jšœžœ˜Jšœ žœ˜,J˜J˜šÏnœžœ4œ˜XJ˜Jšœžœ ˜Jšœžœ˜ Jšœžœ˜ Jšœžœ˜ J™Jšœ™Jšœžœ˜ Jšœžœ˜ Jšœžœ˜ Jšœžœ˜ J˜J˜Jšœ$žœ˜)Jšœžœ˜Jšœ žœ˜Jšœžœ˜J˜J˜šœ žœžœ˜J˜—Jšœ(™(šžœžœ˜J˜ J˜—šžœ˜J˜J˜J˜—Jšœ.™.Jšžœ žœžœ žœ˜?J˜˜7J˜—šœ ™ Jšœ˜Jšœ žœ˜%Jšœžœ žœ˜7J˜Jšœ žœ ˜Jšœžœžœ@˜OJ˜šžœž˜šžœžœž˜#šœ˜Jš œ žœ:žœ žœžœ˜gšžœž˜Jšžœžœ˜Jšœ,™,—Jšœ˜—Jšžœžœ˜—J˜—šžœ!žœ˜)Jšœ%*˜Ošœt˜tšžœžœ žœžœ˜