xls icon indicating copy to clipboard operation
xls copied to clipboard

[enhancement] Add latency-sensitive block construct to DSLX

Open meheff opened this issue 1 year ago • 2 comments

What's hard to do? (limit 100 words)

Expressing cycle-by-cycle latency-sensitive components is difficult in DSLX. It'd be good to have a mechanism for writing things at the RTL-level of abstraction. Many components can be built in a latency insenstive way, but invariably every design needs some latency-sensitive parts.

Current best alternative workaround (limit 100 words)

There is no workaround.

Your view of the "best case XLS enhancement" (limit 100 words)

XLS IR has a block construct which is an RTL-level representation. We need a syntax in DSLX which can lower directly to IR Blocks. This proposal mimics proc dslx syntax.

To express things at the RTL-level we need roughly need ports, registers, and combinational logic.

This syntax adds the following constructs:

  • block: a container analogous to a proc which maps to a verilog module. It is guaranteed to iterate every cycle.

  • wire: a latency-sensitive analog to a channel. It maps to directly to ports and connections between them. Wire inputs can be used directly in expressions. Wire outputs use set to indicate what value is presented on the output port each cycle.

  • block state: analogous to proc state. This maps to registers.

Example syntax of a block which accumulates the value at input port x every cycle and presents the accumulated value on the output port result every cycle. The block has one 32-bit state element.

block accum {
  x: wire<u32> in;
  result: wire<u32> out;

  init { u32:0 }

  config(x: wire<u32> in, result: wire<u32> out) {
    (x, result)
  }

  next(sum: u32) {
    let next_sum = x + sum;
    set(result, next_sum);
    next_sum
  }
}

Instantiations should also be supported. Possible syntax:

block accum_wrapper {
  x: wire<u32> in;
  a: wire<u32> out;
  b: wire<u32> in;
  result: wire<u32> out;

  init { () }

  config(x: wire<u32> in, result: wire<u32> out) {
    let (a_accum_in, a_out) = wire<u32>;
    let (b_in, b_out) = wire<u32>;
    spawn accum(a_out, b_in);
    (x, a_in, b_out, result)
  }

  next(st: ()) {
    set(a, x + u32:1);
    set(result, -2 * b);
    ()
  }
}

TBD: Interrop with latency senstive components. Rough idea is channels can be composed/decomposed from wires to connect blocks and procs.

FYI @cdleary

meheff avatar Jun 07 '24 22:06 meheff

We need a name for this construct. @cdleary do you have any thoughts here?

Cmsonger avatar Jun 24 '24 13:06 Cmsonger

@Cmsonger I thought "block" as @meheff had it in his original post sounded good.

Procs are blocks with the ability to stall and plan out the FSM/pipeline (which I tended to phrase as "always blocks on steroids"), blocks are akin to regular always-blocks.

cdleary avatar Sep 09 '24 03:09 cdleary