973 lines
32 KiB
C
973 lines
32 KiB
C
/**********
|
|
Copyright 1990 Regents of the University of California. All rights reserved.
|
|
Author: 1988 Jaijeet S Roychowdhury
|
|
**********/
|
|
|
|
#include "ngspice.h"
|
|
#include <stdio.h>
|
|
#include "cktdefs.h"
|
|
#include "devdefs.h"
|
|
#include "mos3defs.h"
|
|
#include "trandefs.h"
|
|
#include "distodef.h"
|
|
#include "const.h"
|
|
#include "sperror.h"
|
|
#include "suffix.h"
|
|
|
|
int
|
|
MOS3dSetup(inModel,ckt)
|
|
GENmodel *inModel;
|
|
register CKTcircuit *ckt;
|
|
/* actually load the current value into the
|
|
* sparse matrix previously provided
|
|
*/
|
|
{
|
|
register MOS3model *model = (MOS3model *)inModel;
|
|
register MOS3instance *here;
|
|
double Beta;
|
|
double DrainSatCur;
|
|
double EffectiveLength;
|
|
double GateBulkOverlapCap;
|
|
double GateDrainOverlapCap;
|
|
double GateSourceOverlapCap;
|
|
double OxideCap;
|
|
double SourceSatCur;
|
|
double arg;
|
|
double cdrain;
|
|
double evbs;
|
|
double sarg;
|
|
double sargsw;
|
|
double lvgs;
|
|
double vbd;
|
|
double vbs;
|
|
double vds;
|
|
double vdsat;
|
|
double vgb;
|
|
double vgd;
|
|
double vgs;
|
|
double von;
|
|
double lcapgs2,lcapgs3; /* total gate-source capacitance */
|
|
double lcapgd2,lcapgd3; /* total gate-drain capacitance */
|
|
double lcapgb2,lcapgb3; /* total gate-bulk capacitance */
|
|
double lgbs, lgbs2, lgbs3;
|
|
double lgbd, lgbd2, lgbd3;
|
|
double gm2, gb2, gds2, gmb, gmds, gbds;
|
|
double gm3, gb3, gds3, gm2ds, gm2b, gb2ds, gbds2, gmb2, gmds2, gmbds;
|
|
double lcapbd, lcapbd2, lcapbd3;
|
|
double lcapbs, lcapbs2, lcapbs3;
|
|
double ebd;
|
|
double vt; /* vt at instance temperature */
|
|
Dderivs d_cdrain;
|
|
|
|
|
|
|
|
/* loop through all the MOS3 device models */
|
|
for( ; model != NULL; model = model->MOS3nextModel ) {
|
|
|
|
/* loop through all the instances of the model */
|
|
for (here = model->MOS3instances; here != NULL ;
|
|
here=here->MOS3nextInstance) {
|
|
if (here->MOS3owner != ARCHme) continue;
|
|
|
|
vt = CONSTKoverQ * here->MOS3temp;
|
|
|
|
/* first, we compute a few useful values - these could be
|
|
* pre-computed, but for historical reasons are still done
|
|
* here. They may be moved at the expense of instance size
|
|
*/
|
|
|
|
EffectiveLength=here->MOS3l - 2*model->MOS3latDiff;
|
|
if( (here->MOS3tSatCurDens == 0) ||
|
|
(here->MOS3drainArea == 0) ||
|
|
(here->MOS3sourceArea == 0)) {
|
|
DrainSatCur = here->MOS3tSatCur;
|
|
SourceSatCur = here->MOS3tSatCur;
|
|
} else {
|
|
DrainSatCur = here->MOS3tSatCurDens *
|
|
here->MOS3drainArea;
|
|
SourceSatCur = here->MOS3tSatCurDens *
|
|
here->MOS3sourceArea;
|
|
}
|
|
GateSourceOverlapCap = model->MOS3gateSourceOverlapCapFactor *
|
|
here->MOS3w;
|
|
GateDrainOverlapCap = model->MOS3gateDrainOverlapCapFactor *
|
|
here->MOS3w;
|
|
GateBulkOverlapCap = model->MOS3gateBulkOverlapCapFactor *
|
|
EffectiveLength;
|
|
Beta = here->MOS3tTransconductance * here->MOS3w/EffectiveLength;
|
|
OxideCap = model->MOS3oxideCapFactor * EffectiveLength *
|
|
here->MOS3w;
|
|
|
|
|
|
|
|
/*
|
|
* ok - now to do the start-up operations
|
|
*
|
|
* we must get values for vbs, vds, and vgs from somewhere
|
|
* so we either predict them or recover them from last iteration
|
|
* These are the two most common cases - either a prediction
|
|
* step or the general iteration step and they
|
|
* share some code, so we put them first - others later on
|
|
*/
|
|
|
|
|
|
/* general iteration */
|
|
|
|
vbs = model->MOS3type * (
|
|
*(ckt->CKTrhsOld+here->MOS3bNode) -
|
|
*(ckt->CKTrhsOld+here->MOS3sNodePrime));
|
|
vgs = model->MOS3type * (
|
|
*(ckt->CKTrhsOld+here->MOS3gNode) -
|
|
*(ckt->CKTrhsOld+here->MOS3sNodePrime));
|
|
vds = model->MOS3type * (
|
|
*(ckt->CKTrhsOld+here->MOS3dNodePrime) -
|
|
*(ckt->CKTrhsOld+here->MOS3sNodePrime));
|
|
|
|
/* now some common crunching for some more useful quantities */
|
|
|
|
|
|
|
|
/*
|
|
* now all the preliminaries are over - we can start doing the
|
|
* real work
|
|
*/
|
|
vbd = vbs - vds;
|
|
vgd = vgs - vds;
|
|
vgb = vgs - vbs;
|
|
|
|
/* bulk-source and bulk-drain doides
|
|
* here we just evaluate the ideal diode current and the
|
|
* correspoinding derivative (conductance).
|
|
*/
|
|
if(vbs <= 0) {
|
|
lgbs = SourceSatCur/vt;
|
|
lgbs += ckt->CKTgmin;
|
|
lgbs2 = lgbs3 = 0;
|
|
} else {
|
|
evbs = exp(MIN(MAX_EXP_ARG,vbs/vt));
|
|
lgbs = SourceSatCur*evbs/vt + ckt->CKTgmin;
|
|
lgbs2 = model->MOS3type *0.5 * (lgbs - ckt->CKTgmin)/vt;
|
|
lgbs3 = model->MOS3type *lgbs2/(vt*3);
|
|
|
|
}
|
|
if(vbd <= 0) {
|
|
lgbd = DrainSatCur/vt;
|
|
lgbd += ckt->CKTgmin;
|
|
lgbd2 = lgbd3 = 0;
|
|
} else {
|
|
ebd = exp(MIN(MAX_EXP_ARG,vbd/vt));
|
|
lgbd = DrainSatCur*ebd/vt +ckt->CKTgmin;
|
|
lgbd2 = model->MOS3type *0.5 * (lgbd - ckt->CKTgmin)/vt;
|
|
lgbd3 = model->MOS3type *lgbd2/(vt*3);
|
|
}
|
|
|
|
|
|
/* now to determine whether the user was able to correctly
|
|
* identify the source and drain of his device
|
|
*/
|
|
if(vds >= 0) {
|
|
/* normal mode */
|
|
here->MOS3mode = 1;
|
|
} else {
|
|
/* inverse mode */
|
|
here->MOS3mode = -1;
|
|
}
|
|
|
|
{
|
|
/*
|
|
* subroutine moseq3(vds,vbs,vgs,gm,gds,gmbs,
|
|
* qg,qc,qb,cggb,cgdb,cgsb,cbgb,cbdb,cbsb)
|
|
*/
|
|
|
|
/*
|
|
* this routine evaluates the drain current, its derivatives and
|
|
* the charges associated with the gate, channel and bulk
|
|
* for mosfets based on semi-empirical equations
|
|
*/
|
|
|
|
/*
|
|
common /mosarg/ vto,beta,gamma,phi,phib,cox,xnsub,xnfs,xd,xj,xld,
|
|
1 xlamda,uo,uexp,vbp,utra,vmax,xneff,xl,xw,vbi,von,vdsat,qspof,
|
|
2 beta0,beta1,cdrain,xqco,xqc,fnarrw,fshort,lev
|
|
common /status/ omega,time,delta,delold(7),ag(7),vt,xni,egfet,
|
|
1 xmu,sfactr,mode,modedc,icalc,initf,method,iord,maxord,noncon,
|
|
2 iterno,itemno,nosolv,modac,ipiv,ivmflg,ipostp,iscrch,iofile
|
|
common /knstnt/ twopi,xlog2,xlog10,root2,rad,boltz,charge,ctok,
|
|
1 gmin,reltol,abstol,vntol,trtol,chgtol,eps0,epssil,epsox,
|
|
2 pivtol,pivrel
|
|
*/
|
|
|
|
/* equivalence (xlamda,alpha),(vbp,theta),(uexp,eta),(utra,xkappa)*/
|
|
|
|
double coeff0 = 0.0631353e0;
|
|
double coeff1 = 0.8013292e0;
|
|
double coeff2 = -0.01110777e0;
|
|
double oneoverxl; /* 1/effective length */
|
|
double eta; /* eta from model after length factor */
|
|
double phibs; /* phi - vbs */
|
|
double sqphbs; /* square root of phibs */
|
|
double sqphis; /* square root of phi */
|
|
double wps;
|
|
double oneoverxj; /* 1/junction depth */
|
|
double xjonxl; /* junction depth/effective length */
|
|
double djonxj;
|
|
double wponxj;
|
|
double arga;
|
|
double argb;
|
|
double argc;
|
|
double gammas;
|
|
double fbodys;
|
|
double fbody;
|
|
double onfbdy;
|
|
double qbonco;
|
|
double vbix;
|
|
double wconxj;
|
|
double vth;
|
|
double csonco;
|
|
double cdonco;
|
|
double vgsx;
|
|
double onfg;
|
|
double fgate;
|
|
double us;
|
|
double xn;
|
|
double vdsc;
|
|
double onvdsc;
|
|
double vdsx;
|
|
double cdnorm;
|
|
double cdo;
|
|
double fdrain;
|
|
double gdsat;
|
|
double cdsat;
|
|
double emax;
|
|
double delxl;
|
|
double dlonxl;
|
|
double xlfact;
|
|
double ondvt;
|
|
double onxn;
|
|
double wfact;
|
|
double fshort;
|
|
double lvds, lvbs, lvbd;
|
|
Dderivs d_onxn, d_ondvt, d_wfact, d_MOS3gds;
|
|
Dderivs d_emax, d_delxl, d_dlonxl, d_xlfact;
|
|
Dderivs d_cdonco, d_fdrain, d_cdsat, d_gdsat;
|
|
Dderivs d_vdsx, d_cdo, d_cdnorm, d_Beta, d_dummy;
|
|
Dderivs d_vdsc, d_onvdsc, d_arga, d_argb;
|
|
Dderivs d_onfg, d_fgate, d_us, d_vgsx;
|
|
Dderivs d_von, d_xn, d_vth, d_onfbdy, d_qbonco, d_vbix;
|
|
Dderivs d_argc, d_fshort, d_gammas, d_fbodys, d_fbody;
|
|
Dderivs d_wps, d_wconxj, d_wponxj;
|
|
Dderivs d_phibs, d_sqphbs;
|
|
Dderivs d_p, d_q, d_r, d_zero, d_vdsat;
|
|
|
|
/*
|
|
* bypasses the computation of charges
|
|
*/
|
|
if (here->MOS3mode == 1) {
|
|
lvgs = vgs;
|
|
lvds = vds;
|
|
lvbs = vbs;
|
|
lvbd = vbd;
|
|
} else {
|
|
lvgs = vgd;
|
|
lvds = -vds;
|
|
lvbs = vbd;
|
|
lvbd = vbs;
|
|
}
|
|
|
|
/*
|
|
* reference cdrain equations to source and
|
|
* charge equations to bulk
|
|
*/
|
|
d_p.value = 0.0;
|
|
d_p.d1_p = 1.0;
|
|
d_p.d1_q = 0.0;
|
|
d_p.d1_r = 0.0;
|
|
d_p.d2_p2 = 0.0;
|
|
d_p.d2_q2 = 0.0;
|
|
d_p.d2_r2 = 0.0;
|
|
d_p.d2_pq = 0.0;
|
|
d_p.d2_qr = 0.0;
|
|
d_p.d2_pr = 0.0;
|
|
d_p.d3_p3 = 0.0;
|
|
d_p.d3_q3 = 0.0;
|
|
d_p.d3_r3 = 0.0;
|
|
d_p.d3_p2r = 0.0;
|
|
d_p.d3_p2q = 0.0;
|
|
d_p.d3_q2r = 0.0;
|
|
d_p.d3_pq2 = 0.0;
|
|
d_p.d3_pr2 = 0.0;
|
|
d_p.d3_qr2 = 0.0;
|
|
d_p.d3_pqr = 0.0;
|
|
EqualDeriv(&d_q,&d_p);
|
|
EqualDeriv(&d_r,&d_p);
|
|
EqualDeriv(&d_zero,&d_p);
|
|
d_q.d1_p = d_r.d1_p = d_zero.d1_p = 0.0;
|
|
d_q.d1_q = d_r.d1_r = 1.0;
|
|
vdsat = 0.0;
|
|
EqualDeriv(&d_vdsat,&d_zero);
|
|
oneoverxl = 1.0/EffectiveLength;/*const*/
|
|
eta = model->MOS3eta * 8.15e-22/(model->MOS3oxideCapFactor*
|
|
EffectiveLength*EffectiveLength*EffectiveLength);/*const*/
|
|
/*
|
|
*.....square root term
|
|
*/
|
|
if ( lvbs <= 0.0 ) {
|
|
phibs = here->MOS3tPhi-lvbs;
|
|
EqualDeriv(&d_phibs,&d_q);
|
|
d_phibs.value = lvbs;
|
|
TimesDeriv(&d_phibs,&d_phibs,-1.0);
|
|
d_phibs.value += here->MOS3tPhi;
|
|
sqphbs = sqrt(phibs);
|
|
SqrtDeriv(&d_sqphbs,&d_phibs);
|
|
} else {
|
|
sqphis = sqrt(here->MOS3tPhi);/*const*/
|
|
/*sqphs3 = here->MOS3tPhi*sqphis;const*/
|
|
sqphbs = sqphis/(1.0+lvbs/
|
|
(here->MOS3tPhi+here->MOS3tPhi));
|
|
EqualDeriv(&d_sqphbs,&d_q); d_sqphbs.value = lvbs;
|
|
TimesDeriv(&d_sqphbs,&d_sqphbs,1/(here->MOS3tPhi+here->MOS3tPhi));
|
|
d_sqphbs.value += 1.0;
|
|
InvDeriv(&d_sqphbs,&d_sqphbs);
|
|
TimesDeriv(&d_sqphbs,&d_sqphbs,sqphis);
|
|
phibs = sqphbs*sqphbs;
|
|
MultDeriv(&d_phibs,&d_sqphbs,&d_sqphbs);
|
|
}
|
|
/*
|
|
*.....short channel effect factor
|
|
*/
|
|
if ( (model->MOS3junctionDepth != 0.0) &&
|
|
(model->MOS3coeffDepLayWidth != 0.0) ) {
|
|
wps = model->MOS3coeffDepLayWidth*sqphbs;
|
|
TimesDeriv(&d_wps,&d_sqphbs,model->MOS3coeffDepLayWidth);
|
|
oneoverxj = 1.0/model->MOS3junctionDepth;/*const*/
|
|
xjonxl = model->MOS3junctionDepth*oneoverxl;/*const*/
|
|
djonxj = model->MOS3latDiff*oneoverxj;/*const*/
|
|
wponxj = wps*oneoverxj;
|
|
TimesDeriv(&d_wponxj,&d_wps,oneoverxj);
|
|
wconxj = coeff0+coeff1*wponxj+coeff2*wponxj*wponxj;
|
|
TimesDeriv(&d_wconxj,&d_wponxj,coeff2);
|
|
d_wconxj.value += coeff1;
|
|
MultDeriv(&d_wconxj,&d_wconxj,&d_wponxj);
|
|
d_wconxj.value += coeff0;
|
|
arga = wconxj + djonxj;
|
|
EqualDeriv(&d_arga,&d_wconxj); d_arga.value += djonxj;
|
|
argc = wponxj/(1.0+wponxj);
|
|
EqualDeriv(&d_argc,&d_wponxj);
|
|
d_argc.value += 1.0;
|
|
InvDeriv(&d_argc,&d_argc);
|
|
MultDeriv(&d_argc,&d_argc,&d_wponxj);
|
|
argb = sqrt(1.0-argc*argc);
|
|
MultDeriv(&d_argb,&d_argc,&d_argc);
|
|
TimesDeriv(&d_argb,&d_argb,-1.0);
|
|
d_argb.value += 1.0;
|
|
SqrtDeriv(&d_argb,&d_argb);
|
|
|
|
fshort = 1.0-xjonxl*(arga*argb-djonxj);
|
|
MultDeriv(&d_fshort,&d_arga,&d_argb);
|
|
d_fshort.value -= djonxj;
|
|
TimesDeriv(&d_fshort,&d_fshort,-xjonxl);
|
|
d_fshort.value += 1.0;
|
|
} else {
|
|
fshort = 1.0;
|
|
EqualDeriv(&d_fshort,&d_zero);
|
|
d_fshort.value = 1.0;
|
|
|
|
}
|
|
/*
|
|
*.....body effect
|
|
*/
|
|
gammas = model->MOS3gamma*fshort;
|
|
TimesDeriv(&d_gammas,&d_fshort,model->MOS3gamma);
|
|
fbodys = 0.5*gammas/(sqphbs+sqphbs);
|
|
DivDeriv(&d_fbodys,&d_gammas,&d_sqphbs);
|
|
TimesDeriv(&d_fbodys,&d_fbodys,0.25);
|
|
fbody = fbodys+model->MOS3narrowFactor/here->MOS3w;
|
|
EqualDeriv(&d_fbody,&d_fbodys);
|
|
d_fbody.value += fbody - fbodys;
|
|
|
|
onfbdy = 1.0/(1.0+fbody);
|
|
EqualDeriv(&d_onfbdy,&d_fbody);
|
|
d_onfbdy.value += 1.0;
|
|
InvDeriv(&d_onfbdy,&d_onfbdy);
|
|
qbonco =gammas*sqphbs+model->MOS3narrowFactor*phibs/here->MOS3w;
|
|
EqualDeriv(&d_dummy,&d_phibs);
|
|
TimesDeriv(&d_dummy,&d_dummy,model->MOS3narrowFactor*here->MOS3w);
|
|
MultDeriv(&d_qbonco,&d_gammas,&d_sqphbs);
|
|
PlusDeriv(&d_qbonco,&d_qbonco,&d_dummy);
|
|
/*
|
|
*.....static feedback effect
|
|
*/
|
|
vbix = here->MOS3tVbi*model->MOS3type-eta*(lvds);
|
|
EqualDeriv(&d_vbix,&d_r); d_vbix.value = vbix;
|
|
d_vbix.d1_r = -eta;
|
|
/*
|
|
*.....threshold voltage
|
|
*/
|
|
vth = vbix+qbonco;
|
|
PlusDeriv(&d_vth,&d_vbix,&d_qbonco);
|
|
/*
|
|
*.....joint weak inversion and strong inversion
|
|
*/
|
|
von = vth;
|
|
EqualDeriv(&d_von,&d_vth);
|
|
if ( model->MOS3fastSurfaceStateDensity != 0.0 ) {
|
|
csonco = CHARGE*model->MOS3fastSurfaceStateDensity *
|
|
1e4 /*(cm**2/m**2)*/ *EffectiveLength*here->MOS3w/OxideCap;/*const*/
|
|
cdonco = 0.5*qbonco/phibs;
|
|
DivDeriv(&d_cdonco,&d_qbonco,&d_phibs);
|
|
TimesDeriv(&d_cdonco,&d_cdonco,0.5);
|
|
xn = 1.0+csonco+cdonco;
|
|
EqualDeriv(&d_xn,&d_cdonco);
|
|
d_xn.value += 1.0 + csonco;
|
|
von = vth+vt*xn;
|
|
TimesDeriv(&d_von,&d_xn,vt);
|
|
PlusDeriv(&d_von,&d_von,&d_vth);
|
|
|
|
|
|
} else {
|
|
/*
|
|
*.....cutoff region
|
|
*/
|
|
if ( lvgs <= von ) {
|
|
cdrain = 0.0;
|
|
EqualDeriv(&d_cdrain,&d_zero);
|
|
goto innerline1000;
|
|
}
|
|
}
|
|
/*
|
|
*.....device is on
|
|
*/
|
|
vgsx = MAX(lvgs,von);
|
|
if (lvgs >= von) {
|
|
EqualDeriv(&d_vgsx,&d_p);
|
|
d_vgsx.value = lvgs;
|
|
} else {
|
|
EqualDeriv(&d_vgsx,&d_von);
|
|
}
|
|
/*
|
|
*.....mobility modulation by gate voltage
|
|
*/
|
|
onfg = 1.0+model->MOS3theta*(vgsx-vth);
|
|
TimesDeriv(&d_onfg,&d_vth,-1.0);
|
|
PlusDeriv(&d_onfg,&d_onfg,&d_vgsx);
|
|
TimesDeriv(&d_onfg,&d_onfg,model->MOS3theta);
|
|
d_onfg.value += 1.0;
|
|
fgate = 1.0/onfg;
|
|
InvDeriv(&d_fgate,&d_onfg);
|
|
us = here->MOS3tSurfMob * 1e-4 /*(m**2/cm**2)*/ *fgate;
|
|
TimesDeriv(&d_us,&d_fgate,here->MOS3tSurfMob * 1e-4);
|
|
/*
|
|
*.....saturation voltage
|
|
*/
|
|
vdsat = (vgsx-vth)*onfbdy;
|
|
TimesDeriv(&d_vdsat,&d_vth, -1.0);
|
|
PlusDeriv(&d_vdsat,&d_vdsat,&d_vgsx);
|
|
MultDeriv(&d_vdsat,&d_vdsat,&d_onfbdy);
|
|
if ( model->MOS3maxDriftVel <= 0.0 ) {
|
|
} else {
|
|
vdsc = EffectiveLength*model->MOS3maxDriftVel/us;
|
|
InvDeriv(&d_vdsc,&d_us);
|
|
TimesDeriv(&d_vdsc,&d_vdsc,EffectiveLength*model->MOS3maxDriftVel);
|
|
onvdsc = 1.0/vdsc;
|
|
InvDeriv(&d_onvdsc,&d_vdsc);
|
|
arga = (vgsx-vth)*onfbdy;
|
|
/* note arga = vdsat at this point */
|
|
EqualDeriv(&d_arga,&d_vdsat);
|
|
argb = sqrt(arga*arga+vdsc*vdsc);
|
|
MultDeriv(&d_dummy,&d_arga,&d_arga);
|
|
MultDeriv(&d_argb,&d_vdsc,&d_vdsc);
|
|
PlusDeriv(&d_argb,&d_argb,&d_dummy);
|
|
SqrtDeriv(&d_argb,&d_argb);
|
|
vdsat = arga+vdsc-argb;
|
|
TimesDeriv(&d_vdsat,&d_argb,-1.0);
|
|
PlusDeriv(&d_vdsat,&d_vdsat,&d_vdsc);
|
|
PlusDeriv(&d_vdsat,&d_vdsat,&d_arga);
|
|
}
|
|
/*
|
|
*.....current factors in linear region
|
|
*/
|
|
vdsx = MIN((lvds),vdsat);
|
|
if (lvds < vdsat) {
|
|
EqualDeriv(&d_vdsx,&d_r);
|
|
d_vdsx.value = lvds;
|
|
} else {
|
|
EqualDeriv(&d_vdsx,&d_vdsat);
|
|
}
|
|
|
|
if ( vdsx == 0.0 ) goto line900;
|
|
cdo = vgsx-vth-0.5*(1.0+fbody)*vdsx;
|
|
EqualDeriv(&d_cdo,&d_fbody);
|
|
d_cdo.value += 1.0;
|
|
MultDeriv(&d_cdo,&d_cdo,&d_vdsx);
|
|
TimesDeriv(&d_cdo,&d_cdo,0.5);
|
|
PlusDeriv(&d_cdo,&d_cdo,&d_vth);
|
|
TimesDeriv(&d_cdo,&d_cdo,-1.0);
|
|
PlusDeriv(&d_cdo,&d_cdo,&d_vgsx);
|
|
|
|
|
|
/*
|
|
*.....normalized drain current
|
|
*/
|
|
cdnorm = cdo*vdsx;
|
|
MultDeriv(&d_cdnorm,&d_cdo,&d_vdsx);
|
|
/*
|
|
*.....drain current without velocity saturation effect
|
|
*/
|
|
/* Beta is a constant till now */
|
|
Beta = Beta*fgate;
|
|
TimesDeriv(&d_Beta,&d_fgate,Beta);
|
|
cdrain = Beta*cdnorm;
|
|
MultDeriv(&d_cdrain,&d_Beta,&d_cdnorm);
|
|
/*
|
|
*.....velocity saturation factor
|
|
*/
|
|
if ( model->MOS3maxDriftVel != 0.0 ) {
|
|
fdrain = 1.0/(1.0+vdsx*onvdsc);
|
|
MultDeriv(&d_fdrain,&d_vdsx,&d_onvdsc);
|
|
d_fdrain.value += 1.0;
|
|
InvDeriv(&d_fdrain,&d_fdrain);
|
|
/*
|
|
*.....drain current
|
|
*/
|
|
cdrain = fdrain*cdrain;
|
|
MultDeriv(&d_cdrain,&d_cdrain,&d_fdrain);
|
|
Beta = Beta*fdrain;
|
|
MultDeriv(&d_Beta,&d_Beta,&d_fdrain);
|
|
|
|
}
|
|
/*
|
|
*.....channel length modulation
|
|
*/
|
|
if ( (lvds) <= vdsat ) goto line700;
|
|
if ( model->MOS3maxDriftVel == 0.0 ) goto line510;
|
|
if (model->MOS3alpha == 0.0) goto line700;
|
|
cdsat = cdrain;
|
|
EqualDeriv(&d_cdsat,&d_cdrain);
|
|
gdsat = cdsat*(1.0-fdrain)*onvdsc;
|
|
TimesDeriv(&d_dummy,&d_fdrain,-1.0);
|
|
d_dummy.value += 1.0;
|
|
MultDeriv(&d_gdsat,&d_cdsat,&d_dummy);
|
|
MultDeriv(&d_gdsat,&d_gdsat,&d_onvdsc);
|
|
gdsat = MAX(1.0e-12,gdsat);
|
|
if (gdsat == 1.0e-12) {
|
|
EqualDeriv(&d_gdsat,&d_zero);
|
|
d_gdsat.value = gdsat;
|
|
}
|
|
|
|
emax = cdsat*oneoverxl/gdsat;
|
|
DivDeriv(&d_emax,&d_cdsat,&d_gdsat);
|
|
TimesDeriv(&d_emax,&d_emax,oneoverxl);
|
|
|
|
|
|
arga = 0.5*emax*model->MOS3alpha;
|
|
TimesDeriv(&d_arga,&d_emax,0.5*model->MOS3alpha);
|
|
argc = model->MOS3kappa*model->MOS3alpha;/*const*/
|
|
argb = sqrt(arga*arga+argc*((lvds)-vdsat));
|
|
TimesDeriv(&d_dummy,&d_vdsat,-1.0);
|
|
d_dummy.value += lvds;
|
|
d_dummy.d1_r += 1.0;
|
|
TimesDeriv(&d_argb,&d_dummy,argc);
|
|
MultDeriv(&d_dummy,&d_arga,&d_arga);
|
|
PlusDeriv(&d_argb,&d_argb,&d_dummy);
|
|
SqrtDeriv(&d_argb,&d_argb);
|
|
delxl = argb-arga;
|
|
TimesDeriv(&d_delxl,&d_arga,-1.0);
|
|
PlusDeriv(&d_delxl,&d_argb,&d_delxl);
|
|
goto line520;
|
|
line510:
|
|
delxl = sqrt(model->MOS3kappa*((lvds)-vdsat)*model->MOS3alpha);
|
|
TimesDeriv(&d_delxl,&d_vdsat,-1.0);
|
|
d_delxl.value += lvds;
|
|
d_delxl.d1_r += 1.0;
|
|
TimesDeriv(&d_delxl,&d_delxl,model->MOS3kappa*model->MOS3alpha);
|
|
SqrtDeriv(&d_delxl,&d_delxl);
|
|
|
|
/*
|
|
*.....punch through approximation
|
|
*/
|
|
line520:
|
|
if ( delxl > (0.5*EffectiveLength) ) {
|
|
delxl = EffectiveLength - (EffectiveLength*EffectiveLength/
|
|
delxl*0.25);
|
|
InvDeriv(&d_delxl,&d_delxl);
|
|
TimesDeriv(&d_delxl,&d_delxl,-EffectiveLength*EffectiveLength*0.25);
|
|
d_delxl.value += EffectiveLength;
|
|
}
|
|
/*
|
|
*.....saturation region
|
|
*/
|
|
dlonxl = delxl*oneoverxl;
|
|
TimesDeriv(&d_dlonxl,&d_delxl,oneoverxl);
|
|
xlfact = 1.0/(1.0-dlonxl);
|
|
TimesDeriv(&d_xlfact,&d_dlonxl,-1.0);
|
|
d_xlfact.value += 1.0;
|
|
InvDeriv(&d_xlfact,&d_xlfact);
|
|
|
|
cdrain = cdrain*xlfact;
|
|
MultDeriv(&d_cdrain,&d_cdrain,&d_xlfact);
|
|
/*
|
|
*.....finish strong inversion case
|
|
*/
|
|
line700:
|
|
if ( lvgs < von ) {
|
|
/*
|
|
*.....weak inversion
|
|
*/
|
|
onxn = 1.0/xn;
|
|
InvDeriv(&d_onxn,&d_xn);
|
|
ondvt = onxn/vt;
|
|
TimesDeriv(&d_ondvt,&d_onxn,1/vt);
|
|
wfact = exp( (lvgs-von)*ondvt );
|
|
TimesDeriv(&d_wfact,&d_von,-1.0);
|
|
d_wfact.value += lvgs;
|
|
d_wfact.d1_p += 1.0;
|
|
MultDeriv(&d_wfact,&d_wfact,&d_ondvt);
|
|
ExpDeriv(&d_wfact,&d_wfact);
|
|
cdrain = cdrain*wfact;
|
|
MultDeriv(&d_cdrain,&d_cdrain,&d_wfact);
|
|
}
|
|
/*
|
|
*.....charge computation
|
|
*/
|
|
goto innerline1000;
|
|
/*
|
|
*.....special case of vds = 0.0d0
|
|
*/
|
|
|
|
line900:
|
|
Beta = Beta*fgate;
|
|
/* Beta is still a constant */
|
|
TimesDeriv(&d_Beta,&d_fgate,Beta);
|
|
cdrain = 0.0;
|
|
EqualDeriv(&d_cdrain,&d_zero);
|
|
here->MOS3gds = Beta*(vgsx-vth);
|
|
TimesDeriv(&d_MOS3gds,&d_vth,-1.0);
|
|
PlusDeriv(&d_MOS3gds,&d_MOS3gds,&d_vgsx);
|
|
MultDeriv(&d_MOS3gds,&d_MOS3gds,&d_Beta);
|
|
if ( (model->MOS3fastSurfaceStateDensity != 0.0) &&
|
|
(lvgs < von) ) {
|
|
here->MOS3gds *=exp((lvgs-von)/(vt*xn));
|
|
TimesDeriv(&d_dummy,&d_von,-1.0);
|
|
d_dummy.value += lvgs;
|
|
d_dummy.d1_p += 1.0;
|
|
DivDeriv(&d_dummy,&d_dummy,&d_xn);
|
|
TimesDeriv(&d_dummy,&d_dummy,1/vt);
|
|
ExpDeriv(&d_dummy,&d_dummy);
|
|
MultDeriv(&d_MOS3gds,&d_MOS3gds,&d_dummy);
|
|
}
|
|
d_cdrain.d1_r = d_MOS3gds.value;
|
|
d_cdrain.d2_r2 = d_MOS3gds.d1_r;
|
|
d_cdrain.d3_r3 = d_MOS3gds.d2_r2;
|
|
|
|
|
|
|
|
innerline1000:;
|
|
/*
|
|
*.....done
|
|
*/
|
|
}
|
|
|
|
|
|
/*
|
|
* COMPUTE EQUIVALENT DRAIN CURRENT SOURCE
|
|
*/
|
|
/*
|
|
* now we do the hard part of the bulk-drain and bulk-source
|
|
* diode - we evaluate the non-linear capacitance and
|
|
* charge
|
|
*
|
|
* the basic equations are not hard, but the implementation
|
|
* is somewhat long in an attempt to avoid log/exponential
|
|
* evaluations
|
|
*/
|
|
/*
|
|
* charge storage elements
|
|
*
|
|
*.. bulk-drain and bulk-source depletion capacitances
|
|
*/
|
|
if (vbs < here->MOS3tDepCap){
|
|
arg=1-vbs/here->MOS3tBulkPot;
|
|
/*
|
|
* the following block looks somewhat long and messy,
|
|
* but since most users use the default grading
|
|
* coefficients of .5, and sqrt is MUCH faster than an
|
|
* exp(log()) we use this special case code to buy time.
|
|
* (as much as 10% of total job time!)
|
|
*/
|
|
#ifndef NOSQRT
|
|
if(model->MOS3bulkJctBotGradingCoeff ==
|
|
model->MOS3bulkJctSideGradingCoeff) {
|
|
if(model->MOS3bulkJctBotGradingCoeff == .5) {
|
|
sarg = sargsw = 1/sqrt(arg);
|
|
} else {
|
|
sarg = sargsw =
|
|
exp(-model->MOS3bulkJctBotGradingCoeff*
|
|
log(arg));
|
|
}
|
|
} else {
|
|
if(model->MOS3bulkJctBotGradingCoeff == .5) {
|
|
sarg = 1/sqrt(arg);
|
|
} else {
|
|
#endif /*NOSQRT*/
|
|
sarg = exp(-model->MOS3bulkJctBotGradingCoeff*
|
|
log(arg));
|
|
#ifndef NOSQRT
|
|
}
|
|
if(model->MOS3bulkJctSideGradingCoeff == .5) {
|
|
sargsw = 1/sqrt(arg);
|
|
} else {
|
|
#endif /*NOSQRT*/
|
|
sargsw =exp(-model->MOS3bulkJctSideGradingCoeff*
|
|
log(arg));
|
|
#ifndef NOSQRT
|
|
}
|
|
}
|
|
#endif /*NOSQRT*/
|
|
lcapbs=here->MOS3Cbs*sarg+
|
|
here->MOS3Cbssw*sargsw;
|
|
lcapbs2 = model->MOS3type*0.5/here->MOS3tBulkPot*(
|
|
here->MOS3Cbs*model->MOS3bulkJctBotGradingCoeff*
|
|
sarg/arg + here->MOS3Cbssw*
|
|
model->MOS3bulkJctSideGradingCoeff*sargsw/arg);
|
|
lcapbs3 = here->MOS3Cbs*sarg*
|
|
model->MOS3bulkJctBotGradingCoeff*
|
|
(model->MOS3bulkJctBotGradingCoeff+1);
|
|
lcapbs3 += here->MOS3Cbssw*sargsw*
|
|
model->MOS3bulkJctSideGradingCoeff*
|
|
(model->MOS3bulkJctSideGradingCoeff+1);
|
|
lcapbs3 = lcapbs3/(6*here->MOS3tBulkPot*
|
|
here->MOS3tBulkPot*arg*arg);
|
|
} else {
|
|
/* *(ckt->CKTstate0 + here->MOS3qbs)= here->MOS3f4s +
|
|
vbs*(here->MOS3f2s+vbs*(here->MOS3f3s/2));*/
|
|
lcapbs=here->MOS3f2s+here->MOS3f3s*vbs;
|
|
lcapbs2 = 0.5*here->MOS3f3s;
|
|
lcapbs3 = 0;
|
|
}
|
|
if (vbd < here->MOS3tDepCap) {
|
|
arg=1-vbd/here->MOS3tBulkPot;
|
|
/*
|
|
* the following block looks somewhat long and messy,
|
|
* but since most users use the default grading
|
|
* coefficients of .5, and sqrt is MUCH faster than an
|
|
* exp(log()) we use this special case code to buy time.
|
|
* (as much as 10% of total job time!)
|
|
*/
|
|
#ifndef NOSQRT
|
|
if(model->MOS3bulkJctBotGradingCoeff == .5 &&
|
|
model->MOS3bulkJctSideGradingCoeff == .5) {
|
|
sarg = sargsw = 1/sqrt(arg);
|
|
} else {
|
|
if(model->MOS3bulkJctBotGradingCoeff == .5) {
|
|
sarg = 1/sqrt(arg);
|
|
} else {
|
|
#endif /*NOSQRT*/
|
|
sarg = exp(-model->MOS3bulkJctBotGradingCoeff*
|
|
log(arg));
|
|
#ifndef NOSQRT
|
|
}
|
|
if(model->MOS3bulkJctSideGradingCoeff == .5) {
|
|
sargsw = 1/sqrt(arg);
|
|
} else {
|
|
#endif /*NOSQRT*/
|
|
sargsw =exp(-model->MOS3bulkJctSideGradingCoeff*
|
|
log(arg));
|
|
#ifndef NOSQRT
|
|
}
|
|
}
|
|
#endif /*NOSQRT*/
|
|
lcapbd=here->MOS3Cbd*sarg+
|
|
here->MOS3Cbdsw*sargsw;
|
|
lcapbd2 = model->MOS3type*0.5/here->MOS3tBulkPot*(
|
|
here->MOS3Cbd*model->MOS3bulkJctBotGradingCoeff*
|
|
sarg/arg + here->MOS3Cbdsw*
|
|
model->MOS3bulkJctSideGradingCoeff*sargsw/arg);
|
|
lcapbd3 = here->MOS3Cbd*sarg*
|
|
model->MOS3bulkJctBotGradingCoeff*
|
|
(model->MOS3bulkJctBotGradingCoeff+1);
|
|
lcapbd3 += here->MOS3Cbdsw*sargsw*
|
|
model->MOS3bulkJctSideGradingCoeff*
|
|
(model->MOS3bulkJctSideGradingCoeff+1);
|
|
lcapbd3 = lcapbd3/(6*here->MOS3tBulkPot*
|
|
here->MOS3tBulkPot*arg*arg);
|
|
} else {
|
|
lcapbd=here->MOS3f2d + vbd * here->MOS3f3d;
|
|
lcapbd2=0.5*here->MOS3f3d;
|
|
lcapbd3=0;
|
|
}
|
|
/*
|
|
* meyer's capacitor model
|
|
*/
|
|
/*
|
|
* the meyer capacitance equations are in DEVqmeyer
|
|
* these expressions are derived from those equations.
|
|
* these expressions are incorrect; they assume just one
|
|
* controlling variable for each charge storage element
|
|
* while actually there are several; the MOS3 small
|
|
* signal ac linear model is also wrong because it
|
|
* ignores controlled capacitive elements. these can be
|
|
* corrected (as can the linear ss ac model) if the
|
|
* expressions for the charge are available
|
|
*/
|
|
|
|
|
|
{
|
|
|
|
|
|
double phi;
|
|
double cox;
|
|
double vddif;
|
|
double vddif1;
|
|
double vddif2;
|
|
double vgst;
|
|
/* von, lvgs and vdsat have already been adjusted for
|
|
possible source-drain interchange */
|
|
|
|
|
|
|
|
vgst = lvgs -von;
|
|
phi = here->MOS3tPhi;
|
|
cox = OxideCap;
|
|
if (vgst <= -phi) {
|
|
lcapgb2=lcapgb3=lcapgs2=lcapgs3=lcapgd2=lcapgd3=0;
|
|
} else if (vgst <= -phi/2) {
|
|
lcapgb2= -cox/(4*phi);
|
|
lcapgb3=lcapgs2=lcapgs3=lcapgd2=lcapgd3=0;
|
|
} else if (vgst <= 0) {
|
|
lcapgb2= -cox/(4*phi);
|
|
lcapgb3=lcapgs3=lcapgd2=lcapgd3=0;
|
|
lcapgs2 = cox/(3*phi);
|
|
} else { /* the MOS3modes are around because
|
|
vds has not been adjusted */
|
|
if (vdsat <= here->MOS3mode*vds) {
|
|
lcapgb2=lcapgb3=lcapgs2=lcapgs3=lcapgd2=lcapgd3=0;
|
|
} else {
|
|
vddif = 2.0*vdsat-here->MOS3mode*vds;
|
|
vddif1 = vdsat-here->MOS3mode*vds/*-1.0e-12*/;
|
|
vddif2 = vddif*vddif;
|
|
lcapgd2 = -vdsat*here->MOS3mode*vds*cox/(3*vddif*vddif2);
|
|
lcapgd3 = - here->MOS3mode*vds*cox*(vddif - 6*vdsat)/(9*vddif2*vddif2);
|
|
lcapgs2 = -vddif1*here->MOS3mode*vds*cox/(3*vddif*vddif2);
|
|
lcapgs3 = - here->MOS3mode*vds*cox*(vddif - 6*vddif1)/(9*vddif2*vddif2);
|
|
lcapgb2=lcapgb3=0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* the b-s and b-d diodes need no processing ... */
|
|
here->capbs2 = lcapbs2;
|
|
here->capbs3 = lcapbs3;
|
|
here->capbd2 = lcapbd2;
|
|
here->capbd3 = lcapbd3;
|
|
here->gbs2 = lgbs2;
|
|
here->gbs3 = lgbs3;
|
|
here->gbd2 = lgbd2;
|
|
here->gbd3 = lgbd3;
|
|
here->capgb2 = model->MOS3type*lcapgb2;
|
|
here->capgb3 = lcapgb3;
|
|
/*
|
|
* process to get Taylor coefficients, taking into
|
|
* account type and mode.
|
|
*/
|
|
gm2 = d_cdrain.d2_p2;
|
|
gb2 = d_cdrain.d2_q2;
|
|
gds2 = d_cdrain.d2_r2;
|
|
gmb = d_cdrain.d2_pq;
|
|
gbds = d_cdrain.d2_qr;
|
|
gmds = d_cdrain.d2_pr;
|
|
gm3 = d_cdrain.d3_p3;
|
|
gb3 = d_cdrain.d3_q3;
|
|
gds3 = d_cdrain.d3_r3;
|
|
gm2ds = d_cdrain.d3_p2r;
|
|
gm2b = d_cdrain.d3_p2q;
|
|
gb2ds = d_cdrain.d3_q2r;
|
|
gmb2 = d_cdrain.d3_pq2;
|
|
gmds2 = d_cdrain.d3_pr2;
|
|
gbds2 = d_cdrain.d3_qr2;
|
|
gmbds = d_cdrain.d3_pqr;
|
|
|
|
if (here->MOS3mode == 1)
|
|
{
|
|
/* normal mode - no source-drain interchange */
|
|
|
|
here->cdr_x2 = gm2;
|
|
here->cdr_y2 = gb2;;
|
|
here->cdr_z2 = gds2;;
|
|
here->cdr_xy = gmb;
|
|
here->cdr_yz = gbds;
|
|
here->cdr_xz = gmds;
|
|
here->cdr_x3 = gm3;
|
|
here->cdr_y3 = gb3;
|
|
here->cdr_z3 = gds3;
|
|
here->cdr_x2z = gm2ds;
|
|
here->cdr_x2y = gm2b;
|
|
here->cdr_y2z = gb2ds;
|
|
here->cdr_xy2 = gmb2;
|
|
here->cdr_xz2 = gmds2;
|
|
here->cdr_yz2 = gbds2;
|
|
here->cdr_xyz = gmbds;
|
|
|
|
/* the gate caps have been divided and made into
|
|
Taylor coeffs., but not adjusted for type */
|
|
|
|
here->capgs2 = model->MOS3type*lcapgs2;
|
|
here->capgs3 = lcapgs3;
|
|
here->capgd2 = model->MOS3type*lcapgd2;
|
|
here->capgd3 = lcapgd3;
|
|
} else {
|
|
/*
|
|
* inverse mode - source and drain interchanged
|
|
*/
|
|
|
|
here->cdr_x2 = -gm2;
|
|
here->cdr_y2 = -gb2;
|
|
here->cdr_z2 = -(gm2 + gb2 + gds2 + 2*(gmb + gmds + gbds));
|
|
here->cdr_xy = -gmb;
|
|
here->cdr_yz = gmb + gb2 + gbds;
|
|
here->cdr_xz = gm2 + gmb + gmds;
|
|
here->cdr_x3 = -gm3;
|
|
here->cdr_y3 = -gb3;
|
|
here->cdr_z3 = gm3 + gb3 + gds3 +
|
|
3*(gm2b + gm2ds + gmb2 + gb2ds + gmds2 + gbds2) + 6*gmbds ;
|
|
here->cdr_x2z = gm3 + gm2b + gm2ds;
|
|
here->cdr_x2y = -gm2b;
|
|
here->cdr_y2z = gmb2 + gb3 + gb2ds;
|
|
here->cdr_xy2 = -gmb2;
|
|
here->cdr_xz2 = -(gm3 + 2*(gm2b + gm2ds + gmbds) +
|
|
gmb2 + gmds2);
|
|
here->cdr_yz2 = -(gb3 + 2*(gmb2 + gb2ds + gmbds) +
|
|
gm2b + gbds2);
|
|
here->cdr_xyz = gm2b + gmb2 + gmbds;
|
|
|
|
here->capgs2 = model->MOS3type*lcapgd2;
|
|
here->capgs3 = lcapgd3;
|
|
|
|
here->capgd2 = model->MOS3type*lcapgs2;
|
|
here->capgd3 = lcapgs3;
|
|
|
|
}
|
|
|
|
/* now to adjust for type and multiply by factors to convert to Taylor coeffs. */
|
|
|
|
here->cdr_x2 = 0.5*model->MOS3type*here->cdr_x2;
|
|
here->cdr_y2 = 0.5*model->MOS3type*here->cdr_y2;
|
|
here->cdr_z2 = 0.5*model->MOS3type*here->cdr_z2;
|
|
here->cdr_xy = model->MOS3type*here->cdr_xy;
|
|
here->cdr_yz = model->MOS3type*here->cdr_yz;
|
|
here->cdr_xz = model->MOS3type*here->cdr_xz;
|
|
here->cdr_x3 = here->cdr_x3/6.;
|
|
here->cdr_y3 = here->cdr_y3/6.;
|
|
here->cdr_z3 = here->cdr_z3/6.;
|
|
here->cdr_x2z = 0.5*here->cdr_x2z;
|
|
here->cdr_x2y = 0.5*here->cdr_x2y;
|
|
here->cdr_y2z = 0.5*here->cdr_y2z;
|
|
here->cdr_xy2 = 0.5*here->cdr_xy2;
|
|
here->cdr_xz2 = 0.5*here->cdr_xz2;
|
|
here->cdr_yz2 = 0.5*here->cdr_yz2;
|
|
|
|
|
|
}
|
|
}
|
|
return(OK);
|
|
}
|