logicaffeine_compile/codegen_ana/
stack.rs1use super::target::AnalogTarget;
18use super::{analog_artifact_from_source, AnaLowerError, AnalogArtifact};
19
20#[derive(Clone, Copy, Debug, PartialEq, Eq)]
22pub enum FigureStatus {
23 Sourced(&'static str),
25 Derived(&'static str),
27 Modeled(&'static str),
29}
30
31#[derive(Clone, Copy, Debug, PartialEq)]
33pub struct Material {
34 pub name: &'static str,
35 pub k_w_per_m_k: f64,
37 pub status: FigureStatus,
38}
39
40impl Material {
41 pub fn silicon() -> Material {
43 Material {
44 name: "silicon",
45 k_w_per_m_k: 148.0,
46 status: FigureStatus::Sourced(
47 "Incropera & DeWitt property tables: silicon ~148 W/(m.K) at 300 K \
48 (benchmarks/silicon/analog/thermal-constants.md)",
49 ),
50 }
51 }
52
53 pub fn underfill_class() -> Material {
55 Material {
56 name: "underfill-class",
57 k_w_per_m_k: 0.5,
58 status: FigureStatus::Modeled(
59 "V1 bond-layer background conductance class; vendor filled underfills span \
60 0.3-3 W/(m.K) — OUR declared figure, not a citation",
61 ),
62 }
63 }
64}
65
66#[derive(Clone, Debug, PartialEq)]
68pub enum TierFabric {
69 Analog(AnalogTarget),
71 Digital(DigitalTierPower),
74 Interposer,
76}
77
78#[derive(Clone, Debug, PartialEq)]
80pub struct DigitalTierPower {
81 pub design: &'static str,
82 pub power_w: f64,
84 pub provenance: &'static str,
86}
87
88#[derive(Clone, Debug, PartialEq)]
90pub struct Tier {
91 pub name: &'static str,
92 pub fabric: TierFabric,
93 pub material: Material,
94 pub thickness_um: f64,
95}
96
97#[derive(Clone, Copy, Debug, PartialEq, Eq)]
98pub enum VerticalKind {
99 MicroBump,
100 HybridBond,
101 Tsv,
102}
103
104#[derive(Clone, Debug, PartialEq)]
107pub struct VerticalClass {
108 pub kind: VerticalKind,
109 pub pitch_um: f64,
110 pub r_ohm: f64,
111 pub c_ff: f64,
112 pub g_w_per_k: f64,
113 pub count: usize,
115 pub status: FigureStatus,
116}
117
118impl VerticalClass {
119 pub fn tsv_v1(count: usize) -> VerticalClass {
121 VerticalClass {
122 kind: VerticalKind::Tsv,
123 pitch_um: 40.0,
124 r_ohm: 0.05,
125 c_ff: 40.0,
126 g_w_per_k: 3.15e-4,
127 count,
128 status: FigureStatus::Derived(
129 "g = k_Cu*A/L = 401 * pi*(5e-6)^2 / 100e-6 ~ 3.15e-4 W/K per via (k_Cu SOURCED, \
130 CRC 401 W/(m.K)); R/C/pitch MODELED (benchmarks/silicon/analog/thermal-constants.md)",
131 ),
132 }
133 }
134
135 pub fn microbump_v1(count: usize) -> VerticalClass {
137 VerticalClass {
138 kind: VerticalKind::MicroBump,
139 pitch_um: 50.0,
140 r_ohm: 0.02,
141 c_ff: 25.0,
142 g_w_per_k: 1.0e-4,
143 count,
144 status: FigureStatus::Modeled(
145 "V1 class figures (benchmarks/silicon/analog/thermal-constants.md)",
146 ),
147 }
148 }
149}
150
151#[derive(Clone, Copy, Debug, PartialEq, Eq)]
153pub enum CoolingClass {
154 NaturalConvection,
155 ForcedAir,
156 ColdPlate,
157}
158
159#[derive(Clone, Copy, Debug, PartialEq)]
160pub struct CoolingBoundary {
161 pub ambient_c: f64,
162 pub top: CoolingClass,
163 pub bottom: CoolingClass,
164 pub t_junction_max_c: f64,
165}
166
167impl CoolingBoundary {
168 pub fn forced_air_default() -> CoolingBoundary {
171 CoolingBoundary {
172 ambient_c: 25.0,
173 top: CoolingClass::ForcedAir,
174 bottom: CoolingClass::NaturalConvection,
175 t_junction_max_c: 85.0,
176 }
177 }
178}
179
180#[derive(Clone, Debug, PartialEq)]
182pub enum StackShapeError {
183 GapCountMismatch {
185 tiers: usize,
186 gaps: usize,
187 },
188 NoTiers,
189 EmptyVerticalClass {
191 gap: usize,
192 },
193}
194
195#[derive(Clone, Debug, PartialEq)]
197pub struct StackTarget {
198 pub name: &'static str,
199 pub tiers: Vec<Tier>,
200 pub gaps: Vec<Vec<VerticalClass>>,
201 pub cooling: CoolingBoundary,
202}
203
204impl StackTarget {
205 pub fn single(t: AnalogTarget) -> StackTarget {
208 StackTarget {
209 name: "single-tier",
210 tiers: vec![Tier {
211 name: "analog",
212 fabric: TierFabric::Analog(t),
213 material: Material::silicon(),
214 thickness_um: 300.0,
215 }],
216 gaps: vec![],
217 cooling: CoolingBoundary::forced_air_default(),
218 }
219 }
220
221 pub fn sky130_stack_v1() -> StackTarget {
224 StackTarget {
225 name: "sky130-stack-v1",
226 tiers: vec![
227 Tier {
228 name: "digital",
229 fabric: TierFabric::Digital(DigitalTierPower {
230 design: "soft_core_scalar",
231 power_w: 0.0295,
232 provenance: "OpenLane Sky130 signoff, OpenSTA report_power on the post-route RC-extracted netlist the \
233 ledger ships \
234 (tools/OpenLane/designs/logos_soft_core_scalar/runs/RUN_2026.10.15_17.14.37, \
235 31492840-byte GDS) at its 15 ns signoff clock, typical corner. Switching activity is \
236 OpenSTA's propagated default (no workload VCD) — a signoff EDA power number, not a \
237 bench-measured one.",
238 }),
239 material: Material::silicon(),
240 thickness_um: 300.0,
241 },
242 Tier {
243 name: "analog",
244 fabric: TierFabric::Analog(AnalogTarget::sky130_fpaa()),
245 material: Material::silicon(),
246 thickness_um: 100.0,
247 },
248 ],
249 gaps: vec![vec![VerticalClass::tsv_v1(64), VerticalClass::microbump_v1(256)]],
250 cooling: CoolingBoundary::forced_air_default(),
251 }
252 }
253
254 pub fn validate(&self) -> Result<(), StackShapeError> {
256 if self.tiers.is_empty() {
257 return Err(StackShapeError::NoTiers);
258 }
259 if self.gaps.len() + 1 != self.tiers.len() {
260 return Err(StackShapeError::GapCountMismatch {
261 tiers: self.tiers.len(),
262 gaps: self.gaps.len(),
263 });
264 }
265 for (i, gap) in self.gaps.iter().enumerate() {
266 if gap.iter().any(|v| v.count == 0) {
267 return Err(StackShapeError::EmptyVerticalClass { gap: i });
268 }
269 }
270 Ok(())
271 }
272
273 pub fn analog_tier(&self) -> Option<(usize, &AnalogTarget)> {
276 self.tiers
277 .iter()
278 .enumerate()
279 .find_map(|(i, t)| match &t.fabric {
280 TierFabric::Analog(a) => Some((i, a)),
281 _ => None,
282 })
283 }
284}
285
286pub fn analog_artifact_on_stack(
289 src: &str,
290 fn_name: &str,
291 stack: &StackTarget,
292) -> Result<AnalogArtifact, AnaLowerError> {
293 if let Err(e) = stack.validate() {
294 return Err(AnaLowerError::UnloweredOp(format!(
295 "malformed stack: {e:?}"
296 )));
297 }
298 let Some((_, target)) = stack.analog_tier() else {
299 return Err(AnaLowerError::Unsupported("stack has no analog tier"));
300 };
301 analog_artifact_from_source(src, fn_name, target)
302}