ni-fpga-interface-build

Crates.ioni-fpga-interface-build
lib.rsni-fpga-interface-build
version0.1.0
sourcesrc
created_at2023-10-23 14:08:57.297031
updated_at2023-10-23 14:08:57.297031
descriptionProvide the ability to access an NI FPGA application from Rust
homepage
repositoryhttps://github.com/WiresmithTech/ni-fpga-interface
max_upload_size
id1011349
size64,966
James McNally (JamesMc86)

documentation

README

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

  1. 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
  2. Add the ni-fpga-interface-build crate as a build dependency and ni-fpga-interface as a normal dependency.
  3. 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();
    }
    
  4. Include the generated rust module in a module in your application.
    mod fpga_defs {
        include!(concat!(env!("OUT_DIR"), "/NiFpga_Main.rs"));
    }
    
  5. 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:

  1. The C calls generated by NI.
  2. A safe wrapper around the C calls in Rust.
  3. 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:

  1. Build Support to make it easy to take an FPGA-generated C interface and build it into your Rust project.
  2. Safe wrapper so you don't have to call the unsafe functions directly.
  3. 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

  1. Tackle the build support.
  2. Get the safe wrapper working for standard types.
  3. Get the safe wrapper working for custom types.
  4. Tackle API generation for a single bitfile.
  5. 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.

Commit count: 39

cargo fmt