fabianschuiki / llhd

Low Level Hardware Description — A foundation for building hardware design tools.
http://www.llhd.io
Apache License 2.0
392 stars 30 forks source link

Optimization removes drives in always_ff process #100

Closed fabianschuiki closed 4 years ago

fabianschuiki commented 4 years ago

Currently llhd-opt removes the drives from always_ff processes emitted by moore. Consider the following example:

proc %snitch.param1.always_ff.568.1 (i1$ %clk_i, i1$ %rst_i, i32$ %pc_d) -> (i32$ %pc_q) {
0:
    br %init
init:
    %clk_i1 = prb i1$ %clk_i
    wait %check, %clk_i
check:
    %clk_i2 = prb i1$ %clk_i
    %1 = const i1 0
    %2 = eq i1 %clk_i1, %1
    %3 = neq i1 %clk_i2, %1
    %posedge = and i1 %2, %3
    br %posedge, %init, %event
event:
    %rst_i1 = prb i1$ %rst_i
    %4 = const i32 65536
    %pc_d1 = prb i32$ %pc_d
    %5 = [i32 %pc_d1, %4]
    %6 = mux [2 x i32] %5, i1 %rst_i1
    %7 = const time 0s 1d
    drv i32$ %pc_q, %6, %7
    br %0
}

This gets optimized to:

proc %snitch.param1.always_ff.568.1 (i1$ %clk_i, i1$ %rst_i, i32$ %pc_d) -> (i32$ %pc_q) {
0:
    %clk_i1 = prb i1$ %clk_i
    %1 = const i1 0
    %2 = eq i1 %clk_i1, %1
    br %init
init:
    wait %check, %clk_i
check:
    %clk_i2 = prb i1$ %clk_i
    %3 = neq i1 %clk_i2, %1
    %posedge = and i1 %2, %3
    br %posedge, %init, %0
}

Just executing llhd-opt -p tcm shows that TCM removes the drv i32$ %pc_q, %6, %7 instruction. This should not happen.

fabianschuiki commented 4 years ago

It seems that in this case the TRG assigns two TR0 to blocks 0 and init, and TR1 to check and event, but TR1 has no tail blocks. Running llhd-check --emit-trg yields:

Temporal Regions:
  - %snitch.param1.always_ff.568.1:
    - %0 = t0
    - %init = t0
    - %check = t1
    - %event = t1
    t0:
      Head Blocks: %0
      Head Insts:
      Tail Blocks: %init
      Tail Insts:
      - wait %clk_i, %check
    t1:
      Head Blocks: %check
      Head Insts:
      - wait %clk_i, %check
      Tail Blocks: 
      Tail Insts:

The correct assignments would be:

    [...]
    t0:
      Head Blocks: %0
      Head Insts:
      Tail Blocks: %init
      Tail Insts:
      - wait %clk_i, %check
    t1:
      Head Blocks: %check
      Head Insts:
      - wait %clk_i, %check
      Tail Blocks: %check, %event    ; ***
      Tail Insts:
fabianschuiki commented 4 years ago

The temporal region t1 will not allow the drv instruction to be moved, since its arguments do not dominate all tail blocks. TCM will likely need an additional transform at the beginning which tries to coalesce TR tail blocks into one. In the above example:

proc %snitch.param1.always_ff.568.1 (i1$ %clk_i, i1$ %rst_i, i32$ %pc_d) -> (i32$ %pc_q) {
0:
    br %init
init:
    %clk_i1 = prb i1$ %clk_i
    wait %check, %clk_i
check:
    %clk_i2 = prb i1$ %clk_i
    %1 = const i1 0
    %2 = eq i1 %clk_i1, %1
    %3 = neq i1 %clk_i2, %1
    %posedge = and i1 %2, %3
    br %posedge, %aux, %event
event:
    %rst_i1 = prb i1$ %rst_i
    %4 = const i32 65536
    %pc_d1 = prb i32$ %pc_d
    %5 = [i32 %pc_d1, %4]
    %6 = mux [2 x i32] %5, i1 %rst_i1
    %7 = const time 0s 1d
    drv i32$ %pc_q, %6, %7
    br %aux
aux:
    br %init
}

In this case, the drv instruction may be moved into the aux tail block, with %posedge as the drive condition.

fabianschuiki commented 4 years ago

Minimal example provided by @eduardosm:

proc @foo () -> (i1$ %out) {
0:
    %delta = const time 0s 1d
    %k0 = const i1 0
    drv i1$ %out, %k0, %delta
    br %0
}

is transformed by llhd-opt -p tcm to

proc @foo () -> (i1$ %out) {
0:
    %delta = const time 0s 1d
    %k0 = const i1 0
    br %0
}