use super::{BTreeMap, Digest, ExecutionError, Felt, Kernel, TraceFragment, Word, ONE, ZERO};
use vm_core::chiplets::kernel_rom::TRACE_WIDTH;
#[cfg(test)]
mod tests;
type ProcHashBytes = [u8; 32];
pub struct KernelRom {
access_map: BTreeMap<ProcHashBytes, ProcAccessInfo>,
trace_len: usize,
}
impl KernelRom {
pub fn new(kernel: &Kernel) -> Self {
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,
trace_len: kernel.proc_hashes().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;
}
}
}
}
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]);
}
}