diff --git a/axi4/README.md b/axi4/README.md index 4137a29..403a0de 100644 --- a/axi4/README.md +++ b/axi4/README.md @@ -2,9 +2,7 @@ This project focuses on implementation of AXI4 master interface definitions and a small framework providing support for all of the defined transactions from the AXI master's point of view in the protocol. ## TODO -- Implement basic interface tests - Evaluate functional master - - Use cover groups ### Extras - Functional AXI slave (like above) @@ -66,7 +64,7 @@ Additionally, two global signals are used (see page A2-28) Channel descriptions are available in `./src/main/scala/axi4/Defs.scala`. DUVs must conform to the signal names and interfaces provided to function correctly - hence, their IO should extend either the available master or slave interfaces. To enable this more easily, an AXI master can simply extend the Master class found in `./src/main/scala/axi4/Master.scala`, and vice-versa for AXI slaves for which the relevant class is found in `./src/main/scala/axi4/Slave.scala`. -An example of a black-boxed block-RAM with AXI interface is provided in `./src/main/scala/AXI4Memory.scala` - note, however, that due to copyright of the IP source code, you will need to generate the appropriate IP block within Xilinx Vivado to use the blackbox. +An example of a black-boxed block-RAM with AXI interface is provided in `./src/main/scala/VivadoAXIMemory.scala` - note, however, that due to copyright of the IP source code, you will need to generate the appropriate IP block within Xilinx Vivado to use the blackbox. A basic tester is provided in `./src/test/scala/VivadoAXIMemoryTester.scala`. ### References The full public protocol specification is available from ARM [here](https://developer.arm.com/documentation/ihi0022/e/) and in PDF format [here](http://www.gstitt.ece.ufl.edu/courses/fall15/eel4720_5721/labs/refs/AXI4_specification.pdf). A good video introduction is available from [ARM's YouTube channel](https://www.youtube.com/watch?v=7Vl9JrGgNwk). diff --git a/axi4/src/main/scala/VivadoAXIMemory.scala b/axi4/src/main/scala/VivadoAXIMemory.scala index 710f889..bd86950 100644 --- a/axi4/src/main/scala/VivadoAXIMemory.scala +++ b/axi4/src/main/scala/VivadoAXIMemory.scala @@ -9,7 +9,6 @@ import axi4._ import chisel3._ import chisel3.util._ -import chisel3.experimental._ /** Vivado IP BRAM with full AXI4 interface */ class mymem extends BlackBox with HasBlackBoxResource { @@ -68,8 +67,10 @@ class mymem extends BlackBox with HasBlackBoxResource { val s00_axi_aresetn = Input(Reset()) }) - /** Verilog file in /src/main/resources - top file is mymem.v */ + /** Verilog files in /src/main/resources - top file is mymem.v */ addResource("/mymem.v") + addResource("/myaximem_v1_0.v") + addResource("/myaximem_v1_0_S00_AXI.v") } /** Wrapper for mymem */ @@ -135,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/Transaction.scala b/axi4/src/main/scala/axi4/Transaction.scala index 9e2f34f..cef1765 100644 --- a/axi4/src/main/scala/axi4/Transaction.scala +++ b/axi4/src/main/scala/axi4/Transaction.scala @@ -23,7 +23,7 @@ trait Transaction { * @param size optional beat size, defaults to 1 byte * @param burst optional burst type, defaults to INCR */ -class WriteTransaction(addr: BigInt, data: Seq[BigInt], dataW: Int, size: Int = 0, burst: UInt = BurstEncodings.Incr) { +class WriteTransaction(addr: BigInt, data: Seq[BigInt], dataW: Int, size: Int = 0, burst: UInt = BurstEncodings.Incr) extends Transaction { private[this] val numBytes = 1 << size private[this] val dtsize = numBytes * data.length private[this] val lowerBoundary = (addr / dtsize) * dtsize diff --git a/axi4/src/test/scala/VivadoAXIMemoryTester.scala b/axi4/src/test/scala/VivadoAXIMemoryTester.scala index b1704a3..9451feb 100644 --- a/axi4/src/test/scala/VivadoAXIMemoryTester.scala +++ b/axi4/src/test/scala/VivadoAXIMemoryTester.scala @@ -9,13 +9,15 @@ import axi4._ import chisel3._ import chiseltest._ +import chiseltest.experimental.TestOptionBuilder._ +import chiseltest.internal.VerilatorBackendAnnotation import org.scalatest._ class VivadoAXIMemoryTester extends FlatSpec with ChiselScalatestTester with Matchers { behavior of "AXI4 BRAM" it should "initialize" in { - test(new VivadoAXIMemory()) { + test(new VivadoAXIMemory()).withAnnotations(Seq(VerilatorBackendAnnotation)) { dut => val master = new AXI4FunctionalMaster(dut) master.initialize() @@ -23,33 +25,70 @@ class VivadoAXIMemoryTester extends FlatSpec with ChiselScalatestTester with Mat } } - it should "write and read" in { - test(new VivadoAXIMemory()) { + it should "write and read manually" in { + test(new VivadoAXIMemory()).withAnnotations(Seq(VerilatorBackendAnnotation)) { dut => val master = new AXI4FunctionalMaster(dut) master.initialize() - master.createWriteTrx(0, Seq[BigInt](42), size = 2) - var r = master.checkResponse() + + def printCheck() = { + println("AWREADY = " + dut.io.aw.ready.peek.litToBoolean) + println("WREADY = " + dut.io.dw.ready.peek.litToBoolean) + println("BVALID = " + dut.io.wr.valid.peek.litToBoolean) + println("ARREADY = " + dut.io.ar.ready.peek.litToBoolean) + println("RVALID = " + dut.io.dr.valid.peek.litToBoolean) + } + + // Set some initial values on the necessary signals + dut.io.dw.bits.data.poke(42.U) + dut.io.dw.bits.strb.poke("b1111".U) + dut.io.dw.bits.last.poke(true.B) + printCheck() + + // Write address + dut.io.aw.valid.poke(true.B) do { dut.clock.step() - r = master.checkResponse() - } while (r == None) - var resp = r match { - case Some(r) => r.resp.litValue - case _ => 0 - } - println(s"Got response code $resp") - master.createReadTrx(0, size = 2) - var v = master.checkReadData() + } while (!dut.io.aw.ready.peek.litToBoolean) + printCheck() + dut.clock.step() + dut.io.aw.valid.poke(false.B) + + // Write some data + dut.io.dw.valid.poke(true.B) do { dut.clock.step() - v = master.checkReadData() - } while (v == None) - var values = v match { - case Some(v) => v - case _ => Seq[BigInt](-1) + } while (!dut.io.dw.ready.peek.litToBoolean) + printCheck() + dut.clock.step() + dut.io.dw.valid.poke(false.B) + + // Fetch response + dut.io.wr.ready.poke(true.B) + while (!dut.io.wr.valid.peek.litToBoolean) { + dut.clock.step() } - println(s"Got read data $values") + printCheck() + val r = dut.io.wr.bits.resp.peek.litValue + println(s"Got response $r") + + // Read address + dut.io.ar.valid.poke(true.B) + do { + dut.clock.step() + } while (!dut.io.ar.ready.peek.litToBoolean) + printCheck() + dut.clock.step() + dut.io.ar.valid.poke(false.B) + + // Read some data + dut.io.dr.ready.poke(true.B) + while (!dut.io.dr.valid.peek.litToBoolean) { + dut.clock.step() + } + printCheck() + dut.io.dr.bits.data.expect(42.U) + master.close() } }