use crate::transport::Transport;
use fixed::traits::Fixed;
use std::{
marker::PhantomData,
sync::{
Arc,
Mutex,
Weak,
},
};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum Error {
#[error(transparent)]
Transport(#[from] crate::transport::Error),
#[error("Out of bounds addressing")]
OutOfBounds,
#[error("Size of given data doesn't fit the target")]
BadSize,
#[error("Failed to parse addr_width from the fpg file")]
BadAddrWidth,
}
#[derive(Debug)]
pub struct Bram<T, F> {
transport: Weak<Mutex<T>>,
name: String,
phantom: PhantomData<F>,
size: usize,
}
impl<T, F> Bram<T, F>
where
T: Transport,
F: Fixed,
{
#[must_use]
pub fn new(transport: &Arc<Mutex<T>>, reg_name: &str, size: usize) -> Self {
let transport = Arc::downgrade(transport);
Self {
transport,
name: reg_name.to_string(),
phantom: PhantomData,
size,
}
}
pub fn from_fpg(
transport: Weak<Mutex<T>>,
reg_name: &str,
addr_width: &str,
) -> Result<Self, Error> {
Ok(Self {
transport,
name: reg_name.to_string(),
phantom: PhantomData,
size: 1
<< addr_width
.parse::<usize>()
.map_err(|_| Error::BadAddrWidth)?,
})
}
}
impl<T, F, const N: usize> Bram<T, F>
where
T: Transport,
F: Fixed<Bytes = [u8; N]>,
{
#[allow(clippy::missing_panics_doc)]
pub fn read_addr(&self, addr: usize) -> Result<F, Error> {
if addr >= self.size {
return Err(Error::OutOfBounds);
}
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
Ok(F::from_be_bytes(transport.read(&self.name, addr)?))
}
#[allow(clippy::missing_panics_doc)]
pub fn read(&self) -> Result<Vec<F>, Error> {
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
let total_bytes = self.size * N;
let v = transport.read_n_bytes(&self.name, 0, total_bytes)?;
Ok(v.chunks(N)
.map(|c| F::from_be_bytes(c.try_into().unwrap()))
.collect())
}
#[allow(clippy::missing_panics_doc)]
pub fn write(&self, data: &[F]) -> Result<(), Error> {
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
let total_bytes = self.size * N;
let v = data
.iter()
.flat_map(|f| f.to_be_bytes().to_vec())
.collect::<Vec<_>>();
if v.len() != total_bytes {
return Err(Error::BadSize);
}
transport.write_bytes(&self.name, 0, &v)?;
Ok(())
}
#[allow(clippy::missing_panics_doc)]
pub fn write_addr(&self, addr: usize, val: F) -> Result<(), Error> {
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
Ok(transport.write(&self.name, addr, &(val.to_be_bytes()))?)
}
}