diff --git a/axi4/src/main/scala/VivadoAXIMemory.scala b/axi4/src/main/scala/VivadoAXIMemory.scala index bd86950..9b65a03 100644 --- a/axi4/src/main/scala/VivadoAXIMemory.scala +++ b/axi4/src/main/scala/VivadoAXIMemory.scala @@ -136,5 +136,5 @@ class VivadoAXIMemory extends Slave(1, 10, 32) { /** Clock and reset */ mem.io.s00_axi_aclk := clock - mem.io.s00_axi_aresetn := !reset + mem.io.s00_axi_aresetn := reset } diff --git a/axi4/src/main/scala/axi4/AXI4FunctionalMaster.scala b/axi4/src/main/scala/axi4/AXI4FunctionalMaster.scala index cd92a67..ddef015 100644 --- a/axi4/src/main/scala/axi4/AXI4FunctionalMaster.scala +++ b/axi4/src/main/scala/axi4/AXI4FunctionalMaster.scala @@ -54,22 +54,6 @@ class AXI4FunctionalMaster[T <: Slave](dut: T) { */ def hasInflightOps() = inFlightReads.length > 0 || inFlightWrites.length > 0 - /** Watch the response channel - * - * @note never call this method explicitly - */ - private[this] def respHandler() = { - println("New response handler") - - /** Indicate that interface is ready and wait for response */ - wr.ready.poke(true.B) - while (!wr.valid.peek.isLit) { - clk.step() - } - responses = responses :+ (new Response(wr.bits.resp.peek, wr.bits.id.peek.litValue)) - wr.ready.poke(false.B) - } - /** Handle the write address channel * * @note never call this method explicitly @@ -89,9 +73,9 @@ class AXI4FunctionalMaster[T <: Slave](dut: T) { aw.bits.prot.poke(prot) aw.bits.qos.poke(qos) aw.bits.region.poke(region) - do { + while (!aw.ready.peek.litToBoolean) { clk.step() - } while (!aw.ready.peek.isLit) + } clk.step() aw.valid.poke(false.B) @@ -110,15 +94,16 @@ class AXI4FunctionalMaster[T <: Slave](dut: T) { println("New write handler") /** Write data to slave */ + dw.valid.poke(true.B) while (!inFlightWrites.head.complete) { - dw.valid.poke(true.B) val (data, strb, last) = inFlightWrites.head.next + println("Write " + data.litValue + " with strobe " + strb.toString + " and last " + last.litToBoolean) dw.bits.data.poke(data) dw.bits.strb.poke(strb) dw.bits.last.poke(last) - do { + while (!dw.ready.peek.litToBoolean) { clk.step() - } while (!dw.ready.peek.isLit) + } clk.step() } dw.valid.poke(false.B) @@ -131,6 +116,22 @@ class AXI4FunctionalMaster[T <: Slave](dut: T) { } } + /** Watch the response channel + * + * @note never call this method explicitly + */ + private[this] def respHandler() = { + println("New response handler") + + /** Indicate that interface is ready and wait for response */ + wr.ready.poke(true.B) + while (!wr.valid.peek.litToBoolean) { + clk.step() + } + responses = responses :+ (new Response(wr.bits.resp.peek, wr.bits.id.peek.litValue)) + wr.ready.poke(false.B) + } + /** Handle the read address channel * * @note never call this method explicitly @@ -150,9 +151,9 @@ class AXI4FunctionalMaster[T <: Slave](dut: T) { ar.bits.prot.poke(prot) ar.bits.qos.poke(qos) ar.bits.region.poke(region) - do { + while (!ar.ready.peek.litToBoolean) { clk.step() - } while (!ar.ready.peek.isLit) + } clk.step() ar.valid.poke(false.B) @@ -168,20 +169,21 @@ class AXI4FunctionalMaster[T <: Slave](dut: T) { * @note never call this method explicitly */ private[this] def readHandler(): Unit = { + println("New read handler") + + /** Read data from slave */ + dr.ready.poke(true.B) while (!inFlightReads.head.complete) { - dr.ready.poke(false.B) - if (dr.valid.peek.isLit) { - // Accept read data - dr.ready.poke(true.B) - val resp = dr.bits.resp.peek - if (resp == ResponseEncodings.Decerr || resp == ResponseEncodings.Slverr) - println(s"[Error] reading failed with response $resp") - inFlightReads.head.add(dr.bits.data.peek.litValue) + if (dr.valid.peek.litToBoolean) { + val (data, resp, last) = (dr.bits.data.peek, dr.bits.resp.peek, dr.bits.last.peek) + println(s"Read " + data.litValue + " with response " + resp.litValue + " and last " + last.litToBoolean) + inFlightReads.head.add(data.litValue) } clk.step() } readValues = readValues :+ inFlightReads.head.data inFlightReads = inFlightReads.tail + dr.ready.poke(false.B) } /** Initialize the interface (reset) @@ -259,7 +261,7 @@ class AXI4FunctionalMaster[T <: Slave](dut: T) { * @param id optional id, defaults to ID 0 * @param len optional burst length, defaults to 0 (i.e., 1 beat) * @param size optional beat size, defaults to 1 byte - * @param burst optional burst type, defaults to INCR + * @param burst optional burst type, defaults to FIXED * @param lock optional lock type, defaults to normal access * @param cache optional memory attribute signal, defaults to device non-bufferable * @param prot optional protection type, defaults to non-secure unprivileged data access @@ -277,7 +279,7 @@ class AXI4FunctionalMaster[T <: Slave](dut: T) { id: BigInt = 0, len: Int = 0, size: Int = 0, - burst: UInt = BurstEncodings.Incr, + burst: UInt = BurstEncodings.Fixed, lock: Bool = LockEncodings.NormalAccess, cache: UInt = MemoryEncodings.DeviceNonbuf, prot: UInt = ProtectionEncodings.DataNsecUpriv, @@ -334,7 +336,7 @@ class AXI4FunctionalMaster[T <: Slave](dut: T) { * @param id optional id, defaults to ID 0 * @param len optional burst length, defaults to 0 (i.e., 1 beat) * @param size optional beat size, defaults to 1 byte - * @param burst optional burst type, defaults to INCR + * @param burst optional burst type, defaults to FIXED * @param lock optional lock type, defaults to normal access * @param cache optional memory attribute signal, defaults to device non-bufferable * @param prot optional protection type, defaults to non-secure unprivileged data access @@ -350,7 +352,7 @@ class AXI4FunctionalMaster[T <: Slave](dut: T) { id: BigInt = 0, len: Int = 0, size: Int = 0, - burst: UInt = BurstEncodings.Incr, + burst: UInt = BurstEncodings.Fixed, lock: Bool = LockEncodings.NormalAccess, cache: UInt = MemoryEncodings.DeviceNonbuf, prot: UInt = ProtectionEncodings.DataNsecUpriv, diff --git a/axi4/src/main/scala/axi4/Transaction.scala b/axi4/src/main/scala/axi4/Transaction.scala index cef1765..9193eaa 100644 --- a/axi4/src/main/scala/axi4/Transaction.scala +++ b/axi4/src/main/scala/axi4/Transaction.scala @@ -44,7 +44,7 @@ class WriteTransaction(addr: BigInt, data: Seq[BigInt], dataW: Int, size: Int = val offset = (address / dataW) * dataW val lowerByteLane = address - offset val upperByteLane = if (aligned) lowerByteLane + numBytes-1 else alignedAddress + numBytes-1 - offset - def within(x: Int) = x >= lowerByteLane && x <= upperByteLane + def within(x: Int) = x >= 0 && x <= (upperByteLane - lowerByteLane) val strb = ("b"+(0 until (dataW/8)).foldRight("") { (elem, acc) => if (within(elem)) acc + "1" else acc + "0" }).asUInt /** Update address */ diff --git a/axi4/src/test/scala/VivadoAXIMemoryTester.scala b/axi4/src/test/scala/VivadoAXIMemoryTester.scala index 9451feb..05a1bc8 100644 --- a/axi4/src/test/scala/VivadoAXIMemoryTester.scala +++ b/axi4/src/test/scala/VivadoAXIMemoryTester.scala @@ -92,4 +92,72 @@ class VivadoAXIMemoryTester extends FlatSpec with ChiselScalatestTester with Mat master.close() } } + + it should "write and read with FIXED transactions" in { + test(new VivadoAXIMemory()).withAnnotations(Seq(VerilatorBackendAnnotation)) { + dut => + val master = new AXI4FunctionalMaster(dut) + master.initialize() + + // Create write transaction + master.createWriteTrx(0, Seq(42), size = 2) + + // Wait for the write to complete (spin on response) + var resp = master.checkResponse() + while (resp == None) { + resp = master.checkResponse() + dut.clock.step() + } + val r = resp match { case Some(r) => r.resp.litValue; case _ => ResponseEncodings.Slverr.litValue } + assert(r == 0, "expected write to pass") + + // Create read transaction + master.createReadTrx(0, size = 2) + + // Wait for read to complete (spin on read data) + var data = master.checkReadData() + while (data == None) { + data = master.checkReadData() + dut.clock.step() + } + val d = data match { case Some(v) => v; case _ => Seq() } + assert(d.length == 1 && d(0) == 42, "read data value is incorrect") + + master.close() + } + } + + it should "write and read with INCR transactions" in { + test(new VivadoAXIMemory()).withAnnotations(Seq(VerilatorBackendAnnotation)) { + dut => + val master = new AXI4FunctionalMaster(dut) + master.initialize() + + // Create write transaction + master.createWriteTrx(128, Seq.fill(128)(0x7FFFFFFF), len = 0x7F, size = 2, burst = BurstEncodings.Incr) + + // Wait for the write to complete (spin on response) + var resp = master.checkResponse() + while (resp == None) { + resp = master.checkResponse() + dut.clock.step() + } + val r = resp match { case Some(r) => r.resp.litValue; case _ => ResponseEncodings.Slverr.litValue } + assert(r == 0, "expected write to pass") + + // Create read transaction + master.createReadTrx(128, len = 0x7F, size = 2, burst = BurstEncodings.Incr) + + // Wait for read to complete (spin on read data) + var data = master.checkReadData() + while (data == None) { + data = master.checkReadData() + dut.clock.step() + } + val d = data match { case Some(v) => v; case _ => Seq() } + assert(d.foldLeft(true) { (acc, elem) => acc && (elem == 0x7FFFFFFF) }, "read data value is incorrect") + + master.close() + } + } }