miden_processor/trace/chiplets/ace/
trace.rs1use alloc::vec::Vec;
2use core::ops::Range;
3
4use miden_air::trace::{
5 RowIndex,
6 chiplets::ace::{
7 CLK_IDX, CTX_IDX, EVAL_OP_IDX, ID_0_IDX, ID_1_IDX, ID_2_IDX, M_0_IDX, M_1_IDX, PTR_IDX,
8 READ_NUM_EVAL_IDX, SELECTOR_BLOCK_IDX, SELECTOR_START_IDX, V_0_0_IDX, V_0_1_IDX, V_1_0_IDX,
9 V_1_1_IDX, V_2_0_IDX, V_2_1_IDX,
10 },
11};
12use miden_core::{
13 Felt, Word,
14 field::{BasedVectorSpace, QuadFelt},
15};
16
17use super::{
18 MAX_NUM_ACE_WIRES,
19 instruction::{Op, decode_instruction},
20};
21use crate::{ContextId, errors::AceError};
22
23pub const NUM_ACE_LOGUP_FRACTIONS_READ: usize = 2;
25pub const NUM_ACE_LOGUP_FRACTIONS_EVAL: usize = 3;
27
28#[derive(Debug, Clone)]
33pub struct CircuitEvaluation {
34 ctx: ContextId,
35 clk: RowIndex,
36
37 wire_bus: WireBus,
38
39 col_ptr: Vec<Felt>,
40 col_op: Vec<Felt>,
41
42 col_wire_out: WireColumn,
43 col_wire_left: WireColumn,
44 col_wire_right: WireColumn,
45
46 num_read_rows: u32,
47 num_eval_rows: u32,
48}
49
50impl CircuitEvaluation {
51 pub fn new(ctx: ContextId, clk: RowIndex, num_read_rows: u32, num_eval_rows: u32) -> Self {
58 let num_wires = 2 * (num_read_rows as u64) + (num_eval_rows as u64);
59 assert!(num_wires <= MAX_NUM_ACE_WIRES as u64, "too many wires");
60
61 let num_rows = num_read_rows + num_eval_rows;
62
63 Self {
64 ctx,
65 clk,
66 wire_bus: WireBus::new(num_wires as u32),
67 col_ptr: Vec::with_capacity(num_rows as usize),
68 col_op: Vec::with_capacity(num_eval_rows as usize),
69 col_wire_out: WireColumn::new(num_rows as usize),
70 col_wire_left: WireColumn::new(num_rows as usize),
71 col_wire_right: WireColumn::new(num_eval_rows as usize),
72 num_read_rows,
73 num_eval_rows,
74 }
75 }
76
77 pub fn num_rows(&self) -> usize {
78 (self.num_read_rows + self.num_eval_rows) as usize
79 }
80
81 pub fn clk(&self) -> u32 {
82 self.clk.into()
83 }
84
85 pub fn ctx(&self) -> u32 {
86 self.ctx.into()
87 }
88
89 pub fn num_read_rows(&self) -> u32 {
90 self.num_read_rows
91 }
92
93 pub fn num_eval_rows(&self) -> u32 {
94 self.num_eval_rows
95 }
96
97 pub fn do_read(&mut self, ptr: Felt, word: Word) {
100 let v_0 = QuadFelt::from_basis_coefficients_fn(|i: usize| [word[0], word[1]][i]);
102 let id_0 = self.wire_bus.insert(v_0);
103 self.col_wire_out.push(id_0, v_0);
104
105 let v_1 = QuadFelt::from_basis_coefficients_fn(|i: usize| [word[2], word[3]][i]);
107 let id_1 = self.wire_bus.insert(v_1);
108 self.col_wire_left.push(id_1, v_1);
109
110 self.col_ptr.push(ptr);
112 }
113
114 pub fn do_eval(&mut self, ptr: Felt, instruction: Felt) -> Result<(), AceError> {
117 let (id_l, id_r, op) = decode_instruction(instruction)
119 .ok_or(AceError("failed to decode instruction".into()))?;
120
121 let v_l = self
123 .wire_bus
124 .read_value(id_l)
125 .ok_or(AceError("failed to read from the wiring bus".into()))?;
126 let id_l = Felt::from_u32(id_l);
127 self.col_wire_left.push(id_l, v_l);
128
129 let v_r = self
131 .wire_bus
132 .read_value(id_r)
133 .ok_or(AceError("failed to read from the wiring bus".into()))?;
134 let id_r = Felt::from_u32(id_r);
135 self.col_wire_right.push(id_r, v_r);
136
137 let v_out = match op {
139 Op::Sub => v_l - v_r,
140 Op::Mul => v_l * v_r,
141 Op::Add => v_l + v_r,
142 };
143 let id_out = self.wire_bus.insert(v_out);
144 self.col_wire_out.push(id_out, v_out);
145
146 let op_sub = -Felt::ONE;
148 let op_mul = Felt::ZERO;
149 let op_add = Felt::ONE;
150 let op = match op {
151 Op::Sub => op_sub,
152 Op::Mul => op_mul,
153 Op::Add => op_add,
154 };
155 self.col_op.push(op);
156
157 self.col_ptr.push(ptr);
159
160 Ok(())
161 }
162
163 pub fn fill(&self, offset: usize, columns: &mut [Vec<Felt>]) {
164 let num_read_rows = self.num_read_rows as usize;
165 let num_eval_rows = self.num_eval_rows as usize;
166 let num_rows = num_read_rows + num_eval_rows;
167 let read_range = Range {
168 start: offset,
169 end: offset + num_read_rows,
170 };
171 let eval_range = Range {
172 start: read_range.end,
173 end: read_range.end + num_eval_rows,
174 };
175
176 columns[SELECTOR_START_IDX][offset] = Felt::ONE;
178 columns[SELECTOR_START_IDX][(offset + 1)..].fill(Felt::ZERO);
179
180 let f_read = Felt::ZERO;
182 let f_eval = Felt::ONE;
183 columns[SELECTOR_BLOCK_IDX][read_range.clone()].fill(f_read);
184 columns[SELECTOR_BLOCK_IDX][eval_range.clone()].fill(f_eval);
185
186 let ctx_felt = self.ctx.into();
188 columns[CTX_IDX][offset..offset + num_rows].fill(ctx_felt);
189
190 columns[PTR_IDX][offset..offset + num_rows].copy_from_slice(&self.col_ptr);
192
193 let clk_felt = self.clk.into();
195 columns[CLK_IDX][offset..offset + num_rows].fill(clk_felt);
196
197 let eval_section_first_idx = Felt::from_u32(self.num_eval_rows - 1);
199 columns[READ_NUM_EVAL_IDX][read_range.clone()].fill(eval_section_first_idx);
200
201 columns[EVAL_OP_IDX][eval_range.clone()].copy_from_slice(&self.col_op);
203
204 columns[ID_0_IDX][offset..offset + num_rows].copy_from_slice(&self.col_wire_out.id);
206 columns[V_0_0_IDX][offset..offset + num_rows].copy_from_slice(&self.col_wire_out.v_0);
207 columns[V_0_1_IDX][offset..offset + num_rows].copy_from_slice(&self.col_wire_out.v_1);
208
209 columns[ID_1_IDX][offset..offset + num_rows].copy_from_slice(&self.col_wire_left.id);
211 columns[V_1_0_IDX][offset..offset + num_rows].copy_from_slice(&self.col_wire_left.v_0);
212 columns[V_1_1_IDX][offset..offset + num_rows].copy_from_slice(&self.col_wire_left.v_1);
213
214 columns[ID_2_IDX][eval_range.clone()].copy_from_slice(&self.col_wire_right.id);
216 columns[V_2_0_IDX][eval_range.clone()].copy_from_slice(&self.col_wire_right.v_0);
217 columns[V_2_1_IDX][eval_range.clone()].copy_from_slice(&self.col_wire_right.v_1);
218
219 let mut multiplicities_iter = self.wire_bus.wires.iter().map(|(_v, m)| Felt::from_u32(*m));
221 for row_index in read_range {
223 let m_0 = multiplicities_iter
224 .next()
225 .expect("the m0 multiplicities were not constructed properly");
226 let m_1 = multiplicities_iter
227 .next()
228 .expect("the m1 multiplicities were not constructed properly");
229 columns[M_0_IDX][row_index] = m_0;
230 columns[M_1_IDX][row_index] = m_1;
231 }
232 for row_index in eval_range {
234 let m_0 = multiplicities_iter
235 .next()
236 .expect("the m0 multiplicities were not constructed properly");
237 columns[M_0_IDX][row_index] = m_0;
238 }
239
240 debug_assert!(multiplicities_iter.next().is_none());
241 }
242
243 pub fn output_value(&self) -> Option<QuadFelt> {
245 if !self.wire_bus.is_finalized() {
246 return None;
247 }
248 self.wire_bus.wires.last().map(|(v, _m)| *v)
249 }
250}
251
252#[derive(Debug, Clone)]
255struct WireColumn {
256 id: Vec<Felt>,
257 v_0: Vec<Felt>,
258 v_1: Vec<Felt>,
259}
260
261impl WireColumn {
262 fn new(num_rows: usize) -> Self {
263 Self {
264 id: Vec::with_capacity(num_rows),
265 v_0: Vec::with_capacity(num_rows),
266 v_1: Vec::with_capacity(num_rows),
267 }
268 }
269
270 fn push(&mut self, id: Felt, v: QuadFelt) {
272 self.id.push(id);
273 let v: &[Felt] = v.as_basis_coefficients_slice();
274 self.v_0.push(v[0]);
275 self.v_1.push(v[1]);
276 }
277}
278
279#[derive(Debug, Clone)]
288struct WireBus {
289 id_next: Felt,
291 wires: Vec<(QuadFelt, u32)>,
294 num_wires: u32,
296}
297
298impl WireBus {
299 fn new(num_wires: u32) -> Self {
300 Self {
301 wires: Vec::with_capacity(num_wires as usize),
302 num_wires,
303 id_next: Felt::from_u32(num_wires - 1),
304 }
305 }
306
307 fn insert(&mut self, value: QuadFelt) -> Felt {
309 debug_assert!(!self.is_finalized());
310 self.wires.push((value, 0));
311 let id = self.id_next;
312 self.id_next -= Felt::ONE;
313 id
314 }
315
316 fn read_value(&mut self, id: u32) -> Option<QuadFelt> {
319 let (v, m) = self
321 .num_wires
322 .checked_sub(id + 1)
323 .and_then(|id| self.wires.get_mut(id as usize))?;
324 *m += 1;
325 Some(*v)
326 }
327
328 fn is_finalized(&self) -> bool {
330 self.wires.len() == self.num_wires as usize
331 }
332}