From 93de1390967e03a8ecd97c3c442f7dbef284dd68 Mon Sep 17 00:00:00 2001 From: Nils Wistoff Date: Tue, 5 Dec 2023 14:13:43 +0100 Subject: [PATCH] axi: Upgrade to v0.39.1 Signed-off-by: Nils Wistoff --- Makefile | 1 + vendor/pulp-platform/axi/.gitignore | 1 + vendor/pulp-platform/axi/CHANGELOG.md | 78 +- vendor/pulp-platform/axi/README.md | 96 +- vendor/pulp-platform/axi/VERSION | 2 +- .../pulp-platform/axi/include/axi/assign.svh | 42 + vendor/pulp-platform/axi/include/axi/port.svh | 120 +++ .../pulp-platform/axi/include/axi/typedef.svh | 1 + .../axi/src/axi_burst_splitter.sv | 14 +- vendor/pulp-platform/axi/src/axi_cdc.sv | 24 +- vendor/pulp-platform/axi/src/axi_cdc_dst.sv | 59 +- vendor/pulp-platform/axi/src/axi_cdc_src.sv | 61 +- .../pulp-platform/axi/src/axi_chan_compare.sv | 124 ++- vendor/pulp-platform/axi/src/axi_demux.sv | 856 +++--------------- .../pulp-platform/axi/src/axi_demux_simple.sv | 633 +++++++++++++ .../pulp-platform/axi/src/axi_dw_downsizer.sv | 20 +- vendor/pulp-platform/axi/src/axi_from_mem.sv | 2 +- .../pulp-platform/axi/src/axi_id_serialize.sv | 35 +- .../axi/src/axi_lite_dw_converter.sv | 567 ++++++++++++ .../axi/src/axi_lite_from_mem.sv | 1 + vendor/pulp-platform/axi/src/axi_pkg.sv | 107 ++- vendor/pulp-platform/axi/src/axi_test.sv | 282 +++++- .../pulp-platform/axi/src/axi_to_axi_lite.sv | 6 +- .../axi/src/axi_to_detailed_mem.sv | 746 +++++++++++++++ vendor/pulp-platform/axi/src/axi_to_mem.sv | 613 +------------ .../axi/src/axi_to_mem_banked.sv | 2 +- .../axi/src/axi_to_mem_interleaved.sv | 159 +++- .../pulp-platform/axi/src/axi_to_mem_split.sv | 31 +- vendor/pulp-platform_axi.lock.hjson | 2 +- vendor/pulp-platform_axi.vendor.hjson | 2 +- 30 files changed, 3150 insertions(+), 1537 deletions(-) create mode 100644 vendor/pulp-platform/axi/include/axi/port.svh create mode 100644 vendor/pulp-platform/axi/src/axi_demux_simple.sv create mode 100644 vendor/pulp-platform/axi/src/axi_lite_dw_converter.sv create mode 100644 vendor/pulp-platform/axi/src/axi_to_detailed_mem.sv diff --git a/Makefile b/Makefile index c90baa71542..a8122b34484 100644 --- a/Makefile +++ b/Makefile @@ -182,6 +182,7 @@ src := core/include/$(target)_config_pkg.sv vendor/pulp-platform/axi/src/axi_atop_filter.sv \ vendor/pulp-platform/axi/src/axi_err_slv.sv \ vendor/pulp-platform/axi/src/axi_mux.sv \ + vendor/pulp-platform/axi/src/axi_demux_simple.sv \ vendor/pulp-platform/axi/src/axi_demux.sv \ vendor/pulp-platform/axi/src/axi_xbar.sv \ vendor/pulp-platform/common_cells/src/cdc_2phase.sv \ diff --git a/vendor/pulp-platform/axi/.gitignore b/vendor/pulp-platform/axi/.gitignore index 0797ab03755..10c4aecdd7b 100644 --- a/vendor/pulp-platform/axi/.gitignore +++ b/vendor/pulp-platform/axi/.gitignore @@ -6,3 +6,4 @@ /Bender.lock /Bender.local *.log +*.wlf diff --git a/vendor/pulp-platform/axi/CHANGELOG.md b/vendor/pulp-platform/axi/CHANGELOG.md index 4fe0d619271..33d9a078377 100644 --- a/vendor/pulp-platform/axi/CHANGELOG.md +++ b/vendor/pulp-platform/axi/CHANGELOG.md @@ -7,23 +7,79 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## Unreleased +## 0.39.1 - 2023-09-05 + ### Added -- Add `axi_channel_compare.sv`: Non-synthesizable module comparing two AXI channels of the same type -- Add `axi_bus_compare` and `axi_slave_compare`; two synthesizable verification IPs meant to be used - to compare two AXI buses on an FPGA. -- 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. +- `axi_cdc`: Add `SyncStages` parameter. +- `axi_to_mem_interleaved`: Add interface variant. +- `axi_burst_splitter`: Expose `id_queue`'s `FULL_BW` parameter. +- `axi_chan_compare`: Add parameter to allow reordered transactions. +- Add `AXI_HIGHLIGHT` macro to highlight AXI signals. +- Add flat port instantiation macros. + +### Fixed +- `axi_test`: Avoid false negatives for misaligned reads in `axi_scoreboard`. +- `axi_to_detailed_mem`: Ensure proper propagation or `err` and `exokay` signals. + +## 0.39.0 - 2023-07-20 + +### Added +- Synthesizable IPs: + - `axi_bus_compare` and `axi_slave_compare`; two synthesizable verification IPs meant to be used + to compare two AXI buses on an FPGA. + - `axi_lite_from_mem` and `axi_from_mem` acting like SRAMs making AXI4 requests downstream. + - `axi_lite_dw_converter`: Convert the data width of AXI4-Lite transactions. Emits the + appropriate amount of downstream transactions to perform the whole requested access. + - `axi_rw_join` and `axi_rw_split` to split/join the read and write channels of an AXI bus. +- `CT`-macros allowing to instantiate AXI structs with custom channel type names. +- `axi_pkg': Add documentation to `xbar_cfg_t`. +- Testbench IPs: + - `axi_chan_compare.sv`: Non-synthesizable module comparing two AXI channels of the same type + - Add `axi_file_master` to `axi_test`, allowing file-based AXI verification approaches. + - Add `#_width` functions to `axi_test` returning the width of the AXI channels. ### Changed -- `axi_demux`: Replace FIFO between AW and W channel by a register plus a counter. This prevents - AWs from being issued to one master port while Ws from another burst are ongoing to another - master port. This is required to prevents deadlocks due to circular waits downstream. -- `axi_xbar`: Add parameter `PipelineStages` to `axi_pkg::xbar_cfg_t`. This adds `axi_multicuts` - in the crossed connections in the xbar between the demuxes and muxes. -- `axi_pkg`: Add documentation to `xbar_cfg_t`. +- Synthesizable IPs: + - `axi_demux`: Replace FIFO between AW and W channel by a register plus a counter. This prevents + AWs from being issued to one master port while Ws from another burst are ongoing to another + master port. This is required to prevents deadlocks due to circular waits downstream. Removes + `FallThrough` parameter from `axi_demux`. + - Split the `axi_demux` logic and timing decoupling. A new module called `axi_demux_simple` contains + the core logic. + - `axi_dw_downsizer` uses `axi_pkg::RESP_EXOKAY` as a default value. + - Simplify the `casez` in `axi_id_remap`. + - Add optional explicit mapping to the `axi_id_serialize` module. + - Expand `axi_to_mem` to `axi_to_detailed_mem` exposing all of AXI's side-signals; namely `id`, `user`, + `cache`, `prot`, `qos`, `region`, `atop`. Add possibility to inject `err` and `exokay`. + - `axi_xbar`: Add parameter `PipelineStages` to `axi_pkg::xbar_cfg_t`. This adds `axi_multicuts` + in the crossed connections in the `xbar` between the *demuxes* and *muxes*. Improve inline + documentation. + - Move `mem_to_banks` to `common_cells`. +- `axi_pkg`: Improve for better compatibility with *Vivado*. +- `axi_test: + - `axi_lite_rand_slave`: `R` response field is now randomized. + - Remove excessive prints from random master and slave. + - Properly size-align the address. +- `axi_pkg`: Define `localparams` to define AXI type widths. +- Update `common_cells` from version `v1.26.0` to `v1.27.0`. +- Tooling: + - Use `pulp-platform/pulp-actions/gitlab-ci@v2` in the GitHub CI to communicate with the internal CI. + - Bump `DC Shell version` from `2019.12` to `2022.03` + - No longer check *ModelSim* versions `10.7e` and `2021.3`, add `2022.3`. + - More thorough verification runs for the `xbar`. + - Start transitioning from shell script to Makefile to run simulations. +- Use `scripts/update_authors` to update authors, slight manual fixes performed. ### Fixed +- `axi_to_mem_banked`: Reduce hardware by properly setting `UniqueIds`. +- `axi_to_mem_interleaved` and `axi_to_mem_split` properly instantiates a demultiplexer now. + Adds `test_i` port for DFT. +### Breaking Changes +There are breaking changes between `v0.38.0` and `v0.39.0`: +- `axi_demux`: `FallThrough` parameter was removed. +- `axi_xbar`: `axi_pkg::xbar_cfg_t` added `PipelineStages` parameter. +- `axi_to_mem_interleaved` and `axi_to_mem_split`: Added `test_i` input port. ## 0.38.0 - 2022-09-28 diff --git a/vendor/pulp-platform/axi/README.md b/vendor/pulp-platform/axi/README.md index 01270374496..962185172f7 100644 --- a/vendor/pulp-platform/axi/README.md +++ b/vendor/pulp-platform/axi/README.md @@ -1,5 +1,5 @@ # AXI SystemVerilog Modules for High-Performance On-Chip Communication -[![CI status](https://akurth.net/usrv/ig/shields/pipeline/akurth/axi/master.svg)](https://iis-git.ee.ethz.ch/akurth/axi/commits/master) +[![CI status](https://github.com/pulp-platform/axi/actions/workflows/gitlab-ci.yml/badge.svg?branch=master)](https://github.com/pulp-platform/axi/actions/workflows/gitlab-ci.yml?query=branch%3Amaster) [![GitHub tag (latest SemVer)](https://img.shields.io/github/v/tag/pulp-platform/axi?color=blue&label=current&sort=semver)](CHANGELOG.md) [![SHL-0.51 license](https://img.shields.io/badge/license-SHL--0.51-green)](LICENSE) @@ -19,51 +19,54 @@ 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_simple`](src/axi_demux_simple.sv) | Demux without spill registers. | [Doc](doc/axi_demux.md) | +| [`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_slave_compare`](src/axi_slave_compare.sv) | Compares two slave devices. | | +| [`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 @@ -84,6 +87,7 @@ In addition to the modules above, which are available in synthesis and simulatio | [`axi_chan_logger`](src/axi_test.sv) | Logs the transactions of an AXI4(+ATOPs) port to files. | | [`axi_driver`](src/axi_test.sv) | Low-level driver for AXI4(+ATOPs) that can send and receive individual beats on any channel. | | [`axi_dumper`](src/axi_dumper.sv) | Dumps log to file to be interpreted by `axi_dumper_interpret` script for debugging purposes. | +| [`axi_file_master`](src/axi_test.sv) | AXI4 master for file-based testbenches | | [`axi_lite_driver`](src/axi_test.sv) | Low-level driver for AXI4-Lite that can send and receive individual beats on any channel. | | [`axi_lite_rand_master`](src/axi_test.sv) | AXI4-Lite master component that issues random transactions within user-defined constraints. | | [`axi_lite_rand_slave`](src/axi_test.sv) | AXI4-Lite slave component that responds to transactions with constrainable random delays and data. | diff --git a/vendor/pulp-platform/axi/VERSION b/vendor/pulp-platform/axi/VERSION index ca75280b09b..d2e2400ee94 100644 --- a/vendor/pulp-platform/axi/VERSION +++ b/vendor/pulp-platform/axi/VERSION @@ -1 +1 @@ -0.38.0 +0.39.1 diff --git a/vendor/pulp-platform/axi/include/axi/assign.svh b/vendor/pulp-platform/axi/include/axi/assign.svh index 80667eb09fe..739038f6c5f 100644 --- a/vendor/pulp-platform/axi/include/axi/assign.svh +++ b/vendor/pulp-platform/axi/include/axi/assign.svh @@ -652,4 +652,46 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Macros instantiating signal highlighter from common verification +// https://github.com/pulp-platform/common_verification.git +// `AXI_HIGHLIGHT(__name, __aw_t, __w_t, __b_t, __ar_t, __r_t, __req, __resp) +`define AXI_HIGHLIGHT(__name, __aw_t, __w_t, __b_t, __ar_t, __r_t, __req, __resp) \ + signal_highlighter #( \ + .T ( __aw_t ) \ + ) i_signal_highlighter_``__name``_aw ( \ + .ready_i ( __resp.aw_ready ), \ + .valid_i ( __req.aw_valid ), \ + .data_i ( __req.aw ) \ + ); \ + signal_highlighter #( \ + .T ( __w_t ) \ + ) i_signal_highlighter_``__name``_w ( \ + .ready_i ( __resp.w_ready ), \ + .valid_i ( __req.w_valid ), \ + .data_i ( __req.w ) \ + ); \ + signal_highlighter #( \ + .T ( __b_t ) \ + ) i_signal_highlighter_``__name``_b ( \ + .ready_i ( __req.b_ready ), \ + .valid_i ( __resp.b_valid ), \ + .data_i ( __resp.b ) \ + ); \ + signal_highlighter #( \ + .T ( __ar_t ) \ + ) i_signal_highlighter_``__name``_ar ( \ + .ready_i ( __resp.ar_ready ), \ + .valid_i ( __req.ar_valid ), \ + .data_i ( __req.ar ) \ + ); \ + signal_highlighter #( \ + .T ( __r_t ) \ + ) i_signal_highlighter_``__name``_r ( \ + .ready_i ( __req.r_ready ), \ + .valid_i ( __resp.r_valid ), \ + .data_i ( __resp.r ) \ + ); +//////////////////////////////////////////////////////////////////////////////////////////////////// + `endif diff --git a/vendor/pulp-platform/axi/include/axi/port.svh b/vendor/pulp-platform/axi/include/axi/port.svh new file mode 100644 index 00000000000..c3d7a4a7fae --- /dev/null +++ b/vendor/pulp-platform/axi/include/axi/port.svh @@ -0,0 +1,120 @@ +// Copyright (c) 2014-2023 ETH Zurich, 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: +// - Thomas Benz + +// Macros to add AXI ports + +`ifndef AXI_PORT_SVH_ +`define AXI_PORT_SVH_ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Macros creating flat AXI ports +// `AXI_M_PORT(__name, __addr_t, __data_t, __strb_t, __id_t, __aw_user_t, __w_user_t, __b_user_t, __ar_user_t, __r_user_t) +`define AXI_M_PORT(__name, __addr_t, __data_t, __strb_t, __id_t, __aw_user_t, __w_user_t, __b_user_t, __ar_user_t, __r_user_t) \ + output logic m_axi_``__name``_awvalid, \ + output __id_t m_axi_``__name``_awid, \ + output __addr_t m_axi_``__name``_awaddr, \ + output axi_pkg::len_t m_axi_``__name``_awlen, \ + output axi_pkg::size_t m_axi_``__name``_awsize, \ + output axi_pkg::burst_t m_axi_``__name``_awburst, \ + output logic m_axi_``__name``_awlock, \ + output axi_pkg::cache_t m_axi_``__name``_awcache, \ + output axi_pkg::prot_t m_axi_``__name``_awprot, \ + output axi_pkg::qos_t m_axi_``__name``_awqos, \ + output axi_pkg::region_t m_axi_``__name``_awregion, \ + output __aw_user_t m_axi_``__name``_awuser, \ + output logic m_axi_``__name``_wvalid, \ + output __data_t m_axi_``__name``_wdata, \ + output __strb_t m_axi_``__name``_wstrb, \ + output logic m_axi_``__name``_wlast, \ + output __w_user_t m_axi_``__name``_wuser, \ + output logic m_axi_``__name``_bready, \ + output logic m_axi_``__name``_arvalid, \ + output __id_t m_axi_``__name``_arid, \ + output __addr_t m_axi_``__name``_araddr, \ + output axi_pkg::len_t m_axi_``__name``_arlen, \ + output axi_pkg::size_t m_axi_``__name``_arsize, \ + output axi_pkg::burst_t m_axi_``__name``_arburst, \ + output logic m_axi_``__name``_arlock, \ + output axi_pkg::cache_t m_axi_``__name``_arcache, \ + output axi_pkg::prot_t m_axi_``__name``_arprot, \ + output axi_pkg::qos_t m_axi_``__name``_arqos, \ + output axi_pkg::region_t m_axi_``__name``_arregion, \ + output __ar_user_t m_axi_``__name``_aruser, \ + output logic m_axi_``__name``_rready, \ + input logic m_axi_``__name``_awready, \ + input logic m_axi_``__name``_arready, \ + input logic m_axi_``__name``_wready, \ + input logic m_axi_``__name``_bvalid, \ + input __id_t m_axi_``__name``_bid, \ + input axi_pkg::resp_t m_axi_``__name``_bresp, \ + input __b_user_t m_axi_``__name``_buser, \ + input logic m_axi_``__name``_rvalid, \ + input __id_t m_axi_``__name``_rid, \ + input __data_t m_axi_``__name``_rdata, \ + input axi_pkg::resp_t m_axi_``__name``_rresp, \ + input logic m_axi_``__name``_rlast, \ + input __r_user_t m_axi_``__name``_ruser, \ +//////////////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Macros creating flat AXI ports +// `AXI_S_PORT(__name, __addr_t, __data_t, __strb_t, __id_t, __aw_user_t, __w_user_t, __b_user_t, __ar_user_t, __r_user_t) +`define AXI_S_PORT(__name, __addr_t, __data_t, __strb_t, __id_t, __aw_user_t, __w_user_t, __b_user_t, __ar_user_t, __r_user_t) \ + input logic s_axi_``__name``_awvalid, \ + input __id_t s_axi_``__name``_awid, \ + input __addr_t s_axi_``__name``_awaddr, \ + input axi_pkg::len_t s_axi_``__name``_awlen, \ + input axi_pkg::size_t s_axi_``__name``_awsize, \ + input axi_pkg::burst_t s_axi_``__name``_awburst, \ + input logic s_axi_``__name``_awlock, \ + input axi_pkg::cache_t s_axi_``__name``_awcache, \ + input axi_pkg::prot_t s_axi_``__name``_awprot, \ + input axi_pkg::qos_t s_axi_``__name``_awqos, \ + input axi_pkg::region_t s_axi_``__name``_awregion, \ + input __aw_user_t s_axi_``__name``_awuser, \ + input logic s_axi_``__name``_wvalid, \ + input __data_t s_axi_``__name``_wdata, \ + input __strb_t s_axi_``__name``_wstrb, \ + input logic s_axi_``__name``_wlast, \ + input __w_user_t s_axi_``__name``_wuser, \ + input logic s_axi_``__name``_bready, \ + input logic s_axi_``__name``_arvalid, \ + input __id_t s_axi_``__name``_arid, \ + input __addr_t s_axi_``__name``_araddr, \ + input axi_pkg::len_t s_axi_``__name``_arlen, \ + input axi_pkg::size_t s_axi_``__name``_arsize, \ + input axi_pkg::burst_t s_axi_``__name``_arburst, \ + input logic s_axi_``__name``_arlock, \ + input axi_pkg::cache_t s_axi_``__name``_arcache, \ + input axi_pkg::prot_t s_axi_``__name``_arprot, \ + input axi_pkg::qos_t s_axi_``__name``_arqos, \ + input axi_pkg::region_t s_axi_``__name``_arregion, \ + input __ar_user_t s_axi_``__name``_aruser, \ + input logic s_axi_``__name``_rready, \ + output logic s_axi_``__name``_awready, \ + output logic s_axi_``__name``_arready, \ + output logic s_axi_``__name``_wready, \ + output logic s_axi_``__name``_bvalid, \ + output __id_t s_axi_``__name``_bid, \ + output axi_pkg::resp_t s_axi_``__name``_bresp, \ + output __b_user_t s_axi_``__name``_buser, \ + output logic s_axi_``__name``_rvalid, \ + output __id_t s_axi_``__name``_rid, \ + output __data_t s_axi_``__name``_rdata, \ + output axi_pkg::resp_t s_axi_``__name``_rresp, \ + output logic s_axi_``__name``_rlast, \ + output __r_user_t s_axi_``__name``_ruser, \ +//////////////////////////////////////////////////////////////////////////////////////////////////// + +`endif diff --git a/vendor/pulp-platform/axi/include/axi/typedef.svh b/vendor/pulp-platform/axi/include/axi/typedef.svh index 5eec91171f9..648c3fed7a7 100644 --- a/vendor/pulp-platform/axi/include/axi/typedef.svh +++ b/vendor/pulp-platform/axi/include/axi/typedef.svh @@ -11,6 +11,7 @@ // // Authors: // - Andreas Kurth +// - Thomas Benz // - Florian Zaruba // - Wolfgang Roenninger diff --git a/vendor/pulp-platform/axi/src/axi_burst_splitter.sv b/vendor/pulp-platform/axi/src/axi_burst_splitter.sv index 4fe6f7e780e..4f3626ebc9a 100644 --- a/vendor/pulp-platform/axi/src/axi_burst_splitter.sv +++ b/vendor/pulp-platform/axi/src/axi_burst_splitter.sv @@ -30,6 +30,8 @@ module axi_burst_splitter #( parameter int unsigned MaxReadTxns = 32'd0, // Maximum number of AXI write bursts outstanding at the same time parameter int unsigned MaxWriteTxns = 32'd0, + // Internal ID queue can work in two bandwidth modes: see id_queue.sv for details + parameter bit FullBW = 0, // AXI Bus Types parameter int unsigned AddrWidth = 32'd0, parameter int unsigned DataWidth = 32'd0, @@ -139,7 +141,8 @@ module axi_burst_splitter #( axi_burst_splitter_ax_chan #( .chan_t ( aw_chan_t ), .IdWidth ( IdWidth ), - .MaxTxns ( MaxWriteTxns ) + .MaxTxns ( MaxWriteTxns ), + .FullBW ( FullBW ) ) i_axi_burst_splitter_aw_chan ( .clk_i, .rst_ni, @@ -233,7 +236,8 @@ module axi_burst_splitter #( axi_burst_splitter_ax_chan #( .chan_t ( ar_chan_t ), .IdWidth ( IdWidth ), - .MaxTxns ( MaxReadTxns ) + .MaxTxns ( MaxReadTxns ), + .FullBW ( FullBW ) ) i_axi_burst_splitter_ar_chan ( .clk_i, .rst_ni, @@ -346,6 +350,7 @@ module axi_burst_splitter_ax_chan #( parameter type chan_t = logic, parameter int unsigned IdWidth = 0, parameter int unsigned MaxTxns = 0, + parameter bit FullBW = 0, parameter type id_t = logic[IdWidth-1:0] ) ( input logic clk_i, @@ -371,6 +376,7 @@ module axi_burst_splitter_ax_chan #( logic cnt_alloc_req, cnt_alloc_gnt; axi_burst_splitter_counters #( .MaxTxns ( MaxTxns ), + .FullBW ( FullBW ), .IdWidth ( IdWidth ) ) i_axi_burst_splitter_counters ( .clk_i, @@ -459,6 +465,7 @@ endmodule /// Internal module of [`axi_burst_splitter`](module.axi_burst_splitter) to order transactions. module axi_burst_splitter_counters #( parameter int unsigned MaxTxns = 0, + parameter bit FullBW = 0, parameter int unsigned IdWidth = 0, parameter type id_t = logic [IdWidth-1:0] ) ( @@ -517,7 +524,8 @@ module axi_burst_splitter_counters #( id_queue #( .ID_WIDTH ( $bits(id_t) ), .CAPACITY ( MaxTxns ), - .data_t ( cnt_idx_t ) + .data_t ( cnt_idx_t ), + .FULL_BW ( FullBW ) ) i_idq ( .clk_i, .rst_ni, diff --git a/vendor/pulp-platform/axi/src/axi_cdc.sv b/vendor/pulp-platform/axi/src/axi_cdc.sv index 152cbef24e5..81ad588187b 100644 --- a/vendor/pulp-platform/axi/src/axi_cdc.sv +++ b/vendor/pulp-platform/axi/src/axi_cdc.sv @@ -30,7 +30,9 @@ module axi_cdc #( parameter type axi_req_t = logic, // encapsulates request channels parameter type axi_resp_t = logic, // encapsulates request channels /// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH. - parameter int unsigned LogDepth = 1 + parameter int unsigned LogDepth = 1, + /// Number of synchronization registers to insert on the async pointers + parameter int unsigned SyncStages = 2 ) ( // slave side - clocked by `src_clk_i` input logic src_clk_i, @@ -63,7 +65,8 @@ module axi_cdc #( .r_chan_t ( r_chan_t ), .axi_req_t ( axi_req_t ), .axi_resp_t ( axi_resp_t ), - .LogDepth ( LogDepth ) + .LogDepth ( LogDepth ), + .SyncStages ( SyncStages ) ) i_axi_cdc_src ( .src_clk_i, .src_rst_ni, @@ -94,7 +97,8 @@ module axi_cdc #( .r_chan_t ( r_chan_t ), .axi_req_t ( axi_req_t ), .axi_resp_t ( axi_resp_t ), - .LogDepth ( LogDepth ) + .LogDepth ( LogDepth ), + .SyncStages ( SyncStages ) ) i_axi_cdc_dst ( .dst_clk_i, .dst_rst_ni, @@ -129,7 +133,9 @@ module axi_cdc_intf #( parameter int unsigned AXI_DATA_WIDTH = 0, parameter int unsigned AXI_USER_WIDTH = 0, /// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH. - parameter int unsigned LOG_DEPTH = 1 + parameter int unsigned LOG_DEPTH = 1, + /// Number of synchronization registers to insert on the async pointers + parameter int unsigned SYNC_STAGES = 2 ) ( // slave side - clocked by `src_clk_i` input logic src_clk_i, @@ -171,7 +177,8 @@ module axi_cdc_intf #( .r_chan_t ( r_chan_t ), .axi_req_t ( req_t ), .axi_resp_t ( resp_t ), - .LogDepth ( LOG_DEPTH ) + .LogDepth ( LOG_DEPTH ), + .SyncStages ( SYNC_STAGES ) ) i_axi_cdc ( .src_clk_i, .src_rst_ni, @@ -189,7 +196,9 @@ module axi_lite_cdc_intf #( parameter int unsigned AXI_ADDR_WIDTH = 0, parameter int unsigned AXI_DATA_WIDTH = 0, /// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH. - parameter int unsigned LOG_DEPTH = 1 + parameter int unsigned LOG_DEPTH = 1, + /// Number of synchronization registers to insert on the async pointers + parameter int unsigned SYNC_STAGES = 2 ) ( // slave side - clocked by `src_clk_i` input logic src_clk_i, @@ -229,7 +238,8 @@ module axi_lite_cdc_intf #( .r_chan_t ( r_chan_t ), .axi_req_t ( req_t ), .axi_resp_t ( resp_t ), - .LogDepth ( LOG_DEPTH ) + .LogDepth ( LOG_DEPTH ), + .SyncStages ( SYNC_STAGES ) ) i_axi_cdc ( .src_clk_i, .src_rst_ni, diff --git a/vendor/pulp-platform/axi/src/axi_cdc_dst.sv b/vendor/pulp-platform/axi/src/axi_cdc_dst.sv index f252a980011..c3e76bfc1de 100644 --- a/vendor/pulp-platform/axi/src/axi_cdc_dst.sv +++ b/vendor/pulp-platform/axi/src/axi_cdc_dst.sv @@ -24,6 +24,8 @@ module axi_cdc_dst #( /// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH. parameter int unsigned LogDepth = 1, + /// Number of synchronization registers to insert on the async pointers + parameter int unsigned SyncStages = 2, parameter type aw_chan_t = logic, parameter type w_chan_t = logic, parameter type b_chan_t = logic, @@ -63,7 +65,8 @@ module axi_cdc_dst #( // Other tools, such as VCS, have problems with type parameters constructed through `$bits()`. .T ( aw_chan_t ), `endif - .LOG_DEPTH ( LogDepth ) + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) ) i_cdc_fifo_gray_dst_aw ( .async_data_i ( async_data_slave_aw_data_i ), .async_wptr_i ( async_data_slave_aw_wptr_i ), @@ -81,7 +84,8 @@ module axi_cdc_dst #( `else .T ( w_chan_t ), `endif - .LOG_DEPTH ( LogDepth ) + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) ) i_cdc_fifo_gray_dst_w ( .async_data_i ( async_data_slave_w_data_i ), .async_wptr_i ( async_data_slave_w_wptr_i ), @@ -99,7 +103,8 @@ module axi_cdc_dst #( `else .T ( b_chan_t ), `endif - .LOG_DEPTH ( LogDepth ) + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) ) i_cdc_fifo_gray_src_b ( .src_clk_i ( dst_clk_i ), .src_rst_ni ( dst_rst_ni ), @@ -117,7 +122,8 @@ module axi_cdc_dst #( `else .T ( ar_chan_t ), `endif - .LOG_DEPTH ( LogDepth ) + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) ) i_cdc_fifo_gray_dst_ar ( .dst_clk_i, .dst_rst_ni, @@ -135,7 +141,8 @@ module axi_cdc_dst #( `else .T ( r_chan_t ), `endif - .LOG_DEPTH ( LogDepth ) + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) ) i_cdc_fifo_gray_src_r ( .src_clk_i ( dst_clk_i ), .src_rst_ni ( dst_rst_ni ), @@ -156,7 +163,9 @@ module axi_cdc_dst_intf #( parameter int unsigned AXI_DATA_WIDTH = 0, parameter int unsigned AXI_USER_WIDTH = 0, /// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH. - parameter int unsigned LOG_DEPTH = 1 + parameter int unsigned LOG_DEPTH = 1, + /// Number of synchronization registers to insert on the async pointers + parameter int unsigned SYNC_STAGES = 2 ) ( // asynchronous slave port AXI_BUS_ASYNC_GRAY.Slave src, @@ -183,14 +192,15 @@ module axi_cdc_dst_intf #( resp_t dst_resp; axi_cdc_dst #( - .aw_chan_t ( aw_chan_t ), - .w_chan_t ( w_chan_t ), - .b_chan_t ( b_chan_t ), - .ar_chan_t ( ar_chan_t ), - .r_chan_t ( r_chan_t ), - .axi_req_t ( req_t ), - .axi_resp_t ( resp_t ), - .LogDepth ( LOG_DEPTH ) + .aw_chan_t ( aw_chan_t ), + .w_chan_t ( w_chan_t ), + .b_chan_t ( b_chan_t ), + .ar_chan_t ( ar_chan_t ), + .r_chan_t ( r_chan_t ), + .axi_req_t ( req_t ), + .axi_resp_t ( resp_t ), + .LogDepth ( LOG_DEPTH ), + .SyncStages ( SYNC_STAGES ) ) i_axi_cdc_dst ( .async_data_slave_aw_data_i ( src.aw_data ), .async_data_slave_aw_wptr_i ( src.aw_wptr ), @@ -223,7 +233,9 @@ module axi_lite_cdc_dst_intf #( parameter int unsigned AXI_ADDR_WIDTH = 0, parameter int unsigned AXI_DATA_WIDTH = 0, /// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH. - parameter int unsigned LOG_DEPTH = 1 + parameter int unsigned LOG_DEPTH = 1, + /// Number of synchronization registers to insert on the async pointers + parameter int unsigned SYNC_STAGES = 2 ) ( // asynchronous slave port AXI_LITE_ASYNC_GRAY.Slave src, @@ -248,14 +260,15 @@ module axi_lite_cdc_dst_intf #( resp_t dst_resp; axi_cdc_dst #( - .aw_chan_t ( aw_chan_t ), - .w_chan_t ( w_chan_t ), - .b_chan_t ( b_chan_t ), - .ar_chan_t ( ar_chan_t ), - .r_chan_t ( r_chan_t ), - .axi_req_t ( req_t ), - .axi_resp_t ( resp_t ), - .LogDepth ( LOG_DEPTH ) + .aw_chan_t ( aw_chan_t ), + .w_chan_t ( w_chan_t ), + .b_chan_t ( b_chan_t ), + .ar_chan_t ( ar_chan_t ), + .r_chan_t ( r_chan_t ), + .axi_req_t ( req_t ), + .axi_resp_t ( resp_t ), + .LogDepth ( LOG_DEPTH ), + .SyncStages ( SYNC_STAGES ) ) i_axi_cdc_dst ( .async_data_slave_aw_data_i ( src.aw_data ), .async_data_slave_aw_wptr_i ( src.aw_wptr ), diff --git a/vendor/pulp-platform/axi/src/axi_cdc_src.sv b/vendor/pulp-platform/axi/src/axi_cdc_src.sv index 614a17e45dd..36e4e611940 100644 --- a/vendor/pulp-platform/axi/src/axi_cdc_src.sv +++ b/vendor/pulp-platform/axi/src/axi_cdc_src.sv @@ -23,7 +23,9 @@ /// the FIFO; see the header of `cdc_fifo_gray` for instructions. module axi_cdc_src #( /// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH. - parameter int unsigned LogDepth = 1, + parameter int unsigned LogDepth = 1, + /// Number of synchronization registers to insert on the async pointers + parameter int unsigned SyncStages = 2, parameter type aw_chan_t = logic, parameter type w_chan_t = logic, parameter type b_chan_t = logic, @@ -62,7 +64,8 @@ module axi_cdc_src #( `else .T ( aw_chan_t ), `endif - .LOG_DEPTH ( LogDepth ) + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) ) i_cdc_fifo_gray_src_aw ( .src_clk_i, .src_rst_ni, @@ -80,7 +83,8 @@ module axi_cdc_src #( `else .T ( w_chan_t ), `endif - .LOG_DEPTH ( LogDepth ) + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) ) i_cdc_fifo_gray_src_w ( .src_clk_i, .src_rst_ni, @@ -98,7 +102,8 @@ module axi_cdc_src #( `else .T ( b_chan_t ), `endif - .LOG_DEPTH ( LogDepth ) + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) ) i_cdc_fifo_gray_dst_b ( .dst_clk_i ( src_clk_i ), .dst_rst_ni ( src_rst_ni ), @@ -116,7 +121,8 @@ module axi_cdc_src #( `else .T ( ar_chan_t ), `endif - .LOG_DEPTH ( LogDepth ) + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) ) i_cdc_fifo_gray_src_ar ( .src_clk_i, .src_rst_ni, @@ -134,7 +140,8 @@ module axi_cdc_src #( `else .T ( r_chan_t ), `endif - .LOG_DEPTH ( LogDepth ) + .LOG_DEPTH ( LogDepth ), + .SYNC_STAGES ( SyncStages ) ) i_cdc_fifo_gray_dst_r ( .dst_clk_i ( src_clk_i ), .dst_rst_ni ( src_rst_ni ), @@ -155,7 +162,9 @@ module axi_cdc_src_intf #( parameter int unsigned AXI_DATA_WIDTH = 0, parameter int unsigned AXI_USER_WIDTH = 0, /// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH. - parameter int unsigned LOG_DEPTH = 1 + parameter int unsigned LOG_DEPTH = 1, + /// Number of synchronization registers to insert on the async pointers + parameter int unsigned SYNC_STAGES = 2 ) ( // synchronous slave port - clocked by `src_clk_i` input logic src_clk_i, @@ -185,14 +194,15 @@ module axi_cdc_src_intf #( `AXI_ASSIGN_FROM_RESP(src, src_resp) axi_cdc_src #( - .aw_chan_t ( aw_chan_t ), - .w_chan_t ( w_chan_t ), - .b_chan_t ( b_chan_t ), - .ar_chan_t ( ar_chan_t ), - .r_chan_t ( r_chan_t ), - .axi_req_t ( req_t ), - .axi_resp_t ( resp_t ), - .LogDepth ( LOG_DEPTH ) + .aw_chan_t ( aw_chan_t ), + .w_chan_t ( w_chan_t ), + .b_chan_t ( b_chan_t ), + .ar_chan_t ( ar_chan_t ), + .r_chan_t ( r_chan_t ), + .axi_req_t ( req_t ), + .axi_resp_t ( resp_t ), + .LogDepth ( LOG_DEPTH ), + .SyncStages ( SYNC_STAGES ) ) i_axi_cdc_src ( .src_clk_i, .src_rst_ni, @@ -222,7 +232,9 @@ module axi_lite_cdc_src_intf #( parameter int unsigned AXI_ADDR_WIDTH = 0, parameter int unsigned AXI_DATA_WIDTH = 0, /// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH. - parameter int unsigned LOG_DEPTH = 1 + parameter int unsigned LOG_DEPTH = 1, + /// Number of synchronization registers to insert on the async pointers + parameter int unsigned SYNC_STAGES = 2 ) ( // synchronous slave port - clocked by `src_clk_i` input logic src_clk_i, @@ -250,14 +262,15 @@ module axi_lite_cdc_src_intf #( `AXI_LITE_ASSIGN_FROM_RESP(src, src_resp) axi_cdc_src #( - .aw_chan_t ( aw_chan_t ), - .w_chan_t ( w_chan_t ), - .b_chan_t ( b_chan_t ), - .ar_chan_t ( ar_chan_t ), - .r_chan_t ( r_chan_t ), - .axi_req_t ( req_t ), - .axi_resp_t ( resp_t ), - .LogDepth ( LOG_DEPTH ) + .aw_chan_t ( aw_chan_t ), + .w_chan_t ( w_chan_t ), + .b_chan_t ( b_chan_t ), + .ar_chan_t ( ar_chan_t ), + .r_chan_t ( r_chan_t ), + .axi_req_t ( req_t ), + .axi_resp_t ( resp_t ), + .LogDepth ( LOG_DEPTH ), + .SyncStages ( SYNC_STAGES ) ) i_axi_cdc_src ( .src_clk_i, .src_rst_ni, diff --git a/vendor/pulp-platform/axi/src/axi_chan_compare.sv b/vendor/pulp-platform/axi/src/axi_chan_compare.sv index 4de4ea7415e..1e4e8234776 100644 --- a/vendor/pulp-platform/axi/src/axi_chan_compare.sv +++ b/vendor/pulp-platform/axi/src/axi_chan_compare.sv @@ -9,12 +9,19 @@ // specific language governing permissions and limitations under the License. // // Authors: -// - Thomas Benz +// - Thomas Benz // - Paul Scheffler -// - Tim Fischer +// - Tim Fischer /// Non-synthesizable module comparing two AXI channels of the same type module axi_chan_compare #( + /// Ignore ID field if it was remapped + parameter bit IgnoreId = 1'b0, + /// Allow reordered responses of different IDs, + /// not compatible with `IgnoreId` + parameter bit AllowReordering = 1'b0, + /// AXI ID Width + parameter int unsigned IdWidth = 1, parameter type aw_chan_t = logic, parameter type w_chan_t = logic, parameter type b_chan_t = logic, @@ -121,66 +128,93 @@ module axi_chan_compare #( // verilog_lint: waive-stop line-length endfunction + localparam NumIds = (AllowReordering)? 2**IdWidth : 1; + // queues - aw_chan_t aw_queue [$]; - w_chan_t w_queue [$]; - b_chan_t b_queue [$]; - ar_chan_t ar_queue [$]; - r_chan_t r_queue [$]; + aw_chan_t aw_queue [NumIds-1:0][$]; + w_chan_t w_queue [$]; + b_chan_t b_queue [NumIds-1:0][$]; + ar_chan_t ar_queue [NumIds-1:0][$]; + r_chan_t r_queue [NumIds-1:0][$]; // requests generated at axi A: enqueue elements always_ff @(posedge clk_a_i) begin : proc_enqueue_a // aw if (axi_a_req.aw_valid & axi_a_res.aw_ready) - aw_queue.push_back(axi_a_req.aw); + if (AllowReordering) aw_queue[axi_a_req.aw.id].push_back(axi_a_req.aw); + else aw_queue[0].push_back(axi_a_req.aw); // w if (axi_a_req.w_valid & axi_a_res.w_ready) w_queue.push_back(axi_a_req.w); // ar if (axi_a_req.ar_valid & axi_a_res.ar_ready) - ar_queue.push_back(axi_a_req.ar); + if (AllowReordering) ar_queue[axi_a_req.ar.id].push_back(axi_a_req.ar); + else ar_queue[0].push_back(axi_a_req.ar); end // responses generated at axi B: enqueue elements always_ff @(posedge clk_b_i) begin : proc_enqueue_b // b if (axi_b_res.b_valid & axi_b_req.b_ready) - b_queue.push_back(axi_b_res.b); + if (AllowReordering) b_queue[axi_b_res.b.id].push_back(axi_b_res.b); + else b_queue[0].push_back(axi_b_res.b); // r if (axi_b_res.r_valid & axi_b_req.r_ready) - r_queue.push_back(axi_b_res.r); + if (AllowReordering) r_queue[axi_b_res.r.id].push_back(axi_b_res.r); + else r_queue[0].push_back(axi_b_res.r); end // requests arriving at axi B from A: dequeue elements and check always_ff @(posedge clk_b_i) begin : proc_dequeue_and_check_b // aw if (axi_b_req.aw_valid & axi_b_res.aw_ready) begin - automatic aw_chan_t aw; - if (aw_queue.size() == 0) $error("AW queue is empty!"); - aw = aw_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking - if (axi_b_req.aw !== aw) begin + automatic aw_chan_t aw_exp, aw_recv; + if (AllowReordering) begin + if (aw_queue[axi_b_req.aw.id].size() == 0) $error("AW queue is empty!"); + aw_exp = aw_queue[axi_b_req.aw.id].pop_front(); // verilog_lint: waive always-ff-non-blocking + end else begin + if (aw_queue[0].size() == 0) $error("AW queue is empty!"); + aw_exp = aw_queue[0].pop_front(); // verilog_lint: waive always-ff-non-blocking + end + aw_recv = axi_b_req.aw; + if (IgnoreId) begin + aw_exp.id = 'X; + aw_recv.id = 'X; + end + if (aw_exp !== aw_recv) begin $error("AW mismatch!"); - print_aw(aw, axi_b_req.aw); + print_aw(aw_exp, aw_recv); end end // w if (axi_b_req.w_valid & axi_b_res.w_ready) begin - automatic w_chan_t w; + automatic w_chan_t w_exp, w_recv; if (w_queue.size() == 0) $error("W queue is empty!"); - w = w_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking - if (axi_b_req.w !== w) begin + w_exp = w_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking + w_recv = axi_b_req.w; + if (w_exp !== w_recv) begin $error("W mismatch!"); - print_w(w, axi_b_req.w); + print_w(w_exp, w_recv); end end // ar if (axi_b_req.ar_valid & axi_b_res.ar_ready) begin - automatic ar_chan_t ar; - if (ar_queue.size() == 0) $error("AR queue is empty!"); - ar = ar_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking - if (axi_b_req.ar !== ar) begin + automatic ar_chan_t ar_exp, ar_recv; + if (AllowReordering) begin + if (ar_queue[axi_b_req.ar.id].size() == 0) $error("AR queue is empty!"); + ar_exp = ar_queue[axi_b_req.ar.id].pop_front(); // verilog_lint: waive always-ff-non-blocking + end else begin + if (ar_queue[0].size() == 0) $error("AR queue is empty!"); + ar_exp = ar_queue[0].pop_front(); // verilog_lint: waive always-ff-non-blocking + end + ar_recv = axi_b_req.ar; + if (IgnoreId) begin + ar_exp.id = 'X; + ar_recv.id = 'X; + end + if (ar_exp !== ar_recv) begin $error("AR mismatch!"); - print_ar(ar, axi_b_req.ar); + print_ar(ar_exp, ar_recv); end end end @@ -189,22 +223,42 @@ module axi_chan_compare #( always_ff @(posedge clk_a_i) begin : proc_dequeue_and_check_a // b if (axi_a_res.b_valid & axi_a_req.b_ready) begin - automatic b_chan_t b; - if (b_queue.size() == 0) $error("B queue is empty!"); - b = b_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking - if (axi_a_res.b !== b) begin + automatic b_chan_t b_exp, b_recv; + if (AllowReordering) begin + if (b_queue[axi_a_res.b.id].size() == 0) $error("B queue is empty!"); + b_exp = b_queue[axi_a_res.b.id].pop_front(); // verilog_lint: waive always-ff-non-blocking + end else begin + if (b_queue[0].size() == 0) $error("B queue is empty!"); + b_exp = b_queue[0].pop_front(); // verilog_lint: waive always-ff-non-blocking + end + b_recv = axi_a_res.b; + if (IgnoreId) begin + b_exp.id = 'X; + b_recv.id = 'X; + end + if (b_exp !== b_recv) begin $error("B mismatch!"); - print_b(b, axi_a_res.b); + print_b(b_exp, b_recv); end end // r if (axi_a_res.r_valid & axi_a_req.r_ready) begin - automatic r_chan_t r; - if (r_queue.size() == 0) $error("R queue is empty!"); - r = r_queue.pop_front(); // verilog_lint: waive always-ff-non-blocking - if (axi_a_res.r !== r) begin + automatic r_chan_t r_exp, r_recv; + if (AllowReordering) begin + if (r_queue[axi_a_res.r.id].size() == 0) $error("R queue is empty!"); + r_exp = r_queue[axi_a_res.r.id].pop_front(); // verilog_lint: waive always-ff-non-blocking + end else begin + if (r_queue[0].size() == 0) $error("R queue is empty!"); + r_exp = r_queue[0].pop_front(); // verilog_lint: waive always-ff-non-blocking + end + r_recv = axi_a_res.r; + if (IgnoreId) begin + r_exp.id = 'X; + r_recv.id = 'X; + end + if (r_exp !== r_recv) begin $error("R mismatch!"); - print_r(r, axi_a_res.r); + print_r(r_exp, r_recv); end end end diff --git a/vendor/pulp-platform/axi/src/axi_demux.sv b/vendor/pulp-platform/axi/src/axi_demux.sv index fc061ff09e7..899d835210d 100644 --- a/vendor/pulp-platform/axi/src/axi_demux.sv +++ b/vendor/pulp-platform/axi/src/axi_demux.sv @@ -9,7 +9,9 @@ // specific language governing permissions and limitations under the License. // // Authors: +// - Michael Rogenmoser // - Wolfgang Roenninger +// - Thomas Benz // - Andreas Kurth `include "common_cells/assertions.svh" @@ -73,735 +75,139 @@ module axi_demux #( input axi_resp_t [NoMstPorts-1:0] mst_resps_i ); - localparam int unsigned IdCounterWidth = cf_math_pkg::idx_width(MaxTrans); - typedef logic [IdCounterWidth-1:0] id_cnt_t; - - - // pass through if only one master port - if (NoMstPorts == 32'h1) begin : gen_no_demux - spill_register #( - .T ( aw_chan_t ), - .Bypass ( ~SpillAw ) - ) i_aw_spill_reg ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .valid_i ( slv_req_i.aw_valid ), - .ready_o ( slv_resp_o.aw_ready ), - .data_i ( slv_req_i.aw ), - .valid_o ( mst_reqs_o[0].aw_valid ), - .ready_i ( mst_resps_i[0].aw_ready ), - .data_o ( mst_reqs_o[0].aw ) - ); - spill_register #( - .T ( w_chan_t ), - .Bypass ( ~SpillW ) - ) i_w_spill_reg ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .valid_i ( slv_req_i.w_valid ), - .ready_o ( slv_resp_o.w_ready ), - .data_i ( slv_req_i.w ), - .valid_o ( mst_reqs_o[0].w_valid ), - .ready_i ( mst_resps_i[0].w_ready ), - .data_o ( mst_reqs_o[0].w ) - ); - spill_register #( - .T ( b_chan_t ), - .Bypass ( ~SpillB ) - ) i_b_spill_reg ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .valid_i ( mst_resps_i[0].b_valid ), - .ready_o ( mst_reqs_o[0].b_ready ), - .data_i ( mst_resps_i[0].b ), - .valid_o ( slv_resp_o.b_valid ), - .ready_i ( slv_req_i.b_ready ), - .data_o ( slv_resp_o.b ) - ); - spill_register #( - .T ( ar_chan_t ), - .Bypass ( ~SpillAr ) - ) i_ar_spill_reg ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .valid_i ( slv_req_i.ar_valid ), - .ready_o ( slv_resp_o.ar_ready ), - .data_i ( slv_req_i.ar ), - .valid_o ( mst_reqs_o[0].ar_valid ), - .ready_i ( mst_resps_i[0].ar_ready ), - .data_o ( mst_reqs_o[0].ar ) - ); - spill_register #( - .T ( r_chan_t ), - .Bypass ( ~SpillR ) - ) i_r_spill_reg ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .valid_i ( mst_resps_i[0].r_valid ), - .ready_o ( mst_reqs_o[0].r_ready ), - .data_i ( mst_resps_i[0].r ), - .valid_o ( slv_resp_o.r_valid ), - .ready_i ( slv_req_i.r_ready ), - .data_o ( slv_resp_o.r ) - ); - - // other non degenerate cases - end else begin : gen_demux - - //-------------------------------------- - //-------------------------------------- - // Signal Declarations - //-------------------------------------- - //-------------------------------------- - - //-------------------------------------- - // Write Transaction - //-------------------------------------- - // comes from spill register at input - aw_chan_t slv_aw_chan; - select_t slv_aw_select; - - logic slv_aw_valid, slv_aw_valid_chan, slv_aw_valid_sel; - logic slv_aw_ready, slv_aw_ready_chan, slv_aw_ready_sel; - - // AW ID counter - select_t lookup_aw_select; - logic aw_select_occupied, aw_id_cnt_full; - // Upon an ATOP load, inject IDs from the AW into the AR channel - logic atop_inject; - - // W select counter: stores the decision to which master W beats should go - select_t w_select, w_select_q; - logic w_select_valid; - id_cnt_t w_open; - logic w_cnt_up, w_cnt_down; - - // Register which locks the AW valid signal - logic lock_aw_valid_d, lock_aw_valid_q, load_aw_lock; - logic aw_valid, aw_ready; - - // W channel from spill reg - w_chan_t slv_w_chan; - logic slv_w_valid, slv_w_ready; - - // B channles input into the arbitration - b_chan_t [NoMstPorts-1:0] mst_b_chans; - logic [NoMstPorts-1:0] mst_b_valids, mst_b_readies; - - // B channel to spill register - b_chan_t slv_b_chan; - logic slv_b_valid, slv_b_ready; - - //-------------------------------------- - // Read Transaction - //-------------------------------------- - // comes from spill register at input - logic slv_ar_valid, ar_valid_chan, ar_valid_sel; - logic slv_ar_ready, slv_ar_ready_chan, slv_ar_ready_sel; - - // AR ID counter - select_t lookup_ar_select; - logic ar_select_occupied, ar_id_cnt_full; - logic ar_push; - - // Register which locks the AR valid signel - logic lock_ar_valid_d, lock_ar_valid_q, load_ar_lock; - logic ar_valid, ar_ready; - - // R channles input into the arbitration - r_chan_t [NoMstPorts-1:0] mst_r_chans; - logic [NoMstPorts-1:0] mst_r_valids, mst_r_readies; - - // R channel to spill register - r_chan_t slv_r_chan; - logic slv_r_valid, slv_r_ready; - - //-------------------------------------- - //-------------------------------------- - // Channel Control - //-------------------------------------- - //-------------------------------------- - - //-------------------------------------- - // AW Channel - //-------------------------------------- - // spill register at the channel input - spill_register #( - .T ( aw_chan_t ), - .Bypass ( ~SpillAw ) // because module param indicates if we want a spill reg - ) i_aw_channel_spill_reg ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .valid_i ( slv_req_i.aw_valid ), - .ready_o ( slv_aw_ready_chan ), - .data_i ( slv_req_i.aw ), - .valid_o ( slv_aw_valid_chan ), - .ready_i ( slv_aw_ready ), - .data_o ( slv_aw_chan ) - ); - spill_register #( - .T ( select_t ), - .Bypass ( ~SpillAw ) // because module param indicates if we want a spill reg - ) i_aw_select_spill_reg ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .valid_i ( slv_req_i.aw_valid ), - .ready_o ( slv_aw_ready_sel ), - .data_i ( slv_aw_select_i ), - .valid_o ( slv_aw_valid_sel ), - .ready_i ( slv_aw_ready ), - .data_o ( slv_aw_select ) - ); - assign slv_resp_o.aw_ready = slv_aw_ready_chan & slv_aw_ready_sel; - assign slv_aw_valid = slv_aw_valid_chan & slv_aw_valid_sel; - - // Control of the AW handshake - always_comb begin - // AXI Handshakes - slv_aw_ready = 1'b0; - aw_valid = 1'b0; - // `lock_aw_valid`, used to be protocol conform as it is not allowed to deassert - // a valid if there was no corresponding ready. As this process has to be able to inject - // an AXI ID into the counter of the AR channel on an ATOP, there could be a case where - // this process waits on `aw_ready` but in the mean time on the AR channel the counter gets - // full. - lock_aw_valid_d = lock_aw_valid_q; - load_aw_lock = 1'b0; - // AW ID counter and W FIFO - w_cnt_up = 1'b0; - // ATOP injection into ar counter - atop_inject = 1'b0; - // we had an arbitration decision, the valid is locked, wait for the transaction - if (lock_aw_valid_q) begin - aw_valid = 1'b1; - // transaction - if (aw_ready) begin - slv_aw_ready = 1'b1; - lock_aw_valid_d = 1'b0; - load_aw_lock = 1'b1; - // inject the ATOP if necessary - atop_inject = slv_aw_chan.atop[axi_pkg::ATOP_R_RESP] & AtopSupport; - end - end else begin - // An AW can be handled if `i_aw_id_counter` and `i_counter_open_w` are not full. An ATOP that - // requires an R response can be handled if additionally `i_ar_id_counter` is not full (this - // only applies if ATOPs are supported at all). - if (!aw_id_cnt_full && (w_open != {IdCounterWidth{1'b1}}) && - (!(ar_id_cnt_full && slv_aw_chan.atop[axi_pkg::ATOP_R_RESP]) || - !AtopSupport)) begin - // There is a valid AW vector make the id lookup and go further, if it passes. - // Also stall if previous transmitted AWs still have active W's in flight. - // This prevents deadlocking of the W channel. The counters are there for the - // Handling of the B responses. - if (slv_aw_valid && - ((w_open == '0) || (w_select == slv_aw_select)) && - (!aw_select_occupied || (slv_aw_select == lookup_aw_select))) begin - // connect the handshake - aw_valid = 1'b1; - // push arbitration to the W FIFO regardless, do not wait for the AW transaction - w_cnt_up = 1'b1; - // on AW transaction - if (aw_ready) begin - slv_aw_ready = 1'b1; - atop_inject = slv_aw_chan.atop[axi_pkg::ATOP_R_RESP] & AtopSupport; - // no AW transaction this cycle, lock the decision - end else begin - lock_aw_valid_d = 1'b1; - load_aw_lock = 1'b1; - end - end - end - end - end - - // lock the valid signal, as the selection gets pushed into the W FIFO on first assertion, - // prevent further pushing - `FFLARN(lock_aw_valid_q, lock_aw_valid_d, load_aw_lock, '0, clk_i, rst_ni) - - if (UniqueIds) begin : gen_unique_ids_aw - // If the `UniqueIds` parameter is set, each write transaction has an ID that is unique among - // all in-flight write transactions, or all write transactions with a given ID target the same - // master port as all write transactions with the same ID, or both. This means that the - // signals that are driven by the ID counters if this parameter is not set can instead be - // derived from existing signals. The ID counters can therefore be omitted. - assign lookup_aw_select = slv_aw_select; - assign aw_select_occupied = 1'b0; - assign aw_id_cnt_full = 1'b0; - end else begin : gen_aw_id_counter - axi_demux_id_counters #( - .AxiIdBits ( AxiLookBits ), - .CounterWidth ( IdCounterWidth ), - .mst_port_select_t ( select_t ) - ) i_aw_id_counter ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .lookup_axi_id_i ( slv_aw_chan.id[0+:AxiLookBits] ), - .lookup_mst_select_o ( lookup_aw_select ), - .lookup_mst_select_occupied_o ( aw_select_occupied ), - .full_o ( aw_id_cnt_full ), - .inject_axi_id_i ( '0 ), - .inject_i ( 1'b0 ), - .push_axi_id_i ( slv_aw_chan.id[0+:AxiLookBits] ), - .push_mst_select_i ( slv_aw_select ), - .push_i ( w_cnt_up ), - .pop_axi_id_i ( slv_b_chan.id[0+:AxiLookBits] ), - .pop_i ( slv_b_valid & slv_b_ready ) - ); - // pop from ID counter on outward transaction - end - - // This counter steers the demultiplexer of the W channel. - // `w_select` determines, which handshaking is connected. - // AWs are only forwarded, if the counter is empty, or `w_select_q` is the same as - // `slv_aw_select`. - counter #( - .WIDTH ( IdCounterWidth ), - .STICKY_OVERFLOW ( 1'b0 ) - ) i_counter_open_w ( - .clk_i, - .rst_ni, - .clear_i ( 1'b0 ), - .en_i ( w_cnt_up ^ w_cnt_down ), - .load_i ( 1'b0 ), - .down_i ( w_cnt_down ), - .d_i ( '0 ), - .q_o ( w_open ), - .overflow_o ( /*not used*/ ) - ); - - `FFLARN(w_select_q, slv_aw_select, w_cnt_up, select_t'(0), clk_i, rst_ni) - assign w_select = (|w_open) ? w_select_q : slv_aw_select; - assign w_select_valid = w_cnt_up | (|w_open); - - //-------------------------------------- - // W Channel - //-------------------------------------- - spill_register #( - .T ( w_chan_t ), - .Bypass ( ~SpillW ) - ) i_w_spill_reg( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .valid_i ( slv_req_i.w_valid ), - .ready_o ( slv_resp_o.w_ready ), - .data_i ( slv_req_i.w ), - .valid_o ( slv_w_valid ), - .ready_i ( slv_w_ready ), - .data_o ( slv_w_chan ) - ); - - //-------------------------------------- - // B Channel - //-------------------------------------- - // optional spill register - spill_register #( - .T ( b_chan_t ), - .Bypass ( ~SpillB ) - ) i_b_spill_reg ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .valid_i ( slv_b_valid ), - .ready_o ( slv_b_ready ), - .data_i ( slv_b_chan ), - .valid_o ( slv_resp_o.b_valid ), - .ready_i ( slv_req_i.b_ready ), - .data_o ( slv_resp_o.b ) - ); - - // Arbitration of the different B responses - rr_arb_tree #( - .NumIn ( NoMstPorts ), - .DataType ( b_chan_t ), - .AxiVldRdy( 1'b1 ), - .LockIn ( 1'b1 ) - ) i_b_mux ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .flush_i( 1'b0 ), - .rr_i ( '0 ), - .req_i ( mst_b_valids ), - .gnt_o ( mst_b_readies ), - .data_i ( mst_b_chans ), - .gnt_i ( slv_b_ready ), - .req_o ( slv_b_valid ), - .data_o ( slv_b_chan ), - .idx_o ( ) - ); - - //-------------------------------------- - // AR Channel - //-------------------------------------- - ar_chan_t slv_ar_chan; - select_t slv_ar_select; - spill_register #( - .T ( ar_chan_t ), - .Bypass ( ~SpillAr ) - ) i_ar_chan_spill_reg ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .valid_i ( slv_req_i.ar_valid ), - .ready_o ( slv_ar_ready_chan ), - .data_i ( slv_req_i.ar ), - .valid_o ( ar_valid_chan ), - .ready_i ( slv_ar_ready ), - .data_o ( slv_ar_chan ) - ); - spill_register #( - .T ( select_t ), - .Bypass ( ~SpillAr ) - ) i_ar_sel_spill_reg ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .valid_i ( slv_req_i.ar_valid ), - .ready_o ( slv_ar_ready_sel ), - .data_i ( slv_ar_select_i ), - .valid_o ( ar_valid_sel ), - .ready_i ( slv_ar_ready ), - .data_o ( slv_ar_select ) - ); - assign slv_resp_o.ar_ready = slv_ar_ready_chan & slv_ar_ready_sel; - assign slv_ar_valid = ar_valid_chan & ar_valid_sel; - - // control of the AR handshake - always_comb begin - // AXI Handshakes - slv_ar_ready = 1'b0; - ar_valid = 1'b0; - // `lock_ar_valid`: Used to be protocol conform as it is not allowed to deassert `ar_valid` - // if there was no corresponding `ar_ready`. There is the possibility that an injection - // of a R response from an `atop` from the AW channel can change the occupied flag of the - // `i_ar_id_counter`, even if it was previously empty. This FF prevents the deassertion. - lock_ar_valid_d = lock_ar_valid_q; - load_ar_lock = 1'b0; - // AR id counter - ar_push = 1'b0; - // The process had an arbitration decision in a previous cycle, the valid is locked, - // wait for the AR transaction. - if (lock_ar_valid_q) begin - ar_valid = 1'b1; - // transaction - if (ar_ready) begin - slv_ar_ready = 1'b1; - ar_push = 1'b1; - lock_ar_valid_d = 1'b0; - load_ar_lock = 1'b1; - end - end else begin - // The process can start handling AR transaction if `i_ar_id_counter` has space. - if (!ar_id_cnt_full) begin - // There is a valid AR, so look the ID up. - if (slv_ar_valid && (!ar_select_occupied || - (slv_ar_select == lookup_ar_select))) begin - // connect the AR handshake - ar_valid = 1'b1; - // on transaction - if (ar_ready) begin - slv_ar_ready = 1'b1; - ar_push = 1'b1; - // no transaction this cycle, lock the valid decision! - end else begin - lock_ar_valid_d = 1'b1; - load_ar_lock = 1'b1; - end - end - end - end - end - - // this ff is needed so that ar does not get de-asserted if an atop gets injected - `FFLARN(lock_ar_valid_q, lock_ar_valid_d, load_ar_lock, '0, clk_i, rst_ni) - - if (UniqueIds) begin : gen_unique_ids_ar - // If the `UniqueIds` parameter is set, each read transaction has an ID that is unique among - // all in-flight read transactions, or all read transactions with a given ID target the same - // master port as all read transactions with the same ID, or both. This means that the - // signals that are driven by the ID counters if this parameter is not set can instead be - // derived from existing signals. The ID counters can therefore be omitted. - assign lookup_ar_select = slv_ar_select; - assign ar_select_occupied = 1'b0; - assign ar_id_cnt_full = 1'b0; - end else begin : gen_ar_id_counter - axi_demux_id_counters #( - .AxiIdBits ( AxiLookBits ), - .CounterWidth ( IdCounterWidth ), - .mst_port_select_t ( select_t ) - ) i_ar_id_counter ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .lookup_axi_id_i ( slv_ar_chan.id[0+:AxiLookBits] ), - .lookup_mst_select_o ( lookup_ar_select ), - .lookup_mst_select_occupied_o ( ar_select_occupied ), - .full_o ( ar_id_cnt_full ), - .inject_axi_id_i ( slv_aw_chan.id[0+:AxiLookBits] ), - .inject_i ( atop_inject ), - .push_axi_id_i ( slv_ar_chan.id[0+:AxiLookBits] ), - .push_mst_select_i ( slv_ar_select ), - .push_i ( ar_push ), - .pop_axi_id_i ( slv_r_chan.id[0+:AxiLookBits] ), - .pop_i ( slv_r_valid & slv_r_ready & slv_r_chan.last ) - ); - end - - //-------------------------------------- - // R Channel - //-------------------------------------- - // optional spill register - spill_register #( - .T ( r_chan_t ), - .Bypass ( ~SpillR ) - ) i_r_spill_reg ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .valid_i ( slv_r_valid ), - .ready_o ( slv_r_ready ), - .data_i ( slv_r_chan ), - .valid_o ( slv_resp_o.r_valid ), - .ready_i ( slv_req_i.r_ready ), - .data_o ( slv_resp_o.r ) - ); - - // Arbitration of the different r responses - rr_arb_tree #( - .NumIn ( NoMstPorts ), - .DataType ( r_chan_t ), - .AxiVldRdy( 1'b1 ), - .LockIn ( 1'b1 ) - ) i_r_mux ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .flush_i( 1'b0 ), - .rr_i ( '0 ), - .req_i ( mst_r_valids ), - .gnt_o ( mst_r_readies ), - .data_i ( mst_r_chans ), - .gnt_i ( slv_r_ready ), - .req_o ( slv_r_valid ), - .data_o ( slv_r_chan ), - .idx_o ( ) - ); - - assign ar_ready = ar_valid & mst_resps_i[slv_ar_select].ar_ready; - assign aw_ready = aw_valid & mst_resps_i[slv_aw_select].aw_ready; - - // process that defines the individual demuxes and assignments for the arbitration - // as mst_reqs_o has to be drivem from the same always comb block! - always_comb begin - // default assignments - mst_reqs_o = '0; - slv_w_ready = 1'b0; - w_cnt_down = 1'b0; - - for (int unsigned i = 0; i < NoMstPorts; i++) begin - // AW channel - mst_reqs_o[i].aw = slv_aw_chan; - mst_reqs_o[i].aw_valid = 1'b0; - if (aw_valid && (slv_aw_select == i)) begin - mst_reqs_o[i].aw_valid = 1'b1; - end - - // W channel - mst_reqs_o[i].w = slv_w_chan; - mst_reqs_o[i].w_valid = 1'b0; - if (w_select_valid && (w_select == i)) begin - mst_reqs_o[i].w_valid = slv_w_valid; - slv_w_ready = mst_resps_i[i].w_ready; - w_cnt_down = slv_w_valid & mst_resps_i[i].w_ready & slv_w_chan.last; - end - - // B channel - mst_reqs_o[i].b_ready = mst_b_readies[i]; - - // AR channel - mst_reqs_o[i].ar = slv_ar_chan; - mst_reqs_o[i].ar_valid = 1'b0; - if (ar_valid && (slv_ar_select == i)) begin - mst_reqs_o[i].ar_valid = 1'b1; - end - - // R channel - mst_reqs_o[i].r_ready = mst_r_readies[i]; - end - end - // unpack the response B and R channels for the arbitration - for (genvar i = 0; i < NoMstPorts; i++) begin : gen_b_channels - assign mst_b_chans[i] = mst_resps_i[i].b; - assign mst_b_valids[i] = mst_resps_i[i].b_valid; - assign mst_r_chans[i] = mst_resps_i[i].r; - assign mst_r_valids[i] = mst_resps_i[i].r_valid; - end - - -// Validate parameters. -// pragma translate_off -`ifndef VERILATOR -`ifndef XSIM - initial begin: validate_params - no_mst_ports: assume (NoMstPorts > 0) else - $fatal(1, "The Number of slaves (NoMstPorts) has to be at least 1"); - AXI_ID_BITS: assume (AxiIdWidth >= AxiLookBits) else - $fatal(1, "AxiIdBits has to be equal or smaller than AxiIdWidth."); - end - default disable iff (!rst_ni); - aw_select: assume property( @(posedge clk_i) (slv_req_i.aw_valid |-> - (slv_aw_select_i < NoMstPorts))) else - $fatal(1, "slv_aw_select_i is %d: AW has selected a slave that is not defined.\ - NoMstPorts: %d", slv_aw_select_i, NoMstPorts); - ar_select: assume property( @(posedge clk_i) (slv_req_i.ar_valid |-> - (slv_ar_select_i < NoMstPorts))) else - $fatal(1, "slv_ar_select_i is %d: AR has selected a slave that is not defined.\ - NoMstPorts: %d", slv_ar_select_i, NoMstPorts); - aw_valid_stable: assert property( @(posedge clk_i) (aw_valid && !aw_ready) |=> aw_valid) else - $fatal(1, "aw_valid was deasserted, when aw_ready = 0 in last cycle."); - ar_valid_stable: assert property( @(posedge clk_i) - (ar_valid && !ar_ready) |=> ar_valid) else - $fatal(1, "ar_valid was deasserted, when ar_ready = 0 in last cycle."); - slv_aw_chan_stable: assert property( @(posedge clk_i) (aw_valid && !aw_ready) - |=> $stable(slv_aw_chan)) else - $fatal(1, "slv_aw_chan unstable with valid set."); - slv_aw_select_stable: assert property( @(posedge clk_i) (aw_valid && !aw_ready) - |=> $stable(slv_aw_select)) else - $fatal(1, "slv_aw_select unstable with valid set."); - slv_ar_chan_stable: assert property( @(posedge clk_i) (ar_valid && !ar_ready) - |=> $stable(slv_ar_chan)) else - $fatal(1, "slv_ar_chan unstable with valid set."); - slv_ar_select_stable: assert property( @(posedge clk_i) (ar_valid && !ar_ready) - |=> $stable(slv_ar_select)) else - $fatal(1, "slv_ar_select unstable with valid set."); - internal_ar_select: assert property( @(posedge clk_i) - (ar_valid |-> slv_ar_select < NoMstPorts)) - else $fatal(1, "slv_ar_select illegal while ar_valid."); - internal_aw_select: assert property( @(posedge clk_i) - (aw_valid |-> slv_aw_select < NoMstPorts)) - else $fatal(1, "slv_aw_select illegal while aw_valid."); - w_underflow: assert property( @(posedge clk_i) - ((w_open == '0) && (w_cnt_up ^ w_cnt_down) |-> !w_cnt_down)) else - $fatal(1, "W counter underflowed!"); - `ASSUME(NoAtopAllowed, !AtopSupport && slv_req_i.aw_valid |-> slv_req_i.aw.atop == '0) -`endif -`endif -// pragma translate_on - end -endmodule - -module axi_demux_id_counters #( - // the lower bits of the AXI ID that should be considered, results in 2**AXI_ID_BITS counters - parameter int unsigned AxiIdBits = 2, - parameter int unsigned CounterWidth = 4, - parameter type mst_port_select_t = logic -) ( - input clk_i, // Clock - input rst_ni, // Asynchronous reset active low - // lookup - input logic [AxiIdBits-1:0] lookup_axi_id_i, - output mst_port_select_t lookup_mst_select_o, - output logic lookup_mst_select_occupied_o, - // push - output logic full_o, - input logic [AxiIdBits-1:0] push_axi_id_i, - input mst_port_select_t push_mst_select_i, - input logic push_i, - // inject ATOPs in AR channel - input logic [AxiIdBits-1:0] inject_axi_id_i, - input logic inject_i, - // pop - input logic [AxiIdBits-1:0] pop_axi_id_i, - input logic pop_i -); - localparam int unsigned NoCounters = 2**AxiIdBits; - typedef logic [CounterWidth-1:0] cnt_t; - - // registers, each gets loaded when push_en[i] - mst_port_select_t [NoCounters-1:0] mst_select_q; - - // counter signals - logic [NoCounters-1:0] push_en, inject_en, pop_en, occupied, cnt_full; + axi_req_t slv_req_cut; + axi_resp_t slv_resp_cut; + + logic slv_aw_ready_chan, slv_aw_ready_sel; + logic slv_aw_valid_chan, slv_aw_valid_sel; + + logic slv_ar_ready_chan, slv_ar_ready_sel; + logic slv_ar_valid_chan, slv_ar_valid_sel; + + select_t slv_aw_select, slv_ar_select; + + spill_register #( + .T ( aw_chan_t ), + .Bypass ( ~SpillAw ) + ) i_aw_spill_reg ( + .clk_i, + .rst_ni, + .valid_i ( slv_req_i.aw_valid ), + .ready_o ( slv_aw_ready_chan ), + .data_i ( slv_req_i.aw ), + .valid_o ( slv_aw_valid_chan ), + .ready_i ( slv_resp_cut.aw_ready ), + .data_o ( slv_req_cut.aw ) + ); + spill_register #( + .T ( select_t ), + .Bypass ( ~SpillAw ) + ) i_aw_select_spill_reg ( + .clk_i, + .rst_ni, + .valid_i ( slv_req_i.aw_valid ), + .ready_o ( slv_aw_ready_sel ), + .data_i ( slv_aw_select_i ), + .valid_o ( slv_aw_valid_sel ), + .ready_i ( slv_resp_cut.aw_ready ), + .data_o ( slv_aw_select ) + ); - //----------------------------------- - // Lookup - //----------------------------------- - assign lookup_mst_select_o = mst_select_q[lookup_axi_id_i]; - assign lookup_mst_select_occupied_o = occupied[lookup_axi_id_i]; - //----------------------------------- - // Push and Pop - //----------------------------------- - assign push_en = (push_i) ? (1 << push_axi_id_i) : '0; - assign inject_en = (inject_i) ? (1 << inject_axi_id_i) : '0; - assign pop_en = (pop_i) ? (1 << pop_axi_id_i) : '0; - assign full_o = |cnt_full; - // counters - for (genvar i = 0; i < NoCounters; i++) begin : gen_counters - logic cnt_en, cnt_down, overflow; - cnt_t cnt_delta, in_flight; - always_comb begin - unique case ({push_en[i], inject_en[i], pop_en[i]}) - 3'b001 : begin // pop_i = -1 - cnt_en = 1'b1; - cnt_down = 1'b1; - cnt_delta = cnt_t'(1); - end - 3'b010 : begin // inject_i = +1 - cnt_en = 1'b1; - cnt_down = 1'b0; - cnt_delta = cnt_t'(1); - end - // 3'b011, inject_i & pop_i = 0 --> use default - 3'b100 : begin // push_i = +1 - cnt_en = 1'b1; - cnt_down = 1'b0; - cnt_delta = cnt_t'(1); - end - // 3'b101, push_i & pop_i = 0 --> use default - 3'b110 : begin // push_i & inject_i = +2 - cnt_en = 1'b1; - cnt_down = 1'b0; - cnt_delta = cnt_t'(2); - end - 3'b111 : begin // push_i & inject_i & pop_i = +1 - cnt_en = 1'b1; - cnt_down = 1'b0; - cnt_delta = cnt_t'(1); - end - default : begin // do nothing to the counters - cnt_en = 1'b0; - cnt_down = 1'b0; - cnt_delta = cnt_t'(0); - end - endcase - end + assign slv_resp_o.aw_ready = slv_aw_ready_chan & slv_aw_ready_sel; + assign slv_req_cut.aw_valid = slv_aw_valid_chan & slv_aw_valid_sel; + + spill_register #( + .T ( w_chan_t ), + .Bypass ( ~SpillW ) + ) i_w_spill_reg ( + .clk_i, + .rst_ni, + .valid_i ( slv_req_i.w_valid ), + .ready_o ( slv_resp_o.w_ready ), + .data_i ( slv_req_i.w ), + .valid_o ( slv_req_cut.w_valid ), + .ready_i ( slv_resp_cut.w_ready ), + .data_o ( slv_req_cut.w ) + ); + spill_register #( + .T ( ar_chan_t ), + .Bypass ( ~SpillAr ) + ) i_ar_spill_reg ( + .clk_i, + .rst_ni, + .valid_i ( slv_req_i.ar_valid ), + .ready_o ( slv_ar_ready_chan ), + .data_i ( slv_req_i.ar ), + .valid_o ( slv_ar_valid_chan ), + .ready_i ( slv_resp_cut.ar_ready ), + .data_o ( slv_req_cut.ar ) + ); + spill_register #( + .T ( select_t ), + .Bypass ( ~SpillAr ) + ) i_ar_sel_spill_reg ( + .clk_i, + .rst_ni, + .valid_i ( slv_req_i.ar_valid ), + .ready_o ( slv_ar_ready_sel ), + .data_i ( slv_ar_select_i ), + .valid_o ( slv_ar_valid_sel ), + .ready_i ( slv_resp_cut.ar_ready ), + .data_o ( slv_ar_select ) + ); - delta_counter #( - .WIDTH ( CounterWidth ), - .STICKY_OVERFLOW ( 1'b0 ) - ) i_in_flight_cnt ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .clear_i ( 1'b0 ), - .en_i ( cnt_en ), - .load_i ( 1'b0 ), - .down_i ( cnt_down ), - .delta_i ( cnt_delta ), - .d_i ( '0 ), - .q_o ( in_flight ), - .overflow_o ( overflow ) - ); - assign occupied[i] = |in_flight; - assign cnt_full[i] = overflow | (&in_flight); + assign slv_resp_o.ar_ready = slv_ar_ready_chan & slv_ar_ready_sel; + assign slv_req_cut.ar_valid = slv_ar_valid_chan & slv_ar_valid_sel; + + spill_register #( + .T ( b_chan_t ), + .Bypass ( ~SpillB ) + ) i_b_spill_reg ( + .clk_i, + .rst_ni, + .valid_i ( slv_resp_cut.b_valid ), + .ready_o ( slv_req_cut.b_ready ), + .data_i ( slv_resp_cut.b ), + .valid_o ( slv_resp_o.b_valid ), + .ready_i ( slv_req_i.b_ready ), + .data_o ( slv_resp_o.b ) + ); + spill_register #( + .T ( r_chan_t ), + .Bypass ( ~SpillR ) + ) i_r_spill_reg ( + .clk_i, + .rst_ni, + .valid_i ( slv_resp_cut.r_valid ), + .ready_o ( slv_req_cut.r_ready ), + .data_i ( slv_resp_cut.r ), + .valid_o ( slv_resp_o.r_valid ), + .ready_i ( slv_req_i.r_ready ), + .data_o ( slv_resp_o.r ) + ); - // holds the selection signal for this id - `FFLARN(mst_select_q[i], push_mst_select_i, push_en[i], '0, clk_i, rst_ni) + axi_demux_simple #( + .AxiIdWidth ( AxiIdWidth ), + .AtopSupport( AtopSupport ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .NoMstPorts ( NoMstPorts ), + .MaxTrans ( MaxTrans ), + .AxiLookBits( AxiLookBits ), + .UniqueIds ( UniqueIds ) + ) i_demux_simple ( + .clk_i, + .rst_ni, + .test_i, + + .slv_req_i ( slv_req_cut ), + .slv_aw_select_i ( slv_aw_select ), + .slv_ar_select_i ( slv_ar_select ), + .slv_resp_o ( slv_resp_cut ), + .mst_reqs_o ( mst_reqs_o ), + .mst_resps_i ( mst_resps_i ) + ); -// pragma translate_off -`ifndef VERILATOR -`ifndef XSIM - // Validate parameters. - cnt_underflow: assert property( - @(posedge clk_i) disable iff (~rst_ni) (pop_en[i] |=> !overflow)) else - $fatal(1, "axi_demux_id_counters > Counter: %0d underflowed.\ - The reason is probably a faulty AXI response.", i); -`endif -`endif -// pragma translate_on - end endmodule // interface wrapper diff --git a/vendor/pulp-platform/axi/src/axi_demux_simple.sv b/vendor/pulp-platform/axi/src/axi_demux_simple.sv new file mode 100644 index 00000000000..1c11f794a27 --- /dev/null +++ b/vendor/pulp-platform/axi/src/axi_demux_simple.sv @@ -0,0 +1,633 @@ +// Copyright (c) 2019 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 Roenninger +// - Michael Rogenmoser +// - Thomas Benz +// - Andreas Kurth + +`include "common_cells/assertions.svh" +`include "common_cells/registers.svh" +`include "axi/assign.svh" + +`ifdef QUESTA +// Derive `TARGET_VSIM`, which is used for tool-specific workarounds in this file, from `QUESTA`, +// which is automatically set in Questa. +`define TARGET_VSIM +`endif + +/// Demultiplex one AXI4+ATOP slave port to multiple AXI4+ATOP master ports. +/// +/// The AW and AR slave channels each have a `select` input to determine to which master port the +/// current request is sent. The `select` can, for example, be driven by an address decoding module +/// to map address ranges to different AXI slaves. +/// +/// ## Design overview +/// +/// ![Block diagram](module.axi_demux.png "Block diagram") +/// +/// Beats on the W channel are routed by demultiplexer according to the selection for the +/// corresponding AW beat. This relies on the AXI property that W bursts must be sent in the same +/// order as AW beats and beats from different W bursts may not be interleaved. +/// +/// Beats on the B and R channel are multiplexed from the master ports to the slave port with +/// a round-robin arbitration tree. +module axi_demux_simple #( + parameter int unsigned AxiIdWidth = 32'd0, + parameter bit AtopSupport = 1'b1, + parameter type axi_req_t = logic, + parameter type axi_resp_t = logic, + parameter int unsigned NoMstPorts = 32'd0, + parameter int unsigned MaxTrans = 32'd8, + parameter int unsigned AxiLookBits = 32'd3, + parameter bit UniqueIds = 1'b0, + // Dependent parameters, DO NOT OVERRIDE! + parameter int unsigned SelectWidth = (NoMstPorts > 32'd1) ? $clog2(NoMstPorts) : 32'd1, + parameter type select_t = logic [SelectWidth-1:0] +) ( + input logic clk_i, + input logic rst_ni, + input logic test_i, + // Slave Port + input axi_req_t slv_req_i, + input select_t slv_aw_select_i, + input select_t slv_ar_select_i, + output axi_resp_t slv_resp_o, + // Master Ports + output axi_req_t [NoMstPorts-1:0] mst_reqs_o, + input axi_resp_t [NoMstPorts-1:0] mst_resps_i +); + + localparam int unsigned IdCounterWidth = cf_math_pkg::idx_width(MaxTrans); + typedef logic [IdCounterWidth-1:0] id_cnt_t; + + // pass through if only one master port + if (NoMstPorts == 32'h1) begin : gen_no_demux + `AXI_ASSIGN_REQ_STRUCT(mst_reqs_o[0], slv_req_i) + `AXI_ASSIGN_RESP_STRUCT(slv_resp_o, mst_resps_i[0]) + end else begin + + //-------------------------------------- + //-------------------------------------- + // Signal Declarations + //-------------------------------------- + //-------------------------------------- + + //-------------------------------------- + // Write Transaction + //-------------------------------------- + + // Register which locks the AW valid signal + logic lock_aw_valid_d, lock_aw_valid_q, load_aw_lock; + logic aw_valid, aw_ready; + + // AW ID counter + select_t lookup_aw_select; + logic aw_select_occupied, aw_id_cnt_full; + // Upon an ATOP load, inject IDs from the AW into the AR channel + logic atop_inject; + + // W select counter: stores the decision to which master W beats should go + select_t w_select, w_select_q; + logic w_select_valid; + id_cnt_t w_open; + logic w_cnt_up, w_cnt_down; + + // B channles input into the arbitration + logic [NoMstPorts-1:0] mst_b_valids, mst_b_readies; + + //-------------------------------------- + // Read Transaction + //-------------------------------------- + + // AR ID counter + select_t lookup_ar_select; + logic ar_select_occupied, ar_id_cnt_full; + logic ar_push; + + // Register which locks the AR valid signel + logic lock_ar_valid_d, lock_ar_valid_q, load_ar_lock; + logic ar_valid, ar_ready; + + logic [NoMstPorts-1:0] mst_r_valids, mst_r_readies; + + + + + + + + //-------------------------------------- + // Channel Control + //-------------------------------------- + //-------------------------------------- + + //-------------------------------------- + // AW Channel + //-------------------------------------- + + // Control of the AW handshake + always_comb begin + // AXI Handshakes + slv_resp_o.aw_ready = 1'b0; + aw_valid = 1'b0; + // `lock_aw_valid`, used to be protocol conform as it is not allowed to deassert + // a valid if there was no corresponding ready. As this process has to be able to inject + // an AXI ID into the counter of the AR channel on an ATOP, there could be a case where + // this process waits on `aw_ready` but in the mean time on the AR channel the counter gets + // full. + lock_aw_valid_d = lock_aw_valid_q; + load_aw_lock = 1'b0; + // AW ID counter and W FIFO + w_cnt_up = 1'b0; + // ATOP injection into ar counter + atop_inject = 1'b0; + // we had an arbitration decision, the valid is locked, wait for the transaction + if (lock_aw_valid_q) begin + aw_valid = 1'b1; + // transaction + if (aw_ready) begin + slv_resp_o.aw_ready = 1'b1; + lock_aw_valid_d = 1'b0; + load_aw_lock = 1'b1; + // inject the ATOP if necessary + atop_inject = slv_req_i.aw.atop[axi_pkg::ATOP_R_RESP] & AtopSupport; + end + end else begin + // An AW can be handled if `i_aw_id_counter` and `i_counter_open_w` are not full. An ATOP that + // requires an R response can be handled if additionally `i_ar_id_counter` is not full (this + // only applies if ATOPs are supported at all). + if (!aw_id_cnt_full && (w_open != {IdCounterWidth{1'b1}}) && + (!(ar_id_cnt_full && slv_req_i.aw.atop[axi_pkg::ATOP_R_RESP]) || + !AtopSupport)) begin + // There is a valid AW vector make the id lookup and go further, if it passes. + // Also stall if previous transmitted AWs still have active W's in flight. + // This prevents deadlocking of the W channel. The counters are there for the + // Handling of the B responses. + if (slv_req_i.aw_valid && + ((w_open == '0) || (w_select == slv_aw_select_i)) && + (!aw_select_occupied || (slv_aw_select_i == lookup_aw_select))) begin + // connect the handshake + aw_valid = 1'b1; + // push arbitration to the W FIFO regardless, do not wait for the AW transaction + w_cnt_up = 1'b1; + // on AW transaction + if (aw_ready) begin + slv_resp_o.aw_ready = 1'b1; + atop_inject = slv_req_i.aw.atop[axi_pkg::ATOP_R_RESP] & AtopSupport; + // no AW transaction this cycle, lock the decision + end else begin + lock_aw_valid_d = 1'b1; + load_aw_lock = 1'b1; + end + end + end + end + end + + // lock the valid signal, as the selection gets pushed into the W FIFO on first assertion, + // prevent further pushing + `FFLARN(lock_aw_valid_q, lock_aw_valid_d, load_aw_lock, '0, clk_i, rst_ni) + + if (UniqueIds) begin : gen_unique_ids_aw + // If the `UniqueIds` parameter is set, each write transaction has an ID that is unique among + // all in-flight write transactions, or all write transactions with a given ID target the same + // master port as all write transactions with the same ID, or both. This means that the + // signals that are driven by the ID counters if this parameter is not set can instead be + // derived from existing signals. The ID counters can therefore be omitted. + assign lookup_aw_select = slv_aw_select_i; + assign aw_select_occupied = 1'b0; + assign aw_id_cnt_full = 1'b0; + end else begin : gen_aw_id_counter + axi_demux_id_counters #( + .AxiIdBits ( AxiLookBits ), + .CounterWidth ( IdCounterWidth ), + .mst_port_select_t ( select_t ) + ) i_aw_id_counter ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .lookup_axi_id_i ( slv_req_i.aw.id[0+:AxiLookBits] ), + .lookup_mst_select_o ( lookup_aw_select ), + .lookup_mst_select_occupied_o ( aw_select_occupied ), + .full_o ( aw_id_cnt_full ), + .inject_axi_id_i ( '0 ), + .inject_i ( 1'b0 ), + .push_axi_id_i ( slv_req_i.aw.id[0+:AxiLookBits] ), + .push_mst_select_i ( slv_aw_select_i ), + .push_i ( w_cnt_up ), + .pop_axi_id_i ( slv_resp_o.b.id[0+:AxiLookBits] ), + .pop_i ( slv_resp_o.b_valid & slv_req_i.b_ready ) + ); + // pop from ID counter on outward transaction + end + + // This counter steers the demultiplexer of the W channel. + // `w_select` determines, which handshaking is connected. + // AWs are only forwarded, if the counter is empty, or `w_select_q` is the same as + // `slv_aw_select_i`. + counter #( + .WIDTH ( IdCounterWidth ), + .STICKY_OVERFLOW ( 1'b0 ) + ) i_counter_open_w ( + .clk_i, + .rst_ni, + .clear_i ( 1'b0 ), + .en_i ( w_cnt_up ^ w_cnt_down ), + .load_i ( 1'b0 ), + .down_i ( w_cnt_down ), + .d_i ( '0 ), + .q_o ( w_open ), + .overflow_o ( /*not used*/ ) + ); + + `FFLARN(w_select_q, slv_aw_select_i, w_cnt_up, select_t'(0), clk_i, rst_ni) + assign w_select = (|w_open) ? w_select_q : slv_aw_select_i; + assign w_select_valid = w_cnt_up | (|w_open); + + //-------------------------------------- + // W Channel + //-------------------------------------- + + //-------------------------------------- + // B Channel + //-------------------------------------- + logic [cf_math_pkg::idx_width(NoMstPorts)-1:0] b_idx; + + // Arbitration of the different B responses + rr_arb_tree #( + .NumIn ( NoMstPorts ), + .DataType ( logic ), + .AxiVldRdy( 1'b1 ), + .LockIn ( 1'b1 ) + ) i_b_mux ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .flush_i( 1'b0 ), + .rr_i ( '0 ), + .req_i ( mst_b_valids ), + .gnt_o ( mst_b_readies ), + .data_i ( '0 ), + .gnt_i ( slv_req_i.b_ready ), + .req_o ( slv_resp_o.b_valid ), + .data_o ( ), + .idx_o ( b_idx ) + ); + + always_comb begin + if (slv_resp_o.b_valid) begin + `AXI_SET_B_STRUCT(slv_resp_o.b, mst_resps_i[b_idx].b) + end else begin + slv_resp_o.b = '0; + end + end + + //-------------------------------------- + // AR Channel + //-------------------------------------- + + // control of the AR handshake + always_comb begin + // AXI Handshakes + slv_resp_o.ar_ready = 1'b0; + ar_valid = 1'b0; + // `lock_ar_valid`: Used to be protocol conform as it is not allowed to deassert `ar_valid` + // if there was no corresponding `ar_ready`. There is the possibility that an injection + // of a R response from an `atop` from the AW channel can change the occupied flag of the + // `i_ar_id_counter`, even if it was previously empty. This FF prevents the deassertion. + lock_ar_valid_d = lock_ar_valid_q; + load_ar_lock = 1'b0; + // AR id counter + ar_push = 1'b0; + // The process had an arbitration decision in a previous cycle, the valid is locked, + // wait for the AR transaction. + if (lock_ar_valid_q) begin + ar_valid = 1'b1; + // transaction + if (ar_ready) begin + slv_resp_o.ar_ready = 1'b1; + ar_push = 1'b1; + lock_ar_valid_d = 1'b0; + load_ar_lock = 1'b1; + end + end else begin + // The process can start handling AR transaction if `i_ar_id_counter` has space. + if (!ar_id_cnt_full) begin + // There is a valid AR, so look the ID up. + if (slv_req_i.ar_valid && (!ar_select_occupied || + (slv_ar_select_i == lookup_ar_select))) begin + // connect the AR handshake + ar_valid = 1'b1; + // on transaction + if (ar_ready) begin + slv_resp_o.ar_ready = 1'b1; + ar_push = 1'b1; + // no transaction this cycle, lock the valid decision! + end else begin + lock_ar_valid_d = 1'b1; + load_ar_lock = 1'b1; + end + end + end + end + end + + // this ff is needed so that ar does not get de-asserted if an atop gets injected + `FFLARN(lock_ar_valid_q, lock_ar_valid_d, load_ar_lock, '0, clk_i, rst_ni) + + if (UniqueIds) begin : gen_unique_ids_ar + // If the `UniqueIds` parameter is set, each read transaction has an ID that is unique among + // all in-flight read transactions, or all read transactions with a given ID target the same + // master port as all read transactions with the same ID, or both. This means that the + // signals that are driven by the ID counters if this parameter is not set can instead be + // derived from existing signals. The ID counters can therefore be omitted. + assign lookup_ar_select = slv_ar_select_i; + assign ar_select_occupied = 1'b0; + assign ar_id_cnt_full = 1'b0; + end else begin : gen_ar_id_counter + axi_demux_id_counters #( + .AxiIdBits ( AxiLookBits ), + .CounterWidth ( IdCounterWidth ), + .mst_port_select_t ( select_t ) + ) i_ar_id_counter ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .lookup_axi_id_i ( slv_req_i.ar.id[0+:AxiLookBits] ), + .lookup_mst_select_o ( lookup_ar_select ), + .lookup_mst_select_occupied_o ( ar_select_occupied ), + .full_o ( ar_id_cnt_full ), + .inject_axi_id_i ( slv_req_i.aw.id[0+:AxiLookBits] ), + .inject_i ( atop_inject ), + .push_axi_id_i ( slv_req_i.ar.id[0+:AxiLookBits] ), + .push_mst_select_i ( slv_ar_select_i ), + .push_i ( ar_push ), + .pop_axi_id_i ( slv_resp_o.r.id[0+:AxiLookBits] ), + .pop_i ( slv_resp_o.r_valid & slv_req_i.r_ready & slv_resp_o.r.last ) + ); + end + + //-------------------------------------- + // R Channel + //-------------------------------------- + + logic [cf_math_pkg::idx_width(NoMstPorts)-1:0] r_idx; + + // Arbitration of the different r responses + rr_arb_tree #( + .NumIn ( NoMstPorts ), + .DataType ( logic ), + .AxiVldRdy( 1'b1 ), + .LockIn ( 1'b1 ) + ) i_r_mux ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .flush_i( 1'b0 ), + .rr_i ( '0 ), + .req_i ( mst_r_valids ), + .gnt_o ( mst_r_readies ), + .data_i ( '0 ), + .gnt_i ( slv_req_i.r_ready ), + .req_o ( slv_resp_o.r_valid ), + .data_o (), + .idx_o ( r_idx ) + ); + + always_comb begin + if (slv_resp_o.r_valid) begin + `AXI_SET_R_STRUCT(slv_resp_o.r, mst_resps_i[r_idx].r) + end else begin + slv_resp_o.r = '0; + end + end + + assign ar_ready = ar_valid & mst_resps_i[slv_ar_select_i].ar_ready; + assign aw_ready = aw_valid & mst_resps_i[slv_aw_select_i].aw_ready; + + // process that defines the individual demuxes and assignments for the arbitration + // as mst_reqs_o has to be drivem from the same always comb block! + always_comb begin + // default assignments + mst_reqs_o = '0; + slv_resp_o.w_ready = 1'b0; + w_cnt_down = 1'b0; + + for (int unsigned i = 0; i < NoMstPorts; i++) begin + // AW channel + mst_reqs_o[i].aw = slv_req_i.aw; + mst_reqs_o[i].aw_valid = 1'b0; + if (aw_valid && (slv_aw_select_i == i)) begin + mst_reqs_o[i].aw_valid = 1'b1; + end + + // W channel + mst_reqs_o[i].w = slv_req_i.w; + mst_reqs_o[i].w_valid = 1'b0; + if (w_select_valid && (w_select == i)) begin + mst_reqs_o[i].w_valid = slv_req_i.w_valid; + slv_resp_o.w_ready = mst_resps_i[i].w_ready; + w_cnt_down = slv_req_i.w_valid & mst_resps_i[i].w_ready & slv_req_i.w.last; + end + + // B channel + mst_reqs_o[i].b_ready = mst_b_readies[i]; + + // AR channel + mst_reqs_o[i].ar = slv_req_i.ar; + mst_reqs_o[i].ar_valid = 1'b0; + if (ar_valid && (slv_ar_select_i == i)) begin + mst_reqs_o[i].ar_valid = 1'b1; + end + + // R channel + mst_reqs_o[i].r_ready = mst_r_readies[i]; + end + end + // unpack the response B and R channels for the arbitration + for (genvar i = 0; i < NoMstPorts; i++) begin : gen_b_channels + // assign mst_b_chans[i] = mst_resps_i[i].b; + assign mst_b_valids[i] = mst_resps_i[i].b_valid; + // assign mst_r_chans[i] = mst_resps_i[i].r; + assign mst_r_valids[i] = mst_resps_i[i].r_valid; + end + +// Validate parameters. +// pragma translate_off +`ifndef VERILATOR +`ifndef XSIM + initial begin: validate_params + no_mst_ports: assume (NoMstPorts > 0) else + $fatal(1, "The Number of slaves (NoMstPorts) has to be at least 1"); + AXI_ID_BITS: assume (AxiIdWidth >= AxiLookBits) else + $fatal(1, "AxiIdBits has to be equal or smaller than AxiIdWidth."); + end + default disable iff (!rst_ni); + aw_select: assume property( @(posedge clk_i) (slv_req_i.aw_valid |-> + (slv_aw_select_i < NoMstPorts))) else + $fatal(1, "slv_aw_select_i is %d: AW has selected a slave that is not defined.\ + NoMstPorts: %d", slv_aw_select_i, NoMstPorts); + ar_select: assume property( @(posedge clk_i) (slv_req_i.ar_valid |-> + (slv_ar_select_i < NoMstPorts))) else + $fatal(1, "slv_ar_select_i is %d: AR has selected a slave that is not defined.\ + NoMstPorts: %d", slv_ar_select_i, NoMstPorts); + aw_valid_stable: assert property( @(posedge clk_i) (aw_valid && !aw_ready) |=> aw_valid) else + $fatal(1, "aw_valid was deasserted, when aw_ready = 0 in last cycle."); + ar_valid_stable: assert property( @(posedge clk_i) + (ar_valid && !ar_ready) |=> ar_valid) else + $fatal(1, "ar_valid was deasserted, when ar_ready = 0 in last cycle."); + slv_aw_chan_stable: assert property( @(posedge clk_i) (aw_valid && !aw_ready) + |=> $stable(slv_req_i.aw)) else + $fatal(1, "slv_aw_chan unstable with valid set."); + slv_aw_select_stable: assert property( @(posedge clk_i) (aw_valid && !aw_ready) + |=> $stable(slv_aw_select_i)) else + $fatal(1, "slv_aw_select_i unstable with valid set."); + slv_ar_chan_stable: assert property( @(posedge clk_i) (ar_valid && !ar_ready) + |=> $stable(slv_req_i.ar)) else + $fatal(1, "slv_ar_chan unstable with valid set."); + slv_ar_select_stable: assert property( @(posedge clk_i) (ar_valid && !ar_ready) + |=> $stable(slv_ar_select_i)) else + $fatal(1, "slv_ar_select_i unstable with valid set."); + internal_ar_select: assert property( @(posedge clk_i) + (ar_valid |-> slv_ar_select_i < NoMstPorts)) + else $fatal(1, "slv_ar_select_i illegal while ar_valid."); + internal_aw_select: assert property( @(posedge clk_i) + (aw_valid |-> slv_aw_select_i < NoMstPorts)) + else $fatal(1, "slv_aw_select_i illegal while aw_valid."); + w_underflow: assert property( @(posedge clk_i) + ((w_open == '0) && (w_cnt_up ^ w_cnt_down) |-> !w_cnt_down)) else + $fatal(1, "W counter underflowed!"); + `ASSUME(NoAtopAllowed, !AtopSupport && slv_req_i.aw_valid |-> slv_req_i.aw.atop == '0) +`endif +`endif +// pragma translate_on + end +endmodule + + +module axi_demux_id_counters #( + // the lower bits of the AXI ID that should be considered, results in 2**AXI_ID_BITS counters + parameter int unsigned AxiIdBits = 2, + parameter int unsigned CounterWidth = 4, + parameter type mst_port_select_t = logic +) ( + input clk_i, // Clock + input rst_ni, // Asynchronous reset active low + // lookup + input logic [AxiIdBits-1:0] lookup_axi_id_i, + output mst_port_select_t lookup_mst_select_o, + output logic lookup_mst_select_occupied_o, + // push + output logic full_o, + input logic [AxiIdBits-1:0] push_axi_id_i, + input mst_port_select_t push_mst_select_i, + input logic push_i, + // inject ATOPs in AR channel + input logic [AxiIdBits-1:0] inject_axi_id_i, + input logic inject_i, + // pop + input logic [AxiIdBits-1:0] pop_axi_id_i, + input logic pop_i +); + localparam int unsigned NoCounters = 2**AxiIdBits; + typedef logic [CounterWidth-1:0] cnt_t; + + // registers, each gets loaded when push_en[i] + mst_port_select_t [NoCounters-1:0] mst_select_q; + + // counter signals + logic [NoCounters-1:0] push_en, inject_en, pop_en, occupied, cnt_full; + + //----------------------------------- + // Lookup + //----------------------------------- + assign lookup_mst_select_o = mst_select_q[lookup_axi_id_i]; + assign lookup_mst_select_occupied_o = occupied[lookup_axi_id_i]; + //----------------------------------- + // Push and Pop + //----------------------------------- + assign push_en = (push_i) ? (1 << push_axi_id_i) : '0; + assign inject_en = (inject_i) ? (1 << inject_axi_id_i) : '0; + assign pop_en = (pop_i) ? (1 << pop_axi_id_i) : '0; + assign full_o = |cnt_full; + // counters + for (genvar i = 0; i < NoCounters; i++) begin : gen_counters + logic cnt_en, cnt_down, overflow; + cnt_t cnt_delta, in_flight; + always_comb begin + unique case ({push_en[i], inject_en[i], pop_en[i]}) + 3'b001 : begin // pop_i = -1 + cnt_en = 1'b1; + cnt_down = 1'b1; + cnt_delta = cnt_t'(1); + end + 3'b010 : begin // inject_i = +1 + cnt_en = 1'b1; + cnt_down = 1'b0; + cnt_delta = cnt_t'(1); + end + // 3'b011, inject_i & pop_i = 0 --> use default + 3'b100 : begin // push_i = +1 + cnt_en = 1'b1; + cnt_down = 1'b0; + cnt_delta = cnt_t'(1); + end + // 3'b101, push_i & pop_i = 0 --> use default + 3'b110 : begin // push_i & inject_i = +2 + cnt_en = 1'b1; + cnt_down = 1'b0; + cnt_delta = cnt_t'(2); + end + 3'b111 : begin // push_i & inject_i & pop_i = +1 + cnt_en = 1'b1; + cnt_down = 1'b0; + cnt_delta = cnt_t'(1); + end + default : begin // do nothing to the counters + cnt_en = 1'b0; + cnt_down = 1'b0; + cnt_delta = cnt_t'(0); + end + endcase + end + + delta_counter #( + .WIDTH ( CounterWidth ), + .STICKY_OVERFLOW ( 1'b0 ) + ) i_in_flight_cnt ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .clear_i ( 1'b0 ), + .en_i ( cnt_en ), + .load_i ( 1'b0 ), + .down_i ( cnt_down ), + .delta_i ( cnt_delta ), + .d_i ( '0 ), + .q_o ( in_flight ), + .overflow_o ( overflow ) + ); + assign occupied[i] = |in_flight; + assign cnt_full[i] = overflow | (&in_flight); + + // holds the selection signal for this id + `FFLARN(mst_select_q[i], push_mst_select_i, push_en[i], '0, clk_i, rst_ni) + +// pragma translate_off +`ifndef VERILATOR +`ifndef XSIM + // Validate parameters. + cnt_underflow: assert property( + @(posedge clk_i) disable iff (~rst_ni) (pop_en[i] |=> !overflow)) else + $fatal(1, "axi_demux_id_counters > Counter: %0d underflowed.\ + The reason is probably a faulty AXI response.", i); +`endif +`endif +// pragma translate_on + end +endmodule + diff --git a/vendor/pulp-platform/axi/src/axi_dw_downsizer.sv b/vendor/pulp-platform/axi/src/axi_dw_downsizer.sv index 78b053d998c..88d774ad09f 100644 --- a/vendor/pulp-platform/axi/src/axi_dw_downsizer.sv +++ b/vendor/pulp-platform/axi/src/axi_dw_downsizer.sv @@ -397,11 +397,12 @@ module axi_dw_downsizer #( r_state_d = R_PASSTHROUGH; // Save beat - r_req_d.ar = slv_req_i.ar ; - r_req_d.ar_valid = 1'b1 ; - r_req_d.burst_len = slv_req_i.ar.len ; - r_req_d.orig_ar_size = slv_req_i.ar.size; - r_req_d.injected_aw = 1'b0 ; + r_req_d.ar = slv_req_i.ar ; + r_req_d.ar_valid = 1'b1 ; + r_req_d.burst_len = slv_req_i.ar.len ; + r_req_d.orig_ar_size = slv_req_i.ar.size ; + r_req_d.injected_aw = 1'b0 ; + r_req_d.r.resp = axi_pkg::RESP_EXOKAY; case (r_req_d.ar.burst) axi_pkg::BURST_INCR : begin @@ -476,6 +477,7 @@ module axi_dw_downsizer #( r_req_d.burst_len = w_req_q.orig_aw_len ; r_req_d.orig_ar_size = w_req_q.orig_aw_size ; r_req_d.injected_aw = 1'b1 ; + r_req_d.r.resp = axi_pkg::RESP_EXOKAY ; // Default state r_state_d = R_PASSTHROUGH; @@ -794,10 +796,10 @@ module axi_dw_downsizer #( // Can start a new request as soon as w_state_d is W_IDLE if (w_state_d == W_IDLE) begin // Reset channels - w_req_d.aw = '0 ; - w_req_d.aw_valid = 1'b0 ; - w_req_d.aw_throw_error = 1'b0 ; - w_req_d.burst_resp = axi_pkg::RESP_OKAY; + w_req_d.aw = '0 ; + w_req_d.aw_valid = 1'b0 ; + w_req_d.aw_throw_error = 1'b0 ; + w_req_d.burst_resp = axi_pkg::RESP_EXOKAY; if (!forward_b_beat_full) begin if (slv_req_i.aw_valid && slv_req_i.aw.atop[axi_pkg::ATOP_R_RESP]) begin // ATOP with an R response diff --git a/vendor/pulp-platform/axi/src/axi_from_mem.sv b/vendor/pulp-platform/axi/src/axi_from_mem.sv index 23ed2d3af93..466a384e353 100644 --- a/vendor/pulp-platform/axi/src/axi_from_mem.sv +++ b/vendor/pulp-platform/axi/src/axi_from_mem.sv @@ -10,7 +10,7 @@ // // Authors: // - Christopher Reinwardt -// - Nicole Narr `include "axi/typedef.svh" diff --git a/vendor/pulp-platform/axi/src/axi_id_serialize.sv b/vendor/pulp-platform/axi/src/axi_id_serialize.sv index b3d28e8390c..9a787dd2529 100644 --- a/vendor/pulp-platform/axi/src/axi_id_serialize.sv +++ b/vendor/pulp-platform/axi/src/axi_id_serialize.sv @@ -11,6 +11,7 @@ // // Authors: // - Andreas Kurth +// - Paul Scheffler `include "axi/assign.svh" `include "axi/typedef.svh" @@ -55,7 +56,17 @@ module axi_id_serialize #( /// Request struct type of the AXI4+ATOP master port parameter type mst_req_t = logic, /// Response struct type of the AXI4+ATOP master port - parameter type mst_resp_t = logic + parameter type mst_resp_t = logic, + /// A custom offset (modulo `AxiMstPortMaxUniqIds`, ignored for input IDs remapped through + /// `IdMap`) for the assigned output IDs. + parameter int unsigned MstIdBaseOffset = 32'd0, + /// Explicit input-output ID map. If an input ID `id` does not appear in this mapping (default), + /// it is simply mapped to the output ID `id % AxiMstPortMaxUniqIds`. If `id` appears in more + /// than one entry, it is matched to the *last* matching entry's output ID. + /// Number of Entries in the explicit ID map (default: None) + parameter int unsigned IdMapNumEntries = 32'd0, + /// Explicit ID map; index [0] in each entry is the input ID to match, index [1] the output ID. + parameter int unsigned IdMap [IdMapNumEntries-1:0][0:1] = '{default: {32'b0, 32'b0}} ) ( /// Rising-edge clock of both ports input logic clk_i, @@ -143,9 +154,27 @@ module axi_id_serialize #( /// R channel at master port `AXI_TYPEDEF_R_CHAN_T(mst_r_t, data_t, mst_id_t, user_t) + /// Type for slave ID map + typedef mst_id_t [2**AxiSlvPortIdWidth-1:0] slv_id_map_t; + + /// Resolve target output ID for each possible input ID as a parameter + function automatic slv_id_map_t map_slv_ids(); + slv_id_map_t ret = '0; + // Populate output with default mapping, including `MstIdBaseOffset` + for (int unsigned i = 0; i < 2**AxiSlvPortIdWidth; ++i) + ret[i] = (i + MstIdBaseOffset) % AxiMstPortMaxUniqIds; + // For each explicitly mapped input ID, set the desired output ID + for (int unsigned i = 0; i < IdMapNumEntries; ++i) + ret[IdMap[i][0]] = IdMap[i][1]; + return ret; + endfunction + + /// Input-to-output ID map used + localparam slv_id_map_t SlvIdMap = map_slv_ids(); + select_t slv_aw_select, slv_ar_select; - assign slv_aw_select = select_t'(slv_req_i.aw.id % AxiMstPortMaxUniqIds); // TODO: customizable base - assign slv_ar_select = select_t'(slv_req_i.ar.id % AxiMstPortMaxUniqIds); + assign slv_aw_select = select_t'(SlvIdMap[slv_req_i.aw.id]); + assign slv_ar_select = select_t'(SlvIdMap[slv_req_i.ar.id]); slv_req_t [AxiMstPortMaxUniqIds-1:0] to_serializer_reqs; slv_resp_t [AxiMstPortMaxUniqIds-1:0] to_serializer_resps; diff --git a/vendor/pulp-platform/axi/src/axi_lite_dw_converter.sv b/vendor/pulp-platform/axi/src/axi_lite_dw_converter.sv new file mode 100644 index 00000000000..d8055481fb7 --- /dev/null +++ b/vendor/pulp-platform/axi/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 Roenninger + +/// # 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/vendor/pulp-platform/axi/src/axi_lite_from_mem.sv b/vendor/pulp-platform/axi/src/axi_lite_from_mem.sv index 6b5446c6b0f..e13793b37cf 100644 --- a/vendor/pulp-platform/axi/src/axi_lite_from_mem.sv +++ b/vendor/pulp-platform/axi/src/axi_lite_from_mem.sv @@ -10,6 +10,7 @@ // // Authors: // - Wolfgang Roenninger +// - Nicole Narr /// Protocol adapter which translates memory requests to the AXI4-Lite protocol. /// diff --git a/vendor/pulp-platform/axi/src/axi_pkg.sv b/vendor/pulp-platform/axi/src/axi_pkg.sv index 7ef3bbcd7f5..dd60c451ec4 100644 --- a/vendor/pulp-platform/axi/src/axi_pkg.sv +++ b/vendor/pulp-platform/axi/src/axi_pkg.sv @@ -12,33 +12,58 @@ // Authors: // - Andreas Kurth // - Florian Zaruba +// - Thomas Benz // - Wolfgang Roenninger // - Fabian Schuiki +// - Cyril Koenig // - Matheus Cavalcante //! AXI Package /// Contains all necessary type definitions, constants, and generally useful functions. package axi_pkg; - /// AXI Transaction Burst Type. - typedef logic [1:0] burst_t; + /// AXI Transaction Burst Width. + parameter int unsigned BurstWidth = 32'd2; + /// AXI Transaction Response Width. + parameter int unsigned RespWidth = 32'd2; + /// AXI Transaction Cacheability Width. + parameter int unsigned CacheWidth = 32'd4; + /// AXI Transaction Protection Width. + parameter int unsigned ProtWidth = 32'd3; + /// AXI Transaction Quality of Service Width. + parameter int unsigned QosWidth = 32'd4; + /// AXI Transaction Region Width. + parameter int unsigned RegionWidth = 32'd4; + /// AXI Transaction Length Width. + parameter int unsigned LenWidth = 32'd8; + /// AXI Transaction Size Width. + parameter int unsigned SizeWidth = 32'd3; + /// AXI Lock Width. + parameter int unsigned LockWidth = 32'd1; + /// AXI5 Atomic Operation Width. + parameter int unsigned AtopWidth = 32'd6; + /// AXI5 Non-Secure Address Identifier. + parameter int unsigned NsaidWidth = 32'd4; + + /// AXI Transaction Burst Width. + typedef logic [1:0] burst_t; /// AXI Transaction Response Type. - typedef logic [1:0] resp_t; + typedef logic [1:0] resp_t; /// AXI Transaction Cacheability Type. - typedef logic [3:0] cache_t; + typedef logic [3:0] cache_t; /// AXI Transaction Protection Type. - typedef logic [2:0] prot_t; + typedef logic [2:0] prot_t; /// AXI Transaction Quality of Service Type. - typedef logic [3:0] qos_t; + typedef logic [3:0] qos_t; /// AXI Transaction Region Type. typedef logic [3:0] region_t; /// AXI Transaction Length Type. - typedef logic [7:0] len_t; + typedef logic [7:0] len_t; /// AXI Transaction Size Type. - typedef logic [2:0] size_t; + typedef logic [2:0] size_t; /// AXI5 Atomic Operation Type. - typedef logic [5:0] atop_t; // atomic operations + typedef logic [5:0] atop_t; // atomic operations /// AXI5 Non-Secure Address Identifier. - typedef logic [3:0] nsaid_t; + typedef logic [3:0] nsaid_t; /// In a fixed burst: /// - The address is the same for every transfer in the burst. @@ -175,7 +200,7 @@ package axi_pkg; beat_lower_byte(largest_addr_t addr, size_t size, len_t len, burst_t burst, shortint unsigned strobe_width, shortint unsigned i_beat); largest_addr_t _addr = beat_addr(addr, size, len, burst, i_beat); - return _addr - (_addr / strobe_width) * strobe_width; + return shortint'(_addr - (_addr / strobe_width) * strobe_width); endfunction /// Index of highest byte in beat (see A3-51). @@ -290,6 +315,65 @@ package axi_pkg; endcase endfunction + /// AW Width: Returns the width of the AW channel payload + function automatic int unsigned aw_width(int unsigned addr_width, int unsigned id_width, + int unsigned user_width ); + // Sum the individual bit widths of the signals + return (id_width + addr_width + LenWidth + SizeWidth + BurstWidth + LockWidth + CacheWidth + + ProtWidth + QosWidth + RegionWidth + AtopWidth + user_width ); + endfunction + + /// W Width: Returns the width of the W channel payload + function automatic int unsigned w_width(int unsigned data_width, int unsigned user_width ); + // Sum the individual bit widths of the signals + return (data_width + data_width / 32'd8 + 32'd1 + user_width); + // ^- StrobeWidth ^- LastWidth + endfunction + + /// B Width: Returns the width of the B channel payload + function automatic int unsigned b_width(int unsigned id_width, int unsigned user_width ); + // Sum the individual bit widths of the signals + return (id_width + RespWidth + user_width); + endfunction + + /// AR Width: Returns the width of the AR channel payload + function automatic int unsigned ar_width(int unsigned addr_width, int unsigned id_width, + int unsigned user_width ); + // Sum the individual bit widths of the signals + return (id_width + addr_width + LenWidth + SizeWidth + BurstWidth + LockWidth + CacheWidth + + ProtWidth + QosWidth + RegionWidth + user_width ); + endfunction + + /// R Width: Returns the width of the R channel payload + function automatic int unsigned r_width(int unsigned data_width, int unsigned id_width, + int unsigned user_width ); + // Sum the individual bit widths of the signals + return (id_width + data_width + RespWidth + 32'd1 + user_width); + // ^- LastWidth + endfunction + + /// Request Width: Returns the width of the request channel + function automatic int unsigned req_width(int unsigned addr_width, int unsigned data_width, + int unsigned id_width, int unsigned aw_user_width, + int unsigned ar_user_width, int unsigned w_user_width ); + // Sum the individual bit widths of the signals and their handshakes + // v- valids + return (aw_width(addr_width, id_width, aw_user_width) + 32'd1 + + w_width(data_width, w_user_width) + 32'd1 + + ar_width(addr_width, id_width, ar_user_width) + 32'd1 + 32'd1 + 32'd1 ); + // ^- R, ^- B ready + endfunction + + /// Response Width: Returns the width of the response channel + function automatic int unsigned rsp_width(int unsigned data_width, int unsigned id_width, + int unsigned r_user_width, int unsigned b_user_width ); + // Sum the individual bit widths of the signals and their handshakes + // v- valids + return (r_width(data_width, id_width, r_user_width) + 32'd1 + + b_width(id_width, b_user_width) + 32'd1 + 32'd1 + 32'd1 + 32'd1); + // ^- AW, ^- AR, ^- W ready + endfunction + // ATOP[5:0] /// - Sends a single data value with an address. /// - The target swaps the value at the addressed location with the data value that is supplied in @@ -447,4 +531,5 @@ package axi_pkg; logic [31:0] start_addr; logic [31:0] end_addr; } xbar_rule_32_t; + endpackage diff --git a/vendor/pulp-platform/axi/src/axi_test.sv b/vendor/pulp-platform/axi/src/axi_test.sv index 7c7fab63ef8..c9fca2697c1 100644 --- a/vendor/pulp-platform/axi/src/axi_test.sv +++ b/vendor/pulp-platform/axi/src/axi_test.sv @@ -13,6 +13,7 @@ // - Andreas Kurth // - Wolfgang Roenninger // - Fabian Schuiki +// - Thomas Benz // - Matheus Cavalcante @@ -698,6 +699,7 @@ package axi_test; parameter int RESP_MIN_WAIT_CYCLES = 0, parameter int RESP_MAX_WAIT_CYCLES = 20, // AXI feature usage + parameter int SIZE_ALIGN = 0, parameter int AXI_MAX_BURST_LEN = 0, // maximum number of beats in burst; 0 = AXI max (256) parameter int TRAFFIC_SHAPING = 0, parameter bit AXI_EXCLS = 1'b0, @@ -926,7 +928,7 @@ package axi_test; end end - ax_beat.ax_addr = addr; + ax_beat.ax_addr = axi_pkg::aligned_addr(addr, axi_pkg::size_t'(SIZE_ALIGN) ); rand_success = std::randomize(id); assert(rand_success); rand_success = std::randomize(qos); assert(rand_success); // The random ID *must* be legalized with `legalize_id()` before the beat is sent! This is @@ -1560,7 +1562,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 @@ -1574,7 +1576,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 @@ -1586,7 +1588,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 @@ -1603,7 +1605,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 @@ -1618,7 +1620,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 @@ -1636,26 +1638,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 @@ -1721,22 +1723,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 @@ -1746,7 +1750,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 @@ -1757,7 +1761,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 @@ -1773,7 +1777,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 @@ -2042,6 +2046,7 @@ package axi_test; byte_t act_data; byte_t exp_data[$]; byte_t tst_data[$]; + int first_byte_to_check; forever begin wait (this.ar_sample[id].size() > 0); ar_beat = this.ar_sample[id].pop_front(); @@ -2056,6 +2061,10 @@ package axi_test; ar_beat.ax_burst, i); beat_address = axi_pkg::aligned_addr(beat_address, ar_beat.ax_size); bus_address = axi_pkg::aligned_addr(beat_address, BUS_SIZE); + if(i!=0) + first_byte_to_check = 0; + else + first_byte_to_check = ar_beat.ax_addr - beat_address; if (!this.memory_q.exists(bus_address)) begin for (int unsigned j = 0; j < axi_pkg::num_bytes(BUS_SIZE); j++) begin this.memory_q[bus_address+j].push_back(8'bxxxxxxxx); @@ -2064,7 +2073,7 @@ package axi_test; // Assert that the correct data is read. if (this.check_en[ReadCheck] && (r_beat.r_resp inside {axi_pkg::RESP_OKAY, axi_pkg::RESP_EXOKAY})) begin - for (int unsigned j = 0; j < axi_pkg::num_bytes(ar_beat.ax_size); j++) begin + for (int unsigned j = first_byte_to_check; j < axi_pkg::num_bytes(ar_beat.ax_size); j++) begin idx_data = 8*BUS_SIZE'(beat_address+j); act_data = r_beat.r_data[idx_data+:8]; exp_data = this.memory_q[beat_address+j]; @@ -2305,6 +2314,231 @@ package axi_test; endclass : axi_scoreboard + + class axi_file_master #( + // AXI interface parameters + parameter int AW = 32, + parameter int DW = 32, + parameter int IW = 8, + parameter int UW = 1, + // Stimuli application and test time + parameter time TA = 0ps, + parameter time TT = 0ps + ); + + typedef axi_test::axi_driver #( + .AW(AW), .DW(DW), .IW(IW), .UW(UW), .TA(TA), .TT(TT) + ) axi_driver_t; + + typedef axi_driver_t::ax_beat_t ax_beat_t; + typedef axi_driver_t::b_beat_t b_beat_t; + typedef axi_driver_t::r_beat_t r_beat_t; + typedef axi_driver_t::w_beat_t w_beat_t; + + axi_driver_t drv; + + int read_fd, write_fd; + + // store holding read/write transactions between aw-b and ar-r + logic b_outst[$]; + logic r_outst[$]; + + // proper decoupling: populate queues from file + ax_beat_t aw_queue[$]; + w_beat_t w_queue[$]; + ax_beat_t ar_queue[$]; + + // populated by read file function + int num_reads; + int num_writes; + + function new( + virtual AXI_BUS_DV #( + .AXI_ADDR_WIDTH(AW), + .AXI_DATA_WIDTH(DW), + .AXI_ID_WIDTH(IW), + .AXI_USER_WIDTH(UW) + ) axi + ); + this.drv = new(axi); + this.reset(); + endfunction + + function void reset(); + drv.reset_master(); + endfunction + + function void parse_write(); + // parsing works + int parse_ok; + + // populate according to file + while (!$feof(this.write_fd)) begin + automatic ax_beat_t current_aw = new; + parse_ok = 1; + parse_ok = parse_ok & ($fscanf(this.write_fd, "%d\n", current_aw.ax_id ) != -1); + parse_ok = parse_ok & ($fscanf(this.write_fd, "0x%x\n", current_aw.ax_addr ) != -1); + parse_ok = parse_ok & ($fscanf(this.write_fd, "%d\n", current_aw.ax_len ) != -1); + parse_ok = parse_ok & ($fscanf(this.write_fd, "%d\n", current_aw.ax_size ) != -1); + parse_ok = parse_ok & ($fscanf(this.write_fd, "%d\n", current_aw.ax_burst ) != -1); + parse_ok = parse_ok & ($fscanf(this.write_fd, "%d\n", current_aw.ax_lock ) != -1); + parse_ok = parse_ok & ($fscanf(this.write_fd, "%d\n", current_aw.ax_cache ) != -1); + parse_ok = parse_ok & ($fscanf(this.write_fd, "%d\n", current_aw.ax_prot ) != -1); + parse_ok = parse_ok & ($fscanf(this.write_fd, "%d\n", current_aw.ax_qos ) != -1); + parse_ok = parse_ok & ($fscanf(this.write_fd, "%d\n", current_aw.ax_region) != -1); + parse_ok = parse_ok & ($fscanf(this.write_fd, "%d\n", current_aw.ax_atop ) != -1); + parse_ok = parse_ok & ($fscanf(this.write_fd, "%d\n", current_aw.ax_user ) != -1); + if (parse_ok) begin + this.aw_queue.push_back(current_aw); + this.b_outst.push_back(1'b1); + // $display("%p", current_aw); + end else begin + $warning("Issue parsing AW: %p", current_aw); + end + + // get write data + strobe + for (int i = 0; i <= current_aw.ax_len; i++) begin + automatic w_beat_t current_w = new; + parse_ok = parse_ok & ($fscanf(this.write_fd, "0x%x 0x%x %d\n", current_w.w_data, current_w.w_strb, current_w.w_user) != -1); + current_w.w_last = 1'b0; + if (i == current_aw.ax_len) begin + current_w.w_last = 1'b1; + end + if (parse_ok) begin + this.w_queue.push_back(current_w); + // $display("%p", current_w); + end else begin + $warning("Issue parsing W: %p of AW: %p", current_w, current_aw); + end + end + end + + // debug: print queues + // $display("%p", this.aw_queue); + // $display("%p", this.w_queue); + endfunction + + function void parse_read(); + // parsing works + int parse_ok; + + // populate according to file + while (!$feof(this.read_fd)) begin + automatic ax_beat_t current_ar = new; + parse_ok = 1; + parse_ok = parse_ok & ($fscanf(this.read_fd, "%d\n", current_ar.ax_id ) != -1); + parse_ok = parse_ok & ($fscanf(this.read_fd, "0x%x\n", current_ar.ax_addr ) != -1); + parse_ok = parse_ok & ($fscanf(this.read_fd, "%d\n", current_ar.ax_len ) != -1); + parse_ok = parse_ok & ($fscanf(this.read_fd, "%d\n", current_ar.ax_size ) != -1); + parse_ok = parse_ok & ($fscanf(this.read_fd, "%d\n", current_ar.ax_burst ) != -1); + parse_ok = parse_ok & ($fscanf(this.read_fd, "%d\n", current_ar.ax_lock ) != -1); + parse_ok = parse_ok & ($fscanf(this.read_fd, "%d\n", current_ar.ax_cache ) != -1); + parse_ok = parse_ok & ($fscanf(this.read_fd, "%d\n", current_ar.ax_prot ) != -1); + parse_ok = parse_ok & ($fscanf(this.read_fd, "%d\n", current_ar.ax_qos ) != -1); + parse_ok = parse_ok & ($fscanf(this.read_fd, "%d\n", current_ar.ax_region) != -1); + parse_ok = parse_ok & ($fscanf(this.read_fd, "%d\n", current_ar.ax_user ) != -1); + if (parse_ok) begin + this.ar_queue.push_back(current_ar); + this.r_outst.push_back(1'b1); + // $display("%p", current_ar); + end else begin + $warning("Issue parsing AR: %p", current_ar); + end + end + + // debug: print queues + // $display("%p", this.ar_queue); + endfunction + + function void load_files( + string read_file_name, + string write_file_name + ); + this.read_fd = $fopen(read_file_name, "r"); + this.write_fd = $fopen(write_file_name, "r"); + + // check if files are opened + if (this.read_fd) begin + $info("File %s opened successfully as %d", read_file_name, this.read_fd); + end else begin + $fatal(1, "File %s not found", read_file_name); + end + if (this.write_fd) begin + $info("File %s opened successfully as %d", write_file_name, this.write_fd); + end else begin + $fatal(1, "File %s not found", write_file_name); + end + + // read files + this.parse_read(); + this.parse_write(); + + // update status + this.num_reads = this.ar_queue.size(); + this.num_writes = this.aw_queue.size(); + endfunction + + task run_aw(); + // send aws while there are some left + while (this.aw_queue.size() > 0) begin + // display("Sending AW: %p", this.aw_queue[0]); + drv.send_aw(this.aw_queue[0]); + void'(this.aw_queue.pop_front()); + end + endtask + + task run_w(); + // send ws while there are some left + while (this.w_queue.size() > 0) begin + // $display("Sending W: %p", this.w_queue[0]); + drv.send_w(this.w_queue[0]); + void'(this.w_queue.pop_front()); + end + endtask + + task run_ar(); + // send ars while there are some left + while (this.ar_queue.size() > 0) begin + // $display("Sending AR: %p", this.ar_queue[0]); + drv.send_ar(this.ar_queue[0]); + void'(this.ar_queue.pop_front()); + end + endtask + + task wait_b(); + automatic b_beat_t b_beat = new; + // wait for bs while there are some left + while (this.b_outst.size() > 0) begin + // $display("Waiting B"); + drv.recv_b(b_beat); + void'(this.b_outst.pop_front()); + end + endtask + + task wait_r(); + automatic r_beat_t r_beat = new; + // wait for rs while there are some left + while (this.r_outst.size() > 0) begin + // $display("Waiting R"); + do begin + drv.recv_r(r_beat); + end while (r_beat.r_last !== 1'b1); + void'(this.r_outst.pop_front()); + end + endtask + + task run(); + fork + this.run_aw(); + this.run_w (); + this.run_ar(); + this.wait_b(); + this.wait_r(); + join + endtask + + endclass + endpackage // non synthesisable axi logger module diff --git a/vendor/pulp-platform/axi/src/axi_to_axi_lite.sv b/vendor/pulp-platform/axi/src/axi_to_axi_lite.sv index a0702bb79cb..ad2e4141460 100644 --- a/vendor/pulp-platform/axi/src/axi_to_axi_lite.sv +++ b/vendor/pulp-platform/axi/src/axi_to_axi_lite.sv @@ -22,6 +22,7 @@ module axi_to_axi_lite #( parameter int unsigned AxiUserWidth = 32'd0, parameter int unsigned AxiMaxWriteTxns = 32'd0, parameter int unsigned AxiMaxReadTxns = 32'd0, + parameter bit FullBW = 0, // ID Queue in Full BW mode in axi_burst_splitter parameter bit FallThrough = 1'b1, // FIFOs in Fall through mode in ID reflect parameter type full_req_t = logic, parameter type full_resp_t = logic, @@ -61,6 +62,7 @@ module axi_to_axi_lite #( axi_burst_splitter #( .MaxReadTxns ( AxiMaxReadTxns ), .MaxWriteTxns ( AxiMaxWriteTxns ), + .FullBW ( FullBW ), .AddrWidth ( AxiAddrWidth ), .DataWidth ( AxiDataWidth ), .IdWidth ( AxiIdWidth ), @@ -255,7 +257,8 @@ module axi_to_axi_lite_intf #( parameter int unsigned AXI_MAX_WRITE_TXNS = 32'd1, /// Maximum number of outstanding reads. parameter int unsigned AXI_MAX_READ_TXNS = 32'd1, - parameter bit FALL_THROUGH = 1'b1 + parameter bit FALL_THROUGH = 1'b1, + parameter bit FULL_BW = 0 ) ( input logic clk_i, input logic rst_ni, @@ -304,6 +307,7 @@ module axi_to_axi_lite_intf #( .AxiMaxWriteTxns ( AXI_MAX_WRITE_TXNS ), .AxiMaxReadTxns ( AXI_MAX_READ_TXNS ), .FallThrough ( FALL_THROUGH ), // FIFOs in Fall through mode in ID reflect + .FullBW ( FULL_BW ), .full_req_t ( full_req_t ), .full_resp_t ( full_resp_t ), .lite_req_t ( lite_req_t ), diff --git a/vendor/pulp-platform/axi/src/axi_to_detailed_mem.sv b/vendor/pulp-platform/axi/src/axi_to_detailed_mem.sv new file mode 100644 index 00000000000..8f5c11d21d3 --- /dev/null +++ b/vendor/pulp-platform/axi/src/axi_to_detailed_mem.sv @@ -0,0 +1,746 @@ +// 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: +// - Michael Rogenmoser +// - Thomas Benz + +`include "common_cells/registers.svh" +/// AXI4+ATOP slave module which translates AXI bursts into a memory stream. +/// If both read and write channels of the AXI4+ATOP are active, both will have an +/// utilization of 50%. +module axi_to_detailed_mem #( + /// AXI4+ATOP request type. See `include/axi/typedef.svh`. + parameter type axi_req_t = logic, + /// AXI4+ATOP response type. See `include/axi/typedef.svh`. + parameter type axi_resp_t = logic, + /// Address width, has to be less or equal than the width off the AXI address field. + /// Determines the width of `mem_addr_o`. Has to be wide enough to emit the memory region + /// which should be accessible. + parameter int unsigned AddrWidth = 0, + /// AXI4+ATOP data width. + parameter int unsigned DataWidth = 0, + /// AXI4+ATOP ID width. + parameter int unsigned IdWidth = 0, + /// AXI4+ATOP user width. + parameter int unsigned UserWidth = 0, + /// Number of banks at output, must evenly divide `DataWidth`. + parameter int unsigned NumBanks = 0, + /// Depth of memory response buffer. This should be equal to the memory response latency. + parameter int unsigned BufDepth = 1, + /// Hide write requests if the strb == '0 + parameter bit HideStrb = 1'b0, + /// Depth of output fifo/fall_through_register. Increase for asymmetric backpressure (contention) on banks. + parameter int unsigned OutFifoDepth = 1, + /// Dependent parameter, do not override. Memory address type. + localparam type addr_t = logic [AddrWidth-1:0], + /// Dependent parameter, do not override. Memory data type. + localparam type mem_data_t = logic [DataWidth/NumBanks-1:0], + /// Dependent parameter, do not override. Memory write strobe type. + localparam type mem_strb_t = logic [DataWidth/NumBanks/8-1:0], + /// Dependent parameter, do not override. Memory id type. + localparam type mem_id_t = logic [IdWidth-1:0], + /// Dependent parameter, do not override. Memory user type. + localparam type mem_user_t = logic [UserWidth-1:0] +) ( + /// Clock input. + input logic clk_i, + /// Asynchronous reset, active low. + input logic rst_ni, + /// The unit is busy handling an AXI4+ATOP request. + output logic busy_o, + /// AXI4+ATOP slave port, request input. + input axi_req_t axi_req_i, + /// AXI4+ATOP slave port, response output. + output axi_resp_t axi_resp_o, + /// Memory stream master, request is valid for this bank. + output logic [NumBanks-1:0] mem_req_o, + /// Memory stream master, request can be granted by this bank. + input logic [NumBanks-1:0] mem_gnt_i, + /// Memory stream master, byte address of the request. + output addr_t [NumBanks-1:0] mem_addr_o, + /// Memory stream master, write data for this bank. Valid when `mem_req_o`. + output mem_data_t [NumBanks-1:0] mem_wdata_o, + /// Memory stream master, byte-wise strobe (byte enable). + output mem_strb_t [NumBanks-1:0] mem_strb_o, + /// Memory stream master, `axi_pkg::atop_t` signal associated with this request. + output axi_pkg::atop_t [NumBanks-1:0] mem_atop_o, + /// Memory stream master, lock signal. + output logic [NumBanks-1:0] mem_lock_o, + /// Memory stream master, write enable. Then asserted store of `mem_w_data` is requested. + output logic [NumBanks-1:0] mem_we_o, + /// Memory stream master, ID. Response ID is managed internally, ensure in-order responses. + output mem_id_t [NumBanks-1:0] mem_id_o, + /// Memory stream master, user signal. Ax channel user bits used. + output mem_user_t [NumBanks-1:0] mem_user_o, + /// Memory stream master, cache signal. + output axi_pkg::cache_t [NumBanks-1:0] mem_cache_o, + /// Memory stream master, protection signal. + output axi_pkg::prot_t [NumBanks-1:0] mem_prot_o, + /// Memory stream master, QOS signal. + output axi_pkg::qos_t [NumBanks-1:0] mem_qos_o, + /// Memory stream master, region signal. + output axi_pkg::region_t [NumBanks-1:0] mem_region_o, + /// Memory stream master, response is valid. This module expects always a response valid for a + /// request regardless if the request was a write or a read. + input logic [NumBanks-1:0] mem_rvalid_i, + /// Memory stream master, read response data. + input mem_data_t [NumBanks-1:0] mem_rdata_i, + /// Memory stream master, error response. + input logic [NumBanks-1:0] mem_err_i, + /// Memory stream master, read response exclusive access OK. + input logic [NumBanks-1:0] mem_exokay_i +); + + typedef logic [DataWidth-1:0] axi_data_t; + typedef logic [DataWidth/8-1:0] axi_strb_t; + typedef logic [IdWidth-1:0] axi_id_t; + + typedef struct packed { + addr_t addr; + axi_pkg::atop_t atop; + logic lock; + axi_strb_t strb; + axi_data_t wdata; + logic we; + mem_id_t id; + mem_user_t user; + axi_pkg::cache_t cache; + axi_pkg::prot_t prot; + axi_pkg::qos_t qos; + axi_pkg::region_t region; + } mem_req_t; + + typedef struct packed { + addr_t addr; + axi_pkg::atop_t atop; + logic lock; + axi_strb_t strb; + axi_id_t id; + logic last; + axi_pkg::qos_t qos; + axi_pkg::size_t size; + logic write; + mem_user_t user; + axi_pkg::cache_t cache; + axi_pkg::prot_t prot; + axi_pkg::region_t region; + } meta_t; + + typedef struct packed { + axi_data_t data; + logic [NumBanks-1:0] err; + logic [NumBanks-1:0] exokay; + } mem_rsp_t; + + mem_rsp_t mem_rdata, + m2s_resp; + axi_pkg::len_t r_cnt_d, r_cnt_q, + w_cnt_d, w_cnt_q; + logic arb_valid, arb_ready, + rd_valid, rd_ready, + wr_valid, wr_ready, + sel_b, sel_buf_b, + sel_r, sel_buf_r, + sel_valid, sel_ready, + sel_buf_valid, sel_buf_ready, + sel_lock_d, sel_lock_q, + meta_valid, meta_ready, + meta_buf_valid, meta_buf_ready, + meta_sel_d, meta_sel_q, + m2s_req_valid, m2s_req_ready, + m2s_resp_valid, m2s_resp_ready, + mem_req_valid, mem_req_ready, + mem_rvalid; + mem_req_t m2s_req, + mem_req; + meta_t rd_meta, + rd_meta_d, rd_meta_q, + wr_meta, + wr_meta_d, wr_meta_q, + meta, meta_buf; + + assign busy_o = axi_req_i.aw_valid | axi_req_i.ar_valid | axi_req_i.w_valid | + axi_resp_o.b_valid | axi_resp_o.r_valid | + (r_cnt_q > 0) | (w_cnt_q > 0); + + // Handle reads. + always_comb begin + // Default assignments + axi_resp_o.ar_ready = 1'b0; + rd_meta_d = rd_meta_q; + rd_meta = meta_t'{default: '0}; + rd_valid = 1'b0; + r_cnt_d = r_cnt_q; + // Handle R burst in progress. + if (r_cnt_q > '0) begin + rd_meta_d.last = (r_cnt_q == 8'd1); + rd_meta = rd_meta_d; + rd_meta.addr = rd_meta_q.addr + axi_pkg::num_bytes(rd_meta_q.size); + rd_valid = 1'b1; + if (rd_ready) begin + r_cnt_d--; + rd_meta_d.addr = rd_meta.addr; + end + // Handle new AR if there is one. + end else if (axi_req_i.ar_valid) begin + rd_meta_d = '{ + addr: addr_t'(axi_pkg::aligned_addr(axi_req_i.ar.addr, axi_req_i.ar.size)), + atop: '0, + lock: axi_req_i.ar.lock, + strb: '0, + id: axi_req_i.ar.id, + last: (axi_req_i.ar.len == '0), + qos: axi_req_i.ar.qos, + size: axi_req_i.ar.size, + write: 1'b0, + user: axi_req_i.ar.user, + cache: axi_req_i.ar.cache, + prot: axi_req_i.ar.prot, + region: axi_req_i.ar.region + }; + rd_meta = rd_meta_d; + rd_meta.addr = addr_t'(axi_req_i.ar.addr); + rd_valid = 1'b1; + if (rd_ready) begin + r_cnt_d = axi_req_i.ar.len; + axi_resp_o.ar_ready = 1'b1; + end + end + end + + // Handle writes. + always_comb begin + // Default assignments + axi_resp_o.aw_ready = 1'b0; + axi_resp_o.w_ready = 1'b0; + wr_meta_d = wr_meta_q; + wr_meta = meta_t'{default: '0}; + wr_valid = 1'b0; + w_cnt_d = w_cnt_q; + // Handle W bursts in progress. + if (w_cnt_q > '0) begin + wr_meta_d.last = (w_cnt_q == 8'd1); + wr_meta = wr_meta_d; + wr_meta.addr = wr_meta_q.addr + axi_pkg::num_bytes(wr_meta_q.size); + if (axi_req_i.w_valid) begin + wr_valid = 1'b1; + wr_meta.strb = axi_req_i.w.strb; + if (wr_ready) begin + axi_resp_o.w_ready = 1'b1; + w_cnt_d--; + wr_meta_d.addr = wr_meta.addr; + end + end + // Handle new AW if there is one. + end else if (axi_req_i.aw_valid && axi_req_i.w_valid) begin + wr_meta_d = '{ + addr: addr_t'(axi_pkg::aligned_addr(axi_req_i.aw.addr, axi_req_i.aw.size)), + atop: axi_req_i.aw.atop, + lock: axi_req_i.aw.lock, + strb: axi_req_i.w.strb, + id: axi_req_i.aw.id, + last: (axi_req_i.aw.len == '0), + qos: axi_req_i.aw.qos, + size: axi_req_i.aw.size, + write: 1'b1, + user: axi_req_i.aw.user, + cache: axi_req_i.aw.cache, + prot: axi_req_i.aw.prot, + region: axi_req_i.aw.region + }; + wr_meta = wr_meta_d; + wr_meta.addr = addr_t'(axi_req_i.aw.addr); + wr_valid = 1'b1; + if (wr_ready) begin + w_cnt_d = axi_req_i.aw.len; + axi_resp_o.aw_ready = 1'b1; + axi_resp_o.w_ready = 1'b1; + end + end + end + + // Arbitrate between reads and writes. + stream_mux #( + .DATA_T ( meta_t ), + .N_INP ( 32'd2 ) + ) i_ax_mux ( + .inp_data_i ({wr_meta, rd_meta }), + .inp_valid_i ({wr_valid, rd_valid}), + .inp_ready_o ({wr_ready, rd_ready}), + .inp_sel_i ( meta_sel_d ), + .oup_data_o ( meta ), + .oup_valid_o ( arb_valid ), + .oup_ready_i ( arb_ready ) + ); + always_comb begin + meta_sel_d = meta_sel_q; + sel_lock_d = sel_lock_q; + if (sel_lock_q) begin + meta_sel_d = meta_sel_q; + if (arb_valid && arb_ready) begin + sel_lock_d = 1'b0; + end + end else begin + if (wr_valid ^ rd_valid) begin + // If either write or read is valid but not both, select the valid one. + meta_sel_d = wr_valid; + end else if (wr_valid && rd_valid) begin + // If both write and read are valid, decide according to QoS then burst properties. + // Prioritize higher QoS. + if (wr_meta.qos > rd_meta.qos) begin + meta_sel_d = 1'b1; + end else if (rd_meta.qos > wr_meta.qos) begin + meta_sel_d = 1'b0; + // Decide requests with identical QoS. + end else if (wr_meta.qos == rd_meta.qos) begin + // 1. Prioritize individual writes over read bursts. + // Rationale: Read bursts can be interleaved on AXI but write bursts cannot. + if (wr_meta.last && !rd_meta.last) begin + meta_sel_d = 1'b1; + // 2. Prioritize ongoing burst. + // Rationale: Stalled bursts create back-pressure or require costly buffers. + end else if (w_cnt_q > '0) begin + meta_sel_d = 1'b1; + end else if (r_cnt_q > '0) begin + meta_sel_d = 1'b0; + // 3. Otherwise arbitrate round robin to prevent starvation. + end else begin + meta_sel_d = ~meta_sel_q; + end + end + end + // Lock arbitration if valid but not yet ready. + if (arb_valid && !arb_ready) begin + sel_lock_d = 1'b1; + end + end + end + + // Fork arbitrated stream to meta data, memory requests, and R/B channel selection. + stream_fork #( + .N_OUP ( 32'd3 ) + ) i_fork ( + .clk_i, + .rst_ni, + .valid_i ( arb_valid ), + .ready_o ( arb_ready ), + .valid_o ({sel_valid, meta_valid, m2s_req_valid}), + .ready_i ({sel_ready, meta_ready, m2s_req_ready}) + ); + + assign sel_b = meta.write & meta.last; + assign sel_r = ~meta.write | meta.atop[5]; + + stream_fifo #( + .FALL_THROUGH ( 1'b1 ), + .DEPTH ( 32'd1 + BufDepth ), + .T ( logic[1:0] ) + ) i_sel_buf ( + .clk_i, + .rst_ni, + .flush_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .data_i ({sel_b, sel_r }), + .valid_i ( sel_valid ), + .ready_o ( sel_ready ), + .data_o ({sel_buf_b, sel_buf_r}), + .valid_o ( sel_buf_valid ), + .ready_i ( sel_buf_ready ), + .usage_o ( /* unused */ ) + ); + + stream_fifo #( + .FALL_THROUGH ( 1'b1 ), + .DEPTH ( 32'd1 + BufDepth ), + .T ( meta_t ) + ) i_meta_buf ( + .clk_i, + .rst_ni, + .flush_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .data_i ( meta ), + .valid_i ( meta_valid ), + .ready_o ( meta_ready ), + .data_o ( meta_buf ), + .valid_o ( meta_buf_valid ), + .ready_i ( meta_buf_ready ), + .usage_o ( /* unused */ ) + ); + + // Assemble the actual memory request from meta information and write data. + assign m2s_req = mem_req_t'{ + addr: meta.addr, + atop: meta.atop, + lock: meta.lock, + strb: axi_req_i.w.strb, + wdata: axi_req_i.w.data, + we: meta.write, + id: meta.id, + user: meta.user, + cache: meta.cache, + prot: meta.prot, + qos: meta.qos, + region: meta.region + }; + + // Interface memory as stream. + stream_to_mem #( + .mem_req_t ( mem_req_t ), + .mem_resp_t ( mem_rsp_t ), + .BufDepth ( BufDepth ) + ) i_stream_to_mem ( + .clk_i, + .rst_ni, + .req_i ( m2s_req ), + .req_valid_i ( m2s_req_valid ), + .req_ready_o ( m2s_req_ready ), + .resp_o ( m2s_resp ), + .resp_valid_o ( m2s_resp_valid ), + .resp_ready_i ( m2s_resp_ready ), + .mem_req_o ( mem_req ), + .mem_req_valid_o ( mem_req_valid ), + .mem_req_ready_i ( mem_req_ready ), + .mem_resp_i ( mem_rdata ), + .mem_resp_valid_i ( mem_rvalid ) + ); + + typedef struct packed { + axi_pkg::atop_t atop; + logic lock; + mem_id_t id; + mem_user_t user; + axi_pkg::cache_t cache; + axi_pkg::prot_t prot; + axi_pkg::qos_t qos; + axi_pkg::region_t region; + } tmp_atop_t; + + tmp_atop_t mem_req_atop; + tmp_atop_t [NumBanks-1:0] banked_req_atop; + + assign mem_req_atop = '{ + atop: mem_req.atop, + lock: mem_req.lock, + id: mem_req.id, + user: mem_req.user, + cache: mem_req.cache, + prot: mem_req.prot, + qos: mem_req.qos, + region: mem_req.region + }; + + for (genvar i = 0; i < NumBanks; i++) begin + assign mem_atop_o [i] = banked_req_atop[i].atop; + assign mem_lock_o [i] = banked_req_atop[i].lock; + assign mem_id_o [i] = banked_req_atop[i].id; + assign mem_user_o [i] = banked_req_atop[i].user; + assign mem_cache_o [i] = banked_req_atop[i].cache; + assign mem_prot_o [i] = banked_req_atop[i].prot; + assign mem_qos_o [i] = banked_req_atop[i].qos; + assign mem_region_o[i] = banked_req_atop[i].region; + end + + logic [NumBanks-1:0][1:0] tmp_ersp; + logic [NumBanks-1:0][1:0] bank_ersp; + for (genvar i = 0; i < NumBanks; i++) begin + assign mem_rdata.err[i] = tmp_ersp[i][0]; + assign mem_rdata.exokay[i] = tmp_ersp[i][1]; + assign bank_ersp[i][0] = mem_err_i[i]; + assign bank_ersp[i][1] = mem_exokay_i[i]; + end + + // Split single memory request to desired number of banks. + mem_to_banks_detailed #( + .AddrWidth ( AddrWidth ), + .DataWidth ( DataWidth ), + .RUserWidth ( 2 ), + .NumBanks ( NumBanks ), + .HideStrb ( HideStrb ), + .MaxTrans ( BufDepth ), + .FifoDepth ( OutFifoDepth ), + .WUserWidth ( $bits(tmp_atop_t) ) + ) i_mem_to_banks ( + .clk_i, + .rst_ni, + .req_i ( mem_req_valid ), + .gnt_o ( mem_req_ready ), + .addr_i ( mem_req.addr ), + .wdata_i ( mem_req.wdata ), + .strb_i ( mem_req.strb ), + .wuser_i ( mem_req_atop ), + .we_i ( mem_req.we ), + .rvalid_o ( mem_rvalid ), + .rdata_o ( mem_rdata.data ), + .ruser_o ( tmp_ersp ), + .bank_req_o ( mem_req_o ), + .bank_gnt_i ( mem_gnt_i ), + .bank_addr_o ( mem_addr_o ), + .bank_wdata_o ( mem_wdata_o ), + .bank_strb_o ( mem_strb_o ), + .bank_wuser_o ( banked_req_atop ), + .bank_we_o ( mem_we_o ), + .bank_rvalid_i ( mem_rvalid_i ), + .bank_rdata_i ( mem_rdata_i ), + .bank_ruser_i ( bank_ersp ) + ); + + // Join memory read data and meta data stream. + logic mem_join_valid, mem_join_ready; + stream_join #( + .N_INP ( 32'd2 ) + ) i_join ( + .inp_valid_i ({m2s_resp_valid, meta_buf_valid}), + .inp_ready_o ({m2s_resp_ready, meta_buf_ready}), + .oup_valid_o ( mem_join_valid ), + .oup_ready_i ( mem_join_ready ) + ); + + // Dynamically fork the joined stream to B and R channels. + stream_fork_dynamic #( + .N_OUP ( 32'd2 ) + ) i_fork_dynamic ( + .clk_i, + .rst_ni, + .valid_i ( mem_join_valid ), + .ready_o ( mem_join_ready ), + .sel_i ({sel_buf_b, sel_buf_r }), + .sel_valid_i ( sel_buf_valid ), + .sel_ready_o ( sel_buf_ready ), + .valid_o ({axi_resp_o.b_valid, axi_resp_o.r_valid}), + .ready_i ({axi_req_i.b_ready, axi_req_i.r_ready }) + ); + + localparam NumBytesPerBank = DataWidth/NumBanks/8; + + logic [NumBanks-1:0] meta_buf_bank_strb, meta_buf_size_enable; + logic resp_b_err, resp_b_exokay, resp_r_err, resp_r_exokay; + + // Collect `err` and `exokay` from all banks + // To ensure correct propagation, `err` is grouped with `OR` and `exokay` is grouped with `AND`. + for (genvar i = 0; i < NumBanks; i++) begin + // Set active write banks based on strobe + assign meta_buf_bank_strb[i] = |meta_buf.strb[i*NumBytesPerBank +: NumBytesPerBank]; + // Set active read banks based on size and address offset: (bank.end > addr) && (bank.start < addr+size) + assign meta_buf_size_enable[i] = ((i*NumBytesPerBank + NumBytesPerBank) > (meta_buf.addr % DataWidth/8)) && + ((i*NumBytesPerBank) < ((meta_buf.addr % DataWidth/8) + 1< $stable(axi_req_i.ar)) + else $error("AR must remain stable until handshake has happened!"); + assert property (@(posedge clk_i) + axi_resp_o.r_valid && !axi_req_i.r_ready |=> $stable(axi_resp_o.r)) + else $error("R must remain stable until handshake has happened!"); + assume property (@(posedge clk_i) + axi_req_i.aw_valid && !axi_resp_o.aw_ready |=> $stable(axi_req_i.aw)) + else $error("AW must remain stable until handshake has happened!"); + assume property (@(posedge clk_i) + axi_req_i.w_valid && !axi_resp_o.w_ready |=> $stable(axi_req_i.w)) + else $error("W must remain stable until handshake has happened!"); + assert property (@(posedge clk_i) + axi_resp_o.b_valid && !axi_req_i.b_ready |=> $stable(axi_resp_o.b)) + else $error("B must remain stable until handshake has happened!"); + assert property (@(posedge clk_i) axi_req_i.ar_valid && axi_req_i.ar.len > 0 |-> + axi_req_i.ar.burst == axi_pkg::BURST_INCR) + else $error("Non-incrementing bursts are not supported!"); + assert property (@(posedge clk_i) axi_req_i.aw_valid && axi_req_i.aw.len > 0 |-> + axi_req_i.aw.burst == axi_pkg::BURST_INCR) + else $error("Non-incrementing bursts are not supported!"); + assert property (@(posedge clk_i) meta_valid && meta.atop != '0 |-> meta.write) + else $warning("Unexpected atomic operation on read."); + `endif + // pragma translate_on +endmodule + + +`include "axi/assign.svh" +`include "axi/typedef.svh" +/// Interface wrapper for module `axi_to_mem`. +module axi_to_detailed_mem_intf #( + /// See `axi_to_mem`, parameter `AddrWidth`. + parameter int unsigned ADDR_WIDTH = 32'd0, + /// See `axi_to_mem`, parameter `DataWidth`. + parameter int unsigned DATA_WIDTH = 32'd0, + /// AXI4+ATOP ID width. + parameter int unsigned ID_WIDTH = 32'd0, + /// AXI4+ATOP user width. + parameter int unsigned USER_WIDTH = 32'd0, + /// See `axi_to_mem`, parameter `NumBanks`. + parameter int unsigned NUM_BANKS = 32'd0, + /// See `axi_to_mem`, parameter `BufDepth`. + parameter int unsigned BUF_DEPTH = 32'd1, + /// Hide write requests if the strb == '0 + parameter bit HIDE_STRB = 1'b0, + /// Depth of output fifo/fall_through_register. Increase for asymmetric backpressure (contention) on banks. + parameter int unsigned OUT_FIFO_DEPTH = 32'd1, + /// Dependent parameter, do not override. See `axi_to_mem`, parameter `addr_t`. + localparam type addr_t = logic [ADDR_WIDTH-1:0], + /// Dependent parameter, do not override. See `axi_to_mem`, parameter `mem_data_t`. + localparam type mem_data_t = logic [DATA_WIDTH/NUM_BANKS-1:0], + /// Dependent parameter, do not override. See `axi_to_mem`, parameter `mem_strb_t`. + localparam type mem_strb_t = logic [DATA_WIDTH/NUM_BANKS/8-1:0] +) ( + /// Clock input. + input logic clk_i, + /// Asynchronous reset, active low. + input logic rst_ni, + /// See `axi_to_mem`, port `busy_o`. + output logic busy_o, + /// AXI4+ATOP slave interface port. + AXI_BUS.Slave slv, + /// See `axi_to_mem`, port `mem_req_o`. + output logic [NUM_BANKS-1:0] mem_req_o, + /// See `axi_to_mem`, port `mem_gnt_i`. + input logic [NUM_BANKS-1:0] mem_gnt_i, + /// See `axi_to_mem`, port `mem_addr_o`. + output addr_t [NUM_BANKS-1:0] mem_addr_o, + /// See `axi_to_mem`, port `mem_wdata_o`. + output mem_data_t [NUM_BANKS-1:0] mem_wdata_o, + /// See `axi_to_mem`, port `mem_strb_o`. + output mem_strb_t [NUM_BANKS-1:0] mem_strb_o, + /// See `axi_to_mem`, port `mem_atop_o`. + output axi_pkg::atop_t [NUM_BANKS-1:0] mem_atop_o, + /// See `axi_to_mem`, port `mem_lock_o`. + output logic [NUM_BANKS-1:0] mem_lock_o, + /// See `axi_to_mem`, port `mem_we_o`. + output logic [NUM_BANKS-1:0] mem_we_o, + /// See `axi_to_mem`, port `mem_id_o`. + output logic [NUM_BANKS-1:0] mem_id_o, + /// See `axi_to_mem`, port `mem_user_o`. + output logic [NUM_BANKS-1:0] mem_user_o, + /// See `axi_to_mem`, port `mem_cache_o`. + output axi_pkg::cache_t [NUM_BANKS-1:0] mem_cache_o, + /// See `axi_to_mem`, port `mem_prot_o`. + output axi_pkg::prot_t [NUM_BANKS-1:0] mem_prot_o, + /// See `axi_to_mem`, port `mem_qos_o`. + output axi_pkg::qos_t [NUM_BANKS-1:0] mem_qos_o, + /// See `axi_to_mem`, port `mem_region_o`. + output axi_pkg::region_t [NUM_BANKS-1:0] mem_region_o, + /// See `axi_to_mem`, port `mem_rvalid_i`. + input logic [NUM_BANKS-1:0] mem_rvalid_i, + /// See `axi_to_mem`, port `mem_rdata_i`. + input mem_data_t [NUM_BANKS-1:0] mem_rdata_i, + /// See `axi_to_mem`, port `mem_err_i`. + input logic [NUM_BANKS-1:0] mem_err_i, + /// See `axi_to_mem`, port `mem_exokay_i`. + input logic [NUM_BANKS-1:0] mem_exokay_i +); + typedef logic [ID_WIDTH-1:0] id_t; + typedef logic [DATA_WIDTH-1:0] data_t; + typedef logic [DATA_WIDTH/8-1:0] strb_t; + typedef logic [USER_WIDTH-1:0] user_t; + `AXI_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t, id_t, user_t) + `AXI_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t, user_t) + `AXI_TYPEDEF_B_CHAN_T(b_chan_t, id_t, user_t) + `AXI_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t, id_t, user_t) + `AXI_TYPEDEF_R_CHAN_T(r_chan_t, data_t, id_t, user_t) + `AXI_TYPEDEF_REQ_T(req_t, aw_chan_t, w_chan_t, ar_chan_t) + `AXI_TYPEDEF_RESP_T(resp_t, b_chan_t, r_chan_t) + req_t req; + resp_t resp; + `AXI_ASSIGN_TO_REQ(req, slv) + `AXI_ASSIGN_FROM_RESP(slv, resp) + axi_to_detailed_mem #( + .axi_req_t ( req_t ), + .axi_resp_t ( resp_t ), + .AddrWidth ( ADDR_WIDTH ), + .DataWidth ( DATA_WIDTH ), + .IdWidth ( ID_WIDTH ), + .UserWidth ( USER_WIDTH ), + .NumBanks ( NUM_BANKS ), + .BufDepth ( BUF_DEPTH ), + .HideStrb ( HIDE_STRB ), + .OutFifoDepth ( OUT_FIFO_DEPTH ) + ) i_axi_to_detailed_mem ( + .clk_i, + .rst_ni, + .busy_o, + .axi_req_i ( req ), + .axi_resp_o ( resp ), + .mem_req_o, + .mem_gnt_i, + .mem_addr_o, + .mem_wdata_o, + .mem_strb_o, + .mem_atop_o, + .mem_lock_o, + .mem_we_o, + .mem_id_o, + .mem_user_o, + .mem_cache_o, + .mem_prot_o, + .mem_qos_o, + .mem_region_o, + .mem_rvalid_i, + .mem_rdata_i, + .mem_err_i, + .mem_exokay_i + ); +endmodule diff --git a/vendor/pulp-platform/axi/src/axi_to_mem.sv b/vendor/pulp-platform/axi/src/axi_to_mem.sv index 1b8fd590e80..37992d44969 100644 --- a/vendor/pulp-platform/axi/src/axi_to_mem.sv +++ b/vendor/pulp-platform/axi/src/axi_to_mem.sv @@ -10,6 +10,7 @@ // Authors: // - Michael Rogenmoser +// - Thomas Benz `include "common_cells/registers.svh" /// AXI4+ATOP slave module which translates AXI bursts into a memory stream. @@ -74,390 +75,43 @@ module axi_to_mem #( input mem_data_t [NumBanks-1:0] mem_rdata_i ); - typedef logic [DataWidth-1:0] axi_data_t; - typedef logic [DataWidth/8-1:0] axi_strb_t; - typedef logic [IdWidth-1:0] axi_id_t; - - typedef struct packed { - addr_t addr; - axi_pkg::atop_t atop; - axi_strb_t strb; - axi_data_t wdata; - logic we; - } mem_req_t; - - typedef struct packed { - addr_t addr; - axi_pkg::atop_t atop; - axi_id_t id; - logic last; - axi_pkg::qos_t qos; - axi_pkg::size_t size; - logic write; - } meta_t; - - axi_data_t mem_rdata, - m2s_resp; - axi_pkg::len_t r_cnt_d, r_cnt_q, - w_cnt_d, w_cnt_q; - logic arb_valid, arb_ready, - rd_valid, rd_ready, - wr_valid, wr_ready, - sel_b, sel_buf_b, - sel_r, sel_buf_r, - sel_valid, sel_ready, - sel_buf_valid, sel_buf_ready, - sel_lock_d, sel_lock_q, - meta_valid, meta_ready, - meta_buf_valid, meta_buf_ready, - meta_sel_d, meta_sel_q, - m2s_req_valid, m2s_req_ready, - m2s_resp_valid, m2s_resp_ready, - mem_req_valid, mem_req_ready, - mem_rvalid; - mem_req_t m2s_req, - mem_req; - meta_t rd_meta, - rd_meta_d, rd_meta_q, - wr_meta, - wr_meta_d, wr_meta_q, - meta, meta_buf; - - assign busy_o = axi_req_i.aw_valid | axi_req_i.ar_valid | axi_req_i.w_valid | - axi_resp_o.b_valid | axi_resp_o.r_valid | - (r_cnt_q > 0) | (w_cnt_q > 0); - - // Handle reads. - always_comb begin - // Default assignments - axi_resp_o.ar_ready = 1'b0; - rd_meta_d = rd_meta_q; - rd_meta = meta_t'{default: '0}; - rd_valid = 1'b0; - r_cnt_d = r_cnt_q; - // Handle R burst in progress. - if (r_cnt_q > '0) begin - rd_meta_d.last = (r_cnt_q == 8'd1); - rd_meta = rd_meta_d; - rd_meta.addr = rd_meta_q.addr + axi_pkg::num_bytes(rd_meta_q.size); - rd_valid = 1'b1; - if (rd_ready) begin - r_cnt_d--; - rd_meta_d.addr = rd_meta.addr; - end - // Handle new AR if there is one. - end else if (axi_req_i.ar_valid) begin - rd_meta_d = '{ - addr: addr_t'(axi_pkg::aligned_addr(axi_req_i.ar.addr, axi_req_i.ar.size)), - atop: '0, - id: axi_req_i.ar.id, - last: (axi_req_i.ar.len == '0), - qos: axi_req_i.ar.qos, - size: axi_req_i.ar.size, - write: 1'b0 - }; - rd_meta = rd_meta_d; - rd_meta.addr = addr_t'(axi_req_i.ar.addr); - rd_valid = 1'b1; - if (rd_ready) begin - r_cnt_d = axi_req_i.ar.len; - axi_resp_o.ar_ready = 1'b1; - end - end - end - - // Handle writes. - always_comb begin - // Default assignments - axi_resp_o.aw_ready = 1'b0; - axi_resp_o.w_ready = 1'b0; - wr_meta_d = wr_meta_q; - wr_meta = meta_t'{default: '0}; - wr_valid = 1'b0; - w_cnt_d = w_cnt_q; - // Handle W bursts in progress. - if (w_cnt_q > '0) begin - wr_meta_d.last = (w_cnt_q == 8'd1); - wr_meta = wr_meta_d; - wr_meta.addr = wr_meta_q.addr + axi_pkg::num_bytes(wr_meta_q.size); - if (axi_req_i.w_valid) begin - wr_valid = 1'b1; - if (wr_ready) begin - axi_resp_o.w_ready = 1'b1; - w_cnt_d--; - wr_meta_d.addr = wr_meta.addr; - end - end - // Handle new AW if there is one. - end else if (axi_req_i.aw_valid && axi_req_i.w_valid) begin - wr_meta_d = '{ - addr: addr_t'(axi_pkg::aligned_addr(axi_req_i.aw.addr, axi_req_i.aw.size)), - atop: axi_req_i.aw.atop, - id: axi_req_i.aw.id, - last: (axi_req_i.aw.len == '0), - qos: axi_req_i.aw.qos, - size: axi_req_i.aw.size, - write: 1'b1 - }; - wr_meta = wr_meta_d; - wr_meta.addr = addr_t'(axi_req_i.aw.addr); - wr_valid = 1'b1; - if (wr_ready) begin - w_cnt_d = axi_req_i.aw.len; - axi_resp_o.aw_ready = 1'b1; - axi_resp_o.w_ready = 1'b1; - end - end - end - - // Arbitrate between reads and writes. - stream_mux #( - .DATA_T ( meta_t ), - .N_INP ( 32'd2 ) - ) i_ax_mux ( - .inp_data_i ({wr_meta, rd_meta }), - .inp_valid_i ({wr_valid, rd_valid}), - .inp_ready_o ({wr_ready, rd_ready}), - .inp_sel_i ( meta_sel_d ), - .oup_data_o ( meta ), - .oup_valid_o ( arb_valid ), - .oup_ready_i ( arb_ready ) - ); - always_comb begin - meta_sel_d = meta_sel_q; - sel_lock_d = sel_lock_q; - if (sel_lock_q) begin - meta_sel_d = meta_sel_q; - if (arb_valid && arb_ready) begin - sel_lock_d = 1'b0; - end - end else begin - if (wr_valid ^ rd_valid) begin - // If either write or read is valid but not both, select the valid one. - meta_sel_d = wr_valid; - end else if (wr_valid && rd_valid) begin - // If both write and read are valid, decide according to QoS then burst properties. - // Prioritize higher QoS. - if (wr_meta.qos > rd_meta.qos) begin - meta_sel_d = 1'b1; - end else if (rd_meta.qos > wr_meta.qos) begin - meta_sel_d = 1'b0; - // Decide requests with identical QoS. - end else if (wr_meta.qos == rd_meta.qos) begin - // 1. Prioritize individual writes over read bursts. - // Rationale: Read bursts can be interleaved on AXI but write bursts cannot. - if (wr_meta.last && !rd_meta.last) begin - meta_sel_d = 1'b1; - // 2. Prioritize ongoing burst. - // Rationale: Stalled bursts create back-pressure or require costly buffers. - end else if (w_cnt_q > '0) begin - meta_sel_d = 1'b1; - end else if (r_cnt_q > '0) begin - meta_sel_d = 1'b0; - // 3. Otherwise arbitrate round robin to prevent starvation. - end else begin - meta_sel_d = ~meta_sel_q; - end - end - end - // Lock arbitration if valid but not yet ready. - if (arb_valid && !arb_ready) begin - sel_lock_d = 1'b1; - end - end - end - - // Fork arbitrated stream to meta data, memory requests, and R/B channel selection. - stream_fork #( - .N_OUP ( 32'd3 ) - ) i_fork ( - .clk_i, - .rst_ni, - .valid_i ( arb_valid ), - .ready_o ( arb_ready ), - .valid_o ({sel_valid, meta_valid, m2s_req_valid}), - .ready_i ({sel_ready, meta_ready, m2s_req_ready}) - ); - - assign sel_b = meta.write & meta.last; - assign sel_r = ~meta.write | meta.atop[5]; - - stream_fifo #( - .FALL_THROUGH ( 1'b1 ), - .DEPTH ( 32'd1 + BufDepth ), - .T ( logic[1:0] ) - ) i_sel_buf ( - .clk_i, - .rst_ni, - .flush_i ( 1'b0 ), - .testmode_i ( 1'b0 ), - .data_i ({sel_b, sel_r }), - .valid_i ( sel_valid ), - .ready_o ( sel_ready ), - .data_o ({sel_buf_b, sel_buf_r}), - .valid_o ( sel_buf_valid ), - .ready_i ( sel_buf_ready ), - .usage_o ( /* unused */ ) - ); - - stream_fifo #( - .FALL_THROUGH ( 1'b1 ), - .DEPTH ( 32'd1 + BufDepth ), - .T ( meta_t ) - ) i_meta_buf ( - .clk_i, - .rst_ni, - .flush_i ( 1'b0 ), - .testmode_i ( 1'b0 ), - .data_i ( meta ), - .valid_i ( meta_valid ), - .ready_o ( meta_ready ), - .data_o ( meta_buf ), - .valid_o ( meta_buf_valid ), - .ready_i ( meta_buf_ready ), - .usage_o ( /* unused */ ) - ); - - // Assemble the actual memory request from meta information and write data. - assign m2s_req = mem_req_t'{ - addr: meta.addr, - atop: meta.atop, - strb: axi_req_i.w.strb, - wdata: axi_req_i.w.data, - we: meta.write - }; - - // Interface memory as stream. - stream_to_mem #( - .mem_req_t ( mem_req_t ), - .mem_resp_t ( axi_data_t ), - .BufDepth ( BufDepth ) - ) i_stream_to_mem ( - .clk_i, - .rst_ni, - .req_i ( m2s_req ), - .req_valid_i ( m2s_req_valid ), - .req_ready_o ( m2s_req_ready ), - .resp_o ( m2s_resp ), - .resp_valid_o ( m2s_resp_valid ), - .resp_ready_i ( m2s_resp_ready ), - .mem_req_o ( mem_req ), - .mem_req_valid_o ( mem_req_valid ), - .mem_req_ready_i ( mem_req_ready ), - .mem_resp_i ( mem_rdata ), - .mem_resp_valid_i ( mem_rvalid ) - ); - - // Split single memory request to desired number of banks. - mem_to_banks #( - .AddrWidth ( AddrWidth ), - .DataWidth ( DataWidth ), - .NumBanks ( NumBanks ), - .HideStrb ( HideStrb ), - .MaxTrans ( BufDepth ), - .FifoDepth ( OutFifoDepth ) - ) i_mem_to_banks ( - .clk_i, - .rst_ni, - .req_i ( mem_req_valid ), - .gnt_o ( mem_req_ready ), - .addr_i ( mem_req.addr ), - .wdata_i ( mem_req.wdata ), - .strb_i ( mem_req.strb ), - .atop_i ( mem_req.atop ), - .we_i ( mem_req.we ), - .rvalid_o ( mem_rvalid ), - .rdata_o ( mem_rdata ), - .bank_req_o ( mem_req_o ), - .bank_gnt_i ( mem_gnt_i ), - .bank_addr_o ( mem_addr_o ), - .bank_wdata_o ( mem_wdata_o ), - .bank_strb_o ( mem_strb_o ), - .bank_atop_o ( mem_atop_o ), - .bank_we_o ( mem_we_o ), - .bank_rvalid_i ( mem_rvalid_i ), - .bank_rdata_i ( mem_rdata_i ) - ); - - // Join memory read data and meta data stream. - logic mem_join_valid, mem_join_ready; - stream_join #( - .N_INP ( 32'd2 ) - ) i_join ( - .inp_valid_i ({m2s_resp_valid, meta_buf_valid}), - .inp_ready_o ({m2s_resp_ready, meta_buf_ready}), - .oup_valid_o ( mem_join_valid ), - .oup_ready_i ( mem_join_ready ) - ); - - // Dynamically fork the joined stream to B and R channels. - stream_fork_dynamic #( - .N_OUP ( 32'd2 ) - ) i_fork_dynamic ( + axi_to_detailed_mem #( + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .AddrWidth ( AddrWidth ), + .DataWidth ( DataWidth ), + .IdWidth ( IdWidth ), + .UserWidth ( 1 ), + .NumBanks ( NumBanks ), + .BufDepth ( BufDepth ), + .HideStrb ( HideStrb ), + .OutFifoDepth ( OutFifoDepth ) + ) i_axi_to_detailed_mem ( .clk_i, .rst_ni, - .valid_i ( mem_join_valid ), - .ready_o ( mem_join_ready ), - .sel_i ({sel_buf_b, sel_buf_r }), - .sel_valid_i ( sel_buf_valid ), - .sel_ready_o ( sel_buf_ready ), - .valid_o ({axi_resp_o.b_valid, axi_resp_o.r_valid}), - .ready_i ({axi_req_i.b_ready, axi_req_i.r_ready }) + .busy_o, + .axi_req_i ( axi_req_i ), + .axi_resp_o ( axi_resp_o ), + .mem_req_o ( mem_req_o ), + .mem_gnt_i ( mem_gnt_i ), + .mem_addr_o ( mem_addr_o ), + .mem_wdata_o ( mem_wdata_o ), + .mem_strb_o ( mem_strb_o ), + .mem_atop_o ( mem_atop_o ), + .mem_lock_o (), + .mem_we_o ( mem_we_o ), + .mem_id_o (), + .mem_user_o (), + .mem_cache_o (), + .mem_prot_o (), + .mem_qos_o (), + .mem_region_o (), + .mem_rvalid_i ( mem_rvalid_i ), + .mem_rdata_i ( mem_rdata_i ), + .mem_err_i ('0), + .mem_exokay_i ('0) ); - // Compose B responses. - assign axi_resp_o.b = '{ - id: meta_buf.id, - resp: axi_pkg::RESP_OKAY, - user: '0 - }; - - // Compose R responses. - assign axi_resp_o.r = '{ - data: m2s_resp, - id: meta_buf.id, - last: meta_buf.last, - resp: axi_pkg::RESP_OKAY, - user: '0 - }; - - // Registers - `FFARN(meta_sel_q, meta_sel_d, 1'b0, clk_i, rst_ni) - `FFARN(sel_lock_q, sel_lock_d, 1'b0, clk_i, rst_ni) - `FFARN(rd_meta_q, rd_meta_d, meta_t'{default: '0}, clk_i, rst_ni) - `FFARN(wr_meta_q, wr_meta_d, meta_t'{default: '0}, clk_i, rst_ni) - `FFARN(r_cnt_q, r_cnt_d, '0, clk_i, rst_ni) - `FFARN(w_cnt_q, w_cnt_d, '0, clk_i, rst_ni) - - // Assertions - // pragma translate_off - `ifndef VERILATOR - default disable iff (!rst_ni); - assume property (@(posedge clk_i) - axi_req_i.ar_valid && !axi_resp_o.ar_ready |=> $stable(axi_req_i.ar)) - else $error("AR must remain stable until handshake has happened!"); - assert property (@(posedge clk_i) - axi_resp_o.r_valid && !axi_req_i.r_ready |=> $stable(axi_resp_o.r)) - else $error("R must remain stable until handshake has happened!"); - assume property (@(posedge clk_i) - axi_req_i.aw_valid && !axi_resp_o.aw_ready |=> $stable(axi_req_i.aw)) - else $error("AW must remain stable until handshake has happened!"); - assume property (@(posedge clk_i) - axi_req_i.w_valid && !axi_resp_o.w_ready |=> $stable(axi_req_i.w)) - else $error("W must remain stable until handshake has happened!"); - assert property (@(posedge clk_i) - axi_resp_o.b_valid && !axi_req_i.b_ready |=> $stable(axi_resp_o.b)) - else $error("B must remain stable until handshake has happened!"); - assert property (@(posedge clk_i) axi_req_i.ar_valid && axi_req_i.ar.len > 0 |-> - axi_req_i.ar.burst == axi_pkg::BURST_INCR) - else $error("Non-incrementing bursts are not supported!"); - assert property (@(posedge clk_i) axi_req_i.aw_valid && axi_req_i.aw.len > 0 |-> - axi_req_i.aw.burst == axi_pkg::BURST_INCR) - else $error("Non-incrementing bursts are not supported!"); - assert property (@(posedge clk_i) meta_valid && meta.atop != '0 |-> meta.write) - else $warning("Unexpected atomic operation on read."); - `endif - // pragma translate_on endmodule @@ -557,202 +211,3 @@ module axi_to_mem_intf #( .mem_rdata_i ); endmodule - -/// Split memory access over multiple parallel banks, where each bank has its own req/gnt -/// request and valid response direction. -module mem_to_banks #( - /// Input address width. - parameter int unsigned AddrWidth = 32'd0, - /// Input data width, must be a power of two. - parameter int unsigned DataWidth = 32'd0, - /// Number of banks at output, must evenly divide `DataWidth`. - parameter int unsigned NumBanks = 32'd0, - /// Remove transactions that have zero strobe - parameter bit HideStrb = 1'b0, - /// Number of outstanding transactions - parameter int unsigned MaxTrans = 32'b1, - /// FIFO depth, must be >=1 - parameter int unsigned FifoDepth = 1, - /// Dependent parameter, do not override! Address type. - localparam type addr_t = logic [AddrWidth-1:0], - /// Dependent parameter, do not override! Input data type. - localparam type inp_data_t = logic [DataWidth-1:0], - /// Dependent parameter, do not override! Input write strobe type. - localparam type inp_strb_t = logic [DataWidth/8-1:0], - /// Dependent parameter, do not override! Output data type. - localparam type oup_data_t = logic [DataWidth/NumBanks-1:0], - /// Dependent parameter, do not override! Output write strobe type. - localparam type oup_strb_t = logic [DataWidth/NumBanks/8-1:0] -) ( - /// Clock input. - input logic clk_i, - /// Asynchronous reset, active low. - input logic rst_ni, - /// Memory request to split, request is valid. - input logic req_i, - /// Memory request to split, request can be granted. - output logic gnt_o, - /// Memory request to split, request address, byte-wise. - input addr_t addr_i, - /// Memory request to split, request write data. - input inp_data_t wdata_i, - /// Memory request to split, request write strobe. - input inp_strb_t strb_i, - /// Memory request to split, request Atomic signal from AXI4+ATOP. - input axi_pkg::atop_t atop_i, - /// Memory request to split, request write enable, active high. - input logic we_i, - /// Memory request to split, response is valid. Required for read and write requests - output logic rvalid_o, - /// Memory request to split, response read data. - output inp_data_t rdata_o, - /// Memory bank request, request is valid. - output logic [NumBanks-1:0] bank_req_o, - /// Memory bank request, request can be granted. - input logic [NumBanks-1:0] bank_gnt_i, - /// Memory bank request, request address, byte-wise. Will be different for each bank. - output addr_t [NumBanks-1:0] bank_addr_o, - /// Memory bank request, request write data. - output oup_data_t [NumBanks-1:0] bank_wdata_o, - /// Memory bank request, request write strobe. - output oup_strb_t [NumBanks-1:0] bank_strb_o, - /// Memory bank request, request Atomic signal from AXI4+ATOP. - output axi_pkg::atop_t [NumBanks-1:0] bank_atop_o, - /// Memory bank request, request write enable, active high. - output logic [NumBanks-1:0] bank_we_o, - /// Memory bank request, response is valid. Required for read and write requests - input logic [NumBanks-1:0] bank_rvalid_i, - /// Memory bank request, response read data. - input oup_data_t [NumBanks-1:0] bank_rdata_i -); - - localparam DataBytes = $bits(inp_strb_t); - localparam BitsPerBank = $bits(oup_data_t); - localparam BytesPerBank = $bits(oup_strb_t); - - typedef struct packed { - addr_t addr; - oup_data_t wdata; - oup_strb_t strb; - axi_pkg::atop_t atop; - logic we; - } req_t; - - logic req_valid; - logic [NumBanks-1:0] req_ready, - resp_valid, resp_ready; - req_t [NumBanks-1:0] bank_req, - bank_oup; - logic [NumBanks-1:0] bank_req_internal, bank_gnt_internal, zero_strobe, dead_response; - logic dead_write_fifo_full; - - function automatic addr_t align_addr(input addr_t addr); - return (addr >> $clog2(DataBytes)) << $clog2(DataBytes); - endfunction - - // Handle requests. - assign req_valid = req_i & gnt_o; - for (genvar i = 0; unsigned'(i) < NumBanks; i++) begin : gen_reqs - assign bank_req[i].addr = align_addr(addr_i) + i * BytesPerBank; - assign bank_req[i].wdata = wdata_i[i*BitsPerBank+:BitsPerBank]; - assign bank_req[i].strb = strb_i[i*BytesPerBank+:BytesPerBank]; - assign bank_req[i].atop = atop_i; - assign bank_req[i].we = we_i; - stream_fifo #( - .FALL_THROUGH ( 1'b1 ), - .DATA_WIDTH ( $bits(req_t) ), - .DEPTH ( FifoDepth ), - .T ( req_t ) - ) i_ft_reg ( - .clk_i, - .rst_ni, - .flush_i ( 1'b0 ), - .testmode_i ( 1'b0 ), - .usage_o (), - .data_i ( bank_req[i] ), - .valid_i ( req_valid ), - .ready_o ( req_ready[i] ), - .data_o ( bank_oup[i] ), - .valid_o ( bank_req_internal[i] ), - .ready_i ( bank_gnt_internal[i] ) - ); - assign bank_addr_o[i] = bank_oup[i].addr; - assign bank_wdata_o[i] = bank_oup[i].wdata; - assign bank_strb_o[i] = bank_oup[i].strb; - assign bank_atop_o[i] = bank_oup[i].atop; - assign bank_we_o[i] = bank_oup[i].we; - - assign zero_strobe[i] = (bank_oup[i].strb == '0); - - if (HideStrb) begin - assign bank_req_o[i] = (bank_oup[i].we && zero_strobe[i]) ? 1'b0 : bank_req_internal[i]; - assign bank_gnt_internal[i] = (bank_oup[i].we && zero_strobe[i]) ? 1'b1 : bank_gnt_i[i]; - end else begin - assign bank_req_o[i] = bank_req_internal[i]; - assign bank_gnt_internal[i] = bank_gnt_i[i]; - end - end - - // Grant output if all our requests have been granted. - assign gnt_o = (&req_ready) & (&resp_ready) & !dead_write_fifo_full; - - if (HideStrb) begin : gen_dead_write_fifo - fifo_v3 #( - .FALL_THROUGH ( 1'b1 ), - .DEPTH ( MaxTrans+1 ), - .DATA_WIDTH ( NumBanks ) - ) i_dead_write_fifo ( - .clk_i, - .rst_ni, - .flush_i ( 1'b0 ), - .testmode_i ( 1'b0 ), - .full_o ( dead_write_fifo_full ), - .empty_o (), - .usage_o (), - .data_i ( bank_we_o & zero_strobe ), - .push_i ( req_i & gnt_o ), - .data_o ( dead_response ), - .pop_i ( rvalid_o ) - ); - end else begin - assign dead_response = '0; - assign dead_write_fifo_full = 1'b0; - end - - // Handle responses. - for (genvar i = 0; unsigned'(i) < NumBanks; i++) begin : gen_resp_regs - stream_fifo #( - .FALL_THROUGH ( 1'b1 ), - .DATA_WIDTH ( $bits(oup_data_t) ), - .DEPTH ( FifoDepth ), - .T ( oup_data_t ) - ) i_ft_reg ( - .clk_i, - .rst_ni, - .flush_i ( 1'b0 ), - .testmode_i ( 1'b0 ), - .usage_o (), - .data_i ( bank_rdata_i[i] ), - .valid_i ( bank_rvalid_i[i] ), - .ready_o ( resp_ready[i] ), - .data_o ( rdata_o[i*BitsPerBank+:BitsPerBank] ), - .valid_o ( resp_valid[i] ), - .ready_i ( rvalid_o & !dead_response[i] ) - ); - end - assign rvalid_o = &(resp_valid | dead_response); - - // Assertions - // pragma translate_off - `ifndef VERILATOR - initial begin - assume (DataWidth != 0 && (DataWidth & (DataWidth - 1)) == 0) - else $fatal(1, "Data width must be a power of two!"); - assume (DataWidth % NumBanks == 0) - else $fatal(1, "Data width must be evenly divisible over banks!"); - assume ((DataWidth / NumBanks) % 8 == 0) - else $fatal(1, "Data width of each bank must be divisible into 8-bit bytes!"); - end - `endif - // pragma translate_on -endmodule diff --git a/vendor/pulp-platform/axi/src/axi_to_mem_banked.sv b/vendor/pulp-platform/axi/src/axi_to_mem_banked.sv index cc7c6198d39..548339f372e 100644 --- a/vendor/pulp-platform/axi/src/axi_to_mem_banked.sv +++ b/vendor/pulp-platform/axi/src/axi_to_mem_banked.sv @@ -150,7 +150,7 @@ module axi_to_mem_banked #( .NoMstPorts ( 32'd2 ), .MaxTrans ( MemLatency+2 ), // allow multiple Ax vectors to not starve W channel .AxiLookBits ( 32'd1 ), // select is fixed, do not need it - .UniqueIds ( 1'b0 ), + .UniqueIds ( 1'b1 ), // Can be set as ports are statically selected -> reduces HW .SpillAw ( 1'b1 ), .SpillW ( 1'b1 ), .SpillB ( 1'b1 ), diff --git a/vendor/pulp-platform/axi/src/axi_to_mem_interleaved.sv b/vendor/pulp-platform/axi/src/axi_to_mem_interleaved.sv index a744c1ec0e2..9a5f87805b7 100644 --- a/vendor/pulp-platform/axi/src/axi_to_mem_interleaved.sv +++ b/vendor/pulp-platform/axi/src/axi_to_mem_interleaved.sv @@ -47,6 +47,8 @@ module axi_to_mem_interleaved #( input logic clk_i, /// Asynchronous reset, active low. input logic rst_ni, + /// Testmode enable + input logic test_i, /// The unit is busy handling an AXI4+ATOP request. output logic busy_o, /// AXI4+ATOP slave port, request input. @@ -94,27 +96,26 @@ module axi_to_mem_interleaved #( mem_data_t [NumBanks-1:0] r_mem_rdata, w_mem_rdata; // split AXI bus in read and write - always_comb begin : proc_axi_rw_split - axi_resp_o.r = r_axi_resp.r; - axi_resp_o.r_valid = r_axi_resp.r_valid; - axi_resp_o.ar_ready = r_axi_resp.ar_ready; - axi_resp_o.b = w_axi_resp.b; - axi_resp_o.b_valid = w_axi_resp.b_valid; - axi_resp_o.w_ready = w_axi_resp.w_ready; - axi_resp_o.aw_ready = w_axi_resp.aw_ready; - - w_axi_req = '0; - w_axi_req.aw = axi_req_i.aw; - w_axi_req.aw_valid = axi_req_i.aw_valid; - w_axi_req.w = axi_req_i.w; - w_axi_req.w_valid = axi_req_i.w_valid; - w_axi_req.b_ready = axi_req_i.b_ready; - - r_axi_req = '0; - r_axi_req.ar = axi_req_i.ar; - r_axi_req.ar_valid = axi_req_i.ar_valid; - r_axi_req.r_ready = axi_req_i.r_ready; - end + axi_demux_simple #( + .AxiIdWidth ( IdWidth ), + .AtopSupport ( 1'b1 ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .NoMstPorts ( 2 ), + .MaxTrans ( BufDepth ), + .AxiLookBits ( 1 ), // select is fixed, do not need it + .UniqueIds ( 1'b1 ) // Can be set as ports are statically selected -> reduces HW + ) i_split_read_write ( + .clk_i, + .rst_ni, + .test_i, + .slv_req_i ( axi_req_i ), + .slv_ar_select_i ( 1'b0 ), + .slv_aw_select_i ( 1'b1 ), + .slv_resp_o ( axi_resp_o ), + .mst_reqs_o ( {w_axi_req, r_axi_req} ), + .mst_resps_i ( {w_axi_resp, r_axi_resp} ) + ); axi_to_mem #( .axi_req_t ( axi_req_t ), @@ -246,3 +247,119 @@ module axi_to_mem_interleaved #( end endmodule : axi_to_mem_interleaved + +`include "axi/typedef.svh" +`include "axi/assign.svh" +/// AXI4+ATOP interface wrapper for `axi_to_mem_interleaved` +module axi_to_mem_interleaved_intf #( + /// AXI4+ATOP ID width + parameter int unsigned AXI_ID_WIDTH = 32'd0, + /// AXI4+ATOP address width + parameter int unsigned AXI_ADDR_WIDTH = 32'd0, + /// AXI4+ATOP data width + parameter int unsigned AXI_DATA_WIDTH = 32'd0, + /// AXI4+ATOP user width + parameter int unsigned AXI_USER_WIDTH = 32'd0, + /// Number of memory banks / macros + parameter int unsigned MEM_NUM_BANKS = 32'd4, + /// Read latency of the connected memory in cycles + parameter int unsigned BUF_DEPTH = 32'd1, + /// Hide write requests if the strb == '0 + parameter bit HIDE_STRB = 1'b0, + /// Depth of output fifo/fall_through_register. Increase for asymmetric backpressure (contention) on banks. + parameter int unsigned OUT_FIFO_DEPTH = 32'd1, + + /// Dependent parameter, do not override. Memory address type. + parameter type mem_addr_t = logic [AXI_ADDR_WIDTH-1:0], + /// Dependent parameter, do not override. Memory atomic type. + parameter type mem_atop_t = axi_pkg::atop_t, + /// Dependent parameter, do not override. Memory data type. + parameter type mem_data_t = logic [AXI_DATA_WIDTH/MEM_NUM_BANKS-1:0], + /// Dependent parameter, do not override. Memory write strobe type. + parameter type mem_strb_t = logic [AXI_DATA_WIDTH/MEM_NUM_BANKS/8-1:0] +) ( + /// Clock + input logic clk_i, + /// Asynchronous reset, active low + input logic rst_ni, + /// Status output, busy flag of `axi_to_mem` + output logic busy_o, + /// AXI4+ATOP slave port + AXI_BUS.Slave slv, + /// Memory bank request + output logic [MEM_NUM_BANKS-1:0] mem_req_o, + /// Memory request grant + input logic [MEM_NUM_BANKS-1:0] mem_gnt_i, + /// Request address + output mem_addr_t [MEM_NUM_BANKS-1:0] mem_addr_o, + /// Write data + output mem_data_t [MEM_NUM_BANKS-1:0] mem_wdata_o, + /// Write data byte enable, active high + output mem_strb_t [MEM_NUM_BANKS-1:0] mem_strb_o, + /// Atomic operation + output mem_atop_t [MEM_NUM_BANKS-1:0] mem_atop_o, + /// Write request enable, active high + output logic [MEM_NUM_BANKS-1:0] mem_we_o, + /// Read data valid response, active high + input logic [MEM_NUM_BANKS-1:0] mem_rvalid_i, + /// Read data response + input mem_data_t [MEM_NUM_BANKS-1:0] mem_rdata_i +); + typedef logic [AXI_ID_WIDTH-1:0] id_t; + typedef logic [AXI_ADDR_WIDTH-1:0] addr_t; + typedef logic [AXI_DATA_WIDTH-1:0] data_t; + typedef logic [AXI_DATA_WIDTH/8-1:0] strb_t; + typedef logic [AXI_USER_WIDTH-1:0] user_t; + `AXI_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t, id_t, user_t) + `AXI_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t, user_t) + `AXI_TYPEDEF_B_CHAN_T(b_chan_t, id_t, user_t) + `AXI_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t, id_t, user_t) + `AXI_TYPEDEF_R_CHAN_T(r_chan_t, data_t, id_t, user_t) + `AXI_TYPEDEF_REQ_T(axi_req_t, aw_chan_t, w_chan_t, ar_chan_t) + `AXI_TYPEDEF_RESP_T(axi_resp_t, b_chan_t, r_chan_t) + + axi_req_t mem_axi_req; + axi_resp_t mem_axi_resp; + + `AXI_ASSIGN_TO_REQ(mem_axi_req, slv) + `AXI_ASSIGN_FROM_RESP(slv, mem_axi_resp) + + axi_to_mem_interleaved #( + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .AddrWidth ( AXI_ADDR_WIDTH ), + .DataWidth ( AXI_DATA_WIDTH ), + .IdWidth ( AXI_ID_WIDTH ), + .NumBanks ( MEM_NUM_BANKS ), + .BufDepth ( BUF_DEPTH ), + .HideStrb ( HIDE_STRB ), + .OutFifoDepth ( OUT_FIFO_DEPTH ) + ) i_axi_to_mem_interleaved ( + .clk_i, + .rst_ni, + .busy_o, + .axi_req_i ( mem_axi_req ), + .axi_resp_o ( mem_axi_resp ), + .mem_req_o, + .mem_gnt_i, + .mem_addr_o, + .mem_wdata_o, + .mem_strb_o, + .mem_atop_o, + .mem_we_o, + .mem_rvalid_i, + .mem_rdata_i + ); + +// pragma translate_off +`ifndef VERILATOR + initial begin: p_assertions + assert (AXI_ADDR_WIDTH >= 1) else $fatal(1, "AXI address width must be at least 1!"); + assert (AXI_DATA_WIDTH >= 1) else $fatal(1, "AXI data width must be at least 1!"); + assert (AXI_ID_WIDTH >= 1) else $fatal(1, "AXI ID width must be at least 1!"); + assert (AXI_USER_WIDTH >= 1) else $fatal(1, "AXI user width must be at least 1!"); + end +`endif +// pragma translate_on + +endmodule // axi_to_mem_interleaved_intf diff --git a/vendor/pulp-platform/axi/src/axi_to_mem_split.sv b/vendor/pulp-platform/axi/src/axi_to_mem_split.sv index 107d3fe753e..28ce408310d 100644 --- a/vendor/pulp-platform/axi/src/axi_to_mem_split.sv +++ b/vendor/pulp-platform/axi/src/axi_to_mem_split.sv @@ -10,6 +10,7 @@ // // Authors: // - Michael Rogenmoser +// - Thomas Benz `include "axi/assign.svh" /// AXI4+ATOP to memory-protocol interconnect. Completely separates the read and write channel to @@ -49,6 +50,8 @@ module axi_to_mem_split #( input logic clk_i, /// Asynchronous reset, active low. input logic rst_ni, + /// Testmode enable + input logic test_i, /// The unit is busy handling an AXI4+ATOP request. output logic busy_o, /// AXI4+ATOP slave port, request input. @@ -81,18 +84,26 @@ module axi_to_mem_split #( logic read_busy, write_busy; - axi_rw_split #( - .axi_req_t ( axi_req_t ), - .axi_resp_t ( axi_resp_t ) - ) i_axi_rw_split ( + // split AXI bus in read and write + axi_demux_simple #( + .AxiIdWidth ( IdWidth ), + .AtopSupport ( 1'b1 ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .NoMstPorts ( 2 ), + .MaxTrans ( BufDepth ), + .AxiLookBits ( 1 ), // select is fixed, do not need it + .UniqueIds ( 1'b1 ) // Can be set as ports are statically selected -> reduces HW + ) i_split_read_write ( .clk_i, .rst_ni, - .slv_req_i ( axi_req_i ), - .slv_resp_o ( axi_resp_o ), - .mst_read_req_o ( axi_read_req ), - .mst_read_resp_i ( axi_read_resp ), - .mst_write_req_o ( axi_write_req ), - .mst_write_resp_i ( axi_write_resp ) + .test_i, + .slv_req_i ( axi_req_i ), + .slv_ar_select_i ( 1'b0 ), + .slv_aw_select_i ( 1'b1 ), + .slv_resp_o ( axi_resp_o ), + .mst_reqs_o ( {axi_write_req, axi_read_req} ), + .mst_resps_i ( {axi_write_resp, axi_read_resp} ) ); assign busy_o = read_busy || write_busy; diff --git a/vendor/pulp-platform_axi.lock.hjson b/vendor/pulp-platform_axi.lock.hjson index 0227d9911f0..ab8a089adce 100644 --- a/vendor/pulp-platform_axi.lock.hjson +++ b/vendor/pulp-platform_axi.lock.hjson @@ -9,6 +9,6 @@ upstream: { url: https://github.com/pulp-platform/axi.git - rev: af8b0ce2653997301b1b792c4c6d207b95f63a56 + rev: fccffb5953ec8564218ba05e20adbedec845e014 } } diff --git a/vendor/pulp-platform_axi.vendor.hjson b/vendor/pulp-platform_axi.vendor.hjson index e7f39596cf7..88518b098ed 100644 --- a/vendor/pulp-platform_axi.vendor.hjson +++ b/vendor/pulp-platform_axi.vendor.hjson @@ -15,7 +15,7 @@ // URL url: "https://github.com/pulp-platform/axi.git", // revision - rev: "v0.39.0-beta.2", + rev: "v0.39.1", } // Patch dir for local changes