3 releases (stable)

1.0.1 Aug 23, 2020
1.0.0 May 24, 2020
0.1.0 May 24, 2020

#702 in Procedural macros

22 downloads per month

Custom license

6KB
102 lines

ni-fpga-rs

crates.io docs.rs CI

Use this Rust interface to interact with NI FPGAs! See NI's documentation about the FPGA C interface for more information.

Supported types

This interface supports reading and writing the following types, both indvidually and in fixed-sized arrays:

Primitive types

  • bool
  • u8
  • u16
  • u32
  • u64
  • i8
  • i16
  • i32
  • i64
  • f32 (for SGL registers)
  • f64 (for DBL registers)

Clusters

Clusters are supported via a derive macro. Arrays of Clusters are not guaranteed to be supported.

#[derive(Cluster)]
struct PWMConfig {
    period: u16,
    min_high: u16,
}

Enums

Enums are supported via a derive macro. Arrays of Enums are also supported. One of u8, u16, u32, or u64 will be chosen as a backing type depending on the number of variants.

#[derive(Enum)]
enum SPIDebugState {
    Idle,
    CheckWindow,
    CheckAvailable,
    SetFIFOMark,
    EnableSPI,
    StuffFIFO,
    CheckMark,
    ShuffleData,
    Disable,
}

Fixed-point numeric types

Signed and unsigned FXP types are implemented in the fxp module. The types both take two generic parameters: word length and integer length. Word length is the actual number of bits used by the type. Integer length is such that the maximum value of the type is 2integer length - 1 (half if it is signed) and the resolution of the type is 2integer length - word length. For example, if an unsigned FXP has a word length of 8 and an integer length of 7, then its maximum value is 127 and its resolution is 0.5.

assert_eq!(
    SignedFXP::<8, 7>::from_float(-1.5)? + SignedFXP::<8, 7>::from_float(4.0)?,
    SignedFXP::<8, 7>::from_float(2.5)?,
);

Locating register offsets

Register offset can be found by introspecting /boot/user.lvbitx on a roboRIO. This file is also present in first-rust-competition/cross-images images.

Full example

use ni_fpga::Session;
use ni_fpga_macros::{Cluster, Enum};

#[derive(Cluster, Debug)]
struct PWMConfig {
    period: u16,
    min_high: u16,
}
#[derive(Cluster, Debug)]
struct AnalogTriggerOutput {
    in_hysteresis: bool,
    over_limit: bool,
    rising: bool,
    falling: bool,
}

#[derive(Enum, Debug)]
enum SPIDebugState {
    Idle,
    CheckWindow,
    CheckAvailable,
    SetFIFOMark,
    EnableSPI,
    StuffFIFO,
    CheckMark,
    ShuffleData,
    Disable,
}

fn main() -> Result<(), ni_fpga::Error> {
    let session = Session::open(
        "/boot/user.lvbitx",
        "264D0BA312FF00B741D4742415E1D470",
        "RIO0",
    )?;

    println!("Input voltage: {:?}", session.read::<u16>(99174)?);
    println!("{:#?}", session.read::<PWMConfig>(98536)?);
    println!("{:#?}", session.read::<[AnalogTriggerOutput; 8]>(98424)?);
    println!("{:#?}", session.read::<SPIDebugState>(99314)?);
    Ok(())
}

Contributing

Contributions are welcome and appreciated. Look at open issues to find tasks to work on. We especially need help with:

  • Creating an automated testing strategy
  • Improving documentation
  • Adding support for IRQs and FIFOs (see NI API reference)

Dependencies

~1.5MB
~34K SLoC