1 unstable release
0.1.0 | Oct 23, 2023 |
---|
#1537 in Hardware support
65KB
1.5K
SLoC
ni-fpga-interface
NI FPGA Interface for Rust
This crate is designed to make it easy to talk to a LabVIEW FPGA.
Requirements
- The C interface needs to be exported with the "prefix" set to something without spaces and we expect a single directory with the NiFpga C and H files.
Getting Started
- Generate the FPGA C Interface using the generator that can be downloaded from https://www.ni.com/en/support/downloads/drivers/download.fpga-interface-c-api.html#477242
- Add the
ni-fpga-interface-build
crate as a build dependency andni-fpga-interface
as a normal dependency. - Create a build script for your project and add the builder to the script. For example:
fn main() { let mut fpga_c_interface = ni_fpga_interface_build::FpgaCInterface::from_custom_header( "../fpga_c_interface/NiFpga_Main.h", ); fpga_c_interface.build(); }
- Include the generated rust module in a module in your application.
mod fpga_defs { include!(concat!(env!("OUT_DIR"), "/NiFpga_Main.rs")); }
- Use the
ni-fpga-interface
APIs to access the FPGA.
Examples
See the examples folder for some fully worked examples including build support.
Supported Features
These are the features supported and planned.
Native types are the standard integer types as well as singles and double floats.
Feature | Supported |
---|---|
Registers for native types | ✅ |
Registers for FXP numbers | planned |
Registers for clusters | TBC |
DMA for native types | ✅ |
DMA FIFO controls | ✅ |
IRQs | ✅ |
Session Control | ✅ |
Multi-threading | ✅ |
pattern for multi-fpga support | planned |
dynamic interface for multi-fpga support | planned |
Architecture
The principle behind this approach is going to be to use as much of the generated C as possible instead of pre-building against the expected interface.
This should make us as version independent as possible and reduce the complexity where NI have already solved problems in the generated C code.
It may take time but this builds through 3 layers of abstraction:
- The C calls generated by NI.
- A safe wrapper around the C calls in Rust.
- A generated Rust interface for the specific FPGA bitfile/interface you have provided.
flowchart
subgraph LabVIEW
C[FPGA C Interface]
LV[LabVIEW FPGA] --generates--> C
end
subgraph Build Support
C --bindgen--> Types[FPGA Definitions]
C --cc--> Obj[Compiled NI FPGA Interface]
end
Obj --links--> Wrapper[Safe Generic Wrapper]
Types --mod import--> Direct[Your Code with Direct interface]
Wrapper --mod import--> Direct
Types --generator--> Specific[FPGA Interface Module]
Wrapper --generator--> Specific
Specific --> App[Your Code Using Custom Interface]
The diagram shows the key components and targeted workflow. Names will probably change.
There are 3 key components to this project:
- Build Support to make it easy to take an FPGA-generated C interface and build it into your Rust project.
- Safe wrapper so you don't have to call the unsafe functions directly.
- FPGA Interface generator which will generate a struct in Rust which provides easy access to the interface of a specific FPGA.
Custom Type Support
We will need to support fixed point and clusters which come in as custom types in C.
This needs some code generation as part of the build support to enable a safe wrapper for these types.
Cross Compilation
Cross-compilation should be a first-class consideration to ensure this is easy to use against a compactRIO target.
I have tested the examples build against x64 and armv7 LinuxRT targets.
Planned Approach
- Tackle the build support.
- Get the safe wrapper working for standard types.
- Get the safe wrapper working for custom types.
- Tackle API generation for a single bitfile.
- See if there is a sensible route to abstract the interface across multiple bitfiles (ala Dynamic Interface in LabVIEW)
I'm going to start with this as a single crate but it may make sense to split it later.
Dependencies
~2.5MB
~49K SLoC