FIRRTL to HW assertion error

Hi everyone,

I’m currently playing around with lowering from MLIR std to Verilog. I’m assuming that this can mostly be done by using circt-opt with the exception of exporting Verilog, which requires the circt-translate tool.

The lowerings from std to handshake and from there to FIRRTL worked nicely, but unfortunately, lowering from FIRRTL to HW triggers an assertion error. I’m not sure if I’m doing something wrong or if this is a bug in the FIRRTLToHW pass.

My input file:

module {
  func @main(%arg0: i32) -> (i32) {
    return %arg0 : i32
  }
}

The command I executed:

circt-opt minimal-demo.mlir --lower-std-to-handshake --lower-handshake-to-firrtl --lower-firrtl-to-hw

And the error message:

circt-opt: /home/dinistro/mlir-dev/circt/lib/Conversion/FIRRTLToHW/LowerToHW.cpp:1631: mlir::Value {anonymous}::FIRRTLLowering::getPossiblyInoutLoweredValue(mlir::Value): Assertion `value.getType().isa<FIRRTLType>() && "Should only lower FIRRTL operands"' failed.
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
Stack dump:
circt-opt: /home/dinistro/mlir-dev/circt/lib/Conversion/FIRRTLToHW/LowerToHW.cpp:1631: mlir::Value {anonymous}::FIRRTLLowering::getPossiblyInoutLoweredValue(mlir::Value): Assertion `value.getType().isa<FIRRTLType>() && "Should only lower FIRRTL operands"' failed.
0.      Program arguments: ../build/bAborted (core dumped)

Could somebody tell me if I made a mistake or if this looks like a bug? All this happens with one of the latest commits on the main branch: c75dc614838dfbd40007affea52136d12fca8738.
Note that it didn’t work either with a previous CIRCT version.

Maybe as an additional note, the IR passed to the final lowering step looks as follows:

module {
  firrtl.circuit "main"  {
    firrtl.module @handshake_merge_in_ui32_out_ui32(in %in0: !firrtl.bundle<valid: uint<1>, ready flip: uint<1>, data: uint<32>>, out %out0: !firrtl.bundle<valid: uint<1>, ready flip: uint<1>, data: uint<32>>) {
      %0 = firrtl.subfield %in0(0) : (!firrtl.bundle<valid: uint<1>, ready flip: uint<1>, data: uint<32>>) -> !firrtl.uint<1>
      %1 = firrtl.subfield %in0(1) : (!firrtl.bundle<valid: uint<1>, ready flip: uint<1>, data: uint<32>>) -> !firrtl.uint<1>
      %2 = firrtl.subfield %in0(2) : (!firrtl.bundle<valid: uint<1>, ready flip: uint<1>, data: uint<32>>) -> !firrtl.uint<32>
      %3 = firrtl.subfield %out0(0) : (!firrtl.bundle<valid: uint<1>, ready flip: uint<1>, data: uint<32>>) -> !firrtl.uint<1>
      %4 = firrtl.subfield %out0(1) : (!firrtl.bundle<valid: uint<1>, ready flip: uint<1>, data: uint<32>>) -> !firrtl.uint<1>
      %5 = firrtl.subfield %out0(2) : (!firrtl.bundle<valid: uint<1>, ready flip: uint<1>, data: uint<32>>) -> !firrtl.uint<32>
      %c0_ui1 = firrtl.constant 0 : !firrtl.uint<1>
      %win = firrtl.wire  : !firrtl.uint<1>
      %resultDone = firrtl.wire  : !firrtl.uint<1>
      %6 = firrtl.orr %win : (!firrtl.uint<1>) -> !firrtl.uint<1>
      %c1_ui1 = firrtl.constant 1 : !firrtl.uint<1>
      %7 = firrtl.mux(%0, %c1_ui1, %c0_ui1) : (!firrtl.uint<1>, !firrtl.uint<1>, !firrtl.uint<1>) -> !firrtl.uint<1>
      firrtl.connect %win, %7 : !firrtl.uint<1>, !firrtl.uint<1>
      firrtl.connect %3, %6 : !firrtl.uint<1>, !firrtl.uint<1>
      %c0_ui32 = firrtl.constant 0 : !firrtl.uint<32>
      %8 = firrtl.bits %win 0 to 0 : (!firrtl.uint<1>) -> !firrtl.uint<1>
      %9 = firrtl.mux(%8, %2, %c0_ui32) : (!firrtl.uint<1>, !firrtl.uint<32>, !firrtl.uint<32>) -> !firrtl.uint<32>
      firrtl.connect %5, %9 : !firrtl.uint<32>, !firrtl.uint<32>
      %10 = firrtl.and %6, %4 : (!firrtl.uint<1>, !firrtl.uint<1>) -> !firrtl.uint<1>
      firrtl.connect %resultDone, %10 : !firrtl.uint<1>, !firrtl.uint<1>
      %11 = firrtl.mux(%resultDone, %win, %c0_ui1) : (!firrtl.uint<1>, !firrtl.uint<1>, !firrtl.uint<1>) -> !firrtl.uint<1>
      %12 = firrtl.eq %11, %c1_ui1 : (!firrtl.uint<1>, !firrtl.uint<1>) -> !firrtl.uint<1>
      firrtl.connect %1, %12 : !firrtl.uint<1>, !firrtl.uint<1>
    }
    firrtl.module @main(in %in0: !firrtl.bundle<valid: uint<1>, ready flip: uint<1>, data: uint<32>>, in %inCtrl: !firrtl.bundle<valid: uint<1>, ready flip: uint<1>>, out %out0: !firrtl.bundle<valid: uint<1>, ready flip: uint<1>, data: uint<32>>, out %outCtrl: !firrtl.bundle<valid: uint<1>, ready flip: uint<1>>, in %clock: !firrtl.clock, in %reset: !firrtl.uint<1>) {
      %handshake_merge0_in0, %handshake_merge0_out0 = firrtl.instance handshake_merge0  @handshake_merge_in_ui32_out_ui32(in in0: !firrtl.bundle<valid: uint<1>, ready flip: uint<1>, data: uint<32>>, out out0: !firrtl.bundle<valid: uint<1>, ready flip: uint<1>, data: uint<32>>)
      firrtl.connect %handshake_merge0_in0, %in0 : !firrtl.bundle<valid: uint<1>, ready flip: uint<1>, data: uint<32>>, !firrtl.bundle<valid: uint<1>, ready flip: uint<1>, data: uint<32>>
      firrtl.connect %out0, %handshake_merge0_out0 : !firrtl.bundle<valid: uint<1>, ready flip: uint<1>, data: uint<32>>, !firrtl.bundle<valid: uint<1>, ready flip: uint<1>, data: uint<32>>
      firrtl.connect %outCtrl, %inCtrl : !firrtl.bundle<valid: uint<1>, ready flip: uint<1>>, !firrtl.bundle<valid: uint<1>, ready flip: uint<1>>
    }
  }
}

I’m not yet very familiar with FIRRTL thus I did not reduce the test case further.

The IR here is called “high level FIRRTL” which requires several passes (e.g. LowerTypes, ExpandWhens) before applying lowerToHW pass (you can refer the whole pipeline at p44 [0]). You can use firtool to lower FIRRTL properly (like $ firtool foo.mlir).

[0] https://llvm.org/devmtg/2021-11/slides/2021-CIRCT-LiftingHardwareDevOutOfThe20thCentury.pdf )

1 Like

Thank you very much for your answer. This indeed resolves my problems.

1 Like