diff --git a/difftest/dpi_t1rocket/src/bus.rs b/difftest/dpi_t1rocket/src/bus.rs new file mode 100644 index 000000000..01f2ec053 --- /dev/null +++ b/difftest/dpi_t1rocket/src/bus.rs @@ -0,0 +1,148 @@ +mod mem; + +use mem::*; +use tracing::{debug, error, trace}; + +trait ShadowDevice: Send + Sync { + fn new() -> Box + where + Self: Sized; + fn read_mem(&self, addr: usize, size: usize) -> &[u8]; + fn write_mem(&mut self, addr: usize, data: u8); + fn write_mem_chunk(&mut self, addr: usize, size: usize, strobe: Option<&[bool]>, data: &[u8]); +} + +struct ShadowBusDevice { + base: usize, + size: usize, + device: Box, +} + +const MAX_DEVICES: usize = 3; + +pub(crate) struct ShadowBus { + devices: [ShadowBusDevice; MAX_DEVICES], +} + +impl ShadowBus { + /// Initiate the devices on the bus as specified in `tests/t1.ld` + /// NOTE: For some reason DDR is not aligned in the address space + pub fn new() -> Self { + const DDR_SIZE: usize = 0x80000000; + const SCALAR_SIZE: usize = 0x20000000; + const SRAM_SIZE: usize = 0x00400000; + + Self { + devices: [ + ShadowBusDevice { + base: 0x20000000, + size: SCALAR_SIZE, + device: MemDevice::::new(), + }, + ShadowBusDevice { + base: 0x40000000, + size: DDR_SIZE, + device: MemDevice::::new(), + }, + ShadowBusDevice { + base: 0xc0000000, + size: SRAM_SIZE, + device: MemDevice::::new(), + }, + ], + } + } + + pub fn read_mem_axi(&self, addr: u32, size: u32, bus_size: u32) -> Vec { + assert!( + addr % size == 0 && bus_size % size == 0, + "unaligned access addr={addr:#x} size={size}B dlen={bus_size}B" + ); + + let start = addr as usize; + let end = (addr + size) as usize; + + let handler = self.devices.iter().find(|d| match d { + ShadowBusDevice { base, size, device: _ } => *base <= start && end <= (*base + *size), + }); + + match handler { + Some(ShadowBusDevice { base, size: _, device }) => { + let offset = start - *base; + let data = device.read_mem(offset, size as usize); + + if size < bus_size { + let mut data_padded = vec![0; bus_size as usize]; + let start = (addr % bus_size) as usize; + let end = start + data.len(); + data_padded[start..end].copy_from_slice(data); + + data_padded + } else { + data.to_vec() + } + } + None => { + error!("read addr={addr:#x} size={size}B dlen={bus_size}B leads to nowhere!"); + vec![0; bus_size as usize] + } + } + } + + pub fn write_mem_axi( + &mut self, + addr: u32, + size: u32, + bus_size: u32, + masks: &[bool], + data: &[u8], + ) { + assert!( + addr % size == 0 && bus_size % size == 0, + "unaligned write access addr={addr:#x} size={size}B dlen={bus_size}B" + ); + + if !masks.iter().any(|x| *x) { + trace!("Mask 0 write detected"); + return; + } + + let start = (addr & ((!bus_size) + 1)) as usize; + let end = start + bus_size as usize; + + let handler = self.devices.iter_mut().find(|d| match d { + ShadowBusDevice { base, size, device: _ } => *base <= start && end <= (*base + *size), + }); + + match handler { + Some(ShadowBusDevice { base, size: _, device }) => { + let offset = start - *base; + device.write_mem_chunk(offset, size as usize, Option::from(masks), data); + } + None => { + error!("write addr={addr:#x} size={size}B dlen={bus_size}B leads to nowhere!"); + } + } + } + + pub fn load_mem_seg(&mut self, vaddr: usize, data: &[u8]) { + let handler = self + .devices + .iter_mut() + .find(|d| match d { + ShadowBusDevice { base, size, device: _ } => { + *base <= vaddr as usize && (vaddr as usize + data.len()) <= (*base + *size) + } + }) + .unwrap_or_else(|| { + panic!( + "fail reading ELF into mem with vaddr={:#x}, len={}B: load memory to nowhere", + vaddr, + data.len() + ) + }); + + let offset = vaddr - handler.base; + handler.device.write_mem_chunk(offset, data.len(), None, data) + } +} diff --git a/difftest/dpi_t1rocket/src/bus/mem.rs b/difftest/dpi_t1rocket/src/bus/mem.rs new file mode 100644 index 000000000..20ba1ad9a --- /dev/null +++ b/difftest/dpi_t1rocket/src/bus/mem.rs @@ -0,0 +1,44 @@ +use super::ShadowDevice; + +pub(super) struct MemDevice { + mem: [u8; SIZE], +} + +impl ShadowDevice for MemDevice { + fn new() -> Box + where + Self: Sized, + { + Box::new(Self { mem: [0; SIZE] }) + } + + fn read_mem(&self, addr: usize, size: usize) -> &[u8] { + let start = addr; + let end = addr + size; + &self.mem[start..end] + } + + fn write_mem(&mut self, addr: usize, data: u8) { + self.mem[addr] = data; + } + + fn write_mem_chunk(&mut self, addr: usize, size: usize, strobe: Option<&[bool]>, data: &[u8]) { + assert_eq!( + addr % size, + 0, + "unaligned chunk write addr={addr:#x} size={size}B" + ); + + if let Some(masks) = strobe { + masks.iter().enumerate().for_each(|(i, mask)| { + if *mask { + self.mem[addr + i] = data[i]; + } + }) + } else { + let start = addr; + let end = addr + size; + self.mem[start..end].copy_from_slice(data); + } + } +} diff --git a/difftest/dpi_t1rocket/src/lib.rs b/difftest/dpi_t1rocket/src/lib.rs index b6821eef4..8709fbd2c 100644 --- a/difftest/dpi_t1rocket/src/lib.rs +++ b/difftest/dpi_t1rocket/src/lib.rs @@ -2,6 +2,7 @@ use std::path::PathBuf; use dpi_common::plusarg::PlusArgMatcher; +mod bus; pub mod dpi; pub mod drive;