//======================================================================================
//======================================================================================
// Filename: PSP102_macrodefs.include
//======================================================================================
//======================================================================================
//
//  (c) Copyright notice
//
//  Since 2012 until today, PSP has been co-developed by NXP Semiconductors and
//  Delft University of Technology. For this part of the model, each claim undivided
//  ownership and copyrights
//  Until and including 2011, PSP has been co-developed by NXP Semiconductors and
//  Arizona State University. For this part of the model, NXP Semiconductors claims
//  undivided ownership and copyrights.
//
//
//  Version: 102.5.0, December 2013
//
//======================================================================================
//======================================================================================
//
// Further information can be found in the file releasenotesPSP102.txt
//

/////////////////////////////////////////////
//
// Macros and constants used in PSP
//
/////////////////////////////////////////////

// Explicit Gmin
`define GMIN $simparam("gmin")

`define PMOS                 -1
`define NMOS                 +1

// Some functions
`define MINA(x,y,a)           0.5*((x)+(y)-sqrt(((x)-(y))*((x)-(y))+(a)))
`define MAXA(x,y,a)           0.5*((x)+(y)+sqrt(((x)-(y))*((x)-(y))+(a)))

// Physical constants
`define QMN                   5.951993
`define QMP                   7.448711

// Other constants (PSP-mos)
`define DELTA1                0.02
`define invSqrt2              7.0710678118654746e-01
`define oneSixth              1.6666666666666667e-01


`define OPdef(OPvarname, OPdesc, OPunit) (*desc=OPdesc*) real OPvarname;


/////////////////////////////////////////////////////////////////////////////
//
//  Macro definitions.
//
//  Note that because at present locally scoped variables
//  can only be in named blocks, the intermediate variables
//  used in the macros below must be explicitly declared
//  as variables in the main code.
//
/////////////////////////////////////////////////////////////////////////////


//  sigma    function used in surface potential and other calculations
//           (one call uses expressions for arguments so parentheses
//           around the arguments in the expressions are necessary)
`define sigma(a,c,tau,eta,y) \
    nu         =  (a) + (c); \
    mutau      =  nu * nu + (tau) * (0.5 * ((c) * (c)) - (a)); \
    y          =  (eta) + (a) * nu * (tau) / (mutau + (nu / mutau) * (tau) * (tau) * (c) * ((c) * (c) * `oneThird - (a)));

// modified version of sigma, which takes 4 arguments
`define sigma2(a,b,c,tau,eta,y) \
    nu = (a) + (c); \
    mutau          =  (nu) * (nu) + (tau) * (0.5 * ((c) * (c)) - (a) * (b)); \
    y              =  (eta) + (a) * nu * (tau) / (mutau + (nu / mutau) * (tau) * (tau) * (c) * ((c) * (c) * `oneThird - (a) * (b)));

//
//  sp_s     surface potential calculation
//
`define sp_s(sp,xg,xn,delta) \
    if (abs(xg) <= margin) begin \
        SP_S_temp1 =  inv_xi * inv_xi * `oneSixth * `invSqrt2; \
        sp         =  xg * inv_xi * (1.0 + xg * (1.0 - (delta)) * Gf * SP_S_temp1); \
    end else begin \
        if (xg < -margin) begin \
            SP_S_yg     = -xg; \
            SP_S_ysub   = 1.25 * (SP_S_yg * inv_xi); \
            SP_S_eta    = 0.5 * (SP_S_ysub + 10 - sqrt((SP_S_ysub - 6.0) * (SP_S_ysub - 6.0) + 64.0)); \
            SP_S_temp   = SP_S_yg - SP_S_eta; \
            SP_S_a      = SP_S_temp * SP_S_temp + Gf2*(SP_S_eta + 1.0);\
            SP_S_c      = 2.0 * SP_S_temp - Gf2; \
            SP_S_tau    = -SP_S_eta + ln(SP_S_a * inv_Gf2); \
            `sigma(SP_S_a, SP_S_c, SP_S_tau, SP_S_eta, SP_S_y0) \
            `expl_high(SP_S_y0, SP_S_delta0) \
            SP_S_delta1 = 1.0 / SP_S_delta0; \
            SP_S_temp   = 1.0 / (2.0 + SP_S_y0 * SP_S_y0); \
            SP_S_xi0    = SP_S_y0 * SP_S_y0 * SP_S_temp; \
            SP_S_xi1    = 4.0 * (SP_S_y0 * SP_S_temp * SP_S_temp); \
            SP_S_xi2    = (8.0 * SP_S_temp - 12.0 * SP_S_xi0) * SP_S_temp * SP_S_temp; \
            SP_S_temp   = SP_S_yg - SP_S_y0; \
            SP_S_temp1  = (delta) * SP_S_delta1; \
            SP_S_pC     = 2.0 * SP_S_temp + Gf2 * (SP_S_delta0 - 1.0 - SP_S_temp1 + (delta) * (1.0 - SP_S_xi1)); \
            SP_S_qC     = SP_S_temp * SP_S_temp - Gf2 * (SP_S_delta0 - SP_S_y0 - 1.0 + SP_S_temp1 + (delta) * (SP_S_y0 - 1.0 - SP_S_xi0)); \
            SP_S_temp   = 2.0 - Gf2 * (SP_S_delta0 + SP_S_temp1 - (delta) * SP_S_xi2); \
            SP_S_temp   = SP_S_pC * SP_S_pC - 2.0 * (SP_S_qC * SP_S_temp); \
            sp          = -SP_S_y0 - 2.0 * (SP_S_qC / (SP_S_pC + sqrt(SP_S_temp))); \
        end else begin \
            SP_xg1    = 1.0 / (x1 + Gf * 7.324648775608221e-001); \
            SP_S_A_fac= (xi * x1 * SP_xg1 - 1.0) * SP_xg1; \
            SP_S_xbar = xg * inv_xi * (1.0 + SP_S_A_fac * xg); \
            `expl_low(-SP_S_xbar, SP_S_temp) \
            SP_S_w    = 1.0 - SP_S_temp; \
            SP_S_x1   = xg + Gf2 * 0.5 - Gf * sqrt(xg + Gf2 * 0.25 - SP_S_w); \
            SP_S_bx   = (xn) + 3.0; \
            SP_S_eta  = `MINA(SP_S_x1, SP_S_bx, 5.0) - 0.5 * (SP_S_bx - sqrt(SP_S_bx * SP_S_bx + 5.0)); \
            SP_S_temp = xg - SP_S_eta; \
            SP_S_temp1= exp(-SP_S_eta); \
            SP_S_temp2= 1.0 / (2.0 + SP_S_eta * SP_S_eta); \
            SP_S_xi0  = SP_S_eta * SP_S_eta * SP_S_temp2; \
            SP_S_xi1  = 4.0 * (SP_S_eta * SP_S_temp2 * SP_S_temp2); \
            SP_S_xi2  = (8.0 * SP_S_temp2 - 12.0 * SP_S_xi0) * SP_S_temp2 * SP_S_temp2; \
            SP_S_a    = max(1.0e-40, SP_S_temp * SP_S_temp - Gf2 * (SP_S_temp1 + SP_S_eta - 1.0 - (delta) * (SP_S_eta + 1.0 + SP_S_xi0))); \
            SP_S_b    = 1.0 - 0.5 * (Gf2 * (SP_S_temp1 - (delta) * SP_S_xi2)); \
            SP_S_c    = 2.0 * SP_S_temp + Gf2 * (1.0 - SP_S_temp1 - (delta) * (1.0 + SP_S_xi1)); \
            SP_S_tau  = (xn) - SP_S_eta + ln(SP_S_a / Gf2); \
            `sigma2(SP_S_a, SP_S_b, SP_S_c, SP_S_tau, SP_S_eta, SP_S_x0) \
            if (SP_S_x0 < `se05) begin \
                SP_S_delta0 = exp(SP_S_x0); \
                SP_S_delta1 = 1.0 / SP_S_delta0; \
                SP_S_delta0 = (delta) * SP_S_delta0; \
            end else begin \
                if (SP_S_x0 > (xn) - `se05) begin \
                    SP_S_delta0 = exp(SP_S_x0 - (xn)); \
                    SP_S_delta1 = (delta) / SP_S_delta0; \
                end else begin \
                    SP_S_delta0 = `ke05 / `P3((xn) - SP_S_x0 - `se05); \
                    SP_S_delta1 = `ke05 / `P3(SP_S_x0 - `se05); \
                end \
            end \
            SP_S_temp   = 1.0 / (2.0 + SP_S_x0 * SP_S_x0); \
            SP_S_xi0    = SP_S_x0 * SP_S_x0 * SP_S_temp; \
            SP_S_xi1    = 4.0 * (SP_S_x0 * SP_S_temp * SP_S_temp); \
            SP_S_xi2    = (8.0 * SP_S_temp - 12.0 * SP_S_xi0) * SP_S_temp * SP_S_temp; \
            SP_S_temp   = xg - SP_S_x0; \
            SP_S_pC     = 2.0 * SP_S_temp + Gf2 * (1.0 - SP_S_delta1 + SP_S_delta0 - (delta) * (1.0 + SP_S_xi1)); \
            SP_S_qC     = SP_S_temp * SP_S_temp - Gf2 * (SP_S_delta1 + SP_S_x0 - 1.0 + SP_S_delta0 - (delta) * (SP_S_x0 + 1.0 + SP_S_xi0)); \
            SP_S_temp   = 2.0 - Gf2 * (SP_S_delta1 + SP_S_delta0 - (delta) * SP_S_xi2); \
            SP_S_temp   = SP_S_pC * SP_S_pC - 2.0 * (SP_S_qC * SP_S_temp); \
            sp          = SP_S_x0 + 2.0 * (SP_S_qC / (SP_S_pC + sqrt(SP_S_temp))); \
        end \
    end

//
//  sp_s_d     surface potential calculation at drain (subset of function sp_s)
//
`define sp_s_d(sp,xg,xn,delta) \
    if (abs(xg) <= margin) begin \
        SP_S_temp1 =  inv_xi * inv_xi * `oneSixth * `invSqrt2; \
        sp         =  xg * inv_xi * (1.0 + xg * (1.0 - (delta)) * Gf * SP_S_temp1); \
    end else begin \
        SP_S_bx   = (xn) + 3; \
        SP_S_eta  = `MINA(SP_S_x1, SP_S_bx, 5.0) - 0.5 * (SP_S_bx - sqrt(SP_S_bx * SP_S_bx + 5.0)); \
        SP_S_temp = xg - SP_S_eta; \
        SP_S_temp1= exp(-SP_S_eta); \
        SP_S_temp2= 1.0 / (2.0 + SP_S_eta * SP_S_eta); \
        SP_S_xi0  = SP_S_eta * SP_S_eta * SP_S_temp2; \
        SP_S_xi1  = 4.0 * (SP_S_eta * SP_S_temp2 * SP_S_temp2); \
        SP_S_xi2  = (8.0 * SP_S_temp2 - 12.0 * SP_S_xi0) * SP_S_temp2 * SP_S_temp2; \
        SP_S_a    = max(1.0e-40, SP_S_temp * SP_S_temp - Gf2 * (SP_S_temp1 + SP_S_eta - 1.0 - (delta) * (SP_S_eta + 1.0 + SP_S_xi0))); \
        SP_S_b    = 1.0 - 0.5 * (Gf2 * (SP_S_temp1 - (delta) * SP_S_xi2)); \
        SP_S_c    = 2.0 * SP_S_temp + Gf2 * (1.0 - SP_S_temp1 - (delta) * (1.0 + SP_S_xi1)); \
        SP_S_tau  = (xn) - SP_S_eta + ln(SP_S_a / Gf2); \
        `sigma2(SP_S_a, SP_S_b, SP_S_c, SP_S_tau, SP_S_eta, SP_S_x0) \
        if (SP_S_x0 < `se05) begin \
            SP_S_delta0 = exp(SP_S_x0); \
            SP_S_delta1 = 1.0 / SP_S_delta0; \
            SP_S_delta0 = (delta) * SP_S_delta0; \
        end else begin \
            if (SP_S_x0 > (xn) - `se05) begin \
                SP_S_delta0 = exp(SP_S_x0 - (xn)); \
                SP_S_delta1 = (delta) / SP_S_delta0; \
            end else begin \
                SP_S_delta0 = `ke05 / `P3((xn) - SP_S_x0 - `se05); \
                SP_S_delta1 = `ke05 / `P3(SP_S_x0 - `se05); \
            end \
        end \
        SP_S_temp   = 1.0 / (2.0 + SP_S_x0 * SP_S_x0); \
        SP_S_xi0    = SP_S_x0 * SP_S_x0 * SP_S_temp; \
        SP_S_xi1    = 4.0 * (SP_S_x0 * SP_S_temp * SP_S_temp); \
        SP_S_xi2    = (8.0 * SP_S_temp-12.0 * SP_S_xi0) * SP_S_temp * SP_S_temp; \
        SP_S_temp   = xg - SP_S_x0; \
        SP_S_pC     = 2.0 * SP_S_temp + Gf2 * (1.0 - SP_S_delta1 + SP_S_delta0 - (delta) * (1.0 + SP_S_xi1)); \
        SP_S_qC     = SP_S_temp * SP_S_temp - Gf2 * (SP_S_delta1 + SP_S_x0 - 1.0 + SP_S_delta0 - (delta) * (SP_S_x0 + 1.0 + SP_S_xi0)); \
        SP_S_temp   = 2.0 - Gf2*(SP_S_delta1+SP_S_delta0-(delta)*SP_S_xi2); \
        SP_S_temp   = SP_S_pC * SP_S_pC - 2.0 * (SP_S_qC * SP_S_temp); \
        sp          = SP_S_x0 + 2.0 * (SP_S_qC / (SP_S_pC + sqrt(SP_S_temp)));\
    end

//
//  sp_ov    surface potential calculation for the overlap regions
//
`define sp_ov(sp,xg) \
    if (abs(xg) <= x_mrg_ov) begin \
        sp         =  (-(xg) * inv_xi_ov); \
    end else begin \
        if (xg < -x_mrg_ov) begin \
            SP_OV_yg   = -xg; \
            SP_OV_z    =  x1 * SP_OV_yg * inv_xi_ov; \
            SP_OV_eta  =  0.5 * (SP_OV_z + 10.0 - sqrt((SP_OV_z - 6.0) * (SP_OV_z - 6.0) + 64.0)); \
            SP_OV_a    =  (SP_OV_yg - SP_OV_eta) * (SP_OV_yg - SP_OV_eta) + GOV2 * (SP_OV_eta + 1.0); \
            SP_OV_c    =  2.0 * (SP_OV_yg - SP_OV_eta) - GOV2; \
            SP_OV_tau  =  ln(SP_OV_a / GOV2) - SP_OV_eta; \
            `sigma(SP_OV_a, SP_OV_c, SP_OV_tau, SP_OV_eta, SP_OV_y0) \
            SP_OV_D0   =  exp(SP_OV_y0); \
            SP_OV_temp =  SP_OV_yg - SP_OV_y0; \
            SP_OV_p    =  2.0 * SP_OV_temp + GOV2 * (SP_OV_D0 - 1.0); \
            SP_OV_q    =  SP_OV_temp * SP_OV_temp + GOV2 * (SP_OV_y0 + 1.0 - SP_OV_D0); \
            SP_OV_xi   =  1.0 - GOV2 * 0.5 * SP_OV_D0; \
            SP_OV_temp =  SP_OV_p * SP_OV_p - 4.0 * (SP_OV_xi * SP_OV_q); \
            SP_OV_w    =  2.0 * (SP_OV_q / (SP_OV_p + sqrt(SP_OV_temp))); \
            sp         = -(SP_OV_y0 + SP_OV_w); \
        end else begin \
            SP_OV_Afac =  (xi_ov * x1 * inv_xg1 - 1.0) * inv_xg1; \
            SP_OV_xbar =  xg * inv_xi_ov * (1.0 + SP_OV_Afac * xg); \
            `expl_low(-SP_OV_xbar, SP_OV_temp) \
            SP_OV_w    =  1.0 - SP_OV_temp; \
            SP_OV_x0   =  xg + GOV2 * 0.5 - GOV * sqrt(xg + GOV2 * 0.25 - SP_OV_w); \
            `expl_low(-SP_OV_x0, SP_OV_D0) \
            SP_OV_p    =  2.0 * (xg - SP_OV_x0) + GOV2 * (1 - SP_OV_D0); \
            SP_OV_q    =  (xg - SP_OV_x0) * (xg - SP_OV_x0) - GOV2 * (SP_OV_x0 - 1.0 + SP_OV_D0); \
            SP_OV_xi   =  1.0 - GOV2 * 0.5 * SP_OV_D0; \
            SP_OV_temp =  SP_OV_p * SP_OV_p - 4.0 * (SP_OV_xi * SP_OV_q); \
            SP_OV_u    =  2.0 * (SP_OV_q / (SP_OV_p + sqrt(SP_OV_temp))); \
            sp         =  SP_OV_x0 + SP_OV_u; \
        end \
        sp         = -sp; \
    end

// This construct is used for parasitic resistances.
// If R=0, the Verilog-A compiler should recognize
// that the corresponding nodes can be collapsed
`define CollapsableR(G, R, SN, N1, N2) \
    if ((R) > 0.0) begin \
        I(N1, N2) <+ MULT_i * (G) * V(N1, N2); \
        I(N1, N2) <+ white_noise(MULT_i * SN); \
    end else begin \
        V(N1, N2) <+ 0.0; \
    end

`define NonCollapsableR(G, R, SN, N1, N2) \
    I(N1, N2) <+ MULT_i * (G) * V(N1, N2); \
    I(N1, N2) <+ white_noise(MULT_i * SN, "thermal noise");

// This macro is used for asymmetric overlap regions.
// The following variables are changed to those for source/drain side.
`define ChangeToSource \
    GOV       = GOV_s; \
    GOV2      = GOV2_s; \
    xi_ov     = xi_ov_s; \
    inv_xi_ov = inv_xi_ov_s; \
    x_mrg_ov  = x_mrg_ov_s; \
    inv_xg1   = inv_xg1_s;


`define ChangeToDrain \
    GOV       = GOV_d; \
    GOV2      = GOV2_d; \
    xi_ov     = xi_ov_d; \
    inv_xi_ov = inv_xi_ov_d; \
    x_mrg_ov  = x_mrg_ov_d; \
    inv_xg1   = inv_xg1_d;
