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) ...@@ -73,7 +73,7 @@ class RegisterFile(cfg: RegisterFile.Configuration)
logLevel: Logging.Level) extends Module with Logging { logLevel: Logging.Level) extends Module with Logging {
class ReadData extends Bundle { class ReadData extends Bundle {
val data = io.saxi.readData.bits.data.cloneType 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] override def cloneType = (new ReadData).asInstanceOf[this.type]
} }
...@@ -88,20 +88,20 @@ class RegisterFile(cfg: RegisterFile.Configuration) ...@@ -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 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 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 Axi4Lite.Data.Read, cfg.fifoDepth))
val out_q_rd = Module(new Queue((new ReadData).cloneType, 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(new Axi4Lite.WriteResponse, cfg.fifoDepth))
val out_q_wr = Module(new Queue(io.saxi.writeResp.bits.bresp.cloneType, 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_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: ${in_q_wa.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: ${in_q_wd.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: ${out_q_rd.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: ${out_q_rd.io.enq.bits}") } 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: ${out_q_wr.io.enq.bits}") } 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: ${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.bits.defaults
io.saxi.readData.valid := false.B io.saxi.readData.valid := false.B
...@@ -118,38 +118,49 @@ class RegisterFile(cfg: RegisterFile.Configuration) ...@@ -118,38 +118,49 @@ class RegisterFile(cfg: RegisterFile.Configuration)
in_q_wd.io.enq.valid := io.saxi.writeData.valid in_q_wd.io.enq.valid := io.saxi.writeData.valid
io.saxi.writeData.ready := in_q_wd.io.enq.ready 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.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 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.data := out_q_rd.io.deq.bits.data
io.saxi.readData.bits.resp := out_q_rd.io.deq.bits.resp 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.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 out_q_wr.io.deq.ready := io.saxi.writeResp.ready
io.saxi.writeResp.valid := out_q_wr.io.deq.valid io.saxi.writeResp.valid := out_q_wr.io.deq.valid
io.saxi.writeResp.bits.bresp := out_q_wr.io.deq.bits 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 val addr = in_q_ra.io.deq.bits
read_reg.resp := Response.slverr
for (off <- cfg.regs.keys.toList.sorted) { for (off <- cfg.regs.keys.toList.sorted) {
when (addr === off.U) { cfg.regs(off).read() map { v => 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.data := v
read_reg.resp := Response.okay 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 addr = in_q_wa.io.deq.bits
val v = in_q_wd.io.deq.bits
for (off <- cfg.regs.keys.toList.sorted) { for (off <- cfg.regs.keys.toList.sorted) {
when (addr === off.U) { cfg.regs(off).read() map { v => when (addr === off.U) {
info(p"writing to address $addr -> $v") val r = cfg.regs(off).write(v)
out_q_wr.io.enq.bits := { if (cfg.regs(off).write(v)) Response.okay else Response.slverr } info(p"writing to address 0x${Hexadecimal(addr)} ($addr) -> 0x${Hexadecimal(v)} ($v): 0x${Hexadecimal(r)} ($r)")
out_q_wr.io.enq.valid := true.B resp_reg := r
}} }
} }
out_q_wr_enq_valid := true.B
} }
} }
package chisel.axi.axi4lite package chisel.axi.axi4lite
import chisel.axi._
import Chisel.{Reg, UInt} import Chisel.{Reg, UInt}
/** /**
...@@ -25,7 +26,7 @@ sealed abstract class ControlRegister(_name: Option[String], bitfield: BitfieldM ...@@ -25,7 +26,7 @@ sealed abstract class ControlRegister(_name: Option[String], bitfield: BitfieldM
} }
/** Perform Chisel wiring to value. */ /** Perform Chisel wiring to value. */
def write(v: UInt): Boolean = false def write(v: UInt): UInt = Response.slverr
/** Perform Chisel read on value. **/ /** Perform Chisel read on value. **/
def read(): Option[UInt] def read(): Option[UInt]
...@@ -37,10 +38,11 @@ sealed abstract class ControlRegister(_name: Option[String], bitfield: BitfieldM ...@@ -37,10 +38,11 @@ sealed abstract class ControlRegister(_name: Option[String], bitfield: BitfieldM
* @param bitfield Bit partitioning of the value (optional). * @param bitfield Bit partitioning of the value (optional).
* @param value Constant value for the register. * @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) { extends ControlRegister(name, bitfield) {
override def description: String = "%s - _const:_ 0x%x (%d)".format(super.description, value, value) override def description: String = "%s - _const:_ 0x%x (%d)".format(super.description, value, value)
def read(): Option[UInt] = Some(UInt(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( ...@@ -48,13 +50,13 @@ class ConstantRegister(name: Option[String] = None, bitfield: BitfieldMap = Map(
* @param name Name of the register (optional). * @param name Name of the register (optional).
* @param bitfield Bit partitioning of the value (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) { extends ControlRegister(name, bitfield) {
private lazy val _r = Reg(UInt(width = width)) private lazy val _r = Reg(UInt(width = width))
def read(): Option[UInt] = Some(_r) def read(): Option[UInt] = Some(_r)
override def write(v: UInt) = { override def write(v: UInt) = {
_r := v _r := v
true Response.okay
} }
} }
...@@ -68,7 +70,7 @@ class Register[T <: UInt](name: Option[String] = None, bitfield: BitfieldMap = M ...@@ -68,7 +70,7 @@ class Register[T <: UInt](name: Option[String] = None, bitfield: BitfieldMap = M
class VirtualRegister(name: Option[String] = None, class VirtualRegister(name: Option[String] = None,
bitfield: BitfieldMap = Map(), bitfield: BitfieldMap = Map(),
onRead: () => Option[UInt], onRead: () => Option[UInt],
onWrite: UInt => Boolean) extends ControlRegister(name, bitfield) { onWrite: UInt => UInt) extends ControlRegister(name, bitfield) {
def read() = onRead() def read() = onRead()
override def write(v: UInt) = onWrite(v) override def write(v: UInt) = onWrite(v)
} }
...@@ -2,12 +2,9 @@ package chisel.axi.axi4lite ...@@ -2,12 +2,9 @@ package chisel.axi.axi4lite
import chisel3._ import chisel3._
import chisel3.util._ import chisel3.util._
import chisel.axi._ import chisel.axi._
import chisel.miscutils.Logging
/** AXI4Lite master transaction model. /** AXI4Lite master transaction model. */
* @param isRead true for read transactions.
* @param addr address to read from.
* @param value write value (optional)
**/
sealed abstract trait MasterAction { sealed abstract trait MasterAction {
def isRead: Boolean def isRead: Boolean
def address: Int def address: Int
...@@ -29,88 +26,70 @@ final case class MasterWrite(address: Int, v: BigInt) extends MasterAction { ...@@ -29,88 +26,70 @@ final case class MasterWrite(address: Int, v: BigInt) extends MasterAction {
* @param action Sequence of transactions, executed sequentially without delay. * @param action Sequence of transactions, executed sequentially without delay.
* @param axi implicit AXI configuration. * @param axi implicit AXI configuration.
**/ **/
class ProgrammableMaster(action: Seq[MasterAction]) class ProgrammableMaster(action: Seq[MasterAction])
(implicit axi: Axi4Lite.Configuration) extends Module { (implicit axi: Axi4Lite.Configuration, logLevel: Logging.Level) extends Module with Logging {
cinfo(s"AXI configuration = $axi")
val io = IO(new Bundle { val io = IO(new Bundle {
val maxi = Axi4Lite.Master(axi) val maxi = Axi4Lite.Master(axi)
val out = Decoupled(UInt(axi.dataWidth)) val out = Decoupled(UInt(axi.dataWidth))
val w_resp = Decoupled(new chisel.axi.Axi4Lite.WriteResponse)
val finished = Output(Bool()) 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)
val cnt = RegInit(UInt(log2Ceil(action.length + 1).W), init = 0.U) // current action; last value indicates completion io.finished := cnt === action.length.U
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 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 val ra = RegInit(io.maxi.readAddr.bits.addr.cloneType, init = 0.U)
q.io.enq.bits := io.maxi.readData.bits.data val wa = RegInit(io.maxi.writeAddr.bits.addr.cloneType, init = 0.U)
io.maxi.readData.ready := q.io.enq.ready val wd = RegInit(io.maxi.writeData.bits.data.cloneType, init = 0.U)
io.out <> q.io.deq
io.maxi.writeData.bits.defaults
io.maxi.readAddr.bits.defaults io.maxi.readAddr.bits.defaults
io.maxi.writeAddr.bits.defaults io.maxi.writeAddr.bits.defaults
io.maxi.writeData.bits.defaults
io.maxi.writeData.bits.data := w_data io.out.valid := io.maxi.readData.valid
io.maxi.writeData.valid := state === s_wtransfer io.out.bits := io.maxi.readData.bits.data
io.maxi.readAddr.valid := false.B
io.maxi.readData.ready := state === s_rtransfer
io.maxi.writeAddr.valid := false.B
io.maxi.readAddr.bits.addr := 0.U io.w_resp.valid := io.maxi.writeResp.valid
io.maxi.writeAddr.bits.addr := 0.U io.w_resp.bits <> io.maxi.writeResp.bits
io.w_resp <> io.maxi.writeResp io.maxi.readAddr.valid := ra_valid
io.maxi.writeResp.ready := true.B 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 when (io.maxi.readAddr.fire) { ra_valid := false.B }
for (i <- 0 until action.length) { when (io.maxi.readData.fire) { rd_ready := false.B }
when (i.U === cnt) { when (io.maxi.writeAddr.fire) { wa_valid := false.B }
io.maxi.readAddr.bits.addr := action(i).address.U when (io.maxi.writeData.fire) { wd_valid := false.B }
io.maxi.writeAddr.bits.addr := action(i).address.U when (io.maxi.writeResp.fire) { wr_ready := false.B }
}
}
when (state === s_addr) { when (!signals.reduce(_ || _)) {
for (i <- 0 until action.length) { for (i <- 0 until action.length) {
when (i.U === cnt) { when (i.U === cnt) {
io.maxi.readAddr.valid := action(i).isRead.B //info(s"Starting action #$i: ${action(i)}")
io.maxi.writeAddr.valid := (! action(i).isRead).B ra := action(i).address.U
action(i).value map { v => w_data := v.U } wa := action(i).address.U
} wd := action(i).value.getOrElse(BigInt(0)).U
} ra_valid := action(i).isRead.B
when (io.maxi.readAddr.ready && io.maxi.readAddr.valid) { state := s_rtransfer } rd_ready := action(i).isRead.B
when (io.maxi.writeAddr.ready && io.maxi.writeAddr.valid) { state := s_wtransfer } wa_valid := (!action(i).isRead).B
when (cnt === action.length.U) { state := s_idle } wd_valid := (!action(i).isRead).B
} wr_ready := (!action(i).isRead).B
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
cnt := cnt + 1.U 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 ...@@ -22,113 +22,18 @@ import org.scalatest.prop.Checkers
**/ **/
class RegFileTest(val size: Int, val off: Int, regs: Map[Int, ControlRegister], actions: Seq[MasterAction]) class RegFileTest(val size: Int, val off: Int, regs: Map[Int, ControlRegister], actions: Seq[MasterAction])
(implicit axi: Axi4Lite.Configuration, logLevel: Logging.Level) extends Module { (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 cfg = new RegisterFile.Configuration(regs = regs)
val saxi = Module(new RegisterFile(cfg)) val saxi = Module(new RegisterFile(cfg))
val m = Module(new ProgrammableMaster(actions)) 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 m.io.maxi <> saxi.io.saxi
io.out <> m.io.out
io.finished := m.io.finished 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 io.wresp <> saxi.io.saxi.writeResp
} io.rdata <> saxi.io.saxi.readData
/**
* 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")
} }
/** Unit test suite for Axi4LiteRegisterFile module. **/ /** Unit test suite for Axi4LiteRegisterFile module. **/
...@@ -136,98 +41,116 @@ class RegisterFileSpec extends ChiselFlatSpec with Checkers { ...@@ -136,98 +41,116 @@ class RegisterFileSpec extends ChiselFlatSpec with Checkers {
implicit val logLevel = Logging.Level.Info implicit val logLevel = Logging.Level.Info
// basic Chisel arguments // basic Chisel arguments
val chiselArgs = Array("--fint-write-vcd") 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 generateActionsFromRegMap(regs: Map[Int, Option[ControlRegister]]): Seq[MasterAction] =
private def readTest(size: Int, off: Int) = { regs.toSeq.sortBy(_._1) map { _ match {
val args = chiselArgs ++ Array("--target-dir", "test/Axi4RegisterFileSuite/read/size_%d_off_%d".format(size, off)) case (i, Some(r)) => r match {
// fill constant registers with pattern case c: Register => Seq(MasterWrite(i, i), MasterRead(i))
val regs = (for (i <- 1 until size + 1) yield case c: ConstantRegister => Seq(MasterRead(i))
off * i -> new ConstantRegister(value = BigInt("%02x".format(i) * 4, 16)) case _ => Seq()
).toMap }
// read each of the registers in sequence case (i, None) => Seq(MasterRead(i), MasterWrite(i, i))
val actions = for (i <- 1 until size + 1) yield MasterRead(off * i) }} reduce (_ ++ _)
// run test
Driver.execute(args, () => new RegFileTest(size, off, regs, actions)) private def genericTest(width: DataWidth, regs: Map[Int, Option[ControlRegister]])
{ m => new ReadTester(m) } (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 class GenericTester(width: DataWidth, regs: Map[Int, Option[ControlRegister]], m: RegFileTest) extends PeekPokeTester(m) {
private def writeTest(size: Int, off: Int) = { def waitForReadData {
val args = chiselArgs ++ Array("--target-dir", "test/Axi4RegisterFileSuite/write/size_%d_off_%d".format(size, off)) if (peek(m.io.rdata.valid) != 0) {
// fill constant registers with pattern println("read data is still valid at start of waitForReadData, waiting ...")
val regs = (for (i <- 1 until size + 1) yield var waitCycles = 0
off * i -> new Register(width = axi.dataWidth) while (peek(m.io.rdata.valid) != 0) {
).toMap