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.


module ALU(
  input         clock,
  input  [4:0]  operation,
  input  [63:0] inputs_1,
  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)
        casez_tmp_1 = inputs_0 & inputs_1;
        casez_tmp_1 = inputs_0 | inputs_1;
        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]};
        casez_tmp_1 = inputs_0;


$ verilator --version
Verilator 5.008 2023-03-04 rev v5.008
$ verilator --lint-only
%Warning-LATCH: 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
                ... Use "/* verilator lint_off LATCH */" and lint_on around source to disable this message.
%Error: Exiting due to 1 warning(s)


Flag added to promote all storage to the top level of a module.

Inline Array calculations can cause synthesis failures

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


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;


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

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.


  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 <=

    m.w.clk <= clock
    m.w.en <= w.en
    m.w.addr <= w.addr <=
    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.


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.