chipsalliance / chipsalliance/chisel
[firrtl] Hardware with nested direction generates failing firrtl with Reg / RegNext
- Dominant language
- Scala
- Stars
- 4.8k
- Forks
- 658
- Avg merge
- 18h 59m
- Merged PRs (30d)
- 14
Description
**Type of issue**: Bug Report
**Please provide the steps to reproduce the problem:**
Create an empty Chisel project, then add `Foo.scala` below:
```scala
package foo
import chisel3._
import chisel3.util._
// This is a bundle with nested direction
class BundleWithDirection extends Bundle {
val a = Input(UInt(3.W))
}
class Foo extends Module {
val io = IO(new Bundle{
// And I'd like to make it an input for this module
// For now this works smoothly
val in = new BundleWithDirection
val out = Output(UInt(3.W))
})
// In this example we'll simply delay the input for 1 cycle
val buffer = RegNext(io.in)
io.out := buffer.a
}
```
Then generate verilog code using `(new ChiselStage).execute( ... )` with argument `--target verilog`
**What is the current behavior?**
`firtool` will throw the following error:
```
src/main/scala/Foo.scala:18:25: error: 'firrtl.reg' op result #0 must be a passive non-'const' base type that does not contain analog, but got '!firrtl.bundle>'
val buffer = RegNext(io.in)
^
src/main/scala/Foo.scala:18:25: note: see current operation: %3 = "firrtl.reg"(%arg0) <{annotations = [], name = "buffer", nameKind = #firrtl}> : (!firrtl.clock) -> !firrtl.bundle>
```
**What is the expected behavior?**
The `BundleWithDirection ` bundle only contains `Input` signal, so `RegNext(io.in)` should be able to generate verilog code like
```verilog
reg [2:0] buffer_a; // src/main/scala/Foo.scala:18:25
always @(posedge clock) // src/main/scala/Foo.scala:12:7
buffer_a <= io_in_a; // src/main/scala/Foo.scala:18:25
assign io_out = buffer_a; // src/main/scala/Foo.scala:12:7, :18:25
```
**Please tell us about your environment:**
- version: `7.7.0`
- OS: `Linux 6.8.0-124-generic #124~22.04.1-Ubuntu SMP x86_64 GNU/Linux`
**Other Information**
The Chisel code above could generate fir code with `--target chirrtl`:
```firrtl
FIRRTL version 6.0.0
circuit Foo :%[[
{
"class":"firrtl.transforms.DedupGroupAnnotation",
"target":"~|Foo",
"group":"Foo"
}
]]
layer Verification, bind, "verification" :
layer Assert, bind, "verification/assert" :
layer Temporal, inline :
layer Assume, bind, "verification/assume" :
layer Temporal, inline :
layer Cover, bind, "verification/cover" :
layer Temporal, inline :
public module Foo : @[src/main/scala/Foo.scala 12:7]
input clock : Clock @[src/main/scala/Foo.scala 12:7]
input reset : UInt<1> @[src/main/scala/Foo.scala 12:7]
output io : { in : { flip a : UInt<3>}, out : UInt<3>} @[src/main/scala/Foo.scala 13:16]
reg buffer : { flip a : UInt<3>}, clock @[src/main/scala/Foo.scala 18:25]
connect buffer.a, io.in.a @[src/main/scala/Foo.scala 18:25]
connect io.out, buffer.a @[src/main/scala/Foo.scala 19:12]
```
It seems that the `Input` in Chisel code is interpeted as `output io : { in : { flip a : UInt<3>}, ...}`, and the `RegNext` clones the type `flip a: UInt<3>`, which is considered a non-passive signal by `firtool`, causing it unable to connect `buffer.a` with `io.in.a`
This problem persists with `val buffer = Reg(chiselTypeOf(io.in))`
When removing the `flip` in `reg buffer : { flip a : UInt<3>}, clock`, the `firtool` is able to generate the correct verilog code, indicating that this issue might be fixed by removing the direction notation in fir `reg`.
**What is the use case for changing the behavior?**
Bundles with nested direction works well for grouping signals with similar functionalities together, e.g. gathering all the signals that connects to another specific module, so that they're easy to find, and can be connected using one line of `source <> dest`.
When a bundle consists of signal with the same direction, it should be able to use with `RegNext` or `Reg` for convenient buffering.
Contributor guide
Assessment
This issue has not been assessed yet.