cucapra/dahlia

About RTL interop with Dahlia via Calyx

Opened this issue · 1 comments

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

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.