1use super::error_budget::ErrorSource;
22use super::ontology::Element;
23use super::ontology::{FunctionalMacro, Passive, Semiconductor, SourceElement};
24
25#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
28pub enum PhysicsEffect {
29 NoiseThermal,
31 NoiseFlicker,
32 NoiseShot,
33 NoiseBurst,
34 MismatchArea,
36 MismatchDistance,
37 DriftTemperature,
39 AgingNbti,
40 AgingHci,
41 DriftPackageStress,
42 ParasiticResistance,
44 ParasiticCapacitance,
45 ParasiticInductance,
46 CouplingCapacitive,
48 CouplingSubstrate,
49 CouplingSupply,
50 CouplingElectromagnetic,
51 CouplingThermal,
52 ConverterInl,
54 ConverterDnl,
55 Quantization,
56 SettlingBandwidth,
58}
59
60pub const EFFECT_COUNT: usize = 22;
61
62pub const ALL_EFFECTS: &[PhysicsEffect; EFFECT_COUNT] = &[
64 PhysicsEffect::NoiseThermal,
65 PhysicsEffect::NoiseFlicker,
66 PhysicsEffect::NoiseShot,
67 PhysicsEffect::NoiseBurst,
68 PhysicsEffect::MismatchArea,
69 PhysicsEffect::MismatchDistance,
70 PhysicsEffect::DriftTemperature,
71 PhysicsEffect::AgingNbti,
72 PhysicsEffect::AgingHci,
73 PhysicsEffect::DriftPackageStress,
74 PhysicsEffect::ParasiticResistance,
75 PhysicsEffect::ParasiticCapacitance,
76 PhysicsEffect::ParasiticInductance,
77 PhysicsEffect::CouplingCapacitive,
78 PhysicsEffect::CouplingSubstrate,
79 PhysicsEffect::CouplingSupply,
80 PhysicsEffect::CouplingElectromagnetic,
81 PhysicsEffect::CouplingThermal,
82 PhysicsEffect::ConverterInl,
83 PhysicsEffect::ConverterDnl,
84 PhysicsEffect::Quantization,
85 PhysicsEffect::SettlingBandwidth,
86];
87
88#[derive(Clone, Copy, Debug, PartialEq, Eq)]
94pub enum EffectSlot {
95 Sourced {
97 params: &'static [&'static str],
98 citation: &'static str,
99 },
100 Derived {
102 params: &'static [&'static str],
103 arithmetic: &'static str,
104 },
105 Modeled {
108 params: &'static [&'static str],
109 note: &'static str,
110 },
111 Ideal { justification: &'static str },
113 Unknown,
115}
116
117#[derive(Clone, Copy, Debug, PartialEq, Eq)]
119pub struct EffectRow(pub [EffectSlot; EFFECT_COUNT]);
120
121impl EffectRow {
122 pub const fn unknown() -> Self {
123 EffectRow([EffectSlot::Unknown; EFFECT_COUNT])
124 }
125
126 pub fn with(mut self, e: PhysicsEffect, s: EffectSlot) -> Self {
128 assert!(
129 matches!(self.0[e as usize], EffectSlot::Unknown),
130 "{e:?} classified twice in one row"
131 );
132 self.0[e as usize] = s;
133 self
134 }
135
136 pub fn slot(&self, e: PhysicsEffect) -> EffectSlot {
137 self.0[e as usize]
138 }
139
140 pub fn classified(&self) -> usize {
142 self.0
143 .iter()
144 .filter(|s| !matches!(s, EffectSlot::Unknown))
145 .count()
146 }
147}
148
149pub fn effect_row(e: Element) -> EffectRow {
156 use EffectSlot::{Derived, Ideal, Modeled, Sourced};
157 use PhysicsEffect as Fx;
158 match e {
159 Element::Passive(Passive::Resistor) => EffectRow::unknown()
160 .with(
161 Fx::NoiseThermal,
162 Sourced {
163 params: &[],
164 citation: "Johnson 1928 / Nyquist 1928: 4kTR thermal noise of any \
165 dissipative element — the law behind the dossier's kT/C entry",
166 },
167 )
168 .with(
169 Fx::NoiseShot,
170 Ideal {
171 justification: "no potential barrier: carrier flow in a homogeneous \
172 resistive film is not discrete-injection limited, so a \
173 resistor exhibits Johnson noise only",
174 },
175 ),
176 Element::Passive(Passive::Capacitor) => EffectRow::unknown()
177 .with(
178 Fx::NoiseThermal,
179 Ideal {
180 justification: "a lossless reactance dissipates nothing and generates no \
181 Johnson noise; the sampled kT/C variance is the switch \
182 resistance's noise, booked on the sampling tile",
183 },
184 )
185 .with(
186 Fx::NoiseFlicker,
187 Ideal {
188 justification: "1/f noise requires carrier trapping in an active \
189 conduction path; an ideal dielectric conducts no signal \
190 carriers",
191 },
192 )
193 .with(
194 Fx::NoiseShot,
195 Ideal {
196 justification: "no barrier and no DC conduction current to be \
197 shot-noise limited",
198 },
199 ),
200 Element::Semiconductor(Semiconductor::FloatingGate) => EffectRow::unknown().with(
201 Fx::MismatchArea,
202 Sourced {
203 params: &[],
204 citation: "Hasler Proc. IEEE 108(8) 2020: programmed analog parameters at \
205 better than 1% accuracy over six decades — the programming-accuracy \
206 class of the fabric's weights",
207 },
208 ),
209 Element::Macro(FunctionalMacro::WinnerTakeAll) => EffectRow::unknown().with(
210 Fx::MismatchArea,
211 Modeled {
212 params: &[],
213 note: "competitor input-referred offsets decide near-ties — the O7 margin \
214 certificate's resolution band (2*k*sigma) prices exactly this class",
215 },
216 ),
217 Element::Source(SourceElement::Vccs) => EffectRow::unknown().with(
218 Fx::MismatchArea,
219 Modeled {
220 params: &[],
221 note: "gm mismatch of the transconductor is the physical origin of the linear \
222 tile's gain-error class — the figure is booked on that tile's row",
223 },
224 ),
225 Element::Macro(FunctionalMacro::LinearCombTile) => EffectRow::unknown()
226 .with(
227 Fx::MismatchArea,
228 Derived {
229 params: &["weight_gain_sigma", "offset_sigma_v", "chop_residual_v"],
230 arithmetic: "gain: Hasler 1% programming envelope taken as the 1-sigma \
231 class (SOURCED, conservative round-up); offset: sigma(V_os) \
232 ~ 2.8 mV from the SkyWater vth0_slope mismatch model on a \
233 stated 5T example; chop residual: MODELED intra-pair drift \
234 class — the slot takes the weakest param status",
235 },
236 )
237 .with(
238 Fx::MismatchDistance,
239 Modeled {
240 params: &["gm_distance_sigma_per_pitch", "cross_tier_gain_sigma"],
241 note: "the Pelgrom 1989 distance term exists; no published sky130 \
242 coefficient — 0.2%/pitch parameterizes the placer until the \
243 characterization ladder replaces it. Across separately \
244 manufactured tiers the gradient gives way to a flat 2% categorical \
245 ceiling (dies share no Pelgrom field) — equally MODELED",
246 },
247 )
248 .with(
249 Fx::NoiseThermal,
250 Derived {
251 params: &["noise_floor_v"],
252 arithmetic: "kT/C at C = 1 pF, T = 300 K: sqrt(4.142e-21/1e-12) = 64.4 uV \
253 rms (Johnson-Nyquist; arithmetic in the dossier)",
254 },
255 )
256 .with(
257 Fx::DriftTemperature,
258 Modeled {
259 params: &["gain_tempco_per_k", "offset_tempco_v_per_k"],
260 note: "drift-class design parameters priced against the CERTIFIED thermal \
261 map (S3): gain drifts with the tile-to-operand GRADIENT, offset \
262 with the absolute rise — awaiting characterization",
263 },
264 )
265 .with(
266 Fx::Quantization,
267 Derived {
268 params: &[],
269 arithmetic: "programmed weights snap to the unit-element grid — the \
270 WeightQuant budget term; half-grid arithmetic in gain.rs",
271 },
272 )
273 .with(
274 Fx::SettlingBandwidth,
275 Modeled {
276 params: &[],
277 note: "behavioral tiers assume settled outputs; the ngspice tier measures \
278 sim_term against the pinned settling options",
279 },
280 ),
281 Element::Macro(FunctionalMacro::GilbertMultiplier) => EffectRow::unknown()
282 .with(
283 Fx::MismatchArea,
284 Modeled {
285 params: &[],
286 note: "shares the programmed-weight accuracy class characterized on the \
287 linear tile (the model is tile-generic today; a mul-specific \
288 figure awaits characterization)",
289 },
290 )
291 .with(
292 Fx::Quantization,
293 Derived {
294 params: &[],
295 arithmetic: "the multiplier reference is drawn from the discrete mul_vref \
296 option set — realizability gated at schedule time in gain.rs",
297 },
298 )
299 .with(
300 Fx::SettlingBandwidth,
301 Modeled {
302 params: &[],
303 note: "as the linear tile",
304 },
305 ),
306 Element::Macro(FunctionalMacro::OtaIntegrator) => EffectRow::unknown()
307 .with(
308 Fx::MismatchArea,
309 Modeled {
310 params: &[],
311 note: "tile-generic gain/offset class today",
312 },
313 )
314 .with(
315 Fx::SettlingBandwidth,
316 Modeled {
317 params: &[],
318 note: "integrator rate realizability is target-gated \
319 (require_integrator_rate)",
320 },
321 ),
322 Element::Macro(FunctionalMacro::Comparator) => EffectRow::unknown()
323 .with(
324 Fx::MismatchArea,
325 Modeled {
326 params: &[],
327 note: "input-referred offset of the pair — tile-generic class today; the \
328 O5 CompareBand term prices the ambiguity band",
329 },
330 )
331 .with(
332 Fx::SettlingBandwidth,
333 Modeled {
334 params: &[],
335 note: "regeneration time — control-plane scheduled",
336 },
337 ),
338 Element::Macro(FunctionalMacro::SampleHold) => EffectRow::unknown()
339 .with(
340 Fx::NoiseThermal,
341 Modeled {
342 params: &[],
343 note: "kT/C sampled noise — the switch's Johnson noise frozen on the hold \
344 cap; tile-generic noise class today",
345 },
346 )
347 .with(
348 Fx::CouplingCapacitive,
349 Modeled {
350 params: &[],
351 note: "switch charge injection and clock feedthrough onto the hold node — \
352 awaiting characterization",
353 },
354 )
355 .with(
356 Fx::SettlingBandwidth,
357 Modeled {
358 params: &[],
359 note: "acquisition time — control-plane scheduled",
360 },
361 ),
362 Element::Macro(FunctionalMacro::Dac) => EffectRow::unknown()
363 .with(
364 Fx::Quantization,
365 Derived {
366 params: &[],
367 arithmetic: "half-LSB input quantization — the DacQuant budget term \
368 (half_lsb in error_budget.rs)",
369 },
370 )
371 .with(
372 Fx::ConverterInl,
373 Modeled {
374 params: &[],
375 note: "V1 spec class INL — characterization ladder",
376 },
377 )
378 .with(
379 Fx::ConverterDnl,
380 Modeled {
381 params: &[],
382 note: "V1 spec class DNL — characterization ladder",
383 },
384 ),
385 Element::Macro(FunctionalMacro::Adc) => EffectRow::unknown()
386 .with(
387 Fx::Quantization,
388 Derived {
389 params: &[],
390 arithmetic: "half-LSB output quantization — the AdcQuant budget term \
391 (half_lsb in error_budget.rs)",
392 },
393 )
394 .with(
395 Fx::ConverterInl,
396 Modeled {
397 params: &[],
398 note: "V1 spec class INL — characterization ladder",
399 },
400 )
401 .with(
402 Fx::ConverterDnl,
403 Modeled {
404 params: &[],
405 note: "V1 spec class DNL — characterization ladder",
406 },
407 ),
408 Element::Passive(
410 Passive::Inductor
411 | Passive::MutualInductor
412 | Passive::TransmissionLine
413 | Passive::Varactor
414 | Passive::Memristor
415 | Passive::AnalogSwitch
416 | Passive::Crystal,
417 )
418 | Element::Source(
419 SourceElement::IndependentV
420 | SourceElement::IndependentI
421 | SourceElement::Vcvs
422 | SourceElement::Cccs
423 | SourceElement::Ccvs,
424 )
425 | Element::Semiconductor(
426 Semiconductor::Diode(_)
427 | Semiconductor::Bjt(_)
428 | Semiconductor::Mosfet(_)
429 | Semiconductor::Jfet
430 | Semiconductor::PhotoDiode,
431 )
432 | Element::Macro(
433 FunctionalMacro::DiffPair
434 | FunctionalMacro::TranslinearLoop
435 | FunctionalMacro::Chopper
436 | FunctionalMacro::AnalogSelect
437 | FunctionalMacro::Ota
438 | FunctionalMacro::OpAmp
439 | FunctionalMacro::CurrentMirror
440 | FunctionalMacro::BandgapReference
441 | FunctionalMacro::BiasGenerator
442 | FunctionalMacro::VoltageReference
443 | FunctionalMacro::ClockDistribution
444 | FunctionalMacro::FilterBiquad
445 | FunctionalMacro::Oscillator
446 | FunctionalMacro::Vco
447 | FunctionalMacro::Pll
448 | FunctionalMacro::Lna
449 | FunctionalMacro::Pga
450 | FunctionalMacro::LdoRegulator,
451 ) => EffectRow::unknown(),
452 }
453}
454
455#[derive(Clone, Copy, Debug, PartialEq, Eq)]
457pub enum BudgetTermClass {
458 Physics(&'static [PhysicsEffect]),
461 Analysis(&'static str),
463}
464
465pub fn effect_of(src: &ErrorSource) -> BudgetTermClass {
468 use PhysicsEffect as Fx;
469 match src {
470 ErrorSource::DacQuant { .. } | ErrorSource::AdcQuant { .. } | ErrorSource::WeightQuant => {
471 BudgetTermClass::Physics(&[Fx::Quantization])
472 }
473 ErrorSource::TileGain => {
474 BudgetTermClass::Physics(&[Fx::MismatchArea, Fx::MismatchDistance])
475 }
476 ErrorSource::TileOffset => BudgetTermClass::Physics(&[Fx::MismatchArea]),
477 ErrorSource::Noise => BudgetTermClass::Physics(&[Fx::NoiseThermal]),
478 ErrorSource::ThermalGain | ErrorSource::ThermalOffset => {
479 BudgetTermClass::Physics(&[Fx::DriftTemperature])
480 }
481 ErrorSource::CompareBand => BudgetTermClass::Physics(&[Fx::MismatchArea]),
482 ErrorSource::ChopResidual => BudgetTermClass::Physics(&[Fx::MismatchArea]),
483 ErrorSource::MulInteraction => BudgetTermClass::Analysis(
484 "the second-order eps_a*eps_b cross term of the perturbation bound — arithmetic \
485 of the analysis, not a physical mechanism of the die",
486 ),
487 ErrorSource::EvalRounding => BudgetTermClass::Analysis(
488 "f64 rounding of the behavioral evaluator — the model tier's arithmetic, not the \
489 die's physics",
490 ),
491 }
492}