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}