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