-- File: [Thyme]<Thyme>System>Spice>Level2Model.mesa
-- Last editted by SChen  8-Feb-84 20:21:27

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

  SpiceLevel2: spModelDefs.model= BEGIN
    -- args
    Vd: REAL← args[0];
    Vg: REAL← args[1];
    Vs: REAL← args[2];
    Vb: REAL← args[3];

    -- parms
    l: REAL= parms[0];
    w: REAL= parms[1];
    xd: REAL= parms[2];
    xj: REAL= parms[3];
    Issat: REAL= parms[4];
    Idsat: REAL= parms[5];
    beta: REAL= parms[6];
    phi: REAL= parms[7];
    phiB: REAL= parms[8];
    vbp: REAL= parms[9];
    vbi: REAL= parms[10];
    gamma: REAL= parms[11];
    lambda: REAL← parms[12];
    vt: REAL= parms[13];
    uo: REAL= parms[14];
    vmax: REAL= parms[15];
    QNfs: REAL= parms[16];
    nsub: REAL= parms[17];
    uexp: REAL= parms[18];
    neff: REAL= parms[19];
    eta: REAL= parms[20];
    factor: REAL= parms[21];

    Cox: REAL= parms[22];
    CovGS: REAL= parms[23];
    CovDS: REAL= parms[24];
    CgbM: REAL= parms[25];
    Cgb23rds: REAL= parms[26];

    CbsBottom: REAL= parms[27];
    CbdBottom: REAL= parms[28];
    CbsSW: REAL= parms[29];
    CbdSW: REAL= parms[30];
    CbsFwd1: REAL= parms[31];
    CbsFwd2: REAL= parms[32];
    CbdFwd1: REAL= parms[33];
    CbdFwd2: REAL= parms[34];
    FcByPb: REAL= parms[35];
    sign: REAL= parms[36];

    Vds, Vgs, Vbs, Vbd, Id, Ibs, Ibd, 
    Cgb, Cgs, Cgd, Cbs, Cbd, t1: REAL← 0.0;
    reverse: BOOLEAN← FALSE;
    SArg, DsargDb, SqrtPhi, SqrtPhi3, BArg, DbargDb,
    Vbin, XdBarg, XdSarg, ArgSS, ArgSD, DBArgS, DBArgD,
    ArgD, ArgS, ArgXS, ArgXD, gamaSD, DBXWD, DBXWS, gammaD, 
    DgdDvb, CdOnCo, Xn, VgsT, ArgG, SArg3, SqrtPhib, body,
    UFactor, UEffective, VgsX, ArgV, Arg, BSArg, DbsargDb, bodyS,
    clFactor, von, vdsat, beta1: REAL← 0.0;
    
    IF sign= -1 THEN {Vd← -Vd; Vg← -Vg; Vs← -Vs; Vb← -Vb};
    
    IF Vs > Vd THEN {reverse← TRUE; t1← Vs; Vs← Vd; Vd← t1};
      
    Vds← Vd - Vs; Vgs← Vg - Vs; Vbs← Vb - Vs; Vbd← Vb - Vd;
    
    -- get Ibs
    t1← Vbs/vt;
    IF Vbs > 0 THEN BEGIN
      t1← MIN[t1, 85.0];
      Ibs← sign*Issat*(RealFns.Exp[t1]- 1.0);
    END ELSE Ibs← sign*Issat*t1;
    
    -- get Ibd  
    t1← Vbd/vt;
    IF Vbd > 0 THEN BEGIN
      t1← MIN[t1, 85.0];
      Ibd← sign*Idsat*(RealFns.Exp[t1]- 1.0);
    END ELSE Ibd← sign*Idsat*t1;
      
    -- 100 
    IF Vbs <= 0.0 THEN BEGIN 
      SArg← SqRt[phi-Vbs]; 
      DsargDb← -0.5/SArg;
    END ELSE BEGIN -- ( 110 )
      SqrtPhi← SqRt[phi]; 
      SqrtPhi3← phi*SqrtPhi;
      SArg← SqrtPhi/(1.0+0.5*Vbs/phi); 
      DsargDb← -0.5*SArg*SArg/SqrtPhi3; 
    END;
  
    -- 120
    IF Vds >= Vbs THEN BEGIN 
      BArg← SqRt[phi+Vds-Vbs]; 
      DbargDb← -0.5/BArg;
    END ELSE BEGIN -- 130
      BArg← SqrtPhi/(1.0+0.5*(Vbs-Vds)/phi); 
      DbargDb← -0.5*BArg*BArg/SqrtPhi3; 
    END;
    
    -- 200, Calculate von, narrow channel effect
    Vbin← vbi+factor*(phi-Vbs);
    IF (gamma<=0.0) OR (nsub<=0.0) THEN BEGIN -- 215
      gamaSD← gammaD← gamma;
      DgdDvb← 0.0;
    END ELSE BEGIN 
      XdBarg← xd*BArg; 
      XdSarg← xd*SArg; 
   
      -- short channel effect with Vds # 0
      ArgSS← ArgSD← DBArgS← DBArgD← 0.0;
      IF xj > 0.0 THEN BEGIN
        ArgXS← 1.0+2.0*XdSarg/xj;
        ArgS← SqRt[ArgXS]; 
        ArgSS← 0.5*xj/l*(ArgS-1.0);
        ArgXD← 1.0+2.0*XdBarg/xj;
        ArgD← SqRt[ArgXD]; 
        ArgSD← 0.5*xj/l*(ArgD-1.0);
      END;
      
      --(205)
      gamaSD← gamma*(1.0-ArgSS-ArgSD);
      DBXWD← xd*DbargDb; 
      DBXWS← xd*DsargDb; 
      IF xj>0.0 THEN BEGIN
        DBArgS← 0.5/l*DBXWS/ArgS;
        DBArgD← 0.5/l*DBXWD/ArgD;
      END;
      
      DgdDvb← -gamma*(DBArgS+DBArgD); 
    END;
    
    --220 
    von← Vbin+gamaSD*SArg;
    vdsat← 0.0;

    --225
    BEGIN
      IF QNfs = 0.0 THEN BEGIN -- 230
        VgsT← Vgs-von;
        IF VgsT<=0.0 THEN GOTO done; -- (Id=0) 1050 
      END ELSE BEGIN
        CdOnCo← -(gamaSD*DsargDb+DgdDvb*SArg)+factor;
        Xn← 1.0+QNfs/Cox+CdOnCo; 
        von← von+vt*Xn; 
        ArgG← 1.0/(vt*Xn);
        VgsT← Vgs-von;
      END; 
        
      -- 300
      SArg3← SArg*SArg*SArg;
      SqrtPhib← SqRt[phiB];
      gammaD← gamaSD; 
      body← BArg*BArg*BArg-SArg3; 
      
      -- 400, EVALUATE EFFECTIVE MOBILITY AND ITS DERIVATIVES
      IF VgsT > vbp THEN BEGIN
        UFactor← RealFns.Exp[ uexp*RealFns.Ln[vbp/VgsT] ]; 
        UEffective← uo*UFactor; 
      END ELSE BEGIN  -- 410
        UFactor← 1.0; 
        UEffective← uo; 
      END;
      
      -- 500 EVALUATE SATURATION VOLTAGE AND ITS DERIVATIVES ACCORDING TO
      -- GROVE-FROHMAN EQUATION
      VgsX← Vgs;
      gammaD← gamaSD/eta; 
      IF QNfs#0.0 THEN VgsX← MAX[Vgs, von];
      IF gammaD<=0.0 THEN vdsat← MAX[0.0, (VgsX-Vbin)/eta]
      ELSE BEGIN
        gammaD2: REAL← gammaD*gammaD;
        ArgV← (VgsX-Vbin)/eta+phi-Vbs;
        IF ArgV<=0.0 THEN vdsat← 0.0
        ELSE BEGIN
          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; -- vdsat← DMAX1(vdsat,0.0);
        END;
      END; 
      
      -- 545
      IF vmax > 0.0 THEN BEGIN
      -- EVALUATE SATURATION VOLTAGE AND ITS DERIVATIVES ACCORDING TO
      -- BAUM'S THEORY OF SCATTERING VELOCITY SATURATION 
        v1, v2, xv, a1, b1, c1, d1, a3, b3, A, B, C, R, S, r3, s2, P, p0, p2, fi, 
          y3, delta4, xValid: REAL;
        x4, poly4, a4, b4, Sig1, Sig2: ARRAY[1..4] OF REAL;
        Icount, Jcount: CARDINAL← 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← SqRt[s2/4.0+p0]; 
          ro← RealFns.Ln[ro]/3.0; 
          ro← RealFns.Exp[ro]; 
          fi← RealFns.ArcTan[-2.0*p2, S]; -- arcTan(-2.0*p2/S); 
          y3← 2.0*ro*RealFns.Cos[fi/3.0]-A/3.0;
        END ELSE BEGIN -- 550 
          p3: REAL← RealFns.Exp[ RealFns.Ln[ABS[-S/2.0+p2]]/3.0 ];
          p4: REAL← 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
            Icount← Icount+1; 
            x4[Icount]← -a4[I]/2.0+SqRt[delta4]; 
            Icount← Icount+1; 
            x4[Icount]← -a4[I]/2.0-SqRt[delta4];
          END; 
        ENDLOOP;
        
        FOR J:CARDINAL IN [1..Icount] DO 
          IF x4[J]<=0.0 THEN LOOP; 
          
          poly4[J]← x4[J]*x4[J]*x4[J]*x4[J]+a1*x4[J]*x4[J]*x4[J]; 
          poly4[J]← poly4[J]+b1*x4[J]*x4[J]+c1*x4[J]+d1;
          
          IF ABS[poly4[J]]>1.0E-6 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; -- Block 545
        
      -- 600, EVALUATE EFFECTIVE CHANNEL LENGTH AND ITS DERIVATIVES
      IF Vds # 0.0 THEN BEGIN
        lFactor, ArgV, xdv, lv, ls, SArgV: REAL;
         
        gammaD← gamaSD; 
        BSArg← IF Vbs <= vdsat THEN SqRt[vdsat-Vbs+phi];
               ELSE SqrtPhi/(1.0+0.5*(Vbs-vdsat)/phi);
          
        bodyS← BSArg*BSArg*BSArg-SArg3; 
        IF vmax<=0.0 THEN BEGIN
          IF nsub # 0.0 AND lambda <= 0.0 THEN BEGIN
            ArgV← (Vds-vdsat)/4.0;
            SArgV← SqRt[1.0+ArgV*ArgV];
            Arg← SqRt[ArgV+SArgV]; 
            lFactor← xd/(l*Vds);
            lambda← lFactor*Arg;
	END;
        END ELSE BEGIN -- 603 
          ArgV← (VgsX-Vbin)/eta-vdsat;
          xdv← xd/SqRt[neff]; 
          lv← vmax*xdv/(2.0*UEffective); 
          IF nsub # 0.0 AND lambda <= 0.0 THEN BEGIN
            ArgV← MAX[Vds-vdsat, 0.0]; 
            ls← SqRt[lv*lv+ArgV]; 
            lFactor← xdv/(l*Vds); 
            lambda← lFactor*(ls-lv);
	END;
        END;
      END; -- block 600
      
      -- 620, LIMIT CHANNEL SHORTENING AT PUNCH-THROUGH 
      BEGIN
        leffective, deltaL: REAL;
        xwb: REAL← xd*SqrtPhib;
        ld: REAL← l-xwb;
        clFactor← 1.0-lambda*Vds; 
        leffective← l*clFactor; 
        deltaL← lambda*Vds*l;
        IF nsub=0.0 THEN xwb← 0.25E-6; 
        IF leffective<xwb THEN BEGIN  
          leffective← xwb/(1.0+(deltaL-ld)/xwb);
          clFactor← leffective/l; 
        END;
      END;
  
      -- 700 EVALUATE EFFECTIVE beta (EFFECTIVE KP) 
      beta1← beta*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 
        VdsOn, IdsOn: REAL;
        
        -- SUBTHRESHOLD REGION, IF vdsat<=0.0 THEN Id← 0.0
        IF  vdsat > 0.0 THEN BEGIN -- 830
          VdsOn← MIN[vdsat,Vds];
          IF Vds>vdsat THEN body← bodyS;
          -- 850 
          IdsOn← beta1*((von-Vbin-eta*VdsOn*0.5)*VdsOn-gammaD*body/1.5);
          Id← sign*IdsOn*RealFns.Exp[ArgG*VgsT];
        END;
        
      END ELSE 
        Id← IF Vds <= vdsat THEN -- 900 LINEAR REGION
              sign*beta1*((Vgs-Vbin-eta*Vds/2.0)*Vds-gammaD*body/1.5) 
            ELSE -- 1000 SATURATION REGION
              sign*beta1*((Vgs-Vbin-eta*vdsat/2.0)*vdsat-gammaD*bodyS/1.5);
    EXITS
      done=> NULL;
    END;
    
    -- capacitances, and results
    VgdT: REAL← Vg - Vd - von;

    -- Cgb:
    Cgb← IF VgsT <= 0 THEN CgbM ELSE 0;
  		     
    -- Cgs and Cgd:

    t1← VgsT+VgdT;
    t1← t1*t1; -- (Vgs + Vgd - 2von)↑2
    Cgs← IF VgsT <= 0.0 THEN CovGS
       ELSE IF VgdT <= 0.0 THEN Cgb23rds + CovGS
       ELSE CovGS + Cgb23rds*(1.0 - VgdT*VgdT/t1);
    Cgd← IF VgdT <= 0.0 THEN CovDS
       ELSE CovDS + Cgb23rds*(1.0 - VgsT*VgsT/t1);

    -- Cbs and Cbd

    IF Vbs >= FcByPb THEN -- 520
      Cbs← CbsFwd1 + Vbs*CbsFwd2
    ELSE BEGIN
      t1← RealFns.Ln[1.0-Vbs/PhiB];
      Cbs← CbsBottom*RealFns.Exp[-Mj*t1] + CbsSW*RealFns.Exp[-Mjsw*t1]
    END;
    
    IF Vbd >= FcByPb THEN -- 520
      Cbd← CbdFwd1 + Vbd*CbdFwd2
    ELSE BEGIN
      t1← RealFns.Ln[1.0-Vbd/PhiB];
      Cbd← CbdBottom*RealFns.Exp[-Mj*t1] + CbdSW*RealFns.Exp[-Mjsw*t1]
    END;
    
    -- results order: Id, Cgb, Cgs, Cgd, Cbs, Cbd, Ibs, Ibd.
    results← IF reverse THEN [-Id, Cgb, Cgd, Cgs, Cbd, Cbs, Ibd, Ibs]
                        ELSE [ Id, Cgb, Cgs, Cgd, Cbs, Cbd, Ibs, Ibd];
    END;
  END; -- SpiceLevel2  
  
  initSqRt[];
  
  spModelDefs.EnterModels["SpiceLevel2", SpiceLevel2, 4, 37, 8];

END.