suggestName for IO(Vec(...))

Viewed 122

I have a module like so...

class ApbSplitter (clients : List[ApbRange]) extends MultiIOModule {
  val nApb = clients.length

  val apb   = IO(Vec(nApb, new ApbChannel()))
  val apb_m = IO(Flipped(new ApbChannel))
  ...

What I'd like to do is suggestName to each element of the Vec so that instead of prefixed as apb_0_ apb_1_ etc... it's whatever I provide for each element.

I can apb.suggestName but that only affects the leading prefix and the array indices remain. Doing apb(idx).suggestName("blah") compiles but has no effect.

Any way to make this happen?

3 Answers

Got this to work by eliminating the Vec and creating a list of IO

case class ApbRange (name: String, loAddr : Int, hiAddr : Int)

class ApbSplitter (clients : List[ApbRange]) extends MultiIOModule {
  val apb = clients.map({x => IO(new ApbChannel).suggestName(x.name)})
  val apb_m = IO(Flipped(new ApbChannel))
  ...

Not sure if this is canonical but seems to do the trick just fine.

I am guessing your new apbChannel has a bunch of Input Output signals or wires. So instead of apb(idx).suggestName if your apbChannel has a (say) val ip = Input(Bool()) you can do apb(idx).ip.suggestName("blah")

Answering this with Brian's other post and comment on his own answer on this post in mind. This is going to be a long answer because it touches on a couple of warts in the Chisel API that are being improved but are certainly relevant in the current version (v3.4.3 as of 12 Aug 2021).

Brian's answer is correct that if you want to name the individual fields you need to use a Seq and not a Vec. The reason for this is that, from Chisel's perspective, an IO of type Vec is a single port with an aggregate type, whereas the Seq is just a sequence of unrelated ports. The Seq is a Scala construct (whereas Vec comes from Chisel), so Chisel itself doesn't know anything about the relationship between the ports in the Seq.

The problem then, is that you need a Vec to do dynamic indexing. You can use VecInit to create a dynamically indexable Wire from your Seq whenever you need to do dynamic indexing:

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)
}

This will work so long as deq is itself a port. It will not work if deq were a Wire because <> is a commutative operator and is thus ambiguous when connecting 2 bidirectional wires. For a longer explanation, see this PR comment.

If deq needs to be a Wire for some reason, you could use a helper module that does have Vecs as ports:

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
}

It's a bit of a pain, but it at least resolves the ambiguity. There are other utilities we could build--for example, instead of a custom InnerHelper for each case, we could make a utility method that creates a module so that the returned value of dynamically indexing a Seq is a port of a new submodule, but it's a bit tricky.

The good news is that a better way is coming--DataView in Chisel 3.5 should make it possible to view a Seq as a Vec (rather than having to use VecInit which creates a Wire) which makes it easier to avoid this Wire <> connect ambiguity issue. I also hope to either "fix" <> for Wires or perhaps provide a new operator that is not commutative :<>, but that is not yet being worked on.

Related