CIRCT

Circuit IR Compilers and Tools

EDA Tool Workarounds

This documents various bugs found in EDA tools and their workarounds in circt. Each but will have a brief description, example code, and the mitigation added (with links to the commit when possible).

Automatic Variables Cause Latch Warnings

Verilator issues a latch warning for fully-initialized, automatic variables. This precludes using locally scoped variables. https://github.com/verilator/verilator/issues/4022

Example 

module ALU(
  input         clock,
  input  [4:0]  operation,
  input  [63:0] inputs_1,
                inputs_0,
                inputs_2,
  input  [16:0] immediate,
  output [63:0] output_0
);
  reg  [63:0]  casez_tmp_1;
  always_comb begin
    automatic logic [63:0] lowHigh;
    casez (operation)
      5'b00011:
        casez_tmp_1 = inputs_0 & inputs_1;
      5'b00100:
        casez_tmp_1 = inputs_0 | inputs_1;
      5'b00101:
        casez_tmp_1 = inputs_0 ^ inputs_1;
      5'b01001: begin
        automatic logic [16:0] _aluOutput_T_22 =
          immediate >> {14'h0, inputs_2, inputs_1[0], inputs_0[0]};
        casez_tmp_1 = {63'h0, _aluOutput_T_22[0]};
      end
      default:
        casez_tmp_1 = inputs_0;
    endcase
  end
endmodule

Gives:

$ verilator --version
Verilator 5.008 2023-03-04 rev v5.008
$ verilator --lint-only ALU.sv
%Warning-LATCH: ALU.sv:11:3: Latch inferred for signal 'ALU.unnamedblk1.unnamedblk2._aluOutput_T_22' (not all control paths of combinational always assign a value)
                           : ... Suggest use of always_latch for intentional latches
   11 |   always_comb begin
      |   ^~~~~~~~~~~
                ... For warning description see https://verilator.org/warn/LATCH?v=4.218
                ... Use "/* verilator lint_off LATCH */" and lint_on around source to disable this message.
%Error: Exiting due to 1 warning(s)

Workaround 

Flag added to promote all storage to the top level of a module. https://github.com/llvm/circt/commit/3c8b4b47b600ea6bcc6da56fe9b81d6fe4022e4c

Inline Array calculations can cause synthesis failures

Some tools have bugs (version dependent) in const prop in this case.

Example 

module Foo (input clock, input in, output [2:0] out);
  reg [2:0] state;
  wire [7:0][2:0] array = 24'h4 << 6;
  wire [2:0] a = array[state];
  wire [2:0] b = array[state + 3'h1 + 3'h1];
  // works:      array[state + (3'h1 + 3'h1)]
  // works:      array[state + 3'h2]
  always @(posedge clock) state <= in ? a : b;
  assign out = b;
endmodule

Workaround 

Flag added to export verilog to force array index calculations to not be inline.

https://github.com/llvm/circt/commit/15a1f95f2d59767f20b459a12ac42338de22bc97

Memory semantics changed by synthesis

Read/Write forwarding behavior is dependent on memory size, since the synthesis tool changes it’s mapping based on that. The “optimized” mapping does not preserve the behavior of the verilog. This is a known issue reported on various forums by multiple people. There are some version dependencies on when this manifests.

Example 

Qux:
  module Qux:
    input clock: Clock
    input addr: UInt<1>
    input r: {en: UInt<1>, flip data: {a: UInt<32>, b: UInt<32>}, addr: UInt<1>}
    input w: {en: UInt<1>, data: {a: UInt<32>, b: UInt<32>}, addr: UInt<1>, mask: {a: UInt<1>, b: UInt<1>}}

    mem m :
      data-type => {a: UInt<32>, b: UInt<32>}
      depth => 1
      reader => r
      writer => w
      read-latency => 0
      write-latency => 1
      read-under-write => undefined

    m.r.clk <= clock
    m.r.en <= r.en
    m.r.addr <= r.addr
    r.data <= m.r.data

    m.w.clk <= clock
    m.w.en <= w.en
    m.w.addr <= w.addr
    m.w.data <= w.data
    m.w.mask <= w.mask

Compile with either firtool -repl-seq-mem -repl-seq-mem-file=mem.conf Foo.fir and firrtl -i Foo.fir.

Workaround 

FIRRTL memory lowering has a flag to generate attributes on memory implementations that preserve the behavior described in the verilog. This is not a general solution, this bug could impact anyone making memory-looking things. It was decided not to try to reverse engineer the conditions which cause the bug to manifest (since they are version dependent), thus there isn’t a universal fix that can be applied in the generated verilog.

https://github.com/llvm/circt/commit/e9f443be475e0ef796c0c6af1ce09d6e783fcd5a

Clock Gates and Enables Not Recognized For Registers

Clock gates in some rtl-based power estimation tools are unable to recognize clock gates and enables if they are not generated as if statements in always blocks. This is a very narrow pattern match with significant implications for the tools lint results and quality of analysis results.

Example 

  %count = seq.firreg %2 clock %clock sym @count : i2
  %1 = comb.mux %cond, %value, %count : i2		  %1 = comb.mux bin %cond, %value, %count : i2
  %2 = comb.mux %reset, %c0_i2, %1 : i2		  %2 = comb.mux bin %reset, %c0_i2, %1 : i2

The mux on cond must become an if in the output since it forms a self-loop on the register count.

Workaround 

Effort at several points in lowering make effort to find self-loops through register read and write ports and muxes. These are generated as if statements in the always block that updates the register.

https://github.com/llvm/circt/pull/3815