Skip to main content

warp_devices/
xdma.rs

1use std::fs::File;
2use std::io::Error as IoError;
3use std::mem;
4use std::os::unix::fs::FileExt;
5
6/// Memory alignment for optimal performance of DMA reads and writes.
7pub const DMA_ALIGNMENT: u64 = 4096;
8
9pub type Result<T> = std::result::Result<T, Error>;
10
11#[derive(Debug)]
12pub enum Error {
13    ShellReadFailed(IoError),
14    ShellWriteFailed(IoError),
15    DmaReadFailed(IoError),
16    DmaWriteFailed(IoError),
17}
18
19#[repr(C, align(4096))]
20struct Align4K([u8; 4096]);
21
22/// DMA-engine aligned buffer. Non-reallocatable since reallocations do not preserve alignment. The
23/// size has to be known before creation.
24#[derive(Debug)]
25pub struct DmaBuffer(Vec<u8>);
26
27impl DmaBuffer {
28    pub fn new(n_bytes: usize) -> Self {
29        Self(unsafe { aligned_vec(n_bytes) })
30    }
31
32    pub fn as_slice(&self) -> &[u8] {
33        self.0.as_slice()
34    }
35
36    pub fn as_mut_slice(&mut self) -> &mut [u8] {
37        self.0.as_mut_slice()
38    }
39
40    pub fn get(&self) -> &Vec<u8> {
41        &self.0
42    }
43
44    pub fn get_mut(&mut self) -> &mut Vec<u8> {
45        &mut self.0
46    }
47}
48
49unsafe fn aligned_vec(n_bytes: usize) -> Vec<u8> {
50    let n_units = (n_bytes / mem::size_of::<Align4K>()) + 1;
51
52    let mut aligned: Vec<Align4K> = Vec::with_capacity(n_units);
53
54    let ptr = aligned.as_mut_ptr();
55    let len_units = aligned.len();
56    let cap_units = aligned.capacity();
57
58    mem::forget(aligned);
59
60    Vec::from_raw_parts(
61        ptr as *mut u8,
62        len_units * mem::size_of::<Align4K>(),
63        cap_units * mem::size_of::<Align4K>(),
64    )
65}
66
67pub struct XdmaDevice {
68    /// Device ID
69    pub id: u32,
70    /// User character device
71    pub user_cdev: File,
72    /// Host to card character device
73    pub h2c_cdev: File,
74    /// Card to host character device
75    pub c2h_cdev: File,
76    /// Interrupt Controller
77    //
78    // TODO: create an INTC parameter trait and move it there as a trait const.
79    pub intc_base_addr: u32,
80    /// High Bandwidth Internal Configuration Access Port
81    //
82    // TODO: create an HBICAP parameter trait and move it there as a trait const.
83    pub hbicap_base_addr: u32,
84}
85
86pub trait XdmaOps {
87    fn shell_read(&self, buf: &mut [u8], offset: u64) -> Result<()>;
88    fn shell_write(&self, buf: &[u8], offset: u64) -> Result<()>;
89    fn dma_read(&self, buf: &mut DmaBuffer, offset: u64) -> Result<()>;
90    fn dma_write(&self, buf: &DmaBuffer, offset: u64) -> Result<()>;
91}
92
93impl XdmaOps for XdmaDevice {
94    fn shell_read(&self, buf: &mut [u8], offset: u64) -> Result<()> {
95        self.user_cdev
96            .read_exact_at(buf, offset)
97            .map_err(Error::ShellReadFailed)
98    }
99
100    fn shell_write(&self, buf: &[u8], offset: u64) -> Result<()> {
101        self.user_cdev
102            .write_all_at(buf, offset)
103            .map_err(Error::ShellWriteFailed)
104    }
105
106    fn dma_read(&self, buf: &mut DmaBuffer, offset: u64) -> Result<()> {
107        self.c2h_cdev
108            .read_exact_at(buf.as_mut_slice(), offset)
109            .map_err(Error::DmaReadFailed)
110    }
111
112    fn dma_write(&self, buf: &DmaBuffer, offset: u64) -> Result<()> {
113        self.h2c_cdev
114            .write_all_at(buf.as_slice(), offset)
115            .map_err(Error::DmaWriteFailed)
116    }
117}
118
119#[cfg(test)]
120mod test {
121    use super::*;
122
123    #[test]
124    fn dma_buffer_alignment() {
125        const BUF_LEN: usize = 42;
126
127        let mut buf = DmaBuffer::new(BUF_LEN);
128        buf.get_mut().extend_from_slice(&vec![0u8; BUF_LEN]);
129
130        let ptr = buf.as_mut_slice().as_mut_ptr();
131        let len = buf.get().len();
132        let cap = buf.get().capacity();
133
134        assert_eq!(ptr as u64 % DMA_ALIGNMENT, 0);
135        assert_eq!(len, BUF_LEN);
136        assert_eq!(cap, DMA_ALIGNMENT as usize);
137    }
138
139    #[test]
140    fn file_write_all_at() {
141        let n: u64 = rand::random();
142        let f = File::create(format!("/tmp/xdma-test-{:x}", n)).expect("cannot create file");
143        let buf = vec![b'A', b'B', b'C'];
144        f.write_all_at(buf.as_slice(), 0)
145            .expect("write test failed");
146    }
147}