diff --git a/core/src/air/mod.rs b/core/src/air/mod.rs index bf4aa44c01..2ac7c204f0 100644 --- a/core/src/air/mod.rs +++ b/core/src/air/mod.rs @@ -31,6 +31,12 @@ pub struct AirInteraction { /// /// All `AirBuilder` implementations automatically implement this trait. pub trait CurtaAirBuilder: AirBuilder + MessageBuilder> { + fn assert_word_zero>(&mut self, value: Word) { + for value in value.0 { + self.assert_zero(value); + } + } + fn assert_word_eq>(&mut self, left: Word, right: Word) { for (left, right) in left.0.into_iter().zip(right.0) { self.assert_eq(left, right); diff --git a/core/src/memory/air.rs b/core/src/memory/air.rs index d544a8b846..5579c2d1e0 100644 --- a/core/src/memory/air.rs +++ b/core/src/memory/air.rs @@ -36,7 +36,6 @@ pub struct MemoryCols { pub is_read: Bool, /// The multiplicity of this memory access. pub multiplicity: T, - /// The previous address of the table. Needed for the bus argument access of "less_than" pub prev_addr: Word, /// A decoding of the clk to a 32-bit word. @@ -53,6 +52,14 @@ pub struct MemoryCols { pub is_clk_lt: Bool, /// A flag to indicate whether the memory access consistency is checked. pub is_checked: Bool, + /// A flag to indicate whether this is the last operation for the current address. + pub is_last: Bool, + /// A flag to indicate whether we have a new write event (a write with a non-zero clock cycle) + pub is_new_write: Bool, + /// A flag to inidcate whether we mutated this address in the program execution. + pub is_changed: Bool, + /// Oouput page multiplicity. We send a last event to the page if the data was mutated. + pub out_page_mult: T, } const fn make_col_map() -> MemoryCols { @@ -111,9 +118,6 @@ impl Air for MemoryChip { // Assert that `clk_word` is a decoding of `clk`. let clk_expected = reduce::(local.clk_word); builder.assert_eq(clk_expected, local.clk); - // If the operation is a write, the multiplicity must be 1. - // TODO: Figure out if this constraint is necessary. - // builder.assert_zero(local.is_read.0 * (local.multiplicity - AB::F::one())); // Lookup values validity checks // @@ -172,12 +176,63 @@ impl Air for MemoryChip { .when(next.is_clk_eq.0) .assert_eq(next.clk, local.clk); + // An operation is the last one for an adress if the next address is different from the + // current one. + builder + .when_transition() + .when(local.multiplicity) + .assert_one(local.is_last.0 + next.is_addr_eq.0); + // In the last row, record `is_last` as `1` if the multiplicity is non-zero. + // builder + // .when_last_row() + // .when(local.multiplicity) + // .assert_eq(local.is_last.0, AB::F::one()); + + // Constrain the `is_new_write` flag. + // + // The `is_new_write` flag is set to `1` if the current operation is a write event with a + // a non-zero `clk` (so that's not a write from the initial memory state). + builder + .when(local.clk) + .when(local.is_read.0 - AB::F::one()) + .assert_eq(local.is_new_write.0, AB::F::one()); + + // Constrain the `is_changed` flag. + // + // The `is_changed` flag is set to `1` if the `is_new_write` flag is set to `1` at any point + // in the memory operation for the current address. This can be constrained as follows: + // + `is_changed` is equal to `is_new_write` whenever the current address is new. + // + When the address is the same as the previous one, `is_changed` is equal to the + // OR of `is_changed` from the last row and `is_new_write`. + builder + .when(local.is_addr_eq.0 - AB::F::one()) + .assert_eq(local.is_changed.0, local.is_new_write.0); + builder + .when_transition() + .when(next.multiplicity) + .when(next.is_addr_eq.0) + .assert_eq( + next.is_changed.0, + local.is_changed.0 + next.is_new_write.0 - local.is_changed.0 * next.is_new_write.0, + ); + + // Constrain the `out_page_mult` flag. This flag is set to the AND of `is_changed` and + // `is_last`, so that we send the last event of an address if the data was mutated. These + // hold while the event is not padding (i.e. the multiplicity is non-zero). + builder + .when(local.multiplicity) + .assert_eq(local.out_page_mult, local.is_last.0 * local.is_changed.0); + builder + .when(local.multiplicity - AB::F::one()) + .assert_zero(local.out_page_mult); + + // At every row, record the memory interaction. builder.recieve_memory( local.clk, local.addr, local.value, local.is_read.0, - local.multiplicity, + local.multiplicity, //+ local.out_page_mult, ); } } diff --git a/core/src/memory/mod.rs b/core/src/memory/mod.rs index d5e1e9b262..234c824b3a 100644 --- a/core/src/memory/mod.rs +++ b/core/src/memory/mod.rs @@ -1,4 +1,7 @@ pub mod air; +pub mod page; +pub mod state; +pub mod state_old; pub mod trace; #[derive(Debug, Clone, Copy)] @@ -46,34 +49,13 @@ mod tests { use crate::lookup::InteractionBuilder; use crate::memory::{MemOp, MemoryChip}; - use crate::runtime::tests::simple_program; use crate::runtime::Runtime; + use crate::utils::Chip; use p3_commit::ExtensionMmcs; use super::air::NUM_MEMORY_COLS; - use super::MemoryEvent; - - #[test] - fn test_memory_generate_trace() { - let events = vec![ - MemoryEvent { - clk: 0, - addr: 0, - op: MemOp::Write, - value: 0, - }, - MemoryEvent { - clk: 1, - addr: 0, - op: MemOp::Read, - value: 0, - }, - ]; - let trace: RowMajorMatrix = MemoryChip::generate_trace(&events); - println!("{:?}", trace.values) - } #[test] fn test_memory_prove_babybear() { @@ -120,10 +102,9 @@ mod tests { let program = simple_program(); let mut runtime = Runtime::new(program); runtime.run(); - let events = runtime.memory_events; - let trace: RowMajorMatrix = MemoryChip::generate_trace(&events); let air = MemoryChip::new(); + let trace: RowMajorMatrix = air.generate_trace(&mut runtime); let proof = prove::(&config, &air, &mut challenger, trace); let mut challenger = Challenger::new(perm); diff --git a/core/src/memory/page/air.rs b/core/src/memory/page/air.rs new file mode 100644 index 0000000000..71b43975bd --- /dev/null +++ b/core/src/memory/page/air.rs @@ -0,0 +1,85 @@ +use core::borrow::Borrow; +use core::borrow::BorrowMut; +use core::mem::size_of; + +use crate::air::{Bool, CurtaAirBuilder, Word}; +use p3_air::Air; +use p3_air::BaseAir; +use p3_field::AbstractField; + +use p3_field::Field; +use p3_matrix::MatrixRowSlices; +use valida_derive::AlignedBorrow; + +use super::InputPage; +use super::OutputPage; + +pub const NUM_PAGE_COLS: usize = size_of::>(); +pub const NUM_OUT_PAGE_COLS: usize = size_of::>(); + +#[derive(Debug, Clone, AlignedBorrow)] +#[repr(C)] +pub struct PageCols { + /// The address of the memory access. + pub addr: Word, + /// The value being read from or written to memory. + pub value: Word, +} + +// #[derive(Debug, Clone, AlignedBorrow)] +// #[repr(C)] +// pub struct InputPageCols { + +// } + +#[derive(Debug, Clone, AlignedBorrow)] +#[repr(C)] +pub struct OutputPageCols { + /// The clock cycle value for this memory access. + pub clk: T, + /// Whether the memory was being read from or written to. + pub is_read: Bool, +} + +impl BaseAir for InputPage { + fn width(&self) -> usize { + NUM_PAGE_COLS + } +} + +impl Air for InputPage { + fn eval(&self, builder: &mut AB) { + let main = builder.main(); + let local: &PageCols = main.row_slice(0).borrow(); + + builder.send_memory( + AB::F::zero(), + local.addr, + local.value, + AB::F::zero(), + AB::F::one(), + ) + } +} + +impl BaseAir for OutputPage { + fn width(&self) -> usize { + NUM_PAGE_COLS + NUM_OUT_PAGE_COLS + } +} + +impl Air for OutputPage { + fn eval(&self, builder: &mut AB) { + let main = builder.main(); + let local: &PageCols = main.row_slice(0).borrow(); + let out: &OutputPageCols = main.row_slice(NUM_PAGE_COLS).borrow(); + + builder.send_memory( + out.clk, + local.addr, + local.value, + out.is_read.0, + AB::F::one(), + ) + } +} diff --git a/core/src/memory/page/mod.rs b/core/src/memory/page/mod.rs new file mode 100644 index 0000000000..f797d0810d --- /dev/null +++ b/core/src/memory/page/mod.rs @@ -0,0 +1,35 @@ +mod air; +mod trace; + +pub const PAGE_DEGREE: usize = 10; +pub const PAGE_SIZE: usize = 1 << PAGE_DEGREE; + +#[derive(Debug, Clone, Copy)] +pub struct InputPage { + page_id: u16, +} + +#[derive(Debug, Clone, Copy)] +pub struct OutputPage { + page_id: u16, +} + +impl InputPage { + pub fn new(page_id: u16) -> Self { + Self { page_id } + } + + pub fn page_id(&self) -> u16 { + self.page_id + } +} + +impl OutputPage { + pub fn new(page_id: u16) -> Self { + Self { page_id } + } + + pub fn page_id(&self) -> u16 { + self.page_id + } +} diff --git a/core/src/memory/page/trace.rs b/core/src/memory/page/trace.rs new file mode 100644 index 0000000000..10434b0bc9 --- /dev/null +++ b/core/src/memory/page/trace.rs @@ -0,0 +1,80 @@ +use std::borrow::BorrowMut; + +use p3_field::Field; +use p3_matrix::dense::RowMajorMatrix; +use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; + +use crate::{ + air::{Bool, Word}, + memory::{MemOp, MemoryEvent}, +}; + +use super::{ + air::{OutputPageCols, PageCols, NUM_OUT_PAGE_COLS, NUM_PAGE_COLS}, + InputPage, OutputPage, +}; + +pub struct OutputPageTrace { + pub(crate) page: RowMajorMatrix, + pub(crate) data: RowMajorMatrix, +} + +impl InputPage { + pub(crate) fn generate_trace(&self, in_events: &[MemoryEvent]) -> RowMajorMatrix { + let rows = in_events + .par_iter() + .flat_map(|event| { + let mut row = [F::zero(); NUM_PAGE_COLS]; + + let cols: &mut PageCols = row.as_mut_slice().borrow_mut(); + + cols.addr = Word::from(event.addr); + cols.value = Word::from(event.value); + + row + }) + .collect::>(); + + RowMajorMatrix::new(rows, NUM_PAGE_COLS) + } +} + +impl OutputPage { + pub(crate) fn generate_trace( + &self, + out_events: &[MemoryEvent], + ) -> OutputPageTrace { + let page_rows = out_events + .par_iter() + .flat_map(|event| { + let mut row = [F::zero(); NUM_PAGE_COLS]; + + let cols: &mut PageCols = row.as_mut_slice().borrow_mut(); + + cols.addr = Word::from(event.addr); + cols.value = Word::from(event.value); + + row + }) + .collect::>(); + + let data_rows = out_events + .par_iter() + .flat_map(|event| { + let mut row = [F::zero(); NUM_OUT_PAGE_COLS]; + + let cols: &mut OutputPageCols = row.as_mut_slice().borrow_mut(); + + cols.clk = F::from_canonical_u32(event.clk); + cols.is_read = Bool::from(event.op == MemOp::Read); + + row + }) + .collect::>(); + + OutputPageTrace { + page: RowMajorMatrix::new(page_rows, NUM_PAGE_COLS), + data: RowMajorMatrix::new(data_rows, NUM_OUT_PAGE_COLS), + } + } +} diff --git a/core/src/memory/state/merkle.rs b/core/src/memory/state/merkle.rs new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/core/src/memory/state/merkle.rs @@ -0,0 +1 @@ + diff --git a/core/src/memory/state/mod.rs b/core/src/memory/state/mod.rs new file mode 100644 index 0000000000..ed3b84ded8 --- /dev/null +++ b/core/src/memory/state/mod.rs @@ -0,0 +1,99 @@ +mod merkle; +mod util; + +use std::collections::BTreeMap; + +use crate::{ + memory::{state::util::page_id, MemOp}, + runtime::Runtime, +}; + +use super::{ + page::{InputPage, OutputPage}, + MemoryEvent, +}; + +pub struct MemoryState { + pub input_pages: Vec, + pub output_pages: Vec, + + initial_state: BTreeMap, + + in_events: Vec, + out_events: Vec, +} + +impl MemoryState { + pub fn new(memory_state: BTreeMap) -> Self { + Self { + input_pages: Vec::new(), + output_pages: Vec::new(), + initial_state: memory_state, + in_events: Vec::new(), + out_events: Vec::new(), + } + } + + pub fn update(&mut self, runtime: &mut Runtime) { + let mut events = runtime.memory_events.clone(); + // Sort the events by address and then by clock cycle. + events.sort_by_key(|event| (event.addr, event.clk, event.op)); + + // For each address, do the following: + // 1. If the first event is a read: + // + add a a write of it with `clk = 0` from the initial memory state. + // + register the read event to the `in_events` vector. + // + Add the corresponding pade id to `input_pages`. + // 2. If the address has at least one write event: + // + register the last event of the address to the `out_events` vector. + // + Add the corresponding pade id to `output_pages`. + let mut last_address = None; + let mut last_event = None; + let mut was_written = false; + for event in events { + let current_address = event.addr; + + if let Some(addr) = last_address { + if addr != current_address { + if was_written { + self.out_events.push(last_event.unwrap()); + self.output_pages.push(OutputPage::new(page_id(addr))); + } + if event.op == MemOp::Read { + self.in_events.push(event); + self.input_pages.push(InputPage::new(page_id(addr))); + runtime.memory_events.push(MemoryEvent { + addr, + value: self.initial_state.get(&addr).copied().unwrap_or(0), + clk: 0, + op: MemOp::Write, + }); + } + was_written = event.op == MemOp::Write; + } else { + was_written |= event.op == MemOp::Write; + } + } else { + if event.op == MemOp::Read { + self.in_events.push(event); + self.input_pages + .push(InputPage::new(page_id(current_address))); + runtime.memory_events.push(MemoryEvent { + addr: current_address, + value: self + .initial_state + .get(¤t_address) + .copied() + .unwrap_or(0), + clk: 0, + op: MemOp::Write, + }); + } else { + was_written = true; + } + } + last_address = Some(current_address); + last_event = Some(event); + } + } +} diff --git a/core/src/memory/state/util.rs b/core/src/memory/state/util.rs new file mode 100644 index 0000000000..2a792ce767 --- /dev/null +++ b/core/src/memory/state/util.rs @@ -0,0 +1,8 @@ +use crate::memory::page::PAGE_DEGREE; + +/// Calculate page id from address. +/// +/// The page is is calculated by taking the top `PAGE_DEGREE` bits of the address. +pub fn page_id(address: u32) -> u16 { + (address >> (32 - PAGE_DEGREE)) as u16 +} diff --git a/core/src/memory/state_old/air.rs b/core/src/memory/state_old/air.rs new file mode 100644 index 0000000000..ca0111f750 --- /dev/null +++ b/core/src/memory/state_old/air.rs @@ -0,0 +1,63 @@ +use core::borrow::Borrow; +use core::borrow::BorrowMut; +use core::mem::size_of; + +use crate::air::{Bool, CurtaAirBuilder, Word}; +use p3_air::Air; +use p3_air::AirBuilder; +use p3_air::BaseAir; +use p3_field::AbstractField; + +use p3_field::Field; +use p3_matrix::MatrixRowSlices; +use valida_derive::AlignedBorrow; + +use super::MemoryStateChip; + +pub const NUM_MEMORY_STATE_COLS: usize = size_of::>(); + +#[derive(Debug, Clone, AlignedBorrow)] +#[repr(C)] +pub struct MemoryStateCols { + /// The clock cycle value for this memory access. + pub clk: T, + /// The address of the memory access. + pub addr: Word, + /// The value being read from or written to memory. + pub value: Word, + /// Whether the memory was being read from or written to. + pub is_read: Bool, + + pub is_real: Bool, +} + +impl BaseAir for MemoryStateChip { + fn width(&self) -> usize { + NUM_MEMORY_STATE_COLS + } +} + +impl Air for MemoryStateChip { + fn eval(&self, builder: &mut AB) { + let main = builder.main(); + let local: &MemoryStateCols = main.row_slice(0).borrow(); + + let is_dummy = AB::Expr::one() - local.is_real.0; + + builder.assert_is_bool(local.is_real); + + // If the dummy flag is set, everything else should be set to zero. + builder.when(is_dummy.clone()).assert_zero(local.clk); + builder.when(is_dummy.clone()).assert_word_zero(local.addr); + builder.when(is_dummy.clone()).assert_word_zero(local.value); + builder.when(is_dummy).assert_is_bool(local.is_read); + + builder.send_memory( + local.clk, + local.addr, + local.value, + local.is_read.0, + local.is_real.0, + ); + } +} diff --git a/core/src/memory/state_old/mod.rs b/core/src/memory/state_old/mod.rs new file mode 100644 index 0000000000..01fb4458ed --- /dev/null +++ b/core/src/memory/state_old/mod.rs @@ -0,0 +1,21 @@ +use p3_field::Field; +use p3_matrix::dense::RowMajorMatrix; + +use crate::{runtime::Runtime, utils::Chip}; + +pub mod air; +mod trace; + +pub enum MemoryStateChip { + Output, + Input, +} + +impl Chip for MemoryStateChip { + fn generate_trace(&self, runtime: &mut Runtime) -> RowMajorMatrix { + match self { + MemoryStateChip::Output => Self::generate_trace_output(runtime), + MemoryStateChip::Input => todo!(), + } + } +} diff --git a/core/src/memory/state_old/trace.rs b/core/src/memory/state_old/trace.rs new file mode 100644 index 0000000000..1d8441a4e4 --- /dev/null +++ b/core/src/memory/state_old/trace.rs @@ -0,0 +1,42 @@ +use std::borrow::BorrowMut; + +use p3_field::Field; +use p3_matrix::dense::RowMajorMatrix; + +use crate::{ + air::{Bool, Word}, + memory::{state_old::air::NUM_MEMORY_STATE_COLS, MemOp}, + runtime::Runtime, +}; + +use super::{air::MemoryStateCols, MemoryStateChip}; + +impl MemoryStateChip { + pub fn generate_trace_output(runtime: &mut Runtime) -> RowMajorMatrix { + let last_writes = &runtime.last_memory_events; + + let mut rows = last_writes + .iter() + .flat_map(|event| { + let mut row = [F::zero(); NUM_MEMORY_STATE_COLS]; + + let cols: &mut MemoryStateCols = row.as_mut_slice().borrow_mut(); + + cols.clk = F::from_canonical_u32(event.clk); + cols.addr = Word::from(event.addr); + cols.value = Word::from(event.value); + cols.is_read = Bool::from(event.op == MemOp::Read); + cols.is_real = Bool::from(true); + + row + }) + .collect::>(); + + let dummy_len = last_writes.len().next_power_of_two() - last_writes.len(); + let dummy_rows = (0..dummy_len).flat_map(|_| [F::zero(); NUM_MEMORY_STATE_COLS]); + + rows.extend(dummy_rows); + + RowMajorMatrix::new(rows, NUM_MEMORY_STATE_COLS) + } +} diff --git a/core/src/memory/trace.rs b/core/src/memory/trace.rs index 31990cd8d9..90451db2c2 100644 --- a/core/src/memory/trace.rs +++ b/core/src/memory/trace.rs @@ -1,10 +1,12 @@ -use core::mem::transmute; - +use core::borrow::BorrowMut; use p3_field::PrimeField; use p3_matrix::dense::RowMajorMatrix; +use rayon::vec; +use std::sync::atomic::AtomicBool; use rayon::iter::IndexedParallelIterator; use rayon::iter::IntoParallelRefIterator; +use rayon::iter::IntoParallelRefMutIterator; use rayon::iter::ParallelIterator; use rayon::slice::ParallelSlice; @@ -41,14 +43,7 @@ const fn dummy_events(clk: u32) -> (MemoryEvent, MemoryEvent) { impl Chip for MemoryChip { // TODO: missing STLU events. fn generate_trace(&self, runtime: &mut Runtime) -> RowMajorMatrix { - let Runtime { memory_events, .. } = runtime; - Self::generate_trace(memory_events) - } -} - -impl MemoryChip { - pub fn generate_trace(events: &[MemoryEvent]) -> RowMajorMatrix { - let mut events = events.to_vec(); + let mut events = runtime.memory_events.clone(); // Sort the events by address and then by clock cycle. events.sort_by_key(|event| (event.addr, event.clk, event.op)); @@ -67,6 +62,7 @@ impl MemoryChip { } unique_events = events.clone(); + let real_len = unique_events.len(); multiplicities = vec![1; unique_events.len()]; let mut next_events = unique_events[1..].to_vec(); @@ -92,14 +88,33 @@ impl MemoryChip { }; unique_events.insert(0, first_event); + let mut is_changed_vec = vec![false; multiplicities.len()]; + + let mut is_changed = false; + for (i, event) in unique_events.iter().enumerate().skip(1) { + let prev_address = unique_events[i - 1].addr; + let addr = event.addr; + + let is_new_write = event.op == MemOp::Write && event.clk != 0; + + if prev_address != addr { + is_changed = is_new_write; + } else { + is_changed = is_changed || is_new_write; + } + + is_changed_vec[i - 1] = is_changed; + } + // Create the trace. - let rows = unique_events + let mut rows = unique_events .par_windows(2) - .zip(multiplicities.par_iter()) - .flat_map(|(window, mult)| { + .zip_eq(multiplicities.par_iter()) + .zip_eq(is_changed_vec.par_iter()) + .flat_map(|((window, mult), is_changed)| { let (prev, curr) = (window[0], window[1]); let mut row = [F::zero(); NUM_MEMORY_COLS]; - let cols: &mut MemoryCols = unsafe { transmute(&mut row) }; + let cols: &mut MemoryCols = row.as_mut_slice().borrow_mut(); cols.clk = F::from_canonical_u32(curr.clk); cols.clk_word = Word::from(curr.clk); @@ -114,6 +129,9 @@ impl MemoryChip { cols.multiplicity = F::from_canonical_u32(0); } + let is_new_write = curr.op == MemOp::Write && curr.clk != 0; + cols.is_new_write = Bool::from(is_new_write); + cols.prev_addr = Word::from(prev.addr); cols.prev_clk_word = Word::from(prev.clk); @@ -123,10 +141,33 @@ impl MemoryChip { cols.is_clk_lt = Bool::from(prev.clk < curr.clk); cols.is_checked = Bool::from(curr.op == MemOp::Read && curr.addr == prev.addr); + cols.is_changed = Bool::from(*is_changed); + row }) .collect::>(); + // Set the `is_last` flag and update the `last_memory_events` values. + for (i, row) in rows + .chunks_exact_mut(NUM_MEMORY_COLS) + .enumerate() + .take(real_len) + { + let cols: &mut MemoryCols = row.borrow_mut(); + + let addr = unique_events[i + 1].addr; + let next_addr = unique_events[i + 2].addr; + + cols.is_last = Bool::from(addr != next_addr); + if addr != next_addr { + runtime + .last_memory_events + .push(unique_events[i + 1].clone()); + } + + cols.out_page_mult = cols.is_last.0 * cols.is_changed.0; + } + // Convert the trace to a row major matrix. RowMajorMatrix::new(rows, NUM_MEMORY_COLS) } diff --git a/core/src/prover/runtime.rs b/core/src/prover/runtime.rs index 0f7abe1faa..4dc66bc065 100644 --- a/core/src/prover/runtime.rs +++ b/core/src/prover/runtime.rs @@ -1,8 +1,10 @@ use crate::bytes::ByteChip; use crate::cpu::trace::CpuChip; +use crate::memory::state_old::MemoryStateChip; use crate::runtime::Runtime; use crate::program::ProgramChip; +use crate::prover::debug_cumulative_sums; use crate::prover::generate_permutation_trace; use crate::prover::quotient_values; use crate::utils::AirChip; @@ -19,8 +21,6 @@ use p3_matrix::Matrix; use p3_uni_stark::StarkConfig; use p3_util::log2_strict_usize; -use crate::prover::debug_cumulative_sums; - impl Runtime { /// Prove the program. #[allow(unused)] @@ -35,6 +35,7 @@ impl Runtime { let program = ProgramChip::new(); let cpu = CpuChip::new(); let memory = MemoryChip::new(); + // let mem_state_out = MemoryStateChip::Output; let add = AddChip::new(); let sub = SubChip::new(); let bitwise = BitwiseChip::new(); @@ -45,6 +46,7 @@ impl Runtime { Box::new(program), Box::new(cpu), Box::new(memory), + // Box::new(mem_state_out), Box::new(add), Box::new(sub), Box::new(bitwise), diff --git a/core/src/runtime/mod.rs b/core/src/runtime/mod.rs index 049a53dfce..8b1f2adf69 100644 --- a/core/src/runtime/mod.rs +++ b/core/src/runtime/mod.rs @@ -44,6 +44,9 @@ pub struct Runtime { /// A trace of the memory events which get emitted during execution. pub memory_events: Vec, + /// A trace of the last memory events. + pub last_memory_events: Vec, + /// A trace of the ADD, and ADDI events. pub add_events: Vec, @@ -79,6 +82,7 @@ impl Runtime { memory: BTreeMap::new(), cpu_events: Vec::new(), memory_events: Vec::new(), + last_memory_events: Vec::new(), add_events: Vec::new(), mul_events: Vec::new(), sub_events: Vec::new(),