1#![warn(missing_docs)]
3
4use std::collections::HashMap;
5use std::fmt::{Display, Formatter};
6use std::io::Write;
7#[cfg(any(unix, target_os = "redox"))]
8use std::os::unix::prelude::PermissionsExt;
9use std::path::{Path, PathBuf};
10use std::process::Stdio;
11use std::sync::Arc;
12
13use crate::analysis::ac::Ac;
14use crate::analysis::montecarlo;
15use crate::analysis::montecarlo::MonteCarlo;
16
17use analysis::dc::DcOp;
18use analysis::tran;
19use analysis::tran::Tran;
20use analysis::{Sweep, ac, dc};
21use arcstr::ArcStr;
22use cache::CacheableWithState;
23use cache::error::TryInnerError;
24use error::*;
25use indexmap::{IndexMap, IndexSet};
26use itertools::Itertools;
27use lazy_static::lazy_static;
28use num::complex::Complex64;
29use psfparser::analysis::ac::AcData;
30use psfparser::analysis::dc::DcData;
31use psfparser::analysis::transient::TransientData;
32use regex::Regex;
33use rust_decimal::Decimal;
34use scir::netlist::ConvertibleNetlister;
35use scir::schema::{FromSchema, NoSchema, NoSchemaError};
36use scir::{
37 Library, NamedSliceOne, NetlistLibConversion, ParamValue, SignalInfo, Slice, SliceOnePath,
38};
39use serde::{Deserialize, Serialize};
40use spice::netlist::{
41 HasSpiceLikeNetlist, Include, NetlistKind, NetlistOptions, NetlisterInstance, RenameGround,
42};
43use spice::{BlackboxContents, BlackboxElement, Spice};
44use substrate::context::Installation;
45use substrate::execute::{ExecOpts, Executor, LogOutput};
46use substrate::schematic::conv::ConvertedNodePath;
47use substrate::schematic::schema::Schema;
48use substrate::simulation::options::ic::InitialCondition;
49use substrate::simulation::options::{SimOption, Temperature, ic};
50use substrate::simulation::{SimulationContext, Simulator, SupportedBy};
51use substrate::types::schematic::NodePath;
52use templates::{RunScriptContext, write_run_script};
53use type_dispatch::impl_dispatch;
54
55pub mod analysis;
56pub mod blocks;
57pub mod error;
58pub(crate) mod templates;
59#[cfg(test)]
60mod tests;
61
62#[derive(Debug, Clone)]
64pub enum Primitive {
65 RawInstance {
67 cell: ArcStr,
69 ports: Vec<ArcStr>,
71 params: Vec<(ArcStr, ParamValue)>,
73 },
74 IbisInstance {
78 component: ArcStr,
80 model: PathBuf,
82 ports: Vec<ArcStr>,
84 },
85 SpfInstance {
89 cell: ArcStr,
91 netlist: PathBuf,
93 ports: Vec<ArcStr>,
95 },
96 BlackboxInstance {
98 contents: BlackboxContents,
100 },
101 Spice(spice::Primitive),
105}
106
107#[derive(
109 Copy, Clone, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize,
110)]
111pub enum ErrPreset {
112 Liberal,
114 #[default]
116 Moderate,
117 Conservative,
119}
120
121impl Display for ErrPreset {
122 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
123 match *self {
124 Self::Liberal => write!(f, "liberal"),
125 Self::Moderate => write!(f, "moderate"),
126 Self::Conservative => write!(f, "conservative"),
127 }
128 }
129}
130
131#[derive(Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
133pub enum SimSignal {
134 Raw(ArcStr),
136 ScirVoltage(SliceOnePath),
138 ScirCurrent(SliceOnePath),
140
141 InstanceTail(InstanceTail),
143}
144
145#[derive(Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
147pub struct InstanceTail {
148 pub instance: scir::InstancePath,
150 pub tail: ArcStr,
152}
153
154impl<T: Into<ArcStr>> From<T> for SimSignal {
155 fn from(value: T) -> Self {
156 Self::Raw(value.into())
157 }
158}
159
160impl From<InstanceTail> for SimSignal {
161 fn from(value: InstanceTail) -> Self {
162 Self::InstanceTail(value)
163 }
164}
165
166impl SimSignal {
167 pub fn new(path: impl Into<ArcStr>) -> Self {
169 Self::from(path)
170 }
171
172 pub(crate) fn to_string(&self, lib: &Library<Spectre>, conv: &NetlistLibConversion) -> ArcStr {
173 match self {
174 SimSignal::Raw(raw) => raw.clone(),
175 SimSignal::ScirCurrent(scir) => {
176 ArcStr::from(Spectre::node_current_path(lib, conv, scir))
177 }
178 SimSignal::ScirVoltage(scir) => {
179 ArcStr::from(Spectre::node_voltage_path(lib, conv, scir))
180 }
181 SimSignal::InstanceTail(itail) => {
182 let ipath = Spectre::instance_path(lib, conv, &itail.instance);
183 arcstr::format!("{}.{}", ipath, itail.tail)
184 }
185 }
186 }
187}
188
189#[derive(Debug, Clone, Default)]
191pub struct Spectre {}
192
193#[derive(Debug, Clone, Default)]
197pub struct Options {
198 includes: IndexSet<Include>,
199 saves: IndexMap<SimSignal, u64>,
200 ics: IndexMap<SimSignal, Decimal>,
201 next_save_key: u64,
202 temp: Option<Decimal>,
204 save: Option<SaveOption>,
205 override_flags: Option<String>,
207}
208
209#[derive(Copy, Clone, Debug, Default)]
211pub enum SaveOption {
212 All,
214 Lvl,
216 AllPub,
220 LvlPub,
224 #[default]
228 Selected,
229 None,
231}
232
233impl SaveOption {
234 fn as_str(&self) -> &'static str {
236 match *self {
237 SaveOption::All => "all",
238 SaveOption::Lvl => "lvl",
239 SaveOption::AllPub => "allpub",
240 SaveOption::LvlPub => "lvlpub",
241 SaveOption::Selected => "selected",
242 SaveOption::None => "none",
243 }
244 }
245}
246
247impl Display for SaveOption {
248 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
249 f.write_str(self.as_str())
250 }
251}
252
253impl Options {
254 pub fn include(&mut self, path: impl Into<PathBuf>) {
256 self.includes.insert(Include::new(path));
257 }
258 pub fn include_section(&mut self, path: impl Into<PathBuf>, section: impl Into<ArcStr>) {
260 self.includes.insert(Include::new(path).section(section));
261 }
262
263 fn save_inner(&mut self, save: impl Into<SimSignal>) -> u64 {
264 let save = save.into();
265
266 if let Some(key) = self.saves.get(&save) {
267 *key
268 } else {
269 let save_key = self.next_save_key;
270 self.next_save_key += 1;
271 self.saves.insert(save, save_key);
272 save_key
273 }
274 }
275
276 fn set_ic_inner(&mut self, key: impl Into<SimSignal>, value: Decimal) {
277 self.ics.insert(key.into(), value);
278 }
279
280 pub fn save_tran_voltage(&mut self, save: impl Into<SimSignal>) -> tran::VoltageSaveKey {
282 tran::VoltageSaveKey(self.save_inner(save))
283 }
284
285 pub fn save_tran_current(&mut self, save: impl Into<SimSignal>) -> tran::CurrentSaveKey {
287 tran::CurrentSaveKey(vec![self.save_inner(save)])
288 }
289
290 pub fn save_ac_voltage(&mut self, save: impl Into<SimSignal>) -> ac::VoltageSaveKey {
292 ac::VoltageSaveKey(self.save_inner(save))
293 }
294
295 pub fn save_ac_current(&mut self, save: impl Into<SimSignal>) -> ac::CurrentSaveKey {
297 ac::CurrentSaveKey(vec![self.save_inner(save)])
298 }
299
300 pub fn save_dc_voltage(&mut self, save: impl Into<SimSignal>) -> dc::VoltageSaveKey {
302 dc::VoltageSaveKey(self.save_inner(save))
303 }
304
305 pub fn save_dc_current(&mut self, save: impl Into<SimSignal>) -> dc::CurrentSaveKey {
307 dc::CurrentSaveKey(vec![self.save_inner(save)])
308 }
309
310 pub fn set_temp(&mut self, temp: Decimal) {
312 self.temp = Some(temp);
313 }
314
315 pub fn save(&mut self, save: SaveOption) {
317 self.save = Some(save);
318 }
319
320 pub fn set_flags(&mut self, flags: impl Into<String>) {
324 self.override_flags = Some(flags.into());
325 }
326}
327
328impl SimOption<Spectre> for Temperature {
329 fn set_option(
330 self,
331 opts: &mut <Spectre as Simulator>::Options,
332 _ctx: &SimulationContext<Spectre>,
333 ) {
334 opts.set_temp(*self)
335 }
336}
337
338#[impl_dispatch({&str; &String; ArcStr; String; SimSignal})]
339impl<K> SimOption<Spectre> for InitialCondition<K, ic::Voltage> {
340 fn set_option(
341 self,
342 opts: &mut <Spectre as Simulator>::Options,
343 _ctx: &SimulationContext<Spectre>,
344 ) {
345 opts.set_ic_inner(self.path, *self.value);
346 }
347}
348
349impl SimOption<Spectre> for InitialCondition<&SliceOnePath, ic::Voltage> {
350 fn set_option(
351 self,
352 opts: &mut <Spectre as Simulator>::Options,
353 _ctx: &SimulationContext<Spectre>,
354 ) {
355 opts.set_ic_inner(SimSignal::ScirVoltage(self.path.clone()), *self.value);
356 }
357}
358
359impl SimOption<Spectre> for InitialCondition<&ConvertedNodePath, ic::Voltage> {
360 fn set_option(
361 self,
362 opts: &mut <Spectre as Simulator>::Options,
363 _ctx: &SimulationContext<Spectre>,
364 ) {
365 opts.set_ic_inner(
366 SimSignal::ScirVoltage(match self.path {
367 ConvertedNodePath::Cell(path) => path.clone(),
368 ConvertedNodePath::Primitive {
369 instances, port, ..
370 } => SliceOnePath::new(instances.clone(), NamedSliceOne::new(port.clone())),
371 }),
372 *self.value,
373 );
374 }
375}
376
377impl SimOption<Spectre> for InitialCondition<&NodePath, ic::Voltage> {
378 fn set_option(
379 self,
380 opts: &mut <Spectre as Simulator>::Options,
381 ctx: &SimulationContext<Spectre>,
382 ) {
383 InitialCondition {
384 path: ctx.lib.convert_node_path(self.path).unwrap(),
385 value: self.value,
386 }
387 .set_option(opts, ctx)
388 }
389}
390
391#[impl_dispatch({SliceOnePath; ConvertedNodePath; NodePath})]
392impl<T> SimOption<Spectre> for InitialCondition<T, ic::Voltage> {
393 fn set_option(
394 self,
395 opts: &mut <Spectre as Simulator>::Options,
396 ctx: &SimulationContext<Spectre>,
397 ) {
398 InitialCondition {
399 path: &self.path,
400 value: self.value,
401 }
402 .set_option(opts, ctx)
403 }
404}
405
406#[derive(Debug, Clone, Serialize, Deserialize, Hash, PartialEq, Eq)]
407struct CachedSim {
408 simulation_netlist: Vec<u8>,
409}
410
411struct CachedSimState {
412 input: Vec<Input>,
413 netlist: PathBuf,
414 output_path: PathBuf,
415 log: PathBuf,
416 stdout_path: PathBuf,
417 run_script: PathBuf,
418 work_dir: PathBuf,
419 executor: Arc<dyn Executor>,
420 override_flags: Option<String>,
422}
423
424#[derive(Debug, Clone, Serialize, Deserialize)]
425enum CachedData {
426 Tran(HashMap<String, Vec<f64>>),
427 Ac {
428 freq: Vec<f64>,
429 signals: HashMap<String, Vec<Complex64>>,
430 },
431 DcOp(HashMap<String, f64>),
432 MonteCarlo(Vec<Vec<CachedData>>),
435}
436
437impl CachedData {
438 fn into_output(
439 self,
440 ctx: &SimulationContext<Spectre>,
441 conv: &NetlistLibConversion,
442 saves: &IndexMap<SimSignal, u64>,
443 ) -> Output {
444 match self {
445 CachedData::Tran(mut raw_values) => tran::Output {
446 time: Arc::new(raw_values.remove("time").unwrap()),
447 raw_values: raw_values
448 .into_iter()
449 .map(|(k, v)| (ArcStr::from(k), Arc::new(v)))
450 .collect(),
451 saved_values: saves
452 .iter()
453 .map(|(k, v)| (*v, k.to_string(&ctx.lib.scir, conv)))
454 .collect(),
455 }
456 .into(),
457 CachedData::Ac { freq, signals } => ac::Output {
458 freq: Arc::new(freq),
459 raw_values: signals
460 .into_iter()
461 .map(|(k, v)| (ArcStr::from(k), Arc::new(v)))
462 .collect(),
463 saved_values: saves
464 .iter()
465 .map(|(k, v)| (*v, k.to_string(&ctx.lib.scir, conv)))
466 .collect(),
467 }
468 .into(),
469 CachedData::DcOp(values) => dc::OpOutput {
470 raw_values: values
471 .into_iter()
472 .map(|(k, v)| (ArcStr::from(k), v))
473 .collect(),
474 saved_values: saves
475 .iter()
476 .map(|(k, v)| (*v, k.to_string(&ctx.lib.scir, conv)))
477 .collect(),
478 }
479 .into(),
480 CachedData::MonteCarlo(data) => Output::MonteCarlo(montecarlo::Output(
481 data.into_iter()
482 .map(|data| {
483 data.into_iter()
484 .map(|d| d.into_output(ctx, conv, saves))
485 .collect()
486 })
487 .collect(),
488 )),
489 }
490 }
491}
492
493impl CacheableWithState<CachedSimState> for CachedSim {
494 type Output = Vec<CachedData>;
495 type Error = Arc<Error>;
496
497 fn generate_with_state(
498 &self,
499 state: CachedSimState,
500 ) -> std::result::Result<Self::Output, Self::Error> {
501 let inner = || -> Result<Self::Output> {
502 let CachedSimState {
503 input,
504 netlist,
505 output_path,
506 stdout_path,
507 log,
508 run_script,
509 work_dir,
510 executor,
511 override_flags,
512 } = state;
513 write_run_script(
514 RunScriptContext {
515 netlist: &netlist,
516 raw_output_path: &output_path,
517 log_path: &log,
518 bashrc: None,
519 format: "psfbin",
520 flags: override_flags.as_deref().unwrap_or("++aps +mt"),
521 },
522 &run_script,
523 )?;
524
525 let mut perms = std::fs::metadata(&run_script)?.permissions();
526 #[cfg(any(unix, target_os = "redox"))]
527 perms.set_mode(0o744);
528 std::fs::set_permissions(&run_script, perms)?;
529
530 let mut command = std::process::Command::new("/bin/bash");
531 command
532 .arg(&run_script)
533 .current_dir(&work_dir)
534 .stdin(Stdio::null());
535 executor
536 .execute(
537 command,
538 ExecOpts {
539 logs: LogOutput::File(stdout_path),
540 ..Default::default()
541 },
542 )
543 .map_err(|_| Error::SpectreError)?;
544
545 let mut raw_outputs = Vec::with_capacity(input.len());
546
547 for (i, input) in input.iter().enumerate() {
548 raw_outputs.push(parse_analysis(
549 &output_path,
550 &subanalysis_name("analysis", i),
551 input,
552 )?);
553 }
554 Ok(raw_outputs)
555 };
556 inner().map_err(Arc::new)
557 }
558}
559
560impl ConvertibleNetlister<Spectre> for Spectre {
561 type Error = std::io::Error;
562 type Options<'a> = NetlistOptions<'a>;
563
564 fn write_scir_netlist<W: Write>(
565 &self,
566 lib: &Library<Spectre>,
567 out: &mut W,
568 opts: Self::Options<'_>,
569 ) -> std::result::Result<NetlistLibConversion, Self::Error> {
570 NetlisterInstance::new(self, lib, out, opts).export()
571 }
572}
573
574impl Spectre {
575 fn simulate(
576 &self,
577 ctx: &SimulationContext<Self>,
578 options: Options,
579 input: Vec<Input>,
580 ) -> Result<Vec<Output>> {
581 std::fs::create_dir_all(&ctx.work_dir)?;
582 let netlist = ctx.work_dir.join("netlist.scs");
583 let mut f = std::fs::File::create(&netlist)?;
584 let mut w = Vec::new();
585
586 let includes = options.includes.into_iter().collect::<Vec<_>>();
587 let saves = options.saves.keys().cloned().collect::<Vec<_>>();
588 let ics = options
589 .ics
590 .iter()
591 .map(|(k, v)| (k.clone(), *v))
592 .collect::<Vec<_>>();
593
594 let conv = self.write_scir_netlist(
595 &ctx.lib.scir,
596 &mut w,
597 NetlistOptions::new(
598 NetlistKind::Testbench(RenameGround::Yes("0".into())),
599 &includes,
600 ),
601 )?;
602
603 writeln!(w)?;
604 if let Some(temp) = options.temp {
605 writeln!(w, "settemp1 options temp={}", temp)?;
606 }
607 for save in saves {
608 writeln!(w, "save {}", save.to_string(&ctx.lib.scir, &conv))?;
609 }
610 if let Some(save) = options.save {
611 writeln!(w, "setsave1 options save={}", save)?;
612 }
613 for (k, v) in ics {
614 writeln!(w, "ic {}={}", k.to_string(&ctx.lib.scir, &conv), v)?;
615 }
616
617 writeln!(w)?;
618 for (i, an) in input.iter().enumerate() {
619 an.netlist(&mut w, &subanalysis_name("analysis", i))?;
620 writeln!(w)?;
621 }
622 f.write_all(&w)?;
623
624 let output_path = ctx.work_dir.join("psf");
625 let log = ctx.work_dir.join("spectre.log");
626 let stdout_path = ctx.work_dir.join("spectre.out");
627 let run_script = ctx.work_dir.join("simulate.sh");
628 let work_dir = ctx.work_dir.clone();
629 let executor = ctx.ctx.executor.clone();
630
631 let raw_outputs = ctx
632 .ctx
633 .cache
634 .get_with_state(
635 "spectre.simulation.outputs",
636 CachedSim {
637 simulation_netlist: w,
638 },
639 CachedSimState {
640 input,
641 netlist,
642 output_path,
643 stdout_path,
644 log,
645 run_script,
646 work_dir,
647 executor,
648 override_flags: options.override_flags.clone(),
649 },
650 )
651 .try_inner()
652 .map_err(|e| match e {
653 TryInnerError::CacheError(e) => Error::Caching(e),
654 TryInnerError::GeneratorError(e) => Error::Generator(e.clone()),
655 })?
656 .clone();
657
658 let conv = Arc::new(conv);
659 let outputs = raw_outputs
660 .into_iter()
661 .map(|raw_values| raw_values.into_output(ctx, &conv, &options.saves))
662 .collect();
663
664 Ok(outputs)
665 }
666
667 pub fn escape_identifier(node_name: &str) -> String {
669 lazy_static! {
673 static ref RE: Regex = Regex::new("^(x*)0$").unwrap();
674 }
675 if let Some(caps) = RE.captures(node_name) {
676 let xs = caps.get(1).unwrap();
677 return format!("x{}0", xs.as_str());
678 }
679
680 let mut escaped_name = String::new();
681 for c in node_name.chars() {
682 if c.is_alphanumeric() || c == '_' {
683 escaped_name.push(c);
684 } else {
685 escaped_name.push('\\');
686 escaped_name.push(c);
687 }
688 }
689 escaped_name
690 }
691
692 pub fn instance_path(
695 lib: &Library<Spectre>,
696 conv: &NetlistLibConversion,
697 path: &scir::InstancePath,
698 ) -> String {
699 lib.convert_instance_path_with_conv(conv, path.clone())
700 .join(".")
701 }
702
703 pub fn node_voltage_path(
706 lib: &Library<Spectre>,
707 conv: &NetlistLibConversion,
708 path: &SliceOnePath,
709 ) -> String {
710 lib.convert_slice_one_path_with_conv(conv, path.clone(), |name, index| {
711 let name = Spectre::escape_identifier(name);
712 if let Some(index) = index {
713 arcstr::format!("{}\\[{}\\]", name, index)
714 } else {
715 name.into()
716 }
717 })
718 .join(".")
719 }
720
721 pub fn node_current_path(
724 lib: &Library<Spectre>,
725 conv: &NetlistLibConversion,
726 path: &SliceOnePath,
727 ) -> String {
728 let mut named_path =
729 lib.convert_slice_one_path_with_conv(conv, path.clone(), |name, index| {
730 let name = Spectre::escape_identifier(name);
731 if let Some(index) = index {
732 arcstr::format!("{}\\[{}\\]", name, index)
733 } else {
734 name.into()
735 }
736 });
737 let signal = named_path.pop().unwrap();
738 let mut str_path = named_path.join(".");
739 str_path.push(':');
740 str_path.push_str(&signal);
741 str_path
742 }
743}
744
745impl scir::schema::Schema for Spectre {
746 type Primitive = Primitive;
747}
748
749impl FromSchema<NoSchema> for Spectre {
750 type Error = NoSchemaError;
751
752 fn convert_primitive(
753 _primitive: <NoSchema as Schema>::Primitive,
754 ) -> std::result::Result<<Self as Schema>::Primitive, Self::Error> {
755 Err(NoSchemaError)
756 }
757
758 fn convert_instance(
759 _instance: &mut scir::Instance,
760 _primitive: &<NoSchema as Schema>::Primitive,
761 ) -> std::result::Result<(), Self::Error> {
762 Err(NoSchemaError)
763 }
764}
765
766#[derive(Debug, Clone, Copy)]
768pub enum SpectreConvError {
769 UnsupportedPrimitive,
771 MissingParameter,
773 ExtraParameter,
775 InvalidParameter,
777 InvalidPort,
779}
780
781impl FromSchema<Spice> for Spectre {
782 type Error = SpectreConvError;
783
784 fn convert_primitive(
785 primitive: <Spice as Schema>::Primitive,
786 ) -> std::result::Result<<Self as Schema>::Primitive, Self::Error> {
787 Ok(Primitive::Spice(primitive))
788 }
789
790 fn convert_instance(
791 _instance: &mut scir::Instance,
792 _primitive: &<Spice as Schema>::Primitive,
793 ) -> std::result::Result<(), Self::Error> {
794 Ok(())
795 }
796}
797
798impl Installation for Spectre {}
799
800impl Simulator for Spectre {
801 type Schema = Spectre;
802 type Input = Input;
803 type Options = Options;
804 type Output = Output;
805 type Error = Error;
806
807 fn simulate_inputs(
808 &self,
809 config: &substrate::simulation::SimulationContext<Self>,
810 options: Self::Options,
811 input: Vec<Self::Input>,
812 ) -> Result<Vec<Self::Output>> {
813 self.simulate(config, options, input)
814 }
815}
816
817#[derive(Debug, Clone, Serialize, Deserialize)]
819pub enum Input {
820 Tran(Tran),
822 Ac(Ac),
824 DcOp(DcOp),
826 MonteCarlo(MonteCarlo<Vec<Input>>),
828}
829
830impl From<Tran> for Input {
831 fn from(value: Tran) -> Self {
832 Self::Tran(value)
833 }
834}
835
836impl From<Ac> for Input {
837 fn from(value: Ac) -> Self {
838 Self::Ac(value)
839 }
840}
841
842impl From<DcOp> for Input {
843 fn from(value: DcOp) -> Self {
844 Self::DcOp(value)
845 }
846}
847
848impl<A: SupportedBy<Spectre>> From<MonteCarlo<A>> for Input {
849 fn from(value: MonteCarlo<A>) -> Self {
850 Self::MonteCarlo(value.into())
851 }
852}
853
854#[derive(Debug, Clone)]
856pub enum Output {
857 Tran(tran::Output),
859 Ac(ac::Output),
861 DcOp(analysis::dc::OpOutput),
863 MonteCarlo(montecarlo::Output<Vec<Output>>),
865}
866
867impl From<tran::Output> for Output {
868 fn from(value: tran::Output) -> Self {
869 Self::Tran(value)
870 }
871}
872
873impl From<ac::Output> for Output {
874 fn from(value: ac::Output) -> Self {
875 Self::Ac(value)
876 }
877}
878
879impl From<analysis::dc::OpOutput> for Output {
880 fn from(value: analysis::dc::OpOutput) -> Self {
881 Self::DcOp(value)
882 }
883}
884
885impl TryFrom<Output> for tran::Output {
886 type Error = Error;
887 fn try_from(value: Output) -> Result<Self> {
888 match value {
889 Output::Tran(t) => Ok(t),
890 _ => Err(Error::SpectreError),
891 }
892 }
893}
894
895impl TryFrom<Output> for ac::Output {
896 type Error = Error;
897 fn try_from(value: Output) -> Result<Self> {
898 match value {
899 Output::Ac(ac) => Ok(ac),
900 _ => Err(Error::SpectreError),
901 }
902 }
903}
904
905impl TryFrom<Output> for analysis::dc::OpOutput {
906 type Error = Error;
907 fn try_from(value: Output) -> Result<Self> {
908 match value {
909 Output::DcOp(dcop) => Ok(dcop),
910 _ => Err(Error::SpectreError),
911 }
912 }
913}
914
915impl From<montecarlo::Output<Vec<Output>>> for Output {
916 fn from(value: montecarlo::Output<Vec<Output>>) -> Self {
917 Self::MonteCarlo(value)
918 }
919}
920
921impl TryFrom<Output> for montecarlo::Output<Vec<Output>> {
922 type Error = Error;
923 fn try_from(value: Output) -> Result<Self> {
924 match value {
925 Output::MonteCarlo(mc) => Ok(mc),
926 _ => Err(Error::SpectreError),
927 }
928 }
929}
930
931impl Input {
932 fn netlist<W: Write>(&self, out: &mut W, name: &str) -> Result<()> {
933 write!(out, "{name} ")?;
934 match self {
935 Self::Tran(t) => t.netlist(out),
936 Input::Ac(ac) => ac.netlist(out),
937 Input::DcOp(dcop) => dcop.netlist(out),
938 Self::MonteCarlo(mc) => mc.netlist(out, name),
939 }
940 }
941}
942
943impl Tran {
944 fn netlist<W: Write>(&self, out: &mut W) -> Result<()> {
945 write!(out, "tran stop={}", self.stop)?;
946 if let Some(ref start) = self.start {
947 write!(out, " start={start}")?;
948 }
949 if let Some(errpreset) = self.errpreset {
950 write!(out, " errpreset={errpreset}")?;
951 }
952 if let Some(noisefmax) = self.noise_fmax {
953 write!(out, " noisefmax={noisefmax}")?;
954 }
955 if let Some(noisefmin) = self.noise_fmin {
956 write!(out, " noisefmin={noisefmin}")?;
957 }
958 Ok(())
959 }
960}
961
962impl Ac {
963 fn netlist<W: Write>(&self, out: &mut W) -> Result<()> {
964 write!(out, "ac start={} stop={}", self.start, self.stop)?;
965 match self.sweep {
966 Sweep::Linear(pts) => write!(out, " lin={pts}")?,
967 Sweep::Logarithmic(pts) => write!(out, " log={pts}")?,
968 Sweep::Decade(pts) => write!(out, " dec={pts}")?,
969 };
970 Ok(())
971 }
972}
973
974impl DcOp {
975 fn netlist<W: Write>(&self, out: &mut W) -> Result<()> {
976 write!(out, "dc")?;
977 Ok(())
978 }
979}
980
981fn subanalysis_name(prefix: &str, idx: usize) -> String {
982 format!("{prefix}_{idx}")
983}
984
985fn parse_analysis(output_dir: &Path, name: &str, analysis: &Input) -> Result<CachedData> {
986 Ok(if let Input::MonteCarlo(analysis) = analysis {
987 let mut data = Vec::new();
988 for iter in 1..analysis.numruns + 1 {
989 let mut mc_data = Vec::new();
990 for i in 0..analysis.analysis.len() {
991 let new_name = subanalysis_name(&format!("{}-{:0>3}_{}", name, iter, name), i);
993 mc_data.push(parse_analysis(
994 output_dir,
995 &new_name,
996 &analysis.analysis[i],
997 )?)
998 }
999 data.push(mc_data);
1000 }
1001 CachedData::MonteCarlo(data)
1002 } else {
1003 let file_name = match analysis {
1004 Input::Tran(_) => {
1005 format!("{name}.tran.tran")
1006 }
1007 Input::Ac(_) => format!("{name}.ac"),
1008 Input::DcOp(_) => format!("{name}.dc"),
1009 Input::MonteCarlo(_) => unreachable!(),
1010 };
1011 let psf_path = output_dir.join(file_name);
1012 let psf = std::fs::read(psf_path)?;
1013 let ast = psfparser::binary::parse(&psf).map_err(|_| Error::Parse)?;
1014
1015 match analysis {
1016 Input::Tran(_) => {
1017 let values = TransientData::from_binary(ast).signals;
1018 CachedData::Tran(values)
1019 }
1020 Input::Ac(_) => {
1021 let values = AcData::from_binary(ast);
1022 CachedData::Ac {
1023 freq: values.freq,
1024 signals: values.signals,
1025 }
1026 }
1027 Input::DcOp(_) => {
1028 let values = DcData::from_binary(ast).unwrap_op().signals;
1029 CachedData::DcOp(values)
1030 }
1031 Input::MonteCarlo(_) => {
1032 unreachable!()
1033 }
1034 }
1035 })
1036}
1037
1038impl MonteCarlo<Vec<Input>> {
1039 fn netlist<W: Write>(&self, out: &mut W, name: &str) -> Result<()> {
1040 write!(
1041 out,
1042 "montecarlo variations={} numruns={} savefamilyplots=yes",
1043 self.variations, self.numruns
1044 )?;
1045 if let Some(seed) = self.seed {
1046 write!(out, " seed={seed}")?;
1047 }
1048 if let Some(firstrun) = self.firstrun {
1049 write!(out, " firstrun={firstrun}")?;
1050 }
1051 write!(out, " {{")?;
1052
1053 for (i, an) in self.analysis.iter().enumerate() {
1054 let name = subanalysis_name(name, i);
1055 write!(out, "\n\t")?;
1056 an.netlist(out, &name)?;
1057 }
1058 write!(out, "\n}}")?;
1059
1060 Ok(())
1061 }
1062}
1063
1064impl HasSpiceLikeNetlist for Spectre {
1065 fn write_prelude<W: Write>(&self, out: &mut W, lib: &Library<Spectre>) -> std::io::Result<()> {
1066 writeln!(out, "// Substrate Spectre library\n")?;
1067 writeln!(out, "simulator lang=spectre\n")?;
1068 writeln!(out, "// This is a generated file.")?;
1069 writeln!(
1070 out,
1071 "// Be careful when editing manually: this file may be overwritten.\n"
1072 )?;
1073 writeln!(out, "global 0\n")?;
1074
1075 let ibis = lib
1077 .primitives()
1078 .filter_map(|p| {
1079 if let Primitive::IbisInstance { model, .. } = p.1 {
1080 Some(model.clone())
1081 } else {
1082 None
1083 }
1084 })
1085 .collect::<IndexSet<_>>();
1086 for ibis_path in ibis.iter() {
1087 writeln!(out, "ibis_include {:?}", ibis_path)?;
1088 }
1089
1090 let spfs = lib
1092 .primitives()
1093 .filter_map(|p| {
1094 if let Primitive::SpfInstance { netlist, .. } = p.1 {
1095 Some(netlist.clone())
1096 } else {
1097 None
1098 }
1099 })
1100 .collect::<IndexSet<_>>();
1101 for spf_path in spfs.iter() {
1102 writeln!(out, "dspf_include {:?}", spf_path)?;
1103 }
1104
1105 let includes = lib
1107 .primitives()
1108 .filter_map(|p| {
1109 if let Primitive::Spice(spice::Primitive::RawInstanceWithInclude {
1110 netlist, ..
1111 }) = p.1
1112 {
1113 Some(netlist.clone())
1114 } else {
1115 None
1116 }
1117 })
1118 .collect::<IndexSet<_>>();
1119 for include in includes.iter() {
1120 writeln!(out, "include {:?}", include)?;
1121 }
1122
1123 Ok(())
1124 }
1125
1126 fn write_include<W: Write>(&self, out: &mut W, include: &Include) -> std::io::Result<()> {
1127 if let Some(section) = &include.section {
1128 write!(out, "include {:?} section={}", include.path, section)?;
1129 } else {
1130 write!(out, "include {:?}", include.path)?;
1131 }
1132 Ok(())
1133 }
1134
1135 fn write_start_subckt<W: Write>(
1136 &self,
1137 out: &mut W,
1138 name: &ArcStr,
1139 ports: &[&SignalInfo],
1140 ) -> std::io::Result<()> {
1141 write!(out, "subckt {} (", name)?;
1142 for sig in ports {
1143 if let Some(width) = sig.width {
1144 for i in 0..width {
1145 write!(out, " {}\\[{}\\]", Spectre::escape_identifier(&sig.name), i)?;
1146 }
1147 } else {
1148 write!(out, " {}", Spectre::escape_identifier(&sig.name))?;
1149 }
1150 }
1151 write!(out, " )")?;
1152 Ok(())
1153 }
1154
1155 fn write_end_subckt<W: Write>(&self, out: &mut W, name: &ArcStr) -> std::io::Result<()> {
1156 write!(out, "ends {}", name)
1157 }
1158
1159 fn write_instance<W: Write>(
1160 &self,
1161 out: &mut W,
1162 name: &ArcStr,
1163 connections: Vec<ArcStr>,
1164 child: &ArcStr,
1165 ) -> std::io::Result<ArcStr> {
1166 let name = ArcStr::from(Spectre::escape_identifier(&format!("x{}", name)));
1167 write!(out, "{} (", name)?;
1168
1169 for connection in connections {
1170 write!(out, " {}", connection)?;
1171 }
1172
1173 write!(out, " ) {}", child)?;
1174
1175 Ok(name)
1176 }
1177
1178 fn write_primitive_inst<W: Write>(
1179 &self,
1180 out: &mut W,
1181 name: &ArcStr,
1182 mut connections: HashMap<ArcStr, Vec<ArcStr>>,
1183 primitive: &<Self as Schema>::Primitive,
1184 ) -> std::io::Result<ArcStr> {
1185 Ok(match primitive {
1186 Primitive::RawInstance {
1187 cell,
1188 ports,
1189 params,
1190 } => {
1191 let connections = ports
1192 .iter()
1193 .flat_map(|port| connections.remove(port).unwrap_or_else(|| panic!("raw instance `{name}` must connect to all ports; missing connection to port `{port}`")))
1194 .collect();
1195 let name = self.write_instance(out, name, connections, cell)?;
1196 for (key, value) in params.iter().sorted_by_key(|(key, _)| key) {
1197 write!(out, " {key}={value}")?;
1198 }
1199 name
1200 }
1201 Primitive::BlackboxInstance { contents } => {
1202 for elem in &contents.elems {
1206 match elem {
1207 BlackboxElement::InstanceName => write!(out, "{}", name)?,
1208 BlackboxElement::RawString(s) => write!(out, "{}", s)?,
1209 BlackboxElement::Port(p) => {
1210 for part in connections.get(p).unwrap() {
1211 write!(out, "{}", part)?
1212 }
1213 }
1214 }
1215 }
1216 name.clone()
1217 }
1218 Primitive::Spice(p) => {
1219 writeln!(out, "simulator lang=spice")?;
1220 let name = Spice.write_primitive_inst(out, name, connections, p)?;
1221 writeln!(out, "simulator lang=spectre")?;
1222 name
1223 }
1224 Primitive::IbisInstance {
1225 component, ports, ..
1226 } => {
1227 let connections = ports
1228 .iter()
1229 .flat_map(|port| connections.remove(port).unwrap())
1230 .collect();
1231 self.write_instance(
1232 out,
1233 name,
1234 connections,
1235 &arcstr::format!("{}_ibis", component),
1236 )?
1237 }
1238 Primitive::SpfInstance { cell, ports, .. } => {
1239 let connections = ports
1240 .iter()
1241 .flat_map(|port| connections.remove(port).unwrap())
1242 .collect();
1243 self.write_instance(out, name, connections, cell)?
1244 }
1245 })
1246 }
1247
1248 fn write_slice<W: Write>(
1249 &self,
1250 out: &mut W,
1251 slice: Slice,
1252 info: &SignalInfo,
1253 ) -> std::io::Result<()> {
1254 let name = Spectre::escape_identifier(&info.name);
1255 if let Some(range) = slice.range() {
1256 for i in range.indices() {
1257 write!(out, "{}\\[{}\\]", &name, i)?;
1258 }
1259 } else {
1260 write!(out, "{}", &name)?;
1261 }
1262 Ok(())
1263 }
1264}