Skip to main content

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}