samitbasu / rust-hdl

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

What's the right way to get `posedge` to appear in this Verilog output? #16

Open Boscop opened 1 year ago

Boscop commented 1 year ago

Thanks for making this crate, it seems very useful for hardware design :)

I'm trying to implement https://en.wikipedia.org/wiki/Rule_110 What's the right way to get posedge to appear in this Verilog output?

module top(clock,load,data,q);

    // Module arguments
    input wire  clock;
    input wire  load;
    input wire  [511:0] data;
    output reg  [511:0] q;

    // Update code
    always @(*) begin // It needs posedge here
        if (load) begin
            q = data;
        end
        else begin
            q = (~(q >> 32'h1) & q) | (q ^ (q << 32'h1));
        end
    end

endmodule // top

To match https://www.hackster.io/Mayukhmali_Das/cellular-automata-and-verilog-c2f0ec#toc-rule-110-in-verilog-3

This doesn't work:

use rust_hdl::prelude::*;

#[derive(LogicBlock, Default)]
struct Rule110 {
    pub clock: Signal<In, Clock>,
    pub load: Signal<In, Bit>,
    pub data: Signal<In, Bits<512>>,
    pub q: Signal<Out, Bits<512>>,
}

impl Logic for Rule110 {
    #[hdl_gen]
    fn update(&mut self) {
        if self.clock.pos_edge() {
            if self.load.val() {
                self.q.next = self.data.val();
            } else {
                self.q.next = (!(self.q.val() >> 1) & self.q.val()) | (self.q.val() ^ (self.q.val() << 1));
            }
        }
    }
}

fn main() {
    let mut uut = Rule110::default();
    uut.connect_all();
    std::fs::write("Rule110.v", generate_verilog(&uut)).unwrap();
}
error: Unsupported method call for hardware conversion
  --> src\main.rs:14:6
14 |         if self.clock.pos_edge() {
   |            ^^^^^^^^^^^^^^^^^^^^^

I'd appreciate a hint :)

samitbasu commented 1 year ago

Hi! You need a dff. The Digital Flip Flop is inferred in the Verilog code, but explicit in Rust. So something like:

#[derive(LogicBlock, Default)]
struct Rule110 {
    pub clock: Signal<In, Clock>,
    pub load: Signal<In, Bit>,
    pub data: Signal<In, Bits<512>>,
    pub q: Signal<Out, Bits<512>>,
        dff: DFF<Bits<512>>,
}

impl Logic for Rule110 {
    #[hdl_gen]
    fn update(&mut self) {
                // Clock the flip flip
                self.dff.clock.next = self.clock.val();
                // Prevent latches
                self.dff.d.next = self.dff.q.val();
                // Connect q to the output of the flip flip
                self.q.next = self.dff.q.val();
        if self.load.val() {
                        self.dff.d.next = self.data.val(); 
        } else {
            self.dff.d.next = (!(self.dff.q.val() >> 1) & self.dff.q.val()) | (self.dff.q.val() ^ (self.dff.q.val() << 1));
        }
    }
}
Boscop commented 1 year ago

Thanks, makes sense! Btw, what are the exact rules to prevent latches? Just writing before reading?

Btw, I ran into another problem:

#[derive(LogicStruct, Copy, Clone, Default, PartialEq)]
struct Node {
    k: Bits<32>,
    x: Bits<32>,
    y: Bits<32>,
    z: Bits<32>,
}

#[derive(LogicStruct, Copy, Clone, Default, PartialEq)]
struct Node3 {
    a: Node,
    b: Node,
    c: Node,
}

#[derive(LogicBlock, Default)]
struct Core {
    i: Signal<In, Node3>,
    o: Signal<Out, Node3>,
}

impl Logic for Core {
    #[hdl_gen]
    fn update(&mut self) {
        self.o.next = self.i.val();
        self.o.next.a.k = self.i.val().a.k + 1;
    }
}

generates this invalid expression o$next$a$k = i$val()$a$k + 32'h1;

module top$core(i,o);

    // Module arguments
    input wire  [383:0] i;
    output reg  [383:0] o;

    // Update code
    always @(*) begin
        o = i;
        o$next$a$k = i$val()$a$k + 32'h1;
    end

endmodule // top$core

What is the right way to express this principle? I want some bits to propagate unchanged and some should be changed.

samitbasu commented 1 year ago

Yes. The latch prevention logic is to check for writes before reads (and no un-driven signals).

I guess nested LogicStructs do not work. :( That is a bug.

There is nothing wrong with what you wrote, the code generator simply fails to handle the case of nested LogicStructs.