/* $Id: d_mos3.model,v 26.133 2009年11月26日 04:58:04 al Exp $ -*- C++ -*- * Copyright (C) 2001 Albert Davis * Author: Albert Davis * * 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 3 equivalent */ h_headers { #include "d_mos123.h" } cc_headers { #include "l_denoise.h" } /*--------------------------------------------------------------------------*/ model BUILT_IN_MOS3 { level 3; public_keys { nmos3 polarity=pN; pmos3 polarity=pP; } dev_type BUILT_IN_MOS; inherit BUILT_IN_MOS123; independent { override { double mjsw "" default=.33; double tox "" default=1e-7; double cox "" final_default="P_EPS_OX/tox"; double vto "" final_default=0.0; double gamma "" final_default=0.0; double phi "" final_default=0.6; int mos_level "back-annotate for diode" name=DIODElevel print_test="mos_level != LEVEL" default=LEVEL; } raw_parameters { double kp "transconductance parameter" name=KP final_default=2e-5 print_test="!calc_kp" calc_print_test="calc_kp"; double nfs_cm "fast surface state density" name=NFS default=0.0; double vmax "max drift velocity of carriers" name=VMAx default=NA; double theta "mobility modulation" name=THEta default=0.0; double eta "static feedback" name=ETA default=0.0; double kappa "saturation field vector" name=KAPpa default=0.2; double delta "width effect on threshold voltage" name=DELta default=0.0; } calculated_parameters { double nfs "" calculate="nfs_cm*ICM2M2"; bool calc_kp "" default=false; double alpha "" calculate="((nsub != NA) ? (2. * P_EPS_SI) / (P_Q * nsub) : 0.)"; double xd "coeffDepLayWidth" calculate="sqrt(alpha)"; double cfsox "" calculate="P_Q * nfs / cox"; double delta3 "narrow factor" calculate="delta * M_PI_2 * P_EPS_SI / cox"; } code_pre { if (!has_good_value(tox)) { tox = 1e-7; } 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(((!_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) ? int(polarity) * (-.05 - (egap + int(polarity) * phi) / 2.) : -(tpg * egap + phi) / 2.; double vfb = phi_ms - int(polarity) * P_Q * nss / cox; vto = vfb + phi + gamma * sqrt(phi); calc_vto = true; } } } } temperature_dependent { calculated_parameters { double vt "" calculate="temp * P_K_Q"; double phi "" calculate="m->phi*tempratio + (-2*vt*(1.5*log(tempratio)+P_Q*(arg)))"; double sqrt_phi "" calculate="sqrt(phi)"; double beta "" calculate="(m->kp / tempratio4) * s->w_eff / s->l_eff"; double uo "" calculate="m->uo * tempratio4"; double vbi "" calculate="(fixzero( (m->vto - m->gamma * sqrt(m->phi) +.5*(m->egap-egap) + double(m->polarity)* .5 * (phi-m->phi)), m->phi))"; } code_pre { double temp = c->temp_k(); double tempratio = temp / m->tnom_k; // ratio double tempratio4 = tempratio * sqrt(tempratio); double kt = temp * P_K; double egap = 1.16 - (7.02e-4*temp*temp) / (temp+1108.); double arg = (m->egap*tempratio - egap) / (2*kt); } } /*-----------------------------------------------------------------------*/ tr_eval { #define short_channel (m->xj != NOT_INPUT && m->xj> 0.) #define do_subthreshold (m->nfs != 0.) #define use_vmax (m->vmax != NOT_INPUT) /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ trace0(d->long_label().c_str()); trace3("", d->vds, d->vgs, d->vbs); /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ d->reverse_if_needed(); /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* square root term */ double sarg, v_phi_s, dsarg_dvbs; { if (d->vbs <= 0.) { v_phi_s = t->phi - d->vbs; sarg = sqrt(v_phi_s); dsarg_dvbs = -.5 / sarg; d->sbfwd = false; trace3("sb-ok", sarg, v_phi_s, dsarg_dvbs); }else{ untested(); sarg = t->sqrt_phi / (d->vbs / (2 * t->phi) + 1.); v_phi_s = sarg * sarg; dsarg_dvbs = -v_phi_s / (2 * t->phi*t->sqrt_phi); d->sbfwd = true; trace3("***sb-reversed***", sarg, v_phi_s, dsarg_dvbs); } } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* short channel effect, body effect */ double fbody, dfbody_dvbs, qbonco, dqbdvb; { double fshort, dfs_dvbs; if (short_channel) { static const double D[3] = {.0631353, .8013292, -.01110777}; double wp = m->xd * sarg; double wp_xj = wp / m->xj; double wc_xj = D[0] + D[1] * wp_xj + D[2] * wp_xj * wp_xj; double ld_xj = m->ld / m->xj; double xj_le = m->xj / s->l_eff; double arga = wc_xj + ld_xj; double argc = wp_xj / (wp_xj + 1.); double argb = sqrt(1. - argc * argc); fshort = 1. - xj_le * (arga * argb - ld_xj); double dwp_dvbs = m->xd * dsarg_dvbs; double darga_dvbs = (D[1] + D[2] * (wp_xj + wp_xj)) * dwp_dvbs / m->xj; double dargb_dvbs = -argc * argc * (1. - argc) * dwp_dvbs / (argb*wp); dfs_dvbs = -xj_le * (darga_dvbs * argb + arga * dargb_dvbs); trace2("short-channel", fshort, dfs_dvbs); }else{ fshort = 1.; dfs_dvbs = 0.; trace2("not-short-channel", fshort, dfs_dvbs); } double gamma_fs = m->gamma * fshort; double fbodys = gamma_fs * .5 / (2 * sarg); double fnarrw = m->delta3 / s->w_eff; trace3("", gamma_fs, fbodys, fnarrw); fbody = fbodys + fnarrw; dfbody_dvbs = -fbodys * dsarg_dvbs / sarg + fbodys * dfs_dvbs / fshort; trace2("", fbody, dfbody_dvbs); qbonco = gamma_fs * sarg + fnarrw * v_phi_s; dqbdvb = gamma_fs * dsarg_dvbs + m->gamma * dfs_dvbs * sarg - fnarrw; trace2("", qbonco, dqbdvb); } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* threshold voltage */ double vth, dvth_dvds, dvth_dvbs; { double sigma = m->eta * 8.15e-22 / (m->cox * s->l_eff*s->l_eff*s->l_eff); double vbix = t->vbi - sigma * d->vds; vth = vbix + qbonco; dvth_dvds = -(sigma); dvth_dvbs = dqbdvb; trace3("", vth, dvth_dvds, dvth_dvbs); } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* joint weak inversion and strong inversion */ /* von */ double xn, vtxn, dxn_dvbs, dvon_dvds, dvon_dvbs; { if (do_subthreshold) { xn = 1. + m->cfsox + qbonco / (2 * v_phi_s); vtxn = t->vt * xn; dxn_dvbs = dqbdvb / (2*v_phi_s) - qbonco*dsarg_dvbs / (v_phi_s*sarg); trace3("do_sub", xn, vtxn, dxn_dvbs); d->von = vth + vtxn; dvon_dvds = dvth_dvds; dvon_dvbs = dvth_dvbs + t->vt * dxn_dvbs; d->vgst = d->vgs - d->von; trace4("", d->von, dvon_dvds, dvon_dvbs, d->vgst); d->subthreshold = (d->vgs < d->von); d->cutoff = false; }else{ xn = vtxn = dxn_dvbs = dvon_dvds = dvon_dvbs = 0.; d->von = vth; d->vgst = d->vgs - d->von; trace2("no_sub", vtxn, dxn_dvbs); trace4("", d->von, dvon_dvds, dvon_dvbs, d->vgst); d->subthreshold = false; d->cutoff = (d->vgs <= d->von); if (d->cutoff) { trace0("***** cut off *****"); d->vdsat = 0.; d->ids = 0.; d->gmf = d->gmr = 0.; d->gds = 0.; d->gmbf = d->gmbr = 0.; return; } } } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* device is on */ /* mobility modulation by gate voltage */ double vc, onfg, us, dfg_dvgs, dfg_dvds, dfg_dvbs, beta; { double vgsx = (d->subthreshold) ? d->von : d->vgs; vc = vgsx - vth; onfg = m->theta * vc + 1.; double fgate = 1. / onfg; trace3("", vc, onfg, fgate); us = t->uo * fgate; beta = t->beta * fgate; trace4("", t->beta, beta, t->uo, us); dfg_dvgs = -(m->theta) * fgate * fgate; dfg_dvds = -dfg_dvgs * dvth_dvds; dfg_dvbs = -dfg_dvgs * dvth_dvbs; trace3("", dfg_dvgs, dfg_dvds, dfg_dvbs); } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* saturation voltage */ /* vdsat, saturated */ double dvdsat_dvgs, dvdsat_dvds, dvdsat_dvbs; double onvdsc, vdsx; { double onfbdy = 1. / (fbody + 1.); double dvsdga = onfbdy; d->vdsat = vc * onfbdy; trace2("novm", d->vdsat, dvsdga); if (use_vmax) { double vdsc = s->l_eff * m->vmax / us; double argb = sqrt(d->vdsat * d->vdsat + vdsc * vdsc); d->vdsat += vdsc - argb; dvsdga *= (1. - d->vdsat / argb); trace2("vmax", d->vdsat, dvsdga); dvdsat_dvgs = dvsdga - (1. - vdsc / argb) * vdsc * dfg_dvgs * onfg; onvdsc = 1. / vdsc; }else{ dvdsat_dvgs = dvsdga; onvdsc = NOT_VALID; } d->saturated = (d->vds> d->vdsat); vdsx = (d->saturated) ? d->vdsat : d->vds; trace3("", d->vdsat, vdsx, onvdsc); dvdsat_dvds = -dvdsat_dvgs * dvth_dvds; dvdsat_dvbs = -dvdsat_dvgs * dvth_dvbs - d->vdsat * dfbody_dvbs * dvsdga; trace3("", dvdsat_dvgs, dvdsat_dvds, dvdsat_dvbs); } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* short cut exit if vds == 0 */ if (vdsx == 0.) { /*900*/ trace2("***** vdsx == 0 *****", d->vdsat, d->vds); d->ids = 0.; d->gmf = d->gmr = 0.; d->gds = beta * vc; d->gmbf = d->gmbr = 0.; if (d->subthreshold) { d->gds *= exp(d->vgst / vtxn); } return; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* normalized drain current */ { double cdo = vc - (fbody + 1.) * .5 * vdsx; double dcodvb = -dvth_dvbs - dfbody_dvbs * .5 * vdsx; trace3("", t->beta, cdo, dcodvb); trace4("", vc, fbody, dvth_dvds, vdsx); d->gmf = vdsx; d->gds = vc - (fbody + 1. + dvth_dvds) * vdsx; d->gmbf = dcodvb * vdsx; d->ids = cdo * vdsx; trace4("1", d->ids, d->gmf, d->gds, d->gmbf); } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* scale, but without velocity saturation effect */ { double cd1 = t->beta * d->ids; d->gmf *= beta; d->gmf += dfg_dvgs * cd1; d->gds *= beta; d->gds += dfg_dvds * cd1; d->gmbf *= beta; d->ids *= beta; trace4("2", d->ids, d->gmf, d->gds, d->gmbf); } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* velocity saturation factor */ double fdrain, dfd_dvgs, dfd_dvds, dfd_dvbs; if (use_vmax) { assert(onvdsc != NOT_VALID); fdrain = 1. / (vdsx * onvdsc + 1.); double fd2 = fdrain * fdrain; double arga = fd2 * vdsx * onvdsc * onfg; dfd_dvgs = -dfg_dvgs * arga; dfd_dvds = -dfg_dvds * arga - fd2 * onvdsc; dfd_dvbs = -dfg_dvbs * arga; trace4("", fdrain, dfd_dvgs, dfd_dvds, dfd_dvbs); d->gmf *= fdrain; d->gmf += dfd_dvgs * d->ids; d->gds *= fdrain; d->gds += dfd_dvds * d->ids; d->gmbf *= fdrain; d->gmbf += dfd_dvbs * d->ids; d->ids *= fdrain; beta *= fdrain; trace4("3", d->ids, d->gmf, d->gds, d->gmbf); }else{ fdrain = 0.; /* used only if use_vmax */ dfd_dvgs = 0.; dfd_dvds = 0.; dfd_dvbs = 0.; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* channel length modulation */ double gds0; if (d->saturated) { double d_l, dl_dvd; double ddl_dvgs, ddl_dvds, ddl_dvbs; if (m->alpha == 0.) { d_l = dl_dvd = ddl_dvgs = ddl_dvds = ddl_dvbs = 0.; }else if (use_vmax) { /* use_vmax && m->alpha != 0 */ double gdsat = d->ids * (1. - fdrain) * onvdsc; gdsat = std::max(1e-12,gdsat); double gdoncd = gdsat / d->ids; double gdonfd = gdsat / (1. - fdrain); double gdonfg = gdsat * onfg; double dgdvg = gdoncd * d->gmf - gdonfd * dfd_dvgs + gdonfg * dfg_dvgs; double dgdvd = gdoncd * d->gds - gdonfd * dfd_dvds + gdonfg * dfg_dvds; double dgdvb = gdoncd *d->gmbf - gdonfd * dfd_dvbs + gdonfg * dfg_dvbs; double emax = d->ids / (s->l_eff * gdsat); double emax_o_ids = emax / d->ids; double emax_o_gdsat = emax / gdsat; double demax_dvgs = emax_o_ids * d->gmf - emax_o_gdsat * dgdvg; double demax_dvds = emax_o_ids * d->gds - emax_o_gdsat * dgdvd; double demax_dvbs = emax_o_ids * d->gmbf - emax_o_gdsat * dgdvb; double arga = emax * .5 * m->alpha; double argc = m->kappa * m->alpha; double argb = sqrt(arga * arga + argc * (d->vds - d->vdsat)); d_l = argb - arga; dl_dvd = argc / (argb + argb); double dl_demax = (arga / argb - 1.) * .5 * m->alpha; ddl_dvgs = dl_demax * demax_dvgs; ddl_dvds = dl_demax * demax_dvds - dl_dvd; ddl_dvbs = dl_demax * demax_dvbs; }else{ d_l = sqrt(m->kappa * (d->vds - d->vdsat) * m->alpha); dl_dvd = d_l * .5 / (d->vds - d->vdsat); ddl_dvgs = 0.; ddl_dvds = -dl_dvd; ddl_dvbs = 0.; } if (d_l> s->l_eff * .5) { /* punch through approximation */ d->punchthru = true; d_l = s->l_eff - s->l_eff*s->l_eff / (d_l*4.); double arga = (s->l_eff-d_l)*(s->l_eff-d_l) * 4./(s->l_eff*s->l_eff); ddl_dvgs *= arga; ddl_dvds *= arga; ddl_dvbs *= arga; dl_dvd *= arga; }else{ d->punchthru = false; } if (m->alpha != 0) { double lfact = 1. / (1. - d_l / s->l_eff); d->ids *= lfact; double diddl = d->ids / (s->l_eff - d_l); d->gmf = d->gmf * lfact + diddl * ddl_dvgs; gds0 = d->gds * lfact + diddl * ddl_dvds; d->gmbf = d->gmbf * lfact + diddl * ddl_dvbs; d->gmf += gds0 * dvdsat_dvgs; d->gmbf += gds0 * dvdsat_dvbs; d->gds = gds0 * dvdsat_dvds + diddl * dl_dvd; }else{ gds0 = 0; } trace2("", d_l, dl_dvd); trace3("", ddl_dvgs, ddl_dvds, ddl_dvbs); trace3("4", d->ids, gds0, d_l); }else{ d->punchthru = false; gds0 = 0; /* not saturated */ } trace4("4", d->ids, d->gmf, d->gds, d->gmbf); /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* weak inversion -- subthreshold region */ if (d->subthreshold) { double wfact = exp(d->vgst / vtxn); d->ids *= wfact; double gms = d->gmf * wfact; double gmw = d->ids / vtxn; trace2("subth", gmw, gms); d->gmf = gmw; d->gmf += gds0 * dvdsat_dvgs * wfact; d->gds *= wfact; d->gds += (gms - gmw) * dvon_dvds; d->gmbf *= wfact; d->gmbf += (gms - gmw) * dvon_dvbs - gmw * d->vgst * dxn_dvbs / xn; trace4("5", 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.; } } } /*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/

AltStyle によって変換されたページ (->オリジナル) /