1use crate::consts::{BOLTZ, CHARGE, KOVERQ, REFTEMP, ROOT2};
13use crate::diode::pnjlim;
14use crate::disto::{DistoCtx, DistoSetupCtx};
15use crate::matrix::{AcCtx, AnKind, InitMode, LoadCtx, MatrixBuilder, Slot, TRASH, Var};
16use crate::mosdisto::{
17 DrainDerivs, MosDistoCoeffs, MosJunctions, bulk_diode, mos_disto_load, setup_tail,
18};
19
20const MAX_EXP_ARG: f64 = 709.0;
22
23pub(crate) const MOS_NOISE_SUFFIXES: &[&str] = &["_rd", "_rs", "_id", "_1overf", ""];
27
28pub(crate) struct MosNoise {
31 pub drain: Var,
32 pub source: Var,
33 pub d_p: Var,
34 pub s_p: Var,
35 pub gdpr: f64,
36 pub gspr: f64,
37 pub gm: f64,
39 pub cd: f64,
40 pub kf: f64,
41 pub af: f64,
42 pub nlev: i64,
44 pub gdsnoi: f64,
45 pub w: f64,
47 pub eff_l: f64,
48 pub cox: f64,
50 pub temp: f64,
52 pub vgs: f64,
56 pub vds: f64,
57 pub mode: f64,
58 pub von: f64,
59 pub vdsat: f64,
60 pub typ: f64,
61 pub t_transconductance: f64,
62}
63
64pub(crate) fn mos_noise(ctx: &crate::matrix::NoiseCtx, n: &MosNoise) -> Vec<f64> {
81 const N_MINLOG: f64 = 1e-38;
82 let rd = ctx.thermal_at(n.d_p, n.drain, n.gdpr, n.temp);
83 let rs = ctx.thermal_at(n.s_p, n.source, n.gspr, n.temp);
84 let sid = if n.nlev < 3 {
85 2.0 / 3.0 * n.gm.abs()
86 } else {
87 let vgd = n.vgs - n.vds;
88 let vgst = (if n.mode == 1.0 { n.vgs } else { vgd }) - n.typ * n.von;
89 if vgst > 0.0 {
90 let alpha = if vgst <= n.vds * n.mode {
91 0.0 } else {
93 1.0 - (n.vds * n.mode / (n.typ * n.vdsat))
94 };
95 let beta = n.t_transconductance * n.w / n.eff_l;
96 2.0 / 3.0 * beta * vgst * (1.0 + alpha + alpha * alpha) / (1.0 + alpha) * n.gdsnoi
97 } else {
98 0.0 }
100 };
101 let id = ctx.thermal_at(n.d_p, n.s_p, sid, n.temp);
102 let id_af = || f64::exp(n.af * f64::ln(f64::max(n.cd.abs(), N_MINLOG)));
103 let flicker = match n.nlev {
104 0 => ctx.flicker(
105 n.d_p,
106 n.s_p,
107 n.kf * id_af() / (n.eff_l * n.eff_l * n.cox),
108 1.0,
109 ),
110 1 => ctx.flicker(n.d_p, n.s_p, n.kf * id_af() / (n.w * n.eff_l * n.cox), 1.0),
111 2 | 3 => ctx.flicker(
112 n.d_p,
113 n.s_p,
114 n.kf * n.gm * n.gm / (n.w * n.eff_l * n.cox),
115 n.af,
116 ),
117 _ => ctx.flicker(n.d_p, n.s_p, 1.0, 0.0),
118 };
119 vec![rd, rs, id, flicker, rd + rs + id + flicker]
120}
121
122#[derive(Clone, Debug)]
128pub struct Mos1Model {
129 pub nmos: bool,
131 pub vto: Option<f64>,
134 pub kp: Option<f64>,
136 pub gamma: Option<f64>,
138 pub phi: Option<f64>,
140 pub lambda: f64,
142 pub rd: Option<f64>,
144 pub rs: Option<f64>,
146 pub cbd: Option<f64>,
148 pub cbs: Option<f64>,
150 pub is: f64,
152 pub pb: f64,
154 pub cgso: f64,
157 pub cgdo: f64,
158 pub cgbo: f64,
159 pub rsh: Option<f64>,
161 pub cj: Option<f64>,
163 pub mj: f64,
165 pub cjsw: Option<f64>,
167 pub mjsw: f64,
169 pub js: f64,
171 pub tox: Option<f64>,
173 pub ld: f64,
175 pub u0: Option<f64>,
177 pub fc: f64,
180 pub nsub: Option<f64>,
182 pub tpg: Option<f64>,
185 pub nss: Option<f64>,
187 pub kf: f64,
189 pub af: f64,
190 pub nlev: i64,
195 pub gdsnoi: f64,
198 pub tnom: Option<f64>,
200}
201
202impl Mos1Model {
203 pub fn new(nmos: bool) -> Self {
204 Mos1Model {
205 nmos,
206 vto: None,
207 kp: None,
208 gamma: None,
209 phi: None,
210 lambda: 0.0,
211 rd: None,
212 rs: None,
213 cbd: None,
214 cbs: None,
215 is: 1e-14,
216 pb: 0.8,
217 cgso: 0.0,
218 cgdo: 0.0,
219 cgbo: 0.0,
220 rsh: None,
221 cj: None,
222 mj: 0.5,
223 cjsw: None,
224 mjsw: 0.5,
225 js: 0.0,
226 tox: None,
227 ld: 0.0,
228 u0: None,
229 fc: 0.5,
230 nsub: None,
231 tpg: None,
232 nss: None,
233 kf: 0.0,
234 af: 1.0,
235 nlev: 2,
236 gdsnoi: 1.0,
237 tnom: None,
238 }
239 }
240
241 pub(crate) fn needs_prime(&self, squares: f64) -> bool {
244 self.rd.unwrap_or(0.0) != 0.0 || (self.rsh.unwrap_or(0.0) != 0.0 && squares != 0.0)
245 }
246}
247
248fn egfet(t: f64) -> f64 {
249 1.16 - (7.02e-4 * t * t) / (t + 1108.0)
250}
251
252#[derive(Clone, Copy, Debug)]
256pub(crate) struct MosGeom {
257 pub l: f64,
258 pub w: f64,
259 pub ad: f64,
260 pub as_: f64,
261 pub pd: f64,
262 pub ps: f64,
263 pub nrd: f64,
264 pub nrs: f64,
265}
266
267pub(crate) struct Mos1Temp {
270 pub temp: f64,
274 pub type_: f64, pub vt: f64,
276 pub lat_diff: f64,
278 pub kf: f64,
280 pub af: f64,
281 pub nlev: i64,
283 pub gdsnoi: f64,
284 pub cgso: f64,
285 pub cgdo: f64,
286 pub cgbo: f64,
287 pub oxide_cap_factor: f64,
288 pub gamma: f64,
289 pub lambda: f64,
290 pub mj: f64,
291 pub mjsw: f64,
292 pub t_transconductance: f64,
294 pub t_phi: f64,
295 pub t_vbi: f64,
296 pub t_vto: f64,
297 pub t_sat_cur: f64,
298 pub t_sat_cur_dens: f64,
299 pub t_bulk_pot: f64,
300 pub t_dep_cap: f64,
301 pub drain_vcrit: f64,
302 pub source_vcrit: f64,
303 pub cbd: f64,
306 pub cbdsw: f64,
307 pub cbs: f64,
308 pub cbssw: f64,
309 pub f2d: f64,
310 pub f3d: f64,
311 pub f4d: f64,
312 pub f2s: f64,
313 pub f3s: f64,
314 pub f4s: f64,
315 pub drain_conductance: f64,
316 pub source_conductance: f64,
317 pub notes: Vec<String>,
318}
319
320impl Mos1Temp {
321 pub fn new(
322 name: &str,
323 m: &Mos1Model,
324 g: &MosGeom,
325 temp: f64,
326 default_tnom: f64,
327 ) -> Result<Self, String> {
328 let mut notes = Vec::new();
329 let typ = if m.nmos { 1.0 } else { -1.0 };
330 let tnom = m.tnom.unwrap_or(default_tnom);
331
332 let fact1 = tnom / REFTEMP;
333 let vtnom = tnom * KOVERQ;
334 let kt1 = BOLTZ * tnom;
335 let egfet1 = egfet(tnom);
336 let arg1 = -egfet1 / (kt1 + kt1) + 1.1150877 / (BOLTZ * (REFTEMP + REFTEMP));
337 let pbfact1 = -2.0 * vtnom * (1.5 * f64::ln(fact1) + CHARGE * arg1);
338
339 let mut kp = m.kp;
343 let mut phi = m.phi;
344 let mut gamma = m.gamma;
345 let mut vto = m.vto;
346 let oxide_cap_factor = match m.tox {
347 Some(tox) if tox != 0.0 => {
348 let ocf = 3.9 * 8.854214871e-12 / tox;
349 if kp.is_none() {
350 let u0 = m.u0.unwrap_or(600.0);
351 kp = Some(u0 * ocf * 1e-4 );
352 }
353 if let Some(nsub) = m.nsub {
354 if nsub * 1e6 > 1.45e16 {
355 if phi.is_none() {
356 phi = Some(f64::max(0.1, 2.0 * vtnom * f64::ln(nsub * 1e6 / 1.45e16)));
357 }
358 let phi_v = phi.unwrap_or(0.6);
359 let fermis = typ * 0.5 * phi_v;
360 let tpg = m.tpg.unwrap_or(1.0);
361 let mut wkfng = 3.2;
362 if tpg != 0.0 {
363 let fermig = typ * tpg * 0.5 * egfet1;
364 wkfng = 3.25 + 0.5 * egfet1 - fermig;
365 }
366 let wkfngs = wkfng - (3.25 + 0.5 * egfet1 + fermis);
367 if gamma.is_none() {
368 gamma = Some(
369 f64::sqrt(2.0 * 11.70 * 8.854214871e-12 * CHARGE * nsub * 1e6)
370 / ocf,
371 );
372 }
373 if vto.is_none() {
374 let nss = m.nss.unwrap_or(0.0);
375 let vfb = wkfngs - nss * 1e4 * CHARGE / ocf;
376 vto = Some(vfb + typ * (gamma.unwrap_or(0.0) * phi_v.sqrt() + phi_v));
377 }
378 } else {
379 return Err(format!("{name}: Nsub < Ni"));
380 }
381 }
382 ocf
383 }
384 _ => 0.0,
385 };
386 let kp = kp.unwrap_or(2e-5);
387 let phi = phi.unwrap_or(0.6);
388 let gamma = gamma.unwrap_or(0.0);
389 let vto = vto.unwrap_or(0.0);
390
391 let vt = temp * KOVERQ;
393 let ratio = temp / tnom;
394 let fact2 = temp / REFTEMP;
395 let kt = temp * BOLTZ;
396 let egfet_t = egfet(temp);
397 let arg = -egfet_t / (kt + kt) + 1.1150877 / (BOLTZ * (REFTEMP + REFTEMP));
398 let pbfact = -2.0 * vt * (1.5 * f64::ln(fact2) + CHARGE * arg);
399
400 if g.l - 2.0 * m.ld <= 0.0 {
401 notes.push(format!("{name}: effective channel length less than zero"));
402 }
403 let ratio4 = ratio * ratio.sqrt();
404 let t_transconductance = kp / ratio4;
405 let phio = (phi - pbfact1) / fact1;
406 let t_phi = fact2 * phio + pbfact;
407 let t_vbi =
408 vto - typ * (gamma * phi.sqrt()) + 0.5 * (egfet1 - egfet_t) + typ * 0.5 * (t_phi - phi);
409 let t_vto = t_vbi + typ * gamma * t_phi.sqrt();
410 let t_sat_cur = m.is * f64::exp(-egfet_t / vt + egfet1 / vtnom);
411 let t_sat_cur_dens = m.js * f64::exp(-egfet_t / vt + egfet1 / vtnom);
412
413 let pbo = (m.pb - pbfact1) / fact1;
414 let gmaold = (m.pb - pbo) / pbo;
415 let mut capfact = 1.0 / (1.0 + m.mj * (4e-4 * (tnom - REFTEMP) - gmaold));
416 let mut t_cbd = m.cbd.unwrap_or(0.0) * capfact;
417 let mut t_cbs = m.cbs.unwrap_or(0.0) * capfact;
418 let mut t_cj = m.cj.unwrap_or(0.0) * capfact;
419 capfact = 1.0 / (1.0 + m.mjsw * (4e-4 * (tnom - REFTEMP) - gmaold));
420 let mut t_cjsw = m.cjsw.unwrap_or(0.0) * capfact;
421 let t_bulk_pot = fact2 * pbo + pbfact;
422 let gmanew = (t_bulk_pot - pbo) / pbo;
423 capfact = 1.0 + m.mj * (4e-4 * (temp - REFTEMP) - gmanew);
424 t_cbd *= capfact;
425 t_cbs *= capfact;
426 t_cj *= capfact;
427 capfact = 1.0 + m.mjsw * (4e-4 * (temp - REFTEMP) - gmanew);
428 t_cjsw *= capfact;
429 let t_dep_cap = m.fc * t_bulk_pot;
430
431 let (drain_vcrit, source_vcrit) = if t_sat_cur_dens == 0.0 || g.ad == 0.0 || g.as_ == 0.0 {
432 let v = vt * f64::ln(vt / (ROOT2 * t_sat_cur));
433 (v, v)
434 } else {
435 (
436 vt * f64::ln(vt / (ROOT2 * t_sat_cur_dens * g.ad)),
437 vt * f64::ln(vt / (ROOT2 * t_sat_cur_dens * g.as_)),
438 )
439 };
440
441 let czbd = if m.cbd.is_some() {
444 t_cbd
445 } else if m.cj.is_some() {
446 t_cj * g.ad
447 } else {
448 0.0
449 };
450 let czbdsw = if m.cjsw.is_some() { t_cjsw * g.pd } else { 0.0 };
451 let arg = 1.0 - m.fc;
452 let sarg = f64::exp(-m.mj * f64::ln(arg));
453 let sargsw = f64::exp(-m.mjsw * f64::ln(arg));
454 let f2d = czbd * (1.0 - m.fc * (1.0 + m.mj)) * sarg / arg
455 + czbdsw * (1.0 - m.fc * (1.0 + m.mjsw)) * sargsw / arg;
456 let f3d =
457 czbd * m.mj * sarg / arg / t_bulk_pot + czbdsw * m.mjsw * sargsw / arg / t_bulk_pot;
458 let f4d = czbd * t_bulk_pot * (1.0 - arg * sarg) / (1.0 - m.mj)
459 + czbdsw * t_bulk_pot * (1.0 - arg * sargsw) / (1.0 - m.mjsw)
460 - f3d / 2.0 * (t_dep_cap * t_dep_cap)
461 - t_dep_cap * f2d;
462 let czbs = if m.cbs.is_some() {
463 t_cbs
464 } else if m.cj.is_some() {
465 t_cj * g.as_
466 } else {
467 0.0
468 };
469 let czbssw = if m.cjsw.is_some() { t_cjsw * g.ps } else { 0.0 };
470 let f2s = czbs * (1.0 - m.fc * (1.0 + m.mj)) * sarg / arg
471 + czbssw * (1.0 - m.fc * (1.0 + m.mjsw)) * sargsw / arg;
472 let f3s =
473 czbs * m.mj * sarg / arg / t_bulk_pot + czbssw * m.mjsw * sargsw / arg / t_bulk_pot;
474 let f4s = czbs * t_bulk_pot * (1.0 - arg * sarg) / (1.0 - m.mj)
475 + czbssw * t_bulk_pot * (1.0 - arg * sargsw) / (1.0 - m.mjsw)
476 - f3s / 2.0 * (t_dep_cap * t_dep_cap)
477 - t_dep_cap * f2s;
478
479 let series = |r: Option<f64>, squares: f64| match (r, m.rsh) {
480 (Some(r), _) => {
481 if r != 0.0 {
482 1.0 / r
483 } else {
484 0.0
485 }
486 }
487 (None, Some(rsh)) => {
488 if rsh != 0.0 {
489 1.0 / (rsh * squares)
490 } else {
491 0.0
492 }
493 }
494 (None, None) => 0.0,
495 };
496 let drain_conductance = series(m.rd, g.nrd);
497 let source_conductance = series(m.rs, g.nrs);
498
499 Ok(Mos1Temp {
500 temp,
501 type_: typ,
502 vt,
503 lat_diff: m.ld,
504 kf: m.kf,
505 af: m.af,
506 nlev: m.nlev,
507 gdsnoi: m.gdsnoi,
508 cgso: m.cgso,
509 cgdo: m.cgdo,
510 cgbo: m.cgbo,
511 oxide_cap_factor,
512 gamma,
513 lambda: m.lambda,
514 mj: m.mj,
515 mjsw: m.mjsw,
516 t_transconductance,
517 t_phi,
518 t_vbi,
519 t_vto,
520 t_sat_cur,
521 t_sat_cur_dens,
522 t_bulk_pot,
523 t_dep_cap,
524 drain_vcrit,
525 source_vcrit,
526 cbd: czbd,
527 cbdsw: czbdsw,
528 cbs: czbs,
529 cbssw: czbssw,
530 f2d,
531 f3d,
532 f4d,
533 f2s,
534 f3s,
535 f4s,
536 drain_conductance,
537 source_conductance,
538 notes,
539 })
540 }
541}
542
543pub(crate) fn limvds(vnew: f64, vold: f64) -> f64 {
545 if vold >= 3.5 {
546 if vnew > vold {
547 f64::min(vnew, 3.0 * vold + 2.0)
548 } else if vnew < 3.5 {
549 f64::max(vnew, 2.0)
550 } else {
551 vnew
552 }
553 } else if vnew > vold {
554 f64::min(vnew, 4.0)
555 } else {
556 f64::max(vnew, -0.5)
557 }
558}
559
560pub(crate) fn fetlim(vnew: f64, vold: f64, vto: f64) -> f64 {
563 let vtsthi = f64::abs(2.0 * (vold - vto)) + 2.0;
564 let vtstlo = vtsthi / 2.0 + 2.0;
565 let vtox = vto + 3.5;
566 let delv = vnew - vold;
567 if vold >= vto {
568 if vold >= vtox {
569 if delv <= 0.0 {
570 if vnew >= vtox {
572 if -delv > vtstlo { vold - vtstlo } else { vnew }
573 } else {
574 f64::max(vnew, vto + 2.0)
575 }
576 } else {
577 if delv >= vtsthi { vold + vtsthi } else { vnew }
579 }
580 } else if delv <= 0.0 {
581 f64::max(vnew, vto - 0.5)
582 } else {
583 f64::min(vnew, vto + 4.0)
584 }
585 } else if delv <= 0.0 {
586 if -delv > vtsthi { vold - vtsthi } else { vnew }
587 } else {
588 let vtemp = vto + 0.5;
589 if vnew <= vtemp {
590 if delv > vtstlo { vold + vtstlo } else { vnew }
591 } else {
592 vtemp
593 }
594 }
595}
596
597pub(crate) fn qmeyer(
607 vgs: f64,
608 vgd: f64,
609 _vgb: f64,
610 von: f64,
611 vdsat: f64,
612 phi: f64,
613 cox: f64,
614) -> (f64, f64, f64) {
615 const MAGIC_VDS: f64 = 0.025;
616 let vgst = vgs - von;
617 let vdsat = vdsat.max(MAGIC_VDS);
618 if vgst <= -phi {
619 (0.0, 0.0, cox / 2.0)
620 } else if vgst <= -phi / 2.0 {
621 (0.0, 0.0, -vgst * cox / (2.0 * phi))
622 } else if vgst <= 0.0 {
623 let capgb = -vgst * cox / (2.0 * phi);
624 let mut capgs = vgst * cox / (1.5 * phi) + cox / 3.0;
625 let vds = vgs - vgd;
626 let capgd;
627 if vds >= vdsat {
628 capgd = 0.0;
629 } else {
630 let vddif = 2.0 * vdsat - vds;
631 let vddif1 = vdsat - vds;
632 let vddif2 = vddif * vddif;
633 capgd = capgs * (1.0 - vdsat * vdsat / vddif2);
634 capgs *= 1.0 - vddif1 * vddif1 / vddif2;
635 }
636 (capgs, capgd, capgb)
637 } else {
638 let vds = vgs - vgd;
639 if vdsat <= vds {
640 (cox / 3.0, 0.0, 0.0)
641 } else {
642 let vddif = 2.0 * vdsat - vds;
643 let vddif1 = vdsat - vds;
644 let vddif2 = vddif * vddif;
645 (
646 cox * (1.0 - vddif1 * vddif1 / vddif2) / 3.0,
647 cox * (1.0 - vdsat * vdsat / vddif2) / 3.0,
648 0.0,
649 )
650 }
651 }
652}
653
654const VBD: usize = 0;
656const VBS: usize = 1;
657const VGS: usize = 2;
658const VDS: usize = 3;
659const CAPGS: usize = 4;
660const QGS: usize = 5;
661const CQGS: usize = 6;
662const CAPGD: usize = 7;
663const QGD: usize = 8;
664const CQGD: usize = 9;
665const CAPGB: usize = 10;
666const QGB: usize = 11;
667const CQGB: usize = 12;
668const QBD: usize = 13;
669const CQBD: usize = 14;
670const QBS: usize = 15;
671const CQBS: usize = 16;
672pub(crate) const MOS1_NUM_STATES: usize = 17;
673
674const D_D: usize = 0;
676const G_G: usize = 1;
677const S_S: usize = 2;
678const B_B: usize = 3;
679const DP_DP: usize = 4;
680const SP_SP: usize = 5;
681const D_DP: usize = 6;
682const G_B: usize = 7;
683const G_DP: usize = 8;
684const G_SP: usize = 9;
685const S_SP: usize = 10;
686const B_DP: usize = 11;
687const B_SP: usize = 12;
688const DP_SP: usize = 13;
689const DP_D: usize = 14;
690const B_G: usize = 15;
691const DP_G: usize = 16;
692const SP_G: usize = 17;
693const SP_S: usize = 18;
694const DP_B: usize = 19;
695const SP_B: usize = 20;
696const SP_DP: usize = 21;
697
698pub(crate) struct MosPzConductances {
702 pub xnrm: f64,
703 pub xrev: f64,
704 pub gm: f64,
705 pub gds: f64,
706 pub gmbs: f64,
707 pub gbd: f64,
708 pub gbs: f64,
709 pub gdpr: f64,
710 pub gspr: f64,
711}
712
713pub(crate) fn mos_pz_conductances(
719 ctx: &mut crate::matrix::PzCtx,
720 s: &[Slot; 22],
721 c: &MosPzConductances,
722) {
723 let (xnrm, xrev) = (c.xnrm, c.xrev);
724 let (gm, gds, gmbs, gbd, gbs) = (c.gm, c.gds, c.gmbs, c.gbd, c.gbs);
725 let (gdpr, gspr) = (c.gdpr, c.gspr);
726 ctx.add_g(s[D_D], gdpr);
727 ctx.add_g(s[S_S], gspr);
728 ctx.add_g(s[B_B], gbd + gbs);
729 ctx.add_g(s[DP_DP], gdpr + gds + gbd + xrev * (gm + gmbs));
730 ctx.add_g(s[SP_SP], gspr + gds + gbs + xnrm * (gm + gmbs));
731 ctx.add_g(s[D_DP], -gdpr);
732 ctx.add_g(s[S_SP], -gspr);
733 ctx.add_g(s[B_DP], -gbd);
734 ctx.add_g(s[B_SP], -gbs);
735 ctx.add_g(s[DP_D], -gdpr);
736 ctx.add_g(s[DP_G], (xnrm - xrev) * gm);
737 ctx.add_g(s[DP_B], -gbd + (xnrm - xrev) * gmbs);
738 ctx.add_g(s[DP_SP], -gds - xnrm * (gm + gmbs));
739 ctx.add_g(s[SP_G], -(xnrm - xrev) * gm);
740 ctx.add_g(s[SP_S], -gspr);
741 ctx.add_g(s[SP_B], -gbs - (xnrm - xrev) * gmbs);
742 ctx.add_g(s[SP_DP], -gds - xrev * (gm + gmbs));
743}
744
745pub(crate) struct MeyerPzCaps {
750 pub capgs: f64,
751 pub capgd: f64,
752 pub capgb: f64,
753 pub capbd: f64,
754 pub capbs: f64,
755}
756
757pub(crate) fn meyer_pz_load(
762 ctx: &mut crate::matrix::PzCtx,
763 s: &[Slot; 22],
764 caps: &MeyerPzCaps,
765 g: &MosPzConductances,
766) {
767 let xgs = caps.capgs;
768 let xgd = caps.capgd;
769 let xgb = caps.capgb;
770 let xbd = caps.capbd;
771 let xbs = caps.capbs;
772 ctx.add_cap(s[G_G], xgd + xgs + xgb);
773 ctx.add_cap(s[B_B], xgb + xbd + xbs);
774 ctx.add_cap(s[DP_DP], xgd + xbd);
775 ctx.add_cap(s[SP_SP], xgs + xbs);
776 ctx.add_cap(s[G_B], -xgb);
777 ctx.add_cap(s[G_DP], -xgd);
778 ctx.add_cap(s[G_SP], -xgs);
779 ctx.add_cap(s[B_G], -xgb);
780 ctx.add_cap(s[B_DP], -xbd);
781 ctx.add_cap(s[B_SP], -xbs);
782 ctx.add_cap(s[DP_G], -xgd);
783 ctx.add_cap(s[DP_B], -xbd);
784 ctx.add_cap(s[SP_G], -xgs);
785 ctx.add_cap(s[SP_B], -xbs);
786 mos_pz_conductances(ctx, s, g);
787}
788
789pub(crate) struct Mos1I {
790 drain: Var,
791 gate: Var,
792 source: Var,
793 bulk: Var,
794 d_p: Var,
795 s_p: Var,
796 t: Mos1Temp,
797 geom: MosGeom,
798 off: bool,
799 ic_vds: f64,
800 ic_vgs: f64,
801 ic_vbs: f64,
802 ic_given: crate::devices::IcGiven,
803 st: usize,
804 s: [Slot; 22],
805 mode: f64, von: f64,
808 vdsat: f64,
809 gm: f64,
810 gds: f64,
811 gmbs: f64,
812 gbd: f64,
813 gbs: f64,
814 capbd: f64,
815 capbs: f64,
816 cd: f64,
817 cbs: f64,
818 cbd: f64,
819 dist: MosDistoCoeffs,
821}
822
823impl Mos1I {
824 #[allow(clippy::too_many_arguments)]
825 pub fn new(
826 nodes: [Var; 4], primes: [Var; 2], t: Mos1Temp,
829 geom: MosGeom,
830 off: bool,
831 ic: (f64, f64, f64), st: usize,
833 ) -> Self {
834 Mos1I {
835 drain: nodes[0],
836 gate: nodes[1],
837 source: nodes[2],
838 bulk: nodes[3],
839 d_p: primes[0],
840 s_p: primes[1],
841 t,
842 geom,
843 off,
844 ic_vds: ic.0,
845 ic_vgs: ic.1,
846 ic_vbs: ic.2,
847 ic_given: crate::devices::IcGiven::ALL,
848 st,
849 s: [TRASH; 22],
850 mode: 1.0,
851 von: 0.0,
852 vdsat: 0.0,
853 gm: 0.0,
854 gds: 0.0,
855 gmbs: 0.0,
856 gbd: 0.0,
857 gbs: 0.0,
858 capbd: 0.0,
859 capbs: 0.0,
860 cd: 0.0,
861 cbs: 0.0,
862 cbd: 0.0,
863 dist: MosDistoCoeffs::default(),
864 }
865 }
866}
867
868impl crate::devices::Device for Mos1I {
869 fn par_load(&self) -> bool {
870 true
871 }
872
873 fn set_ic(&mut self, rhs: &[f64]) {
876 use crate::devices::getic;
877 let g = self.ic_given;
878 getic(g, 2, &mut self.ic_vbs, rhs, self.bulk, self.source);
879 getic(g, 0, &mut self.ic_vds, rhs, self.drain, self.source);
880 getic(g, 1, &mut self.ic_vgs, rhs, self.gate, self.source);
881 }
882
883 fn set_ic_given(&mut self, given: crate::devices::IcGiven) {
884 self.ic_given = given;
885 }
886
887 fn conv_test(&self, ctx: &crate::matrix::ConvCtx) -> bool {
890 let typ = self.t.type_;
891 let st = self.st;
892 let vbs = typ * (ctx.rhs[self.bulk] - ctx.rhs[self.s_p]);
893 let vgs = typ * (ctx.rhs[self.gate] - ctx.rhs[self.s_p]);
894 let vds = typ * (ctx.rhs[self.d_p] - ctx.rhs[self.s_p]);
895 let vbd = vbs - vds;
896 let vgd = vgs - vds;
897 let vgdo = ctx.states.get(0, st + VGS) - ctx.states.get(0, st + VDS);
898 let delvbs = vbs - ctx.states.get(0, st + VBS);
899 let delvbd = vbd - ctx.states.get(0, st + VBD);
900 let delvgs = vgs - ctx.states.get(0, st + VGS);
901 let delvds = vds - ctx.states.get(0, st + VDS);
902 let delvgd = vgd - vgdo;
903 let cdhat = if self.mode >= 0.0 {
904 self.cd - self.gbd * delvbd + self.gmbs * delvbs + self.gm * delvgs + self.gds * delvds
905 } else {
906 self.cd - (self.gbd - self.gmbs) * delvbd - self.gm * delvgd + self.gds * delvds
907 };
908 let cbhat = self.cbs + self.cbd + self.gbd * delvbd + self.gbs * delvbs;
909 let tol = ctx.reltol * f64::max(cdhat.abs(), self.cd.abs()) + ctx.abstol;
910 if (cdhat - self.cd).abs() >= tol {
911 return false;
912 }
913 let cb = self.cbs + self.cbd;
914 let tol = ctx.reltol * f64::max(cbhat.abs(), cb.abs()) + ctx.abstol;
915 (cbhat - cb).abs() <= tol
916 }
917
918 fn alloc(&mut self, mb: &mut MatrixBuilder) {
919 let (d, g, s, b) = (self.drain, self.gate, self.source, self.bulk);
920 let (dp, sp) = (self.d_p, self.s_p);
921 self.s = [
922 mb.slot(d, d),
923 mb.slot(g, g),
924 mb.slot(s, s),
925 mb.slot(b, b),
926 mb.slot(dp, dp),
927 mb.slot(sp, sp),
928 mb.slot(d, dp),
929 mb.slot(g, b),
930 mb.slot(g, dp),
931 mb.slot(g, sp),
932 mb.slot(s, sp),
933 mb.slot(b, dp),
934 mb.slot(b, sp),
935 mb.slot(dp, sp),
936 mb.slot(dp, d),
937 mb.slot(b, g),
938 mb.slot(dp, g),
939 mb.slot(sp, g),
940 mb.slot(sp, s),
941 mb.slot(dp, b),
942 mb.slot(sp, b),
943 mb.slot(sp, dp),
944 ];
945 }
946
947 #[allow(clippy::too_many_lines)]
948 fn load(&mut self, ctx: &mut LoadCtx) {
949 let t = &self.t;
950 let typ = t.type_;
951 let vt = t.vt;
952 let st = self.st;
953
954 let eff_l = self.geom.l - 2.0 * t.lat_diff;
955 let (drain_sat_cur, source_sat_cur) =
956 if t.t_sat_cur_dens == 0.0 || self.geom.ad == 0.0 || self.geom.as_ == 0.0 {
957 (t.t_sat_cur, t.t_sat_cur)
958 } else {
959 (
960 t.t_sat_cur_dens * self.geom.ad,
961 t.t_sat_cur_dens * self.geom.as_,
962 )
963 };
964 let gate_source_overlap_cap = t.cgso * self.geom.w;
965 let gate_drain_overlap_cap = t.cgdo * self.geom.w;
966 let gate_bulk_overlap_cap = t.cgbo * eff_l;
967 let beta = t.t_transconductance * self.geom.w / eff_l;
968 let oxide_cap = t.oxide_cap_factor * eff_l * self.geom.w;
969
970 let mut check = true;
973 let (mut vbs, mut vgs, mut vds);
974 let predictor = matches!(ctx.mode, InitMode::Pred | InitMode::Tran);
975 let general = matches!(ctx.mode, InitMode::Float | InitMode::Smsig)
976 || (ctx.mode == InitMode::Fix && !self.off);
977 if predictor || general {
978 if predictor {
979 let xfact = ctx.pred_xfact;
982 let mut pred = |o: usize| {
983 let s1 = ctx.states.get(1, st + o);
984 ctx.states.set(0, st + o, s1);
985 (1.0 + xfact) * s1 - xfact * ctx.states.get(2, st + o)
986 };
987 vbs = pred(VBS);
988 vgs = pred(VGS);
989 vds = pred(VDS);
990 let vbd0 = ctx.states.get(0, st + VBS) - ctx.states.get(0, st + VDS);
991 ctx.states.set(0, st + VBD, vbd0);
992 } else {
993 vbs = typ * (ctx.sol[self.bulk] - ctx.sol[self.s_p]);
994 vgs = typ * (ctx.sol[self.gate] - ctx.sol[self.s_p]);
995 vds = typ * (ctx.sol[self.d_p] - ctx.sol[self.s_p]);
996 }
997 let mut vbd = vbs - vds;
998 let mut vgd = vgs - vds;
999 let vgdo = ctx.states.get(0, st + VGS) - ctx.states.get(0, st + VDS);
1000
1001 let von = typ * self.von;
1003 if ctx.states.get(0, st + VDS) >= 0.0 {
1004 vgs = fetlim(vgs, ctx.states.get(0, st + VGS), von);
1005 vds = vgs - vgd;
1006 vds = limvds(vds, ctx.states.get(0, st + VDS));
1007 } else {
1008 vgd = fetlim(vgd, vgdo, von);
1009 vds = vgs - vgd;
1010 vds = -limvds(-vds, -ctx.states.get(0, st + VDS));
1013 vgs = vgd + vds;
1014 }
1015 if vds >= 0.0 {
1016 vbs = pnjlim(
1017 vbs,
1018 ctx.states.get(0, st + VBS),
1019 vt,
1020 t.source_vcrit,
1021 &mut check,
1022 );
1023 } else {
1024 vbd = pnjlim(
1025 vbd,
1026 ctx.states.get(0, st + VBD),
1027 vt,
1028 t.drain_vcrit,
1029 &mut check,
1030 );
1031 vbs = vbd + vds;
1032 }
1033 } else if ctx.mode == InitMode::Jct && !self.off {
1034 vds = typ * self.ic_vds;
1035 vgs = typ * self.ic_vgs;
1036 vbs = typ * self.ic_vbs;
1037 if vds == 0.0 && vgs == 0.0 && vbs == 0.0 && !(ctx.kind == AnKind::TranOp && ctx.uic) {
1038 vbs = -1.0;
1039 vgs = typ * t.t_vto;
1040 vds = 0.0;
1041 }
1042 } else {
1043 vbs = 0.0;
1044 vgs = 0.0;
1045 vds = 0.0;
1046 }
1047
1048 let vbd = vbs - vds;
1049 let vgd = vgs - vds;
1050 let vgb = vgs - vbs;
1051
1052 let (mut gbs, mut cbs);
1056 if vbs <= -3.0 * vt {
1057 gbs = ctx.gmin;
1058 cbs = gbs * vbs - source_sat_cur;
1059 } else {
1060 let evbs = f64::exp(f64::min(MAX_EXP_ARG, vbs / vt));
1061 gbs = source_sat_cur * evbs / vt + ctx.gmin;
1062 cbs = source_sat_cur * (evbs - 1.0) + ctx.gmin * vbs;
1063 }
1064 let (mut gbd, mut cbd);
1065 if vbd <= -3.0 * vt {
1066 gbd = ctx.gmin;
1067 cbd = gbd * vbd - drain_sat_cur;
1068 } else {
1069 let evbd = f64::exp(f64::min(MAX_EXP_ARG, vbd / vt));
1070 gbd = drain_sat_cur * evbd / vt + ctx.gmin;
1071 cbd = drain_sat_cur * (evbd - 1.0) + ctx.gmin * vbd;
1072 }
1073
1074 self.mode = if vds >= 0.0 { 1.0 } else { -1.0 };
1076 let mode = self.mode;
1077
1078 let cdrain;
1080 let (gm, gds, gmbs);
1081 let (von, vdsat);
1082 {
1083 let vx = if mode == 1.0 { vbs } else { vbd };
1084 let sarg = if vx <= 0.0 {
1085 (t.t_phi - vx).sqrt()
1086 } else {
1087 let s = t.t_phi.sqrt();
1088 f64::max(0.0, s - vx / (s + s))
1089 };
1090 von = t.t_vbi * typ + t.gamma * sarg;
1091 let vgst = (if mode == 1.0 { vgs } else { vgd }) - von;
1092 vdsat = f64::max(vgst, 0.0);
1093 let arg = if sarg <= 0.0 {
1094 0.0
1095 } else {
1096 t.gamma / (sarg + sarg)
1097 };
1098 if vgst <= 0.0 {
1099 cdrain = 0.0;
1101 gm = 0.0;
1102 gds = 0.0;
1103 gmbs = 0.0;
1104 } else {
1105 let betap = beta * (1.0 + t.lambda * (vds * mode));
1106 if vgst <= vds * mode {
1107 cdrain = betap * vgst * vgst * 0.5;
1109 gm = betap * vgst;
1110 gds = t.lambda * beta * vgst * vgst * 0.5;
1111 gmbs = gm * arg;
1112 } else {
1113 cdrain = betap * (vds * mode) * (vgst - 0.5 * (vds * mode));
1115 gm = betap * (vds * mode);
1116 gds = betap * (vgst - vds * mode)
1117 + t.lambda * beta * (vds * mode) * (vgst - 0.5 * (vds * mode));
1118 gmbs = gm * arg;
1119 }
1120 }
1121 }
1122 self.von = typ * von;
1123 self.vdsat = typ * vdsat;
1124 self.gm = gm;
1125 self.gds = gds;
1126 self.gmbs = gmbs;
1127
1128 let mut cd = mode * cdrain - cbd;
1130
1131 let smsig = ctx.mode == InitMode::Smsig;
1132 if ctx.kind == AnKind::Tran || ctx.kind == AnKind::TranOp || smsig {
1133 let dep = |v: f64, cz: f64, czsw: f64, f2: f64, f3: f64, f4: f64| {
1138 if cz != 0.0 || czsw != 0.0 {
1139 if v < t.t_dep_cap {
1140 let arg = 1.0 - v / t.t_bulk_pot;
1141 let (sarg, sargsw);
1142 if t.mj == t.mjsw {
1143 if t.mj == 0.5 {
1144 sarg = 1.0 / arg.sqrt();
1145 sargsw = sarg;
1146 } else {
1147 sarg = f64::exp(-t.mj * f64::ln(arg));
1148 sargsw = sarg;
1149 }
1150 } else {
1151 sarg = if t.mj == 0.5 {
1152 1.0 / arg.sqrt()
1153 } else {
1154 f64::exp(-t.mj * f64::ln(arg))
1155 };
1156 sargsw = if t.mjsw == 0.5 {
1157 1.0 / arg.sqrt()
1158 } else {
1159 f64::exp(-t.mjsw * f64::ln(arg))
1160 };
1161 }
1162 let q = t.t_bulk_pot
1163 * (cz * (1.0 - arg * sarg) / (1.0 - t.mj)
1164 + czsw * (1.0 - arg * sargsw) / (1.0 - t.mjsw));
1165 (q, cz * sarg + czsw * sargsw)
1166 } else {
1167 (f4 + v * (f2 + v * f3 / 2.0), f2 + v * f3)
1168 }
1169 } else {
1170 (0.0, 0.0)
1171 }
1172 };
1173 let (qbs, capbs) = dep(vbs, t.cbs, t.cbssw, t.f2s, t.f3s, t.f4s);
1174 ctx.states.set(0, st + QBS, qbs);
1175 self.capbs = capbs;
1176 let (qbd, capbd) = dep(vbd, t.cbd, t.cbdsw, t.f2d, t.f3d, t.f4d);
1177 ctx.states.set(0, st + QBD, qbd);
1178 self.capbd = capbd;
1179
1180 if ctx.kind == AnKind::Tran {
1181 let (geq, _) = ctx.integrate(self.capbd, st + QBD);
1183 gbd += geq;
1184 cbd += ctx.states.get(0, st + CQBD);
1185 cd -= ctx.states.get(0, st + CQBD);
1186 let (geq, _) = ctx.integrate(self.capbs, st + QBS);
1187 gbs += geq;
1188 cbs += ctx.states.get(0, st + CQBS);
1189 }
1190 }
1191 self.gbs = gbs;
1192 self.gbd = gbd;
1193
1194 if (!self.off || !matches!(ctx.mode, InitMode::Fix | InitMode::Smsig)) && check {
1196 ctx.noncon = true;
1197 }
1198
1199 ctx.states.set(0, st + VBS, vbs);
1201 ctx.states.set(0, st + VBD, vbd);
1202 ctx.states.set(0, st + VGS, vgs);
1203 ctx.states.set(0, st + VDS, vds);
1204
1205 let (mut capgs, mut capgd, mut capgb) = (0.0, 0.0, 0.0);
1207 if ctx.kind == AnKind::Tran || ctx.kind == AnKind::TranOp || smsig {
1208 let (half_gs, half_gd, half_gb) = if mode > 0.0 {
1211 qmeyer(vgs, vgd, vgb, von, vdsat, t.t_phi, oxide_cap)
1212 } else {
1213 let (o1, o2, o3) = qmeyer(vgd, vgs, vgb, von, vdsat, t.t_phi, oxide_cap);
1214 (o2, o1, o3)
1215 };
1216 ctx.states.set(0, st + CAPGS, half_gs);
1217 ctx.states.set(0, st + CAPGD, half_gd);
1218 ctx.states.set(0, st + CAPGB, half_gb);
1219 let vgs1 = ctx.states.get(1, st + VGS);
1220 let vgd1 = vgs1 - ctx.states.get(1, st + VDS);
1221 let vgb1 = vgs1 - ctx.states.get(1, st + VBS);
1222 if ctx.kind == AnKind::TranOp || smsig {
1223 capgs = 2.0 * ctx.states.get(0, st + CAPGS) + gate_source_overlap_cap;
1224 capgd = 2.0 * ctx.states.get(0, st + CAPGD) + gate_drain_overlap_cap;
1225 capgb = 2.0 * ctx.states.get(0, st + CAPGB) + gate_bulk_overlap_cap;
1226 } else {
1227 capgs = ctx.states.get(0, st + CAPGS)
1228 + ctx.states.get(1, st + CAPGS)
1229 + gate_source_overlap_cap;
1230 capgd = ctx.states.get(0, st + CAPGD)
1231 + ctx.states.get(1, st + CAPGD)
1232 + gate_drain_overlap_cap;
1233 capgb = ctx.states.get(0, st + CAPGB)
1234 + ctx.states.get(1, st + CAPGB)
1235 + gate_bulk_overlap_cap;
1236 }
1237
1238 if smsig {
1239 return;
1242 }
1243
1244 if predictor {
1245 let xfact = ctx.pred_xfact;
1247 for o in [QGS, QGD, QGB] {
1248 let v = (1.0 + xfact) * ctx.states.get(1, st + o)
1249 - xfact * ctx.states.get(2, st + o);
1250 ctx.states.set(0, st + o, v);
1251 }
1252 } else if ctx.kind == AnKind::Tran {
1253 let q = (vgs - vgs1) * capgs + ctx.states.get(1, st + QGS);
1254 ctx.states.set(0, st + QGS, q);
1255 let q = (vgd - vgd1) * capgd + ctx.states.get(1, st + QGD);
1256 ctx.states.set(0, st + QGD, q);
1257 let q = (vgb - vgb1) * capgb + ctx.states.get(1, st + QGB);
1258 ctx.states.set(0, st + QGB, q);
1259 } else {
1260 ctx.states.set(0, st + QGS, vgs * capgs);
1262 ctx.states.set(0, st + QGD, vgd * capgd);
1263 ctx.states.set(0, st + QGB, vgb * capgb);
1264 }
1265 }
1266
1267 let (mut gcgs, mut ceqgs, mut gcgd, mut ceqgd, mut gcgb, mut ceqgb) =
1270 (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
1271 if ctx.mode != InitMode::Tran && ctx.kind == AnKind::Tran {
1272 if capgs == 0.0 {
1273 ctx.states.set(0, st + CQGS, 0.0);
1274 }
1275 if capgd == 0.0 {
1276 ctx.states.set(0, st + CQGD, 0.0);
1277 }
1278 if capgb == 0.0 {
1279 ctx.states.set(0, st + CQGB, 0.0);
1280 }
1281 let (g, ceq) = ctx.integrate(capgs, st + QGS);
1282 gcgs = g;
1283 ceqgs = ceq - gcgs * vgs + ctx.ag[0] * ctx.states.get(0, st + QGS);
1284 let (g, ceq) = ctx.integrate(capgd, st + QGD);
1285 gcgd = g;
1286 ceqgd = ceq - gcgd * vgd + ctx.ag[0] * ctx.states.get(0, st + QGD);
1287 let (g, ceq) = ctx.integrate(capgb, st + QGB);
1288 gcgb = g;
1289 ceqgb = ceq - gcgb * vgb + ctx.ag[0] * ctx.states.get(0, st + QGB);
1290 }
1291
1292 let ceqbs = typ * (cbs - gbs * vbs);
1297 let ceqbd = typ * (cbd - gbd * vbd);
1298 let (xnrm, xrev, cdreq);
1299 if mode >= 0.0 {
1300 xnrm = 1.0;
1301 xrev = 0.0;
1302 cdreq = typ * (cdrain - gds * vds - gm * vgs - gmbs * vbs);
1303 } else {
1304 xnrm = 0.0;
1305 xrev = 1.0;
1306 cdreq = -typ * (cdrain - gds * (-vds) - gm * vgd - gmbs * vbd);
1307 }
1308 self.cd = cd;
1310 self.cbs = cbs;
1311 self.cbd = cbd;
1312 ctx.add_rhs(self.gate, -(typ * (ceqgs + ceqgb + ceqgd)));
1313 ctx.add_rhs(self.bulk, -(ceqbs + ceqbd - typ * ceqgb));
1314 ctx.add_rhs(self.d_p, ceqbd - cdreq + typ * ceqgd);
1315 ctx.add_rhs(self.s_p, cdreq + ceqbs + typ * ceqgs);
1316
1317 let gdpr = t.drain_conductance;
1319 let gspr = t.source_conductance;
1320 ctx.add(self.s[D_D], gdpr);
1321 ctx.add(self.s[G_G], gcgd + gcgs + gcgb);
1322 ctx.add(self.s[S_S], gspr);
1323 ctx.add(self.s[B_B], gbd + gbs + gcgb);
1324 ctx.add(self.s[DP_DP], gdpr + gds + gbd + xrev * (gm + gmbs) + gcgd);
1325 ctx.add(self.s[SP_SP], gspr + gds + gbs + xnrm * (gm + gmbs) + gcgs);
1326 ctx.add(self.s[D_DP], -gdpr);
1327 ctx.add(self.s[G_B], -gcgb);
1328 ctx.add(self.s[G_DP], -gcgd);
1329 ctx.add(self.s[G_SP], -gcgs);
1330 ctx.add(self.s[S_SP], -gspr);
1331 ctx.add(self.s[B_G], -gcgb);
1332 ctx.add(self.s[B_DP], -gbd);
1333 ctx.add(self.s[B_SP], -gbs);
1334 ctx.add(self.s[DP_D], -gdpr);
1335 ctx.add(self.s[DP_G], (xnrm - xrev) * gm - gcgd);
1336 ctx.add(self.s[DP_B], -gbd + (xnrm - xrev) * gmbs);
1337 ctx.add(self.s[DP_SP], -gds - xnrm * (gm + gmbs));
1338 ctx.add(self.s[SP_G], -(xnrm - xrev) * gm - gcgs);
1339 ctx.add(self.s[SP_S], -gspr);
1340 ctx.add(self.s[SP_B], -gbs - (xnrm - xrev) * gmbs);
1341 ctx.add(self.s[SP_DP], -gds - xrev * (gm + gmbs));
1342 }
1343
1344 fn trunc(&self, t: &crate::integ::TerrCtx, time_step: &mut f64) {
1345 crate::integ::terr(self.st + QGS, t, time_step);
1346 crate::integ::terr(self.st + QGD, t, time_step);
1347 crate::integ::terr(self.st + QGB, t, time_step);
1348 }
1349
1350 fn noise_suffixes(&self) -> Option<&'static [&'static str]> {
1353 Some(MOS_NOISE_SUFFIXES)
1354 }
1355
1356 fn noise_dens(&self, ctx: &crate::matrix::NoiseCtx) -> Vec<f64> {
1357 let n = MosNoise {
1358 drain: self.drain,
1359 source: self.source,
1360 d_p: self.d_p,
1361 s_p: self.s_p,
1362 gdpr: self.t.drain_conductance,
1363 gspr: self.t.source_conductance,
1364 gm: self.gm,
1365 cd: self.cd,
1366 kf: self.t.kf,
1367 af: self.t.af,
1368 nlev: self.t.nlev,
1369 gdsnoi: self.t.gdsnoi,
1370 w: self.geom.w,
1371 eff_l: self.geom.l - 2.0 * self.t.lat_diff,
1372 temp: self.t.temp,
1373 vgs: ctx.states.get(0, self.st + VGS),
1374 vds: ctx.states.get(0, self.st + VDS),
1375 mode: self.mode,
1376 von: self.von,
1377 vdsat: self.vdsat,
1378 typ: self.t.type_,
1379 t_transconductance: self.t.t_transconductance,
1380 cox: if self.t.oxide_cap_factor == 0.0 {
1381 3.9 * 8.854214871e-12 / 1e-7
1384 } else {
1385 self.t.oxide_cap_factor
1386 },
1387 };
1388 mos_noise(ctx, &n)
1389 }
1390
1391 fn load_ac(&mut self, ctx: &mut AcCtx) {
1393 let t = &self.t;
1394 let st = self.st;
1395 let (xnrm, xrev) = if self.mode < 0.0 {
1396 (0.0, 1.0)
1397 } else {
1398 (1.0, 0.0)
1399 };
1400 let eff_l = self.geom.l - 2.0 * t.lat_diff;
1401 let gate_source_overlap_cap = t.cgso * self.geom.w;
1402 let gate_drain_overlap_cap = t.cgdo * self.geom.w;
1403 let gate_bulk_overlap_cap = t.cgbo * eff_l;
1404 let capgs = 2.0 * ctx.states.get(0, st + CAPGS) + gate_source_overlap_cap;
1405 let capgd = 2.0 * ctx.states.get(0, st + CAPGD) + gate_drain_overlap_cap;
1406 let capgb = 2.0 * ctx.states.get(0, st + CAPGB) + gate_bulk_overlap_cap;
1407 let xgs = capgs * ctx.omega;
1408 let xgd = capgd * ctx.omega;
1409 let xgb = capgb * ctx.omega;
1410 let xbd = self.capbd * ctx.omega;
1411 let xbs = self.capbs * ctx.omega;
1412 let (gm, gds, gmbs) = (self.gm, self.gds, self.gmbs);
1413 let (gbd, gbs) = (self.gbd, self.gbs);
1414 let gdpr = t.drain_conductance;
1415 let gspr = t.source_conductance;
1416
1417 ctx.add_b(self.s[G_G], xgd + xgs + xgb);
1418 ctx.add_b(self.s[B_B], xgb + xbd + xbs);
1419 ctx.add_b(self.s[DP_DP], xgd + xbd);
1420 ctx.add_b(self.s[SP_SP], xgs + xbs);
1421 ctx.add_b(self.s[G_B], -xgb);
1422 ctx.add_b(self.s[G_DP], -xgd);
1423 ctx.add_b(self.s[G_SP], -xgs);
1424 ctx.add_b(self.s[B_G], -xgb);
1425 ctx.add_b(self.s[B_DP], -xbd);
1426 ctx.add_b(self.s[B_SP], -xbs);
1427 ctx.add_b(self.s[DP_G], -xgd);
1428 ctx.add_b(self.s[DP_B], -xbd);
1429 ctx.add_b(self.s[SP_G], -xgs);
1430 ctx.add_b(self.s[SP_B], -xbs);
1431 ctx.add_g(self.s[D_D], gdpr);
1432 ctx.add_g(self.s[S_S], gspr);
1433 ctx.add_g(self.s[B_B], gbd + gbs);
1434 ctx.add_g(self.s[DP_DP], gdpr + gds + gbd + xrev * (gm + gmbs));
1435 ctx.add_g(self.s[SP_SP], gspr + gds + gbs + xnrm * (gm + gmbs));
1436 ctx.add_g(self.s[D_DP], -gdpr);
1437 ctx.add_g(self.s[S_SP], -gspr);
1438 ctx.add_g(self.s[B_DP], -gbd);
1439 ctx.add_g(self.s[B_SP], -gbs);
1440 ctx.add_g(self.s[DP_D], -gdpr);
1441 ctx.add_g(self.s[DP_G], (xnrm - xrev) * gm);
1442 ctx.add_g(self.s[DP_B], -gbd + (xnrm - xrev) * gmbs);
1443 ctx.add_g(self.s[DP_SP], -gds - xnrm * (gm + gmbs));
1444 ctx.add_g(self.s[SP_G], -(xnrm - xrev) * gm);
1445 ctx.add_g(self.s[SP_S], -gspr);
1446 ctx.add_g(self.s[SP_B], -gbs - (xnrm - xrev) * gmbs);
1447 ctx.add_g(self.s[SP_DP], -gds - xrev * (gm + gmbs));
1448 }
1449
1450 fn load_pz(&mut self, ctx: &mut crate::matrix::PzCtx) -> Result<(), &'static str> {
1452 let t = &self.t;
1453 let st = self.st;
1454 let (xnrm, xrev) = if self.mode < 0.0 {
1455 (0.0, 1.0)
1456 } else {
1457 (1.0, 0.0)
1458 };
1459 let eff_l = self.geom.l - 2.0 * t.lat_diff;
1460 let caps = MeyerPzCaps {
1461 capgs: 2.0 * ctx.states.get(0, st + CAPGS) + t.cgso * self.geom.w,
1462 capgd: 2.0 * ctx.states.get(0, st + CAPGD) + t.cgdo * self.geom.w,
1463 capgb: 2.0 * ctx.states.get(0, st + CAPGB) + t.cgbo * eff_l,
1464 capbd: self.capbd,
1465 capbs: self.capbs,
1466 };
1467 let g = MosPzConductances {
1468 xnrm,
1469 xrev,
1470 gm: self.gm,
1471 gds: self.gds,
1472 gmbs: self.gmbs,
1473 gbd: self.gbd,
1474 gbs: self.gbs,
1475 gdpr: t.drain_conductance,
1476 gspr: t.source_conductance,
1477 };
1478 meyer_pz_load(ctx, &self.s, &caps, &g);
1479 Ok(())
1480 }
1481
1482 #[allow(clippy::too_many_lines)]
1488 fn disto_setup(&mut self, ctx: &DistoSetupCtx) -> Result<(), &'static str> {
1489 let t = &self.t;
1490 let g = &self.geom;
1491 let typ = t.type_;
1492 let vt = t.vt;
1493 let sol = ctx.sol;
1494 let gmin = ctx.gmin;
1495 let eff_l = g.l - 2.0 * t.lat_diff;
1496 let (drain_sat_cur, source_sat_cur) =
1497 if t.t_sat_cur_dens == 0.0 || g.ad == 0.0 || g.as_ == 0.0 {
1498 (t.t_sat_cur, t.t_sat_cur)
1499 } else {
1500 (t.t_sat_cur_dens * g.ad, t.t_sat_cur_dens * g.as_)
1501 };
1502 let beta = t.t_transconductance * g.w / eff_l;
1503 let oxide_cap = t.oxide_cap_factor * eff_l * g.w;
1504
1505 let vbs = typ * (sol[self.bulk] - sol[self.s_p]);
1506 let vgs = typ * (sol[self.gate] - sol[self.s_p]);
1507 let vds = typ * (sol[self.d_p] - sol[self.s_p]);
1508 let vbd = vbs - vds;
1509 let vgd = vgs - vds;
1510
1511 let gbs = bulk_diode(vbs, source_sat_cur, vt, gmin, typ);
1512 let gbd = bulk_diode(vbd, drain_sat_cur, vt, gmin, typ);
1513 let mode = if vds >= 0.0 { 1.0 } else { -1.0 };
1514
1515 let lvbs = if mode == 1.0 { vbs } else { vbd };
1516 let (sarg, dvondvbs, d2vondvbs2, d3vondvbs3);
1517 if lvbs <= 0.0 {
1518 sarg = f64::sqrt(t.t_phi - lvbs);
1519 if -t.gamma != 0.0 {
1520 let d1 = -t.gamma * 0.5 / sarg;
1521 let d2 = -d1 * 0.5 / (sarg * sarg);
1522 dvondvbs = d1;
1523 d2vondvbs2 = d2;
1524 d3vondvbs3 = 1.5 * d2 / (sarg * sarg);
1525 } else {
1526 (dvondvbs, d2vondvbs2, d3vondvbs3) = (0.0, 0.0, 0.0);
1527 }
1528 } else {
1529 let s0 = f64::sqrt(t.t_phi);
1530 let d1 = if t.gamma != 0.0 {
1531 -t.gamma / (s0 + s0)
1532 } else {
1533 0.0
1534 };
1535 (d2vondvbs2, d3vondvbs3) = (0.0, 0.0);
1536 sarg = f64::max(0.0, s0 - lvbs / (s0 + s0));
1537 dvondvbs = if sarg <= 0.0 { 0.0 } else { d1 };
1538 }
1539 let von = t.t_vbi * typ + t.gamma * sarg;
1540 let vgst = (if mode == 1.0 { vgs } else { vgd }) - von;
1541 let vdsat = f64::max(vgst, 0.0);
1542 let lambda = t.lambda;
1543 let mut dd = DrainDerivs::default();
1544 if vgst > 0.0 {
1545 let betap = beta * (1.0 + lambda * (vds * mode));
1546 if vgst <= vds * mode {
1547 let gm = betap * vgst;
1549 let gmds = vgst * lambda * beta;
1550 let gmb = -betap * dvondvbs;
1551 dd = DrainDerivs {
1552 gm2: betap,
1553 gds2: 0.0,
1554 gb2: -(gm * d2vondvbs2 - betap * dvondvbs * dvondvbs),
1555 gmds,
1556 gbds: -gmds * dvondvbs,
1557 gmb,
1558 gm3: 0.0,
1559 gb3: -(gmb * d2vondvbs2 + gm * d3vondvbs3
1560 - betap * 2.0 * dvondvbs * d2vondvbs2),
1561 gds3: 0.0,
1562 gm2ds: beta * lambda,
1563 gm2b: 0.0,
1564 gmb2: -betap * d2vondvbs2,
1565 gb2ds: -(gmds * d2vondvbs2 - dvondvbs * dvondvbs * beta * lambda),
1566 gmds2: 0.0,
1567 gbds2: 0.0,
1568 gmbds: -beta * lambda * dvondvbs,
1569 };
1570 } else {
1571 let gm = betap * (vds * mode);
1573 let gmds = beta * lambda * vds * mode + betap;
1574 let gmds2 = 2.0 * lambda * beta;
1575 dd = DrainDerivs {
1576 gm2: 0.0,
1577 gb2: -(gm * d2vondvbs2),
1578 gds2: 2.0 * beta * lambda * (vgst - vds * mode) - betap,
1579 gmds,
1580 gbds: -gmds * dvondvbs,
1581 gmb: 0.0,
1582 gm3: 0.0,
1583 gb3: -gm * d3vondvbs3,
1584 gds3: -beta * lambda * 3.,
1585 gm2ds: 0.0,
1586 gm2b: 0.0,
1587 gmb2: 0.0,
1588 gmds2,
1589 gb2ds: -(gmds * d2vondvbs2),
1590 gbds2: -gmds2 * dvondvbs,
1591 gmbds: 0.0,
1592 };
1593 }
1594 }
1595
1596 let junctions = MosJunctions {
1597 cbs: t.cbs,
1598 cbssw: t.cbssw,
1599 f3s: t.f3s,
1600 cbd: t.cbd,
1601 cbdsw: t.cbdsw,
1602 f3d: t.f3d,
1603 dep_cap: t.t_dep_cap,
1604 bulk_pot: t.t_bulk_pot,
1605 mj: t.mj,
1606 mjsw: t.mjsw,
1607 };
1608 self.dist = setup_tail(
1609 typ,
1610 mode,
1611 (vbs, vbd, vds),
1612 (vgst, vdsat),
1613 t.t_phi,
1614 oxide_cap,
1615 &junctions,
1616 &dd,
1617 gbs,
1618 gbd,
1619 );
1620 Ok(())
1621 }
1622
1623 fn disto_load(&self, ctx: &mut DistoCtx) {
1625 mos_disto_load(&self.dist, (self.gate, self.bulk, self.d_p, self.s_p), ctx);
1626 }
1627}
1628
1629#[cfg(test)]
1630mod tests {
1631 use super::*;
1632
1633 #[test]
1634 fn fetlim_matches_the_c() {
1635 let vto = 1.0;
1637 let vold = 5.0;
1638 let vtsthi = (2.0f64 * (vold - vto)).abs() + 2.0;
1639 assert_eq!(fetlim(30.0, vold, vto), vold + vtsthi);
1640 assert_eq!(fetlim(0.2, 0.0, 1.0), 0.2);
1642 assert_eq!(fetlim(10.0, 0.0, 1.0), 1.5);
1644 }
1645
1646 #[test]
1647 fn limvds_matches_the_c() {
1648 assert_eq!(limvds(10.0, 1.0), 4.0);
1649 assert_eq!(limvds(-3.0, 1.0), -0.5);
1650 assert_eq!(limvds(20.0, 5.0), 17.0); assert_eq!(limvds(1.0, 5.0), 2.0);
1652 }
1653
1654 #[test]
1655 fn qmeyer_regions() {
1656 let cox = 1e-12;
1657 let phi = 0.6;
1658 let (gs, gd, gb) = qmeyer(-2.0, -2.0, 0.0, 0.0, 0.0, phi, cox);
1660 assert_eq!((gs, gd), (0.0, 0.0));
1661 assert_eq!(gb, cox / 2.0);
1662 let (gs, gd, gb) = qmeyer(2.0, -3.0, 0.0, 0.0, 2.0, phi, cox);
1664 assert_eq!(gs, cox / 3.0);
1665 assert_eq!((gd, gb), (0.0, 0.0));
1666 let (gs, gd, _) = qmeyer(2.0, 1.9, 0.0, 0.0, 2.0, phi, cox);
1668 assert!(gs > 0.0 && gd > 0.0);
1669 assert!((gs + gd) < 2.0 * cox / 3.0 + 1e-15);
1670 }
1671
1672 #[test]
1673 fn temp_defaults_match_mos1temp() {
1674 let mut m = Mos1Model::new(true);
1676 m.vto = Some(0.5);
1677 m.phi = Some(0.7);
1678 m.kp = Some(1.0e-6);
1679 m.gamma = Some(1.83);
1680 m.lambda = 0.115;
1681 m.cgso = 1e-6;
1682 m.cgdo = 1e-6;
1683 m.cbd = Some(50e-12);
1684 m.cbs = Some(50e-12);
1685 let g = MosGeom {
1686 l: 5e-6,
1687 w: 250e-6,
1688 ad: 0.0,
1689 as_: 0.0,
1690 pd: 0.0,
1691 ps: 0.0,
1692 nrd: 1.0,
1693 nrs: 1.0,
1694 };
1695 let t = Mos1Temp::new("m1", &m, &g, REFTEMP, REFTEMP).unwrap();
1696 assert!((t.t_phi - 0.7).abs() < 1e-12);
1698 assert!((t.t_vto - 0.5).abs() < 1e-12);
1699 assert!((t.t_transconductance - 1.0e-6).abs() < 1e-18);
1700 assert_eq!(t.drain_conductance, 0.0);
1701 assert!((t.cbd - 50e-12).abs() < 1e-24);
1702 assert!(!m.needs_prime(1.0));
1704 }
1705}