Logo Search packages:      
Sourcecode: gnucap version File versions  Download package

d_mos5.cc

/* $Id: d_mos5.model,v 21.14 2002/03/26 09:20:25 al Exp $ -*- C++ -*-
 * Copyright (C) 2001 Albert Davis
 * Author: Albert Davis <aldavis@ieee.org>
 *
 * This file is part of "Gnucap", the Gnu Circuit Analysis Package
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.
 *------------------------------------------------------------------
 * Spice BSIM2 model
 * derived from Spice3f4,Copyright 1990 Regents of the University of California
 * 1988 Min-Chie Jeng, Hong J. Park, Thomas L. Quarles
 * Recoded for Gnucap model compiler, Al Davis, 2000
 */
/* This file is automatically generated. DO NOT EDIT */
#include "ap.h"
#include "d_mos5.h"
/*--------------------------------------------------------------------------*/
const double NA(NOT_INPUT);
const double INF(BIGBIG);
/*--------------------------------------------------------------------------*/
int MODEL_MOS5::_count = 0;
/*--------------------------------------------------------------------------*/
SDP_MOS5::SDP_MOS5(const COMMON_COMPONENT* cc)
  :SDP_MOS_BASE(cc)
{
  assert(cc);
  const COMMON_MOS* c = prechecked_cast<const COMMON_MOS*>(cc);
  assert(c);
  const MODEL_MOS5* m = prechecked_cast<const MODEL_MOS5*>(c->model());
  assert(m);

      l_eff -= m->dl;
      w_eff -= m->dw;
      cgate = m->cox * w_eff * l_eff;
      double L = l_eff/MICRON2METER;
      double W = w_eff/MICRON2METER;
      double CoxWoverL = 1e-4 * m->cox * w_eff / l_eff;
  cgate = m->cox * w_eff * l_eff;
  phi = m->phi(L, W);
  vfb = m->vfb(L, W);
  k1 = m->k1(L, W);
  k2 = m->k2(L, W);
  eta0 = m->eta0(L, W);
  etaB = m->etaB(L, W);
  mob0 = m->mob0(L, W);
  mob0B = m->mob0B(L, W);
  mobs0 = m->mobs0(L, W);
  mobsB = m->mobsB(L, W);
  mob20 = m->mob20(L, W);
  mob2B = m->mob2B(L, W);
  mob2G = m->mob2G(L, W);
  mob30 = m->mob30(L, W);
  mob3B = m->mob3B(L, W);
  mob3G = m->mob3G(L, W);
  mob40 = m->mob40(L, W);
  mob4B = m->mob4B(L, W);
  mob4G = m->mob4G(L, W);
  ua0 = m->ua0(L, W);
  uaB = m->uaB(L, W);
  ub0 = m->ub0(L, W);
  ubB = m->ubB(L, W);
  u10 = m->u10(L, W);
  u1B = m->u1B(L, W);
  u1D = m->u1D(L, W);
  n0 = m->n0(L, W);
  nB = m->nB(L, W);
  nD = m->nD(L, W);
  vof0 = m->vof0(L, W);
  vofB = m->vofB(L, W);
  vofD = m->vofD(L, W);
  ai0 = m->ai0(L, W);
  aiB = m->aiB(L, W);
  bi0 = m->bi0(L, W);
  biB = m->biB(L, W);
  vghigh = m->vghigh(L, W);
  vglow = m->vglow(L, W);
  beta0 = mob0 * CoxWoverL;
  beta0B = mob0B * CoxWoverL;
  betas0 = mobs0 * CoxWoverL;
  betas0 = std::max(betas0, 1.01*beta0);
  betasB = mobsB * CoxWoverL;
  beta20 = mob20;
  beta2B = mob2B;
  beta2G = mob2G;
  beta30 = mob30 * CoxWoverL;
  beta3B = mob3B * CoxWoverL;
  beta3G = mob3G * CoxWoverL;
  beta40 = mob40 * CoxWoverL;
  beta4B = mob4B * CoxWoverL;
  beta4G = mob4G * CoxWoverL;
  Phis3 = sqrt(phi) * phi;
  One_Third_CoxWL = cgate / 3.0;
  Two_Third_CoxWL = 2.0 * One_Third_CoxWL;

      double tmp = betas0 - beta0 - beta0B * m->vbb;
      if ((-betasB * m->vbb) > tmp) {
      untested();
      betasB = -tmp / m->vbb;
      }
      Arg = betasB - beta0B - m->vdd * (beta3B - m->vdd * beta4B);
}
/*--------------------------------------------------------------------------*/
TDP_MOS5::TDP_MOS5(const DEV_MOS* d)
  :TDP_MOS_BASE(d)
{
}
/*--------------------------------------------------------------------------*/
MODEL_MOS5::MODEL_MOS5()
  :MODEL_MOS_BASE(),
   phi(0.),
   vfb(0.),
   k1(0.),
   k2(0.),
   eta0(0.),
   etaB(0.),
   mob0(0.),
   mob0B(0.),
   mobs0(0.),
   mobsB(0.),
   mob20(0.),
   mob2B(0.),
   mob2G(0.),
   mob30(0.),
   mob3B(0.),
   mob3G(0.),
   mob40(0.),
   mob4B(0.),
   mob4G(0.),
   ua0(0.),
   uaB(0.),
   ub0(0.),
   ubB(0.),
   u10(0.),
   u1B(0.),
   u1D(0.),
   n0(0.),
   nB(0.),
   nD(0.),
   vof0(0.),
   vofB(0.),
   vofD(0.),
   ai0(0.),
   aiB(0.),
   bi0(0.),
   biB(0.),
   vghigh(0.),
   vglow(0.),
   dl(0.),
   dw(0.),
   tox(0.),
   vdd(0.),
   vgg(0.),
   vbb(0.),
   wdf(0.),
   dell(0.),
   temp(300.15),
   xpart(0.),
   cox(NA),
   vdd2(NA),
   vgg2(NA),
   vbb2(NA),
   Vtm(NA)
{
  ++_count;
  mjsw = NA;
  pb = NA;
  pbsw = NA;
  cjo = 0.0;
  mos_level = LEVEL;
}
/*--------------------------------------------------------------------------*/
bool MODEL_MOS5::parse_front(CS& cmd)
{
  return MODEL_MOS_BASE::parse_front(cmd);
}
/*--------------------------------------------------------------------------*/
bool MODEL_MOS5::parse_params(CS& cmd)
{
  return ONE_OF
    || get(cmd, "DIODElevel", &mos_level)
    || get(cmd, "PHI", &phi)
    || get(cmd, "VFB", &vfb)
    || get(cmd, "K1", &k1)
    || get(cmd, "K2", &k2)
    || get(cmd, "ETA0", &eta0)
    || get(cmd, "ETAB", &etaB)
    || get(cmd, "MU0", &mob0)
    || get(cmd, "MU0B", &mob0B)
    || get(cmd, "MUS0", &mobs0)
    || get(cmd, "MUSB", &mobsB)
    || get(cmd, "MU20", &mob20)
    || get(cmd, "MU2B", &mob2B)
    || get(cmd, "MU2G", &mob2G)
    || get(cmd, "MU30", &mob30)
    || get(cmd, "MU3B", &mob3B)
    || get(cmd, "MU3G", &mob3G)
    || get(cmd, "MU40", &mob40)
    || get(cmd, "MU4B", &mob4B)
    || get(cmd, "MU4G", &mob4G)
    || get(cmd, "UA0", &ua0)
    || get(cmd, "UAB", &uaB)
    || get(cmd, "UB0", &ub0)
    || get(cmd, "UBB", &ubB)
    || get(cmd, "U10", &u10)
    || get(cmd, "U1B", &u1B)
    || get(cmd, "U1D", &u1D)
    || get(cmd, "N0", &n0, mPOSITIVE)
    || get(cmd, "NB", &nB)
    || get(cmd, "ND", &nD)
    || get(cmd, "VOF0", &vof0)
    || get(cmd, "VOFB", &vofB)
    || get(cmd, "VOFD", &vofD)
    || get(cmd, "AI0", &ai0)
    || get(cmd, "AIB", &aiB)
    || get(cmd, "BI0", &bi0)
    || get(cmd, "BIB", &biB)
    || get(cmd, "VGHIGH", &vghigh)
    || get(cmd, "VGLOW", &vglow)
    || get(cmd, "DL", &dl, mSCALE, MICRON2METER)
    || get(cmd, "DW", &dw, mSCALE, MICRON2METER)
    || get(cmd, "TOX", &tox, mSCALE, MICRON2METER)
    || get(cmd, "VDD", &vdd)
    || get(cmd, "VGG", &vgg)
    || get(cmd, "VBB", &vbb)
    || get(cmd, "WDF", &wdf)
    || get(cmd, "DELL", &dell)
    || get(cmd, "TEMP", &temp, mOFFSET, -ABS_ZERO)
    || get(cmd, "XPART", &xpart)
    || MODEL_MOS_BASE::parse_params(cmd)
    ;
}
/*--------------------------------------------------------------------------*/
void MODEL_MOS5::parse_finish()
{
  MODEL_MOS_BASE::parse_finish();
  if (mjsw == NA) {
    mjsw = .33;
  }
  if (pb == NA) {
    pb = 0.1;
  }
  pb = std::max(pb, 0.1);
  if (pbsw == NA) {
    pbsw = pb;
  }
  pbsw = std::max(pbsw, 0.1);
  cmodel = ((!cmodel)?1:cmodel);
  tox = std::max(tox, 1e-20);
  cox = 3.453e-11 /*E_OX*/ / tox;
  vdd2 = 2 * vdd;
  vgg2 = 2 * vgg;
  vbb2 = 2 * vbb;
  Vtm = 8.625e-5 /*K/Q*/ * (temp -.15);
}
/*--------------------------------------------------------------------------*/
SDP_CARD* MODEL_MOS5::new_sdp(const COMMON_COMPONENT* c)const
{
  assert(c);
  {if (dynamic_cast<const COMMON_MOS*>(c)) {
    return new SDP_MOS5(c);
  }else{
    return MODEL_MOS_BASE::new_sdp(c);
  }}
}
/*--------------------------------------------------------------------------*/
void MODEL_MOS5::print_front(OMSTREAM& o)const
{
  MODEL_MOS_BASE::print_front(o);
}
/*--------------------------------------------------------------------------*/
void MODEL_MOS5::print_params(OMSTREAM& o)const
{
  o << "level=5";
  MODEL_MOS_BASE::print_params(o);
  if (mos_level != LEVEL)
    o << "  diodelevel=" << mos_level;
  phi.print(o, "phi");
  vfb.print(o, "vfb");
  k1.print(o, "k1");
  k2.print(o, "k2");
  eta0.print(o, "eta0");
  etaB.print(o, "etab");
  mob0.print(o, "mu0");
  mob0B.print(o, "mu0b");
  mobs0.print(o, "mus0");
  mobsB.print(o, "musb");
  mob20.print(o, "mu20");
  mob2B.print(o, "mu2b");
  mob2G.print(o, "mu2g");
  mob30.print(o, "mu30");
  mob3B.print(o, "mu3b");
  mob3G.print(o, "mu3g");
  mob40.print(o, "mu40");
  mob4B.print(o, "mu4b");
  mob4G.print(o, "mu4g");
  ua0.print(o, "ua0");
  uaB.print(o, "uab");
  ub0.print(o, "ub0");
  ubB.print(o, "ubb");
  u10.print(o, "u10");
  u1B.print(o, "u1b");
  u1D.print(o, "u1d");
  n0.print(o, "n0");
  nB.print(o, "nb");
  nD.print(o, "nd");
  vof0.print(o, "vof0");
  vofB.print(o, "vofb");
  vofD.print(o, "vofd");
  ai0.print(o, "ai0");
  aiB.print(o, "aib");
  bi0.print(o, "bi0");
  biB.print(o, "bib");
  vghigh.print(o, "vghigh");
  vglow.print(o, "vglow");
  o << "  dl=" << dl/(MICRON2METER);
  o << "  dw=" << dw/(MICRON2METER);
  o << "  tox=" << tox/(MICRON2METER);
  o << "  vdd=" << vdd;
  o << "  vgg=" << vgg;
  o << "  vbb=" << vbb;
  o << "  wdf=" << wdf;
  o << "  dell=" << dell;
  o << "  temp=" << temp-(-ABS_ZERO);
  o << "  xpart=" << xpart;
}
/*--------------------------------------------------------------------------*/
void MODEL_MOS5::print_calculated(OMSTREAM& o)const
{
  MODEL_MOS_BASE::print_calculated(o);
}
/*--------------------------------------------------------------------------*/
bool MODEL_MOS5::is_valid(const COMMON_COMPONENT* cc)const
{
  return MODEL_MOS_BASE::is_valid(cc);
}
/*--------------------------------------------------------------------------*/
void MODEL_MOS5::tr_eval(COMPONENT* brh)const
{
  DEV_MOS* d = prechecked_cast<DEV_MOS*>(brh);
  assert(d);
  const COMMON_MOS* c = prechecked_cast<const COMMON_MOS*>(d->common());
  assert(c);
  const SDP_MOS5* s = prechecked_cast<const SDP_MOS5*>(c->sdp());
  assert(s);
  const MODEL_MOS5* m = this;

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
    trace3("", d->vds, d->vgs, d->vbs);
    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 
    d->reverse_if_needed();
    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
    trace4("", c->lo, m->dl, c->wo, m->dw);
    
    double Vbs = std::max(m->vbb2, d->vbs);
    double Vgs = std::min(m->vgg2, d->vgs);
    double Vds = std::min(m->vdd2, d->vds);
    trace3("", Vbs, Vgs, Vds);
    
    /* Threshold Voltage. */
    double Phisb, dPhisb_dVb, T1s, dT1s_dVb;
    {if (Vbs <= 0.0) {
      d->sbfwd = false;
      Phisb = s->phi - Vbs;
      dPhisb_dVb = -1.0;
      T1s = sqrt(Phisb);
      dT1s_dVb = -0.5 / T1s;
      trace4("-", Phisb, dPhisb_dVb, T1s, dT1s_dVb);
    }else{
      d->sbfwd = true;
      double tmp = s->phi / (s->phi + Vbs);
      Phisb = s->phi * tmp;
      dPhisb_dVb = -tmp * tmp;
      T1s = s->Phis3 / (s->phi + 0.5 * Vbs);
      dT1s_dVb = -0.5 * T1s * T1s / s->Phis3;
      trace4("+", Phisb, dPhisb_dVb, T1s, dT1s_dVb);
    }}
    
    double Eta = s->eta0 + s->etaB * Vbs;
    double Ua = s->ua0 + s->uaB * Vbs;
    double Ub = s->ub0 + s->ubB * Vbs;
    double U1s = s->u10 + s->u1B * Vbs;
    trace4("", Eta, Ua, Ub, U1s);
    
    d->von = s->vfb + s->phi + s->k1*T1s - s->k2*Phisb - Eta*Vds;
    double dVth_dVd = -Eta;
    double dVth_dVb = s->k1 * dT1s_dVb + s->k2 - s->etaB * Vds;
    d->vgst = Vgs - d->von;
    trace4("", d->von, dVth_dVd, dVth_dVb, d->vgst);
    
    double Aa, dAa_dVb, Inv_Aa;
    {
      double tmp = 1.0 / (1.744 + 0.8364 * Phisb);
      double Gg = 1.0 - tmp;
      double dGg_dVb = 0.8364 * tmp * tmp * dPhisb_dVb;
      double T0 = Gg / T1s;
      trace4("", tmp, Gg, dGg_dVb, T0);
      double tmp1 = 0.5 * T0 * s->k1;
      Aa = 1.0 + tmp1;
      dAa_dVb = (Aa - 1.0) * (dGg_dVb / Gg - dT1s_dVb / T1s);
      Inv_Aa = 1.0 / Aa;
      trace4("", tmp1, Aa, dAa_dVb, Inv_Aa);
    }
    
    trace3("", s->vghigh, s->vglow, d->vgst);
    
    double Exp0, Exp1, n, Vgeff, dVgeff_dVg, dVgeff_dVd, dVgeff_dVb;
    {if ((d->vgst >= s->vghigh) || (s->n0 == 0.0)) {
      Exp0 = NOT_VALID;
      Exp1 = NOT_VALID;
      n = NOT_VALID;
      Vgeff = d->vgst;
      dVgeff_dVg = 1.0;
      dVgeff_dVd = -dVth_dVd;
      dVgeff_dVb = -dVth_dVb;
      trace0("vgst>vghigh");
    }else{
      double Vof = s->vof0 + s->vofB * Vbs + s->vofD * Vds;
      n = s->n0 + s->nB / T1s + s->nD * Vds;
      double tmp = 0.5 / (n * m->Vtm);
      trace3("", Vof, n, tmp);
      
      double ExpArg1 = -Vds / m->Vtm;
      ExpArg1 = std::max(ExpArg1, -30.0);
      Exp1 = exp(ExpArg1);
      double tmp1 = 1.0 - Exp1;
      tmp1 = std::max(tmp1, 1.0e-18);
      double tmp2 = 2.0 * Aa * tmp1;
      trace4("", ExpArg1, Exp1, tmp1, tmp2);
      
      // exports Exp0, Vgeff, dVgeff_dVg, dVgeff_dVd, dVgeff_dVb
      {if (d->vgst <= s->vglow) {
      double ExpArg = d->vgst * tmp;
      ExpArg = std::max(ExpArg, -30.0);
      Exp0 = exp(0.5 * Vof + ExpArg);
      Vgeff = sqrt(tmp2) * m->Vtm * Exp0;
      double T0 = n * m->Vtm;
      dVgeff_dVg = Vgeff * tmp;
      dVgeff_dVd = dVgeff_dVg * (n / tmp1 * Exp1 - dVth_dVd 
                           - d->vgst * s->nD / n + T0 * s->vofD);
      dVgeff_dVb = dVgeff_dVg * (s->vofB * T0 - dVth_dVb 
                         + s->nB * d->vgst / (n * T1s * T1s) * dT1s_dVb
                         + T0 * Inv_Aa * dAa_dVb);
      trace0("vgst<vglow");
      }else{
      double ExpArg = s->vglow * tmp;
      ExpArg = std::max(ExpArg, -30.0);
      Exp0 = exp(0.5 * Vof + ExpArg);
      Vgeff = sqrt(2.0 * Aa * (1.0 - Exp1)) * m->Vtm * Exp0;
      double Con1 = s->vghigh;
      double Con3 = Vgeff;
      double Con4 = Con3 * tmp;
      double SqrVghigh = s->vghigh * s->vghigh;
      double SqrVglow = s->vglow * s->vglow;
      double CubVghigh = s->vghigh * SqrVghigh;
      double CubVglow = s->vglow * SqrVglow;
      double T0 = 2.0 * s->vghigh;
      double T1 = 2.0 * s->vglow;
      double T2 = 3.0 * SqrVghigh;
      double T3 = 3.0 * SqrVglow;
      double T4 = s->vghigh - s->vglow;
      double T5 = SqrVghigh - SqrVglow;
      double T6 = CubVghigh - CubVglow;
      double T7 = Con1 - Con3;
      double delta = (T1-T0) * T6 + (T2-T3) * T5 + (T0*T3 - T1*T2) * T4;
      delta = 1.0 / delta;
      double Coeffb = (T1 - Con4 * T0) * T6 + (Con4 * T2 - T3) * T5 
        + (T0 * T3 - T1 * T2) * T7;
      double Coeffc = (Con4-1.0) * T6 + (T2-T3) * T7 + (T3 - Con4*T2) * T4;
      double Coeffd = (T1-T0) * T7 + (1.0-Con4) * T5 + (Con4*T0 - T1) * T4;
      double Coeffa = SqrVghigh * (Coeffc + Coeffd * T0);
      Vgeff = (Coeffa + d->vgst * (Coeffb + d->vgst*(Coeffc+d->vgst*Coeffd)))
        *delta;
      dVgeff_dVg = (Coeffb + d->vgst*(2.0*Coeffc+3.0*d->vgst*Coeffd)) *delta;
      T7 = Con3 * tmp;
      double T8 = dT1s_dVb * s->nB / (T1s * T1s * n);
      double T9 = n * m->Vtm;
      double dCon3_dVd = T7*(n*Exp1/tmp1 -s->vglow*s->nD/n + T9*s->vofD);
      double dCon3_dVb = T7*(T9*Inv_Aa*dAa_dVb + s->vglow*T8 + T9*s->vofB);
      double dCon4_dVd = tmp * dCon3_dVd - T7 * s->nD / n;
      double dCon4_dVb = tmp * dCon3_dVb + T7 * T8;
      
      double dCoeffb_dVd = dCon4_dVd*(T2*T5-T0*T6) + dCon3_dVd*(T1*T2-T0*T3);
      double dCoeffc_dVd = dCon4_dVd * (T6 - T2*T4) + dCon3_dVd * (T3 - T2);
      double dCoeffd_dVd = dCon4_dVd * (T0*T4 - T5) + dCon3_dVd * (T0 - T1);
      double dCoeffa_dVd = SqrVghigh * (dCoeffc_dVd + dCoeffd_dVd * T0);
      
      dVgeff_dVd = -dVgeff_dVg * dVth_dVd + (dCoeffa_dVd 
            + d->vgst * (dCoeffb_dVd + d->vgst 
                      * (dCoeffc_dVd + d->vgst * dCoeffd_dVd))) * delta;
      
      double dCoeffb_dVb = dCon4_dVb*(T2*T5-T0*T6) + dCon3_dVb*(T1*T2-T0*T3);
      double dCoeffc_dVb = dCon4_dVb * (T6 - T2*T4) + dCon3_dVb * (T3 - T2);
      double dCoeffd_dVb = dCon4_dVb * (T0*T4 - T5) + dCon3_dVb * (T0 - T1);
      double dCoeffa_dVb = SqrVghigh * (dCoeffc_dVb + dCoeffd_dVb * T0);
      
      dVgeff_dVb = -dVgeff_dVg * dVth_dVb + (dCoeffa_dVb 
            + d->vgst * (dCoeffb_dVb + d->vgst
                       * (dCoeffc_dVb + d->vgst * dCoeffd_dVb))) * delta;
      trace0("else");
      }}
    }}
    trace3("", Exp0, Exp1, n);
    trace4("", Vgeff, dVgeff_dVg, dVgeff_dVd, dVgeff_dVb);
    
    double dVdsat_dVd, dVdsat_dVg, dVdsat_dVb;
    {if (Vgeff > 0.0) { // normal operation
      d->cutoff = false;
      double Uvert = 1.0 + Vgeff * (Ua + Vgeff * Ub);
      Uvert = std::max(Uvert, 0.2);
      double Inv_Uvert = 1.0 / Uvert;
      double dUvert_dVg, dUvert_dVd, dUvert_dVb;
      {
      double T8 = Ua + 2.0 * Ub * Vgeff;
      dUvert_dVg = T8 * dVgeff_dVg;
      dUvert_dVd = T8 * dVgeff_dVd;
      dUvert_dVb = T8 * dVgeff_dVb + Vgeff * (s->uaB + Vgeff * s->ubB);
      trace2("", T8, Uvert);
      trace3("", dUvert_dVg, dUvert_dVd, dUvert_dVb);
      }
      
      double Vc, dVc_dVg, dVc_dVd, dVc_dVb;
      {
      double T8 = U1s * Inv_Aa * Inv_Uvert;
      Vc = T8 * Vgeff;
      double T9 = Vc * Inv_Uvert;
      dVc_dVg = T8 * dVgeff_dVg - T9 * dUvert_dVg;
      dVc_dVd = T8 * dVgeff_dVd - T9 * dUvert_dVd;
      dVc_dVb = T8 * dVgeff_dVb 
        + s->u1B * Vgeff * Inv_Aa * Inv_Uvert
        - Vc * Inv_Aa * dAa_dVb
        - T9 * dUvert_dVb;
      trace3("", T8, T9, Vc);
      trace3("", dVc_dVg, dVc_dVd, dVc_dVb);
      }
      
      double Kk, dKk_dVc;
      {
      double tmp2 = sqrt(1.0 + 2.0 * Vc);
      Kk = 0.5 * (1.0 + Vc + tmp2);
      dKk_dVc = 0.5  + 0.5 / tmp2;
      trace3("", tmp2, Kk, dKk_dVc);
      }
      
      {
      double T8 = Inv_Aa / sqrt(Kk);
      d->vdsat = std::max(Vgeff * T8, 1.0e-18);
      double T9 = 0.5 * d->vdsat * dKk_dVc / Kk;
      dVdsat_dVd = T8 * dVgeff_dVd - T9 * dVc_dVd;
      dVdsat_dVg = T8 * dVgeff_dVg - T9 * dVc_dVg;
      dVdsat_dVb = T8 * dVgeff_dVb - T9 * dVc_dVb - d->vdsat*Inv_Aa*dAa_dVb;
      trace3("", T8, T9, d->vdsat);
      trace3("", dVdsat_dVd, dVdsat_dVg, dVdsat_dVb);
      }
      
      double Beta, dBeta_dVd, dBeta_dVg, dBeta_dVb;
      {
      double Beta0 = s->beta0  + s->beta0B * Vbs;
      double Betas = s->betas0 + s->betasB * Vbs;
      double Beta2 = s->beta20 + s->beta2B * Vbs + s->beta2G * Vgs;
      double Beta3 = s->beta30 + s->beta3B * Vbs + s->beta3G * Vgs;
      double Beta4 = s->beta40 + s->beta4B * Vbs + s->beta4G * Vgs;
      double Beta1 = Betas - (Beta0 + m->vdd * (Beta3 - m->vdd * Beta4));
      trace4("", Beta0, s->beta0, s->beta0B, Vbs);
      trace4("", Betas, s->betas0, s->betasB, Vgs);
      trace4("", Beta2, s->beta20, s->beta2B, s->beta2G);
      trace4("", Beta3, s->beta30, s->beta3B, s->beta3G);
      trace4("", Beta4, s->beta40, s->beta4B, s->beta4G);
      trace2("", Beta1, m->vdd);
      
      double T0 = Vds * Beta2 / d->vdsat;
      T0 = std::min(T0, 30.0);
      double T1 = exp(T0);
      double T2 = T1 * T1;
      double T3 = T2 + 1.0;
      trace4("", T0, T1, T2, T3);
      double tanh = (T2 - 1.0) / T3;
      double Sqrsech = 4.0 * T2 / (T3 * T3);
      trace2("", tanh, Sqrsech);
      
      Beta = Beta0 + Beta1 * tanh + Vds * (Beta3 - Beta4 * Vds);
      double T4 = Beta1 * Sqrsech / d->vdsat;
      double T5 = m->vdd * tanh;
      dBeta_dVd = Beta3 - 2.0*Beta4*Vds + T4*(Beta2-T0*dVdsat_dVd);
      dBeta_dVg = T4 * (s->beta2G * Vds - T0 * dVdsat_dVg)
        + s->beta3G * (Vds - T5) 
        - s->beta4G * (Vds * Vds - m->vdd * T5);
      double dBeta1_dVb = s->Arg;
      dBeta_dVb = s->beta0B
        + dBeta1_dVb * tanh
        + Vds * (s->beta3B - Vds * s->beta4B)
        + T4 * (s->beta2B * Vds - T0 * dVdsat_dVb);
      trace3("", T4, T5, dBeta1_dVb);
      trace4("", Beta, dBeta_dVd, dBeta_dVg, dBeta_dVb);
      }
      
      {if (d->vgst > s->vglow) {    // not subthreshold
      d->subthreshold = false;
      {if (Vds <= d->vdsat) {       // triode region
        d->saturated = false;
        double T3 = Vds / d->vdsat;
        double T4 = T3 - 1.0;
        double T2 =  1.0 - s->u1D * T4 * T4;
        double U1 =  U1s * T2;
        double Utot = Uvert + U1 * Vds;
        Utot = std::max(Utot, 0.5);
        double Inv_Utot = 1.0 / Utot;
        double T5 = 2.0 * U1s * s->u1D * T4 / d->vdsat;
        double dU1_dVd = T5 * (T3 * dVdsat_dVd - 1.0);
        double dU1_dVg = T5 * T3 * dVdsat_dVg;
        double dU1_dVb = T5 * T3 * dVdsat_dVb + s->u1B * T2;
        double dUtot_dVd = dUvert_dVd + U1 + Vds * dU1_dVd;
        double dUtot_dVg = dUvert_dVg + Vds * dU1_dVg;
        double dUtot_dVb = dUvert_dVb + Vds * dU1_dVb;
        
        double tmp1 = (Vgeff - 0.5 * Aa * Vds);
        double tmp3 = tmp1 * Vds;
        double Betaeff = Beta * Inv_Utot;
        d->ids = Betaeff * tmp3;
        double T6 = d->ids / Betaeff * Inv_Utot;
        d->gds = T6 * (dBeta_dVd - Betaeff * dUtot_dVd)
          + Betaeff * (tmp1 + (dVgeff_dVd - 0.5 * Aa) * Vds);
        d->gmf = T6 * (dBeta_dVg - Betaeff * dUtot_dVg)
          + Betaeff * Vds * dVgeff_dVg;
        d->gmbf = T6 * (dBeta_dVb - Betaeff * dUtot_dVb)
          + Betaeff * Vds * (dVgeff_dVb - 0.5 * Vds * dAa_dVb);
      }else{      // Saturation
        d->saturated = true;
        double Inv_Kk = 1.0 / Kk;
        double tmp1 = Vgeff * Inv_Aa * Inv_Kk;
        double tmp3 = 0.5 * Vgeff * tmp1;
        double Betaeff = Beta * Inv_Uvert;
        d->ids = Betaeff * tmp3;
        double T0 = d->ids / Betaeff * Inv_Uvert;
        double T1 = Betaeff * Vgeff * Inv_Aa * Inv_Kk;
        double T2 = d->ids * Inv_Kk * dKk_dVc;
        
        {if (s->ai0 != 0.0) {
          double Ai = s->ai0 + s->aiB * Vbs;
          double Bi = s->bi0 + s->biB * Vbs;
          double T5 = Bi / (Vds - d->vdsat);
          T5 = std::min(T5, 30.0);
          double T6 = exp(-T5);
          double FR = 1.0 + Ai * T6;
          double T7 = T5 / (Vds - d->vdsat);
          double T8 = (1.0 - FR) * T7;
          double dFR_dVd = T8 * (dVdsat_dVd - 1.0);
          double dFR_dVg = T8 * dVdsat_dVg;
          double dFR_dVb = T8 * dVdsat_dVb 
            + T6 * (s->aiB - Ai * s->biB / (Vds - d->vdsat));
          
          d->gds = (T0 * (dBeta_dVd - Betaeff * dUvert_dVd) 
                 + T1 * dVgeff_dVd - T2 * dVc_dVd) * FR + d->ids * dFR_dVd;
          d->gmf = (T0 * (dBeta_dVg - Betaeff * dUvert_dVg) 
                 + T1 * dVgeff_dVg - T2 * dVc_dVg) * FR + d->ids * dFR_dVg;
          d->gmbf = (T0 * (dBeta_dVb - Betaeff * dUvert_dVb)
                  + T1 * dVgeff_dVb - T2 * dVc_dVb 
                  - d->ids * Inv_Aa * dAa_dVb) * FR + d->ids * dFR_dVb;
          d->ids *= FR;
        }else{
          d->gds = T0 * (dBeta_dVd - Betaeff * dUvert_dVd)
            + T1 * dVgeff_dVd - T2 * dVc_dVd;
          d->gmf = T0 * (dBeta_dVg - Betaeff * dUvert_dVg)
            + T1 * dVgeff_dVg - T2 * dVc_dVg;
          d->gmbf = T0 * (dBeta_dVb - Betaeff * dUvert_dVb)
            + T1 * dVgeff_dVb - T2 * dVc_dVb - d->ids * Inv_Aa * dAa_dVb;
        }}
      }}
      }else{      // subthreshold
      d->subthreshold = true;
      assert(Exp0 != NOT_VALID);
      double T0 = Exp0 * Exp0;
      assert(Exp1 != NOT_VALID);
      double T1 = Exp1;
      trace4("sub", Exp0, Exp1, T0, T1);
      trace2("", n, m->Vtm);
      d->ids = Beta * m->Vtm * m->Vtm * T0 * (1.0 - T1);
      double T2 = d->ids / Beta;
      double T4 = n * m->Vtm;
      double T3 = d->ids / T4;
      trace4("", d->ids, T2, T4, T3);
      double FR, dFR_dVd, dFR_dVg, dFR_dVb;
      {if ((Vds > d->vdsat) && s->ai0 != 0.0) {
        d->saturated = true;
        double Ai = s->ai0 + s->aiB * Vbs;
        double Bi = s->bi0 + s->biB * Vbs;
        double T5 = Bi / (Vds - d->vdsat);
        trace3("", Ai, Bi, T5);
        T5 = std::min(T5, 30.0);
        double T6 = exp(-T5);
        FR = 1.0 + Ai * T6;
        double T7 = T5 / (Vds - d->vdsat);
        double T8 = (1.0 - FR) * T7;
        trace4("", T5, T6, T7, T8);
        dFR_dVd = T8 * (dVdsat_dVd - 1.0);
        dFR_dVg = T8 * dVdsat_dVg;
        dFR_dVb = T8 * dVdsat_dVb + T6 * (s->aiB-Ai*s->biB/(Vds-d->vdsat));
        trace4("ai0!=0", FR, dFR_dVd, dFR_dVg, dFR_dVb);
      }else{
        d->saturated = false;
        FR = 1.0;
        dFR_dVd = 0.0;
        dFR_dVg = 0.0;
        dFR_dVb = 0.0;
        trace4("ai0==0", FR, dFR_dVd, dFR_dVg, dFR_dVb);
      }}
      
      d->gds = (T2 * dBeta_dVd 
              + T3 * (s->vofD * T4 - dVth_dVd - s->nD * d->vgst / n)
              + Beta * m->Vtm * T0 * T1) * FR + d->ids * dFR_dVd;
      d->gmf = (T2 * dBeta_dVg + T3) * FR + d->ids * dFR_dVg;
      d->gmbf = (T2 * dBeta_dVb + T3 * (s->vofB * T4 - dVth_dVb + s->nB
            * d->vgst / (n * T1s * T1s) * dT1s_dVb)) * FR + d->ids*dFR_dVb;
      d->ids *= FR;
      }}
    }else{  // cutoff???
      d->cutoff = true;
      // reachable only if vghigh and vgst both negative
      d->vdsat = 0.0;
      dVdsat_dVd = dVdsat_dVg = dVdsat_dVb = 0.0;
      d->ids = 0.0;
      d->gmf  = 0.0;
      d->gds = 0.0;
      d->gmbf = 0.0;
    }}
    trace4("", d->vdsat, dVdsat_dVd, dVdsat_dVg, dVdsat_dVb);
    trace4("", d->ids, d->gmf, d->gds, d->gmbf);
    
    /* Some Limiting of DC Parameters */
    d->gds = std::max(d->gds, 1.0e-20);
    d->ids = std::max(d->ids, 1.0e-50);
    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
    double Vbseff, dVbseff_dVb;
    {if (Vbs < 0.0) {
      Vbseff = Vbs;
      dVbseff_dVb = 1.0;
    }else{
      Vbseff = s->phi - Phisb;
      dVbseff_dVb = -dPhisb_dVb;
    }}
    trace3("", Vbs, Vbseff, dVbseff_dVb);
    
    double Arg1 = Vgs - Vbseff - s->vfb;
    double Arg2 = Arg1 - d->vgst;
    trace2("", Arg1, Arg2);
    double Qbulk = s->One_Third_CoxWL * Arg2;
    double dQbulk_dVb = s->One_Third_CoxWL * (dVth_dVb - dVbseff_dVb);
    double dQbulk_dVd = s->One_Third_CoxWL * dVth_dVd;
    trace3("", Qbulk, dQbulk_dVb, dQbulk_dVd);
    {if (Arg1 <= 0.0) { // accumulation region
      d->qgate = s->cgate * Arg1;
      d->qbulk = -(d->qgate);
      d->qdrn = 0.0;
      
      d->cggb = s->cgate;
      d->cgdb = 0.0;
      d->cgsb = -d->cggb * (1.0 - dVbseff_dVb);
      
      d->cdgb = 0.0;
      d->cddb = 0.0;
      d->cdsb = 0.0;
      
      d->cbgb = -s->cgate;
      d->cbdb = 0.0;
      d->cbsb = -d->cgsb;
      trace4("", d->qgate, d->cggb, d->cgdb, d->cgsb);
      trace4("", d->qbulk, d->cbgb, d->cbdb, d->cbsb);
      trace4("acc", d->qdrn, d->cdgb, d->cddb, d->cdsb);
    }else if (d->vgst <= 0.0) { // subthreshold
      double T2 = Arg1 / Arg2;
      double T3 = T2 * T2 * (s->cgate - s->Two_Third_CoxWL * T2);
      
      d->qgate = s->cgate * Arg1 * (1.0 - T2 * (1.0 - T2 / 3.0));
      d->qbulk = -(d->qgate);
      d->qdrn = 0.0;
      
      d->cggb = s->cgate * (1.0 - T2 * (2.0 - T2));
      d->cgdb = T3 * dVth_dVd;
      double tmp = T3 * dVth_dVb - (d->cggb + T3) * dVbseff_dVb;
      d->cgsb = -(d->cggb + d->cgdb + tmp);
      
      d->cbgb = -d->cggb;
      d->cbdb = -d->cgdb;
      d->cbsb = -d->cgsb;
      
      d->cdgb = 0.0;
      d->cddb = 0.0;
      d->cdsb = 0.0;
      trace3("", T2, T3, tmp);
      trace4("", d->qgate, d->cggb, d->cgdb, d->cgsb);
      trace4("", d->qbulk, d->cbgb, d->cbdb, d->cbsb);
      trace4("sub", d->qdrn, d->cdgb, d->cddb, d->cdsb);
    }else{
      double Vdsat; // changes dVdsat_dVd, dVdsat_dVg, dVdsat_dVb;
      {if (d->vgst < s->vghigh) {
      double Uvert = 1.0 + d->vgst * (Ua + d->vgst * Ub);
      Uvert = std::max(Uvert, 0.2);
      double Inv_Uvert = 1.0 / Uvert;
      double dUvert_dVg = Ua + 2.0 * Ub * d->vgst;
      double dUvert_dVd = -dUvert_dVg * dVth_dVd;
      double dUvert_dVb = -dUvert_dVg * dVth_dVb 
        + d->vgst * (s->uaB + d->vgst * s->ubB);
      trace2("", Uvert, Inv_Uvert);
      trace3("", dUvert_dVg, dUvert_dVd, dUvert_dVb);
      
      double T8 = U1s * Inv_Aa * Inv_Uvert;
      double Vc = T8 * d->vgst;
      double T9 = Vc * Inv_Uvert;
      double dVc_dVg = T8 - T9 * dUvert_dVg;
      double dVc_dVd = -T8 * dVth_dVd - T9 * dUvert_dVd;
      double dVc_dVb = -T8 * dVth_dVb
        + s->u1B * d->vgst * Inv_Aa * Inv_Uvert 
        - Vc * Inv_Aa * dAa_dVb - T9 * dUvert_dVb;
      trace3("", T8, T9, Vc);
      trace3("", dVc_dVg, dVc_dVd, dVc_dVb);
      
      double tmp2 = sqrt(1.0 + 2.0 * Vc);
      double Kk = 0.5 * (1.0 + Vc + tmp2);
      double dKk_dVc = 0.5  + 0.5 / tmp2;
      trace3("", tmp2, Kk, dKk_dVc);
      
      T8 = Inv_Aa / sqrt(Kk);
      Vdsat = d->vgst * T8;
      T9 = 0.5 * Vdsat * dKk_dVc / Kk;
      trace2("", T8, T9);
      dVdsat_dVd = -T8 * dVth_dVd - T9 * dVc_dVd;
      dVdsat_dVg = T8 - T9 * dVc_dVg;
      dVdsat_dVb = -T8*dVth_dVb - T9*dVc_dVb - Vdsat*Inv_Aa*dAa_dVb;
      trace2("new", d->vdsat, Vdsat);
      trace3("", dVdsat_dVd, dVdsat_dVg, dVdsat_dVb);
      }else{
      Vdsat = d->vdsat;
      trace2("keep", d->vdsat, Vdsat);
      trace3("", dVdsat_dVd, dVdsat_dVg, dVdsat_dVb);
      // keep dVdsat_dVd, dVdsat_dVg, dVdsat_dVb;
      }}
      {if (Vds >= Vdsat) {       /* saturation region */
      d->cggb = s->Two_Third_CoxWL;
      d->cgdb = -d->cggb * dVth_dVd + dQbulk_dVd;
      double tmp = -d->cggb * dVth_dVb + dQbulk_dVb;
      d->cgsb = -(d->cggb + d->cgdb + tmp);
      trace1("", tmp);
      
      d->cbgb = 0.0;
      d->cbdb = -dQbulk_dVd;
      d->cbsb = dQbulk_dVd + dQbulk_dVb;
      
      d->cdgb = -0.4 * d->cggb;
      d->cddb = -d->cdgb * dVth_dVd;
      tmp = -d->cdgb * dVth_dVb;
      d->cdsb = -(d->cdgb + d->cddb + tmp);
      trace1("", tmp);
      
      d->qbulk = -Qbulk;
      d->qgate = s->Two_Third_CoxWL * d->vgst + Qbulk;
      d->qdrn = d->cdgb * d->vgst;
      trace4("", d->qgate, d->cggb, d->cgdb, d->cgsb);
      trace4("", d->qbulk, d->cbgb, d->cbdb, d->cbsb);
      trace4("sat", d->qdrn, d->cdgb, d->cddb, d->cdsb);
      }else{       /* linear region  */
      double T7 = Vds / Vdsat;
      double T8 = d->vgst / Vdsat;
      double T6 = T7 * T8;
      double T9 = 1.0 - T7;
      double Vgdt = d->vgst * T9;
      double T0 = d->vgst / (d->vgst + Vgdt);
      double T1 = Vgdt / (d->vgst + Vgdt);
      double T5 = T0 * T1;
      double T2 = 1.0 - T1 + T5;
      double T3 = 1.0 - T0 + T5;
      trace4("", T7, T8, T6, T9);
      trace2("", Vgdt, T0);
      trace4("", T1, T5, T2, T3);
      
      double dVgdt_dVg = T9 + T6 * dVdsat_dVg;
      double dVgdt_dVd = T6 * dVdsat_dVd - T8 -T9 * dVth_dVd;
      double dVgdt_dVb = T6 * dVdsat_dVb -T9 * dVth_dVb;
      trace3("", dVgdt_dVg, dVgdt_dVd, dVgdt_dVb);
      
      d->qgate = s->Two_Third_CoxWL * (d->vgst + Vgdt 
                           - Vgdt * T0) + Qbulk;
      d->qbulk = -Qbulk;
      d->qdrn = -s->One_Third_CoxWL * (0.2 * Vgdt 
                            + 0.8 * d->vgst + Vgdt * T1 
                            + 0.2 * T5 * (Vgdt - d->vgst));
      
      d->cggb = s->Two_Third_CoxWL * (T2 + T3 * dVgdt_dVg);
      d->cgdb = s->Two_Third_CoxWL * (T3*dVgdt_dVd-T2*dVth_dVd) + dQbulk_dVd;
      double tmp = dQbulk_dVb +s->Two_Third_CoxWL*(T3*dVgdt_dVb-T2*dVth_dVb);
      d->cgsb = -(d->cggb + d->cgdb + tmp);
      trace1("", tmp);
      
      T2 = 0.8 - 0.4 * T1 * (2.0 * T1 + T0 + T0 * (T1 - T0));
      T3 = 0.2 + T1 + T0 * (1.0 - 0.4 * T0 * (T1 + 3.0 * T0));
      d->cdgb = -s->One_Third_CoxWL * (T2 + T3 * dVgdt_dVg);
      d->cddb = s->One_Third_CoxWL * (T2 * dVth_dVd - T3 * dVgdt_dVd);
      tmp = s->One_Third_CoxWL * (T2 * dVth_dVb - T3 * dVgdt_dVb);
      d->cdsb = -(d->cdgb + tmp + d->cddb);
      trace3("", T2, T3, tmp);
      
      d->cbgb = 0.0;
      d->cbdb = -dQbulk_dVd;
      d->cbsb = dQbulk_dVd + dQbulk_dVb;
      trace4("", d->qgate, d->cggb, d->cgdb, d->cgsb);
      trace4("", d->qbulk, d->cbgb, d->cbdb, d->cbsb);
      trace4("lin", d->qdrn, d->cdgb, d->cddb, d->cdsb);
      }}
    }}
    {if (d->reversed){
      d->ids *= -1;
      d->gmr = d->gmf;
      d->gmbr = d->gmbf;
      d->gmf = d->gmbf = 0;
    }else{
      d->gmr = d->gmbr = 0.;
    }}
}
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/

Generated by  Doxygen 1.6.0   Back to index