llvm / llvm/circt

[FIRRTL] duplicated `assert` and `$fatal` with dynamic subaccess

Open
#6,604 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C++
Stars
2.2k
Forks
524
Avg merge
3d 2h
Merged PRs (30d)
46

Description

Hi, I found that using `assert` with dynamic subaccess in firrtl would generate two different type but duplicated `assert` and `$fatal` in verilog.

## Duplicated `assert` and `$fatal` with dynamic subaccess
``` scala
FIRRTL version 3.3.0
circuit our :%[[
{
"class":"firrtl.transforms.DedupGroupAnnotation",
"target":"~our|our",
"group":"our"
}
]]
module our : @[src/main/scala/gcd/GCD.scala 16:7]
input clock : Clock
input reset : UInt<1>
output io : { flip addr : UInt<1>, flip wen : UInt<1>[2]} @[src/main/scala/gcd/GCD.scala 17:14]

node _T = eq(reset, UInt<1>(0h0)) @[src/main/scala/gcd/GCD.scala 21:9]
when _T : @[src/main/scala/gcd/GCD.scala 21:9]
node _T_1 = eq(io.wen[io.addr], UInt<1>(0h0)) @[src/main/scala/gcd/GCD.scala 21:9]
when _T_1 : @[src/main/scala/gcd/GCD.scala 21:9]
printf(clock, UInt<1>(0h1), "Assertion failed: gg!\n at GCD.scala:21 assert(io.wen(io.addr), \"gg!\")\n") : printf @[src/main/scala/gcd/GCD.scala 21:9]
assert(clock, io.wen[io.addr], UInt<1>(0h1), "") : assert @[src/main/scala/gcd/GCD.scala 21:9]
```
generates
``` verilog
// Generated by CIRCT firtool-1.62.0
// Standard header to adapt well known macros for prints and assertions.

// Users can define 'ASSERT_VERBOSE_COND' to add an extra gate to assert error printing.
`ifndef ASSERT_VERBOSE_COND_
`ifdef ASSERT_VERBOSE_COND
`define ASSERT_VERBOSE_COND_ (`ASSERT_VERBOSE_COND)
`else // ASSERT_VERBOSE_COND
`define ASSERT_VERBOSE_COND_ 1
`endif // ASSERT_VERBOSE_COND
`endif // not def ASSERT_VERBOSE_COND_

// Users can define 'STOP_COND' to add an extra gate to stop conditions.
`ifndef STOP_COND_
`ifdef STOP_COND
`define STOP_COND_ (`STOP_COND)
`else // STOP_COND
`define STOP_COND_ 1
`endif // STOP_COND
`endif // not def STOP_COND_

module our( // src/main/scala/gcd/GCD.scala:16:7
input clock,
reset,
io_addr, // src/main/scala/gcd/GCD.scala:17:14
io_wen_0, // src/main/scala/gcd/GCD.scala:17:14
io_wen_1 // src/main/scala/gcd/GCD.scala:17:14
);

wire _GEN = io_addr ? io_wen_1 : io_wen_0; // src/main/scala/gcd/GCD.scala:21:9
`ifndef SYNTHESIS // src/main/scala/gcd/GCD.scala:21:9
always @(posedge clock) begin // src/main/scala/gcd/GCD.scala:21:9
if (~reset & ~_GEN) begin // src/main/scala/gcd/GCD.scala:21:9
if (`ASSERT_VERBOSE_COND_) // src/main/scala/gcd/GCD.scala:21:9
$error("Assertion failed: gg!\n at GCD.scala:21 assert(io.wen(io.addr), \"gg!\")\n"); // src/main/scala/gcd/GCD.scala:21:9
if (`STOP_COND_) // src/main/scala/gcd/GCD.scala:21:9
$fatal; // src/main/scala/gcd/GCD.scala:21:9
end
end // always @(posedge)
`endif // not def SYNTHESIS
always @(posedge clock) begin // src/main/scala/gcd/GCD.scala:21:9
if (~reset) // src/main/scala/gcd/GCD.scala:21:9
assert__assert: assert(_GEN); // src/main/scala/gcd/GCD.scala:21:9
end // always @(posedge)
endmodule
```
Here firtool not only generates
``` verilog
if (~reset & ~_GEN) begin // src/main/scala/gcd/GCD.scala:21:9
if (`ASSERT_VERBOSE_COND_) // src/main/scala/gcd/GCD.scala:21:9
$error("Assertion failed: gg!\n at GCD.scala:21 assert(io.wen(io.addr), \"gg!\")\n"); // src/main/scala/gcd/GCD.scala:21:9
if (`STOP_COND_) // src/main/scala/gcd/GCD.scala:21:9
$fatal; // src/main/scala/gcd/GCD.scala:21:9
end
```
but also generates
```verilog
if (~reset) // src/main/scala/gcd/GCD.scala:21:9
assert__assert: assert(_GEN); // src/main/scala/gcd/GCD.scala:21:9
```
which is duplicted.
## Only one `$fatal` generated with static subaccess

If we change `io.addr` to `0`, like:
```scala
FIRRTL version 3.3.0
circuit our :%[[
{
"class":"firrtl.transforms.DedupGroupAnnotation",
"target":"~our|our",
"group":"our"
}
]]
module our : @[src/main/scala/gcd/GCD.scala 16:7]
input clock : Clock
input reset : UInt<1>
output io : { flip addr : UInt<1>, flip wen : UInt<1>[2]} @[src/main/scala/gcd/GCD.scala 17:14]

node _T = eq(reset, UInt<1>(0h0)) @[src/main/scala/gcd/GCD.scala 21:9]
when _T : @[src/main/scala/gcd/GCD.scala 21:9]
node _T_1 = eq(io.wen[0], UInt<1>(0h0)) @[src/main/scala/gcd/GCD.scala 21:9]
when _T_1 : @[src/main/scala/gcd/GCD.scala 21:9]
printf(clock, UInt<1>(0h1), "Assertion failed: gg!\n at GCD.scala:21 assert(io.wen(0.U), \"gg!\")\n") : printf @[src/main/scala/gcd/GCD.scala 21:9]
assert(clock, io.wen[0], UInt<1>(0h1), "") : assert @[src/main/scala/gcd/GCD.scala 21:9]
```
it would generate only one `$fatal` without duplicated `assert`:
``` verilog
// Generated by CIRCT firtool-1.62.0
// Standard header to adapt well known macros for prints and assertions.

// Users can define 'ASSERT_VERBOSE_COND' to add an extra gate to assert error printing.
`ifndef ASSERT_VERBOSE_COND_
`ifdef ASSERT_VERBOSE_COND
`define ASSERT_VERBOSE_COND_ (`ASSERT_VERBOSE_COND)
`else // ASSERT_VERBOSE_COND
`define ASSERT_VERBOSE_COND_ 1
`endif // ASSERT_VERBOSE_COND
`endif // not def ASSERT_VERBOSE_COND_

// Users can define 'STOP_COND' to add an extra gate to stop conditions.
`ifndef STOP_COND_
`ifdef STOP_COND
`define STOP_COND_ (`STOP_COND)
`else // STOP_COND
`define STOP_COND_ 1
`endif // STOP_COND
`endif // not def STOP_COND_

module our( // src/main/scala/gcd/GCD.scala:16:7
input clock,
reset,
io_addr, // src/main/scala/gcd/GCD.scala:17:14
io_wen_0, // src/main/scala/gcd/GCD.scala:17:14
io_wen_1 // src/main/scala/gcd/GCD.scala:17:14
);

`ifndef SYNTHESIS // src/main/scala/gcd/GCD.scala:21:9
always @(posedge clock) begin // src/main/scala/gcd/GCD.scala:21:9
if (~reset & ~io_wen_0) begin // src/main/scala/gcd/GCD.scala:21:9
if (`ASSERT_VERBOSE_COND_) // src/main/scala/gcd/GCD.scala:21:9
$error("Assertion failed: gg!\n at GCD.scala:21 assert(io.wen(0.U), \"gg!\")\n"); // src/main/scala/gcd/GCD.scala:21:9
if (`STOP_COND_) // src/main/scala/gcd/GCD.scala:21:9
$fatal; // src/main/scala/gcd/GCD.scala:21:9
end
end // always @(posedge)
`endif // not def SYNTHESIS
endmodule
```

## Expected behaviour
Dynamic subaccess with `assert` in firrtl may not generate `assert` but only generate `$fatal` and `$error` in verilog.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the dynamic and static subaccess examples with firtool 1.62.0 and compare their generated Verilog. Trace the FIRRTL assert handling entry point responsible for converting the operation, then verify that the dynamic case emits only the intended $error and $fatal behavior without a second Verilog assert.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, scala
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.