spectre/analysis/
dc.rs

1//! Spectre DC sweeps and operating point analyses.
2
3use crate::{InstanceTail, SimSignal, Spectre};
4use arcstr::ArcStr;
5use scir::{NamedSliceOne, SliceOnePath};
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use substrate::{
9    schematic::conv::ConvertedNodePath,
10    simulation::{
11        Analysis, SimulationContext, Simulator, SupportedBy,
12        data::{Save, SaveOutput},
13    },
14    types::schematic::{NestedNode, NestedTerminal, RawNestedNode},
15};
16
17/// A DC operating point analysis.
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct DcOp;
20
21/// The result of a [`DcOp`] analysis.
22#[derive(Debug, Clone)]
23pub struct OpOutput {
24    /// A map from signal name to value.
25    pub raw_values: HashMap<ArcStr, f64>,
26    /// A map from a save ID to a raw value identifier.
27    pub(crate) saved_values: HashMap<u64, ArcStr>,
28}
29
30/// An identifier for a saved DC voltage.
31#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
32pub struct VoltageSaveKey(pub(crate) u64);
33
34/// An identifier for a saved DC current.
35#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
36pub struct CurrentSaveKey(pub(crate) Vec<u64>);
37
38impl Analysis for DcOp {
39    type Output = OpOutput;
40}
41
42impl SupportedBy<Spectre> for DcOp {
43    fn into_input(self, inputs: &mut Vec<<Spectre as Simulator>::Input>) {
44        inputs.push(self.into());
45    }
46    fn from_output(
47        outputs: &mut impl Iterator<Item = <Spectre as Simulator>::Output>,
48    ) -> <Self as Analysis>::Output {
49        let item = outputs.next().unwrap();
50        item.try_into().unwrap()
51    }
52}
53
54impl Save<Spectre, DcOp> for SaveOutput {
55    type SaveKey = ();
56    type Saved = OpOutput;
57
58    fn save(
59        &self,
60        _ctx: &SimulationContext<Spectre>,
61        _opts: &mut <Spectre as Simulator>::Options,
62    ) -> <Self as Save<Spectre, DcOp>>::SaveKey {
63    }
64
65    fn from_saved(
66        output: &<DcOp as Analysis>::Output,
67        _key: &<Self as Save<Spectre, DcOp>>::SaveKey,
68    ) -> <Self as Save<Spectre, DcOp>>::Saved {
69        output.clone()
70    }
71}
72
73impl Save<Spectre, DcOp> for NestedNode {
74    type SaveKey = VoltageSaveKey;
75    type Saved = f64;
76
77    fn save(
78        &self,
79        ctx: &SimulationContext<Spectre>,
80        opts: &mut <Spectre as Simulator>::Options,
81    ) -> <Self as Save<Spectre, DcOp>>::SaveKey {
82        opts.save_dc_voltage(SimSignal::ScirVoltage(
83            match ctx.lib.convert_node_path(&self.path()).unwrap() {
84                ConvertedNodePath::Cell(path) => path.clone(),
85                ConvertedNodePath::Primitive {
86                    instances, port, ..
87                } => SliceOnePath::new(instances.clone(), NamedSliceOne::new(port.clone())),
88            },
89        ))
90    }
91
92    fn from_saved(
93        output: &<DcOp as Analysis>::Output,
94        key: &<Self as Save<Spectre, DcOp>>::SaveKey,
95    ) -> <Self as Save<Spectre, DcOp>>::Saved {
96        *output
97            .raw_values
98            .get(output.saved_values.get(&key.0).unwrap())
99            .unwrap()
100    }
101}
102
103impl Save<Spectre, DcOp> for RawNestedNode {
104    type SaveKey = VoltageSaveKey;
105    type Saved = f64;
106
107    fn save(
108        &self,
109        ctx: &SimulationContext<Spectre>,
110        opts: &mut <Spectre as Simulator>::Options,
111    ) -> <Self as Save<Spectre, DcOp>>::SaveKey {
112        let itail = InstanceTail {
113            instance: ctx.lib.convert_instance_path(self.instances()).unwrap(),
114            tail: self.tail().clone(),
115        };
116        opts.save_dc_voltage(itail)
117    }
118
119    fn from_saved(
120        output: &<DcOp as Analysis>::Output,
121        key: &<Self as Save<Spectre, DcOp>>::SaveKey,
122    ) -> <Self as Save<Spectre, DcOp>>::Saved {
123        *output
124            .raw_values
125            .get(output.saved_values.get(&key.0).unwrap())
126            .unwrap()
127    }
128}
129
130/// Data saved from a nested terminal in an [`DcOp`] simulation.
131pub struct NestedTerminalOutput {
132    /// The voltage at the terminal.
133    pub v: f64,
134    /// The current flowing through the terminal.
135    pub i: f64,
136}
137
138impl Save<Spectre, DcOp> for NestedTerminal {
139    type SaveKey = (VoltageSaveKey, CurrentSaveKey);
140    type Saved = NestedTerminalOutput;
141
142    fn save(
143        &self,
144        ctx: &SimulationContext<Spectre>,
145        opts: &mut <Spectre as Simulator>::Options,
146    ) -> <Self as Save<Spectre, DcOp>>::SaveKey {
147        (
148            <NestedNode as Save<Spectre, DcOp>>::save(self, ctx, opts),
149            CurrentSaveKey(
150                ctx.lib
151                    .convert_terminal_path(&self.path())
152                    .unwrap()
153                    .into_iter()
154                    .flat_map(|path| {
155                        opts.save_tran_current(SimSignal::ScirCurrent(match path {
156                            ConvertedNodePath::Cell(path) => path.clone(),
157                            ConvertedNodePath::Primitive {
158                                instances, port, ..
159                            } => SliceOnePath::new(
160                                instances.clone(),
161                                NamedSliceOne::new(port.clone()),
162                            ),
163                        }))
164                        .0
165                    })
166                    .collect(),
167            ),
168        )
169    }
170
171    fn from_saved(
172        output: &<DcOp as Analysis>::Output,
173        key: &<Self as Save<Spectre, DcOp>>::SaveKey,
174    ) -> <Self as Save<Spectre, DcOp>>::Saved {
175        let v = *output
176            .raw_values
177            .get(output.saved_values.get(&key.0.0).unwrap())
178            .unwrap();
179        let i = key
180            .1
181            .0
182            .iter()
183            .map(|key| {
184                output
185                    .raw_values
186                    .get(output.saved_values.get(key).unwrap())
187                    .unwrap()
188            })
189            .sum();
190
191        NestedTerminalOutput { v, i }
192    }
193}