Skip to content

Commit

Permalink
Add DMA Zero Copy Interface
Browse files Browse the repository at this point in the history
Added the zero copy interface but currently untested as it needs a local
FPGA target.
  • Loading branch information
JamesMc86 committed Sep 28, 2023
1 parent 7110f41 commit ae629e4
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 14 deletions.
49 changes: 49 additions & 0 deletions examples/host_example/examples/dmas_zero_copy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//! This example demonstrates the zero copy interface to the DMAs.
//!
//! This involves getting a read or write region from the DMA and writing to that.
//!
//! When it is dropped then that memory is committed back to the DMA.
mod fpga_defs {
include!(concat!(env!("OUT_DIR"), "/NiFpga_Main.rs"));
}

fn main() {
let session = host_example::connect_fpga();

// The FPGA bit file should take a stream of u32s and return the lower half of each.

let inputs = [0x12345678, 0x9ABCDEF0, 0x13579BDF, 0x2468ACE0];
let expected_outputs = [0x5678, 0xDEF0, 0x9BDF, 0xACE0];

let mut to_fpga_fifo = fpga_defs::fifos::NumbersToFPGA;
let mut from_fpga_fifo = fpga_defs::fifos::NumbersFromFPGA;

println!("Writing to FIFO");
let (write_region, input_remaining) = to_fpga_fifo.get_write_region(&session, 4, None).unwrap();
println!("{input_remaining} free space in input FIFO");
write_region.elements.copy_from_slice(&inputs);
drop(write_region);

println!("Reading from FIFO");
let mut outputs = vec![0u16; 4];

// Read into a mutable buffer of the data type. Notice here we are just using the slice
// of the first 2 elements indicating we want to read 2 elements.
let (read_region, output_remaining) =
from_fpga_fifo.get_read_region(&session, 2, None).unwrap();
assert_eq!(output_remaining, 2);
println!("{output_remaining} elements remaining in output FIFO after first read");
outputs[0..2].copy_from_slice(read_region.elements);
drop(read_region);

// This will read the next two elements into the rest of the buffer.
let (read_region, output_remaining) =
from_fpga_fifo.get_read_region(&session, 2, None).unwrap();
assert_eq!(output_remaining, 0);
outputs[2..4].copy_from_slice(read_region.elements);

assert_eq!(outputs, expected_outputs);

//Last read region is implicitly dropped here.
}
1 change: 1 addition & 0 deletions ni-fpga-interface/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ impl NiFpgaStatus {
-63038 => "The NI-RIO software on the host is not compatible with the software on the target. Upgrade the NI-RIO software on the host in order to connect to this target.",
-63040 => "A connection could not be established to the specified remote device. Ensure that the device is on and accessible over the network, that NI-RIO software is installed, and that the RIO server is running and properly configured.",
-63043 => "The RPC session is invalid. The target may have reset or been rebooted. Check the network connection and retry the operation.",
-63045 => "The requested feature is not supported when using a remote RIO session. Use a local RIO session instead.",
-63082 => "The operation could not complete because another session is accessing the FIFO. Close the other session and retry.",
-63083 => "A Read FIFO or Write FIFO function was called while the host had acquired elements of the FIFO. Release all acquired elements before reading or writing.",
-63084 => "A function was called using a misaligned address. The address must be a multiple of the size of the data type.",
Expand Down
30 changes: 25 additions & 5 deletions ni-fpga-interface/src/fifos.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::error::FPGAError;
use crate::session::fifo_control::FifoAddress;
use crate::session::{FifoInterface, NativeFpgaType, Session};
use crate::session::{FifoInterface, FifoReadRegion, FifoWriteRegion, NativeFpgaType, Session};
use std::marker::PhantomData;
use std::time::Duration;

Expand All @@ -9,7 +9,8 @@ pub struct ReadFifo<T: NativeFpgaType> {
phantom: PhantomData<T>,
}

impl<T: NativeFpgaType> ReadFifo<T> {
// check the 'static - should never have a lifetime in this - can we remove it somehow?
impl<T: NativeFpgaType + 'static> ReadFifo<T> {
pub const fn new(address: u32) -> Self {
Self {
address,
Expand All @@ -23,7 +24,16 @@ impl<T: NativeFpgaType> ReadFifo<T> {
timeout: Option<Duration>,
data: &mut [T],
) -> Result<usize, FPGAError> {
session.read(self.address, data, timeout)
session.read_fifo(self.address, data, timeout)
}

pub fn get_read_region<'d, 's: 'd>(
&'d mut self,
session: &'s impl FifoInterface<T>,
elements: usize,
timeout: Option<Duration>,
) -> Result<(FifoReadRegion<'s, 'd, T>, usize), FPGAError> {
session.zero_copy_read(self.address, elements, timeout)
}
}

Expand All @@ -32,7 +42,8 @@ pub struct WriteFifo<T: NativeFpgaType> {
phantom: PhantomData<T>,
}

impl<T: NativeFpgaType> WriteFifo<T> {
// see note on 'static above. should try to remove it.
impl<T: NativeFpgaType + 'static> WriteFifo<T> {
pub const fn new(address: u32) -> Self {
Self {
address,
Expand All @@ -46,6 +57,15 @@ impl<T: NativeFpgaType> WriteFifo<T> {
timeout: Option<Duration>,
data: &[T],
) -> Result<usize, FPGAError> {
session.write(self.address, data, timeout)
session.write_fifo(self.address, data, timeout)
}

pub fn get_write_region<'d, 's: 'd>(
&'d mut self,
session: &'s impl FifoInterface<T>,
elements: usize,
timeout: Option<Duration>,
) -> Result<(FifoWriteRegion<'s, 'd, T>, usize), FPGAError> {
session.zero_copy_write(self.address, elements, timeout)
}
}
23 changes: 14 additions & 9 deletions ni-fpga-interface/src/session/data_interfaces.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,24 +79,29 @@ pub trait FifoInterface<T: NativeFpgaType> {
/// Reads the elements into the provided buffer up to the size of the buffer.
///
/// returns the number of elements left in the buffer to read.
fn read(&self, fifo: FifoAddress, buffer: &mut [T], timeout: Option<Duration>)
-> Result<usize>;
fn read_fifo(
&self,
fifo: FifoAddress,
buffer: &mut [T],
timeout: Option<Duration>,
) -> Result<usize>;

/// Writes the elements to the FPGA from the data slice.
///
/// Returns the amount of free space in the FIFO.
fn write(&self, fifo: FifoAddress, data: &[T], timeout: Option<Duration>) -> Result<usize>;
fn write_fifo(&self, fifo: FifoAddress, data: &[T], timeout: Option<Duration>)
-> Result<usize>;

/// Provides a region of memory to read from the FIFO.
fn read_no_copy(
fn zero_copy_read(
&self,
fifo: FifoAddress,
elements: usize,
timeout: Option<Duration>,
) -> Result<(FifoReadRegion<T>, usize)>;

/// Provides a region of memory to write to the FIFO.
fn write_no_copy(
fn zero_copy_write(
&self,
fifo: FifoAddress,
elements: usize,
Expand Down Expand Up @@ -143,24 +148,24 @@ macro_rules! impl_type_session_interface {
}

impl FifoInterface<$rust_type> for Session {
fn read(&self, fifo: u32, data: &mut [$rust_type], timeout: Option<Duration>) -> Result< usize> {
fn read_fifo(&self, fifo: u32, data: &mut [$rust_type], timeout: Option<Duration>) -> Result< usize> {
let mut elements_remaining: size_t = 0;
let return_code = unsafe {[< NiFpga_ReadFifo $fpga_type >](self.handle, fifo, data.as_mut_ptr(), data.len(), timeout.into(), &mut elements_remaining)};
to_fpga_result(elements_remaining, return_code)
}
fn write(&self, fifo: u32, data: &[$rust_type], timeout: Option<Duration>) -> Result<usize> {
fn write_fifo(&self, fifo: u32, data: &[$rust_type], timeout: Option<Duration>) -> Result<usize> {
let mut elements_remaining: size_t = 0;
let return_code = unsafe {[< NiFpga_WriteFifo $fpga_type >](self.handle, fifo, data.as_ptr(), data.len(), timeout.into(), &mut elements_remaining)};
to_fpga_result(elements_remaining, return_code)
}
fn read_no_copy(&self, fifo: u32, elements: usize, timeout: Option<Duration>) -> Result<(FifoReadRegion<$rust_type>, usize)> {
fn zero_copy_read(&self, fifo: u32, elements: usize, timeout: Option<Duration>) -> Result<(FifoReadRegion<$rust_type>, usize)> {
let mut elements_remaining: size_t = 0;
let mut data: *const $rust_type = std::ptr::null();
let return_code = unsafe {[< NiFpga_AcquireFifoReadElements $fpga_type >](self.handle, fifo, &mut data, elements, timeout.into(), &mut elements_remaining, &mut elements_remaining)};
let read_region = FifoReadRegion{session: self, fifo, elements: unsafe {std::slice::from_raw_parts(data, elements)}};
to_fpga_result((read_region, elements_remaining), return_code)
}
fn write_no_copy(&self, fifo: u32, elements: usize, timeout: Option<Duration>) -> Result<(FifoWriteRegion<$rust_type>, usize)> {
fn zero_copy_write(&self, fifo: u32, elements: usize, timeout: Option<Duration>) -> Result<(FifoWriteRegion<$rust_type>, usize)> {
let mut elements_remaining: size_t = 0;
let mut data: *mut $rust_type = std::ptr::null_mut();
let return_code = unsafe {[< NiFpga_AcquireFifoWriteElements $fpga_type >](self.handle, fifo, &mut data, elements, timeout.into(), &mut elements_remaining, &mut elements_remaining)};
Expand Down

0 comments on commit ae629e4

Please sign in to comment.