1use std::collections::HashMap;
7
8use dune_netlist::ir::{self, Netlist, ParamValue};
9
10use crate::circuit::{
11 Bjt, Bsim1, Bsim2, Bsim3, Capacitor, Cccs, Ccvs, Circuit, Diode, Inductor, Isource, Jfet, Ltra,
12 Mes, Mos1, Mos2, Mos3, Mos6, Mutual, Resistor, SwitchI, SwitchV, Tra, Vccs, Vcvs, Vsource,
13};
14use crate::consts::CTOK;
15use crate::devices::IcGiven;
16use crate::diode::DiodeModel;
17use crate::error::Error;
18
19impl Circuit {
20 pub fn from_netlist(nl: &Netlist) -> Result<(Circuit, Vec<String>), Error> {
24 let mut problems: Vec<String> = Vec::new();
25 let mut notes: Vec<String> = Vec::new();
26
27 let mut dmodels: HashMap<&str, DiodeModel> = HashMap::new();
29 let mut qmodels: HashMap<&str, crate::bjt::BjtModel> = HashMap::new();
30 enum MosModel {
31 M1(crate::mos1::Mos1Model),
32 M2(crate::mos2::Mos2Model),
33 M3(crate::mos3::Mos3Model),
34 M6(crate::mos6::Mos6Model),
35 B1(Box<crate::bsim1::Bsim1Model>),
36 B2(Box<crate::bsim2::Bsim2Model>),
37 B3(Box<crate::bsim3::Bsim3Model>),
38 }
39 let mut mmodels: HashMap<&str, MosModel> = HashMap::new();
40 let mut unsupported_models: HashMap<&str, String> = HashMap::new();
44 let mut omodels: HashMap<&str, crate::ltra::LtraModel> = HashMap::new();
45 let mut umodels: HashMap<&str, UrcModel> = HashMap::new();
46 let mut jmodels: HashMap<&str, crate::jfet::JfetModel> = HashMap::new();
47 let mut zmodels: HashMap<&str, crate::mes::MesModel> = HashMap::new();
48 let mut smodels: HashMap<&str, crate::switch::SwModel> = HashMap::new();
49 let mut wmodels: HashMap<&str, crate::switch::CswModel> = HashMap::new();
50 let mut rmodels: HashMap<&str, ResModel> = HashMap::new();
51 let mut cmodels: HashMap<&str, CapModel> = HashMap::new();
52 for m in &nl.models {
53 match m.mtype.as_str() {
54 "r" => match res_model(m, &mut notes) {
55 Ok(rm) => {
56 rmodels.insert(m.name.as_str(), rm);
57 }
58 Err(e) => problems.push(format!("model `{}`: {e}", m.name)),
59 },
60 "c" => match cap_model(m, &mut notes) {
61 Ok(cm) => {
62 cmodels.insert(m.name.as_str(), cm);
63 }
64 Err(e) => problems.push(format!("model `{}`: {e}", m.name)),
65 },
66 "sw" => match sw_model(m, &mut notes) {
67 Ok(sm) => {
68 smodels.insert(m.name.as_str(), sm);
69 }
70 Err(e) => problems.push(format!("model `{}`: {e}", m.name)),
71 },
72 "csw" => match csw_model(m, &mut notes) {
73 Ok(wm) => {
74 wmodels.insert(m.name.as_str(), wm);
75 }
76 Err(e) => problems.push(format!("model `{}`: {e}", m.name)),
77 },
78 "njf" | "pjf" => {
84 let level = num_param(&m.params, "level").unwrap_or(1.0);
85 if level != 1.0 {
86 unsupported_models.insert(
87 m.name.as_str(),
88 format!("model `{}`: JFET level {level} is not implemented", m.name),
89 );
90 } else {
91 match jfet_model(m, &mut notes) {
92 Ok(jm) => {
93 jmodels.insert(m.name.as_str(), jm);
94 }
95 Err(e) => problems.push(format!("model `{}`: {e}", m.name)),
96 }
97 }
98 }
99 "nmf" | "pmf" => {
100 let level = num_param(&m.params, "level").unwrap_or(1.0);
101 if level != 1.0 {
102 unsupported_models.insert(
103 m.name.as_str(),
104 format!(
105 "model `{}`: MESFET level {level} is not implemented",
106 m.name
107 ),
108 );
109 } else {
110 match mes_model(m, &mut notes) {
111 Ok(zm) => {
112 zmodels.insert(m.name.as_str(), zm);
113 }
114 Err(e) => problems.push(format!("model `{}`: {e}", m.name)),
115 }
116 }
117 }
118 "urc" => match urc_model(m, &mut notes) {
119 Ok(um) => {
120 umodels.insert(m.name.as_str(), um);
121 }
122 Err(e) => problems.push(format!("model `{}`: {e}", m.name)),
123 },
124 "ltra" => match ltra_model(m, &mut notes) {
125 Ok(lm) => {
126 omodels.insert(m.name.as_str(), lm);
127 }
128 Err(e) => problems.push(format!("model `{}`: {e}", m.name)),
129 },
130 "d" => match diode_model(m, &mut notes) {
131 Ok(dm) => {
132 dmodels.insert(m.name.as_str(), dm);
133 }
134 Err(e) => problems.push(format!("model `{}`: {e}", m.name)),
135 },
136 "npn" | "pnp" => match bjt_model(m, &mut notes) {
137 Ok(bm) => {
138 qmodels.insert(m.name.as_str(), bm);
139 }
140 Err(e) => problems.push(format!("model `{}`: {e}", m.name)),
141 },
142 "nmos" | "pmos" => {
143 let level = num_param(&m.params, "level").unwrap_or(1.0);
146 if level == 1.0 {
147 match mos1_model(m, &mut notes) {
148 Ok(mm) => {
149 mmodels.insert(m.name.as_str(), MosModel::M1(mm));
150 }
151 Err(e) => problems.push(format!("model `{}`: {e}", m.name)),
152 }
153 } else if level == 2.0 {
154 match mos2_model(m, &mut notes) {
155 Ok(mm) => {
156 mmodels.insert(m.name.as_str(), MosModel::M2(mm));
157 }
158 Err(e) => problems.push(format!("model `{}`: {e}", m.name)),
159 }
160 } else if level == 3.0 {
161 match mos3_model(m, &mut notes) {
162 Ok(mm) => {
163 mmodels.insert(m.name.as_str(), MosModel::M3(mm));
164 }
165 Err(e) => problems.push(format!("model `{}`: {e}", m.name)),
166 }
167 } else if level == 4.0 {
168 match bsim1_model(m, &mut notes) {
169 Ok(mm) => {
170 mmodels.insert(m.name.as_str(), MosModel::B1(Box::new(mm)));
171 }
172 Err(e) => problems.push(format!("model `{}`: {e}", m.name)),
173 }
174 } else if level == 5.0 {
175 match bsim2_model(m, &mut notes) {
176 Ok(mm) => {
177 mmodels.insert(m.name.as_str(), MosModel::B2(Box::new(mm)));
178 }
179 Err(e) => problems.push(format!("model `{}`: {e}", m.name)),
180 }
181 } else if level == 8.0 || level == 49.0 {
182 match bsim3_model(m, &mut notes) {
186 Ok(Ok(mm)) => {
187 mmodels.insert(m.name.as_str(), MosModel::B3(Box::new(mm)));
188 }
189 Ok(Err(reason)) => {
190 unsupported_models.insert(
191 m.name.as_str(),
192 format!("model `{}`: {reason}", m.name),
193 );
194 }
195 Err(e) => problems.push(format!("model `{}`: {e}", m.name)),
196 }
197 } else if level == 6.0 {
198 match mos6_model(m, &mut notes) {
199 Ok(mm) => {
200 mmodels.insert(m.name.as_str(), MosModel::M6(mm));
201 }
202 Err(e) => problems.push(format!("model `{}`: {e}", m.name)),
203 }
204 } else {
205 unsupported_models.insert(
206 m.name.as_str(),
207 format!(
208 "model `{}`: MOS level {level} is not implemented yet (Phase 3)",
209 m.name
210 ),
211 );
212 }
213 }
214 _ => {}
215 }
216 }
217
218 let mut ckt = Circuit::new(nl.title.clone());
219
220 for el in &nl.elements {
221 let name = el.name.clone();
222 match &el.device {
223 ir::Device::Resistor {
224 value,
225 model,
226 params,
227 } => {
228 let rm = match model {
229 Some(m) => match rmodels.get(m.as_str()) {
230 Some(rm) => Some(rm),
231 None => {
232 problems.push(format!("`{name}`: unknown resistor model `{m}`"));
233 continue;
234 }
235 },
236 None => None,
237 };
238 match res_instance(&name, *value, rm, params, &mut notes) {
239 Ok((r, temp)) => {
240 if let Some(t) = temp {
241 ckt.set_res_temp(&name, t);
242 }
243 add(
244 &mut ckt,
245 Resistor {
246 name,
247 nodes: two(&el.nodes),
248 value: r,
249 },
250 &mut problems,
251 );
252 }
253 Err(e) => problems.push(e),
254 }
255 }
256 ir::Device::Capacitor {
257 value,
258 model,
259 params,
260 } => {
261 let cm = match model {
262 Some(m) => match cmodels.get(m.as_str()) {
263 Some(cm) => Some(cm),
264 None => {
265 problems.push(format!("`{name}`: unknown capacitor model `{m}`"));
266 continue;
267 }
268 },
269 None => None,
270 };
271 let ic = num_param(params, "ic");
272 match cap_instance(&name, *value, cm, params, &mut notes) {
273 Ok(c) => add(
274 &mut ckt,
275 Capacitor {
276 name,
277 nodes: two(&el.nodes),
278 value: c,
279 ic,
280 },
281 &mut problems,
282 ),
283 Err(e) => problems.push(e),
284 }
285 }
286 ir::Device::Inductor { value, params } => {
287 let ic = num_param(params, "ic");
288 match value {
289 Some(v) => add(
290 &mut ckt,
291 Inductor {
292 name,
293 nodes: two(&el.nodes),
294 value: *v,
295 ic,
296 },
297 &mut problems,
298 ),
299 None => problems.push(format!("`{name}`: inductor has no value")),
300 }
301 }
302 ir::Device::Mutual { l1, l2, k } => add(
303 &mut ckt,
304 Mutual {
305 name,
306 l1: l1.clone(),
307 l2: l2.clone(),
308 k: *k,
309 },
310 &mut problems,
311 ),
312 ir::Device::Source {
313 kind,
314 dc,
315 tran,
316 ac,
317 distof1,
318 distof2,
319 port,
320 } => {
321 let ac = ac.as_ref().map(|a| (a.mag, a.phase));
322 let distof1 = distof1.as_ref().map(|d| (d.mag, d.phase));
323 let distof2 = distof2.as_ref().map(|d| (d.mag, d.phase));
324 let wave = tran.as_ref().map(|t| {
325 let args = t.args.clone();
326 match t.kind {
327 ir::TranFnKind::Pulse => crate::srcfn::Wave::Pulse(args),
328 ir::TranFnKind::Sin => crate::srcfn::Wave::Sin(args),
329 ir::TranFnKind::Exp => crate::srcfn::Wave::Exp(args),
330 ir::TranFnKind::Pwl => crate::srcfn::Wave::Pwl(args),
331 ir::TranFnKind::Sffm => crate::srcfn::Wave::Sffm(args),
332 ir::TranFnKind::TrNoise => crate::srcfn::Wave::TrNoise(args),
333 ir::TranFnKind::TrRandom => crate::srcfn::Wave::TrRandom(args),
334 }
335 });
336 match kind {
337 ir::SourceKind::Voltage => add(
338 &mut ckt,
339 Vsource {
340 name,
341 nodes: two(&el.nodes),
342 dc: *dc,
343 wave,
344 ac,
345 distof1,
346 distof2,
347 port: port.map(|p| crate::circuit::RfPort {
348 num: p.num,
349 z0: p.z0,
350 }),
351 },
352 &mut problems,
353 ),
354 ir::SourceKind::Current => add(
355 &mut ckt,
356 Isource {
357 name,
358 nodes: two(&el.nodes),
359 dc: *dc,
360 wave,
361 ac,
362 distof1,
363 distof2,
364 },
365 &mut problems,
366 ),
367 }
368 }
369 ir::Device::Vcvs { gain } => add(
370 &mut ckt,
371 Vcvs {
372 name,
373 nodes: four(&el.nodes),
374 gain: gain.unwrap_or(0.0),
375 },
376 &mut problems,
377 ),
378 ir::Device::Vccs { gain } => add(
379 &mut ckt,
380 Vccs {
381 name,
382 nodes: four(&el.nodes),
383 gain: gain.unwrap_or(0.0),
384 },
385 &mut problems,
386 ),
387 ir::Device::Cccs { vname, gain } => add(
388 &mut ckt,
389 Cccs {
390 name,
391 nodes: two(&el.nodes),
392 ctrl: vname.clone(),
393 gain: gain.unwrap_or(0.0),
394 },
395 &mut problems,
396 ),
397 ir::Device::Ccvs { vname, gain } => add(
398 &mut ckt,
399 Ccvs {
400 name,
401 nodes: two(&el.nodes),
402 ctrl: vname.clone(),
403 gain: gain.unwrap_or(0.0),
404 },
405 &mut problems,
406 ),
407 ir::Device::Diode {
408 model,
409 area,
410 off,
411 params,
412 } => {
413 let Some(dm) = dmodels.get(model.as_str()) else {
414 problems.push(format!(
415 "`{name}`: unable to find definition of model `{model}`"
416 ));
417 continue;
418 };
419 let mut temp = None;
420 let mut ic = None;
421 for p in params {
422 match (p.name.as_str(), &p.value) {
423 ("temp", ParamValue::Num(v)) => temp = Some(v + CTOK),
424 ("dtemp", ParamValue::Num(v)) => ckt.set_dtemp(&name, *v),
426 ("ic", ParamValue::Num(v)) => ic = Some(*v),
427 _ => problems.push(format!("`{name}`: unknown parameter ({})", p.name)),
428 }
429 }
430 add(
431 &mut ckt,
432 Diode {
433 name,
434 nodes: two(&el.nodes),
435 model: dm.clone(),
436 area: area.unwrap_or(1.0),
437 off: *off,
438 temp,
439 ic,
440 },
441 &mut problems,
442 );
443 }
444 ir::Device::Bjt {
445 model,
446 area,
447 off,
448 params,
449 } => {
450 let Some(bm) = qmodels.get(model.as_str()) else {
451 problems.push(format!(
452 "`{name}`: unable to find definition of model `{model}`"
453 ));
454 continue;
455 };
456 let mut temp = None;
457 let mut ic = (0.0, 0.0);
458 let mut icg = IcGiven::NONE;
459 for p in params {
460 match (p.name.as_str(), &p.value) {
461 ("temp", ParamValue::Num(v)) => temp = Some(v + CTOK),
462 ("dtemp", ParamValue::Num(v)) => ckt.set_dtemp(&name, *v),
464 ("ic", ParamValue::Num(v)) => {
465 ic = (*v, 0.0);
466 icg = IcGiven::first(1);
467 }
468 ("ic", ParamValue::NumList(v)) => {
469 ic = (
470 v.first().copied().unwrap_or(0.0),
471 v.get(1).copied().unwrap_or(0.0),
472 );
473 icg = IcGiven::first(v.len().min(2));
474 }
475 ("area", ParamValue::Num(_)) => {}
476 _ => problems.push(format!("`{name}`: unknown parameter ({})", p.name)),
477 }
478 }
479 let mut nodes4 = [
480 el.nodes[0].clone(),
481 el.nodes[1].clone(),
482 el.nodes[2].clone(),
483 "0".to_string(),
484 ];
485 if let Some(s) = el.nodes.get(3) {
486 nodes4[3] = s.clone();
487 }
488 add_ic(
489 &mut ckt,
490 Bjt {
491 name,
492 nodes: nodes4,
493 model: bm.clone(),
494 area: area.unwrap_or(1.0),
495 off: *off,
496 ic,
497 temp,
498 },
499 icg,
500 &mut problems,
501 );
502 }
503 ir::Device::Mosfet { model, params } => {
504 let Some(mm) = mmodels.get(model.as_str()) else {
505 if let Some(reason) = unsupported_models.get(model.as_str()) {
506 problems.push(format!("`{name}`: {reason}"));
507 } else {
508 problems.push(format!(
509 "`{name}`: unable to find definition of model `{model}`"
510 ));
511 }
512 continue;
513 };
514 let mut inst = MosInst::new();
515 let is_b3 = matches!(mm, MosModel::B3(_));
516 for p in params {
517 if is_b3 {
518 match (p.name.as_str(), &p.value) {
521 ("m", ParamValue::Num(v)) => {
522 inst.m = *v;
523 continue;
524 }
525 ("delvto", ParamValue::Num(v)) => {
526 inst.delvto = *v;
527 continue;
528 }
529 ("mulu0", ParamValue::Num(v)) => {
530 inst.mulu0 = *v;
531 continue;
532 }
533 ("icvds", ParamValue::Num(v)) => {
534 inst.ic.0 = *v;
535 continue;
536 }
537 ("icvgs", ParamValue::Num(v)) => {
538 inst.ic.1 = *v;
539 continue;
540 }
541 ("icvbs", ParamValue::Num(v)) => {
542 inst.ic.2 = *v;
543 continue;
544 }
545 ("nqsmod" | "acnqsmod", ParamValue::Num(v)) => {
546 if *v != 0.0 {
547 problems.push(format!(
548 "`{name}`: BSIM3 {} = {v} (the non-quasi-static model) is not implemented",
549 p.name
550 ));
551 }
552 continue;
553 }
554 ("geo", ParamValue::Num(_)) => continue,
556 ("temp" | "dtemp", _) => {
557 problems
558 .push(format!("`{name}`: unknown parameter ({})", p.name));
559 continue;
560 }
561 _ => {}
562 }
563 }
564 match (p.name.as_str(), &p.value) {
565 ("l", ParamValue::Num(v)) => inst.l = Some(*v),
566 ("w", ParamValue::Num(v)) => inst.w = Some(*v),
567 ("ad", ParamValue::Num(v)) => inst.ad = Some(*v),
568 ("as", ParamValue::Num(v)) => inst.as_ = Some(*v),
569 ("pd", ParamValue::Num(v)) => inst.pd = *v,
570 ("ps", ParamValue::Num(v)) => inst.ps = *v,
571 ("nrd", ParamValue::Num(v)) => inst.nrd = *v,
572 ("nrs", ParamValue::Num(v)) => inst.nrs = *v,
573 ("off", ParamValue::Flag) => inst.off = true,
574 ("temp", ParamValue::Num(v)) => inst.temp = Some(v + CTOK),
575 ("dtemp", ParamValue::Num(v)) => inst.dtemp = Some(*v),
577 ("ic", ParamValue::Num(v)) => {
578 inst.ic = (*v, 0.0, 0.0);
579 inst.ic_given = IcGiven::first(1);
580 }
581 ("ic", ParamValue::NumList(v)) => {
582 inst.ic = (
583 v.first().copied().unwrap_or(0.0),
584 v.get(1).copied().unwrap_or(0.0),
585 v.get(2).copied().unwrap_or(0.0),
586 );
587 inst.ic_given = IcGiven::first(v.len().min(3));
588 }
589 _ => problems.push(format!("`{name}`: unknown parameter ({})", p.name)),
590 }
591 }
592 if matches!(mm, MosModel::B1(_) | MosModel::B2(_)) {
593 for (given, kw) in [
596 (inst.temp.is_some(), "temp"),
597 (inst.dtemp.is_some(), "dtemp"),
598 ] {
599 if given {
600 problems.push(format!("`{name}`: unknown parameter ({kw})"));
601 }
602 }
603 } else if let Some(d) = inst.dtemp {
604 ckt.set_dtemp(&name, d);
605 }
606 let nodes = four(&el.nodes);
607 match mm {
608 MosModel::M1(m1) => add_ic(
609 &mut ckt,
610 Mos1 {
611 name,
612 nodes,
613 model: m1.clone(),
614 l: inst.l,
615 w: inst.w,
616 ad: inst.ad,
617 as_: inst.as_,
618 pd: inst.pd,
619 ps: inst.ps,
620 nrd: inst.nrd,
621 nrs: inst.nrs,
622 off: inst.off,
623 ic: inst.ic,
624 temp: inst.temp,
625 },
626 inst.ic_given,
627 &mut problems,
628 ),
629 MosModel::M2(m2) => add_ic(
630 &mut ckt,
631 Mos2 {
632 name,
633 nodes,
634 model: m2.clone(),
635 l: inst.l,
636 w: inst.w,
637 ad: inst.ad,
638 as_: inst.as_,
639 pd: inst.pd,
640 ps: inst.ps,
641 nrd: inst.nrd,
642 nrs: inst.nrs,
643 off: inst.off,
644 ic: inst.ic,
645 temp: inst.temp,
646 },
647 inst.ic_given,
648 &mut problems,
649 ),
650 MosModel::M3(m3) => add_ic(
651 &mut ckt,
652 Mos3 {
653 name,
654 nodes,
655 model: m3.clone(),
656 l: inst.l,
657 w: inst.w,
658 ad: inst.ad,
659 as_: inst.as_,
660 pd: inst.pd,
661 ps: inst.ps,
662 nrd: inst.nrd,
663 nrs: inst.nrs,
664 off: inst.off,
665 ic: inst.ic,
666 temp: inst.temp,
667 },
668 inst.ic_given,
669 &mut problems,
670 ),
671 MosModel::M6(m6) => add_ic(
672 &mut ckt,
673 Mos6 {
674 name,
675 nodes,
676 model: m6.clone(),
677 l: inst.l,
678 w: inst.w,
679 ad: inst.ad,
680 as_: inst.as_,
681 pd: inst.pd,
682 ps: inst.ps,
683 nrd: inst.nrd,
684 nrs: inst.nrs,
685 off: inst.off,
686 ic: inst.ic,
687 temp: inst.temp,
688 },
689 inst.ic_given,
690 &mut problems,
691 ),
692 MosModel::B1(b1) => add_ic(
693 &mut ckt,
694 Bsim1 {
695 name,
696 nodes,
697 model: (**b1).clone(),
698 l: inst.l,
699 w: inst.w,
700 ad: inst.ad,
701 as_: inst.as_,
702 pd: inst.pd,
703 ps: inst.ps,
704 nrd: inst.nrd,
705 nrs: inst.nrs,
706 off: inst.off,
707 ic: inst.ic,
708 },
709 inst.ic_given,
710 &mut problems,
711 ),
712 MosModel::B3(b3) => add_ic(
713 &mut ckt,
714 Bsim3 {
715 name,
716 nodes,
717 model: (**b3).clone(),
718 l: inst.l,
719 w: inst.w,
720 ad: inst.ad.unwrap_or(0.0),
721 as_: inst.as_.unwrap_or(0.0),
722 pd: inst.pd,
723 ps: inst.ps,
724 nrd: inst.nrd,
725 nrs: inst.nrs,
726 m: inst.m,
727 delvto: inst.delvto,
728 mulu0: inst.mulu0,
729 off: inst.off,
730 ic: inst.ic,
731 },
732 inst.ic_given,
733 &mut problems,
734 ),
735 MosModel::B2(b2) => add_ic(
736 &mut ckt,
737 Bsim2 {
738 name,
739 nodes,
740 model: (**b2).clone(),
741 l: inst.l,
742 w: inst.w,
743 ad: inst.ad,
744 as_: inst.as_,
745 pd: inst.pd,
746 ps: inst.ps,
747 nrd: inst.nrd,
748 nrs: inst.nrs,
749 off: inst.off,
750 ic: inst.ic,
751 },
752 inst.ic_given,
753 &mut problems,
754 ),
755 }
756 }
757 ir::Device::Jfet {
758 model,
759 area,
760 off,
761 params,
762 } => {
763 let Some(jm) = jmodels.get(model.as_str()) else {
764 if let Some(reason) = unsupported_models.get(model.as_str()) {
765 problems.push(format!("`{name}`: {reason}"));
766 } else {
767 problems.push(format!(
768 "`{name}`: unable to find definition of model `{model}`"
769 ));
770 }
771 continue;
772 };
773 let mut area = *area;
774 let mut temp = None;
775 let mut ic = (0.0, 0.0);
776 let mut icg = IcGiven::NONE;
777 for p in params {
778 match (p.name.as_str(), &p.value) {
779 ("temp", ParamValue::Num(v)) => temp = Some(v + CTOK),
780 ("dtemp", ParamValue::Num(v)) => ckt.set_dtemp(&name, *v),
782 ("area", ParamValue::Num(v)) => area = Some(*v),
783 ("ic", ParamValue::Num(v)) => {
784 ic.0 = *v;
785 icg = icg.with(0);
786 }
787 ("ic", ParamValue::NumList(v)) if (1..=2).contains(&v.len()) => {
788 ic = (v[0], v.get(1).copied().unwrap_or(ic.1));
789 icg = IcGiven(icg.0 | IcGiven::first(v.len()).0);
790 }
791 _ => problems.push(format!("`{name}`: unknown parameter ({})", p.name)),
792 }
793 }
794 add_ic(
795 &mut ckt,
796 Jfet {
797 name,
798 nodes: three(&el.nodes),
799 model: jm.clone(),
800 area: area.unwrap_or(1.0),
801 off: *off,
802 ic,
803 temp,
804 },
805 icg,
806 &mut problems,
807 );
808 }
809 ir::Device::Mesfet {
810 model,
811 area,
812 off,
813 params,
814 } => {
815 let Some(zm) = zmodels.get(model.as_str()) else {
816 if let Some(reason) = unsupported_models.get(model.as_str()) {
817 problems.push(format!("`{name}`: {reason}"));
818 } else {
819 problems.push(format!(
820 "`{name}`: unable to find definition of model `{model}`"
821 ));
822 }
823 continue;
824 };
825 let mut area = *area;
826 let mut ic = (0.0, 0.0);
827 let mut icg = IcGiven::NONE;
828 for p in params {
829 match (p.name.as_str(), &p.value) {
830 ("area", ParamValue::Num(v)) => area = Some(*v),
831 ("icvds", ParamValue::Num(v)) => {
832 ic.0 = *v;
833 icg = icg.with(0);
834 }
835 ("icvgs", ParamValue::Num(v)) => {
836 ic.1 = *v;
837 icg = icg.with(1);
838 }
839 ("ic", ParamValue::Num(v)) => {
840 ic.0 = *v;
841 icg = icg.with(0);
842 }
843 ("ic", ParamValue::NumList(v)) if (1..=2).contains(&v.len()) => {
844 ic = (v[0], v.get(1).copied().unwrap_or(ic.1));
845 icg = IcGiven(icg.0 | IcGiven::first(v.len()).0);
846 }
847 _ => problems.push(format!("`{name}`: unknown parameter ({})", p.name)),
848 }
849 }
850 add_ic(
851 &mut ckt,
852 Mes {
853 name,
854 nodes: three(&el.nodes),
855 model: zm.clone(),
856 area: area.unwrap_or(1.0),
857 off: *off,
858 ic,
859 },
860 icg,
861 &mut problems,
862 );
863 }
864 ir::Device::SwitchV { model, state } => {
865 let Some(sm) = smodels.get(model.as_str()) else {
866 problems.push(format!(
867 "`{name}`: unable to find definition of model `{model}`"
868 ));
869 continue;
870 };
871 add(
872 &mut ckt,
873 SwitchV {
874 name,
875 nodes: four(&el.nodes),
876 model: sm.clone(),
877 on: *state == Some(ir::OnOff::On),
878 },
879 &mut problems,
880 );
881 }
882 ir::Device::SwitchI {
883 vname,
884 model,
885 state,
886 } => {
887 let Some(wm) = wmodels.get(model.as_str()) else {
888 problems.push(format!(
889 "`{name}`: unable to find definition of model `{model}`"
890 ));
891 continue;
892 };
893 add(
894 &mut ckt,
895 SwitchI {
896 name,
897 nodes: two(&el.nodes),
898 ctrl: vname.clone(),
899 model: wm.clone(),
900 on: *state == Some(ir::OnOff::On),
901 },
902 &mut problems,
903 );
904 }
905 ir::Device::TlineLossy { model } => {
906 let Some(lm) = omodels.get(model.as_str()) else {
907 problems.push(format!(
908 "`{name}`: unable to find definition of model `{model}`"
909 ));
910 continue;
911 };
912 add(
913 &mut ckt,
914 Ltra {
915 name,
916 nodes: four(&el.nodes),
917 model: lm.clone(),
918 },
919 &mut problems,
920 );
921 }
922 ir::Device::Behavioral { kind, expr } => add(
923 &mut ckt,
924 crate::circuit::Asrc {
925 name,
926 nodes: two(&el.nodes),
927 kind: *kind,
928 expr: expr.clone(),
929 },
930 &mut problems,
931 ),
932 ir::Device::Urc { model, params } => {
933 let Some(um) = umodels.get(model.as_str()) else {
934 problems.push(format!(
935 "`{name}`: unable to find definition of model `{model}`"
936 ));
937 continue;
938 };
939 match urc_expand(&name, &el.nodes, um, params) {
940 Ok(specs) => {
941 for spec in specs {
942 add(&mut ckt, spec, &mut problems);
943 }
944 }
945 Err(e) => problems.push(e),
946 }
947 }
948 ir::Device::TlineIdeal { params } => match tra_spec(&name, &el.nodes, params) {
949 Ok(t) => add(&mut ckt, t, &mut problems),
950 Err(e) => problems.push(e),
951 },
952 other => {
953 problems.push(format!(
954 "`{name}`: {} is not implemented yet (Phase {})",
955 device_kind_name(other),
956 device_phase(other),
957 ));
958 }
959 }
960 }
961
962 for ni in &nl.node_inits {
963 match ni.kind {
964 ir::NodeInitKind::Nodeset => ckt.nodeset(ni.node.clone(), ni.value),
965 ir::NodeInitKind::Ic => ckt.ic(ni.node.clone(), ni.value),
966 }
967 }
968
969 if !problems.is_empty() {
970 return Err(Error::Elab(problems.join("\n")));
971 }
972 Ok((ckt, notes))
973 }
974}
975
976fn tra_spec(name: &str, nodes: &[String], params: &[ir::Param]) -> Result<Tra, String> {
981 let mut z0 = None;
982 let mut t = Tra {
983 name: name.to_string(),
984 nodes: four(nodes),
985 z0: 0.0,
986 td: None,
987 f: None,
988 nl: None,
989 ic: [0.0; 4],
990 };
991 for p in params {
992 match (p.name.as_str(), &p.value) {
993 ("z0" | "zo", ParamValue::Num(v)) => z0 = Some(*v),
994 ("td", ParamValue::Num(v)) => t.td = Some(*v),
995 ("f", ParamValue::Num(v)) => t.f = Some(*v),
996 ("nl", ParamValue::Num(v)) => t.nl = Some(*v),
997 ("v1", ParamValue::Num(v)) => t.ic[0] = *v,
998 ("i1", ParamValue::Num(v)) => t.ic[1] = *v,
999 ("v2", ParamValue::Num(v)) => t.ic[2] = *v,
1000 ("i2", ParamValue::Num(v)) => t.ic[3] = *v,
1001 ("ic", ParamValue::Num(v)) => t.ic[0] = *v,
1002 ("ic", ParamValue::NumList(v)) if (1..=4).contains(&v.len()) => {
1003 t.ic[..v.len()].copy_from_slice(v);
1004 }
1005 _ => return Err(format!("`{name}`: unknown parameter ({})", p.name)),
1006 }
1007 }
1008 t.z0 = z0.ok_or_else(|| format!("`{name}`: transmission line z0 must be given"))?;
1010 Ok(t)
1011}
1012
1013fn ltra_model(m: &ir::Model, notes: &mut Vec<String>) -> Result<crate::ltra::LtraModel, String> {
1020 use crate::ltra::LtraInterp;
1021 let mut lm = crate::ltra::LtraModel::default();
1022 for p in &m.params {
1023 let num = match p.value {
1024 ParamValue::Num(v) => Some(v),
1025 _ => None,
1026 };
1027 let need = || num.ok_or_else(|| format!("parameter {} needs a numeric value", p.name));
1028 match p.name.as_str() {
1029 "ltra" => {}
1030 "r" => lm.r = Some(need()?),
1031 "l" => lm.l = Some(need()?),
1032 "g" => lm.g = Some(need()?),
1033 "c" => lm.c = Some(need()?),
1034 "len" => lm.len = Some(need()?),
1035 "rel" => lm.rel = Some(need()?),
1036 "abs" => lm.abs = Some(need()?),
1037 "compactrel" => lm.compactrel = need()?,
1038 "compactabs" => lm.compactabs = need()?,
1039 "nocontrol" => {}
1041 "steplimit" => lm.steplimit = true,
1042 "nosteplimit" => lm.steplimit = false,
1043 "lininterp" => lm.interp = Some(LtraInterp::Linear),
1044 "quadinterp" => lm.interp = Some(LtraInterp::Quadratic),
1045 "mixedinterp" => lm.interp = Some(LtraInterp::Mixed),
1046 "truncnr" => lm.truncnr = true,
1047 "truncdontcut" => lm.truncdontcut = true,
1048 other => notes.push(format!(
1049 "model `{}`: unrecognized parameter ({other}) - ignored",
1050 m.name
1051 )),
1052 }
1053 }
1054 Ok(lm)
1055}
1056
1057fn bjt_model(m: &ir::Model, notes: &mut Vec<String>) -> Result<crate::bjt::BjtModel, String> {
1060 let mut bm = crate::bjt::BjtModel::new(m.mtype == "npn");
1061 for p in &m.params {
1062 let v = match p.value {
1063 ParamValue::Num(v) => v,
1064 ParamValue::Flag if p.name == "npn" || p.name == "pnp" => {
1066 bm.npn = p.name == "npn";
1067 continue;
1068 }
1069 _ => return Err(format!("parameter {} needs a numeric value", p.name)),
1070 };
1071 match p.name.as_str() {
1072 "is" => bm.is = v,
1073 "bf" => bm.bf = v,
1074 "nf" => bm.nf = v,
1075 "vaf" | "va" => bm.vaf = v,
1076 "ikf" | "ik" => bm.ikf = v,
1077 "ise" => bm.ise = v,
1078 "ne" => bm.ne = v,
1079 "br" => bm.br = v,
1080 "nr" => bm.nr = v,
1081 "var" | "vb" => bm.var = v,
1082 "ikr" => bm.ikr = v,
1083 "isc" => bm.isc = v,
1084 "nc" => bm.nc = v,
1085 "rb" => bm.rb = v,
1086 "irb" => bm.irb = v,
1087 "rbm" => bm.rbm = Some(v),
1088 "re" => bm.re = v,
1089 "rc" => bm.rc = v,
1090 "cje" => bm.cje = v,
1091 "vje" | "pe" => bm.vje = v,
1092 "mje" | "me" => bm.mje = v,
1093 "tf" => bm.tf = v,
1094 "xtf" => bm.xtf = v,
1095 "vtf" => bm.vtf = v,
1096 "itf" => bm.itf = v,
1097 "ptf" => bm.ptf = v,
1098 "cjc" => bm.cjc = v,
1099 "vjc" | "pc" => bm.vjc = v,
1100 "mjc" | "mc" => bm.mjc = v,
1101 "xcjc" => bm.xcjc = v,
1102 "tr" => bm.tr = v,
1103 "cjs" | "ccs" => bm.cjs = v,
1104 "vjs" | "ps" => bm.vjs = v,
1105 "mjs" | "ms" => bm.mjs = v,
1106 "xtb" => bm.xtb = v,
1107 "eg" => bm.eg = v,
1108 "xti" => bm.xti = v,
1109 "fc" => bm.fc = Some(v),
1110 "kf" => bm.kf = v,
1111 "af" => bm.af = v,
1112 "tnom" => bm.tnom = Some(v + CTOK),
1113 other => notes.push(format!(
1114 "model `{}`: unrecognized parameter ({other}) - ignored",
1115 m.name
1116 )),
1117 }
1118 }
1119 Ok(bm)
1120}
1121
1122fn mos1_model(m: &ir::Model, notes: &mut Vec<String>) -> Result<crate::mos1::Mos1Model, String> {
1126 let mut mm = crate::mos1::Mos1Model::new(m.mtype == "nmos");
1127 for p in &m.params {
1128 let v = match p.value {
1129 ParamValue::Num(v) => v,
1130 ParamValue::Flag if p.name == "nmos" || p.name == "pmos" => {
1132 mm.nmos = p.name == "nmos";
1133 continue;
1134 }
1135 _ => return Err(format!("parameter {} needs a numeric value", p.name)),
1136 };
1137 match p.name.as_str() {
1138 "level" => {} "vto" | "vt0" => mm.vto = Some(v),
1140 "kp" => mm.kp = Some(v),
1141 "gamma" => mm.gamma = Some(v),
1142 "phi" => mm.phi = Some(v),
1143 "lambda" => mm.lambda = v,
1144 "rd" => mm.rd = Some(v),
1145 "rs" => mm.rs = Some(v),
1146 "cbd" => mm.cbd = Some(v),
1147 "cbs" => mm.cbs = Some(v),
1148 "is" => mm.is = v,
1149 "pb" => mm.pb = v,
1150 "cgso" => mm.cgso = v,
1151 "cgdo" => mm.cgdo = v,
1152 "cgbo" => mm.cgbo = v,
1153 "rsh" => mm.rsh = Some(v),
1154 "cj" => mm.cj = Some(v),
1155 "mj" => mm.mj = v,
1156 "cjsw" => mm.cjsw = Some(v),
1157 "mjsw" => mm.mjsw = v,
1158 "js" => mm.js = v,
1159 "tox" => mm.tox = Some(v),
1160 "ld" => mm.ld = v,
1161 "u0" | "uo" => mm.u0 = Some(v),
1162 "fc" => mm.fc = v,
1163 "nsub" => mm.nsub = Some(v),
1164 "tpg" => mm.tpg = Some(v),
1165 "nss" => mm.nss = Some(v),
1166 "kf" => mm.kf = v,
1167 "af" => mm.af = v,
1168 "nlev" => mm.nlev = (0.5 + v).floor() as i64,
1170 "gdsnoi" => mm.gdsnoi = v,
1171 "tnom" => mm.tnom = Some(v + CTOK),
1172 other => notes.push(format!(
1173 "model `{}`: unrecognized parameter ({other}) - ignored",
1174 m.name
1175 )),
1176 }
1177 }
1178 Ok(mm)
1179}
1180
1181#[derive(Default)]
1185struct MosInst {
1186 l: Option<f64>,
1187 w: Option<f64>,
1188 ad: Option<f64>,
1189 as_: Option<f64>,
1190 pd: f64,
1191 ps: f64,
1192 nrd: f64,
1193 nrs: f64,
1194 off: bool,
1195 ic: (f64, f64, f64),
1196 ic_given: IcGiven,
1198 temp: Option<f64>,
1199 dtemp: Option<f64>,
1201 m: f64,
1203 delvto: f64,
1204 mulu0: f64,
1205}
1206
1207impl MosInst {
1208 fn new() -> Self {
1209 MosInst {
1210 ic_given: IcGiven::NONE,
1211 nrd: 1.0,
1212 nrs: 1.0,
1213 m: 1.0,
1214 mulu0: 1.0,
1215 ..Default::default()
1216 }
1217 }
1218}
1219
1220fn mos2_model(m: &ir::Model, notes: &mut Vec<String>) -> Result<crate::mos2::Mos2Model, String> {
1223 let mut mm = crate::mos2::Mos2Model::new(m.mtype == "nmos");
1224 for p in &m.params {
1225 let v = match p.value {
1226 ParamValue::Num(v) => v,
1227 ParamValue::Flag if p.name == "nmos" || p.name == "pmos" => {
1228 mm.nmos = p.name == "nmos";
1229 continue;
1230 }
1231 _ => return Err(format!("parameter {} needs a numeric value", p.name)),
1232 };
1233 match p.name.as_str() {
1234 "level" => {}
1235 "vto" | "vt0" => mm.vto = Some(v),
1236 "kp" => mm.kp = Some(v),
1237 "gamma" => mm.gamma = Some(v),
1238 "phi" => mm.phi = Some(v),
1239 "lambda" => mm.lambda = v,
1240 "rd" => mm.rd = Some(v),
1241 "rs" => mm.rs = Some(v),
1242 "cbd" => mm.cbd = Some(v),
1243 "cbs" => mm.cbs = Some(v),
1244 "is" => mm.is = v,
1245 "pb" => mm.pb = v,
1246 "cgso" => mm.cgso = v,
1247 "cgdo" => mm.cgdo = v,
1248 "cgbo" => mm.cgbo = v,
1249 "rsh" => mm.rsh = Some(v),
1250 "cj" => mm.cj = Some(v),
1251 "mj" => mm.mj = v,
1252 "cjsw" => mm.cjsw = Some(v),
1253 "mjsw" => mm.mjsw = v,
1254 "js" => mm.js = v,
1255 "tox" => mm.tox = Some(v),
1256 "ld" => mm.ld = v,
1257 "u0" | "uo" => mm.u0 = Some(v),
1258 "fc" => mm.fc = v,
1259 "nsub" => mm.nsub = Some(v),
1260 "tpg" => mm.tpg = Some(v),
1261 "nss" => mm.nss = Some(v),
1262 "delta" => mm.delta = v,
1263 "uexp" => mm.uexp = v,
1264 "ucrit" => mm.ucrit = v,
1265 "vmax" => mm.vmax = v,
1266 "xj" => mm.xj = v,
1267 "neff" => mm.neff = v,
1268 "nfs" => mm.nfs = v,
1269 "kf" => mm.kf = v,
1270 "af" => mm.af = v,
1271 "nlev" => mm.nlev = (0.5 + v).floor() as i64,
1273 "gdsnoi" => mm.gdsnoi = v,
1274 "tnom" => mm.tnom = Some(v + CTOK),
1275 other => notes.push(format!(
1276 "model `{}`: unrecognized parameter ({other}) - ignored",
1277 m.name
1278 )),
1279 }
1280 }
1281 Ok(mm)
1282}
1283
1284fn mos3_model(m: &ir::Model, notes: &mut Vec<String>) -> Result<crate::mos3::Mos3Model, String> {
1290 let mut mm = crate::mos3::Mos3Model::new(m.mtype == "nmos");
1291 for p in &m.params {
1292 let v = match p.value {
1293 ParamValue::Num(v) => v,
1294 ParamValue::Flag if p.name == "nmos" || p.name == "pmos" => {
1295 mm.nmos = p.name == "nmos";
1296 continue;
1297 }
1298 _ => return Err(format!("parameter {} needs a numeric value", p.name)),
1299 };
1300 match p.name.as_str() {
1301 "level" => {}
1302 "vto" | "vt0" => mm.vto = Some(v),
1303 "kp" => mm.kp = Some(v),
1304 "gamma" => mm.gamma = Some(v),
1305 "phi" => mm.phi = Some(v),
1306 "rd" => mm.rd = Some(v),
1307 "rs" => mm.rs = Some(v),
1308 "cbd" => mm.cbd = Some(v),
1309 "cbs" => mm.cbs = Some(v),
1310 "is" => mm.is = v,
1311 "pb" => mm.pb = v,
1312 "cgso" => mm.cgso = v,
1313 "cgdo" => mm.cgdo = v,
1314 "cgbo" => mm.cgbo = v,
1315 "rsh" => mm.rsh = Some(v),
1316 "cj" => mm.cj = Some(v),
1317 "mj" => mm.mj = v,
1318 "cjsw" => mm.cjsw = Some(v),
1319 "mjsw" => mm.mjsw = v,
1320 "js" => mm.js = v,
1321 "tox" => mm.tox = Some(v),
1322 "ld" => mm.ld = v,
1323 "u0" | "uo" => mm.u0 = Some(v),
1324 "fc" => mm.fc = v,
1325 "nsub" => mm.nsub = Some(v),
1326 "tpg" => mm.tpg = Some(v),
1327 "nss" => mm.nss = Some(v),
1328 "vmax" => mm.vmax = v,
1329 "xj" => mm.xj = v,
1330 "nfs" => mm.nfs = v,
1331 "eta" => mm.eta = v,
1332 "delta" => mm.delta = v,
1333 "theta" => mm.theta = v,
1334 "kappa" => mm.kappa = v,
1335 "kf" => mm.kf = v,
1336 "af" => mm.af = v,
1337 "nlev" => mm.nlev = (0.5 + v).floor() as i64,
1339 "gdsnoi" => mm.gdsnoi = v,
1340 "tnom" => mm.tnom = Some(v + CTOK),
1341 other => notes.push(format!(
1342 "model `{}`: unrecognized parameter ({other}) - ignored",
1343 m.name
1344 )),
1345 }
1346 }
1347 Ok(mm)
1348}
1349
1350fn mos6_model(m: &ir::Model, notes: &mut Vec<String>) -> Result<crate::mos6::Mos6Model, String> {
1353 let mut mm = crate::mos6::Mos6Model::new(m.mtype == "nmos");
1354 for p in &m.params {
1355 let v = match p.value {
1356 ParamValue::Num(v) => v,
1357 ParamValue::Flag if p.name == "nmos" || p.name == "pmos" => {
1358 mm.nmos = p.name == "nmos";
1359 continue;
1360 }
1361 _ => return Err(format!("parameter {} needs a numeric value", p.name)),
1362 };
1363 match p.name.as_str() {
1364 "level" => {}
1365 "vto" | "vt0" => mm.vto = Some(v),
1366 "kv" => mm.kv = v,
1367 "nv" => mm.nv = v,
1368 "kc" => mm.kc = Some(v),
1369 "nc" => mm.nc = v,
1370 "nvth" => mm.nvth = v,
1371 "ps" => mm.ps = v,
1372 "gamma" => mm.gamma = Some(v),
1373 "gamma1" => mm.gamma1 = v,
1374 "sigma" => mm.sigma = v,
1375 "phi" => mm.phi = Some(v),
1376 "lambda" => mm.lambda = Some(v),
1377 "lambda0" => mm.lambda0 = Some(v),
1378 "lambda1" => mm.lambda1 = v,
1379 "rd" => mm.rd = Some(v),
1380 "rs" => mm.rs = Some(v),
1381 "cbd" => mm.cbd = Some(v),
1382 "cbs" => mm.cbs = Some(v),
1383 "is" => mm.is = v,
1384 "pb" => mm.pb = v,
1385 "cgso" => mm.cgso = v,
1386 "cgdo" => mm.cgdo = v,
1387 "cgbo" => mm.cgbo = v,
1388 "rsh" => mm.rsh = Some(v),
1389 "cj" => mm.cj = Some(v),
1390 "mj" => mm.mj = v,
1391 "cjsw" => mm.cjsw = Some(v),
1392 "mjsw" => mm.mjsw = v,
1393 "js" => mm.js = v,
1394 "tox" => mm.tox = Some(v),
1395 "ld" => mm.ld = v,
1396 "u0" | "uo" => mm.u0 = Some(v),
1397 "fc" => mm.fc = v,
1398 "nsub" => mm.nsub = Some(v),
1399 "tpg" => mm.tpg = Some(v),
1400 "nss" => mm.nss = Some(v),
1401 "kf" => mm.kf = v,
1402 "af" => mm.af = v,
1403 "tnom" => mm.tnom = Some(v + CTOK),
1404 other => notes.push(format!(
1405 "model `{}`: unrecognized parameter ({other}) - ignored",
1406 m.name
1407 )),
1408 }
1409 }
1410 Ok(mm)
1411}
1412
1413#[allow(clippy::too_many_lines)]
1416fn bsim1_model(m: &ir::Model, notes: &mut Vec<String>) -> Result<crate::bsim1::Bsim1Model, String> {
1417 let mut b = crate::bsim1::Bsim1Model::new(m.mtype == "nmos");
1418 for p in &m.params {
1419 let v = match p.value {
1420 ParamValue::Num(v) => v,
1421 ParamValue::Flag if p.name == "nmos" || p.name == "pmos" => {
1422 b.nmos = p.name == "nmos";
1423 continue;
1424 }
1425 _ => return Err(format!("parameter {} needs a numeric value", p.name)),
1426 };
1427 match p.name.as_str() {
1428 "level" => {}
1429 "vfb" => b.vfb0 = v,
1430 "lvfb" => b.vfbl = v,
1431 "wvfb" => b.vfbw = v,
1432 "phi" => b.phi0 = v,
1433 "lphi" => b.phil = v,
1434 "wphi" => b.phiw = v,
1435 "k1" => b.k10 = v,
1436 "lk1" => b.k1l = v,
1437 "wk1" => b.k1w = v,
1438 "k2" => b.k20 = v,
1439 "lk2" => b.k2l = v,
1440 "wk2" => b.k2w = v,
1441 "eta" => b.eta0 = v,
1442 "leta" => b.etal = v,
1443 "weta" => b.etaw = v,
1444 "x2e" => b.etab0 = v,
1445 "lx2e" => b.etabl = v,
1446 "wx2e" => b.etabw = v,
1447 "x3e" => b.etad0 = v,
1448 "lx3e" => b.etadl = v,
1449 "wx3e" => b.etadw = v,
1450 "dl" => b.deltal = v,
1451 "dw" => b.deltaw = v,
1452 "muz" => b.mobzero = v,
1453 "x2mz" => b.mobzerob0 = v,
1454 "lx2mz" => b.mobzerobl = v,
1455 "wx2mz" => b.mobzerobw = v,
1456 "mus" => b.mobvdd0 = v,
1457 "lmus" => b.mobvddl = v,
1458 "wmus" => b.mobvddw = v,
1459 "x2ms" => b.mobvddb0 = v,
1460 "lx2ms" => b.mobvddbl = v,
1461 "wx2ms" => b.mobvddbw = v,
1462 "x3ms" => b.mobvddd0 = v,
1463 "lx3ms" => b.mobvdddl = v,
1464 "wx3ms" => b.mobvdddw = v,
1465 "u0" => b.ugs0 = v,
1466 "lu0" => b.ugsl = v,
1467 "wu0" => b.ugsw = v,
1468 "x2u0" => b.ugsb0 = v,
1469 "lx2u0" => b.ugsbl = v,
1470 "wx2u0" => b.ugsbw = v,
1471 "u1" => b.uds0 = v,
1472 "lu1" => b.udsl = v,
1473 "wu1" => b.udsw = v,
1474 "x2u1" => b.udsb0 = v,
1475 "lx2u1" => b.udsbl = v,
1476 "wx2u1" => b.udsbw = v,
1477 "x3u1" => b.udsd0 = v,
1478 "lx3u1" => b.udsdl = v,
1479 "wx3u1" => b.udsdw = v,
1480 "n0" => b.n00 = v,
1481 "ln0" => b.n0l = v,
1482 "wn0" => b.n0w = v,
1483 "nb" => b.nb0 = v,
1484 "lnb" => b.nbl = v,
1485 "wnb" => b.nbw = v,
1486 "nd" => b.nd0 = v,
1487 "lnd" => b.ndl = v,
1488 "wnd" => b.ndw = v,
1489 "tox" => b.tox = v,
1490 "temp" => b.temp = v,
1491 "vdd" => b.vdd = v,
1492 "cgso" => b.cgso = v,
1493 "cgdo" => b.cgdo = v,
1494 "cgbo" => b.cgbo = v,
1495 "xpart" => b.xpart = v,
1496 "rsh" => b.rsh = v,
1497 "js" => b.js = v,
1498 "pb" => b.pb = v,
1499 "mj" => b.mj = v,
1500 "pbsw" => b.pbsw = v,
1501 "mjsw" => b.mjsw = v,
1502 "cj" => b.cj = v,
1503 "cjsw" => b.cjsw = v,
1504 "wdf" => b.wdf = v,
1505 "dell" => b.dell = v,
1506 "kf" => b.kf = v,
1508 "af" => b.af = v,
1509 other => notes.push(format!(
1510 "model `{}`: unrecognized parameter ({other}) - ignored",
1511 m.name
1512 )),
1513 }
1514 }
1515 Ok(b)
1516}
1517
1518fn bsim3_model(
1525 m: &ir::Model,
1526 notes: &mut Vec<String>,
1527) -> Result<Result<crate::bsim3::Bsim3Model, String>, String> {
1528 let mut b = crate::bsim3::Bsim3Model::new(m.mtype == "nmos");
1529 for p in &m.params {
1530 if p.name == "version" {
1531 let v = match &p.value {
1534 ParamValue::Str(s) => s.clone(),
1535 ParamValue::Num(v) => format!("{v}"),
1536 ParamValue::NumList(v) => v
1537 .iter()
1538 .map(|x| format!("{x}"))
1539 .collect::<Vec<_>>()
1540 .join("."),
1541 ParamValue::Flag => return Err("parameter version needs a value".into()),
1542 };
1543 if !v.starts_with("3.3") {
1544 return Ok(Err(format!(
1545 "BSIM3 version {v} is not implemented (only 3.3.x, ngspice's default)"
1546 )));
1547 }
1548 b.set_version(&v);
1549 continue;
1550 }
1551 let v = match p.value {
1552 ParamValue::Num(v) => v,
1553 ParamValue::Flag if p.name == "nmos" || p.name == "pmos" => continue,
1554 _ => return Err(format!("parameter {} needs a numeric value", p.name)),
1555 };
1556 if p.name == "level" {
1557 continue;
1558 }
1559 if let Err(e) = b.set(&p.name, v) {
1560 notes.push(format!("model `{}`: {e} (ignored)", m.name));
1562 }
1563 }
1564 let int = |k: &str| b.get_int(k);
1565 if int("nqsmod") == Some(1) || int("acnqsmod") == Some(1) {
1566 return Ok(Err(
1567 "the BSIM3 non-quasi-static model (NQSMOD/ACNQSMOD = 1) is not implemented".into(),
1568 ));
1569 }
1570 if int("acm").is_some_and(|a| a != 0) {
1571 return Ok(Err(
1572 "BSIM3 ACM > 0 junction geometry is not implemented".into()
1573 ));
1574 }
1575 if int("capmod").is_some_and(|c| !(0..=3).contains(&c)) {
1576 return Ok(Err("BSIM3 CAPMOD must be 0, 1, 2 or 3".into()));
1577 }
1578 Ok(Ok(b))
1579}
1580
1581#[allow(clippy::too_many_lines)]
1585fn bsim2_model(m: &ir::Model, notes: &mut Vec<String>) -> Result<crate::bsim2::Bsim2Model, String> {
1586 let mut b = crate::bsim2::Bsim2Model::new(m.mtype == "nmos");
1587 for p in &m.params {
1588 let v = match p.value {
1589 ParamValue::Num(v) => v,
1590 ParamValue::Flag if p.name == "nmos" || p.name == "pmos" => {
1591 b.nmos = p.name == "nmos";
1592 continue;
1593 }
1594 _ => return Err(format!("parameter {} needs a numeric value", p.name)),
1595 };
1596 match p.name.as_str() {
1597 "level" => {}
1598 "vfb" => b.vfb = v,
1599 "lvfb" => b.lvfb = v,
1600 "wvfb" => b.wvfb = v,
1601 "phi" => b.phi = v,
1602 "lphi" => b.lphi = v,
1603 "wphi" => b.wphi = v,
1604 "k1" => b.k1 = v,
1605 "lk1" => b.lk1 = v,
1606 "wk1" => b.wk1 = v,
1607 "k2" => b.k2 = v,
1608 "lk2" => b.lk2 = v,
1609 "wk2" => b.wk2 = v,
1610 "eta0" => b.eta0 = v,
1611 "leta0" => b.leta0 = v,
1612 "weta0" => b.weta0 = v,
1613 "etab" => b.etab = v,
1614 "letab" => b.letab = v,
1615 "wetab" => b.wetab = v,
1616 "dl" => b.dl = v,
1617 "dw" => b.dw = v,
1618 "mu0" => b.mu0 = v,
1619 "mu0b" => b.mu0b = v,
1620 "lmu0b" => b.lmu0b = v,
1621 "wmu0b" => b.wmu0b = v,
1622 "mus0" => b.mus0 = v,
1623 "lmus0" => b.lmus0 = v,
1624 "wmus0" => b.wmus0 = v,
1625 "musb" => b.musb = v,
1626 "lmusb" => b.lmusb = v,
1627 "wmusb" => b.wmusb = v,
1628 "mu20" => b.mu20 = v,
1629 "lmu20" => b.lmu20 = v,
1630 "wmu20" => b.wmu20 = v,
1631 "mu2b" => b.mu2b = v,
1632 "lmu2b" => b.lmu2b = v,
1633 "wmu2b" => b.wmu2b = v,
1634 "mu2g" => b.mu2g = v,
1635 "lmu2g" => b.lmu2g = v,
1636 "wmu2g" => b.wmu2g = v,
1637 "mu30" => b.mu30 = v,
1638 "lmu30" => b.lmu30 = v,
1639 "wmu30" => b.wmu30 = v,
1640 "mu3b" => b.mu3b = v,
1641 "lmu3b" => b.lmu3b = v,
1642 "wmu3b" => b.wmu3b = v,
1643 "mu3g" => b.mu3g = v,
1644 "lmu3g" => b.lmu3g = v,
1645 "wmu3g" => b.wmu3g = v,
1646 "mu40" => b.mu40 = v,
1647 "lmu40" => b.lmu40 = v,
1648 "wmu40" => b.wmu40 = v,
1649 "mu4b" => b.mu4b = v,
1650 "lmu4b" => b.lmu4b = v,
1651 "wmu4b" => b.wmu4b = v,
1652 "mu4g" => b.mu4g = v,
1653 "lmu4g" => b.lmu4g = v,
1654 "wmu4g" => b.wmu4g = v,
1655 "ua0" => b.ua0 = v,
1656 "lua0" => b.lua0 = v,
1657 "wua0" => b.wua0 = v,
1658 "uab" => b.uab = v,
1659 "luab" => b.luab = v,
1660 "wuab" => b.wuab = v,
1661 "ub0" => b.ub0 = v,
1662 "lub0" => b.lub0 = v,
1663 "wub0" => b.wub0 = v,
1664 "ubb" => b.ubb = v,
1665 "lubb" => b.lubb = v,
1666 "wubb" => b.wubb = v,
1667 "u10" => b.u10 = v,
1668 "lu10" => b.lu10 = v,
1669 "wu10" => b.wu10 = v,
1670 "u1b" => b.u1b = v,
1671 "lu1b" => b.lu1b = v,
1672 "wu1b" => b.wu1b = v,
1673 "u1d" => b.u1d = v,
1674 "lu1d" => b.lu1d = v,
1675 "wu1d" => b.wu1d = v,
1676 "n0" => b.n0 = v,
1677 "ln0" => b.ln0 = v,
1678 "wn0" => b.wn0 = v,
1679 "nb" => b.nb = v,
1680 "lnb" => b.lnb = v,
1681 "wnb" => b.wnb = v,
1682 "nd" => b.nd = v,
1683 "lnd" => b.lnd = v,
1684 "wnd" => b.wnd = v,
1685 "vof0" => b.vof0 = v,
1686 "lvof0" => b.lvof0 = v,
1687 "wvof0" => b.wvof0 = v,
1688 "vofb" => b.vofb = v,
1689 "lvofb" => b.lvofb = v,
1690 "wvofb" => b.wvofb = v,
1691 "vofd" => b.vofd = v,
1692 "lvofd" => b.lvofd = v,
1693 "wvofd" => b.wvofd = v,
1694 "ai0" => b.ai0 = v,
1695 "lai0" => b.lai0 = v,
1696 "wai0" => b.wai0 = v,
1697 "aib" => b.aib = v,
1698 "laib" => b.laib = v,
1699 "waib" => b.waib = v,
1700 "bi0" => b.bi0 = v,
1701 "lbi0" => b.lbi0 = v,
1702 "wbi0" => b.wbi0 = v,
1703 "bib" => b.bib = v,
1704 "lbib" => b.lbib = v,
1705 "wbib" => b.wbib = v,
1706 "vghigh" => b.vghigh = v,
1707 "lvghigh" => b.lvghigh = v,
1708 "wvghigh" => b.wvghigh = v,
1709 "vglow" => b.vglow = v,
1710 "lvglow" => b.lvglow = v,
1711 "wvglow" => b.wvglow = v,
1712 "tox" => b.tox = v,
1713 "temp" => b.temp = v,
1714 "vdd" => b.vdd = v,
1715 "vgg" => b.vgg = v,
1716 "vbb" => b.vbb = v,
1717 "cgso" => b.cgso = v,
1718 "cgdo" => b.cgdo = v,
1719 "cgbo" => b.cgbo = v,
1720 "xpart" => b.xpart = v,
1721 "rsh" => b.rsh = v,
1722 "js" => b.js = v,
1723 "pb" => b.pb = v,
1724 "mj" => b.mj = v,
1725 "pbsw" => b.pbsw = v,
1726 "mjsw" => b.mjsw = v,
1727 "cj" => b.cj = v,
1728 "cjsw" => b.cjsw = v,
1729 "wdf" => b.wdf = v,
1730 "dell" => b.dell = v,
1731 "kf" => b.kf = v,
1733 "af" => b.af = v,
1734 other => notes.push(format!(
1735 "model `{}`: unrecognized parameter ({other}) - ignored",
1736 m.name
1737 )),
1738 }
1739 }
1740 Ok(b)
1741}
1742
1743fn add(ckt: &mut Circuit, spec: impl Into<crate::circuit::Spec>, problems: &mut Vec<String>) {
1744 if let Err(e) = ckt.add(spec) {
1745 problems.push(e.to_string());
1746 }
1747}
1748
1749fn add_ic(
1752 ckt: &mut Circuit,
1753 spec: impl Into<crate::circuit::Spec>,
1754 given: IcGiven,
1755 problems: &mut Vec<String>,
1756) {
1757 let spec = spec.into();
1758 let name = spec.name().to_string();
1759 match ckt.add(spec) {
1760 Ok(()) => ckt.set_ic_given(&name, given),
1761 Err(e) => problems.push(e.to_string()),
1762 }
1763}
1764
1765fn num_param(params: &[ir::Param], name: &str) -> Option<f64> {
1766 params
1767 .iter()
1768 .find_map(|p| match (&p.value, p.name == name) {
1769 (ParamValue::Num(v), true) => Some(*v),
1770 _ => None,
1771 })
1772}
1773
1774#[derive(Clone, Copy, Debug)]
1777struct ResModel {
1778 rsh: f64,
1779 defw: f64,
1780 narrow: f64,
1781 tc1: f64,
1782 tc2: f64,
1783 tnom: Option<f64>,
1784}
1785
1786const RES_MODEL_NGSPICE: &[&str] = &[
1789 "short", "dlr", "tce", "kf", "af", "lf", "wf", "ef", "res", "defl", "bv_max",
1790];
1791
1792fn res_model(m: &ir::Model, notes: &mut Vec<String>) -> Result<ResModel, String> {
1793 let mut rm = ResModel {
1796 rsh: 0.0,
1797 defw: 10e-6,
1798 narrow: 0.0,
1799 tc1: 0.0,
1800 tc2: 0.0,
1801 tnom: None,
1802 };
1803 for p in &m.params {
1804 let v = match p.value {
1805 ParamValue::Num(v) => v,
1806 ParamValue::Flag if p.name == "r" => continue,
1807 _ => return Err(format!("parameter {} needs a numeric value", p.name)),
1808 };
1809 match p.name.as_str() {
1810 "rsh" => rm.rsh = v,
1811 "defw" => rm.defw = v,
1812 "narrow" => rm.narrow = v,
1813 "tc1" => rm.tc1 = v,
1814 "tc2" => rm.tc2 = v,
1815 "tnom" => rm.tnom = Some(v + CTOK),
1816 other if RES_MODEL_NGSPICE.contains(&other) => notes.push(format!(
1817 "model `{}`: {other} is an ngspice resistor parameter, not in the 3f5 - ignored",
1818 m.name
1819 )),
1820 other => notes.push(format!(
1821 "model `{}`: unrecognized parameter ({other}) - ignored",
1822 m.name
1823 )),
1824 }
1825 }
1826 Ok(rm)
1827}
1828
1829fn res_instance(
1841 name: &str,
1842 value: Option<f64>,
1843 rm: Option<&ResModel>,
1844 params: &[ir::Param],
1845 notes: &mut Vec<String>,
1846) -> Result<(f64, Option<crate::circuit::ResTemp>), String> {
1847 let mut value = value;
1848 let mut l = None;
1849 let mut w = None;
1850 let mut temp = None;
1851 let mut dtemp = None;
1852 for p in params {
1853 let v = match p.value {
1854 ParamValue::Num(v) => v,
1855 _ => {
1856 notes.push(format!("`{name}`: resistor parameter {} ignored", p.name));
1857 continue;
1858 }
1859 };
1860 match p.name.as_str() {
1861 "resistance" => value = Some(v),
1862 "l" => l = Some(v),
1863 "w" => w = Some(v),
1864 "temp" => temp = Some(v + CTOK),
1865 "dtemp" => dtemp = Some(v),
1867 other => notes.push(format!(
1868 "`{name}`: resistor parameter {other} is not in the 3f5 - ignored"
1869 )),
1870 }
1871 }
1872 if rm.is_none() && (l.is_some() || w.is_some()) {
1873 notes.push(format!("`{name}`: L/W without a resistor model ignored"));
1874 }
1875 let r = match (value, rm) {
1876 (Some(v), _) => v,
1877 (None, None) => return Err(format!("`{name}`: resistor has no value")),
1878 (None, Some(m)) => {
1879 let l = l.unwrap_or(0.0);
1880 let w = w.unwrap_or(m.defw);
1881 if m.rsh != 0.0 && l != 0.0 {
1882 m.rsh * (l - m.narrow) / (w - m.narrow)
1883 } else {
1884 notes.push(format!("`{name}`: resistance=0, set to 1000"));
1885 1000.0
1886 }
1887 }
1888 };
1889 if temp.is_some() && dtemp.is_some() {
1892 notes.push(format!(
1893 "`{name}`: Instance temperature specified, dtemp ignored"
1894 ));
1895 dtemp = None;
1896 }
1897 let rt = (rm.is_some() || temp.is_some() || dtemp.is_some()).then(|| crate::circuit::ResTemp {
1898 tc1: rm.map_or(0.0, |m| m.tc1),
1899 tc2: rm.map_or(0.0, |m| m.tc2),
1900 tnom: rm.and_then(|m| m.tnom),
1901 temp,
1902 dtemp: dtemp.unwrap_or(0.0),
1903 });
1904 Ok((r, rt))
1905}
1906
1907#[derive(Clone, Copy, Debug)]
1910struct CapModel {
1911 cj: f64,
1912 cjsw: f64,
1913 defw: f64,
1914 narrow: f64,
1915}
1916
1917const CAP_MODEL_NGSPICE: &[&str] = &[
1919 "cap", "short", "del", "tc1", "tc2", "tnom", "di", "thick", "defl", "bv_max",
1920];
1921
1922fn cap_model(m: &ir::Model, notes: &mut Vec<String>) -> Result<CapModel, String> {
1923 let mut cm = CapModel {
1926 cj: 0.0,
1927 cjsw: 0.0,
1928 defw: 10e-6,
1929 narrow: 0.0,
1930 };
1931 for p in &m.params {
1932 let v = match p.value {
1933 ParamValue::Num(v) => v,
1934 ParamValue::Flag if p.name == "c" => continue,
1935 _ => return Err(format!("parameter {} needs a numeric value", p.name)),
1936 };
1937 match p.name.as_str() {
1938 "cj" => cm.cj = v,
1939 "cjsw" => cm.cjsw = v,
1940 "defw" => cm.defw = v,
1941 "narrow" => cm.narrow = v,
1942 other if CAP_MODEL_NGSPICE.contains(&other) => notes.push(format!(
1943 "model `{}`: {other} is an ngspice capacitor parameter, not in the 3f5 - ignored",
1944 m.name
1945 )),
1946 other => notes.push(format!(
1947 "model `{}`: unrecognized parameter ({other}) - ignored",
1948 m.name
1949 )),
1950 }
1951 }
1952 Ok(cm)
1953}
1954
1955fn cap_instance(
1960 name: &str,
1961 value: Option<f64>,
1962 cm: Option<&CapModel>,
1963 params: &[ir::Param],
1964 notes: &mut Vec<String>,
1965) -> Result<f64, String> {
1966 let mut value = value;
1967 let mut l = None;
1968 let mut w = None;
1969 for p in params {
1970 let v = match p.value {
1971 ParamValue::Num(v) => v,
1972 _ => {
1973 notes.push(format!("`{name}`: capacitor parameter {} ignored", p.name));
1974 continue;
1975 }
1976 };
1977 match p.name.as_str() {
1978 "capacitance" => value = Some(v),
1979 "ic" => {}
1980 "l" => l = Some(v),
1981 "w" => w = Some(v),
1982 other => notes.push(format!(
1983 "`{name}`: capacitor parameter {other} is not in the 3f5 - ignored"
1984 )),
1985 }
1986 }
1987 if cm.is_none() && (l.is_some() || w.is_some()) {
1988 notes.push(format!("`{name}`: L/W without a capacitor model ignored"));
1989 }
1990 match (value, cm) {
1991 (Some(v), _) => Ok(v),
1992 (None, None) => Err(format!("`{name}`: capacitor has no value")),
1993 (None, Some(m)) => {
1994 let l = l.unwrap_or(0.0) - m.narrow;
1995 let w = w.unwrap_or(m.defw) - m.narrow;
1996 Ok(m.cj * w * l + m.cjsw * 2.0 * (l + w))
1997 }
1998 }
1999}
2000
2001fn two(nodes: &[String]) -> [String; 2] {
2002 [nodes[0].clone(), nodes[1].clone()]
2003}
2004
2005fn three(nodes: &[String]) -> [String; 3] {
2006 [nodes[0].clone(), nodes[1].clone(), nodes[2].clone()]
2007}
2008
2009fn four(nodes: &[String]) -> [String; 4] {
2010 [
2011 nodes[0].clone(),
2012 nodes[1].clone(),
2013 nodes[2].clone(),
2014 nodes[3].clone(),
2015 ]
2016}
2017
2018fn device_kind_name(d: &ir::Device) -> &'static str {
2019 match d {
2020 ir::Device::Bjt { .. } => "the BJT",
2021 ir::Device::Mosfet { .. } => "the MOSFET",
2022 ir::Device::Jfet { .. } => "the JFET",
2023 ir::Device::Mesfet { .. } => "the MESFET",
2024 ir::Device::SwitchV { .. } | ir::Device::SwitchI { .. } => "the switch",
2025 ir::Device::TlineIdeal { .. } | ir::Device::TlineLossy { .. } => "the transmission line",
2026 ir::Device::Urc { .. } => "the URC line",
2027 ir::Device::Behavioral { .. } => "the B source",
2028 ir::Device::SubcktCall { .. } => "an unexpanded subcircuit call",
2029 _ => "this device",
2030 }
2031}
2032
2033fn device_phase(d: &ir::Device) -> &'static str {
2034 match d {
2035 ir::Device::SubcktCall { .. } => "1 — the definition is missing",
2036 _ => "3",
2037 }
2038}
2039
2040fn diode_model(m: &ir::Model, notes: &mut Vec<String>) -> Result<DiodeModel, String> {
2043 let mut dm = DiodeModel::default();
2044 for p in &m.params {
2045 let v = match p.value {
2046 ParamValue::Num(v) => v,
2047 _ => return Err(format!("parameter {} needs a numeric value", p.name)),
2048 };
2049 match p.name.as_str() {
2050 "is" => dm.is = v,
2051 "rs" => dm.rs = v,
2052 "n" => dm.n = v,
2053 "tt" => dm.tt = v,
2054 "cjo" | "cj0" => dm.cjo = v,
2055 "vj" => dm.vj = v,
2056 "m" => dm.m = v,
2057 "eg" => dm.eg = v,
2058 "xti" => dm.xti = v,
2059 "kf" => dm.kf = v,
2060 "af" => dm.af = v,
2061 "fc" => dm.fc = v,
2062 "bv" => dm.bv = Some(v),
2063 "ibv" => dm.ibv = v,
2064 "tnom" => dm.tnom = Some(v + CTOK),
2065 other => notes.push(format!(
2066 "model `{}`: unrecognized parameter ({other}) - ignored",
2067 m.name
2068 )),
2069 }
2070 }
2071 Ok(dm)
2072}
2073
2074fn jfet_model(m: &ir::Model, notes: &mut Vec<String>) -> Result<crate::jfet::JfetModel, String> {
2079 let mut jm = crate::jfet::JfetModel::new(m.mtype == "njf");
2080 for p in &m.params {
2081 let v = match p.value {
2082 ParamValue::Num(v) => v,
2083 ParamValue::Flag if p.name == "njf" || p.name == "pjf" => {
2084 jm.njf = p.name == "njf";
2085 continue;
2086 }
2087 _ => return Err(format!("parameter {} needs a numeric value", p.name)),
2088 };
2089 match p.name.as_str() {
2090 "level" => {}
2091 "vt0" | "vto" => jm.vto = v,
2092 "beta" => jm.beta = v,
2093 "lambda" => jm.lambda = v,
2094 "rd" => jm.rd = v,
2095 "rs" => jm.rs = v,
2096 "cgs" => jm.cgs = v,
2097 "cgd" => jm.cgd = v,
2098 "pb" => jm.pb = v,
2099 "is" => jm.is = v,
2100 "fc" => jm.fc = v,
2101 "b" => jm.b = v,
2102 "tnom" => jm.tnom = Some(v + CTOK),
2103 "kf" => jm.kf = v,
2104 "af" => jm.af = v,
2105 "nlev" => jm.nlev = (0.5 + v).floor() as i64,
2107 "gdsnoi" => jm.gdsnoi = v,
2108 other => notes.push(format!(
2109 "model `{}`: unrecognized parameter ({other}) - ignored",
2110 m.name
2111 )),
2112 }
2113 }
2114 Ok(jm)
2115}
2116
2117fn switch_resistance(name: &str, v: f64) -> Result<f64, String> {
2120 if v == 0.0 || !v.is_finite() {
2121 return Err(format!("{name} must be a nonzero finite resistance"));
2122 }
2123 Ok(v)
2124}
2125
2126fn sw_model(m: &ir::Model, notes: &mut Vec<String>) -> Result<crate::switch::SwModel, String> {
2130 let mut sm = crate::switch::SwModel::default();
2131 for p in &m.params {
2132 let v = match p.value {
2133 ParamValue::Num(v) => v,
2134 ParamValue::Flag if p.name == "sw" => continue,
2135 _ => return Err(format!("parameter {} needs a numeric value", p.name)),
2136 };
2137 match p.name.as_str() {
2138 "vt" => sm.vt = v,
2139 "vh" => sm.vh = v,
2140 "ron" => sm.ron = Some(switch_resistance("ron", v)?),
2141 "roff" => sm.roff = Some(switch_resistance("roff", v)?),
2142 other => notes.push(format!(
2143 "model `{}`: unrecognized parameter ({other}) - ignored",
2144 m.name
2145 )),
2146 }
2147 }
2148 Ok(sm)
2149}
2150
2151fn csw_model(m: &ir::Model, notes: &mut Vec<String>) -> Result<crate::switch::CswModel, String> {
2155 let mut wm = crate::switch::CswModel::default();
2156 for p in &m.params {
2157 let v = match p.value {
2158 ParamValue::Num(v) => v,
2159 ParamValue::Flag if p.name == "csw" => continue,
2160 _ => return Err(format!("parameter {} needs a numeric value", p.name)),
2161 };
2162 match p.name.as_str() {
2163 "it" => wm.it = v,
2164 "ih" => wm.ih = v,
2165 "ron" => wm.ron = Some(switch_resistance("ron", v)?),
2166 "roff" => wm.roff = Some(switch_resistance("roff", v)?),
2167 other => notes.push(format!(
2168 "model `{}`: unrecognized parameter ({other}) - ignored",
2169 m.name
2170 )),
2171 }
2172 }
2173 Ok(wm)
2174}
2175
2176fn mes_model(m: &ir::Model, notes: &mut Vec<String>) -> Result<crate::mes::MesModel, String> {
2180 let mut zm = crate::mes::MesModel::new(m.mtype == "nmf");
2181 for p in &m.params {
2182 let v = match p.value {
2183 ParamValue::Num(v) => v,
2184 ParamValue::Flag if p.name == "nmf" || p.name == "pmf" => {
2185 zm.nmf = p.name == "nmf";
2186 continue;
2187 }
2188 _ => return Err(format!("parameter {} needs a numeric value", p.name)),
2189 };
2190 match p.name.as_str() {
2191 "level" => {}
2192 "vt0" | "vto" => zm.vto = v,
2193 "alpha" => zm.alpha = v,
2194 "beta" => zm.beta = v,
2195 "lambda" => zm.lambda = v,
2196 "b" => zm.b = v,
2197 "rd" => zm.rd = v,
2198 "rs" => zm.rs = v,
2199 "cgs" => zm.cgs = v,
2200 "cgd" => zm.cgd = v,
2201 "pb" => zm.pb = v,
2202 "is" => zm.is = v,
2203 "fc" => zm.fc = v,
2204 "kf" => zm.kf = v,
2205 "af" => zm.af = v,
2206 other => notes.push(format!(
2207 "model `{}`: unrecognized parameter ({other}) - ignored",
2208 m.name
2209 )),
2210 }
2211 }
2212 Ok(zm)
2213}
2214
2215#[derive(Clone, Debug)]
2218struct UrcModel {
2219 k: f64,
2222 fmax: f64,
2224 rperl: f64,
2226 cperl: f64,
2228 isperl: Option<f64>,
2231 rsperl: f64,
2233}
2234
2235fn urc_model(m: &ir::Model, notes: &mut Vec<String>) -> Result<UrcModel, String> {
2236 let mut um = UrcModel {
2237 k: 1.5,
2238 fmax: 1e9,
2239 rperl: 1000.0,
2240 cperl: 1e-12,
2241 isperl: None,
2242 rsperl: 0.0,
2243 };
2244 for p in &m.params {
2245 if p.name == "urc" && p.value == ParamValue::Flag {
2248 continue;
2249 }
2250 let v = match p.value {
2251 ParamValue::Num(v) => v,
2252 _ => return Err(format!("parameter {} needs a numeric value", p.name)),
2253 };
2254 match p.name.as_str() {
2255 "k" => um.k = v,
2256 "fmax" => um.fmax = v,
2257 "rperl" => um.rperl = v,
2258 "cperl" => um.cperl = v,
2259 "isperl" => um.isperl = Some(v),
2260 "rsperl" => um.rsperl = v,
2261 other => notes.push(format!(
2262 "model `{}`: unrecognized parameter ({other}) - ignored",
2263 m.name
2264 )),
2265 }
2266 }
2267 Ok(um)
2268}
2269
2270const URC_MAX_LUMPS: i64 = 10_000;
2274
2275fn urc_expand(
2286 name: &str,
2287 nodes: &[String],
2288 m: &UrcModel,
2289 params: &[ir::Param],
2290) -> Result<Vec<crate::circuit::Spec>, String> {
2291 use crate::circuit::{Capacitor, Diode, Resistor, Spec};
2292 let mut len = None;
2293 let mut lumps = None;
2294 for p in params {
2295 match (p.name.as_str(), &p.value) {
2296 ("l", ParamValue::Num(v)) => len = Some(*v),
2297 ("n", ParamValue::Num(v)) => lumps = Some(v.trunc()),
2299 _ => return Err(format!("`{name}`: unknown parameter ({})", p.name)),
2300 }
2301 }
2302 let Some(len) = len.filter(|l| *l > 0.0 && l.is_finite()) else {
2303 return Err(format!("`{name}`: URC line needs a length L > 0"));
2304 };
2305 let p = m.k;
2306 if p.is_nan() || p <= 1.0 || !p.is_finite() {
2307 return Err(format!(
2308 "`{name}`: URC model K = {p} must exceed 1 (the lump ratio)"
2309 ));
2310 }
2311 let r0 = len * m.rperl;
2312 let c0 = len * m.cperl;
2313 let i0 = len * m.isperl.unwrap_or(0.0);
2314 let n = match lumps {
2315 Some(n) => n,
2316 None => {
2317 let wnorm = m.fmax * r0 * c0 * 2.0 * std::f64::consts::PI;
2318 let est = (wnorm * (((p - 1.0) / p) * ((p - 1.0) / p))).ln() / p.ln();
2319 let est = if 3.0 > est { 3.0 } else { est };
2322 if wnorm < 35.0 { 3.0 } else { est.trunc() }
2323 }
2324 };
2325 if n.is_nan() || n < 1.0 || n > URC_MAX_LUMPS as f64 {
2326 return Err(format!(
2327 "`{name}`: URC line with {n} lumps (must be 1..={URC_MAX_LUMPS})"
2328 ));
2329 }
2330 let n = n as i64;
2331 let r1 = (r0 * (p - 1.0)) / ((2.0 * p.powf(n as f64)) - 2.0);
2332 let c1 = (c0 * (p - 1.0)) / (p.powf((n - 1) as f64) * (p + 1.0) - 2.0);
2333 let i1 = (i0 * (p - 1.0)) / (p.powf((n - 1) as f64) * (p + 1.0) - 2.0);
2334 let rd = len * n as f64 * m.rsperl;
2335 let diode_model = m.isperl.map(|_| DiodeModel {
2336 cjo: c1,
2337 rs: rd,
2338 is: i1,
2339 ..DiodeModel::default()
2340 });
2341
2342 let gnd = nodes[2].clone();
2343 let mut lowl = nodes[0].clone();
2344 let mut hir = nodes[1].clone();
2345 let mut prop = 1.0;
2346 let mut out: Vec<Spec> = Vec::new();
2347 for i in 1..=n {
2348 let hil = format!("{name}#hi{i}");
2349 let lowr = if i == n {
2350 hil.clone()
2351 } else {
2352 format!("{name}#lo{i}")
2353 };
2354 let r = prop * r1;
2355 let c = prop * c1;
2356 out.push(
2357 Resistor {
2358 name: format!("{name}#rlo{i}"),
2359 nodes: [lowl.clone(), lowr.clone()],
2360 value: r,
2361 }
2362 .into(),
2363 );
2364 out.push(
2365 Resistor {
2366 name: format!("{name}#rhi{i}"),
2367 nodes: [hil.clone(), hir.clone()],
2368 value: r,
2369 }
2370 .into(),
2371 );
2372 let shunt = |tag: &str, node: &str| -> Spec {
2373 match &diode_model {
2374 Some(dm) => Diode {
2375 name: format!("{name}#d{tag}{i}"),
2376 nodes: [node.to_string(), gnd.clone()],
2377 model: dm.clone(),
2378 area: prop,
2379 off: false,
2380 temp: None,
2381 ic: None,
2382 }
2383 .into(),
2384 None => Capacitor {
2385 name: format!("{name}#c{tag}{i}"),
2386 nodes: [node.to_string(), gnd.clone()],
2387 value: c,
2388 ic: None,
2389 }
2390 .into(),
2391 }
2392 };
2393 out.push(shunt("lo", &lowr));
2394 if i != n {
2395 out.push(shunt("hi", &hil));
2396 }
2397 prop *= p;
2398 lowl = lowr;
2399 hir = hil;
2400 }
2401 Ok(out)
2402}