用布赖恩的other post 回答这个问题,并在脑海中评论他自己对这篇文章的回答。这将是一个很长的答案,因为它涉及到 Chisel API 中的几个缺陷,这些缺陷正在改进,但在当前版本中肯定是相关的(截至 2021 年 8 月 12 日的 v3.4.3)。
Brian 的回答是正确的,如果要命名各个字段,则需要使用 Seq 而不是 Vec。这样做的原因是,从 Chisel 的角度来看,Vec 类型的 IO 是具有聚合类型的单个端口,而 Seq 只是一系列不相关的端口。 Seq 是一个 Scala 构造(而 Vec 来自 Chisel),因此 Chisel 本身并不知道 Seq 中的端口之间的关系。
那么问题是你需要一个Vec 来做动态索引。当您需要进行动态索引时,您可以使用VecInit 从您的Seq 创建一个可动态索引的Wire:
For example:
class MyModule(names: Seq[String]) extends RawModule {
val enq = names.map(n => IO(Flipped(Decoupled(UInt(8.W)))).suggestName(n))
val idx = IO(Input(UInt(log2Ceil(names.size).W)))
val deq = IO(Decoupled(UInt(8.W)))
// enqWire connects all fields of enq
val enqWire = VecInit(enq)
// Need to make sure backpressure is always driven
enqWire.foreach(_.ready := false.B)
deq <> enqWire(idx)
}
只要deq 本身就是一个端口,这将起作用。如果deq 是 Wire,它将不起作用,因为 <> 是可交换运算符,因此在连接 2 条双向电线时会产生歧义。如需更详细的说明,请参阅this PR comment。
如果由于某种原因deq 需要成为Wire,您可以使用确实将 Vecs 作为端口的辅助模块:
For example:
class InnerHelper(n: Int) extends RawModule {
val enq = IO(Flipped(Vec(n, Decoupled(UInt(8.W)))))
val idx = IO(Input(UInt(log2Ceil(n).W)))
val jdx = IO(Input(UInt(log2Ceil(n).W)))
val deq = IO(Vec(n, Decoupled(UInt(8.W))))
// backpressure defaults
enq.foreach(_.ready := false.B)
deq.foreach { x =>
x.valid := false.B
x.bits := DontCare
}
deq(jdx) <> enq(idx)
}
class MyModule(names: Seq[String]) extends RawModule {
val enq = names.map(n => IO(Flipped(Decoupled(UInt(8.W)))).suggestName(n))
val idx = IO(Input(UInt(log2Ceil(names.size).W)))
val jdx = IO(Input(UInt(log2Ceil(names.size).W)))
val deq = names.map(n => IO(Decoupled(UInt(8.W))).suggestName(s"${n}_out"))
val helper = Module(new InnerHelper(names.size))
helper.enq <> enq
helper.idx := idx
helper.jdx := jdx
helper.deq <> deq
}
这有点痛苦,但它至少解决了歧义。我们可以构建其他实用程序——例如,我们可以创建一个实用程序方法来创建一个模块,以便动态索引 Seq 的返回值是新的子模块,但有点棘手。
好消息是一种更好的方法即将到来——Chisel 3.5 中的DataView 应该可以查看 Seq 为 Vec(而不是必须使用 @ 987654350@ 创建一个 Wire) 可以更轻松地避免此 Wire <> 连接歧义问题。我也希望为 Wires “修复”<>,或者提供一个新的运算符,它不是可交换的 :<>,但这还没有工作。