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}