use super::{Digest, ExecutionError, Felt, Kernel, TraceFragment, Word, ONE, ZERO};
use alloc::collections::BTreeMap;
use miden_air::trace::chiplets::kernel_rom::TRACE_WIDTH;
#[cfg(test)]
mod tests;
type ProcHashBytes = [u8; 32];
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_hash: Digest) -> Result<(), ExecutionError> {
let proc_hash_bytes: ProcHashBytes = proc_hash.into();
let access_info = self
.access_map
.get_mut(&proc_hash_bytes)
.ok_or(ExecutionError::SyscallTargetNotInKernel(proc_hash))?;
if access_info.num_accesses > 0 {
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 = 0;
for (idx, access_info) in self.access_map.values().enumerate() {
let idx = Felt::from(idx as u16);
access_info.write_into_trace(trace, row, idx);
row += 1;
for _ in 1..access_info.num_accesses {
access_info.write_into_trace(trace, row, idx);
row += 1;
}
}
}
pub const fn kernel(&self) -> &Kernel {
&self.kernel
}
}
struct ProcAccessInfo {
proc_hash: Word,
num_accesses: usize,
}
impl ProcAccessInfo {
pub fn new(proc_hash: Digest) -> Self {
Self {
proc_hash: proc_hash.into(),
num_accesses: 0,
}
}
pub fn write_into_trace(&self, trace: &mut TraceFragment, row: usize, idx: Felt) {
let s0 = if self.num_accesses == 0 { ZERO } else { ONE };
trace.set(row, 0, s0);
trace.set(row, 1, idx);
trace.set(row, 2, self.proc_hash[0]);
trace.set(row, 3, self.proc_hash[1]);
trace.set(row, 4, self.proc_hash[2]);
trace.set(row, 5, self.proc_hash[3]);
}
}