From c7ae04bbf420af9579937e93004bf106831509fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Mon, 2 Sep 2024 18:01:42 +0200 Subject: [PATCH] runtime: consolidate tests --- ceno_emul/src/vm_state.rs | 14 ++++++++ ceno_emul/tests/data/ceno_rt_mem | Bin 9520 -> 10748 bytes ceno_emul/tests/test_elf.rs | 60 +++++-------------------------- ceno_rt/examples/ceno_rt_mem.rs | 48 +++++++++++++++++-------- 4 files changed, 56 insertions(+), 66 deletions(-) diff --git a/ceno_emul/src/vm_state.rs b/ceno_emul/src/vm_state.rs index 92d0b33a4..f677a29ae 100644 --- a/ceno_emul/src/vm_state.rs +++ b/ceno_emul/src/vm_state.rs @@ -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; @@ -35,6 +36,19 @@ impl VMState { } } + pub fn new_from_elf(platform: Platform, elf: &[u8]) -> Result { + 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 } diff --git a/ceno_emul/tests/data/ceno_rt_mem b/ceno_emul/tests/data/ceno_rt_mem index 6cc8681c2421c6f2b174e939540d6efde6daf269..6d266c4447dce7a75dad36b3c837141cac7166dd 100755 GIT binary patch literal 10748 zcmeHNU5Fc16h4#PiK|^|+AMZO!D+KCRl6bclVl@^E$-F|i?*Ur^NVs1Rs2`52YfAThKS3eezAj7eR_vL9y{WcP87}bXye!U*^KO_kQO) zKX=YeCOdcMt%b#dk|fcvrRZmhkYYeNzc*wIQb?w~w1w^u`Havj=#?HESY%c~&;Gq_ zLdTr)3oy$#WXK~Nw;sy`I+iqq`6J&4Ri2JE!9zJl)H$r&W_M!B!tptG(vk`!6-X+O zR3NE9Qh}rbNd=M$Bo#<1kW?V4KvIE?6}YgCj`dfh-VTW_W~G%uc3h%1p5C*PG|0*; z)R(W*1-Sgs+dsv++@5U_rc|V#IICPIaMBJunAd4+67ySSnR;h;FHkmpF_qhOg`D@k zO7-PJI=Aa8jX|H=b%~s*t0~x~>OG>|j70EJ`m#Kol@GvgCCan*+ih-Pgri@1O<8%r zl-qNO&hEZW&Z?a11Hr;ZqD;4rXNG5Vasb{{h<|UH8nrUM>vB28e`@z zJliBKsX$VJqyk9=k_sdhNGgz2AgMr7fusUS1^%B3@H42t+zlM%gzI;`c1uz7R1yBl{z;TsQed>Qs}{1Nap>^IaW=Kop5 z$K&mb@n3+C<1c{^gOAsbaoj(SZ$|ca<5_=f#Gm&kZhtTQJL2Dq=QyWE;`L&90RMCA zI7Kqfe9(HL$Np>yaR~AGj3(=N+u!h`_B;Ni{iy$cjh`G@k2-?OBlu}xV|@{6dUgY! zrsqfSizE1z5&ZfHwnlJ$1HLh60szE%P~r-WmU>*wP>hz zZPuwT=L?iy>^Q#LFqgan&79hxP|1Al1;c6kZlzy{g(GfX2C1HFg1N85bLv zToSYEIstxH*xAwM?5bYW^jfi|TeXEv{A!-vZds1wnZD~R`(2k;?YcF~nsdr-vFy|e zoB63CUwb*|EC=u?T6$U6tWwFX8oJ?>#4`0w(%retqj0aFIJH{Y)(Zu_;#8`_-QZdk z#gCUVYq=&qDGk@`29_U$R#**ei=sz%%*&!jILt*nO;~Coo+jdHIFl z#~_@C?fCmXpcEfl_0ONM=!tPou!O-q62Uq>6h2mVczhEV3kq}avG>H_N6r>6b6%`dl6h%sv6(UUp!*{N2@D2?X)jiUAeCM2def@d7 z(#)P8E_S}sG>vjuq8F4Q;UM|AnpPYVRHCzVfcB;JvdjxuaSMi3hZ6E~ywP*=9Q906MO~t59(CWU(PFcj?ypsue5!8mHs@TzpmWY zX+0r&s8dqC_dv!KO`BKG9Hwe*3u{cO;EVb;ehj#a>;IiR`akvOURCrGZ7&f;$m1GH z6a$I@#eiZ!F`yVw3@8Q^1BwB~fMP%~pcwc_41}{N_QggJMuW*%SVpUDnDy~sy&n%Z z#o1tOrQ7cLYqqdW+o_vQ-F7}F>I}x$gYXN{>0A)jieVWJofkN8IEefW(vBf4mGy9= zVGr!7<&NyBiPU>i{9k^FcILITL!?flY|&fXL}5SX4f6VV1S`M?;g_<>fD?uK2>BxB zb==QBpCl;a5ZNNWA^Rvv=MFHH0J%_De}4i0C_5-g-5&Z00nbpRQlg*pQ2G)*T)@9A z;NKVU9}D=&0)Dy+@62~W@cV36VrTvwnE4U#M@XZ8;Q9W*fyDp8Lj;>L>aRH1pWa^? zj33fCL66v!vHxelCCcmI%ZP)UF&RBC_Q>c)5gGnuGz!MZ$&ZTR#c??3&f);2Xmb>M zUHE0~gDgRmwG{`IsKpdWZsegGdR{K#0dSsTIgI6S+P3!a~peX!hzpX*qXTj(oF z-uDTTcqe&ghb1AA&gn=5llL7#%==~UG;!Z0;9R)IdJ>Ag-rf?=0i8lF7w%!HL$k9< uJQPI#qR40Y6w24-BlF*k>}-gLuAL_OT1KhK_xlpC*moa&)x3yW9=-u97}{(A diff --git a/ceno_emul/tests/test_elf.rs b/ceno_emul/tests/test_elf.rs index 9e0f516d3..d813ffc55 100644 --- a/ceno_emul/tests/test_elf.rs +++ b/ceno_emul/tests/test_elf.rs @@ -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(()) } diff --git a/ceno_rt/examples/ceno_rt_mem.rs b/ceno_rt/examples/ceno_rt_mem.rs index 746bdae5e..382ce78c8 100644 --- a/ceno_rt/examples/ceno_rt_mem.rs +++ b/ceno_rt/examples/ceno_rt_mem.rs @@ -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(x: T) -> T { + unsafe { read_volatile(&x) } +}