use super::{
Transport,
TransportResult,
};
use crate::core::{
Register,
RegisterMap,
};
use casper_utils::design_sources::FpgaDesign;
use std::collections::HashMap;
use thiserror::Error;
#[derive(Debug)]
pub struct Mock {
memory: HashMap<usize, u8>,
registers: RegisterMap,
}
#[derive(Debug, Error)]
pub enum Error {
#[error("Tried to interact with an address that doesn't exist")]
Addressing,
}
impl Mock {
#[must_use]
pub fn new(registers: RegisterMap) -> Self {
let mut memory: HashMap<usize, u8> = HashMap::default();
for Register { addr, length } in registers.values() {
for i in 0..*length {
memory.insert(addr + i, 0u8);
}
}
Self { memory, registers }
}
}
impl Transport for Mock {
fn is_running(&mut self) -> TransportResult<bool> {
Ok(true)
}
fn read_n_bytes(&mut self, device: &str, offset: usize, n: usize) -> TransportResult<Vec<u8>> {
let dev = self
.registers
.get(device)
.ok_or_else(|| super::Error::DeviceNotFound(device.to_string()))?;
let mut bytes = vec![0u8; n];
for i in offset..(offset + n) {
let byte = self.memory.get(&(dev.addr + i)).ok_or(Error::Addressing)?;
bytes[i - offset] = *byte;
}
Ok(bytes)
}
fn read<T, const N: usize>(&mut self, device: &str, offset: usize) -> TransportResult<T>
where
T: super::Deserialize<Chunk = [u8; N]>,
super::Error: std::convert::From<<T as super::Deserialize>::Error>,
{
let bytes: [u8; N] = self.read_bytes(device, offset)?;
Ok(T::deserialize(bytes)?)
}
fn write_bytes(&mut self, device: &str, offset: usize, data: &[u8]) -> TransportResult<()> {
let dev = self
.registers
.get(device)
.ok_or_else(|| super::Error::DeviceNotFound(device.to_string()))?;
if dev.length - offset < data.len() {
return Err(Error::Addressing.into());
}
for (i, byte) in data.iter().enumerate() {
self.memory.insert(dev.addr + i + offset, *byte);
}
Ok(())
}
fn write<T, const N: usize>(
&mut self,
device: &str,
offset: usize,
data: &T,
) -> TransportResult<()>
where
T: super::Serialize<Chunk = [u8; N]>,
{
self.write_bytes(device, offset, &data.serialize())
}
fn listdev(&mut self) -> TransportResult<RegisterMap> {
Ok(self.registers.clone())
}
fn program<D>(&mut self, _design: &D, _force: bool) -> TransportResult<()>
where
D: FpgaDesign,
{
todo!()
}
fn deprogram(&mut self) -> TransportResult<()> {
todo!()
}
}
#[cfg(test)]
mod tests {
use super::*;
use paste::paste;
macro_rules! test_rw_num {
($num:ty, $v:literal) => {
paste! {
#[test]
#[allow(clippy::float_cmp)]
fn [<test_rw_$num>]() {
let mut transport = Mock::new(HashMap::from([(
"sys_scratchpad".into(),
Register { addr: 0, length: core::mem::size_of::<$num>() },
)]));
let num: $num = $v;
transport.write("sys_scratchpad", 0, &num).unwrap();
let read_num: $num = transport.read("sys_scratchpad", 0).unwrap();
assert_eq!(read_num, num);
}
}
};
}
#[test]
fn test_read() {
let mut transport = Mock::new(HashMap::from([(
"sys_scratchpad".into(),
Register { addr: 0, length: 4 },
)]));
let bytes = transport.read_bytes("sys_scratchpad", 0).unwrap();
assert_eq!(bytes, [0, 0, 0, 0]);
}
#[test]
fn test_read_offset() {
let mut transport = Mock::new(HashMap::from([(
"sys_scratchpad".into(),
Register { addr: 0, length: 4 },
)]));
let bytes = transport.read_bytes("sys_scratchpad", 2).unwrap();
assert_eq!(bytes, [0, 0]);
}
#[test]
fn test_write_read() {
let mut transport = Mock::new(HashMap::from([(
"sys_scratchpad".into(),
Register { addr: 0, length: 4 },
)]));
let write_bytes = [1, 2, 3, 4];
transport
.write_bytes("sys_scratchpad", 0, &write_bytes)
.unwrap();
let read_bytes = transport.read_bytes("sys_scratchpad", 0).unwrap();
assert_eq!(read_bytes, write_bytes);
}
#[test]
fn test_write_read_offset() {
let mut transport = Mock::new(HashMap::from([(
"sys_scratchpad".into(),
Register { addr: 0, length: 4 },
)]));
let write_bytes = [7, 8];
transport
.write_bytes("sys_scratchpad", 2, &write_bytes)
.unwrap();
let read_bytes = transport.read_bytes("sys_scratchpad", 0).unwrap();
assert_eq!(read_bytes, [0, 0, 7, 8]);
let read_bytes = transport.read_bytes("sys_scratchpad", 2).unwrap();
assert_eq!(read_bytes, [7, 8]);
}
#[test]
fn test_const_size() {
let mut transport = Mock::new(HashMap::from([(
"sys_scratchpad".into(),
Register { addr: 0, length: 4 },
)]));
let write_bytes = [1, 2, 3, 4];
transport
.write_bytes("sys_scratchpad", 0, &write_bytes)
.unwrap();
let read_bytes = transport.read_bytes("sys_scratchpad", 0).unwrap();
assert_eq!(read_bytes, write_bytes);
}
test_rw_num!(u8, 42);
test_rw_num!(u16, 0xDEAD);
test_rw_num!(u32, 0xDEAD_BEEF);
test_rw_num!(u64, 0xDEAD_BEEF_B0BA_CAFE);
test_rw_num!(u128, 0xDEAD_BEEF_B0BA_CAFE_0000_0000_0000);
test_rw_num!(i8, -42);
test_rw_num!(i16, -0xDEA);
test_rw_num!(i32, -0x0DEA_DBEE);
test_rw_num!(i64, -0x0DEA_DBEE_FB0B_ACAF);
test_rw_num!(i128, -0xDEAD_BEEF_B0BA_CAFE_0000_0000_0000);
test_rw_num!(f32, 1.618);
test_rw_num!(f64, -6.022e23);
}