ni_fpga_interface/
fifos.rs

1//! Provides the high level interface for DMA FIFOs.
2
3use crate::error::FPGAError;
4use crate::nifpga_sys::*;
5use crate::session::{FifoInterface, FifoReadRegion, FifoWriteRegion, NativeFpgaType, Session};
6use std::marker::PhantomData;
7use std::time::Duration;
8
9/// The elements that are common between read and write FIFOs.
10pub trait Fifo {
11    fn address(&self) -> FifoAddress;
12
13    /// Begins DMA data transfer between the FPGA target and the host computer. This method is optional.
14    /// This method is optional as the DMA is automatically started when you attempt to read or write.
15    ///
16    /// You might want to use this method if:
17    /// *  you want to start data transfer with the DMA FIFO before you read the first element of the FIFO.
18    /// * You have reconfigured the FIFO with [`Fifo::configure`] and you want to force the buffer size to be committed.
19    ///
20    /// ```rust
21    /// # use ni_fpga_interface::fifos::{ ReadFifo, Fifo};
22    /// # use ni_fpga_interface::session::Session;
23    ///
24    ///
25    /// let session = Session::new("main.lvbitx", "sig", "RIO0").unwrap();
26    /// let mut fifo = ReadFifo::<u64>::new(1);
27    /// fifo.start(&session).unwrap();
28    ///
29    /// ```
30    fn start(&mut self, session: &Session) -> Result<(), FPGAError> {
31        session.start_fifo(self.address())
32    }
33
34    /// Stops the DMA data transfer between the FPGA target and the host computer.
35    /// This method deletes all data from the host memory and FPGA parts of the FIFO.
36    ///
37    /// This method is optional. Most applications do not require using the Stop method.
38    /// ```rust
39    /// # use ni_fpga_interface::fifos::{ ReadFifo, Fifo};
40    /// # use ni_fpga_interface::session::Session;
41    ///
42    ///
43    /// let session = Session::new("main.lvbitx", "sig", "RIO0").unwrap();
44    /// let mut fifo = ReadFifo::<u64>::new(1);
45    /// fifo.stop(&session).unwrap();
46    ///
47    /// ```
48    fn stop(&mut self, session: &Session) -> Result<(), FPGAError> {
49        session.stop_fifo(self.address())
50    }
51
52    /// Specifies the capacity, or depth, in elements of the host FIFO of the DMA channel.
53    /// The new depth is implemented when the next FIFO Start, FIFO Read, or FIFO Write method executes.
54    /// Before the new depth is set, the driver empties all data from the host memory and FPGA FIFO.
55    ///
56    /// This method is optional as the buffer is set by default to 10000 elements or twice the size of the FPGA buffer size.
57    ///
58    /// NI recommend this is set to 5 times the number of elements you specify to read and write.
59    ///
60    /// This method returns the actual size configured which may be larger than the request.
61    ///
62    /// ```rust
63    /// # use ni_fpga_interface::fifos::{ ReadFifo, Fifo};
64    /// # use ni_fpga_interface::session::Session;
65    ///
66    ///
67    /// let session = Session::new("main.lvbitx", "sig", "RIO0").unwrap();
68    /// let mut fifo = ReadFifo::<u64>::new(1);
69    ///
70    /// let configured_depth = fifo.configure(&session, 10_000).unwrap();
71    /// // Start to apply the config.
72    /// fifo.start(&session).unwrap();
73    ///
74    /// ```
75    fn configure(&mut self, session: &Session, requested_depth: usize) -> Result<usize, FPGAError> {
76        session.configure_fifo(self.address(), requested_depth)
77    }
78
79    fn get_peer_to_peer_fifo_endpoint(
80        &self,
81        session: &Session,
82    ) -> Result<PeerToPeerEndpoint, FPGAError> {
83        session.get_peer_to_peer_fifo_endpoint(self.address())
84    }
85}
86
87/// A FIFO that can be read from.
88pub struct ReadFifo<T: NativeFpgaType> {
89    address: FifoAddress,
90    phantom: PhantomData<T>,
91}
92
93// check the 'static - should never have a lifetime in this - can we remove it somehow?
94impl<T: NativeFpgaType + 'static> ReadFifo<T> {
95    pub const fn new(address: FifoAddress) -> Self {
96        Self {
97            address,
98            phantom: PhantomData,
99        }
100    }
101
102    /// Read from the FIFO into the provided buffer.
103    /// The size of the read is determined by the size of the data slice.
104    ///
105    /// The timeout can be [`None`] to indicate an infinite timeout or a [`Duration`] to indicate a timeout.
106    ///
107    /// Returns the number of elements still to be read.
108    ///
109    /// # Example
110    ///
111    /// ```rust
112    /// # use ni_fpga_interface::fifos::{ ReadFifo, Fifo};
113    /// # use ni_fpga_interface::session::Session;
114    /// use std::time::Duration;
115    ///
116    /// let session = Session::new("main.lvbitx", "sig", "RIO0").unwrap();
117    /// let mut fifo = ReadFifo::<u64>::new(1);
118    /// let mut buffer = [0u64; 10];
119    /// let remaining = fifo.read(&session, Some(Duration::from_millis(100)), &mut buffer).unwrap();
120    /// ```
121    pub fn read(
122        &mut self,
123        session: &impl FifoInterface<T>,
124        timeout: Option<Duration>,
125        data: &mut [T],
126    ) -> Result<usize, FPGAError> {
127        session.read_fifo(self.address, data, timeout)
128    }
129
130    /// Provides a mechanism to read from the FIFO without copying the data.
131    ///
132    /// This function returns a read region. This contains a view of the data in the DMA driver.
133    /// It also returns the number of elements remaining in the buffer.
134    ///
135    /// The timeout can be [`None`] to indicate an infinite timeout or a [`Duration`] to indicate a timeout.
136    ///
137    /// To write to the FPGA you must overwrite the elements in the region and then drop it.
138    ///
139    /// # Example
140    /// ```rust
141    /// # use ni_fpga_interface::fifos::{ ReadFifo, Fifo};
142    /// # use ni_fpga_interface::session::Session;
143    ///
144    /// let session = Session::new("main.lvbitx", "sig", "RIO0").unwrap();
145    /// let mut fifo = ReadFifo::<u64>::new(1);
146    /// let (read_region, remaining) = fifo.get_read_region(&session, 1000, None).unwrap();
147    /// // Do something with the data in the read region.
148    /// println!("{:?}", read_region.elements);
149    /// // Drop the read region to commit the data back to the DMA driver.
150    /// drop(read_region);
151    /// ```
152    pub fn get_read_region<'d, 's: 'd>(
153        &'d mut self,
154        session: &'s impl FifoInterface<T>,
155        elements: usize,
156        timeout: Option<Duration>,
157    ) -> Result<(FifoReadRegion<'s, 'd, T>, usize), FPGAError> {
158        session.zero_copy_read(self.address, elements, timeout)
159    }
160
161    /// Returns the number of elements available to read.
162    ///
163    /// Warning: This achieves this by reading zero elements from the FIFO so it will start the FIFO if stopped.
164    pub fn elements_available(&self, session: &impl FifoInterface<T>) -> Result<usize, FPGAError> {
165        let mut empty_buffer: [T; 0] = [];
166        session.read_fifo(self.address, &mut empty_buffer, None)
167    }
168}
169
170impl<T: NativeFpgaType> Fifo for ReadFifo<T> {
171    fn address(&self) -> FifoAddress {
172        self.address
173    }
174}
175
176/// A FIFO that can be written to.
177pub struct WriteFifo<T: NativeFpgaType> {
178    address: FifoAddress,
179    phantom: PhantomData<T>,
180}
181
182// see note on 'static above. should try to remove it.
183impl<T: NativeFpgaType + 'static> WriteFifo<T> {
184    pub const fn new(address: u32) -> Self {
185        Self {
186            address,
187            phantom: PhantomData,
188        }
189    }
190
191    /// Write to the FIFO from the provided buffer.
192    /// The size of the write is determined by the size of the data slice.
193    ///
194    /// The timeout can be [`None`] to indicate an infinite timeout or a [`Duration`] to indicate a timeout.
195    ///
196    /// Returns the number of elements free in the FIFO.
197    ///
198    /// # Example
199    /// ```rust
200    /// # use ni_fpga_interface::fifos::{ WriteFifo, Fifo};
201    /// # use ni_fpga_interface::session::Session;
202    /// use std::time::Duration;
203    ///
204    /// let session = Session::new("main.lvbitx", "sig", "RIO0").unwrap();
205    /// let mut fifo = WriteFifo::<u64>::new(1);
206    /// let buffer = [0u64; 10];
207    /// let remaining = fifo.write(&session, Some(Duration::from_millis(100)), &buffer).unwrap();
208    /// ```
209    pub fn write(
210        &mut self,
211        session: &impl FifoInterface<T>,
212        timeout: Option<Duration>,
213        data: &[T],
214    ) -> Result<usize, FPGAError> {
215        session.write_fifo(self.address, data, timeout)
216    }
217
218    /// Provides a way to get a reference to the write region of the FIFO.
219    ///
220    /// This enables you to write into the FIFO buffer without an additional copy.
221    ///
222    /// This function returns a write region. This contains a view of the data in the DMA driver.
223    /// It also returns the number of elements remaining in the buffer.
224    ///
225    /// The timeout can be [`None`] to indicate an infinite timeout or a [`Duration`] to indicate a timeout.
226    ///
227    /// To write to the FPGA you must overwrite the elements in the region and then drop it.
228    ///
229    /// # Example
230    /// ```rust
231    /// # use ni_fpga_interface::fifos::{ WriteFifo, Fifo};
232    /// # use ni_fpga_interface::session::Session;
233    ///
234    /// let session = Session::new("main.lvbitx", "sig", "RIO0").unwrap();
235    /// let mut fifo = WriteFifo::<u64>::new(1);
236    /// let (write_region, remaining) = fifo.get_write_region(&session, 1000, None).unwrap();
237    /// // Do something with the data in the write region.
238    /// write_region.elements[0] = 1;
239    /// // Drop the write region to commit the data back to the DMA driver.
240    /// drop(write_region);
241    /// ```
242    pub fn get_write_region<'d, 's: 'd>(
243        &'d mut self,
244        session: &'s impl FifoInterface<T>,
245        elements: usize,
246        timeout: Option<Duration>,
247    ) -> Result<(FifoWriteRegion<'s, 'd, T>, usize), FPGAError> {
248        session.zero_copy_write(self.address, elements, timeout)
249    }
250
251    /// Returns the number of elements free to write.
252    /// Warning: This achieves this by writing zero elements to the FIFO so it will start the FIFO if stopped.
253    pub fn space_available(&self, session: &impl FifoInterface<T>) -> Result<usize, FPGAError> {
254        let empty_buffer: [T; 0] = [];
255        session.write_fifo(self.address, &empty_buffer, None)
256    }
257}
258
259impl<T: NativeFpgaType> Fifo for WriteFifo<T> {
260    fn address(&self) -> FifoAddress {
261        self.address
262    }
263}