Skip to content

Commit

Permalink
runtime: consolidate tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Aurélien Nicolas committed Sep 2, 2024
1 parent 8285d59 commit c7ae04b
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 66 deletions.
14 changes: 14 additions & 0 deletions ceno_emul/src/vm_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::{
platform::Platform,
rv32im::{DecodedInstruction, Emulator, Instruction, TrapCause},
tracer::{Change, StepRecord, Tracer},
Program,
};
use anyhow::{anyhow, Result};
use std::iter::from_fn;
Expand Down Expand Up @@ -35,6 +36,19 @@ impl VMState {
}
}

pub fn new_from_elf(platform: Platform, elf: &[u8]) -> Result<Self> {
let mut state = Self::new(platform);
let program = Program::load_elf(elf, u32::MAX).unwrap();
for (addr, word) in program.image.iter() {
let addr = ByteAddr(*addr).waddr();
state.init_memory(addr, *word);
}
if program.entry != state.platform.pc_start() {
return Err(anyhow!("Invalid entrypoint {:x}", program.entry));
}
Ok(state)
}

pub fn succeeded(&self) -> bool {
self.succeeded
}
Expand Down
Binary file modified ceno_emul/tests/data/ceno_rt_mem
Binary file not shown.
60 changes: 8 additions & 52 deletions ceno_emul/tests/test_elf.rs
Original file line number Diff line number Diff line change
@@ -1,76 +1,32 @@
use anyhow::Result;
use ceno_emul::{ByteAddr, Program, StepRecord, VMState, CENO_PLATFORM};
use ceno_emul::{ByteAddr, StepRecord, VMState, CENO_PLATFORM};

#[test]
fn test_ceno_rt_mini() -> Result<()> {
let mut state = VMState::new(CENO_PLATFORM);

// Load an ELF program in memory.
let program_elf = include_bytes!("./data/ceno_rt_mini");
let program = Program::load_elf(program_elf, u32::MAX)?;
for (addr, word) in program.image.iter() {
let addr = ByteAddr(*addr).waddr();
state.init_memory(addr, *word);
}
assert_eq!(program.entry, CENO_PLATFORM.pc_start());

let mut state = VMState::new_from_elf(CENO_PLATFORM, program_elf)?;
let _steps = run(&mut state)?;

Ok(())
}

#[test]
fn test_ceno_rt_panic() -> Result<()> {
let mut state = VMState::new(CENO_PLATFORM);

// Load an ELF program in memory.
let program_elf = include_bytes!("./data/ceno_rt_panic");
let program = Program::load_elf(program_elf, u32::MAX)?;
for (addr, word) in program.image.iter() {
let addr = ByteAddr(*addr).waddr();
state.init_memory(addr, *word);
}
assert_eq!(program.entry, CENO_PLATFORM.pc_start());

let mut state = VMState::new_from_elf(CENO_PLATFORM, program_elf)?;
let res = run(&mut state);
assert!(matches!(res, Err(e) if e.to_string().contains("EcallError")));

assert!(matches!(res, Err(e) if e.to_string().contains("EcallError")));
Ok(())
}

#[test]
fn test_ceno_rt_mem() -> Result<()> {
let mut state = VMState::new(CENO_PLATFORM);

// Load an ELF program in memory.
let program_elf = include_bytes!("./data/ceno_rt_mem");
let program = Program::load_elf(program_elf, u32::MAX)?;
for (addr, word) in program.image.iter() {
let addr = ByteAddr(*addr).waddr();
state.init_memory(addr, *word);
}
assert_eq!(program.entry, CENO_PLATFORM.pc_start());

let mut prev_step = StepRecord::default();
for step in state.iter_until_success() {
match step {
Ok(step) => {
// println!("{:?}", step);
prev_step = step;
}
Err(e) => {
println!("pc = {:?}", prev_step.pc().after);
return Err(e);
}
}
}

for i in 0..4 {
let addr = ByteAddr(CENO_PLATFORM.ram_start()).waddr() + i as u32;
let value = state.peek_memory(addr);
println!("{:?} = 0x{:08x}", addr, value);
}
let mut state = VMState::new_from_elf(CENO_PLATFORM, program_elf)?;
let _steps = run(&mut state)?;

let value = state.peek_memory(ByteAddr(CENO_PLATFORM.ram_start()).waddr());
assert_eq!(value, 6765, "Expected Fibonacci 20, got {}", value);
Ok(())
}

Expand Down
48 changes: 34 additions & 14 deletions ceno_rt/examples/ceno_rt_mem.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,54 @@
#![no_main]
#![no_std]
use core::ptr::addr_of_mut;

// Use volatile functions to prevent compiler optimizations.
use core::ptr::{read_volatile, write_volatile};

#[allow(unused_imports)]
use ceno_rt;
const OUTPUT_ADDRESS: u32 = 0x8000_0000;

#[no_mangle]
#[inline(never)]
fn main() {
let y = my_recurse(3, 0);
output(y);
test_data_section();

let out = fibonacci_recurse(20, 0, 1);
test_output(out);
}

/// Test the .data section is loaded and read/write works.
#[inline(never)]
fn test_data_section() {
// Use X[1] to be sure it is not the same as *OUTPUT_ADDRESS.
static mut X: [u32; 2] = [0, 42];

unsafe {
assert_eq!(read_volatile(&X[1]), 42);
write_volatile(&mut X[1], 99);
assert_eq!(read_volatile(&X[1]), 99);
}
}

// A sufficiently complicated function to test the stack.
#[inline(never)]
#[no_mangle]
fn my_recurse(x: u32, y: u32) -> u32 {
if x == 0 {
y
fn fibonacci_recurse(count: u32, a: u32, b: u32) -> u32 {
let count = black_box(count);
if count == 0 {
a
} else {
my_recurse(x - 1, y * 3 + 5)
fibonacci_recurse(count - 1, b, a + b)
}
}

// A global variable to test writing to memory.
static mut OUTPUT: u32 = 0;

// Store the output to a specific memory location so the emulator tests can find it.
#[inline(never)]
fn output(out: u32) {
fn test_output(out: u32) {
unsafe {
// Volatile write to prevent the compiler from optimizing this away.
core::ptr::write_volatile(addr_of_mut!(OUTPUT), out);
write_volatile(OUTPUT_ADDRESS as *mut u32, out);
}
}

fn black_box<T>(x: T) -> T {
unsafe { read_volatile(&x) }
}

0 comments on commit c7ae04b

Please sign in to comment.