Steve Burns
Steve Burns

Reputation: 319

chisel3: Want to use Vec, but need to use IndexedSeq

In generating a tapped shift register using "scanLeft", I needed to use a IndexedSeq of registers and explicit copying of the result of scanLeft to an output wire. Here are four examples (with testbench). Only the first one works. I like the second one best, and wonder why it is not correct.

package tsr

import chisel3._
import chisel3.util._
import chisel3.iotesters._
import org.scalatest.{Matchers, FlatSpec}

object TappedShiftRegister {

  def apply[ T <: Data]( d : T, n : Int) : Vec[T] = {
    val rs = IndexedSeq.tabulate( n){ (i) => Reg( d.cloneType)}
    val rs0 = rs.scanLeft( d){ case (x, r) => r := x; r}
    val ys = Wire( Vec( n+1, d.cloneType))
    (ys zip rs0).foreach{ case (y,r) => y := r}
    ys
  }

  def apply1[ T <: Data]( d : T, n : Int) : Vec[T] = { // ChiselException
    val rs = Vec.tabulate( n){ (i) => Reg( d.cloneType)}
    Vec( rs.scanLeft( d){ case (x, r) => r := x; r})
  }

  def apply2[ T <: Data]( d : T, n : Int) : Vec[T] = { // ChiselException
    val rs = IndexedSeq.tabulate( n){ (i) => Reg( d.cloneType)}
    Vec( rs.scanLeft( d){ case (x, r) => r := x; r})
  }

  def apply3[ T <: Data]( d : T, n : Int) : Vec[T] = { // Wrong values
    val rs = Vec.tabulate( n){ (i) => Reg( d.cloneType)}
    val rs0 = rs.scanLeft( d){ case (x, r) => r := x; r}
    val ys = Wire( Vec( n+1, d.cloneType))
    (ys zip rs0).foreach{ case (y,r) => y := r}
    ys
  }

}

class TappedShiftRegisterIfc extends Module {
  val io = IO( new Bundle {
    val inp = Input( UInt(8.W))
    val out = Output( Vec( 5, UInt(8.W)))
  })
}

class GenericTSRTest( factory : () => TappedShiftRegisterIfc) extends FlatSpec with Matchers {
  it should "meet all PeekPokeTester expectations" in {
    chisel3.iotesters.Driver( factory, "firrtl") { c => new PeekPokeTester(c) {
      val N = 4
      val off = 47
      for { i <- 0 until 100} {
        poke( c.io.inp, off+i)
        expect( c.io.out(0), off+i) // mealy output
        step(1)
        for { j <- 0 until N if i > j} {
          expect( c.io.out(j+1), off+i-j) // moore outputs
        }
      }
    }} should be (true)
  }
}

class TSRTest  extends GenericTSRTest( () => new TappedShiftRegisterIfc { io.out := TappedShiftRegister( io.inp, 4) })
class TSRTest1 extends GenericTSRTest( () => new TappedShiftRegisterIfc { io.out := TappedShiftRegister.apply1( io.inp, 4)})
class TSRTest2 extends GenericTSRTest( () => new TappedShiftRegisterIfc { io.out := TappedShiftRegister.apply2( io.inp, 4)})
class TSRTest3 extends GenericTSRTest( () => new TappedShiftRegisterIfc { io.out := TappedShiftRegister.apply3( io.inp, 4)})

Upvotes: 0

Views: 692

Answers (1)

Chick Markley
Chick Markley

Reputation: 4051

First. The following, closest to what you like, works. I added to your test suite.

def apply4[ T <: Data]( d : T, n : Int) : Vec[T] = { // No ChiselException!
  val rs = Seq.fill(n){ Reg(d.cloneType)}
  VecInit(rs.scanLeft(d){ case (x, r) => r := x; r})
}
class TSRTest4 extends GenericTSRTest( () => new TappedShiftRegisterIfc { io.out := TappedShiftRegister.apply4( io.inp, 4)})

Details:

  • The Vec.tabulate has been deprecated, use VecInit instead.
  • Using VecInit instead of the Seq resulted in an extra delay. I'm still trying to figure that out, but the rule of thumb, should be don't create Vecs unless you really need them. And in this case you only need the one Vec so that you can assign it directly to the output IO.
  • It's pretty easy to see the problem in the Firrtl file.

Upvotes: 1

Related Questions