1use crate::consts::{BOLTZ, CHARGE, KOVERQ, REFTEMP, ROOT2};
14use crate::diode::pnjlim;
15use crate::matrix::{AcCtx, AnKind, InitMode, LoadCtx, MatrixBuilder, Slot, TRASH, Var};
16use crate::mos1::{MosGeom, fetlim, limvds, qmeyer};
17
18const MAX_EXP_ARG: f64 = 709.0;
20
21#[derive(Clone, Debug)]
24pub struct Mos6Model {
25 pub nmos: bool,
27 pub vto: Option<f64>,
29 pub kv: f64,
31 pub nv: f64,
33 pub kc: Option<f64>,
36 pub nc: f64,
38 pub nvth: f64,
40 pub ps: f64,
41 pub gamma: Option<f64>,
43 pub gamma1: f64,
45 pub sigma: f64,
49 pub phi: Option<f64>,
51 pub lambda: Option<f64>,
53 pub lambda0: Option<f64>,
55 pub lambda1: f64,
56 pub rd: Option<f64>,
58 pub rs: Option<f64>,
59 pub cbd: Option<f64>,
61 pub cbs: Option<f64>,
62 pub is: f64,
64 pub pb: f64,
66 pub cgso: f64,
68 pub cgdo: f64,
69 pub cgbo: f64,
70 pub rsh: Option<f64>,
72 pub cj: Option<f64>,
74 pub mj: f64,
76 pub cjsw: Option<f64>,
78 pub mjsw: f64,
80 pub js: f64,
82 pub tox: Option<f64>,
84 pub ld: f64,
86 pub u0: Option<f64>,
88 pub fc: f64,
90 pub nsub: Option<f64>,
92 pub tpg: Option<f64>,
94 pub nss: Option<f64>,
96 pub kf: f64,
98 pub af: f64,
99 pub tnom: Option<f64>,
101}
102
103impl Mos6Model {
104 pub fn new(nmos: bool) -> Self {
105 Mos6Model {
106 nmos,
107 vto: None,
108 kv: 2.0,
109 nv: 0.5,
110 kc: None,
111 nc: 1.0,
112 nvth: 0.5,
113 ps: 0.0,
114 gamma: None,
115 gamma1: 0.0,
116 sigma: 0.0,
117 phi: None,
118 lambda: None,
119 lambda0: None,
120 lambda1: 0.0,
121 rd: None,
122 rs: None,
123 cbd: None,
124 cbs: None,
125 is: 1e-14,
126 pb: 0.8,
127 cgso: 0.0,
128 cgdo: 0.0,
129 cgbo: 0.0,
130 rsh: None,
131 cj: None,
132 mj: 0.5,
133 cjsw: None,
134 mjsw: 0.5,
135 js: 0.0,
136 tox: None,
137 ld: 0.0,
138 u0: None,
139 fc: 0.5,
140 nsub: None,
141 tpg: None,
142 nss: None,
143 kf: 0.0,
144 af: 1.0,
145 tnom: None,
146 }
147 }
148
149 pub(crate) fn needs_prime(&self, squares: f64) -> bool {
151 self.rd.unwrap_or(0.0) != 0.0 || (self.rsh.unwrap_or(0.0) != 0.0 && squares != 0.0)
152 }
153}
154
155fn egfet(t: f64) -> f64 {
156 1.16 - (7.02e-4 * t * t) / (t + 1108.0)
157}
158
159#[derive(Debug)]
161pub(crate) struct Mos6Temp {
162 pub type_: f64,
163 pub vt: f64,
164 pub lat_diff: f64,
165 pub cgso: f64,
166 pub cgdo: f64,
167 pub cgbo: f64,
168 pub oxide_cap_factor: f64,
169 pub gamma: f64,
170 pub gamma1: f64,
171 pub sigma: f64,
172 pub kv: f64,
173 pub nv: f64,
174 pub nc: f64,
175 pub lambda0: f64,
176 pub lambda1: f64,
177 pub mj: f64,
178 pub mjsw: f64,
179 pub t_kc: f64,
181 pub t_phi: f64,
182 pub t_vbi: f64,
183 pub t_vto: f64,
184 pub t_sat_cur: f64,
185 pub t_sat_cur_dens: f64,
186 pub t_bulk_pot: f64,
187 pub t_dep_cap: f64,
188 pub drain_vcrit: f64,
189 pub source_vcrit: f64,
190 pub cbd: f64,
191 pub cbdsw: f64,
192 pub cbs: f64,
193 pub cbssw: f64,
194 pub f2d: f64,
195 pub f3d: f64,
196 pub f4d: f64,
197 pub f2s: f64,
198 pub f3s: f64,
199 pub f4s: f64,
200 pub drain_conductance: f64,
201 pub source_conductance: f64,
202 pub notes: Vec<String>,
203}
204
205impl Mos6Temp {
206 pub fn new(
207 name: &str,
208 m: &Mos6Model,
209 g: &MosGeom,
210 temp: f64,
211 default_tnom: f64,
212 ) -> Result<Self, String> {
213 let mut notes = Vec::new();
214 let typ = if m.nmos { 1.0 } else { -1.0 };
215 let tnom = m.tnom.unwrap_or(default_tnom);
216
217 let fact1 = tnom / REFTEMP;
218 let vtnom = tnom * KOVERQ;
219 let kt1 = BOLTZ * tnom;
220 let egfet1 = egfet(tnom);
221 let arg1 = -egfet1 / (kt1 + kt1) + 1.1150877 / (BOLTZ * (REFTEMP + REFTEMP));
222 let pbfact1 = -2.0 * vtnom * (1.5 * f64::ln(fact1) + CHARGE * arg1);
223
224 let mut kc = m.kc;
227 let mut phi = m.phi;
228 let mut gamma = m.gamma;
229 let mut vto = m.vto;
230 let oxide_cap_factor = match m.tox {
231 Some(tox) if tox != 0.0 => {
232 let ocf = 3.9 * 8.854214871e-12 / tox;
233 if kc.is_none() {
234 let u0 = m.u0.unwrap_or(600.0);
235 kc = Some(0.5 * u0 * ocf * 1e-4 );
236 }
237 if let Some(nsub) = m.nsub {
238 if nsub * 1e6 > 1.45e16 {
239 if phi.is_none() {
240 phi = Some(f64::max(0.1, 2.0 * vtnom * f64::ln(nsub * 1e6 / 1.45e16)));
241 }
242 let phi_v = phi.unwrap_or(0.6);
243 let fermis = typ * 0.5 * phi_v;
244 let tpg = m.tpg.unwrap_or(1.0);
245 let mut wkfng = 3.2;
246 if tpg != 0.0 {
247 let fermig = typ * tpg * 0.5 * egfet1;
248 wkfng = 3.25 + 0.5 * egfet1 - fermig;
249 }
250 let wkfngs = wkfng - (3.25 + 0.5 * egfet1 + fermis);
251 if gamma.is_none() {
252 gamma = Some(
253 f64::sqrt(2.0 * 11.70 * 8.854214871e-12 * CHARGE * nsub * 1e6)
254 / ocf,
255 );
256 }
257 if vto.is_none() {
258 let nss = m.nss.unwrap_or(0.0);
259 let vfb = wkfngs - nss * 1e4 * CHARGE / ocf;
260 vto = Some(vfb + typ * (gamma.unwrap_or(0.0) * phi_v.sqrt() + phi_v));
261 }
262 } else {
263 return Err(format!("{name}: Nsub < Ni"));
264 }
265 }
266 ocf
267 }
268 _ => 0.0,
269 };
270 let kc = kc.unwrap_or(5e-5);
271 let phi = phi.unwrap_or(0.6);
272 let gamma = gamma.unwrap_or(0.0);
273 let vto = vto.unwrap_or(0.0);
274 let lambda0 = m.lambda0.or(m.lambda).unwrap_or(0.0);
276
277 let vt = temp * KOVERQ;
279 let ratio = temp / tnom;
280 let fact2 = temp / REFTEMP;
281 let kt = temp * BOLTZ;
282 let egfet_t = egfet(temp);
283 let arg = -egfet_t / (kt + kt) + 1.1150877 / (BOLTZ * (REFTEMP + REFTEMP));
284 let pbfact = -2.0 * vt * (1.5 * f64::ln(fact2) + CHARGE * arg);
285
286 if g.l - 2.0 * m.ld <= 0.0 {
287 notes.push(format!("{name}: effective channel length less than zero"));
288 }
289 let ratio4 = ratio * ratio.sqrt();
290 let t_kc = kc / ratio4;
291 let phio = (phi - pbfact1) / fact1;
292 let t_phi = fact2 * phio + pbfact;
293 let t_vbi =
294 vto - typ * (gamma * phi.sqrt()) + 0.5 * (egfet1 - egfet_t) + typ * 0.5 * (t_phi - phi);
295 let t_vto = t_vbi + typ * gamma * t_phi.sqrt();
296 let t_sat_cur = m.is * f64::exp(-egfet_t / vt + egfet1 / vtnom);
297 let t_sat_cur_dens = m.js * f64::exp(-egfet_t / vt + egfet1 / vtnom);
298
299 let pbo = (m.pb - pbfact1) / fact1;
300 let gmaold = (m.pb - pbo) / pbo;
301 let mut capfact = 1.0 / (1.0 + m.mj * (4e-4 * (tnom - REFTEMP) - gmaold));
302 let mut t_cbd = m.cbd.unwrap_or(0.0) * capfact;
303 let mut t_cbs = m.cbs.unwrap_or(0.0) * capfact;
304 let mut t_cj = m.cj.unwrap_or(0.0) * capfact;
305 capfact = 1.0 / (1.0 + m.mjsw * (4e-4 * (tnom - REFTEMP) - gmaold));
306 let mut t_cjsw = m.cjsw.unwrap_or(0.0) * capfact;
307 let t_bulk_pot = fact2 * pbo + pbfact;
308 let gmanew = (t_bulk_pot - pbo) / pbo;
309 capfact = 1.0 + m.mj * (4e-4 * (temp - REFTEMP) - gmanew);
310 t_cbd *= capfact;
311 t_cbs *= capfact;
312 t_cj *= capfact;
313 capfact = 1.0 + m.mjsw * (4e-4 * (temp - REFTEMP) - gmanew);
314 t_cjsw *= capfact;
315 let t_dep_cap = m.fc * t_bulk_pot;
316
317 let (drain_vcrit, source_vcrit) = if t_sat_cur_dens == 0.0 || g.ad == 0.0 || g.as_ == 0.0 {
318 let v = vt * f64::ln(vt / (ROOT2 * t_sat_cur));
319 (v, v)
320 } else {
321 (
322 vt * f64::ln(vt / (ROOT2 * t_sat_cur_dens * g.ad)),
323 vt * f64::ln(vt / (ROOT2 * t_sat_cur_dens * g.as_)),
324 )
325 };
326
327 let czbd = if m.cbd.is_some() {
328 t_cbd
329 } else if m.cj.is_some() {
330 t_cj * g.ad
331 } else {
332 0.0
333 };
334 let czbdsw = if m.cjsw.is_some() { t_cjsw * g.pd } else { 0.0 };
335 let arg = 1.0 - m.fc;
336 let sarg = f64::exp(-m.mj * f64::ln(arg));
337 let sargsw = f64::exp(-m.mjsw * f64::ln(arg));
338 let f2d = czbd * (1.0 - m.fc * (1.0 + m.mj)) * sarg / arg
339 + czbdsw * (1.0 - m.fc * (1.0 + m.mjsw)) * sargsw / arg;
340 let f3d =
341 czbd * m.mj * sarg / arg / t_bulk_pot + czbdsw * m.mjsw * sargsw / arg / t_bulk_pot;
342 let f4d = czbd * t_bulk_pot * (1.0 - arg * sarg) / (1.0 - m.mj)
343 + czbdsw * t_bulk_pot * (1.0 - arg * sargsw) / (1.0 - m.mjsw)
344 - f3d / 2.0 * (t_dep_cap * t_dep_cap)
345 - t_dep_cap * f2d;
346 let czbs = if m.cbs.is_some() {
347 t_cbs
348 } else if m.cj.is_some() {
349 t_cj * g.as_
350 } else {
351 0.0
352 };
353 let czbssw = if m.cjsw.is_some() { t_cjsw * g.ps } else { 0.0 };
354 let f2s = czbs * (1.0 - m.fc * (1.0 + m.mj)) * sarg / arg
355 + czbssw * (1.0 - m.fc * (1.0 + m.mjsw)) * sargsw / arg;
356 let f3s =
357 czbs * m.mj * sarg / arg / t_bulk_pot + czbssw * m.mjsw * sargsw / arg / t_bulk_pot;
358 let f4s = czbs * t_bulk_pot * (1.0 - arg * sarg) / (1.0 - m.mj)
359 + czbssw * t_bulk_pot * (1.0 - arg * sargsw) / (1.0 - m.mjsw)
360 - f3s / 2.0 * (t_dep_cap * t_dep_cap)
361 - t_dep_cap * f2s;
362
363 let series = |r: Option<f64>, squares: f64| match (r, m.rsh) {
364 (Some(r), _) => {
365 if r != 0.0 {
366 1.0 / r
367 } else {
368 0.0
369 }
370 }
371 (None, Some(rsh)) => {
372 if rsh != 0.0 {
373 1.0 / (rsh * squares)
374 } else {
375 0.0
376 }
377 }
378 (None, None) => 0.0,
379 };
380
381 Ok(Mos6Temp {
382 type_: typ,
383 vt,
384 lat_diff: m.ld,
385 cgso: m.cgso,
386 cgdo: m.cgdo,
387 cgbo: m.cgbo,
388 oxide_cap_factor,
389 gamma,
390 gamma1: m.gamma1,
391 sigma: m.sigma,
392 kv: m.kv,
393 nv: m.nv,
394 nc: m.nc,
395 lambda0,
396 lambda1: m.lambda1,
397 mj: m.mj,
398 mjsw: m.mjsw,
399 t_kc,
400 t_phi,
401 t_vbi,
402 t_vto,
403 t_sat_cur,
404 t_sat_cur_dens,
405 t_bulk_pot,
406 t_dep_cap,
407 drain_vcrit,
408 source_vcrit,
409 cbd: czbd,
410 cbdsw: czbdsw,
411 cbs: czbs,
412 cbssw: czbssw,
413 f2d,
414 f3d,
415 f4d,
416 f2s,
417 f3s,
418 f4s,
419 drain_conductance: series(m.rd, g.nrd),
420 source_conductance: series(m.rs, g.nrs),
421 notes,
422 })
423 }
424}
425
426const VBD: usize = 0;
428const VBS: usize = 1;
429const VGS: usize = 2;
430const VDS: usize = 3;
431const CAPGS: usize = 4;
432const QGS: usize = 5;
433const CQGS: usize = 6;
434const CAPGD: usize = 7;
435const QGD: usize = 8;
436const CQGD: usize = 9;
437const CAPGB: usize = 10;
438const QGB: usize = 11;
439const CQGB: usize = 12;
440const QBD: usize = 13;
441const CQBD: usize = 14;
442const QBS: usize = 15;
443const CQBS: usize = 16;
444pub(crate) const MOS6_NUM_STATES: usize = 17;
445
446const D_D: usize = 0;
448const G_G: usize = 1;
449const S_S: usize = 2;
450const B_B: usize = 3;
451const DP_DP: usize = 4;
452const SP_SP: usize = 5;
453const D_DP: usize = 6;
454const G_B: usize = 7;
455const G_DP: usize = 8;
456const G_SP: usize = 9;
457const S_SP: usize = 10;
458const B_DP: usize = 11;
459const B_SP: usize = 12;
460const DP_SP: usize = 13;
461const DP_D: usize = 14;
462const B_G: usize = 15;
463const DP_G: usize = 16;
464const SP_G: usize = 17;
465const SP_S: usize = 18;
466const DP_B: usize = 19;
467const SP_B: usize = 20;
468const SP_DP: usize = 21;
469
470pub(crate) struct Mos6I {
471 drain: Var,
472 gate: Var,
473 source: Var,
474 bulk: Var,
475 d_p: Var,
476 s_p: Var,
477 t: Mos6Temp,
478 geom: MosGeom,
479 off: bool,
480 ic_vds: f64,
481 ic_vgs: f64,
482 ic_vbs: f64,
483 ic_given: crate::devices::IcGiven,
484 st: usize,
485 s: [Slot; 22],
486 mode: f64,
488 von: f64,
489 vdsat: f64,
490 gm: f64,
491 gds: f64,
492 gmbs: f64,
493 gbd: f64,
494 gbs: f64,
495 capbd: f64,
496 capbs: f64,
497 cd: f64,
498 cbs: f64,
499 cbd: f64,
500}
501
502impl Mos6I {
503 #[allow(clippy::too_many_arguments)]
504 pub fn new(
505 nodes: [Var; 4], primes: [Var; 2], t: Mos6Temp,
508 geom: MosGeom,
509 off: bool,
510 ic: (f64, f64, f64), st: usize,
512 ) -> Self {
513 Mos6I {
514 drain: nodes[0],
515 gate: nodes[1],
516 source: nodes[2],
517 bulk: nodes[3],
518 d_p: primes[0],
519 s_p: primes[1],
520 t,
521 geom,
522 off,
523 ic_vds: ic.0,
524 ic_vgs: ic.1,
525 ic_vbs: ic.2,
526 ic_given: crate::devices::IcGiven::ALL,
527 st,
528 s: [TRASH; 22],
529 mode: 1.0,
530 von: 0.0,
531 vdsat: 0.0,
532 gm: 0.0,
533 gds: 0.0,
534 gmbs: 0.0,
535 gbd: 0.0,
536 gbs: 0.0,
537 capbd: 0.0,
538 capbs: 0.0,
539 cd: 0.0,
540 cbs: 0.0,
541 cbd: 0.0,
542 }
543 }
544}
545
546impl crate::devices::Device for Mos6I {
547 fn par_load(&self) -> bool {
548 true
549 }
550
551 fn set_ic(&mut self, rhs: &[f64]) {
554 use crate::devices::getic;
555 let g = self.ic_given;
556 getic(g, 2, &mut self.ic_vbs, rhs, self.bulk, self.source);
557 getic(g, 0, &mut self.ic_vds, rhs, self.drain, self.source);
558 getic(g, 1, &mut self.ic_vgs, rhs, self.gate, self.source);
559 }
560
561 fn set_ic_given(&mut self, given: crate::devices::IcGiven) {
562 self.ic_given = given;
563 }
564
565 fn conv_test(&self, ctx: &crate::matrix::ConvCtx) -> bool {
567 let typ = self.t.type_;
568 let st = self.st;
569 let vbs = typ * (ctx.rhs[self.bulk] - ctx.rhs[self.s_p]);
570 let vgs = typ * (ctx.rhs[self.gate] - ctx.rhs[self.s_p]);
571 let vds = typ * (ctx.rhs[self.d_p] - ctx.rhs[self.s_p]);
572 let vbd = vbs - vds;
573 let vgd = vgs - vds;
574 let vgdo = ctx.states.get(0, st + VGS) - ctx.states.get(0, st + VDS);
575 let delvbs = vbs - ctx.states.get(0, st + VBS);
576 let delvbd = vbd - ctx.states.get(0, st + VBD);
577 let delvgs = vgs - ctx.states.get(0, st + VGS);
578 let delvds = vds - ctx.states.get(0, st + VDS);
579 let delvgd = vgd - vgdo;
580 let cdhat = if self.mode >= 0.0 {
581 self.cd - self.gbd * delvbd + self.gmbs * delvbs + self.gm * delvgs + self.gds * delvds
582 } else {
583 self.cd - (self.gbd - self.gmbs) * delvbd - self.gm * delvgd + self.gds * delvds
584 };
585 let cbhat = self.cbs + self.cbd + self.gbd * delvbd + self.gbs * delvbs;
586 let tol = ctx.reltol * f64::max(cdhat.abs(), self.cd.abs()) + ctx.abstol;
587 if (cdhat - self.cd).abs() >= tol {
588 return false;
589 }
590 let cb = self.cbs + self.cbd;
591 let tol = ctx.reltol * f64::max(cbhat.abs(), cb.abs()) + ctx.abstol;
592 (cbhat - cb).abs() <= tol
593 }
594
595 fn alloc(&mut self, mb: &mut MatrixBuilder) {
596 let (d, g, s, b) = (self.drain, self.gate, self.source, self.bulk);
597 let (dp, sp) = (self.d_p, self.s_p);
598 self.s = [
599 mb.slot(d, d),
600 mb.slot(g, g),
601 mb.slot(s, s),
602 mb.slot(b, b),
603 mb.slot(dp, dp),
604 mb.slot(sp, sp),
605 mb.slot(d, dp),
606 mb.slot(g, b),
607 mb.slot(g, dp),
608 mb.slot(g, sp),
609 mb.slot(s, sp),
610 mb.slot(b, dp),
611 mb.slot(b, sp),
612 mb.slot(dp, sp),
613 mb.slot(dp, d),
614 mb.slot(b, g),
615 mb.slot(dp, g),
616 mb.slot(sp, g),
617 mb.slot(sp, s),
618 mb.slot(dp, b),
619 mb.slot(sp, b),
620 mb.slot(sp, dp),
621 ];
622 }
623
624 #[allow(clippy::too_many_lines)]
625 fn load(&mut self, ctx: &mut LoadCtx) {
626 let t = &self.t;
627 let typ = t.type_;
628 let vt = t.vt;
629 let st = self.st;
630
631 let eff_l = self.geom.l - 2.0 * t.lat_diff;
632 let (drain_sat_cur, source_sat_cur) =
633 if t.t_sat_cur_dens == 0.0 || self.geom.ad == 0.0 || self.geom.as_ == 0.0 {
634 (t.t_sat_cur, t.t_sat_cur)
635 } else {
636 (
637 t.t_sat_cur_dens * self.geom.ad,
638 t.t_sat_cur_dens * self.geom.as_,
639 )
640 };
641 let gate_source_overlap_cap = t.cgso * self.geom.w;
642 let gate_drain_overlap_cap = t.cgdo * self.geom.w;
643 let gate_bulk_overlap_cap = t.cgbo * eff_l;
644 let betac = t.t_kc * self.geom.w / eff_l;
645 let oxide_cap = t.oxide_cap_factor * eff_l * self.geom.w;
646
647 let mut check = true;
649 let (mut vbs, mut vgs, mut vds);
650 let predictor = matches!(ctx.mode, InitMode::Pred | InitMode::Tran);
651 let general = matches!(ctx.mode, InitMode::Float | InitMode::Smsig)
652 || (ctx.mode == InitMode::Fix && !self.off);
653 if predictor || general {
654 if predictor {
655 let xfact = ctx.pred_xfact;
656 let mut pred = |o: usize| {
657 let s1 = ctx.states.get(1, st + o);
658 ctx.states.set(0, st + o, s1);
659 (1.0 + xfact) * s1 - xfact * ctx.states.get(2, st + o)
660 };
661 vbs = pred(VBS);
662 vgs = pred(VGS);
663 vds = pred(VDS);
664 let vbd0 = ctx.states.get(0, st + VBS) - ctx.states.get(0, st + VDS);
665 ctx.states.set(0, st + VBD, vbd0);
666 } else {
667 vbs = typ * (ctx.sol[self.bulk] - ctx.sol[self.s_p]);
668 vgs = typ * (ctx.sol[self.gate] - ctx.sol[self.s_p]);
669 vds = typ * (ctx.sol[self.d_p] - ctx.sol[self.s_p]);
670 }
671 let mut vbd = vbs - vds;
672 let mut vgd = vgs - vds;
673 let vgdo = ctx.states.get(0, st + VGS) - ctx.states.get(0, st + VDS);
674
675 let von = typ * self.von;
676 if ctx.states.get(0, st + VDS) >= 0.0 {
677 vgs = fetlim(vgs, ctx.states.get(0, st + VGS), von);
678 vds = vgs - vgd;
679 vds = limvds(vds, ctx.states.get(0, st + VDS));
680 } else {
681 vgd = fetlim(vgd, vgdo, von);
682 vds = vgs - vgd;
683 vds = -limvds(-vds, -ctx.states.get(0, st + VDS));
684 vgs = vgd + vds;
685 }
686 if vds >= 0.0 {
687 vbs = pnjlim(
688 vbs,
689 ctx.states.get(0, st + VBS),
690 vt,
691 t.source_vcrit,
692 &mut check,
693 );
694 } else {
695 vbd = pnjlim(
696 vbd,
697 ctx.states.get(0, st + VBD),
698 vt,
699 t.drain_vcrit,
700 &mut check,
701 );
702 vbs = vbd + vds;
703 }
704 } else if ctx.mode == InitMode::Jct && !self.off {
705 vds = typ * self.ic_vds;
706 vgs = typ * self.ic_vgs;
707 vbs = typ * self.ic_vbs;
708 if vds == 0.0 && vgs == 0.0 && vbs == 0.0 && !(ctx.kind == AnKind::TranOp && ctx.uic) {
709 vbs = -1.0;
710 vgs = typ * t.t_vto;
711 vds = 0.0;
712 }
713 } else {
714 vbs = 0.0;
715 vgs = 0.0;
716 vds = 0.0;
717 }
718
719 let vbd = vbs - vds;
720 let vgd = vgs - vds;
721 let vgb = vgs - vbs;
722
723 let (mut gbs, mut cbs);
725 if vbs <= -3.0 * vt {
726 gbs = ctx.gmin;
727 cbs = gbs * vbs - source_sat_cur;
728 } else {
729 let evbs = f64::exp(f64::min(MAX_EXP_ARG, vbs / vt));
730 gbs = source_sat_cur * evbs / vt + ctx.gmin;
731 cbs = source_sat_cur * (evbs - 1.0) + ctx.gmin * vbs;
732 }
733 let (mut gbd, mut cbd);
734 if vbd <= -3.0 * vt {
735 gbd = ctx.gmin;
736 cbd = gbd * vbd - drain_sat_cur;
737 } else {
738 let evbd = f64::exp(f64::min(MAX_EXP_ARG, vbd / vt));
739 gbd = drain_sat_cur * evbd / vt + ctx.gmin;
740 cbd = drain_sat_cur * (evbd - 1.0) + ctx.gmin * vbd;
741 }
742
743 self.mode = if vds >= 0.0 { 1.0 } else { -1.0 };
744 let mode = self.mode;
745
746 let cdrain;
750 let (gm, gds, gmbs);
751 let (von, vdsat);
752 {
753 let vbsvbd = if mode == 1.0 { vbs } else { vbd };
754 let sarg = if vbsvbd <= 0.0 {
755 (t.t_phi - vbsvbd).sqrt()
756 } else {
757 let s = t.t_phi.sqrt();
758 f64::max(0.0, s - vbsvbd / (s + s))
759 };
760 let vdshere = vds * mode;
761 von = t.t_vbi * typ + t.gamma * sarg - t.gamma1 * vbsvbd - t.sigma * vdshere;
762 let vgon = (if mode == 1.0 { vgs } else { vgd }) - von;
763 if vgon <= 0.0 {
764 vdsat = 0.0;
766 cdrain = 0.0;
767 gm = 0.0;
768 gds = 0.0;
769 gmbs = 0.0;
770 } else {
771 let mut vonbm = 0.0;
774 if sarg > 0.0 {
775 vonbm = if vbsvbd <= 0.0 {
776 t.gamma1 + t.gamma / (sarg + sarg)
777 } else {
778 t.gamma1 + t.gamma / 2.0 / t.t_phi.sqrt()
779 };
780 }
781 let lnvgon = vgon.ln();
782 vdsat = t.kv * f64::exp(lnvgon * t.nv);
783 let idsat = betac * f64::exp(lnvgon * t.nc);
784 let lambda = t.lambda0 - t.lambda1 * vbsvbd;
785 let mut cdr = idsat * (1.0 + lambda * vdshere);
787 let mut gm_l = cdr * t.nc / vgon;
788 let mut gds_l = gm_l * t.sigma + idsat * lambda;
789 let mut gmbs_l = gm_l * vonbm - idsat * t.lambda1 * vdshere;
790 if vdsat > vdshere {
791 let vdst = vdshere / vdsat;
793 let vdst2 = (2.0 - vdst) * vdst;
794 let vdstg = -vdst * t.nv / vgon;
795 let ivdst1 = cdr * (2.0 - vdst - vdst);
796 cdr *= vdst2;
797 gm_l = gm_l * vdst2 + ivdst1 * vdstg;
798 gds_l = gds_l * vdst2 + ivdst1 * (1.0 / vdsat + vdstg * t.sigma);
799 gmbs_l = gmbs_l * vdst2 + ivdst1 * vdstg * vonbm;
800 }
801 cdrain = cdr;
802 gm = gm_l;
803 gds = gds_l;
804 gmbs = gmbs_l;
805 }
806 }
807 self.von = typ * von;
808 self.vdsat = typ * vdsat;
809 self.gm = gm;
810 self.gds = gds;
811 self.gmbs = gmbs;
812
813 let mut cd = mode * cdrain - cbd;
814
815 let smsig = ctx.mode == InitMode::Smsig;
816 if ctx.kind == AnKind::Tran || ctx.kind == AnKind::TranOp || smsig {
817 let dep = |v: f64, cz: f64, czsw: f64, f2: f64, f3: f64, f4: f64| {
819 if cz != 0.0 || czsw != 0.0 {
820 if v < t.t_dep_cap {
821 let arg = 1.0 - v / t.t_bulk_pot;
822 let (sarg, sargsw);
823 if t.mj == t.mjsw {
824 if t.mj == 0.5 {
825 sarg = 1.0 / arg.sqrt();
826 sargsw = sarg;
827 } else {
828 sarg = f64::exp(-t.mj * f64::ln(arg));
829 sargsw = sarg;
830 }
831 } else {
832 sarg = if t.mj == 0.5 {
833 1.0 / arg.sqrt()
834 } else {
835 f64::exp(-t.mj * f64::ln(arg))
836 };
837 sargsw = if t.mjsw == 0.5 {
838 1.0 / arg.sqrt()
839 } else {
840 f64::exp(-t.mjsw * f64::ln(arg))
841 };
842 }
843 let q = t.t_bulk_pot
844 * (cz * (1.0 - arg * sarg) / (1.0 - t.mj)
845 + czsw * (1.0 - arg * sargsw) / (1.0 - t.mjsw));
846 (q, cz * sarg + czsw * sargsw)
847 } else {
848 (f4 + v * (f2 + v * f3 / 2.0), f2 + v * f3)
849 }
850 } else {
851 (0.0, 0.0)
852 }
853 };
854 let (qbs, capbs) = dep(vbs, t.cbs, t.cbssw, t.f2s, t.f3s, t.f4s);
855 ctx.states.set(0, st + QBS, qbs);
856 self.capbs = capbs;
857 let (qbd, capbd) = dep(vbd, t.cbd, t.cbdsw, t.f2d, t.f3d, t.f4d);
858 ctx.states.set(0, st + QBD, qbd);
859 self.capbd = capbd;
860
861 if ctx.kind == AnKind::Tran {
862 let (geq, _) = ctx.integrate(self.capbd, st + QBD);
863 gbd += geq;
864 cbd += ctx.states.get(0, st + CQBD);
865 cd -= ctx.states.get(0, st + CQBD);
866 let (geq, _) = ctx.integrate(self.capbs, st + QBS);
867 gbs += geq;
868 cbs += ctx.states.get(0, st + CQBS);
869 }
870 }
871 self.gbs = gbs;
872 self.gbd = gbd;
873
874 if (!self.off || !matches!(ctx.mode, InitMode::Fix | InitMode::Smsig)) && check {
875 ctx.noncon = true;
876 }
877
878 ctx.states.set(0, st + VBS, vbs);
879 ctx.states.set(0, st + VBD, vbd);
880 ctx.states.set(0, st + VGS, vgs);
881 ctx.states.set(0, st + VDS, vds);
882
883 let (mut capgs, mut capgd, mut capgb) = (0.0, 0.0, 0.0);
885 if ctx.kind == AnKind::Tran || ctx.kind == AnKind::TranOp || smsig {
886 let (half_gs, half_gd, half_gb) = if mode > 0.0 {
887 qmeyer(vgs, vgd, vgb, von, vdsat, t.t_phi, oxide_cap)
888 } else {
889 let (o1, o2, o3) = qmeyer(vgd, vgs, vgb, von, vdsat, t.t_phi, oxide_cap);
890 (o2, o1, o3)
891 };
892 ctx.states.set(0, st + CAPGS, half_gs);
893 ctx.states.set(0, st + CAPGD, half_gd);
894 ctx.states.set(0, st + CAPGB, half_gb);
895 let vgs1 = ctx.states.get(1, st + VGS);
896 let vgd1 = vgs1 - ctx.states.get(1, st + VDS);
897 let vgb1 = vgs1 - ctx.states.get(1, st + VBS);
898 if ctx.kind == AnKind::TranOp || smsig {
899 capgs = 2.0 * ctx.states.get(0, st + CAPGS) + gate_source_overlap_cap;
900 capgd = 2.0 * ctx.states.get(0, st + CAPGD) + gate_drain_overlap_cap;
901 capgb = 2.0 * ctx.states.get(0, st + CAPGB) + gate_bulk_overlap_cap;
902 } else {
903 capgs = ctx.states.get(0, st + CAPGS)
904 + ctx.states.get(1, st + CAPGS)
905 + gate_source_overlap_cap;
906 capgd = ctx.states.get(0, st + CAPGD)
907 + ctx.states.get(1, st + CAPGD)
908 + gate_drain_overlap_cap;
909 capgb = ctx.states.get(0, st + CAPGB)
910 + ctx.states.get(1, st + CAPGB)
911 + gate_bulk_overlap_cap;
912 }
913
914 if smsig {
915 return;
916 }
917
918 if predictor {
919 let xfact = ctx.pred_xfact;
920 for o in [QGS, QGD, QGB] {
921 let v = (1.0 + xfact) * ctx.states.get(1, st + o)
922 - xfact * ctx.states.get(2, st + o);
923 ctx.states.set(0, st + o, v);
924 }
925 } else if ctx.kind == AnKind::Tran {
926 let q = (vgs - vgs1) * capgs + ctx.states.get(1, st + QGS);
927 ctx.states.set(0, st + QGS, q);
928 let q = (vgd - vgd1) * capgd + ctx.states.get(1, st + QGD);
929 ctx.states.set(0, st + QGD, q);
930 let q = (vgb - vgb1) * capgb + ctx.states.get(1, st + QGB);
931 ctx.states.set(0, st + QGB, q);
932 } else {
933 ctx.states.set(0, st + QGS, capgs * vgs);
934 ctx.states.set(0, st + QGD, capgd * vgd);
935 ctx.states.set(0, st + QGB, capgb * vgb);
936 }
937 }
938
939 let (mut gcgs, mut ceqgs, mut gcgd, mut ceqgd, mut gcgb, mut ceqgb) =
941 (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
942 if ctx.mode != InitMode::Tran && ctx.kind == AnKind::Tran {
943 if capgs == 0.0 {
944 ctx.states.set(0, st + CQGS, 0.0);
945 }
946 if capgd == 0.0 {
947 ctx.states.set(0, st + CQGD, 0.0);
948 }
949 if capgb == 0.0 {
950 ctx.states.set(0, st + CQGB, 0.0);
951 }
952 let (g, ceq) = ctx.integrate(capgs, st + QGS);
953 gcgs = g;
954 ceqgs = ceq - gcgs * vgs + ctx.ag[0] * ctx.states.get(0, st + QGS);
955 let (g, ceq) = ctx.integrate(capgd, st + QGD);
956 gcgd = g;
957 ceqgd = ceq - gcgd * vgd + ctx.ag[0] * ctx.states.get(0, st + QGD);
958 let (g, ceq) = ctx.integrate(capgb, st + QGB);
959 gcgb = g;
960 ceqgb = ceq - gcgb * vgb + ctx.ag[0] * ctx.states.get(0, st + QGB);
961 }
962
963 let ceqbs = typ * (cbs - gbs * vbs);
965 let ceqbd = typ * (cbd - gbd * vbd);
966 let (xnrm, xrev, cdreq);
967 if mode >= 0.0 {
968 xnrm = 1.0;
969 xrev = 0.0;
970 cdreq = typ * (cdrain - gds * vds - gm * vgs - gmbs * vbs);
971 } else {
972 xnrm = 0.0;
973 xrev = 1.0;
974 cdreq = -typ * (cdrain - gds * (-vds) - gm * vgd - gmbs * vbd);
975 }
976 self.cd = cd;
978 self.cbs = cbs;
979 self.cbd = cbd;
980 ctx.add_rhs(self.gate, -(typ * (ceqgs + ceqgb + ceqgd)));
981 ctx.add_rhs(self.bulk, -(ceqbs + ceqbd - typ * ceqgb));
982 ctx.add_rhs(self.d_p, ceqbd - cdreq + typ * ceqgd);
983 ctx.add_rhs(self.s_p, cdreq + ceqbs + typ * ceqgs);
984
985 let gdpr = t.drain_conductance;
987 let gspr = t.source_conductance;
988 ctx.add(self.s[D_D], gdpr);
989 ctx.add(self.s[G_G], gcgd + gcgs + gcgb);
990 ctx.add(self.s[S_S], gspr);
991 ctx.add(self.s[B_B], gbd + gbs + gcgb);
992 ctx.add(self.s[DP_DP], gdpr + gds + gbd + xrev * (gm + gmbs) + gcgd);
993 ctx.add(self.s[SP_SP], gspr + gds + gbs + xnrm * (gm + gmbs) + gcgs);
994 ctx.add(self.s[D_DP], -gdpr);
995 ctx.add(self.s[G_B], -gcgb);
996 ctx.add(self.s[G_DP], -gcgd);
997 ctx.add(self.s[G_SP], -gcgs);
998 ctx.add(self.s[S_SP], -gspr);
999 ctx.add(self.s[B_G], -gcgb);
1000 ctx.add(self.s[B_DP], -gbd);
1001 ctx.add(self.s[B_SP], -gbs);
1002 ctx.add(self.s[DP_D], -gdpr);
1003 ctx.add(self.s[DP_G], (xnrm - xrev) * gm - gcgd);
1004 ctx.add(self.s[DP_B], -gbd + (xnrm - xrev) * gmbs);
1005 ctx.add(self.s[DP_SP], -gds - xnrm * (gm + gmbs));
1006 ctx.add(self.s[SP_G], -(xnrm - xrev) * gm - gcgs);
1007 ctx.add(self.s[SP_S], -gspr);
1008 ctx.add(self.s[SP_B], -gbs - (xnrm - xrev) * gmbs);
1009 ctx.add(self.s[SP_DP], -gds - xrev * (gm + gmbs));
1010 }
1011
1012 fn trunc(&self, t: &crate::integ::TerrCtx, time_step: &mut f64) {
1013 crate::integ::terr(self.st + QGS, t, time_step);
1014 crate::integ::terr(self.st + QGD, t, time_step);
1015 crate::integ::terr(self.st + QGB, t, time_step);
1016 }
1017
1018 fn load_ac(&mut self, _ctx: &mut AcCtx) {}
1021
1022 fn load_pz(&mut self, _ctx: &mut crate::matrix::PzCtx) -> Result<(), &'static str> {
1027 Err("the level-6 MOSFET has no pole-zero load (no mos6pzld.c in the 3f5 or ngspice)")
1028 }
1029}
1030
1031#[cfg(test)]
1032mod tests {
1033 use super::*;
1034
1035 fn n10l5() -> Mos6Model {
1037 let mut m = Mos6Model::new(true);
1038 m.kc = Some(3.8921e-5);
1039 m.nc = 1.1739;
1040 m.kv = 0.91602;
1041 m.nv = 0.87225;
1042 m.lambda0 = Some(0.013333);
1043 m.lambda1 = 0.0046901;
1044 m.vto = Some(0.69486);
1045 m.gamma = Some(0.60309);
1046 m.phi = Some(1.0);
1047 m.tox = Some(1.98e-8);
1048 m.ld = 0.1e-6;
1049 m.nsub = Some(4.9999999e16);
1050 m.nss = Some(0.0);
1051 m.cj = Some(4.091e-4);
1052 m.mj = 0.307;
1053 m.pb = 1.0;
1054 m.cjsw = Some(3.078e-10);
1055 m.mjsw = 1.0e-2;
1056 m.cgso = 3.93e-10;
1057 m.cgdo = 3.93e-10;
1058 m.tpg = Some(1.0);
1059 m
1060 }
1061
1062 #[test]
1063 fn temp_uses_given_parameters_at_tnom() {
1064 let g = MosGeom {
1065 l: 1e-6,
1066 w: 5e-6,
1067 ad: 0.0,
1068 as_: 0.0,
1069 pd: 0.0,
1070 ps: 0.0,
1071 nrd: 1.0,
1072 nrs: 1.0,
1073 };
1074 let t = Mos6Temp::new("mn11", &n10l5(), &g, REFTEMP, REFTEMP).unwrap();
1075 assert!((t.t_kc - 3.8921e-5).abs() < 1e-12);
1076 assert!((t.t_phi - 1.0).abs() < 1e-12);
1077 assert!((t.t_vto - 0.69486).abs() < 1e-9);
1078 assert!((t.lambda0 - 0.013333).abs() < 1e-12);
1079 assert_eq!(t.drain_conductance, 0.0);
1080 }
1081
1082 #[test]
1083 fn lambda_alias_seeds_lambda0() {
1084 let mut m = Mos6Model::new(true);
1085 m.lambda = Some(0.02);
1086 let g = MosGeom {
1087 l: 1e-6,
1088 w: 1e-6,
1089 ad: 0.0,
1090 as_: 0.0,
1091 pd: 0.0,
1092 ps: 0.0,
1093 nrd: 1.0,
1094 nrs: 1.0,
1095 };
1096 let t = Mos6Temp::new("m1", &m, &g, REFTEMP, REFTEMP).unwrap();
1097 assert!((t.lambda0 - 0.02).abs() < 1e-15);
1098 m.lambda0 = Some(0.05);
1100 let t = Mos6Temp::new("m1", &m, &g, REFTEMP, REFTEMP).unwrap();
1101 assert!((t.lambda0 - 0.05).abs() < 1e-15);
1102 }
1103}