/* $Id: d_mos6.model,v 26.133 2009/11/26 04:58:04 al Exp $ -*- C++ -*-
 * Copyright (C) 2001 Albert Davis
 * Author: Albert Davis <aldavis@gnu.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 3, 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., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 *------------------------------------------------------------------
 * mos model equations: spice level 6 equivalent
 */
/* This file is automatically generated. DO NOT EDIT */

#include "l_denoise.h"
#include "globals.h"
#include "e_elemnt.h"
#include "d_mos6.h"
/*--------------------------------------------------------------------------*/
const double NA(NOT_INPUT);
const double INF(BIGBIG);
/*--------------------------------------------------------------------------*/
int MODEL_BUILT_IN_MOS6::_count = 0;
/*--------------------------------------------------------------------------*/
const int LEVEL(6);
/*--------------------------------------------------------------------------*/
namespace MODEL_BUILT_IN_MOS6_DISPATCHER { 
  static DEV_BUILT_IN_MOS p1d;
  static MODEL_BUILT_IN_MOS6 p1(&p1d);
  static DISPATCHER<MODEL_CARD>::INSTALL
    d1(&model_dispatcher, "nmos6|pmos6", &p1);
}
/*--------------------------------------------------------------------------*/
void SDP_BUILT_IN_MOS6::init(const COMMON_COMPONENT* cc)
{
  assert(cc);
  SDP_BUILT_IN_MOS123::init(cc);
}
/*--------------------------------------------------------------------------*/
TDP_BUILT_IN_MOS6::TDP_BUILT_IN_MOS6(const DEV_BUILT_IN_MOS* d)
  :TDP_BUILT_IN_MOS123(d)
{
  assert(d);
  const COMMON_BUILT_IN_MOS* c = prechecked_cast<const COMMON_BUILT_IN_MOS*>(d->common());
  assert(c);
  const SDP_BUILT_IN_MOS6* s = prechecked_cast<const SDP_BUILT_IN_MOS6*>(c->sdp());
  assert(s);
  const MODEL_BUILT_IN_MOS6* m = prechecked_cast<const MODEL_BUILT_IN_MOS6*>(c->model());
  assert(m);
  const CARD_LIST* par_scope = d->scope();
  assert(par_scope);
    // final adjust: code_pre

      double temp = d->_sim->_temp_c + P_CELSIUS0;
      double tempratio  = temp / m->tnom_k;
      double tempratio4 = tempratio * sqrt(tempratio);
      double kt = temp * P_K;
      double vt = temp * P_K_Q;
      double egap = 1.16 - (7.02e-4*temp*temp) / (temp+1108.);
      double arg = (m->egap*tempratio - egap) / (2*kt);
    // final adjust: override
    // final adjust: raw
    // final adjust: mid
    // final adjust: calculated
    phi = m->phi*tempratio + (-2*vt*(1.5*log(tempratio)+P_Q*(arg)));
    beta = (m->kc / tempratio4) * s->w_eff / s->l_eff;
    vbi = (fixzero(
	(m->vto - m->gamma * sqrt(m->phi)
	 +.5*(m->egap-egap) + m->polarity* .5 * (phi-m->phi)), m->phi));
    // final adjust: post
    // final adjust: done
}
/*--------------------------------------------------------------------------*/
MODEL_BUILT_IN_MOS6::MODEL_BUILT_IN_MOS6(const BASE_SUBCKT* p)
  :MODEL_BUILT_IN_MOS123(p),
   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)
{
  if (ENV::run_mode != rPRE_MAIN) {
    ++_count;
  }else{
  }
  set_default(&mjsw, .5);
  set_default(&cox, NA);
  set_default(&vto, NA);
  set_default(&gamma, NA);
  set_default(&phi, NA);
  set_default(&mos_level, LEVEL);
}
/*--------------------------------------------------------------------------*/
MODEL_BUILT_IN_MOS6::MODEL_BUILT_IN_MOS6(const MODEL_BUILT_IN_MOS6& p)
  :MODEL_BUILT_IN_MOS123(p),
   kv(p.kv),
   nv(p.nv),
   kc(p.kc),
   nc(p.nc),
   nvth(p.nvth),
   ps(p.ps),
   gamma1(p.gamma1),
   sigma(p.sigma),
   lambda0(p.lambda0),
   lambda1(p.lambda1),
   calc_kc(p.calc_kc)
{
  if (ENV::run_mode != rPRE_MAIN) {
    ++_count;
  }else{untested();//194
  }
}
/*--------------------------------------------------------------------------*/
std::string MODEL_BUILT_IN_MOS6::dev_type()const
{
  if (polarity == pN) {
    return "nmos6";
  }else if (polarity == pP) {
    return "pmos6";
  }else{untested();//235
    return MODEL_BUILT_IN_MOS123::dev_type();
  }
}
/*--------------------------------------------------------------------------*/
void MODEL_BUILT_IN_MOS6::set_dev_type(const std::string& new_type)
{
  if (Umatch(new_type, "nmos6 ")) {
    polarity = pN;
  }else if (Umatch(new_type, "pmos6 ")) {
    polarity = pP;
  }else{
    MODEL_BUILT_IN_MOS123::set_dev_type(new_type);
  }
}
/*--------------------------------------------------------------------------*/
void MODEL_BUILT_IN_MOS6::precalc_first()
{
    const CARD_LIST* par_scope = scope();
    assert(par_scope);
    MODEL_BUILT_IN_MOS123::precalc_first();
    e_val(&(this->kv), 2.0, par_scope);
    e_val(&(this->nv), 0.5, par_scope);
    e_val(&(this->kc), NA, par_scope);
    e_val(&(this->nc), 1.0, par_scope);
    e_val(&(this->nvth), 0.5, par_scope);
    e_val(&(this->ps), 0.0, par_scope);
    e_val(&(this->gamma1), 0.0, par_scope);
    e_val(&(this->sigma), 0.0, par_scope);
    e_val(&(this->lambda0), 0.0, par_scope);
    e_val(&(this->lambda1), 0.0, par_scope);
    // final adjust: code_pre

      if (tox != NA) {
	cox = P_EPS_OX / tox;
	if (kc == NA) {
	  kc = .5 * uo * cox;
	  calc_kc = true;
	}
	if (nsub != NA) {
	  if (phi == NA) {
	    phi = (2. * P_K_Q) * tnom_k * log(nsub/NI);
	    if (phi < .1) {
	      untested();
	      error(((!_sim->is_first_expand()) ? (bDEBUG) : (bWARNING)),
		    long_label() + ": calculated phi too small, using .1\n");
	      phi = .1;
	    }
	    calc_phi = true;
	  }
	  if (gamma == NA) {
	    gamma = sqrt(2. * P_EPS_SI * P_Q * nsub) / cox;
	    calc_gamma = true;
	  }
	  if (vto == NA) {
	    double phi_ms = (tpg == gtMETAL)
	      ? polarity * (-.05 - (egap + polarity * phi) / 2.)
	      : -(tpg * egap + phi) / 2.;
	    double vfb = phi_ms - polarity * P_Q * nss / cox;
	    vto = vfb + phi + gamma * sqrt(phi);
	    calc_vto = true;
	  }
	}
      }
    // final adjust: override
    if (cox == NA) {
      cox = 0.;
    }else{
    }
    if (vto == NA) {
      vto = 0.;
    }else{
    }
    if (gamma == NA) {
      gamma = 0.;
    }else{
    }
    if (phi == NA) {
      phi = .6;
    }else{
    }
    // final adjust: raw
    e_val(&(this->kv), 2.0, par_scope);
    e_val(&(this->nv), 0.5, par_scope);
    e_val(&(this->kc), 5e-5, par_scope);
    e_val(&(this->nc), 1.0, par_scope);
    e_val(&(this->nvth), 0.5, par_scope);
    e_val(&(this->ps), 0.0, par_scope);
    e_val(&(this->gamma1), 0.0, par_scope);
    e_val(&(this->sigma), 0.0, par_scope);
    e_val(&(this->lambda0), 0.0, par_scope);
    e_val(&(this->lambda1), 0.0, par_scope);
    // final adjust: mid
    // final adjust: calculated
    // final adjust: post
    // final adjust: done
}
/*--------------------------------------------------------------------------*/
void MODEL_BUILT_IN_MOS6::precalc_last()
{
    MODEL_BUILT_IN_MOS123::precalc_last();
}
/*--------------------------------------------------------------------------*/
SDP_CARD* MODEL_BUILT_IN_MOS6::new_sdp(COMMON_COMPONENT* c)const
{
  assert(c);
  if (COMMON_BUILT_IN_MOS* cc = dynamic_cast<COMMON_BUILT_IN_MOS*>(c)) {
    if (cc->_sdp) {
      cc->_sdp->init(cc);
      return cc->_sdp;
    }else{
      delete cc->_sdp;
      return new SDP_BUILT_IN_MOS6(c);
    }
  }else{
    return MODEL_BUILT_IN_MOS123::new_sdp(c);
  }
}
/*--------------------------------------------------------------------------*/
void MODEL_BUILT_IN_MOS6::set_param_by_index(int i, std::string& value, int offset)
{
  switch (MODEL_BUILT_IN_MOS6::param_count() - 1 - i) {
  case 0: level = value; break; //6
  case 1: lambda0 = value; break;
  case 2: unreachable(); break;
  case 3: unreachable(); break;
  case 4: unreachable(); break;
  case 5: unreachable(); break;
  case 6: unreachable(); break;
  case 7: mos_level = value; break;
  case 8: kv = value; break;
  case 9: nv = value; break;
  case 10: kc = value; break;
  case 11: nc = value; break;
  case 12: nvth = value; break;
  case 13: ps = value; break;
  case 14: gamma1 = value; break;
  case 15: sigma = value; break;
  case 16: lambda0 = value; break;
  case 17: lambda1 = value; break;
  default: MODEL_BUILT_IN_MOS123::set_param_by_index(i, value, offset); break;
  }
}
/*--------------------------------------------------------------------------*/
bool MODEL_BUILT_IN_MOS6::param_is_printable(int i)const
{
  switch (MODEL_BUILT_IN_MOS6::param_count() - 1 - i) {
  case 0:  return (true);
  case 1:  return (true);
  case 2:  return (false);
  case 3:  return (false);
  case 4:  return (false);
  case 5:  return (false);
  case 6:  return (false);
  case 7:  return (mos_level != LEVEL);
  case 8:  return (true);
  case 9:  return (true);
  case 10:  return (!calc_kc);
  case 11:  return (true);
  case 12:  return (true);
  case 13:  return (true);
  case 14:  return (true);
  case 15:  return (true);
  case 16:  return (true);
  case 17:  return (true);
  default: return MODEL_BUILT_IN_MOS123::param_is_printable(i);
  }
}
/*--------------------------------------------------------------------------*/
std::string MODEL_BUILT_IN_MOS6::param_name(int i)const
{
  switch (MODEL_BUILT_IN_MOS6::param_count() - 1 - i) {
  case 0:  return "level";
  case 1:  return "lambda";
  case 2:  return "=====";
  case 3:  return "=====";
  case 4:  return "=====";
  case 5:  return "=====";
  case 6:  return "=====";
  case 7:  return "diodelevel";
  case 8:  return "kv";
  case 9:  return "nv";
  case 10:  return "kc";
  case 11:  return "nc";
  case 12:  return "nvth";
  case 13:  return "ps";
  case 14:  return "gamma1";
  case 15:  return "sigma";
  case 16:  return "lambda0";
  case 17:  return "lambda1";
  default: return MODEL_BUILT_IN_MOS123::param_name(i);
  }
}
/*--------------------------------------------------------------------------*/
std::string MODEL_BUILT_IN_MOS6::param_name(int i, int j)const
{
  if (j == 0) {
    return param_name(i);
  }else if (j == 1) {
    switch (MODEL_BUILT_IN_MOS6::param_count() - 1 - i) {
    case 0:  return "";
    case 1:  return "";
    case 2:  return "";
    case 3:  return "";
    case 4:  return "";
    case 5:  return "";
    case 6:  return "";
    case 7:  return "";
    case 8:  return "";
    case 9:  return "";
    case 10:  return "";
    case 11:  return "";
    case 12:  return "";
    case 13:  return "";
    case 14:  return "";
    case 15:  return "";
    case 16:  return "";
    case 17:  return "";
    default: return MODEL_BUILT_IN_MOS123::param_name(i, j);
    }
  }else if (i < 18) {
    return "";
  }else{
    return MODEL_BUILT_IN_MOS123::param_name(i, j);
  }
}
/*--------------------------------------------------------------------------*/
std::string MODEL_BUILT_IN_MOS6::param_value(int i)const
{
  switch (MODEL_BUILT_IN_MOS6::param_count() - 1 - i) {
  case 0:  return "6";
  case 1:  return lambda0.string();
  case 2:  unreachable(); return "";
  case 3:  unreachable(); return "";
  case 4:  unreachable(); return "";
  case 5:  unreachable(); return "";
  case 6:  unreachable(); return "";
  case 7:  return mos_level.string();
  case 8:  return kv.string();
  case 9:  return nv.string();
  case 10:  return kc.string();
  case 11:  return nc.string();
  case 12:  return nvth.string();
  case 13:  return ps.string();
  case 14:  return gamma1.string();
  case 15:  return sigma.string();
  case 16:  return lambda0.string();
  case 17:  return lambda1.string();
  default: return MODEL_BUILT_IN_MOS123::param_value(i);
  }
}
/*--------------------------------------------------------------------------*/
bool MODEL_BUILT_IN_MOS6::is_valid(const COMPONENT* d)const
{
  assert(d);
  return MODEL_BUILT_IN_MOS123::is_valid(d);
}
/*--------------------------------------------------------------------------*/
void MODEL_BUILT_IN_MOS6::tr_eval(COMPONENT* brh)const
{
  DEV_BUILT_IN_MOS* d = prechecked_cast<DEV_BUILT_IN_MOS*>(brh);
  assert(d);
  const COMMON_BUILT_IN_MOS* c = prechecked_cast<const COMMON_BUILT_IN_MOS*>(d->common());
  assert(c);
  const SDP_BUILT_IN_MOS6* s = prechecked_cast<const SDP_BUILT_IN_MOS6*>(c->sdp());
  assert(s);
  const MODEL_BUILT_IN_MOS6* m = this;
  const TDP_BUILT_IN_MOS6 T(d);
  const TDP_BUILT_IN_MOS6* t = &T;

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
    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.) {
      d->sbfwd = false;
      sarg = sqrt(t->phi - d->vbs);
    }else{
      d->sbfwd = true;
      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.;
    }
}
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
