rust-hdl 0.46.0

Write firmware for FPGAs in Rust
Documentation
use rust_hdl::prelude::*;
use std::fs::File;

struct SignalLister {}

impl VerilogVisitor for SignalLister {
    fn visit_signal(&mut self, s: &str) {
        println!("Signal: {}", s);
    }
}

#[test]
fn test_write_modules_nested_ports() {
    #[derive(Clone, Debug, Default, LogicInterface)]
    struct MyBus {
        pub data: FIFORead<8>,
        pub cmd: FIFORead<3>,
    }

    #[derive(Clone, Debug, Default, LogicInterface)]
    struct FIFORead<const D: usize> {
        pub read: Signal<In, Bit>,
        pub output: Signal<Out, Bits<D>>,
        pub empty: Signal<Out, Bit>,
        pub almost_empty: Signal<Out, Bit>,
        pub underflow: Signal<Out, Bit>,
    }

    #[derive(Clone, Debug, Default, LogicBlock)]
    struct Widget {
        pub clock: Signal<In, Clock>,
        pub bus: MyBus,
    }

    impl Logic for Widget {
        fn update(&mut self) {}

        fn connect(&mut self) {
            self.bus.data.almost_empty.connect();
            self.bus.data.empty.connect();
            self.bus.data.underflow.connect();
            self.bus.data.output.connect();
            self.bus.cmd.almost_empty.connect();
            self.bus.cmd.empty.connect();
            self.bus.cmd.underflow.connect();
            self.bus.cmd.output.connect();
        }
    }

    #[derive(Clone, Debug, Default, LogicBlock)]
    struct UUT {
        pub bus: MyBus,
        widget_a: Widget,
        widget_b: Widget,
        pub clock: Signal<In, Clock>,
        pub select: Signal<In, Bit>,
    }

    impl Logic for UUT {
        #[hdl_gen]
        fn update(&mut self) {
            self.widget_a.clock.next = self.clock.val();
            self.widget_b.clock.next = self.clock.val();

            if self.select.val() {
                self.bus.cmd.underflow.next = self.widget_a.bus.cmd.underflow.val();
                self.bus.cmd.almost_empty.next = self.widget_a.bus.cmd.almost_empty.val();
                self.bus.cmd.empty.next = self.widget_a.bus.cmd.empty.val();
                self.bus.cmd.output.next = self.widget_a.bus.cmd.output.val() + 1;
                self.widget_a.bus.cmd.read.next = self.bus.cmd.read.val();

                self.bus.data.underflow.next = self.widget_a.bus.data.underflow.val();
                self.bus.data.almost_empty.next = self.widget_a.bus.data.almost_empty.val();
                self.bus.data.empty.next = self.widget_a.bus.data.empty.val();
                self.bus.data.output.next = self.widget_a.bus.data.output.val();
                self.widget_a.bus.data.read.next = self.bus.data.read.val();
            } else {
                self.bus.cmd.underflow.next = self.widget_b.bus.cmd.underflow.val();
                self.bus.cmd.almost_empty.next = self.widget_b.bus.cmd.almost_empty.val();
                self.bus.cmd.empty.next = self.widget_b.bus.cmd.empty.val();
                self.bus.cmd.output.next = self.widget_b.bus.cmd.output.val();
                self.widget_b.bus.cmd.read.next = self.bus.cmd.read.val();

                self.bus.data.underflow.next = self.widget_b.bus.data.underflow.val();
                self.bus.data.almost_empty.next = self.widget_b.bus.data.almost_empty.val();
                self.bus.data.empty.next = self.widget_b.bus.data.empty.val();
                self.bus.data.output.next = self.widget_b.bus.data.output.val();
                self.widget_b.bus.data.read.next = self.bus.data.read.val();
            }
        }
    }

    let mut uut = UUT::default();
    uut.connect_all();
    check_all(&uut).unwrap();
    let mut defines = ModuleDefines::default();
    uut.accept("uut", &mut defines);
    defines.defines();
    let code = uut.hdl();
    let mut sig = SignalLister {};
    if let rust_hdl::core::ast::Verilog::Combinatorial(q) = code {
        sig.visit_block(&q);
    }
    let mut jnk = File::create(vcd_path!("test.vcd")).unwrap();
    let dev = write_vcd_header(&mut jnk, &uut);
    let _dev = write_vcd_dump(dev, &uut);
}