Skip to content

Commit

Permalink
emul-trace-mem: Keep track of previous memory op (#184)
Browse files Browse the repository at this point in the history
_Issue #111, part 2._

- Extend the trace with links from each step to the previous steps that
last touched the same memory addresses.
- A lot of refactoring to avoid having `(u32, u32, u32)` everywhere.
- **[See
Documentation](https://github.com/scroll-tech/ceno/pull/184/files#diff-1068ec7481772ce29c055d915f4ff8449817856b0bec6229b7c30db3be593593R10)**

---------

Co-authored-by: Aurélien Nicolas <[email protected]>
  • Loading branch information
2 people authored and hero78119 committed Sep 30, 2024
1 parent 5f7dba0 commit d472e27
Show file tree
Hide file tree
Showing 7 changed files with 407 additions and 198 deletions.
34 changes: 31 additions & 3 deletions ceno_emul/src/addr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,17 @@ use std::{fmt, ops};

pub const WORD_SIZE: usize = 4;

// Type aliases to clarify the code without wrapper types.
pub type Word = u32;
pub type Addr = u32;
pub type Cycle = u64;
pub type RegIdx = usize;

#[derive(Clone, Copy, Default, PartialEq)]
#[derive(Clone, Copy, Default, PartialEq, Eq, Hash)]
pub struct ByteAddr(pub u32);

#[derive(Clone, Copy, Default, PartialEq)]
pub struct WordAddr(pub u32);
#[derive(Clone, Copy, Default, PartialEq, Eq, Hash)]
pub struct WordAddr(u32);

impl From<ByteAddr> for WordAddr {
fn from(addr: ByteAddr) -> Self {
Expand All @@ -38,6 +42,30 @@ impl From<WordAddr> for ByteAddr {
}
}

impl From<u32> for ByteAddr {
fn from(addr: u32) -> ByteAddr {
ByteAddr(addr)
}
}

impl From<u32> for WordAddr {
fn from(addr: u32) -> WordAddr {
ByteAddr(addr).waddr()
}
}

impl From<ByteAddr> for u32 {
fn from(addr: ByteAddr) -> Self {
addr.0
}
}

impl From<WordAddr> for u32 {
fn from(addr: WordAddr) -> Self {
addr.baddr().0
}
}

impl ByteAddr {
pub const fn waddr(self) -> WordAddr {
WordAddr(self.0 / WORD_SIZE as u32)
Expand Down
6 changes: 3 additions & 3 deletions ceno_emul/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
mod addr;
pub use addr::{ByteAddr, RegIdx, WordAddr};
pub use addr::*;

mod platform;
pub use platform::{Platform, CENO_PLATFORM};

mod tracer;
pub use tracer::{Change, StepRecord};
pub use tracer::{Change, MemOp, ReadOp, StepRecord, Tracer, WriteOp};

mod vm_state;
pub use vm_state::VMState;

mod rv32im;
pub use rv32im::{DecodedInstruction, InsnCategory, InsnKind};
pub use rv32im::{DecodedInstruction, EmuContext, InsnCategory, InsnKind};
66 changes: 52 additions & 14 deletions ceno_emul/src/platform.rs
Original file line number Diff line number Diff line change
@@ -1,74 +1,112 @@
use crate::addr::{Addr, RegIdx};

/// The Platform struct holds the parameters of the VM.
/// It defines:
/// - the layout of virtual memory,
/// - special addresses, such as the initial PC,
/// - codes of environment calls.
pub struct Platform;

pub const CENO_PLATFORM: Platform = Platform;

impl Platform {
// Virtual memory layout.

pub fn rom_start(&self) -> u32 {
pub const fn rom_start(&self) -> Addr {
0x2000_0000
}

pub fn rom_end(&self) -> u32 {
pub const fn rom_end(&self) -> Addr {
0x3000_0000 - 1
}

pub fn is_rom(&self, addr: u32) -> bool {
pub fn is_rom(&self, addr: Addr) -> bool {
(self.rom_start()..=self.rom_end()).contains(&addr)
}

pub fn ram_start(&self) -> u32 {
pub const fn ram_start(&self) -> Addr {
0x8000_0000
}

pub fn ram_end(&self) -> u32 {
pub const fn ram_end(&self) -> Addr {
0xFFFF_FFFF
}

pub fn is_ram(&self, addr: u32) -> bool {
pub fn is_ram(&self, addr: Addr) -> bool {
(self.ram_start()..=self.ram_end()).contains(&addr)
}

/// Virtual address of a register.
pub const fn register_vma(&self, index: RegIdx) -> Addr {
// Register VMAs are aligned, cannot be confused with indices, and readable in hex.
(index << 8) as Addr
}

/// Virtual address of the program counter.
pub const fn pc_vma(&self) -> Addr {
self.register_vma(32)
}

// Startup.

pub fn pc_start(&self) -> u32 {
pub const fn pc_start(&self) -> Addr {
self.rom_start()
}

// Permissions.

pub fn can_read(&self, addr: u32) -> bool {
pub fn can_read(&self, addr: Addr) -> bool {
self.is_rom(addr) || self.is_ram(addr)
}

pub fn can_write(&self, addr: u32) -> bool {
pub fn can_write(&self, addr: Addr) -> bool {
self.is_ram(addr)
}

pub fn can_execute(&self, addr: u32) -> bool {
pub fn can_execute(&self, addr: Addr) -> bool {
self.is_rom(addr)
}

// Environment calls.

/// Register containing the ecall function code. (x5, t0)
pub fn reg_ecall(&self) -> usize {
pub const fn reg_ecall(&self) -> RegIdx {
5
}

/// Register containing the first function argument. (x10, a0)
pub fn reg_arg0(&self) -> usize {
pub const fn reg_arg0(&self) -> RegIdx {
10
}

/// The code of ecall HALT.
pub fn ecall_halt(&self) -> u32 {
pub const fn ecall_halt(&self) -> u32 {
0
}

/// The code of success.
pub fn code_success(&self) -> u32 {
pub const fn code_success(&self) -> u32 {
0
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_no_overlap() {
let p = CENO_PLATFORM;
assert!(p.can_execute(p.pc_start()));
// ROM and RAM do not overlap.
assert!(!p.is_rom(p.ram_start()));
assert!(!p.is_rom(p.ram_end()));
assert!(!p.is_ram(p.rom_start()));
assert!(!p.is_ram(p.rom_end()));
// Registers do not overlap with ROM or RAM.
for reg in [p.pc_vma(), p.register_vma(0), p.register_vma(31)] {
assert!(!p.is_rom(reg));
assert!(!p.is_ram(reg));
}
}
}
Loading

0 comments on commit d472e27

Please sign in to comment.