diff --git a/difftest/dpi_t1rocketemu/src/dpi.rs b/difftest/dpi_t1rocketemu/src/dpi.rs index 8072a9e1a..1d8a9c7a7 100644 --- a/difftest/dpi_t1rocketemu/src/dpi.rs +++ b/difftest/dpi_t1rocketemu/src/dpi.rs @@ -5,7 +5,7 @@ use dpi_common::plusarg::PlusArgMatcher; use dpi_common::DpiTarget; use std::ffi::c_longlong; use svdpi::SvScope; -use tracing::{debug, info}; +use tracing::debug; use crate::drive::Driver; use crate::OnlineArgs; @@ -233,16 +233,6 @@ unsafe extern "C" fn axi_write_loadStoreAXI( driver.axi_write(awaddr as u32, awsize as u32, 32, strobe, data); driver.update_commit_cycle(); - - // TODO: move it to MMIO device - if awaddr as u32 == crate::EXIT_POS { - let exit_data = u32::from_le_bytes(data.try_into().expect("slice with incorrect length")); - if exit_data == crate::EXIT_CODE { - info!("driver is ready to quit"); - driver.success = true; - driver.quit = true; - } - } }); } @@ -341,7 +331,8 @@ unsafe extern "C" fn t1_cosim_init() { #[no_mangle] unsafe extern "C" fn t1_cosim_final() { TARGET.with(|driver| { - dpi_common::util::write_perf_json(crate::get_t(), driver.success); + let success = driver.exit_flag.is_finish(); + dpi_common::util::write_perf_json(crate::get_t(), success); }); } diff --git a/difftest/dpi_t1rocketemu/src/drive.rs b/difftest/dpi_t1rocketemu/src/drive.rs index 5e2774f38..2ed461b86 100644 --- a/difftest/dpi_t1rocketemu/src/drive.rs +++ b/difftest/dpi_t1rocketemu/src/drive.rs @@ -1,4 +1,5 @@ use crate::get_t; +use crate::interconnect::simctrl::ExitFlagRef; use crate::interconnect::{create_emu_addrspace, AddressSpace}; use crate::OnlineArgs; use svdpi::SvScope; @@ -37,13 +38,12 @@ pub(crate) struct Driver { addr_space: AddressSpace, - pub(crate) quit: bool, - pub(crate) success: bool, + pub(crate) exit_flag: ExitFlagRef, } impl Driver { pub(crate) fn new(scope: SvScope, args: &OnlineArgs) -> Self { - let mut addr_space = create_emu_addrspace(); + let (mut addr_space, exit_flag) = create_emu_addrspace(); // pass e_entry to rocket let (e_entry, _fn_sym_tab) = Self::load_elf(&args.elf_file, &mut addr_space).expect("fail creating simulator"); @@ -59,8 +59,7 @@ impl Driver { addr_space, - quit: false, - success: false, + exit_flag, } } @@ -198,7 +197,7 @@ impl Driver { let tick = get_t(); - if self.quit { + if self.exit_flag.is_finish() { trace!("[{tick}] watchdog quit"); return WATCHDOG_QUIT; } diff --git a/difftest/dpi_t1rocketemu/src/interconnect.rs b/difftest/dpi_t1rocketemu/src/interconnect.rs index 60cc24b01..ee65650f2 100644 --- a/difftest/dpi_t1rocketemu/src/interconnect.rs +++ b/difftest/dpi_t1rocketemu/src/interconnect.rs @@ -1,8 +1,10 @@ use std::any::Any; use framebuffer::FrameBuffer; +use simctrl::{ExitFlagRef, SimCtrl}; pub mod framebuffer; +pub mod simctrl; #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] pub struct AddrInfo { @@ -23,8 +25,8 @@ impl AddrInfo { // However, since the functions are safe, // even if contracts violate, implementions must not break memory safety, pub trait Device: Any + Send + Sync { - // It's OK to side have effect for mmio device - // Panic for bus error + /// It's OK to side have effect for mmio device + /// Panic for bus error fn mem_read(&mut self, addr: AddrInfo, data: &mut [u8]); // Behave as if `mem_write_masked` with full mask, @@ -48,6 +50,50 @@ pub trait DeviceExt: Device + Sized { } } +// Represents a MMIO devices consists of 4-byte 'registers'. +// Support only 4-byte aligned read/write, not support write mask +// `offset` is offset in bytes from base address, guaranteed to be multiple of 4. +// I choose offset in byte since it's usaully better aligned with document +pub trait RegDevice { + // Panic for bus error + fn reg_read(&mut self, offset: u32) -> u32; + + // Panic for bus error + fn reg_write(&mut self, offset: u32, value: u32); +} + +impl Device for T { + fn mem_read(&mut self, addr: AddrInfo, data: &mut [u8]) { + // allows only 4-byte aligned access + assert_eq!(4, addr.len); + assert!(addr.offset % 4 == 0); + + let data: &mut [u8; 4] = data.try_into().unwrap(); + let value = self.reg_read(addr.offset); + *data = u32::to_le_bytes(value); + } + + fn mem_write(&mut self, addr: AddrInfo, data: &[u8]) { + // allows only 4-byte aligned access + assert_eq!(4, addr.len); + assert!(addr.offset % 4 == 0); + + let value = u32::from_le_bytes(data.try_into().unwrap()); + self.reg_write(addr.offset, value); + } + + fn mem_write_masked(&mut self, addr: AddrInfo, data: &[u8], mask: &[bool]) { + // allows only 4-byte aligned access + assert_eq!(4, addr.len); + assert!(addr.offset % 4 == 0); + assert!(mask.iter().all(|&x| x)); + + let data: &[u8; 4] = data.try_into().unwrap(); + let value = u32::from_le_bytes(*data); + self.reg_write(addr.offset, value); + } +} + pub struct RegularMemory { data: Vec, } @@ -148,21 +194,28 @@ impl AddressSpace { /// Memory map: /// - 0x0400_0000 - 0x0600_0000 : framebuffer +/// - 0x4000_0000 - 0x4000_1000 : simctrl /// - 0x2000_0000 - 0xc000_0000 : ddr /// - 0xc000_0000 - 0xc040_0000 : sram -pub fn create_emu_addrspace() -> AddressSpace { +/// TODO: simctrl is inside ddr, move it elsewhere +pub fn create_emu_addrspace() -> (AddressSpace, ExitFlagRef) { const DDR_BASE: u32 = 0x2000_0000; const DDR_SIZE: u32 = 0xa000_0000; const SRAM_BASE: u32 = 0xc000_0000; const SRAM_SIZE: u32 = 0x0040_0000; + const SIMCTRL_BASE: u32 = 0x4000_0000; + const SIMCTRL_SIZE: u32 = 0x0000_1000; // one page const DISPLAY_BASE: u32 = 0x0400_0000; const DISPLAY_SIZE: u32 = 0x0200_0000; + let exit_flag = ExitFlagRef::new(); + let devices = vec![ + SimCtrl::new(exit_flag.clone()).with_addr(SIMCTRL_BASE, SIMCTRL_SIZE), RegularMemory::with_size(DDR_SIZE).with_addr(DDR_BASE, DDR_SIZE), RegularMemory::with_size(SRAM_SIZE).with_addr(SRAM_BASE, SRAM_SIZE), FrameBuffer::new().with_addr(DISPLAY_BASE, DISPLAY_SIZE), ]; - AddressSpace { devices } + (AddressSpace { devices }, exit_flag) } diff --git a/difftest/dpi_t1rocketemu/src/interconnect/simctrl.rs b/difftest/dpi_t1rocketemu/src/interconnect/simctrl.rs new file mode 100644 index 000000000..b84efbaef --- /dev/null +++ b/difftest/dpi_t1rocketemu/src/interconnect/simctrl.rs @@ -0,0 +1,60 @@ +use std::sync::{ + atomic::{AtomicU32, Ordering}, + Arc, +}; + +use tracing::{info, warn}; + +use super::RegDevice; + +#[derive(Default, Debug, Clone)] +pub struct ExitFlagRef(Arc); + +impl ExitFlagRef { + pub fn new() -> Self { + Self::default() + } + + pub fn is_finish(&self) -> bool { + self.0.load(Ordering::Acquire) != 0 + } + + pub fn mark_finish(&self) { + self.0.store(1, Ordering::Release); + } +} + +pub const EXIT_CODE: u32 = 0xdead_beef; + +/// Reg map: +/// - 0x0000 : WO, write EXIT_CODE to mark simulation finish +pub struct SimCtrl { + exit_flag: ExitFlagRef, +} + +impl SimCtrl { + pub fn new(exit_flag: ExitFlagRef) -> Self { + SimCtrl { exit_flag } + } +} + +impl RegDevice for SimCtrl { + fn reg_read(&mut self, offset: u32) -> u32 { + let _ = offset; + unimplemented!() + } + + fn reg_write(&mut self, reg_offset: u32, value: u32) { + match reg_offset { + 0 => { + if value == EXIT_CODE { + self.exit_flag.mark_finish(); + info!("simctrl: write EXIT_POS with EXIT_CODE, ready to quit"); + } else { + warn!("simctrl: write EXIT_POS with value 0x{value:08x}, ignored"); + } + } + _ => panic!(), + } + } +} diff --git a/difftest/dpi_t1rocketemu/src/lib.rs b/difftest/dpi_t1rocketemu/src/lib.rs index 447f4a03d..e1016145c 100644 --- a/difftest/dpi_t1rocketemu/src/lib.rs +++ b/difftest/dpi_t1rocketemu/src/lib.rs @@ -32,10 +32,6 @@ impl OnlineArgs { } } -// quit signal -pub const EXIT_POS: u32 = 0x4000_0000; -pub const EXIT_CODE: u32 = 0xdead_beef; - // keep in sync with TestBench.ClockGen // the value is measured in simulation time unit pub const CYCLE_PERIOD: u64 = 20000;