samitbasu / rust-hdl

A framework for writing FPGA firmware using the Rust Programming Language
Other
326 stars 18 forks source link

RustHDL

Redirect in Progress

Due to the concerns about having a project with "rust" in the name, I'm changing this project name to rhdl, and moving active development there. Sorry for the trouble, but I'd hate to have the project yanked at some point in the future for something so silly.

I am also in the process of rebuilding this project from scratch to be significantly faster and more powerful. So I will do the new development in the rhdl crate/repo, and migrate the existing widgets over time. At that point, I will archive this repo.

New docs launched! Check them out at https://rust-hdl.org...

Write FPGA Firmware using Rust!

RustHDL is a crate that allows you to write FPGA firmware using Rust! Specifically, rust-hdl compiles a subset of Rust down to Verilog so that you can synthesize firmware for your FPGA using standard tools. It also provides tools for simulation, verification, and analysis along with strongly typed interfaces for making sure your design works before heading to the bench. The workflow is very similar to GPU programming. You write everything in Rust, including an update kernel that compiles down onto the hardware. You can simulate and verify everything from the safety and comfort of your Rust environment, and then head over to standard synthesis tools to get files that program your FPGA.

Links

You may want:

Features

Quickstart

The definitive example in FPGA firmware land is a simple LED blinker. This typically involves a clock that is fed to the FPGA with a pre-defined frequency, and an output signal that can control an LED. Because we don't know what FPGA we are using, we will do this in simulation first. We want a blink that is 250 msec long every second, and our clock speed is (a comically slow) 10kHz. Here is a minimal working Blinky! example:

use std::time::Duration;
use rust_hdl::prelude::*;
use rust_hdl::docs::vcd2svg::vcd_to_svg;

const CLOCK_SPEED_HZ : u64 = 10_000;

#[derive(LogicBlock)]  // <- This turns the struct into something you can simulate/synthesize
struct Blinky {
    pub clock: Signal<In, Clock>, // <- input signal, type is clock
    pulser: Pulser,               // <- sub-circuit, a widget that generates pulses
    pub led: Signal<Out, Bit>,    // <- output signal, type is single bit
}

impl Default for Blinky {
   fn default() -> Self {
       Self {
         clock: Default::default(),
         pulser: Pulser::new(CLOCK_SPEED_HZ, 1.0, Duration::from_millis(250)),
         led: Default::default(),
       }
    }
}

impl Logic for Blinky {
    #[hdl_gen] // <- this turns the update function into an HDL Kernel that can be turned into Verilog
    fn update(&mut self) {
       // v-- write to the .next member     v-- read from .val() method
       self.pulser.clock.next = self.clock.val();
       self.pulser.enable.next = true.into();
       self.led.next = self.pulser.pulse.val();
    }
}

fn main() {
    // v--- build a simple simulation (1 testbench, single clock)
    let mut sim = simple_sim!(Blinky, clock, CLOCK_SPEED_HZ, ep, {
        let mut x = ep.init()?;
        wait_clock_cycles!(ep, clock, x, 4*CLOCK_SPEED_HZ);
        ep.done(x)
    });

    // v--- construct the circuit
    let mut uut = Blinky::default();
    uut.connect_all();
    sim.run_to_file(Box::new(uut), 5 * SIMULATION_TIME_ONE_SECOND, "blinky.vcd").unwrap();
    vcd_to_svg("/tmp/blinky.vcd","images/blinky_all.svg",&["uut.clock", "uut.led"], 0, 4_000_000_000_000).unwrap();
    vcd_to_svg("/tmp/blinky.vcd","images/blinky_pulse.svg",&["uut.clock", "uut.led"], 900_000_000_000, 1_500_000_000_000).unwrap();
}

Running the above (a release run is highly recommended) will generate a vcd file (which is a trace file for FPGAs and hardware in general). You can open this using e.g., gtkwave. If you have, for example, an Alchitry Cu board you can generate a bitstream for this exampling with a single call. It's a little more involved, so we will cover that in the detailed documentation. It will also render that vcd file into an svg you can view with an ordinary web browser. This is the end result showing the entire simulation: blinky_all Here is a zoom in showing the pulse to the LED blinky_pulse

The flow behind RustHDL is the following:

The rest is detail. Some final things to keep in mind.

License: MIT