1use crate::consts::{BOLTZ, CHARGE, KOVERQ, REFTEMP, ROOT2};
14use crate::dderivs::Dderivs;
15use crate::diode::pnjlim;
16use crate::disto::{DistoCtx, DistoSetupCtx};
17use crate::matrix::{AcCtx, AnKind, InitMode, LoadCtx, MatrixBuilder, Slot, TRASH, Var};
18use crate::mos1::{MeyerPzCaps, MosGeom, MosPzConductances, fetlim, limvds, meyer_pz_load, qmeyer};
19use crate::mosdisto::{
20 DrainDerivs, MosDistoCoeffs, MosJunctions, bulk_diode, mos_disto_load, setup_tail,
21};
22
23const EPSSIL: f64 = 11.7 * 8.854214871e-12;
25
26#[derive(Clone, Debug)]
31pub struct Mos2Model {
32 pub nmos: bool,
34 pub vto: Option<f64>,
36 pub kp: Option<f64>,
38 pub gamma: Option<f64>,
40 pub phi: Option<f64>,
42 pub lambda: f64,
45 pub rd: Option<f64>,
47 pub rs: Option<f64>,
48 pub cbd: Option<f64>,
50 pub cbs: Option<f64>,
51 pub is: f64,
53 pub pb: f64,
55 pub cgso: f64,
57 pub cgdo: f64,
58 pub cgbo: f64,
59 pub rsh: Option<f64>,
61 pub cj: Option<f64>,
65 pub mj: f64,
67 pub cjsw: Option<f64>,
69 pub mjsw: f64,
71 pub js: f64,
73 pub tox: Option<f64>,
75 pub ld: f64,
77 pub u0: Option<f64>,
79 pub fc: f64,
81 pub nsub: Option<f64>,
83 pub tpg: Option<f64>,
85 pub nss: Option<f64>,
87 pub delta: f64,
89 pub uexp: f64,
92 pub ucrit: f64,
94 pub vmax: f64,
96 pub xj: f64,
98 pub neff: f64,
100 pub nfs: f64,
102 pub kf: f64,
104 pub af: f64,
105 pub nlev: i64,
110 pub gdsnoi: f64,
113 pub tnom: Option<f64>,
115}
116
117impl Mos2Model {
118 pub fn new(nmos: bool) -> Self {
119 Mos2Model {
120 nmos,
121 vto: None,
122 kp: None,
123 gamma: None,
124 phi: None,
125 lambda: 0.0,
126 rd: None,
127 rs: None,
128 cbd: None,
129 cbs: None,
130 is: 1e-14,
131 pb: 0.8,
132 cgso: 0.0,
133 cgdo: 0.0,
134 cgbo: 0.0,
135 rsh: None,
136 cj: None,
137 mj: 0.5,
138 cjsw: None,
139 mjsw: 0.33,
140 js: 0.0,
141 tox: None,
142 ld: 0.0,
143 u0: None,
144 fc: 0.5,
145 nsub: None,
146 tpg: None,
147 nss: None,
148 delta: 0.0,
149 uexp: 0.0,
150 ucrit: 1e4,
151 vmax: 0.0,
152 xj: 0.0,
153 neff: 1.0,
154 nfs: 0.0,
155 kf: 0.0,
156 af: 1.0,
157 nlev: 2,
158 gdsnoi: 1.0,
159 tnom: None,
160 }
161 }
162
163 pub(crate) fn needs_prime(&self, squares: f64) -> bool {
165 self.rd.unwrap_or(0.0) != 0.0 || (self.rsh.unwrap_or(0.0) != 0.0 && squares != 0.0)
166 }
167}
168
169fn egfet(t: f64) -> f64 {
170 1.16 - (7.02e-4 * t * t) / (t + 1108.0)
171}
172
173#[derive(Debug)]
175pub(crate) struct Mos2Temp {
176 pub temp: f64,
180 pub type_: f64,
181 pub vt: f64,
182 pub lat_diff: f64,
184 pub kf: f64,
186 pub af: f64,
187 pub nlev: i64,
189 pub gdsnoi: f64,
190 pub cgso: f64,
191 pub cgdo: f64,
192 pub cgbo: f64,
193 pub oxide_cap_factor: f64,
194 pub gamma: f64,
195 pub lambda: f64,
196 pub mj: f64,
197 pub mjsw: f64,
198 pub delta: f64,
199 pub xj: f64,
200 pub nfs: f64,
201 pub ucrit: f64,
202 pub uexp: f64,
203 pub vmax: f64,
204 pub neff: f64,
205 pub u0: f64,
209 pub nsub: f64,
211 pub xd: f64,
213 pub t_transconductance: f64,
215 pub t_phi: f64,
216 pub t_vbi: f64,
217 pub t_vto: f64,
218 pub t_sat_cur: f64,
219 pub t_sat_cur_dens: f64,
220 pub t_bulk_pot: f64,
221 pub t_dep_cap: f64,
222 pub drain_vcrit: f64,
223 pub source_vcrit: f64,
224 pub cbd: f64,
225 pub cbdsw: f64,
226 pub cbs: f64,
227 pub cbssw: f64,
228 pub f2d: f64,
229 pub f3d: f64,
230 pub f4d: f64,
231 pub f2s: f64,
232 pub f3s: f64,
233 pub f4s: f64,
234 pub drain_conductance: f64,
235 pub source_conductance: f64,
236 pub notes: Vec<String>,
237}
238
239impl Mos2Temp {
240 pub fn new(
241 name: &str,
242 m: &Mos2Model,
243 g: &MosGeom,
244 temp: f64,
245 default_tnom: f64,
246 ) -> Result<Self, String> {
247 let mut notes = Vec::new();
248 let typ = if m.nmos { 1.0 } else { -1.0 };
249 let tnom = m.tnom.unwrap_or(default_tnom);
250
251 let fact1 = tnom / REFTEMP;
252 let vtnom = tnom * KOVERQ;
253 let kt1 = BOLTZ * tnom;
254 let egfet1 = egfet(tnom);
255 let arg1 = -egfet1 / (kt1 + kt1) + 1.1150877 / (BOLTZ * (REFTEMP + REFTEMP));
256 let pbfact1 = -2.0 * vtnom * (1.5 * f64::ln(fact1) + CHARGE * arg1);
257
258 let tox = m.tox.unwrap_or(1e-7);
261 let oxide_cap_factor = 3.9 * 8.854214871e-12 / tox;
262 let u0 = m.u0.unwrap_or(600.0);
263 let mut kp = m.kp;
264 if kp.is_none() {
265 kp = Some(u0 * 1e-4 * oxide_cap_factor);
266 }
267 let mut phi = m.phi;
268 let mut gamma = m.gamma;
269 let mut vto = m.vto;
270 let mut xd = 0.0;
271 let mut nsub = 0.0;
272 if let Some(nsub_given) = m.nsub {
273 if nsub_given * 1e6 > 1.45e16 {
274 nsub = nsub_given;
275 if phi.is_none() {
276 phi = Some(f64::max(
277 0.1,
278 2.0 * vtnom * f64::ln(nsub_given * 1e6 / 1.45e16),
279 ));
280 }
281 let phi_v = phi.unwrap_or(0.6);
282 let fermis = typ * 0.5 * phi_v;
283 let tpg = m.tpg.unwrap_or(1.0);
284 let mut wkfng = 3.2;
285 if tpg != 0.0 {
286 let fermig = typ * tpg * 0.5 * egfet1;
287 wkfng = 3.25 + 0.5 * egfet1 - fermig;
288 }
289 let wkfngs = wkfng - (3.25 + 0.5 * egfet1 + fermis);
290 if gamma.is_none() {
291 gamma = Some(
292 f64::sqrt(2.0 * 11.70 * 8.854214871e-12 * CHARGE * nsub_given * 1e6)
293 / oxide_cap_factor,
294 );
295 }
296 if vto.is_none() {
297 let nss = m.nss.unwrap_or(0.0);
298 let vfb = wkfngs - nss * 1e4 * CHARGE / oxide_cap_factor;
299 vto = Some(vfb + typ * (gamma.unwrap_or(0.0) * phi_v.sqrt() + phi_v));
300 }
301 xd = f64::sqrt((EPSSIL + EPSSIL) / (CHARGE * nsub_given * 1e6));
302 } else {
303 return Err(format!("{name}: Nsub < Ni"));
304 }
305 }
306 let kp = kp.unwrap_or(2e-5);
307 let phi = phi.unwrap_or(0.6);
308 let gamma = gamma.unwrap_or(0.0);
309 let vto = vto.unwrap_or(0.0);
310
311 let vt = temp * KOVERQ;
313 let ratio = temp / tnom;
314 let fact2 = temp / REFTEMP;
315 let kt = temp * BOLTZ;
316 let egfet_t = egfet(temp);
317 let arg = -egfet_t / (kt + kt) + 1.1150877 / (BOLTZ * (REFTEMP + REFTEMP));
318 let pbfact = -2.0 * vt * (1.5 * f64::ln(fact2) + CHARGE * arg);
319
320 if g.l - 2.0 * m.ld <= 0.0 {
321 notes.push(format!("{name}: effective channel length less than zero"));
322 }
323 let ratio4 = ratio * ratio.sqrt();
324 let t_transconductance = kp / ratio4;
325 let phio = (phi - pbfact1) / fact1;
326 let t_phi = fact2 * phio + pbfact;
327 let t_vbi =
328 vto - typ * (gamma * phi.sqrt()) + 0.5 * (egfet1 - egfet_t) + typ * 0.5 * (t_phi - phi);
329 let t_vto = t_vbi + typ * gamma * t_phi.sqrt();
330 let t_sat_cur = m.is * f64::exp(-egfet_t / vt + egfet1 / vtnom);
331 let t_sat_cur_dens = m.js * f64::exp(-egfet_t / vt + egfet1 / vtnom);
332
333 let pbo = (m.pb - pbfact1) / fact1;
334 let gmaold = (m.pb - pbo) / pbo;
335 let mut capfact = 1.0 / (1.0 + m.mj * (4e-4 * (tnom - REFTEMP) - gmaold));
336 let mut t_cbd = m.cbd.unwrap_or(0.0) * capfact;
337 let mut t_cbs = m.cbs.unwrap_or(0.0) * capfact;
338 let mut t_cj = m.cj.unwrap_or(0.0) * capfact;
339 capfact = 1.0 / (1.0 + m.mjsw * (4e-4 * (tnom - REFTEMP) - gmaold));
340 let mut t_cjsw = m.cjsw.unwrap_or(0.0) * capfact;
341 let t_bulk_pot = fact2 * pbo + pbfact;
342 let gmanew = (t_bulk_pot - pbo) / pbo;
343 capfact = 1.0 + m.mj * (4e-4 * (temp - REFTEMP) - gmanew);
344 t_cbd *= capfact;
345 t_cbs *= capfact;
346 t_cj *= capfact;
347 capfact = 1.0 + m.mjsw * (4e-4 * (temp - REFTEMP) - gmanew);
348 t_cjsw *= capfact;
349 let t_dep_cap = m.fc * t_bulk_pot;
350
351 let (drain_vcrit, source_vcrit) = if t_sat_cur_dens == 0.0 || g.ad == 0.0 || g.as_ == 0.0 {
352 let v = vt * f64::ln(vt / (ROOT2 * t_sat_cur));
353 (v, v)
354 } else {
355 (
356 vt * f64::ln(vt / (ROOT2 * t_sat_cur_dens * g.ad)),
357 vt * f64::ln(vt / (ROOT2 * t_sat_cur_dens * g.as_)),
358 )
359 };
360
361 let czbd = if m.cbd.is_some() {
362 t_cbd
363 } else if m.cj.is_some() {
364 t_cj * g.ad
365 } else {
366 0.0
367 };
368 let czbdsw = if m.cjsw.is_some() { t_cjsw * g.pd } else { 0.0 };
369 let arg = 1.0 - m.fc;
370 let sarg = f64::exp(-m.mj * f64::ln(arg));
371 let sargsw = f64::exp(-m.mjsw * f64::ln(arg));
372 let f2d = czbd * (1.0 - m.fc * (1.0 + m.mj)) * sarg / arg
373 + czbdsw * (1.0 - m.fc * (1.0 + m.mjsw)) * sargsw / arg;
374 let f3d =
375 czbd * m.mj * sarg / arg / t_bulk_pot + czbdsw * m.mjsw * sargsw / arg / t_bulk_pot;
376 let f4d = czbd * t_bulk_pot * (1.0 - arg * sarg) / (1.0 - m.mj)
377 + czbdsw * t_bulk_pot * (1.0 - arg * sargsw) / (1.0 - m.mjsw)
378 - f3d / 2.0 * (t_dep_cap * t_dep_cap)
379 - t_dep_cap * f2d;
380 let czbs = if m.cbs.is_some() {
381 t_cbs
382 } else if m.cj.is_some() {
383 t_cj * g.as_
384 } else {
385 0.0
386 };
387 let czbssw = if m.cjsw.is_some() { t_cjsw * g.ps } else { 0.0 };
388 let f2s = czbs * (1.0 - m.fc * (1.0 + m.mj)) * sarg / arg
389 + czbssw * (1.0 - m.fc * (1.0 + m.mjsw)) * sargsw / arg;
390 let f3s =
391 czbs * m.mj * sarg / arg / t_bulk_pot + czbssw * m.mjsw * sargsw / arg / t_bulk_pot;
392 let f4s = czbs * t_bulk_pot * (1.0 - arg * sarg) / (1.0 - m.mj)
393 + czbssw * t_bulk_pot * (1.0 - arg * sargsw) / (1.0 - m.mjsw)
394 - f3s / 2.0 * (t_dep_cap * t_dep_cap)
395 - t_dep_cap * f2s;
396
397 let series = |r: Option<f64>, squares: f64| match (r, m.rsh) {
398 (Some(r), _) => {
399 if r != 0.0 {
400 1.0 / r
401 } else {
402 0.0
403 }
404 }
405 (None, Some(rsh)) => {
406 if rsh != 0.0 {
407 1.0 / (rsh * squares)
408 } else {
409 0.0
410 }
411 }
412 (None, None) => 0.0,
413 };
414
415 Ok(Mos2Temp {
416 temp,
417 type_: typ,
418 vt,
419 lat_diff: m.ld,
420 kf: m.kf,
421 af: m.af,
422 nlev: m.nlev,
423 gdsnoi: m.gdsnoi,
424 cgso: m.cgso,
425 cgdo: m.cgdo,
426 cgbo: m.cgbo,
427 oxide_cap_factor,
428 gamma,
429 lambda: m.lambda,
430 mj: m.mj,
431 mjsw: m.mjsw,
432 delta: m.delta,
433 xj: m.xj,
434 nfs: m.nfs,
435 ucrit: m.ucrit,
436 uexp: m.uexp,
437 vmax: m.vmax,
438 neff: m.neff,
439 u0,
440 nsub,
441 xd,
442 t_transconductance,
443 t_phi,
444 t_vbi,
445 t_vto,
446 t_sat_cur,
447 t_sat_cur_dens,
448 t_bulk_pot,
449 t_dep_cap,
450 drain_vcrit,
451 source_vcrit,
452 cbd: czbd,
453 cbdsw: czbdsw,
454 cbs: czbs,
455 cbssw: czbssw,
456 f2d,
457 f3d,
458 f4d,
459 f2s,
460 f3s,
461 f4s,
462 drain_conductance: series(m.rd, g.nrd),
463 source_conductance: series(m.rs, g.nrs),
464 notes,
465 })
466 }
467}
468
469const VBD: usize = 0;
471const VBS: usize = 1;
472const VGS: usize = 2;
473const VDS: usize = 3;
474const CAPGS: usize = 4;
475const QGS: usize = 5;
476const CQGS: usize = 6;
477const CAPGD: usize = 7;
478const QGD: usize = 8;
479const CQGD: usize = 9;
480const CAPGB: usize = 10;
481const QGB: usize = 11;
482const CQGB: usize = 12;
483const QBD: usize = 13;
484const CQBD: usize = 14;
485const QBS: usize = 15;
486const CQBS: usize = 16;
487pub(crate) const MOS2_NUM_STATES: usize = 17;
488
489const D_D: usize = 0;
491const G_G: usize = 1;
492const S_S: usize = 2;
493const B_B: usize = 3;
494const DP_DP: usize = 4;
495const SP_SP: usize = 5;
496const D_DP: usize = 6;
497const G_B: usize = 7;
498const G_DP: usize = 8;
499const G_SP: usize = 9;
500const S_SP: usize = 10;
501const B_DP: usize = 11;
502const B_SP: usize = 12;
503const DP_SP: usize = 13;
504const DP_D: usize = 14;
505const B_G: usize = 15;
506const DP_G: usize = 16;
507const SP_G: usize = 17;
508const SP_S: usize = 18;
509const DP_B: usize = 19;
510const SP_B: usize = 20;
511const SP_DP: usize = 21;
512
513const SIG1: [f64; 4] = [1.0, -1.0, 1.0, -1.0];
515const SIG2: [f64; 4] = [1.0, 1.0, -1.0, -1.0];
516
517pub(crate) struct Mos2I {
518 drain: Var,
519 gate: Var,
520 source: Var,
521 bulk: Var,
522 d_p: Var,
523 s_p: Var,
524 t: Mos2Temp,
525 geom: MosGeom,
526 off: bool,
527 ic_vds: f64,
528 ic_vgs: f64,
529 ic_vbs: f64,
530 ic_given: crate::devices::IcGiven,
531 st: usize,
532 s: [Slot; 22],
533 mode: f64,
535 von: f64,
536 vdsat: f64,
537 gm: f64,
538 gds: f64,
539 gmbs: f64,
540 gbd: f64,
541 gbs: f64,
542 capbd: f64,
543 capbs: f64,
544 cd: f64,
545 cbs: f64,
546 cbd: f64,
547 dist: MosDistoCoeffs,
549}
550
551impl Mos2I {
552 #[allow(clippy::too_many_arguments)]
553 pub fn new(
554 nodes: [Var; 4], primes: [Var; 2], t: Mos2Temp,
557 geom: MosGeom,
558 off: bool,
559 ic: (f64, f64, f64), st: usize,
561 ) -> Self {
562 Mos2I {
563 drain: nodes[0],
564 gate: nodes[1],
565 source: nodes[2],
566 bulk: nodes[3],
567 d_p: primes[0],
568 s_p: primes[1],
569 t,
570 geom,
571 off,
572 ic_vds: ic.0,
573 ic_vgs: ic.1,
574 ic_vbs: ic.2,
575 ic_given: crate::devices::IcGiven::ALL,
576 st,
577 s: [TRASH; 22],
578 mode: 1.0,
579 von: 0.0,
580 vdsat: 0.0,
581 gm: 0.0,
582 gds: 0.0,
583 gmbs: 0.0,
584 gbd: 0.0,
585 gbs: 0.0,
586 capbd: 0.0,
587 capbs: 0.0,
588 cd: 0.0,
589 cbs: 0.0,
590 cbd: 0.0,
591 dist: MosDistoCoeffs::default(),
592 }
593 }
594
595 #[allow(clippy::too_many_arguments, clippy::too_many_lines)]
601 fn eval(
602 &mut self,
603 vbs: f64,
604 vbd: f64,
605 vgs: f64,
606 vgd: f64,
607 vds: f64,
608 beta: f64,
609 oxide_cap: f64,
610 eff_l: f64,
611 vt: f64,
612 ) -> (f64, f64, f64) {
613 let t = &self.t;
614 let typ = t.type_;
615 let mode = self.mode;
616 let mut xlamda = t.lambda;
617 let lvbs = if mode == 1.0 { vbs } else { vbd };
618 let lvds = mode * vds;
619 let lvgs = if mode == 1.0 { vgs } else { vgd };
620 let phi_min_vbs = t.t_phi - lvbs;
621
622 let sphi = t.t_phi.sqrt();
625 let sphi3 = t.t_phi * sphi;
626 let (sarg, dsrgdb, d2sdb2);
627 if lvbs <= 0.0 {
628 sarg = phi_min_vbs.sqrt();
629 dsrgdb = -0.5 / sarg;
630 d2sdb2 = 0.5 * dsrgdb / phi_min_vbs;
631 } else {
632 sarg = sphi / (1.0 + 0.5 * lvbs / t.t_phi);
633 let tmp = sarg / sphi3;
634 dsrgdb = -0.5 * sarg * tmp;
635 d2sdb2 = -dsrgdb * tmp;
636 }
637 let (mut barg, mut dbrgdb, d2bdb2);
638 if lvds - lvbs >= 0.0 {
639 barg = (phi_min_vbs + lvds).sqrt();
640 dbrgdb = -0.5 / barg;
641 d2bdb2 = 0.5 * dbrgdb / (phi_min_vbs + lvds);
642 } else {
643 barg = sphi / (1.0 + 0.5 * (lvbs - lvds) / t.t_phi);
644 let tmp = barg / sphi3;
645 dbrgdb = -0.5 * barg * tmp;
646 d2bdb2 = -dbrgdb * tmp;
647 }
648
649 let factor = 0.125 * t.delta * 2.0 * std::f64::consts::PI * EPSSIL / oxide_cap * eff_l;
652 let eta = 1.0 + factor;
653 let vbin = t.t_vbi * typ + factor * phi_min_vbs;
654 let (gamasd, dgddvb, dgdvds, dgddb2);
655 if t.gamma > 0.0 || t.nsub > 0.0 {
656 let xwd = t.xd * barg;
657 let xws = t.xd * sarg;
658 let mut argss = 0.0;
659 let mut argsd = 0.0;
660 let mut dbargs = 0.0;
661 let mut dbargd = 0.0;
662 let mut dgdvds_l = 0.0;
663 let mut dgddb2_l = 0.0;
664 let mut args = 0.0;
665 let mut argd = 0.0;
666 let mut argxs = 0.0;
667 let mut argxd = 0.0;
668 if t.xj > 0.0 {
669 let tmp = 2.0 / t.xj;
670 argxs = 1.0 + xws * tmp;
671 argxd = 1.0 + xwd * tmp;
672 args = argxs.sqrt();
673 argd = argxd.sqrt();
674 let tmp = 0.5 * t.xj / eff_l;
675 argss = tmp * (args - 1.0);
676 argsd = tmp * (argd - 1.0);
677 }
678 gamasd = t.gamma * (1.0 - argss - argsd);
679 let dbxwd = t.xd * dbrgdb;
680 let dbxws = t.xd * dsrgdb;
681 if t.xj > 0.0 {
682 let tmp = 0.5 / eff_l;
683 dbargs = tmp * dbxws / args;
684 dbargd = tmp * dbxwd / argd;
685 let dasdb2 =
686 -t.xd * (d2sdb2 + dsrgdb * dsrgdb * t.xd / (t.xj * argxs)) / (eff_l * args);
687 let daddb2 =
688 -t.xd * (d2bdb2 + dbrgdb * dbrgdb * t.xd / (t.xj * argxd)) / (eff_l * argd);
689 dgddb2_l = -0.5 * t.gamma * (dasdb2 + daddb2);
690 }
691 dgddvb = -t.gamma * (dbargs + dbargd);
692 if t.xj > 0.0 {
693 let ddxwd = -dbxwd;
694 dgdvds_l = -t.gamma * 0.5 * ddxwd / (eff_l * argd);
695 }
696 dgdvds = dgdvds_l;
697 dgddb2 = dgddb2_l;
698 } else {
699 gamasd = t.gamma;
700 dgddvb = 0.0;
701 dgdvds = 0.0;
702 dgddb2 = 0.0;
703 }
704
705 let cdrain;
706 let mut von = vbin + gamasd * sarg;
707 let vth = von;
708 let mut vdsat = 0.0;
709 let mut gm = self.gm;
713 let gds;
714 let mut gmbs = self.gmbs;
715
716 'doneval: {
717 let mut xn = 1.0;
718 let mut argg = 0.0;
719 let vgst;
720 if t.nfs != 0.0 && oxide_cap != 0.0 {
721 let cfs = CHARGE * t.nfs * 1e4 ;
722 let cdonco = -(gamasd * dsrgdb + dgddvb * sarg) + factor;
723 xn = 1.0 + cfs / oxide_cap * self.geom.w * eff_l + cdonco;
724 let tmp = vt * xn;
725 von += tmp;
726 argg = 1.0 / tmp;
727 vgst = lvgs - von;
728 } else {
729 vgst = lvgs - von;
730 if lvgs <= von {
738 gds = 0.0;
740 gm = 0.0;
741 gmbs = 0.0;
742 cdrain = 0.0;
743 break 'doneval;
744 }
745 }
746
747 let sarg3 = sarg * sarg * sarg;
749 let sbiarg = t.t_bulk_pot.sqrt();
750 let mut gammad = gamasd;
751 let mut dgdvbs = dgddvb;
752 let mut body = barg * barg * barg - sarg3;
753 let mut gdbdv = 2.0 * gammad * (barg * barg * dbrgdb - sarg * sarg * dsrgdb);
754 let mut dodvbs = -factor + dgdvbs * sarg + gammad * dsrgdb;
755 let mut dxndvb = 0.0;
756 let mut dxndvd = 0.0;
757 let mut dodvds = 0.0;
758 if t.nfs != 0.0 && oxide_cap != 0.0 {
759 dxndvb = 2.0 * dgdvbs * dsrgdb + gammad * d2sdb2 + dgddb2 * sarg;
760 dodvbs += vt * dxndvb;
761 dxndvd = dgdvds * dsrgdb;
762 dodvds = dgdvds * sarg + vt * dxndvd;
763 }
764
765 let (ufact, ueff, dudvgs, dudvds, dudvbs);
767 let crit = t.ucrit * 100.0 * EPSSIL / t.oxide_cap_factor;
768 if oxide_cap > 0.0 && vgst > crit {
769 let udenom = vgst;
770 ufact = f64::exp(t.uexp * f64::ln(crit / udenom));
771 ueff = t.u0 * 1e-4 * ufact;
772 dudvgs = -ufact * t.uexp / udenom;
773 dudvds = 0.0;
774 dudvbs = t.uexp * ufact * dodvbs / vgst;
775 } else {
776 ufact = 1.0;
777 ueff = t.u0 * 1e-4;
778 dudvgs = 0.0;
779 dudvds = 0.0;
780 dudvbs = 0.0;
781 }
782
783 let mut vgsx = lvgs;
786 gammad = gamasd / eta;
787 dgdvbs = dgddvb;
788 if t.nfs != 0.0 && oxide_cap != 0.0 {
789 vgsx = f64::max(lvgs, von);
790 }
791 let mut dsdvgs;
792 let mut dsdvbs;
793 if gammad > 0.0 {
794 let gammd2 = gammad * gammad;
795 let argv = (vgsx - vbin) / eta + phi_min_vbs;
796 if argv <= 0.0 {
797 vdsat = 0.0;
798 dsdvgs = 0.0;
799 dsdvbs = 0.0;
800 } else {
801 let arg = (1.0 + 4.0 * argv / gammd2).sqrt();
802 vdsat = (vgsx - vbin) / eta + gammd2 * (1.0 - arg) / 2.0;
803 vdsat = f64::max(vdsat, 0.0);
804 dsdvgs = (1.0 - 1.0 / arg) / eta;
805 dsdvbs = (gammad * (1.0 - arg) + 2.0 * argv / (gammad * arg)) / eta * dgdvbs
806 + 1.0 / arg
807 + factor * dsdvgs;
808 }
809 } else {
810 vdsat = f64::max((vgsx - vbin) / eta, 0.0);
811 dsdvgs = 1.0;
812 dsdvbs = 0.0;
813 }
814 if t.vmax > 0.0 {
815 let v1 = (vgsx - vbin) / eta + phi_min_vbs;
817 let v2 = phi_min_vbs;
818 let xv = t.vmax * eff_l / ueff;
819 let a1 = gammad / 0.75;
820 let b1 = -2.0 * (v1 + xv);
821 let c1 = -2.0 * gammad * xv;
822 let d1 = 2.0 * v1 * (v2 + xv) - v2 * v2 - 4.0 / 3.0 * gammad * sarg3;
823 let a = -b1;
824 let b = a1 * c1 - 4.0 * d1;
825 let c = -d1 * (a1 * a1 - 4.0 * b1) - c1 * c1;
826 let r = -a * a / 3.0 + b;
827 let s = 2.0 * a * a * a / 27.0 - a * b / 3.0 + c;
828 let r3 = r * r * r;
829 let s2 = s * s;
830 let p = s2 / 4.0 + r3 / 27.0;
831 let p0 = p.abs();
832 let p2 = p0.sqrt();
833 let y3 = if p < 0.0 {
834 let ro = (s2 / 4.0 + p0).sqrt();
835 let ro = f64::exp(f64::ln(ro) / 3.0);
836 let fi = f64::atan(-2.0 * p2 / s);
837 2.0 * ro * f64::cos(fi / 3.0) - a / 3.0
838 } else {
839 let p3 = f64::exp(f64::ln((-s / 2.0 + p2).abs()) / 3.0);
840 let p4 = f64::exp(f64::ln((-s / 2.0 - p2).abs()) / 3.0);
841 p3 + p4 - a / 3.0
842 };
843 let a3 = (a1 * a1 / 4.0 - b1 + y3).sqrt();
844 let b3 = (y3 * y3 / 4.0 - d1).sqrt();
845 let mut x4 = [0.0f64; 8];
846 let mut iknt = 0usize;
847 for i in 0..4 {
848 let a4 = a1 / 2.0 + SIG1[i] * a3;
849 let b4 = y3 / 2.0 + SIG2[i] * b3;
850 let delta4 = a4 * a4 / 4.0 - b4;
851 if delta4 < 0.0 {
852 continue;
853 }
854 let tmp = delta4.sqrt();
855 x4[iknt] = -a4 / 2.0 + tmp;
856 iknt += 1;
857 x4[iknt] = -a4 / 2.0 - tmp;
858 iknt += 1;
859 }
860 let mut jknt = 0usize;
861 let mut xvalid = 0.0;
862 for &x in x4.iter().take(iknt) {
863 if x <= 0.0 {
864 continue;
865 }
866 let mut poly4 = x * x * x * x + a1 * x * x * x;
867 poly4 += b1 * x * x + c1 * x + d1;
868 if poly4.abs() > 1.0e-6 {
869 continue;
870 }
871 jknt += 1;
872 if jknt <= 1 {
873 xvalid = x;
874 }
875 if x > xvalid {
876 continue;
877 }
878 xvalid = x;
879 }
880 if jknt > 0 {
881 vdsat = xvalid * xvalid - phi_min_vbs;
882 }
883 }
884
885 let mut dldvgs = 0.0;
887 let mut dldvds = 0.0;
888 let mut dldvbs = 0.0;
889 let mut bsarg = 0.0;
890 let mut dbsrdb = 0.0;
891 let mut bodys = 0.0;
892 let mut gdbdvs = 0.0;
893 if lvds != 0.0 {
894 gammad = gamasd;
895 if lvbs - vdsat <= 0.0 {
896 bsarg = (vdsat + phi_min_vbs).sqrt();
897 dbsrdb = -0.5 / bsarg;
898 } else {
899 bsarg = sphi / (1.0 + 0.5 * (lvbs - vdsat) / t.t_phi);
900 dbsrdb = -0.5 * bsarg * bsarg / sphi3;
901 }
902 bodys = bsarg * bsarg * bsarg - sarg3;
903 gdbdvs = 2.0 * gammad * (bsarg * bsarg * dbsrdb - sarg * sarg * dsrgdb);
904 'line610: {
905 let dldsat;
906 if t.vmax <= 0.0 {
907 if t.nsub == 0.0 || xlamda > 0.0 {
908 break 'line610;
909 }
910 let argv = (lvds - vdsat) / 4.0;
911 let sargv = (1.0 + argv * argv).sqrt();
912 let arg = (argv + sargv).sqrt();
913 let xlfact = t.xd / (eff_l * lvds);
914 xlamda = xlfact * arg;
915 dldsat = lvds * xlamda / (8.0 * sargv);
916 } else {
917 let argv = (vgsx - vbin) / eta - vdsat;
918 let xdv = t.xd / t.neff.sqrt();
919 let xlv = t.vmax * xdv / (2.0 * ueff);
920 let vqchan = argv - gammad * bsarg;
921 let dqdsat = -1.0 + gammad * dbsrdb;
922 let vl = t.vmax * eff_l;
923 let dfunds = vl * dqdsat - ueff * vqchan;
924 let dfundg = (vl - ueff * vdsat) / eta;
925 let dfundb = -vl * (1.0 + dqdsat - factor / eta)
926 + ueff * (gdbdvs - dgdvbs * bodys / 1.5) / eta;
927 dsdvgs = -dfundg / dfunds;
928 dsdvbs = -dfundb / dfunds;
929 if t.nsub == 0.0 || xlamda > 0.0 {
930 break 'line610;
931 }
932 let argv = f64::max(lvds - vdsat, 0.0);
933 let xls = (xlv * xlv + argv).sqrt();
934 let xlfact = xdv / (eff_l * lvds);
935 xlamda = xlfact * (xls - xlv);
936 dldsat = xdv / (2.0 * xls) / eff_l;
937 }
938 dldvgs = dldsat * dsdvgs;
939 dldvds = -xlamda + dldsat;
940 dldvbs = dldsat * dsdvbs;
941 }
942 }
943
944 let mut xwb = t.xd * sbiarg;
946 let xld = eff_l - xwb;
947 let mut clfact = 1.0 - xlamda * lvds;
948 dldvds = -xlamda - dldvds;
949 let mut xleff = eff_l * clfact;
950 let deltal = xlamda * lvds * eff_l;
951 if t.nsub == 0.0 {
952 xwb = 0.25e-6;
953 }
954 if xleff < xwb {
955 xleff = xwb / (1.0 + (deltal - xld) / xwb);
956 clfact = xleff / eff_l;
957 let dfact = xleff * xleff / (xwb * xwb);
958 dldvgs *= dfact;
959 dldvds *= dfact;
960 dldvbs *= dfact;
961 }
962
963 let beta1 = beta * ufact / clfact;
965
966 gammad = gamasd;
968 dgdvbs = dgddvb;
969 if lvds <= 1.0e-10 {
970 if lvgs <= von {
971 if t.nfs == 0.0 || oxide_cap == 0.0 {
972 gds = 0.0;
973 } else {
974 gds = beta1 * (von - vbin - gammad * sarg) * f64::exp(argg * (lvgs - von));
975 }
976 } else {
977 gds = beta1 * (lvgs - vbin - gammad * sarg);
978 }
979 gm = 0.0;
980 gmbs = 0.0;
981 cdrain = 0.0;
982 break 'doneval;
983 }
984
985 if lvgs <= von {
986 if vdsat <= 0.0 {
988 gds = 0.0;
989 if lvgs > vth {
990 cdrain = 0.0;
993 break 'doneval;
994 }
995 gm = 0.0;
996 gmbs = 0.0;
997 cdrain = 0.0;
998 break 'doneval;
999 }
1000 let vdson = f64::min(vdsat, lvds);
1001 if lvds > vdsat {
1002 barg = bsarg;
1003 #[allow(unused_assignments)] {
1005 dbrgdb = dbsrdb;
1006 }
1007 body = bodys;
1008 gdbdv = gdbdvs;
1009 }
1010 let cdson =
1011 beta1 * ((von - vbin - eta * vdson * 0.5) * vdson - gammad * body / 1.5);
1012 let didvds = beta1 * (von - vbin - eta * vdson - gammad * barg);
1013 let mut gdson = -cdson * dldvds / clfact - beta1 * dgdvds * body / 1.5;
1014 if lvds < vdsat {
1015 gdson += didvds;
1016 }
1017 let mut gbson = -cdson * dldvbs / clfact
1018 + beta1 * (dodvbs * vdson + factor * vdson - dgdvbs * body / 1.5 - gdbdv);
1019 if lvds > vdsat {
1020 gbson += didvds * dsdvbs;
1021 }
1022 let expg = f64::exp(argg * (lvgs - von));
1023 cdrain = cdson * expg;
1024 let gmw = cdrain * argg;
1025 gm = gmw;
1026 if lvds > vdsat {
1027 gm = gmw + didvds * dsdvgs * expg;
1028 }
1029 let tmp = gmw * (lvgs - von) / xn;
1030 gds = gdson * expg - gm * dodvds - tmp * dxndvd;
1031 gmbs = gbson * expg - gm * dodvbs - tmp * dxndvb;
1032 break 'doneval;
1033 }
1034
1035 if lvds <= vdsat {
1036 cdrain = beta1 * ((lvgs - vbin - eta * lvds / 2.0) * lvds - gammad * body / 1.5);
1038 let arg = cdrain * (dudvgs / ufact - dldvgs / clfact);
1039 gm = arg + beta1 * lvds;
1040 let arg = cdrain * (dudvds / ufact - dldvds / clfact);
1041 gds =
1042 arg + beta1 * (lvgs - vbin - eta * lvds - gammad * barg - dgdvds * body / 1.5);
1043 let arg = cdrain * (dudvbs / ufact - dldvbs / clfact);
1044 gmbs = arg - beta1 * (gdbdv + dgdvbs * body / 1.5 - factor * lvds);
1045 } else {
1046 cdrain = beta1 * ((lvgs - vbin - eta * vdsat / 2.0) * vdsat - gammad * bodys / 1.5);
1048 let arg = cdrain * (dudvgs / ufact - dldvgs / clfact);
1049 gm = arg
1050 + beta1 * vdsat
1051 + beta1 * (lvgs - vbin - eta * vdsat - gammad * bsarg) * dsdvgs;
1052 gds = -cdrain * dldvds / clfact - beta1 * dgdvds * bodys / 1.5;
1053 let arg = cdrain * (dudvbs / ufact - dldvbs / clfact);
1054 gmbs = arg - beta1 * (gdbdvs + dgdvbs * bodys / 1.5 - factor * vdsat)
1055 + beta1 * (lvgs - vbin - eta * vdsat - gammad * bsarg) * dsdvbs;
1056 }
1057 }
1058
1059 self.gm = gm;
1060 self.gds = gds;
1061 self.gmbs = gmbs;
1062 (cdrain, von, vdsat)
1063 }
1064}
1065
1066impl Mos2I {
1067 #[allow(
1089 unused_assignments,
1090 clippy::too_many_lines,
1091 clippy::cognitive_complexity
1092 )]
1093 fn disto_coeffs(&self, sol: &[f64], gmin: f64) -> MosDistoCoeffs {
1094 let t = &self.t;
1095 let g = &self.geom;
1096 let typ = t.type_;
1097 let vt = t.vt;
1098 let eff_l = g.l - 2.0 * t.lat_diff;
1099 let (drain_sat_cur, source_sat_cur) =
1100 if t.t_sat_cur_dens == 0.0 || g.ad == 0.0 || g.as_ == 0.0 {
1101 (t.t_sat_cur, t.t_sat_cur)
1102 } else {
1103 (t.t_sat_cur_dens * g.ad, t.t_sat_cur_dens * g.as_)
1104 };
1105 let beta = t.t_transconductance * g.w / eff_l;
1106 let oxide_cap = t.oxide_cap_factor * eff_l * g.w;
1107
1108 let vbs = typ * (sol[self.bulk] - sol[self.s_p]);
1109 let vgs = typ * (sol[self.gate] - sol[self.s_p]);
1110 let vds = typ * (sol[self.d_p] - sol[self.s_p]);
1111 let vbd = vbs - vds;
1112 let vgd = vgs - vds;
1113
1114 let gbs = bulk_diode(vbs, source_sat_cur, vt, gmin, typ);
1115 let gbd = bulk_diode(vbd, drain_sat_cur, vt, gmin, typ);
1116 let mode = if vds >= 0.0 { 1.0 } else { -1.0 };
1117
1118 let d_p = Dderivs::var_p();
1119 let d_q = Dderivs::var_q();
1120 let d_r = Dderivs::var_r();
1121 let d_zero = Dderivs::default();
1122 let mut d_cdrain = Dderivs::default();
1123
1124 let lvbs = if mode == 1.0 { vbs } else { vbd };
1125 let lvds = mode * vds;
1126 let lvgs = if mode == 1.0 { vgs } else { vgd };
1127 let phi_min_vbs = t.t_phi - lvbs;
1128 let mut xlamda = t.lambda;
1129 let mut d_xlamda = d_zero;
1131 let mut vdsat = 0.0;
1132 let mut von;
1133 let vgst;
1134 let mut sphi = 0.0;
1135 let mut sphi3 = 0.0;
1136 let mut doneval = false;
1137
1138 let d_phi_min_vbs = Dderivs {
1139 value: phi_min_vbs,
1140 d1_q: -1.0,
1141 ..d_zero
1142 };
1143
1144 'eval: {
1147 let (sarg, d_sarg, dsrgdb, d_dsrgdb);
1148 if lvbs <= 0.0 {
1149 sarg = f64::sqrt(phi_min_vbs);
1150 d_sarg = d_phi_min_vbs.sqrt();
1151 dsrgdb = -0.5 / sarg;
1152 d_dsrgdb = d_sarg.inv().times(-0.5);
1153 } else {
1154 sphi = f64::sqrt(t.t_phi);
1155 sphi3 = t.t_phi * sphi;
1156 sarg = sphi / (1.0 + 0.5 * lvbs / t.t_phi);
1157 let mut s = Dderivs { value: lvbs, ..d_q }.times(0.5 / t.t_phi);
1158 s.value += 1.0;
1159 d_sarg = s.inv().times(sphi);
1160 dsrgdb = -0.5 * sarg * sarg / sphi3;
1161 d_dsrgdb = d_sarg.mult(&d_sarg).times(-0.5 / sphi3);
1162 }
1163 let (mut barg, mut d_barg, dbrgdb, d_dbrgdb);
1164 if lvds - lvbs >= 0.0 {
1165 barg = f64::sqrt(phi_min_vbs + lvds);
1166 let mut b = d_phi_min_vbs;
1167 b.value += lvds;
1168 b.d1_r += 1.0;
1169 d_barg = b.sqrt();
1170 dbrgdb = -0.5 / barg;
1171 d_dbrgdb = d_barg.inv().times(-0.5);
1172 } else {
1173 sphi = f64::sqrt(t.t_phi);
1174 sphi3 = t.t_phi * sphi;
1175 barg = sphi / (1.0 + 0.5 * (lvbs - lvds) / t.t_phi);
1176 let mut b = Dderivs {
1177 value: lvbs - lvds,
1178 ..d_q
1179 };
1180 b.d1_r -= 1.0;
1181 let mut b = b.times(0.5 / t.t_phi);
1182 b.value += 1.0;
1183 d_barg = b.inv().times(sphi);
1184 dbrgdb = -0.5 * barg * barg / sphi3;
1185 d_dbrgdb = d_barg.mult(&d_barg).times(-0.5 / sphi3);
1186 }
1187
1188 let factor = 0.125 * t.delta * 2.0 * std::f64::consts::PI * EPSSIL / oxide_cap * eff_l;
1190 let eta = 1.0 + factor;
1191 let vbin = t.t_vbi * typ + factor * phi_min_vbs;
1192 let mut d_vbin = d_phi_min_vbs.times(factor);
1193 d_vbin.value += t.t_vbi * typ;
1194
1195 let (gamasd, d_gamasd, dgddvb, d_dgddvb);
1196 if t.gamma > 0.0 || t.nsub > 0.0 {
1197 let xwd = t.xd * barg;
1198 let xws = t.xd * sarg;
1199 let d_xwd = d_barg.times(t.xd);
1200 let d_xws = d_sarg.times(t.xd);
1201 let mut argss = 0.0;
1202 let mut argsd = 0.0;
1203 let mut d_argss = d_zero;
1204 let mut d_argsd = d_zero;
1205 let (mut args, mut argd) = (0.0, 0.0);
1206 let (mut d_args, mut d_argd) = (d_zero, d_zero);
1207 if t.xj > 0.0 {
1208 let tmp = 2.0 / t.xj;
1209 let argxs = 1.0 + xws * tmp;
1210 let mut d_argxs = d_xws.times(tmp);
1211 d_argxs.value += 1.0;
1212 let argxd = 1.0 + xwd * tmp;
1213 let mut d_argxd = d_xwd.times(tmp);
1214 d_argxd.value += 1.0;
1215 args = f64::sqrt(argxs);
1216 d_args = d_argxs.sqrt();
1217 argd = f64::sqrt(argxd);
1218 d_argd = d_argxd.sqrt();
1219 let tmp = 0.5 * t.xj / eff_l;
1220 argss = tmp * (args - 1.0);
1221 d_argss = d_args.times(tmp);
1222 d_argss.value -= tmp;
1223 argsd = tmp * (argd - 1.0);
1224 d_argsd = d_argd.times(tmp);
1225 d_argsd.value -= tmp;
1226 }
1227 gamasd = t.gamma * (1.0 - argss - argsd);
1228 let mut gs = d_argss.plus(&d_argsd);
1229 gs.value -= 1.0;
1230 d_gamasd = gs.times(-t.gamma);
1231 let dbxwd = t.xd * dbrgdb;
1232 let dbxws = t.xd * dsrgdb;
1233 let d_dbxwd = d_dbrgdb.times(t.xd);
1234 let d_dbxws = d_dsrgdb.times(t.xd);
1235 let (mut dbargs, mut dbargd) = (0.0, 0.0);
1236 let (mut d_dbargs, mut d_dbargd) = (d_zero, d_zero);
1237 if t.xj > 0.0 {
1238 let tmp = 0.5 / eff_l;
1239 dbargs = tmp * dbxws / args;
1240 dbargd = tmp * dbxwd / argd;
1241 d_dbargs = d_dbxws.div(&d_args).times(tmp);
1242 d_dbargd = d_dbxwd.div(&d_argd).times(tmp);
1243 }
1244 dgddvb = -t.gamma * (dbargs + dbargd);
1245 d_dgddvb = d_dbargs.plus(&d_dbargd).times(-t.gamma);
1246 } else {
1247 gamasd = t.gamma;
1248 d_gamasd = Dderivs::constant(t.gamma);
1249 dgddvb = 0.0;
1250 d_dgddvb = d_zero;
1251 }
1252 von = vbin + gamasd * sarg;
1253 let mut d_von = d_gamasd.mult(&d_sarg).plus(&d_vbin);
1254 let mut d_vdsat = d_zero;
1255 let mut d_argg = d_zero;
1256 let mut d_vgst;
1257 if t.nfs != 0.0 && oxide_cap != 0.0 {
1258 let cfs = CHARGE * t.nfs * 1e4;
1259 let cdonco = -(gamasd * dsrgdb + dgddvb * sarg) + factor;
1260 let d_dummy = d_dgddvb.mult(&d_sarg);
1261 let mut d_cdonco = d_gamasd.mult(&d_dsrgdb).plus(&d_dummy).times(-1.0);
1262 d_cdonco.value += factor;
1263 let xn = 1.0 + cfs / oxide_cap * g.w * eff_l + cdonco;
1264 let d_xn = Dderivs {
1265 value: xn,
1266 ..d_cdonco
1267 };
1268 let tmp = vt * xn;
1269 let d_tmp = d_xn.times(vt);
1270 von += tmp;
1271 d_von = d_von.plus(&d_tmp);
1272 d_argg = d_tmp.inv();
1273 vgst = lvgs - von;
1274 d_vgst = d_von.times(-1.0).plus(&d_p);
1275 d_vgst.value += lvgs;
1276 } else {
1277 vgst = lvgs - von;
1278 d_vgst = d_von.times(-1.0).plus(&d_p);
1279 d_vgst.value += lvgs;
1280 if lvgs <= von {
1281 break 'eval;
1283 }
1284 }
1285
1286 let sarg3 = sarg * sarg * sarg;
1287 let d_sarg3 = d_sarg.cube();
1288 let sbiarg = f64::sqrt(t.t_bulk_pot);
1289 let mut gammad;
1290 let mut d_gammad;
1291 let mut body = barg * barg * barg - sarg3;
1292 let mut d_body = d_sarg3.times(-1.0).plus(&d_barg.cube());
1293 let _ = body;
1294
1295 let (ufact, d_ufact, ueff, d_ueff);
1297 let tmp = t.ucrit * 100.0 * EPSSIL / t.oxide_cap_factor;
1298 if oxide_cap > 0.0 && vgst > tmp {
1299 let udenom = vgst;
1300 let d_udenom = d_vgst;
1301 ufact = f64::exp(t.uexp * f64::ln(tmp / udenom));
1302 let d_dummy = d_udenom.inv().times(tmp);
1303 d_ufact = d_dummy.pow(t.uexp);
1304 ueff = t.u0 * 1e-4 * ufact;
1305 d_ueff = d_ufact.times(t.u0 * 1e-4);
1306 } else {
1307 ufact = 0.0;
1308 d_ufact = d_zero;
1309 ueff = t.u0 * 1e-4;
1310 d_ueff = Dderivs::constant(ueff);
1311 }
1312 let _ = ufact;
1313
1314 let mut vgsx = lvgs;
1316 let mut d_vgsx = Dderivs { value: lvgs, ..d_p };
1317 gammad = gamasd / eta;
1318 d_gammad = d_gamasd.times(1.0 / eta);
1319 if t.nfs != 0.0 && oxide_cap != 0.0 {
1320 vgsx = f64::max(lvgs, von);
1321 d_vgsx = if lvgs > von {
1322 Dderivs { value: lvgs, ..d_p }
1323 } else {
1324 d_von
1325 };
1326 }
1327 if gammad > 0.0 {
1328 let gammd2 = gammad * gammad;
1329 let d_gammd2 = d_gammad.mult(&d_gammad);
1330 let argv = (vgsx - vbin) / eta + phi_min_vbs;
1331 let d_argv = d_vgsx.plus(&d_vgsx).times(1.0 / eta).plus(&d_phi_min_vbs);
1332 if argv <= 0.0 {
1333 vdsat = 0.0;
1334 d_vdsat = d_zero;
1335 } else {
1336 let mut d_arg = d_argv.div(&d_gammd2).times(4.0);
1337 d_arg.value += 1.0;
1338 let _ = (d_arg.sqrt(), gammd2);
1339 let d_dumarg = d_zero;
1342 let d_dummy = d_dumarg.times(-1.0).plus(&d_gammd2).times(0.5);
1343 let v = d_phi_min_vbs.times(-1.0).plus(&d_argv);
1344 d_vdsat = d_dummy.plus(&v);
1345 vdsat = f64::max(vdsat, 0.0);
1346 }
1347 } else {
1348 vdsat = (vgsx - vbin) / eta;
1349 d_vdsat = d_vbin.times(-1.0).plus(&d_vgsx).times(1.0 / eta);
1350 vdsat = f64::max(vdsat, 0.0);
1351 }
1352 if t.vmax > 0.0 {
1353 let gammd2 = gammad * gammad;
1355 let d_gammd2 = d_gammad.mult(&d_gammad);
1356 let v1 = (vgsx - vbin) / eta + phi_min_vbs;
1357 let d_v1 = d_vbin
1358 .times(-1.0)
1359 .plus(&d_vgsx)
1360 .times(1.0 / eta)
1361 .plus(&d_phi_min_vbs);
1362 let v2 = phi_min_vbs;
1363 let d_v2 = d_phi_min_vbs;
1364 let xv = t.vmax * eff_l / ueff;
1365 let d_xv = d_ueff.inv().times(t.vmax * eff_l);
1366 let a1 = gammad / 0.75;
1367 let d_a1 = d_gammad.times(4.0 / 3.0);
1368 let b1 = -2.0 * (v1 + xv);
1369 let d_b1 = d_v1.plus(&d_xv).times(-2.0);
1370 let c1 = -2.0 * gammad * xv;
1371 let d_c1 = d_gammad.mult(&d_xv).times(-2.0);
1372 let d1 = 2.0 * v1 * (v2 + xv) - v2 * v2 - 4.0 / 3.0 * gammad * sarg3;
1373 let mut d_d1 = d_gammad.mult(&d_sarg3).times(4.0 / 3.0);
1374 d_d1 = d_d1.plus(&d_v2.mult(&d_v2)).times(-1.0);
1375 d_d1 = d_d1.plus(&d_v2.plus(&d_xv).mult(&d_v1).times(2.0));
1376 let a = -b1;
1377 let d_a = d_b1.times(-1.0);
1378 let b = a1 * c1 - 4.0 * d1;
1379 let d_b = d_d1.times(-4.0).plus(&d_a1.mult(&d_c1));
1380 let c = -d1 * (a1 * a1 - 4.0 * b1) - c1 * c1;
1381 let d_dummy = d_b1.times(-4.0).plus(&d_a1.mult(&d_a1));
1382 let d_c = d_dummy.mult(&d_d1).plus(&d_c1.mult(&d_c1)).times(-1.0);
1383 let r = -a * a / 3.0 + b;
1384 let d_arr = d_a.mult(&d_a).times(-1.0 / 3.0).plus(&d_b);
1385 let s = 2.0 * a * a * a / 27.0 - a * b / 3.0 + c;
1386 let d_s = d_a
1387 .cube()
1388 .times(2.0 / 27.0)
1389 .plus(&d_c)
1390 .plus(&d_a.mult(&d_b).times(-1.0 / 3.0));
1391 let r3 = r * r * r;
1392 let d_r3 = d_arr.cube();
1393 let s2 = s * s;
1394 let d_s2 = d_s.mult(&d_s);
1395 let p = s2 / 4.0 + r3 / 27.0;
1396 let d_pee = d_s2.times(0.25).plus(&d_r3.times(1.0 / 27.0));
1397 let p0 = p.abs();
1398 let d_p0 = if p < 0.0 { d_pee.times(-1.0) } else { d_zero };
1399 let p2 = f64::sqrt(p0);
1400 let d_p2 = d_p0.sqrt();
1401 let (y3, d_y3);
1402 if p < 0.0 {
1403 let ro = f64::exp(f64::ln(f64::sqrt(s2 / 4.0 + p0)) / 3.0);
1404 let d_ro = d_s2.times(0.25).plus(&d_p0).pow(1.0 / 6.0);
1405 let fi = f64::atan(-2.0 * p2 / s);
1406 let d_fi = d_p2.div(&d_s).times(-2.0).atan();
1407 y3 = 2.0 * ro * f64::cos(fi / 3.0) - a / 3.0;
1408 let d_dummy = d_fi.times(1.0 / 3.0).cos();
1409 d_y3 = d_ro.mult(&d_dummy).times(2.0).plus(&d_a.times(-1.0 / 3.0));
1410 } else {
1411 let mut p3 = -s / 2.0 + p2;
1412 let mut d_p3 = d_s.times(-0.5).plus(&d_p2);
1413 p3 = f64::exp(f64::ln(p3.abs()) / 3.0);
1414 if p3 < 0.0 {
1415 d_p3 = d_p3.times(-1.0);
1416 }
1417 d_p3 = d_p3.pow(1.0 / 3.0);
1418 let mut p4 = -s / 2.0 - p2;
1419 let mut d_p4 = d_s.times(0.5).plus(&d_p2);
1420 if p4 < 0.0 {
1421 d_p4 = d_p4.times(-1.0);
1422 }
1423 p4 = f64::exp(f64::ln(p4.abs()) / 3.0);
1424 d_p4 = d_p4.pow(1.0 / 3.0);
1425 y3 = p3 + p4 - a / 3.0;
1426 d_y3 = d_a.times(-1.0 / 3.0).plus(&d_p4).plus(&d_p3);
1427 }
1428 let a3 = f64::sqrt(a1 * a1 / 4.0 - b1 + y3);
1429 let d_a3 = d_a1
1430 .mult(&d_a1)
1431 .times(0.25)
1432 .plus(&d_y3)
1433 .plus(&d_b1.times(-1.0))
1434 .sqrt();
1435 let b3 = f64::sqrt(y3 * y3 / 4.0 - d1);
1436 let d_b3 = d_y3.mult(&d_y3).times(0.25).plus(&d_d1.times(-1.0)).sqrt();
1437 let mut x4 = [0.0; 8];
1438 let mut d_x4 = [d_zero; 8];
1439 let mut iknt = 0;
1440 for i in 0..4 {
1441 let a4 = a1 / 2.0 + SIG1[i] * a3;
1442 let d_a4 = d_a1.times(0.5).plus(&d_a3.times(SIG1[i]));
1443 let b4 = y3 / 2.0 + SIG2[i] * b3;
1444 let d_b4 = d_y3.times(0.5).plus(&d_b3.times(SIG2[i]));
1445 let delta4 = a4 * a4 / 4.0 - b4;
1446 let d_delta4 = d_a4.mult(&d_a4).times(0.25).plus(&d_b4.times(-1.0));
1447 if delta4 < 0.0 {
1448 continue;
1449 }
1450 let tmp = f64::sqrt(delta4);
1451 let d_tmp = d_delta4.sqrt();
1452 x4[iknt] = -a4 / 2.0 + tmp;
1453 d_x4[iknt] = d_a4.times(-0.5).plus(&d_tmp);
1454 iknt += 1;
1455 x4[iknt] = -a4 / 2.0 - tmp;
1456 d_x4[iknt] = d_a4.times(-0.5).plus(&d_tmp);
1457 iknt += 1;
1458 }
1459 let mut jknt = 0;
1460 let mut xvalid = 0.0;
1461 let mut d_xvalid = d_zero;
1462 for j in 0..iknt {
1463 if x4[j] <= 0.0 {
1464 continue;
1465 }
1466 let x = x4[j];
1467 let poly4 = x * x * x * x + a1 * x * x * x + b1 * x * x + c1 * x + d1;
1468 if poly4.abs() > 1.0e-6 {
1469 continue;
1470 }
1471 jknt += 1;
1472 if jknt <= 1 {
1473 xvalid = x;
1474 d_xvalid = d_x4[j];
1475 }
1476 if x > xvalid {
1477 continue;
1478 }
1479 xvalid = x;
1480 d_xvalid = d_x4[j];
1481 }
1482 if jknt > 0 {
1483 vdsat = xvalid * xvalid - phi_min_vbs;
1484 d_vdsat = d_xvalid.mult(&d_xvalid).plus(&d_phi_min_vbs.times(-1.0));
1485 }
1486 let _ = (gammd2, d_gammd2, v2);
1487 }
1488
1489 let (mut bsarg, mut bodys) = (0.0, 0.0);
1491 let (mut d_bsarg, mut d_bodys) = (d_zero, d_zero);
1492 if lvds != 0.0 {
1493 gammad = gamasd;
1494 d_gammad = d_gamasd;
1495 if lvbs - vdsat <= 0.0 {
1496 bsarg = f64::sqrt(vdsat + phi_min_vbs);
1497 d_bsarg = d_vdsat.plus(&d_phi_min_vbs).sqrt();
1498 } else {
1499 bsarg = sphi / (1.0 + 0.5 * (lvbs - vdsat) / t.t_phi);
1500 let mut b = d_vdsat.times(-1.0);
1501 b.value += lvbs;
1502 b.d1_r += 1.0;
1503 let mut b = b.times(0.5 / t.t_phi);
1504 b.value += 1.0;
1505 d_bsarg = b.inv().times(sphi);
1506 }
1507 bodys = bsarg * bsarg * bsarg - sarg3;
1508 d_bodys = d_bsarg.cube().plus(&d_sarg3.times(-1.0));
1509 if t.vmax <= 0.0 {
1510 if t.nsub != 0.0 && xlamda <= 0.0 {
1511 let argv = (lvds - vdsat) / 4.0;
1512 let mut a = d_vdsat.times(-1.0);
1513 a.value += lvds;
1514 a.d1_r += 1.0;
1515 let d_argv = a.times(0.25);
1516 let sargv = f64::sqrt(1.0 + argv * argv);
1517 let mut sv = d_argv.mult(&d_argv);
1518 sv.value += 1.0;
1519 let d_sargv = sv.sqrt();
1520 let arg = f64::sqrt(argv + sargv);
1521 let d_arg = d_sargv.plus(&d_argv).sqrt();
1522 let xlfact = t.xd / (eff_l * lvds);
1523 let d_xlfact = Dderivs { value: lvds, ..d_r }.inv().times(t.xd / eff_l);
1524 xlamda = xlfact * arg;
1525 d_xlamda = d_xlfact.mult(&d_arg);
1526 }
1527 } else {
1528 let xdv = t.xd / f64::sqrt(t.neff);
1529 let xlv = t.vmax * xdv / (2.0 * ueff);
1530 let d_xlv = d_ueff.inv().times(t.vmax * xdv * 0.5);
1531 if t.nsub != 0.0 && xlamda <= 0.0 {
1532 let mut argv = lvds - vdsat;
1533 let mut d_argv = d_vdsat.times(-1.0);
1534 d_argv.value += lvds;
1535 d_argv.d1_r += 1.0;
1536 if argv < 0.0 {
1537 d_argv = d_zero;
1538 }
1539 argv = f64::max(argv, 0.0);
1540 let xls = f64::sqrt(xlv * xlv + argv);
1541 let d_xls = d_xlv.mult(&d_xlv).plus(&d_argv).sqrt();
1542 let xlfact = xdv / (eff_l * lvds);
1543 let mut xf = d_r;
1544 xf.value += lvds;
1545 let d_xlfact = xf.inv().times(xdv / eff_l);
1546 xlamda = xlfact * (xls - xlv);
1547 d_xlamda = d_xlv.times(-1.0).plus(&d_xls).mult(&d_xlfact);
1548 }
1549 }
1550 }
1551
1552 let mut xwb = t.xd * sbiarg;
1554 let xld = eff_l - xwb;
1555 let mut clfact = 1.0 - xlamda * lvds;
1556 let mut d_clfact = Dderivs {
1557 value: lvds,
1558 d1_r: -1.0,
1559 ..d_r
1560 }
1561 .mult(&d_xlamda);
1562 d_clfact.value += 1.0;
1563 let mut xleff = eff_l * clfact;
1564 let deltal = xlamda * lvds * eff_l;
1565 let d_delta1 = Dderivs {
1566 value: eff_l * lvds,
1567 d1_r: eff_l,
1568 ..d_r
1569 }
1570 .mult(&d_xlamda);
1571 if t.nsub == 0.0 {
1572 xwb = 0.25e-6;
1573 }
1574 if xleff < xwb {
1575 xleff = xwb / (1.0 + (deltal - xld) / xwb);
1576 let mut xl = d_delta1;
1577 xl.value -= xld;
1578 let mut xl = xl.times(1.0 / xwb);
1579 xl.value += 1.0;
1580 let d_xleff = xl.inv().times(xwb);
1581 clfact = xleff / eff_l;
1582 d_clfact = d_xleff.times(1.0 / eff_l);
1583 }
1584 let _ = clfact;
1585
1586 let d_beta1 = d_ufact.div(&d_clfact).times(beta);
1588 gammad = gamasd;
1589 d_gammad = d_gamasd;
1590 let _ = gammad;
1591
1592 if lvds <= 1.0e-10 {
1593 if lvgs <= von {
1594 if t.nfs == 0.0 || oxide_cap == 0.0 {
1595 d_cdrain.d1_q = 0.0;
1596 d_cdrain.d2_q2 = 0.0;
1597 d_cdrain.d3_q3 = 0.0;
1598 break 'eval;
1599 }
1600 let d_dummy = d_gammad
1601 .mult(&d_sarg)
1602 .plus(&d_vbin)
1603 .times(-1.0)
1604 .plus(&d_von);
1605 let mut d_mos2gds = d_beta1.mult(&d_dummy);
1606 let mut d_dummy = d_von.times(-1.0).plus(&d_p);
1607 d_dummy.value += lvgs;
1608 let d_dummy = d_dummy.mult(&d_argg).exp();
1609 d_mos2gds = d_mos2gds.mult(&d_dummy);
1610 d_cdrain.d1_r = d_mos2gds.value;
1611 d_cdrain.d2_r2 = d_mos2gds.d1_r;
1612 d_cdrain.d3_r3 = d_mos2gds.d2_r2;
1613 break 'eval;
1614 }
1615 let d_mos2gds = d_gammad
1616 .mult(&d_sarg)
1617 .plus(&d_vbin)
1618 .times(-1.0)
1619 .mult(&d_beta1);
1620 d_cdrain.d1_r = d_mos2gds.value;
1621 d_cdrain.d2_r2 = d_mos2gds.d1_r;
1622 d_cdrain.d3_r3 = d_mos2gds.d2_r2;
1623 break 'eval;
1624 }
1625
1626 if lvgs <= von {
1627 if vdsat <= 0.0 {
1629 d_cdrain.d1_r = 0.0;
1630 d_cdrain.d2_r2 = 0.0;
1631 d_cdrain.d3_r3 = 0.0;
1632 break 'eval;
1633 }
1634 let d_vdson = if vdsat <= lvds {
1635 d_vdsat
1636 } else {
1637 Dderivs { value: lvds, ..d_r }
1638 };
1639 if lvds > vdsat {
1640 barg = bsarg;
1641 d_barg = d_bsarg;
1642 body = bodys;
1643 d_body = d_bodys;
1644 }
1645 let _ = (barg, d_barg, body);
1646 let d_cdson = d_gammad.mult(&d_body).times(-1.0 / 1.5);
1647 let d_dummy = d_vdson
1648 .times(0.5 * eta)
1649 .plus(&d_vbin)
1650 .times(-1.0)
1651 .plus(&d_von)
1652 .mult(&d_vdson)
1653 .plus(&d_cdson);
1654 let d_cdson = d_dummy.mult(&d_beta1);
1655 let mut d_expg = d_von.times(-1.0);
1656 d_expg.value += lvgs;
1657 d_expg.d1_p += 1.0;
1658 let d_expg = d_expg.mult(&d_argg).exp();
1659 d_cdrain = d_cdson.mult(&d_expg);
1660 break 'eval;
1661 }
1662
1663 if lvds <= vdsat {
1665 let mut c = Dderivs {
1667 value: eta * lvds * 0.5,
1668 d1_r: 0.5 * eta,
1669 ..d_r
1670 }
1671 .plus(&d_vbin)
1672 .times(-1.0);
1673 c.value += lvgs;
1674 c.d1_p += 1.0;
1675 let c = c.mult(&Dderivs { value: lvds, ..d_r });
1676 let d_dummy = d_gammad.mult(&d_body).times(-1.0 / 1.5);
1677 d_cdrain = c.plus(&d_dummy).mult(&d_beta1);
1678 } else {
1679 let mut c = d_vdsat.times(0.5 * eta).plus(&d_vbin).times(-1.0);
1681 c.value += lvgs;
1682 c.d1_p += 1.0;
1683 let c = c.mult(&d_vdsat);
1684 let d_dummy = d_gammad.mult(&d_bodys).times(-1.0 / 1.5);
1685 d_cdrain = c.plus(&d_dummy).mult(&d_beta1);
1686 }
1687 doneval = true;
1689 }
1690 if !doneval {
1691 d_cdrain = Dderivs {
1693 d1_r: d_cdrain.d1_r,
1694 d2_r2: d_cdrain.d2_r2,
1695 d3_r3: d_cdrain.d3_r3,
1696 ..d_zero
1697 };
1698 }
1699 let _ = von;
1700 let junctions = MosJunctions {
1701 cbs: t.cbs,
1702 cbssw: t.cbssw,
1703 f3s: t.f3s,
1704 cbd: t.cbd,
1705 cbdsw: t.cbdsw,
1706 f3d: t.f3d,
1707 dep_cap: t.t_dep_cap,
1708 bulk_pot: t.t_bulk_pot,
1709 mj: t.mj,
1710 mjsw: t.mjsw,
1711 };
1712 setup_tail(
1713 typ,
1714 mode,
1715 (vbs, vbd, vds),
1716 (vgst, vdsat),
1717 t.t_phi,
1718 oxide_cap,
1719 &junctions,
1720 &DrainDerivs::from_dderivs(&d_cdrain),
1721 gbs,
1722 gbd,
1723 )
1724 }
1725}
1726
1727impl crate::devices::Device for Mos2I {
1728 fn par_load(&self) -> bool {
1729 true
1730 }
1731
1732 fn set_ic(&mut self, rhs: &[f64]) {
1735 use crate::devices::getic;
1736 let g = self.ic_given;
1737 getic(g, 2, &mut self.ic_vbs, rhs, self.bulk, self.source);
1738 getic(g, 0, &mut self.ic_vds, rhs, self.drain, self.source);
1739 getic(g, 1, &mut self.ic_vgs, rhs, self.gate, self.source);
1740 }
1741
1742 fn set_ic_given(&mut self, given: crate::devices::IcGiven) {
1743 self.ic_given = given;
1744 }
1745
1746 fn conv_test(&self, ctx: &crate::matrix::ConvCtx) -> bool {
1749 let typ = self.t.type_;
1750 let st = self.st;
1751 let vbs = typ * (ctx.rhs[self.bulk] - ctx.rhs[self.s_p]);
1752 let vgs = typ * (ctx.rhs[self.gate] - ctx.rhs[self.s_p]);
1753 let vds = typ * (ctx.rhs[self.d_p] - ctx.rhs[self.s_p]);
1754 let vbd = vbs - vds;
1755 let vgd = vgs - vds;
1756 let vgdo = ctx.states.get(0, st + VGS) - ctx.states.get(0, st + VDS);
1757 let delvbs = vbs - ctx.states.get(0, st + VBS);
1758 let delvbd = vbd - ctx.states.get(0, st + VBD);
1759 let delvgs = vgs - ctx.states.get(0, st + VGS);
1760 let delvds = vds - ctx.states.get(0, st + VDS);
1761 let delvgd = vgd - vgdo;
1762 let cdhat = if self.mode >= 0.0 {
1763 self.cd - self.gbd * delvbd + self.gmbs * delvbs + self.gm * delvgs + self.gds * delvds
1764 } else {
1765 self.cd - (self.gbd - self.gmbs) * delvbd - self.gm * delvgd + self.gds * delvds
1766 };
1767 let cbhat = self.cbs + self.cbd + self.gbd * delvbd + self.gbs * delvbs;
1768 let tol = ctx.reltol * f64::max(cdhat.abs(), self.cd.abs()) + ctx.abstol;
1769 if (cdhat - self.cd).abs() >= tol {
1770 return false;
1771 }
1772 let cb = self.cbs + self.cbd;
1773 let tol = ctx.reltol * f64::max(cbhat.abs(), cb.abs()) + ctx.abstol;
1774 (cbhat - cb).abs() <= tol
1775 }
1776
1777 fn alloc(&mut self, mb: &mut MatrixBuilder) {
1778 let (d, g, s, b) = (self.drain, self.gate, self.source, self.bulk);
1779 let (dp, sp) = (self.d_p, self.s_p);
1780 self.s = [
1781 mb.slot(d, d),
1782 mb.slot(g, g),
1783 mb.slot(s, s),
1784 mb.slot(b, b),
1785 mb.slot(dp, dp),
1786 mb.slot(sp, sp),
1787 mb.slot(d, dp),
1788 mb.slot(g, b),
1789 mb.slot(g, dp),
1790 mb.slot(g, sp),
1791 mb.slot(s, sp),
1792 mb.slot(b, dp),
1793 mb.slot(b, sp),
1794 mb.slot(dp, sp),
1795 mb.slot(dp, d),
1796 mb.slot(b, g),
1797 mb.slot(dp, g),
1798 mb.slot(sp, g),
1799 mb.slot(sp, s),
1800 mb.slot(dp, b),
1801 mb.slot(sp, b),
1802 mb.slot(sp, dp),
1803 ];
1804 }
1805
1806 #[allow(clippy::too_many_lines)]
1807 fn load(&mut self, ctx: &mut LoadCtx) {
1808 let typ = self.t.type_;
1809 let vt = self.t.vt;
1810 let st = self.st;
1811
1812 let eff_l = self.geom.l - 2.0 * self.t.lat_diff;
1813 let (drain_sat_cur, source_sat_cur) =
1814 if self.t.t_sat_cur_dens == 0.0 || self.geom.ad == 0.0 || self.geom.as_ == 0.0 {
1815 (self.t.t_sat_cur, self.t.t_sat_cur)
1816 } else {
1817 (
1818 self.t.t_sat_cur_dens * self.geom.ad,
1819 self.t.t_sat_cur_dens * self.geom.as_,
1820 )
1821 };
1822 let gate_source_overlap_cap = self.t.cgso * self.geom.w;
1823 let gate_drain_overlap_cap = self.t.cgdo * self.geom.w;
1824 let gate_bulk_overlap_cap = self.t.cgbo * eff_l;
1825 let beta = self.t.t_transconductance * self.geom.w / eff_l;
1826 let oxide_cap = self.t.oxide_cap_factor * eff_l * self.geom.w;
1827
1828 let mut check = true;
1830 let (mut vbs, mut vgs, mut vds);
1831 let predictor = matches!(ctx.mode, InitMode::Pred | InitMode::Tran);
1832 let general = matches!(ctx.mode, InitMode::Float | InitMode::Smsig)
1833 || (ctx.mode == InitMode::Fix && !self.off);
1834 if predictor || general {
1835 if predictor {
1836 let xfact = ctx.pred_xfact;
1837 let mut pred = |o: usize| {
1838 let s1 = ctx.states.get(1, st + o);
1839 ctx.states.set(0, st + o, s1);
1840 (1.0 + xfact) * s1 - xfact * ctx.states.get(2, st + o)
1841 };
1842 vbs = pred(VBS);
1843 vgs = pred(VGS);
1844 vds = pred(VDS);
1845 let vbd0 = ctx.states.get(0, st + VBS) - ctx.states.get(0, st + VDS);
1846 ctx.states.set(0, st + VBD, vbd0);
1847 } else {
1848 vbs = typ * (ctx.sol[self.bulk] - ctx.sol[self.s_p]);
1849 vgs = typ * (ctx.sol[self.gate] - ctx.sol[self.s_p]);
1850 vds = typ * (ctx.sol[self.d_p] - ctx.sol[self.s_p]);
1851 }
1852 let mut vbd = vbs - vds;
1853 let mut vgd = vgs - vds;
1854 let vgdo = ctx.states.get(0, st + VGS) - ctx.states.get(0, st + VDS);
1855
1856 let von = typ * self.von;
1857 if ctx.states.get(0, st + VDS) >= 0.0 {
1858 vgs = fetlim(vgs, ctx.states.get(0, st + VGS), von);
1859 vds = vgs - vgd;
1860 vds = limvds(vds, ctx.states.get(0, st + VDS));
1861 } else {
1862 vgd = fetlim(vgd, vgdo, von);
1863 vds = vgs - vgd;
1864 vds = -limvds(-vds, -ctx.states.get(0, st + VDS));
1865 vgs = vgd + vds;
1866 }
1867 if vds >= 0.0 {
1868 vbs = pnjlim(
1869 vbs,
1870 ctx.states.get(0, st + VBS),
1871 vt,
1872 self.t.source_vcrit,
1873 &mut check,
1874 );
1875 } else {
1876 vbd = pnjlim(
1877 vbd,
1878 ctx.states.get(0, st + VBD),
1879 vt,
1880 self.t.drain_vcrit,
1881 &mut check,
1882 );
1883 vbs = vbd + vds;
1884 }
1885 } else if ctx.mode == InitMode::Jct && !self.off {
1886 vds = typ * self.ic_vds;
1887 vgs = typ * self.ic_vgs;
1888 vbs = typ * self.ic_vbs;
1889 if vds == 0.0 && vgs == 0.0 && vbs == 0.0 && !(ctx.kind == AnKind::TranOp && ctx.uic) {
1890 vbs = -1.0;
1891 vgs = typ * self.t.t_vto;
1892 vds = 0.0;
1893 }
1894 } else {
1895 vbs = 0.0;
1896 vgs = 0.0;
1897 vds = 0.0;
1898 }
1899
1900 let vbd = vbs - vds;
1901 let vgd = vgs - vds;
1902 let vgb = vgs - vbs;
1903
1904 const MAX_EXP_ARG: f64 = 709.0;
1908 let (mut gbs, mut cbs);
1909 if vbs <= -3.0 * vt {
1910 gbs = ctx.gmin;
1911 cbs = gbs * vbs - source_sat_cur;
1912 } else {
1913 let evbs = f64::exp(f64::min(MAX_EXP_ARG, vbs / vt));
1914 gbs = source_sat_cur * evbs / vt + ctx.gmin;
1915 cbs = source_sat_cur * (evbs - 1.0) + ctx.gmin * vbs;
1916 }
1917 let (mut gbd, mut cbd);
1918 if vbd <= -3.0 * vt {
1919 gbd = ctx.gmin;
1920 cbd = gbd * vbd - drain_sat_cur;
1921 } else {
1922 let evbd = f64::exp(f64::min(MAX_EXP_ARG, vbd / vt));
1923 gbd = drain_sat_cur * evbd / vt + ctx.gmin;
1924 cbd = drain_sat_cur * (evbd - 1.0) + ctx.gmin * vbd;
1925 }
1926
1927 self.mode = if vds >= 0.0 { 1.0 } else { -1.0 };
1928 let mode = self.mode;
1929
1930 let (cdrain, von, vdsat) = self.eval(vbs, vbd, vgs, vgd, vds, beta, oxide_cap, eff_l, vt);
1932 self.von = typ * von;
1933 self.vdsat = typ * vdsat;
1934 let (gm, gds, gmbs) = (self.gm, self.gds, self.gmbs);
1935
1936 let mut cd = mode * cdrain - cbd;
1937
1938 let t = &self.t;
1939 let smsig = ctx.mode == InitMode::Smsig;
1940 if ctx.kind == AnKind::Tran || ctx.kind == AnKind::TranOp || smsig {
1941 let dep = |v: f64, cz: f64, czsw: f64, f2: f64, f3: f64, f4: f64| {
1944 if cz != 0.0 || czsw != 0.0 {
1945 if v < t.t_dep_cap {
1946 let arg = 1.0 - v / t.t_bulk_pot;
1947 let (sarg, sargsw);
1948 if t.mj == t.mjsw {
1949 if t.mj == 0.5 {
1950 sarg = 1.0 / arg.sqrt();
1951 sargsw = sarg;
1952 } else {
1953 sarg = f64::exp(-t.mj * f64::ln(arg));
1954 sargsw = sarg;
1955 }
1956 } else {
1957 sarg = if t.mj == 0.5 {
1958 1.0 / arg.sqrt()
1959 } else {
1960 f64::exp(-t.mj * f64::ln(arg))
1961 };
1962 sargsw = if t.mjsw == 0.5 {
1963 1.0 / arg.sqrt()
1964 } else {
1965 f64::exp(-t.mjsw * f64::ln(arg))
1966 };
1967 }
1968 let q = t.t_bulk_pot
1969 * (cz * (1.0 - arg * sarg) / (1.0 - t.mj)
1970 + czsw * (1.0 - arg * sargsw) / (1.0 - t.mjsw));
1971 (q, cz * sarg + czsw * sargsw)
1972 } else {
1973 (f4 + v * (f2 + v * f3 / 2.0), f2 + v * f3)
1974 }
1975 } else {
1976 (0.0, 0.0)
1977 }
1978 };
1979 let (qbs, capbs) = dep(vbs, t.cbs, t.cbssw, t.f2s, t.f3s, t.f4s);
1980 ctx.states.set(0, st + QBS, qbs);
1981 self.capbs = capbs;
1982 let (qbd, capbd) = dep(vbd, t.cbd, t.cbdsw, t.f2d, t.f3d, t.f4d);
1983 ctx.states.set(0, st + QBD, qbd);
1984 self.capbd = capbd;
1985
1986 if ctx.kind == AnKind::Tran {
1987 let (geq, _) = ctx.integrate(self.capbd, st + QBD);
1988 gbd += geq;
1989 cbd += ctx.states.get(0, st + CQBD);
1990 cd -= ctx.states.get(0, st + CQBD);
1991 let (geq, _) = ctx.integrate(self.capbs, st + QBS);
1992 gbs += geq;
1993 cbs += ctx.states.get(0, st + CQBS);
1994 }
1995 }
1996 self.gbs = gbs;
1997 self.gbd = gbd;
1998
1999 if (!self.off || !matches!(ctx.mode, InitMode::Fix | InitMode::Smsig)) && check {
2000 ctx.noncon = true;
2001 }
2002
2003 ctx.states.set(0, st + VBS, vbs);
2004 ctx.states.set(0, st + VBD, vbd);
2005 ctx.states.set(0, st + VGS, vgs);
2006 ctx.states.set(0, st + VDS, vds);
2007
2008 let (mut capgs, mut capgd, mut capgb) = (0.0, 0.0, 0.0);
2010 if ctx.kind == AnKind::Tran || ctx.kind == AnKind::TranOp || smsig {
2011 let (half_gs, half_gd, half_gb) = if mode > 0.0 {
2012 qmeyer(vgs, vgd, vgb, von, vdsat, t.t_phi, oxide_cap)
2013 } else {
2014 let (o1, o2, o3) = qmeyer(vgd, vgs, vgb, von, vdsat, t.t_phi, oxide_cap);
2015 (o2, o1, o3)
2016 };
2017 ctx.states.set(0, st + CAPGS, half_gs);
2018 ctx.states.set(0, st + CAPGD, half_gd);
2019 ctx.states.set(0, st + CAPGB, half_gb);
2020 let vgs1 = ctx.states.get(1, st + VGS);
2021 let vgd1 = vgs1 - ctx.states.get(1, st + VDS);
2022 let vgb1 = vgs1 - ctx.states.get(1, st + VBS);
2023 if ctx.kind == AnKind::TranOp || smsig {
2024 capgs = 2.0 * ctx.states.get(0, st + CAPGS) + gate_source_overlap_cap;
2025 capgd = 2.0 * ctx.states.get(0, st + CAPGD) + gate_drain_overlap_cap;
2026 capgb = 2.0 * ctx.states.get(0, st + CAPGB) + gate_bulk_overlap_cap;
2027 } else {
2028 capgs = ctx.states.get(0, st + CAPGS)
2029 + ctx.states.get(1, st + CAPGS)
2030 + gate_source_overlap_cap;
2031 capgd = ctx.states.get(0, st + CAPGD)
2032 + ctx.states.get(1, st + CAPGD)
2033 + gate_drain_overlap_cap;
2034 capgb = ctx.states.get(0, st + CAPGB)
2035 + ctx.states.get(1, st + CAPGB)
2036 + gate_bulk_overlap_cap;
2037 }
2038
2039 if smsig {
2040 return;
2041 }
2042
2043 if predictor {
2044 let xfact = ctx.pred_xfact;
2045 for o in [QGS, QGD, QGB] {
2046 let v = (1.0 + xfact) * ctx.states.get(1, st + o)
2047 - xfact * ctx.states.get(2, st + o);
2048 ctx.states.set(0, st + o, v);
2049 }
2050 } else if ctx.kind == AnKind::Tran {
2051 let q = (vgs - vgs1) * capgs + ctx.states.get(1, st + QGS);
2052 ctx.states.set(0, st + QGS, q);
2053 let q = (vgd - vgd1) * capgd + ctx.states.get(1, st + QGD);
2054 ctx.states.set(0, st + QGD, q);
2055 let q = (vgb - vgb1) * capgb + ctx.states.get(1, st + QGB);
2056 ctx.states.set(0, st + QGB, q);
2057 } else {
2058 ctx.states.set(0, st + QGS, capgs * vgs);
2059 ctx.states.set(0, st + QGD, capgd * vgd);
2060 ctx.states.set(0, st + QGB, capgb * vgb);
2061 }
2062 }
2063
2064 let (mut gcgs, mut ceqgs, mut gcgd, mut ceqgd, mut gcgb, mut ceqgb) =
2066 (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
2067 if ctx.mode != InitMode::Tran && ctx.kind == AnKind::Tran {
2068 if capgs == 0.0 {
2069 ctx.states.set(0, st + CQGS, 0.0);
2070 }
2071 if capgd == 0.0 {
2072 ctx.states.set(0, st + CQGD, 0.0);
2073 }
2074 if capgb == 0.0 {
2075 ctx.states.set(0, st + CQGB, 0.0);
2076 }
2077 let (g, ceq) = ctx.integrate(capgs, st + QGS);
2078 gcgs = g;
2079 ceqgs = ceq - gcgs * vgs + ctx.ag[0] * ctx.states.get(0, st + QGS);
2080 let (g, ceq) = ctx.integrate(capgd, st + QGD);
2081 gcgd = g;
2082 ceqgd = ceq - gcgd * vgd + ctx.ag[0] * ctx.states.get(0, st + QGD);
2083 let (g, ceq) = ctx.integrate(capgb, st + QGB);
2084 gcgb = g;
2085 ceqgb = ceq - gcgb * vgb + ctx.ag[0] * ctx.states.get(0, st + QGB);
2086 }
2087
2088 let ceqbs = typ * (cbs - gbs * vbs);
2093 let ceqbd = typ * (cbd - gbd * vbd);
2094 let (xnrm, xrev, cdreq);
2095 if mode >= 0.0 {
2096 xnrm = 1.0;
2097 xrev = 0.0;
2098 cdreq = typ * (cdrain - gds * vds - gm * vgs - gmbs * vbs);
2099 } else {
2100 xnrm = 0.0;
2101 xrev = 1.0;
2102 cdreq = -typ * (cdrain - gds * (-vds) - gm * vgd - gmbs * vbd);
2103 }
2104 self.cd = cd;
2106 self.cbs = cbs;
2107 self.cbd = cbd;
2108 ctx.add_rhs(self.gate, -(typ * (ceqgs + ceqgb + ceqgd)));
2109 ctx.add_rhs(self.bulk, -(ceqbs + ceqbd - typ * ceqgb));
2110 ctx.add_rhs(self.d_p, ceqbd - cdreq + typ * ceqgd);
2111 ctx.add_rhs(self.s_p, cdreq + ceqbs + typ * ceqgs);
2112
2113 let gdpr = self.t.drain_conductance;
2115 let gspr = self.t.source_conductance;
2116 ctx.add(self.s[D_D], gdpr);
2117 ctx.add(self.s[G_G], gcgd + gcgs + gcgb);
2118 ctx.add(self.s[S_S], gspr);
2119 ctx.add(self.s[B_B], gbd + gbs + gcgb);
2120 ctx.add(self.s[DP_DP], gdpr + gds + gbd + xrev * (gm + gmbs) + gcgd);
2121 ctx.add(self.s[SP_SP], gspr + gds + gbs + xnrm * (gm + gmbs) + gcgs);
2122 ctx.add(self.s[D_DP], -gdpr);
2123 ctx.add(self.s[G_B], -gcgb);
2124 ctx.add(self.s[G_DP], -gcgd);
2125 ctx.add(self.s[G_SP], -gcgs);
2126 ctx.add(self.s[S_SP], -gspr);
2127 ctx.add(self.s[B_G], -gcgb);
2128 ctx.add(self.s[B_DP], -gbd);
2129 ctx.add(self.s[B_SP], -gbs);
2130 ctx.add(self.s[DP_D], -gdpr);
2131 ctx.add(self.s[DP_G], (xnrm - xrev) * gm - gcgd);
2132 ctx.add(self.s[DP_B], -gbd + (xnrm - xrev) * gmbs);
2133 ctx.add(self.s[DP_SP], -gds - xnrm * (gm + gmbs));
2134 ctx.add(self.s[SP_G], -(xnrm - xrev) * gm - gcgs);
2135 ctx.add(self.s[SP_S], -gspr);
2136 ctx.add(self.s[SP_B], -gbs - (xnrm - xrev) * gmbs);
2137 ctx.add(self.s[SP_DP], -gds - xrev * (gm + gmbs));
2138 }
2139
2140 fn trunc(&self, t: &crate::integ::TerrCtx, time_step: &mut f64) {
2141 crate::integ::terr(self.st + QGS, t, time_step);
2142 crate::integ::terr(self.st + QGD, t, time_step);
2143 crate::integ::terr(self.st + QGB, t, time_step);
2144 }
2145
2146 fn noise_suffixes(&self) -> Option<&'static [&'static str]> {
2149 Some(crate::mos1::MOS_NOISE_SUFFIXES)
2150 }
2151
2152 fn noise_dens(&self, ctx: &crate::matrix::NoiseCtx) -> Vec<f64> {
2153 let n = crate::mos1::MosNoise {
2154 drain: self.drain,
2155 source: self.source,
2156 d_p: self.d_p,
2157 s_p: self.s_p,
2158 gdpr: self.t.drain_conductance,
2159 gspr: self.t.source_conductance,
2160 gm: self.gm,
2161 cd: self.cd,
2162 kf: self.t.kf,
2163 af: self.t.af,
2164 nlev: self.t.nlev,
2165 gdsnoi: self.t.gdsnoi,
2166 w: self.geom.w,
2167 eff_l: self.geom.l - 2.0 * self.t.lat_diff,
2168 cox: self.t.oxide_cap_factor,
2169 temp: self.t.temp,
2170 vgs: ctx.states.get(0, self.st + VGS),
2171 vds: ctx.states.get(0, self.st + VDS),
2172 mode: self.mode,
2173 von: self.von,
2174 vdsat: self.vdsat,
2175 typ: self.t.type_,
2176 t_transconductance: self.t.t_transconductance,
2177 };
2178 crate::mos1::mos_noise(ctx, &n)
2179 }
2180
2181 fn disto_setup(&mut self, ctx: &DistoSetupCtx) -> Result<(), &'static str> {
2183 self.dist = self.disto_coeffs(ctx.sol, ctx.gmin);
2184 Ok(())
2185 }
2186
2187 fn disto_load(&self, ctx: &mut DistoCtx) {
2189 mos_disto_load(&self.dist, (self.gate, self.bulk, self.d_p, self.s_p), ctx);
2190 }
2191
2192 fn load_ac(&mut self, ctx: &mut AcCtx) {
2194 let t = &self.t;
2195 let st = self.st;
2196 let (xnrm, xrev) = if self.mode < 0.0 {
2197 (0.0, 1.0)
2198 } else {
2199 (1.0, 0.0)
2200 };
2201 let eff_l = self.geom.l - 2.0 * t.lat_diff;
2202 let capgs = 2.0 * ctx.states.get(0, st + CAPGS) + t.cgso * self.geom.w;
2203 let capgd = 2.0 * ctx.states.get(0, st + CAPGD) + t.cgdo * self.geom.w;
2204 let capgb = 2.0 * ctx.states.get(0, st + CAPGB) + t.cgbo * eff_l;
2205 let xgs = capgs * ctx.omega;
2206 let xgd = capgd * ctx.omega;
2207 let xgb = capgb * ctx.omega;
2208 let xbd = self.capbd * ctx.omega;
2209 let xbs = self.capbs * ctx.omega;
2210 let (gm, gds, gmbs) = (self.gm, self.gds, self.gmbs);
2211 let (gbd, gbs) = (self.gbd, self.gbs);
2212 let gdpr = t.drain_conductance;
2213 let gspr = t.source_conductance;
2214
2215 ctx.add_b(self.s[G_G], xgd + xgs + xgb);
2216 ctx.add_b(self.s[B_B], xgb + xbd + xbs);
2217 ctx.add_b(self.s[DP_DP], xgd + xbd);
2218 ctx.add_b(self.s[SP_SP], xgs + xbs);
2219 ctx.add_b(self.s[G_B], -xgb);
2220 ctx.add_b(self.s[G_DP], -xgd);
2221 ctx.add_b(self.s[G_SP], -xgs);
2222 ctx.add_b(self.s[B_G], -xgb);
2223 ctx.add_b(self.s[B_DP], -xbd);
2224 ctx.add_b(self.s[B_SP], -xbs);
2225 ctx.add_b(self.s[DP_G], -xgd);
2226 ctx.add_b(self.s[DP_B], -xbd);
2227 ctx.add_b(self.s[SP_G], -xgs);
2228 ctx.add_b(self.s[SP_B], -xbs);
2229 ctx.add_g(self.s[D_D], gdpr);
2230 ctx.add_g(self.s[S_S], gspr);
2231 ctx.add_g(self.s[B_B], gbd + gbs);
2232 ctx.add_g(self.s[DP_DP], gdpr + gds + gbd + xrev * (gm + gmbs));
2233 ctx.add_g(self.s[SP_SP], gspr + gds + gbs + xnrm * (gm + gmbs));
2234 ctx.add_g(self.s[D_DP], -gdpr);
2235 ctx.add_g(self.s[S_SP], -gspr);
2236 ctx.add_g(self.s[B_DP], -gbd);
2237 ctx.add_g(self.s[B_SP], -gbs);
2238 ctx.add_g(self.s[DP_D], -gdpr);
2239 ctx.add_g(self.s[DP_G], (xnrm - xrev) * gm);
2240 ctx.add_g(self.s[DP_B], -gbd + (xnrm - xrev) * gmbs);
2241 ctx.add_g(self.s[DP_SP], -gds - xnrm * (gm + gmbs));
2242 ctx.add_g(self.s[SP_G], -(xnrm - xrev) * gm);
2243 ctx.add_g(self.s[SP_S], -gspr);
2244 ctx.add_g(self.s[SP_B], -gbs - (xnrm - xrev) * gmbs);
2245 ctx.add_g(self.s[SP_DP], -gds - xrev * (gm + gmbs));
2246 }
2247
2248 fn load_pz(&mut self, ctx: &mut crate::matrix::PzCtx) -> Result<(), &'static str> {
2251 let t = &self.t;
2252 let st = self.st;
2253 let (xnrm, xrev) = if self.mode < 0.0 {
2254 (0.0, 1.0)
2255 } else {
2256 (1.0, 0.0)
2257 };
2258 let eff_l = self.geom.l - 2.0 * t.lat_diff;
2259 let caps = MeyerPzCaps {
2260 capgs: 2.0 * ctx.states.get(0, st + CAPGS) + t.cgso * self.geom.w,
2261 capgd: 2.0 * ctx.states.get(0, st + CAPGD) + t.cgdo * self.geom.w,
2262 capgb: 2.0 * ctx.states.get(0, st + CAPGB) + t.cgbo * eff_l,
2263 capbd: self.capbd,
2264 capbs: self.capbs,
2265 };
2266 let g = MosPzConductances {
2267 xnrm,
2268 xrev,
2269 gm: self.gm,
2270 gds: self.gds,
2271 gmbs: self.gmbs,
2272 gbd: self.gbd,
2273 gbs: self.gbs,
2274 gdpr: t.drain_conductance,
2275 gspr: t.source_conductance,
2276 };
2277 meyer_pz_load(ctx, &self.s, &caps, &g);
2278 Ok(())
2279 }
2280}
2281
2282#[cfg(test)]
2283mod tests {
2284 use super::*;
2285
2286 fn amp_model() -> Mos2Model {
2288 let mut m = Mos2Model::new(true);
2289 m.nsub = Some(2.2e15);
2290 m.u0 = Some(575.0);
2291 m.ucrit = 49e3;
2292 m.uexp = 0.1;
2293 m.tox = Some(0.11e-6);
2294 m.xj = 2.95e-6;
2295 m.ld = 2.4485e-6;
2296 m.nss = Some(3.2e10);
2297 m.kp = Some(2e-5);
2298 m.phi = Some(0.6);
2299 m.cgso = 1.5e-9;
2300 m.cgdo = 1.5e-9;
2301 m.cbd = Some(4.5e-15);
2302 m.cbs = Some(4.5e-15);
2303 m
2304 }
2305
2306 #[test]
2307 fn temp_derives_gamma_vto_xd_from_nsub() {
2308 let g = MosGeom {
2309 l: 25.4e-6,
2310 w: 88.9e-6,
2311 ad: 0.0,
2312 as_: 0.0,
2313 pd: 0.0,
2314 ps: 0.0,
2315 nrd: 1.0,
2316 nrs: 1.0,
2317 };
2318 let t = Mos2Temp::new("m1", &_model(), &g, REFTEMP, REFTEMP).unwrap();
2319 let cox = 3.9 * 8.854214871e-12 / 0.11e-6;
2321 let gamma = f64::sqrt(2.0 * 11.70 * 8.854214871e-12 * CHARGE * 2.2e15 * 1e6) / cox;
2322 assert!((t.gamma - gamma).abs() < 1e-12);
2323 let xd = f64::sqrt(2.0 * EPSSIL / (CHARGE * 2.2e15 * 1e6));
2325 assert!((t.xd - xd).abs() < 1e-18);
2326 assert!((t.t_transconductance - 2e-5).abs() < 1e-18);
2328 assert!((t.t_phi - 0.6).abs() < 1e-12);
2330 assert!(t.t_vto.is_finite());
2332 assert!(t.t_vto.abs() < 5.0);
2333 }
2334
2335 #[test]
2336 fn nsub_below_ni_is_fatal() {
2337 let mut m = Mos2Model::new(true);
2338 m.nsub = Some(1e9);
2339 let g = MosGeom {
2340 l: 1e-5,
2341 w: 1e-5,
2342 ad: 0.0,
2343 as_: 0.0,
2344 pd: 0.0,
2345 ps: 0.0,
2346 nrd: 1.0,
2347 nrs: 1.0,
2348 };
2349 let e = Mos2Temp::new("m1", &m, &g, REFTEMP, REFTEMP).unwrap_err();
2350 assert!(e.contains("Nsub < Ni"));
2351 }
2352}