/* $Id: d_mos1.model,v 25.92 2006/06/28 15:02:53 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., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 *------------------------------------------------------------------
 * mos model equations: spice level 1 equivalent
 */
/* This file is automatically generated. DO NOT EDIT */
#include "ap.h"
#include "d_mos1.h"
/*--------------------------------------------------------------------------*/
const double NA(NOT_INPUT);
const double INF(BIGBIG);
/*--------------------------------------------------------------------------*/
int MODEL_MOS1::_count = 0;
/*--------------------------------------------------------------------------*/
SDP_MOS1::SDP_MOS1(const COMMON_COMPONENT* cc)
  :SDP_MOS123(cc)
{
}
/*--------------------------------------------------------------------------*/
TDP_MOS1::TDP_MOS1(const DEV_MOS* d)
  :TDP_MOS123(d)
{
  assert(d);
  const COMMON_MOS* c = prechecked_cast<const COMMON_MOS*>(d->common());
  assert(c);
  const SDP_MOS1* s = prechecked_cast<const SDP_MOS1*>(c->sdp());
  assert(s);
  const MODEL_MOS1* m = prechecked_cast<const MODEL_MOS1*>(c->model());
  assert(m);
    // final adjust: code_pre

      double temp = 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->kp * tempratio4 * s->w_eff / s->l_eff;
    sqrt_phi = sqrt(phi);
    // final adjust: post
    // final adjust: done
}
/*--------------------------------------------------------------------------*/
MODEL_MOS1::MODEL_MOS1()
  :MODEL_MOS123(),
   kp(NA),
   calc_kp(false)
{
  ++_count;
  mjsw = .5;
  cox = NA;
  vto = NA;
  gamma = NA;
  phi = NA;
  mos_level = LEVEL;
}
/*--------------------------------------------------------------------------*/
bool MODEL_MOS1::parse_front(CS& cmd)
{
  return MODEL_MOS123::parse_front(cmd);
}
/*--------------------------------------------------------------------------*/
bool MODEL_MOS1::parse_params(CS& cmd)
{
  return ONE_OF
    || get(cmd, "DIODElevel", &mos_level)
    || get(cmd, "KP", &kp)
    || MODEL_MOS123::parse_params(cmd)
    ;
}
/*--------------------------------------------------------------------------*/
void MODEL_MOS1::elabo1()
{
  if (1 || !evaluated()) {
    const CARD_LIST* par_scope = scope();
    assert(par_scope);
    MODEL_MOS123::elabo1();
    // final adjust: code_pre

      if (tox != NA) {
	cox = P_EPS_OX / tox;
	if (kp == NA) {
	  kp = uo * cox;
	  calc_kp = true;
	}
	{if (nsub != NA) {
	  if (phi == NA) {
	    phi = (2. * P_K_Q) * tnom_k * 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. * P_EPS_SI * P_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 - P_Q * nss / cox;
	    vto = vfb + polarity * (phi + gamma * sqrt(phi));
	    calc_vto = true;
	  }
	}else{
	  // tox is input, nsub isn't
	}}
      }
    // final adjust: override
    if (cox == NA) {
      cox = 0.;
    }
    if (vto == NA) {
      vto = 0.;
    }
    if (gamma == NA) {
      gamma = 0.;
    }
    if (phi == NA) {
      phi = .6;
    }
    // final adjust: raw
    this->kp.e_val(2e-5, par_scope);
    // final adjust: mid
    // final adjust: calculated
    // final adjust: post
    // final adjust: done
  }else{
    untested();
  }
}
/*--------------------------------------------------------------------------*/
SDP_CARD* MODEL_MOS1::new_sdp(const COMMON_COMPONENT* c)const
{
  assert(c);
  {if (dynamic_cast<const COMMON_MOS*>(c)) {
    return new SDP_MOS1(c);
  }else{
    return MODEL_MOS123::new_sdp(c);
  }}
}
/*--------------------------------------------------------------------------*/
void MODEL_MOS1::print_front(OMSTREAM& o)const
{
  MODEL_MOS123::print_front(o);
}
/*--------------------------------------------------------------------------*/
void MODEL_MOS1::print_params(OMSTREAM& o)const
{
  o << "level=1";
  MODEL_MOS123::print_params(o);
  if (mos_level != LEVEL)
    o << "  diodelevel=" << mos_level;
  if (!calc_kp)
    o << "  kp=" << kp;
}
/*--------------------------------------------------------------------------*/
void MODEL_MOS1::print_calculated(OMSTREAM& o)const
{
  MODEL_MOS123::print_calculated(o);
  if (calc_kp)
    o << "* kp=" << kp;
}
/*--------------------------------------------------------------------------*/
bool MODEL_MOS1::is_valid(const COMMON_COMPONENT* cc)const
{
  return MODEL_MOS123::is_valid(cc);
}
/*--------------------------------------------------------------------------*/
void MODEL_MOS1::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_MOS1* s = prechecked_cast<const SDP_MOS1*>(c->sdp());
  assert(s);
  const MODEL_MOS1* m = this;
  const TDP_MOS1 T(d);
  const TDP_MOS1* t = &T;

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 
    trace1(d->long_label().c_str(), d->evaliter());
    trace3("", d->vds, d->vgs, d->vbs);
    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 
    d->reverse_if_needed();
    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 
    double sarg, dsarg_dvbs;
    {
      {if (d->vbs <= 0.) {
	sarg = sqrt(t->phi - d->vbs);
	dsarg_dvbs = -.5 / sarg;
	d->sbfwd = false;
	trace2("sb-ok", sarg, dsarg_dvbs);
      }else{
	untested();
	sarg = t->sqrt_phi / (1. + .5 * d->vbs / t->phi);
	dsarg_dvbs = -.5 * sarg * sarg / t->phi*t->sqrt_phi; /* is wrong!! */
	d->sbfwd = true;
	trace2("***sb-reversed***", sarg, dsarg_dvbs);
      }}
    }
    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 
    {
      {if (d->vbs - d->vds <= 0.) {
	d->dbfwd = false;
      }else{
	d->dbfwd = true;
      }}
    }
    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 
    d->von = m->vto + m->gamma * (sarg - t->sqrt_phi);
    d->vgst = d->vdsat = d->vgs - d->von;
    if (d->vdsat < 0.) {
      d->vdsat = 0.;
    }
    d->cutoff = (d->vgst < 0.);
    d->saturated = (d->vds > d->vdsat);
    trace3("", d->von, d->vgst, d->vdsat);
    double Lambda = (m->lambda != NA) ? m->lambda : 0.;
    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 
    {if (d->cutoff) {
      d->gds = d->gmf = d->ids = d->gmbf = 0.;
      trace4("cut", d->ids, d->gmf, d->gds, d->gmbf);
    }else if (d->saturated) {
      d->gmf  = t->beta * d->vgst * (1. + Lambda * d->vds);
      d->ids = d->gmf * (.5 * d->vgst);
      d->gds = .5 * t->beta * Lambda * d->vgst * d->vgst;
      d->gmbf = - d->gmf * m->gamma * dsarg_dvbs;
      trace4("sat", d->ids, d->gmf, d->gds, d->gmbf);
    }else{ /* triode */
      d->gmf  = t->beta * d->vds * (1. + Lambda * d->vds);
      d->ids = d->gmf * (d->vgst - .5*d->vds);
      d->gds = t->beta * ((d->vgst - d->vds) 
			 + Lambda * d->vds * (2.*d->vgst - 1.5*d->vds));
      d->gmbf = -d->gmf * m->gamma * dsarg_dvbs;
      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.;
    }}
}
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
