-- File: [Cherry]<Thyme>System>C03>Level2Model.mesa
-- Last editted by SChen  February 19, 1984  3:17 PM

DIRECTORY AltoDefs, Real, RealFns, spModelDefs;
Level2Model: PROGRAM
  IMPORTS RealFns, spModelDefs=
BEGIN

  modelError: SIGNAL[s: STRING]= CODE;

-- Temporary square root kluge until RealFns is fixed.

  SingleReal: TYPE= RECORD[m2: CARDINAL, -- Backwards!!!!
                           sign: BOOLEAN,
                           exp: [0..256),
                           m1: [0..128)];

  SquareReal: TYPE= RECORD[m2: CARDINAL, -- Backwards!!!!
                           sign: BOOLEAN,
                           expD2: [0..128),
                           index: [0..16),
                           m1: [0..16)];

  guesses: ARRAY[0..16) OF REAL;

  SqRt: PROCEDURE[x: REAL] RETURNS[y: REAL]= BEGIN
    xFmt: SquareReal;
    yFmt: SingleReal;

    IF x < 0.0 THEN SIGNAL modelError["SqRt of non-positive"];
    xFmt← LOOPHOLE[x, SquareReal];
    yFmt← LOOPHOLE[guesses[xFmt.index], SingleReal];
    yFmt.exp← yFmt.exp + xFmt.expD2 - 63;
    y← LOOPHOLE[yFmt, REAL];
    y← LOOPHOLE[LOOPHOLE[y + x/y, LONG CARDINAL] - 40000000B, REAL];
    y← LOOPHOLE[LOOPHOLE[y + x/y, LONG CARDINAL] - 40000000B, REAL];
  END; -- SqRt

  initSqRt: PROCEDURE= BEGIN
    i: CARDINAL;
    x1, x2: REAL;
    xFmt: SquareReal;

    FOR i IN [0..16) DO
      xFmt← [0, FALSE, 63, i, 0];
      x1← LOOPHOLE[xFmt, REAL];
      IF i < 15 THEN xFmt← [0, FALSE, 63, i + 1, 0]
      ELSE xFmt← [0, FALSE, 64, 0, 0];
      x2← LOOPHOLE[xFmt, REAL];
      guesses[i]← 2.0*(RealFns.SqRt[x1]*x2 -
                          RealFns.SqRt[x2]*x1)/(x2-x1)
    ENDLOOP
  END; -- initSqRt

  LimitVgs: PROC[vgs, vgsOld, vto: REAL] RETURNS[vgsNew: REAL]= BEGIN
    vtstHigh, vtstLow, vtoPlus35, deltaV: REAL;
    vtstHigh← 2.0 + 2.0*ABS[vgsOld-vto];
    vtstLow← vtstHigh/2.0 + 2.0;
    vtoPlus35← vto + 3.5;
    deltaV← vgs-vgsOld;
    vgsNew← vgs;
    IF vgsOld < vto THEN BEGIN
      -- OFF
      IF deltaV > 0 THEN BEGIN
        vTemp: REAL← vto + 0.5;
        IF vgsNew > vTemp THEN vgsNew← vTemp; 
      END;
    END ELSE IF vgsOld < vtoPlus35 THEN BEGIN
      -- MIDDLE
      vgsNew← IF deltaV > 0 THEN MIN[vgsNew, vto + 4.0] -- increasing
              ELSE MAX[vgsNew, vto - 0.5]; -- decreasing
    END ELSE BEGIN
      -- ON
      IF deltaV > 0 THEN BEGIN -- staying on
        IF deltaV > vtstHigh THEN vgsNew← vgsOld + vtstHigh;
      END ELSE BEGIN -- going off
        IF vgsNew < vtoPlus35 THEN vgsNew← MAX[vgsNew, vto + 2.0]
        ELSE IF -deltaV > vtstLow THEN vgsNew ← vgsOld - vtstLow;
      END;
    END;
  END; -- LimitVgs

  LimitVds: PROC[vds, vdsOld: REAL] RETURNS[vdsNew: REAL]= BEGIN
    vdsNew← vds;
    IF vdsOld < 3.5 THEN BEGIN
      vdsNew← IF vdsNew <= vdsOld THEN MAX[vdsNew, -5]
              ELSE MIN[vdsNew, 4];
    END ELSE BEGIN
      IF vdsNew <= vdsOld THEN 
        {IF vdsNew < 3.5 THEN vdsNew← MAX[vdsNew, 2]}
      ELSE vdsNew← MIN[vdsNew, 2.0 + 3.0*vdsOld];
    END;
  END; -- LimitVds

  LimitVbs: PROC[vbs, vbsOld, vt, vcrit: REAL] 
    RETURNS[vbsNew: REAL]= BEGIN
    vbsNew← vbs;
    IF vbsNew > vcrit THEN BEGIN
      vLimit: REAL← vt + vt;
      deltaV: REAL← vbsNew - vbsOld;
      IF ABS[deltaV] > vLimit THEN BEGIN
        IF vbsOld <= 0 THEN vbsNew← vt*RealFns.Ln[vbsNew/vt]
        ELSE BEGIN
          arg: REAL← 1.0 + deltaV/vt;
          vbsNew← IF arg <= 0 THEN vcrit 
                              ELSE vbsOld + vt*RealFns.Ln[arg];
        END;
      END;
    END;
  END; -- LimitVbs

  Baum: PROC[VgsX, Vbin, Eta, Phi, Vbs, Vmax, 
    L, UEffective, gammaD, SArg3: REAL] RETURNS[Vdsat: REAL]= BEGIN

    v1, v2, xv, a1, b1, c1, d1, a3, b3, A, B, C, R, S, 
    r3, s2, P, p0, p2, fi, y3, delta4, xValid: REAL← 0.0;
    x4, poly4: ARRAY[1..8] OF REAL;
    a4, b4, Sig1, Sig2: ARRAY[1..4] OF REAL;
    Icount, Jcount: CARDINAL← 0;

    Icount← Jcount← 0;
    x4← poly4← ALL[0.0];
    a4← b4← ALL[0.0];
    Sig1← [1.0, -1.0, 1.0, -1.0];
    Sig2← [1.0, 1.0, -1.0, -1.0];
        
    v1← (VgsX-Vbin)/Eta+Phi-Vbs; 
    v2← Phi-Vbs;
    xv← Vmax*L/UEffective;
         
    a1← gammaD/0.75; b1← -2.0*(v1+xv); c1← -2.0*gammaD*xv; 
    d1← 2.0*v1*(v2+xv)-v2*v2-4.0/3.0*gammaD*SArg3;
        
    A← -b1; B← a1*c1-4.0*d1; C← -d1*(a1*a1-4.0*b1)-c1*c1;
       
    R← -A*A/3.0+B; r3← R*R*R;
    S← 2.0*A*A*A/27.0-A*B/3.0+C; s2← S*S;
       
    P← s2/4.0+r3/27.0; p0← ABS[P]; p2← SqRt[p0];
       
    -- get y3 value.
    IF P<0.0 THEN BEGIN 
      ro: REAL;
      ro← SqRt[s2/4.0+p0]; 
      ro← RealFns.Ln[ro]/3.0; 
      ro← RealFns.Exp[ro]; 
      fi← RealFns.ArcTan[-2.0*p2/S, 1]; -- arcTan(-2.0*p2/S); 
      y3← 2.0*ro*RealFns.Cos[fi/3.0]-A/3.0;
    END ELSE BEGIN -- 550 
      p3, p4: REAL;
      p3← RealFns.Exp[ RealFns.Ln[ABS[-S/2.0+p2]]/3.0 ];
      p4← RealFns.Exp[ RealFns.Ln[ABS[-S/2.0-p2]]/3.0 ];
      y3← p3+p4-A/3.0;
    END;
    
    a3← SqRt[a1*a1/4.0-b1+y3]; 
    b3← SqRt[y3*y3/4.0-d1];
    
    -- from a3, b3, Sig1, Sig2, y3 -> a4, b4 -> delta4 -> x4
    FOR I:CARDINAL IN[1..4] DO
      a4[I]← a1/2.0+Sig1[I]*a3; 
      b4[I]← y3/2.0+Sig2[I]*b3; 
      delta4← a4[I]*a4[I]/4.0-b4[I];
      IF delta4>=0.0 THEN BEGIN
        sqrtd4: REAL;
        sqrtd4← SqRt[delta4];
        Icount← Icount+1; 
        x4[Icount]← a4[I]/(-2.0)+sqrtd4; 
        Icount← Icount+1; 
        x4[Icount]← a4[I]/(-2.0)-sqrtd4;
      END; 
    ENDLOOP;
    
    FOR J:CARDINAL IN [1..Icount] DO
      tmp: REAL; 
      IF x4[J]<=0.0 AND J<Icount THEN LOOP; 
      
      tmp← (x4[J]+a1);
      tmp← (tmp*x4[J]+b1);
      tmp← (tmp*x4[J]+c1);
      poly4[J]← (tmp*x4[J]+d1);
      IF ABS[poly4[J]]>1.0E-6 AND J<Icount THEN LOOP; 
      Jcount← Jcount+1; 
      IF Jcount<=1 THEN xValid← x4[J];
      IF x4[J]<=xValid THEN xValid← x4[J];
    ENDLOOP;
    
    IF Jcount>0 THEN Vdsat← xValid*xValid+Vbs-Phi
    ELSE SIGNAL spModelDefs.Retreat[
      "Error in immediate velocity saturation model"];
  END; -- Baum

  Grove: PROC[gammaD, VgsX, Vbin, Eta, Phi, Vbs: REAL]
    RETURNS[Vdsat: REAL]= BEGIN
    gammaD2, ArgV: REAL;
    gammaD2← gammaD*gammaD;
    ArgV← (VgsX-Vbin)/Eta+Phi-Vbs;
    IF ArgV<=0.0 THEN Vdsat← 0.0
    ELSE BEGIN
      Arg: REAL;
      Arg← SqRt[1.0+4.0*ArgV/gammaD2];
      Vdsat← (VgsX-Vbin)/Eta+gammaD2*(1.0-Arg)/2.0;
      IF Vdsat<0.0 THEN Vdsat←0.0;
    END;
  END; -- Grove

  JcnCurrent: PROC[vt, v, isat: REAL] RETURNS[i: REAL]= BEGIN
    t1: REAL← v/vt;
    IF v > 0 THEN BEGIN
      t1← MIN[t1, 85.0];
      i← isat*(RealFns.Exp[t1]- 1.0);
    END ELSE i← IF t1 <= -1.0 THEN -isat ELSE isat*t1;
  END; -- JcnCurrent

  Pair: TYPE= RECORD[f, df: REAL];

  FandDF: PROC[v, p, sp, sp3: REAL] RETURNS[pair: Pair]= BEGIN
    IF v <= 0 THEN BEGIN
      pair.f← SqRt[p-v]; 
      pair.df← -0.5/pair.f;
    END ELSE BEGIN -- 130
      pair.f← sp/(1.0+0.5*v/p); 
      pair.df← -0.5*pair.f*pair.f/sp3; 
    END;
  END; -- FandDF
        
  SpiceLevel2: spModelDefs.model= BEGIN

    -- args
    VdX: CARDINAL= 0;
    VgX: CARDINAL= 1;
    VsX: CARDINAL= 2;
    VbX: CARDINAL= 3;

    -- oldArgs
    oldVdX: CARDINAL= 0;
    oldVgX: CARDINAL= 1;
    oldVsX: CARDINAL= 2;
    oldVbX: CARDINAL= 3;

    -- parms
    LX: CARDINAL= 0;
    WX: CARDINAL= 1;
    XdX: CARDINAL= 2;
    XjX: CARDINAL= 3;
    IssatX: CARDINAL= 4;
    IdsatX: CARDINAL= 5;
    BetaX: CARDINAL= 6;
    PhiX: CARDINAL= 7;
    SqRtPhiX: CARDINAL= 8;
    SqRtPhi3X: CARDINAL= 9;
    PbX: CARDINAL= 10;
    SqRtPbX: CARDINAL= 11;
    VbpX: CARDINAL= 12;
    VbiX: CARDINAL= 13;
    GammaX: CARDINAL= 14;
    LambdaX: CARDINAL= 15;
    VtX: CARDINAL= 16;
    UoX: CARDINAL= 17;
    VmaxX: CARDINAL= 18;
    QNfsX: CARDINAL= 19;
    NsubX: CARDINAL= 20;
    UexpX: CARDINAL= 21;
    NeffX: CARDINAL= 22;
    EtaX: CARDINAL= 23;
    FactorX: CARDINAL= 24;
    CoxX: CARDINAL= 25;
    CovGSX: CARDINAL= 26;
    CovGDX: CARDINAL= 27;
    CgbMX: CARDINAL= 28;
    Cgb23rdsX: CARDINAL= 29;
    CbsBottomX: CARDINAL= 30;
    CbdBottomX: CARDINAL= 31;
    CbsSWX: CARDINAL= 32;
    CbdSWX: CARDINAL= 33;
    CbsFwd1X: CARDINAL= 34;
    CbsFwd2X: CARDINAL= 35;
    CbdFwd1X: CARDINAL= 36;
    CbdFwd2X: CARDINAL= 37;
    MjX: CARDINAL= 38;
    MjswX: CARDINAL= 39;
    FcPbX: CARDINAL= 40;
    VonX: CARDINAL= 41;
    minVX: CARDINAL= 42;
    maxVX: CARDINAL= 43;
    SignX: CARDINAL= 44;
    
    reverse: BOOLEAN;
    sign, Vd, Vg, Vs, Vb, oldVd, oldVg, oldVs, oldVb,
    Vds, Vgs, Vbs, Vgd, Vbd, 
    oldVds, oldVgs, oldVbs, oldVgd, oldVbd, 
    Id, Cgb, Cgs, Cgd, Cbs, Cbd, t1,
    Vbin, XdBarg, XdSarg, ArgSS, ArgSD, DBArgS, DBArgD,
    ArgD, ArgS, gamaSD, DBXWD, DBXWS, gammaD, 
    DgdDvb, VgsT, VgdT,
    SArg3, body, UFactor, UEffective, 
    VgsX, bodyS, clFactor, Vdsat, ArgG, beta1, Vcrit: REAL;
    SArg, BArg: Pair;
    
    LimitNodeVoltage: PROC[oldV: REAL]
      RETURNS[newV: REAL]= BEGIN
      newV← MIN[parms[maxVX], MAX[parms[minVX], oldV]];
    END; -- LimitNodeVoltage

    Id← 0;
    sign← parms[SignX];
    Vd← LimitNodeVoltage[sign*args[VdX]];
    Vg← LimitNodeVoltage[sign*args[VgX]]; 
    Vs← LimitNodeVoltage[sign*args[VsX]];
    Vb← LimitNodeVoltage[sign*args[VbX]];
    oldVd← LimitNodeVoltage[sign*args[oldVdX]];
    oldVg← LimitNodeVoltage[sign*args[oldVgX]]; 
    oldVs← LimitNodeVoltage[sign*args[oldVsX]];
    oldVb← LimitNodeVoltage[sign*args[oldVbX]];
    
    Vds← Vd - Vs; Vgs← Vg - Vs; Vbs← Vb - Vs; 
    Vgd← Vg - Vd; Vbd← Vb - Vd;
    oldVds← oldVd - oldVs; oldVgs← oldVg - oldVs; oldVbs← oldVb - oldVs;
    oldVgd← oldVg - oldVd; oldVbd← oldVb - oldVd;

    IF oldVds >= 0 THEN BEGIN
      Vgs← LimitVgs[Vgs, oldVgs, parms[VonX]];
      Vds← Vgs - Vgd;
      Vds← LimitVds[Vds, oldVds];
      Vgd← Vgs - Vds;
    END ELSE BEGIN
      Vgd← LimitVgs[Vgd, oldVgd, parms[VonX]];
      Vds← Vgs - Vgd;
      Vds← -LimitVds[-Vds, -oldVds];
      Vgs← Vgd + Vds;
    END;
    IF Vds >= 0 THEN BEGIN
      Vcrit← parms[VtX]*RealFns.Ln[parms[VtX]/(1.414*parms[IssatX])];
      Vbs← LimitVbs[Vbs, oldVbs, parms[VtX], Vcrit];
      Vbd← Vbs - Vds;
      args[VdX]← sign*(Vs + Vds);
      args[VgX]← sign*(Vs + Vgs);
      args[VbX]← sign*(Vs + Vbs);
    END ELSE BEGIN
      Vcrit← parms[VtX]*RealFns.Ln[parms[VtX]/(1.414*parms[IdsatX])];
      Vbd← LimitVbs[Vbd, oldVbd, parms[VtX], Vcrit];
      Vbs← Vbd + Vds;
      args[VsX]← sign*(Vd - Vds);
      args[VgX]← sign*(Vd + Vgd);
      args[VbX]← sign*(Vd + Vbd);
    END;

    -- get Ibs and Ibd
    results[6]← sign*JcnCurrent[parms[VtX], Vbs, parms[IssatX]];
    results[7]← sign*JcnCurrent[parms[VtX], Vbd, parms[IdsatX]];
      
    IF Vds >= 0 THEN reverse← FALSE
    ELSE {reverse← TRUE; Vds← -Vds;
          t1← Vbs; Vbs← Vbd; Vbd← t1;
          t1← Vgs; Vgs← Vgd; Vgd← t1;
          };

    -- 100
    SArg← FandDF[Vbs, parms[PhiX], parms[SqRtPhiX], parms[SqRtPhi3X]];
    BArg← FandDF[Vbd, parms[PhiX], parms[SqRtPhiX], parms[SqRtPhi3X]];
    
    -- 200, Calculate parms[VonX], narrow channel effect
    Vbin← parms[VbiX]+parms[FactorX]*(parms[PhiX]-Vbs);
    XdBarg← parms[XdX]*BArg.f; 
    XdSarg← parms[XdX]*SArg.f; 

    -- short channel effect with Vds # 0
    ArgSS← ArgSD← DBArgS← DBArgD← 0.0;
    IF parms[XjX] > 0.0 THEN BEGIN
      ArgXS, ArgXD: REAL;
      ArgXS← 1.0+2.0*XdSarg/parms[XjX];
      ArgS← SqRt[ArgXS]; 
      ArgSS← 0.5*parms[XjX]/parms[LX]*(ArgS-1.0);
      ArgXD← 1.0+2.0*XdBarg/parms[XjX];
      ArgD← SqRt[ArgXD]; 
      ArgSD← 0.5*parms[XjX]/parms[LX]*(ArgD-1.0);
    END;
    
    --(205)
    gamaSD← parms[GammaX]*(1.0-ArgSS-ArgSD);
    DBXWD← parms[XdX]*BArg.df; 
    DBXWS← parms[XdX]*SArg.df; 
    IF parms[XjX] > 0.0 THEN BEGIN
      DBArgS← 0.5/parms[LX]*DBXWS/ArgS;
      DBArgD← 0.5/parms[LX]*DBXWD/ArgD;
    END;
    
    DgdDvb← -parms[GammaX]*(DBArgS+DBArgD); 
    
    --220 
    parms[VonX]← Vbin+gamaSD*SArg.f;
    Vdsat← 0.0;

    --225
    BEGIN
      IF parms[QNfsX] = 0.0 THEN BEGIN -- 230
        VgsT← Vgs-parms[VonX];
        IF VgsT<=0.0 THEN GOTO done; -- (Id=0) 1050 
      END ELSE BEGIN
        CdOnCo, Xn: REAL;
        CdOnCo← -(gamaSD*SArg.df+DgdDvb*SArg.f)+parms[FactorX];
        Xn← 1.0+parms[QNfsX]/parms[CoxX]+CdOnCo; 
        parms[VonX]← parms[VonX]+parms[VtX]*Xn; 
        ArgG← 1.0/(parms[VtX]*Xn);
        VgsT← Vgs-parms[VonX];
      END; 
        
      -- 300
      SArg3← SArg.f*SArg.f*SArg.f;
      gammaD← gamaSD; 
      body← BArg.f*BArg.f*BArg.f-SArg3; 
      
      -- 400, EVALUATE EFFECTIVE MOBILITY AND ITS DERIVATIVES
      IF VgsT > parms[VbpX] THEN BEGIN
        UFactor← RealFns.Exp[ parms[UexpX]*RealFns.Ln[parms[VbpX]/VgsT] ]; 
        UEffective← parms[UoX]*UFactor; 
      END ELSE BEGIN  -- 410
        UFactor← 1.0; 
        UEffective← parms[UoX]; 
      END;
      
      -- 500 EVALUATE SATURATION VOLTAGE AND ITS DERIVATIVES
      -- ACCORDING TO GROVE-FROHMAN EQUATION
      VgsX← Vgs;
      gammaD← gamaSD/parms[EtaX]; 
      IF parms[QNfsX]#0.0 THEN VgsX← MAX[Vgs, parms[VonX]];
      Vdsat← IF gammaD <= 0.0 THEN MAX[0.0, (VgsX-Vbin)/parms[EtaX]]
      ELSE Grove[gammaD, VgsX, Vbin, parms[EtaX], parms[PhiX], Vbs]; 
      
      -- 545
      IF parms[VmaxX] > 0.0 THEN Vdsat← Baum[
        VgsX, Vbin, parms[EtaX], parms[PhiX], Vbs, parms[VmaxX], 
        parms[LX], UEffective, gammaD, SArg3];
        
      -- 600, EVALUATE EFFECTIVE CHANNEL LENGTH AND ITS DERIVATIVES
      IF Vds # 0.0 THEN BEGIN
        lFactor, ArgV, xdv, lv, ls, SArgV, BSArg: REAL;
         
        gammaD← gamaSD; 
        BSArg← IF Vbs <= Vdsat THEN SqRt[Vdsat-Vbs+parms[PhiX]]
               ELSE parms[SqRtPhiX]/(1.0+0.5*(Vbs-Vdsat)/parms[PhiX]);
          
        bodyS← BSArg*BSArg*BSArg-SArg3; 
        IF parms[VmaxX] <=0.0 THEN BEGIN
          IF parms[NsubX] # 0.0 AND parms[LambdaX] <= 0.0 THEN BEGIN
            Arg: REAL;
            ArgV← (Vds-Vdsat)/4.0;
            SArgV← SqRt[1.0+ArgV*ArgV];
            Arg← SqRt[ArgV+SArgV]; 
            lFactor← parms[XdX]/(parms[LX]*Vds);
            parms[LambdaX]← lFactor*Arg;
          END;
        END ELSE BEGIN -- 603 
          ArgV← (VgsX-Vbin)/parms[EtaX]-Vdsat;
          xdv← parms[XdX]/SqRt[parms[NeffX]]; 
          lv← parms[VmaxX]*xdv/(2.0*UEffective); 
          IF parms[NsubX] # 0.0 AND parms[LambdaX] <= 0.0 THEN BEGIN
            ArgV← MAX[Vds-Vdsat, 0.0]; 
            ls← SqRt[lv*lv+ArgV]; 
            lFactor← xdv/(parms[LX]*Vds); 
            parms[LambdaX]← lFactor*(ls-lv);
          END;
        END; 
      END; -- block 600
      
        -- 620, LIMIT CHANNEL SHORTENING AT PUNCH-THROUGH 
      BEGIN
        leffective, deltaL, xwb, ld: REAL;
        xwb← parms[XdX]*parms[SqRtPbX];
        ld← parms[LX]-xwb;
        clFactor← 1.0-parms[LambdaX]*Vds; 
        leffective← parms[LX]*clFactor; 
        deltaL← parms[LambdaX]*Vds*parms[LX];
        IF parms[NsubX]=0.0 THEN xwb← 0.25E-6; 
        IF leffective<xwb THEN BEGIN  
          leffective← xwb/(1.0+(deltaL-ld)/xwb);
          clFactor← leffective/parms[LX]; 
        END;
      END;

      -- 700 EVALUATE EFFECTIVE beta (EFFECTIVE KP) 
      beta1← parms[BetaX]*UFactor/clFactor;
 
      -- TEST FOR MODE OF OPERATION AND BRANCH APPROPRIATELY
      gammaD← gamaSD; 
      IF Vds <= 1.0E-8 THEN GOTO done; -- (Id=0) 1050
      -- 730
      IF VgsT <= 0.0 THEN BEGIN -- else goto 900 
        -- SUBTHRESHOLD REGION, IF Vdsat<=0.0 THEN Id← 0.0
        IF  Vdsat > 0.0 THEN BEGIN -- 830
          VdsOn, IdsOn, ArgGVgsT: REAL;
          VdsOn← MIN[Vdsat,Vds];
          ArgGVgsT← ArgG*VgsT;
          IF Vds > Vdsat THEN body← bodyS;
          IdsOn← beta1*( (parms[VonX] - Vbin -
                   parms[EtaX]*VdsOn*0.5)*VdsOn - gammaD*body/1.5 );
          -- if ArgGVgsT < -ln(IdsOn) - 34.539 then Id < 1E-15 ~ 0
          Id← IF ArgGVgsT < (- 34.539 - RealFns.Ln[IdsOn]) THEN 0
              ELSE sign*IdsOn*RealFns.Exp[ArgGVgsT];
        END;
      
      END ELSE 
        Id← IF Vds <= Vdsat THEN -- 900 LINEAR REGION
              sign*beta1*((Vgs-Vbin-parms[EtaX]*Vds/2.0)*Vds-gammaD*body/1.5) 
            ELSE -- 1000 SATURATION REGION
              sign*beta1*((Vgs-Vbin-parms[EtaX]*Vdsat/2.0)*Vdsat-gammaD*bodyS/1.5);
    EXITS
      done=> NULL;
    END;
      
    -- capacitances, and results
    VgdT← Vgd - parms[VonX];

    -- Cgb:
    Cgb← IF VgsT <= 0 THEN parms[CgbMX] ELSE 0;
                       
    -- Cgs and Cgd:

    t1← VgsT+VgdT;
    t1← t1*t1; -- (Vgs + Vgd - 2Von)↑2
    Cgs← IF VgsT <= 0.0 THEN parms[CovGSX]
       ELSE IF VgdT <= 0.0 THEN parms[Cgb23rdsX] + parms[CovGSX]
       ELSE parms[CovGSX] + parms[Cgb23rdsX]*(1.0 - VgdT*VgdT/t1);
    Cgd← IF VgdT <= 0.0 THEN parms[CovGDX]
       ELSE parms[CovGDX] + parms[Cgb23rdsX]*(1.0 - VgsT*VgsT/t1);

    -- Cbs and Cbd

    IF Vbs >= parms[FcPbX] THEN -- 520
      Cbs← parms[CbsFwd1X] + Vbs*parms[CbsFwd2X]
    ELSE BEGIN
      t1← RealFns.Ln[1.0-Vbs/parms[PbX]];
      Cbs← parms[CbsBottomX]*RealFns.Exp[-parms[MjX]*t1] + parms[CbsSWX]*RealFns.Exp[-parms[MjswX]*t1]
    END;
    
    IF Vbd >= parms[FcPbX] THEN -- 520
      Cbd← parms[CbdFwd1X] + Vbd*parms[CbdFwd2X]
    ELSE BEGIN
      t1← RealFns.Ln[1.0-Vbd/parms[PbX]];
      Cbd← parms[CbdBottomX]*RealFns.Exp[-parms[MjX]*t1] + parms[CbdSWX]*RealFns.Exp[-parms[MjswX]*t1]
    END;
    
    -- results order: Id, Cgb, Cgs, Cgd, Cbs, Cbd, Ibs, Ibd.
    IF reverse THEN BEGIN
      results[0]← -Id;
      results[1]← Cgb;
      results[2]← Cgd;
      results[3]← Cgs;
      results[4]← Cbd;
      results[5]← Cbs;
    END ELSE BEGIN
      results[0]← Id;
      results[1]← Cgb;
      results[2]← Cgs;
      results[3]← Cgd;
      results[4]← Cbs;
      results[5]← Cbd;
    END;
  END; -- SpiceLevel2  
  
  initSqRt[];
  
  spModelDefs.EnterModels["SpiceLevel2", SpiceLevel2, 4, 45, 8];
END.
2/12/84:-
  created to support Spice level2 model.
2/19/84:-
  completed addition of Voltage limiting stmts; modified Poly4 calculation.
2/22/84:- 

  added protection for week inversion current calculation to insure that Exp(ArgG*VgsT) doesn't blow up.