From a8388224ff4e55848279a4f0d8a58d1dd19bf88c Mon Sep 17 00:00:00 2001 From: hansemandse Date: Thu, 3 Dec 2020 22:51:34 +0100 Subject: [PATCH 01/10] cleanup and renaming --- axi4/src/main/scala/VivadoAXIMemory.scala | 9 +- .../scala/axi4/AXI4FunctionalMaster.scala | 2 +- axi4/src/main/scala/axi4/Defs.scala | 102 ++++++++---------- axi4/src/main/scala/axi4/Master.scala | 19 +--- axi4/src/main/scala/axi4/Slave.scala | 19 +--- .../test/scala/VivadoAXIMemoryTester.scala | 12 +-- 6 files changed, 67 insertions(+), 96 deletions(-) diff --git a/axi4/src/main/scala/VivadoAXIMemory.scala b/axi4/src/main/scala/VivadoAXIMemory.scala index 9b65a03..bc51e4c 100644 --- a/axi4/src/main/scala/VivadoAXIMemory.scala +++ b/axi4/src/main/scala/VivadoAXIMemory.scala @@ -74,7 +74,7 @@ class mymem extends BlackBox with HasBlackBoxResource { } /** Wrapper for mymem */ -class VivadoAXIMemory extends Slave(1, 10, 32) { +class VivadoAXIMemory extends Slave(10, 32, 1) { /** Instantiate a memory */ val mem = Module(new mymem()) @@ -96,6 +96,7 @@ class VivadoAXIMemory extends Slave(1, 10, 32) { mem.io.s00_axi_awprot := aw.prot mem.io.s00_axi_awregion := aw.region mem.io.s00_axi_awqos := aw.qos + // Leave user port unconnected mem.io.s00_axi_awvalid := io.aw.valid io.aw.ready := mem.io.s00_axi_awready @@ -103,12 +104,15 @@ class VivadoAXIMemory extends Slave(1, 10, 32) { mem.io.s00_axi_wdata := dw.data mem.io.s00_axi_wstrb := dw.strb mem.io.s00_axi_wlast := dw.last + // Leave user port unconnected mem.io.s00_axi_wvalid := io.dw.valid io.dw.ready := mem.io.s00_axi_wready /** Write response */ wr.id := mem.io.s00_axi_bid wr.resp := mem.io.s00_axi_bresp + // Connect user port to constant 0 + wr.user := 0.U io.wr.valid := mem.io.s00_axi_bvalid mem.io.s00_axi_bready := io.wr.ready @@ -123,6 +127,7 @@ class VivadoAXIMemory extends Slave(1, 10, 32) { mem.io.s00_axi_arprot := ar.prot mem.io.s00_axi_arregion := ar.region mem.io.s00_axi_arqos := ar.qos + // Leave user port unconnected mem.io.s00_axi_arvalid := io.ar.valid io.ar.ready := mem.io.s00_axi_arready @@ -131,6 +136,8 @@ class VivadoAXIMemory extends Slave(1, 10, 32) { dr.data := mem.io.s00_axi_rdata dr.resp := mem.io.s00_axi_rresp dr.last := mem.io.s00_axi_rlast + // Connect user port to constant 0 + dr.user := 0.U io.dr.valid := mem.io.s00_axi_rvalid mem.io.s00_axi_rready := io.dr.ready diff --git a/axi4/src/main/scala/axi4/AXI4FunctionalMaster.scala b/axi4/src/main/scala/axi4/AXI4FunctionalMaster.scala index d0bb460..4712e06 100644 --- a/axi4/src/main/scala/axi4/AXI4FunctionalMaster.scala +++ b/axi4/src/main/scala/axi4/AXI4FunctionalMaster.scala @@ -19,7 +19,7 @@ import scala.util.Random * @param dut a slave DUT * */ -class AXI4FunctionalMaster[T <: Slave](dut: T) { +class FunctionalMaster[T <: Slave](dut: T) { /** DUT information */ private[this] val idW = dut.idW private[this] val addrW = dut.addrW diff --git a/axi4/src/main/scala/axi4/Defs.scala b/axi4/src/main/scala/axi4/Defs.scala index c3f7434..4655815 100644 --- a/axi4/src/main/scala/axi4/Defs.scala +++ b/axi4/src/main/scala/axi4/Defs.scala @@ -62,21 +62,18 @@ object ResponseEncodings { val Decerr = "b11".U } -/** User signal trait */ -trait UserSignal { - val user = Output(UInt()) -} - /** AXI4 write address base interface * * Defines the mandatory signals in the interface * - * @param idW the width of the AWID signal in bits * @param addrW the width of the AWADDR signal in bits + * @param idW the width of the AWID signal in bits, defaults to 0 + * @param userW the width of the AWUSER signal in bits, defaults to 0 */ -abstract class AXI4WABase(val idW: Int, val addrW: Int) extends Bundle { - require(idW > 0, "the id width must be a positive integer") +class WA(val addrW: Int, val idW: Int = 0, val userW: Int = 0) extends Bundle { require(addrW > 0, "the address width must be a positive integer") + require(idW >= 0, "the id width must be a non-negative integer") + require(userW >= 0, "the user with must be a non-negative integer") val id = Output(UInt(idW.W)) val addr = Output(UInt(addrW.W)) val len = Output(UInt(8.W)) @@ -87,50 +84,53 @@ abstract class AXI4WABase(val idW: Int, val addrW: Int) extends Bundle { val prot = Output(UInt(3.W)) val qos = Output(UInt(4.W)) val region = Output(UInt(4.W)) + val user = Output(UInt(userW.W)) } -class AXI4WA(idW: Int, addrW: Int) extends AXI4WABase(idW, addrW) -class AXI4WAUser(idW: Int, addrW: Int) extends AXI4WA(idW, addrW) with UserSignal /** AXI4 write data base interface * * Defines the mandatory signals in the interface * * @param dataW the width of the WDATA signal in bits + * @param userW the width of the WUSER signal in bits, defaults to 0 */ -abstract class AXI4WDBase(val dataW: Int) extends Bundle { +class WD(val dataW: Int, val userW: Int = 0) extends Bundle { require(dataW > 0, "the data width must be a positive integer") require(isPow2(dataW / 8), "the data width must be a power of 2 multiple of bytes") + require(userW >= 0, "the user with must be a non-negative integer") val data = Output(UInt(dataW.W)) val strb = Output(UInt((dataW/8).W)) val last = Output(Bool()) + val user = Output(UInt(userW.W)) } -class AXI4WD(dataW: Int) extends AXI4WDBase(dataW) -class AXI4WDUser(dataW: Int) extends AXI4WD(dataW) with UserSignal /** AXI4 write response base interface * * Defines the mandatory signals in the interface * - * @param idW the width of the BID signal in bits + * @param idW the width of the BID signal in bits, defaults to 0 + * @param userW the width of the BUSER signal in bits, defaults to 0 */ -abstract class AXI4WRBase(val idW: Int) extends Bundle { - require(idW > 0, "the id width must be a positive integer") +class WR(val idW: Int = 0, val userW: Int = 0) extends Bundle { + require(idW >= 0, "the id width must be a non-negative integer") + require(userW >= 0, "the user with must be a non-negative integer") val id = Input(UInt(idW.W)) val resp = Input(UInt(2.W)) + val user = Input(UInt(userW.W)) } -class AXI4WR(idW: Int) extends AXI4WRBase(idW) -class AXI4WRUser(idW: Int) extends AXI4WR(idW) with UserSignal /** AXI4 read address base interface * * Defines the mandatory signals in the interface * - * @param idW the width of the ARID signal in bits * @param addrW the width of the ARADDR signal in bits + * @param idW the width of the ARID signal in bits, defaults to 0 + * @param userW the width of the ARUSER signal in bits, defaults to 0 */ -abstract class AXI4RABase(val idW: Int, val addrW: Int) extends Bundle { - require(idW > 0, "the id width must be a positive integer") +class RA(val addrW: Int, val idW: Int = 0, val userW: Int = 0) extends Bundle { require(addrW > 0, "the address width must be a positive integer") + require(idW >= 0, "the id width must be a non-negative integer") + require(userW >= 0, "the user with must be a non-negative integer") val id = Output(UInt(idW.W)) val addr = Output(UInt(addrW.W)) val len = Output(UInt(8.W)) @@ -141,37 +141,38 @@ abstract class AXI4RABase(val idW: Int, val addrW: Int) extends Bundle { val prot = Output(UInt(3.W)) val qos = Output(UInt(4.W)) val region = Output(UInt(4.W)) + val user = Output(UInt(userW.W)) } -class AXI4RA(idW: Int, addrW: Int) extends AXI4RABase(idW, addrW) -class AXI4RAUser(idW: Int, addrW: Int) extends AXI4RA(idW, addrW) with UserSignal /** AXI4 read data base interface * * Defines the mandatory signals in the interface * - * @param idW the width of the RID signal in bits * @param dataW the width of the RDATA signal in bits + * @param idW the width of the RID signal in bits, defaults to 0 + * @param userW the width of the RUSER signal in bits, defaults to 0 */ -abstract class AXI4RDBase(val idW: Int, val dataW: Int) extends Bundle { - require(idW > 0, "the id width must be a positive integer") +class RD(val dataW: Int, val idW: Int = 0, val userW: Int = 0) extends Bundle { require(isPow2(dataW / 8), "the data width must be a power of 2 multiple of bytes") + require(idW >= 0, "the id width must be a non-negative integer") + require(userW >= 0, "the user with must be a non-negative integer") val id = Input(UInt(idW.W)) val data = Input(UInt(dataW.W)) val resp = Input(UInt(2.W)) val last = Input(Bool()) + val user = Input(UInt(userW.W)) } -class AXI4RD(idW: Int, dataW: Int) extends AXI4RDBase(idW, dataW) -class AXI4RDUser(idW: Int, dataW: Int) extends AXI4RD(idW, dataW) with UserSignal /** AXI4 master interface * * Components meant to use this interface should extend it * - * @param idW the width of the ID signals in bits * @param addrW the width of the address signals in bits * @param dataW the width of the data read/write signals in bits + * @param idW the width of the ID signals in bits, defaults to 0 + * @param userW the width of the user signals in bits, defaults to 0 */ -abstract class AXI4MasterBase(val idW: Int, val addrW: Int, val dataW: Int) extends Bundle { +class MasterInterface(val addrW: Int, val dataW: Int, val idW: Int = 0, val userW: Int = 0) extends Bundle { /** Fields implementing each of the AXI channels * * [[aw]] is the write address channel @@ -180,30 +181,23 @@ abstract class AXI4MasterBase(val idW: Int, val addrW: Int, val dataW: Int) exte * [[ar]] is the read address channel * [[dr]] is the read data channel */ - val aw = Decoupled(new AXI4WA(idW, addrW)) - val dw = Decoupled(new AXI4WD(dataW)) - val wr = Flipped(Decoupled(Flipped(new AXI4WR(idW)))) - val ar = Decoupled(new AXI4RA(idW, addrW)) - val dr = Flipped(Decoupled(Flipped(new AXI4RD(idW, dataW)))) -} -class AXI4Master(idW: Int, addrW: Int, dataW: Int) extends AXI4MasterBase(idW, addrW, dataW) -class AXI4MasterUser(idW: Int, addrW: Int, dataW: Int) extends AXI4Master(idW, addrW, dataW) { - override val aw = Decoupled(new AXI4WAUser(idW, addrW)) - override val dw = Decoupled(new AXI4WDUser(dataW)) - override val wr = Flipped(Decoupled(Flipped(new AXI4WRUser(idW)))) - override val ar = Decoupled(new AXI4RAUser(idW, addrW)) - override val dr = Flipped(Decoupled(Flipped(new AXI4RDUser(idW, dataW)))) + val aw = Decoupled(new WA(addrW, idW, userW)) + val dw = Decoupled(new WD(dataW, userW)) + val wr = Flipped(Decoupled(Flipped(new WR(idW, userW)))) + val ar = Decoupled(new RA(addrW, idW, userW)) + val dr = Flipped(Decoupled(Flipped(new RD(dataW, idW, userW)))) } /** AXI4 slave interface * * Components meant to use this interface should extend it * - * @param idW the width of the ID signals in bits * @param addrW the width of the address signals in bits * @param dataW the width of the data read/write signals in bits + * @param idW the width of the ID signals in bits, defaults to 0 + * @param userW the width of the user signals in bits, defaults to 0 */ -abstract class AXI4SlaveBase(val idW: Int, val addrW: Int, val dataW: Int) extends Bundle { +class SlaveInterface(val addrW: Int, val dataW: Int, val idW: Int = 0, val userW: Int = 0) extends Bundle { /** Fields implementing each of the AXI channels * * [[aw]] is the write address channel @@ -212,17 +206,9 @@ abstract class AXI4SlaveBase(val idW: Int, val addrW: Int, val dataW: Int) exten * [[ar]] is the read address channel * [[dr]] is the read data channel */ - val aw = Flipped(Decoupled(new AXI4WA(idW, addrW))) - val dw = Flipped(Decoupled(new AXI4WD(dataW))) - val wr = Flipped(Flipped(Decoupled(Flipped(new AXI4WR(idW))))) - val ar = Flipped(Decoupled(new AXI4RA(idW, addrW))) - val dr = Flipped(Flipped(Decoupled(Flipped(new AXI4RD(idW, dataW))))) -} -class AXI4Slave(idW: Int, addrW: Int, dataW: Int) extends AXI4SlaveBase(idW, addrW, dataW) -class AXI4SlaveUser(idW: Int, addrW: Int, dataW: Int) extends AXI4Slave(idW, addrW, dataW) { - override val aw = Flipped(Decoupled(new AXI4WAUser(idW, addrW))) - override val dw = Flipped(Decoupled(new AXI4WDUser(dataW))) - override val wr = Flipped(Flipped(Decoupled(Flipped(new AXI4WRUser(idW))))) - override val ar = Flipped(Decoupled(new AXI4RAUser(idW, addrW))) - override val dr = Flipped(Flipped(Decoupled(Flipped(new AXI4RDUser(idW, dataW))))) + val aw = Flipped(Decoupled(new WA(addrW, idW, userW))) + val dw = Flipped(Decoupled(new WD(dataW, userW))) + val wr = Flipped(Flipped(Decoupled(Flipped(new WR(idW, userW))))) + val ar = Flipped(Decoupled(new RA(addrW, idW, userW))) + val dr = Flipped(Flipped(Decoupled(Flipped(new RD(dataW, idW, userW))))) } diff --git a/axi4/src/main/scala/axi4/Master.scala b/axi4/src/main/scala/axi4/Master.scala index 249f5db..bbc8ed4 100644 --- a/axi4/src/main/scala/axi4/Master.scala +++ b/axi4/src/main/scala/axi4/Master.scala @@ -14,22 +14,11 @@ import chisel3._ * * An empty class representing an AXI master * - * @param idW the width of the ID signals in bits * @param addrW the width of the address signals in bits * @param dataW the width of the data read/write signals in bits + * @param idW the width of the ID signals in bits, defaults to 0 + * @param userW the width of the user signals in bits, defaults to 0 */ -abstract class Master(val idW: Int, val addrW: Int, val dataW: Int) extends Module { - val io = IO(new AXI4Master(idW, addrW, dataW)) -} - -/** AXI4 master with user signals - * - * An empty class representing an AXI master with user signals - * - * @param idW the width of the ID signals in bits - * @param addrW the width of the address signals in bits - * @param dataW the width of the data read/write signals in bits - */ -abstract class MasterUser(override val idW: Int, override val addrW: Int, override val dataW: Int) extends Master(idW, addrW, dataW) { - override val io = IO(new AXI4MasterUser(idW, addrW, dataW)) +abstract class Master(val addrW: Int, val dataW: Int, val idW: Int = 0, val userW: Int = 0) extends Module { + val io = IO(new MasterInterface(addrW, dataW, idW, userW)) } diff --git a/axi4/src/main/scala/axi4/Slave.scala b/axi4/src/main/scala/axi4/Slave.scala index 8f89e02..d892adb 100644 --- a/axi4/src/main/scala/axi4/Slave.scala +++ b/axi4/src/main/scala/axi4/Slave.scala @@ -14,22 +14,11 @@ import chisel3._ * * An empty class representing an AXI slave * - * @param idW the width of the ID signals in bits * @param addrW the width of the address signals in bits * @param dataW the width of the data read/write signals in bits + * @param idW the width of the ID signals in bits, defaults to 0 + * @param userW the width of the user signals in bits, defaults to 0 */ -abstract class Slave(val idW: Int, val addrW: Int, val dataW: Int) extends Module { - val io = IO(new AXI4Slave(idW, addrW, dataW)) -} - -/** AXI4 slave with user signals - * - * An empty class representing an AXI slave with user signals - * - * @param idW the width of the ID signals in bits - * @param addrW the width of the address signals in bits - * @param dataW the width of the data read/write signals in bits - */ -abstract class SlaveUser(override val idW: Int, override val addrW: Int, override val dataW: Int) extends Slave(idW, addrW, dataW) { - override val io = IO(new AXI4SlaveUser(idW, addrW, dataW)) +abstract class Slave(val addrW: Int, val dataW: Int, val idW: Int = 0, val userW: Int = 0) extends Module { + val io = IO(new SlaveInterface(addrW, dataW, idW, userW)) } diff --git a/axi4/src/test/scala/VivadoAXIMemoryTester.scala b/axi4/src/test/scala/VivadoAXIMemoryTester.scala index 51a934b..9a09d16 100644 --- a/axi4/src/test/scala/VivadoAXIMemoryTester.scala +++ b/axi4/src/test/scala/VivadoAXIMemoryTester.scala @@ -20,14 +20,14 @@ class VivadoAXIMemoryTester extends FlatSpec with ChiselScalatestTester with Mat it should "initialize" in { test(new VivadoAXIMemory()).withAnnotations(Seq(VerilatorBackendAnnotation)) { dut => - val master = new AXI4FunctionalMaster(dut) + val master = new FunctionalMaster(dut) } } it should "write and read manually" in { test(new VivadoAXIMemory()).withAnnotations(Seq(VerilatorBackendAnnotation)) { dut => - val master = new AXI4FunctionalMaster(dut) + val master = new FunctionalMaster(dut) def printCheck() = { println("AWREADY = " + dut.io.aw.ready.peek.litToBoolean) @@ -92,7 +92,7 @@ class VivadoAXIMemoryTester extends FlatSpec with ChiselScalatestTester with Mat it should "write and read with FIXED transactions" in { test(new VivadoAXIMemory()).withAnnotations(Seq(VerilatorBackendAnnotation)) { dut => - val master = new AXI4FunctionalMaster(dut) + val master = new FunctionalMaster(dut) // Create write transaction master.createWriteTrx(0, Seq(42), size = 2) @@ -123,7 +123,7 @@ class VivadoAXIMemoryTester extends FlatSpec with ChiselScalatestTester with Mat it should "write and read with INCR transactions" in { test(new VivadoAXIMemory()).withAnnotations(Seq(VerilatorBackendAnnotation)) { dut => - val master = new AXI4FunctionalMaster(dut) + val master = new FunctionalMaster(dut) // Create write transaction master.createWriteTrx(128, Seq.fill(128)(0x7FFFFFFF), len = 0x7F, size = 2, burst = BurstEncodings.Incr) @@ -154,7 +154,7 @@ class VivadoAXIMemoryTester extends FlatSpec with ChiselScalatestTester with Mat it should "write and read with WRAP transactions" in { test(new VivadoAXIMemory()).withAnnotations(Seq(VerilatorBackendAnnotation)) { dut => - val master = new AXI4FunctionalMaster(dut) + val master = new FunctionalMaster(dut) // addr = 96 // dtsize = 4 * 16 = 64 @@ -192,7 +192,7 @@ class VivadoAXIMemoryTester extends FlatSpec with ChiselScalatestTester with Mat it should "handle multiple in-flight transactions" in { test(new VivadoAXIMemory()).withAnnotations(Seq(VerilatorBackendAnnotation)) { dut => - val master = new AXI4FunctionalMaster(dut) + val master = new FunctionalMaster(dut) // Create two write transactions val rng = new Random(42) From f4482d9ad3e6f77cc951ff898a155cd4f739e429 Mon Sep 17 00:00:00 2001 From: Hans Jakob Date: Fri, 4 Dec 2020 15:38:20 +0100 Subject: [PATCH 02/10] update with package object --- axi4/README.md | 199 +++++----- axi4/src/main/scala/VivadoAXIMemory.scala | 294 +++++++------- axi4/src/main/scala/axi4/Defs.scala | 363 +++++++----------- ...nalMaster.scala => FunctionalMaster.scala} | 0 axi4/src/main/scala/axi4/Master.scala | 48 +-- axi4/src/main/scala/axi4/Slave.scala | 48 +-- axi4/src/main/scala/axi4/Transaction.scala | 302 +++++++-------- axi4/src/main/scala/package.scala | 60 +++ 8 files changed, 654 insertions(+), 660 deletions(-) rename axi4/src/main/scala/axi4/{AXI4FunctionalMaster.scala => FunctionalMaster.scala} (100%) create mode 100644 axi4/src/main/scala/package.scala diff --git a/axi4/README.md b/axi4/README.md index 86c4092..e74db7e 100644 --- a/axi4/README.md +++ b/axi4/README.md @@ -1,100 +1,99 @@ -# Verification of AMBA AXI Interfaced Components -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. - -### Extras -- Functional AXI slave (like above) - - Concurrent interfaces -- AXI Lite interface -- AXI Stream interface (see [here](https://developer.arm.com/documentation/ihi0051/latest/)) - -## Documentation -The AXI protocol defines five independent channels for reads and writes as listed below. As they are independent, read/write addresses may be transferred to a slave ahead of transferring the data. So-called _transactions_ consist of one or more _tranfers_ across a set of channels. Data is transferred in _bursts_ which consist of one or more _beats_. The protocol also supports multiple outstanding transactions and out-of-order completion by using tagged packets. All channels use simple decoupled ready-valid signalling. - -### Channels -The following channels are defined in the AXI4 protocol. ID fields are partly optional in that they do not have to be unique as the interconnect must append master numbers to them to ensure correct operation. -- _Write address channel_ used to initiate a write transaction from master to slave. A transfer contains ID, address, burst length, burst type, cacheability etc. (see page A2-29) - - `AWID [_:0]` ID field - - `AWADDR [_:0]` start address of a write transaction - - `AWLEN [7:0]` number of beats in the burst - - `AWSIZE [2:0]` beat size encoding, i.e. number of bytes per beat is 2^AWSIZE - - `AWBURST [1:0]` one of _FIXED_, _INCR_, or _WRAP_ - - `AWLOCK`, `AWCACHE [3:0]`, `AWPROT [2:0]`, and `AWQOS [3:0]` control data protection and quality of service - - _Optional_ `AWREGION [3:0]` allows sharing of a single physical interface for multiple logical regions - - _Optional_ `AWUSER [_:0]` - - `AWREADY` and `AWVALID` handshake signals - -- _Write data channel_ used to transfer write data from master to slave. A transfer contains ID, data, byte-wide write enable etc. (see page A2-30) - - `WDATA [_:0]` write data - - `WSTRB [_:0]` write strobe (i.e. one bit per byte of data) - - `WLAST` indicates whether this is the last beat in a burst - - _Optional_ `WUSER [_:0]` - - `WREADY` and `WVALID` handshake signals - -- _Write response channel_ used to inform a master about the completion of a write transaction. A transfer contains ID, write status etc. (see page A2-31) - - `BID [_:0]` ID field - - `BRESP [1:0]` write response from the slave; i.e. one of _OKAY_, _EXOKAY_, _SLVERR_, or _DECERR_ - - _Optional_ `BUSER` - - `BREADY` and `BVALID` handshake signals - -- _Read address channel_ used to initiate a read transaction from slave to master. A transfer contains ID, address, burst length, burst type, cacheability etc. (see page A2-32) - - `ARID [_:0]` ID field - - `ARADDR [_:0]` start address of a read transaction - - `ARLEN [7:0]` number of beats in the burst - - `ARSIZE [2:0]` beat size encoding - - `ARBURST [1:0]` one of _FIXED_, _INCR_, or _WRAP_ - - `ARLOCK`, `ARCACHE [3:0]`, `ARPROT [2:0]`, and `ARQOS [3:0]` control data protection and quality of service - - _Optional_ `ARREGION [3:0]` allows sharing of a single physical interface for multiple logical regions - - _Optional_ `ARUSER [_:0]` - - `ARREADY` and `ARVALID` handshake signals - -- _Read data channel_ used to transfer read data from slave to master. A transfer contains ID, data etc. (see page A2-33) - - `RID [_:0]` ID field - - `RDATA [_:0]` read data - - `RRESP [1:0]` read response from the slave - - `RLAST` indicates whether this is the last beat in a burst - - _Optional_ `RUSER [_:0]` - - `RREADY` and `RVALID` handshake signals - -Additionally, two global signals are used (see page A2-28) -- `ACLK` a shared global clock signal -- `ARESETn` a shared global active-low reset signal - -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/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). - -## Notes -The following are taken from the specification. - -### Regarding ready-valid interface -- No combinational logic between `Ready` and `Valid` in neither master nor slave components -- A source _may not_ wait until `Ready` is asserted before asserting `Valid` -- A destination _may_ wait until `Valid` is asserted before asserting `Ready` -- Once asserted, `Valid` _must_ remain asserted until a handshake occurs -- If `Ready` is asserted, it _can_ be deasserted before assertion of `Valid` -- Transfers occur at the first rising edge after both `Ready` and `Valid` are asserted -- Address channels _should_ per default have `AWREADY` and `ARREADY` asserted to avoid transfers taking at least two cycles - -### Regarding channel relationships -- A write response must always follow the last write transfer in a write transaction -- Read data must always follow the address to which it the data relates -- Write data _can_ appear before or simultaneously with the write address for the transaction - -### Regarding transactions -- Bursts _must not_ cross a 4KB boundary (to avoid device address space mapping issues) -- Burst length is in \[1, 256\] for burst type INCR; for all other burst types it is in \[1, 16\] -- Bursts _must_ be completed (i.e. no support for early termination) -- Beat size _must not_ exceed the data bus width of neither the master nor the slave. -- Narrow transfers (i.e. transfers using a subsection of the data bus) are implemented using `WSTRB` -- Byte invariance means that some transfers can be little endian while others can be big endian -- Unaligned transfers are indicated either with the low-order address bits or using `WSTRB` -- Only one response is returned for write transactions, whereas a different response may be returned for each beat in a read transaction - and the entire transaction _must_ be completed regardless of potential errors -- Asserting `AxCACHE[1]` means that a transaction is _modifiable_ and can, for example, be broken down into multiple transactions, combined with other transactions, or fetch more data than requested -- Transaction ordering must be preserved for non-modifiable transactions with the same ID to the same slave (see pages A4-63 to A4-64) - -### Regarding multiple transactions -- The ID signals can be used to identify separate transactions that must be returned in order -- AXI4 does _not_ support write data interleaving +# Verification of AMBA AXI Interfaced Components +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 +- Change bundles to simple signals instead of I/O +- AXI Lite interface +- AXI Stream interface (see [here](https://developer.arm.com/documentation/ihi0051/latest/)) + +## Documentation +The AXI protocol defines five independent channels for reads and writes as listed below. As they are independent, read/write addresses may be transferred to a slave ahead of transferring the data. So-called _transactions_ consist of one or more _tranfers_ across a set of channels. Data is transferred in _bursts_ which consist of one or more _beats_. The protocol also supports multiple outstanding transactions and out-of-order completion by using tagged packets. All channels use simple decoupled ready-valid signalling. + +### Channels +The following channels are defined in the AXI4 protocol. ID fields are partly optional in that they do not have to be unique as the interconnect must append master numbers to them to ensure correct operation. +- _Write address channel_ used to initiate a write transaction from master to slave. A transfer contains ID, address, burst length, burst type, cacheability etc. (see page A2-29) + - `AWID [_:0]` ID field + - `AWADDR [_:0]` start address of a write transaction + - `AWLEN [7:0]` number of beats in the burst + - `AWSIZE [2:0]` beat size encoding, i.e. number of bytes per beat is 2^AWSIZE + - `AWBURST [1:0]` one of _FIXED_, _INCR_, or _WRAP_ + - `AWLOCK`, `AWCACHE [3:0]`, `AWPROT [2:0]`, and `AWQOS [3:0]` control data protection and quality of service + - _Optional_ `AWREGION [3:0]` allows sharing of a single physical interface for multiple logical regions + - _Optional_ `AWUSER [_:0]` + - `AWREADY` and `AWVALID` handshake signals + +- _Write data channel_ used to transfer write data from master to slave. A transfer contains ID, data, byte-wide write enable etc. (see page A2-30) + - `WDATA [_:0]` write data + - `WSTRB [_:0]` write strobe (i.e. one bit per byte of data) + - `WLAST` indicates whether this is the last beat in a burst + - _Optional_ `WUSER [_:0]` + - `WREADY` and `WVALID` handshake signals + +- _Write response channel_ used to inform a master about the completion of a write transaction. A transfer contains ID, write status etc. (see page A2-31) + - `BID [_:0]` ID field + - `BRESP [1:0]` write response from the slave; i.e. one of _OKAY_, _EXOKAY_, _SLVERR_, or _DECERR_ + - _Optional_ `BUSER` + - `BREADY` and `BVALID` handshake signals + +- _Read address channel_ used to initiate a read transaction from slave to master. A transfer contains ID, address, burst length, burst type, cacheability etc. (see page A2-32) + - `ARID [_:0]` ID field + - `ARADDR [_:0]` start address of a read transaction + - `ARLEN [7:0]` number of beats in the burst + - `ARSIZE [2:0]` beat size encoding + - `ARBURST [1:0]` one of _FIXED_, _INCR_, or _WRAP_ + - `ARLOCK`, `ARCACHE [3:0]`, `ARPROT [2:0]`, and `ARQOS [3:0]` control data protection and quality of service + - _Optional_ `ARREGION [3:0]` allows sharing of a single physical interface for multiple logical regions + - _Optional_ `ARUSER [_:0]` + - `ARREADY` and `ARVALID` handshake signals + +- _Read data channel_ used to transfer read data from slave to master. A transfer contains ID, data etc. (see page A2-33) + - `RID [_:0]` ID field + - `RDATA [_:0]` read data + - `RRESP [1:0]` read response from the slave + - `RLAST` indicates whether this is the last beat in a burst + - _Optional_ `RUSER [_:0]` + - `RREADY` and `RVALID` handshake signals + +Additionally, two global signals are used (see page A2-28) +- `ACLK` a shared global clock signal +- `ARESETn` a shared global active-low reset signal + +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/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). + +## Notes +The following are taken from the specification. + +### Regarding ready-valid interface +- No combinational logic between `Ready` and `Valid` in neither master nor slave components +- A source _may not_ wait until `Ready` is asserted before asserting `Valid` +- A destination _may_ wait until `Valid` is asserted before asserting `Ready` +- Once asserted, `Valid` _must_ remain asserted until a handshake occurs +- If `Ready` is asserted, it _can_ be deasserted before assertion of `Valid` +- Transfers occur at the first rising edge after both `Ready` and `Valid` are asserted +- Address channels _should_ per default have `AWREADY` and `ARREADY` asserted to avoid transfers taking at least two cycles + +### Regarding channel relationships +- A write response must always follow the last write transfer in a write transaction +- Read data must always follow the address to which it the data relates +- Write data _can_ appear before or simultaneously with the write address for the transaction + +### Regarding transactions +- Bursts _must not_ cross a 4KB boundary (to avoid device address space mapping issues) +- Burst length is in \[1, 256\] for burst type INCR; for all other burst types it is in \[1, 16\] +- Bursts _must_ be completed (i.e. no support for early termination) +- Beat size _must not_ exceed the data bus width of neither the master nor the slave. +- Narrow transfers (i.e. transfers using a subsection of the data bus) are implemented using `WSTRB` +- Byte invariance means that some transfers can be little endian while others can be big endian +- Unaligned transfers are indicated either with the low-order address bits or using `WSTRB` +- Only one response is returned for write transactions, whereas a different response may be returned for each beat in a read transaction - and the entire transaction _must_ be completed regardless of potential errors +- Asserting `AxCACHE[1]` means that a transaction is _modifiable_ and can, for example, be broken down into multiple transactions, combined with other transactions, or fetch more data than requested +- Transaction ordering must be preserved for non-modifiable transactions with the same ID to the same slave (see pages A4-63 to A4-64) + +### Regarding multiple transactions +- The ID signals can be used to identify separate transactions that must be returned in order +- AXI4 does _not_ support write data interleaving diff --git a/axi4/src/main/scala/VivadoAXIMemory.scala b/axi4/src/main/scala/VivadoAXIMemory.scala index bc51e4c..54659cd 100644 --- a/axi4/src/main/scala/VivadoAXIMemory.scala +++ b/axi4/src/main/scala/VivadoAXIMemory.scala @@ -1,147 +1,147 @@ -/** - * Author: Hans Jakob Damsgaard, hansjakobdamsgaard@gmail.com - * - * Purpose: Implementation of a testing framework for AXI4-compliant devices. - * - * Content: A blackbox for an AXI4 block RAM generated by Vivado. -*/ - -import axi4._ -import chisel3._ -import chisel3.util._ - -/** Vivado IP BRAM with full AXI4 interface */ -class mymem extends BlackBox with HasBlackBoxResource { - val io = IO(new Bundle { - /** Address write */ - val s00_axi_awid = Input(UInt(1.W)) - val s00_axi_awaddr = Input(UInt(10.W)) - val s00_axi_awlen = Input(UInt(8.W)) - val s00_axi_awsize = Input(UInt(3.W)) - val s00_axi_awburst = Input(UInt(2.W)) - val s00_axi_awlock = Input(Bool()) - val s00_axi_awcache = Input(UInt(4.W)) - val s00_axi_awprot = Input(UInt(3.W)) - val s00_axi_awregion = Input(UInt(4.W)) - val s00_axi_awqos = Input(UInt(4.W)) - val s00_axi_awvalid = Input(Bool()) - val s00_axi_awready = Output(Bool()) - - /** Data write */ - val s00_axi_wdata = Input(UInt(32.W)) - val s00_axi_wstrb = Input(UInt(4.W)) - val s00_axi_wlast = Input(Bool()) - val s00_axi_wvalid = Input(Bool()) - val s00_axi_wready = Output(Bool()) - - /** Write response */ - val s00_axi_bid = Output(UInt(1.W)) - val s00_axi_bresp = Output(UInt(2.W)) - val s00_axi_bvalid = Output(Bool()) - val s00_axi_bready = Input(Bool()) - - /** Address read */ - val s00_axi_arid = Input(UInt(1.W)) - val s00_axi_araddr = Input(UInt(10.W)) - val s00_axi_arlen = Input(UInt(8.W)) - val s00_axi_arsize = Input(UInt(3.W)) - val s00_axi_arburst = Input(UInt(2.W)) - val s00_axi_arlock = Input(Bool()) - val s00_axi_arcache = Input(UInt(4.W)) - val s00_axi_arprot = Input(UInt(3.W)) - val s00_axi_arregion = Input(UInt(4.W)) - val s00_axi_arqos = Input(UInt(4.W)) - val s00_axi_arvalid = Input(Bool()) - val s00_axi_arready = Output(Bool()) - - /** Data read */ - val s00_axi_rid = Output(UInt(1.W)) - val s00_axi_rdata = Output(UInt(32.W)) - val s00_axi_rresp = Output(UInt(2.W)) - val s00_axi_rlast = Output(Bool()) - val s00_axi_rvalid = Output(Bool()) - val s00_axi_rready = Input(Bool()) - - /** Clock and reset */ - val s00_axi_aclk = Input(Clock()) - val s00_axi_aresetn = Input(Reset()) - }) - - /** 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 */ -class VivadoAXIMemory extends Slave(10, 32, 1) { - /** Instantiate a memory */ - val mem = Module(new mymem()) - - /** Connect it up with the slave's AXI interface */ - val aw = io.aw.bits - val dw = io.dw.bits - val wr = io.wr.bits - val ar = io.ar.bits - val dr = io.dr.bits - - /** Address write */ - mem.io.s00_axi_awid := aw.id - mem.io.s00_axi_awaddr := aw.addr - mem.io.s00_axi_awlen := aw.len - mem.io.s00_axi_awsize := aw.size - mem.io.s00_axi_awburst := aw.burst - mem.io.s00_axi_awlock := aw.lock - mem.io.s00_axi_awcache := aw.cache - mem.io.s00_axi_awprot := aw.prot - mem.io.s00_axi_awregion := aw.region - mem.io.s00_axi_awqos := aw.qos - // Leave user port unconnected - mem.io.s00_axi_awvalid := io.aw.valid - io.aw.ready := mem.io.s00_axi_awready - - /** Data write */ - mem.io.s00_axi_wdata := dw.data - mem.io.s00_axi_wstrb := dw.strb - mem.io.s00_axi_wlast := dw.last - // Leave user port unconnected - mem.io.s00_axi_wvalid := io.dw.valid - io.dw.ready := mem.io.s00_axi_wready - - /** Write response */ - wr.id := mem.io.s00_axi_bid - wr.resp := mem.io.s00_axi_bresp - // Connect user port to constant 0 - wr.user := 0.U - io.wr.valid := mem.io.s00_axi_bvalid - mem.io.s00_axi_bready := io.wr.ready - - /** Address read */ - mem.io.s00_axi_arid := ar.id - mem.io.s00_axi_araddr := ar.addr - mem.io.s00_axi_arlen := ar.len - mem.io.s00_axi_arsize := ar.size - mem.io.s00_axi_arburst := ar.burst - mem.io.s00_axi_arlock := ar.lock - mem.io.s00_axi_arcache := ar.cache - mem.io.s00_axi_arprot := ar.prot - mem.io.s00_axi_arregion := ar.region - mem.io.s00_axi_arqos := ar.qos - // Leave user port unconnected - mem.io.s00_axi_arvalid := io.ar.valid - io.ar.ready := mem.io.s00_axi_arready - - /** Data read */ - dr.id := mem.io.s00_axi_rid - dr.data := mem.io.s00_axi_rdata - dr.resp := mem.io.s00_axi_rresp - dr.last := mem.io.s00_axi_rlast - // Connect user port to constant 0 - dr.user := 0.U - io.dr.valid := mem.io.s00_axi_rvalid - mem.io.s00_axi_rready := io.dr.ready - - /** Clock and reset */ - mem.io.s00_axi_aclk := clock - mem.io.s00_axi_aresetn := reset -} +/** + * Author: Hans Jakob Damsgaard, hansjakobdamsgaard@gmail.com + * + * Purpose: Implementation of a testing framework for AXI4-compliant devices. + * + * Content: A blackbox for an AXI4 block RAM generated by Vivado. +*/ + +import axi4._ +import chisel3._ +import chisel3.util._ + +/** Vivado IP BRAM with full AXI4 interface */ +class mymem extends BlackBox with HasBlackBoxResource { + val io = IO(new Bundle { + /** Address write */ + val s00_axi_awid = Input(UInt(1.W)) + val s00_axi_awaddr = Input(UInt(10.W)) + val s00_axi_awlen = Input(UInt(8.W)) + val s00_axi_awsize = Input(UInt(3.W)) + val s00_axi_awburst = Input(UInt(2.W)) + val s00_axi_awlock = Input(Bool()) + val s00_axi_awcache = Input(UInt(4.W)) + val s00_axi_awprot = Input(UInt(3.W)) + val s00_axi_awregion = Input(UInt(4.W)) + val s00_axi_awqos = Input(UInt(4.W)) + val s00_axi_awvalid = Input(Bool()) + val s00_axi_awready = Output(Bool()) + + /** Data write */ + val s00_axi_wdata = Input(UInt(32.W)) + val s00_axi_wstrb = Input(UInt(4.W)) + val s00_axi_wlast = Input(Bool()) + val s00_axi_wvalid = Input(Bool()) + val s00_axi_wready = Output(Bool()) + + /** Write response */ + val s00_axi_bid = Output(UInt(1.W)) + val s00_axi_bresp = Output(UInt(2.W)) + val s00_axi_bvalid = Output(Bool()) + val s00_axi_bready = Input(Bool()) + + /** Address read */ + val s00_axi_arid = Input(UInt(1.W)) + val s00_axi_araddr = Input(UInt(10.W)) + val s00_axi_arlen = Input(UInt(8.W)) + val s00_axi_arsize = Input(UInt(3.W)) + val s00_axi_arburst = Input(UInt(2.W)) + val s00_axi_arlock = Input(Bool()) + val s00_axi_arcache = Input(UInt(4.W)) + val s00_axi_arprot = Input(UInt(3.W)) + val s00_axi_arregion = Input(UInt(4.W)) + val s00_axi_arqos = Input(UInt(4.W)) + val s00_axi_arvalid = Input(Bool()) + val s00_axi_arready = Output(Bool()) + + /** Data read */ + val s00_axi_rid = Output(UInt(1.W)) + val s00_axi_rdata = Output(UInt(32.W)) + val s00_axi_rresp = Output(UInt(2.W)) + val s00_axi_rlast = Output(Bool()) + val s00_axi_rvalid = Output(Bool()) + val s00_axi_rready = Input(Bool()) + + /** Clock and reset */ + val s00_axi_aclk = Input(Clock()) + val s00_axi_aresetn = Input(Reset()) + }) + + /** 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 */ +class VivadoAXIMemory extends Slave(10, 32, 1) { + /** Instantiate a memory */ + val mem = Module(new mymem()) + + /** Connect it up with the slave's AXI interface */ + val aw = io.aw.bits + val dw = io.dw.bits + val wr = io.wr.bits + val ar = io.ar.bits + val dr = io.dr.bits + + /** Address write */ + mem.io.s00_axi_awid := aw.id + mem.io.s00_axi_awaddr := aw.addr + mem.io.s00_axi_awlen := aw.len + mem.io.s00_axi_awsize := aw.size + mem.io.s00_axi_awburst := aw.burst + mem.io.s00_axi_awlock := aw.lock + mem.io.s00_axi_awcache := aw.cache + mem.io.s00_axi_awprot := aw.prot + mem.io.s00_axi_awregion := aw.region + mem.io.s00_axi_awqos := aw.qos + // Leave user port unconnected + mem.io.s00_axi_awvalid := io.aw.valid + io.aw.ready := mem.io.s00_axi_awready + + /** Data write */ + mem.io.s00_axi_wdata := dw.data + mem.io.s00_axi_wstrb := dw.strb + mem.io.s00_axi_wlast := dw.last + // Leave user port unconnected + mem.io.s00_axi_wvalid := io.dw.valid + io.dw.ready := mem.io.s00_axi_wready + + /** Write response */ + wr.id := mem.io.s00_axi_bid + wr.resp := mem.io.s00_axi_bresp + // Connect user port to constant 0 + wr.user := 0.U + io.wr.valid := mem.io.s00_axi_bvalid + mem.io.s00_axi_bready := io.wr.ready + + /** Address read */ + mem.io.s00_axi_arid := ar.id + mem.io.s00_axi_araddr := ar.addr + mem.io.s00_axi_arlen := ar.len + mem.io.s00_axi_arsize := ar.size + mem.io.s00_axi_arburst := ar.burst + mem.io.s00_axi_arlock := ar.lock + mem.io.s00_axi_arcache := ar.cache + mem.io.s00_axi_arprot := ar.prot + mem.io.s00_axi_arregion := ar.region + mem.io.s00_axi_arqos := ar.qos + // Leave user port unconnected + mem.io.s00_axi_arvalid := io.ar.valid + io.ar.ready := mem.io.s00_axi_arready + + /** Data read */ + dr.id := mem.io.s00_axi_rid + dr.data := mem.io.s00_axi_rdata + dr.resp := mem.io.s00_axi_rresp + dr.last := mem.io.s00_axi_rlast + // Connect user port to constant 0 + dr.user := 0.U + io.dr.valid := mem.io.s00_axi_rvalid + mem.io.s00_axi_rready := io.dr.ready + + /** Clock and reset */ + mem.io.s00_axi_aclk := clock + mem.io.s00_axi_aresetn := reset +} diff --git a/axi4/src/main/scala/axi4/Defs.scala b/axi4/src/main/scala/axi4/Defs.scala index 4655815..49df6ec 100644 --- a/axi4/src/main/scala/axi4/Defs.scala +++ b/axi4/src/main/scala/axi4/Defs.scala @@ -1,214 +1,149 @@ -/** - * Author: Hans Jakob Damsgaard, hansjakobdamsgaard@gmail.com - * - * Purpose: Implementation of a testing framework for AXI4-compliant devices. - * - * Content: Interface definitions for AXI4. -*/ - -package axi4 - -import chisel3._ -import chisel3.util._ - -/** Constant naming according to https://docs.scala-lang.org/style/naming-conventions.html */ - -/** AXI4 burst encodings */ -object BurstEncodings { - val Fixed = "b00".U - val Incr = "b01".U - val Wrap = "b10".U -} - -/** AXI lock encodings */ -object LockEncodings { - val NormalAccess = false.B - val ExclusiveAccess = true.B -} - -/** AXI4 memory encodings */ -object MemoryEncodings { - val DeviceNonbuf = "b0000".U - val DeviceBuf = "b0001".U - val NormalNonbuf = "b0010".U - val NormalBuf = "b0011".U - val WtNoalloc = "b0110".U - val WtReadalloc = "b0110".U - val WtWritealloc = "b1110".U - val WtRwalloc = "b1110".U - val WbNoalloc = "b0111".U - val WbReadalloc = "b0111".U - val WbWritealloc = "b1111".U - val WbRwalloc = "b1111".U -} - -/** AXI4 protection encodings */ -object ProtectionEncodings { - val DataSecUpriv = "b000".U - val DataSecPriv = "b001".U - val DataNsecUpriv = "b010".U - val DataNsecPriv = "b011".U - val InstrSecUpriv = "b100".U - val InstrSecPriv = "b101".U - val InstrNsecUpriv = "b110".U - val InstrNsecPriv = "b111".U -} - -/** AXI4 response encodings */ -object ResponseEncodings { - val Okay = "b00".U - val Exokay = "b01".U - val Slverr = "b10".U - val Decerr = "b11".U -} - -/** AXI4 write address base interface - * - * Defines the mandatory signals in the interface - * - * @param addrW the width of the AWADDR signal in bits - * @param idW the width of the AWID signal in bits, defaults to 0 - * @param userW the width of the AWUSER signal in bits, defaults to 0 - */ -class WA(val addrW: Int, val idW: Int = 0, val userW: Int = 0) extends Bundle { - require(addrW > 0, "the address width must be a positive integer") - require(idW >= 0, "the id width must be a non-negative integer") - require(userW >= 0, "the user with must be a non-negative integer") - val id = Output(UInt(idW.W)) - val addr = Output(UInt(addrW.W)) - val len = Output(UInt(8.W)) - val size = Output(UInt(3.W)) - val burst = Output(UInt(2.W)) - val lock = Output(Bool()) - val cache = Output(UInt(4.W)) - val prot = Output(UInt(3.W)) - val qos = Output(UInt(4.W)) - val region = Output(UInt(4.W)) - val user = Output(UInt(userW.W)) -} - -/** AXI4 write data base interface - * - * Defines the mandatory signals in the interface - * - * @param dataW the width of the WDATA signal in bits - * @param userW the width of the WUSER signal in bits, defaults to 0 - */ -class WD(val dataW: Int, val userW: Int = 0) extends Bundle { - require(dataW > 0, "the data width must be a positive integer") - require(isPow2(dataW / 8), "the data width must be a power of 2 multiple of bytes") - require(userW >= 0, "the user with must be a non-negative integer") - val data = Output(UInt(dataW.W)) - val strb = Output(UInt((dataW/8).W)) - val last = Output(Bool()) - val user = Output(UInt(userW.W)) -} - -/** AXI4 write response base interface - * - * Defines the mandatory signals in the interface - * - * @param idW the width of the BID signal in bits, defaults to 0 - * @param userW the width of the BUSER signal in bits, defaults to 0 - */ -class WR(val idW: Int = 0, val userW: Int = 0) extends Bundle { - require(idW >= 0, "the id width must be a non-negative integer") - require(userW >= 0, "the user with must be a non-negative integer") - val id = Input(UInt(idW.W)) - val resp = Input(UInt(2.W)) - val user = Input(UInt(userW.W)) -} - -/** AXI4 read address base interface - * - * Defines the mandatory signals in the interface - * - * @param addrW the width of the ARADDR signal in bits - * @param idW the width of the ARID signal in bits, defaults to 0 - * @param userW the width of the ARUSER signal in bits, defaults to 0 - */ -class RA(val addrW: Int, val idW: Int = 0, val userW: Int = 0) extends Bundle { - require(addrW > 0, "the address width must be a positive integer") - require(idW >= 0, "the id width must be a non-negative integer") - require(userW >= 0, "the user with must be a non-negative integer") - val id = Output(UInt(idW.W)) - val addr = Output(UInt(addrW.W)) - val len = Output(UInt(8.W)) - val size = Output(UInt(3.W)) - val burst = Output(UInt(2.W)) - val lock = Output(Bool()) - val cache = Output(UInt(4.W)) - val prot = Output(UInt(3.W)) - val qos = Output(UInt(4.W)) - val region = Output(UInt(4.W)) - val user = Output(UInt(userW.W)) -} - -/** AXI4 read data base interface - * - * Defines the mandatory signals in the interface - * - * @param dataW the width of the RDATA signal in bits - * @param idW the width of the RID signal in bits, defaults to 0 - * @param userW the width of the RUSER signal in bits, defaults to 0 - */ -class RD(val dataW: Int, val idW: Int = 0, val userW: Int = 0) extends Bundle { - require(isPow2(dataW / 8), "the data width must be a power of 2 multiple of bytes") - require(idW >= 0, "the id width must be a non-negative integer") - require(userW >= 0, "the user with must be a non-negative integer") - val id = Input(UInt(idW.W)) - val data = Input(UInt(dataW.W)) - val resp = Input(UInt(2.W)) - val last = Input(Bool()) - val user = Input(UInt(userW.W)) -} - -/** AXI4 master interface - * - * Components meant to use this interface should extend it - * - * @param addrW the width of the address signals in bits - * @param dataW the width of the data read/write signals in bits - * @param idW the width of the ID signals in bits, defaults to 0 - * @param userW the width of the user signals in bits, defaults to 0 - */ -class MasterInterface(val addrW: Int, val dataW: Int, val idW: Int = 0, val userW: Int = 0) extends Bundle { - /** Fields implementing each of the AXI channels - * - * [[aw]] is the write address channel - * [[dw]] is the write data channel - * [[wr]] is the write response channel - * [[ar]] is the read address channel - * [[dr]] is the read data channel - */ - val aw = Decoupled(new WA(addrW, idW, userW)) - val dw = Decoupled(new WD(dataW, userW)) - val wr = Flipped(Decoupled(Flipped(new WR(idW, userW)))) - val ar = Decoupled(new RA(addrW, idW, userW)) - val dr = Flipped(Decoupled(Flipped(new RD(dataW, idW, userW)))) -} - -/** AXI4 slave interface - * - * Components meant to use this interface should extend it - * - * @param addrW the width of the address signals in bits - * @param dataW the width of the data read/write signals in bits - * @param idW the width of the ID signals in bits, defaults to 0 - * @param userW the width of the user signals in bits, defaults to 0 - */ -class SlaveInterface(val addrW: Int, val dataW: Int, val idW: Int = 0, val userW: Int = 0) extends Bundle { - /** Fields implementing each of the AXI channels - * - * [[aw]] is the write address channel - * [[dw]] is the write data channel - * [[wr]] is the write response channel - * [[ar]] is the read address channel - * [[dr]] is the read data channel - */ - val aw = Flipped(Decoupled(new WA(addrW, idW, userW))) - val dw = Flipped(Decoupled(new WD(dataW, userW))) - val wr = Flipped(Flipped(Decoupled(Flipped(new WR(idW, userW))))) - val ar = Flipped(Decoupled(new RA(addrW, idW, userW))) - val dr = Flipped(Flipped(Decoupled(Flipped(new RD(dataW, idW, userW))))) -} +/** + * Author: Hans Jakob Damsgaard, hansjakobdamsgaard@gmail.com + * + * Purpose: Implementation of a testing framework for AXI4-compliant devices. + * + * Content: Interface definitions for AXI4. +*/ + +package axi4 + +import chisel3._ +import chisel3.util._ + +/** AXI4 write address interface + * + * @param addrW the width of the AWADDR signal in bits + * @param idW the width of the AWID signal in bits, defaults to 0 + * @param userW the width of the AWUSER signal in bits, defaults to 0 + */ +class WA(val addrW: Int, val idW: Int = 0, val userW: Int = 0) extends Bundle { + require(addrW > 0, "the address width must be a positive integer") + require(idW >= 0, "the id width must be a non-negative integer") + require(userW >= 0, "the user with must be a non-negative integer") + val id = Output(UInt(idW.W)) + val addr = Output(UInt(addrW.W)) + val len = Output(UInt(8.W)) + val size = Output(UInt(3.W)) + val burst = Output(UInt(2.W)) + val lock = Output(Bool()) + val cache = Output(UInt(4.W)) + val prot = Output(UInt(3.W)) + val qos = Output(UInt(4.W)) + val region = Output(UInt(4.W)) + val user = Output(UInt(userW.W)) +} + +/** AXI4 write data interface + * + * @param dataW the width of the WDATA signal in bits + * @param userW the width of the WUSER signal in bits, defaults to 0 + */ +class WD(val dataW: Int, val userW: Int = 0) extends Bundle { + require(dataW > 0, "the data width must be a positive integer") + require(isPow2(dataW / 8), "the data width must be a power of 2 multiple of bytes") + require(userW >= 0, "the user with must be a non-negative integer") + val data = Output(UInt(dataW.W)) + val strb = Output(UInt((dataW/8).W)) + val last = Output(Bool()) + val user = Output(UInt(userW.W)) +} + +/** AXI4 write response interface + * + * @param idW the width of the BID signal in bits, defaults to 0 + * @param userW the width of the BUSER signal in bits, defaults to 0 + */ +class WR(val idW: Int = 0, val userW: Int = 0) extends Bundle { + require(idW >= 0, "the id width must be a non-negative integer") + require(userW >= 0, "the user with must be a non-negative integer") + val id = Input(UInt(idW.W)) + val resp = Input(UInt(2.W)) + val user = Input(UInt(userW.W)) +} + +/** AXI4 read address interface + * + * @param addrW the width of the ARADDR signal in bits + * @param idW the width of the ARID signal in bits, defaults to 0 + * @param userW the width of the ARUSER signal in bits, defaults to 0 + */ +class RA(val addrW: Int, val idW: Int = 0, val userW: Int = 0) extends Bundle { + require(addrW > 0, "the address width must be a positive integer") + require(idW >= 0, "the id width must be a non-negative integer") + require(userW >= 0, "the user with must be a non-negative integer") + val id = Output(UInt(idW.W)) + val addr = Output(UInt(addrW.W)) + val len = Output(UInt(8.W)) + val size = Output(UInt(3.W)) + val burst = Output(UInt(2.W)) + val lock = Output(Bool()) + val cache = Output(UInt(4.W)) + val prot = Output(UInt(3.W)) + val qos = Output(UInt(4.W)) + val region = Output(UInt(4.W)) + val user = Output(UInt(userW.W)) +} + +/** AXI4 read data interface + * + * @param dataW the width of the RDATA signal in bits + * @param idW the width of the RID signal in bits, defaults to 0 + * @param userW the width of the RUSER signal in bits, defaults to 0 + */ +class RD(val dataW: Int, val idW: Int = 0, val userW: Int = 0) extends Bundle { + require(isPow2(dataW / 8), "the data width must be a power of 2 multiple of bytes") + require(idW >= 0, "the id width must be a non-negative integer") + require(userW >= 0, "the user with must be a non-negative integer") + val id = Input(UInt(idW.W)) + val data = Input(UInt(dataW.W)) + val resp = Input(UInt(2.W)) + val last = Input(Bool()) + val user = Input(UInt(userW.W)) +} + +/** AXI4 master interface + * + * @param addrW the width of the address signals in bits + * @param dataW the width of the data read/write signals in bits + * @param idW the width of the ID signals in bits, defaults to 0 + * @param userW the width of the user signals in bits, defaults to 0 + */ +class MasterInterface(val addrW: Int, val dataW: Int, val idW: Int = 0, val userW: Int = 0) extends Bundle { + /** Fields implementing each of the AXI channels + * + * [[aw]] is the write address channel + * [[dw]] is the write data channel + * [[wr]] is the write response channel + * [[ar]] is the read address channel + * [[dr]] is the read data channel + */ + val aw = Decoupled(new WA(addrW, idW, userW)) + val dw = Decoupled(new WD(dataW, userW)) + val wr = Flipped(Decoupled(Flipped(new WR(idW, userW)))) + val ar = Decoupled(new RA(addrW, idW, userW)) + val dr = Flipped(Decoupled(Flipped(new RD(dataW, idW, userW)))) +} + +/** AXI4 slave interface + * + * @param addrW the width of the address signals in bits + * @param dataW the width of the data read/write signals in bits + * @param idW the width of the ID signals in bits, defaults to 0 + * @param userW the width of the user signals in bits, defaults to 0 + */ +class SlaveInterface(val addrW: Int, val dataW: Int, val idW: Int = 0, val userW: Int = 0) extends Bundle { + /** Fields implementing each of the AXI channels + * + * [[aw]] is the write address channel + * [[dw]] is the write data channel + * [[wr]] is the write response channel + * [[ar]] is the read address channel + * [[dr]] is the read data channel + */ + val aw = Flipped(Decoupled(new WA(addrW, idW, userW))) + val dw = Flipped(Decoupled(new WD(dataW, userW))) + val wr = Flipped(Flipped(Decoupled(Flipped(new WR(idW, userW))))) + val ar = Flipped(Decoupled(new RA(addrW, idW, userW))) + val dr = Flipped(Flipped(Decoupled(Flipped(new RD(dataW, idW, userW))))) +} diff --git a/axi4/src/main/scala/axi4/AXI4FunctionalMaster.scala b/axi4/src/main/scala/axi4/FunctionalMaster.scala similarity index 100% rename from axi4/src/main/scala/axi4/AXI4FunctionalMaster.scala rename to axi4/src/main/scala/axi4/FunctionalMaster.scala diff --git a/axi4/src/main/scala/axi4/Master.scala b/axi4/src/main/scala/axi4/Master.scala index bbc8ed4..4a181c4 100644 --- a/axi4/src/main/scala/axi4/Master.scala +++ b/axi4/src/main/scala/axi4/Master.scala @@ -1,24 +1,24 @@ -/** - * Author: Hans Jakob Damsgaard, hansjakobdamsgaard@gmail.com - * - * Purpose: Implementation of a testing framework for AXI4-compliant devices. - * - * Content: An empty AXI master with relevant interface. -*/ - -package axi4 - -import chisel3._ - -/** AXI4 master - * - * An empty class representing an AXI master - * - * @param addrW the width of the address signals in bits - * @param dataW the width of the data read/write signals in bits - * @param idW the width of the ID signals in bits, defaults to 0 - * @param userW the width of the user signals in bits, defaults to 0 - */ -abstract class Master(val addrW: Int, val dataW: Int, val idW: Int = 0, val userW: Int = 0) extends Module { - val io = IO(new MasterInterface(addrW, dataW, idW, userW)) -} +/** + * Author: Hans Jakob Damsgaard, hansjakobdamsgaard@gmail.com + * + * Purpose: Implementation of a testing framework for AXI4-compliant devices. + * + * Content: An empty AXI master with relevant interface. +*/ + +package axi4 + +import chisel3._ + +/** AXI4 master + * + * An empty class representing an AXI master + * + * @param addrW the width of the address signals in bits + * @param dataW the width of the data read/write signals in bits + * @param idW the width of the ID signals in bits, defaults to 0 + * @param userW the width of the user signals in bits, defaults to 0 + */ +abstract class Master(val addrW: Int, val dataW: Int, val idW: Int = 0, val userW: Int = 0) extends Module { + val io = IO(new MasterInterface(addrW, dataW, idW, userW)) +} diff --git a/axi4/src/main/scala/axi4/Slave.scala b/axi4/src/main/scala/axi4/Slave.scala index d892adb..f1a550b 100644 --- a/axi4/src/main/scala/axi4/Slave.scala +++ b/axi4/src/main/scala/axi4/Slave.scala @@ -1,24 +1,24 @@ -/** - * Author: Hans Jakob Damsgaard, hansjakobdamsgaard@gmail.com - * - * Purpose: Implementation of a testing framework for AXI4-compliant devices. - * - * Content: An empty AXI slave with relevant interface. -*/ - -package axi4 - -import chisel3._ - -/** AXI4 slave - * - * An empty class representing an AXI slave - * - * @param addrW the width of the address signals in bits - * @param dataW the width of the data read/write signals in bits - * @param idW the width of the ID signals in bits, defaults to 0 - * @param userW the width of the user signals in bits, defaults to 0 - */ -abstract class Slave(val addrW: Int, val dataW: Int, val idW: Int = 0, val userW: Int = 0) extends Module { - val io = IO(new SlaveInterface(addrW, dataW, idW, userW)) -} +/** + * Author: Hans Jakob Damsgaard, hansjakobdamsgaard@gmail.com + * + * Purpose: Implementation of a testing framework for AXI4-compliant devices. + * + * Content: An empty AXI slave with relevant interface. +*/ + +package axi4 + +import chisel3._ + +/** AXI4 slave + * + * An empty class representing an AXI slave + * + * @param addrW the width of the address signals in bits + * @param dataW the width of the data read/write signals in bits + * @param idW the width of the ID signals in bits, defaults to 0 + * @param userW the width of the user signals in bits, defaults to 0 + */ +abstract class Slave(val addrW: Int, val dataW: Int, val idW: Int = 0, val userW: Int = 0) extends Module { + val io = IO(new SlaveInterface(addrW, dataW, idW, userW)) +} diff --git a/axi4/src/main/scala/axi4/Transaction.scala b/axi4/src/main/scala/axi4/Transaction.scala index f3484d3..1ef6299 100644 --- a/axi4/src/main/scala/axi4/Transaction.scala +++ b/axi4/src/main/scala/axi4/Transaction.scala @@ -1,151 +1,151 @@ -/** - * Author: Hans Jakob Damsgaard, hansjakobdamsgaard@gmail.com - * - * Purpose: Implementation of a testing framework for AXI4-compliant devices. - * - * Content: Transaction classes for functional masters and slaves. -*/ - -package axi4 - -import chisel3._ - -/** Transaction superclass */ -trait Transaction { - def complete: Boolean -} - -/** Write transaction - * - * @param addr start write address - * @param data list of data to write - * @param dataW slave's data bus width - * @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 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 - * @param qos optional QoS, defaults to 0 - * @param region optional region, defaults to 0 - */ -class WriteTransaction( - val addr: BigInt, - val data: Seq[BigInt], - dataW: Int, - val id: BigInt = 0, - val len: Int = 0, - val size: Int = 0, - val burst: UInt = BurstEncodings.Fixed, - val lock: Bool = LockEncodings.NormalAccess, - val cache: UInt = MemoryEncodings.DeviceNonbuf, - val prot: UInt = ProtectionEncodings.DataNsecUpriv, - val qos: UInt = 0.U, - val region: UInt = 0.U) extends Transaction { - private[this] val numBytes = 1 << size - private[this] val dtsize = numBytes * data.length - private[this] val lowerBoundary = (addr / dtsize) * dtsize - private[this] val upperBoundary = lowerBoundary + dtsize - private[this] val alignedAddress = ((addr / numBytes) * numBytes) - private[this] var aligned = addr == alignedAddress - private[this] var address = addr - private[this] var count = 0 - - private[this] var _addrSent = false - private[this] var _dataSent = false - - /** Getter and setter for [[addrSent]] */ - def addrSent = _addrSent - def addrSent_=(newValue: Boolean): Unit = _addrSent = newValue - - /** Getter and setter for [[dataSent]] */ - def dataSent = _dataSent - def dataSent_=(newValue: Boolean): Unit = _dataSent = newValue - - /** Get next (data, strb, last) tuple - * - * @return (data, strb, last) tuple - * - * @note has side effect on internal index count - */ - def next() = { - /** Strobe calculation */ - 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 >= 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 */ - if (burst != BurstEncodings.Fixed) { - if (aligned) { - address += numBytes - if (burst == BurstEncodings.Wrap) { - if (address >= upperBoundary) { - address = lowerBoundary - } - } - } else { - address += numBytes - aligned = true - } - } - count += 1 - - /** Return data to write */ - (data(count-1).U, strb, complete.B) - } - def complete = data.length == count -} - -/** Read transaction - * - * @param addr start read address - * @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 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 - * @param qos optional QoS, defaults to 0 - * @param region optional region, defaults to 0 - */ -class ReadTransaction( - val addr: BigInt, - val id: BigInt = 0, - val len: Int = 0, - val size: Int = 0, - val burst: UInt = BurstEncodings.Fixed, - val lock: Bool = LockEncodings.NormalAccess, - val cache: UInt = MemoryEncodings.DeviceNonbuf, - val prot: UInt = ProtectionEncodings.DataNsecUpriv, - val qos: UInt = 0.U, - val region: UInt = 0.U) extends Transaction { - var data = Seq[BigInt]() - - private[this] var _addrSent = false - - /** Getter and setter for [[addrSent]] */ - def addrSent = _addrSent - def addrSent_=(newValue: Boolean): Unit = _addrSent = newValue - - /** Add element to data sequence - * - * @param v value to add - * - * @note has side effect on internal data sequence - */ - def add(v: BigInt) = { - data = data :+ v - } - def complete = data.length == (len + 1) -} - -/** Transaction response - * - * @param resp transaction response - * @param id optional id - */ -case class Response(val resp: UInt, val id: BigInt = 0) +/** + * Author: Hans Jakob Damsgaard, hansjakobdamsgaard@gmail.com + * + * Purpose: Implementation of a testing framework for AXI4-compliant devices. + * + * Content: Transaction classes for functional masters and slaves. +*/ + +package axi4 + +import chisel3._ + +/** Transaction superclass */ +trait Transaction { + def complete: Boolean +} + +/** Write transaction + * + * @param addr start write address + * @param data list of data to write + * @param dataW slave's data bus width + * @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 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 + * @param qos optional QoS, defaults to 0 + * @param region optional region, defaults to 0 + */ +class WriteTransaction( + val addr: BigInt, + val data: Seq[BigInt], + dataW: Int, + val id: BigInt = 0, + val len: Int = 0, + val size: Int = 0, + val burst: UInt = BurstEncodings.Fixed, + val lock: Bool = LockEncodings.NormalAccess, + val cache: UInt = MemoryEncodings.DeviceNonbuf, + val prot: UInt = ProtectionEncodings.DataNsecUpriv, + val qos: UInt = 0.U, + val region: UInt = 0.U) extends Transaction { + private[this] val numBytes = 1 << size + private[this] val dtsize = numBytes * data.length + private[this] val lowerBoundary = (addr / dtsize) * dtsize + private[this] val upperBoundary = lowerBoundary + dtsize + private[this] val alignedAddress = ((addr / numBytes) * numBytes) + private[this] var aligned = addr == alignedAddress + private[this] var address = addr + private[this] var count = 0 + + private[this] var _addrSent = false + private[this] var _dataSent = false + + /** Getter and setter for [[addrSent]] */ + def addrSent = _addrSent + def addrSent_=(newValue: Boolean): Unit = _addrSent = newValue + + /** Getter and setter for [[dataSent]] */ + def dataSent = _dataSent + def dataSent_=(newValue: Boolean): Unit = _dataSent = newValue + + /** Get next (data, strb, last) tuple + * + * @return (data, strb, last) tuple + * + * @note has side effect on internal index count + */ + def next() = { + /** Strobe calculation */ + 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 >= 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 */ + if (burst != BurstEncodings.Fixed) { + if (aligned) { + address += numBytes + if (burst == BurstEncodings.Wrap) { + if (address >= upperBoundary) { + address = lowerBoundary + } + } + } else { + address += numBytes + aligned = true + } + } + count += 1 + + /** Return data to write */ + (data(count-1).U, strb, complete.B) + } + def complete = data.length == count +} + +/** Read transaction + * + * @param addr start read address + * @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 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 + * @param qos optional QoS, defaults to 0 + * @param region optional region, defaults to 0 + */ +class ReadTransaction( + val addr: BigInt, + val id: BigInt = 0, + val len: Int = 0, + val size: Int = 0, + val burst: UInt = BurstEncodings.Fixed, + val lock: Bool = LockEncodings.NormalAccess, + val cache: UInt = MemoryEncodings.DeviceNonbuf, + val prot: UInt = ProtectionEncodings.DataNsecUpriv, + val qos: UInt = 0.U, + val region: UInt = 0.U) extends Transaction { + var data = Seq[BigInt]() + + private[this] var _addrSent = false + + /** Getter and setter for [[addrSent]] */ + def addrSent = _addrSent + def addrSent_=(newValue: Boolean): Unit = _addrSent = newValue + + /** Add element to data sequence + * + * @param v value to add + * + * @note has side effect on internal data sequence + */ + def add(v: BigInt) = { + data = data :+ v + } + def complete = data.length == (len + 1) +} + +/** Transaction response + * + * @param resp transaction response + * @param id optional id + */ +case class Response(val resp: UInt, val id: BigInt = 0) diff --git a/axi4/src/main/scala/package.scala b/axi4/src/main/scala/package.scala new file mode 100644 index 0000000..9d6ae2c --- /dev/null +++ b/axi4/src/main/scala/package.scala @@ -0,0 +1,60 @@ +/** + * Author: Hans Jakob Damsgaard, hansjakobdamsgaard@gmail.com + * + * Purpose: Implementation of a testing framework for AXI4-compliant devices. + * + * Content: Package object for full AXI4. +*/ + +import chisel3._ + +package object axi4 { + /** AXI4 burst encodings */ + object BurstEncodings { + val Fixed = "b00".U + val Incr = "b01".U + val Wrap = "b10".U + } + + /** AXI lock encodings */ + object LockEncodings { + val NormalAccess = false.B + val ExclusiveAccess = true.B + } + + /** AXI4 memory encodings */ + object MemoryEncodings { + val DeviceNonbuf = "b0000".U + val DeviceBuf = "b0001".U + val NormalNonbuf = "b0010".U + val NormalBuf = "b0011".U + val WtNoalloc = "b0110".U + val WtReadalloc = "b0110".U + val WtWritealloc = "b1110".U + val WtRwalloc = "b1110".U + val WbNoalloc = "b0111".U + val WbReadalloc = "b0111".U + val WbWritealloc = "b1111".U + val WbRwalloc = "b1111".U + } + + /** AXI4 protection encodings */ + object ProtectionEncodings { + val DataSecUpriv = "b000".U + val DataSecPriv = "b001".U + val DataNsecUpriv = "b010".U + val DataNsecPriv = "b011".U + val InstrSecUpriv = "b100".U + val InstrSecPriv = "b101".U + val InstrNsecUpriv = "b110".U + val InstrNsecPriv = "b111".U + } + + /** AXI4 response encodings */ + object ResponseEncodings { + val Okay = "b00".U + val Exokay = "b01".U + val Slverr = "b10".U + val Decerr = "b11".U + } +} From 04ebb1ff7da875faacdcb0a78b2dd8bb2fabd6fb Mon Sep 17 00:00:00 2001 From: hansemandse Date: Fri, 4 Dec 2020 22:10:47 +0100 Subject: [PATCH 03/10] more clean up --- axi4/.gitignore | 1 + axi4/src/main/scala/axi4/Bundles.scala | 151 ++++++++++++++++++ axi4/src/main/scala/axi4/Defs.scala | 149 ----------------- .../main/scala/axi4/FunctionalMaster.scala | 18 ++- axi4/src/main/scala/axi4/Interfaces.scala | 58 +++++++ axi4/src/main/scala/axi4/Transaction.scala | 10 +- 6 files changed, 229 insertions(+), 158 deletions(-) create mode 100644 axi4/src/main/scala/axi4/Bundles.scala delete mode 100644 axi4/src/main/scala/axi4/Defs.scala create mode 100644 axi4/src/main/scala/axi4/Interfaces.scala diff --git a/axi4/.gitignore b/axi4/.gitignore index 7a2ef20..6dd29ed 100644 --- a/axi4/.gitignore +++ b/axi4/.gitignore @@ -1,5 +1,6 @@ # Ignore metals-related temp files .bloop/ +.ionide/ .metals/ .vscode/ project/.bloop/ diff --git a/axi4/src/main/scala/axi4/Bundles.scala b/axi4/src/main/scala/axi4/Bundles.scala new file mode 100644 index 0000000..0dcec64 --- /dev/null +++ b/axi4/src/main/scala/axi4/Bundles.scala @@ -0,0 +1,151 @@ +/** + * Author: Hans Jakob Damsgaard, hansjakobdamsgaard@gmail.com + * + * Purpose: Implementation of a testing framework for AXI4-compliant devices. + * + * Content: Bundle definitions for AXI4. +*/ + +package axi4 + +import chisel3._ +import chisel3.util.isPow2 + +/** AXI4 write address + * + * @param addrW the width of the AWADDR signal in bits + * @param idW the width of the AWID signal in bits + * @param userW the width of the AWUSER signal in bits + */ +class WA(val addrW: Int, val idW: Int, val userW: Int) extends Bundle { + require(addrW > 0, "the address width must be a positive integer") + require(idW >= 0, "the id width must be a non-negative integer") + require(userW >= 0, "the user with must be a non-negative integer") + val id = UInt(idW.W) + val addr = UInt(addrW.W) + val len = UInt(8.W) + val size = UInt(3.W) + val burst = UInt(2.W) + val lock = Bool() + val cache = UInt(4.W) + val prot = UInt(3.W) + val qos = UInt(4.W) + val region = UInt(4.W) + val user = UInt(userW.W) +} +object WA { + /** Alternative constructor + * + * @param addrW the width of the AWADDR signal in bits + * @param idW the width of the AWID signal in bits, defaults to 0 + * @param userW the width of the AWUSER signal in bits, defaults to 0 + * @return + */ + def apply(addrW: Int, idW: Int = 0, userW: Int = 0) = new WA(addrW, idW, userW) +} + +/** AXI4 write data + * + * @param dataW the width of the WDATA signal in bits + * @param userW the width of the WUSER signal in bits + */ +class WD(val dataW: Int, val userW: Int) extends Bundle { + require(dataW > 0, "the data width must be a positive integer") + require(isPow2(dataW / 8), "the data width must be a power of 2 multiple of bytes") + require(userW >= 0, "the user with must be a non-negative integer") + val data = UInt(dataW.W) + val strb = UInt((dataW/8).W) + val last = Bool() + val user = UInt(userW.W) +} +object WD { + /** Alternative constructor + * + * @param dataW the width of the WDATA signal in bits + * @param userW the width of the WUSER signal in bits, defaults to 0 + * @return an unitialized WD object + */ + def apply(dataW: Int, userW: Int = 0) = new WD(dataW, userW) +} + +/** AXI4 write response + * + * @param idW the width of the BID signal in bits + * @param userW the width of the BUSER signal in bits + */ +class WR(val idW: Int, val userW: Int) extends Bundle { + require(idW >= 0, "the id width must be a non-negative integer") + require(userW >= 0, "the user with must be a non-negative integer") + val id = UInt(idW.W) + val resp = UInt(2.W) + val user = UInt(userW.W) +} +object WR { + /** Alternative constructor + * + * @param idW the width of the BID signal in bits, defaults to 0 + * @param userW the width of the BUSER signal in bits, defaults to 0 + * @return an unitialized WR object + */ + def apply(idW: Int = 0, userW: Int = 0) = new WR(idW, userW) +} + +/** AXI4 read address + * + * @param addrW the width of the ARADDR signal in bits + * @param idW the width of the ARID signal in bits + * @param userW the width of the ARUSER signal in bits + */ +class RA(val addrW: Int, val idW: Int, val userW: Int) extends Bundle { + require(addrW > 0, "the address width must be a positive integer") + require(idW >= 0, "the id width must be a non-negative integer") + require(userW >= 0, "the user with must be a non-negative integer") + val id = UInt(idW.W) + val addr = UInt(addrW.W) + val len = UInt(8.W) + val size = UInt(3.W) + val burst = UInt(2.W) + val lock = Bool() + val cache = UInt(4.W) + val prot = UInt(3.W) + val qos = UInt(4.W) + val region = UInt(4.W) + val user = UInt(userW.W) +} +object RA { + /** Alternative constructor + * + * @param addrW the width of the ARADDR signal in bits + * @param idW the width of the ARID signal in bits, defaults to 0 + * @param userW the width of the ARUSER signal in bits, defaults to 0 + * @return an unitialized RA object + */ + def apply(addrW: Int, idW: Int = 0, userW: Int = 0) = new RA(addrW, idW, userW) +} + +/** AXI4 read data + * + * @param dataW the width of the RDATA signal in bits + * @param idW the width of the RID signal in bits + * @param userW the width of the RUSER signal in bits + */ +class RD(val dataW: Int, val idW: Int, val userW: Int) extends Bundle { + require(isPow2(dataW / 8), "the data width must be a power of 2 multiple of bytes") + require(idW >= 0, "the id width must be a non-negative integer") + require(userW >= 0, "the user with must be a non-negative integer") + val id = UInt(idW.W) + val data = UInt(dataW.W) + val resp = UInt(2.W) + val last = Bool() + val user = UInt(userW.W) +} +object RD { + /** Alternative constructor + * + * @param dataW the width of the RDATA signal in bits + * @param idW the width of the RID signal in bits, defaults to 0 + * @param userW the width of the RUSER signal in bits, defaults to 0 + * @return an uninitialized RD object + */ + def apply(dataW: Int, idW: Int = 0, userW: Int = 0) = new RD(dataW, idW, userW) +} diff --git a/axi4/src/main/scala/axi4/Defs.scala b/axi4/src/main/scala/axi4/Defs.scala deleted file mode 100644 index 49df6ec..0000000 --- a/axi4/src/main/scala/axi4/Defs.scala +++ /dev/null @@ -1,149 +0,0 @@ -/** - * Author: Hans Jakob Damsgaard, hansjakobdamsgaard@gmail.com - * - * Purpose: Implementation of a testing framework for AXI4-compliant devices. - * - * Content: Interface definitions for AXI4. -*/ - -package axi4 - -import chisel3._ -import chisel3.util._ - -/** AXI4 write address interface - * - * @param addrW the width of the AWADDR signal in bits - * @param idW the width of the AWID signal in bits, defaults to 0 - * @param userW the width of the AWUSER signal in bits, defaults to 0 - */ -class WA(val addrW: Int, val idW: Int = 0, val userW: Int = 0) extends Bundle { - require(addrW > 0, "the address width must be a positive integer") - require(idW >= 0, "the id width must be a non-negative integer") - require(userW >= 0, "the user with must be a non-negative integer") - val id = Output(UInt(idW.W)) - val addr = Output(UInt(addrW.W)) - val len = Output(UInt(8.W)) - val size = Output(UInt(3.W)) - val burst = Output(UInt(2.W)) - val lock = Output(Bool()) - val cache = Output(UInt(4.W)) - val prot = Output(UInt(3.W)) - val qos = Output(UInt(4.W)) - val region = Output(UInt(4.W)) - val user = Output(UInt(userW.W)) -} - -/** AXI4 write data interface - * - * @param dataW the width of the WDATA signal in bits - * @param userW the width of the WUSER signal in bits, defaults to 0 - */ -class WD(val dataW: Int, val userW: Int = 0) extends Bundle { - require(dataW > 0, "the data width must be a positive integer") - require(isPow2(dataW / 8), "the data width must be a power of 2 multiple of bytes") - require(userW >= 0, "the user with must be a non-negative integer") - val data = Output(UInt(dataW.W)) - val strb = Output(UInt((dataW/8).W)) - val last = Output(Bool()) - val user = Output(UInt(userW.W)) -} - -/** AXI4 write response interface - * - * @param idW the width of the BID signal in bits, defaults to 0 - * @param userW the width of the BUSER signal in bits, defaults to 0 - */ -class WR(val idW: Int = 0, val userW: Int = 0) extends Bundle { - require(idW >= 0, "the id width must be a non-negative integer") - require(userW >= 0, "the user with must be a non-negative integer") - val id = Input(UInt(idW.W)) - val resp = Input(UInt(2.W)) - val user = Input(UInt(userW.W)) -} - -/** AXI4 read address interface - * - * @param addrW the width of the ARADDR signal in bits - * @param idW the width of the ARID signal in bits, defaults to 0 - * @param userW the width of the ARUSER signal in bits, defaults to 0 - */ -class RA(val addrW: Int, val idW: Int = 0, val userW: Int = 0) extends Bundle { - require(addrW > 0, "the address width must be a positive integer") - require(idW >= 0, "the id width must be a non-negative integer") - require(userW >= 0, "the user with must be a non-negative integer") - val id = Output(UInt(idW.W)) - val addr = Output(UInt(addrW.W)) - val len = Output(UInt(8.W)) - val size = Output(UInt(3.W)) - val burst = Output(UInt(2.W)) - val lock = Output(Bool()) - val cache = Output(UInt(4.W)) - val prot = Output(UInt(3.W)) - val qos = Output(UInt(4.W)) - val region = Output(UInt(4.W)) - val user = Output(UInt(userW.W)) -} - -/** AXI4 read data interface - * - * @param dataW the width of the RDATA signal in bits - * @param idW the width of the RID signal in bits, defaults to 0 - * @param userW the width of the RUSER signal in bits, defaults to 0 - */ -class RD(val dataW: Int, val idW: Int = 0, val userW: Int = 0) extends Bundle { - require(isPow2(dataW / 8), "the data width must be a power of 2 multiple of bytes") - require(idW >= 0, "the id width must be a non-negative integer") - require(userW >= 0, "the user with must be a non-negative integer") - val id = Input(UInt(idW.W)) - val data = Input(UInt(dataW.W)) - val resp = Input(UInt(2.W)) - val last = Input(Bool()) - val user = Input(UInt(userW.W)) -} - -/** AXI4 master interface - * - * @param addrW the width of the address signals in bits - * @param dataW the width of the data read/write signals in bits - * @param idW the width of the ID signals in bits, defaults to 0 - * @param userW the width of the user signals in bits, defaults to 0 - */ -class MasterInterface(val addrW: Int, val dataW: Int, val idW: Int = 0, val userW: Int = 0) extends Bundle { - /** Fields implementing each of the AXI channels - * - * [[aw]] is the write address channel - * [[dw]] is the write data channel - * [[wr]] is the write response channel - * [[ar]] is the read address channel - * [[dr]] is the read data channel - */ - val aw = Decoupled(new WA(addrW, idW, userW)) - val dw = Decoupled(new WD(dataW, userW)) - val wr = Flipped(Decoupled(Flipped(new WR(idW, userW)))) - val ar = Decoupled(new RA(addrW, idW, userW)) - val dr = Flipped(Decoupled(Flipped(new RD(dataW, idW, userW)))) -} - -/** AXI4 slave interface - * - * @param addrW the width of the address signals in bits - * @param dataW the width of the data read/write signals in bits - * @param idW the width of the ID signals in bits, defaults to 0 - * @param userW the width of the user signals in bits, defaults to 0 - */ -class SlaveInterface(val addrW: Int, val dataW: Int, val idW: Int = 0, val userW: Int = 0) extends Bundle { - /** Fields implementing each of the AXI channels - * - * [[aw]] is the write address channel - * [[dw]] is the write data channel - * [[wr]] is the write response channel - * [[ar]] is the read address channel - * [[dr]] is the read data channel - */ - val aw = Flipped(Decoupled(new WA(addrW, idW, userW))) - val dw = Flipped(Decoupled(new WD(dataW, userW))) - val wr = Flipped(Flipped(Decoupled(Flipped(new WR(idW, userW))))) - val ar = Flipped(Decoupled(new RA(addrW, idW, userW))) - val dr = Flipped(Flipped(Decoupled(Flipped(new RD(dataW, idW, userW))))) -} diff --git a/axi4/src/main/scala/axi4/FunctionalMaster.scala b/axi4/src/main/scala/axi4/FunctionalMaster.scala index 4712e06..e61a22e 100644 --- a/axi4/src/main/scala/axi4/FunctionalMaster.scala +++ b/axi4/src/main/scala/axi4/FunctionalMaster.scala @@ -54,7 +54,7 @@ class FunctionalMaster[T <: Slave](dut: T) { /** Default values on all signals */ // Address write - aw.bits.id.poke(0.U) + if (aw.bits.idW > 0) aw.bits.id.poke(0.U) aw.bits.addr.poke(0.U) aw.bits.len.poke(0.U) aw.bits.size.poke(0.U) @@ -64,19 +64,21 @@ class FunctionalMaster[T <: Slave](dut: T) { aw.bits.prot.poke(ProtectionEncodings.DataNsecUpriv) aw.bits.qos.poke(0.U) aw.bits.region.poke(0.U) + if (aw.bits.userW > 0) aw.bits.user.poke(0.U) aw.valid.poke(false.B) // Data write dw.bits.data.poke(0.U) dw.bits.strb.poke(0.U) dw.bits.last.poke(false.B) + if (dw.bits.userW > 0) dw.bits.user.poke(0.U) dw.valid.poke(false.B) // Write response wr.ready.poke(false.B) // Address read - ar.bits.id.poke(0.U) + if (ar.bits.idW > 0) ar.bits.id.poke(0.U) ar.bits.addr.poke(0.U) ar.bits.len.poke(0.U) ar.bits.size.poke(0.U) @@ -86,6 +88,7 @@ class FunctionalMaster[T <: Slave](dut: T) { ar.bits.prot.poke(ProtectionEncodings.DataNsecUpriv) ar.bits.qos.poke(0.U) ar.bits.region.poke(0.U) + if (ar.bits.userW > 0) ar.bits.user.poke(0.U) ar.valid.poke(false.B) // Data read @@ -122,7 +125,7 @@ class FunctionalMaster[T <: Slave](dut: T) { /** Write address to slave */ aw.valid.poke(true.B) - aw.bits.id.poke(head.id.U) + if (aw.bits.idW > 0) aw.bits.id.poke(head.id.U) aw.bits.addr.poke(head.addr.U) aw.bits.len.poke(head.len.U) aw.bits.size.poke(head.size.U) @@ -132,6 +135,7 @@ class FunctionalMaster[T <: Slave](dut: T) { aw.bits.prot.poke(head.prot) aw.bits.qos.poke(head.qos) aw.bits.region.poke(head.region) + if (aw.bits.userW > 0) aw.bits.user.poke(head.user) while (!aw.ready.peek.litToBoolean) { clk.step() } @@ -163,11 +167,12 @@ class FunctionalMaster[T <: Slave](dut: T) { /** Write data to slave */ dw.valid.poke(true.B) while (!head.complete) { - val (data, strb, last) = head.next + val (data, strb, last, user) = 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) + if (dw.bits.userW > 0) dw.bits.user.poke(user) while (!dw.ready.peek.litToBoolean) { clk.step() } @@ -202,7 +207,7 @@ class FunctionalMaster[T <: Slave](dut: T) { while (!wr.valid.peek.litToBoolean) { clk.step() } - responses = responses :+ (new Response(wr.bits.resp.peek, wr.bits.id.peek.litValue)) + responses = responses :+ (new Response(wr.bits.resp.peek, if (wr.bits.idW > 0) wr.bits.id.peek.litValue else 0)) wr.ready.poke(false.B) /** Update queue */ @@ -225,7 +230,7 @@ class FunctionalMaster[T <: Slave](dut: T) { /** Write address to slave */ ar.valid.poke(true.B) - ar.bits.id.poke(head.id.U) + if (ar.bits.idW > 0) ar.bits.id.poke(head.id.U) ar.bits.addr.poke(head.addr.U) ar.bits.len.poke(head.len.U) ar.bits.size.poke(head.size.U) @@ -235,6 +240,7 @@ class FunctionalMaster[T <: Slave](dut: T) { ar.bits.prot.poke(head.prot) ar.bits.qos.poke(head.qos) ar.bits.region.poke(head.region) + if (ar.bits.userW > 0) ar.bits.user.poke(head.user) while (!ar.ready.peek.litToBoolean) { clk.step() } diff --git a/axi4/src/main/scala/axi4/Interfaces.scala b/axi4/src/main/scala/axi4/Interfaces.scala new file mode 100644 index 0000000..950c83b --- /dev/null +++ b/axi4/src/main/scala/axi4/Interfaces.scala @@ -0,0 +1,58 @@ +/** + * Author: Hans Jakob Damsgaard, hansjakobdamsgaard@gmail.com + * + * Purpose: Implementation of a testing framework for AXI4-compliant devices. + * + * Content: Interface definitions for AXI4. +*/ + +package axi4 + +import chisel3._ +import chisel3.util.Decoupled + +/** AXI4 master interface + * + * @param addrW the width of the address signals in bits + * @param dataW the width of the data read/write signals in bits + * @param idW the width of the ID signals in bits, defaults to 0 + * @param userW the width of the user signals in bits, defaults to 0 + */ +class MasterInterface(val addrW: Int, val dataW: Int, val idW: Int = 0, val userW: Int = 0) extends Bundle { + /** Fields implementing each of the AXI channels + * + * [[aw]] is the write address channel + * [[dw]] is the write data channel + * [[wr]] is the write response channel + * [[ar]] is the read address channel + * [[dr]] is the read data channel + */ + val aw = Decoupled(Output(WA(addrW, idW, userW))) //Decoupled(new WA(addrW, idW, userW)) + val dw = Decoupled(Output(WD(dataW, userW))) //Decoupled(new WD(dataW, userW)) + val wr = Flipped(Decoupled(Output(WR(idW, userW)))) //Flipped(Decoupled(Flipped(new WR(idW, userW)))) + val ar = Decoupled(RA(addrW, idW, userW)) //Decoupled(new RA(addrW, idW, userW)) + val dr = Flipped(Decoupled(Output(RD(dataW, idW, userW)))) //Flipped(Decoupled(Flipped(new RD(dataW, idW, userW)))) +} + +/** AXI4 slave interface + * + * @param addrW the width of the address signals in bits + * @param dataW the width of the data read/write signals in bits + * @param idW the width of the ID signals in bits, defaults to 0 + * @param userW the width of the user signals in bits, defaults to 0 + */ +class SlaveInterface(val addrW: Int, val dataW: Int, val idW: Int = 0, val userW: Int = 0) extends Bundle { + /** Fields implementing each of the AXI channels + * + * [[aw]] is the write address channel + * [[dw]] is the write data channel + * [[wr]] is the write response channel + * [[ar]] is the read address channel + * [[dr]] is the read data channel + */ + val aw = Flipped(Decoupled(Output(WA(addrW, idW, userW)))) //Flipped(Decoupled(new WA(addrW, idW, userW))) + val dw = Flipped(Decoupled(Output(WD(dataW, userW)))) //Flipped(Decoupled(new WD(dataW, userW))) + val wr = Decoupled(Output(WR(idW, userW))) //Flipped(Flipped(Decoupled(Flipped(new WR(idW, userW))))) + val ar = Flipped(Decoupled(Output(RA(addrW, idW, userW)))) //Flipped(Decoupled(new RA(addrW, idW, userW))) + val dr = Decoupled(Output(RD(dataW, idW, userW))) //Flipped(Flipped(Decoupled(Flipped(new RD(dataW, idW, userW))))) +} diff --git a/axi4/src/main/scala/axi4/Transaction.scala b/axi4/src/main/scala/axi4/Transaction.scala index 1ef6299..dfb58a6 100644 --- a/axi4/src/main/scala/axi4/Transaction.scala +++ b/axi4/src/main/scala/axi4/Transaction.scala @@ -29,6 +29,7 @@ trait Transaction { * @param prot optional protection type, defaults to non-secure unprivileged data access * @param qos optional QoS, defaults to 0 * @param region optional region, defaults to 0 + * @param user optional user, defaults to 0 */ class WriteTransaction( val addr: BigInt, @@ -42,7 +43,8 @@ class WriteTransaction( val cache: UInt = MemoryEncodings.DeviceNonbuf, val prot: UInt = ProtectionEncodings.DataNsecUpriv, val qos: UInt = 0.U, - val region: UInt = 0.U) extends Transaction { + val region: UInt = 0.U, + val user: UInt = 0.U) extends Transaction { private[this] val numBytes = 1 << size private[this] val dtsize = numBytes * data.length private[this] val lowerBoundary = (addr / dtsize) * dtsize @@ -94,7 +96,7 @@ class WriteTransaction( count += 1 /** Return data to write */ - (data(count-1).U, strb, complete.B) + (data(count-1).U, strb, complete.B, user) } def complete = data.length == count } @@ -111,6 +113,7 @@ class WriteTransaction( * @param prot optional protection type, defaults to non-secure unprivileged data access * @param qos optional QoS, defaults to 0 * @param region optional region, defaults to 0 + * @param user optional user, defaults to 0 */ class ReadTransaction( val addr: BigInt, @@ -122,7 +125,8 @@ class ReadTransaction( val cache: UInt = MemoryEncodings.DeviceNonbuf, val prot: UInt = ProtectionEncodings.DataNsecUpriv, val qos: UInt = 0.U, - val region: UInt = 0.U) extends Transaction { + val region: UInt = 0.U, + val user: UInt = 0.U) extends Transaction { var data = Seq[BigInt]() private[this] var _addrSent = false From 01ec8a6b7b31897a4975ccb6474cd830e1b46a9c Mon Sep 17 00:00:00 2001 From: hansemandse Date: Sat, 5 Dec 2020 17:51:49 +0100 Subject: [PATCH 04/10] add default values --- axi4/src/main/scala/axi4/Bundles.scala | 69 ++++++++++++++++++- .../main/scala/axi4/FunctionalMaster.scala | 35 ++-------- 2 files changed, 74 insertions(+), 30 deletions(-) diff --git a/axi4/src/main/scala/axi4/Bundles.scala b/axi4/src/main/scala/axi4/Bundles.scala index 0dcec64..cbf1fe6 100644 --- a/axi4/src/main/scala/axi4/Bundles.scala +++ b/axi4/src/main/scala/axi4/Bundles.scala @@ -10,6 +10,7 @@ package axi4 import chisel3._ import chisel3.util.isPow2 +import chisel3.experimental.BundleLiterals._ /** AXI4 write address * @@ -39,9 +40,24 @@ object WA { * @param addrW the width of the AWADDR signal in bits * @param idW the width of the AWID signal in bits, defaults to 0 * @param userW the width of the AWUSER signal in bits, defaults to 0 - * @return + * @return an unitialized WA object */ def apply(addrW: Int, idW: Int = 0, userW: Int = 0) = new WA(addrW, idW, userW) + + /** Default values for this channel + * + * @param in a WA object + * @return an initialized (hardware) WA object + */ + def default(in: WA) = { + var defLits = Seq((x: WA) => x.addr -> 0.U, (x: WA) => x.len -> 0.U, (x: WA) => x.size -> 0.U, + (x: WA) => x.burst -> BurstEncodings.Fixed, (x: WA) => x.lock -> LockEncodings.NormalAccess, + (x: WA) => x.cache -> MemoryEncodings.DeviceNonbuf, (x: WA) => x.prot -> ProtectionEncodings.DataNsecUpriv, + (x: WA) => x.qos -> 0.U, (x: WA) => x.region -> 0.U) + if (in.idW > 0) defLits = defLits :+ ((x: WA) => x.id -> 0.U) + if (in.userW > 0) defLits = defLits :+ ((x: WA) => x.user -> 0.U) + (new WA(in.addrW, in.idW, in.userW)).Lit(defLits :_*) + } } /** AXI4 write data @@ -66,6 +82,17 @@ object WD { * @return an unitialized WD object */ def apply(dataW: Int, userW: Int = 0) = new WD(dataW, userW) + + /** Default values for this channel + * + * @param in a WD object + * @return an initialized (hardware) WD object + */ + def default(in: WD) = { + var defLits = Seq((x: WD) => x.data -> 0.U, (x: WD) => x.strb -> 0.U, (x: WD) => x.last -> false.B) + if (in.userW > 0) defLits = defLits :+ ((x: WD) => x.user -> 0.U) + (new WD(in.dataW, in.userW)).Lit(defLits :_*) + } } /** AXI4 write response @@ -88,6 +115,18 @@ object WR { * @return an unitialized WR object */ def apply(idW: Int = 0, userW: Int = 0) = new WR(idW, userW) + + /** Default values for this channel + * + * @param in a WR object + * @return an initialized (hardware) WR object + */ + def default(in: WR) = { + var defLits = Seq((x: WR) => x.resp -> ResponseEncodings.Okay) + if (in.idW > 0) defLits = defLits :+ ((x: WR) => x.id -> 0.U) + if (in.userW > 0) defLits = defLits :+ ((x: WR) => x.user -> 0.U) + (new WR(in.idW, in.userW)).Lit(defLits :_*) + } } /** AXI4 read address @@ -121,6 +160,21 @@ object RA { * @return an unitialized RA object */ def apply(addrW: Int, idW: Int = 0, userW: Int = 0) = new RA(addrW, idW, userW) + + /** Default values for this channel + * + * @param in an RA object + * @return an initialized (hardware) RA object + */ + def default(in: RA) = { + var defLits = Seq((x: RA) => x.addr -> 0.U, (x: RA) => x.len -> 0.U, (x: RA) => x.size -> 0.U, + (x: RA) => x.burst -> BurstEncodings.Fixed, (x: RA) => x.lock -> LockEncodings.NormalAccess, + (x: RA) => x.cache -> MemoryEncodings.DeviceNonbuf, (x: RA) => x.prot -> ProtectionEncodings.DataNsecUpriv, + (x: RA) => x.qos -> 0.U, (x: RA) => x.region -> 0.U) + if (in.idW > 0) defLits = defLits :+ ((x: RA) => x.id -> 0.U) + if (in.userW > 0) defLits = defLits :+ ((x: RA) => x.user -> 0.U) + (new RA(in.addrW, in.idW, in.userW)).Lit(defLits :_*) + } } /** AXI4 read data @@ -148,4 +202,17 @@ object RD { * @return an uninitialized RD object */ def apply(dataW: Int, idW: Int = 0, userW: Int = 0) = new RD(dataW, idW, userW) + + /** Default values for this channel + * + * @param in an RD object + * @return an initialized (hardware) RD object + */ + def default(in: RD) = { + var defLits = Seq((x: RD) => x.data -> 0.U, (x: RD) => x.resp -> ResponseEncodings.Okay, + (x: RD) => x.last -> false.B) + if (in.idW > 0) defLits = defLits :+ ((x: RD) => x.id -> 0.U) + if (in.userW > 0) defLits = defLits :+ ((x: RD) => x.user -> 0.U) + (new RD(in.dataW, in.idW, in.userW)).Lit(defLits :_*) + } } diff --git a/axi4/src/main/scala/axi4/FunctionalMaster.scala b/axi4/src/main/scala/axi4/FunctionalMaster.scala index e61a22e..136e754 100644 --- a/axi4/src/main/scala/axi4/FunctionalMaster.scala +++ b/axi4/src/main/scala/axi4/FunctionalMaster.scala @@ -39,56 +39,33 @@ class FunctionalMaster[T <: Slave](dut: T) { private[this] var awaitingWAddr = Seq[WriteTransaction]() private[this] var awaitingWrite = Seq[WriteTransaction]() private[this] var awaitingResp = Seq[WriteTransaction]() - private[this] var responses = Seq[Response]() + private[this] var responses = Seq[Response]() private[this] var wAddrT: TesterThreadList = _ private[this] var writeT: TesterThreadList = _ - private[this] var respT: TesterThreadList = _ + private[this] var respT: TesterThreadList = _ // For reads private[this] var awaitingRAddr = Seq[ReadTransaction]() private[this] var awaitingRead = Seq[ReadTransaction]() private[this] var readValues = Seq[Seq[BigInt]]() private[this] var rAddrT: TesterThreadList = _ - private[this] var readT: TesterThreadList = _ + private[this] var readT: TesterThreadList = _ // For random data private[this] val rng = new Random(42) /** Default values on all signals */ // Address write - if (aw.bits.idW > 0) aw.bits.id.poke(0.U) - aw.bits.addr.poke(0.U) - aw.bits.len.poke(0.U) - aw.bits.size.poke(0.U) - aw.bits.burst.poke(BurstEncodings.Fixed) - aw.bits.lock.poke(LockEncodings.NormalAccess) - aw.bits.cache.poke(MemoryEncodings.DeviceNonbuf) - aw.bits.prot.poke(ProtectionEncodings.DataNsecUpriv) - aw.bits.qos.poke(0.U) - aw.bits.region.poke(0.U) - if (aw.bits.userW > 0) aw.bits.user.poke(0.U) + aw.bits.pokePartial(WA.default(aw.bits)) aw.valid.poke(false.B) // Data write - dw.bits.data.poke(0.U) - dw.bits.strb.poke(0.U) - dw.bits.last.poke(false.B) - if (dw.bits.userW > 0) dw.bits.user.poke(0.U) + dw.bits.pokePartial(WD.default(dw.bits)) dw.valid.poke(false.B) // Write response wr.ready.poke(false.B) // Address read - if (ar.bits.idW > 0) ar.bits.id.poke(0.U) - ar.bits.addr.poke(0.U) - ar.bits.len.poke(0.U) - ar.bits.size.poke(0.U) - ar.bits.burst.poke(BurstEncodings.Fixed) - ar.bits.lock.poke(LockEncodings.NormalAccess) - ar.bits.cache.poke(MemoryEncodings.DeviceNonbuf) - ar.bits.prot.poke(ProtectionEncodings.DataNsecUpriv) - ar.bits.qos.poke(0.U) - ar.bits.region.poke(0.U) - if (ar.bits.userW > 0) ar.bits.user.poke(0.U) + ar.bits.pokePartial(RA.default(ar.bits)) ar.valid.poke(false.B) // Data read From 40fe6e0f058ae213eac66db8f6af88aa60f7915b Mon Sep 17 00:00:00 2001 From: hansemandse Date: Fri, 11 Dec 2020 21:08:47 +0100 Subject: [PATCH 05/10] refactor master --- axi4/src/main/scala/VivadoAXIMemory.scala | 80 +++++------ .../main/scala/axi4/FunctionalMaster.scala | 129 +++++++----------- axi4/src/main/scala/axi4/Interfaces.scala | 32 ++--- axi4/src/main/scala/axi4/Transaction.scala | 86 ++++-------- .../test/scala/VivadoAXIMemoryTester.scala | 38 +++--- 5 files changed, 154 insertions(+), 211 deletions(-) diff --git a/axi4/src/main/scala/VivadoAXIMemory.scala b/axi4/src/main/scala/VivadoAXIMemory.scala index 54659cd..43c0b61 100644 --- a/axi4/src/main/scala/VivadoAXIMemory.scala +++ b/axi4/src/main/scala/VivadoAXIMemory.scala @@ -79,34 +79,34 @@ class VivadoAXIMemory extends Slave(10, 32, 1) { val mem = Module(new mymem()) /** Connect it up with the slave's AXI interface */ - val aw = io.aw.bits - val dw = io.dw.bits + val wa = io.wa.bits + val wd = io.wd.bits val wr = io.wr.bits - val ar = io.ar.bits - val dr = io.dr.bits + val ra = io.ra.bits + val rd = io.rd.bits /** Address write */ - mem.io.s00_axi_awid := aw.id - mem.io.s00_axi_awaddr := aw.addr - mem.io.s00_axi_awlen := aw.len - mem.io.s00_axi_awsize := aw.size - mem.io.s00_axi_awburst := aw.burst - mem.io.s00_axi_awlock := aw.lock - mem.io.s00_axi_awcache := aw.cache - mem.io.s00_axi_awprot := aw.prot - mem.io.s00_axi_awregion := aw.region - mem.io.s00_axi_awqos := aw.qos + mem.io.s00_axi_awid := wa.id + mem.io.s00_axi_awaddr := wa.addr + mem.io.s00_axi_awlen := wa.len + mem.io.s00_axi_awsize := wa.size + mem.io.s00_axi_awburst := wa.burst + mem.io.s00_axi_awlock := wa.lock + mem.io.s00_axi_awcache := wa.cache + mem.io.s00_axi_awprot := wa.prot + mem.io.s00_axi_awregion := wa.region + mem.io.s00_axi_awqos := wa.qos // Leave user port unconnected - mem.io.s00_axi_awvalid := io.aw.valid - io.aw.ready := mem.io.s00_axi_awready + mem.io.s00_axi_awvalid := io.wa.valid + io.wa.ready := mem.io.s00_axi_awready /** Data write */ - mem.io.s00_axi_wdata := dw.data - mem.io.s00_axi_wstrb := dw.strb - mem.io.s00_axi_wlast := dw.last + mem.io.s00_axi_wdata := wd.data + mem.io.s00_axi_wstrb := wd.strb + mem.io.s00_axi_wlast := wd.last // Leave user port unconnected - mem.io.s00_axi_wvalid := io.dw.valid - io.dw.ready := mem.io.s00_axi_wready + mem.io.s00_axi_wvalid := io.wd.valid + io.wd.ready := mem.io.s00_axi_wready /** Write response */ wr.id := mem.io.s00_axi_bid @@ -117,29 +117,29 @@ class VivadoAXIMemory extends Slave(10, 32, 1) { mem.io.s00_axi_bready := io.wr.ready /** Address read */ - mem.io.s00_axi_arid := ar.id - mem.io.s00_axi_araddr := ar.addr - mem.io.s00_axi_arlen := ar.len - mem.io.s00_axi_arsize := ar.size - mem.io.s00_axi_arburst := ar.burst - mem.io.s00_axi_arlock := ar.lock - mem.io.s00_axi_arcache := ar.cache - mem.io.s00_axi_arprot := ar.prot - mem.io.s00_axi_arregion := ar.region - mem.io.s00_axi_arqos := ar.qos + mem.io.s00_axi_arid := ra.id + mem.io.s00_axi_araddr := ra.addr + mem.io.s00_axi_arlen := ra.len + mem.io.s00_axi_arsize := ra.size + mem.io.s00_axi_arburst := ra.burst + mem.io.s00_axi_arlock := ra.lock + mem.io.s00_axi_arcache := ra.cache + mem.io.s00_axi_arprot := ra.prot + mem.io.s00_axi_arregion := ra.region + mem.io.s00_axi_arqos := ra.qos // Leave user port unconnected - mem.io.s00_axi_arvalid := io.ar.valid - io.ar.ready := mem.io.s00_axi_arready + mem.io.s00_axi_arvalid := io.ra.valid + io.ra.ready := mem.io.s00_axi_arready /** Data read */ - dr.id := mem.io.s00_axi_rid - dr.data := mem.io.s00_axi_rdata - dr.resp := mem.io.s00_axi_rresp - dr.last := mem.io.s00_axi_rlast + rd.id := mem.io.s00_axi_rid + rd.data := mem.io.s00_axi_rdata + rd.resp := mem.io.s00_axi_rresp + rd.last := mem.io.s00_axi_rlast // Connect user port to constant 0 - dr.user := 0.U - io.dr.valid := mem.io.s00_axi_rvalid - mem.io.s00_axi_rready := io.dr.ready + rd.user := 0.U + io.rd.valid := mem.io.s00_axi_rvalid + mem.io.s00_axi_rready := io.rd.ready /** Clock and reset */ mem.io.s00_axi_aclk := clock diff --git a/axi4/src/main/scala/axi4/FunctionalMaster.scala b/axi4/src/main/scala/axi4/FunctionalMaster.scala index 136e754..eea6643 100644 --- a/axi4/src/main/scala/axi4/FunctionalMaster.scala +++ b/axi4/src/main/scala/axi4/FunctionalMaster.scala @@ -10,6 +10,7 @@ package axi4 import chisel3._ import chisel3.util._ +import chisel3.experimental.BundleLiterals._ import chiseltest._ import chiseltest.internal.TesterThreadList import scala.util.Random @@ -26,11 +27,11 @@ class FunctionalMaster[T <: Slave](dut: T) { private[this] val dataW = dut.dataW /** Shortcuts to the channel IO */ - private[this] val aw = dut.io.aw - private[this] val dw = dut.io.dw + private[this] val wa = dut.io.wa + private[this] val wd = dut.io.wd private[this] val wr = dut.io.wr - private[this] val ar = dut.io.ar - private[this] val dr = dut.io.dr + private[this] val ra = dut.io.ra + private[this] val rd = dut.io.rd private[this] val resetn = dut.reset private[this] val clk = dut.clock @@ -54,22 +55,22 @@ class FunctionalMaster[T <: Slave](dut: T) { /** Default values on all signals */ // Address write - aw.bits.pokePartial(WA.default(aw.bits)) - aw.valid.poke(false.B) + wa.bits.pokePartial(WA.default(wa.bits)) + wa.valid.poke(false.B) // Data write - dw.bits.pokePartial(WD.default(dw.bits)) - dw.valid.poke(false.B) + wd.bits.pokePartial(WD.default(wd.bits)) + wd.valid.poke(false.B) // Write response wr.ready.poke(false.B) // Address read - ar.bits.pokePartial(RA.default(ar.bits)) - ar.valid.poke(false.B) + ra.bits.pokePartial(RA.default(ra.bits)) + ra.valid.poke(false.B) // Data read - dr.ready.poke(false.B) + rd.ready.poke(false.B) // Reset slave device controller resetn.poke(false.B) @@ -101,23 +102,11 @@ class FunctionalMaster[T <: Slave](dut: T) { val head = awaitingWAddr.head /** Write address to slave */ - aw.valid.poke(true.B) - if (aw.bits.idW > 0) aw.bits.id.poke(head.id.U) - aw.bits.addr.poke(head.addr.U) - aw.bits.len.poke(head.len.U) - aw.bits.size.poke(head.size.U) - aw.bits.burst.poke(head.burst) - aw.bits.lock.poke(head.lock) - aw.bits.cache.poke(head.cache) - aw.bits.prot.poke(head.prot) - aw.bits.qos.poke(head.qos) - aw.bits.region.poke(head.region) - if (aw.bits.userW > 0) aw.bits.user.poke(head.user) - while (!aw.ready.peek.litToBoolean) { - clk.step() - } + wa.valid.poke(true.B) + wa.bits.pokePartial(head.ctrl) + while (!wa.ready.peek.litToBoolean) clk.step() clk.step() - aw.valid.poke(false.B) + wa.valid.poke(false.B) /** Update transaction and queue */ awaitingWAddr = awaitingWAddr.tail @@ -137,25 +126,18 @@ class FunctionalMaster[T <: Slave](dut: T) { while (!awaitingWrite.isEmpty) { /** Get the current transaction */ val head = awaitingWrite.head - while (!head.addrSent) { - clk.step() - } + while (!head.addrSent) clk.step() /** Write data to slave */ - dw.valid.poke(true.B) + wd.valid.poke(true.B) while (!head.complete) { - val (data, strb, last, user) = 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) - if (dw.bits.userW > 0) dw.bits.user.poke(user) - while (!dw.ready.peek.litToBoolean) { - clk.step() - } + val nextVal = head.next + wd.bits.pokePartial(nextVal) + println("Write " + nextVal.data.litValue + " with strobe " + nextVal.strb.toString + " and last " + nextVal.last.litToBoolean) + while (!wd.ready.peek.litToBoolean) clk.step() clk.step() } - dw.valid.poke(false.B) + wd.valid.poke(false.B) /** Update transaction and queue */ awaitingWrite = awaitingWrite.tail @@ -175,15 +157,11 @@ class FunctionalMaster[T <: Slave](dut: T) { while (!awaitingResp.isEmpty) { /** Get the current transaction */ val head = awaitingResp.head - while (!head.dataSent) { - clk.step() - } + while (!head.dataSent) clk.step() /** Indicate that interface is ready and wait for response */ wr.ready.poke(true.B) - while (!wr.valid.peek.litToBoolean) { - clk.step() - } + while (!wr.valid.peek.litToBoolean) clk.step() responses = responses :+ (new Response(wr.bits.resp.peek, if (wr.bits.idW > 0) wr.bits.id.peek.litValue else 0)) wr.ready.poke(false.B) @@ -206,23 +184,11 @@ class FunctionalMaster[T <: Slave](dut: T) { val head = awaitingRAddr.head /** Write address to slave */ - ar.valid.poke(true.B) - if (ar.bits.idW > 0) ar.bits.id.poke(head.id.U) - ar.bits.addr.poke(head.addr.U) - ar.bits.len.poke(head.len.U) - ar.bits.size.poke(head.size.U) - ar.bits.burst.poke(head.burst) - ar.bits.lock.poke(head.lock) - ar.bits.cache.poke(head.cache) - ar.bits.prot.poke(head.prot) - ar.bits.qos.poke(head.qos) - ar.bits.region.poke(head.region) - if (ar.bits.userW > 0) ar.bits.user.poke(head.user) - while (!ar.ready.peek.litToBoolean) { - clk.step() - } + ra.valid.poke(true.B) + ra.bits.pokePartial(head.ctrl) + while (!ra.ready.peek.litToBoolean) clk.step() clk.step() - ar.valid.poke(false.B) + ra.valid.poke(false.B) /** Update transaction and queue */ awaitingRAddr = awaitingRAddr.tail @@ -242,22 +208,20 @@ class FunctionalMaster[T <: Slave](dut: T) { while (!awaitingRead.isEmpty) { /** Get the current transaction */ val head = awaitingRead.head - while (!head.addrSent) { - clk.step() - } + while (!head.addrSent) clk.step() /** Read data from slave */ - dr.ready.poke(true.B) + rd.ready.poke(true.B) while (!head.complete) { - if (dr.valid.peek.litToBoolean) { - val (data, resp, last) = (dr.bits.data.peek, dr.bits.resp.peek, dr.bits.last.peek) + if (rd.valid.peek.litToBoolean) { + val (data, resp, last) = (rd.bits.data.peek, rd.bits.resp.peek, rd.bits.last.peek) println(s"Read " + data.litValue + " with response " + resp.litValue + " and last " + last.litToBoolean) head.add(data.litValue) } clk.step() } readValues = readValues :+ head.data - dr.ready.poke(false.B) + rd.ready.poke(false.B) /** Update queue */ awaitingRead = awaitingRead.tail @@ -294,6 +258,7 @@ class FunctionalMaster[T <: Slave](dut: T) { * @param prot optional protection type, defaults to non-secure unprivileged data access * @param qos optional QoS, defaults to 0 * @param region optional region, defaults to 0 + * @param user optional user, defaults to 0 * * @note [[addr]] must fit within the slave DUT's write address width * @note entries in [[data]] must fit within the slave DUT's write data width, and the list can have at most [[len]] entries @@ -311,7 +276,8 @@ class FunctionalMaster[T <: Slave](dut: T) { cache: UInt = MemoryEncodings.DeviceNonbuf, prot: UInt = ProtectionEncodings.DataNsecUpriv, qos: UInt = 0.U, - region: UInt = 0.U) = { + region: UInt = 0.U, + user: UInt = 0.U) = { require(log2Up(addr) <= addrW, s"address must fit within DUT's write address width (got $addr)") require(log2Up(id) <= idW, s"ID must fit within DUT's ID width (got $id)") @@ -343,11 +309,15 @@ class FunctionalMaster[T <: Slave](dut: T) { val tdata = if (data != Nil) { require(data.length == burstLen, "given data length should match burst length") data - } else - Seq.fill(burstLen) { BigInt(numBytes, rng) } + } else Seq.fill(burstLen) { BigInt(numBytes, rng) } /** Create and queue new write transaction */ - val trx = new WriteTransaction(addr, data, dataW, id, len, size, burst, lock, cache, prot, qos, region) + var lits = Seq((x: WA) => x.addr -> addr.U, (x: WA) => x.len -> len.U, (x: WA) => x.size -> size.U, + (x: WA) => x.burst -> burst, (x: WA) => x.lock -> lock, (x: WA) => x.cache -> cache, + (x: WA) => x.prot -> prot, (x: WA) => x.qos -> qos, (x: WA) => x.region -> region) + if (wa.bits.idW > 0) lits = lits :+ ((x: WA) => x.id -> id.U) + if (wa.bits.userW > 0) lits = lits :+ ((x: WA) => x.user -> user) + val trx = new WriteTransaction((new WA(wa.bits.addrW, wa.bits.idW, wa.bits.userW)).Lit(lits :_*), wd.bits, data) awaitingWAddr = awaitingWAddr :+ trx awaitingWrite = awaitingWrite :+ trx awaitingResp = awaitingResp :+ trx @@ -370,6 +340,7 @@ class FunctionalMaster[T <: Slave](dut: T) { * @param prot optional protection type, defaults to non-secure unprivileged data access * @param qos optional QoS, defaults to 0 * @param region optional region, defaults to 0 + * @param user optional user, defaults to 0 * * @note [[addr]] must fit within the slave DUT's write address width * @note [[id]] must fit within DUT's ID width, likewise [[size]] cannot be greater than the DUT's write data width @@ -385,7 +356,8 @@ class FunctionalMaster[T <: Slave](dut: T) { cache: UInt = MemoryEncodings.DeviceNonbuf, prot: UInt = ProtectionEncodings.DataNsecUpriv, qos: UInt = 0.U, - region: UInt = 0.U) = { + region: UInt = 0.U, + user: UInt = 0.U) = { require(log2Up(addr) <= addrW, s"address must fit within DUT's write address width (got $addr)") require(log2Up(id) <= idW, s"ID must fit within DUT's ID width (got $id)") @@ -414,7 +386,12 @@ class FunctionalMaster[T <: Slave](dut: T) { } /** Create and queue new read transaction */ - val trx = new ReadTransaction(addr, id, len, size, burst, lock, cache, prot, qos, region) + var lits = Seq((x: RA) => x.addr -> addr.U, (x: RA) => x.len -> len.U, (x: RA) => x.size -> size.U, + (x: RA) => x.burst -> burst, (x: RA) => x.lock -> lock, (x: RA) => x.cache -> cache, + (x: RA) => x.prot -> prot, (x: RA) => x.qos -> qos, (x: RA) => x.region -> region) + if (ra.bits.idW > 0) lits = lits :+ ((x: RA) => x.id -> id.U) + if (ra.bits.userW > 0) lits = lits :+ ((x: RA) => x.user -> user) + val trx = new ReadTransaction((new RA(ra.bits.addrW, ra.bits.idW, ra.bits.userW)).Lit(lits :_*)) awaitingRAddr = awaitingRAddr :+ trx awaitingRead = awaitingRead :+ trx diff --git a/axi4/src/main/scala/axi4/Interfaces.scala b/axi4/src/main/scala/axi4/Interfaces.scala index 950c83b..3e4f808 100644 --- a/axi4/src/main/scala/axi4/Interfaces.scala +++ b/axi4/src/main/scala/axi4/Interfaces.scala @@ -21,17 +21,17 @@ import chisel3.util.Decoupled class MasterInterface(val addrW: Int, val dataW: Int, val idW: Int = 0, val userW: Int = 0) extends Bundle { /** Fields implementing each of the AXI channels * - * [[aw]] is the write address channel - * [[dw]] is the write data channel + * [[wa]] is the write address channel + * [[wd]] is the write data channel * [[wr]] is the write response channel - * [[ar]] is the read address channel - * [[dr]] is the read data channel + * [[ra]] is the read address channel + * [[rd]] is the read data channel */ - val aw = Decoupled(Output(WA(addrW, idW, userW))) //Decoupled(new WA(addrW, idW, userW)) - val dw = Decoupled(Output(WD(dataW, userW))) //Decoupled(new WD(dataW, userW)) + val wa = Decoupled(Output(WA(addrW, idW, userW))) //Decoupled(new WA(addrW, idW, userW)) + val wd = Decoupled(Output(WD(dataW, userW))) //Decoupled(new WD(dataW, userW)) val wr = Flipped(Decoupled(Output(WR(idW, userW)))) //Flipped(Decoupled(Flipped(new WR(idW, userW)))) - val ar = Decoupled(RA(addrW, idW, userW)) //Decoupled(new RA(addrW, idW, userW)) - val dr = Flipped(Decoupled(Output(RD(dataW, idW, userW)))) //Flipped(Decoupled(Flipped(new RD(dataW, idW, userW)))) + val ra = Decoupled(RA(addrW, idW, userW)) //Decoupled(new RA(addrW, idW, userW)) + val rd = Flipped(Decoupled(Output(RD(dataW, idW, userW)))) //Flipped(Decoupled(Flipped(new RD(dataW, idW, userW)))) } /** AXI4 slave interface @@ -44,15 +44,15 @@ class MasterInterface(val addrW: Int, val dataW: Int, val idW: Int = 0, val user class SlaveInterface(val addrW: Int, val dataW: Int, val idW: Int = 0, val userW: Int = 0) extends Bundle { /** Fields implementing each of the AXI channels * - * [[aw]] is the write address channel - * [[dw]] is the write data channel + * [[wa]] is the write address channel + * [[wd]] is the write data channel * [[wr]] is the write response channel - * [[ar]] is the read address channel - * [[dr]] is the read data channel + * [[ra]] is the read address channel + * [[rd]] is the read data channel */ - val aw = Flipped(Decoupled(Output(WA(addrW, idW, userW)))) //Flipped(Decoupled(new WA(addrW, idW, userW))) - val dw = Flipped(Decoupled(Output(WD(dataW, userW)))) //Flipped(Decoupled(new WD(dataW, userW))) + val wa = Flipped(Decoupled(Output(WA(addrW, idW, userW)))) //Flipped(Decoupled(new WA(addrW, idW, userW))) + val wd = Flipped(Decoupled(Output(WD(dataW, userW)))) //Flipped(Decoupled(new WD(dataW, userW))) val wr = Decoupled(Output(WR(idW, userW))) //Flipped(Flipped(Decoupled(Flipped(new WR(idW, userW))))) - val ar = Flipped(Decoupled(Output(RA(addrW, idW, userW)))) //Flipped(Decoupled(new RA(addrW, idW, userW))) - val dr = Decoupled(Output(RD(dataW, idW, userW))) //Flipped(Flipped(Decoupled(Flipped(new RD(dataW, idW, userW))))) + val ra = Flipped(Decoupled(Output(RA(addrW, idW, userW)))) //Flipped(Decoupled(new RA(addrW, idW, userW))) + val rd = Decoupled(Output(RD(dataW, idW, userW))) //Flipped(Flipped(Decoupled(Flipped(new RD(dataW, idW, userW))))) } diff --git a/axi4/src/main/scala/axi4/Transaction.scala b/axi4/src/main/scala/axi4/Transaction.scala index dfb58a6..643ae43 100644 --- a/axi4/src/main/scala/axi4/Transaction.scala +++ b/axi4/src/main/scala/axi4/Transaction.scala @@ -9,6 +9,8 @@ package axi4 import chisel3._ +import chisel3.experimental.BundleLiterals._ +import chisel3.internal.requireIsHardware /** Transaction superclass */ trait Transaction { @@ -17,41 +19,22 @@ trait Transaction { /** Write transaction * - * @param addr start write address - * @param data list of data to write - * @param dataW slave's data bus width - * @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 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 - * @param qos optional QoS, defaults to 0 - * @param region optional region, defaults to 0 - * @param user optional user, defaults to 0 + * @param ctrl an initialized WA object + * @param dw a WD object representing the write data channel + * @param data a sequence of data to write */ class WriteTransaction( - val addr: BigInt, - val data: Seq[BigInt], - dataW: Int, - val id: BigInt = 0, - val len: Int = 0, - val size: Int = 0, - val burst: UInt = BurstEncodings.Fixed, - val lock: Bool = LockEncodings.NormalAccess, - val cache: UInt = MemoryEncodings.DeviceNonbuf, - val prot: UInt = ProtectionEncodings.DataNsecUpriv, - val qos: UInt = 0.U, - val region: UInt = 0.U, - val user: UInt = 0.U) extends Transaction { - private[this] val numBytes = 1 << size + val ctrl: WA, + val dw: WD, + val data: Seq[BigInt]) extends Transaction { + requireIsHardware(ctrl, "ctrl must be an initialized WA object") + private[this] val numBytes = 1 << ctrl.size.litValue.intValue private[this] val dtsize = numBytes * data.length - private[this] val lowerBoundary = (addr / dtsize) * dtsize + private[this] val lowerBoundary = (ctrl.addr.litValue / dtsize) * dtsize private[this] val upperBoundary = lowerBoundary + dtsize - private[this] val alignedAddress = ((addr / numBytes) * numBytes) - private[this] var aligned = addr == alignedAddress - private[this] var address = addr + private[this] val alignedAddress = ((ctrl.addr.litValue / numBytes) * numBytes) + private[this] var aligned = ctrl.addr.litValue == alignedAddress + private[this] var address = ctrl.addr.litValue private[this] var count = 0 private[this] var _addrSent = false @@ -73,17 +56,17 @@ class WriteTransaction( */ def next() = { /** Strobe calculation */ - val offset = (address / dataW) * dataW + val offset = (address / dw.dataW) * dw.dataW val lowerByteLane = address - offset val upperByteLane = if (aligned) lowerByteLane + numBytes-1 else alignedAddress + numBytes-1 - offset 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 + val strb = ("b"+(0 until (dw.dataW/8)).foldRight("") { (elem, acc) => if (within(elem)) acc + "1" else acc + "0" }).asUInt /** Update address */ - if (burst != BurstEncodings.Fixed) { + if (ctrl.burst != BurstEncodings.Fixed) { if (aligned) { address += numBytes - if (burst == BurstEncodings.Wrap) { + if (ctrl.burst == BurstEncodings.Wrap) { if (address >= upperBoundary) { address = lowerBoundary } @@ -96,37 +79,20 @@ class WriteTransaction( count += 1 /** Return data to write */ - (data(count-1).U, strb, complete.B, user) + var lits = Seq((x: WD) => x.data -> data(count-1).U, (x: WD) => x.strb -> strb, + (x: WD) => x.last -> complete.B) + if (dw.userW > 0) lits = lits :+ ((x: WD) => x.user -> ctrl.user) + (new WD(dw.dataW, dw.userW)).Lit(lits :_*) } def complete = data.length == count } /** Read transaction * - * @param addr start read address - * @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 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 - * @param qos optional QoS, defaults to 0 - * @param region optional region, defaults to 0 - * @param user optional user, defaults to 0 + * @param ctrl an initialized RA object */ -class ReadTransaction( - val addr: BigInt, - val id: BigInt = 0, - val len: Int = 0, - val size: Int = 0, - val burst: UInt = BurstEncodings.Fixed, - val lock: Bool = LockEncodings.NormalAccess, - val cache: UInt = MemoryEncodings.DeviceNonbuf, - val prot: UInt = ProtectionEncodings.DataNsecUpriv, - val qos: UInt = 0.U, - val region: UInt = 0.U, - val user: UInt = 0.U) extends Transaction { +class ReadTransaction(val ctrl: RA) extends Transaction { + requireIsHardware(ctrl, "ctrl must be an initialized RA object") var data = Seq[BigInt]() private[this] var _addrSent = false @@ -144,7 +110,7 @@ class ReadTransaction( def add(v: BigInt) = { data = data :+ v } - def complete = data.length == (len + 1) + def complete = data.length == (ctrl.len.litValue + 1) } /** Transaction response diff --git a/axi4/src/test/scala/VivadoAXIMemoryTester.scala b/axi4/src/test/scala/VivadoAXIMemoryTester.scala index 9a09d16..6ab9e90 100644 --- a/axi4/src/test/scala/VivadoAXIMemoryTester.scala +++ b/axi4/src/test/scala/VivadoAXIMemoryTester.scala @@ -30,36 +30,36 @@ class VivadoAXIMemoryTester extends FlatSpec with ChiselScalatestTester with Mat val master = new FunctionalMaster(dut) def printCheck() = { - println("AWREADY = " + dut.io.aw.ready.peek.litToBoolean) - println("WREADY = " + dut.io.dw.ready.peek.litToBoolean) + println("AWREADY = " + dut.io.wa.ready.peek.litToBoolean) + println("WREADY = " + dut.io.wd.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) + println("ARREADY = " + dut.io.ra.ready.peek.litToBoolean) + println("RVALID = " + dut.io.rd.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) + dut.io.wd.bits.data.poke(42.U) + dut.io.wd.bits.strb.poke("b1111".U) + dut.io.wd.bits.last.poke(true.B) printCheck() // Write address - dut.io.aw.valid.poke(true.B) + dut.io.wa.valid.poke(true.B) do { dut.clock.step() - } while (!dut.io.aw.ready.peek.litToBoolean) + } while (!dut.io.wa.ready.peek.litToBoolean) printCheck() dut.clock.step() - dut.io.aw.valid.poke(false.B) + dut.io.wa.valid.poke(false.B) // Write some data - dut.io.dw.valid.poke(true.B) + dut.io.wd.valid.poke(true.B) do { dut.clock.step() - } while (!dut.io.dw.ready.peek.litToBoolean) + } while (!dut.io.wd.ready.peek.litToBoolean) printCheck() dut.clock.step() - dut.io.dw.valid.poke(false.B) + dut.io.wd.valid.poke(false.B) // Fetch response dut.io.wr.ready.poke(true.B) @@ -71,21 +71,21 @@ class VivadoAXIMemoryTester extends FlatSpec with ChiselScalatestTester with Mat println(s"Got response $r") // Read address - dut.io.ar.valid.poke(true.B) + dut.io.ra.valid.poke(true.B) do { dut.clock.step() - } while (!dut.io.ar.ready.peek.litToBoolean) + } while (!dut.io.ra.ready.peek.litToBoolean) printCheck() dut.clock.step() - dut.io.ar.valid.poke(false.B) + dut.io.ra.valid.poke(false.B) // Read some data - dut.io.dr.ready.poke(true.B) - while (!dut.io.dr.valid.peek.litToBoolean) { + dut.io.rd.ready.poke(true.B) + while (!dut.io.rd.valid.peek.litToBoolean) { dut.clock.step() } printCheck() - dut.io.dr.bits.data.expect(42.U) + dut.io.rd.bits.data.expect(42.U) } } From 33b0000e552a6cec5431c696983c3567b511b248 Mon Sep 17 00:00:00 2001 From: hansemandse Date: Fri, 11 Dec 2020 21:43:34 +0100 Subject: [PATCH 06/10] update package --- axi4/src/main/scala/axi4/Interfaces.scala | 20 +++++++-------- axi4/src/main/scala/axi4/Master.scala | 24 ------------------ axi4/src/main/scala/axi4/Slave.scala | 24 ------------------ axi4/src/main/scala/{ => axi4}/package.scala | 26 ++++++++++++++++++++ 4 files changed, 36 insertions(+), 58 deletions(-) delete mode 100644 axi4/src/main/scala/axi4/Master.scala delete mode 100644 axi4/src/main/scala/axi4/Slave.scala rename axi4/src/main/scala/{ => axi4}/package.scala (58%) diff --git a/axi4/src/main/scala/axi4/Interfaces.scala b/axi4/src/main/scala/axi4/Interfaces.scala index 3e4f808..73a2e39 100644 --- a/axi4/src/main/scala/axi4/Interfaces.scala +++ b/axi4/src/main/scala/axi4/Interfaces.scala @@ -27,11 +27,11 @@ class MasterInterface(val addrW: Int, val dataW: Int, val idW: Int = 0, val user * [[ra]] is the read address channel * [[rd]] is the read data channel */ - val wa = Decoupled(Output(WA(addrW, idW, userW))) //Decoupled(new WA(addrW, idW, userW)) - val wd = Decoupled(Output(WD(dataW, userW))) //Decoupled(new WD(dataW, userW)) - val wr = Flipped(Decoupled(Output(WR(idW, userW)))) //Flipped(Decoupled(Flipped(new WR(idW, userW)))) - val ra = Decoupled(RA(addrW, idW, userW)) //Decoupled(new RA(addrW, idW, userW)) - val rd = Flipped(Decoupled(Output(RD(dataW, idW, userW)))) //Flipped(Decoupled(Flipped(new RD(dataW, idW, userW)))) + val wa = Decoupled(Output(WA(addrW, idW, userW))) + val wd = Decoupled(Output(WD(dataW, userW))) + val wr = Flipped(Decoupled(Output(WR(idW, userW)))) + val ra = Decoupled(RA(addrW, idW, userW)) + val rd = Flipped(Decoupled(Output(RD(dataW, idW, userW)))) } /** AXI4 slave interface @@ -50,9 +50,9 @@ class SlaveInterface(val addrW: Int, val dataW: Int, val idW: Int = 0, val userW * [[ra]] is the read address channel * [[rd]] is the read data channel */ - val wa = Flipped(Decoupled(Output(WA(addrW, idW, userW)))) //Flipped(Decoupled(new WA(addrW, idW, userW))) - val wd = Flipped(Decoupled(Output(WD(dataW, userW)))) //Flipped(Decoupled(new WD(dataW, userW))) - val wr = Decoupled(Output(WR(idW, userW))) //Flipped(Flipped(Decoupled(Flipped(new WR(idW, userW))))) - val ra = Flipped(Decoupled(Output(RA(addrW, idW, userW)))) //Flipped(Decoupled(new RA(addrW, idW, userW))) - val rd = Decoupled(Output(RD(dataW, idW, userW))) //Flipped(Flipped(Decoupled(Flipped(new RD(dataW, idW, userW))))) + val wa = Flipped(Decoupled(Output(WA(addrW, idW, userW)))) + val wd = Flipped(Decoupled(Output(WD(dataW, userW)))) + val wr = Decoupled(Output(WR(idW, userW))) + val ra = Flipped(Decoupled(Output(RA(addrW, idW, userW)))) + val rd = Decoupled(Output(RD(dataW, idW, userW))) } diff --git a/axi4/src/main/scala/axi4/Master.scala b/axi4/src/main/scala/axi4/Master.scala deleted file mode 100644 index 4a181c4..0000000 --- a/axi4/src/main/scala/axi4/Master.scala +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Author: Hans Jakob Damsgaard, hansjakobdamsgaard@gmail.com - * - * Purpose: Implementation of a testing framework for AXI4-compliant devices. - * - * Content: An empty AXI master with relevant interface. -*/ - -package axi4 - -import chisel3._ - -/** AXI4 master - * - * An empty class representing an AXI master - * - * @param addrW the width of the address signals in bits - * @param dataW the width of the data read/write signals in bits - * @param idW the width of the ID signals in bits, defaults to 0 - * @param userW the width of the user signals in bits, defaults to 0 - */ -abstract class Master(val addrW: Int, val dataW: Int, val idW: Int = 0, val userW: Int = 0) extends Module { - val io = IO(new MasterInterface(addrW, dataW, idW, userW)) -} diff --git a/axi4/src/main/scala/axi4/Slave.scala b/axi4/src/main/scala/axi4/Slave.scala deleted file mode 100644 index f1a550b..0000000 --- a/axi4/src/main/scala/axi4/Slave.scala +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Author: Hans Jakob Damsgaard, hansjakobdamsgaard@gmail.com - * - * Purpose: Implementation of a testing framework for AXI4-compliant devices. - * - * Content: An empty AXI slave with relevant interface. -*/ - -package axi4 - -import chisel3._ - -/** AXI4 slave - * - * An empty class representing an AXI slave - * - * @param addrW the width of the address signals in bits - * @param dataW the width of the data read/write signals in bits - * @param idW the width of the ID signals in bits, defaults to 0 - * @param userW the width of the user signals in bits, defaults to 0 - */ -abstract class Slave(val addrW: Int, val dataW: Int, val idW: Int = 0, val userW: Int = 0) extends Module { - val io = IO(new SlaveInterface(addrW, dataW, idW, userW)) -} diff --git a/axi4/src/main/scala/package.scala b/axi4/src/main/scala/axi4/package.scala similarity index 58% rename from axi4/src/main/scala/package.scala rename to axi4/src/main/scala/axi4/package.scala index 9d6ae2c..229204f 100644 --- a/axi4/src/main/scala/package.scala +++ b/axi4/src/main/scala/axi4/package.scala @@ -9,6 +9,32 @@ import chisel3._ package object axi4 { + /** AXI4 master + * + * An empty class representing an AXI master + * + * @param addrW the width of the address signals in bits + * @param dataW the width of the data read/write signals in bits + * @param idW the width of the ID signals in bits, defaults to 0 + * @param userW the width of the user signals in bits, defaults to 0 + */ + abstract class Master(val addrW: Int, val dataW: Int, val idW: Int = 0, val userW: Int = 0) extends Module { + val io = IO(new MasterInterface(addrW, dataW, idW, userW)) + } + + /** AXI4 slave + * + * An empty class representing an AXI slave + * + * @param addrW the width of the address signals in bits + * @param dataW the width of the data read/write signals in bits + * @param idW the width of the ID signals in bits, defaults to 0 + * @param userW the width of the user signals in bits, defaults to 0 + */ + abstract class Slave(val addrW: Int, val dataW: Int, val idW: Int = 0, val userW: Int = 0) extends Module { + val io = IO(new SlaveInterface(addrW, dataW, idW, userW)) + } + /** AXI4 burst encodings */ object BurstEncodings { val Fixed = "b00".U From e0a3c223a00f783e73ead3c22031537f6b53e7fa Mon Sep 17 00:00:00 2001 From: hansemandse Date: Sun, 3 Jan 2021 13:05:50 +0100 Subject: [PATCH 07/10] add axi4 lite support --- axi4/src/main/scala/axi4/Bundles.scala | 155 +++++++++++++++++++--- axi4/src/main/scala/axi4/Interfaces.scala | 70 ++++++++-- axi4/src/main/scala/axi4/package.scala | 26 +++- 3 files changed, 214 insertions(+), 37 deletions(-) diff --git a/axi4/src/main/scala/axi4/Bundles.scala b/axi4/src/main/scala/axi4/Bundles.scala index cbf1fe6..50d49fb 100644 --- a/axi4/src/main/scala/axi4/Bundles.scala +++ b/axi4/src/main/scala/axi4/Bundles.scala @@ -12,24 +12,46 @@ import chisel3._ import chisel3.util.isPow2 import chisel3.experimental.BundleLiterals._ -/** AXI4 write address +/** AXI4 Lite write address + * + * @param addrW the width of the AWADDR signal in bits + */ +class WALite(val addrW: Int) extends Bundle { + require(addrW > 0, "the address width must be a positive integer") + val addr = UInt(addrW.W) + val prot = UInt(3.W) +} +object WALite { + /** Alternative constructor + * + * @param addrW the width of the AWADDR signal in bits + * @return an unitialized WALite object + */ + def apply(addrW: Int) = new WALite(addrW) + + /** Default values for this channel + * + * @param in a WALite object + * @return an initialized (hardware) WALite object + */ + def default(in: WALite) = (new WALite(in.addrW)).Lit(_.addr -> 0.U, _.prot -> ProtectionEncodings.DataNsecUpriv) +} + +/** AXI4 full write address * * @param addrW the width of the AWADDR signal in bits * @param idW the width of the AWID signal in bits * @param userW the width of the AWUSER signal in bits */ -class WA(val addrW: Int, val idW: Int, val userW: Int) extends Bundle { - require(addrW > 0, "the address width must be a positive integer") +class WA(override val addrW: Int, val idW: Int, val userW: Int) extends WALite(addrW) { require(idW >= 0, "the id width must be a non-negative integer") require(userW >= 0, "the user with must be a non-negative integer") val id = UInt(idW.W) - val addr = UInt(addrW.W) val len = UInt(8.W) val size = UInt(3.W) val burst = UInt(2.W) val lock = Bool() val cache = UInt(4.W) - val prot = UInt(3.W) val qos = UInt(4.W) val region = UInt(4.W) val user = UInt(userW.W) @@ -60,17 +82,40 @@ object WA { } } -/** AXI4 write data +/** AXI4 Lite write data + * + * @param dataW the width of the WDATA signal in bits + */ +class WDLite(val dataW: Int) extends Bundle { + require(dataW == 32 || dataW == 64, "the data width must be either 32 or 64 bits") + val data = UInt(dataW.W) + val strb = UInt((dataW/8).W) +} +object WDLite { + /** Alternative constructor + * + * @param dataW the width of the WDATA signal in bits, defaults to 32 + * @return an unitialized WDLite object + */ + def apply(dataW: Int = 32) = new WDLite(dataW) + + /** Default values for this channel + * + * @param in a WDLite object + * @return an initialized (hardware) WDLite object + */ + def default(in: WDLite) = (new WDLite(in.dataW)).Lit(_.data -> 0.U, _.strb -> 0.U) +} + +/** AXI4 full write data * * @param dataW the width of the WDATA signal in bits * @param userW the width of the WUSER signal in bits */ -class WD(val dataW: Int, val userW: Int) extends Bundle { +class WD(override val dataW: Int, val userW: Int) extends WDLite(dataW) { require(dataW > 0, "the data width must be a positive integer") require(isPow2(dataW / 8), "the data width must be a power of 2 multiple of bytes") require(userW >= 0, "the user with must be a non-negative integer") - val data = UInt(dataW.W) - val strb = UInt((dataW/8).W) val last = Bool() val user = UInt(userW.W) } @@ -95,16 +140,37 @@ object WD { } } -/** AXI4 write response +/** AXI4 Lite write response + * + * @param idW the width of the BID signal in bits + */ +class WRLite extends Bundle { + val resp = UInt(2.W) +} +object WRLite { + /** Alternative constructor + * + * @return an unitialized WRLite object + */ + def apply() = new WRLite() + + /** Default values for this channel + * + * @param in a WRLite object + * @return an initialized (hardware) WRLite object + */ + def default(in: WRLite) = (new WRLite()).Lit(_.resp -> ResponseEncodings.Okay) +} + +/** AXI4 full write response * * @param idW the width of the BID signal in bits * @param userW the width of the BUSER signal in bits */ -class WR(val idW: Int, val userW: Int) extends Bundle { +class WR(val idW: Int, val userW: Int) extends WRLite { require(idW >= 0, "the id width must be a non-negative integer") require(userW >= 0, "the user with must be a non-negative integer") val id = UInt(idW.W) - val resp = UInt(2.W) val user = UInt(userW.W) } object WR { @@ -129,24 +195,46 @@ object WR { } } -/** AXI4 read address +/** AXI4 Lite read address + * + * @param addrW the width of the ARADDR signal in bits + */ +class RALite(val addrW: Int) extends Bundle { + require(addrW > 0, "the address width must be a positive integer") + val addr = UInt(addrW.W) + val prot = UInt(3.W) +} +object RALite { + /** Alternative constructor + * + * @param addrW the width of the ARADDR signal in bits + * @return an unitialized RALite object + */ + def apply(addrW: Int) = new RALite(addrW) + + /** Default values for this channel + * + * @param in a RALite object + * @return an initialized (hardware) RALite object + */ + def default(in: RALite) = (new RALite(in.addrW)).Lit(_.addr -> 0.U, _.prot -> ProtectionEncodings.DataNsecUpriv) +} + +/** AXI4 full read address * * @param addrW the width of the ARADDR signal in bits * @param idW the width of the ARID signal in bits * @param userW the width of the ARUSER signal in bits */ -class RA(val addrW: Int, val idW: Int, val userW: Int) extends Bundle { - require(addrW > 0, "the address width must be a positive integer") +class RA(override val addrW: Int, val idW: Int, val userW: Int) extends RALite(addrW) { require(idW >= 0, "the id width must be a non-negative integer") require(userW >= 0, "the user with must be a non-negative integer") val id = UInt(idW.W) - val addr = UInt(addrW.W) val len = UInt(8.W) val size = UInt(3.W) val burst = UInt(2.W) val lock = Bool() val cache = UInt(4.W) - val prot = UInt(3.W) val qos = UInt(4.W) val region = UInt(4.W) val user = UInt(userW.W) @@ -177,19 +265,44 @@ object RA { } } -/** AXI4 read data +/** AXI4 Lite read data + * + * @param dataW the width of the RDATA signal in bits + * @param idW the width of the RID signal in bits + * @param userW the width of the RUSER signal in bits + */ +class RDLite(val dataW: Int) extends Bundle { + require(dataW == 32 || dataW == 64, "the data width must be either 32 or 64 bits") + val data = UInt(dataW.W) + val resp = UInt(2.W) +} +object RDLite { + /** Alternative constructor + * + * @param dataW the width of the RDATA signal in bits, defaults to 32 + * @return an uninitialized RDLite object + */ + def apply(dataW: Int = 32) = new RDLite(dataW) + + /** Default values for this channel + * + * @param in a RDLite object + * @return an initialized (hardware) RDLite object + */ + def default(in: RDLite) = (new RDLite(in.dataW)).Lit(_.data -> 0.U, _.resp -> ResponseEncodings.Okay) +} + +/** AXI4 full read data * * @param dataW the width of the RDATA signal in bits * @param idW the width of the RID signal in bits * @param userW the width of the RUSER signal in bits */ -class RD(val dataW: Int, val idW: Int, val userW: Int) extends Bundle { +class RD(override val dataW: Int, val idW: Int, val userW: Int) extends RDLite(dataW) { require(isPow2(dataW / 8), "the data width must be a power of 2 multiple of bytes") require(idW >= 0, "the id width must be a non-negative integer") require(userW >= 0, "the user with must be a non-negative integer") val id = UInt(idW.W) - val data = UInt(dataW.W) - val resp = UInt(2.W) val last = Bool() val user = UInt(userW.W) } diff --git a/axi4/src/main/scala/axi4/Interfaces.scala b/axi4/src/main/scala/axi4/Interfaces.scala index 73a2e39..ce78eea 100644 --- a/axi4/src/main/scala/axi4/Interfaces.scala +++ b/axi4/src/main/scala/axi4/Interfaces.scala @@ -11,14 +11,56 @@ package axi4 import chisel3._ import chisel3.util.Decoupled -/** AXI4 master interface +/** AXI4-Lite master interface + * + * @param addrW the width of the address signals in bits + * @param dataW the width of the data read/write signals in bits + */ +class MasterInterfaceLite(val addrW: Int, val dataW: Int) extends Bundle { + /** Fields implementing each of the AXI channels + * + * [[wa]] is the write address channel + * [[wd]] is the write data channel + * [[wr]] is the write response channel + * [[ra]] is the read address channel + * [[rd]] is the read data channel + */ + val wa = Decoupled(Output(WALite(addrW))) + val wd = Decoupled(Output(WDLite(dataW))) + val wr = Flipped(Decoupled(Output(WRLite()))) + val ra = Decoupled(RALite(addrW)) + val rd = Flipped(Decoupled(Output(RDLite(dataW)))) +} + +/** AXI4 full master interface * * @param addrW the width of the address signals in bits * @param dataW the width of the data read/write signals in bits * @param idW the width of the ID signals in bits, defaults to 0 * @param userW the width of the user signals in bits, defaults to 0 */ -class MasterInterface(val addrW: Int, val dataW: Int, val idW: Int = 0, val userW: Int = 0) extends Bundle { +class MasterInterface(override val addrW: Int, override val dataW: Int, val idW: Int = 0, val userW: Int = 0) extends MasterInterfaceLite(addrW, dataW) { + /** Fields implementing each of the AXI channels + * + * [[wa]] is the write address channel + * [[wd]] is the write data channel + * [[wr]] is the write response channel + * [[ra]] is the read address channel + * [[rd]] is the read data channel + */ + override val wa = Decoupled(Output(WA(addrW, idW, userW))) + override val wd = Decoupled(Output(WD(dataW, userW))) + override val wr = Flipped(Decoupled(Output(WR(idW, userW)))) + override val ra = Decoupled(RA(addrW, idW, userW)) + override val rd = Flipped(Decoupled(Output(RD(dataW, idW, userW)))) +} + +/** AXI4-Lite slave interface + * + * @param addrW the width of the address signals in bits + * @param dataW the width of the data read/write signals in bits + */ +class SlaveInterfaceLite(val addrW: Int, val dataW: Int) extends Bundle { /** Fields implementing each of the AXI channels * * [[wa]] is the write address channel @@ -27,21 +69,21 @@ class MasterInterface(val addrW: Int, val dataW: Int, val idW: Int = 0, val user * [[ra]] is the read address channel * [[rd]] is the read data channel */ - val wa = Decoupled(Output(WA(addrW, idW, userW))) - val wd = Decoupled(Output(WD(dataW, userW))) - val wr = Flipped(Decoupled(Output(WR(idW, userW)))) - val ra = Decoupled(RA(addrW, idW, userW)) - val rd = Flipped(Decoupled(Output(RD(dataW, idW, userW)))) + val wa = Flipped(Decoupled(Output(WALite(addrW)))) + val wd = Flipped(Decoupled(Output(WDLite(dataW)))) + val wr = Decoupled(Output(WRLite())) + val ra = Flipped(Decoupled(Output(RALite(addrW)))) + val rd = Decoupled(Output(RDLite(dataW))) } -/** AXI4 slave interface +/** AXI4 full slave interface * * @param addrW the width of the address signals in bits * @param dataW the width of the data read/write signals in bits * @param idW the width of the ID signals in bits, defaults to 0 * @param userW the width of the user signals in bits, defaults to 0 */ -class SlaveInterface(val addrW: Int, val dataW: Int, val idW: Int = 0, val userW: Int = 0) extends Bundle { +class SlaveInterface(override val addrW: Int, override val dataW: Int, val idW: Int = 0, val userW: Int = 0) extends SlaveInterfaceLite(addrW, dataW) { /** Fields implementing each of the AXI channels * * [[wa]] is the write address channel @@ -50,9 +92,9 @@ class SlaveInterface(val addrW: Int, val dataW: Int, val idW: Int = 0, val userW * [[ra]] is the read address channel * [[rd]] is the read data channel */ - val wa = Flipped(Decoupled(Output(WA(addrW, idW, userW)))) - val wd = Flipped(Decoupled(Output(WD(dataW, userW)))) - val wr = Decoupled(Output(WR(idW, userW))) - val ra = Flipped(Decoupled(Output(RA(addrW, idW, userW)))) - val rd = Decoupled(Output(RD(dataW, idW, userW))) + override val wa = Flipped(Decoupled(Output(WA(addrW, idW, userW)))) + override val wd = Flipped(Decoupled(Output(WD(dataW, userW)))) + override val wr = Decoupled(Output(WR(idW, userW))) + override val ra = Flipped(Decoupled(Output(RA(addrW, idW, userW)))) + override val rd = Decoupled(Output(RD(dataW, idW, userW))) } diff --git a/axi4/src/main/scala/axi4/package.scala b/axi4/src/main/scala/axi4/package.scala index 229204f..776724e 100644 --- a/axi4/src/main/scala/axi4/package.scala +++ b/axi4/src/main/scala/axi4/package.scala @@ -9,7 +9,18 @@ import chisel3._ package object axi4 { - /** AXI4 master + /** AXI4-Lite master + * + * An empty class representing an AXI master + * + * @param addrW the width of the address signals in bits + * @param dataW the width of the data read/write signals in bits + */ + abstract class LiteMaster(val addrW: Int, val dataW: Int) extends Module { + val io = IO(new MasterInterfaceLite(addrW, dataW)) + } + + /** AXI4 full master * * An empty class representing an AXI master * @@ -22,7 +33,18 @@ package object axi4 { val io = IO(new MasterInterface(addrW, dataW, idW, userW)) } - /** AXI4 slave + /** AXI4-Lite slave + * + * An empty class representing an AXI slave + * + * @param addrW the width of the address signals in bits + * @param dataW the width of the data read/write signals in bits + */ + abstract class LiteSlave(val addrW: Int, val dataW: Int) extends Module { + val io = IO(new SlaveInterfaceLite(addrW, dataW)) + } + + /** AXI4 full slave * * An empty class representing an AXI slave * From 3e7f2292461c502ab2881899de9f2b1ef879867d Mon Sep 17 00:00:00 2001 From: hansemandse Date: Sun, 3 Jan 2021 13:12:59 +0100 Subject: [PATCH 08/10] update readme --- axi4/README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/axi4/README.md b/axi4/README.md index e74db7e..cbe6106 100644 --- a/axi4/README.md +++ b/axi4/README.md @@ -2,8 +2,6 @@ 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 -- Change bundles to simple signals instead of I/O -- AXI Lite interface - AXI Stream interface (see [here](https://developer.arm.com/documentation/ihi0051/latest/)) ## Documentation From 3216b96a451ca521e2e7fbf6a36912271ee6b203 Mon Sep 17 00:00:00 2001 From: hansemandse Date: Fri, 8 Jan 2021 15:01:49 +0100 Subject: [PATCH 09/10] move axi4 from class2020 -> chiselverify --- README.md | 1 + axi4/.gitignore | 17 - axi4/.scalafmt.conf | 7 - axi4/README.md | 97 ---- axi4/build.sbt | 14 - axi4/project/build.properties | 1 - axi4/src/main/scala/VivadoAXIMemory.scala | 147 ------ axi4/src/main/scala/axi4/Bundles.scala | 331 -------------- .../main/scala/axi4/FunctionalMaster.scala | 430 ------------------ axi4/src/main/scala/axi4/Interfaces.scala | 100 ---- axi4/src/main/scala/axi4/Transaction.scala | 121 ----- axi4/src/main/scala/axi4/package.scala | 108 ----- .../test/scala/VivadoAXIMemoryTester.scala | 247 ---------- 13 files changed, 1 insertion(+), 1620 deletions(-) delete mode 100644 axi4/.gitignore delete mode 100644 axi4/.scalafmt.conf delete mode 100644 axi4/README.md delete mode 100644 axi4/build.sbt delete mode 100644 axi4/project/build.properties delete mode 100644 axi4/src/main/scala/VivadoAXIMemory.scala delete mode 100644 axi4/src/main/scala/axi4/Bundles.scala delete mode 100644 axi4/src/main/scala/axi4/FunctionalMaster.scala delete mode 100644 axi4/src/main/scala/axi4/Interfaces.scala delete mode 100644 axi4/src/main/scala/axi4/Transaction.scala delete mode 100644 axi4/src/main/scala/axi4/package.scala delete mode 100644 axi4/src/test/scala/VivadoAXIMemoryTester.scala diff --git a/README.md b/README.md index 0adbb76..99af226 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ Slides for testing in software - https://docs.google.com/presentation/d/1vtVaw38 - by Hans Jakob Damsgaard - Implementation of interface and transaction specifications for the AMBA AXI4 protocol in Scala to make testing of compliant Chisel components easier. - found in `./axi4/` +- UPDATE: moved to [chiselverify](https://github.com/chiselverify/chiselverify) ### Assertions with time - by Niels Frederik Frandsen and Victor Alexander Hansen diff --git a/axi4/.gitignore b/axi4/.gitignore deleted file mode 100644 index 6dd29ed..0000000 --- a/axi4/.gitignore +++ /dev/null @@ -1,17 +0,0 @@ -# Ignore metals-related temp files -.bloop/ -.ionide/ -.metals/ -.vscode/ -project/.bloop/ -project/project/ -project/src/ -project/target/ -project/metals.sbt - -# Ignore generated files -target/ -test_run_dir/ - -# Ignore IP files -src/main/resources/ \ No newline at end of file diff --git a/axi4/.scalafmt.conf b/axi4/.scalafmt.conf deleted file mode 100644 index aba989f..0000000 --- a/axi4/.scalafmt.conf +++ /dev/null @@ -1,7 +0,0 @@ -version = 2.6.4 - -maxColumn = 120 -align = most -continuationIndent.defnSite = 2 -assumeStandardLibraryStripMargin = true -docstrings = ScalaDoc diff --git a/axi4/README.md b/axi4/README.md deleted file mode 100644 index cbe6106..0000000 --- a/axi4/README.md +++ /dev/null @@ -1,97 +0,0 @@ -# Verification of AMBA AXI Interfaced Components -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 -- AXI Stream interface (see [here](https://developer.arm.com/documentation/ihi0051/latest/)) - -## Documentation -The AXI protocol defines five independent channels for reads and writes as listed below. As they are independent, read/write addresses may be transferred to a slave ahead of transferring the data. So-called _transactions_ consist of one or more _tranfers_ across a set of channels. Data is transferred in _bursts_ which consist of one or more _beats_. The protocol also supports multiple outstanding transactions and out-of-order completion by using tagged packets. All channels use simple decoupled ready-valid signalling. - -### Channels -The following channels are defined in the AXI4 protocol. ID fields are partly optional in that they do not have to be unique as the interconnect must append master numbers to them to ensure correct operation. -- _Write address channel_ used to initiate a write transaction from master to slave. A transfer contains ID, address, burst length, burst type, cacheability etc. (see page A2-29) - - `AWID [_:0]` ID field - - `AWADDR [_:0]` start address of a write transaction - - `AWLEN [7:0]` number of beats in the burst - - `AWSIZE [2:0]` beat size encoding, i.e. number of bytes per beat is 2^AWSIZE - - `AWBURST [1:0]` one of _FIXED_, _INCR_, or _WRAP_ - - `AWLOCK`, `AWCACHE [3:0]`, `AWPROT [2:0]`, and `AWQOS [3:0]` control data protection and quality of service - - _Optional_ `AWREGION [3:0]` allows sharing of a single physical interface for multiple logical regions - - _Optional_ `AWUSER [_:0]` - - `AWREADY` and `AWVALID` handshake signals - -- _Write data channel_ used to transfer write data from master to slave. A transfer contains ID, data, byte-wide write enable etc. (see page A2-30) - - `WDATA [_:0]` write data - - `WSTRB [_:0]` write strobe (i.e. one bit per byte of data) - - `WLAST` indicates whether this is the last beat in a burst - - _Optional_ `WUSER [_:0]` - - `WREADY` and `WVALID` handshake signals - -- _Write response channel_ used to inform a master about the completion of a write transaction. A transfer contains ID, write status etc. (see page A2-31) - - `BID [_:0]` ID field - - `BRESP [1:0]` write response from the slave; i.e. one of _OKAY_, _EXOKAY_, _SLVERR_, or _DECERR_ - - _Optional_ `BUSER` - - `BREADY` and `BVALID` handshake signals - -- _Read address channel_ used to initiate a read transaction from slave to master. A transfer contains ID, address, burst length, burst type, cacheability etc. (see page A2-32) - - `ARID [_:0]` ID field - - `ARADDR [_:0]` start address of a read transaction - - `ARLEN [7:0]` number of beats in the burst - - `ARSIZE [2:0]` beat size encoding - - `ARBURST [1:0]` one of _FIXED_, _INCR_, or _WRAP_ - - `ARLOCK`, `ARCACHE [3:0]`, `ARPROT [2:0]`, and `ARQOS [3:0]` control data protection and quality of service - - _Optional_ `ARREGION [3:0]` allows sharing of a single physical interface for multiple logical regions - - _Optional_ `ARUSER [_:0]` - - `ARREADY` and `ARVALID` handshake signals - -- _Read data channel_ used to transfer read data from slave to master. A transfer contains ID, data etc. (see page A2-33) - - `RID [_:0]` ID field - - `RDATA [_:0]` read data - - `RRESP [1:0]` read response from the slave - - `RLAST` indicates whether this is the last beat in a burst - - _Optional_ `RUSER [_:0]` - - `RREADY` and `RVALID` handshake signals - -Additionally, two global signals are used (see page A2-28) -- `ACLK` a shared global clock signal -- `ARESETn` a shared global active-low reset signal - -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/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). - -## Notes -The following are taken from the specification. - -### Regarding ready-valid interface -- No combinational logic between `Ready` and `Valid` in neither master nor slave components -- A source _may not_ wait until `Ready` is asserted before asserting `Valid` -- A destination _may_ wait until `Valid` is asserted before asserting `Ready` -- Once asserted, `Valid` _must_ remain asserted until a handshake occurs -- If `Ready` is asserted, it _can_ be deasserted before assertion of `Valid` -- Transfers occur at the first rising edge after both `Ready` and `Valid` are asserted -- Address channels _should_ per default have `AWREADY` and `ARREADY` asserted to avoid transfers taking at least two cycles - -### Regarding channel relationships -- A write response must always follow the last write transfer in a write transaction -- Read data must always follow the address to which it the data relates -- Write data _can_ appear before or simultaneously with the write address for the transaction - -### Regarding transactions -- Bursts _must not_ cross a 4KB boundary (to avoid device address space mapping issues) -- Burst length is in \[1, 256\] for burst type INCR; for all other burst types it is in \[1, 16\] -- Bursts _must_ be completed (i.e. no support for early termination) -- Beat size _must not_ exceed the data bus width of neither the master nor the slave. -- Narrow transfers (i.e. transfers using a subsection of the data bus) are implemented using `WSTRB` -- Byte invariance means that some transfers can be little endian while others can be big endian -- Unaligned transfers are indicated either with the low-order address bits or using `WSTRB` -- Only one response is returned for write transactions, whereas a different response may be returned for each beat in a read transaction - and the entire transaction _must_ be completed regardless of potential errors -- Asserting `AxCACHE[1]` means that a transaction is _modifiable_ and can, for example, be broken down into multiple transactions, combined with other transactions, or fetch more data than requested -- Transaction ordering must be preserved for non-modifiable transactions with the same ID to the same slave (see pages A4-63 to A4-64) - -### Regarding multiple transactions -- The ID signals can be used to identify separate transactions that must be returned in order -- AXI4 does _not_ support write data interleaving diff --git a/axi4/build.sbt b/axi4/build.sbt deleted file mode 100644 index 55c601b..0000000 --- a/axi4/build.sbt +++ /dev/null @@ -1,14 +0,0 @@ -scalaVersion := "2.12.12" - -scalacOptions := Seq("-Xsource:2.11") - -resolvers ++= Seq( - Resolver.sonatypeRepo("snapshots"), - Resolver.sonatypeRepo("releases") -) - -libraryDependencies += "edu.berkeley.cs" %% "chisel3" % "3.1.6" -libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.5" % "test" -libraryDependencies += "edu.berkeley.cs" %% "chisel-iotesters" % "1.4.2" -libraryDependencies += "edu.berkeley.cs" %% "chiseltest" % "0.2.2" - diff --git a/axi4/project/build.properties b/axi4/project/build.properties deleted file mode 100644 index 5a9ed92..0000000 --- a/axi4/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=1.3.4 diff --git a/axi4/src/main/scala/VivadoAXIMemory.scala b/axi4/src/main/scala/VivadoAXIMemory.scala deleted file mode 100644 index 43c0b61..0000000 --- a/axi4/src/main/scala/VivadoAXIMemory.scala +++ /dev/null @@ -1,147 +0,0 @@ -/** - * Author: Hans Jakob Damsgaard, hansjakobdamsgaard@gmail.com - * - * Purpose: Implementation of a testing framework for AXI4-compliant devices. - * - * Content: A blackbox for an AXI4 block RAM generated by Vivado. -*/ - -import axi4._ -import chisel3._ -import chisel3.util._ - -/** Vivado IP BRAM with full AXI4 interface */ -class mymem extends BlackBox with HasBlackBoxResource { - val io = IO(new Bundle { - /** Address write */ - val s00_axi_awid = Input(UInt(1.W)) - val s00_axi_awaddr = Input(UInt(10.W)) - val s00_axi_awlen = Input(UInt(8.W)) - val s00_axi_awsize = Input(UInt(3.W)) - val s00_axi_awburst = Input(UInt(2.W)) - val s00_axi_awlock = Input(Bool()) - val s00_axi_awcache = Input(UInt(4.W)) - val s00_axi_awprot = Input(UInt(3.W)) - val s00_axi_awregion = Input(UInt(4.W)) - val s00_axi_awqos = Input(UInt(4.W)) - val s00_axi_awvalid = Input(Bool()) - val s00_axi_awready = Output(Bool()) - - /** Data write */ - val s00_axi_wdata = Input(UInt(32.W)) - val s00_axi_wstrb = Input(UInt(4.W)) - val s00_axi_wlast = Input(Bool()) - val s00_axi_wvalid = Input(Bool()) - val s00_axi_wready = Output(Bool()) - - /** Write response */ - val s00_axi_bid = Output(UInt(1.W)) - val s00_axi_bresp = Output(UInt(2.W)) - val s00_axi_bvalid = Output(Bool()) - val s00_axi_bready = Input(Bool()) - - /** Address read */ - val s00_axi_arid = Input(UInt(1.W)) - val s00_axi_araddr = Input(UInt(10.W)) - val s00_axi_arlen = Input(UInt(8.W)) - val s00_axi_arsize = Input(UInt(3.W)) - val s00_axi_arburst = Input(UInt(2.W)) - val s00_axi_arlock = Input(Bool()) - val s00_axi_arcache = Input(UInt(4.W)) - val s00_axi_arprot = Input(UInt(3.W)) - val s00_axi_arregion = Input(UInt(4.W)) - val s00_axi_arqos = Input(UInt(4.W)) - val s00_axi_arvalid = Input(Bool()) - val s00_axi_arready = Output(Bool()) - - /** Data read */ - val s00_axi_rid = Output(UInt(1.W)) - val s00_axi_rdata = Output(UInt(32.W)) - val s00_axi_rresp = Output(UInt(2.W)) - val s00_axi_rlast = Output(Bool()) - val s00_axi_rvalid = Output(Bool()) - val s00_axi_rready = Input(Bool()) - - /** Clock and reset */ - val s00_axi_aclk = Input(Clock()) - val s00_axi_aresetn = Input(Reset()) - }) - - /** 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 */ -class VivadoAXIMemory extends Slave(10, 32, 1) { - /** Instantiate a memory */ - val mem = Module(new mymem()) - - /** Connect it up with the slave's AXI interface */ - val wa = io.wa.bits - val wd = io.wd.bits - val wr = io.wr.bits - val ra = io.ra.bits - val rd = io.rd.bits - - /** Address write */ - mem.io.s00_axi_awid := wa.id - mem.io.s00_axi_awaddr := wa.addr - mem.io.s00_axi_awlen := wa.len - mem.io.s00_axi_awsize := wa.size - mem.io.s00_axi_awburst := wa.burst - mem.io.s00_axi_awlock := wa.lock - mem.io.s00_axi_awcache := wa.cache - mem.io.s00_axi_awprot := wa.prot - mem.io.s00_axi_awregion := wa.region - mem.io.s00_axi_awqos := wa.qos - // Leave user port unconnected - mem.io.s00_axi_awvalid := io.wa.valid - io.wa.ready := mem.io.s00_axi_awready - - /** Data write */ - mem.io.s00_axi_wdata := wd.data - mem.io.s00_axi_wstrb := wd.strb - mem.io.s00_axi_wlast := wd.last - // Leave user port unconnected - mem.io.s00_axi_wvalid := io.wd.valid - io.wd.ready := mem.io.s00_axi_wready - - /** Write response */ - wr.id := mem.io.s00_axi_bid - wr.resp := mem.io.s00_axi_bresp - // Connect user port to constant 0 - wr.user := 0.U - io.wr.valid := mem.io.s00_axi_bvalid - mem.io.s00_axi_bready := io.wr.ready - - /** Address read */ - mem.io.s00_axi_arid := ra.id - mem.io.s00_axi_araddr := ra.addr - mem.io.s00_axi_arlen := ra.len - mem.io.s00_axi_arsize := ra.size - mem.io.s00_axi_arburst := ra.burst - mem.io.s00_axi_arlock := ra.lock - mem.io.s00_axi_arcache := ra.cache - mem.io.s00_axi_arprot := ra.prot - mem.io.s00_axi_arregion := ra.region - mem.io.s00_axi_arqos := ra.qos - // Leave user port unconnected - mem.io.s00_axi_arvalid := io.ra.valid - io.ra.ready := mem.io.s00_axi_arready - - /** Data read */ - rd.id := mem.io.s00_axi_rid - rd.data := mem.io.s00_axi_rdata - rd.resp := mem.io.s00_axi_rresp - rd.last := mem.io.s00_axi_rlast - // Connect user port to constant 0 - rd.user := 0.U - io.rd.valid := mem.io.s00_axi_rvalid - mem.io.s00_axi_rready := io.rd.ready - - /** Clock and reset */ - mem.io.s00_axi_aclk := clock - mem.io.s00_axi_aresetn := reset -} diff --git a/axi4/src/main/scala/axi4/Bundles.scala b/axi4/src/main/scala/axi4/Bundles.scala deleted file mode 100644 index 50d49fb..0000000 --- a/axi4/src/main/scala/axi4/Bundles.scala +++ /dev/null @@ -1,331 +0,0 @@ -/** - * Author: Hans Jakob Damsgaard, hansjakobdamsgaard@gmail.com - * - * Purpose: Implementation of a testing framework for AXI4-compliant devices. - * - * Content: Bundle definitions for AXI4. -*/ - -package axi4 - -import chisel3._ -import chisel3.util.isPow2 -import chisel3.experimental.BundleLiterals._ - -/** AXI4 Lite write address - * - * @param addrW the width of the AWADDR signal in bits - */ -class WALite(val addrW: Int) extends Bundle { - require(addrW > 0, "the address width must be a positive integer") - val addr = UInt(addrW.W) - val prot = UInt(3.W) -} -object WALite { - /** Alternative constructor - * - * @param addrW the width of the AWADDR signal in bits - * @return an unitialized WALite object - */ - def apply(addrW: Int) = new WALite(addrW) - - /** Default values for this channel - * - * @param in a WALite object - * @return an initialized (hardware) WALite object - */ - def default(in: WALite) = (new WALite(in.addrW)).Lit(_.addr -> 0.U, _.prot -> ProtectionEncodings.DataNsecUpriv) -} - -/** AXI4 full write address - * - * @param addrW the width of the AWADDR signal in bits - * @param idW the width of the AWID signal in bits - * @param userW the width of the AWUSER signal in bits - */ -class WA(override val addrW: Int, val idW: Int, val userW: Int) extends WALite(addrW) { - require(idW >= 0, "the id width must be a non-negative integer") - require(userW >= 0, "the user with must be a non-negative integer") - val id = UInt(idW.W) - val len = UInt(8.W) - val size = UInt(3.W) - val burst = UInt(2.W) - val lock = Bool() - val cache = UInt(4.W) - val qos = UInt(4.W) - val region = UInt(4.W) - val user = UInt(userW.W) -} -object WA { - /** Alternative constructor - * - * @param addrW the width of the AWADDR signal in bits - * @param idW the width of the AWID signal in bits, defaults to 0 - * @param userW the width of the AWUSER signal in bits, defaults to 0 - * @return an unitialized WA object - */ - def apply(addrW: Int, idW: Int = 0, userW: Int = 0) = new WA(addrW, idW, userW) - - /** Default values for this channel - * - * @param in a WA object - * @return an initialized (hardware) WA object - */ - def default(in: WA) = { - var defLits = Seq((x: WA) => x.addr -> 0.U, (x: WA) => x.len -> 0.U, (x: WA) => x.size -> 0.U, - (x: WA) => x.burst -> BurstEncodings.Fixed, (x: WA) => x.lock -> LockEncodings.NormalAccess, - (x: WA) => x.cache -> MemoryEncodings.DeviceNonbuf, (x: WA) => x.prot -> ProtectionEncodings.DataNsecUpriv, - (x: WA) => x.qos -> 0.U, (x: WA) => x.region -> 0.U) - if (in.idW > 0) defLits = defLits :+ ((x: WA) => x.id -> 0.U) - if (in.userW > 0) defLits = defLits :+ ((x: WA) => x.user -> 0.U) - (new WA(in.addrW, in.idW, in.userW)).Lit(defLits :_*) - } -} - -/** AXI4 Lite write data - * - * @param dataW the width of the WDATA signal in bits - */ -class WDLite(val dataW: Int) extends Bundle { - require(dataW == 32 || dataW == 64, "the data width must be either 32 or 64 bits") - val data = UInt(dataW.W) - val strb = UInt((dataW/8).W) -} -object WDLite { - /** Alternative constructor - * - * @param dataW the width of the WDATA signal in bits, defaults to 32 - * @return an unitialized WDLite object - */ - def apply(dataW: Int = 32) = new WDLite(dataW) - - /** Default values for this channel - * - * @param in a WDLite object - * @return an initialized (hardware) WDLite object - */ - def default(in: WDLite) = (new WDLite(in.dataW)).Lit(_.data -> 0.U, _.strb -> 0.U) -} - -/** AXI4 full write data - * - * @param dataW the width of the WDATA signal in bits - * @param userW the width of the WUSER signal in bits - */ -class WD(override val dataW: Int, val userW: Int) extends WDLite(dataW) { - require(dataW > 0, "the data width must be a positive integer") - require(isPow2(dataW / 8), "the data width must be a power of 2 multiple of bytes") - require(userW >= 0, "the user with must be a non-negative integer") - val last = Bool() - val user = UInt(userW.W) -} -object WD { - /** Alternative constructor - * - * @param dataW the width of the WDATA signal in bits - * @param userW the width of the WUSER signal in bits, defaults to 0 - * @return an unitialized WD object - */ - def apply(dataW: Int, userW: Int = 0) = new WD(dataW, userW) - - /** Default values for this channel - * - * @param in a WD object - * @return an initialized (hardware) WD object - */ - def default(in: WD) = { - var defLits = Seq((x: WD) => x.data -> 0.U, (x: WD) => x.strb -> 0.U, (x: WD) => x.last -> false.B) - if (in.userW > 0) defLits = defLits :+ ((x: WD) => x.user -> 0.U) - (new WD(in.dataW, in.userW)).Lit(defLits :_*) - } -} - -/** AXI4 Lite write response - * - * @param idW the width of the BID signal in bits - */ -class WRLite extends Bundle { - val resp = UInt(2.W) -} -object WRLite { - /** Alternative constructor - * - * @return an unitialized WRLite object - */ - def apply() = new WRLite() - - /** Default values for this channel - * - * @param in a WRLite object - * @return an initialized (hardware) WRLite object - */ - def default(in: WRLite) = (new WRLite()).Lit(_.resp -> ResponseEncodings.Okay) -} - -/** AXI4 full write response - * - * @param idW the width of the BID signal in bits - * @param userW the width of the BUSER signal in bits - */ -class WR(val idW: Int, val userW: Int) extends WRLite { - require(idW >= 0, "the id width must be a non-negative integer") - require(userW >= 0, "the user with must be a non-negative integer") - val id = UInt(idW.W) - val user = UInt(userW.W) -} -object WR { - /** Alternative constructor - * - * @param idW the width of the BID signal in bits, defaults to 0 - * @param userW the width of the BUSER signal in bits, defaults to 0 - * @return an unitialized WR object - */ - def apply(idW: Int = 0, userW: Int = 0) = new WR(idW, userW) - - /** Default values for this channel - * - * @param in a WR object - * @return an initialized (hardware) WR object - */ - def default(in: WR) = { - var defLits = Seq((x: WR) => x.resp -> ResponseEncodings.Okay) - if (in.idW > 0) defLits = defLits :+ ((x: WR) => x.id -> 0.U) - if (in.userW > 0) defLits = defLits :+ ((x: WR) => x.user -> 0.U) - (new WR(in.idW, in.userW)).Lit(defLits :_*) - } -} - -/** AXI4 Lite read address - * - * @param addrW the width of the ARADDR signal in bits - */ -class RALite(val addrW: Int) extends Bundle { - require(addrW > 0, "the address width must be a positive integer") - val addr = UInt(addrW.W) - val prot = UInt(3.W) -} -object RALite { - /** Alternative constructor - * - * @param addrW the width of the ARADDR signal in bits - * @return an unitialized RALite object - */ - def apply(addrW: Int) = new RALite(addrW) - - /** Default values for this channel - * - * @param in a RALite object - * @return an initialized (hardware) RALite object - */ - def default(in: RALite) = (new RALite(in.addrW)).Lit(_.addr -> 0.U, _.prot -> ProtectionEncodings.DataNsecUpriv) -} - -/** AXI4 full read address - * - * @param addrW the width of the ARADDR signal in bits - * @param idW the width of the ARID signal in bits - * @param userW the width of the ARUSER signal in bits - */ -class RA(override val addrW: Int, val idW: Int, val userW: Int) extends RALite(addrW) { - require(idW >= 0, "the id width must be a non-negative integer") - require(userW >= 0, "the user with must be a non-negative integer") - val id = UInt(idW.W) - val len = UInt(8.W) - val size = UInt(3.W) - val burst = UInt(2.W) - val lock = Bool() - val cache = UInt(4.W) - val qos = UInt(4.W) - val region = UInt(4.W) - val user = UInt(userW.W) -} -object RA { - /** Alternative constructor - * - * @param addrW the width of the ARADDR signal in bits - * @param idW the width of the ARID signal in bits, defaults to 0 - * @param userW the width of the ARUSER signal in bits, defaults to 0 - * @return an unitialized RA object - */ - def apply(addrW: Int, idW: Int = 0, userW: Int = 0) = new RA(addrW, idW, userW) - - /** Default values for this channel - * - * @param in an RA object - * @return an initialized (hardware) RA object - */ - def default(in: RA) = { - var defLits = Seq((x: RA) => x.addr -> 0.U, (x: RA) => x.len -> 0.U, (x: RA) => x.size -> 0.U, - (x: RA) => x.burst -> BurstEncodings.Fixed, (x: RA) => x.lock -> LockEncodings.NormalAccess, - (x: RA) => x.cache -> MemoryEncodings.DeviceNonbuf, (x: RA) => x.prot -> ProtectionEncodings.DataNsecUpriv, - (x: RA) => x.qos -> 0.U, (x: RA) => x.region -> 0.U) - if (in.idW > 0) defLits = defLits :+ ((x: RA) => x.id -> 0.U) - if (in.userW > 0) defLits = defLits :+ ((x: RA) => x.user -> 0.U) - (new RA(in.addrW, in.idW, in.userW)).Lit(defLits :_*) - } -} - -/** AXI4 Lite read data - * - * @param dataW the width of the RDATA signal in bits - * @param idW the width of the RID signal in bits - * @param userW the width of the RUSER signal in bits - */ -class RDLite(val dataW: Int) extends Bundle { - require(dataW == 32 || dataW == 64, "the data width must be either 32 or 64 bits") - val data = UInt(dataW.W) - val resp = UInt(2.W) -} -object RDLite { - /** Alternative constructor - * - * @param dataW the width of the RDATA signal in bits, defaults to 32 - * @return an uninitialized RDLite object - */ - def apply(dataW: Int = 32) = new RDLite(dataW) - - /** Default values for this channel - * - * @param in a RDLite object - * @return an initialized (hardware) RDLite object - */ - def default(in: RDLite) = (new RDLite(in.dataW)).Lit(_.data -> 0.U, _.resp -> ResponseEncodings.Okay) -} - -/** AXI4 full read data - * - * @param dataW the width of the RDATA signal in bits - * @param idW the width of the RID signal in bits - * @param userW the width of the RUSER signal in bits - */ -class RD(override val dataW: Int, val idW: Int, val userW: Int) extends RDLite(dataW) { - require(isPow2(dataW / 8), "the data width must be a power of 2 multiple of bytes") - require(idW >= 0, "the id width must be a non-negative integer") - require(userW >= 0, "the user with must be a non-negative integer") - val id = UInt(idW.W) - val last = Bool() - val user = UInt(userW.W) -} -object RD { - /** Alternative constructor - * - * @param dataW the width of the RDATA signal in bits - * @param idW the width of the RID signal in bits, defaults to 0 - * @param userW the width of the RUSER signal in bits, defaults to 0 - * @return an uninitialized RD object - */ - def apply(dataW: Int, idW: Int = 0, userW: Int = 0) = new RD(dataW, idW, userW) - - /** Default values for this channel - * - * @param in an RD object - * @return an initialized (hardware) RD object - */ - def default(in: RD) = { - var defLits = Seq((x: RD) => x.data -> 0.U, (x: RD) => x.resp -> ResponseEncodings.Okay, - (x: RD) => x.last -> false.B) - if (in.idW > 0) defLits = defLits :+ ((x: RD) => x.id -> 0.U) - if (in.userW > 0) defLits = defLits :+ ((x: RD) => x.user -> 0.U) - (new RD(in.dataW, in.idW, in.userW)).Lit(defLits :_*) - } -} diff --git a/axi4/src/main/scala/axi4/FunctionalMaster.scala b/axi4/src/main/scala/axi4/FunctionalMaster.scala deleted file mode 100644 index eea6643..0000000 --- a/axi4/src/main/scala/axi4/FunctionalMaster.scala +++ /dev/null @@ -1,430 +0,0 @@ -/** - * Author: Hans Jakob Damsgaard, hansjakobdamsgaard@gmail.com - * - * Purpose: Implementation of a testing framework for AXI4-compliant devices. - * - * Content: A functional AXI master implemented with ChiselTest. -*/ - -package axi4 - -import chisel3._ -import chisel3.util._ -import chisel3.experimental.BundleLiterals._ -import chiseltest._ -import chiseltest.internal.TesterThreadList -import scala.util.Random - -/** An AXI4 functional master - * - * @param dut a slave DUT - * - */ -class FunctionalMaster[T <: Slave](dut: T) { - /** DUT information */ - private[this] val idW = dut.idW - private[this] val addrW = dut.addrW - private[this] val dataW = dut.dataW - - /** Shortcuts to the channel IO */ - private[this] val wa = dut.io.wa - private[this] val wd = dut.io.wd - private[this] val wr = dut.io.wr - private[this] val ra = dut.io.ra - private[this] val rd = dut.io.rd - private[this] val resetn = dut.reset - private[this] val clk = dut.clock - - /** Threads and transaction state */ - // For writes - private[this] var awaitingWAddr = Seq[WriteTransaction]() - private[this] var awaitingWrite = Seq[WriteTransaction]() - private[this] var awaitingResp = Seq[WriteTransaction]() - private[this] var responses = Seq[Response]() - private[this] var wAddrT: TesterThreadList = _ - private[this] var writeT: TesterThreadList = _ - private[this] var respT: TesterThreadList = _ - // For reads - private[this] var awaitingRAddr = Seq[ReadTransaction]() - private[this] var awaitingRead = Seq[ReadTransaction]() - private[this] var readValues = Seq[Seq[BigInt]]() - private[this] var rAddrT: TesterThreadList = _ - private[this] var readT: TesterThreadList = _ - // For random data - private[this] val rng = new Random(42) - - /** Default values on all signals */ - // Address write - wa.bits.pokePartial(WA.default(wa.bits)) - wa.valid.poke(false.B) - - // Data write - wd.bits.pokePartial(WD.default(wd.bits)) - wd.valid.poke(false.B) - - // Write response - wr.ready.poke(false.B) - - // Address read - ra.bits.pokePartial(RA.default(ra.bits)) - ra.valid.poke(false.B) - - // Data read - rd.ready.poke(false.B) - - // Reset slave device controller - resetn.poke(false.B) - clk.step() - resetn.poke(true.B) - - /** Check for in-flight operations - * - * @return Boolean - */ - def hasInflightOps() = !awaitingWAddr.isEmpty || !awaitingWrite.isEmpty || !awaitingResp.isEmpty || !awaitingRAddr.isEmpty || !awaitingRead.isEmpty - - /** Check for responses or read data - * - * @return Boolean - */ - def hasRespOrReadData() = !responses.isEmpty || !readValues.isEmpty - - /** Handle the write address channel - * - * @note never call this method explicitly - */ - private[this] def writeAddrHandler(): Unit = { - println("New write address handler") - - /** Run this thread as long as the master is initialized or more transactions are waiting */ - while (!awaitingWAddr.isEmpty) { - /** Get the current transaction */ - val head = awaitingWAddr.head - - /** Write address to slave */ - wa.valid.poke(true.B) - wa.bits.pokePartial(head.ctrl) - while (!wa.ready.peek.litToBoolean) clk.step() - clk.step() - wa.valid.poke(false.B) - - /** Update transaction and queue */ - awaitingWAddr = awaitingWAddr.tail - head.addrSent = true - } - println("Closing write address handler") - } - - /** Handle the data write channel - * - * @note never call this method explicitly - */ - private[this] def writeHandler(): Unit = { - println("New write handler") - - /** Run this thread as long as the master is initialized or more transactions are waiting */ - while (!awaitingWrite.isEmpty) { - /** Get the current transaction */ - val head = awaitingWrite.head - while (!head.addrSent) clk.step() - - /** Write data to slave */ - wd.valid.poke(true.B) - while (!head.complete) { - val nextVal = head.next - wd.bits.pokePartial(nextVal) - println("Write " + nextVal.data.litValue + " with strobe " + nextVal.strb.toString + " and last " + nextVal.last.litToBoolean) - while (!wd.ready.peek.litToBoolean) clk.step() - clk.step() - } - wd.valid.poke(false.B) - - /** Update transaction and queue */ - awaitingWrite = awaitingWrite.tail - head.dataSent = true - } - println("Closing write handler") - } - - /** Watch the response channel - * - * @note never call this method explicitly - */ - private[this] def respHandler() = { - println("New response handler") - - /** Run this thread as long as the master is initialized or more transactions are waiting */ - while (!awaitingResp.isEmpty) { - /** Get the current transaction */ - val head = awaitingResp.head - while (!head.dataSent) clk.step() - - /** 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, if (wr.bits.idW > 0) wr.bits.id.peek.litValue else 0)) - wr.ready.poke(false.B) - - /** Update queue */ - awaitingResp = awaitingResp.tail - } - println("Closing response handler") - } - - /** Handle the read address channel - * - * @note never call this method explicitly - */ - private[this] def readAddrHandler(): Unit = { - println("New read address handler") - - /** Run this thread as long as the master is initialized or more transactions are waiting */ - while (!awaitingRAddr.isEmpty) { - /** Get the current transaction */ - val head = awaitingRAddr.head - - /** Write address to slave */ - ra.valid.poke(true.B) - ra.bits.pokePartial(head.ctrl) - while (!ra.ready.peek.litToBoolean) clk.step() - clk.step() - ra.valid.poke(false.B) - - /** Update transaction and queue */ - awaitingRAddr = awaitingRAddr.tail - head.addrSent = true - } - println("Closing read address handler") - } - - /** Handle the data read channel - * - * @note never call this method explicitly - */ - private[this] def readHandler(): Unit = { - println("New read handler") - - /** Run this thread as long as the master is initialized or more transactions are waiting */ - while (!awaitingRead.isEmpty) { - /** Get the current transaction */ - val head = awaitingRead.head - while (!head.addrSent) clk.step() - - /** Read data from slave */ - rd.ready.poke(true.B) - while (!head.complete) { - if (rd.valid.peek.litToBoolean) { - val (data, resp, last) = (rd.bits.data.peek, rd.bits.resp.peek, rd.bits.last.peek) - println(s"Read " + data.litValue + " with response " + resp.litValue + " and last " + last.litToBoolean) - head.add(data.litValue) - } - clk.step() - } - readValues = readValues :+ head.data - rd.ready.poke(false.B) - - /** Update queue */ - awaitingRead = awaitingRead.tail - } - println("Closing read handler") - } - - /** Destructor - * - * @note joins all non-null thread pointers and checks for responses and read data waiting in queues - */ - override def finalize() = { - /** Join handlers */ - if (wAddrT != null) wAddrT.join() - if (writeT != null) writeT.join() - if (respT != null) respT.join() - if (rAddrT != null) rAddrT.join() - if (readT != null) readT.join() - - /** Check for unchecked responses and read data */ - if (hasRespOrReadData) println(s"WARNING: master had ${responses.length} responses and ${readValues.length} Seq's of read data waiting") - } - - /** Start a write transaction to the given address - * - * @param addr start write address - * @param data optional list of data to write, defaults to random data - * @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 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 - * @param qos optional QoS, defaults to 0 - * @param region optional region, defaults to 0 - * @param user optional user, defaults to 0 - * - * @note [[addr]] must fit within the slave DUT's write address width - * @note entries in [[data]] must fit within the slave DUT's write data width, and the list can have at most [[len]] entries - * @note [[id]] must fit within DUT's ID width, likewise [[size]] cannot be greater than the DUT's write data width - * @note [[burst]], [[lock]], [[cache]], and [[prot]] should be a set of those defined in Defs.scala - */ - def createWriteTrx( - addr: BigInt, - data: Seq[BigInt] = Seq[BigInt](), - id: BigInt = 0, - len: Int = 0, - size: Int = 0, - burst: UInt = BurstEncodings.Fixed, - lock: Bool = LockEncodings.NormalAccess, - cache: UInt = MemoryEncodings.DeviceNonbuf, - prot: UInt = ProtectionEncodings.DataNsecUpriv, - qos: UInt = 0.U, - region: UInt = 0.U, - user: UInt = 0.U) = { - require(log2Up(addr) <= addrW, s"address must fit within DUT's write address width (got $addr)") - require(log2Up(id) <= idW, s"ID must fit within DUT's ID width (got $id)") - - /** [[len]] and [[size]] checks - * - [[size]] must be less than or equal to the write data width - * - [[len]] must be <= 15 for FIXED and WRAP transactions, only INCR can go beyond - * - Bursts cannot cross 4KB boundaries - */ - val startAddr = addr - val numBytes = 1 << size - val burstLen = len + 1 - val alignedAddr = (startAddr / numBytes) * numBytes - val wrapBoundary = (startAddr / (numBytes * burstLen)) * (numBytes * burstLen) - require(numBytes <= dataW, s"size must be less than or equal to the write data width") - burst match { - case BurstEncodings.Fixed => - require(burstLen <= 16, s"len for FIXED transactions must be less than or equal to 15 (got $len)") - require(((startAddr + numBytes) >> 12) == (startAddr >> 12), "burst cannot cross 4KB boundary") - case BurstEncodings.Incr => - require(burstLen <= 256, s"len for INCR transactions must be less than or equal to 255 (got $len)") - require(((startAddr + numBytes * burstLen) >> 12) == (startAddr >> 12), "burst cannot cross 4KB boundary") - case BurstEncodings.Wrap => - require(burstLen <= 16, s"len for WRAP transactions must be less than or equal to 15 (got $len)") - require((startAddr >> 12) == (wrapBoundary >> 12), "burst cannot cross 4KB boundary") - case _ => throw new IllegalArgumentException("invalid burst type entered") - } - - /** Select data */ - val tdata = if (data != Nil) { - require(data.length == burstLen, "given data length should match burst length") - data - } else Seq.fill(burstLen) { BigInt(numBytes, rng) } - - /** Create and queue new write transaction */ - var lits = Seq((x: WA) => x.addr -> addr.U, (x: WA) => x.len -> len.U, (x: WA) => x.size -> size.U, - (x: WA) => x.burst -> burst, (x: WA) => x.lock -> lock, (x: WA) => x.cache -> cache, - (x: WA) => x.prot -> prot, (x: WA) => x.qos -> qos, (x: WA) => x.region -> region) - if (wa.bits.idW > 0) lits = lits :+ ((x: WA) => x.id -> id.U) - if (wa.bits.userW > 0) lits = lits :+ ((x: WA) => x.user -> user) - val trx = new WriteTransaction((new WA(wa.bits.addrW, wa.bits.idW, wa.bits.userW)).Lit(lits :_*), wd.bits, data) - awaitingWAddr = awaitingWAddr :+ trx - awaitingWrite = awaitingWrite :+ trx - awaitingResp = awaitingResp :+ trx - - /** If this was the first transaction, fork new handlers */ - if (awaitingWAddr.length == 1) wAddrT = fork { writeAddrHandler() } - if (awaitingWrite.length == 1) writeT = fork { writeHandler() } - if (awaitingResp.length == 1) respT = fork { respHandler() } - } - - /** Start a write transaction to the given address - * - * @param addr start read address - * @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 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 - * @param qos optional QoS, defaults to 0 - * @param region optional region, defaults to 0 - * @param user optional user, defaults to 0 - * - * @note [[addr]] must fit within the slave DUT's write address width - * @note [[id]] must fit within DUT's ID width, likewise [[size]] cannot be greater than the DUT's write data width - * @note [[burst]], [[lock]], [[cache]], and [[prot]] should be a set of those defined in Defs.scala - */ - def createReadTrx( - addr: BigInt, - id: BigInt = 0, - len: Int = 0, - size: Int = 0, - burst: UInt = BurstEncodings.Fixed, - lock: Bool = LockEncodings.NormalAccess, - cache: UInt = MemoryEncodings.DeviceNonbuf, - prot: UInt = ProtectionEncodings.DataNsecUpriv, - qos: UInt = 0.U, - region: UInt = 0.U, - user: UInt = 0.U) = { - require(log2Up(addr) <= addrW, s"address must fit within DUT's write address width (got $addr)") - require(log2Up(id) <= idW, s"ID must fit within DUT's ID width (got $id)") - - /** [[len]] and [[size]] checks - * - [[size]] must be less than or equal to the write data width - * - [[len]] must be <= 15 for FIXED and WRAP transactions, only INCR can go beyond - * - Bursts cannot cross 4KB boundaries - */ - val startAddr = addr - val numBytes = 1 << size - val burstLen = len + 1 - val alignedAddr = (startAddr / numBytes) * numBytes - val wrapBoundary = (startAddr / (numBytes * burstLen)) * (numBytes * burstLen) - require(numBytes <= dataW, s"size must be less than or equal to the write data width") - burst match { - case BurstEncodings.Fixed => - require(burstLen <= 16, s"len for FIXED transactions must be less than or equal to 15 (got $len)") - require(((startAddr + numBytes) >> 12) == (startAddr >> 12), "burst cannot cross 4KB boundary") - case BurstEncodings.Incr => - require(burstLen <= 256, s"len for INCR transactions must be less than or equal to 255 (got $len)") - require(((startAddr + numBytes * burstLen) >> 12) == (startAddr >> 12), "burst cannot cross 4KB boundary") - case BurstEncodings.Wrap => - require(burstLen <= 16, s"len for WRAP transactions must be less than or equal to 15 (got $len)") - require((startAddr >> 12) == (wrapBoundary >> 12), "burst cannot cross 4KB boundary") - case _ => throw new IllegalArgumentException("invalid burst type entered") - } - - /** Create and queue new read transaction */ - var lits = Seq((x: RA) => x.addr -> addr.U, (x: RA) => x.len -> len.U, (x: RA) => x.size -> size.U, - (x: RA) => x.burst -> burst, (x: RA) => x.lock -> lock, (x: RA) => x.cache -> cache, - (x: RA) => x.prot -> prot, (x: RA) => x.qos -> qos, (x: RA) => x.region -> region) - if (ra.bits.idW > 0) lits = lits :+ ((x: RA) => x.id -> id.U) - if (ra.bits.userW > 0) lits = lits :+ ((x: RA) => x.user -> user) - val trx = new ReadTransaction((new RA(ra.bits.addrW, ra.bits.idW, ra.bits.userW)).Lit(lits :_*)) - awaitingRAddr = awaitingRAddr :+ trx - awaitingRead = awaitingRead :+ trx - - /** If this was the first transaction, fork new handlers */ - if (awaitingRAddr.length == 1) rAddrT = fork { readAddrHandler() } - if (awaitingRead.length == 1) readT = fork { readHandler() } - } - - /** Check for write response - * - * @note write responses are continuously stored in an internal queue by a second thread - * @note reading is destructive; i.e., the response being checked is removed from the queue - */ - def checkResponse() = { - responses match { - case r :: tail => - responses = tail - Some(r) - case _ => None - } - } - - /** Check for read data - * - * @note read values are continuously stored in an internal queue by a second thread spawned when creating a new read transaction - * @note reading is destructive; i.e., the data being returned is removed from the queue - */ - def checkReadData() = { - readValues match { - case v :: tail => - readValues = tail - Some(v) - case _ => None - } - } -} diff --git a/axi4/src/main/scala/axi4/Interfaces.scala b/axi4/src/main/scala/axi4/Interfaces.scala deleted file mode 100644 index ce78eea..0000000 --- a/axi4/src/main/scala/axi4/Interfaces.scala +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Author: Hans Jakob Damsgaard, hansjakobdamsgaard@gmail.com - * - * Purpose: Implementation of a testing framework for AXI4-compliant devices. - * - * Content: Interface definitions for AXI4. -*/ - -package axi4 - -import chisel3._ -import chisel3.util.Decoupled - -/** AXI4-Lite master interface - * - * @param addrW the width of the address signals in bits - * @param dataW the width of the data read/write signals in bits - */ -class MasterInterfaceLite(val addrW: Int, val dataW: Int) extends Bundle { - /** Fields implementing each of the AXI channels - * - * [[wa]] is the write address channel - * [[wd]] is the write data channel - * [[wr]] is the write response channel - * [[ra]] is the read address channel - * [[rd]] is the read data channel - */ - val wa = Decoupled(Output(WALite(addrW))) - val wd = Decoupled(Output(WDLite(dataW))) - val wr = Flipped(Decoupled(Output(WRLite()))) - val ra = Decoupled(RALite(addrW)) - val rd = Flipped(Decoupled(Output(RDLite(dataW)))) -} - -/** AXI4 full master interface - * - * @param addrW the width of the address signals in bits - * @param dataW the width of the data read/write signals in bits - * @param idW the width of the ID signals in bits, defaults to 0 - * @param userW the width of the user signals in bits, defaults to 0 - */ -class MasterInterface(override val addrW: Int, override val dataW: Int, val idW: Int = 0, val userW: Int = 0) extends MasterInterfaceLite(addrW, dataW) { - /** Fields implementing each of the AXI channels - * - * [[wa]] is the write address channel - * [[wd]] is the write data channel - * [[wr]] is the write response channel - * [[ra]] is the read address channel - * [[rd]] is the read data channel - */ - override val wa = Decoupled(Output(WA(addrW, idW, userW))) - override val wd = Decoupled(Output(WD(dataW, userW))) - override val wr = Flipped(Decoupled(Output(WR(idW, userW)))) - override val ra = Decoupled(RA(addrW, idW, userW)) - override val rd = Flipped(Decoupled(Output(RD(dataW, idW, userW)))) -} - -/** AXI4-Lite slave interface - * - * @param addrW the width of the address signals in bits - * @param dataW the width of the data read/write signals in bits - */ -class SlaveInterfaceLite(val addrW: Int, val dataW: Int) extends Bundle { - /** Fields implementing each of the AXI channels - * - * [[wa]] is the write address channel - * [[wd]] is the write data channel - * [[wr]] is the write response channel - * [[ra]] is the read address channel - * [[rd]] is the read data channel - */ - val wa = Flipped(Decoupled(Output(WALite(addrW)))) - val wd = Flipped(Decoupled(Output(WDLite(dataW)))) - val wr = Decoupled(Output(WRLite())) - val ra = Flipped(Decoupled(Output(RALite(addrW)))) - val rd = Decoupled(Output(RDLite(dataW))) -} - -/** AXI4 full slave interface - * - * @param addrW the width of the address signals in bits - * @param dataW the width of the data read/write signals in bits - * @param idW the width of the ID signals in bits, defaults to 0 - * @param userW the width of the user signals in bits, defaults to 0 - */ -class SlaveInterface(override val addrW: Int, override val dataW: Int, val idW: Int = 0, val userW: Int = 0) extends SlaveInterfaceLite(addrW, dataW) { - /** Fields implementing each of the AXI channels - * - * [[wa]] is the write address channel - * [[wd]] is the write data channel - * [[wr]] is the write response channel - * [[ra]] is the read address channel - * [[rd]] is the read data channel - */ - override val wa = Flipped(Decoupled(Output(WA(addrW, idW, userW)))) - override val wd = Flipped(Decoupled(Output(WD(dataW, userW)))) - override val wr = Decoupled(Output(WR(idW, userW))) - override val ra = Flipped(Decoupled(Output(RA(addrW, idW, userW)))) - override val rd = Decoupled(Output(RD(dataW, idW, userW))) -} diff --git a/axi4/src/main/scala/axi4/Transaction.scala b/axi4/src/main/scala/axi4/Transaction.scala deleted file mode 100644 index 643ae43..0000000 --- a/axi4/src/main/scala/axi4/Transaction.scala +++ /dev/null @@ -1,121 +0,0 @@ -/** - * Author: Hans Jakob Damsgaard, hansjakobdamsgaard@gmail.com - * - * Purpose: Implementation of a testing framework for AXI4-compliant devices. - * - * Content: Transaction classes for functional masters and slaves. -*/ - -package axi4 - -import chisel3._ -import chisel3.experimental.BundleLiterals._ -import chisel3.internal.requireIsHardware - -/** Transaction superclass */ -trait Transaction { - def complete: Boolean -} - -/** Write transaction - * - * @param ctrl an initialized WA object - * @param dw a WD object representing the write data channel - * @param data a sequence of data to write - */ -class WriteTransaction( - val ctrl: WA, - val dw: WD, - val data: Seq[BigInt]) extends Transaction { - requireIsHardware(ctrl, "ctrl must be an initialized WA object") - private[this] val numBytes = 1 << ctrl.size.litValue.intValue - private[this] val dtsize = numBytes * data.length - private[this] val lowerBoundary = (ctrl.addr.litValue / dtsize) * dtsize - private[this] val upperBoundary = lowerBoundary + dtsize - private[this] val alignedAddress = ((ctrl.addr.litValue / numBytes) * numBytes) - private[this] var aligned = ctrl.addr.litValue == alignedAddress - private[this] var address = ctrl.addr.litValue - private[this] var count = 0 - - private[this] var _addrSent = false - private[this] var _dataSent = false - - /** Getter and setter for [[addrSent]] */ - def addrSent = _addrSent - def addrSent_=(newValue: Boolean): Unit = _addrSent = newValue - - /** Getter and setter for [[dataSent]] */ - def dataSent = _dataSent - def dataSent_=(newValue: Boolean): Unit = _dataSent = newValue - - /** Get next (data, strb, last) tuple - * - * @return (data, strb, last) tuple - * - * @note has side effect on internal index count - */ - def next() = { - /** Strobe calculation */ - val offset = (address / dw.dataW) * dw.dataW - val lowerByteLane = address - offset - val upperByteLane = if (aligned) lowerByteLane + numBytes-1 else alignedAddress + numBytes-1 - offset - def within(x: Int) = x >= 0 && x <= (upperByteLane - lowerByteLane) - val strb = ("b"+(0 until (dw.dataW/8)).foldRight("") { (elem, acc) => if (within(elem)) acc + "1" else acc + "0" }).asUInt - - /** Update address */ - if (ctrl.burst != BurstEncodings.Fixed) { - if (aligned) { - address += numBytes - if (ctrl.burst == BurstEncodings.Wrap) { - if (address >= upperBoundary) { - address = lowerBoundary - } - } - } else { - address += numBytes - aligned = true - } - } - count += 1 - - /** Return data to write */ - var lits = Seq((x: WD) => x.data -> data(count-1).U, (x: WD) => x.strb -> strb, - (x: WD) => x.last -> complete.B) - if (dw.userW > 0) lits = lits :+ ((x: WD) => x.user -> ctrl.user) - (new WD(dw.dataW, dw.userW)).Lit(lits :_*) - } - def complete = data.length == count -} - -/** Read transaction - * - * @param ctrl an initialized RA object - */ -class ReadTransaction(val ctrl: RA) extends Transaction { - requireIsHardware(ctrl, "ctrl must be an initialized RA object") - var data = Seq[BigInt]() - - private[this] var _addrSent = false - - /** Getter and setter for [[addrSent]] */ - def addrSent = _addrSent - def addrSent_=(newValue: Boolean): Unit = _addrSent = newValue - - /** Add element to data sequence - * - * @param v value to add - * - * @note has side effect on internal data sequence - */ - def add(v: BigInt) = { - data = data :+ v - } - def complete = data.length == (ctrl.len.litValue + 1) -} - -/** Transaction response - * - * @param resp transaction response - * @param id optional id - */ -case class Response(val resp: UInt, val id: BigInt = 0) diff --git a/axi4/src/main/scala/axi4/package.scala b/axi4/src/main/scala/axi4/package.scala deleted file mode 100644 index 776724e..0000000 --- a/axi4/src/main/scala/axi4/package.scala +++ /dev/null @@ -1,108 +0,0 @@ -/** - * Author: Hans Jakob Damsgaard, hansjakobdamsgaard@gmail.com - * - * Purpose: Implementation of a testing framework for AXI4-compliant devices. - * - * Content: Package object for full AXI4. -*/ - -import chisel3._ - -package object axi4 { - /** AXI4-Lite master - * - * An empty class representing an AXI master - * - * @param addrW the width of the address signals in bits - * @param dataW the width of the data read/write signals in bits - */ - abstract class LiteMaster(val addrW: Int, val dataW: Int) extends Module { - val io = IO(new MasterInterfaceLite(addrW, dataW)) - } - - /** AXI4 full master - * - * An empty class representing an AXI master - * - * @param addrW the width of the address signals in bits - * @param dataW the width of the data read/write signals in bits - * @param idW the width of the ID signals in bits, defaults to 0 - * @param userW the width of the user signals in bits, defaults to 0 - */ - abstract class Master(val addrW: Int, val dataW: Int, val idW: Int = 0, val userW: Int = 0) extends Module { - val io = IO(new MasterInterface(addrW, dataW, idW, userW)) - } - - /** AXI4-Lite slave - * - * An empty class representing an AXI slave - * - * @param addrW the width of the address signals in bits - * @param dataW the width of the data read/write signals in bits - */ - abstract class LiteSlave(val addrW: Int, val dataW: Int) extends Module { - val io = IO(new SlaveInterfaceLite(addrW, dataW)) - } - - /** AXI4 full slave - * - * An empty class representing an AXI slave - * - * @param addrW the width of the address signals in bits - * @param dataW the width of the data read/write signals in bits - * @param idW the width of the ID signals in bits, defaults to 0 - * @param userW the width of the user signals in bits, defaults to 0 - */ - abstract class Slave(val addrW: Int, val dataW: Int, val idW: Int = 0, val userW: Int = 0) extends Module { - val io = IO(new SlaveInterface(addrW, dataW, idW, userW)) - } - - /** AXI4 burst encodings */ - object BurstEncodings { - val Fixed = "b00".U - val Incr = "b01".U - val Wrap = "b10".U - } - - /** AXI lock encodings */ - object LockEncodings { - val NormalAccess = false.B - val ExclusiveAccess = true.B - } - - /** AXI4 memory encodings */ - object MemoryEncodings { - val DeviceNonbuf = "b0000".U - val DeviceBuf = "b0001".U - val NormalNonbuf = "b0010".U - val NormalBuf = "b0011".U - val WtNoalloc = "b0110".U - val WtReadalloc = "b0110".U - val WtWritealloc = "b1110".U - val WtRwalloc = "b1110".U - val WbNoalloc = "b0111".U - val WbReadalloc = "b0111".U - val WbWritealloc = "b1111".U - val WbRwalloc = "b1111".U - } - - /** AXI4 protection encodings */ - object ProtectionEncodings { - val DataSecUpriv = "b000".U - val DataSecPriv = "b001".U - val DataNsecUpriv = "b010".U - val DataNsecPriv = "b011".U - val InstrSecUpriv = "b100".U - val InstrSecPriv = "b101".U - val InstrNsecUpriv = "b110".U - val InstrNsecPriv = "b111".U - } - - /** AXI4 response encodings */ - object ResponseEncodings { - val Okay = "b00".U - val Exokay = "b01".U - val Slverr = "b10".U - val Decerr = "b11".U - } -} diff --git a/axi4/src/test/scala/VivadoAXIMemoryTester.scala b/axi4/src/test/scala/VivadoAXIMemoryTester.scala deleted file mode 100644 index 6ab9e90..0000000 --- a/axi4/src/test/scala/VivadoAXIMemoryTester.scala +++ /dev/null @@ -1,247 +0,0 @@ -/** - * Author: Hans Jakob Damsgaard, hansjakobdamsgaard@gmail.com - * - * Purpose: Implementation of a testing framework for AXI4-compliant devices. - * - * Content: A tester for an AXI4 interfaced Vivado BRAM IP. -*/ - -import axi4._ -import chisel3._ -import chiseltest._ -import chiseltest.experimental.TestOptionBuilder._ -import chiseltest.internal.VerilatorBackendAnnotation -import org.scalatest._ -import scala.util.Random - -class VivadoAXIMemoryTester extends FlatSpec with ChiselScalatestTester with Matchers { - behavior of "AXI4 BRAM" - - it should "initialize" in { - test(new VivadoAXIMemory()).withAnnotations(Seq(VerilatorBackendAnnotation)) { - dut => - val master = new FunctionalMaster(dut) - } - } - - it should "write and read manually" in { - test(new VivadoAXIMemory()).withAnnotations(Seq(VerilatorBackendAnnotation)) { - dut => - val master = new FunctionalMaster(dut) - - def printCheck() = { - println("AWREADY = " + dut.io.wa.ready.peek.litToBoolean) - println("WREADY = " + dut.io.wd.ready.peek.litToBoolean) - println("BVALID = " + dut.io.wr.valid.peek.litToBoolean) - println("ARREADY = " + dut.io.ra.ready.peek.litToBoolean) - println("RVALID = " + dut.io.rd.valid.peek.litToBoolean) - } - - // Set some initial values on the necessary signals - dut.io.wd.bits.data.poke(42.U) - dut.io.wd.bits.strb.poke("b1111".U) - dut.io.wd.bits.last.poke(true.B) - printCheck() - - // Write address - dut.io.wa.valid.poke(true.B) - do { - dut.clock.step() - } while (!dut.io.wa.ready.peek.litToBoolean) - printCheck() - dut.clock.step() - dut.io.wa.valid.poke(false.B) - - // Write some data - dut.io.wd.valid.poke(true.B) - do { - dut.clock.step() - } while (!dut.io.wd.ready.peek.litToBoolean) - printCheck() - dut.clock.step() - dut.io.wd.valid.poke(false.B) - - // Fetch response - dut.io.wr.ready.poke(true.B) - while (!dut.io.wr.valid.peek.litToBoolean) { - dut.clock.step() - } - printCheck() - val r = dut.io.wr.bits.resp.peek.litValue - println(s"Got response $r") - - // Read address - dut.io.ra.valid.poke(true.B) - do { - dut.clock.step() - } while (!dut.io.ra.ready.peek.litToBoolean) - printCheck() - dut.clock.step() - dut.io.ra.valid.poke(false.B) - - // Read some data - dut.io.rd.ready.poke(true.B) - while (!dut.io.rd.valid.peek.litToBoolean) { - dut.clock.step() - } - printCheck() - dut.io.rd.bits.data.expect(42.U) - } - } - - it should "write and read with FIXED transactions" in { - test(new VivadoAXIMemory()).withAnnotations(Seq(VerilatorBackendAnnotation)) { - dut => - val master = new FunctionalMaster(dut) - - // 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") - } - } - - it should "write and read with INCR transactions" in { - test(new VivadoAXIMemory()).withAnnotations(Seq(VerilatorBackendAnnotation)) { - dut => - val master = new FunctionalMaster(dut) - - // Create write transaction - master.createWriteTrx(128, Seq.fill(128)(0x7FFFFFFF), len = 0x7F, size = 2, burst = BurstEncodings.Incr) - - // Wait for the write to complete - 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 - 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") - } - } - - it should "write and read with WRAP transactions" in { - test(new VivadoAXIMemory()).withAnnotations(Seq(VerilatorBackendAnnotation)) { - dut => - val master = new FunctionalMaster(dut) - - // addr = 96 - // dtsize = 4 * 16 = 64 - // Lower Wrap Boundary = INT(addr / dtsize) * dtsize = 64 - // Upper Wrap Boundary = Lower Wrap Boundary + dtsize = 128 - val inputData = (0 to 15).toSeq.map(x => BigInt(x)) - val outputData = inputData.takeRight(8) ++ inputData.take(8) - - // Create write transaction - master.createWriteTrx(96, inputData, len = 0xF, size = 2, burst = BurstEncodings.Wrap) - - // Wait for the write to complete - 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(64, len = 0xF, size = 2, burst = BurstEncodings.Incr) - - // Wait for read to complete - 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.zip(outputData).map { x => x._1 == x._2 }.foldLeft(true) { (acc, elem) => acc && elem }, "read data value is incorrect") - } - } - - it should "handle multiple in-flight transactions" in { - test(new VivadoAXIMemory()).withAnnotations(Seq(VerilatorBackendAnnotation)) { - dut => - val master = new FunctionalMaster(dut) - - // Create two write transactions - val rng = new Random(42) - val data1 = Seq.fill(32) { BigInt(32, rng) } - val data2 = Seq.fill(16) { BigInt(32, rng) } - master.createWriteTrx(512, data1, len = 0x1F, size = 2, burst = BurstEncodings.Incr) - master.createWriteTrx(192, data2, len = 0xF, size = 1) - - // Wait for the first write to complete - var resp = master.checkResponse() - while (resp == None) { - resp = master.checkResponse() - dut.clock.step() - } - var r = resp match { case Some(r) => r.resp.litValue; case _ => ResponseEncodings.Slverr.litValue } - assert(r == 0, "expected write to pass") - - // Create first read transaction - master.createReadTrx(512, len = 0x1F, size = 2, burst = BurstEncodings.Incr) - - // Wait for the first read to complete - var data = master.checkReadData() - while (data == None) { - data = master.checkReadData() - dut.clock.step() - } - var d = data match { case Some(v) => v; case _ => Seq() } - assert(d.zip(data1).foldLeft(true) { (acc, elem) => acc && (elem._1 == elem._2) }, "read data value is incorrect") - - // Wait for the second write to complete - resp = master.checkResponse() - while (resp == None) { - resp = master.checkResponse() - dut.clock.step() - } - r = resp match { case Some(r) => r.resp.litValue; case _ => ResponseEncodings.Slverr.litValue } - assert(r == 0, "expected write to pass") - - // Create second read transaction - master.createReadTrx(192, size = 1) - - // Wait for the second read to complete - data = master.checkReadData() - while (data == None) { - data = master.checkReadData() - dut.clock.step() - } - d = data match { case Some(v) => v; case _ => Seq() } - assert(d(0) == (data2.last & ((1 << 16) - 1)), "read data value is incorrect") - } - } -} From 456c59bf256f29cdc9e485a0da95207c739b04bd Mon Sep 17 00:00:00 2001 From: Martin Schoeberl Date: Tue, 23 Feb 2021 10:04:30 +0100 Subject: [PATCH 10/10] lab1: no limit on JDK version --- lab1/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/lab1/README.md b/lab1/README.md index 994767f..d6eaf74 100644 --- a/lab1/README.md +++ b/lab1/README.md @@ -33,6 +33,5 @@ With IntelliJ import the lab2 project as follows: * Click *Import Project*, or on a running IntelliJ: *File - New - Project from Existing Source...* * Navigate to ```.../class2020/lab1``` and select the file ```build.sbt```, press *Open* - * Make sure to select JDK 1.8 (not Java 11!) * Press OK on the next dialog box