logicaffeine_compile/codegen_ana/
calibrate.rs1use super::cell::AnalogNetlist;
11use super::cell::{ASrc, AnalogCell};
12use super::error_budget::{bump_mul, outward_sum, ErrorSource, ErrorTerm, OutputBudget};
13use super::gain::{GainSchedule, MathBound, RangeCertificate};
14use super::nonideal::{InstanceSample, NonIdealityModel, KSIGMA};
15use super::place::AnalogPlacement;
16use super::target::AnalogTarget;
17
18#[derive(Clone, Copy, Debug, PartialEq)]
21pub struct TrimSpec {
22 pub gain_step: f64,
24 pub offset_step_v: f64,
26}
27
28pub fn v1_trim(target: &AnalogTarget) -> TrimSpec {
31 TrimSpec {
32 gain_step: target.weight_lsb() / 4.0,
33 offset_step_v: 0.5e-3,
34 }
35}
36
37#[derive(Clone, Debug, PartialEq)]
39pub struct CalibrationTable {
40 pub gain_trim: Vec<Vec<f64>>,
41 pub offset_trim_v: Vec<f64>,
42}
43
44fn snap(x: f64, step: f64) -> f64 {
45 (x / step).round() * step
46}
47
48pub fn calibrate(inst: &InstanceSample, spec: &TrimSpec) -> CalibrationTable {
50 CalibrationTable {
51 gain_trim: inst
52 .leg_gain_err
53 .iter()
54 .map(|legs| legs.iter().map(|e| snap(-e, spec.gain_step)).collect())
55 .collect(),
56 offset_trim_v: inst
57 .offset_v
58 .iter()
59 .map(|o| snap(-o, spec.offset_step_v))
60 .collect(),
61 }
62}
63
64pub fn apply_calibration(inst: &InstanceSample, table: &CalibrationTable) -> InstanceSample {
67 InstanceSample {
68 leg_gain_err: inst
69 .leg_gain_err
70 .iter()
71 .zip(&table.gain_trim)
72 .map(|(legs, trims)| legs.iter().zip(trims).map(|(e, t)| e + t).collect())
73 .collect(),
74 offset_v: inst
75 .offset_v
76 .iter()
77 .zip(&table.offset_trim_v)
78 .map(|(o, t)| o + t)
79 .collect(),
80 noise_v: inst.noise_v.clone(),
81 }
82}
83
84pub fn error_budget_calibrated(
88 netlist: &AnalogNetlist,
89 cert: &RangeCertificate,
90 schedule: &GainSchedule,
91 target: &AnalogTarget,
92 model: &NonIdealityModel,
93 _placement: &AnalogPlacement,
94 spec: &TrimSpec,
95) -> OutputBudget {
96 let base = super::error_budget::error_budget(netlist, cert, schedule, target);
97 let realized = super::cell::AnalogNetlist {
98 n_in: schedule.quantized.n_in,
99 cells: schedule.realized_math_cells(),
100 out: schedule.quantized.out,
101 };
102 let s_of = |i: usize| f64::powi(2.0, schedule.per_net_scale_log2[i]);
103 let q = &realized;
104 let bound_of = |src: &ASrc| -> MathBound {
105 match src {
106 ASrc::In(k) => cert.input_bounds[*k],
107 ASrc::Val(v) => cert.per_net[*v].math,
108 ASrc::Const(c) => MathBound { lo: *c, hi: *c },
109 }
110 };
111 let mut gain_d = Vec::with_capacity(q.cells.len());
114 let mut off_d = Vec::with_capacity(q.cells.len());
115 let mut noise_d = Vec::with_capacity(q.cells.len());
116 for (i, cell) in q.cells.iter().enumerate() {
117 let g = match cell {
118 AnalogCell::Sum { inputs, weights } => outward_sum(
119 inputs
120 .iter()
121 .zip(weights)
122 .map(|(x, w)| bump_mul(spec.gain_step / 2.0, w.abs() * bound_of(x).max_abs())),
123 ),
124 AnalogCell::GainConst { a, k } => {
125 bump_mul(spec.gain_step / 2.0, k.abs() * bound_of(a).max_abs())
126 }
127 AnalogCell::Mul { a, b } => bump_mul(
128 spec.gain_step / 2.0,
129 bound_of(a).max_abs() * bound_of(b).max_abs(),
130 ),
131 AnalogCell::Select { .. } => 0.0,
132 AnalogCell::Exp { .. }
133 | AnalogCell::Log { .. }
134 | AnalogCell::Tanh { .. }
135 | AnalogCell::Sqrt { .. }
136 | AnalogCell::Div { .. } => {
137 bump_mul(spec.gain_step / 2.0, cert.per_net[i].math.max_abs())
138 }
139 };
140 gain_d.push(g);
141 off_d.push((spec.offset_step_v / 2.0 / s_of(i)).next_up());
142 noise_d.push((KSIGMA * model.noise_floor_v.0 / s_of(i)).next_up());
143 }
144 let mut terms = base.terms;
145 let tg = super::error_budget::propagate_injected_pub(q, &bound_of, &gain_d);
146 if tg > 0.0 {
147 terms.push(ErrorTerm {
148 eps_math: tg,
149 source: ErrorSource::TileGain,
150 });
151 }
152 let to = super::error_budget::propagate_injected_pub(q, &bound_of, &off_d);
153 if to > 0.0 {
154 terms.push(ErrorTerm {
155 eps_math: to,
156 source: ErrorSource::TileOffset,
157 });
158 }
159 let tn = super::error_budget::propagate_injected_pub(q, &bound_of, &noise_d);
160 if tn > 0.0 {
161 terms.push(ErrorTerm {
162 eps_math: tn,
163 source: ErrorSource::Noise,
164 });
165 }
166 let eps_math = outward_sum(terms.iter().map(|t| t.eps_math));
167 OutputBudget { eps_math, terms }
168}