Skip to main content

miden_processor/trace/chiplets/
mod.rs

1use alloc::vec::Vec;
2
3use miden_air::trace::{CHIPLETS_WIDTH, chiplets::hasher::HasherState};
4use miden_core::{mast::OpBatch, program::Kernel};
5
6use crate::{
7    Felt, ONE, Word, ZERO,
8    crypto::merkle::MerklePath,
9    trace::{RowIndex, TraceFragment, range::RangeChecker},
10};
11
12mod bitwise;
13use bitwise::Bitwise;
14
15mod hasher;
16use hasher::Hasher;
17#[cfg(test)]
18pub(crate) use hasher::init_state_from_words;
19
20mod memory;
21use memory::Memory;
22
23mod ace;
24use ace::AceHints;
25pub use ace::{Ace, CircuitEvaluation, MAX_NUM_ACE_WIRES, PTR_OFFSET_ELEM, PTR_OFFSET_WORD};
26
27mod kernel_rom;
28use kernel_rom::KernelRom;
29
30mod aux_trace;
31
32pub use aux_trace::AuxTraceBuilder;
33
34#[cfg(test)]
35mod tests;
36
37// TRACE
38// ================================================================================================
39
40pub struct ChipletsTrace {
41    pub(crate) trace: [Vec<Felt>; CHIPLETS_WIDTH],
42    pub(crate) aux_builder: AuxTraceBuilder,
43}
44
45// CHIPLETS MODULE OF HASHER, BITWISE, MEMORY, ACE, AND KERNEL ROM CHIPLETS
46// ================================================================================================
47
48/// This module manages the VM's hasher, bitwise, memory, arithmetic circuit evaluation (ACE)
49/// and kernel ROM chiplets and is responsible for building a final execution trace from their
50/// stacked execution traces and chiplet selectors.
51///
52/// The module's trace can be thought of as 6 stacked segments in the following form:
53///
54/// * Hasher segment: contains the trace and selector for the hasher chiplet. This segment fills the
55///   first rows of the trace up to the length of the hasher `trace_len`.
56///   - column 0: selector column with values set to ZERO
57///   - columns 1-16: execution trace of hash chiplet
58///   - columns 17-20: unused columns padded with ZERO
59///
60/// * Bitwise segment: contains the trace and selectors for the bitwise chiplet. This segment begins
61///   at the end of the hasher segment and fills the next rows of the trace for the `trace_len` of
62///   the bitwise chiplet.
63///   - column 0: selector column with values set to ONE
64///   - column 1: selector column with values set to ZERO
65///   - columns 2-14: execution trace of bitwise chiplet
66///   - columns 15-20: unused columns padded with ZERO
67///
68/// * Memory segment: contains the trace and selectors for the memory chiplet.  This segment begins
69///   at the end of the bitwise segment and fills the next rows of the trace for the `trace_len` of
70///   the memory chiplet.
71///   - column 0-1: selector columns with values set to ONE
72///   - column 2: selector column with values set to ZERO
73///   - columns 3-17: execution trace of memory chiplet
74///   - columns 18-20: unused columns padded with ZERO
75///
76/// * ACE segment: contains the trace and selectors for the arithmetic circuit evaluation chiplet.
77///   This segment begins at the end of the memory segment and fills the next rows of the trace for
78///   the `trace_len` of the ACE chiplet.
79///   - column 0-2: selector columns with values set to ONE
80///   - column 3: selector column with values set to ZERO
81///   - columns 4-20: execution trace of ACE chiplet
82///
83/// * Kernel ROM segment: contains the trace and selectors for the kernel ROM chiplet * This segment
84///   begins at the end of the memory segment and fills the next rows of the trace for the
85///   `trace_len` of the kernel ROM chiplet.
86///   - column 0-3: selector columns with values set to ONE
87///   - column 4: selector column with values set to ZERO
88///   - columns 5-9: execution trace of kernel ROM chiplet
89///   - columns 10-20: unused column padded with ZERO
90///
91/// * Padding segment: unused. This segment begins at the end of the kernel ROM segment and fills
92///   the rest of the execution trace minus the number of random rows. When it finishes, the
93///   execution trace should have exactly enough rows remaining for the specified number of random
94///   rows.
95///   - columns 0-4: selector columns with values set to ONE
96///   - columns 5-20: unused columns padded with ZERO
97///
98///
99/// The following is a pictorial representation of the chiplet module:
100///
101/// ```text
102///             +---+--------------------------------------------------------------+------+
103///             | 0 |                                                              |------|
104///             | . |         Hash chiplet                                         |------|
105///             | . |         16 columns                                           |------|
106///             | . |       constraint degree 8                                    |------|
107///             | 0 |                                                              |------|
108///             +---+---+------------------------------------------------------+---+------+
109///             | 1 | 0 |                                                      |----------|
110///             | . | . |                  Bitwise chiplet                     |----------|
111///             | . | . |                    13 columns                        |----------|
112///             | . | . |               constraint degree 5                    |----------|
113///             | . | . |                                                      |----------|
114///             | . | 0 |                                                      |----------|
115///             | . +---+---+--------------------------------------------------+-----+----+
116///             | . | 1 | 0 |                                                        |----|
117///             | . | . | . |            Memory chiplet                              |----|
118///             | . | . | . |              15 columns                                |----|
119///             | . | . | . |          constraint degree 9                           |----|
120///             | . | . | 0 |                                                        |----|
121///             | . + . +---+---+----------------------------------------------------+----+
122///             | . | . | 1 | 0 |                                                         |
123///             | . | . | . | . |          ACE chiplet                                    |
124///             | . | . | . | . |            16 columns                                   |
125///             | . | . | . | . |        constraint degree 5                              |
126///             | . | . | . | 0 |                                                         |
127///             | . + . | . +---+---+---------------------------+-------------------------+
128///             | . | . | . | 1 | 0 |                           |-------------------------|
129///             | . | . | . | . | . |     Kernel ROM chiplet    |-------------------------|
130///             | . | . | . | . | . |     5 columns             |-------------------------|
131///             | . | . | . | . | . |     constraint degree 9   |-------------------------|
132///             | . | . | . | . | 0 |                           |-------------------------|
133///             | . + . | . | . +---+---+-----------------------+-------------------------+
134///             | . | . | . | . | 1 | 0 |-------------------------------------------------|
135///             | . | . | . | . | . | . |-------------------------------------------------|
136///             | . | . | . | . | . | . |-------------------------------------------------|
137///             | . | . | . | . | . | . |-------------------------------------------------|
138///             | . | . | . | . | . | . |-------------------- Padding --------------------|
139///             | . + . | . | . | . | . |-------------------------------------------------|
140///             | . | . | . | . | . | . |-------------------------------------------------|
141///             | . | . | . | . | . | . |-------------------------------------------------|
142///             | . | . | . | . | . | . |-------------------------------------------------|
143///             | 1 | 1 | 1 | 1 | 1 | 0 |-------------------------------------------------|
144///             +---+---+---+---+---------------------------------------------------------+
145/// ```
146#[derive(Debug)]
147pub struct Chiplets {
148    pub hasher: Hasher,
149    pub bitwise: Bitwise,
150    pub memory: Memory,
151    pub ace: Ace,
152    pub kernel_rom: KernelRom,
153}
154
155impl Chiplets {
156    // CONSTRUCTOR
157    // --------------------------------------------------------------------------------------------
158    /// Returns a new [Chiplets] component instantiated with the provided Kernel.
159    pub fn new(kernel: Kernel) -> Self {
160        Self {
161            hasher: Hasher::default(),
162            bitwise: Bitwise::default(),
163            memory: Memory::default(),
164            kernel_rom: KernelRom::new(kernel),
165            ace: Ace::default(),
166        }
167    }
168
169    // PUBLIC ACCESSORS
170    // --------------------------------------------------------------------------------------------
171
172    /// Returns the length of the trace required to accommodate chiplet components and 1
173    /// mandatory padding row required for ensuring sufficient trace length for auxiliary connector
174    /// columns that rely on the memory chiplet.
175    pub fn trace_len(&self) -> usize {
176        self.hasher.trace_len()
177            + self.bitwise.trace_len()
178            + self.memory.trace_len()
179            + self.kernel_rom.trace_len()
180            + self.ace.trace_len()
181            + 1
182    }
183
184    /// Returns the index of the first row of `Bitwise` execution trace.
185    pub fn bitwise_start(&self) -> RowIndex {
186        self.hasher.trace_len().into()
187    }
188
189    /// Returns the index of the first row of the `Memory` execution trace.
190    pub fn memory_start(&self) -> RowIndex {
191        self.bitwise_start() + self.bitwise.trace_len()
192    }
193
194    /// Returns the index of the first row of `KernelRom` execution trace.
195    pub fn ace_start(&self) -> RowIndex {
196        self.memory_start() + self.memory.trace_len()
197    }
198
199    /// Returns the index of the first row of `KernelRom` execution trace.
200    pub fn kernel_rom_start(&self) -> RowIndex {
201        self.ace_start() + self.ace.trace_len()
202    }
203
204    /// Returns the index of the first row of the padding section of the execution trace.
205    pub fn padding_start(&self) -> RowIndex {
206        self.kernel_rom_start() + self.kernel_rom.trace_len()
207    }
208
209    // EXECUTION TRACE
210    // --------------------------------------------------------------------------------------------
211
212    /// Adds all range checks required by the memory chiplet to the provided `RangeChecker``
213    /// instance.
214    pub fn append_range_checks(&self, range_checker: &mut RangeChecker) {
215        self.memory.append_range_checks(self.memory_start(), range_checker);
216    }
217
218    /// Returns an execution trace of the chiplets containing the stacked traces of the
219    /// Hasher, Bitwise, ACE, Memory chiplets, and kernel ROM chiplet.
220    pub fn into_trace(self, trace_len: usize) -> ChipletsTrace {
221        assert!(self.trace_len() <= trace_len, "target trace length too small");
222
223        // Allocate columns for the trace of the chiplets.
224        let mut trace = (0..CHIPLETS_WIDTH)
225            .map(|_| vec![Felt::ZERO; trace_len])
226            .collect::<Vec<_>>()
227            .try_into()
228            .expect("failed to convert vector to array");
229        let ace_hint = self.fill_trace(&mut trace);
230
231        ChipletsTrace {
232            trace,
233            aux_builder: AuxTraceBuilder::new(ace_hint),
234        }
235    }
236
237    // HELPER METHODS
238    // --------------------------------------------------------------------------------------------
239
240    /// Fills the provided trace for the chiplets module with the stacked execution traces of the
241    /// Hasher, Bitwise, Memory, ACE, and kernel ROM chiplets along with selector columns
242    /// to identify each individual chiplet trace in addition to padding to fill the rest of
243    /// the trace.
244    fn fill_trace(self, trace: &mut [Vec<Felt>; CHIPLETS_WIDTH]) -> AceHints {
245        // get the rows where:usize  chiplets begin.
246        let bitwise_start: usize = self.bitwise_start().into();
247        let memory_start: usize = self.memory_start().into();
248        let ace_start: usize = self.ace_start().into();
249        let kernel_rom_start: usize = self.kernel_rom_start().into();
250        let padding_start: usize = self.padding_start().into();
251
252        let Chiplets { hasher, bitwise, memory, kernel_rom, ace } = self;
253
254        // populate external selector columns for all chiplets
255        trace[0][bitwise_start..].fill(ONE);
256        trace[1][memory_start..].fill(ONE);
257        trace[2][ace_start..].fill(ONE);
258        trace[3][kernel_rom_start..].fill(ONE);
259        trace[4][padding_start..].fill(ONE);
260
261        // allocate fragments to be filled with the respective execution traces of each chiplet
262        let mut hasher_fragment = TraceFragment::new(CHIPLETS_WIDTH, hasher.trace_len());
263        let mut bitwise_fragment = TraceFragment::new(CHIPLETS_WIDTH, bitwise.trace_len());
264        let mut memory_fragment = TraceFragment::new(CHIPLETS_WIDTH, memory.trace_len());
265        let mut ace_fragment = TraceFragment::new(CHIPLETS_WIDTH, ace.trace_len());
266        let mut kernel_rom_fragment = TraceFragment::new(CHIPLETS_WIDTH, kernel_rom.trace_len());
267
268        // add the hasher, bitwise, memory, ACE, and kernel ROM segments to their respective
269        // fragments so they can be filled with the chiplet traces
270        for (column_num, column) in trace.iter_mut().enumerate().skip(1) {
271            match column_num {
272                1 => {
273                    // column 1 is relevant only for the hasher
274                    hasher_fragment.push_column_slice(column);
275                },
276                2 => {
277                    // column 2 is relevant to the hasher and to bitwise chiplet
278                    let rest = hasher_fragment.push_column_slice(column);
279                    bitwise_fragment.push_column_slice(rest);
280                },
281                3 => {
282                    // column 3 is relevant for hasher, bitwise, and memory chiplets
283                    let rest = hasher_fragment.push_column_slice(column);
284                    let rest = bitwise_fragment.push_column_slice(rest);
285                    memory_fragment.push_column_slice(rest);
286                },
287                4 | 10..=14 => {
288                    // columns 4 - 10 to 14 are relevant for hasher, bitwise, memory chiplets and
289                    // ace chiplet
290                    let rest = hasher_fragment.push_column_slice(column);
291                    let rest = bitwise_fragment.push_column_slice(rest);
292                    let rest = memory_fragment.push_column_slice(rest);
293                    ace_fragment.push_column_slice(rest);
294                },
295                5..=9 => {
296                    // columns 5 - 9 are relevant to all chiplets
297                    let rest = hasher_fragment.push_column_slice(column);
298                    let rest = bitwise_fragment.push_column_slice(rest);
299                    let rest = memory_fragment.push_column_slice(rest);
300                    let rest = ace_fragment.push_column_slice(rest);
301                    kernel_rom_fragment.push_column_slice(rest);
302                },
303                15 | 16 => {
304                    // columns 15 and 16 are relevant only for the hasher, memory and ace chiplets
305                    let rest = hasher_fragment.push_column_slice(column);
306                    // skip bitwise chiplet
307                    let (_, rest) = rest.split_at_mut(bitwise.trace_len());
308                    let rest = memory_fragment.push_column_slice(rest);
309                    ace_fragment.push_column_slice(rest);
310                },
311                17 => {
312                    // column 17 is relevant only for the memory chiplet
313                    // skip the hasher and bitwise chiplets
314                    let (_, rest) = column.split_at_mut(hasher.trace_len() + bitwise.trace_len());
315                    let rest = memory_fragment.push_column_slice(rest);
316                    ace_fragment.push_column_slice(rest);
317                },
318                18 | 19 => {
319                    // column 18 and 19 are relevant only for the ACE chiplet
320                    // skip the hasher, bitwise and memory chiplets
321                    let (_, rest) = column.split_at_mut(
322                        hasher.trace_len() + bitwise.trace_len() + memory.trace_len(),
323                    );
324                    ace_fragment.push_column_slice(rest);
325                },
326                _ => panic!("invalid column index"),
327            }
328        }
329
330        // fill the fragments with the execution trace from each chiplet in parallel
331        // The chiplets are independent and can be processed concurrently
332
333        // Fill independent chiplets in parallel: hasher, bitwise, memory, kernel_rom
334        // Note: ACE must be processed separately since it returns a value
335        // Use ThreadPool::install() to prevent nested parallelism from column operations
336        rayon::scope(|s| {
337            s.spawn(move |_| {
338                hasher.fill_trace(&mut hasher_fragment);
339            });
340            s.spawn(move |_| {
341                bitwise.fill_trace(&mut bitwise_fragment);
342            });
343            s.spawn(move |_| {
344                memory.fill_trace(&mut memory_fragment);
345            });
346            s.spawn(move |_| {
347                kernel_rom.fill_trace(&mut kernel_rom_fragment);
348            });
349        });
350
351        // Process ACE chiplet separately as it returns ace_sections
352        let ace_sections = ace.fill_trace(&mut ace_fragment);
353        AceHints::new(ace_start, ace_sections)
354    }
355}
356
357// HELPER STRUCTS
358// ================================================================================================
359
360/// Result of a Merkle tree node update. The result contains the old Merkle_root, which
361/// corresponding to the old_value, and the new merkle_root, for the updated value. As well as the
362/// row address of the execution trace at which the computation started.
363#[derive(Debug, Copy, Clone)]
364pub struct MerkleRootUpdate {
365    address: Felt,
366    old_root: Word,
367    new_root: Word,
368}
369
370impl MerkleRootUpdate {
371    pub fn get_address(&self) -> Felt {
372        self.address
373    }
374    pub fn get_old_root(&self) -> Word {
375        self.old_root
376    }
377    pub fn get_new_root(&self) -> Word {
378        self.new_root
379    }
380}