diff --git a/.ci/Memora.yml b/.ci/Memora.yml index 176afb4ff..deda96773 100644 --- a/.ci/Memora.yml +++ b/.ci/Memora.yml @@ -149,6 +149,32 @@ artifacts: outputs: - build/axi_iw_converter-%.tested + axi_lite_dw_converter-%: + inputs: + - Bender.yml + - include + - scripts/run_vsim.sh + - src/axi_pkg.sv + - src/axi_intf.sv + - src/axi_test.sv + - src/axi_lite_dw_converter.sv + - test/tb_axi_lite_dw_converter.sv + outputs: + - build/axi_lite_dw_converter-%.tested + + axi_lite_mailbox-%: + inputs: + - Bender.yml + - include + - scripts/run_vsim.sh + - src/axi_pkg.sv + - src/axi_intf.sv + - src/axi_test.sv + - src/axi_lite_mailbox.sv + - test/tb_axi_lite_mailbox.sv + outputs: + - build/axi_lite_mailbox-%.tested + axi_lite_regs-%: inputs: - Bender.yml @@ -188,19 +214,6 @@ artifacts: outputs: - build/axi_lite_to_axi-%.tested - axi_lite_mailbox-%: - inputs: - - Bender.yml - - include - - scripts/run_vsim.sh - - src/axi_pkg.sv - - src/axi_intf.sv - - src/axi_test.sv - - src/axi_lite_mailbox.sv - - test/tb_axi_lite_mailbox.sv - outputs: - - build/axi_lite_mailbox-%.tested - axi_lite_xbar-%: inputs: - Bender.yml diff --git a/.gitignore b/.gitignore index 0797ab037..10c4aecdd 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ /Bender.lock /Bender.local *.log +*.wlf diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 15844b86e..2a34c9572 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -118,6 +118,16 @@ axi_iw_converter: variables: TEST_MODULE: axi_iw_converter +axi_lite_dw_converter: + <<: *run_vsim + variables: + TEST_MODULE: axi_lite_dw_converter + +axi_lite_mailbox: + <<: *run_vsim + variables: + TEST_MODULE: axi_lite_mailbox + axi_lite_regs: <<: *run_vsim variables: @@ -133,11 +143,6 @@ axi_lite_to_axi: variables: TEST_MODULE: axi_lite_to_axi -axi_lite_mailbox: - <<: *run_vsim - variables: - TEST_MODULE: axi_lite_mailbox - axi_lite_xbar: <<: *run_vsim variables: diff --git a/Bender.yml b/Bender.yml index 7a348ac14..5f74c1f1b 100644 --- a/Bender.yml +++ b/Bender.yml @@ -49,6 +49,7 @@ sources: - src/axi_isolate.sv - src/axi_join.sv - src/axi_lite_demux.sv + - src/axi_lite_dw_converter.sv - src/axi_lite_from_mem.sv - src/axi_lite_join.sv - src/axi_lite_lfsr.sv @@ -110,6 +111,7 @@ sources: - test/tb_axi_dw_upsizer.sv - test/tb_axi_fifo.sv - test/tb_axi_isolate.sv + - test/tb_axi_lite_dw_converter.sv - test/tb_axi_lite_mailbox.sv - test/tb_axi_lite_regs.sv - test/tb_axi_iw_converter.sv diff --git a/CHANGELOG.md b/CHANGELOG.md index e6bb2ed08..47c8e0ec8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Add `axi_lite_from_mem` and `axi_from_mem` acting like SRAMs making AXI4 requests downstream. - Add `axi_rw_join` and `axi_rw_split` to split/join AXI buses. - Add `#_width` functions returning the width of the AXI channels. +- Add `axi_lite_dw_converter`: Convert the data width of AXI4-Lite transactions. Emmits the + appropriate amount of downstream transactions to perform the whole requested access. + ### Changed - `axi_demux`: Replace FIFO between AW and W channel by a register plus a counter. This prevents @@ -25,6 +28,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Move `mem_to_banks` to `common_cells`. - Update `common_cells` from version `v1.26.0` to `v1.27.0`. - `axi_pkg`: Define `localparams` to define AXI type widths. +- `axi_test:axi_rand_lite_slave`: R response field is now random. ### Fixed diff --git a/README.md b/README.md index 012703744..807fad8ca 100644 --- a/README.md +++ b/README.md @@ -19,51 +19,52 @@ The **design and microarchitecture** of the modules in this repository is descri In addition to the documents linked in the following table, we are setting up [documentation auto-generated from inline docstrings](https://pulp-platform.github.io/axi/master). (Replace `master` in that URL with a tag to get the documentation for a specific version.) -| Name | Description | Doc | -|------------------------------------------------------|------------------------------------------------------------------------------------------------------|--------------------------------| -| [`axi_atop_filter`](src/axi_atop_filter.sv) | Filters atomic operations (ATOPs), i.e., write transactions that have a non-zero `aw_atop` value. | | -| [`axi_burst_splitter`](src/axi_burst_splitter.sv) | Split AXI4 burst transfers into single-beat transactions. | | -| [`axi_cdc`](src/axi_cdc.sv) | AXI clock domain crossing based on a Gray FIFO implementation. | | -| [`axi_cut`](src/axi_cut.sv) | Breaks all combinatorial paths between its input and output. | | -| [`axi_delayer`](src/axi_delayer.sv) | Synthesizable module which can (randomly) delays AXI channels. | | -| [`axi_demux`](src/axi_demux.sv) | Demultiplexes an AXI bus from one slave port to multiple master ports. | [Doc](doc/axi_demux.md) | -| [`axi_dw_converter`](src/axi_dw_converter.sv) | A data width converter between AXI interfaces of any data width. | | -| [`axi_dw_downsizer`](src/axi_dw_downsizer.sv) | A data width converter between a wide AXI master and a narrower AXI slave. | | -| [`axi_dw_upsizer`](src/axi_dw_upsizer.sv) | A data width converter between a narrow AXI master and a wider AXI slave. | | -| [`axi_err_slv`](src/axi_err_slv.sv) | Always responds with an AXI decode/slave error for transactions which are sent to it. | | -| [`axi_fifo`](src/axi_fifo.sv) | A Fifo for each AXI4 channel to buffer requests. | | -| [`axi_from_mem`](src/axi_from_mem.sv) | This module acts like an SRAM and makes AXI4 requests downstream. | | -| [`axi_id_prepend`](src/axi_id_prepend.sv) | This module prepends/strips the MSB from the AXI IDs. | | -| [`axi_id_remap`](src/axi_id_remap.sv) | Remap AXI IDs from wide IDs at the slave port to narrower IDs at the master port. | [Doc][doc.axi_id_remap] | -| [`axi_id_serialize`](src/axi_id_serialize.sv) | Reduce AXI IDs by serializing transactions when necessary. | [Doc][doc.axi_id_serialize] | -| [`axi_intf`](src/axi_intf.sv) | This file defines the interfaces we support. | | -| [`axi_isolate`](src/axi_isolate.sv) | A module that can isolate downstream slaves from receiving new AXI4 transactions. | | -| [`axi_iw_converter`](src/axi_iw_converter.sv) | Convert between any two AXI ID widths. | [Doc][doc.axi_iw_converter] | -| [`axi_join`](src/axi_join.sv) | A connector that joins two AXI interfaces. | | -| [`axi_lfsr`](src/axi_lfsr.sv) | AXI4-attached LFSR; read returns pseudo-random data, writes are compressed into a checksum. | | -| [`axi_lite_demux`](src/axi_lite_demux.sv) | Demultiplexes an AXI4-Lite bus from one slave port to multiple master ports. | [Doc](doc/axi_lite_demux.md) | -| [`axi_lite_from_mem`](src/axi_lite_from_mem.sv) | This module acts like an SRAM and makes AXI4-Lite requests downstream. | | -| [`axi_lite_join`](src/axi_lite_join.sv) | A connector that joins two AXI-Lite interfaces. | | -| [`axi_lite_lfsr`](src/axi_lite_lfsr.sv) | AXI4-Lite-attached LFSR; read returns pseudo-random data, writes are compressed into a checksum. | | -| [`axi_lite_mailbox`](src/axi_lite_mailbox.sv) | A AXI4-Lite Mailbox with two slave ports and usage triggered irq. | [Doc](doc/axi_lite_mailbox.md) | -| [`axi_lite_mux`](src/axi_lite_mux.sv) | Multiplexes AXI4-Lite slave ports down to one master port. | [Doc](doc/axi_lite_mux.md) | -| [`axi_lite_regs`](src/axi_lite_regs.sv) | AXI4-Lite registers with optional read-only and protection features. | [Doc][doc.axi_lite_regs] | -| [`axi_lite_to_apb`](src/axi_lite_to_apb.sv) | AXI4-Lite to APB4 protocol converter. | | -| [`axi_lite_to_axi`](src/axi_lite_to_axi.sv) | AXI4-Lite to AXI4 protocol converter. | | -| [`axi_lite_xbar`](src/axi_lite_xbar.sv) | Fully-connected AXI4-Lite crossbar with an arbitrary number of slave and master ports. | [Doc](doc/axi_lite_xbar.md) | -| [`axi_modify_address`](src/axi_modify_address.sv) | A connector that allows addresses of AXI requests to be changed. | | -| [`axi_multicut`](src/axi_multicut.sv) | AXI register which can be used to relax timing pressure on long AXI buses. | | -| [`axi_mux`](src/axi_mux.sv) | Multiplexes the AXI4 slave ports down to one master port. | [Doc](doc/axi_mux.md) | -| [`axi_pkg`](src/axi_pkg.sv) | Contains AXI definitions, common structs, and useful helper functions. | | -| [`axi_rw_join`](src/axi_rw_join.sv) | Joins a read and a write slave into one single read / write master. | | -| [`axi_rw_split`](src/axi_rw_split.sv) | Splits a single read / write slave into one read and one write master. | | -| [`axi_serializer`](src/axi_serializer.sv) | Serializes transactions with different IDs to the same ID. | | -| [`axi_throttle`](src/axi_throttle.sv) | Limits the maximum number of outstanding transfers sent to the downstream logic. | | -| [`axi_test`](src/axi_test.sv) | A set of testbench utilities for AXI interfaces. | | -| [`axi_to_axi_lite`](src/axi_to_axi_lite.sv) | AXI4 to AXI4-Lite protocol converter. | | -| [`axi_to_mem`](src/axi_to_mem.sv) | AXI4 to memory protocol (req, gnt, rvalid) converter. Additional banked, interleaved, split variant. | | -| [`axi_xbar`](src/axi_xbar.sv) | Fully-connected AXI4+ATOP crossbar with an arbitrary number of slave and master ports. | [Doc](doc/axi_xbar.md) | -| [`axi_xp`](src/axi_xp.sv) | AXI Crosspoint (XP) with homomorphous slave and master ports. | | +| Name | Description | Doc | +|---------------------------------------------------------|------------------------------------------------------------------------------------------------------|----------------------------------| +| [`axi_atop_filter`](src/axi_atop_filter.sv) | Filters atomic operations (ATOPs), i.e., write transactions that have a non-zero `aw_atop` value. | | +| [`axi_burst_splitter`](src/axi_burst_splitter.sv) | Split AXI4 burst transfers into single-beat transactions. | | +| [`axi_cdc`](src/axi_cdc.sv) | AXI clock domain crossing based on a Gray FIFO implementation. | | +| [`axi_cut`](src/axi_cut.sv) | Breaks all combinatorial paths between its input and output. | | +| [`axi_delayer`](src/axi_delayer.sv) | Synthesizable module which can (randomly) delays AXI channels. | | +| [`axi_demux`](src/axi_demux.sv) | Demultiplexes an AXI bus from one slave port to multiple master ports. | [Doc](doc/axi_demux.md) | +| [`axi_dw_converter`](src/axi_dw_converter.sv) | A data width converter between AXI interfaces of any data width. | | +| [`axi_dw_downsizer`](src/axi_dw_downsizer.sv) | A data width converter between a wide AXI master and a narrower AXI slave. | | +| [`axi_dw_upsizer`](src/axi_dw_upsizer.sv) | A data width converter between a narrow AXI master and a wider AXI slave. | | +| [`axi_err_slv`](src/axi_err_slv.sv) | Always responds with an AXI decode/slave error for transactions which are sent to it. | | +| [`axi_fifo`](src/axi_fifo.sv) | A Fifo for each AXI4 channel to buffer requests. | | +| [`axi_from_mem`](src/axi_from_mem.sv) | This module acts like an SRAM and makes AXI4 requests downstream. | | +| [`axi_id_prepend`](src/axi_id_prepend.sv) | This module prepends/strips the MSB from the AXI IDs. | | +| [`axi_id_remap`](src/axi_id_remap.sv) | Remap AXI IDs from wide IDs at the slave port to narrower IDs at the master port. | [Doc][doc.axi_id_remap] | +| [`axi_id_serialize`](src/axi_id_serialize.sv) | Reduce AXI IDs by serializing transactions when necessary. | [Doc][doc.axi_id_serialize] | +| [`axi_intf`](src/axi_intf.sv) | This file defines the interfaces we support. | | +| [`axi_isolate`](src/axi_isolate.sv) | A module that can isolate downstream slaves from receiving new AXI4 transactions. | | +| [`axi_iw_converter`](src/axi_iw_converter.sv) | Convert between any two AXI ID widths. | [Doc][doc.axi_iw_converter] | +| [`axi_join`](src/axi_join.sv) | A connector that joins two AXI interfaces. | | +| [`axi_lfsr`](src/axi_lfsr.sv) | AXI4-attached LFSR; read returns pseudo-random data, writes are compressed into a checksum. | | +| [`axi_lite_demux`](src/axi_lite_demux.sv) | Demultiplexes an AXI4-Lite bus from one slave port to multiple master ports. | [Doc](doc/axi_lite_demux.md) | +| [`axi_lite_dw_converter`](src/axi_lite_dw_converter.sv) | A data width converter between two AXI-Lite busses | [Doc][doc.axi_lite_dw_converter] | +| [`axi_lite_from_mem`](src/axi_lite_from_mem.sv) | This module acts like an SRAM and makes AXI4-Lite requests downstream. | | +| [`axi_lite_join`](src/axi_lite_join.sv) | A connector that joins two AXI-Lite interfaces. | | +| [`axi_lite_lfsr`](src/axi_lite_lfsr.sv) | AXI4-Lite-attached LFSR; read returns pseudo-random data, writes are compressed into a checksum. | | +| [`axi_lite_mailbox`](src/axi_lite_mailbox.sv) | A AXI4-Lite Mailbox with two slave ports and usage triggered irq. | [Doc](doc/axi_lite_mailbox.md) | +| [`axi_lite_mux`](src/axi_lite_mux.sv) | Multiplexes AXI4-Lite slave ports down to one master port. | [Doc](doc/axi_lite_mux.md) | +| [`axi_lite_regs`](src/axi_lite_regs.sv) | AXI4-Lite registers with optional read-only and protection features. | [Doc][doc.axi_lite_regs] | +| [`axi_lite_to_apb`](src/axi_lite_to_apb.sv) | AXI4-Lite to APB4 protocol converter. | | +| [`axi_lite_to_axi`](src/axi_lite_to_axi.sv) | AXI4-Lite to AXI4 protocol converter. | | +| [`axi_lite_xbar`](src/axi_lite_xbar.sv) | Fully-connected AXI4-Lite crossbar with an arbitrary number of slave and master ports. | [Doc](doc/axi_lite_xbar.md) | +| [`axi_modify_address`](src/axi_modify_address.sv) | A connector that allows addresses of AXI requests to be changed. | | +| [`axi_multicut`](src/axi_multicut.sv) | AXI register which can be used to relax timing pressure on long AXI buses. | | +| [`axi_mux`](src/axi_mux.sv) | Multiplexes the AXI4 slave ports down to one master port. | [Doc](doc/axi_mux.md) | +| [`axi_pkg`](src/axi_pkg.sv) | Contains AXI definitions, common structs, and useful helper functions. | | +| [`axi_rw_join`](src/axi_rw_join.sv) | Joins a read and a write slave into one single read / write master. | | +| [`axi_rw_split`](src/axi_rw_split.sv) | Splits a single read / write slave into one read and one write master. | | +| [`axi_serializer`](src/axi_serializer.sv) | Serializes transactions with different IDs to the same ID. | | +| [`axi_throttle`](src/axi_throttle.sv) | Limits the maximum number of outstanding transfers sent to the downstream logic. | | +| [`axi_test`](src/axi_test.sv) | A set of testbench utilities for AXI interfaces. | | +| [`axi_to_axi_lite`](src/axi_to_axi_lite.sv) | AXI4 to AXI4-Lite protocol converter. | | +| [`axi_to_mem`](src/axi_to_mem.sv) | AXI4 to memory protocol (req, gnt, rvalid) converter. Additional banked, interleaved, split variant. | | +| [`axi_xbar`](src/axi_xbar.sv) | Fully-connected AXI4+ATOP crossbar with an arbitrary number of slave and master ports. | [Doc](doc/axi_xbar.md) | +| [`axi_xp`](src/axi_xp.sv) | AXI Crosspoint (XP) with homomorphous slave and master ports. | | ## Synthesizable Verification Modules diff --git a/scripts/run_vsim.sh b/scripts/run_vsim.sh index ad1312415..b9656ddf6 100755 --- a/scripts/run_vsim.sh +++ b/scripts/run_vsim.sh @@ -225,6 +225,13 @@ exec_test() { done done ;; + axi_lite_dw_converter) + for DWSLV in 32 64 128; do + for DWMST in 16 32 64; do + call_vsim tb_axi_lite_dw_converter -gTbAxiDataWidthSlv=$DWSLV -gTbAxiDataWidthMst=$DWMST + done + done + ;; *) call_vsim tb_$1 -t 1ns -coverage -voptargs="+acc +cover=bcesfx" ;; diff --git a/src/axi_lite_dw_converter.sv b/src/axi_lite_dw_converter.sv new file mode 100644 index 000000000..f70940674 --- /dev/null +++ b/src/axi_lite_dw_converter.sv @@ -0,0 +1,567 @@ +// Copyright 2020 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// Authors: +// - Wolfgang Rönninger + +/// # AXI4-Lite data width downsize module. +/// +/// ## Down Conversion +/// +/// The module will be in this mode if `AxiSlvPortDataWidth > AxiMstPortDataWidth`. +/// The module does multiple transactions on the master port for each transaction of the salve port. +/// +/// The address on the master port will be aligned to the bus width, regardless what the input +/// address was. The number of transactions generated on the master port is equal to the +/// `DownsizeFactor = AxiSlvPortDataWidth / AxiMstPortDataWidth`. +/// +/// Eg: `AxiAddrWidth == 32'd16`, `AxiSlvPortDataWidth == 32'64`, `AxiMstPortDataWidth == 32'd32' +/// This is for write transactions. Reads are accessing the whole width of the slave port. +/// +/// +/// | EG NUM | SLV ADDR | SLV W DATA | SLV W STRB | MST ADDR | MST W DATA | MST W STRB | +/// |--------|----------|--------------------|------------|----------|------------|------------| +/// | 1 | 0x0000 | 0xBEEFBEEFAAAABBBB | 0xAB | 0x0000 | 0xAAAABBBB | 0xB | +/// | 1 | | | | 0x0004 | 0xBEEFBEEF | 0xA | +/// | | | | | | | | +/// | 2 | 0x0000 | 0xBEEFBEEFAAAABBBB | 0xF0 | 0x0000 | 0xAAAABBBB | 0x0 | +/// | 2 | | | | 0x0004 | 0xBEEFBEEF | 0xF | +/// | | | | | | | | +/// | 3 | 0x0004 | 0xBEEFBEEFAAAABBBB | 0xF0 | 0x0000 | 0xAAAABBBB | 0x0 | +/// | 3 | | | | 0x0004 | 0xBEEFBEEF | 0xF | +/// | | | | | | | | +/// | 4 | 0x0004 | 0xBEEFBEEFAAAABBBB | 0x0F | 0x0000 | 0xAAAABBBB | 0xF | +/// | 4 | | | | 0x0004 | 0xBEEFBEEF | 0x0 | +/// | | | | | | | | +/// | 5 | 0x0005 | 0xBEEFBE0000000000 | 0xE0 | 0x0000 | 0x00000000 | 0x0 | +/// | 5 | | | | 0x0004 | 0xBEEFBE00 | 0xE | +/// +/// Response field is aggregated (OR'ed) between the multiple requests made on the master port. +/// If one of the requests on the master port errors, the error response of the request +/// on the slave port will also signal an error. +/// +/// ## Up conversion +/// +/// The module will be in this mode if `AxiSlvPortDataWidth < AxiMstPortDataWidth`. +/// This mode will generate the same amount of transactions on the master port as on the slave port. +/// Data is replicated to match the bus width. Write strobes are silenced for the byte lanes not +/// written. +/// +/// ## Pass Through +/// +/// The module will be in this mode if `AxiSlvPortDataWidth == AxiMstPortDataWidth`. +/// Here the module passes through the slave port to the master port. +`include "common_cells/registers.svh" +module axi_lite_dw_converter #( + /// AXI4-Lite address width of the ports. + parameter int unsigned AxiAddrWidth = 32'd0, + /// AXI4-Lite data width of the slave port. + parameter int unsigned AxiSlvPortDataWidth = 32'd0, + /// AXI4-Lite data width of the master port. + parameter int unsigned AxiMstPortDataWidth = 32'd0, + /// AXI4-Lite AW channel struct type. This is for both ports the same. + parameter type axi_lite_aw_t = logic, + /// AXI4-Lite W channel struct type of the slave port. + parameter type axi_lite_slv_w_t = logic, + /// AXI4-Lite W channel struct type of the master port. + parameter type axi_lite_mst_w_t = logic, + /// AXI4-Lite B channel struct type. This is for both ports. + parameter type axi_lite_b_t = logic, + /// AXI4-Lite AR channel struct type. This is for both ports. + parameter type axi_lite_ar_t = logic, + /// AXI4-Lite R channel struct type of the slave port. + parameter type axi_lite_slv_r_t = logic, + /// AXI4-Lite R channel struct type of the master port. + parameter type axi_lite_mst_r_t = logic, + /// AXI4-Lite request struct of the slave port. + parameter type axi_lite_slv_req_t = logic, + /// AXI4-Lite response struct of the slave port. + parameter type axi_lite_slv_res_t = logic, + /// AXI4-Lite request struct of the master port. + parameter type axi_lite_mst_req_t = logic, + /// AXI4-Lite response struct of the master port. + parameter type axi_lite_mst_res_t = logic +) ( + /// Clock, positive edge triggered. + input logic clk_i, + /// Asynchrounous reset, active low. + input logic rst_ni, + /// Salve port, AXI4-Lite request. + input axi_lite_slv_req_t slv_req_i, + /// Salve port, AXI4-Lite response. + output axi_lite_slv_res_t slv_res_o, + /// Master port, AXI4-Lite request. + output axi_lite_mst_req_t mst_req_o, + /// Master port, AXI4-Lite response. + input axi_lite_mst_res_t mst_res_i +); + // Strobe parameter for the two AXI4-Lite ports. + localparam int unsigned AxiSlvPortStrbWidth = AxiSlvPortDataWidth / 32'd8; + localparam int unsigned AxiMstPortStrbWidth = AxiMstPortDataWidth / 32'd8; + typedef logic [AxiAddrWidth-1:0] addr_t; + + // AXI4-Lite downsizer + if (AxiSlvPortDataWidth > AxiMstPortDataWidth) begin : gen_downsizer + // The Downsize factor determines how often the data channel has to be multiplexed. + localparam int unsigned DownsizeFactor = AxiSlvPortDataWidth / AxiMstPortDataWidth; + // Selection width for choosing the byte lanes. + localparam int unsigned SelWidth = $clog2(DownsizeFactor); + // Type for the selection signal. + typedef logic [SelWidth-1:0] sel_t; + // Offset determines, which part of the address corresponds to the `w_chan_sel` signal. + localparam int unsigned SelOffset = $clog2(AxiMstPortStrbWidth); + + // Calculate the output address for the master port. + // `address`: The address as seen on the salve port. + // `sel`: T The current selection. + // `l_zero`: If set, the lowest bits are zero, for all generated addresses after the first. + function automatic addr_t out_address(input addr_t address, input sel_t sel); + out_address = address; + out_address[SelOffset+:SelWidth] = sel; + out_address[SelOffset-1:0] = SelOffset'(0); + endfunction : out_address + + // Write channels. + // Input spill register of the AW channel. + axi_lite_aw_t aw_chan_spill; + logic aw_chan_spill_valid, aw_chan_spill_ready; + + spill_register #( + .T ( axi_lite_aw_t ), + .Bypass ( 1'b0 ) + ) i_spill_register_aw ( + .clk_i, + .rst_ni, + .valid_i ( slv_req_i.aw_valid ), + .ready_o ( slv_res_o.aw_ready ), + .data_i ( slv_req_i.aw ), + .valid_o ( aw_chan_spill_valid ), + .ready_i ( aw_chan_spill_ready ), + .data_o ( aw_chan_spill ) + ); + + sel_t aw_sel_q, aw_sel_d; + logic aw_sel_load; + // AW channel output assignment + always_comb begin : proc_aw_chan_oup + mst_req_o.aw = aw_chan_spill; + mst_req_o.aw.addr = out_address(aw_chan_spill.addr, aw_sel_q); + end + // Slave port aw is valid, if there is something in the spill register. + assign mst_req_o.aw_valid = aw_chan_spill_valid; + assign aw_chan_spill_ready = mst_res_i.aw_ready & (&aw_sel_q); + + assign aw_sel_load = mst_req_o.aw_valid & mst_res_i.aw_ready; + assign aw_sel_d = sel_t'(aw_sel_q + 1'b1); + `FFLARN(aw_sel_q, aw_sel_d, aw_sel_load, '0, clk_i, rst_ni) + + // Input spill register of the W channel. + axi_lite_slv_w_t w_chan_spill; + logic w_chan_spill_valid, w_chan_spill_ready; + spill_register #( + .T ( axi_lite_slv_w_t ), + .Bypass ( 1'b0 ) + ) i_spill_register_w ( + .clk_i, + .rst_ni, + .valid_i ( slv_req_i.w_valid ), + .ready_o ( slv_res_o.w_ready ), + .data_i ( slv_req_i.w ), + .valid_o ( w_chan_spill_valid ), + .ready_i ( w_chan_spill_ready ), + .data_o ( w_chan_spill ) + ); + + // Data multiplexer on the W channel + sel_t w_sel_q, w_sel_d; + logic w_sel_load; + // W channel output assignment + assign mst_req_o.w = axi_lite_mst_w_t'{ + data: w_chan_spill.data[w_sel_q*AxiMstPortDataWidth+:AxiMstPortDataWidth], + strb: w_chan_spill.strb[w_sel_q*AxiMstPortStrbWidth+:AxiMstPortStrbWidth], + default: '0 + }; + assign mst_req_o.w_valid = w_chan_spill_valid; + assign w_chan_spill_ready = mst_res_i.w_ready & (&w_sel_q); + + assign w_sel_load = mst_req_o.w_valid & mst_res_i.w_ready; + assign w_sel_d = sel_t'(w_sel_q + 1'b1); + `FFLARN(w_sel_q, w_sel_d, w_sel_load, '0, clk_i, rst_ni) + + // B response aggregation + // Slave port B output is the aggregated error of the last few B responses. + sel_t b_sel_q, b_sel_d; + axi_pkg::resp_t b_resp_q, b_resp_d; + logic b_resp_load; + + assign slv_res_o.b = axi_lite_b_t'{ + resp: b_resp_q | mst_res_i.b.resp, + default: '0 + }; + // Output is valid, if it is the last b response for the wide W, we have something + // in the B FIFO and the B response is valid from the master port. + assign slv_res_o.b_valid = mst_res_i.b_valid & (&b_sel_q); + + // Assign the b_channel ready output. The master port is ready if something is in the + // B FIFO. Except, if it is the last one which should do a response on the slave port. + assign mst_req_o.b_ready = (&b_sel_q) ? slv_req_i.b_ready : 1'b1; + // B channel error response retention FF + assign b_sel_d = sel_t'(b_sel_q + 1'b1); + assign b_resp_d = (&b_sel_q) ? axi_pkg::RESP_OKAY : (b_resp_q | mst_res_i.b.resp); + assign b_resp_load = mst_res_i.b_valid & mst_req_o.b_ready; + `FFLARN(b_sel_q, b_sel_d, b_resp_load, '0, clk_i, rst_ni) + `FFLARN(b_resp_q, b_resp_d, b_resp_load, axi_pkg::RESP_OKAY, clk_i, rst_ni) + + // Read channels. + // Input spill register of the AW channel. + axi_lite_ar_t ar_chan_spill; + logic ar_chan_spill_valid, ar_chan_spill_ready; + + spill_register #( + .T ( axi_lite_ar_t ), + .Bypass ( 1'b0 ) + ) i_spill_register_ar ( + .clk_i, + .rst_ni, + .valid_i ( slv_req_i.ar_valid ), + .ready_o ( slv_res_o.ar_ready ), + .data_i ( slv_req_i.ar ), + .valid_o ( ar_chan_spill_valid ), + .ready_i ( ar_chan_spill_ready ), + .data_o ( ar_chan_spill ) + ); + + sel_t ar_sel_q, ar_sel_d; + logic ar_sel_load; + // AR channel output assignment + always_comb begin : proc_ar_chan_oup + mst_req_o.ar = ar_chan_spill; + mst_req_o.ar.addr = out_address(ar_chan_spill.addr, ar_sel_q); + end + // Slave port aw is valid, if there is something in the spill register. + assign mst_req_o.ar_valid = ar_chan_spill_valid; + assign ar_chan_spill_ready = mst_res_i.ar_ready & (&ar_sel_q); + + assign ar_sel_load = mst_req_o.ar_valid & mst_res_i.ar_ready; + assign ar_sel_d = sel_t'(ar_sel_q + 1'b1); + `FFLARN(ar_sel_q, ar_sel_d, ar_sel_load, '0, clk_i, rst_ni) + + // Responses have to be aggregated, one FF less, as the last data is feed directly through. + sel_t r_sel_q, r_sel_d; + logic r_sel_load; + axi_lite_mst_r_t [DownsizeFactor-2:0] r_chan_mst_q; + logic [DownsizeFactor-2:0] r_chan_mst_load; + for (genvar i = 0; unsigned'(i) < (DownsizeFactor-1); i++) begin : gen_r_chan_ff + assign r_chan_mst_load[i] = (sel_t'(i) == r_sel_q) & mst_res_i.r_valid & mst_req_o.r_ready; + `FFLARN(r_chan_mst_q[i], mst_res_i.r, r_chan_mst_load[i], axi_lite_mst_r_t'{default: '0}, clk_i, rst_ni) + end + assign r_sel_load = mst_res_i.r_valid & mst_req_o.r_ready; + assign r_sel_d = sel_t'(r_sel_q + 1'b1); + `FFLARN(r_sel_q, r_sel_d, r_sel_load, '0, clk_i, rst_ni) + + always_comb begin : proc_r_chan_oup + slv_res_o.r = axi_lite_slv_r_t'{ + resp: mst_res_i.r.resp, + default: '0 + }; + // Response is the OR of all responses + for (int unsigned i = 0; i < (DownsizeFactor-1); i++) begin + slv_res_o.r.resp = slv_res_o.r.resp | r_chan_mst_q[i].resp; + slv_res_o.r.data[i*AxiMstPortDataWidth+:AxiMstPortDataWidth] = r_chan_mst_q[i].data; + end + // The highest bits of the data can be directly the master port. + slv_res_o.r.data[(DownsizeFactor-1)*AxiMstPortDataWidth+:AxiMstPortDataWidth] = + mst_res_i.r.data; + end + + assign slv_res_o.r_valid = (&r_sel_q) ? mst_res_i.r_valid : 1'b0; + assign mst_req_o.r_ready = (&r_sel_q) ? slv_req_i.r_ready : 1'b1; + + end else if (AxiMstPortDataWidth > AxiSlvPortDataWidth) begin : gen_upsizer + // The upsize factor determines the amount of replication. + localparam int unsigned UpsizeFactor = AxiMstPortDataWidth / AxiSlvPortDataWidth; + + // Selection type and offset for the address + localparam int unsigned SelOffset = $clog2(AxiSlvPortStrbWidth); + localparam int unsigned SelWidth = $clog2(UpsizeFactor); + typedef logic [SelWidth-1:0] sel_t; + + // AW channel can be passed through, however block handshake if FIFO is full. + assign mst_req_o.aw = slv_req_i.aw; + // Lock the valid on the master port if it has been given. + logic lock_aw_q, lock_aw_d, load_aw_lock; + // W channel needs a FIFO to determine the silencing of the strobe signal. + logic w_full, w_empty, w_push, w_pop; + sel_t aw_sel, w_sel; + + // AW channel handshake control + always_comb begin : proc_aw_handshake + // default assignment + load_aw_lock = 1'b0; // the FF is toggling back and forth when loaded. + mst_req_o.aw_valid = 1'b0; + slv_res_o.aw_ready = 1'b0; + w_push = 1'b0; + + if (lock_aw_q) begin + mst_req_o.aw_valid = 1'b1; + slv_res_o.aw_ready = mst_res_i.aw_ready; + if (mst_res_i.aw_ready) begin + load_aw_lock = 1'b1; + end + end else begin + // Only connect handshake if there is space in the FIFO + if (!w_full) begin + mst_req_o.aw_valid = slv_req_i.aw_valid; + slv_res_o.aw_ready = mst_res_i.aw_ready; + // If there is a valid on the slave port, push the FIFO + if (slv_req_i.aw_valid) begin + w_push = 1'b1; + // When no transaction, lock AW + if (!mst_res_i.aw_ready) begin + load_aw_lock = 1'b1; + end + end + end + end + end + assign lock_aw_d = ~lock_aw_q; + `FFLARN(lock_aw_q, lock_aw_d, load_aw_lock, 1'b0, clk_i, rst_ni) + + // The selection comes from part of the AW address. + assign aw_sel = sel_t'(slv_req_i.aw.addr >> SelOffset); + + fifo_v3 #( + .FALL_THROUGH ( 1'b1 ), + .DEPTH ( UpsizeFactor ), + .dtype ( sel_t ) + ) i_fifo_w_sel ( + .clk_i, + .rst_ni, + .flush_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .full_o ( w_full ), + .empty_o ( w_empty ), + .usage_o ( /*not used*/ ), + .data_i ( aw_sel ), + .push_i ( w_push ), + .data_o ( w_sel ), + .pop_i ( w_pop ) + ); + // Pop if there is a W transaction on the master port. + assign w_pop = mst_req_o.w_valid & mst_res_i.w_ready; + + // Replicate Data but silence strobe signal. + assign mst_req_o.w = axi_lite_mst_w_t'{ + data: {UpsizeFactor{slv_req_i.w.data}}, + strb: {AxiMstPortStrbWidth{1'b0}} | (slv_req_i.w.strb << (w_sel * AxiSlvPortStrbWidth)), + default: '0 + }; + + // Connect W handshake if the selection is in the FIFO + assign mst_req_o.w_valid = slv_req_i.w_valid & ~w_empty; + assign slv_res_o.w_ready = mst_res_i.w_ready & ~w_empty; + + + // B channel can be passed through + assign slv_res_o.b = mst_res_i.b; + assign slv_res_o.b_valid = mst_res_i.b_valid; + assign mst_req_o.b_ready = slv_req_i.b_ready; + + + // AR channel can be passed through, however block handshake if FIFO is full. + assign mst_req_o.ar = slv_req_i.ar; + // Lock the valid on the master port if it has been given. + logic lock_ar_q, lock_ar_d, load_ar_lock; + // W channel needs a FIFO to determine the silencing of the strobe signal. + logic r_full, r_empty, r_push, r_pop; + sel_t ar_sel, r_sel; + + // AW channel handshake control + always_comb begin : proc_ar_handshake + // default assignment + load_ar_lock = 1'b0; // the FF is toggling back and forth when loaded. + mst_req_o.ar_valid = 1'b0; + slv_res_o.ar_ready = 1'b0; + r_push = 1'b0; + + if (lock_ar_q) begin + mst_req_o.ar_valid = 1'b1; + slv_res_o.ar_ready = mst_res_i.ar_ready; + if (mst_res_i.ar_ready) begin + load_ar_lock = 1'b1; + end + end else begin + // Only connect handshake if there is space in the FIFO + if (!r_full) begin + mst_req_o.ar_valid = slv_req_i.ar_valid; + slv_res_o.ar_ready = mst_res_i.ar_ready; + // If there is a valid on the slave port, push the FIFO + if (slv_req_i.ar_valid) begin + r_push = 1'b1; + // When no transaction, lock AW + if (!mst_res_i.ar_ready) begin + load_ar_lock = 1'b1; + end + end + end + end + end + assign lock_ar_d = ~lock_ar_q; + `FFLARN(lock_ar_q, lock_ar_d, load_ar_lock, 1'b0, clk_i, rst_ni) + + // The selection comes from part of the AW address. + assign ar_sel = sel_t'(slv_req_i.ar.addr >> SelOffset); + + fifo_v3 #( + .FALL_THROUGH ( 1'b1 ), + .DEPTH ( UpsizeFactor ), + .dtype ( sel_t ) + ) i_fifo_r_sel ( + .clk_i, + .rst_ni, + .flush_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .full_o ( r_full ), + .empty_o ( r_empty ), + .usage_o ( /*not used*/ ), + .data_i ( ar_sel ), + .push_i ( r_push ), + .data_o ( r_sel ), + .pop_i ( r_pop ) + ); + // Pop if there is a R transaction on the slave port. + assign r_pop = slv_res_o.r_valid & slv_req_i.r_ready; + + // R channel has to be cut out + assign slv_res_o.r = axi_lite_slv_r_t'{ + data: mst_res_i.r.data[(r_sel*AxiSlvPortDataWidth)+:AxiSlvPortDataWidth], + resp: mst_res_i.r.resp, + default: '0 + }; + // Connect R handshake if there is something in the FIFO. + assign slv_res_o.r_valid = mst_res_i.r_valid & ~r_empty; + assign mst_req_o.r_ready = slv_req_i.r_ready & ~r_empty; + + end else begin : gen_passthrough + assign mst_req_o = slv_req_i; + assign slv_res_o = mst_res_i; + end + + // Assertions, check params + // pragma translate_off + `ifndef VERILATOR + initial begin + assume (AxiAddrWidth > 0) else $fatal(1, "AXI address width has to be > 0"); + assume (AxiSlvPortDataWidth > 8) else $fatal(1, "AxiSlvPortDataWidth has to be > 8"); + assume (AxiMstPortDataWidth > 8) else $fatal(1, "AxiMstPortDataWidth has to be > 8"); + assume ($onehot(AxiSlvPortDataWidth)) else $fatal(1, "AxiSlvPortDataWidth must be power of 2"); + assume ($onehot(AxiMstPortDataWidth)) else $fatal(1, "AxiMstPortDataWidth must be power of 2"); + end + default disable iff (~rst_ni); + stable_aw: assert property (@(posedge clk_i) + (mst_req_o.aw_valid && !mst_res_i.aw_ready) |=> $stable(mst_req_o.aw)) else + $fatal(1, "AW must remain stable until handshake happened."); + stable_w: assert property (@(posedge clk_i) + (mst_req_o.w_valid && !mst_res_i.w_ready) |=> $stable(mst_req_o.w)) else + $fatal(1, "W must remain stable until handshake happened."); + stable_b: assert property (@(posedge clk_i) + (slv_res_o.b_valid && !slv_req_i.b_ready) |=> $stable(slv_res_o.b)) else + $fatal(1, "B must remain stable until handshake happened."); + stable_ar: assert property (@(posedge clk_i) + (mst_req_o.ar_valid && !mst_res_i.ar_ready) |=> $stable(mst_req_o.ar)) else + $fatal(1, "AR must remain stable until handshake happened."); + stable_r: assert property (@(posedge clk_i) + (slv_res_o.r_valid && !slv_req_i.r_ready) |=> $stable(slv_res_o.r)) else + $fatal(1, "R must remain stable until handshake happened."); + `endif + // pragma translate_on +endmodule + +/// Interface wrapper for `axi_lite_dw_converter`. +`include "axi/typedef.svh" +`include "axi/assign.svh" +module axi_lite_dw_converter_intf #( + /// AXI4-Lite address width of the ports. + parameter int unsigned AXI_ADDR_WIDTH = 32'd0, + /// AXI4-Lite data width of the slave port. + parameter int unsigned AXI_SLV_PORT_DATA_WIDTH = 32'd0, + /// AXI4-Lite data width of the master port. + parameter int unsigned AXI_MST_PORT_DATA_WIDTH = 32'd0 +) ( + /// Clock, positive edge triggered. + input logic clk_i, + /// Asynchrounous reset, active low. + input logic rst_ni, + /// Slave port interface. + AXI_LITE.Slave slv, + /// Master port interface. + AXI_LITE.Master mst +); + // AXI configuration + localparam int unsigned AxiStrbWidthSlv = AXI_SLV_PORT_DATA_WIDTH / 32'd8; + localparam int unsigned AxiStrbWidthMst = AXI_MST_PORT_DATA_WIDTH / 32'd8; + // Type definitions + typedef logic [AXI_ADDR_WIDTH-1:0] lite_addr_t; + typedef logic [AXI_SLV_PORT_DATA_WIDTH-1:0] lite_data_slv_t; + typedef logic [AxiStrbWidthSlv-1:0] lite_strb_slv_t; + typedef logic [AXI_MST_PORT_DATA_WIDTH-1:0] lite_data_mst_t; + typedef logic [AxiStrbWidthMst-1:0] lite_strb_mst_t; + + + `AXI_LITE_TYPEDEF_AW_CHAN_T(aw_chan_lite_t, lite_addr_t) + `AXI_LITE_TYPEDEF_W_CHAN_T(w_chan_lite_slv_t, lite_data_slv_t, lite_strb_slv_t) + `AXI_LITE_TYPEDEF_W_CHAN_T(w_chan_lite_mst_t, lite_data_mst_t, lite_strb_mst_t) + `AXI_LITE_TYPEDEF_B_CHAN_T(b_chan_lite_t) + + `AXI_LITE_TYPEDEF_AR_CHAN_T(ar_chan_lite_t, lite_addr_t) + `AXI_LITE_TYPEDEF_R_CHAN_T(r_chan_lite_slv_t, lite_data_slv_t) + `AXI_LITE_TYPEDEF_R_CHAN_T(r_chan_lite_mst_t, lite_data_mst_t) + + + `AXI_LITE_TYPEDEF_REQ_T(req_lite_slv_t, aw_chan_lite_t, w_chan_lite_slv_t, ar_chan_lite_t) + `AXI_LITE_TYPEDEF_RESP_T(res_lite_slv_t, b_chan_lite_t, r_chan_lite_slv_t) + + `AXI_LITE_TYPEDEF_REQ_T(req_lite_mst_t, aw_chan_lite_t, w_chan_lite_mst_t, ar_chan_lite_t) + `AXI_LITE_TYPEDEF_RESP_T(res_lite_mst_t, b_chan_lite_t, r_chan_lite_mst_t) + + req_lite_slv_t slv_req; + res_lite_slv_t slv_res; + req_lite_mst_t mst_req; + res_lite_mst_t mst_res; + + `AXI_LITE_ASSIGN_TO_REQ(slv_req, slv) + `AXI_LITE_ASSIGN_FROM_RESP(slv, slv_res) + `AXI_LITE_ASSIGN_FROM_REQ(mst, mst_req) + `AXI_LITE_ASSIGN_TO_RESP(mst_res, mst) + + axi_lite_dw_converter #( + .AxiAddrWidth ( AXI_ADDR_WIDTH ), + .AxiSlvPortDataWidth ( AXI_SLV_PORT_DATA_WIDTH ), + .AxiMstPortDataWidth ( AXI_MST_PORT_DATA_WIDTH ), + .axi_lite_aw_t ( aw_chan_lite_t ), + .axi_lite_slv_w_t ( w_chan_lite_slv_t ), + .axi_lite_mst_w_t ( w_chan_lite_mst_t ), + .axi_lite_b_t ( b_chan_lite_t ), + .axi_lite_ar_t ( ar_chan_lite_t ), + .axi_lite_slv_r_t ( r_chan_lite_slv_t ), + .axi_lite_mst_r_t ( r_chan_lite_mst_t ), + .axi_lite_slv_req_t ( req_lite_slv_t ), + .axi_lite_slv_res_t ( res_lite_slv_t ), + .axi_lite_mst_req_t ( req_lite_mst_t ), + .axi_lite_mst_res_t ( res_lite_mst_t ) + ) i_axi_lite_dw_converter ( + .clk_i, + .rst_ni, + .slv_req_i ( slv_req ), + .slv_res_o ( slv_res ), + .mst_req_o ( mst_req ), + .mst_res_i ( mst_res ) + ); +endmodule diff --git a/src/axi_test.sv b/src/axi_test.sv index ae74cf1ac..6bb951449 100644 --- a/src/axi_test.sv +++ b/src/axi_test.sv @@ -1561,7 +1561,7 @@ package axi_test; ar_addr = addr_t'($urandom_range(MIN_ADDR, MAX_ADDR)); ar_prot = prot_t'($urandom()); this.ar_queue.push_back(ar_addr); - $display("%0t %s> Send AR with ADDR: %h PROT: %b", $time(), this.name, ar_addr, ar_prot); + // $display("%0t %s> Send AR with ADDR: %h PROT: %b", $time(), this.name, ar_addr, ar_prot); drv.send_ar(ar_addr, ar_prot); end endtask : send_ars @@ -1575,7 +1575,7 @@ package axi_test; ar_addr = this.ar_queue.pop_front(); rand_wait(RESP_MIN_WAIT_CYCLES, RESP_MAX_WAIT_CYCLES); drv.recv_r(r_data, r_resp); - $display("%0t %s> Recv R with DATA: %h RESP: %0h", $time(), this.name, r_data, r_resp); + // $display("%0t %s> Recv R with DATA: %h RESP: %0h", $time(), this.name, r_data, r_resp); end endtask : recv_rs @@ -1587,7 +1587,7 @@ package axi_test; aw_addr = addr_t'($urandom_range(MIN_ADDR, MAX_ADDR)); aw_prot = prot_t'($urandom()); this.aw_queue.push_back(aw_addr); - $display("%0t %s> Send AW with ADDR: %h PROT: %b", $time(), this.name, aw_addr, aw_prot); + // $display("%0t %s> Send AW with ADDR: %h PROT: %b", $time(), this.name, aw_addr, aw_prot); this.drv.send_aw(aw_addr, aw_prot); this.b_queue.push_back(1'b1); end @@ -1604,7 +1604,7 @@ package axi_test; aw_addr = aw_queue.pop_front(); rand_success = std::randomize(w_data); assert(rand_success); rand_success = std::randomize(w_strb); assert(rand_success); - $display("%0t %s> Send W with DATA: %h STRB: %h", $time(), this.name, w_data, w_strb); + // $display("%0t %s> Send W with DATA: %h STRB: %h", $time(), this.name, w_data, w_strb); this.drv.send_w(w_data, w_strb); w_queue.push_back(1'b1); end @@ -1619,7 +1619,7 @@ package axi_test; go_b = this.w_queue.pop_front(); rand_wait(RESP_MIN_WAIT_CYCLES, RESP_MAX_WAIT_CYCLES); this.drv.recv_b(b_resp); - $display("%0t %s> Recv B with RESP: %h", $time(), this.name, b_resp); + // $display("%0t %s> Recv B with RESP: %h", $time(), this.name, b_resp); end endtask : recv_bs @@ -1637,26 +1637,26 @@ package axi_test; // write data to a specific address task automatic write(input addr_t w_addr, input prot_t w_prot = prot_t'(0), input data_t w_data, input strb_t w_strb, output axi_pkg::resp_t b_resp); - $display("%0t %s> Write to ADDR: %h, PROT: %b DATA: %h, STRB: %h", - $time(), this.name, w_addr, w_prot, w_data, w_strb); + // $display("%0t %s> Write to ADDR: %h, PROT: %b DATA: %h, STRB: %h", + // $time(), this.name, w_addr, w_prot, w_data, w_strb); fork this.drv.send_aw(w_addr, w_prot); this.drv.send_w(w_data, w_strb); join this.drv.recv_b(b_resp); - $display("%0t %s> Received write response from ADDR: %h RESP: %h", - $time(), this.name, w_addr, b_resp); + // $display("%0t %s> Received write response from ADDR: %h RESP: %h", + // $time(), this.name, w_addr, b_resp); endtask : write // read data from a specific location task automatic read(input addr_t r_addr, input prot_t r_prot = prot_t'(0), output data_t r_data, output axi_pkg::resp_t r_resp); - $display("%0t %s> Read from ADDR: %h PROT: %b", - $time(), this.name, r_addr, r_prot); + // $display("%0t %s> Read from ADDR: %h PROT: %b", + // $time(), this.name, r_addr, r_prot); this.drv.send_ar(r_addr, r_prot); this.drv.recv_r(r_data, r_resp); - $display("%0t %s> Recieved read response from ADDR: %h DATA: %h RESP: %h", - $time(), this.name, r_addr, r_data, r_resp); + // $display("%0t %s> Recieved read response from ADDR: %h DATA: %h RESP: %h", + // $time(), this.name, r_addr, r_data, r_resp); endtask : read endclass @@ -1722,22 +1722,24 @@ package axi_test; automatic prot_t ar_prot; rand_wait(AX_MIN_WAIT_CYCLES, AX_MAX_WAIT_CYCLES); this.drv.recv_ar(ar_addr, ar_prot); - $display("%0t %s> Recv AR with ADDR: %h PROT: %b", $time(), this.name, ar_addr, ar_prot); + // $display("%0t %s> Recv AR with ADDR: %h PROT: %b", $time(), this.name, ar_addr, ar_prot); this.ar_queue.push_back(ar_addr); end endtask : recv_ars task automatic send_rs(); forever begin - automatic logic rand_success; - automatic addr_t ar_addr; - automatic data_t r_data; + automatic logic rand_success; + automatic addr_t ar_addr; + automatic data_t r_data; + automatic axi_pkg::resp_t r_resp; wait (ar_queue.size() > 0); ar_addr = this.ar_queue.pop_front(); rand_success = std::randomize(r_data); assert(rand_success); + rand_success = std::randomize(r_resp); assert(rand_success); rand_wait(R_MIN_WAIT_CYCLES, R_MAX_WAIT_CYCLES); - $display("%0t %s> Send R with DATA: %h", $time(), this.name, r_data); - this.drv.send_r(r_data, axi_pkg::RESP_OKAY); + // $display("%0t %s> Send R with DATA: %h RESP: %h", $time(), this.name, r_data, r_resp); + this.drv.send_r(r_data, r_resp); end endtask : send_rs @@ -1747,7 +1749,7 @@ package axi_test; automatic prot_t aw_prot; rand_wait(AX_MIN_WAIT_CYCLES, AX_MAX_WAIT_CYCLES); this.drv.recv_aw(aw_addr, aw_prot); - $display("%0t %s> Recv AW with ADDR: %h PROT: %b", $time(), this.name, aw_addr, aw_prot); + // $display("%0t %s> Recv AW with ADDR: %h PROT: %b", $time(), this.name, aw_addr, aw_prot); this.aw_queue.push_back(aw_addr); end endtask : recv_aws @@ -1758,7 +1760,7 @@ package axi_test; automatic strb_t w_strb; rand_wait(RESP_MIN_WAIT_CYCLES, RESP_MAX_WAIT_CYCLES); this.drv.recv_w(w_data, w_strb); - $display("%0t %s> Recv W with DATA: %h SRTB: %h", $time(), this.name, w_data, w_strb); + // $display("%0t %s> Recv W with DATA: %h SRTB: %h", $time(), this.name, w_data, w_strb); this.b_queue.push_back(1'b1); end endtask : recv_ws @@ -1774,7 +1776,7 @@ package axi_test; go_b = this.b_queue.pop_front(); rand_wait(RESP_MIN_WAIT_CYCLES, RESP_MAX_WAIT_CYCLES); rand_success = std::randomize(b_resp); assert(rand_success); - $display("%0t %s> Send B with RESP: %h", $time(), this.name, b_resp); + // $display("%0t %s> Send B with RESP: %h", $time(), this.name, b_resp); this.drv.send_b(b_resp); end endtask : send_bs diff --git a/test/axi_synth_bench.sv b/test/axi_synth_bench.sv index edc77d4b4..334846abc 100644 --- a/test/axi_synth_bench.sv +++ b/test/axi_synth_bench.sv @@ -192,7 +192,18 @@ module axi_synth_bench ( end end + // AXI4-Lite DW converter + for (genvar i = 0; i < 3; i++) begin + for (genvar j = 0; j < 3; j++) begin + localparam int unsigned SLV_DW[3] = {32, 64, 128}; + localparam int unsigned MST_DW[3] = {16, 32, 64}; + synth_axi_lite_dw_converter #( + .AXI_SLV_PORT_DATA_WIDTH (SLV_DW[i]), + .AXI_MST_PORT_DATA_WIDTH (MST_DW[j]) + ) i_axi_lite_dw_converter (.*); + end + end endmodule @@ -680,6 +691,7 @@ module synth_axi_iw_converter # ( ); endmodule + module synth_axi_to_mem_banked #( parameter int unsigned AxiDataWidth = 32'd0, parameter int unsigned BankNum = 32'd0, @@ -747,3 +759,37 @@ module synth_axi_to_mem_banked #( ); endmodule + + +module synth_axi_lite_dw_converter #( + parameter int unsigned AXI_SLV_PORT_DATA_WIDTH = 32'd0, + parameter int unsigned AXI_MST_PORT_DATA_WIDTH = 32'd0 +) ( + input logic clk_i, + input logic rst_ni +); + + localparam int unsigned AXI_ADDR_WIDTH = 32'd64; + + AXI_LITE #( + .AXI_ADDR_WIDTH ( AXI_ADDR_WIDTH ), + .AXI_DATA_WIDTH ( AXI_SLV_PORT_DATA_WIDTH ) + ) slv_intf (); + + AXI_LITE #( + .AXI_ADDR_WIDTH ( AXI_ADDR_WIDTH ), + .AXI_DATA_WIDTH ( AXI_MST_PORT_DATA_WIDTH ) + ) mst_intf (); + + axi_lite_dw_converter_intf #( + .AXI_ADDR_WIDTH ( AXI_ADDR_WIDTH ), + .AXI_SLV_PORT_DATA_WIDTH ( AXI_SLV_PORT_DATA_WIDTH ), + .AXI_MST_PORT_DATA_WIDTH ( AXI_MST_PORT_DATA_WIDTH ) + ) i_axi_lite_dw_converter_intf ( + .clk_i, + .rst_ni, + .slv ( slv_intf ), + .mst ( mst_intf ) + ); + +endmodule diff --git a/test/tb_axi_lite_dw_converter.sv b/test/tb_axi_lite_dw_converter.sv new file mode 100644 index 000000000..65319b0ad --- /dev/null +++ b/test/tb_axi_lite_dw_converter.sv @@ -0,0 +1,450 @@ +// Copyright (c) 2020 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// Author: Wolfgang Roenninger + + +`include "axi/typedef.svh" +`include "axi/assign.svh" +/// Testbench for the AXI4-Lite data width conversion module. +module tb_axi_lite_dw_converter #( + /// Address width of the AXI4-Lite ports. + parameter int unsigned TbAxiAddrWidth = 32'd32, + /// Data Width of the slave port. + parameter int unsigned TbAxiDataWidthSlv = 32'd32, + /// Data Width of the master port. + parameter int unsigned TbAxiDataWidthMst = 32'd128, + /// Number of write transactions. + parameter int unsigned NumWrites = 32'd10000, + /// Number of read tranactions. + parameter int unsigned NumReads = 32'd10000 +); + // Random master no Transactions + // timing parameters + localparam time CyclTime = 10ns; + localparam time ApplTime = 2ns; + localparam time TestTime = 8ns; + // AXI configuration + localparam int unsigned TbAxiStrbWidthSlv = TbAxiDataWidthSlv / 32'd8; + localparam int unsigned TbAxiStrbWidthMst = TbAxiDataWidthMst / 32'd8; + // Type definitions + typedef logic [TbAxiAddrWidth-1:0] addr_t; + typedef logic [TbAxiDataWidthSlv-1:0] data_slv_t; + typedef logic [TbAxiStrbWidthSlv-1:0] strb_slv_t; + typedef logic [TbAxiDataWidthMst-1:0] data_mst_t; + typedef logic [TbAxiStrbWidthMst-1:0] strb_mst_t; + + + `AXI_LITE_TYPEDEF_AW_CHAN_T(aw_chan_lite_t, addr_t) + `AXI_LITE_TYPEDEF_W_CHAN_T(w_chan_lite_slv_t, data_slv_t, strb_slv_t) + `AXI_LITE_TYPEDEF_W_CHAN_T(w_chan_lite_mst_t, data_mst_t, strb_mst_t) + `AXI_LITE_TYPEDEF_B_CHAN_T(b_chan_lite_t) + + `AXI_LITE_TYPEDEF_AR_CHAN_T(ar_chan_lite_t, addr_t) + `AXI_LITE_TYPEDEF_R_CHAN_T(r_chan_lite_slv_t, data_slv_t) + `AXI_LITE_TYPEDEF_R_CHAN_T(r_chan_lite_mst_t, data_mst_t) + + + `AXI_LITE_TYPEDEF_REQ_T(req_lite_slv_t, aw_chan_lite_t, w_chan_lite_slv_t, ar_chan_lite_t) + `AXI_LITE_TYPEDEF_RESP_T(res_lite_slv_t, b_chan_lite_t, r_chan_lite_slv_t) + + `AXI_LITE_TYPEDEF_REQ_T(req_lite_mst_t, aw_chan_lite_t, w_chan_lite_mst_t, ar_chan_lite_t) + `AXI_LITE_TYPEDEF_RESP_T(res_lite_mst_t, b_chan_lite_t, r_chan_lite_mst_t) + + + typedef axi_test::axi_lite_rand_master #( + // AXI interface parameters + .AW ( TbAxiAddrWidth ), + .DW ( TbAxiDataWidthSlv ), + // Stimuli application and test time + .TA ( ApplTime ), + .TT ( TestTime ), + .MIN_ADDR ( 0 ), + .MAX_ADDR ( '1 ), + .MAX_READ_TXNS ( 100 ), + .MAX_WRITE_TXNS ( 100 ) + ) rand_lite_master_t; + typedef axi_test::axi_lite_rand_slave #( + // AXI interface parameters + .AW ( TbAxiAddrWidth ), + .DW ( TbAxiDataWidthMst ), + // Stimuli application and test time + .TA ( ApplTime ), + .TT ( TestTime ) + ) rand_lite_slave_t; + + // ------------- + // DUT signals + // ------------- + logic clk; + // DUT signals + logic rst_n; + logic end_of_sim; + + // master structs + req_lite_slv_t master_req; + res_lite_slv_t master_res; + + // slave structs + req_lite_mst_t slave_req; + res_lite_mst_t slave_res; + + // ------------------------------- + // AXI Interfaces + // ------------------------------- + AXI_LITE #( + .AXI_ADDR_WIDTH ( TbAxiAddrWidth ), + .AXI_DATA_WIDTH ( TbAxiDataWidthSlv ) + ) master (); + AXI_LITE_DV #( + .AXI_ADDR_WIDTH ( TbAxiAddrWidth ), + .AXI_DATA_WIDTH ( TbAxiDataWidthSlv ) + ) master_dv (clk); + `AXI_LITE_ASSIGN(master, master_dv) + `AXI_LITE_ASSIGN_TO_REQ(master_req, master) + `AXI_LITE_ASSIGN_TO_RESP(master_res, master) + + AXI_LITE #( + .AXI_ADDR_WIDTH ( TbAxiAddrWidth ), + .AXI_DATA_WIDTH ( TbAxiDataWidthMst ) + ) slave (); + AXI_LITE_DV #( + .AXI_ADDR_WIDTH ( TbAxiAddrWidth ), + .AXI_DATA_WIDTH ( TbAxiDataWidthMst ) + ) slave_dv (clk); + `AXI_LITE_ASSIGN(slave_dv, slave) + `AXI_LITE_ASSIGN_TO_REQ(slave_req, slave) + `AXI_LITE_ASSIGN_TO_RESP(slave_res, slave) + // ------------------------------- + // AXI Rand Masters and Slaves + // ------------------------------- + // Masters control simulation run time + initial begin : proc_generate_traffic + automatic rand_lite_master_t lite_axi_master = new ( master_dv, "MST_0"); + automatic data_slv_t data = '0; + automatic axi_pkg::resp_t resp = '0; + end_of_sim <= 1'b0; + lite_axi_master.reset(); + @(posedge rst_n); + lite_axi_master.write(32'h0000_1100, axi_pkg::prot_t'('0), data_slv_t'(64'hDEADBEEFDEADBEEF), + strb_slv_t'(8'hFF), resp); + lite_axi_master.read(32'h0000_e100, axi_pkg::prot_t'('0), data, resp); + lite_axi_master.run(NumReads, NumWrites); + end_of_sim <= 1'b1; + end + + initial begin : proc_recieve_traffic + automatic rand_lite_slave_t lite_axi_slave = new( slave_dv , "SLV_0"); + lite_axi_slave.reset(); + @(posedge rst_n); + lite_axi_slave.run(); + end + + // FIFOs for sampling the channels + // Slave Port + aw_chan_lite_t fifo_slv_aw[$]; + w_chan_lite_slv_t fifo_slv_w[$]; + b_chan_lite_t fifo_slv_b[$]; + ar_chan_lite_t fifo_slv_ar[$]; + r_chan_lite_slv_t fifo_slv_r[$]; + + aw_chan_lite_t fifo_mst_aw[$]; + w_chan_lite_mst_t fifo_mst_w[$]; + b_chan_lite_t fifo_mst_b[$]; + ar_chan_lite_t fifo_mst_ar[$]; + r_chan_lite_mst_t fifo_mst_r[$]; + // Sampling processes + initial begin : proc_sample + forever begin + @(posedge clk); + #TestTime; + if (rst_n) begin + if (master_req.aw_valid && master_res.aw_ready) begin + fifo_slv_aw.push_back(master_req.aw); + end + if (master_req.w_valid && master_res.w_ready) begin + fifo_slv_w.push_back(master_req.w); + end + if (master_res.b_valid && master_req.b_ready) begin + fifo_slv_b.push_back(master_res.b); + end + if (master_req.ar_valid && master_res.ar_ready) begin + fifo_slv_ar.push_back(master_req.ar); + end + if (master_res.r_valid && master_req.r_ready) begin + fifo_slv_r.push_back(master_res.r); + end + if (slave_req.aw_valid && slave_res.aw_ready) begin + fifo_mst_aw.push_back(slave_req.aw); + end + if (slave_req.w_valid && slave_res.w_ready) begin + fifo_mst_w.push_back(slave_req.w); + end + if (slave_res.b_valid && slave_req.b_ready) begin + fifo_mst_b.push_back(slave_res.b); + end + if (slave_req.ar_valid && slave_res.ar_ready) begin + fifo_mst_ar.push_back(slave_req.ar); + end + if (slave_res.r_valid && slave_req.r_ready) begin + fifo_mst_r.push_back(slave_res.r); + end + end + end + end + + if (TbAxiDataWidthMst < TbAxiDataWidthSlv) begin : gen_down_gm + localparam int unsigned DataDivFactor = TbAxiDataWidthSlv / TbAxiDataWidthMst; + localparam int unsigned AddrShift = $clog2(TbAxiDataWidthMst/8); + localparam int unsigned SelWidth = $clog2(DataDivFactor); + typedef logic [SelWidth-1:0] sel_t; + aw_chan_lite_t fifo_exp_aw[$]; + w_chan_lite_mst_t fifo_exp_w[$]; + ar_chan_lite_t fifo_exp_ar[$]; + + // Golden model for down conversion + initial begin : proc_write_exp_gen + automatic aw_chan_lite_t chan_aw, exp_aw; + automatic w_chan_lite_slv_t chan_w; + automatic w_chan_lite_mst_t exp_w; + automatic int unsigned num_vectors; // Number of expected transactions generated + automatic addr_t exp_addr; + automatic sel_t w_sel; + + forever begin + wait((fifo_slv_aw.size() > 0) && (fifo_slv_w.size() > 0)); + chan_aw = fifo_slv_aw.pop_front(); + chan_w = fifo_slv_w.pop_front(); + exp_addr = chan_aw.addr; + exp_addr = exp_addr >> (AddrShift + SelWidth); + for (int unsigned i = 0; i < DataDivFactor; i++) begin + // Generate expected AW vector + exp_aw = aw_chan_lite_t'{ + addr: ((exp_addr << SelWidth) + i) << AddrShift, + prot: chan_aw.prot + }; + fifo_exp_aw.push_back(exp_aw); + // Generate expected W vector + exp_w = w_chan_lite_mst_t'{ + data: chan_w.data[i*TbAxiDataWidthMst+:TbAxiDataWidthMst], + strb: chan_w.strb[i*TbAxiStrbWidthMst+:TbAxiStrbWidthMst] + }; + fifo_exp_w.push_back(exp_w); + end + end + end + + initial begin : proc_aw_exp_check + automatic aw_chan_lite_t chan_aw, exp_aw; + forever begin + wait((fifo_exp_aw.size() > 0) && (fifo_mst_aw.size() > 0)); + chan_aw = fifo_mst_aw.pop_front(); + exp_aw = fifo_exp_aw.pop_front(); + assert (chan_aw.addr == exp_aw.addr) else + $error("Master port> AW.addr is not expected: EXP: %h ACT:%h", + exp_aw.addr, chan_aw.addr); + assert (chan_aw.prot == exp_aw.prot) else + $error("Master port> AW.prot is not expected: EXP: %h ACT:%h", + exp_aw.prot, chan_aw.prot); + end + end + initial begin : proc_w_exp_check + automatic w_chan_lite_mst_t chan_w, exp_w; + forever begin + wait((fifo_exp_w.size() > 0) && (fifo_mst_w.size() > 0)); + chan_w = fifo_mst_w.pop_front(); + exp_w = fifo_exp_w.pop_front(); + assert (chan_w.data == exp_w.data) else + $error("Master port> W.data is not expected: EXP: %h ACT:%h", + exp_w.data, chan_w.data); + assert (chan_w.strb == exp_w.strb) else + $error("Master port> W.strb is not expected: EXP: %h ACT:%h", + exp_w.strb, chan_w.strb); + end + end + initial begin : proc_b_exp_check + automatic b_chan_lite_t exp_b, act_b; + forever begin + // wait until there are this many B responses in the sampling FIFO + wait(fifo_mst_b.size() >= DataDivFactor); + exp_b = axi_pkg::RESP_OKAY; + for (int unsigned i = 0; i < DataDivFactor; i++) begin + act_b = fifo_mst_b.pop_front(); + exp_b = exp_b | act_b; + end + // Do the check + wait(fifo_slv_b.size() > 0); + act_b = fifo_slv_b.pop_front(); + assert(exp_b.resp == act_b.resp) else + $error("Slave port> B.resp is not expected: EXP: %h ACT: %h", exp_b.resp, act_b.resp); + end + end + initial begin : proc_ar_exp_check + automatic ar_chan_lite_t chan_ar, exp_ar; + forever begin + wait((fifo_exp_ar.size() > 0) && (fifo_mst_ar.size() > 0)); + chan_ar = fifo_mst_ar.pop_front(); + exp_ar = fifo_exp_ar.pop_front(); + assert (chan_ar.addr == exp_ar.addr) else + $error("Master port> AR.addr is not expected: EXP: %h ACT:%h", + exp_ar.addr, chan_ar.addr); + assert (chan_ar.prot == exp_ar.prot) else + $error("Master port> AR.prot is not expected: EXP: %h ACT:%h", + exp_ar.prot, chan_ar.prot); + end + end + initial begin : proc_r_exp_check + automatic r_chan_lite_slv_t exp_r, act_r; + automatic r_chan_lite_mst_t mst_r; + forever begin + wait(fifo_slv_r.size() > 0); + act_r = fifo_slv_r.pop_front(); + exp_r = r_chan_lite_slv_t'{default: '0}; + // Build the expected R response from the master port fifo. + for (int unsigned i = 0; i < DataDivFactor; i++) begin + mst_r = fifo_mst_r.pop_front(); + exp_r.data[i*TbAxiDataWidthMst+:TbAxiDataWidthMst] = mst_r.data; + exp_r.resp = exp_r.resp | mst_r.resp; + end + assert (act_r.data == exp_r.data) else + $error("Slave port> R.data is not expected: EXP: %h ACT:%h", + exp_r.data, act_r.data); + assert (act_r.resp == exp_r.resp) else + $error("Slave port> R.resp is not expected: EXP: %h ACT:%h", + exp_r.resp, act_r.resp); + end + end + end else if (TbAxiDataWidthMst == TbAxiDataWidthSlv) begin + initial begin : proc_passthrough_check + forever begin + @(posedge clk); + #TestTime; + if (rst_n) begin + assert (master_req == slave_req); + assert (master_res == slave_res); + end + end + end + end else begin + // Upsizer + localparam int unsigned DataMultFactor = TbAxiDataWidthMst / TbAxiDataWidthSlv; + localparam int unsigned AddrShift = $clog2(TbAxiDataWidthSlv/8); + localparam int unsigned SelWidth = $clog2(DataMultFactor); + typedef logic [SelWidth-1:0] sel_t; + + sel_t fifo_exp_r_offs[$]; + + initial begin : proc_aw_w_check + automatic aw_chan_lite_t mst_aw_chan, slv_aw_chan; + automatic w_chan_lite_slv_t slv_w_chan; + automatic w_chan_lite_mst_t mst_w_chan; + automatic addr_t exp_addr; + automatic sel_t w_sel; + automatic logic [TbAxiDataWidthMst/8-1:0] slv_w_strb_concat; + + forever begin + wait((fifo_slv_aw.size() > 0) && (fifo_mst_aw.size() > 0) && (fifo_slv_w.size() > 0) && (fifo_mst_w.size() > 0)); + slv_aw_chan = fifo_slv_aw.pop_front(); + slv_w_chan = fifo_slv_w.pop_front(); + mst_aw_chan = fifo_mst_aw.pop_front(); + mst_w_chan = fifo_mst_w.pop_front(); + w_sel = slv_aw_chan.addr[AddrShift+SelWidth-1:AddrShift]; + + assert (slv_aw_chan == mst_aw_chan) else $error("Master port> AW is not expected: EXP: %h ACT: %h", + slv_aw_chan, mst_aw_chan); + assert (slv_w_chan.data == mst_w_chan.data[TbAxiDataWidthSlv-1:0]) else $error("Master port> W.data is not expected: EXP: %h ACT: %h", + slv_w_chan.data, mst_w_chan.data); + + slv_w_strb_concat = slv_w_chan.strb << (w_sel*TbAxiDataWidthSlv/8); + assert (slv_w_strb_concat == mst_w_chan.strb) else $error("Master port> W.strb is not expected: EXP: %h ACT: %h", + slv_w_chan.strb, mst_w_chan.strb); + end + end + + initial begin : proc_b_check + automatic b_chan_lite_t b_act, b_exp; + forever begin + wait((fifo_slv_b.size() > 0) && (fifo_mst_b.size() > 0)); + b_act = fifo_slv_b.pop_front(); + b_exp = fifo_mst_b.pop_front(); + assert (b_act == b_exp) else $error("Slave port> B.resp is not expected: EXP: %h ACT:%h", + b_exp.resp, b_act.resp); + end + end + + initial begin : proc_ar_check + automatic ar_chan_lite_t mst_ar_chan, slv_ar_chan; + automatic sel_t r_sel; + + forever begin + wait((fifo_slv_ar.size() > 0) && (fifo_mst_ar.size() > 0)); + slv_ar_chan = fifo_slv_ar.pop_front(); + mst_ar_chan = fifo_mst_ar.pop_front(); + r_sel = slv_ar_chan.addr[AddrShift+SelWidth-1:AddrShift]; + fifo_exp_r_offs.push_back(r_sel); + + assert (slv_ar_chan == mst_ar_chan) else $error("Master port> AR is not expected: EXP: %h ACT: %h", + slv_ar_chan, mst_ar_chan); + end + end + + initial begin : proc_r_check + automatic r_chan_lite_slv_t slv_r_chan; + automatic r_chan_lite_mst_t mst_r_chan; + automatic sel_t r_sel; + + forever begin + wait((fifo_slv_r.size() > 0) && (fifo_mst_r.size() > 0) && (fifo_exp_r_offs.size() > 0)); + slv_r_chan = fifo_slv_r.pop_front(); + mst_r_chan = fifo_mst_r.pop_front(); + r_sel = fifo_exp_r_offs.pop_front(); + + assert (slv_r_chan.resp == mst_r_chan.resp) else $error("Slave port> R.resp is not expected: EXP: %h, ACT: %h", + mst_r_chan.resp, slv_r_chan.resp); + assert (slv_r_chan.data == data_slv_t'(mst_r_chan.data >> (r_sel*TbAxiDataWidthSlv))) else $error("Slave port> R.data is not expected: EXP: %h, ACT: %h", + mst_r_chan.data, slv_r_chan.data); + end + end + + end + + + initial begin : proc_stop_sim + wait (end_of_sim); + repeat (1000) @(posedge clk); + $display("Simulation stopped as all Masters transferred their data, Success.",); + $stop(); + end + + //----------------------------------- + // Clock generator + //----------------------------------- + clk_rst_gen #( + .ClkPeriod ( CyclTime ), + .RstClkCycles( 5 ) + ) i_clk_gen ( + .clk_o (clk), + .rst_no(rst_n) + ); + + //----------------------------------- + // DUT + //----------------------------------- + axi_lite_dw_converter_intf #( + .AXI_ADDR_WIDTH ( TbAxiAddrWidth ), + .AXI_SLV_PORT_DATA_WIDTH ( TbAxiDataWidthSlv ), + .AXI_MST_PORT_DATA_WIDTH ( TbAxiDataWidthMst ) + ) i_axi_lite_dw_downsizer_dut ( + .clk_i ( clk ), + .rst_ni ( rst_n ), + .slv ( master ), + .mst ( slave ) + ); +endmodule