testing
[gnucap-felix.git] / src / d_mos2.cc
blob24443cff453ccbe9dca64e5b2175e8f74f92db41
1 /* $Id: d_mos2.cc,v 1.2 2010-07-09 12:14:21 felix Exp $ -*- C++ -*-
2 * Copyright (C) 2001 Albert Davis
3 * Author: Albert Davis <aldavis@gnu.org>
5 * This file is part of "Gnucap", the Gnu Circuit Analysis Package
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.
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.
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
24 /* This file is automatically generated. DO NOT EDIT */
26 #include "l_compar.h"
27 #include "l_denoise.h"
28 #include "globals.h"
29 #include "e_elemnt.h"
30 #include "d_mos2.h"
31 /*--------------------------------------------------------------------------*/
32 const double NA(NOT_INPUT);
33 const double INF(BIGBIG);
34 /*--------------------------------------------------------------------------*/
35 int MODEL_BUILT_IN_MOS2::_count = 0;
36 /*--------------------------------------------------------------------------*/
37 const int LEVEL(2);
38 /*--------------------------------------------------------------------------*/
39 namespace MODEL_BUILT_IN_MOS2_DISPATCHER {
40 static DEV_BUILT_IN_MOS p1d;
41 static MODEL_BUILT_IN_MOS2 p1(&p1d);
42 static DISPATCHER<MODEL_CARD>::INSTALL
43 d1(&model_dispatcher, "nmos2|pmos2", &p1);
45 /*--------------------------------------------------------------------------*/
46 void SDP_BUILT_IN_MOS2::init(const COMMON_COMPONENT* cc)
48 assert(cc);
49 SDP_BUILT_IN_MOS123::init(cc);
50 const COMMON_BUILT_IN_MOS* c = prechecked_cast<const COMMON_BUILT_IN_MOS*>(cc);
51 assert(c);
52 const MODEL_BUILT_IN_MOS2* m = prechecked_cast<const MODEL_BUILT_IN_MOS2*>(c->model());
53 assert(m);
54 const CARD_LIST* par_scope = m->scope();
55 assert(par_scope); USE(par_scope);
56 // adjust: override
57 // adjust: raw
58 // adjust: calculated
59 relxj = ((m->xj != NA && m->xj > 0)
60 ? .5 * m->xj / l_eff
61 : NA);
62 eta_1 = ((cgate != 0)
63 ? M_PI_4 * P_EPS_SI * m->delta / cgate * l_eff
64 : 0.);
65 eta = eta_1 + 1.;
66 eta_2 = eta / 2.;
67 // code_post
69 /*--------------------------------------------------------------------------*/
70 TDP_BUILT_IN_MOS2::TDP_BUILT_IN_MOS2(const DEV_BUILT_IN_MOS* d)
71 :TDP_BUILT_IN_MOS123(d)
73 assert(d);
74 const COMMON_BUILT_IN_MOS* c = prechecked_cast<const COMMON_BUILT_IN_MOS*>(d->common());
75 assert(c);
76 const SDP_BUILT_IN_MOS2* s = prechecked_cast<const SDP_BUILT_IN_MOS2*>(c->sdp());
77 assert(s);
78 const MODEL_BUILT_IN_MOS2* m = prechecked_cast<const MODEL_BUILT_IN_MOS2*>(c->model());
79 assert(m);
80 const CARD_LIST* par_scope = d->scope();
81 assert(par_scope); USE(par_scope);
82 // final adjust: code_pre
84 double temp = d->_sim->_temp_c + P_CELSIUS0;
85 double tempratio = temp / m->tnom_k; // ratio
86 double tempratio4 = tempratio * sqrt(tempratio);
87 double kt = temp * P_K;
88 double egap = 1.16 - (7.02e-4*temp*temp) / (temp+1108.);
89 double arg = (m->egap*tempratio - egap) / (2*kt);
90 // final adjust: override
91 // final adjust: raw
92 // final adjust: mid
93 // final adjust: calculated
94 vt = temp * P_K_Q;
95 phi = m->phi*tempratio + (-2*vt*(1.5*log(tempratio)+P_Q*(arg)));
96 sqrt_phi = sqrt(phi);
97 phi_sqrt_phi = phi * sqrt_phi;
98 beta = (m->kp / tempratio4) * s->w_eff / s->l_eff;
99 uo = m->uo * tempratio4;
100 vbi = (fixzero(
101 (m->vto - m->gamma * sqrt(m->phi)
102 +.5*(m->egap-egap) + m->polarity* .5 * (phi-m->phi)), m->phi));
103 // final adjust: post
104 // final adjust: done
106 /*--------------------------------------------------------------------------*/
107 MODEL_BUILT_IN_MOS2::MODEL_BUILT_IN_MOS2(const BASE_SUBCKT* p)
108 :MODEL_BUILT_IN_MOS123(p),
109 kp(NA),
110 nfs_cm(0.0),
111 vmax(NA),
112 neff(1.0),
113 ucrit_cm(1e4),
114 uexp(NA),
115 utra(NA),
116 delta(0.0),
117 nfs(NA),
118 ucrit(NA),
119 calc_kp(false),
120 alpha(NA),
121 xd(NA),
122 xwb(NA),
123 vbp(NA),
124 cfsox(NA)
126 if (ENV::run_mode != rPRE_MAIN) {
127 ++_count;
128 }else{
130 set_default(&mjsw, .33);
131 set_default(&tox, 1e-7);
132 set_default(&cox, NA);
133 set_default(&vto, NA);
134 set_default(&gamma, NA);
135 set_default(&phi, NA);
136 set_default(&mos_level, LEVEL);
138 /*--------------------------------------------------------------------------*/
139 MODEL_BUILT_IN_MOS2::MODEL_BUILT_IN_MOS2(const MODEL_BUILT_IN_MOS2& p)
140 :MODEL_BUILT_IN_MOS123(p),
141 kp(p.kp),
142 nfs_cm(p.nfs_cm),
143 vmax(p.vmax),
144 neff(p.neff),
145 ucrit_cm(p.ucrit_cm),
146 uexp(p.uexp),
147 utra(p.utra),
148 delta(p.delta),
149 nfs(p.nfs),
150 ucrit(p.ucrit),
151 calc_kp(p.calc_kp),
152 alpha(p.alpha),
153 xd(p.xd),
154 xwb(p.xwb),
155 vbp(p.vbp),
156 cfsox(p.cfsox)
158 if (ENV::run_mode != rPRE_MAIN) {
159 ++_count;
160 }else{untested();//194
163 /*--------------------------------------------------------------------------*/
164 std::string MODEL_BUILT_IN_MOS2::dev_type()const
166 if (polarity == pN) {
167 return "nmos2";
168 }else if (polarity == pP) {
169 return "pmos2";
170 }else{untested();//235
171 return MODEL_BUILT_IN_MOS123::dev_type();
174 /*--------------------------------------------------------------------------*/
175 void MODEL_BUILT_IN_MOS2::set_dev_type(const std::string& new_type)
177 if (Umatch(new_type, "nmos2 ")) {
178 polarity = pN;
179 }else if (Umatch(new_type, "pmos2 ")) {
180 polarity = pP;
181 }else{
182 MODEL_BUILT_IN_MOS123::set_dev_type(new_type);
185 /*--------------------------------------------------------------------------*/
186 void MODEL_BUILT_IN_MOS2::precalc_first()
188 const CARD_LIST* par_scope = scope();
189 assert(par_scope);
190 MODEL_BUILT_IN_MOS123::precalc_first();
191 e_val(&(this->kp), NA, par_scope);
192 e_val(&(this->nfs_cm), 0.0, par_scope);
193 e_val(&(this->vmax), NA, par_scope);
194 e_val(&(this->neff), 1.0, par_scope);
195 e_val(&(this->ucrit_cm), 1e4, par_scope);
196 e_val(&(this->uexp), NA, par_scope);
197 e_val(&(this->utra), NA, par_scope);
198 e_val(&(this->delta), 0.0, par_scope);
199 // final adjust: code_pre
201 if (!has_good_value(tox)) {
202 tox = 1e-7;
204 cox = P_EPS_OX / tox;
205 if (kp == NA) {
206 kp = uo * cox;
207 calc_kp = true;
209 if (nsub != NA) {
210 if (phi == NA) {
211 phi = (2. * P_K_Q) * tnom_k * log(nsub/NI);
212 if (phi < .1) {
213 untested();
214 error(((!_sim->is_first_expand()) ? (bDEBUG) : (bWARNING)),
215 long_label() + ": calculated phi too small, using .1\n");
216 phi = .1;
218 calc_phi = true;
220 if (gamma == NA) {
221 gamma = sqrt(2. * P_EPS_SI * P_Q * nsub) / cox;
222 calc_gamma = true;
224 if (vto == NA) {
225 double phi_ms = (tpg == gtMETAL)
226 ? polarity * (-.05 - (egap + polarity * phi) / 2.)
227 : -(tpg * egap + phi) / 2.;
228 double vfb = phi_ms - polarity * P_Q * nss / cox;
229 vto = vfb + phi + gamma * sqrt(phi);
230 calc_vto = true;
233 // final adjust: override
234 if (cox == NA) {
235 cox = P_EPS_OX/tox;
236 }else{
238 if (vto == NA) {
239 vto = 0.0;
240 }else{
242 if (gamma == NA) {
243 gamma = 0.0;
244 }else{
246 if (phi == NA) {
247 phi = 0.6;
248 }else{
250 // final adjust: raw
251 e_val(&(this->kp), 2e-5, par_scope);
252 e_val(&(this->nfs_cm), 0.0, par_scope);
253 e_val(&(this->vmax), NA, par_scope);
254 e_val(&(this->neff), 1.0, par_scope);
255 e_val(&(this->ucrit_cm), 1e4, par_scope);
256 e_val(&(this->uexp), NA, par_scope);
257 e_val(&(this->utra), NA, par_scope);
258 e_val(&(this->delta), 0.0, par_scope);
259 // final adjust: mid
260 // final adjust: calculated
261 nfs = nfs_cm*ICM2M2;
262 ucrit = ucrit_cm*ICM2M;
263 alpha = ((nsub != NA)
264 ? (2. * P_EPS_SI) / (P_Q * nsub)
265 : 0.);
266 xd = sqrt(alpha);
267 xwb = ((nsub != NA)
268 ? xd * sqrt(pb)
269 : .25e-6);
270 vbp = ucrit * P_EPS_SI / cox;
271 cfsox = P_Q * nfs / cox;
272 // final adjust: post
273 // final adjust: done
275 /*--------------------------------------------------------------------------*/
276 void MODEL_BUILT_IN_MOS2::precalc_last()
278 MODEL_BUILT_IN_MOS123::precalc_last();
280 /*--------------------------------------------------------------------------*/
281 SDP_CARD* MODEL_BUILT_IN_MOS2::new_sdp(COMMON_COMPONENT* c)const
283 assert(c);
284 if (COMMON_BUILT_IN_MOS* cc = dynamic_cast<COMMON_BUILT_IN_MOS*>(c)) {
285 if (cc->_sdp) {
286 cc->_sdp->init(cc);
287 return cc->_sdp;
288 }else{
289 delete cc->_sdp;
290 return new SDP_BUILT_IN_MOS2(c);
292 }else{
293 return MODEL_BUILT_IN_MOS123::new_sdp(c);
296 /*--------------------------------------------------------------------------*/
297 void MODEL_BUILT_IN_MOS2::set_param_by_index(int i, std::string& value, int offset)
299 switch (MODEL_BUILT_IN_MOS2::param_count() - 1 - i) {
300 case 0: level = value; break; //2
301 case 1: unreachable(); break;
302 case 2: unreachable(); break;
303 case 3: unreachable(); break;
304 case 4: unreachable(); break;
305 case 5: unreachable(); break;
306 case 6: unreachable(); break;
307 case 7: mos_level = value; break;
308 case 8: kp = value; break;
309 case 9: nfs_cm = value; break;
310 case 10: vmax = value; break;
311 case 11: neff = value; break;
312 case 12: ucrit_cm = value; break;
313 case 13: uexp = value; break;
314 case 14: utra = value; break;
315 case 15: delta = value; break;
316 default: MODEL_BUILT_IN_MOS123::set_param_by_index(i, value, offset); break;
319 /*--------------------------------------------------------------------------*/
320 bool MODEL_BUILT_IN_MOS2::param_is_printable(int i)const
322 switch (MODEL_BUILT_IN_MOS2::param_count() - 1 - i) {
323 case 0: return (true);
324 case 1: return (false);
325 case 2: return (false);
326 case 3: return (false);
327 case 4: return (false);
328 case 5: return (false);
329 case 6: return (false);
330 case 7: return (mos_level != LEVEL);
331 case 8: return (!calc_kp);
332 case 9: return (true);
333 case 10: return (vmax.has_hard_value());
334 case 11: return (neff != 1.0 || lambda == NA);
335 case 12: return (ucrit_cm != 1e4 || uexp != NA);
336 case 13: return (uexp.has_hard_value());
337 case 14: return (false);
338 case 15: return (true);
339 default: return MODEL_BUILT_IN_MOS123::param_is_printable(i);
342 /*--------------------------------------------------------------------------*/
343 std::string MODEL_BUILT_IN_MOS2::param_name(int i)const
345 switch (MODEL_BUILT_IN_MOS2::param_count() - 1 - i) {
346 case 0: return "level";
347 case 1: return "=====";
348 case 2: return "=====";
349 case 3: return "=====";
350 case 4: return "=====";
351 case 5: return "=====";
352 case 6: return "=====";
353 case 7: return "diodelevel";
354 case 8: return "kp";
355 case 9: return "nfs";
356 case 10: return "vmax";
357 case 11: return "neff";
358 case 12: return "ucrit";
359 case 13: return "uexp";
360 case 14: return "utra";
361 case 15: return "delta";
362 default: return MODEL_BUILT_IN_MOS123::param_name(i);
365 /*--------------------------------------------------------------------------*/
366 std::string MODEL_BUILT_IN_MOS2::param_name(int i, int j)const
368 if (j == 0) {
369 return param_name(i);
370 }else if (j == 1) {
371 switch (MODEL_BUILT_IN_MOS2::param_count() - 1 - i) {
372 case 0: return "";
373 case 1: return "";
374 case 2: return "";
375 case 3: return "";
376 case 4: return "";
377 case 5: return "";
378 case 6: return "";
379 case 7: return "";
380 case 8: return "";
381 case 9: return "";
382 case 10: return "";
383 case 11: return "";
384 case 12: return "";
385 case 13: return "";
386 case 14: return "";
387 case 15: return "";
388 default: return MODEL_BUILT_IN_MOS123::param_name(i, j);
390 }else if (i < 16) {
391 return "";
392 }else{
393 return MODEL_BUILT_IN_MOS123::param_name(i, j);
396 /*--------------------------------------------------------------------------*/
397 std::string MODEL_BUILT_IN_MOS2::param_value(int i)const
399 switch (MODEL_BUILT_IN_MOS2::param_count() - 1 - i) {
400 case 0: return "2";
401 case 1: unreachable(); return "";
402 case 2: unreachable(); return "";
403 case 3: unreachable(); return "";
404 case 4: unreachable(); return "";
405 case 5: unreachable(); return "";
406 case 6: unreachable(); return "";
407 case 7: return mos_level.string();
408 case 8: return kp.string();
409 case 9: return nfs_cm.string();
410 case 10: return vmax.string();
411 case 11: return neff.string();
412 case 12: return ucrit_cm.string();
413 case 13: return uexp.string();
414 case 14: return utra.string();
415 case 15: return delta.string();
416 default: return MODEL_BUILT_IN_MOS123::param_value(i);
419 /*--------------------------------------------------------------------------*/
420 bool MODEL_BUILT_IN_MOS2::is_valid(const COMPONENT* d)const
422 assert(d);
423 return MODEL_BUILT_IN_MOS123::is_valid(d);
425 /*--------------------------------------------------------------------------*/
426 void MODEL_BUILT_IN_MOS2::tr_eval(COMPONENT* brh)const
428 DEV_BUILT_IN_MOS* d = prechecked_cast<DEV_BUILT_IN_MOS*>(brh);
429 assert(d);
430 const COMMON_BUILT_IN_MOS* c = prechecked_cast<const COMMON_BUILT_IN_MOS*>(d->common());
431 assert(c);
432 const SDP_BUILT_IN_MOS2* s = prechecked_cast<const SDP_BUILT_IN_MOS2*>(c->sdp());
433 assert(s);
434 const MODEL_BUILT_IN_MOS2* m = this;
435 const TDP_BUILT_IN_MOS2 T(d);
436 const TDP_BUILT_IN_MOS2* t = &T;
438 #define short_channel (m->xj != NOT_INPUT && m->xj > 0.)
439 #define do_subthreshold (m->nfs != 0.)
440 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
441 // trace1(d->long_label().c_str(), d->evaliter());
442 trace3("", d->vds, d->vgs, d->vbs);
443 assert(m->tnom_k > 0);
444 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
445 d->reverse_if_needed();
446 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
447 double v_phi_s = t->phi - d->vbs;
448 double sarg, dsarg_dvbs, d2sdb2, sarg3;
450 if (d->vbs <= 0.) {
451 sarg = sqrt(v_phi_s);
452 dsarg_dvbs = -.5 / sarg;
453 d2sdb2 = .5 * dsarg_dvbs / v_phi_s;
454 d->sbfwd = false;
455 trace3("sb-ok", sarg, v_phi_s, dsarg_dvbs);
456 }else{
457 if (OPT::mosflags & 01000) {
458 sarg = t->sqrt_phi / (1. + .5 * d->vbs / t->phi);
459 dsarg_dvbs = -.5 * sarg * sarg / t->phi_sqrt_phi;
460 d2sdb2 = -dsarg_dvbs * sarg / t->phi_sqrt_phi;
461 untested();
462 trace3("***sb-reversed(01000)***", sarg, v_phi_s, dsarg_dvbs);
463 }else{
464 sarg = t->sqrt_phi
465 / (1. + .5 * d->vbs / t->phi
466 + .375 * d->vbs * d->vbs / (t->phi * t->phi));
467 dsarg_dvbs = (-.5 * sarg * sarg / t->phi_sqrt_phi)
468 * (1. + 1.5 * d->vbs / t->phi);
469 d2sdb2 = (-dsarg_dvbs * sarg / t->phi_sqrt_phi)
470 - (.75 * sarg / (t->phi_sqrt_phi * t->phi))
471 * (2. * d->vbs * dsarg_dvbs + sarg);
472 trace3("***sb-reversed(00000)***", sarg, v_phi_s, dsarg_dvbs);
474 d->sbfwd = true;
476 sarg3 = sarg*sarg*sarg;
477 assert(sarg > 0.);
478 assert(dsarg_dvbs < 0.);
479 assert(up_order(-1/t->phi, d2sdb2, 1/t->phi));
480 trace2("", d2sdb2, sarg3);
482 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
483 double barg, dbarg_dvbs, d2bdb2;
485 double vbd = d->vbs - d->vds;
486 double v_phi_d = t->phi - vbd;
487 if (vbd <= 0.) {
488 barg = sqrt(v_phi_d);
489 dbarg_dvbs = -.5 / barg;
490 d2bdb2 = .5 * dbarg_dvbs / v_phi_d;
491 //d->dbfwd = false;
492 trace4("db-ok", barg, v_phi_d, dbarg_dvbs, d2bdb2);
493 }else{
494 if (OPT::mosflags & 01000) {
495 barg = t->sqrt_phi / (1. + .5 * vbd / t->phi);
496 dbarg_dvbs = -.5 * barg * barg / t->phi_sqrt_phi;
497 d2bdb2 = -dbarg_dvbs * barg / t->phi_sqrt_phi;
498 untested();
499 trace4("***db-reversed(00000)***",barg, v_phi_d, dbarg_dvbs, d2bdb2);
500 }else{
501 barg = t->sqrt_phi
502 / (1. + .5 * vbd / t->phi
503 + .375 * vbd * vbd / (t->phi * t->phi));
504 dbarg_dvbs = (-.5 * barg * barg / t->phi_sqrt_phi)
505 * (1. + 1.5 * vbd / t->phi);
506 d2bdb2 = (-dbarg_dvbs * barg / t->phi_sqrt_phi)
507 - (.75 * barg / (t->phi_sqrt_phi * t->phi))
508 * (2. * vbd * dbarg_dvbs + barg);
509 trace4("***db-reversed(00000)***",barg, v_phi_d, dbarg_dvbs, d2bdb2);
511 //d->dbfwd = true;
513 assert(barg > 0.);
514 assert(dbarg_dvbs < 0.);
515 assert(up_order(-1/t->phi, d2bdb2, 1/t->phi));
517 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
518 double gamma_s, dgamma_s_dvds, dgamma_s_dvbs, dgddb2;
520 if (short_channel) {
521 double argxd = 1. + 2. * barg * m->xd / m->xj;
522 assert(argxd > 0);
523 double argd = sqrt(argxd);
524 trace2("", argxd, argd);
526 double alpha_d = s->relxj * (argd - 1.);
527 double dalpha_d_dvds = m->xd / (4. * s->l_eff * argd * barg);
528 double dalpha_d_dvbs = -dalpha_d_dvds;
529 trace3("", alpha_d, dalpha_d_dvds, dalpha_d_dvbs);
531 double argxs = 1. + 2. * sarg * m->xd / m->xj;
532 assert(argxs > 0);
533 double args = sqrt(argxs);
534 trace2("", argxs, args);
536 double alpha_s = s->relxj * (args - 1.);
537 double dalpha_s_dvbs = -m->xd / (4. * s->l_eff * args * sarg);
538 trace2("", alpha_s, dalpha_s_dvbs);
540 gamma_s = m->gamma * (1. - alpha_s - alpha_d);
541 dgamma_s_dvds = -m->gamma * dalpha_d_dvds;
542 dgamma_s_dvbs = -m->gamma * (dalpha_d_dvbs + dalpha_s_dvbs);
544 double dasdb2=-m->xd*(d2sdb2+dsarg_dvbs*dsarg_dvbs*m->xd/(m->xj*argxs))
545 / (s->l_eff*args);
546 double daddb2=-m->xd*(d2bdb2+dbarg_dvbs*dbarg_dvbs*m->xd/(m->xj*argxd))
547 / (s->l_eff*argd);
548 dgddb2 = -.5 * m->gamma * (dasdb2 + daddb2);
550 if (gamma_s <= 0. && m->gamma > 0.) {
551 untested();
552 error(bTRACE, d->long_label() + ": gamma is negative\n");
553 error(bTRACE, "+ gamma_s=%g, alpha_s=%g, alpha_d=%g\n",
554 gamma_s, alpha_s, alpha_d);
556 trace4("no short chan", gamma_s, dgamma_s_dvds, dgamma_s_dvds, dgddb2);
557 }else{
558 gamma_s = m->gamma;
559 dgamma_s_dvds = dgamma_s_dvbs = 0.;
560 dgddb2 = 0.;
561 trace4("short channel", gamma_s, dgamma_s_dvds, dgamma_s_dvds, dgddb2);
564 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
565 /* von, subthreshold, cutoff, vgst */
566 double vc, vc_eta, dvon_dvbs;
567 double xn, vtxn, dxn_dvbs; /* subthreshold only */
569 double vbin = t->vbi + s->eta_1 * v_phi_s;
570 d->von = vbin + gamma_s * sarg;
571 dvon_dvbs = -s->eta_1 + dgamma_s_dvbs * sarg + gamma_s * dsarg_dvbs;
572 trace3("guess", vbin, d->von, dvon_dvbs);
574 if (do_subthreshold) {
575 double cdonco = -(gamma_s*dsarg_dvbs + dgamma_s_dvbs*sarg) + s->eta_1;
576 xn = 1. + m->cfsox + cdonco;
577 vtxn = t->vt * xn;
578 dxn_dvbs = 2. * dgamma_s_dvbs * dsarg_dvbs
579 + gamma_s * d2sdb2 + dgddb2 * sarg;
580 trace3("do_sub", xn, vtxn, dxn_dvbs);
582 d->von += vtxn;
583 dvon_dvbs += t->vt * dxn_dvbs;
584 d->vgst = d->vgs - d->von;
585 trace3("", d->von, dvon_dvbs, d->vgst);
587 d->subthreshold = (d->vgs < d->von);
588 d->cutoff = false;
589 }else{
590 xn = vtxn = dxn_dvbs = 0.;
591 d->vgst = d->vgs - d->von;
592 trace3("no_sub", xn, vtxn, dxn_dvbs);
593 trace3("", d->von, dvon_dvbs, d->vgst);
595 d->subthreshold = false;
596 d->cutoff = (d->vgs < d->von);
597 if (d->cutoff) {
598 trace0("***** cut off *****");
599 d->ids = 0.;
600 d->gmf = d->gmr = 0.;
601 d->gds = 0.;
602 d->gmbf = d->gmbr = 0.;
603 return;
606 double vgsx = (d->subthreshold) ? d->von : d->vgs;
607 vc = vgsx - vbin;
608 vc_eta = vc / s->eta;
609 trace3("", vgsx, vc, vc_eta);
611 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
613 double ufact, duf_dvgs, duf_dvds, duf_dvbs, ueff;
615 if (m->uexp != NOT_INPUT && d->vgst > m->vbp) {
616 ufact = pow(m->vbp/d->vgst, m->uexp);
617 duf_dvgs = -ufact * m->uexp / d->vgst;
618 duf_dvds = 0.; /* wrong, but as per spice2 */
619 duf_dvbs = dvon_dvbs * ufact * m->uexp / d->vgst;
620 trace4("calc ufact", ufact, duf_dvgs, duf_dvds, duf_dvbs);
621 }else{
622 ufact = 1.;
623 duf_dvgs = duf_dvds = duf_dvbs = 0.;
624 trace4("def ufact", ufact, duf_dvgs, duf_dvds, duf_dvbs);
626 ueff = t->uo * ufact; /* ???? */
627 trace2("", ufact, ueff);
629 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
630 /* vdsat according to Baum's Theory of scattering velocity saturation */
631 int use_vmax = m->vmax != NOT_INPUT;
632 if (use_vmax) {
633 double gammad = gamma_s / s->eta;
634 double v1 = vc_eta + v_phi_s;
635 double v2 = v_phi_s;
636 double xv = m->vmax * s->l_eff / ueff;
637 double a1 = gammad * (4./3.);
638 double b1 = -2. * (v1+xv);
639 double c1 = -2. * gammad * xv; /* end of scope */
640 double d1 = 2.*v1*(v2+xv) - v2*v2 - (4./3.)*gammad*sarg3;
641 double a = -b1; /* xv, v1, v2, sarg3 */
642 double b = a1 * c1 - 4. * d1;
643 double C = -d1 * (a1*a1 - 4.*b1) - c1*c1;
644 double r = -a*a / 3. + b;
645 double r3 = r*r*r; /* r */
646 double S = 2. * a*a*a / 27. - a*b / 3. + C; /* b, c */
647 double s2 = S*S;
648 double p = s2 / 4. + r3 / 27.; /* r */
649 double y3;
650 if (p < 0.) { /* p */
651 double ro = pow((-r3 / 27), (1./6.)); /* s2, r3 */
652 double fi = atan(-2. * sqrt(-p) / S);
653 y3 = 2. * ro * cos(fi/3.) - a / 3.;
654 }else{
655 double p2 = sqrt(p);
656 double p3 = pow((fabs(-S/2.+p2)), (1./3.));
657 double p4 = pow((fabs(-S/2.-p2)), (1./3.)); /* s */
658 y3 = p3 + p4 - a / 3.; /* a */
661 double x4[8];
662 int iknt = 0;
663 if (a1*a1 / 4. - b1 + y3 < 0. && y3*y3 / 4. - d1 < 0.) {
664 untested();
665 error(bWARNING,
666 "%s: internal error: a3,b4, a1=%g, b1=%g, y3=%g, d1=%g\n",
667 d->long_label().c_str(), a1, b1, y3, d1);
668 }else{
669 double a3 = sqrt(a1*a1 / 4. - b1 + y3);
670 double b3 = sqrt(y3*y3 / 4. - d1);
671 for (int i = 0; i < 4; i++) {
672 static const double sig1[4] = {1., -1., 1., -1.};
673 static const double sig2[4] = {1., 1., -1., -1.};
674 double a4 = a1 / 2. + sig1[i] * a3;
675 double b4 = y3 / 2. + sig2[i] * b3; /* y3 */
676 double delta4 = a4*a4 / 4. - b4;
677 if (delta4 >= 0.) {
678 double sd4 = sqrt(delta4);
679 x4[iknt++] = - a4 / 2. + sd4;
680 x4[iknt++] = - a4 / 2. - sd4; /* i */
685 double xvalid = 0.;
686 int root_count = 0;
687 for (int j = 0; j < iknt; j++) { /* iknt */
688 if (x4[j] > 0.) {
689 double poly4 = x4[j]*x4[j]*x4[j]*x4[j]/* ~= 0, used as check */
690 + a1 * x4[j]*x4[j]*x4[j] /* roundoff error not */
691 + b1 * x4[j]*x4[j] /* propagated, so ok */
692 + c1 * x4[j]
693 + d1; /* a1, b1, c1, d1 */
694 if (fabs(poly4) <= 1e-6) {
695 root_count++;
696 if (root_count <= 1) { /* xvalid = min(x4[j]) */
697 xvalid=x4[j];
699 if (x4[j] <= xvalid) {
700 xvalid=x4[j]; /* x4[], j */
701 }else{
702 untested();
707 if (root_count <= 0) { /* root_count */
708 error(bTRACE, d->long_label() + ": Baum's theory rejected\n");
709 use_vmax = false;
710 d->vdsat = 0.;
711 trace1("use_vmax rejected", d->vdsat);
712 }else{
713 d->vdsat = xvalid*xvalid - v_phi_s;
714 trace1("use_vmax", d->vdsat);
716 }else{
717 d->vdsat = 0.;
718 trace1("!use_vmax", d->vdsat);
720 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
721 /* vdsat according to Grove-Frohman equation */
722 double dvdsat_dvgs = NOT_VALID;
723 double dvdsat_dvbs = NOT_VALID;
724 if (!use_vmax) {
725 if (gamma_s > 0.) {
726 double argv = vc_eta + v_phi_s;
727 if (argv > 0.) {
728 double gammad = gamma_s / s->eta;
729 double gammd2 = gammad * gammad;
730 double arg1 = sqrt(1. + 4. * argv / gammd2);
731 d->vdsat = vc_eta + gammd2 * (1.-arg1) / 2.;
732 dvdsat_dvgs = (1. - 1./arg1) / s->eta;
733 dvdsat_dvbs = (gammad * (1.-arg1) + 2.*argv / (gammad*arg1))
734 / s->eta * dgamma_s_dvbs
735 + 1./arg1 + s->eta_1 * dvdsat_dvgs;
736 trace3("!use_vmax,gamma>0,argv>0",d->vdsat,dvdsat_dvgs,dvdsat_dvbs);
737 }else{
738 d->vdsat = 0.;
739 dvdsat_dvgs = dvdsat_dvbs = 0.;
740 error(bTRACE, d->long_label() + ": argv is negative\n");
741 trace2("argv<0", argv, vc);
742 trace3("!use_vmax,gamma>0,argv<=0",d->vdsat,dvdsat_dvgs,dvdsat_dvbs);
744 }else{
745 d->vdsat = vc_eta;
746 dvdsat_dvgs = 1.;
747 dvdsat_dvbs = 0.;
748 trace3("!use_vmax, gamma<=0", d->vdsat, dvdsat_dvgs, dvdsat_dvbs);
750 }else{
751 /* dvdsat_dvgs, dvdsat_dvbs deferred */
752 trace3("use_vmax", d->vdsat, dvdsat_dvgs, dvdsat_dvbs);
754 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
755 if (d->vdsat < 0.) {
756 error(bWARNING,
757 "%s: calculated vdsat (%g) < 0. using vdsat = 0.\n",
758 d->long_label().c_str(), d->vdsat);
759 d->vdsat = 0.;
761 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
762 double bsarg, dbsarg_dvbs;
764 double vbdsat = d->vbs - d->vdsat;
765 if (vbdsat <= 0.) {
766 double v_phi_ds = t->phi - vbdsat;
767 bsarg = sqrt(v_phi_ds);
768 dbsarg_dvbs = -.5 / bsarg;
769 trace3("vbdsat <= 0", vbdsat, bsarg, dbsarg_dvbs);
770 }else{
771 bsarg = t->sqrt_phi / (1. + .5 * vbdsat / t->phi);
772 dbsarg_dvbs = -.5 * bsarg * bsarg / t->phi_sqrt_phi;
773 trace3("vbdsat > 0", vbdsat, bsarg, dbsarg_dvbs);
776 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
777 /* local dvdsat_dvgs, dvdsat_dvbs maybe */
779 if (use_vmax) {
780 double bodys = bsarg*bsarg*bsarg - sarg3;
781 double gdbdvs =
782 2. * gamma_s * (bsarg*bsarg*dbsarg_dvbs - sarg*sarg*dsarg_dvbs);
783 double argv = vc_eta - d->vdsat;
784 double vqchan = argv - gamma_s * bsarg;
785 double dqdsat = -1. + gamma_s * dbsarg_dvbs;
786 double vl = m->vmax * s->l_eff;
787 double dfunds = vl * dqdsat - ueff * vqchan;
788 double dfundg = (vl - ueff * d->vdsat) / s->eta;
789 double dfundb = -vl * (1. + dqdsat - s->eta_1 / s->eta)
790 + ueff * (gdbdvs - dgamma_s_dvbs * bodys / 1.5) / s->eta;
791 dvdsat_dvgs = -dfundg / dfunds;
792 dvdsat_dvbs = -dfundb / dfunds;
793 trace2("use_vmax", dvdsat_dvgs, dvdsat_dvbs);
794 }else{
795 /* dvdsat_dvgs, dvdsat_dvbs already set */
796 trace2("!use_vmax", dvdsat_dvgs, dvdsat_dvbs);
798 assert(dvdsat_dvgs != NOT_VALID);
799 assert(dvdsat_dvbs != NOT_VALID);
801 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
802 double dl_dvgs, dl_dvds, dl_dvbs, clfact;
804 if (d->vds >= 0.) {
805 if (m->lambda == NOT_INPUT) {
806 double dldsat;
807 if (use_vmax) {
808 double xdv = m->xd / sqrt(m->neff);
809 double xlv = m->vmax * xdv / (2. * ueff);
810 double argv = d->vds - d->vdsat;
811 if (argv < 0.) {
812 argv = 0.;
814 double xls = sqrt(xlv*xlv + argv);
815 double dl = (xls-xlv) * xdv;
816 /* lambda = dl / (s->l_eff * d->vds); */
817 clfact = (1. - dl / s->l_eff);
818 dldsat = xdv / (2. * xls * s->l_eff);
819 }else{
820 double argv = (d->vds - d->vdsat) / 4.;
821 double sargv = sqrt(1. + argv*argv);
822 if (argv + sargv >= 0.) {
823 double dl = m->xd * sqrt(argv + sargv);
824 /* lambda = dl / (s->l_eff * d->vds); */
825 clfact = (1. - dl / s->l_eff);
826 /* dldsat = lambda * d->vds / (8. * sargv); */
827 dldsat = dl / (s->l_eff * 8. * sargv);
828 }else{
829 /* lambda = 0.; */
830 clfact = 1.;
831 dldsat = 0.;
832 untested();
833 error(bWARNING,
834 "%s: internal error: vds(%g) < vdsat(%g)\n",
835 d->long_label().c_str(), d->vds, d->vdsat);
838 dl_dvgs = dvdsat_dvgs * dldsat;
839 dl_dvds = - dldsat;
840 dl_dvbs = dvdsat_dvbs * dldsat;
841 }else{
842 /* lambda = m->lambda; */
843 clfact = (1. - m->lambda * d->vds);
844 dl_dvgs = dl_dvbs = 0.;
845 dl_dvds = -m->lambda;
848 /* clfact = (1. - lambda * d->vds); */
849 if (clfact < m->xwb/s->l_eff) {
850 double leff = m->xwb / (2. - (clfact * s->l_eff / m->xwb));
851 double dfact = (leff * leff) / (m->xwb * m->xwb);
852 clfact = leff / s->l_eff;
853 dl_dvgs *= dfact;
854 dl_dvds *= dfact;
855 dl_dvbs *= dfact;
857 }else{ /* vds <= 0. */
858 /* lambda = 0.; */
859 clfact = 1.;
860 dl_dvgs = dl_dvds = dl_dvbs = 0.;
861 trace1("*** vds < 0 ***", d->vds);
863 trace4("", dl_dvgs, dl_dvds, dl_dvbs, clfact);
865 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
866 /* ids, gmf, gds, gmbf */
868 d->saturated = (d->vds > d->vdsat);
869 double vdsx = (d->saturated) ? d->vdsat : d->vds;
870 double bargx = (d->saturated) ? bsarg : barg;
871 double body = bargx*bargx*bargx - sarg3;
872 double expg = (d->subthreshold) ? exp(d->vgst / vtxn) : 1.;
873 trace4("", vdsx, bargx, body, expg);
875 trace3("", t->beta, ufact, clfact);
876 double beta = t->beta * ufact / clfact;
877 double ids_on =
878 beta * ((vc - s->eta_2 * vdsx) * vdsx - (2./3.) * gamma_s * body);
879 trace4("", beta, vc, (s->eta*vdsx), (gamma_s*bargx));
880 double didvds = beta * (vc - s->eta * vdsx - gamma_s * bargx);
881 fixzero(&didvds, ids_on);
882 trace4("", beta, ids_on, didvds, d->saturated);
884 d->ids = ids_on * expg;
886 d->gmf = beta * vdsx;
887 d->gmf += ids_on * (duf_dvgs/ufact - dl_dvgs/clfact);
888 if (d->saturated) {
889 d->gmf += didvds * dvdsat_dvgs;
891 if (d->subthreshold) {
892 d->gmf = ids_on / vtxn;
893 if (d->saturated) {
894 d->gmf += didvds * dvdsat_dvgs;
896 d->gmf *= expg;
899 d->gds = (d->saturated) ? 0.: didvds;
900 d->gds += ids_on * (duf_dvds/ufact - dl_dvds/clfact);
901 if (short_channel) {
902 d->gds -= beta * (2./3.) * body * dgamma_s_dvds;
904 if (d->subthreshold) {
905 double dxndvd = dgamma_s_dvds * dsarg_dvbs;
906 double dodvds = dgamma_s_dvds * sarg + t->vt * dxndvd;
907 double gmw = d->ids * d->vgst / (vtxn * xn);
908 d->gds *= expg;
909 d->gds -= d->gmf * dodvds + gmw * dxndvd;
912 d->gmbf = beta * (s->eta_1 * vdsx - gamma_s * (sarg - bargx));
913 d->gmbf += ids_on * (duf_dvbs/ufact - dl_dvbs/clfact);
914 if (short_channel) {
915 d->gmbf -= beta * (2./3.) * body * dgamma_s_dvbs;
917 if (d->saturated) {
918 d->gmbf += didvds * dvdsat_dvbs;
920 if (d->subthreshold) {
921 double gmw = d->ids * d->vgst / (vtxn * xn);
922 d->gmbf += beta * dvon_dvbs * vdsx;
923 d->gmbf *= expg;
924 d->gmbf -= d->gmf * dvon_dvbs + gmw * dxn_dvbs;
926 trace4("", d->ids, d->gmf, d->gds, d->gmbf);
928 if (d->reversed) {
929 d->ids *= -1;
930 d->gmr = d->gmf;
931 d->gmbr = d->gmbf;
932 d->gmf = d->gmbf = 0;
933 }else{
934 d->gmr = d->gmbr = 0.;
937 /*--------------------------------------------------------------------------*/
938 /*--------------------------------------------------------------------------*/
939 /*--------------------------------------------------------------------------*/
940 /*--------------------------------------------------------------------------*/
941 // vim:ts=8:sw=2:noet