From f4482d9ad3e6f77cc951ff898a155cd4f739e429 Mon Sep 17 00:00:00 2001 From: Hans Jakob Date: Fri, 4 Dec 2020 15:38:20 +0100 Subject: [PATCH] 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 + } +}