testing
[gnucap-felix.git] / src / d_mos2.model
blob196508ee5a1a91988593dc7696bbf24fe6e139fc
1 /* $Id: d_mos2.model,v 26.133 2009/11/26 04:58:04 al Exp $ -*- C++ -*-
2  * Copyright (C) 2001 Albert Davis
3  * Author: Albert Davis <aldavis@gnu.org>
4  *
5  * This file is part of "Gnucap", the Gnu Circuit Analysis Package
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3, or (at your option)
10  * any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20  * 02110-1301, USA.
21  *------------------------------------------------------------------
22  * mos model equations: spice level 2 equivalent
23  */
24 h_headers {
25 #include "d_mos123.h"
27 cc_headers {
28 #include "l_compar.h"
29 #include "l_denoise.h"
31 /*--------------------------------------------------------------------------*/
32 model BUILT_IN_MOS2 {
33   level 2;
34   public_keys {
35     nmos2 polarity=pN;
36     pmos2 polarity=pP;
37   }
38   dev_type BUILT_IN_MOS;
39   inherit BUILT_IN_MOS123;
40   independent {
41     override {
42       double mjsw "" default=.33;
43       double tox "" default=1e-7;
44       double cox "" final_default="P_EPS_OX/tox";
45       double vto "" final_default=0.0;
46       double gamma "" final_default=0.0;
47       double phi "" final_default=0.6;
48       int mos_level "back-annotate for diode" name=DIODElevel 
49         print_test="mos_level != LEVEL" default=LEVEL;
50     }
51     raw_parameters {
52       double kp "transconductance parameter"
53         name=KP final_default=2e-5 
54         print_test="!calc_kp" calc_print_test="calc_kp";
55       double nfs_cm "fast surface state density" 
56         name=NFS default=0.0;
57       double vmax "max drift velocity of carriers"
58         name=VMAx default=NA;
59       double neff "total channel charge coefficient"
60         name=NEFf default=1.0 positive 
61         print_test="neff != 1.0 || lambda == NA";
62       double ucrit_cm "critical field mobility degradation"
63         name=UCRit default=1e4
64         print_test="ucrit_cm != 1e4 || uexp != NA";
65       double uexp "critical field exponent in mob.deg."
66         name=UEXp default=NA;
67       double utra "transverse field coefficient (not used)"
68         name=UTRa default=NA print_test=false;
69       double delta "width effect on threshold voltage" 
70         name=DELta default=0.0;
71     }
72     calculated_parameters {
73       double nfs "" calculate="nfs_cm*ICM2M2";
74       double ucrit "" calculate="ucrit_cm*ICM2M";
75       bool calc_kp "" default=false;
76       double alpha "" calculate="((nsub != NA)
77         ? (2. * P_EPS_SI) / (P_Q * nsub)
78         : 0.)";
79       double xd "coeffDepLayWidth" calculate="sqrt(alpha)";
80       double xwb "" calculate="((nsub != NA)
81         ? xd * sqrt(pb)
82         : .25e-6)";
83       double vbp "" calculate="ucrit * P_EPS_SI / cox";
84       double cfsox "" calculate="P_Q * nfs / cox";
85     }
86     code_pre {      
87       if (!has_good_value(tox)) {
88         tox = 1e-7;
89       }
90       cox = P_EPS_OX / tox;
91       if (kp == NA) {
92         kp = uo * cox;
93         calc_kp = true;
94       }
95       if (nsub != NA) {
96         if (phi == NA) {
97           phi = (2. * P_K_Q) * tnom_k * log(nsub/NI);
98           if (phi < .1) {
99             untested();
100             error(((!_sim->is_first_expand()) ? (bDEBUG) : (bWARNING)),
101                   long_label() + ": calculated phi too small, using .1\n");
102             phi = .1;
103           }
104           calc_phi = true;
105         }
106         if (gamma == NA) {
107           gamma = sqrt(2. * P_EPS_SI * P_Q * nsub) / cox;
108           calc_gamma = true;
109         }
110         if (vto == NA) {
111           double phi_ms = (tpg == gtMETAL)
112             ? polarity * (-.05 - (egap + polarity * phi) / 2.)
113             : -(tpg * egap + phi) / 2.;
114           double vfb = phi_ms - polarity * P_Q * nss / cox;
115           vto = vfb + phi + gamma * sqrt(phi);
116           calc_vto = true;
117         }
118       }
119     }
120   }
121   size_dependent {
122     calculated_parameters {
123       double relxj "" calculate="((m->xj != NA && m->xj > 0)
124                ? .5 * m->xj / l_eff
125                : NA)";
126       double eta_1 "" calculate="((cgate != 0)
127                ? M_PI_4 * P_EPS_SI * m->delta / cgate * l_eff
128                : 0.)";
129       double eta "" calculate="eta_1 + 1.";
130       double eta_2 "" calculate="eta / 2.";
131     }
132   }
133   temperature_dependent {
134     calculated_parameters {
135       double vt "" calculate="temp * P_K_Q";
136       double phi "" calculate="m->phi*tempratio + (-2*vt*(1.5*log(tempratio)+P_Q*(arg)))";
137       double sqrt_phi "" calculate="sqrt(phi)";
138       double phi_sqrt_phi "" calculate="phi * sqrt_phi";
139       double beta "" calculate="(m->kp / tempratio4) * s->w_eff / s->l_eff";
140       double uo "" calculate="m->uo * tempratio4";
141       double vbi "" calculate="(fixzero(
142         (m->vto - m->gamma * sqrt(m->phi)
143          +.5*(m->egap-egap) + m->polarity* .5 * (phi-m->phi)), m->phi))";
144     }
145     code_pre {
146       double temp = d->_sim->_temp_c + P_CELSIUS0;
147       double tempratio  = temp / m->tnom_k; // ratio
148       double tempratio4 = tempratio * sqrt(tempratio);
149       double kt = temp * P_K;
150       double egap = 1.16 - (7.02e-4*temp*temp) / (temp+1108.);
151       double arg = (m->egap*tempratio - egap) / (2*kt);
152     }
153   }
154   /*-----------------------------------------------------------------------*/
155   tr_eval {
156     #define short_channel       (m->xj != NOT_INPUT  &&  m->xj > 0.)
157     #define do_subthreshold     (m->nfs != 0.)
158     /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 
159     trace0(d->long_label().c_str());
160     trace3("", d->vds, d->vgs, d->vbs);
161     assert(m->tnom_k > 0);
162     /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 
163     d->reverse_if_needed();
164     /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 
165     double v_phi_s = t->phi - d->vbs;
166     double sarg, dsarg_dvbs, d2sdb2, sarg3;
167     {
168       if (d->vbs <= 0.) {
169         sarg = sqrt(v_phi_s);
170         dsarg_dvbs = -.5 / sarg;
171         d2sdb2 = .5 * dsarg_dvbs / v_phi_s;
172         d->sbfwd = false;
173         trace3("sb-ok", sarg, v_phi_s, dsarg_dvbs);
174       }else{
175         if (OPT::mosflags & 01000) {
176           sarg = t->sqrt_phi / (1. + .5 * d->vbs / t->phi);
177           dsarg_dvbs = -.5 * sarg * sarg / t->phi_sqrt_phi;
178           d2sdb2 = -dsarg_dvbs * sarg / t->phi_sqrt_phi;
179           untested();
180           trace3("***sb-reversed(01000)***", sarg, v_phi_s, dsarg_dvbs);
181         }else{
182           sarg = t->sqrt_phi 
183             / (1. + .5 * d->vbs / t->phi 
184                + .375 * d->vbs * d->vbs / (t->phi * t->phi));
185           dsarg_dvbs = (-.5 * sarg * sarg / t->phi_sqrt_phi) 
186             * (1. + 1.5 * d->vbs / t->phi);
187           d2sdb2 = (-dsarg_dvbs * sarg / t->phi_sqrt_phi)
188             - (.75 * sarg / (t->phi_sqrt_phi * t->phi)) 
189             * (2. * d->vbs * dsarg_dvbs + sarg);
190           untested();
191           trace3("***sb-reversed(00000)***", sarg, v_phi_s, dsarg_dvbs);
192         }
193         d->sbfwd = true;
194       }
195       sarg3 = sarg*sarg*sarg;
196       assert(sarg > 0.);
197       assert(dsarg_dvbs < 0.);
198       assert(up_order(-1/t->phi, d2sdb2, 1/t->phi));
199       trace2("", d2sdb2, sarg3);
200     }
201     /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 
202     double barg, dbarg_dvbs, d2bdb2;
203     {
204       double vbd = d->vbs - d->vds;
205       double v_phi_d = t->phi - vbd;
206       if (vbd <= 0.) {
207         barg = sqrt(v_phi_d);
208         dbarg_dvbs = -.5 / barg;
209         d2bdb2 = .5 * dbarg_dvbs / v_phi_d;
210         //d->dbfwd = false;
211         trace4("db-ok", barg, v_phi_d, dbarg_dvbs, d2bdb2);
212       }else{
213         if (OPT::mosflags & 01000) {
214           barg = t->sqrt_phi / (1. + .5 * vbd / t->phi);
215           dbarg_dvbs = -.5 * barg * barg / t->phi_sqrt_phi;
216           d2bdb2 = -dbarg_dvbs * barg / t->phi_sqrt_phi;
217           untested();
218           trace4("***db-reversed(00000)***",barg, v_phi_d, dbarg_dvbs, d2bdb2);
219         }else{
220           barg = t->sqrt_phi 
221             / (1. + .5 * vbd / t->phi 
222                + .375 * vbd * vbd / (t->phi * t->phi));
223           dbarg_dvbs = (-.5 * barg * barg / t->phi_sqrt_phi) 
224             * (1. + 1.5 * vbd / t->phi);
225           d2bdb2 = (-dbarg_dvbs * barg / t->phi_sqrt_phi)
226             - (.75 * barg / (t->phi_sqrt_phi * t->phi)) 
227             * (2. * vbd * dbarg_dvbs + barg);
228           trace4("***db-reversed(00000)***",barg, v_phi_d, dbarg_dvbs, d2bdb2);
229         }
230         //d->dbfwd = true;
231       }
232       assert(barg > 0.);
233       assert(dbarg_dvbs < 0.);
234       assert(up_order(-1/t->phi, d2bdb2, 1/t->phi));
235     }
236     /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 
237     double gamma_s, dgamma_s_dvds, dgamma_s_dvbs, dgddb2;
238     {
239       if (short_channel) {
240         double argxd = 1. + 2. * barg * m->xd / m->xj;
241         assert(argxd > 0);
242         double argd = sqrt(argxd);
243         trace2("", argxd, argd);
244         
245         double alpha_d = s->relxj * (argd - 1.);
246         double dalpha_d_dvds = m->xd / (4. * s->l_eff * argd * barg);
247         double dalpha_d_dvbs = -dalpha_d_dvds;
248         trace3("", alpha_d, dalpha_d_dvds, dalpha_d_dvbs);
249         
250         double argxs = 1. + 2. * sarg * m->xd / m->xj;
251         assert(argxs > 0);
252         double args = sqrt(argxs);
253         trace2("", argxs, args);
254         
255         double alpha_s = s->relxj * (args - 1.);
256         double dalpha_s_dvbs = -m->xd / (4. * s->l_eff * args * sarg);
257         trace2("", alpha_s, dalpha_s_dvbs);
258         
259         gamma_s = m->gamma * (1. - alpha_s - alpha_d);
260         dgamma_s_dvds = -m->gamma *  dalpha_d_dvds;
261         dgamma_s_dvbs = -m->gamma * (dalpha_d_dvbs + dalpha_s_dvbs);
262         
263         double dasdb2=-m->xd*(d2sdb2+dsarg_dvbs*dsarg_dvbs*m->xd/(m->xj*argxs))
264           / (s->l_eff*args);
265         double daddb2=-m->xd*(d2bdb2+dbarg_dvbs*dbarg_dvbs*m->xd/(m->xj*argxd))
266           / (s->l_eff*argd);
267         dgddb2 = -.5 * m->gamma * (dasdb2 + daddb2);
268         
269         if (gamma_s <= 0. && m->gamma > 0.) {
270           untested();
271           error(bTRACE, d->long_label() + ": gamma is negative\n");
272           error(bTRACE, "+   gamma_s=%g, alpha_s=%g, alpha_d=%g\n",
273                 gamma_s,    alpha_s,    alpha_d);
274         }
275         trace4("no short chan", gamma_s, dgamma_s_dvds, dgamma_s_dvds, dgddb2);
276       }else{
277         gamma_s = m->gamma;
278         dgamma_s_dvds = dgamma_s_dvbs = 0.;
279         dgddb2 = 0.;
280         trace4("short channel", gamma_s, dgamma_s_dvds, dgamma_s_dvds, dgddb2);
281       }
282     }
283     /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 
284     /* von, subthreshold, cutoff, vgst */
285     double vc, vc_eta, dvon_dvbs;
286     double xn, vtxn, dxn_dvbs;  /* subthreshold only */
287     {
288       double vbin = t->vbi + s->eta_1 * v_phi_s;
289       d->von = vbin + gamma_s * sarg;
290       dvon_dvbs = -s->eta_1 + dgamma_s_dvbs * sarg + gamma_s * dsarg_dvbs;
291       trace3("guess", vbin, d->von, dvon_dvbs);
292       
293       if (do_subthreshold) {
294         double cdonco = -(gamma_s*dsarg_dvbs + dgamma_s_dvbs*sarg) + s->eta_1;
295         xn = 1. + m->cfsox + cdonco;
296         vtxn = t->vt * xn;
297         dxn_dvbs = 2. * dgamma_s_dvbs * dsarg_dvbs
298           + gamma_s * d2sdb2 + dgddb2 * sarg;
299         trace3("do_sub", xn, vtxn, dxn_dvbs);
300         
301         d->von += vtxn;
302         dvon_dvbs += t->vt * dxn_dvbs;
303         d->vgst = d->vgs - d->von;
304         trace3("", d->von, dvon_dvbs, d->vgst);
305         
306         d->subthreshold = (d->vgs < d->von);
307         d->cutoff = false;
308       }else{
309         xn = vtxn = dxn_dvbs = 0.;
310         d->vgst = d->vgs - d->von;
311         trace3("no_sub", xn, vtxn, dxn_dvbs);
312         trace3("", d->von, dvon_dvbs, d->vgst);
313         
314         d->subthreshold = false;
315         d->cutoff = (d->vgs < d->von);
316         if (d->cutoff) {
317           trace0("***** cut off *****");
318           d->ids = 0.;
319           d->gmf = d->gmr = 0.;
320           d->gds = 0.;
321           d->gmbf = d->gmbr = 0.;
322           return;
323         }
324       }
325       double vgsx = (d->subthreshold) ? d->von : d->vgs;
326       vc = vgsx - vbin;
327       vc_eta = vc / s->eta;
328       trace3("", vgsx, vc, vc_eta);
329     }
330     /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 
332     double ufact, duf_dvgs, duf_dvds, duf_dvbs, ueff;
333     {
334       if (m->uexp != NOT_INPUT  &&  d->vgst > m->vbp) {
335         ufact = pow(m->vbp/d->vgst, m->uexp);
336         duf_dvgs = -ufact * m->uexp / d->vgst;
337         duf_dvds = 0.;  /* wrong, but as per spice2 */
338         duf_dvbs = dvon_dvbs * ufact * m->uexp / d->vgst;
339         trace4("calc ufact", ufact, duf_dvgs, duf_dvds, duf_dvbs);
340       }else{
341         ufact = 1.;
342         duf_dvgs = duf_dvds = duf_dvbs = 0.;
343         trace4("def ufact", ufact, duf_dvgs, duf_dvds, duf_dvbs);
344       }
345       ueff = t->uo * ufact;     /* ???? */
346       trace2("", ufact, ueff);
347     }
348     /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 
349     /* vdsat  according to Baum's Theory of scattering velocity saturation  */
350     int use_vmax = m->vmax != NOT_INPUT;
351     if (use_vmax) {
352       double gammad = gamma_s / s->eta;
353       double v1 = vc_eta + v_phi_s;
354       double v2 = v_phi_s;
355       double xv = m->vmax * s->l_eff / ueff;
356       double a1 = gammad * (4./3.);
357       double b1 = -2. * (v1+xv);
358       double c1 = -2. * gammad * xv;                    /* end of scope */
359       double d1 = 2.*v1*(v2+xv) - v2*v2 - (4./3.)*gammad*sarg3;
360       double a = -b1;                                   /* xv, v1, v2, sarg3 */
361       double b = a1 * c1 - 4. * d1;
362       double C = -d1 * (a1*a1 - 4.*b1) - c1*c1;
363       double r = -a*a / 3. + b;
364       double r3 = r*r*r;                                /* r */
365       double S = 2. * a*a*a / 27. - a*b / 3. + C;       /* b, c */
366       double s2 = S*S;
367       double p = s2 / 4. + r3 / 27.;                    /* r */
368       double y3;
369       if (p < 0.) {                                     /* p */
370         double ro = pow((-r3 / 27), (1./6.));           /* s2, r3 */
371         double fi = atan(-2. * sqrt(-p) / S);
372         y3 = 2. * ro * cos(fi/3.) - a / 3.;
373       }else{
374         double p2 = sqrt(p);
375         double p3 = pow((fabs(-S/2.+p2)), (1./3.));
376         double p4 = pow((fabs(-S/2.-p2)), (1./3.));     /* s */
377         y3 = p3 + p4 - a / 3.;                          /* a */
378         untested();
379       }
380       
381       double x4[8];
382       int iknt = 0;
383       if (a1*a1 / 4. - b1 + y3  < 0.  &&  y3*y3 / 4. - d1  < 0.) {
384         untested();
385         error(bWARNING,
386               "%s: internal error: a3,b4, a1=%g, b1=%g, y3=%g, d1=%g\n",
387               d->long_label().c_str(),    a1,    b1,    y3,    d1);
388       }else{
389         double a3 = sqrt(a1*a1 / 4. - b1 + y3);
390         double b3 = sqrt(y3*y3 / 4. - d1);
391         for (int i = 0;   i < 4;   i++) {
392           static const double sig1[4] = {1., -1., 1., -1.};
393           static const double sig2[4] = {1., 1., -1., -1.};
394           double a4 = a1 / 2. + sig1[i] * a3;
395           double b4 = y3 / 2. + sig2[i] * b3;           /* y3 */
396           double delta4 = a4*a4 / 4. - b4;
397           if (delta4 >= 0.) {
398             double sd4 = sqrt(delta4);
399             x4[iknt++] = - a4 / 2. + sd4;
400             x4[iknt++] = - a4 / 2. - sd4;               /* i */
401           }
402         }
403       }
404       
405       double xvalid = 0.;
406       int root_count = 0;
407       for (int j = 0;   j < iknt;   j++) {                      /* iknt */
408         if (x4[j] > 0.) {
409           double poly4 = x4[j]*x4[j]*x4[j]*x4[j]/* ~= 0, used as check  */
410             + a1 * x4[j]*x4[j]*x4[j]            /* roundoff error not   */
411             + b1 * x4[j]*x4[j]                  /* propagated, so ok    */
412             + c1 * x4[j]
413             + d1;                               /* a1, b1, c1, d1 */
414           if (fabs(poly4) <= 1e-6) {
415             root_count++;
416             if (root_count <= 1) {              /* xvalid = min(x4[j]) */
417               xvalid=x4[j];
418             }
419             if (x4[j] <= xvalid) {
420               xvalid=x4[j];                             /* x4[], j */
421             }else{
422               untested();
423             }
424           }
425         }
426       }
427       if (root_count <= 0) {                            /* root_count */
428         error(bTRACE, d->long_label() + ": Baum's theory rejected\n");
429         use_vmax = false;
430         d->vdsat = 0.;
431         trace1("use_vmax rejected", d->vdsat);
432       }else{
433         d->vdsat = xvalid*xvalid - v_phi_s;
434         trace1("use_vmax", d->vdsat);
435       }
436     }else{
437       d->vdsat = 0.;
438       trace1("!use_vmax", d->vdsat);
439     }
440     /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 
441     /* vdsat     according to Grove-Frohman equation  */
442     double dvdsat_dvgs = NOT_VALID;
443     double dvdsat_dvbs = NOT_VALID;
444     if (!use_vmax) {
445       if (gamma_s > 0.) {
446         double argv = vc_eta + v_phi_s;
447         if (argv > 0.) {
448           double gammad = gamma_s / s->eta;
449           double gammd2 = gammad * gammad;
450           double arg1 = sqrt(1. + 4. * argv / gammd2);
451           d->vdsat = vc_eta  +  gammd2 * (1.-arg1) / 2.;
452           dvdsat_dvgs = (1. - 1./arg1) / s->eta;
453           dvdsat_dvbs = (gammad * (1.-arg1) + 2.*argv / (gammad*arg1))
454             / s->eta * dgamma_s_dvbs
455             + 1./arg1 + s->eta_1 * dvdsat_dvgs;
456           trace3("!use_vmax,gamma>0,argv>0",d->vdsat,dvdsat_dvgs,dvdsat_dvbs);
457         }else{
458           untested();
459           d->vdsat = 0.;
460           dvdsat_dvgs = dvdsat_dvbs = 0.;
461           error(bTRACE, d->long_label() + ": argv is negative\n");
462           trace2("argv<0", argv, vc);
463           trace3("!use_vmax,gamma>0,argv<=0",d->vdsat,dvdsat_dvgs,dvdsat_dvbs);
464         }
465       }else{
466         d->vdsat = vc_eta;
467         dvdsat_dvgs = 1.;
468         dvdsat_dvbs = 0.;
469         trace3("!use_vmax, gamma<=0", d->vdsat, dvdsat_dvgs, dvdsat_dvbs);
470       }
471     }else{
472       /* dvdsat_dvgs, dvdsat_dvbs   deferred */
473       trace3("use_vmax", d->vdsat, dvdsat_dvgs, dvdsat_dvbs);
474     }
475     /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 
476     if (d->vdsat < 0.) {
477       error(bWARNING,
478             "%s: calculated vdsat (%g) < 0.  using vdsat = 0.\n",
479             d->long_label().c_str(), d->vdsat);
480       d->vdsat = 0.;
481     }
482     /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 
483     double bsarg, dbsarg_dvbs;
484     {
485       double vbdsat = d->vbs - d->vdsat;
486       if (vbdsat <= 0.) {
487         double v_phi_ds = t->phi - vbdsat;
488         bsarg = sqrt(v_phi_ds);
489         dbsarg_dvbs = -.5 / bsarg;
490         trace3("vbdsat <= 0", vbdsat, bsarg, dbsarg_dvbs);
491       }else{
492         bsarg = t->sqrt_phi / (1. + .5 * vbdsat / t->phi);
493         dbsarg_dvbs = -.5 * bsarg * bsarg / t->phi_sqrt_phi;
494         untested();
495         trace3("vbdsat > 0", vbdsat, bsarg, dbsarg_dvbs);
496       }
497     }
498     /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 
499     /* local   dvdsat_dvgs, dvdsat_dvbs   maybe */
500     {
501       if (use_vmax) {
502         double bodys = bsarg*bsarg*bsarg - sarg3;
503         double gdbdvs =
504           2. * gamma_s * (bsarg*bsarg*dbsarg_dvbs - sarg*sarg*dsarg_dvbs);
505         double argv = vc_eta - d->vdsat;
506         double vqchan = argv - gamma_s * bsarg;
507         double dqdsat = -1. + gamma_s * dbsarg_dvbs;
508         double vl = m->vmax * s->l_eff;
509         double dfunds = vl * dqdsat - ueff * vqchan;
510         double dfundg = (vl - ueff * d->vdsat) / s->eta;
511         double dfundb = -vl * (1. + dqdsat - s->eta_1 / s->eta)
512           + ueff * (gdbdvs - dgamma_s_dvbs * bodys / 1.5) / s->eta;
513         dvdsat_dvgs = -dfundg / dfunds;
514         dvdsat_dvbs = -dfundb / dfunds;
515         trace2("use_vmax", dvdsat_dvgs, dvdsat_dvbs);
516       }else{
517         /* dvdsat_dvgs, dvdsat_dvbs   already set */
518         trace2("!use_vmax", dvdsat_dvgs, dvdsat_dvbs);
519       }
520       assert(dvdsat_dvgs != NOT_VALID);
521       assert(dvdsat_dvbs != NOT_VALID);
522     }
523     /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 
524     double  dl_dvgs, dl_dvds, dl_dvbs, clfact;
525     {
526       if (d->vds >= 0.) {
527         if (m->lambda == NOT_INPUT) {
528           double dldsat;
529           if (use_vmax) {
530             double xdv = m->xd / sqrt(m->neff);
531             double xlv = m->vmax * xdv / (2. * ueff);
532             double argv = d->vds - d->vdsat;
533             if (argv < 0.) {
534               argv = 0.;
535             }
536             double xls = sqrt(xlv*xlv + argv);
537             double dl = (xls-xlv) * xdv;
538             /* lambda = dl / (s->l_eff * d->vds); */
539             clfact = (1. - dl / s->l_eff);
540             dldsat = xdv / (2. * xls * s->l_eff);
541           }else{
542             double argv = (d->vds - d->vdsat) / 4.;
543             double sargv = sqrt(1. + argv*argv);
544             if (argv + sargv >= 0.) {
545               double dl = m->xd * sqrt(argv + sargv);
546               /* lambda = dl / (s->l_eff * d->vds); */
547               clfact = (1. - dl / s->l_eff);
548               /* dldsat = lambda * d->vds / (8. * sargv); */
549               dldsat = dl / (s->l_eff * 8. * sargv);
550             }else{
551               /* lambda = 0.; */
552               clfact = 1.;
553               dldsat = 0.;
554               untested();
555               error(bWARNING,
556                     "%s: internal error: vds(%g) < vdsat(%g)\n",
557                     d->long_label().c_str(), d->vds,   d->vdsat);
558             }
559           }
560           dl_dvgs =  dvdsat_dvgs * dldsat;
561           dl_dvds =              - dldsat;
562           dl_dvbs =  dvdsat_dvbs * dldsat;
563         }else{
564           /* lambda = m->lambda; */
565           clfact = (1. - m->lambda * d->vds);
566           dl_dvgs = dl_dvbs = 0.;
567           dl_dvds = -m->lambda;
568         }
569         
570         /* clfact = (1. - lambda * d->vds); */
571         if (clfact < m->xwb/s->l_eff) {
572           double leff = m->xwb / (2. - (clfact * s->l_eff / m->xwb));
573           double dfact = (leff * leff) / (m->xwb * m->xwb);
574           clfact = leff / s->l_eff;
575           dl_dvgs *= dfact;
576           dl_dvds *= dfact;
577           dl_dvbs *= dfact;
578         }
579       }else{  /* vds <= 0. */
580         /* lambda = 0.; */
581         clfact = 1.;
582         dl_dvgs = dl_dvds = dl_dvbs = 0.;
583         trace1("*** vds < 0 ***", d->vds);
584       }
585       trace4("", dl_dvgs, dl_dvds, dl_dvbs, clfact);
586     }
587     /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 
588     /* ids, gmf, gds, gmbf */
589     {
590       d->saturated = (d->vds > d->vdsat);
591       double vdsx =  (d->saturated) ? d->vdsat : d->vds;
592       double bargx = (d->saturated) ? bsarg : barg;
593       double body = bargx*bargx*bargx - sarg3;
594       double expg = (d->subthreshold) ? exp(d->vgst / vtxn) : 1.;
595       trace4("", vdsx, bargx, body, expg);
596       
597       trace3("", t->beta, ufact, clfact);
598       double beta = t->beta * ufact / clfact;
599       double ids_on = 
600         beta * ((vc - s->eta_2 * vdsx) * vdsx  - (2./3.) * gamma_s * body);
601       trace4("", beta, vc, (s->eta*vdsx),  (gamma_s*bargx));
602       double didvds = beta * (vc  -  s->eta * vdsx  -  gamma_s * bargx);
603       fixzero(&didvds, ids_on);
604       trace4("", beta, ids_on, didvds, d->saturated);
605       
606       d->ids = ids_on * expg;
607       
608       d->gmf = beta * vdsx;
609       d->gmf += ids_on * (duf_dvgs/ufact - dl_dvgs/clfact);
610       if (d->saturated) {
611         d->gmf += didvds * dvdsat_dvgs;
612       }
613       if (d->subthreshold) {
614         d->gmf = ids_on / vtxn;
615         if (d->saturated) {
616           d->gmf += didvds * dvdsat_dvgs;
617         }
618         d->gmf *= expg;
619       }
620       
621       d->gds = (d->saturated) ? 0.: didvds;
622       d->gds += ids_on * (duf_dvds/ufact - dl_dvds/clfact);
623       if (short_channel) {
624         d->gds -= beta * (2./3.) * body * dgamma_s_dvds;
625       }
626       if (d->subthreshold) {
627         double dxndvd = dgamma_s_dvds * dsarg_dvbs;
628         double dodvds = dgamma_s_dvds * sarg + t->vt * dxndvd;
629         double gmw = d->ids * d->vgst / (vtxn * xn);
630         d->gds *= expg;
631         d->gds -= d->gmf * dodvds + gmw * dxndvd;
632       }
633       
634       d->gmbf = beta * (s->eta_1 * vdsx - gamma_s * (sarg - bargx));
635       d->gmbf += ids_on * (duf_dvbs/ufact - dl_dvbs/clfact);
636       if (short_channel) {
637         d->gmbf -= beta * (2./3.) * body * dgamma_s_dvbs;
638       }
639       if (d->saturated) {
640         d->gmbf += didvds * dvdsat_dvbs;
641       }
642       if (d->subthreshold) {
643         double gmw = d->ids * d->vgst / (vtxn * xn);
644         d->gmbf += beta * dvon_dvbs * vdsx;
645         d->gmbf *= expg;
646         d->gmbf -= d->gmf * dvon_dvbs + gmw * dxn_dvbs;
647       }
648       trace4("", d->ids, d->gmf, d->gds, d->gmbf);
649     }
650     if (d->reversed) {
651       d->ids *= -1;
652       d->gmr = d->gmf;
653       d->gmbr = d->gmbf;
654       d->gmf = d->gmbf = 0;
655     }else{
656       d->gmr = d->gmbr = 0.;
657     }
658   }
660 /*--------------------------------------------------------------------------*/
661 /*--------------------------------------------------------------------------*/