1 /* $Id: d_mos.model $ -*- 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)
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
21 *------------------------------------------------------------------
22 * data structures and defaults for mos model.
23 * internal units all mks (meters)
24 * but some user input parameters are in cm.
27 * device: mxxxx d g s b mname <device args> <model card args>
28 * model: .model mname NMOS <args>
29 * or .model mname PMOS <args>
37 #include "d_mos_base.h"
39 /*--------------------------------------------------------------------------*/
42 model_type BUILT_IN_MOS_BASE;
48 id short_to=d short_if="!OPT::rstray || s->rd == 0.";
49 is short_to=s short_if="!OPT::rstray || s->rs == 0.";
51 args db BUILT_IN_DIODE {
53 perim = double(c->pd);
54 is_raw = double(s->idsat);
55 cj_raw = double(m->cbd);
58 set_modelname(modelname());
61 args sb BUILT_IN_DIODE {
63 perim = double(c->ps);
64 is_raw = double(s->issat);
65 cj_raw = double(m->cbs);
68 set_modelname(modelname());
71 resistor Rs {s is} value="s->rs"
72 omit="!OPT::rstray || s->rs == 0.";
73 resistor Rd {d id} value="s->rd"
74 omit="!OPT::rstray || s->rd == 0.";
75 diode Ddb {b id} args="db" reverse="m->polarity==pP"
76 omit="_n[n_b].n_() == _n[n_d].n_() || s->idsat == 0.";
77 diode Dsb {b is} args="sb" reverse="m->polarity==pP"
78 omit="_n[n_b].n_() == _n[n_s].n_() || s->issat == 0.";
80 capacitor Cgs {g is} value="s->cgso" eval=Cgs
81 omit="!OPT::cstray || _n[n_g].n_() == _n[n_s].n_()";
82 capacitor Cgd {g id} value="s->cgdo" eval=Cgd
83 omit="!OPT::cstray || _n[n_g].n_() == _n[n_d].n_()";
84 capacitor Cgb {g b} value="s->cgbo" eval=Cgb
85 omit="!OPT::cstray || _n[n_b].n_() == _n[n_g].n_()";
86 fpoly_cap Cqgs {g is g b is b id b}
87 state=qgs // qgate, cgs, cggb, cgsb, cgdb
88 omit="m->cmodel != 0 || !OPT::cstray
89 || _n[n_g].n_() == _n[n_is].n_()";
90 fpoly_cap Cqgd {g id g b id b is b}
91 state=qgd // qgate, cgs, cggb, cgsb, cgdb
92 omit="m->cmodel != 0 || !OPT::cstray
93 || _n[n_g].n_() == _n[n_id].n_()";
95 fpoly_cap Cqds {id is g b is b id b}
96 state=qdrn // qdrn, cds, cdgb, cdsb, cddb
97 omit="m->cmodel != 0 || !OPT::cstray
98 || _n[n_id].n_() == _n[n_is].n_()";
100 fpoly_cap Cqbs {b is g b is b id b}
102 omit="m->cmodel != 0 || !OPT::cstray
103 || _n[n_b].n_() == _n[n_is].n_()";
105 fpoly_cap Cqbd {b id g b id b is b}
107 omit="m->cmodel != 0 || !OPT::cstray
108 || _n[n_b].n_() == _n[n_id].n_()";
109 cpoly_g Ids {id is g is id g
110 b is id b} state=idsxxx;
111 cpoly_g Idb {id b id is g is b is}
113 omit="!(m->needs_isub) || _n[n_d].n_() == _n[n_b].n_()";
114 cpoly_g Isb {is b is id g id b id}
116 omit="!(m->needs_isub) || _n[n_s].n_() == _n[n_b].n_()";
119 v = "@n_d[V] - @n_s[V]";
120 vds = "@n_d[V] - @n_s[V]";
121 vgs = "@n_g[V] - @n_s[V]";
122 vbs = "@n_b[V] - @n_s[V]";
126 vgd = "@n_g[V] - @n_d[V]";
127 vbd = "@n_b[V] - @n_d[V]";
128 vsd = "@n_s[V] - @n_d[V]";
129 vdm = "(@n_d[V] - @n_s[V] + @n_d[V] - @n_d[V]) / 2.";
130 vgm = "(@n_g[V] - @n_s[V] + @n_g[V] - @n_d[V]) / 2.";
131 vbm = "(@n_b[V] - @n_s[V] + @n_b[V] - @n_d[V]) / 2.";
132 vsm = "(@n_s[V] - @n_s[V] + @n_s[V] - @n_d[V]) / 2.";
133 vdg = "@n_d[V] - @n_g[V]";
134 vbg = "@n_b[V] - @n_g[V]";
135 vsg = "@n_s[V] - @n_g[V]";
136 vdb = "@n_d[V] - @n_b[V]";
137 vgb = "@n_g[V] - @n_b[V]";
138 vsb = "@n_s[V] - @n_b[V]";
143 "i{d}" = "(_Rd) ? @Rd[I] : (@Ids[I] - @Cgd[I] - @Ddb[I] * m->polarity)";
144 is = "(_Rs) ? @Rs[I] : (-@Ids[I] - @Cgs[I] - @Dsb[I] * m->polarity)";
145 ig = "@Cgs[I] + @Cgd[I] + @Cgb[I]";
146 ib = "- @Ddb[I] * m->polarity - @Dsb[I] * m->polarity - @Cgb[I]";
149 "cgso{vl}" = "@Cgs[NV]";
150 "cgdo{vl}" = "@Cgd[NV]";
151 "cgbo{vl}" = "@Cgb[NV]";
155 "cgs{m}" = "@Cgs[EV] - @Cgs[NV]";
156 "cgd{m}" = "@Cgd[EV] - @Cgd[NV]";
157 "cgb{m}" = "@Cgb[EV] - @Cgb[NV]";
161 gm = "(reversed) ? gmr : gmf";
162 "gmb{s}" = "(reversed) ? gmbr : gmbf";
165 vth = "von * m->polarity";
166 ids = "m->polarity * ((reversed) ? -ids : ids)";
167 "idst{ray}" = "- @Cgd[I] + @Ddb[I] * m->polarity";
168 p ="@Rs[P] +@Rd[P] +@Ddb[P] +@Dsb[P] +@Cgs[P] +@Cgd[P] +@Cgb[P] +@Ids[P]";
169 pd="@Rs[PD]+@Rd[PD]+@Ddb[PD]+@Dsb[PD]+@Cgs[PD]+@Cgd[PD]+@Cgb[PD]+@Ids[PD]";
170 ps="@Rs[PS]+@Rd[PS]+@Ddb[PS]+@Dsb[PS]+@Cgs[PS]+@Cgd[PS]+@Cgb[PS]+@Ids[PS]";
171 REgion = "static_cast<double>((!cutoff) + (!subthreshold * 2)
172 + (saturated * 4) + (sbfwd * 10) + ((vbs > vds) * 20)
173 + (punchthru * 40)) * ((reversed)? -1 : 1)";
174 SUBthreshold = "static_cast<double>(subthreshold)";
175 CUToff = "static_cast<double>(cutoff)";
176 SATurated= "static_cast<double>(saturated)";
177 TRIode = "static_cast<double>(!saturated && !subthreshold)";
178 SBFwd = "static_cast<double>(sbfwd)";
179 DBFwd = "static_cast<double>(vbs > vds)";
180 REVersed = "static_cast<double>(reversed)";
181 status = "static_cast<double>(converged() * 2)";
184 calculated_parameters {
185 // ordinary drain current
186 double ids "" default=0.;
188 double gds "dids/dvds" default=0.;
189 double gmf "dids/dvgs" default=0.;
190 double gmr "dids/dvgd" default=0.;
191 double gmbf "dids/dvbs" default=0.;
192 double gmbr "dids/dvbd" default=0.;
195 double idb "" default=0.;
196 double idbxxx "" default=0.;
197 double gdbdb "placeholder" default=0.;
198 double gdbds "disub/dvds" default=0.;
199 double gdbgs "disub/dvgs" default=0.;
200 double gdbbs "disub/dvbs" default=0.;
203 double isb "" default=0.;
204 double isbxxx "" default=0.;
205 double gsbsb "placeholder" default=0.;
206 double gsbsd "disub/dvds" default=0.;
207 double gsbgd "disub/dvgs" default=0.;
208 double gsbbd "disub/dvbs" default=0.;
211 double qgate "raw" default=0.;
212 double cgs "dqgate_vgs placeholder" default=0.;
213 double cggb "dqgate_vgb" default=0.;
214 double cgsb "dqgate_vsb" default=0.;
215 double cgdb "dqgate_vdb" default=0.;
217 double qgs "forward mode" default=0.;
218 double cgsgs "dqgs_vgs placeholder" default=0.;
219 double cgsgb "dqgs_vgb" default=0.;
220 double cgssb "dqgs_vsb" default=0.;
221 double cgsdb "dqgs_vdb" default=0.;
223 double qgd "reverse mode" default=0.;
224 double cgdgd "dqgd_vgs placeholder" default=0.;
225 double cgdgb "dqgd_vgb" default=0.;
226 double cgdsb "dqgd_vsb" default=0.;
227 double cgddb "dqgd_vdb" default=0.;
230 double qdrn "Qds" default=0.;
231 double cdsds "dqds_vds placeholder" default=0.;
232 double cdgb "dqds_vgb" default=0.;
233 double cdsb "dqds_vsb" default=0.;
234 double cddb "dqds_vdb" default=0.;
237 double qbulk "raw" default=0.;
238 double cbs "dqbs_vbs placeholder" default=0.;
239 double cbgb "dqbs_vbg" default=0.;
240 double cbsb "dqbs_vsb" default=0.;
241 double cbdb "dqbs_vdb" default=0.;
243 double qbs "Qbs forward" default=0.;
244 double cbsbs "dqbs_vbs placeholder" default=0.;
245 double cbsgb "dqbs_vbg" default=0.;
246 double cbssb "dqbs_vsb" default=0.;
247 double cbsdb "dqbs_vdb" default=0.;
249 double qbd "Qbd reverse" default=0.;
250 double cbdbd "dqbd_vbd placeholder" default=0.;
251 double cbdgb "dqbd_vbg" default=0.;
252 double cbdsb "dqbd_vsb" default=0.;
253 double cbddb "dqbd_vdb" default=0.;
256 double gtau "" default=0.;
257 double cqgb "" default=0.;
258 double cqsb "" default=0.;
259 double cqdb "" default=0.;
260 double cqbb "" default=0.;
262 double tconst "" default=0.;
263 double cgb "placeholder" default=0.; // capacitors and charges
264 double qgb "placeholder" default=0.;
265 double qgd "" default=0.;
266 double cgd "" default=0.;
267 double qgs "" default=0.;
268 //double cgs "" default=0.;
270 double vgs "terminal voltages" default=0.;
271 double vds "" default=0.;
272 double vbs "" default=0.;
274 double vdsat "saturation voltage" default=0.;
275 double vgst "vgs - von." default=0.;
276 double von "actual threshold voltage" default=0.;
277 bool reversed "flag: Vgs < 0, reverse s & d" default=false;
278 bool cutoff "flag: in cut off region" default=false;
279 bool subthreshold "flag: subthreshold region" default=false;
280 bool saturated "flag: in saturation region" default=false;
281 bool sbfwd "flag: sb diode fwd biased" default=false;
282 bool punchthru "flag: punch thru region" default=false;
287 double l_in "drawn (optical) channel length"
288 name=L positive default="OPT::defl";
289 double w_in "channel width (drawn)"
290 name=W positive default="OPT::defw";
291 double ad_in "drain area, drawn"
292 name=AD positive default="OPT::defad"
293 print_test="has_hard_value(ad_in)";
294 double as_in "source area, drawn"
295 name=AS positive default="OPT::defas"
296 print_test="has_hard_value(as_in)";
297 double pd "drain perimeter" name=PD positive default=0.0
298 print_test="has_hard_value(pd)";
299 double ps "source perimeter" name=PS positive default=0.0
300 print_test="has_hard_value(ps)";
301 double nrd "drain # squares" name=NRD positive default=1.0
302 print_test="has_hard_value(nrd)";
303 double nrs "source # squares" name=NRS positive default=1.0
304 print_test="has_hard_value(nrs)";
310 /*--------------------------------------------------------------------*/
312 STORAGE* brh = prechecked_cast<STORAGE*>(d);
315 double cap = brh->value();
317 if (m->cmodel != 0) {
318 if (p->vgst < - s->phi) { /* accumulation */
320 }else if (p->vgst < 0.) { /* depletion */
321 cap += s->cgate * (-p->vgst) / s->phi;
322 }else{ /* active, overlap only */
326 if (d->_sim->analysis_is_tran_dynamic()) {
327 cap = (brh->_y[0].f1 + brh->_y[1].f1) / 2;
328 brh->_y[0].f0 = (brh->_y[0].x - brh->_y[1].x) * cap + brh->_y[1].f0;
330 assert(d->_sim->analysis_is_static() || d->_sim->analysis_is_restore());
331 brh->_y[0].f0 = brh->_y[0].x * brh->_y[0].f1;
333 trace3(brh->long_label().c_str(), brh->_y[0].x, brh->_y[0].f0, brh->_y[0].f1);
335 /*--------------------------------------------------------------------*/
337 STORAGE* brh = prechecked_cast<STORAGE*>(d);
341 if (m->cmodel != 0) {
342 assert(p->vdsat >= 0.);
343 assert(p->vds >= 0.);
344 double vbs = (m->cmodel == 3) ? 0. : p->vbs;
345 double vdbsat = p->vdsat - vbs;
346 double vdb = p->vds - vbs;
347 double ddif = 2. * vdbsat - vdb;
349 if (!p->reversed) { // treat as Cgs
351 if (p->vdsat > p->vds) { /* linear */
352 cap = (2./3.) * s->cgate * (1. - (vdbsat*vdbsat)/(ddif*ddif));
354 cap *= 10. * p->vgst; // smooth discontinuity
358 }else{ // treat as Cgs
359 if (p->vgst >= -s->phi/2.) { /* depletion or active */
360 cap = (2./3.) * s->cgate;
361 if (p->vdsat > p->vds) { /* linear */
362 double ndif = p->vdsat - p->vds;
363 cap *= 1. - (ndif*ndif)/(ddif*ddif);
366 cap *= 1. + p->vgst / (s->phi);
367 cap *= 1. + p->vgst / (s->phi);
372 cap += brh->value(); /* else overlap only */
375 if (d->_sim->analysis_is_tran_dynamic()) {
376 cap = (brh->_y[0].f1 + brh->_y[1].f1) / 2;
377 brh->_y[0].f0 = (brh->_y[0].x - brh->_y[1].x) * cap + brh->_y[1].f0;
379 assert(d->_sim->analysis_is_static() || d->_sim->analysis_is_restore());
380 brh->_y[0].f0 = brh->_y[0].x * brh->_y[0].f1;
382 trace3(brh->long_label().c_str(), brh->_y[0].x, brh->_y[0].f0, brh->_y[0].f1);
384 /*--------------------------------------------------------------------*/
386 STORAGE* brh = prechecked_cast<STORAGE*>(d);
390 if (m->cmodel != 0) {
391 assert(p->vdsat >= 0.);
392 assert(p->vds >= 0.);
393 double vbs = (m->cmodel == 3) ? 0. : p->vbs;
394 double vdbsat = p->vdsat - vbs;
395 double vdb = p->vds - vbs;
396 double ddif = 2. * vdbsat - vdb;
398 if (p->reversed) { // treat as Cgd
400 if (p->vdsat > p->vds) { /* linear */
401 cap = (2./3.) * s->cgate * (1. - (vdbsat*vdbsat)/(ddif*ddif));
403 cap *= 10. * p->vgst; // smooth discontinuity
407 }else{ // treat as Cgs
408 if (p->vgst >= -s->phi/2.) { /* depletion or active */
409 cap = (2./3.) * s->cgate;
410 if (p->vdsat > p->vds) { /* linear */
411 double ndif = p->vdsat - p->vds;
412 cap *= 1. - (ndif*ndif)/(ddif*ddif);
415 cap *= 1. + p->vgst / (s->phi);
416 cap *= 1. + p->vgst / (s->phi);
421 cap += brh->value(); /* else overlap only */
424 if (d->_sim->analysis_is_tran_dynamic()) {
425 cap = (brh->_y[0].f1 + brh->_y[1].f1) / 2;
426 brh->_y[0].f0 = (brh->_y[0].x - brh->_y[1].x) * cap + brh->_y[1].f0;
428 assert(d->_sim->analysis_is_static() || d->_sim->analysis_is_restore());
429 brh->_y[0].f0 = brh->_y[0].x * brh->_y[0].f1;
431 trace3(brh->long_label().c_str(), brh->_y[0].x, brh->_y[0].f0, brh->_y[0].f1);
433 /*--------------------------------------------------------------------*/
434 function reverse_if_needed() {
436 error(bTRACE, long_label() + ": reversing\n");
437 error(bTRACE, "before: vds=%g vgs=%g vbs=%g\n", vds, vgs, vbs);
438 reversed = !reversed;
442 error(bTRACE, "after: vds=%g vgs=%g vbs=%g\n", vds, vgs, vbs);
443 if (OPT::dampstrategy & dsREVERSE) {
444 _sim->_fulldamp = true;
446 error(bTRACE, long_label() + ":reverse damp\n");
448 if (!(OPT::mosflags & 0040)) {
449 vbs = std::min(vbs,0.);
455 /*--------------------------------------------------------------------*/
457 /*--------------------------------------------------------------------------*/
459 /*--------------------------------------------------------------------------*/
460 bool DEV_BUILT_IN_MOS::tr_needs_eval()const
462 if (is_q_for_eval()) {
465 }else if (!converged()) {
468 const COMMON_BUILT_IN_MOS* c = prechecked_cast<const COMMON_BUILT_IN_MOS*>(common());
470 const MODEL_BUILT_IN_MOS_BASE* m=prechecked_cast<const MODEL_BUILT_IN_MOS_BASE*>(c->model());
472 polarity_t polarity = m->polarity;
473 node_t& eff_s((reversed) ? _n[n_id] : _n[n_is]);
474 node_t& eff_d((reversed) ? _n[n_is] : _n[n_id]);
475 return !(conchk(vds,polarity*(eff_d.v0()-eff_s.v0()),OPT::vntol)
476 && conchk(vgs, polarity*(_n[n_g].v0()-eff_s.v0()),
478 && conchk(vbs, polarity*(_n[n_b].v0()-eff_s.v0()),
482 /*--------------------------------------------------------------------------*/
483 bool DEV_BUILT_IN_MOS::do_tr()
485 const COMMON_BUILT_IN_MOS* c = prechecked_cast<const COMMON_BUILT_IN_MOS*>(common());
487 const MODEL_BUILT_IN_MOS_BASE* m = prechecked_cast<const MODEL_BUILT_IN_MOS_BASE*>(c->model());
490 bool was_cutoff = cutoff;
491 bool was_subthreshold = subthreshold;
492 bool was_saturated = saturated;
493 bool was_reversed = reversed;
494 bool was_sbfwd = sbfwd;
495 polarity_t polarity = m->polarity;
497 if (_sim->is_initial_step()) {
499 vds = vgs = vbs = 0.;
501 double Vds, Vgs, Vbs;
503 Vds = polarity * volts_limited(_n[n_is],_n[n_id]);
504 Vgs = polarity * volts_limited(_n[n_g],_n[n_id]);
505 Vbs = polarity * volts_limited(_n[n_b],_n[n_id]);
507 Vds = polarity * volts_limited(_n[n_id],_n[n_is]);
508 Vgs = polarity * volts_limited(_n[n_g],_n[n_is]);
509 Vbs = polarity * volts_limited(_n[n_b],_n[n_is]);
511 vgs = fet_limit_vgs(Vgs, vgs, von);
512 if (_n[n_d].n_() == _n[n_g].n_()) {
513 vds = Vds + (vgs - Vgs);
515 // Spice hacks Vds here, but my tests show that it often makes
516 // convergence worse, and never improves it.
517 // I am guessing that it does help when drain and gate are connected,
518 // and Spice does it here in case they are and cannot be determined
519 // whether they are or not.
520 // The hack maintains Vdg after Vgs limiting.
521 //Vds = Vds + (vgs - Vgs);
522 vds = fet_limit_vds(Vds, vds);
524 vbs = std::min(Vbs, 0.);
525 //vbs = pnj_limit(double Vbs, double vbs, double vt, double vcrit);
531 assert(qgate == qgate);
534 assert(qdrn == qdrn);
535 assert(qbulk == qbulk);
541 assert(qgate == qgate);
544 assert(qdrn == qdrn);
545 assert(qbulk == qbulk);
550 idsxxx = ids + vds*gds + vgs*gmr + vbs*gmbr;
551 isbxxx = isb - vds*gsbsd - vgs*gsbgd - vbs*gsbbd;
554 idsxxx = ids - vds*gds - vgs*gmf - vbs*gmbf;
555 idbxxx = idb - vds*gdbds - vgs*gdbgs - vbs*gdbbs;
561 set_converged(subckt()->do_tr());
563 trace3(long_label().c_str(), vds, vgs, vbs);
564 trace4("", ids, gmf, gds, gmbf);
565 trace4("", ids, gmr, gds, gmbr);
566 if (was_cutoff != cutoff || was_subthreshold != subthreshold
567 || was_saturated != saturated || was_reversed != reversed
568 || was_sbfwd != sbfwd) {
569 if (OPT::dampstrategy & dsDEVREGION) {
570 _sim->_fulldamp = true;
573 #if defined(DO_TRACE)
574 error(bTRACE,"%s: region change\n", long_label().c_str());
581 /*--------------------------------------------------------------------------*/
582 /*--------------------------------------------------------------------------*/