How to create a vector of vector when I'm defining IO

Viewed 1418

I'm trying to define Vector of Vector for my IO, but I'm getting an error from the chisel saying:

vec element 'Vec(chisel3.util.DecoupledIO@2b57)' must be hardware, not a bare Chisel type

The code which I've written is like the following:

//Module's argument
   ,val ArgsOut: Array[Int]
...
...

val Args = for (i <- 0 until ArgsOut.length) yield {
      val arg = Vec(ArgsOut(i), Decoupled(UInt()))
      arg
 }

 val outputArg = Vec(Args)
4 Answers

Some things to consider

  1. IO ports, i.e. members of the IO Bundle, must be chisel hardware constructs, Args is a scala Vector type, it needs to be chisel Vec
  2. All elements of a Vec must be the same size, mostly a consequence of the need to be able to index elements of the Vec. You have each element of Args as a Vec whos's length is determined by some the elements of ArgsOut. Vec(n, type) will not
    1. Do really mean to have a 2D Vec(Vec( of decoupled IO's?
  3. Your UInt in the Decoupled has an unknown width. This is not strictly speaking an error, because Firrtl can infer the width in most situations. But again this can be a problem for the requirement that a Vec's element are all the same length. Inferred widths in IOs should be used cautiously.

I was able to construct at IOBundle like this

  val io = IO(new Bundle {
    val args = Vec(ArgsOut.length, Vec(ArgsOut(0), Decoupled(UInt(18.W))))
    val outputArg = Flipped(args)
  })

Which compiles but may not quite be what you had in mind. I was able to connect the io's using

io.outputArg <> io.args

If this doesn't seem to fit your use case, I need to know a bit more how you intend to use the fields and we should be able to figure out how to wire them up.

Here is an illustration of how to use a subclass of Record to manage a virtual array of Vectors of varying length. This example runs for me. It is not exactly the same as your examples. But I think it makes things clearer. This is for the use case where you do not need to access your Vecs via a UInt, but is for when you need to match a heterogenous mix of Vectors.

import chisel3._
import chisel3.iotesters.PeekPokeTester
import org.scalatest.{FreeSpec, Matchers}

import scala.collection.immutable.ListMap

final class VariableBundle(elts: (String, Vec[UInt])*) extends Record {
  val elements = ListMap(elts map { case (field, elt) => field -> elt.chiselCloneType }: _*)
  def apply(elt: String): Vec[UInt] = elements(elt)
  override def cloneType = (new VecVecBundle(elements.toList: _*)).asInstanceOf[this.type]
}

class SeqIO(val sizes: Array[Int]) extends Module {
  val io = IO(new VariableBundle(Seq.tabulate(sizes.length) { i =>
        s"vec_in_$i" -> Input(Vec(sizes(i), UInt(8.W)))
      } ++
      Seq.tabulate(sizes.length) { i =>
        s"vec_out_$i" -> Output(Vec(sizes(i), UInt(8.W)))
      }:_*
    )
  )

  for(i <- sizes.indices) {
    io(s"vec_out_$i") := io(s"vec_in_$i")
  }
}

class SeqIOTester(c: SeqIO) extends PeekPokeTester(c) {
  for(i <- c.sizes.indices) {
    for(j <- 0 until c.sizes(i)) {
      poke(c.io(s"vec_in_$i")(j), j)
    }
  }

  step(1)

  for(i <- c.sizes.indices) {
    for(j <- 0 until c.sizes(i)) {
      expect(c.io(s"vec_out_$i")(j), j)
    }
  }

}

class SeqIOSpec extends FreeSpec with Matchers {
  "illustrate how to build bundles that have vecs wrapping different sized vecs" in {
    iotesters.Driver.execute(Array.empty[String], () => new SeqIO(Array(1, 2, 3, 4))) { c =>
      new SeqIOTester(c)
    } should be (true)
  }
}

Let me write the actual code:

class LoopLatch(val NumInputs: Int,
                val ArgsOut: Array[Int],
                val ID: Int)
               (implicit val p: Parameters) extends Module with CoreParams with UniformPrintfs {

  val io = IO(new Bundle {
    val inputArg = Vec(NumInputs, Flipped(Decoupled(new DataBundle())))

    val Args = Vec(for (i <- 0 until ArgsOut.length) yield {
      val arg = Vec(ArgsOut(i), Decoupled(new DataBundle()))
      arg
    })

DataBundle() is a custom data type which I have defined it in a separate file. What I really want to do is that. I want to pass an array to the module like ArgsOut and then build a vector of vector of Databundle and each subsequent vector has its own number of Databundle which comes from the input array.

The loopLatch module is a wrapper for other module, LiveInNode which I have designed. Actually the LoopLatch module only has a vector of this elements and then provide an IO for it. Here is the actual code:

class LoopStart(val NumInputs: Int,
            val ArgsOut: Array[Int],
            val ID: Int)
           (implicit val p: Parameters) extends Module with CoreParams with UniformPrintfs {

  val io = IO(new Bundle {
    val inputArg = Vec(NumInputs, Flipped(Decoupled(new DataBundle())))
    val Args = Vec(ArgsOut.length, Vec(ArgsOut(0), Decoupled(new DataBundle())))


    val Out = new VecVecBundle(
      Seq("inputArg" -> Vec(NumInputs, Flipped(Decoupled(new DataBundle())))) ++
        (ArgsOut.indices).map { i =>
          val arg = Vec(ArgsOut(i), Decoupled(new DataBundle()))
          s"Args_$i" -> arg
        }:_*
    )

    val pSignal = Vec(NumInputs, Flipped(Decoupled(Bool())))

    val Finish = Vec(NumInputs, Flipped(Decoupled(new ControlBundle())))

  }
  )

  val Args = for (i <- 0 until NumInputs) yield {
    val arg = Module(new LiveInNode(NumOuts = 1, ID = i))
    arg
  }

  //Iterating over each loopelement and connect them to the IO
  for (i <- 0 until NumInputs) {
    Args(i).io.InData <> io.inputArg(i)
    Args(i).io.Finish <> io.Finish(i)
    Args(i).io.pred <> io.pSignal(i)
  }

  for (i <- 0 until ArgsOut.length) {
      io.Out("Args_0") <> Args(i).io.Out
  }

}
Related