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}