/* $Id: d_mos6.cc,v 20.13 2001/10/15 00:57:11 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.
 *------------------------------------------------------------------
 * mos model equations: spice level 6 equivalent
 */
/* This file is automatically generated. DO NOT EDIT */

#include "l_denoise.h"
#include "ap.h"
#include "d_mos6.h"
/*--------------------------------------------------------------------------*/
const double NA(NOT_INPUT);
/*--------------------------------------------------------------------------*/
int MODEL_MOS6::_count = 0;
/*--------------------------------------------------------------------------*/
SDP_MOS6::SDP_MOS6(const COMMON_COMPONENT* cc)
  :SDP_MOS123(cc)
{
}
/*--------------------------------------------------------------------------*/
TDP_MOS6::TDP_MOS6(const DEV_MOS* d)
  :TDP_MOS123(d)
{
  assert(d);
  const COMMON_MOS* c = prechecked_cast<const COMMON_MOS*>(d->common());
  assert(c);
  const SDP_MOS6* b = prechecked_cast<const SDP_MOS6*>(c->sdp());
  assert(b);
  const MODEL_MOS6* m = prechecked_cast<const MODEL_MOS6*>(c->model());
  assert(m);

      double temp = SIM::temp;
      double tempratio  = temp / m->_tnom;
      double tempratio4 = tempratio * sqrt(tempratio);
      double kt = temp * K;
      double vt = kt / Q;
      double egap = 1.16 - (7.02e-4*temp*temp) / (temp+1108.);
      double arg = (m->egap*tempratio - egap) / (2*kt);

  phi = m->phi*tempratio + (-2*vt*(1.5*log(tempratio)+Q*(arg)));
  beta = m->kc * tempratio4 * b->w_eff / b->l_eff;
  vbi = (fixzero(
	(m->vto - m->polarity * m->gamma * sqrt(m->phi)
	 +.5*(m->egap-egap) + m->polarity* .5 * (phi-m->phi)), m->phi));
}
/*--------------------------------------------------------------------------*/
MODEL_MOS6::MODEL_MOS6()
  :MODEL_MOS123(),
   kv(2.0),
   nv(0.5),
   kc(NA),
   nc(1.0),
   nvth(0.5),
   ps(0.0),
   gamma1(0.0),
   sigma(0.0),
   lambda0(0.0),
   lambda1(0.0),
   calc_kc(false)
{
  ++_count;
  mjsw = .5;
  cox = NA;
  vto = NA;
  gamma = NA;
  phi = NA;
  mos_level = LEVEL;
}
/*--------------------------------------------------------------------------*/
bool MODEL_MOS6::parse_front(CS& cmd)
{
  return MODEL_MOS123::parse_front(cmd);
}
/*--------------------------------------------------------------------------*/
void MODEL_MOS6::parse_params(CS& cmd)
{
  get(cmd, "LAMBDA", &lambda0);
  get(cmd, "DIODElevel", &mos_level);
  get(cmd, "KV", &kv);
  get(cmd, "NV", &nv);
  get(cmd, "KC", &kc);
  get(cmd, "NC", &nc);
  get(cmd, "NVTH", &nvth);
  get(cmd, "PS", &ps);
  get(cmd, "GAMMA1", &gamma1);
  get(cmd, "SIGMA", &sigma);
  get(cmd, "LAMBDA0", &lambda0);
  get(cmd, "LAMBDA1", &lambda1);
  MODEL_MOS123::parse_params(cmd);
}
/*--------------------------------------------------------------------------*/
void MODEL_MOS6::parse_finish()
{
  MODEL_MOS123::parse_finish();

      if (tox != NA) {
	cox = E_OX / tox;
	if (kc == NA) {
	  kc = .5 * uo * cox;
	  calc_kc = true;
	}
	if (nsub != NA) {
	  if (phi == NA) {
	    phi = (2.*KoQ) * _tnom * log(nsub/NI);
	    if (phi < .1) {
	      untested();
	      error(bWARNING,
		    long_label() + ": calculated phi too small, using .1\n");
	      phi = .1;
	    }
	    calc_phi = true;
	  }
	  if (gamma == NA) {
	    gamma = sqrt(2. * E_SI * Q * nsub) / cox;
	    calc_gamma = true;
	  }
	  if (vto == NA) {
	    double phi_ms = (tpg == gtMETAL)
	      ? -.05 - (egap + polarity * phi) / 2.
	      : -polarity * (tpg * egap + phi) / 2.;
	    double vfb = phi_ms - Q * nss / cox;
	    vto = vfb + polarity * (phi + gamma * sqrt(phi));
	    calc_vto = true;
	  }
	}
      }

  if (cox == NA) {
    cox = 0.;
  }
  if (vto == NA) {
    vto = 0.;
  }
  if (gamma == NA) {
    gamma = 0.;
  }
  if (phi == NA) {
    phi = .6;
  }
  if (kc == NA) {
    kc = 5e-5;
  }
}
/*--------------------------------------------------------------------------*/
SDP_CARD* MODEL_MOS6::new_sdp(const COMMON_COMPONENT* c)const
{
  assert(c);
  {if (dynamic_cast<const COMMON_MOS*>(c)) {
    return new SDP_MOS6(c);
  }else{
    return MODEL_MOS123::new_sdp(c);
  }}
}
/*--------------------------------------------------------------------------*/
void MODEL_MOS6::print_front(OMSTREAM& o)const
{
  MODEL_MOS123::print_front(o);
}
/*--------------------------------------------------------------------------*/
void MODEL_MOS6::print_params(OMSTREAM& o)const
{
  o << "level=6";
  MODEL_MOS123::print_params(o);
  o << "  lambda=" << lambda0;
  if (mos_level != LEVEL)
    o << "  diodelevel=" << mos_level;
  o << "  kv=" << kv;
  o << "  nv=" << nv;
  if (!calc_kc)
    o << "  kc=" << kc;
  o << "  nc=" << nc;
  o << "  nvth=" << nvth;
  o << "  ps=" << ps;
  o << "  gamma1=" << gamma1;
  o << "  sigma=" << sigma;
  o << "  lambda0=" << lambda0;
  o << "  lambda1=" << lambda1;
}
/*--------------------------------------------------------------------------*/
void MODEL_MOS6::print_calculated(OMSTREAM& o)const
{
  MODEL_MOS123::print_calculated(o);
}
/*--------------------------------------------------------------------------*/
void MODEL_MOS6::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_MOS6* b = prechecked_cast<const SDP_MOS6*>(c->sdp());
  assert(b);
  const MODEL_MOS6* m = this;
  TDP_MOS6 t(d);

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
    trace1(d->long_label().c_str(), d->evaliter());
    trace3("", d->vds, d->vgs, d->vbs);
    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 
    d->reverse_if_needed();
    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
    double sarg;
    {if (d->vbs <= 0.){
      sarg = sqrt(t.phi - d->vbs);
    }else{
      sarg = sqrt(t.phi);
      sarg = sarg - d->vbs / (sarg+sarg);
      {if (sarg < 0.){
	untested();
	sarg = 0.;
      }else{
	untested();
      }}
    }}
    trace3("", t.phi, d->vbs, sarg);
    assert(sarg >= 0.);
    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
    trace4("", d->vds, t.vbi, m->gamma, m->gamma1);
    d->von = t.vbi + m->gamma * sarg - m->gamma1 * d->vbs;
    // - m->sigma  * d->vds;  // what is this?????
    d->vgst = d->vgs - d->von;
    trace3("", d->vds, d->von, d->vgst);
    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
    d->cutoff = (d->vgst <= 0.);
    {if (d->cutoff){
      d->vdsat = 0.;
      d->ids = d->gmf = d->gds = d->gmbf = 0.;
      trace4("cut", d->ids, d->gmf, d->gds, d->gmbf);
    }else{
      double vonbm;
      {if (d->vbs <= 0.){
	vonbm = m->gamma1 + m->gamma / (sarg + sarg);
      }else{
	vonbm = m->gamma1 + m->gamma * .5 / sqrt(t.phi);
	untested();
      }}
      trace3("", m->nc, m->lambda0, m->lambda1);
      double logvgon = log(d->vgst);
      double idsat = t.beta * exp(logvgon * m->nc);
      double Lambda = m->lambda0 - m->lambda1 * d->vbs;
      trace4("", vonbm, logvgon, idsat, Lambda);
      
      d->ids = idsat * (1 + Lambda * d->vds);
      d->gmf  = d->ids * m->nc / d->vgst;
      d->gds = d->gmf * m->sigma + idsat * Lambda;
      d->gmbf = d->gmf * vonbm - idsat * m->lambda1 * d->vds;
      
      d->vdsat = m->kv * exp(logvgon * m->nv);
      trace4("sat", d->ids, d->gmf, d->gds, d->gmbf);
      
      d->saturated = (d->vdsat <= d->vds);
      if (!d->saturated){
	double vdst   = d->vds / d->vdsat;
	double vdst2  = (2 - vdst) * vdst;
	double vdstg  = - vdst * m->nv / d->vgst;
	double ivdst1 = d->ids * (2 - vdst - vdst);
	d->ids *= vdst2;
	d->gmf  = d->gmf  * vdst2 + ivdst1 * vdstg;
	d->gds = d->gds * vdst2 + ivdst1 * (1 / d->vdsat + vdstg * m->sigma);
	d->gmbf = d->gmbf * vdst2 + ivdst1 * vdstg * vonbm;
	trace4("lin", d->ids, d->gmf, d->gds, d->gmbf);
      }
    }}
    {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.;
    }}
}
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
