Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

emul-trace-mem: Keep track of previous memory op #184

Merged
merged 14 commits into from
Sep 4, 2024
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 {
kunxian-xia marked this conversation as resolved.
Show resolved Hide resolved
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
Loading