use alloc::collections::BTreeMap;
use miden_air::trace::{RowIndex, chiplets::kernel_rom::TRACE_WIDTH};
use miden_core::field::PrimeCharacteristicRing;
use super::{Felt, Kernel, TraceFragment, Word as Digest};
use crate::errors::OperationError;
#[cfg(test)]
mod tests;
type ProcHashBytes = [u8; 32];
#[derive(Debug)]
pub struct KernelRom {
access_map: BTreeMap<ProcHashBytes, ProcAccessInfo>,
kernel: Kernel,
trace_len: usize,
}
impl KernelRom {
pub fn new(kernel: Kernel) -> Self {
let trace_len = kernel.proc_hashes().len();
let mut access_map = BTreeMap::new();
for &proc_hash in kernel.proc_hashes() {
access_map.insert(proc_hash.into(), ProcAccessInfo::new(proc_hash));
}
Self { access_map, kernel, trace_len }
}
pub const fn trace_len(&self) -> usize {
self.trace_len
}
pub fn access_proc(&mut self, proc_root: Digest) -> Result<(), OperationError> {
let proc_hash_bytes: ProcHashBytes = proc_root.into();
let access_info = self
.access_map
.get_mut(&proc_hash_bytes)
.ok_or(OperationError::SyscallTargetNotInKernel { proc_root })?;
self.trace_len += 1;
access_info.num_accesses += 1;
Ok(())
}
pub fn fill_trace(self, trace: &mut TraceFragment) {
debug_assert_eq!(TRACE_WIDTH, trace.width(), "inconsistent trace fragment width");
let mut row = RowIndex::from(0);
for access_info in self.access_map.values() {
access_info.write_into_trace(trace, row, true);
row += 1_u32;
for _ in 0..access_info.num_accesses {
access_info.write_into_trace(trace, row, false);
row += 1_u32;
}
}
}
pub const fn kernel(&self) -> &Kernel {
&self.kernel
}
}
#[derive(Debug)]
struct ProcAccessInfo {
proc_hash: Digest,
num_accesses: usize,
}
impl ProcAccessInfo {
pub fn new(proc_hash: Digest) -> Self {
Self { proc_hash, num_accesses: 0 }
}
pub fn write_into_trace(&self, trace: &mut TraceFragment, row: RowIndex, is_first: bool) {
let s_first = Felt::from_bool(is_first);
trace.set(row, 0, s_first);
trace.set(row, 1, self.proc_hash[0]);
trace.set(row, 2, self.proc_hash[1]);
trace.set(row, 3, self.proc_hash[2]);
trace.set(row, 4, self.proc_hash[3]);
}
}