circt icon indicating copy to clipboard operation
circt copied to clipboard

[FIRRTL] Failed to optimize `bits(cat(cat(..))`.

Open uenoku opened this issue 5 months ago • 0 comments
trafficstars

  firrtl.circuit "A" {
    firrtl.module @A(in %clock: !firrtl.clock, in %u: !firrtl.uint<1>, in %cond: !firrtl.uint<1>, out %a: !firrtl.uint<2>) attributes {convention = #firrtl<convention scalarized>} {
      %c0_ui2 = firrtl.constant 0 : !firrtl.uint<2>
      %cat_1 = firrtl.cat %u, %c0_ui2 {name = "cat_1"} : (!firrtl.uint<1>, !firrtl.uint<2>) -> !firrtl.uint<3>
      %cat_2 = firrtl.cat %cat_1, %u {name = "cat_2"} : (!firrtl.uint<3>, !firrtl.uint<1>) -> !firrtl.uint<4>
      %0 = firrtl.bits %cat_2 2 to 1 : (!firrtl.uint<4>) -> !firrtl.uint<2>
      firrtl.matchingconnect %a, %0 : !firrtl.uint<2>
    }
  }

Even though %0 is constant 0 it's not optimized at FIRRTL. At core dialect operands are flattened to comb.concat, and it's obviously canonicalized.

This issue causes this kind of unstable behavior for register optimizations:

FIRRTL version 5.0.0

circuit A:
  public module A :
    input clock : Clock
    input u: UInt<1>
    input cond : UInt<1>
    output a : UInt<2>
    output b: UInt<2>
    reg r_a : UInt<2>, clock
    reg r_b : UInt<2>, clock
    connect a, r_a
    connect b, r_b
    node cat_1 = cat(u, UInt<2>(0))
    node cat_2 = cat(cat_1, u)
    node next_a = bits(cat_2, 3, 1)
    when cond :
      connect r_a, next_a
      connect r_b, UInt<1>(0)

Output:

 reg [1:0] r_a;
  always @(posedge clock) begin
    if (cond)
      r_a <= 2'h0;
  end // always @(posedge)
  assign a = r_a;
  assign b = 2'h0;

Currently only r_b is optimized because the driver of r_b is mux(cond, 0, r_b) whereas r_a cannot be optimized since the driver of r_a is mux(cond, cat, r_a) (specifically this condition https://github.com/llvm/circt/blob/b33791db110010ca2f7de63cdbeedea0654165b7/lib/Dialect/FIRRTL/FIRRTLFolds.cpp#L3125)

uenoku avatar Jun 07 '25 02:06 uenoku