Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

About RTL interop with Dahlia via Calyx #428

Open
Mark1626 opened this issue Mar 21, 2024 · 1 comment
Open

About RTL interop with Dahlia via Calyx #428

Mark1626 opened this issue Mar 21, 2024 · 1 comment

Comments

@Mark1626
Copy link
Collaborator

For my Posit bindings I modified Dahila's source, nonetheless this was another idea I found very interesting

Interop with RTL

Dahlia has an elegant interop with RTL via Calyx.

Suppose we have a SV module like this

module PositAdd(
  input         clock,
                reset,
  input  [31:0] io_num1,
                io_num2,
  input         io_sub,
  output        io_isZero,
                io_isNaR,
  output [31:0] io_out
);

by creating Calyx binding

extern "P32/P32.sv" {
    comb primitive PositAdd[](
        @write_together(1) io_num1: 32,
        @write_together(1) io_num2: 32,
        @write_together(1) io_sub: 1,
        @clk clock: 1,
        @reset reset: 1
    ) -> (
        io_isZero: 1,
        io_isNaR : 1,
        io_out: 32
    );
}

I can use this in Dahlia like so

import futil("P32.futil") { def PositAdd(io_num1: ubit<32>, io_num2: ubit<32>): ubit<32>; }

decl a: ubit<32>[128];
decl b: ubit<32>[128];
decl c: ubit<32>[128];

for (let i:bit<8>=0..128) {
    c[i] := PositAdd(a[i], b[i]);
}

The generated Calyx IR.

import "primitives/core.futil";
import "primitives/memories/seq.futil";
import "primitives/binary_operators.futil";
import "P32.futil";
component main() -> () {
  cells {
    PositAdd0 = PositAdd();
    @external(1) a = seq_mem_d1(32,128,8);
    a_read0_0 = std_reg(32);
    ...
    ...
    ...
  }
  wires {
    ...
    ...
    ...
    group let3 {
      v_0.in = PositAdd0.out;   <-------------------------- Dahlia assumes that the RTL has a output port called out
      v_0.write_en = 1'd1;
      let3[done] = v_0.done;
    }
    ...
    ...
    ...
  }
  control {
    seq {
      @pos(0) let0;
      @bound(128) while le0.out with cond0 {
        seq {
          par {
            @pos(1) let1;
            @pos(2) let2;
          }
          invoke PositAdd0(io_num1=a_read0_0.out, io_num2=b_read0_0.out)();
          let3;
          @pos(3) upd0;
          @pos(0) upd1;
        }
      }
    }
  }
}

Limitation

It seems to expect that the RTL has a port called out. I think fixing this limitation can open up a lot of interesting possibilities

@rachitnigam
Copy link
Member

Thanks for opening this! And yeah, I think you're right that there is something to be addressed there. The silly hack is to of course wrap the PositAdd module in a wrapper module and rename the ports for that module to be out.

A more natural solution might be to extend the import definitions to allow for named ports:

def PositAdd(io_num1: ubit<32>, io_num2: ubit<32>): (io_out: ubit<32>);

This information would then get passed to the backend to allow for this integration. Another limitation of the current approach is that we cannot encode the timing information of PositAdd which means Calyx's optimizations around static scheduling are disable.

For example, because we know that std_fp_mult takes exactly four cycles using the @interval, we can optimize the control program to be static and lower the latency. This kind of information is hard to specify in import definitions today.

The better, longer-term solution might be to support proper modules in Dahlia so that we can specify these definitions once and use them in various programs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants