-
Notifications
You must be signed in to change notification settings - Fork 636
Description
Type of issue: Bug Report
Please provide the steps to reproduce the problem:
The scala-cli snippet below gives a few different errors that you can cause:
- Set
readerUnintError
totrue
- Set
writerUnintError
totrue
- Comment out the existing
myMem.writePorts
connection, and use one of the other commented-out options
//> using scala 2.13.16
//> using repository https://s01.oss.sonatype.org/content/repositories/snapshots
//> using dep org.chipsalliance::chisel:7.0.0-RC1+28-2f256a83-SNAPSHOT
//> using plugin org.chipsalliance:::chisel-plugin:7.0.0-RC1+28-2f256a83-SNAPSHOT
//> using options -unchecked -deprecation -language:reflectiveCalls -feature -Xcheckinit -Xfatal-warnings -Ywarn-dead-code -Ywarn-unused -Ymacro-annotations
import chisel3._
import chisel3.util._
class Reader(readPort: MemoryReadPort[UInt], uninitError: Boolean = false) extends Module {
// `Flipped` because default `MemoryReadPort` alignment is `Input`
val port = IO(Flipped(readPort))
if (uninitError) {
port :<= DontCare
} else {
port.address :<= DontCare
port.enable :<= DontCare
}
}
class Writer(writePort: MemoryWritePort[UInt], uninitError: Boolean = false) extends Module {
// `Flipped` because default `MemoryWritePort` alignment is `Input`
val port = IO(Flipped(writePort))
if (uninitError) {
port :<= DontCare
} else {
port.address :<= DontCare
port.data :<= DontCare
port.enable :<= DontCare
}
}
class Foo extends Module {
val myMem = SRAM(8, UInt(5.W), 1, 1, 0)
val readPort = chiselTypeOf(myMem.readPorts.head)
val writePort = chiselTypeOf(myMem.writePorts.head)
// If set to `true`, firtool error: sink * not fully initialized in "Reader"
val readerUnintError = false
// If set to `true`, firtool error: sink * not fully initialized in "Writer"
val writerUnintError = false
val reader = Module(new Reader(readPort, readerUnintError))
val writer = Module(new Writer(writePort, writerUnintError))
// This works
reader.port :<>= myMem.readPorts.head
// firtool error: sink * not fully initialized in "Foo"
// myMem.writePorts.head :<= writer.port
// Chisel error: port.* in Writer cannot be written from module Foo.
// myMem.writePorts.head :<>= writer.port
// This works, but the LHS-vs-RHS isn't intuitive.
// writer.port :>= myMem.writePorts.head
// This works, has the LHS-vs-RHS I would expect, but is inconvenient due to enumerating each bundle element
myMem.writePorts.head.address :<= writer.port.address
myMem.writePorts.head.data :<= writer.port.data
myMem.writePorts.head.enable :<= writer.port.enable
}
object Main extends App {
import _root_.circt.stage.ChiselStage
println(
ChiselStage.emitSystemVerilog(
new Foo,
firtoolOpts = Array("-disable-all-randomization", "-strip-debug-info", "-default-layer-specialization=enable")
)
)
}
What is the current behavior?
I'm unable to make connections to the Memory*Port
bundles that I would expect, perhaps due to their default alignment being Input
rather than Output
:
chisel/src/main/scala-2/chisel3/util/SRAM.scala
Lines 23 to 82 in 2f256a8
/** A bundle of signals representing a memory read port. | |
* | |
* @tparam tpe The data type of the memory port | |
* @param addrWidth The width of the address signal | |
*/ | |
class MemoryReadPort[T <: Data](tpe: T, addrWidth: Int) extends Bundle { | |
val address = Input(UInt(addrWidth.W)) | |
val enable = Input(Bool()) | |
val data = Output(tpe) | |
} | |
/** A bundle of signals representing a memory write port. | |
* | |
* @tparam tpe The data type of the memory port | |
* @param addrWidth The width of the address signal | |
* @param masked Whether this read/write port should have an optional mask. | |
* | |
* @note `masked` is only valid if tpe is a Vec; if this is not the case no mask will be initialized | |
* regardless of the value of `masked`. | |
*/ | |
class MemoryWritePort[T <: Data](tpe: T, addrWidth: Int, masked: Boolean) extends Bundle { | |
val address = Input(UInt(addrWidth.W)) | |
val enable = Input(Bool()) | |
val data = Input(tpe) | |
val mask: Option[Vec[Bool]] = if (masked) { | |
val maskSize = tpe match { | |
case vec: Vec[_] => vec.size | |
case _ => 0 | |
} | |
Some(Input(Vec(maskSize, Bool()))) | |
} else { | |
None | |
} | |
} | |
/** A bundle of signals representing a memory read/write port. | |
* | |
* @tparam tpe The data type of the memory port | |
* @param addrWidth The width of the address signal | |
* @param masked Whether this read/write port should have an optional mask. | |
* | |
* @note `masked` is only valid if tpe is a Vec; if this is not the case no mask will be initialized | |
* regardless of the value of `masked`. | |
*/ | |
class MemoryReadWritePort[T <: Data](tpe: T, addrWidth: Int, masked: Boolean) extends Bundle { | |
val address = Input(UInt(addrWidth.W)) | |
val enable = Input(Bool()) | |
val isWrite = Input(Bool()) | |
val readData = Output(tpe) | |
val writeData = Input(tpe) | |
val mask: Option[Vec[Bool]] = if (masked) { | |
val maskSize = tpe match { | |
case vec: Vec[_] => vec.size | |
case _ => 0 | |
} | |
Some(Input(Vec(maskSize, Bool()))) | |
} else { | |
None | |
} | |
} |
Rather than being able to make connections like port :<= DontCare
, I either need to break out the fields of port
and connect to them individually (port.address :<= DontCare; port.enable :<= DontCare; ...
), or I need to use an unintuitive LHS-vs-RHS style: DontCare :>= port
. Is this the intended behavior, or is this just an annoying edge case in the Connectable
(or SRAM) API?
What is the expected behavior?
I should be able to connect bundles with matching alignment, and put the producer and consumer on the sides of the Connectable operator that I would expect.
Please tell us about your environment:
- version: 7.0.0-RC1+28-2f256a83-SNAPSHOT
- OS: macOS 15.5
Other Information
What is the use case for changing the behavior?
Making the Connectable API more convenient for users.