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

Implement configurable read and write delays

* considering the significant delays for real-world rw access
  to memory, AxiSlaveModel should have optional delays to sim
  that behavior
* extracted config to AxiSlaveModelConfiguration class
* adapted existing unit test suites
* bugfix in Axi2AxiSuite: afa now waits for writes to finish
parent c7b36058
......@@ -25,6 +25,7 @@ class Axi2AxiModule(val dataWidth: Int,
val sz = size.getOrElse(pow(2, addrWidth.get).toInt)
val aw = addrWidth.getOrElse(log2Up(sz * (dataWidth / 8)))
println ("Axi2AxiModule: address bits = %d, size = %d".format(aw, sz))
val cfg = AxiSlaveModelConfiguration(addrWidth = Some(aw), dataWidth = dataWidth, size = size)
val dsrc = Module(new DecoupledDataSource(
gen = UInt(width = dataWidth),
......@@ -39,11 +40,7 @@ class Axi2AxiModule(val dataWidth: Int,
dataWidth = dataWidth,
burstSize = Some(fifoDepth)))
val saxi = Module(new AxiSlaveModel(
addrWidth = addrWidth,
dataWidth = dataWidth,
idWidth = 1,
size = size))
val saxi = Module(new AxiSlaveModel(cfg))
val afa = Module(new AxiFifoAdapter(
addrWidth = aw,
......@@ -59,7 +56,7 @@ class Axi2AxiModule(val dataWidth: Int,
saxi.io.saxi.writeResp <> fad.io.maxi.writeResp
saxi.io.saxi.readAddr <> afa.io.maxi.readAddr
saxi.io.saxi.readData <> afa.io.maxi.readData
afa.reset := dsrc.io.out.valid
afa.reset := dsrc.io.out.valid || fad.fifo.io.count > UInt(0)
fad.io.base := base
afa.io.base := base
io.deq <> afa.io.deq
......@@ -88,7 +85,6 @@ class Axi2AxiTester(m: Axi2AxiModule) extends Tester(m) {
}
reset (10)
while (peek(m.afa.reset) != 0) step (1)
poke(m.io.deq.ready, true)
for (i <- 0 until m.sz) {
while (peek(m.afa.io.deq.valid) == 0) step (1)
......
......@@ -12,11 +12,11 @@ class AxiFifoAdapterModule1(
) extends Module {
val addrWidth = log2Up(dataWidth * fifoDepth * blockSize / 8)
val cfg = AxiSlaveModelConfiguration(addrWidth = Some(addrWidth), dataWidth = dataWidth)
val io = new Bundle
val afa = Module (new AxiFifoAdapter(addrWidth = addrWidth,
dataWidth = dataWidth, idWidth = 1, fifoDepth = fifoDepth))
val saxi = Module (new AxiSlaveModel(addrWidth = Some(addrWidth),
dataWidth = dataWidth, idWidth = 1))
val saxi = Module (new AxiSlaveModel(cfg))
val dqr = Reg(init = Bool(true))
afa.io.base := UInt(0)
......
......@@ -2,23 +2,17 @@ package chisel.axiutils
import Chisel._
import AXIDefs._
class AxiSlaveModelIO(addrWidth: Int, dataWidth: Int, idWidth: Int) extends Bundle {
val saxi = new AXIMasterIF(addrWidth, dataWidth, idWidth).flip()
class AxiSlaveModelIO(val cfg: AxiSlaveModelConfiguration) extends Bundle {
val saxi = new AXIMasterIF(cfg.addrWidth, cfg.dataWidth, cfg.idWidth).flip()
}
class AxiSlaveModel(val addrWidth: Option[Int],
val dataWidth: Int,
val idWidth: Int = 1,
val size: Option[Int] = None) extends Module {
require (idWidth == 1, "only idWidth = 1 supported at the moment")
require (!size.isEmpty || !addrWidth.isEmpty, "specify size or addrWidth, or both")
class AxiSlaveModel(val cfg: AxiSlaveModelConfiguration) extends Module {
val aw = cfg.addrWidth
val sz = cfg.size
println ("AxiSlaveModel: %s".format(cfg.toString))
val aw = addrWidth.getOrElse(scala.math.pow(2, size.get * (dataWidth / 8)).toInt)
val sz = size.getOrElse(scala.math.pow(2, addrWidth.get).toInt)
println ("AxiSlaveModel: address bits = %d, size = %d".format(aw, sz))
val io = new AxiSlaveModelIO(addrWidth = aw, dataWidth = dataWidth, idWidth = idWidth)
val mem = Mem(sz, UInt(width = dataWidth))
val io = new AxiSlaveModelIO(cfg)
val mem = Mem(sz, UInt(width = cfg.dataWidth))
/** WRITE PROCESS **/
val wa_valid = RegNext(io.saxi.writeAddr.valid)
......@@ -31,7 +25,7 @@ class AxiSlaveModel(val addrWidth: Option[Int],
val wa_burst = RegNext(io.saxi.writeAddr.bits.burst)
val wa = Reg(UInt(width = aw))
val wd = Reg(UInt(width = dataWidth))
val wd = Reg(UInt(width = cfg.dataWidth))
val wr = Reg(UInt(width = 2))
val wl = Reg(io.saxi.writeAddr.bits.len.cloneType())
......@@ -39,9 +33,10 @@ class AxiSlaveModel(val addrWidth: Option[Int],
val wd_hs = Reg(Bool()) // data handshake complete?
val wr_hs = Reg(Bool()) // response handshake complete?
val wr_valid = Reg(Bool()) // response valid
val wr_wait = Reg(UInt(width = log2Up(cfg.writeDelay + 1)))
io.saxi.writeAddr.ready := !wa_hs
io.saxi.writeData.ready := wa_hs && !wr_valid
io.saxi.writeData.ready := wa_hs && !wr_valid && wr_wait === UInt(0)
io.saxi.writeResp.bits.resp := wr
io.saxi.writeResp.bits.id := UInt(0)
io.saxi.writeResp.valid := wa_hs && wr_valid
......@@ -54,29 +49,34 @@ class AxiSlaveModel(val addrWidth: Option[Int],
wa_hs := Bool(false)
wd_hs := Bool(false)
wr_hs := Bool(false)
wr_wait := UInt(0)
}
.otherwise {
when (!wa_hs && wa_valid) {
wa := wa_addr
wl := wa_len
wa_hs := Bool(true)
assert (wa_size === UInt(if (dataWidth > 8) log2Up(dataWidth / 8) else 0), "wa_size is not supported".format(wa_size))
assert (wa_size === UInt(if (cfg.dataWidth > 8) log2Up(cfg.dataWidth / 8) else 0), "wa_size is not supported".format(wa_size))
assert (wa_burst < UInt(2), "wa_burst type (b%s) not supported".format(wa_burst))
if (cfg.writeDelay > 0) wr_wait := UInt(cfg.writeDelay)
}
when (wa_hs && wd_valid && !wr_valid) {
mem(if (dataWidth > 8) wa >> UInt(log2Up(dataWidth / 8)) else wa) := wd_data
printf("writing data 0x%x to address 0x%x\n", wd_data, wa)
when (io.saxi.writeData.bits.last || wl === UInt(0)) { wr_valid := Bool(true) }
.otherwise {
wl := wl - UInt(1)
// increase address in INCR bursts
when (wa_burst === UInt("b01")) { wa := wa + UInt(dataWidth / 8) }
when (wa_hs && wr_wait > UInt(0)) { wr_wait := wr_wait - UInt(1) }
when (wa_hs && wr_wait === UInt(0)) {
when (wa_hs && wd_valid && !wr_valid) {
mem(if (cfg.dataWidth > 8) wa >> UInt(log2Up(cfg.dataWidth / 8)) else wa) := wd_data
printf("writing data 0x%x to address 0x%x\n", wd_data, wa)
when (io.saxi.writeData.bits.last || wl === UInt(0)) { wr_valid := Bool(true) }
.otherwise {
wl := wl - UInt(1)
// increase address in INCR bursts
when (wa_burst === UInt("b01")) { wa := wa + UInt(cfg.dataWidth / 8) }
}
}
when (wa_hs && wr_valid && wr_ready) {
wa_hs := Bool(false)
wd_hs := Bool(false)
wr_valid := Bool(false)
}
}
when (wa_hs && wr_valid && wr_ready) {
wa_hs := Bool(false)
wd_hs := Bool(false)
wr_valid := Bool(false)
}
}
......@@ -91,14 +91,16 @@ class AxiSlaveModel(val addrWidth: Option[Int],
val rr_resp = UInt(0, width = 2) // response: OKAY
val rr_wait = Reg(UInt(width = log2Up(cfg.readDelay + 1)))
val ra_hs = Reg(Bool())
val ra_ready = !reset && !ra_hs
io.saxi.readAddr.ready := ra_ready
io.saxi.readData.valid := ra_hs
io.saxi.readData.bits.data := mem(if (dataWidth > 8) ra >> UInt(log2Up(dataWidth / 8)) else ra)
val rd_valid = ra_hs && rr_wait === UInt(0)
io.saxi.readData.valid := rd_valid
io.saxi.readData.bits.data := mem(if (cfg.dataWidth > 8) ra >> UInt(log2Up(cfg.dataWidth / 8)) else ra)
io.saxi.readData.bits.last := ra_hs && l === UInt(0)
io.saxi.readData.bits.resp := rr_resp
......@@ -111,16 +113,19 @@ class AxiSlaveModel(val addrWidth: Option[Int],
ra_size := UInt(0)
ra_burst := UInt(0)
ra_hs := Bool(false)
rr_wait := UInt(0)
}
.otherwise {
when (!ra_hs && io.saxi.readAddr.ready && io.saxi.readAddr.valid) {
ra := io.saxi.readAddr.bits.addr
ra_hs := Bool(true)
l := io.saxi.readAddr.bits.len
if (cfg.readDelay > 0) rr_wait := UInt(cfg.readDelay)
}
when (ra_hs && io.saxi.readData.ready) {
when (ra_hs && rr_wait > UInt(0)) { rr_wait := rr_wait - UInt(1) }
when (rd_valid && io.saxi.readData.ready) {
l := l - UInt(1)
ra := ra + UInt(dataWidth / 8)
ra := ra + UInt(cfg.dataWidth / 8)
when (l === UInt(0)) { ra_hs := Bool(false) }
}
}
......
package chisel.axiutils
import Chisel._
/**
* AxiSlaveModelConfiguration configures an AxiSlaveModel instance.
* @param addrWidth address bits for AXI4 interface.
* @param dataWidth word width for AXI4 interface.
* @param idWidth id bits for AXI4 interface.
* @param size size of memory to model (in bytes).
* @param readDelay simulated delay between read address handshake and data.
* @param writeDelay simulated delay between write address handshake and data.
**/
class AxiSlaveModelConfiguration(
val addrWidth: Int,
val dataWidth: Int,
val idWidth: Int,
val size: Int,
val readDelay: Int,
val writeDelay: Int
) {
require (addrWidth > 0 && addrWidth <= 64, "addrWidth (%d) must be 0 < addrWidth <= 64".format(addrWidth))
require (dataWidth > 0 && dataWidth <= 256, "dataWidth (%d) must be 0 < dataWidth <= 256".format(dataWidth))
require (idWidth == 1, "id width (%d) is not supported, use 1bit".format(idWidth))
require (readDelay >= 0, "read delay (%d) must be >= 0".format(readDelay))
require (writeDelay >= 0, "write delay (%d) must be >= 0".format(writeDelay))
override def toString: String =
"addrWidth = %d, dataWidth = %d, idWidth = %d, size = %d, readDelay = %d, writeDelay = %d"
.format(addrWidth, dataWidth, idWidth, size, readDelay, writeDelay)
}
object AxiSlaveModelConfiguration {
/**
* Construct an AxiSlaveModelConfiguration. Size and address width are optional,
* but one needs to be supplied to determine simulated memory size.
* @param addrWidth address bits for AXI4 interface (optional).
* @param dataWidth word width for AXI4 interface.
* @param idWidth id bits for AXI4 interface (default: 1).
* @param size size of memory to model in bytes (optional).
* @param readDelay simulated delay between read address handshake and data (default: 0).
* @param writeDelay simulated delay between write address handshake and data (default: 0).
**/
def apply(addrWidth: Option[Int] = None, dataWidth: Int, idWidth: Int = 1,
size: Option[Int] = None, readDelay: Int = 100, writeDelay: Int = 600) = {
require (!size.isEmpty || !addrWidth.isEmpty, "specify size or addrWidth, or both")
val sz: Int = size.getOrElse(scala.math.pow(2, addrWidth.get).toInt)
val aw: Int = addrWidth.getOrElse(log2Up(size.get * dataWidth / 8).toInt)
new AxiSlaveModelConfiguration(addrWidth = aw, dataWidth = dataWidth, idWidth = idWidth,
size = sz, readDelay = readDelay, writeDelay = writeDelay)
}
}
......@@ -5,15 +5,15 @@ import org.scalatest.junit.JUnitSuite
import org.junit.Test
import org.junit.Assert._
class FifoAxiAdapterModule1(dataWidth : Int, size: Int) extends Module {
class FifoAxiAdapterModule1(val dataWidth : Int, size: Int) extends Module {
val io = new Bundle
val cfg = AxiSlaveModelConfiguration(dataWidth = dataWidth, size = Some(size))
val datasrc = Module (new DecoupledDataSource(UInt(width = dataWidth), size = 256, n => UInt(n), false))
val fad = Module (new FifoAxiAdapter(fifoDepth = size,
addrWidth = log2Up(size),
dataWidth = dataWidth,
burstSize = Some(16)))
val saxi = Module (new AxiSlaveModel(addrWidth = Some(log2Up(size * (dataWidth / 8))),
dataWidth = dataWidth, idWidth = 1))
val saxi = Module (new AxiSlaveModel(cfg))
fad.io.base := UInt(0)
fad.io.enq <> datasrc.io.out
......@@ -22,7 +22,7 @@ class FifoAxiAdapterModule1(dataWidth : Int, size: Int) extends Module {
class FifoAxiAdapterSuite extends JUnitSuite {
@Test def test1 {
chiselMainTest(Array("--genHarness", "--backend", "c", "--vcd", "--targetDir", "test", "--compile", "--test"),
chiselMainTest(Array("--genHarness", "--backend", "c", "--vcd", "--targetDir", "test/fad", "--compile", "--test"),
() => Module(new FifoAxiAdapterModule1(dataWidth = 8, size = 256))) { m => new FifoAxiAdapterModule1Test(m) }
}
}
......@@ -39,7 +39,7 @@ class FifoAxiAdapterModule1Test(fad: FifoAxiAdapterModule1) extends Tester(fad,
}
def toBinaryString(v: BigInt) =
"b%%%ds".format(fad.saxi.dataWidth).format(v.toString(2)).replace(' ', '0')
"b%%%ds".format(fad.dataWidth).format(v.toString(2)).replace(' ', '0')
reset(10)
while (peek(fad.datasrc.io.out.valid) != 0 || peek(fad.fad.fifo.io.count) > 0) step(1)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment