
//
//  Verilog-A definition of an ideal MOSFET model.
//
//  Version 1.0, 01-Jan-2011
//
//  For "Operation and Modeling of The MOS Transistor," Third Edition
//  by Yannis Tsividis and Colin McAndrew
//  Oxford University Press, 2011
//
//  Copyright Oxford University Press, 2011. All rights reserved.
//  
//  Software is distributed as is, without warranty or service
//  support. Oxford University Press and its employees are not
//  liable for the condition or performance of the software.
//  
//  Oxford University Press owns the copyright but shall not be liable for any
//  infringement of copyright or other proprietary rights brought by third
//  parties against the users of the software.
//  
//  Oxford University Press hereby disclaims all implied warranties.
//  
//  Oxford University Press grants the users the right to copy, modify,
//  and use the software in conjunction with usage of the abovementioned
//  book, for both personal and classroom use.
//

//
//  This an ideal surface potential based MOS transistor model.
//  The surface potential solution is non-iterative and is based on Appendix A of:
//    G. Gildenblat, H. Wang, T.-L. Chen, X. Gu, and X. Cai, "SP: An advanced surface-potential-based
//    compact MOSFET model," IEEE J. Solid-State Circuits, vol. 39., no. 9. pp. 1394-1406, Sep. 2004
//  The basic I_DS and charge modeling equations are based on those described in:
//    H. Wang, T.-L. Chen, and G. Gildenblat, "Quasi-static and nonquasi-static compact MOSFET models
//    based on symmetric linearization of the bulk and inversion charges," IEEE Trans. Electron Devices,
//    vol. 50, no. 11, pp. 2262-2272, Nov. 2003
//  but with a slightly different bulk charge model as described in the book.
//
//  The model is for an intrinsic MOS transistor and does not include any parasitics
//  (series resistance, junction diode current and capacitance, overlap and fringing capacitance)
//  or flicker or shot noise.
//
//  There is a switch in the model form at V_GB just greater than V_FB. I_DS and the inversion
//  charge essentially go to zero at that bias, so the model is simplified to reflect that and to avoid
//  numerical evaluation problems for V_GB biases below that value. The first of the above references
//  provides a conditioning of the surface potential equation (SPE) that ensures continuity of the
//  trans-capacitances if there is a switch to the simplified model form at V_GB=0. However, for
//  understanding the operation of the MOS transistor this level detail is not necessary and can
//  obscure the basic way the model works. For this reason a simple "hard" switch is used.
//
//  Symbols used are as similar as possible to those in the book, and the underscore "_"
//  character indicates a subscript. Where appropriate, an additional subscript to indicate
//  the unit is added. All model parameters are in upper case, so the T_OX model parameter is
//  the t_ox symbol from the text. There are more efficient ways to write the code, the style
//  used is intended to emphasize clarity and readability.
//

`include "disciplines.vams"

//
//  Physical constants and other generally useful numbers
//

`define TABS_NIST2004     273.15                 // (K)    (NIST2004) 0C
`define QQ_NIST2004       1.60217653e-19         // (C)    (NIST2004) mag. of electronic charge
`define KB_NIST2004       1.3806505e-23          // (J/K)  (NIST2004) Boltzmann constant
`define EPS0_NIST2004     8.854187817e-14        // (F/cm) (NIST2004) Electric constant
`define EPS_OX            3.45313324863e-13      // (F/cm) EPS0_NIST2004*3.90
`define EPS_SI            1.035939974589e-12     // (F/cm) EPS0_NIST2004*11.7
`define oneSixth          0.1666666666666667
`define oneThird          0.3333333333333333
`define twoThirds         0.6666666666666667
`define sqrtTwo           1.414213562373095
`define inv_sqrtTwo       0.7071067811865475
`define switchPoint       1.0e-3

//
//  Macro that computes the surface potential
//
//  Outputs:
//           psi_s      the surface potential
//  Inputs:
//           Vgb        gate-bulk    voltage
//           Vcb        channel-bulk voltage
//           phi_t      the thermal voltage
//           twoPhi_F   twice the Fermi potential = 2*phi_t*ln(Nsub/Ni)
//           Vfb        the flatband voltage
//           G          gamma/sqrt(phi_t) where gamma is the body effect coefficient
//           blockName  name (must be unique) for the block, to allow locally scoped variables
//

`define psi_calc(psi_s,Vgb,Vcb,phi_t,twoPhi_F,Vfb,G,blockName) \
begin : blockName \
    real xg, xi, x23, xg23, DEL_n, Gsq, yg, z, eta, a, c, tau, y0, DEL_0, DEL_1, p, q, x, u, xn, b, xbar, Ebar, omega, x0, xsub ; \
    xg    =  ((Vgb)-(Vfb))/(phi_t); \
    xi    =  1.0+(G)*`inv_sqrtTwo; \
    if (abs(xg/xi)<1.0e-7) begin \
        x     =  xg/xi; \
    end else begin \
        if ((Vcb)<0.0) begin \
            x23   =  ((twoPhi_F)+(Vcb))/(2.0*(phi_t)); \
        end else begin \
            x23   =  (0.5*(twoPhi_F)+(Vcb))/(phi_t); \
        end \
        xg23  =  (G)*sqrt(x23-1.0); \
        DEL_n =  exp(-((twoPhi_F)+(Vcb))/(phi_t)); \
        Gsq   =  (G)*(G); \
        if (xg<0.0) begin \
            yg    = -xg; \
            z     =  1.25*yg/xi; \
            eta   =  0.5*(z+10.0-sqrt((z-6.0)*(z-6.0)+64.0)); \
            a     =  (yg-eta)*(yg-eta)+Gsq*(eta+1.0); \
            c     =  2.0*(yg-eta)-Gsq; \
            tau   = -eta+ln(a/Gsq); \
            u     =  (a+c)*(a+c)/tau+0.5*c*c-a; \
            y0    =  eta+a*(a+c)/(u+c*(a+c)*(c*c*`oneThird-a)/u); \
            DEL_0 =  exp(y0); \
            DEL_1 =  1.0/DEL_0; \
            p     =  2.0*(yg-y0)+Gsq*(DEL_0-1.0+DEL_n*(1.0-DEL_1)); \
            q     =  (yg-y0)*(yg-y0)+Gsq*(y0-DEL_0+1.0+DEL_n*(1.0-DEL_1-y0)); \
            x     = -y0-2.0*q/(p+sqrt(p*p-2.0*q*(2.0-Gsq*(DEL_0+DEL_n*DEL_1)))); \
        end else begin \
            if (xg<xg23) begin \
                xbar  =  xg*(1.0+xg*(xi*x23-xg23)/(xg23*xg23))/xi; \
                Ebar  =  exp(-xbar); \
                omega =  1.0-Ebar-DEL_n*((1.0/Ebar)-xbar-1.0); \
                y0    =  xg+0.5*Gsq-(G)*sqrt(xg+0.25*Gsq-omega); \
                DEL_0 =  exp(y0); \
                DEL_1 =  1.0/DEL_0; \
                p     =  2.0*(xg-y0)+Gsq*(1.0-DEL_1+DEL_n*(DEL_0-1.0)); \
                q     =  (xg-y0)*(xg-y0)-Gsq*(y0+DEL_1-1.0+DEL_n*(DEL_0-y0-1.0)); \
                x0    =  y0+2.0*q/(p+sqrt(p*p-2.0*q*(2.0-Gsq*(DEL_1+DEL_n*DEL_0)))); \
            end else begin \
                xsub  =  xg+0.5*Gsq-(G)*sqrt(xg+0.25*Gsq-1.0); \
                xn    =  ((twoPhi_F)+(Vcb))/(phi_t); \
                b     =  xn+3.0; \
                eta   =  0.5*(xsub+b-sqrt((xsub-b)*(xsub-b)+5.0)); \
                a     =  (xg-eta)*(xg-eta)-Gsq*(eta-1.0); \
                c     =  2.0*(xg-eta)+Gsq; \
                tau   =  xn-eta+ln(a/(Gsq)); \
                u     =  (a+c)*(a+c)/tau+0.5*c*c-a; \
                x0    =  eta+a*(a+c)/(u+c*(a+c)*(c*c*`oneThird-a)/u); \
            end \
            DEL_0 =  exp(x0); \
            DEL_1 =  1.0/DEL_0; \
            p     =  2.0*(xg-x0)+Gsq*(1.0-DEL_1+DEL_n*(DEL_0-1.0)); \
            q     =  (xg-x0)*(xg-x0)-Gsq*(x0+DEL_1-1.0+DEL_n*(DEL_0-x0-1.0)); \
            x     =  x0+2.0*q/(p+sqrt(p*p-2.0*q*(2.0-Gsq*(DEL_1+DEL_n*DEL_0)))); \
        end \
    end \
    psi_s =  (phi_t)*x; \
end

//
//  Start of ideal MOS transistor model code
//

module idealMos(term_D,term_G,term_S,term_B);

//
//  Node definitions
//

inout      term_D,term_G,term_S,term_B;
electrical term_D,term_G,term_S,term_B;

//
//  Branch definitions
//

branch (term_D,term_B)   b_db;
branch (term_G,term_B)   b_gb;
branch (term_S,term_B)   b_sb;
branch (term_D,term_S)   b_ds;

//
//  Instance parameters (note that $mfactor is implicitly handled for Verilog-A LRM2.2 and later)
//

parameter real    W          =   10.0e-6     from(    0.0    :    inf    );            // (m)       gate   width
parameter real    L          =   10.0e-6     from(    0.0    :    inf    );            // (m)       gate   length

//
//  Special model parameters, some may be simulator global parameters
//

parameter integer TYPE       =   -1          from[   -1      :    1      ] exclude 0;  //           MOSFET type: -1 is NMOS, +1 is PMOS
parameter real    T_NOM      =   27.0        from[ -250.0    : 1000.0    ];            // (degC)    nominal (reference) temperature
parameter real    T_MIN      = -100.0        from[ -250.0    :   27.0    ];            // (degC)    minimum ambient temperature
parameter real    T_MAX      =  500.0        from[   27.0    : 1000.0    ];            // (degC)    maximum ambient temperature

//
//  General model parameters
//

parameter real    V_FB       =   -0.8;                                                 // (V)       flatband voltage
parameter real    T_OX       =    2.5        from[    1.0    :  200.0    ];            // (nm)      oxide thickness
parameter real    N_SUB      =    5.0e+17    from[    1.0e+14:    1.0e+19];            // (/cm^3)   oxide thickness
parameter real    MU         =  400.0        from[    0.0    :20000.0    ];            // (cm^2/Vs) low field mobility
parameter real    THETA      =    0.0        from[    0.0    :    1.0    ];            // (/V)      mobility degradation with field
parameter real    ETA        =    0.5        from[    0.0    :    1.0    ];            //           effective field parameter: nmos=1/2 pmos=1/3

//
//  minimium conductance (added to junctions, to help convergence)
//

parameter real    gmin       =    1.0e-12    from[    0.0    :     1.00  ];            //          minimum conductance

//
//  Temperature dependence model parameters
//

parameter real    T_VFB      =    0.0;                                                 // (V/K)    V_FB temperature coefficient
parameter real    X_MU       =    0.0;                                                 //          MU   temperature exponent

analog begin : analogBlock

    real tini_K, tdev_C, del_T, rat_T;
    real V_FB_T, MU_T;
    real phi_t, C_ox_cm2, twoPhi_F, gamma, G;
    real C_ox, beta;
    real V_DB, V_GB, V_SB;
    real psi_s0, psi_sL, psi_sL0, I_DS, Q_G, Q_I, Q_D, Q_S, S_iw;
    real exp_mps0, phi_e0, exp_mpsL, phi_eL, psi_sm, exp_mpsm, phi_em, phi_am, phi_im;
    real alpha_m, H, mu_deg;
    real I_DB, I_SB;

//
//  Code independent of bias or instance parameters
//

    begin : initializeModel
        tdev_C   =  $temperature-`TABS_NIST2004;
        if (tdev_C<T_MIN) begin
            $strobe("ERROR (idealMos): ambient temperature is lower than allowed minimum %g",T_MIN);
            $finish(0);
        end
        if (tdev_C>T_MAX) begin
            $strobe("ERROR (idealMos): ambient temperature is higher than allowed maximum %g",T_MAX);
            $finish(0);
        end
        tini_K   =  T_NOM+`TABS_NIST2004;
        del_T    =  tdev_C-T_NOM;
        rat_T    = $temperature/tini_K;
        V_FB_T   =  V_FB+T_VFB*del_T;
        MU_T     =  MU*pow(rat_T,X_MU);
        phi_t    = `KB_NIST2004*$temperature/`QQ_NIST2004;
        C_ox_cm2 = `EPS_OX/(T_OX*1.0e-7);
        twoPhi_F =  2.0*phi_t*ln(N_SUB/1.45e10);
        gamma    =  sqrt(2.0*`QQ_NIST2004*`EPS_SI*N_SUB)/C_ox_cm2;
        G        =  gamma/sqrt(phi_t);
    end // initializeModel

//
//  Code independent of bias but dependent on instance parameters
//

    begin : initializeInstance
        C_ox     =  C_ox_cm2*W*L*1.0e4;
        beta     =  MU_T*C_ox_cm2*W/L;
    end // initializeInstance

//
//  DC bias dependent quantities
//
//  Note that the model is symmetric therefore no source/drain flipping is required
//

    begin : evaluateStatic
        V_GB     = -TYPE*V(b_gb);
        V_DB     = -TYPE*V(b_db);
        V_SB     = -TYPE*V(b_sb);
        `psi_calc(psi_s0,V_GB,V_SB,phi_t,twoPhi_F,V_FB_T,G,psi_s0_block)
        `psi_calc(psi_sL,V_GB,V_DB,phi_t,twoPhi_F,V_FB_T,G,psi_sL_block)
        if (((V_GB-V_FB_T)/phi_t)<=`switchPoint) begin // simplified code for when the inversion charge is essentially zero
            mu_deg   =  1.0;
            I_DS     =  0.0;
        end else begin // code that includes calculations for inversion charge and current
            psi_sL0  =  psi_sL-psi_s0;
            exp_mps0 =  exp(-psi_s0/phi_t);
            phi_e0   =  exp(-(twoPhi_F+V_SB)/phi_t)*(phi_t/exp_mps0-psi_s0-phi_t);
            exp_mpsL =  exp(-psi_sL/phi_t);
            phi_eL   =  exp(-(twoPhi_F+V_DB)/phi_t)*(phi_t/exp_mpsL-psi_sL-phi_t);
            psi_sm   =  0.5*(psi_s0+psi_sL);
            exp_mpsm =  exp(-psi_sm/phi_t);
            phi_em   =  0.5*(phi_e0+phi_eL)+psi_sL0*psi_sL0*(0.125*exp_mpsm/phi_t-0.25/(gamma*gamma));
            phi_am   =  phi_t*exp_mpsm+psi_sm-phi_t;
            phi_im   =  gamma*phi_em/(sqrt(phi_am+phi_em)+sqrt(phi_am));
            alpha_m  =  1.0+0.5*gamma*(1.0-exp_mpsm)/sqrt(phi_am);
            mu_deg   =  1.0/(1.0+THETA*(gamma*sqrt(phi_am)+ETA*phi_im));
            I_DS     =  beta*mu_deg*(phi_im+alpha_m*phi_t)*psi_sL0;
        end
        I_DS     = -TYPE*I_DS;
        I_DB     = -TYPE*gmin*V_DB;
        I_SB     = -TYPE*gmin*V_SB;
    end // evaluateStatic

    begin : evaluateDynamic
        if (((V_GB-V_FB_T)/phi_t)<=`switchPoint) begin // simplified code for when the inversion charge is essentially zero
            Q_G      =  C_ox*(V_GB-V_FB_T-0.5*(psi_s0+psi_sL));
            Q_D      =  0.0;
            Q_S      =  0.0;
        end else begin // code that includes calculations for inversion charge and current
            H        =  phi_t+phi_im/alpha_m;
            Q_G      =  C_ox*(gamma*sqrt(phi_am+phi_em)+psi_sL0*psi_sL0/(12.0*H));
            Q_I      = -C_ox*(phi_im+alpha_m*psi_sL0*psi_sL0/(12.0*H));
            Q_D      = -0.5*C_ox*(phi_im-`oneSixth*alpha_m*psi_sL0*(1.0-0.5*(psi_sL0/H)*(1.0+0.1*psi_sL0/H)));
            Q_S      =  Q_I-Q_D;
        end
        Q_G      = -TYPE*Q_G;
        Q_D      = -TYPE*Q_D;
        Q_S      = -TYPE*Q_S;
    end // evaluateDynamic

    begin : loadStatic
        I(b_ds) <+  I_DS;
        I(b_db) <+  I_DB;
        I(b_sb) <+  I_SB;
    end // loadStatic

    begin : loadDynamic
        I(b_gb) <+  ddt(Q_G);
        I(b_db) <+  ddt(Q_D);
        I(b_sb) <+  ddt(Q_S);
    end // loadDynamic

//
//  Noise contributions
//

    begin : noise
        S_iw     =  4.0e-4*`KB_NIST2004*$temperature*MU_T*mu_deg*abs(Q_I)/(L*L);
        I(b_ds) <+  white_noise(S_iw);
    end // noise

end // analog
endmodule
