/* $Id: d_mos.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.
 *------------------------------------------------------------------
 * data structures and defaults for mos model.
 * internal units all mks (meters)
 * but some user input parameters are in cm.
 *
 * netlist syntax:
 * device:  mxxxx d g s b mname <device args> <model card args>
 * model:   .model mname NMOS <args>
 *	or  .model mname PMOS <args>
 */
/* This file is automatically generated. DO NOT EDIT */

#include "d_cap.h"
#include "d_admit.h"
#include "d_res.h"
#include "d_mos_base.h"
#include "ap.h"
#include "d_mos.h"
/*--------------------------------------------------------------------------*/
const double NA(NOT_INPUT);
/*--------------------------------------------------------------------------*/
int DEV_MOS::_count = 0;
int COMMON_MOS::_count = -1;
static COMMON_MOS Default_MOS(CC_STATIC);
/*--------------------------------------------------------------------------*/
COMMON_MOS::COMMON_MOS(int c)
  :COMMON_COMPONENT(c),
   l_in(NA),
   w_in(NA),
   ad_in(NA),
   as_in(NA),
   pd(0.0),
   ps(0.0),
   nrd(1.0),
   nrs(1.0),
   _sdp(0),
   _db(0),
   _sb(0)
{
  ++_count;
}
/*--------------------------------------------------------------------------*/
COMMON_MOS::COMMON_MOS(const COMMON_MOS& p)
  :COMMON_COMPONENT(p),
   l_in(p.l_in),
   w_in(p.w_in),
   ad_in(p.ad_in),
   as_in(p.as_in),
   pd(p.pd),
   ps(p.ps),
   nrd(p.nrd),
   nrs(p.nrs),
   _sdp(0),
   _db(0),
   _sb(0)
{
  ++_count;
}
/*--------------------------------------------------------------------------*/
COMMON_MOS::~COMMON_MOS()
{
  detach_common(&_db);
  detach_common(&_sb);
  --_count;
  delete _sdp;
}
/*--------------------------------------------------------------------------*/
bool COMMON_MOS::operator==(const COMMON_COMPONENT& x)const
{
  const COMMON_MOS* p = dynamic_cast<const COMMON_MOS*>(&x);
  return (p
    && l_in == p->l_in
    && w_in == p->w_in
    && ad_in == p->ad_in
    && as_in == p->as_in
    && pd == p->pd
    && ps == p->ps
    && nrd == p->nrd
    && nrs == p->nrs
    && is_equal(x));
}
/*--------------------------------------------------------------------------*/
void COMMON_MOS::parse(CS& cmd)
{
  assert(!has_model());
  parse_modelname(cmd);
  int here = cmd.cursor();
  do{
  get(cmd, "L", &l_in, mPOSITIVE);
  get(cmd, "W", &w_in, mPOSITIVE);
  get(cmd, "AD", &ad_in, mPOSITIVE);
  get(cmd, "AS", &as_in, mPOSITIVE);
  get(cmd, "PD", &pd, mPOSITIVE);
  get(cmd, "PS", &ps, mPOSITIVE);
  get(cmd, "NRD", &nrd, mPOSITIVE);
  get(cmd, "NRS", &nrs, mPOSITIVE);
  }while (cmd.more() && !cmd.stuck(&here));
  cmd.check(bWARNING, "what's this?");
}
/*--------------------------------------------------------------------------*/
void COMMON_MOS::print(OMSTREAM& o)const
{
  o << "  " << modelname();
  o.setfloatwidth(7);
  if (l_in != NA)
    o << "  l=" << l_in;
  if (w_in != NA)
    o << "  w=" << w_in;
  if (ad_in != NA)
    o << "  ad=" << ad_in;
  if (as_in != NA)
    o << "  as=" << as_in;
  if (pd != 0.)
    o << "  pd=" << pd;
  if (ps != 0.)
    o << "  ps=" << ps;
  o << "  nrd=" << nrd;
  o << "  nrs=" << nrs;
  o << '\n';
}
/*--------------------------------------------------------------------------*/
void COMMON_MOS::expand()
{
  const MODEL_MOS_BASE* m = dynamic_cast<const MODEL_MOS_BASE*>(attach_model());
  if (!m) {
    untested();
    error(bERROR, "model " + modelname() + " is not a mosfet");
  }
  delete _sdp;
  _sdp = m->new_sdp(this);
  assert(_sdp);
  const SDP_MOS_BASE* b = dynamic_cast<const SDP_MOS_BASE*>(_sdp);
  assert(b);

  COMMON_DIODE* db = new COMMON_DIODE;
  db->area = b->ad;
  db->perim = pd;
  db->is_raw = b->idsat;
  db->cj_raw = m->cbd;
  db->cjsw_raw = NA;
  db->off = true;
  db->set_modelname(modelname());
  db->attach(model());
  attach_common(db, &_db);

  COMMON_DIODE* sb = new COMMON_DIODE;
  sb->area = b->as;
  sb->perim = ps;
  sb->is_raw = b->issat;
  sb->cj_raw = m->cbs;
  sb->cjsw_raw = NA;
  sb->off = true;
  sb->set_modelname(modelname());
  sb->attach(model());
  attach_common(sb, &_sb);

}
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
static EVAL_MOS_Cgb Eval_Cgb(CC_STATIC);
void EVAL_MOS_Cgb::tr_eval(ELEMENT* d)const
{
  assert(d);
  DEV_MOS* p = prechecked_cast<DEV_MOS*>(d->owner());
  assert(p);
  const COMMON_MOS* c = prechecked_cast<const COMMON_MOS*>(p->common());
  assert(c);
  const SDP_MOS_BASE* b = prechecked_cast<const SDP_MOS_BASE*>(c->sdp());
  assert(b);
  const MODEL_MOS_BASE* m = prechecked_cast<const MODEL_MOS_BASE*>(c->model());
  assert(m);

    DEV_CAPACITANCE* brh = prechecked_cast<DEV_CAPACITANCE*>(d);
    assert(brh);

    double cap = brh->value();
    {if (p->vgst < - b->phi) { 			/* accumulation */
      cap += b->cgate;
    }else if (p->vgst < 0.) {			/* depletion */
      cap += b->cgate * (-p->vgst) / b->phi;
    }else{					/* active, overlap only */
    }}
    
    brh->_y0.f1 = cap;
    {if (SIM::phase == SIM::pTRAN) {
      cap = (brh->_y0.f1 + brh->_q[1].f1) / 2;
      brh->_y0.f0 = (brh->_y0.x - brh->_q[1].x) * cap + brh->_q[1].f0;
    }else{
      brh->_y0.f0 = brh->_y0.x * brh->_y0.f1;
    }}
    trace3(brh->long_label().c_str(), brh->_y0.x, brh->_y0.f0, brh->_y0.f1);
}
/*--------------------------------------------------------------------------*/
static EVAL_MOS_Cgd Eval_Cgd(CC_STATIC);
void EVAL_MOS_Cgd::tr_eval(ELEMENT* d)const
{
  assert(d);
  DEV_MOS* p = prechecked_cast<DEV_MOS*>(d->owner());
  assert(p);
  const COMMON_MOS* c = prechecked_cast<const COMMON_MOS*>(p->common());
  assert(c);
  const SDP_MOS_BASE* b = prechecked_cast<const SDP_MOS_BASE*>(c->sdp());
  assert(b);
  const MODEL_MOS_BASE* m = prechecked_cast<const MODEL_MOS_BASE*>(c->model());
  assert(m);

    DEV_CAPACITANCE* brh = prechecked_cast<DEV_CAPACITANCE*>(d);
    assert(brh);

    assert(p->vdsat >= 0.);
    assert(p->vds >= 0.);
    double vbs    = (m->cmodel == 3) ? 0. : p->vbs;
    double vdbsat = p->vdsat - vbs;
    double vdb    = p->vds   - vbs;
    double ddif   = 2. * vdbsat - vdb;
    
    double cap = 0;
    {if (!p->reversed) { // treat as Cgs
      if (p->vgst >= 0.) {
	if (p->vdsat > p->vds) {		/* linear */
	  cap = (2./3.) * b->cgate * (1. - (vdbsat*vdbsat)/(ddif*ddif));
	  if (p->vgst <= .1) {
	    cap *= 10. * p->vgst;	// smooth discontinuity
	  }
	}
      }
    }else{ // treat as Cgs
      if (p->vgst >= -b->phi/2.) {		/* depletion  or active */
	cap = (2./3.) * b->cgate;
	if (p->vdsat > p->vds) {			/* linear */
	  double ndif   = p->vdsat - p->vds;
	  cap *= 1. - (ndif*ndif)/(ddif*ddif);
	}
	if (p->vgst <= 0) {
	  cap *= 1. + p->vgst / (b->phi);
	  cap *= 1. + p->vgst / (b->phi);
	}
      }
    }}
    cap += brh->value();		/* else overlap only */
    
    brh->_y0.f1 = cap;
    {if (SIM::phase == SIM::pTRAN) {
      cap = (brh->_y0.f1 + brh->_q[1].f1) / 2;
      brh->_y0.f0 = (brh->_y0.x - brh->_q[1].x) * cap + brh->_q[1].f0;
    }else{
      brh->_y0.f0 = brh->_y0.x * brh->_y0.f1;
    }}
    trace3(brh->long_label().c_str(), brh->_y0.x, brh->_y0.f0, brh->_y0.f1);
}
/*--------------------------------------------------------------------------*/
static EVAL_MOS_Cgs Eval_Cgs(CC_STATIC);
void EVAL_MOS_Cgs::tr_eval(ELEMENT* d)const
{
  assert(d);
  DEV_MOS* p = prechecked_cast<DEV_MOS*>(d->owner());
  assert(p);
  const COMMON_MOS* c = prechecked_cast<const COMMON_MOS*>(p->common());
  assert(c);
  const SDP_MOS_BASE* b = prechecked_cast<const SDP_MOS_BASE*>(c->sdp());
  assert(b);
  const MODEL_MOS_BASE* m = prechecked_cast<const MODEL_MOS_BASE*>(c->model());
  assert(m);

    DEV_CAPACITANCE* brh = prechecked_cast<DEV_CAPACITANCE*>(d);
    assert(brh);

    assert(p->vdsat >= 0.);
    assert(p->vds >= 0.);
    double vbs    = (m->cmodel == 3) ? 0. : p->vbs;
    double vdbsat = p->vdsat - vbs;
    double vdb    = p->vds   - vbs;
    double ddif   = 2. * vdbsat - vdb;
    
    double cap = 0;
    {if (p->reversed) { // treat as Cgd
      if (p->vgst >= 0.) {
	if (p->vdsat > p->vds) {		/* linear */
	  cap = (2./3.) * b->cgate * (1. - (vdbsat*vdbsat)/(ddif*ddif));
	  if (p->vgst <= .1) {
	    cap *= 10. * p->vgst;	// smooth discontinuity
	  }
	}
      }
    }else{ // treat as Cgs
      if (p->vgst >= -b->phi/2.) {		/* depletion  or active */
	cap = (2./3.) * b->cgate;
	if (p->vdsat > p->vds) {			/* linear */
	  double ndif   = p->vdsat - p->vds;
	  cap *= 1. - (ndif*ndif)/(ddif*ddif);
	}
	if (p->vgst <= 0) {
	  cap *= 1. + p->vgst / (b->phi);
	  cap *= 1. + p->vgst / (b->phi);
	}
      }
    }}
    cap += brh->value();		/* else overlap only */
    
    brh->_y0.f1 = cap;
    {if (SIM::phase == SIM::pTRAN) {
      cap = (brh->_y0.f1 + brh->_q[1].f1) / 2;
      brh->_y0.f0 = (brh->_y0.x - brh->_q[1].x) * cap + brh->_q[1].f0;
    }else{
      brh->_y0.f0 = brh->_y0.x * brh->_y0.f1;
    }}
    trace3(brh->long_label().c_str(), brh->_y0.x, brh->_y0.f0, brh->_y0.f1);
}
/*--------------------------------------------------------------------------*/
DEV_MOS::DEV_MOS()
  :BASE_SUBCKT(),
   ids(0.),
   gds(0.),
   gmf(0.),
   gmr(0.),
   gmbf(0.),
   gmbr(0.),
   isub(0.),
   gbbs(0.),
   gbgs(0.),
   gbds(0.),
   qgate(0.),
   cggb(0.),
   cgsb(0.),
   cgdb(0.),
   qdrn(0.),
   cdgb(0.),
   cdsb(0.),
   cddb(0.),
   qbulk(0.),
   cbgb(0.),
   cbsb(0.),
   cbdb(0.),
   gtau(0.),
   cqgb(0.),
   cqsb(0.),
   cqdb(0.),
   cqbb(0.),
   tconst(0.),
   cgb(0.),
   qgb(0.),
   qgd(0.),
   cgd(0.),
   qgs(0.),
   cgs(0.),
   vgs(0.),
   vds(0.),
   vbs(0.),
   vdsat(0.),
   vgst(0.),
   von(0.),
   reversed(false),
   cutoff(false),
   subthreshold(false),
   saturated(false),
   sbfwd(false),
   dbfwd(false),
   punchthru(false),
   _Rs(0),
   _Rd(0),
   _Ddb(0),
   _Dsb(0),
   _Cgs(0),
   _Cgd(0),
   _Cgb(0),
   _Ids(0)
{
  set_inode_count(2);
  _n = _nodes + 2;
  attach_common(&Default_MOS);
  ++_count;
}
/*--------------------------------------------------------------------------*/
DEV_MOS::DEV_MOS(const DEV_MOS& p)
  :BASE_SUBCKT(p),
   ids(p.ids),
   gds(p.gds),
   gmf(p.gmf),
   gmr(p.gmr),
   gmbf(p.gmbf),
   gmbr(p.gmbr),
   isub(p.isub),
   gbbs(p.gbbs),
   gbgs(p.gbgs),
   gbds(p.gbds),
   qgate(p.qgate),
   cggb(p.cggb),
   cgsb(p.cgsb),
   cgdb(p.cgdb),
   qdrn(p.qdrn),
   cdgb(p.cdgb),
   cdsb(p.cdsb),
   cddb(p.cddb),
   qbulk(p.qbulk),
   cbgb(p.cbgb),
   cbsb(p.cbsb),
   cbdb(p.cbdb),
   gtau(p.gtau),
   cqgb(p.cqgb),
   cqsb(p.cqsb),
   cqdb(p.cqdb),
   cqbb(p.cqbb),
   tconst(p.tconst),
   cgb(p.cgb),
   qgb(p.qgb),
   qgd(p.qgd),
   cgd(p.cgd),
   qgs(p.qgs),
   cgs(p.cgs),
   vgs(p.vgs),
   vds(p.vds),
   vbs(p.vbs),
   vdsat(p.vdsat),
   vgst(p.vgst),
   von(p.von),
   reversed(p.reversed),
   cutoff(p.cutoff),
   subthreshold(p.subthreshold),
   saturated(p.saturated),
   sbfwd(p.sbfwd),
   dbfwd(p.dbfwd),
   punchthru(p.punchthru),
   _Rs(0),
   _Rd(0),
   _Ddb(0),
   _Dsb(0),
   _Cgs(0),
   _Cgd(0),
   _Cgb(0),
   _Ids(0)
{
  _n = _nodes + 2;
  for (int ii = -2; ii < 4; ++ii) {
    _n[ii] = p._n[ii];
  }
  ++_count;
}
/*--------------------------------------------------------------------------*/
void DEV_MOS::parse(CS& cmd)
{
  assert(has_common());
  COMMON_MOS* c = prechecked_cast<COMMON_MOS*>(common()->clone());
  assert(c);

  parse_Label(cmd);
  parse_nodes(cmd,numnodes(),numnodes());
  c->parse(cmd);
  attach_common(c);
}
/*--------------------------------------------------------------------------*/
void DEV_MOS::print(OMSTREAM& o, int)const
{
  const COMMON_MOS* c = prechecked_cast<const COMMON_MOS*>(common());
  assert(c);

  o << short_label();
  printnodes(o,numnodes());
  c->print(o);
}
/*--------------------------------------------------------------------------*/
void DEV_MOS::expand()
{
  COMMON_MOS* c = prechecked_cast<COMMON_MOS*>(mutable_common());
  assert(c);
  c->expand();
  const MODEL_MOS_BASE* m = prechecked_cast<const MODEL_MOS_BASE*>(c->model());
  assert(m);
  const SDP_MOS_BASE* b = prechecked_cast<const SDP_MOS_BASE*>(c->sdp());
  assert(b);

  {if (!OPT::rstray || b->rd == 0.) {
    _n[n_idrain] = _n[n_drain];
  }else{
    _n[n_idrain].t = STATUS::newnode_model();
  }}
  {if (!OPT::rstray || b->rs == 0.) {
    _n[n_isource] = _n[n_source];
  }else{
    _n[n_isource].t = STATUS::newnode_model();
  }}

  {if (!OPT::rstray || b->rs == 0.) {
    if (_Rs) {
      subckt().erase(_Rs);
      _Rs = NULL;
    }
  }else{
    if (!_Rs) {
      _Rs = new DEV_RESISTANCE;
      subckt().push_front(_Rs);
    }
    {{
      node_t nodes[] = {_n[n_source], _n[n_isource]};
      _Rs->set_parameters("Rs", this, NULL, b->rs, 0, 0, 2, nodes);
    }}
  }}
  {if (!OPT::rstray || b->rd == 0.) {
    if (_Rd) {
      subckt().erase(_Rd);
      _Rd = NULL;
    }
  }else{
    if (!_Rd) {
      _Rd = new DEV_RESISTANCE;
      subckt().push_front(_Rd);
    }
    {{
      node_t nodes[] = {_n[n_drain], _n[n_idrain]};
      _Rd->set_parameters("Rd", this, NULL, b->rd, 0, 0, 2, nodes);
    }}
  }}
  {if (_n[n_bulk].t == _n[n_idrain].t || b->idsat == 0.) {
    if (_Ddb) {
      subckt().erase(_Ddb);
      _Ddb = NULL;
    }
  }else{
    if (!_Ddb) {
      _Ddb = new DEV_DIODE;
      subckt().push_front(_Ddb);
    }
    {if (m->polarity==pP) {
      node_t nodes[] = {_n[n_idrain], _n[n_bulk]};
      _Ddb->set_parameters("Ddb", this, c->_db, 0., 0, 0, 2, nodes);
    }else{
      node_t nodes[] = {_n[n_bulk], _n[n_idrain]};
      _Ddb->set_parameters("Ddb", this, c->_db, 0., 0, 0, 2, nodes);
    }}
  }}
  {if (_n[n_bulk].t == _n[n_isource].t || b->issat == 0.) {
    if (_Dsb) {
      subckt().erase(_Dsb);
      _Dsb = NULL;
    }
  }else{
    if (!_Dsb) {
      _Dsb = new DEV_DIODE;
      subckt().push_front(_Dsb);
    }
    {if (m->polarity==pP) {
      node_t nodes[] = {_n[n_isource], _n[n_bulk]};
      _Dsb->set_parameters("Dsb", this, c->_sb, 0., 0, 0, 2, nodes);
    }else{
      node_t nodes[] = {_n[n_bulk], _n[n_isource]};
      _Dsb->set_parameters("Dsb", this, c->_sb, 0., 0, 0, 2, nodes);
    }}
  }}
  {if (!OPT::cstray || _n[n_gate].t == _n[n_isource].t) {
    if (_Cgs) {
      subckt().erase(_Cgs);
      _Cgs = NULL;
    }
  }else{
    if (!_Cgs) {
      _Cgs = new DEV_CAPACITANCE;
      subckt().push_front(_Cgs);
    }
    {{
      node_t nodes[] = {_n[n_gate], _n[n_isource]};
      _Cgs->set_parameters("Cgs", this, &Eval_Cgs, (m->cgso*b->w_eff), 0, 0, 2, nodes);
    }}
  }}
  {if (!OPT::cstray || _n[n_gate].t == _n[n_idrain].t) {
    if (_Cgd) {
      subckt().erase(_Cgd);
      _Cgd = NULL;
    }
  }else{
    if (!_Cgd) {
      _Cgd = new DEV_CAPACITANCE;
      subckt().push_front(_Cgd);
    }
    {{
      node_t nodes[] = {_n[n_gate], _n[n_idrain]};
      _Cgd->set_parameters("Cgd", this, &Eval_Cgd, (m->cgdo*b->w_eff), 0, 0, 2, nodes);
    }}
  }}
  {if (!OPT::cstray || _n[n_bulk].t == _n[n_gate].t) {
    if (_Cgb) {
      subckt().erase(_Cgb);
      _Cgb = NULL;
    }
  }else{
    if (!_Cgb) {
      _Cgb = new DEV_CAPACITANCE;
      subckt().push_front(_Cgb);
    }
    {{
      node_t nodes[] = {_n[n_gate], _n[n_bulk]};
      _Cgb->set_parameters("Cgb", this, &Eval_Cgb, (m->cgbo*b->l_eff), 0, 0, 2, nodes);
    }}
  }}
  {{
    if (!_Ids) {
      _Ids = new DEV_POLY_G;
      subckt().push_front(_Ids);
    }
    {{
      node_t nodes[] = {_n[n_idrain], _n[n_isource], _n[n_gate], _n[n_isource], _n[n_idrain], _n[n_gate], _n[n_bulk], _n[n_isource], _n[n_idrain], _n[n_bulk]};
      _Ids->set_parameters("Ids", this, NULL, 0., 6, &ids, 10, nodes);
    }}
  }}
  assert(subckt().exists());
  subckt().expand();
  assert(!constant());
  subckt().set_slave();
}
/*--------------------------------------------------------------------------*/
double DEV_MOS::tr_probe_num(CS& cmd)const
{
  const COMMON_MOS* c = prechecked_cast<const COMMON_MOS*>(common());
  assert(c);
  const MODEL_MOS_BASE* m = prechecked_cast<const MODEL_MOS_BASE*>(c->model());
  assert(m);
  const SDP_MOS_BASE* b = prechecked_cast<const SDP_MOS_BASE*>(c->sdp());
  assert(b);

  {if (cmd.pmatch("VDS")) {
    return  _n[n_drain].v0() - _n[n_source].v0();
  }else if (cmd.pmatch("VGS")) {
    return  _n[n_gate].v0() - _n[n_source].v0();
  }else if (cmd.pmatch("VBS")) {
    return  _n[n_bulk].v0() - _n[n_source].v0();
  }else if (cmd.pmatch("VDSInt")) {
    return  vds;
  }else if (cmd.pmatch("VGSInt")) {
    return  vgs;
  }else if (cmd.pmatch("VBSInt")) {
    return  vbs;
  }else if (cmd.pmatch("VGD")) {
    return  _n[n_gate].v0() - _n[n_drain].v0();
  }else if (cmd.pmatch("VBD")) {
    return  _n[n_bulk].v0() - _n[n_drain].v0();
  }else if (cmd.pmatch("VSD")) {
    return  _n[n_source].v0() - _n[n_drain].v0();
  }else if (cmd.pmatch("VDM")) {
    return  ( _n[n_drain].v0() - _n[n_source].v0() + _n[n_drain].v0() - _n[n_drain].v0() ) / 2.;
  }else if (cmd.pmatch("VGM")) {
    return  ( _n[n_gate].v0() - _n[n_source].v0() + _n[n_gate].v0() - _n[n_drain].v0() ) / 2.;
  }else if (cmd.pmatch("VBM")) {
    return  ( _n[n_bulk].v0() - _n[n_source].v0() + _n[n_bulk].v0() - _n[n_drain].v0() ) / 2.;
  }else if (cmd.pmatch("VSM")) {
    return  ( _n[n_source].v0() - _n[n_source].v0() + _n[n_source].v0() - _n[n_drain].v0() ) / 2.;
  }else if (cmd.pmatch("VDG")) {
    return  _n[n_drain].v0() - _n[n_gate].v0();
  }else if (cmd.pmatch("VBG")) {
    return  _n[n_bulk].v0() - _n[n_gate].v0();
  }else if (cmd.pmatch("VSG")) {
    return  _n[n_source].v0() - _n[n_gate].v0();
  }else if (cmd.pmatch("VDB")) {
    return  _n[n_drain].v0() - _n[n_bulk].v0();
  }else if (cmd.pmatch("VGB")) {
    return  _n[n_gate].v0() - _n[n_bulk].v0();
  }else if (cmd.pmatch("VSB")) {
    return  _n[n_source].v0() - _n[n_bulk].v0();
  }else if (cmd.pmatch("VD")) {
    return  _n[n_drain].v0();
  }else if (cmd.pmatch("VG")) {
    return  _n[n_gate].v0();
  }else if (cmd.pmatch("VB")) {
    return  _n[n_bulk].v0();
  }else if (cmd.pmatch("VS")) {
    return  _n[n_source].v0();
  }else if (cmd.pmatch("Id")) {
    return  (_Rd) ? CARD::probe(_Rd,"I") : CARD::probe(_Ids,"I") - CARD::probe(_Cgd,"I") + CARD::probe(_Ddb,"I") * m->polarity;
  }else if (cmd.pmatch("IS")) {
    return  (_Rs) ? CARD::probe(_Rs,"I") : - CARD::probe(_Ids,"I") - CARD::probe(_Cgs,"I") + CARD::probe(_Dsb,"I") * m->polarity;
  }else if (cmd.pmatch("IG")) {
    return  CARD::probe(_Cgs,"I") + CARD::probe(_Cgd,"I") + CARD::probe(_Cgb,"I");
  }else if (cmd.pmatch("IB")) {
    return  - CARD::probe(_Ddb,"I") * m->polarity - CARD::probe(_Dsb,"I") * m->polarity - CARD::probe(_Cgb,"I");
  }else if (cmd.pmatch("IBD")) {
    return  CARD::probe(_Ddb,"I");
  }else if (cmd.pmatch("IBS")) {
    return  CARD::probe(_Dsb,"I");
  }else if (cmd.pmatch("CGSOvl")) {
    return  CARD::probe(_Cgs,"NV");
  }else if (cmd.pmatch("CGDOvl")) {
    return  CARD::probe(_Cgd,"NV");
  }else if (cmd.pmatch("CGBOvl")) {
    return  CARD::probe(_Cgb,"NV");
  }else if (cmd.pmatch("CGST")) {
    return  CARD::probe(_Cgs,"EV");
  }else if (cmd.pmatch("CGDT")) {
    return  CARD::probe(_Cgd,"EV");
  }else if (cmd.pmatch("CGBT")) {
    return  CARD::probe(_Cgb,"EV");
  }else if (cmd.pmatch("CGSm")) {
    return  CARD::probe(_Cgs,"EV") - CARD::probe(_Cgs,"NV");
  }else if (cmd.pmatch("CGDm")) {
    return  CARD::probe(_Cgd,"EV") - CARD::probe(_Cgd,"NV");
  }else if (cmd.pmatch("CGBm")) {
    return  CARD::probe(_Cgb,"EV") - CARD::probe(_Cgb,"NV");
  }else if (cmd.pmatch("CBD")) {
    return  CARD::probe(_Ddb,"Cap");
  }else if (cmd.pmatch("CBS")) {
    return  CARD::probe(_Dsb,"Cap");
  }else if (cmd.pmatch("CGATE")) {
    return  b->cgate;
  }else if (cmd.pmatch("GMR")) {
    return  gmr;
  }else if (cmd.pmatch("GMF")) {
    return  gmf;
  }else if (cmd.pmatch("GM")) {
    return  (reversed) ? gmr : gmf;
  }else if (cmd.pmatch("GDS")) {
    return  gds;
  }else if (cmd.pmatch("GMBR")) {
    return  gmbr;
  }else if (cmd.pmatch("GMBF")) {
    return  gmbf;
  }else if (cmd.pmatch("GMB")) {
    return  (reversed) ? gmbr : gmbf;
  }else if (cmd.pmatch("GBD")) {
    return  CARD::probe(_Ddb,"G");
  }else if (cmd.pmatch("GBS")) {
    return  CARD::probe(_Dsb,"G");
  }else if (cmd.pmatch("VGST")) {
    return  vgst;
  }else if (cmd.pmatch("VON")) {
    return  von;
  }else if (cmd.pmatch("VDSAT")) {
    return  vdsat * m->polarity;
  }else if (cmd.pmatch("VTH")) {
    return  von * m->polarity;
  }else if (cmd.pmatch("IDS")) {
    return  m->polarity * ((reversed) ? -ids : ids);
  }else if (cmd.pmatch("IDSTray")) {
    return  - CARD::probe(_Cgd,"I") + CARD::probe(_Ddb,"I") * m->polarity;
  }else if (cmd.pmatch("P")) {
    return  CARD::probe(_Rs,"P") + CARD::probe(_Rd,"P") + CARD::probe(_Ddb,"P") + CARD::probe(_Dsb,"P") + CARD::probe(_Cgs,"P") + CARD::probe(_Cgd,"P") + CARD::probe(_Cgb,"P") + CARD::probe(_Ids,"P");
  }else if (cmd.pmatch("PD")) {
    return  CARD::probe(_Rs,"PD") + CARD::probe(_Rd,"PD") + CARD::probe(_Ddb,"PD") + CARD::probe(_Dsb,"PD") + CARD::probe(_Cgs,"PD") + CARD::probe(_Cgd,"PD") + CARD::probe(_Cgb,"PD") + CARD::probe(_Ids,"PD");
  }else if (cmd.pmatch("PS")) {
    return  CARD::probe(_Rs,"PS") + CARD::probe(_Rd,"PS") + CARD::probe(_Ddb,"PS") + CARD::probe(_Dsb,"PS") + CARD::probe(_Cgs,"PS") + CARD::probe(_Cgd,"PS") + CARD::probe(_Cgb,"PS") + CARD::probe(_Ids,"PS");
  }else if (cmd.pmatch("REgion")) {
    return  static_cast<double>((!cutoff) + (!subthreshold * 2) + (saturated * 4) + (sbfwd * 10) + (dbfwd * 20) + (punchthru * 40)) * ((reversed)? -1 : 1);
  }else if (cmd.pmatch("Status")) {
    return  static_cast<double>(converged() * 2);
  }else {
    return BASE_SUBCKT::tr_probe_num(cmd);
  }}
}
/*--------------------------------------------------------------------------*/
void DEV_MOS::limit_mos(double Vds, double Vgs, double Vbs)
{

    /* Spice style vgs limiting */
    {if (!(OPT::mosflags & 0010) && STATUS::iter[SIM::mode] > 1) {
      //assert(vgst == vgs - von);
      {if (Vgs > vgs) {			/* increasing */
	{if (vgst < 0) {			/* was off */
	  vgs = std::min(Vgs, von + .5);
	}else if (vgst < 3.5) {		/* ??? */
	  vgs = std::min(Vgs, von + 4.);
	}else{
	  vgs = std::min(Vgs, 3.*vgs - 2.*von + 2.);
	}}
      }else{				/* decreasing */
	{if (vgst < 0) {			/* off */
	  vgs = std::max(Vgs, 3. * vgs - 2. * von - 2.);
	}else if (vgst < 3.5) {
	  vgs = std::max(Vgs, von - .5);
	}else{
	  vgs = std::max(Vgs, von + 2.);
	}}
      }}
      //Vds += vgs - Vgs;		/* vds patch (per Spice) not done */
    }else{				/* because it usually makes it worse */
      vgs = Vgs;
    }}
    {if (_n[n_drain].t == _n[n_gate].t) {	/* gd tied, limiting done */
      vds = Vds + (vgs - Vgs);		/* it seems that vds = vgs should */
    					/* work, but it will be a little off */
					/* if there is resistance */
      
					/* Spice style vds limiting */
    }else if (!(OPT::mosflags & 0020) && STATUS::iter[SIM::mode] > 1) {
      {if (Vds <= vds) {			/* decreasing */
	{if (vds < 3.5) {
	  vds = std::max(Vds,-.5);
	}else{
	  vds = std::max(Vds,2.);
	}}
      }else{				/* increasing */
	{if (vds < 3.5) {
	  vds = std::min(Vds,4.);
	}else{
	  vds = std::min(Vds, 3.*vds + 2.);
	}}
      }}
      //vgs += vds - Vds;
    }else{
      vds = Vds;
    }}
    
    {if (!(OPT::mosflags & 0040) && STATUS::iter[SIM::mode] > 1) {
      //    if (Vbs > 0.) {
      //      if (vbs >= 0.) {
      //        vbs = std::min(Vbs,vbs+.1);
      //      }else{
      //        vbs = 0.;
      //      }
      //    }else{
      //      vbs = Vbs;
      //    }
      vbs = std::min(Vbs,0.);
    }else{
      vbs = Vbs;
    }}
    if (OPT::dampstrategy & dsDEVLIMIT
	&& (vgs != Vgs || vds != Vds || vbs != Vbs)) {
      untested();
      SIM::fulldamp = true;
      error(bTRACE, long_label() + ":device limit damp\n");
    }
    trace1(long_label().c_str(), evaliter());
    trace3("prop", Vds, Vgs, Vbs);
    trace3("using", vds, vgs, vbs);
}
/*--------------------------------------------------------------------------*/
void DEV_MOS::reverse_if_needed()
{

    if (vds < 0) {
      error(bTRACE, long_label() + ": reversing\n");
      error(bTRACE, "before: vds=%g vgs=%g vbs=%g\n", vds, vgs, vbs);
      reversed = !reversed;
      vgs -= vds;
      vbs -= vds;
      vds = -vds;
      error(bTRACE, "after: vds=%g vgs=%g vbs=%g\n", vds, vgs, vbs);
      if (OPT::dampstrategy & dsREVERSE) {
	SIM::fulldamp = true;
	untested();
	error(bTRACE, long_label() + ":reverse damp\n");
      }
      {if (!(OPT::mosflags & 0040)) {
	vbs = std::min(vbs,0.);
      }else{
	untested();
      }}
    }
}
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/

/*--------------------------------------------------------------------------*/
bool DEV_MOS::tr_needs_eval()
{
  {if (is_q_for_eval()) {
    untested();
    return false;
  }else if (!converged()) {
    return true;
  }else{
    const COMMON_MOS* c = prechecked_cast<const COMMON_MOS*>(common());
    assert(c);
    const MODEL_MOS_BASE* m=prechecked_cast<const MODEL_MOS_BASE*>(c->model());
    assert(m);
    polarity_t polarity = m->polarity;
    node_t eff_source((reversed) ? _n[n_idrain] : _n[n_isource]);
    node_t eff_drain((reversed) ? _n[n_isource] : _n[n_idrain]);
    return !(conchk(vds,polarity*(eff_drain.v0()-eff_source.v0()),OPT::vntol)
	     && conchk(vgs, polarity*(_n[n_gate].v0()-eff_source.v0()),
		       OPT::vntol)
	     && conchk(vbs, polarity*(_n[n_bulk].v0()-eff_source.v0()),
		       OPT::vntol));
  }}
}
/*--------------------------------------------------------------------------*/
bool DEV_MOS::do_tr()
{
  const COMMON_MOS* c = prechecked_cast<const COMMON_MOS*>(common());
  assert(c);
  const MODEL_MOS_BASE* m = prechecked_cast<const MODEL_MOS_BASE*>(c->model());
  assert(m);
  assert(subckt().exists());

  double Vds, Vgs, Vbs;

  bool was_cutoff = cutoff;
  bool was_subthreshold = subthreshold;
  bool was_saturated = saturated;
  bool was_reversed = reversed;
  bool was_dbfwd = dbfwd;
  bool was_sbfwd = sbfwd;
  polarity_t polarity = m->polarity;

  {if (STATUS::iter[SIM::mode] <= 1) {
    reversed = false;
    Vds = Vgs = Vbs = 0.;
    if (OPT::mosflags & 0100) {
      untested();
      //Vds = m->vto;
    }
    if (OPT::mosflags & 0200) {
      untested();
      //Vgs = m->vto;
    }
    if (OPT::mosflags & 0400  &&  _n[n_source].t != _n[n_bulk].t) {
      untested();
      Vbs = -1;
    }
    if (_n[n_drain].t == _n[n_gate].t) {
      Vds = Vgs;
    }
  }else if (reversed) {
    Vds = polarity * volts_limited(_n[n_isource],_n[n_idrain]);
    Vgs = polarity * volts_limited(_n[n_gate],_n[n_idrain]);
    Vbs = polarity * volts_limited(_n[n_bulk],_n[n_idrain]);
  }else{
    Vds = polarity * volts_limited(_n[n_idrain],_n[n_isource]);
    Vgs = polarity * volts_limited(_n[n_gate],_n[n_isource]);
    Vbs = polarity * volts_limited(_n[n_bulk],_n[n_isource]);
  }}

  limit_mos(Vds, Vgs, Vbs);// also sets the new vds,vgs,vbs
  m->tr_eval(this);

  ids *= polarity;

  set_converged(subckt().do_tr());
  
  trace3(long_label().c_str(), vds, vgs, vbs);
  trace4("", ids, gmf, gds, gmbf);
  trace4("", ids, gmr, gds, gmbr);
  if (was_cutoff != cutoff  ||  was_subthreshold != subthreshold  
  	||  was_saturated != saturated  ||  was_reversed != reversed  
	||  was_dbfwd != dbfwd  ||  was_sbfwd != sbfwd) {
    if (OPT::dampstrategy & dsDEVREGION) {
      SIM::fulldamp = true;
    }
    #if defined(DO_TRACE)
      error(bTRACE,"%s:%d: region change\n", long_label().c_str(), evaliter());
    #endif
  }
  return converged();
}
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
