miden_processor/trace/chiplets/mod.rs
1use alloc::vec::Vec;
2
3use miden_air::trace::{
4 CHIPLETS_WIDTH,
5 chiplets::{
6 KERNEL_ROM_TRACE_WIDTH,
7 ace::ACE_CHIPLET_NUM_COLS,
8 bitwise::TRACE_WIDTH as BITWISE_WIDTH,
9 hasher::{HasherState, TRACE_WIDTH as HASHER_WIDTH},
10 memory::TRACE_WIDTH as MEMORY_WIDTH,
11 },
12};
13use miden_core::{mast::OpBatch, program::Kernel};
14
15use crate::{
16 Felt, ONE, Word, ZERO,
17 crypto::merkle::MerklePath,
18 trace::{
19 ChipletTraceFragment, RowIndex,
20 range::RangeChecker,
21 utils::{CHIP_CLK_COL, DATA_COL_START, S_00_COL},
22 },
23};
24
25mod bitwise;
26use bitwise::Bitwise;
27
28mod hasher;
29use hasher::Hasher;
30
31mod memory;
32use memory::Memory;
33
34mod ace;
35pub use ace::{Ace, CircuitEvaluation, MAX_NUM_ACE_WIRES, PTR_OFFSET_ELEM, PTR_OFFSET_WORD};
36
37mod kernel_rom;
38use kernel_rom::KernelRom;
39
40#[cfg(test)]
41mod tests;
42
43// TRACE
44// ================================================================================================
45
46pub struct ChipletsTrace {
47 pub(crate) trace: Vec<Felt>,
48}
49
50// CHIPLETS MODULE OF HASHER, BITWISE, MEMORY, ACE, AND KERNEL ROM CHIPLETS
51// ================================================================================================
52
53/// This module manages the VM's hasher, bitwise, memory, arithmetic circuit evaluation (ACE)
54/// and kernel ROM chiplets and is responsible for building a final execution trace from their
55/// stacked execution traces and chiplet selectors.
56///
57/// The module's trace can be thought of as 6 stacked segments in the following form.
58///
59/// The chiplet system uses two physical selector columns (`s_00 = column 0` and
60/// `s_01 = column 1`) plus the virtual `s0 = 1 - (s_00 + s_01)` to partition rows into three
61/// top-level regions. Columns 3-6 (`s1..s4`) subdivide the `s0` region. Column 2 holds
62/// `chip_clk`, the chiplet-trace row counter.
63///
64/// * Hasher segment: fills the first rows of the trace up to the hasher `trace_len`. Split into
65/// controller (s_00=0, s_01=1) and permutation (s_00=1, s_01=0) sub-regions.
66/// - column 0 (s_00): 0 on controller rows, 1 on permutation rows
67/// - column 1 (s_01): 1 on controller rows, 0 on permutation rows
68/// - columns 3-21: execution trace of hash chiplet
69///
70/// * Bitwise segment: begins at the end of the hasher segment.
71/// - column 1 (s_01): ZERO
72/// - column 3 (s1): ZERO
73/// - columns 4-16: execution trace of bitwise chiplet
74/// - columns 17-21: unused columns padded with ZERO
75///
76/// * Memory segment: begins at the end of the bitwise segment.
77/// - column 1 (s_01): ZERO
78/// - column 3 (s1): ONE
79/// - column 4 (s2): ZERO
80/// - columns 5-21: execution trace of memory chiplet
81///
82/// * ACE segment: begins at the end of the memory segment.
83/// - column 1 (s_01): ZERO
84/// - column 3-4 (s1, s2): ONE
85/// - column 5 (s3): ZERO
86/// - columns 6-21: execution trace of ACE chiplet
87///
88/// * Kernel ROM segment: begins at the end of the ACE segment.
89/// - column 1 (s_01): ZERO
90/// - columns 3-5 (s1, s2, s3): ONE
91/// - column 6 (s4): ZERO
92/// - columns 7-11: execution trace of kernel ROM chiplet
93/// - columns 12-21: unused columns padded with ZERO
94///
95/// * Padding segment: fills the rest of the trace.
96/// - column 1 (s_01): ZERO
97/// - columns 3-6 (s1..s4): ONE
98/// - columns 7-21: unused columns padded with ZERO
99///
100///
101/// The following is a pictorial representation of the chiplet module:
102///
103/// ```text
104/// s_00 s_01 clk s1 s2 s3 s4
105/// [0] [1] [2] [3] [4] [5] [6]
106/// +---+---+-------------------------------------------------------+
107/// ctrl | 0 | 1 | Hash chiplet (controller rows) |
108/// | . | . | 19 columns |
109/// | 0 | 1 | constraint degree 9 |
110/// +---+---+ +
111/// perm | 1 | 0 | Hash chiplet (permutation rows) |
112/// | . | . | |
113/// | 1 | 0 | |
114/// +---+---+---+---------------------------------------------------+
115/// | 0 | 0 | 0 | |---|
116/// | . | . | . | Bitwise chiplet |---|
117/// | . | . | . | 13 columns |---|
118/// | 0 | 0 | 0 | constraint degree 5 |---|
119/// | . | . +---+---+---------------------------------------------+-+
120/// | . | . | 1 | 0 | |-|
121/// | . | . | . | . | Memory chiplet |-|
122/// | . | . | . | . | 17 columns |-|
123/// | . | . | . | 0 | constraint degree 9 |-|
124/// | . | . + . +---+---+-------------------------------------------+
125/// | . | . | . | 1 | 0 | |-|
126/// | . | . | . | . | . | ACE chiplet |-|
127/// | . | . | . | . | . | 16 columns |-|
128/// | . | . | . | . | 0 | constraint degree 5 |-|
129/// | . | . + . | . +---+---+-----------------------+----------------+
130/// | . | . | . | . | 1 | 0 | |----------------|
131/// | . | . | . | . | . | . | Kernel ROM chiplet |----------------|
132/// | . | . | . | . | . | . | 5 columns |----------------|
133/// | . | . | . | . | . | 0 | constraint degree 9 |----------------|
134/// | . | . + . | . | . +---+-----------------------+----------------+
135/// | . | . | . | . | . | 1 |------- Padding --------| |
136/// | . | . | . | . | . | . | | |
137/// | 0 | 0 | . | 1 | 1 | 1 | 1 | 0 |
138/// +---+---+---+---+---+---+------------------------+---------------+
139/// ```
140#[derive(Debug)]
141pub struct Chiplets {
142 pub hasher: Hasher,
143 pub bitwise: Bitwise,
144 pub memory: Memory,
145 pub ace: Ace,
146 pub kernel_rom: KernelRom,
147}
148
149impl Chiplets {
150 // CONSTRUCTOR
151 // --------------------------------------------------------------------------------------------
152 /// Returns a new [Chiplets] component instantiated with the provided Kernel.
153 pub fn new(kernel: Kernel) -> Self {
154 Self {
155 hasher: Hasher::default(),
156 bitwise: Bitwise::default(),
157 memory: Memory::default(),
158 kernel_rom: KernelRom::new(kernel),
159 ace: Ace::default(),
160 }
161 }
162
163 // PUBLIC ACCESSORS
164 // --------------------------------------------------------------------------------------------
165
166 /// Returns the length of the trace required to accommodate chiplet components and 1
167 /// mandatory padding row required for ensuring sufficient trace length for auxiliary connector
168 /// columns that rely on the memory chiplet.
169 pub fn trace_len(&self) -> usize {
170 self.hasher.trace_len()
171 + self.bitwise.trace_len()
172 + self.memory.trace_len()
173 + self.kernel_rom.trace_len()
174 + self.ace.trace_len()
175 + 1
176 }
177
178 /// Returns the index of the first row of `Bitwise` execution trace.
179 pub fn bitwise_start(&self) -> RowIndex {
180 self.hasher.trace_len().into()
181 }
182
183 /// Returns the index of the first row of the `Memory` execution trace.
184 pub fn memory_start(&self) -> RowIndex {
185 self.bitwise_start() + self.bitwise.trace_len()
186 }
187
188 /// Returns the index of the first row of `KernelRom` execution trace.
189 pub fn ace_start(&self) -> RowIndex {
190 self.memory_start() + self.memory.trace_len()
191 }
192
193 /// Returns the index of the first row of `KernelRom` execution trace.
194 pub fn kernel_rom_start(&self) -> RowIndex {
195 self.ace_start() + self.ace.trace_len()
196 }
197
198 /// Returns the index of the first row of the padding section of the execution trace.
199 pub fn padding_start(&self) -> RowIndex {
200 self.kernel_rom_start() + self.kernel_rom.trace_len()
201 }
202
203 // EXECUTION TRACE
204 // --------------------------------------------------------------------------------------------
205
206 /// Adds all range checks required by the memory chiplet to the provided `RangeChecker``
207 /// instance.
208 pub fn append_range_checks(&self, range_checker: &mut RangeChecker) {
209 self.memory.append_range_checks(self.memory_start(), range_checker);
210 }
211
212 /// Returns an execution trace of the chiplets containing the stacked traces of the
213 /// Hasher, Bitwise, ACE, Memory chiplets, and kernel ROM chiplet.
214 pub fn into_trace(self, trace_len: usize) -> ChipletsTrace {
215 assert!(self.trace_len() <= trace_len, "target trace length too small");
216
217 let mut trace = vec![Felt::ZERO; CHIPLETS_WIDTH * trace_len];
218 self.fill_trace(&mut trace, trace_len);
219
220 ChipletsTrace { trace }
221 }
222
223 // HELPER METHODS
224 // --------------------------------------------------------------------------------------------
225
226 /// Fills the provided trace for the chiplets module with the stacked execution traces of the
227 /// Hasher, Bitwise, Memory, ACE, and kernel ROM chiplets along with selector columns
228 /// to identify each individual chiplet trace in addition to padding to fill the rest of
229 /// the trace.
230 fn fill_trace(self, trace: &mut [Felt], trace_len: usize) {
231 const W: usize = CHIPLETS_WIDTH;
232 debug_assert_eq!(trace.len(), W * trace_len);
233
234 let memory_start: usize = self.memory_start().into();
235 let ace_start: usize = self.ace_start().into();
236 let kernel_rom_start: usize = self.kernel_rom_start().into();
237 let padding_start: usize = self.padding_start().into();
238
239 let Chiplets { hasher, bitwise, memory, kernel_rom, ace } = self;
240
241 // Per-chiplet row counts. Chiplets are stacked vertically, so each one's region is a
242 // contiguous band of rows: hasher [0, h), bitwise [h, h+b), and so on.
243 let hasher_len = hasher.trace_len();
244 let bitwise_len = bitwise.trace_len();
245 let memory_len = memory.trace_len();
246 let ace_len = ace.trace_len();
247 let kernel_rom_len = kernel_rom.trace_len();
248
249 // Chiplets nest hasher ⊃ bitwise ⊃ memory ⊃ ace ⊃ kernel_rom and begin at columns
250 // 3, 4, 5, 6, 7; the widest (hasher) fills every data column up to the final column.
251 // Each chiplet's `copy_rows_from` writes its prefix selector ONEs and `chip_clk` along
252 // with its data; `s_01` (col 1) is hasher-set per row, `s_00` (col 0) is scattered from
253 // the hasher's last source column, and padding rows are filled directly below.
254 // The hasher data band (`HASHER_WIDTH` columns) starts at `DATA_COL_START`, with its last
255 // column scattered to `S_00_COL`, so the contiguous part fills the trace to its full width.
256 const _: () = assert!(DATA_COL_START + HASHER_WIDTH - 1 == CHIPLETS_WIDTH);
257
258 // Carve `trace` into the per-chiplet contiguous row bands.
259 let (hasher_band, rest) = trace.split_at_mut(hasher_len * W);
260 let (bitwise_band, rest) = rest.split_at_mut(bitwise_len * W);
261 let (memory_band, rest) = rest.split_at_mut(memory_len * W);
262 let (ace_band, rest) = rest.split_at_mut(ace_len * W);
263 let (kernel_band, padding_band) = rest.split_at_mut(kernel_rom_len * W);
264
265 let mut hasher_fragment =
266 ChipletTraceFragment::with_scattered_last(hasher_band, W, 3, HASHER_WIDTH, 0, S_00_COL);
267 let mut bitwise_fragment = ChipletTraceFragment::with_overheads(
268 bitwise_band,
269 W,
270 4,
271 BITWISE_WIDTH,
272 hasher_len,
273 &[],
274 );
275 let mut memory_fragment = ChipletTraceFragment::with_overheads(
276 memory_band,
277 W,
278 5,
279 MEMORY_WIDTH,
280 memory_start,
281 &[3],
282 );
283 let mut ace_fragment = ChipletTraceFragment::with_overheads(
284 ace_band,
285 W,
286 6,
287 ACE_CHIPLET_NUM_COLS,
288 ace_start,
289 &[3, 4],
290 );
291 let mut kernel_rom_fragment = ChipletTraceFragment::with_overheads(
292 kernel_band,
293 W,
294 7,
295 KERNEL_ROM_TRACE_WIDTH,
296 kernel_rom_start,
297 &[3, 4, 5],
298 );
299
300 rayon::scope(|s| {
301 s.spawn(move |_| {
302 hasher.fill_trace(&mut hasher_fragment);
303 });
304 s.spawn(move |_| {
305 bitwise.fill_trace(&mut bitwise_fragment);
306 });
307 s.spawn(move |_| {
308 memory.fill_trace(&mut memory_fragment);
309 });
310 s.spawn(move |_| {
311 kernel_rom.fill_trace(&mut kernel_rom_fragment);
312 });
313 s.spawn(move |_| {
314 ace.fill_trace(&mut ace_fragment);
315 });
316 s.spawn(move |_| {
317 fill_padding_rows(padding_band, padding_start);
318 });
319 });
320 }
321}
322
323/// Fills padding rows after the kernel ROM region: the four `s1..s4` selectors = ONE,
324/// chip_clk = row + 1.
325fn fill_padding_rows(band: &mut [Felt], row_offset: usize) {
326 const W: usize = CHIPLETS_WIDTH;
327 let (rows, _) = band.as_chunks_mut::<W>();
328 for (i, row) in rows.iter_mut().enumerate() {
329 row[DATA_COL_START] = ONE;
330 row[DATA_COL_START + 1] = ONE;
331 row[DATA_COL_START + 2] = ONE;
332 row[DATA_COL_START + 3] = ONE;
333 row[CHIP_CLK_COL] = Felt::from_u32((row_offset + i + 1) as u32);
334 }
335}
336
337// HELPER STRUCTS
338// ================================================================================================
339
340/// Result of a Merkle tree node update. The result contains the old Merkle_root, which
341/// corresponding to the old_value, and the new merkle_root, for the updated value. As well as the
342/// row address of the execution trace at which the computation started.
343#[derive(Debug, Copy, Clone)]
344pub struct MerkleRootUpdate {
345 address: Felt,
346 old_root: Word,
347 new_root: Word,
348}
349
350impl MerkleRootUpdate {
351 pub fn get_address(&self) -> Felt {
352 self.address
353 }
354 pub fn get_old_root(&self) -> Word {
355 self.old_root
356 }
357 pub fn get_new_root(&self) -> Word {
358 self.new_root
359 }
360}