Commit f26ba638 authored by Jens Korinth's avatar Jens Korinth
Browse files

Finish streamlined implementation of RegisterFile

* entirely based on Queues now, fixed firing logic
* still with workaround for problem in Queue with optional fields
* simplified ProgrammableMaster
* changed Registers write method to return Response instead of Boolean
* switched completely to Spec testing: register files are generated
  ad-hoc, corresponding master program and testing steps are
  automatically generated
parent effaef3e
......@@ -73,7 +73,7 @@ class RegisterFile(cfg: RegisterFile.Configuration)
logLevel: Logging.Level) extends Module with Logging {
class ReadData extends Bundle {
val data = io.saxi.readData.bits.data.cloneType
val resp = io.saxi.readData.bits.data.cloneType
val resp = io.saxi.readData.bits.resp.cloneType
override def cloneType = (new ReadData).asInstanceOf[this.type]
}
......@@ -88,20 +88,20 @@ class RegisterFile(cfg: RegisterFile.Configuration)
val in_q_wd = Module(new Queue(io.saxi.writeData.bits.data.cloneType, entries = cfg.fifoDepth, pipe = true))
val read_reg = Reg((new ReadData).cloneType)
val resp_reg = RegInit(io.saxi.writeResp.bits.bresp.cloneType, init = Response.slverr)
val resp_reg = RegNext(Response.slverr, init = Response.slverr)
//val out_q_rd = Module(new Queue(new Axi4Lite.Data.Read, cfg.fifoDepth))
val out_q_rd = Module(new Queue((new ReadData).cloneType, cfg.fifoDepth))
//val out_q_wr = Module(new Queue(new Axi4Lite.WriteResponse, cfg.fifoDepth))
val out_q_wr = Module(new Queue(io.saxi.writeResp.bits.bresp.cloneType, cfg.fifoDepth))
when (in_q_ra.io.enq.fire) { info(p"received read address: ${in_q_ra.io.enq.bits}") }
when (in_q_wa.io.enq.fire) { info(p"received write address: ${in_q_wa.io.enq.bits}") }
when (in_q_wd.io.enq.fire) { info(p"received write data: ${in_q_wd.io.enq.bits}") }
when (out_q_rd.io.enq.fire) { info(p"enq read data: ${out_q_rd.io.enq.bits}") }
when (out_q_rd.io.deq.fire) { info(p"deq read data: ${out_q_rd.io.enq.bits}") }
when (out_q_wr.io.enq.fire) { info(p"enq write resp: ${out_q_wr.io.enq.bits}") }
when (out_q_wr.io.deq.fire) { info(p"deq write resp: ${out_q_wr.io.enq.bits}") }
/*when (in_q_ra.io.enq.fire) { info(p"received read address: 0x${Hexadecimal(in_q_ra.io.enq.bits)} (${in_q_ra.io.enq.bits})") }
when (in_q_wa.io.enq.fire) { info(p"received write address: 0x${Hexadecimal(in_q_wa.io.enq.bits)} (${in_q_wa.io.enq.bits})") }
when (in_q_wd.io.enq.fire) { info(p"received write data: 0x${Hexadecimal(in_q_wd.io.enq.bits)} (${in_q_wd.io.enq.bits})") }
when (out_q_rd.io.enq.fire) { info(p"enq read data: 0x${Hexadecimal(out_q_rd.io.enq.bits.data)} (${out_q_rd.io.enq.bits.data})") }
when (out_q_rd.io.deq.fire) { info(p"deq read data: 0x${Hexadecimal(out_q_rd.io.deq.bits.data)} (${out_q_rd.io.deq.bits.data})") }
when (out_q_wr.io.enq.fire) { info(p"enq write resp: 0x${Hexadecimal(out_q_wr.io.enq.bits)} (${out_q_wr.io.enq.bits})") }
when (out_q_wr.io.deq.fire) { info(p"deq write resp: 0x${Hexadecimal(out_q_wr.io.deq.bits)} (${out_q_wr.io.deq.bits})") }*/
io.saxi.readData.bits.defaults
io.saxi.readData.valid := false.B
......@@ -118,38 +118,49 @@ class RegisterFile(cfg: RegisterFile.Configuration)
in_q_wd.io.enq.valid := io.saxi.writeData.valid
io.saxi.writeData.ready := in_q_wd.io.enq.ready
val out_q_rd_enq_valid = RegNext(false.B, init = false.B)
out_q_rd.io.enq.bits := read_reg
out_q_rd.io.enq.valid := false.B
out_q_rd.io.enq.valid := out_q_rd_enq_valid
out_q_rd.io.deq.ready := io.saxi.readData.ready
io.saxi.readData.bits.data := out_q_rd.io.deq.bits.data
io.saxi.readData.bits.resp := out_q_rd.io.deq.bits.resp
io.saxi.readData.valid := out_q_rd.io.deq.valid
val out_q_wr_enq_valid = RegNext(false.B, init = false.B)
out_q_wr.io.enq.bits := resp_reg
out_q_wr.io.enq.valid := false.B
out_q_wr.io.enq.valid := out_q_wr_enq_valid
out_q_wr.io.deq.ready := io.saxi.writeResp.ready
io.saxi.writeResp.valid := out_q_wr.io.deq.valid
io.saxi.writeResp.bits.bresp := out_q_wr.io.deq.bits
when (in_q_ra.io.deq.valid) {
in_q_ra.io.deq.ready := out_q_rd.io.enq.ready
when (in_q_ra.io.deq.fire) {
val addr = in_q_ra.io.deq.bits
read_reg.resp := Response.slverr
for (off <- cfg.regs.keys.toList.sorted) {
when (addr === off.U) { cfg.regs(off).read() map { v =>
info(p"reading from address $addr -> $v")
info(p"reading from address 0x${Hexadecimal(addr)} ($addr) -> 0x${Hexadecimal(v)} ($v)")
read_reg.data := v
read_reg.resp := Response.okay
}}
}
in_q_ra.io.deq.ready := RegNext(out_q_rd.io.enq.ready)
out_q_rd_enq_valid := true.B
}
when (in_q_wa.io.deq.valid && in_q_wd.io.deq.valid && out_q_wr.io.enq.ready) {
in_q_wa.io.deq.ready := in_q_wd.io.deq.valid && out_q_wr.io.enq.ready
in_q_wd.io.deq.ready := in_q_wa.io.deq.valid && out_q_wr.io.enq.ready
when (in_q_wa.io.deq.fire) {
val addr = in_q_wa.io.deq.bits
val v = in_q_wd.io.deq.bits
for (off <- cfg.regs.keys.toList.sorted) {
when (addr === off.U) { cfg.regs(off).read() map { v =>
info(p"writing to address $addr -> $v")
out_q_wr.io.enq.bits := { if (cfg.regs(off).write(v)) Response.okay else Response.slverr }
out_q_wr.io.enq.valid := true.B
}}
when (addr === off.U) {
val r = cfg.regs(off).write(v)
info(p"writing to address 0x${Hexadecimal(addr)} ($addr) -> 0x${Hexadecimal(v)} ($v): 0x${Hexadecimal(r)} ($r)")
resp_reg := r
}
}
out_q_wr_enq_valid := true.B
}
}
package chisel.axi.axi4lite
import chisel.axi._
import Chisel.{Reg, UInt}
/**
......@@ -25,7 +26,7 @@ sealed abstract class ControlRegister(_name: Option[String], bitfield: BitfieldM
}
/** Perform Chisel wiring to value. */
def write(v: UInt): Boolean = false
def write(v: UInt): UInt = Response.slverr
/** Perform Chisel read on value. **/
def read(): Option[UInt]
......@@ -37,10 +38,11 @@ sealed abstract class ControlRegister(_name: Option[String], bitfield: BitfieldM
* @param bitfield Bit partitioning of the value (optional).
* @param value Constant value for the register.
**/
class ConstantRegister(name: Option[String] = None, bitfield: BitfieldMap = Map(), value: BigInt)
class ConstantRegister(name: Option[String] = None, bitfield: BitfieldMap = Map(), val value: BigInt)
extends ControlRegister(name, bitfield) {
override def description: String = "%s - _const:_ 0x%x (%d)".format(super.description, value, value)
def read(): Option[UInt] = Some(UInt(value))
override lazy val toString: String = s"%s: 0x%x (%d)".format(name getOrElse "const", value, value)
}
/**
......@@ -48,13 +50,13 @@ class ConstantRegister(name: Option[String] = None, bitfield: BitfieldMap = Map(
* @param name Name of the register (optional).
* @param bitfield Bit partitioning of the value (optional).
**/
class Register[T <: UInt](name: Option[String] = None, bitfield: BitfieldMap = Map(), width: Int)
class Register(name: Option[String] = None, bitfield: BitfieldMap = Map(), width: Int)
extends ControlRegister(name, bitfield) {
private lazy val _r = Reg(UInt(width = width))
def read(): Option[UInt] = Some(_r)
override def write(v: UInt) = {
_r := v
true
Response.okay
}
}
......@@ -68,7 +70,7 @@ class Register[T <: UInt](name: Option[String] = None, bitfield: BitfieldMap = M
class VirtualRegister(name: Option[String] = None,
bitfield: BitfieldMap = Map(),
onRead: () => Option[UInt],
onWrite: UInt => Boolean) extends ControlRegister(name, bitfield) {
onWrite: UInt => UInt) extends ControlRegister(name, bitfield) {
def read() = onRead()
override def write(v: UInt) = onWrite(v)
}
......@@ -2,12 +2,9 @@ package chisel.axi.axi4lite
import chisel3._
import chisel3.util._
import chisel.axi._
import chisel.miscutils.Logging
/** AXI4Lite master transaction model.
* @param isRead true for read transactions.
* @param addr address to read from.
* @param value write value (optional)
**/
/** AXI4Lite master transaction model. */
sealed abstract trait MasterAction {
def isRead: Boolean
def address: Int
......@@ -29,88 +26,70 @@ final case class MasterWrite(address: Int, v: BigInt) extends MasterAction {
* @param action Sequence of transactions, executed sequentially without delay.
* @param axi implicit AXI configuration.
**/
class ProgrammableMaster(action: Seq[MasterAction])
(implicit axi: Axi4Lite.Configuration) extends Module {
class ProgrammableMaster(action: Seq[MasterAction])
(implicit axi: Axi4Lite.Configuration, logLevel: Logging.Level) extends Module with Logging {
cinfo(s"AXI configuration = $axi")
val io = IO(new Bundle {
val maxi = Axi4Lite.Master(axi)
val out = Decoupled(UInt(axi.dataWidth))
val w_resp = Decoupled(new chisel.axi.Axi4Lite.WriteResponse)
val finished = Output(Bool())
val w_resp = Irrevocable(new chisel.axi.Axi4Lite.WriteResponse)
})
val s_addr :: s_wtransfer :: s_rtransfer :: s_response :: s_idle :: Nil = Enum(5)
val cnt = RegInit(UInt(log2Ceil(action.length + 1).W), init = 0.U) // current action; last value indicates completion
val state = RegInit(s_addr)
val w_data = RegInit(UInt(axi.dataWidth), 0.U)
val r_data = RegNext(io.maxi.readData.bits.data, init = 0.U)
val cnt = RegInit(UInt(log2Ceil(action.length + 1).W), init = 0.U)
io.finished := cnt === action.length.U
val q = Module(new Queue(UInt(axi.dataWidth), action.length))
val ra_valid = RegInit(false.B)
val rd_ready = RegInit(false.B)
val wa_valid = RegInit(false.B)
val wd_valid = RegInit(false.B)
val wr_ready = RegInit(false.B)
val signals = Vec(ra_valid, rd_ready, wa_valid, wd_valid, wr_ready)
q.io.enq.valid := false.B
q.io.enq.bits := io.maxi.readData.bits.data
io.maxi.readData.ready := q.io.enq.ready
io.out <> q.io.deq
val ra = RegInit(io.maxi.readAddr.bits.addr.cloneType, init = 0.U)
val wa = RegInit(io.maxi.writeAddr.bits.addr.cloneType, init = 0.U)
val wd = RegInit(io.maxi.writeData.bits.data.cloneType, init = 0.U)
io.maxi.writeData.bits.defaults
io.maxi.readAddr.bits.defaults
io.maxi.writeAddr.bits.defaults
io.maxi.writeData.bits.defaults
io.maxi.writeData.bits.data := w_data
io.maxi.writeData.valid := state === s_wtransfer
io.maxi.readAddr.valid := false.B
io.maxi.readData.ready := state === s_rtransfer
io.maxi.writeAddr.valid := false.B
io.out.valid := io.maxi.readData.valid
io.out.bits := io.maxi.readData.bits.data
io.maxi.readAddr.bits.addr := 0.U
io.maxi.writeAddr.bits.addr := 0.U
io.w_resp.valid := io.maxi.writeResp.valid
io.w_resp.bits <> io.maxi.writeResp.bits
io.w_resp <> io.maxi.writeResp
io.maxi.writeResp.ready := true.B
io.maxi.readAddr.valid := ra_valid
io.maxi.writeAddr.valid := wa_valid
io.maxi.writeData.valid := wd_valid
io.maxi.readData.ready := rd_ready
io.maxi.writeResp.ready := wr_ready
io.finished := cnt === action.length.U
io.maxi.readAddr.bits.addr := ra
io.maxi.writeAddr.bits.addr := wa
io.maxi.writeData.bits.data := wd
// always assign address from current action
for (i <- 0 until action.length) {
when (i.U === cnt) {
io.maxi.readAddr.bits.addr := action(i).address.U
io.maxi.writeAddr.bits.addr := action(i).address.U
}
}
when (io.maxi.readAddr.fire) { ra_valid := false.B }
when (io.maxi.readData.fire) { rd_ready := false.B }
when (io.maxi.writeAddr.fire) { wa_valid := false.B }
when (io.maxi.writeData.fire) { wd_valid := false.B }
when (io.maxi.writeResp.fire) { wr_ready := false.B }
when (state === s_addr) {
when (!signals.reduce(_ || _)) {
for (i <- 0 until action.length) {
when (i.U === cnt) {
io.maxi.readAddr.valid := action(i).isRead.B
io.maxi.writeAddr.valid := (! action(i).isRead).B
action(i).value map { v => w_data := v.U }
}
}
when (io.maxi.readAddr.ready && io.maxi.readAddr.valid) { state := s_rtransfer }
when (io.maxi.writeAddr.ready && io.maxi.writeAddr.valid) { state := s_wtransfer }
when (cnt === action.length.U) { state := s_idle }
}
when (state === s_rtransfer) {
for (i <- 0 until action.length) {
val readReady = action(i).isRead.B && io.maxi.readData.ready && io.maxi.readData.valid
when (i.U === cnt && readReady) {
q.io.enq.valid := io.maxi.readData.bits.resp === 0.U // response OKAY
//info(s"Starting action #$i: ${action(i)}")
ra := action(i).address.U
wa := action(i).address.U
wd := action(i).value.getOrElse(BigInt(0)).U
ra_valid := action(i).isRead.B
rd_ready := action(i).isRead.B
wa_valid := (!action(i).isRead).B
wd_valid := (!action(i).isRead).B
wr_ready := (!action(i).isRead).B
cnt := cnt + 1.U
state := s_addr
}
}
}
when (state === s_wtransfer) {
for (i <- 0 until action.length) {
val writeReady = (!action(i).isRead).B && io.maxi.writeData.ready && io.maxi.writeData.valid
when (i.U === cnt && writeReady) {
cnt := cnt + 1.U
state := s_response
}
}
}
when (state === s_response && io.maxi.writeResp.valid) { state := s_addr }
}
......@@ -22,113 +22,18 @@ import org.scalatest.prop.Checkers
**/
class RegFileTest(val size: Int, val off: Int, regs: Map[Int, ControlRegister], actions: Seq[MasterAction])
(implicit axi: Axi4Lite.Configuration, logLevel: Logging.Level) extends Module {
val io = IO(new Bundle {
val out = Decoupled(UInt(axi.dataWidth))
val finished = Output(Bool())
val rresp = Output(chisel.axi.Response.okay.cloneType)
val wresp = Irrevocable(new chisel.axi.Axi4Lite.WriteResponse)
})
val cfg = new RegisterFile.Configuration(regs = regs)
val saxi = Module(new RegisterFile(cfg))
val m = Module(new ProgrammableMaster(actions))
val io = IO(new Bundle {
val rdata = Irrevocable(saxi.io.saxi.readData.bits.cloneType)
val wresp = Irrevocable(new chisel.axi.Axi4Lite.WriteResponse)
val finished = Output(Bool())
})
m.io.maxi <> saxi.io.saxi
io.out <> m.io.out
io.finished := m.io.finished
m.io.w_resp.ready := true.B
io.rresp := m.io.maxi.readData.bits.resp
io.wresp <> saxi.io.saxi.writeResp
}
/**
* ReadTester checks attempts to read from all registers.
* @param m configured RegFileTest module
* @param isTrace turns on debug output (default: true)
**/
class ReadTester(m: RegFileTest) extends PeekPokeTester(m) {
reset(10)
poke(m.io.out.ready, true)
var steps = m.size * 10 // no more than 10 clock cycles per read
for (i <- 1 until m.size + 1 if steps > 0) {
// wait til output queue is ready
while (steps > 0 && peek(m.io.out.ready) == 0 || peek(m.io.out.valid) == 0) {
steps -= 1
step(1)
}
val v = peek(m.io.out.bits)
val e = BigInt("%02x".format(i) * 4, 16)
val resp = peek(m.io.rresp)
expect (resp == 0, "read #%d: resp is 0x%x (%d), should be 0 (OKAY)".format(i, resp, resp))
expect(v == e, "at action #%d, expected: 0x%x (%d) but found %x (%d)".format(i, e, e, v, v))
step(1)
}
expect(peek(m.io.finished) != 0, "finished signal should be true at end of test")
}
/**
* WriteTester checks attempts to write to all registers.
* @param m configured RegFileTest module
* @param isTrace turns on debug output (default: true)
**/
class WriteTester(m: RegFileTest) extends PeekPokeTester(m) {
reset(10)
poke(m.io.out.ready, true)
println("running for a total of %d steps max ...".format(m.size * 20))
var steps = m.size * 20 // no more than 10 clock cycles per read+write
for (i <- 1 until m.size + 1 if steps > 0) {
while (steps > 0 && (peek(m.io.out.ready) == 0 || peek(m.io.out.valid) == 0)) {
steps -= 1
step(1)
}
val v = peek(m.io.out.bits)
val e = BigInt("%02x".format(i) * 4, 16)
expect(v == e, "at output #%d, expected: 0x%x (%d), found %x (%d)".format(i, e, e, v, v))
step(1)
}
expect(peek(m.io.finished) != 0, "finished signal should be true at end of test")
}
/**
* InvalidReadTester checks invalid read attempts for proper return code.
* @param m configured RegFileTest module
* @param reads number of invalid reads to perform
* @param isTrace turns on debug output (default: true)
**/
class InvalidReadTester(m: RegFileTest, reads: Int) extends PeekPokeTester(m) {
reset(10)
println("performing %d invalid reads ...")
var steps = reads * 10
for (i <- 1 until reads + 1 if steps > 0) {
while (steps > 0 && (peek(m.io.out.ready) == 0 || peek(m.io.out.valid) == 0)) {
steps -= 1
step(1)
}
val resp = peek(m.io.out.bits)
expect (resp == 2, "read #%d: resp is 0x%x (%d), should be 2 (SLVERR)".format(i, resp, resp))
step(1)
}
expect(peek(m.io.finished) != 0, "finished signal should be true at end of test")
}
/**
* InvalidWriteTester checks invalid write attempts for proper return code.
* @param m configured RegFileTest module
* @param writes number of invalid writes to perform
* @param isTrace turns on debug output (default: true)
**/
class InvalidWriteTester(m: RegFileTest, writes: Int) extends PeekPokeTester(m) {
reset(10)
println("performing %d invalid writes ...".format(writes))
var steps = writes * 10
for (i <- 1 until writes + 1 if steps > 0) {
while (steps > 0 && peek(m.io.wresp.valid) == 0) {
steps -= 1
step(1)
}
val resp = peek(m.io.wresp.bits.bresp)
expect(resp == 2, "write #%d: resp is 0x%x (%d), should be 2 (SLVERR)".format(i, resp, resp))
step(1)
}
expect(peek(m.io.finished) != 0, "finished signal should be true at end of test")
io.rdata <> saxi.io.saxi.readData
}
/** Unit test suite for Axi4LiteRegisterFile module. **/
......@@ -136,98 +41,116 @@ class RegisterFileSpec extends ChiselFlatSpec with Checkers {
implicit val logLevel = Logging.Level.Info
// basic Chisel arguments
val chiselArgs = Array("--fint-write-vcd")
// implicit AXI configuration
implicit val axi = Axi4Lite.Configuration(dataWidth = Axi4Lite.Width32, addrWidth = AddrWidth(32))
/** Attempts to read from all registers. **/
private def readTest(size: Int, off: Int) = {
val args = chiselArgs ++ Array("--target-dir", "test/Axi4RegisterFileSuite/read/size_%d_off_%d".format(size, off))
// fill constant registers with pattern
val regs = (for (i <- 1 until size + 1) yield
off * i -> new ConstantRegister(value = BigInt("%02x".format(i) * 4, 16))
).toMap
// read each of the registers in sequence
val actions = for (i <- 1 until size + 1) yield MasterRead(off * i)
// run test
Driver.execute(args, () => new RegFileTest(size, off, regs, actions))
{ m => new ReadTester(m) }
private def generateActionsFromRegMap(regs: Map[Int, Option[ControlRegister]]): Seq[MasterAction] =
regs.toSeq.sortBy(_._1) map { _ match {
case (i, Some(r)) => r match {
case c: Register => Seq(MasterWrite(i, i), MasterRead(i))
case c: ConstantRegister => Seq(MasterRead(i))
case _ => Seq()
}
case (i, None) => Seq(MasterRead(i), MasterWrite(i, i))
}} reduce (_ ++ _)
private def genericTest(width: DataWidth, regs: Map[Int, Option[ControlRegister]])
(implicit axi: Axi4Lite.Configuration) = {
val testDir = "test/axi4lite/RegisterFileSpec/generic/%d/%d".format(width: Int, scala.util.Random.nextInt)
println(s"Test results here: $testDir, width = $width")
val args = chiselArgs ++ Array("--target-dir", testDir)
val actions = generateActionsFromRegMap(regs)
Driver.execute(args, () => new RegFileTest(regs.size, width / 8, regs filter { case (_, or) => or.nonEmpty } map { case (i, or) => (i, or.get) }, actions))
{ m => new GenericTester(width, regs, m) } // FIXME implement generic Tester
}
/** Attempts to write to all registers. **/
private def writeTest(size: Int, off: Int) = {
val args = chiselArgs ++ Array("--target-dir", "test/Axi4RegisterFileSuite/write/size_%d_off_%d".format(size, off))
// fill constant registers with pattern
val regs = (for (i <- 1 until size + 1) yield
off * i -> new Register(width = axi.dataWidth)
).toMap
// read each of the registers in sequence
val actions = (for (i <- 1 until size + 1) yield Seq(
MasterWrite(off * i, BigInt("%02x".format(i) * (axi.dataWidth/8), 16)),
MasterRead(off * i)
)) reduce (_++_)
// run test
Driver.execute(args, () => new RegFileTest(size, off, regs, actions))
{ m => new WriteTester(m) }
}
private class GenericTester(width: DataWidth, regs: Map[Int, Option[ControlRegister]], m: RegFileTest) extends PeekPokeTester(m) {
def waitForReadData {
if (peek(m.io.rdata.valid) != 0) {
println("read data is still valid at start of waitForReadData, waiting ...")
var waitCycles = 0
while (peek(m.io.rdata.valid) != 0) {
waitCycles += 1
step(1)
}
println(s"waited $waitCycles cycles for read data valid signal to go low")
}
println("waiting for read data ...")
var steps = 100
while (steps > 0 && peek(m.io.rdata.valid) == 0) { steps -= 1; step(1) }
expect(m.io.rdata.valid, 1, "expected read data to arrive, but it did not")
println(s"wait for read data took ${100 - steps} cycles")
}
/** Attempts to perform invalid reads and checks return code. **/
private def invalidReads(reads: Int) = {
val args = chiselArgs ++ Array("--target-dir", "test/Axi4RegisterFileSuite/invalidReads/%d".format(reads))
// only zero is valid register
val regs = Map( 0 -> new ConstantRegister(value = 0) )
// read from increasing addresses (all above 0 are invalid)
val actions = for (i <- 1 until reads + 1) yield MasterRead(i * (axi.dataWidth / 8))
// run test
Driver.execute(args, () => new RegFileTest(1, 4, regs, actions))
{ m => new InvalidReadTester(m, reads) }
}
def waitForWriteResp {
if (peek(m.io.wresp.valid) != 0) {
println("write resp is still valid at start of waitForReadData, waiting ...")
var waitCycles = 0
while (peek(m.io.wresp.valid) != 0) {
waitCycles += 1
step(1)
}
println(s"waited $waitCycles cycles for write resp valid signal to go low")
}
println("waiting for write response ...")
var steps = 100
while (steps > 0 && peek(m.io.wresp.valid) == 0) { steps -= 1; step(1) }
expect(m.io.wresp.valid, 1, "expected write response to arrive, but it did not")
println(s"wait for write response took ${100 - steps} cycles")
}
/** Attempts to perform invalid writes and checks return code. **/
private def invalidWrites(writes: Int) = {
val args = chiselArgs ++ Array("--target-dir", "test/Axi4RegisterFileSuite/invalidWrites/%d".format(writes))
// only zero is valid register
val regs = Map( 0 -> new ConstantRegister(value = 0) )
// write from increasing addresses (all above 0 are invalid)
val actions = for (i <- 1 until writes + 1) yield MasterWrite(i * (axi.dataWidth / 8), 42)
// run test
Driver.execute(args, () => new RegFileTest(1, 4, regs, actions))
{ m => new InvalidWriteTester(m, writes) }
}
def test(r: Register, off: Int) {
waitForWriteResp
val bresp = peek(m.io.wresp.bits.bresp)
expect (m.io.wresp.bits.bresp, Response.okay, s"[$off] write response is 0x%x (%d), should be 0 (OKAY)".format(bresp, bresp))
waitForReadData
val resp = peek(m.io.rdata.bits.resp)
expect (m.io.rdata.bits.resp, Response.okay, s"[$off] read response is 0x%x (%d), should be 0 (OKAY)".format(resp, resp))
val data = peek(m.io.rdata.bits.data)
expect (m.io.rdata.bits.data, off, s"[$off] read data is 0x%x (%d), should be %d".format(data, data, off))
}
private def generateActionsFromRegMap(regs: Map[Int, Option[ControlRegister]]): Seq[MasterAction] = regs map { _ match {
case (i, Some(r)) => r match {
case c: Register[_] => Seq(MasterWrite(i, i), MasterRead(i))
case c: ConstantRegister => Seq(MasterRead(i))
case _ => Seq()
def test(r: ConstantRegister, off: Int) {
waitForReadData
val resp = peek(m.io.rdata.bits.resp)
val data = peek(m.io.rdata.bits.data)
expect (m.io.rdata.bits.resp, Response.okay, s"[$off] read response is 0x%x (%d), should be 0 (OKAY)".format(resp, resp))
expect (m.io.rdata.bits.data, r.value, s"[$off] read data is 0x%x (%d), should be %d".format(data, data, r.value))
}
case (i, None) => Seq(MasterRead(i), MasterWrite(i, i))
}} reduce (_ ++ _)
private def genericTest(width: DataWidth, regs: Map[Int, Option[ControlRegister]]) = {
val args = chiselArgs ++ Array("--target-dir", "test/axi4lite/RegisterFileSpec/generic/%d/%d"
.format(width: Int, scala.util.Random.nextInt)) // FIXME find a nice name, or output it somehow
val actions = generateActionsFromRegMap(regs)
Driver