Get an item in Seq using UInt

Viewed 485

I'm trying to write a cache memory, so I created a Seq of type Mem because I'm trying to have access to all elements in a set of the cache at the same time.

val metaMem = Seq.fill(nWays) (Mem((nSets), new MetaData))

and then I want to have indexing like below:

   metaMem(way).write(set, MD)

But, because, way is UInt in my code, and seq only accepts Int for indexing, it would cause compile error. Does anyone have any suggestions on how to solve this problem? Thanks a lot

1 Answers

Typically to access a hardware element using a dynamic value in a UInt you must use a Vec. The easiest way in a case like this is to create a Vec by using VecInit which, given a seq, defines the Vec and wires up your elements.

But Memories are not a subclass of Data (as is required by Vec/VecInit). Here is a very naive example of a simple module that creates a bank of memories and provides read/write access to them.

/** Simulate a VecLike bank of memories
  */
class MemBank(val banks: Int, val bankDepth: Int) extends MultiIOModule {
  val bank = IO(Input(UInt(16.W)))
  val address = IO(Input(UInt(16.W)))
  val isRead = IO(Input(Bool()))
  val inputValue = IO(Input(UInt(32.W)))
  val outputValue = IO(Output(UInt(32.W)))

  val mems = Seq.fill(banks) { Mem(bankDepth, UInt(32.W)) }

  outputValue := DontCare

  when(isRead) {
    (0 until banks).foldLeft(when(false.B) {}) {
      case (whenContext, bankIndex) =>
        whenContext.elsewhen(bank === bankIndex.U) {
          outputValue := mems(bankIndex)(address)
        }
    }
  }.otherwise {
    (0 until banks).foldLeft(when(false.B) {}) {
      case (whenContext, bankIndex) =>
        whenContext.elsewhen(bank === bankIndex.U) {
          mems(bankIndex)(address) := inputValue
        }
    }
  }
}

And here is a UnitTest that can demonstrate using this module

class MemBankTest extends FreeSpec with ChiselScalatestTester {

  "MemBankSimulation should work" in {
    test(new MemBank(banks = 3, bankDepth = 3)) { dut =>
      // write values into memory
      dut.isRead.poke(false.B)
      for (bank <- 0 until dut.banks) {
        for (address <- 0 until dut.bankDepth) {
          dut.clock.step()
          dut.bank.poke(bank.U)
          dut.address.poke(address.U)
          dut.inputValue.poke((bank * 1000 + address).U)
        }
      }

      // read values out of memory banks
      dut.isRead.poke(true.B)
      for (bank <- 0 until dut.banks) {
        for (address <- 0 until dut.bankDepth) {
          dut.clock.step()
          dut.bank.poke(bank.U)
          dut.address.poke(address.U)
          println(f"Bank $bank%3d at address $address%3d contains ${dut.outputValue.peek().litValue()}%6d")
        }
      }
    }
  }
}

There are a lot of other ways to do this and I would encourage you to look at other chisel projects like rocket-chip to see how multiple memories are handled. It would still be good to see how this approach works. It creates a Seq of Mem but it use foldLeft (a very useful method) to create a set of Muxes that select the bank you are interested in. There are two parallel uses of this to control reading and writing separately. I hope this helps, this is a good and tough question.

Related