diff --git a/Bender.yml b/Bender.yml index 6d66c7b321..e0cbe7d686 100644 --- a/Bender.yml +++ b/Bender.yml @@ -6,7 +6,7 @@ package: - "Andreas Kuster " dependencies: - axi: { git: "https://github.com/pulp-platform/axi.git", version: 0.31.0 } + axi: { git: "https://github.com/pulp-platform/axi.git", version: 0.39.0-beta.2 } common_cells: { git: "https://github.com/pulp-platform/common_cells", version: 1.23.0 } fpnew: { git: "https://github.com/openhwgroup/cvfpu.git", version: 0.7.0 } diff --git a/corev_apu/fpga/src/ariane_xilinx.sv b/corev_apu/fpga/src/ariane_xilinx.sv index f3490034c3..c0374c7a3b 100644 --- a/corev_apu/fpga/src/ariane_xilinx.sv +++ b/corev_apu/fpga/src/ariane_xilinx.sv @@ -287,6 +287,7 @@ localparam axi_pkg::xbar_cfg_t AXI_XBAR_CFG = '{ MaxSlvTrans: 1, // Probably requires update FallThrough: 1'b0, LatencyMode: axi_pkg::CUT_ALL_PORTS, + PipelineStages: 1, AxiIdWidthSlvPorts: AxiIdWidthMaster, AxiIdUsedSlvPorts: AxiIdWidthMaster, UniqueIds: 1'b0, diff --git a/corev_apu/tb/ariane_testharness.sv b/corev_apu/tb/ariane_testharness.sv index 8916f3559a..d253f9995b 100644 --- a/corev_apu/tb/ariane_testharness.sv +++ b/corev_apu/tb/ariane_testharness.sv @@ -359,8 +359,8 @@ module ariane_testharness #( `AXI_ASSIGN_FROM_RESP(master[ariane_soc::GPIO], gpio_resp) axi_err_slv #( .AxiIdWidth ( ariane_soc::IdWidthSlave ), - .req_t ( ariane_axi_soc::req_slv_t ), - .resp_t ( ariane_axi_soc::resp_slv_t ) + .axi_req_t ( ariane_axi_soc::req_slv_t ), + .axi_resp_t ( ariane_axi_soc::resp_slv_t ) ) i_gpio_err_slv ( .clk_i ( clk_i ), .rst_ni ( ndmreset_n ), @@ -495,6 +495,7 @@ module ariane_testharness #( MaxSlvTrans: unsigned'(1), // Probably requires update FallThrough: 1'b0, LatencyMode: axi_pkg::NO_LATENCY, + PipelineStages: 1, AxiIdWidthSlvPorts: unsigned'(ariane_soc::IdWidth), AxiIdUsedSlvPorts: unsigned'(ariane_soc::IdWidth), UniqueIds: 1'b0, diff --git a/vendor/pulp-platform/axi/.gitignore b/vendor/pulp-platform/axi/.gitignore index c9eb4f5072..0797ab0375 100644 --- a/vendor/pulp-platform/axi/.gitignore +++ b/vendor/pulp-platform/axi/.gitignore @@ -5,3 +5,4 @@ /build /Bender.lock /Bender.local +*.log diff --git a/vendor/pulp-platform/axi/CHANGELOG.md b/vendor/pulp-platform/axi/CHANGELOG.md index dff9830beb..4fe0d61927 100644 --- a/vendor/pulp-platform/axi/CHANGELOG.md +++ b/vendor/pulp-platform/axi/CHANGELOG.md @@ -5,6 +5,196 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## Unreleased + +### 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. + +### 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`. + +### Fixed + + +## 0.38.0 - 2022-09-28 + +### Added +- Add `axi_dumper` and `axi_dumper_interpret` script to dump log from an AXI bus for debugging + purposes. +- Add FuseSoC and Vivado XSIM limited test to CI +- `assign.svh`: Add macros to assign flat buses using the Vivado naming style. +- `axi_lfsr` and `axi_lite_lfsr`: Add AXI4 and AXI4 Lite LFSR Subordinate devices. +- `axi_xp`: Add crosspoint with homomorphous slave and master ports. + +### Changed +- Improve compatibility with FuseSoC +- Improve compatibility with Vivado XSIM +- Performance improvements to `axi_to_mem` +- Use `scripts/update_authors` to update authors, slight manual fixes performed. + +`v0.38.0` is fully **backward-compatible** to `v0.36.0` and `v0.37.0`. + + +## 0.37.0 - 2022-08-30 + +### Added +- `axi_fifo`: Inserts a FIFO into all 5 AXI4 channels; add module and its testbench +- `axi_test`: Add `mapped` mode to the random classes as well as additional functionality to the + scoreboard class. +- `axi_throttle`: Add a module that limits the maximum number of outstanding transfers sent to the + downstream logic. +- `axi_to_mem`: AXI4+ATOP slave to control on chip memory. +- `axi_to_mem_banked`: AXI4+ATOP slave to control on chip memory, with banking support, higher + throughput than `axi_to_mem`. +- `axi_to_mem_interleaved`: AXI4+ATOP slave to control on chip memory, interleaved to prevent + deadlocks. +- `axi_to_mem_split`: AXI4+ATOP slave to control memory protocol interconnect. +- `Bender`: Add dependency `tech_cells_generic` `v0.2.2` for generic SRAM macro for simulation. + +### Changed +- `axi_demux`: Add module docstring +- `axi_sim_mem`: Add the capability to emit read and write errors +- `Bender`: Update dependency `common_cells` to `v1.26.0` from `v1.21.0` (required by + `axi_throttle`) +- Remove `docs` directory, move content to `doc` folder. `docs` is automatically created and + populated during the CI run. +- Update vsim version to `2021.3` in CI, drop test for `2020.1` and `2021.1` + +### Fixed +- `axi_lite_demux`: Improve compatibility with vsim version 10.7b. +- `axi_lite_mux`: Reduce complexity of W channel at master port by removing an unnecessary + multiplexer. + +`v0.37.0` is fully **backward-compatible** to `v0.36.0`. + + +## 0.36.0 - 2022-07-07 + +### Added +- Add Monitor modport to `AXI_BUS`, `AXI_LITE`, and `AXI_LITE_DV` interfaces. + + +## 0.35.3 - 2022-05-03 + +### Fixed +- `axi_demux`: Eliminate unnecessary stalls of AW channel when the AR channel has reached its + maximum number of transactions. Prior to this fix, `axi_demux` would always stall AWs while read + transactions were at their maximum (that is, while `MaxTrans` read transactions were outstanding). + However, this stall is only required when the AW that is being handled by `axi_demux` is an atomic + operation (ATOP) that entails an R response. This fix therefore removes unnecessary stalls as + well as an unnecessary dependency between reads and writes. The integrity of data or transactions + was not affected by this problem. + + +## 0.35.2 - 2022-04-14 + +### Fixed +- `axi_lite_mux_intf`: Fix type of `slv` and `mst` interface ports; they were `AXI_BUS` instead of + `AXI_LITE`. +- `axi_xbar_intf`: Fix order of parameters. Prior to this fix, the `CONNECTIVITY` parameter was + defined using the `Cfg` parameter before the `Cfg` parameter was defined. +- `axi_test::axi_rand_master`: Improve compatibility with simulators by changing an implication + inside an assertion to a conditional assertion. + + +## 0.35.1 - 2022-03-31 + +### Fixed +- `axi_demux` and `axi_lite_demux`: Add missing spill registers for configurations with a single + master port. +- `axi_demux_intf`: Add missing parameter (`ATOP_SUPPORT`) to optionally disable support for atomic + operations. +- `axi_mux` and `axi_lite_mux`: Add missing spill registers for configurations with a single slave + port. +- `axi_lite_mux_intf`: Add missing parameter values on the internal `axi_lite_mux` instance + (`axi_req_t` and `axi_resp_t`). +- `axi_sim_mem`: Propagate the AR channel's user signal correctly to the monitor. + + +## 0.35.0 - 2022-03-11 + +### Added +- `axi_sim_mem`: Add monitoring interface to observe the point of coherency between the write and + the read channel. + +### Fixed +- `axi_sim_mem`: Keep R response stable while not accepted. + + +## 0.34.0 - 2022-03-09 + +### Added +- `axi_demux` and `axi_isolate`: Add parameter `AtopSupport` to optionally disable the support for + atomic operations (ATOPs). This parameter defaults to `1'b1`, i.e., ATOPs are supported. + Therefore, this change is backward-compatible. +- `axi_isolate`: Add parameter `TerminateTransaction` to optionally respond to transactions during + isolation. This parameter defaults to `1'b0`, i.e., transactions do not get responses. + Therefore, this change is backward-compatible. +- `axi_xbar`: Add `Connectivity` parameter to enable the implementation of partially-connected + crossbars. This parameter defaults to `'1`, i.e., every slave port is connected to every master + port. Therefore, this change is backward-compatible. +- `axi_test`: Add monitor class `axi_monitor`. +- `axi_test::axi_driver`: Add monitor tasks. + +### Changed +- `axi_isolate`: Add parameters for the address, data, ID, and user signal width. This is required + for the implementation of the `TerminateTransaction` parameter (see *Added* section). This change + is **backward-incompatible** for all instances of `axi_isolate` outside this repository. Users + must update all instances of `axi_isolate` in their code. The interface variant is not affected + and remains backward-compatible. + + +## 0.33.1 - 2022-02-26 + +### Fixed +- `axi_xbar_intf`: Add missing `ATOPS` parameter to optionally disable the support of atomic + operations (introduced in v0.25.0 for `axi_xbar`). The default value of the added parameter makes + this fix backward-compatible. + + +## 0.33.0 - 2022-02-21 + +### Added +- Add `axi_sim_mem_intf` interface variant of `axi_sim_mem`. + +### Fixed +- `axi_cdc`: Improve compatibility with VCS by restricting a QuestaSim workaround to be used only + for QuestaSim (issue #207). +- `axi_id_remap`: Improve compatibility with Verilator by excluding `assert`s for that tool. +- `axi_lite_demux`: Improve compatibility with VCS (issue #187 reported for `axi_demux`, which was + fixed in v0.29.2). +- `axi_xbar`: Improve compatibility with VCS by adding VCS-specific code that does not use constant + function calls (#208). + + +## 0.32.0 - 2022-01-25 + +### Changed +- `axi_atop_filter`, `axi_burst_splitter`, `axi_cut`, `axi_delayer`, `axi_demux`, `axi_err_slv`, + `axi_isolate`, `axi_lite_demux`, `axi_lite_mux`, `axi_lite_to_axi`, and `axi_lite_xbar`, + `axi_multicut`, `axi_serializer`, `axi_sim_mem`: Prefix `req_t` and `resp_t` type parameters with + `axi_`. This prevents type collisions in tools that have problems with correct type resolution + and isolation. This change is **backward-incompatible** for all instances of the listed modules + outside this repository. Users must update all instances of the listed modules in their code. + Interface variants are not affected and remain backward-compatible. + + +## 0.31.1 - 2022-01-17 + +### Fixed +- `axi_xbar`: Fix signal width for single master port. Before this fix, a crossbar instantiated + with a single master port would contain arrays with incorrect dimensions. + + ## 0.31.0 - 2021-12-07 ### Added diff --git a/vendor/pulp-platform/axi/Makefile b/vendor/pulp-platform/axi/Makefile new file mode 100644 index 0000000000..5d97a95427 --- /dev/null +++ b/vendor/pulp-platform/axi/Makefile @@ -0,0 +1,90 @@ +# 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 + +# select IIS-internal tool commands if we run on IIS machines +ifneq (,$(wildcard /etc/iis.version)) + VSIM ?= questa-2022.3 vsim + SYNOPSYS_DC ?= synopsys-2022.03 dcnxt_shell +else + VSIM ?= vsim + SYNOPSYS_DC ?= dc_shell +endif + +TBS ?= axi_addr_test \ + axi_atop_filter \ + axi_cdc axi_delayer \ + axi_dw_downsizer \ + axi_dw_upsizer \ + axi_fifo \ + axi_isolate \ + axi_iw_converter \ + axi_lite_regs \ + axi_lite_to_apb \ + axi_lite_to_axi \ + axi_lite_mailbox \ + axi_lite_xbar \ + axi_modify_address \ + axi_serializer \ + axi_sim_mem \ + axi_to_axi_lite \ + axi_to_mem_banked \ + axi_xbar + +SIM_TARGETS := $(addsuffix .log,$(addprefix sim-,$(TBS))) + + +.SHELL: bash + +.PHONY: help all sim_all clean + + +help: + @echo "" + @echo "elab.log: elaborates all files using Synopsys DC" + @echo "compile.log: compile files using Questasim" + @echo "sim-#TB#.log: simulates a given testbench, available TBs are:" + @echo "$(addprefix ###############-#,$(TBS))" | sed -e 's/ /\n/g' | sed -e 's/#/ /g' + @echo "sim_all: simulates all available testbenches" + @echo "" + @echo "clean: cleans generated files" + @echo "" + + +all: compile.log elab.log sim_all + + +sim_all: $(SIM_TARGETS) + + +build: + mkdir -p $@ + + +elab.log: Bender.yml | build + export SYNOPSYS_DC="$(SYNOPSYS_DC)"; cd build && ../scripts/synth.sh | tee ../$@ + (! grep -n "Error:" $@) + + +compile.log: Bender.yml | build + export VSIM="$(VSIM)"; cd build && ../scripts/compile_vsim.sh | tee ../$@ + (! grep -n "Error:" $@) + + +sim-%.log: compile.log + export VSIM="$(VSIM)"; cd build && ../scripts/run_vsim.sh --random-seed $* | tee ../$@ + (! grep -n "Error:" $@) + (! grep -n "Fatal:" $@) + + +clean: + rm -rf build + rm -f *.log diff --git a/vendor/pulp-platform/axi/README.md b/vendor/pulp-platform/axi/README.md index ab639efc2a..0127037449 100644 --- a/vendor/pulp-platform/axi/README.md +++ b/vendor/pulp-platform/axi/README.md @@ -3,7 +3,7 @@ [![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) -This repository provides modules to build on-chip communication networks adhering to the [AXI4 or AXI4-Lite standards](https://developer.arm.com/documentation/ihi0022/f-b). For high-performance communication, we implement AXI4[+ATOPs from AXI5](#atomic-operations). For lightweight communication, we implement AXI4-Lite. We aim to provide a complete end-to-end communication platform, including endpoints such as DMA engines and on-chip memory controllers. +This repository provides modules to build on-chip communication networks adhering to the [AXI4 or AXI4-Lite standards][AMBA 5 Spec]. For high-performance communication, we implement AXI4[+ATOPs from AXI5](#atomic-operations). For lightweight communication, we implement AXI4-Lite. We aim to provide a complete end-to-end communication platform, including endpoints such as DMA engines and on-chip memory controllers. Our **design goals** are: - **Topology Independence**: We provide elementary building blocks such as protocol [multiplexers](src/axi_mux.sv) and [demultiplexers](src/axi_demux.sv) that allow users to implement any network topology. We also provide commonly used interconnecting components such as a [crossbar](src/axi_xbar.sv). @@ -19,41 +19,60 @@ 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_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_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_join`](src/axi_lite_join.sv) | A connector that joins two AXI-Lite interfaces. | | -| [`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_serializer`](src/axi_serializer.sv) | Serializes transactions with different IDs to the same ID. | | -| [`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_xbar`](src/axi_xbar.sv) | Fully-connected AXI4+ATOP crossbar with an arbitrary number of slave and master ports. | [Doc](doc/axi_xbar.md) | +| 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. | | + +## Synthesizable Verification Modules + +The following modules are meant to be used for verification purposes only but are synthesizable to be used in FPGA environments. + +| Name | Description | +|------------------------------------------------------|---------------------------------------------------------------------------------------------------------| +| [`axi_bus_compare`](src/axi_bus_compare.sv) | Compares two buses of the same type (and in the same clock domain), returns events on mismatch. | +| [`axi_slave_compare`](src/axi_slave_compare.sv) | Compares two slave devices of the same type (and in the same clock domain), returns events on mismatch. | ### Simulation-Only Modules @@ -61,8 +80,10 @@ In addition to the modules above, which are available in synthesis and simulatio | Name | Description | |------------------------------------------------------|--------------------------------------------------------------------------------------------------------| +| [`axi_chan_compare`](src/axi_chan_compare.sv) | Non-synthesizable module comparing two AXI channels of the same type | | [`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_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. | @@ -75,7 +96,7 @@ In addition to the modules above, which are available in synthesis and simulatio ## Atomic Operations -AXI4+ATOPs means the full AXI4 specification plus atomic operations (ATOPs) as defined in Section E2.1 of the AMBA5 specification. This has the following implications for modules that do not implement ATOPs and systems that include such modules: +AXI4+ATOPs means the full AXI4 specification plus atomic operations (ATOPs) as defined in Section E1.1 of the [AMBA 5 specification][AMBA 5 Spec]. This has the following implications for modules that do not implement ATOPs and systems that include such modules: - Masters that do not issue ATOPs must set `aw_atop` to `'0`. - Slaves that do not support ATOPs must specify this in their interface documentation and can ignore the `aw_atop` signal. @@ -83,7 +104,9 @@ AXI4+ATOPs means the full AXI4 specification plus atomic operations (ATOPs) as d 1. slaves that do not support ATOPs are behind an [`axi_atop_filter`](src/axi_atop_filter.sv) if any master could issue an ATOP to such slaves and 2. the `aw_atop` signal is well-defined at the input of any (non-AXI4-Lite) module in this repository. -Masters and slaves that do support ATOPs must adhere to Section E2.1 of the AMBA5 specification. +Masters and slaves that do support ATOPs must adhere to Section E1.1 of the [AMBA 5 specification][AMBA 5 Spec]. In particular: +- ATOPs that have the `aw_atop[axi_pkg::ATOP_R_RESP]` bit set generate a write response (B channel) beat and at least one read response (R channel) beat. All modules for which the `aw_atop[axi_pkg::ATOP_R_RESP]` bit could be set at their master port must be able to handle both B and R beats (in any order and without requiring a simultaneous handshake) for each such ATOP request. All modules for which the `aw_atop[axi_pkg::ATOP_R_RESP]` bit could be set at their slave port must respond with the appropriate number of B and R beats for each such ATOP request. +- ATOPs must not use the same AXI ID as any other transaction that is outstanding at the same time. ## Which EDA Tools Are Supported? @@ -96,9 +119,13 @@ We aim to be compatible with a wide range of EDA tools. For this reason, we str - the workaround does not break functionality in other tools, and - the workaround does not significantly complicate code or add maintenance overhead. +In addition, we suggest to report issues with the SystemVerilog language support directly to the EDA vendor. Our code is fully open and +can / should be shared with the EDA vendor as a testcase for any language problem encountered. + All code in each release and on the default branch is tested on a recent version of at least one industry-standard RTL simulator and synthesizer. You can examine the [CI settings](./.gitlab-ci.yml) to find out which version of which tool we are running. +[AMBA 5 Spec]: https://developer.arm.com/documentation/ihi0022/hc [IEEE 1800-2012]: https://standards.ieee.org/standard/1800-2012.html [doc.axi_id_remap]: https://pulp-platform.github.io/axi/master/module.axi_id_remap [doc.axi_id_serialize]: https://pulp-platform.github.io/axi/master/module.axi_id_serialize diff --git a/vendor/pulp-platform/axi/VERSION b/vendor/pulp-platform/axi/VERSION index 26bea73e81..ca75280b09 100644 --- a/vendor/pulp-platform/axi/VERSION +++ b/vendor/pulp-platform/axi/VERSION @@ -1 +1 @@ -0.31.0 +0.38.0 diff --git a/vendor/pulp-platform/axi/include/axi/assign.svh b/vendor/pulp-platform/axi/include/axi/assign.svh index 14bb1944c6..80667eb09f 100644 --- a/vendor/pulp-platform/axi/include/axi/assign.svh +++ b/vendor/pulp-platform/axi/include/axi/assign.svh @@ -11,7 +11,7 @@ // // Authors: // - Andreas Kurth -// - Wolfgang Roenninger +// - Nils Wistoff // Macros to assign AXI Interfaces and Structs @@ -538,4 +538,118 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Macros for assigning flattened AXI ports to req/resp AXI structs +// Flat AXI ports are required by the Vivado IP Integrator. Vivado naming convention is followed. +// +// Usage Example: +// `AXI_ASSIGN_MASTER_TO_FLAT("my_bus", my_req_struct, my_rsp_struct) +`define AXI_ASSIGN_MASTER_TO_FLAT(pat, req, rsp) \ + assign m_axi_``pat``_awvalid = req.aw_valid; \ + assign m_axi_``pat``_awid = req.aw.id; \ + assign m_axi_``pat``_awaddr = req.aw.addr; \ + assign m_axi_``pat``_awlen = req.aw.len; \ + assign m_axi_``pat``_awsize = req.aw.size; \ + assign m_axi_``pat``_awburst = req.aw.burst; \ + assign m_axi_``pat``_awlock = req.aw.lock; \ + assign m_axi_``pat``_awcache = req.aw.cache; \ + assign m_axi_``pat``_awprot = req.aw.prot; \ + assign m_axi_``pat``_awqos = req.aw.qos; \ + assign m_axi_``pat``_awregion = req.aw.region; \ + assign m_axi_``pat``_awuser = req.aw.user; \ + \ + assign m_axi_``pat``_wvalid = req.w_valid; \ + assign m_axi_``pat``_wdata = req.w.data; \ + assign m_axi_``pat``_wstrb = req.w.strb; \ + assign m_axi_``pat``_wlast = req.w.last; \ + assign m_axi_``pat``_wuser = req.w.user; \ + \ + assign m_axi_``pat``_bready = req.b_ready; \ + \ + assign m_axi_``pat``_arvalid = req.ar_valid; \ + assign m_axi_``pat``_arid = req.ar.id; \ + assign m_axi_``pat``_araddr = req.ar.addr; \ + assign m_axi_``pat``_arlen = req.ar.len; \ + assign m_axi_``pat``_arsize = req.ar.size; \ + assign m_axi_``pat``_arburst = req.ar.burst; \ + assign m_axi_``pat``_arlock = req.ar.lock; \ + assign m_axi_``pat``_arcache = req.ar.cache; \ + assign m_axi_``pat``_arprot = req.ar.prot; \ + assign m_axi_``pat``_arqos = req.ar.qos; \ + assign m_axi_``pat``_arregion = req.ar.region; \ + assign m_axi_``pat``_aruser = req.ar.user; \ + \ + assign m_axi_``pat``_rready = req.r_ready; \ + \ + assign rsp.aw_ready = m_axi_``pat``_awready; \ + assign rsp.ar_ready = m_axi_``pat``_arready; \ + assign rsp.w_ready = m_axi_``pat``_wready; \ + \ + assign rsp.b_valid = m_axi_``pat``_bvalid; \ + assign rsp.b.id = m_axi_``pat``_bid; \ + assign rsp.b.resp = m_axi_``pat``_bresp; \ + assign rsp.b.user = m_axi_``pat``_buser; \ + \ + assign rsp.r_valid = m_axi_``pat``_rvalid; \ + assign rsp.r.id = m_axi_``pat``_rid; \ + assign rsp.r.data = m_axi_``pat``_rdata; \ + assign rsp.r.resp = m_axi_``pat``_rresp; \ + assign rsp.r.last = m_axi_``pat``_rlast; \ + assign rsp.r.user = m_axi_``pat``_ruser; + +`define AXI_ASSIGN_SLAVE_TO_FLAT(pat, req, rsp) \ + assign req.aw_valid = s_axi_``pat``_awvalid; \ + assign req.aw.id = s_axi_``pat``_awid; \ + assign req.aw.addr = s_axi_``pat``_awaddr; \ + assign req.aw.len = s_axi_``pat``_awlen; \ + assign req.aw.size = s_axi_``pat``_awsize; \ + assign req.aw.burst = s_axi_``pat``_awburst; \ + assign req.aw.lock = s_axi_``pat``_awlock; \ + assign req.aw.cache = s_axi_``pat``_awcache; \ + assign req.aw.prot = s_axi_``pat``_awprot; \ + assign req.aw.qos = s_axi_``pat``_awqos; \ + assign req.aw.region = s_axi_``pat``_awregion; \ + assign req.aw.user = s_axi_``pat``_awuser; \ + \ + assign req.w_valid = s_axi_``pat``_wvalid; \ + assign req.w.data = s_axi_``pat``_wdata; \ + assign req.w.strb = s_axi_``pat``_wstrb; \ + assign req.w.last = s_axi_``pat``_wlast; \ + assign req.w.user = s_axi_``pat``_wuser; \ + \ + assign req.b_ready = s_axi_``pat``_bready; \ + \ + assign req.ar_valid = s_axi_``pat``_arvalid; \ + assign req.ar.id = s_axi_``pat``_arid; \ + assign req.ar.addr = s_axi_``pat``_araddr; \ + assign req.ar.len = s_axi_``pat``_arlen; \ + assign req.ar.size = s_axi_``pat``_arsize; \ + assign req.ar.burst = s_axi_``pat``_arburst; \ + assign req.ar.lock = s_axi_``pat``_arlock; \ + assign req.ar.cache = s_axi_``pat``_arcache; \ + assign req.ar.prot = s_axi_``pat``_arprot; \ + assign req.ar.qos = s_axi_``pat``_arqos; \ + assign req.ar.region = s_axi_``pat``_arregion; \ + assign req.ar.user = s_axi_``pat``_aruser; \ + \ + assign req.r_ready = s_axi_``pat``_rready; \ + \ + assign s_axi_``pat``_awready = rsp.aw_ready; \ + assign s_axi_``pat``_arready = rsp.ar_ready; \ + assign s_axi_``pat``_wready = rsp.w_ready; \ + \ + assign s_axi_``pat``_bvalid = rsp.b_valid; \ + assign s_axi_``pat``_bid = rsp.b.id; \ + assign s_axi_``pat``_bresp = rsp.b.resp; \ + assign s_axi_``pat``_buser = rsp.b.user; \ + \ + assign s_axi_``pat``_rvalid = rsp.r_valid; \ + assign s_axi_``pat``_rid = rsp.r.id; \ + assign s_axi_``pat``_rdata = rsp.r.data; \ + assign s_axi_``pat``_rresp = rsp.r.resp; \ + assign s_axi_``pat``_rlast = rsp.r.last; \ + assign s_axi_``pat``_ruser = rsp.r.user; +//////////////////////////////////////////////////////////////////////////////////////////////////// + + `endif diff --git a/vendor/pulp-platform/axi/include/axi/typedef.svh b/vendor/pulp-platform/axi/include/axi/typedef.svh index a2a860e509..5eec91171f 100644 --- a/vendor/pulp-platform/axi/include/axi/typedef.svh +++ b/vendor/pulp-platform/axi/include/axi/typedef.svh @@ -104,6 +104,28 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +// All AXI4+ATOP Channels and Request/Response Structs in One Macro - Custom Type Name Version +// +// This can be used whenever the user is not interested in "precise" control of the naming of the +// individual channels. +// +// Usage Example: +// `AXI_TYPEDEF_ALL_CT(axi, axi_req_t, axi_rsp_t, addr_t, id_t, data_t, strb_t, user_t) +// +// This defines `axi_req_t` and `axi_rsp_t` request/response structs as well as `axi_aw_chan_t`, +// `axi_w_chan_t`, `axi_b_chan_t`, `axi_ar_chan_t`, and `axi_r_chan_t` channel structs. +`define AXI_TYPEDEF_ALL_CT(__name, __req, __rsp, __addr_t, __id_t, __data_t, __strb_t, __user_t) \ + `AXI_TYPEDEF_AW_CHAN_T(__name``_aw_chan_t, __addr_t, __id_t, __user_t) \ + `AXI_TYPEDEF_W_CHAN_T(__name``_w_chan_t, __data_t, __strb_t, __user_t) \ + `AXI_TYPEDEF_B_CHAN_T(__name``_b_chan_t, __id_t, __user_t) \ + `AXI_TYPEDEF_AR_CHAN_T(__name``_ar_chan_t, __addr_t, __id_t, __user_t) \ + `AXI_TYPEDEF_R_CHAN_T(__name``_r_chan_t, __data_t, __id_t, __user_t) \ + `AXI_TYPEDEF_REQ_T(__req, __name``_aw_chan_t, __name``_w_chan_t, __name``_ar_chan_t) \ + `AXI_TYPEDEF_RESP_T(__rsp, __name``_b_chan_t, __name``_r_chan_t) +//////////////////////////////////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////////////////////////////////////// // All AXI4+ATOP Channels and Request/Response Structs in One Macro // @@ -115,14 +137,8 @@ // // This defines `axi_req_t` and `axi_resp_t` request/response structs as well as `axi_aw_chan_t`, // `axi_w_chan_t`, `axi_b_chan_t`, `axi_ar_chan_t`, and `axi_r_chan_t` channel structs. -`define AXI_TYPEDEF_ALL(__name, __addr_t, __id_t, __data_t, __strb_t, __user_t) \ - `AXI_TYPEDEF_AW_CHAN_T(__name``_aw_chan_t, __addr_t, __id_t, __user_t) \ - `AXI_TYPEDEF_W_CHAN_T(__name``_w_chan_t, __data_t, __strb_t, __user_t) \ - `AXI_TYPEDEF_B_CHAN_T(__name``_b_chan_t, __id_t, __user_t) \ - `AXI_TYPEDEF_AR_CHAN_T(__name``_ar_chan_t, __addr_t, __id_t, __user_t) \ - `AXI_TYPEDEF_R_CHAN_T(__name``_r_chan_t, __data_t, __id_t, __user_t) \ - `AXI_TYPEDEF_REQ_T(__name``_req_t, __name``_aw_chan_t, __name``_w_chan_t, __name``_ar_chan_t) \ - `AXI_TYPEDEF_RESP_T(__name``_resp_t, __name``_b_chan_t, __name``_r_chan_t) +`define AXI_TYPEDEF_ALL(__name, __addr_t, __id_t, __data_t, __strb_t, __user_t) \ + `AXI_TYPEDEF_ALL_CT(__name, __name``_req_t, __name``_resp_t, __addr_t, __id_t, __data_t, __strb_t, __user_t) //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -185,6 +201,29 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +// All AXI4-Lite Channels and Request/Response Structs in One Macro - Custom Type Name Version +// +// This can be used whenever the user is not interested in "precise" control of the naming of the +// individual channels. +// +// Usage Example: +// `AXI_LITE_TYPEDEF_ALL_CT(axi_lite, axi_lite_req_t, axi_lite_rsp_t, addr_t, data_t, strb_t) +// +// This defines `axi_lite_req_t` and `axi_lite_resp_t` request/response structs as well as +// `axi_lite_aw_chan_t`, `axi_lite_w_chan_t`, `axi_lite_b_chan_t`, `axi_lite_ar_chan_t`, and +// `axi_lite_r_chan_t` channel structs. +`define AXI_LITE_TYPEDEF_ALL_CT(__name, __req, __rsp, __addr_t, __data_t, __strb_t) \ + `AXI_LITE_TYPEDEF_AW_CHAN_T(__name``_aw_chan_t, __addr_t) \ + `AXI_LITE_TYPEDEF_W_CHAN_T(__name``_w_chan_t, __data_t, __strb_t) \ + `AXI_LITE_TYPEDEF_B_CHAN_T(__name``_b_chan_t) \ + `AXI_LITE_TYPEDEF_AR_CHAN_T(__name``_ar_chan_t, __addr_t) \ + `AXI_LITE_TYPEDEF_R_CHAN_T(__name``_r_chan_t, __data_t) \ + `AXI_LITE_TYPEDEF_REQ_T(__req, __name``_aw_chan_t, __name``_w_chan_t, __name``_ar_chan_t) \ + `AXI_LITE_TYPEDEF_RESP_T(__rsp, __name``_b_chan_t, __name``_r_chan_t) +//////////////////////////////////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////////////////////////////////////// // All AXI4-Lite Channels and Request/Response Structs in One Macro // @@ -197,14 +236,8 @@ // This defines `axi_lite_req_t` and `axi_lite_resp_t` request/response structs as well as // `axi_lite_aw_chan_t`, `axi_lite_w_chan_t`, `axi_lite_b_chan_t`, `axi_lite_ar_chan_t`, and // `axi_lite_r_chan_t` channel structs. -`define AXI_LITE_TYPEDEF_ALL(__name, __addr_t, __data_t, __strb_t) \ - `AXI_LITE_TYPEDEF_AW_CHAN_T(__name``_aw_chan_t, __addr_t) \ - `AXI_LITE_TYPEDEF_W_CHAN_T(__name``_w_chan_t, __data_t, __strb_t) \ - `AXI_LITE_TYPEDEF_B_CHAN_T(__name``_b_chan_t) \ - `AXI_LITE_TYPEDEF_AR_CHAN_T(__name``_ar_chan_t, __addr_t) \ - `AXI_LITE_TYPEDEF_R_CHAN_T(__name``_r_chan_t, __data_t) \ - `AXI_LITE_TYPEDEF_REQ_T(__name``_req_t, __name``_aw_chan_t, __name``_w_chan_t, __name``_ar_chan_t) \ - `AXI_LITE_TYPEDEF_RESP_T(__name``_resp_t, __name``_b_chan_t, __name``_r_chan_t) +`define AXI_LITE_TYPEDEF_ALL(__name, __addr_t, __data_t, __strb_t) \ + `AXI_LITE_TYPEDEF_ALL_CT(__name, __name``_req_t, __name``_resp_t, __addr_t, __data_t, __strb_t) //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/vendor/pulp-platform/axi/src/axi_atop_filter.sv b/vendor/pulp-platform/axi/src/axi_atop_filter.sv index 4a2ecff430..de60516a38 100644 --- a/vendor/pulp-platform/axi/src/axi_atop_filter.sv +++ b/vendor/pulp-platform/axi/src/axi_atop_filter.sv @@ -40,22 +40,22 @@ module axi_atop_filter #( /// Maximum number of in-flight AXI write transactions parameter int unsigned AxiMaxWriteTxns = 0, /// AXI request type - parameter type req_t = logic, + parameter type axi_req_t = logic, /// AXI response type - parameter type resp_t = logic + parameter type axi_resp_t = logic ) ( /// Rising-edge clock of both ports - input logic clk_i, + input logic clk_i, /// Asynchronous reset, active low - input logic rst_ni, + input logic rst_ni, /// Slave port request - input req_t slv_req_i, + input axi_req_t slv_req_i, /// Slave port response - output resp_t slv_resp_o, + output axi_resp_t slv_resp_o, /// Master port request - output req_t mst_req_o, + output axi_req_t mst_req_o, /// Master port response - input resp_t mst_resp_i + input axi_resp_t mst_resp_i ); // Minimum counter width is 2 to detect underflows. @@ -137,8 +137,8 @@ module axi_atop_filter #( mst_req_o.aw_valid = 1'b0; // Do not let AW pass to master port. slv_resp_o.aw_ready = 1'b1; // Absorb AW on slave port. id_d = slv_req_i.aw.id; // Store ID for B response. - // All atomic operations except atomic stores require a response on the R channel. - if (slv_req_i.aw.atop[5:4] != axi_pkg::ATOP_ATOMICSTORE) begin + // Some atomic operations require a response on the R channel. + if (slv_req_i.aw.atop[axi_pkg::ATOP_R_RESP]) begin // Push R response command. We do not have to wait for the ready of the register // because we know it is ready: we are its only master and will wait for the register to // be emptied before going back to the `W_FEEDTHROUGH` state. @@ -405,11 +405,11 @@ module axi_atop_filter_intf #( `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) + `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) - req_t slv_req, mst_req; - resp_t slv_resp, mst_resp; + axi_req_t slv_req, mst_req; + axi_resp_t slv_resp, mst_resp; `AXI_ASSIGN_TO_REQ(slv_req, slv) `AXI_ASSIGN_FROM_RESP(slv, slv_resp) @@ -422,8 +422,8 @@ module axi_atop_filter_intf #( // Maximum number of AXI write bursts outstanding at the same time .AxiMaxWriteTxns ( AXI_MAX_WRITE_TXNS ), // AXI request & response type - .req_t ( req_t ), - .resp_t ( resp_t ) + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ) ) i_axi_atop_filter ( .clk_i, .rst_ni, diff --git a/vendor/pulp-platform/axi/src/axi_burst_splitter.sv b/vendor/pulp-platform/axi/src/axi_burst_splitter.sv index a455847909..4fe6f7e780 100644 --- a/vendor/pulp-platform/axi/src/axi_burst_splitter.sv +++ b/vendor/pulp-platform/axi/src/axi_burst_splitter.sv @@ -35,19 +35,19 @@ module axi_burst_splitter #( parameter int unsigned DataWidth = 32'd0, parameter int unsigned IdWidth = 32'd0, parameter int unsigned UserWidth = 32'd0, - parameter type req_t = logic, - parameter type resp_t = logic + parameter type axi_req_t = logic, + parameter type axi_resp_t = logic ) ( input logic clk_i, input logic rst_ni, // Input / Slave Port - input req_t slv_req_i, - output resp_t slv_resp_o, + input axi_req_t slv_req_i, + output axi_resp_t slv_resp_o, // Output / Master Port - output req_t mst_req_o, - input resp_t mst_resp_i + output axi_req_t mst_req_o, + input axi_resp_t mst_resp_i ); typedef logic [AddrWidth-1:0] addr_t; @@ -62,28 +62,27 @@ module axi_burst_splitter #( `AXI_TYPEDEF_R_CHAN_T(r_chan_t, data_t, id_t, user_t) // Demultiplex between supported and unsupported transactions. - req_t act_req, unsupported_req; - resp_t act_resp, unsupported_resp; - logic sel_aw_unsupported, sel_ar_unsupported; + axi_req_t act_req, unsupported_req; + axi_resp_t act_resp, unsupported_resp; + logic sel_aw_unsupported, sel_ar_unsupported; localparam int unsigned MaxTxns = (MaxReadTxns > MaxWriteTxns) ? MaxReadTxns : MaxWriteTxns; axi_demux #( - .AxiIdWidth ( IdWidth ), - .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 ), - .req_t ( req_t ), - .resp_t ( resp_t ), - .NoMstPorts ( 2 ), - .MaxTrans ( MaxTxns ), - .AxiLookBits ( IdWidth ), - .FallThrough ( 1'b1 ), - .SpillAw ( 1'b0 ), - .SpillW ( 1'b0 ), - .SpillB ( 1'b0 ), - .SpillAr ( 1'b0 ), - .SpillR ( 1'b0 ) + .AxiIdWidth ( IdWidth ), + .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 ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .NoMstPorts ( 2 ), + .MaxTrans ( MaxTxns ), + .AxiLookBits ( IdWidth ), + .SpillAw ( 1'b0 ), + .SpillW ( 1'b0 ), + .SpillB ( 1'b0 ), + .SpillAr ( 1'b0 ), + .SpillR ( 1'b0 ) ) i_demux_supported_vs_unsupported ( .clk_i, .rst_ni, @@ -119,8 +118,8 @@ module axi_burst_splitter #( // Respond to unsupported transactions with slave errors. axi_err_slv #( .AxiIdWidth ( IdWidth ), - .req_t ( req_t ), - .resp_t ( resp_t ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), .Resp ( axi_pkg::RESP_SLVERR ), .ATOPs ( 1'b0 ), // The burst splitter does not support ATOPs. .MaxTrans ( 1 ) // Splitting bursts implies a low-performance bus. diff --git a/vendor/pulp-platform/axi/src/axi_bus_compare.sv b/vendor/pulp-platform/axi/src/axi_bus_compare.sv new file mode 100644 index 0000000000..b92e6159f7 --- /dev/null +++ b/vendor/pulp-platform/axi/src/axi_bus_compare.sv @@ -0,0 +1,585 @@ +// Copyright (c) 2019-2020 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 + +`include "axi/assign.svh" +/// Synthesizable test module comparing two AXI channels of the same type +/// This module is meant to be used in FPGA-based verification. +module axi_bus_compare #( + /// ID width of the AXI4+ATOP interface + parameter int unsigned AxiIdWidth = 32'd0, + /// FIFO depth + parameter int unsigned FifoDepth = 32'd0, + /// AW channel type of the AXI4+ATOP interface + parameter type axi_aw_chan_t = logic, + /// W channel type of the AXI4+ATOP interface + parameter type axi_w_chan_t = logic, + /// B channel type of the AXI4+ATOP interface + parameter type axi_b_chan_t = logic, + /// AR channel type of the AXI4+ATOP interface + parameter type axi_ar_chan_t = logic, + /// R channel type of the AXI4+ATOP interface + parameter type axi_r_chan_t = logic, + /// Request struct type of the AXI4+ATOP slave port + parameter type axi_req_t = logic, + /// Response struct type of the AXI4+ATOP slave port + parameter type axi_rsp_t = logic, + /// ID type (*do not overwrite*) + parameter type id_t = logic [2**AxiIdWidth-1:0] +)( + /// Clock + input logic clk_i, + /// Asynchronous reset, active low + input logic rst_ni, + /// Testmode + input logic testmode_i, + /// AXI4+ATOP A channel request in + input axi_req_t axi_a_req_i, + /// AXI4+ATOP A channel response out + output axi_rsp_t axi_a_rsp_o, + /// AXI4+ATOP A channel request out + output axi_req_t axi_a_req_o, + /// AXI4+ATOP A channel response in + input axi_rsp_t axi_a_rsp_i, + /// AXI4+ATOP B channel request in + input axi_req_t axi_b_req_i, + /// AXI4+ATOP B channel response out + output axi_rsp_t axi_b_rsp_o, + /// AXI4+ATOP B channel request out + output axi_req_t axi_b_req_o, + /// AXI4+ATOP B channel response in + input axi_rsp_t axi_b_rsp_i, + /// AW mismatch + output id_t aw_mismatch_o, + /// W mismatch + output logic w_mismatch_o, + /// B mismatch + output id_t b_mismatch_o, + /// AR mismatch + output id_t ar_mismatch_o, + /// R mismatch + output id_t r_mismatch_o, + /// General mismatch + output logic mismatch_o, + /// Unit is busy + output logic busy_o +); + + + //----------------------------------- + // Channel Signals + //----------------------------------- + // assign request payload A + + `AXI_ASSIGN_AW_STRUCT(axi_a_req_o.aw, axi_a_req_i.aw) + `AXI_ASSIGN_W_STRUCT(axi_a_req_o.w, axi_a_req_i.w) + `AXI_ASSIGN_AR_STRUCT(axi_a_req_o.ar, axi_a_req_i.ar) + + // assign response payload A + `AXI_ASSIGN_R_STRUCT(axi_a_rsp_o.r, axi_a_rsp_i.r) + `AXI_ASSIGN_B_STRUCT(axi_a_rsp_o.b, axi_a_rsp_i.b) + + // assign request payload B + `AXI_ASSIGN_AW_STRUCT(axi_b_req_o.aw, axi_b_req_i.aw) + `AXI_ASSIGN_W_STRUCT(axi_b_req_o.w, axi_b_req_i.w) + `AXI_ASSIGN_AR_STRUCT(axi_b_req_o.ar, axi_b_req_i.ar) + + // assign response payload B + `AXI_ASSIGN_R_STRUCT(axi_b_rsp_o.r, axi_b_rsp_i.r) + `AXI_ASSIGN_B_STRUCT(axi_b_rsp_o.b, axi_b_rsp_i.b) + + // fifo handshaking signals A + id_t fifo_valid_aw_a, fifo_ready_aw_a; + id_t fifo_valid_b_a, fifo_ready_b_a; + id_t fifo_valid_ar_a, fifo_ready_ar_a; + id_t fifo_valid_r_a, fifo_ready_r_a; + + logic fifo_sel_valid_aw_a, fifo_sel_ready_aw_a; + logic fifo_sel_valid_w_a, fifo_sel_ready_w_a; + logic fifo_sel_valid_b_a, fifo_sel_ready_b_a; + logic fifo_sel_valid_ar_a, fifo_sel_ready_ar_a; + logic fifo_sel_valid_r_a, fifo_sel_ready_r_a; + + // fifo handshaking signals B + id_t fifo_valid_aw_b, fifo_ready_aw_b; + id_t fifo_valid_b_b, fifo_ready_b_b; + id_t fifo_valid_ar_b, fifo_ready_ar_b; + id_t fifo_valid_r_b, fifo_ready_r_b; + + logic fifo_sel_valid_aw_b, fifo_sel_ready_aw_b; + logic fifo_sel_valid_w_b, fifo_sel_ready_w_b; + logic fifo_sel_valid_b_b, fifo_sel_ready_b_b; + logic fifo_sel_valid_ar_b, fifo_sel_ready_ar_b; + logic fifo_sel_valid_r_b, fifo_sel_ready_r_b; + + + //----------------------------------- + // FIFO Output Signals + //----------------------------------- + id_t fifo_cmp_valid_aw_a; + logic fifo_cmp_valid_w_a; + id_t fifo_cmp_valid_b_a; + id_t fifo_cmp_valid_ar_a; + id_t fifo_cmp_valid_r_a; + + id_t fifo_cmp_valid_aw_b; + logic fifo_cmp_valid_w_b; + id_t fifo_cmp_valid_b_b; + id_t fifo_cmp_valid_ar_b; + id_t fifo_cmp_valid_r_b; + + axi_aw_chan_t [2**AxiIdWidth-1:0] fifo_cmp_data_aw_a; + axi_w_chan_t fifo_cmp_data_w_a; + axi_b_chan_t [2**AxiIdWidth-1:0] fifo_cmp_data_b_a; + axi_ar_chan_t [2**AxiIdWidth-1:0] fifo_cmp_data_ar_a; + axi_r_chan_t [2**AxiIdWidth-1:0] fifo_cmp_data_r_a; + + axi_aw_chan_t [2**AxiIdWidth-1:0] fifo_cmp_data_aw_b; + axi_w_chan_t fifo_cmp_data_w_b; + axi_b_chan_t [2**AxiIdWidth-1:0] fifo_cmp_data_b_b; + axi_ar_chan_t [2**AxiIdWidth-1:0] fifo_cmp_data_ar_b; + axi_r_chan_t [2**AxiIdWidth-1:0] fifo_cmp_data_r_b; + + + //----------------------------------- + // Channel A stream forks + //----------------------------------- + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_aw_a ( + .clk_i, + .rst_ni, + .valid_i ( axi_a_req_i.aw_valid ), + .ready_o ( axi_a_rsp_o.aw_ready ), + .valid_o ( {fifo_sel_valid_aw_a, axi_a_req_o.aw_valid} ), + .ready_i ( {fifo_sel_ready_aw_a, axi_a_rsp_i.aw_ready} ) + ); + + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_w_a ( + .clk_i, + .rst_ni, + .valid_i ( axi_a_req_i.w_valid ), + .ready_o ( axi_a_rsp_o.w_ready ), + .valid_o ( {fifo_sel_valid_w_a, axi_a_req_o.w_valid} ), + .ready_i ( {fifo_sel_ready_w_a, axi_a_rsp_i.w_ready} ) + ); + + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_b_a ( + .clk_i, + .rst_ni, + .valid_i ( axi_a_rsp_i.b_valid ), + .ready_o ( axi_a_req_o.b_ready ), + .valid_o ( {fifo_sel_valid_b_a, axi_a_rsp_o.b_valid} ), + .ready_i ( {fifo_sel_ready_b_a, axi_a_req_i.b_ready} ) + ); + + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_ar_a ( + .clk_i, + .rst_ni, + .valid_i ( axi_a_req_i.ar_valid ), + .ready_o ( axi_a_rsp_o.ar_ready ), + .valid_o ( {fifo_sel_valid_ar_a, axi_a_req_o.ar_valid} ), + .ready_i ( {fifo_sel_ready_ar_a, axi_a_rsp_i.ar_ready} ) + ); + + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_r_a ( + .clk_i, + .rst_ni, + .valid_i ( axi_a_rsp_i.r_valid ), + .ready_o ( axi_a_req_o.r_ready ), + .valid_o ( {fifo_sel_valid_r_a, axi_a_rsp_o.r_valid} ), + .ready_i ( {fifo_sel_ready_r_a, axi_a_req_i.r_ready} ) + ); + + + //----------------------------------- + // Channel A FIFOs + //----------------------------------- + for (genvar id = 0; id < 2**AxiIdWidth; id++) begin : gen_fifos_a + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DATA_WIDTH ( 1'b0 ), + .DEPTH ( FifoDepth ), + .T ( axi_aw_chan_t ) + ) i_stream_fifo_aw_a ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( 1'b0 ), + .usage_o ( /* NOT CONNECTED */ ), + .data_i ( axi_a_req_i.aw ), + .valid_i ( fifo_valid_aw_a [id] ), + .ready_o ( fifo_ready_aw_a [id] ), + .data_o ( fifo_cmp_data_aw_a [id] ), + .valid_o ( fifo_cmp_valid_aw_a [id] ), + .ready_i ( fifo_cmp_valid_aw_a [id] & fifo_cmp_valid_aw_b [id] ) + ); + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DATA_WIDTH ( 1'b0 ), + .DEPTH ( FifoDepth ), + .T ( axi_b_chan_t ) + ) i_stream_fifo_b_a ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( 1'b0 ), + .usage_o ( /* NOT CONNECTED */ ), + .data_i ( axi_a_rsp_i.b ), + .valid_i ( fifo_valid_b_a [id] ), + .ready_o ( fifo_ready_b_a [id] ), + .data_o ( fifo_cmp_data_b_a [id] ), + .valid_o ( fifo_cmp_valid_b_a [id] ), + .ready_i ( fifo_cmp_valid_b_a [id] & fifo_cmp_valid_b_b [id] ) + ); + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DATA_WIDTH ( 1'b0 ), + .DEPTH ( FifoDepth ), + .T ( axi_ar_chan_t ) + ) i_stream_fifo_ar_a ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( 1'b0 ), + .usage_o ( /* NOT CONNECTED */ ), + .data_i ( axi_a_req_i.ar ), + .valid_i ( fifo_valid_ar_a [id] ), + .ready_o ( fifo_ready_ar_a [id] ), + .data_o ( fifo_cmp_data_ar_a [id] ), + .valid_o ( fifo_cmp_valid_ar_a [id] ), + .ready_i ( fifo_cmp_valid_ar_a [id] & fifo_cmp_valid_ar_b [id] ) + ); + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DATA_WIDTH ( 1'b0 ), + .DEPTH ( FifoDepth ), + .T ( axi_r_chan_t ) + ) i_stream_fifo_r_a ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( 1'b0 ), + .usage_o ( /* NOT CONNECTED */ ), + .data_i ( axi_a_rsp_i.r ), + .valid_i ( fifo_valid_r_a [id] ), + .ready_o ( fifo_ready_r_a [id] ), + .data_o ( fifo_cmp_data_r_a [id] ), + .valid_o ( fifo_cmp_valid_r_a [id] ), + .ready_i ( fifo_cmp_valid_r_a [id] & fifo_cmp_valid_r_b [id] ) + ); + end + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DATA_WIDTH ( 1'b0 ), + .DEPTH ( FifoDepth ), + .T ( axi_w_chan_t ) + ) i_stream_fifo_w_a ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( 1'b0 ), + .usage_o ( /* NOT CONNECTED */ ), + .data_i ( axi_a_req_i.w ), + .valid_i ( fifo_sel_valid_w_a ), + .ready_o ( fifo_sel_ready_w_a ), + .data_o ( fifo_cmp_data_w_a ), + .valid_o ( fifo_cmp_valid_w_a ), + .ready_i ( fifo_cmp_valid_w_a & fifo_cmp_valid_w_b ) + ); + + + //----------------------------------- + // Input Handshaking A + //----------------------------------- + always_comb begin : gen_handshaking_a + // aw + // defaults + fifo_valid_aw_a = '0; + fifo_sel_ready_aw_a = '0; + // assign according id + fifo_valid_aw_a [axi_a_req_i.aw.id] = fifo_sel_valid_aw_a; + fifo_sel_ready_aw_a = fifo_ready_aw_a[axi_a_req_i.aw.id]; + + + // b + // defaults + fifo_valid_b_a = '0; + fifo_sel_ready_b_a = '0; + // assign according id + fifo_valid_b_a [axi_a_rsp_i.b.id] = fifo_sel_valid_b_a; + fifo_sel_ready_b_a = fifo_ready_b_a[axi_a_rsp_i.b.id]; + + // ar + // defaults + fifo_valid_ar_a = '0; + fifo_sel_ready_ar_a = '0; + // assign according id + fifo_valid_ar_a [axi_a_req_i.ar.id] = fifo_sel_valid_ar_a; + fifo_sel_ready_ar_a = fifo_ready_ar_a[axi_a_req_i.ar.id]; + + // b + // defaults + fifo_valid_r_a = '0; + fifo_sel_ready_r_a = '0; + // assign according id + fifo_valid_r_a [axi_a_rsp_i.r.id] = fifo_sel_valid_r_a; + fifo_sel_ready_r_a = fifo_ready_r_a[axi_a_rsp_i.r.id]; + end + + + //----------------------------------- + // Channel B stream forks + //----------------------------------- + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_aw_b ( + .clk_i, + .rst_ni, + .valid_i ( axi_b_req_i.aw_valid ), + .ready_o ( axi_b_rsp_o.aw_ready ), + .valid_o ( {fifo_sel_valid_aw_b, axi_b_req_o.aw_valid} ), + .ready_i ( {fifo_sel_ready_aw_b, axi_b_rsp_i.aw_ready} ) + ); + + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_w_b ( + .clk_i, + .rst_ni, + .valid_i ( axi_b_req_i.w_valid ), + .ready_o ( axi_b_rsp_o.w_ready ), + .valid_o ( {fifo_sel_valid_w_b, axi_b_req_o.w_valid} ), + .ready_i ( {fifo_sel_ready_w_b, axi_b_rsp_i.w_ready} ) + ); + + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_b_b ( + .clk_i, + .rst_ni, + .valid_i ( axi_b_rsp_i.b_valid ), + .ready_o ( axi_b_req_o.b_ready ), + .valid_o ( {fifo_sel_valid_b_b, axi_b_rsp_o.b_valid} ), + .ready_i ( {fifo_sel_ready_b_b, axi_b_req_i.b_ready} ) + ); + + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_ar_b ( + .clk_i, + .rst_ni, + .valid_i ( axi_b_req_i.ar_valid ), + .ready_o ( axi_b_rsp_o.ar_ready ), + .valid_o ( {fifo_sel_valid_ar_b, axi_b_req_o.ar_valid} ), + .ready_i ( {fifo_sel_ready_ar_b, axi_b_rsp_i.ar_ready} ) + ); + + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_r_b ( + .clk_i, + .rst_ni, + .valid_i ( axi_b_rsp_i.r_valid ), + .ready_o ( axi_b_req_o.r_ready ), + .valid_o ( {fifo_sel_valid_r_b, axi_b_rsp_o.r_valid} ), + .ready_i ( {fifo_sel_ready_r_b, axi_b_req_i.r_ready} ) + ); + + + //----------------------------------- + // Channel B FIFOs + //----------------------------------- + for (genvar id = 0; id < 2**AxiIdWidth; id++) begin : gen_fifos_b + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DATA_WIDTH ( 1'b0 ), + .DEPTH ( FifoDepth ), + .T ( axi_aw_chan_t ) + ) i_stream_fifo_aw_b ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( 1'b0 ), + .usage_o ( /* NOT CONNECTED */ ), + .data_i ( axi_b_req_i.aw ), + .valid_i ( fifo_valid_aw_b [id] ), + .ready_o ( fifo_ready_aw_b [id] ), + .data_o ( fifo_cmp_data_aw_b [id] ), + .valid_o ( fifo_cmp_valid_aw_b [id] ), + .ready_i ( fifo_cmp_valid_aw_a [id] & fifo_cmp_valid_aw_b [id] ) + ); + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DATA_WIDTH ( 1'b0 ), + .DEPTH ( FifoDepth ), + .T ( axi_b_chan_t ) + ) i_stream_fifo_b_b ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( 1'b0 ), + .usage_o ( /* NOT CONNECTED */ ), + .data_i ( axi_b_rsp_i.b ), + .valid_i ( fifo_valid_b_b [id] ), + .ready_o ( fifo_ready_b_b [id] ), + .data_o ( fifo_cmp_data_b_b [id] ), + .valid_o ( fifo_cmp_valid_b_b [id] ), + .ready_i ( fifo_cmp_valid_b_a [id] & fifo_cmp_valid_b_b [id] ) + ); + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DATA_WIDTH ( 1'b0 ), + .DEPTH ( FifoDepth ), + .T ( axi_ar_chan_t ) + ) i_stream_fifo_ar_b ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( 1'b0 ), + .usage_o ( /* NOT CONNECTED */ ), + .data_i ( axi_b_req_i.ar ), + .valid_i ( fifo_valid_ar_b [id] ), + .ready_o ( fifo_ready_ar_b [id] ), + .data_o ( fifo_cmp_data_ar_b [id] ), + .valid_o ( fifo_cmp_valid_ar_b [id] ), + .ready_i ( fifo_cmp_valid_ar_a [id] & fifo_cmp_valid_ar_b [id] ) + ); + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DATA_WIDTH ( 1'b0 ), + .DEPTH ( FifoDepth ), + .T ( axi_r_chan_t ) + ) i_stream_fifo_r_b ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( 1'b0 ), + .usage_o ( /* NOT CONNECTED */ ), + .data_i ( axi_b_rsp_i.r ), + .valid_i ( fifo_valid_r_b [id] ), + .ready_o ( fifo_ready_r_b [id] ), + .data_o ( fifo_cmp_data_r_b [id] ), + .valid_o ( fifo_cmp_valid_r_b [id] ), + .ready_i ( fifo_cmp_valid_r_a [id] & fifo_cmp_valid_r_b [id] ) + ); + end + + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DATA_WIDTH ( 1'b0 ), + .DEPTH ( FifoDepth ), + .T ( axi_w_chan_t ) + ) i_stream_fifo_w_b ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( 1'b0 ), + .usage_o ( /* NOT CONNECTED */ ), + .data_i ( axi_b_req_i.w ), + .valid_i ( fifo_sel_valid_w_b ), + .ready_o ( fifo_sel_ready_w_b ), + .data_o ( fifo_cmp_data_w_b ), + .valid_o ( fifo_cmp_valid_w_b ), + .ready_i ( fifo_cmp_valid_w_a & fifo_cmp_valid_w_b ) + ); + + + //----------------------------------- + // Input Handshaking B + //----------------------------------- + always_comb begin : gen_handshaking_b + // aw + // defaults + fifo_valid_aw_b = '0; + fifo_sel_ready_aw_b = '0; + // assign according id + fifo_valid_aw_b [axi_b_req_i.aw.id] = fifo_sel_valid_aw_b; + fifo_sel_ready_aw_b = fifo_ready_aw_b[axi_b_req_i.aw.id]; + + + // b + // defaults + fifo_valid_b_b = '0; + fifo_sel_ready_b_b = '0; + // assign according id + fifo_valid_b_b [axi_b_rsp_i.b.id] = fifo_sel_valid_b_b; + fifo_sel_ready_b_b = fifo_ready_b_b[axi_b_rsp_i.b.id]; + + // ar + // defaults + fifo_valid_ar_b = '0; + fifo_sel_ready_ar_b = '0; + // assign according id + fifo_valid_ar_b [axi_b_req_i.ar.id] = fifo_sel_valid_ar_b; + fifo_sel_ready_ar_b = fifo_ready_ar_b[axi_b_req_i.ar.id]; + + // b + // defaults + fifo_valid_r_b = '0; + fifo_sel_ready_r_b = '0; + // assign according id + fifo_valid_r_b [axi_b_rsp_i.r.id] = fifo_sel_valid_r_b; + fifo_sel_ready_r_b = fifo_ready_r_b[axi_b_rsp_i.r.id]; + end + + + //----------------------------------- + // Comparison + //----------------------------------- + for (genvar id = 0; id < 2**AxiIdWidth; id++) begin : gen_cmp + assign aw_mismatch_o [id] = (fifo_cmp_valid_aw_a [id] & fifo_cmp_valid_aw_b [id]) ? + fifo_cmp_data_aw_a [id] == fifo_cmp_data_aw_b [id] : '0; + assign b_mismatch_o [id] = (fifo_cmp_valid_b_a [id] & fifo_cmp_valid_b_b [id]) ? + fifo_cmp_data_b_a [id] == fifo_cmp_data_b_b [id] : '0; + assign ar_mismatch_o [id] = (fifo_cmp_valid_ar_a [id] & fifo_cmp_valid_ar_b [id]) ? + fifo_cmp_data_ar_a [id] == fifo_cmp_data_ar_b [id] : '0; + assign r_mismatch_o [id] = (fifo_cmp_valid_r_a [id] & fifo_cmp_valid_r_b [id]) ? + fifo_cmp_data_r_a [id] == fifo_cmp_data_r_b [id] : '0; + end + + assign w_mismatch_o = (fifo_cmp_valid_w_a & fifo_cmp_valid_w_b ) ? + fifo_cmp_data_w_a != fifo_cmp_data_w_b : '0; + + + //----------------------------------- + // Outputs + //----------------------------------- + assign busy_o = (|fifo_cmp_valid_aw_a) | (|fifo_cmp_valid_aw_b) | + (|fifo_cmp_valid_w_a) | (|fifo_cmp_valid_w_b) | + (|fifo_cmp_valid_b_a) | (|fifo_cmp_valid_b_b) | + (|fifo_cmp_valid_ar_a) | (|fifo_cmp_valid_ar_b) | + (|fifo_cmp_valid_r_a) | (|fifo_cmp_valid_r_b); + + + assign mismatch_o = (|aw_mismatch_o) | (|w_mismatch_o) | (|b_mismatch_o) | + (|ar_mismatch_o) | (|r_mismatch_o); + +endmodule diff --git a/vendor/pulp-platform/axi/src/axi_cdc.sv b/vendor/pulp-platform/axi/src/axi_cdc.sv index 1e422ed729..152cbef24e 100644 --- a/vendor/pulp-platform/axi/src/axi_cdc.sv +++ b/vendor/pulp-platform/axi/src/axi_cdc.sv @@ -11,10 +11,8 @@ // // Authors: // - Andreas Kurth -// - Fabian Schuiki -// - Florian Zaruba +// - Luca Valente // - Wolfgang Roenninger -// - Luca Valente `include "axi/assign.svh" diff --git a/vendor/pulp-platform/axi/src/axi_cdc_dst.sv b/vendor/pulp-platform/axi/src/axi_cdc_dst.sv index d365156cee..f252a98001 100644 --- a/vendor/pulp-platform/axi/src/axi_cdc_dst.sv +++ b/vendor/pulp-platform/axi/src/axi_cdc_dst.sv @@ -10,10 +10,8 @@ // specific language governing permissions and limitations under the License. // // Authors: +// - Luca Valente // - Andreas Kurth -// - Fabian Schuiki -// - Florian Zaruba -// - Luca Valente `include "axi/assign.svh" `include "axi/typedef.svh" @@ -58,7 +56,13 @@ module axi_cdc_dst #( ); cdc_fifo_gray_dst #( +`ifdef QUESTA + // Workaround for a bug in Questa: Pass flat logic vector instead of struct to type parameter. .T ( logic [$bits(aw_chan_t)-1:0] ), +`else + // Other tools, such as VCS, have problems with type parameters constructed through `$bits()`. + .T ( aw_chan_t ), +`endif .LOG_DEPTH ( LogDepth ) ) i_cdc_fifo_gray_dst_aw ( .async_data_i ( async_data_slave_aw_data_i ), @@ -72,7 +76,11 @@ module axi_cdc_dst #( ); cdc_fifo_gray_dst #( +`ifdef QUESTA .T ( logic [$bits(w_chan_t)-1:0] ), +`else + .T ( w_chan_t ), +`endif .LOG_DEPTH ( LogDepth ) ) i_cdc_fifo_gray_dst_w ( .async_data_i ( async_data_slave_w_data_i ), @@ -86,7 +94,11 @@ module axi_cdc_dst #( ); cdc_fifo_gray_src #( +`ifdef QUESTA .T ( logic [$bits(b_chan_t)-1:0] ), +`else + .T ( b_chan_t ), +`endif .LOG_DEPTH ( LogDepth ) ) i_cdc_fifo_gray_src_b ( .src_clk_i ( dst_clk_i ), @@ -100,7 +112,11 @@ module axi_cdc_dst #( ); cdc_fifo_gray_dst #( +`ifdef QUESTA .T ( logic [$bits(ar_chan_t)-1:0] ), +`else + .T ( ar_chan_t ), +`endif .LOG_DEPTH ( LogDepth ) ) i_cdc_fifo_gray_dst_ar ( .dst_clk_i, @@ -114,7 +130,11 @@ module axi_cdc_dst #( ); cdc_fifo_gray_src #( +`ifdef QUESTA .T ( logic [$bits(r_chan_t)-1:0] ), +`else + .T ( r_chan_t ), +`endif .LOG_DEPTH ( LogDepth ) ) i_cdc_fifo_gray_src_r ( .src_clk_i ( dst_clk_i ), diff --git a/vendor/pulp-platform/axi/src/axi_cdc_src.sv b/vendor/pulp-platform/axi/src/axi_cdc_src.sv index 0f93ae82b7..614a17e45d 100644 --- a/vendor/pulp-platform/axi/src/axi_cdc_src.sv +++ b/vendor/pulp-platform/axi/src/axi_cdc_src.sv @@ -10,10 +10,8 @@ // specific language governing permissions and limitations under the License. // // Authors: +// - Luca Valente // - Andreas Kurth -// - Fabian Schuiki -// - Florian Zaruba -// - Luca Valente `include "axi/assign.svh" `include "axi/typedef.svh" @@ -58,7 +56,12 @@ module axi_cdc_src #( ); cdc_fifo_gray_src #( + // Workaround for a bug in Questa (see comment in `axi_cdc_dst` for details). +`ifdef QUESTA .T ( logic [$bits(aw_chan_t)-1:0] ), +`else + .T ( aw_chan_t ), +`endif .LOG_DEPTH ( LogDepth ) ) i_cdc_fifo_gray_src_aw ( .src_clk_i, @@ -72,7 +75,11 @@ module axi_cdc_src #( ); cdc_fifo_gray_src #( +`ifdef QUESTA .T ( logic [$bits(w_chan_t)-1:0] ), +`else + .T ( w_chan_t ), +`endif .LOG_DEPTH ( LogDepth ) ) i_cdc_fifo_gray_src_w ( .src_clk_i, @@ -86,7 +93,11 @@ module axi_cdc_src #( ); cdc_fifo_gray_dst #( +`ifdef QUESTA .T ( logic [$bits(b_chan_t)-1:0] ), +`else + .T ( b_chan_t ), +`endif .LOG_DEPTH ( LogDepth ) ) i_cdc_fifo_gray_dst_b ( .dst_clk_i ( src_clk_i ), @@ -100,7 +111,11 @@ module axi_cdc_src #( ); cdc_fifo_gray_src #( +`ifdef QUESTA .T ( logic [$bits(ar_chan_t)-1:0] ), +`else + .T ( ar_chan_t ), +`endif .LOG_DEPTH ( LogDepth ) ) i_cdc_fifo_gray_src_ar ( .src_clk_i, @@ -114,7 +129,11 @@ module axi_cdc_src #( ); cdc_fifo_gray_dst #( +`ifdef QUESTA .T ( logic [$bits(r_chan_t)-1:0] ), +`else + .T ( r_chan_t ), +`endif .LOG_DEPTH ( LogDepth ) ) i_cdc_fifo_gray_dst_r ( .dst_clk_i ( src_clk_i ), diff --git a/vendor/pulp-platform/axi/src/axi_chan_compare.sv b/vendor/pulp-platform/axi/src/axi_chan_compare.sv new file mode 100644 index 0000000000..4de4ea7415 --- /dev/null +++ b/vendor/pulp-platform/axi/src/axi_chan_compare.sv @@ -0,0 +1,212 @@ +// Copyright 2018 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: +// - Thomas Benz +// - Paul Scheffler +// - Tim Fischer + +/// Non-synthesizable module comparing two AXI channels of the same type +module axi_chan_compare #( + parameter type aw_chan_t = logic, + parameter type w_chan_t = logic, + parameter type b_chan_t = logic, + parameter type ar_chan_t = logic, + parameter type r_chan_t = logic, + parameter type req_t = logic, + parameter type resp_t = logic +)( + input logic clk_a_i, + input logic clk_b_i, + input req_t axi_a_req, + input resp_t axi_a_res, + input req_t axi_b_req, + input resp_t axi_b_res +); + + function automatic void print_aw ( + input aw_chan_t aw_expected, + input aw_chan_t aw_received + ); + // verilog_lint: waive-start line-length + $display("AW | expected | received "); + $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); + $display("id: | %64d | %64d", aw_expected.id, aw_received.id); + $display("addr: | %64x | %64x", aw_expected.addr, aw_received.addr); + $display("len: | %64d | %64d", aw_expected.len, aw_received.len); + $display("size: | %64d | %64d", aw_expected.size, aw_received.size); + $display("burst: | %64d | %64d", aw_expected.burst, aw_received.burst); + $display("lock: | %64d | %64d", aw_expected.lock, aw_received.lock); + $display("cache: | %64d | %64d", aw_expected.cache, aw_received.cache); + $display("prot: | %64d | %64d", aw_expected.prot, aw_received.prot); + $display("qos: | %64d | %64d", aw_expected.qos, aw_received.qos); + $display("region: | %64d | %64d", aw_expected.region, aw_received.region); + $display("user: | %64d | %64d", aw_expected.user, aw_received.user); + $display("atop: | %64d | %64d", aw_expected.atop, aw_received.atop); + $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); + // verilog_lint: waive-stop line-length + endfunction + + function automatic void print_ar ( + input ar_chan_t ar_expected, + input ar_chan_t ar_received + ); + // verilog_lint: waive-start line-length + $display("AR | expected | received "); + $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); + $display("id: | %64d | %64d", ar_expected.id, ar_received.id); + $display("addr: | %64x | %64x", ar_expected.addr, ar_received.addr); + $display("len: | %64d | %64d", ar_expected.len, ar_received.len); + $display("size: | %64d | %64d", ar_expected.size, ar_received.size); + $display("burst: | %64d | %64d", ar_expected.burst, ar_received.burst); + $display("lock: | %64d | %64d", ar_expected.lock, ar_received.lock); + $display("cache: | %64d | %64d", ar_expected.cache, ar_received.cache); + $display("prot: | %64d | %64d", ar_expected.prot, ar_received.prot); + $display("qos: | %64d | %64d", ar_expected.qos, ar_received.qos); + $display("region: | %64d | %64d", ar_expected.region, ar_received.region); + $display("user: | %64d | %64d", ar_expected.user, ar_received.user); + $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); + // verilog_lint: waive-stop line-length + endfunction + + function automatic void print_w ( + input w_chan_t w_expected, + input w_chan_t w_received + ); + // verilog_lint: waive-start line-length + $display("W | expected | received "); + $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); + $display("data: | %64x | %64x", w_expected.data, w_received.data); + $display("strb: | %64d | %64d", w_expected.strb, w_received.strb); + $display("last: | %64d | %64d", w_expected.last, w_received.last); + $display("user: | %64d | %64d", w_expected.user, w_received.user); + $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); + // verilog_lint: waive-stop line-length + endfunction + + function automatic void print_b ( + input b_chan_t b_expected, + input b_chan_t b_received + ); + // verilog_lint: waive-start line-length + $display("B | expected | received "); + $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); + $display("id: | %64d | %64d", b_expected.id, b_received.id); + $display("resp: | %64d | %64d", b_expected.resp, b_received.resp); + $display("user: | %64d | %64d", b_expected.user, b_received.user); + $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); + // verilog_lint: waive-stop line-length + endfunction + + function automatic void print_r ( + input r_chan_t r_expected, + input r_chan_t r_received + ); + // verilog_lint: waive-start line-length + $display("R | expected | received "); + $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); + $display("id: | %64d | %64d", r_expected.id, r_received.id); + $display("data: | %64x | %64x", r_expected.data, r_received.data); + $display("resp: | %64d | %64d", r_expected.resp, r_received.resp); + $display("last: | %64d | %64d", r_expected.last, r_received.last); + $display("user: | %64d | %64d", r_expected.user, r_received.user); + $display("--------|------------------------------------------------------------------|-----------------------------------------------------------------"); + // verilog_lint: waive-stop line-length + endfunction + + // 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 [$]; + + // 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); + // 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); + 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); + // r + if (axi_b_res.r_valid & axi_b_req.r_ready) + r_queue.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 + $error("AW mismatch!"); + print_aw(aw, axi_b_req.aw); + end + end + // w + if (axi_b_req.w_valid & axi_b_res.w_ready) begin + automatic w_chan_t w; + 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 + $error("W mismatch!"); + print_w(w, axi_b_req.w); + 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 + $error("AR mismatch!"); + print_ar(ar, axi_b_req.ar); + end + end + end + + // responses arriving at axi A from B: dequeue elements and check + 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 + $error("B mismatch!"); + print_b(b, axi_a_res.b); + 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 + $error("R mismatch!"); + print_r(r, axi_a_res.r); + end + end + end + +endmodule : axi_chan_compare diff --git a/vendor/pulp-platform/axi/src/axi_cut.sv b/vendor/pulp-platform/axi/src/axi_cut.sv index 6c31321b67..34278ca621 100644 --- a/vendor/pulp-platform/axi/src/axi_cut.sv +++ b/vendor/pulp-platform/axi/src/axi_cut.sv @@ -19,25 +19,25 @@ /// Breaks all combinatorial paths between its input and output. module axi_cut #( // bypass enable - parameter bit Bypass = 1'b0, + parameter bit Bypass = 1'b0, // AXI channel structs - parameter type aw_chan_t = logic, - parameter type w_chan_t = logic, - parameter type b_chan_t = logic, - parameter type ar_chan_t = logic, - parameter type r_chan_t = logic, + parameter type aw_chan_t = logic, + parameter type w_chan_t = logic, + parameter type b_chan_t = logic, + parameter type ar_chan_t = logic, + parameter type r_chan_t = logic, // AXI request & response structs - parameter type req_t = logic, - parameter type resp_t = logic + parameter type axi_req_t = logic, + parameter type axi_resp_t = logic ) ( - input logic clk_i, - input logic rst_ni, + input logic clk_i, + input logic rst_ni, // salve port - input req_t slv_req_i, - output resp_t slv_resp_o, + input axi_req_t slv_req_i, + output axi_resp_t slv_resp_o, // master port - output req_t mst_req_o, - input resp_t mst_resp_i + output axi_req_t mst_req_o, + input axi_resp_t mst_resp_i ); // a spill register for each channel @@ -145,11 +145,11 @@ module axi_cut_intf #( `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) + `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) - req_t slv_req, mst_req; - resp_t slv_resp, mst_resp; + axi_req_t slv_req, mst_req; + axi_resp_t slv_resp, mst_resp; `AXI_ASSIGN_TO_REQ(slv_req, in) `AXI_ASSIGN_FROM_RESP(in, slv_resp) @@ -158,14 +158,14 @@ module axi_cut_intf #( `AXI_ASSIGN_TO_RESP(mst_resp, out) axi_cut #( - .Bypass ( BYPASS ), - .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 ), - .req_t ( req_t ), - .resp_t ( resp_t ) + .Bypass ( BYPASS ), + .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 ( axi_req_t ), + .axi_resp_t ( axi_resp_t ) ) i_axi_cut ( .clk_i, .rst_ni, @@ -219,11 +219,11 @@ module axi_lite_cut_intf #( `AXI_LITE_TYPEDEF_B_CHAN_T(b_chan_t) `AXI_LITE_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t) `AXI_LITE_TYPEDEF_R_CHAN_T(r_chan_t, data_t) - `AXI_LITE_TYPEDEF_REQ_T(req_t, aw_chan_t, w_chan_t, ar_chan_t) - `AXI_LITE_TYPEDEF_RESP_T(resp_t, b_chan_t, r_chan_t) + `AXI_LITE_TYPEDEF_REQ_T(axi_req_t, aw_chan_t, w_chan_t, ar_chan_t) + `AXI_LITE_TYPEDEF_RESP_T(axi_resp_t, b_chan_t, r_chan_t) - req_t slv_req, mst_req; - resp_t slv_resp, mst_resp; + axi_req_t slv_req, mst_req; + axi_resp_t slv_resp, mst_resp; `AXI_LITE_ASSIGN_TO_REQ(slv_req, in) `AXI_LITE_ASSIGN_FROM_RESP(in, slv_resp) @@ -232,14 +232,14 @@ module axi_lite_cut_intf #( `AXI_LITE_ASSIGN_TO_RESP(mst_resp, out) axi_cut #( - .Bypass ( BYPASS ), - .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 ), - .req_t ( req_t ), - .resp_t ( resp_t ) + .Bypass ( BYPASS ), + .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 ( axi_req_t ), + .axi_resp_t ( axi_resp_t ) ) i_axi_cut ( .clk_i, .rst_ni, diff --git a/vendor/pulp-platform/axi/src/axi_delayer.sv b/vendor/pulp-platform/axi/src/axi_delayer.sv index cab18eb596..8d217d14ea 100644 --- a/vendor/pulp-platform/axi/src/axi_delayer.sv +++ b/vendor/pulp-platform/axi/src/axi_delayer.sv @@ -16,28 +16,28 @@ /// Synthesizable module that (randomly) delays AXI channels. module axi_delayer #( // AXI channel types - parameter type aw_chan_t = logic, - parameter type w_chan_t = logic, - parameter type b_chan_t = logic, - parameter type ar_chan_t = logic, - parameter type r_chan_t = logic, + parameter type aw_chan_t = logic, + parameter type w_chan_t = logic, + parameter type b_chan_t = logic, + parameter type ar_chan_t = logic, + parameter type r_chan_t = logic, // AXI request & response types - parameter type req_t = logic, - parameter type resp_t = logic, + parameter type axi_req_t = logic, + parameter type axi_resp_t = logic, // delay parameters parameter bit StallRandomInput = 0, parameter bit StallRandomOutput = 0, parameter int unsigned FixedDelayInput = 1, parameter int unsigned FixedDelayOutput = 1 ) ( - input logic clk_i, // Clock - input logic rst_ni, // Asynchronous reset active low + input logic clk_i, // Clock + input logic rst_ni, // Asynchronous reset active low // slave port - input req_t slv_req_i, - output resp_t slv_resp_o, + input axi_req_t slv_req_i, + output axi_resp_t slv_resp_o, // master port - output req_t mst_req_o, - input resp_t mst_resp_i + output axi_req_t mst_req_o, + input axi_resp_t mst_resp_i ); // AW stream_delay #( @@ -152,11 +152,11 @@ module axi_delayer_intf #( `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) + `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) - req_t slv_req, mst_req; - resp_t slv_resp, mst_resp; + axi_req_t slv_req, mst_req; + axi_resp_t slv_resp, mst_resp; `AXI_ASSIGN_TO_REQ(slv_req, slv) `AXI_ASSIGN_FROM_RESP(slv, slv_resp) @@ -170,8 +170,8 @@ module axi_delayer_intf #( .b_chan_t ( b_chan_t ), .ar_chan_t ( ar_chan_t ), .r_chan_t ( r_chan_t ), - .req_t ( req_t ), - .resp_t ( resp_t ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), .StallRandomInput ( STALL_RANDOM_INPUT ), .StallRandomOutput ( STALL_RANDOM_OUTPUT ), .FixedDelayInput ( FIXED_DELAY_INPUT ), diff --git a/vendor/pulp-platform/axi/src/axi_demux.sv b/vendor/pulp-platform/axi/src/axi_demux.sv index 99a18c8dca..fc061ff09e 100644 --- a/vendor/pulp-platform/axi/src/axi_demux.sv +++ b/vendor/pulp-platform/axi/src/axi_demux.sv @@ -12,24 +12,45 @@ // - Wolfgang Roenninger // - Andreas Kurth +`include "common_cells/assertions.svh" `include "common_cells/registers.svh" -// axi_demux: Demultiplex an AXI bus from one slave port to multiple master ports. -// See `doc/axi_demux.md` for the documentation, including the definition of parameters and ports. +`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 #( parameter int unsigned AxiIdWidth = 32'd0, + parameter bit AtopSupport = 1'b1, parameter type aw_chan_t = logic, parameter type w_chan_t = logic, parameter type b_chan_t = logic, parameter type ar_chan_t = logic, parameter type r_chan_t = logic, - parameter type req_t = logic, - parameter type resp_t = logic, + 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, - parameter bit FallThrough = 1'b0, parameter bit SpillAw = 1'b1, parameter bit SpillW = 1'b0, parameter bit SpillB = 1'b0, @@ -39,37 +60,91 @@ module axi_demux #( 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, + input logic clk_i, + input logic rst_ni, + input logic test_i, // Slave Port - input req_t slv_req_i, - input select_t slv_aw_select_i, - input select_t slv_ar_select_i, - output resp_t slv_resp_o, + 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 req_t [NoMstPorts-1:0] mst_reqs_o, - input resp_t [NoMstPorts-1:0] mst_resps_i + output axi_req_t [NoMstPorts-1:0] mst_reqs_o, + input axi_resp_t [NoMstPorts-1:0] mst_resps_i ); - localparam int unsigned IdCounterWidth = MaxTrans > 1 ? $clog2(MaxTrans) : 1; + localparam int unsigned IdCounterWidth = cf_math_pkg::idx_width(MaxTrans); + typedef logic [IdCounterWidth-1:0] id_cnt_t; - //-------------------------------------- - // Typedefs for the FIFOs / Queues - //-------------------------------------- - typedef struct packed { - aw_chan_t aw_chan; - select_t aw_select; - } aw_chan_select_t; - typedef struct packed { - ar_chan_t ar_chan; - select_t ar_select; - } ar_chan_select_t; // pass through if only one master port if (NoMstPorts == 32'h1) begin : gen_no_demux - assign mst_reqs_o[0] = slv_req_i; - assign slv_resp_o = mst_resps_i; + 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 @@ -83,20 +158,23 @@ module axi_demux #( // Write Transaction //-------------------------------------- // comes from spill register at input - aw_chan_select_t slv_aw_chan_select; - logic slv_aw_valid, slv_aw_ready; + 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; - logic aw_push; // Upon an ATOP load, inject IDs from the AW into the AR channel logic atop_inject; - // W FIFO: stores the decision to which master W beats should go - logic w_fifo_pop; - logic w_fifo_full, w_fifo_empty; - select_t w_select; + // 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; @@ -118,8 +196,8 @@ module axi_demux #( // Read Transaction //-------------------------------------- // comes from spill register at input - ar_chan_select_t slv_ar_chan_select; - logic slv_ar_valid, slv_ar_ready; + 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; @@ -148,30 +226,34 @@ module axi_demux #( // AW Channel //-------------------------------------- // spill register at the channel input - `ifdef TARGET_VSIM - // Workaround for bug in Questa 2020.2 and 2021.1: Flatten the struct into a logic vector before - // instantiating `spill_register`. - typedef logic [$bits(aw_chan_select_t)-1:0] aw_chan_select_flat_t; - `else - typedef aw_chan_select_t aw_chan_select_flat_t; - `endif - aw_chan_select_flat_t slv_aw_chan_select_in_flat, - slv_aw_chan_select_out_flat; - assign slv_aw_chan_select_in_flat = {slv_req_i.aw, slv_aw_select_i}; spill_register #( - .T ( aw_chan_select_flat_t ), - .Bypass ( ~SpillAw ) // because module param indicates if we want a spill reg - ) 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_aw_chan_select_in_flat ), - .valid_o ( slv_aw_valid ), - .ready_i ( slv_aw_ready ), - .data_o ( slv_aw_chan_select_out_flat ) + .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_aw_chan_select = slv_aw_chan_select_out_flat; + 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 @@ -186,9 +268,9 @@ module axi_demux #( lock_aw_valid_d = lock_aw_valid_q; load_aw_lock = 1'b0; // AW ID counter and W FIFO - aw_push = 1'b0; + w_cnt_up = 1'b0; // ATOP injection into ar counter - atop_inject = 1'b0; + 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; @@ -197,23 +279,31 @@ module axi_demux #( slv_aw_ready = 1'b1; lock_aw_valid_d = 1'b0; load_aw_lock = 1'b1; - atop_inject = slv_aw_chan_select.aw_chan.atop[5]; // inject the ATOP if necessary + // inject the ATOP if necessary + atop_inject = slv_aw_chan.atop[axi_pkg::ATOP_R_RESP] & AtopSupport; end end else begin - // Process can start handling a transaction if its `i_aw_id_counter` and `w_fifo` have - // space in them. Further check if we could inject something on the AR channel. - if (!aw_id_cnt_full && !w_fifo_full && !ar_id_cnt_full) begin - // there is a valid AW vector make the id lookup and go further, if it passes - if (slv_aw_valid && (!aw_select_occupied || - (slv_aw_chan_select.aw_select == lookup_aw_select))) 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 - aw_push = 1'b1; + w_cnt_up = 1'b1; // on AW transaction if (aw_ready) begin slv_aw_ready = 1'b1; - atop_inject = slv_aw_chan_select.aw_chan.atop[5]; + 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; @@ -234,7 +324,7 @@ module axi_demux #( // 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_chan_select.aw_select; + 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 @@ -243,42 +333,46 @@ module axi_demux #( .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_select.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_select.aw_chan.id[0+:AxiLookBits] ), - .push_mst_select_i ( slv_aw_chan_select.aw_select ), - .push_i ( aw_push ), - .pop_axi_id_i ( slv_b_chan.id[0+:AxiLookBits] ), - .pop_i ( slv_b_valid & slv_b_ready ) + .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 - // FIFO to save W selection - fifo_v3 #( - .FALL_THROUGH ( FallThrough ), - .DEPTH ( MaxTrans ), - .dtype ( select_t ) - ) i_w_fifo ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .flush_i ( 1'b0 ), - .testmode_i( test_i ), - .full_o ( w_fifo_full ), - .empty_o ( w_fifo_empty ), - .usage_o ( ), - .data_i ( slv_aw_chan_select.aw_select ), - .push_i ( aw_push ), // controlled from proc_aw_chan - .data_o ( w_select ), // where the w beat should go - .pop_i ( w_fifo_pop ) // controlled from proc_w_chan + // 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 //-------------------------------------- @@ -337,30 +431,36 @@ module axi_demux #( //-------------------------------------- // AR Channel //-------------------------------------- - `ifdef TARGET_VSIM - // Workaround for bug in Questa 2020.2 and 2021.1: Flatten the struct into a logic vector before - // instantiating `spill_register`. - typedef logic [$bits(ar_chan_select_t)-1:0] ar_chan_select_flat_t; - `else - typedef ar_chan_select_t ar_chan_select_flat_t; - `endif - ar_chan_select_flat_t slv_ar_chan_select_in_flat, - slv_ar_chan_select_out_flat; - assign slv_ar_chan_select_in_flat = {slv_req_i.ar, slv_ar_select_i}; + ar_chan_t slv_ar_chan; + select_t slv_ar_select; spill_register #( - .T ( ar_chan_select_flat_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_ar_chan_select_in_flat ), - .valid_o ( slv_ar_valid ), - .ready_i ( slv_ar_ready ), - .data_o ( slv_ar_chan_select_out_flat ) + .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_ar_chan_select = slv_ar_chan_select_out_flat; + 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 @@ -391,7 +491,7 @@ module axi_demux #( 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_chan_select.ar_select == lookup_ar_select))) begin + (slv_ar_select == lookup_ar_select))) begin // connect the AR handshake ar_valid = 1'b1; // on transaction @@ -417,7 +517,7 @@ module axi_demux #( // 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_chan_select.ar_select; + 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 @@ -426,19 +526,19 @@ module axi_demux #( .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_select.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_select.aw_chan.id[0+:AxiLookBits] ), - .inject_i ( atop_inject ), - .push_axi_id_i ( slv_ar_chan_select.ar_chan.id[0+:AxiLookBits] ), - .push_mst_select_i ( slv_ar_chan_select.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 ) + .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 @@ -480,8 +580,8 @@ module axi_demux #( .idx_o ( ) ); - assign ar_ready = ar_valid & mst_resps_i[slv_ar_chan_select.ar_select].ar_ready; - assign aw_ready = aw_valid & mst_resps_i[slv_aw_chan_select.aw_select].aw_ready; + 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! @@ -489,32 +589,32 @@ module axi_demux #( // default assignments mst_reqs_o = '0; slv_w_ready = 1'b0; - w_fifo_pop = 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_select.aw_chan; + mst_reqs_o[i].aw = slv_aw_chan; mst_reqs_o[i].aw_valid = 1'b0; - if (aw_valid && (slv_aw_chan_select.aw_select == i)) begin + 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_fifo_empty && (w_select == i)) begin + 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_fifo_pop = slv_w_valid & mst_resps_i[i].w_ready & slv_w_chan.last; + 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_select.ar_chan; + mst_reqs_o[i].ar = slv_ar_chan; mst_reqs_o[i].ar_valid = 1'b0; - if (ar_valid && (slv_ar_chan_select.ar_select == i)) begin + if (ar_valid && (slv_ar_select == i)) begin mst_reqs_o[i].ar_valid = 1'b1; end @@ -555,18 +655,28 @@ module axi_demux #( 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."); - aw_stable: assert property( @(posedge clk_i) (aw_valid && !aw_ready) - |=> $stable(slv_aw_chan_select)) else - $fatal(1, "slv_aw_chan_select unstable with valid set."); - ar_stable: assert property( @(posedge clk_i) (ar_valid && !ar_ready) - |=> $stable(slv_ar_chan_select)) else - $fatal(1, "slv_aw_chan_select unstable with valid set."); + 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_chan_select.ar_select < NoMstPorts)) - else $fatal(1, "slv_ar_chan_select.ar_select illegal while ar_valid."); + (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_chan_select.aw_select < NoMstPorts)) - else $fatal(1, "slv_aw_chan_select.aw_select illegal while aw_valid."); + (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 @@ -699,6 +809,7 @@ endmodule `include "axi/typedef.svh" module axi_demux_intf #( parameter int unsigned AXI_ID_WIDTH = 32'd0, // Synopsys DC requires default value for params + parameter bit ATOP_SUPPORT = 1'b1, parameter int unsigned AXI_ADDR_WIDTH = 32'd0, parameter int unsigned AXI_DATA_WIDTH = 32'd0, parameter int unsigned AXI_USER_WIDTH = 32'd0, @@ -706,7 +817,6 @@ module axi_demux_intf #( parameter int unsigned MAX_TRANS = 32'd8, parameter int unsigned AXI_LOOK_BITS = 32'd3, parameter bit UNIQUE_IDS = 1'b0, - parameter bit FALL_THROUGH = 1'b0, parameter bit SPILL_AW = 1'b1, parameter bit SPILL_W = 1'b0, parameter bit SPILL_B = 1'b0, @@ -735,13 +845,13 @@ module axi_demux_intf #( `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) + `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) - req_t slv_req; - resp_t slv_resp; - req_t [NO_MST_PORTS-1:0] mst_req; - resp_t [NO_MST_PORTS-1:0] mst_resp; + axi_req_t slv_req; + axi_resp_t slv_resp; + axi_req_t [NO_MST_PORTS-1:0] mst_req; + axi_resp_t [NO_MST_PORTS-1:0] mst_resp; `AXI_ASSIGN_TO_REQ(slv_req, slv) `AXI_ASSIGN_FROM_RESP(slv, slv_resp) @@ -753,18 +863,18 @@ module axi_demux_intf #( axi_demux #( .AxiIdWidth ( AXI_ID_WIDTH ), // ID Width - .aw_chan_t ( aw_chan_t ), // AW Channel Type - .w_chan_t ( w_chan_t ), // W Channel Type - .b_chan_t ( b_chan_t ), // B Channel Type - .ar_chan_t ( ar_chan_t ), // AR Channel Type - .r_chan_t ( r_chan_t ), // R Channel Type - .req_t ( req_t ), - .resp_t ( resp_t ), + .AtopSupport ( ATOP_SUPPORT ), + .aw_chan_t ( aw_chan_t ), // AW Channel Type + .w_chan_t ( w_chan_t ), // W Channel Type + .b_chan_t ( b_chan_t ), // B Channel Type + .ar_chan_t ( ar_chan_t ), // AR Channel Type + .r_chan_t ( r_chan_t ), // R Channel Type + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), .NoMstPorts ( NO_MST_PORTS ), .MaxTrans ( MAX_TRANS ), .AxiLookBits ( AXI_LOOK_BITS ), .UniqueIds ( UNIQUE_IDS ), - .FallThrough ( FALL_THROUGH ), .SpillAw ( SPILL_AW ), .SpillW ( SPILL_W ), .SpillB ( SPILL_B ), diff --git a/vendor/pulp-platform/axi/src/axi_dumper.sv b/vendor/pulp-platform/axi/src/axi_dumper.sv new file mode 100644 index 0000000000..c3031ea5c3 --- /dev/null +++ b/vendor/pulp-platform/axi/src/axi_dumper.sv @@ -0,0 +1,215 @@ +// Copyright (c) 2019 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: +// - Michael Rogenmoser + +/// Simulation-Only dumper for AXI transactions +/// +/// This module writes all handshaked AXI beats to a log file. To use in simulation, +/// ensure `TARGET_SIMULATION` is defined. +module axi_dumper #( + parameter BusName = "axi_bus", + parameter bit LogAW = 1'b1, + parameter bit LogAR = 1'b1, + parameter bit LogW = 1'b0, + parameter bit LogB = 1'b0, + parameter bit LogR = 1'b0, + parameter type axi_req_t = logic, + parameter type axi_resp_t = logic +) ( + input logic clk_i, + input logic rst_ni, + input axi_req_t axi_req_i, + input axi_resp_t axi_resp_i +); + +`ifdef TARGET_SIMULATION + string fn; + integer f; + initial begin + #1; + $sformat(fn, "axi_trace_%s.log", BusName); + f = $fopen(fn, "w"); + $display("[Tracer] Logging axi accesses to %s", fn); + end + + always_ff @(posedge clk_i) begin : proc_tracer + automatic string aw_data [string]; + automatic string ar_data [string]; + automatic string w_data [string]; + automatic string b_data [string]; + automatic string r_data [string]; + + automatic string aw_string; + automatic string ar_string; + automatic string w_string; + automatic string b_string; + automatic string r_string; + + if (rst_ni) begin + aw_data = '{ + "type" : "\"AW\"", + "time" : $sformatf("%d", $time()), + "id" : $sformatf("0x%0x", axi_req_i.aw.id), + "addr" : $sformatf("0x%0x", axi_req_i.aw.addr), + "len" : $sformatf("0x%0x", axi_req_i.aw.len), + "size" : $sformatf("0x%0x", axi_req_i.aw.size), + "burst" : $sformatf("0x%0x", axi_req_i.aw.burst), + "lock" : $sformatf("0x%0x", axi_req_i.aw.lock), + "cache" : $sformatf("0x%0x", axi_req_i.aw.cache), + "prot" : $sformatf("0x%0x", axi_req_i.aw.prot), + "qos" : $sformatf("0x%0x", axi_req_i.aw.qos), + "region" : $sformatf("0x%0x", axi_req_i.aw.region), + "atop" : $sformatf("0x%0x", axi_req_i.aw.atop), + "user" : $sformatf("0x%0x", axi_req_i.aw.user) + }; + ar_data = '{ + "type" : "\"AR\"", + "time" : $sformatf("%d", $time()), + "id" : $sformatf("0x%0x", axi_req_i.ar.id), + "addr" : $sformatf("0x%0x", axi_req_i.ar.addr), + "len" : $sformatf("0x%0x", axi_req_i.ar.len), + "size" : $sformatf("0x%0x", axi_req_i.ar.size), + "burst" : $sformatf("0x%0x", axi_req_i.ar.burst), + "lock" : $sformatf("0x%0x", axi_req_i.ar.lock), + "cache" : $sformatf("0x%0x", axi_req_i.ar.cache), + "prot" : $sformatf("0x%0x", axi_req_i.ar.prot), + "qos" : $sformatf("0x%0x", axi_req_i.ar.qos), + "region" : $sformatf("0x%0x", axi_req_i.ar.region), + "user" : $sformatf("0x%0x", axi_req_i.ar.user) + }; + w_data = '{ + "type" : "\"W\"", + "time" : $sformatf("%d", $time()), + "data" : $sformatf("0x%0x", axi_req_i.w.data), + "strb" : $sformatf("0x%0x", axi_req_i.w.strb), + "last" : $sformatf("0x%0x", axi_req_i.w.last), + "user" : $sformatf("0x%0x", axi_req_i.w.user) + }; + b_data = '{ + "type" : "\"B\"", + "time" : $sformatf("%d", $time()), + "id" : $sformatf("0x%0x", axi_resp_i.b.id), + "resp" : $sformatf("0x%0x", axi_resp_i.b.resp), + "user" : $sformatf("0x%0x", axi_resp_i.b.user) + }; + r_data = '{ + "type" : "\"R\"", + "time" : $sformatf("%d", $time()), + "id" : $sformatf("0x%0x", axi_resp_i.r.id), + "data" : $sformatf("0x%0x", axi_resp_i.r.data), + "resp" : $sformatf("0x%0x", axi_resp_i.r.resp), + "last" : $sformatf("0x%0x", axi_resp_i.r.last), + "user" : $sformatf("0x%0x", axi_resp_i.r.user) + }; + if (LogAW && axi_req_i.aw_valid && axi_resp_i.aw_ready) begin + aw_string = "{"; + foreach(aw_data[key]) aw_string = $sformatf("%s'%s': %s, ", aw_string, key, aw_data[key]); + aw_string = $sformatf("%s}", aw_string); + $fwrite(f, aw_string); + $fwrite(f, "\n"); + end + if (LogAR && axi_req_i.ar_valid && axi_resp_i.ar_ready) begin + ar_string = "{"; + foreach(ar_data[key]) ar_string = $sformatf("%s'%s': %s, ", ar_string, key, ar_data[key]); + ar_string = $sformatf("%s}", ar_string); + $fwrite(f, ar_string); + $fwrite(f, "\n"); + end + if (LogW && axi_req_i.w_valid && axi_resp_i.w_ready) begin + w_string = "{"; + foreach(w_data[key]) w_string = $sformatf("%s'%s': %s, ", w_string, key, w_data[key]); + w_string = $sformatf("%s}", w_string); + $fwrite(f, w_string); + $fwrite(f, "\n"); + end + if (LogB && axi_resp_i.b_valid && axi_req_i.b_ready) begin + b_string = "{"; + foreach(b_data[key]) b_string = $sformatf("%s'%s': %s, ", b_string, key, b_data[key]); + b_string = $sformatf("%s}", b_string); + $fwrite(f, b_string); + $fwrite(f, "\n"); + end + if (LogR && axi_resp_i.r_valid && axi_req_i.r_ready) begin + r_string = "{"; + foreach(r_data[key]) r_string = $sformatf("%s'%s': %s, ", r_string, key, r_data[key]); + r_string = $sformatf("%s}", r_string); + $fwrite(f, r_string); + $fwrite(f, "\n"); + end + end + end + final begin + $fclose(f); + end + +`endif + +endmodule + + +`include "axi/assign.svh" +`include "axi/typedef.svh" + +module axi_dumper_intf #( + parameter BusName = "axi_bus", + parameter bit LogAW = 1'b1, + parameter bit LogAR = 1'b1, + parameter bit LogW = 1'b0, + parameter bit LogB = 1'b0, + parameter bit LogR = 1'b0, + parameter int unsigned AXI_ID_WIDTH = 32'd0, + parameter int unsigned AXI_ADDR_WIDTH = 32'd0, + parameter int unsigned AXI_DATA_WIDTH = 32'd0, + parameter int unsigned AXI_USER_WIDTH = 32'd0 +) ( + input logic clk_i, + input logic rst_ni, + AXI_BUS_DV.Monitor axi_bus +); + + 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 axi_req; + axi_resp_t axi_resp; + + `AXI_ASSIGN_TO_REQ(axi_req, axi_bus) + `AXI_ASSIGN_TO_RESP(axi_resp, axi_bus) + + axi_dumper #( + .BusName ( BusName ), + .LogAW ( LogAW ), + .LogAR ( LogAR ), + .LogW ( LogW ), + .LogB ( LogB ), + .LogR ( LogR ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ) + ) i_axi_dumper ( + .clk_i, + .rst_ni, + .axi_req_i ( axi_req ), + .axi_resp_i ( axi_resp ) + ); + +endmodule diff --git a/vendor/pulp-platform/axi/src/axi_dw_downsizer.sv b/vendor/pulp-platform/axi/src/axi_dw_downsizer.sv index 2139ac67de..78b053d998 100644 --- a/vendor/pulp-platform/axi/src/axi_dw_downsizer.sv +++ b/vendor/pulp-platform/axi/src/axi_dw_downsizer.sv @@ -188,8 +188,8 @@ module axi_dw_downsizer #( axi_err_slv #( .AxiIdWidth(AxiIdWidth ), .Resp (axi_pkg::RESP_SLVERR), - .req_t (axi_mst_req_t ), - .resp_t (axi_mst_resp_t ) + .axi_req_t (axi_mst_req_t ), + .axi_resp_t(axi_mst_resp_t ) ) i_axi_err_slv ( .clk_i (clk_i ), .rst_ni (rst_ni ), @@ -216,8 +216,8 @@ module axi_dw_downsizer #( .b_chan_t (b_chan_t ), .ar_chan_t (ar_chan_t ), .r_chan_t (mst_r_chan_t ), - .req_t (axi_mst_req_t ), - .resp_t (axi_mst_resp_t), + .axi_req_t (axi_mst_req_t ), + .axi_resp_t (axi_mst_resp_t), .NoMstPorts (2 ), .MaxTrans (AxiMaxReads ), .SpillAw (1'b1 ) // Required to break dependency between AW and W channels @@ -800,7 +800,7 @@ module axi_dw_downsizer #( w_req_d.burst_resp = axi_pkg::RESP_OKAY; if (!forward_b_beat_full) begin - if (slv_req_i.aw_valid && slv_req_i.aw.atop[5]) begin // ATOP with an R response + if (slv_req_i.aw_valid && slv_req_i.aw.atop[axi_pkg::ATOP_R_RESP]) begin // ATOP with an R response inject_aw_into_ar_req = 1'b1 ; slv_resp_o.aw_ready = inject_aw_into_ar_gnt; end else begin // Regular AW diff --git a/vendor/pulp-platform/axi/src/axi_dw_upsizer.sv b/vendor/pulp-platform/axi/src/axi_dw_upsizer.sv index 1defebf05c..9908b78603 100644 --- a/vendor/pulp-platform/axi/src/axi_dw_upsizer.sv +++ b/vendor/pulp-platform/axi/src/axi_dw_upsizer.sv @@ -185,8 +185,8 @@ module axi_dw_upsizer #( axi_err_slv #( .AxiIdWidth(AxiIdWidth ), .Resp (axi_pkg::RESP_SLVERR), - .req_t (axi_mst_req_t ), - .resp_t (axi_mst_resp_t ) + .axi_req_t (axi_mst_req_t ), + .axi_resp_t(axi_mst_resp_t ) ) i_axi_err_slv ( .clk_i (clk_i ), .rst_ni (rst_ni ), @@ -213,8 +213,8 @@ module axi_dw_upsizer #( .b_chan_t (b_chan_t ), .ar_chan_t (ar_chan_t ), .r_chan_t (mst_r_chan_t ), - .req_t (axi_mst_req_t ), - .resp_t (axi_mst_resp_t), + .axi_req_t (axi_mst_req_t ), + .axi_resp_t (axi_mst_resp_t), .NoMstPorts (2 ), .MaxTrans (AxiMaxReads ), .SpillAw (1'b1 ) // Required to break dependency between AW and W channels @@ -656,7 +656,7 @@ module axi_dw_upsizer #( w_req_d.w = '0 ; w_req_d.w_valid = 1'b0; - if (slv_req_i.aw_valid && slv_req_i.aw.atop[5]) begin // ATOP with an R response + if (slv_req_i.aw_valid && slv_req_i.aw.atop[axi_pkg::ATOP_R_RESP]) begin // ATOP with an R response inject_aw_into_ar_req = 1'b1 ; slv_resp_o.aw_ready = inject_aw_into_ar_gnt; end else begin // Regular AW diff --git a/vendor/pulp-platform/axi/src/axi_err_slv.sv b/vendor/pulp-platform/axi/src/axi_err_slv.sv index f3c807dc3f..e7719c429b 100644 --- a/vendor/pulp-platform/axi/src/axi_err_slv.sv +++ b/vendor/pulp-platform/axi/src/axi_err_slv.sv @@ -18,20 +18,20 @@ module axi_err_slv #( parameter int unsigned AxiIdWidth = 0, // AXI ID Width - parameter type req_t = logic, // AXI 4 request struct, with atop field - parameter type resp_t = logic, // AXI 4 response struct + parameter type axi_req_t = logic, // AXI 4 request struct, with atop field + parameter type axi_resp_t = logic, // AXI 4 response struct parameter axi_pkg::resp_t Resp = axi_pkg::RESP_DECERR, // Error generated by this slave. parameter int unsigned RespWidth = 32'd64, // Data response width, gets zero extended or truncated to r.data. parameter logic [RespWidth-1:0] RespData = 64'hCA11AB1EBADCAB1E, // Hexvalue for data return value parameter bit ATOPs = 1'b1, // Activate support for ATOPs. Set to 1 if this slave could ever get an atomic AXI transaction. parameter int unsigned MaxTrans = 1 // Maximum # of accepted transactions before stalling ) ( - input logic clk_i, // Clock - input logic rst_ni, // Asynchronous reset active low - input logic test_i, // Testmode enable + input logic clk_i, // Clock + input logic rst_ni, // Asynchronous reset active low + input logic test_i, // Testmode enable // slave port - input req_t slv_req_i, - output resp_t slv_resp_o + input axi_req_t slv_req_i, + output axi_resp_t slv_resp_o ); typedef logic [AxiIdWidth-1:0] id_t; typedef struct packed { @@ -39,15 +39,15 @@ module axi_err_slv #( axi_pkg::len_t len; } r_data_t; - req_t err_req; - resp_t err_resp; + axi_req_t err_req; + axi_resp_t err_resp; if (ATOPs) begin axi_atop_filter #( .AxiIdWidth ( AxiIdWidth ), .AxiMaxWriteTxns ( MaxTrans ), - .req_t ( req_t ), - .resp_t ( resp_t ) + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ) ) i_atop_filter ( .clk_i, .rst_ni, diff --git a/vendor/pulp-platform/axi/src/axi_fifo.sv b/vendor/pulp-platform/axi/src/axi_fifo.sv new file mode 100644 index 0000000000..8e4cbdfb11 --- /dev/null +++ b/vendor/pulp-platform/axi/src/axi_fifo.sv @@ -0,0 +1,256 @@ +// Copyright (c) 2014-2022 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: +// - Noah Huetter +// - Florian Zaruba +// - Fabian Schuiki + +// AXI4 Fifo +// +// Can be used to buffer transactions + +module axi_fifo #( + parameter int unsigned Depth = 32'd1, // Number of FiFo slots. + parameter bit FallThrough = 1'b0, // fifos are in fall-through mode + // AXI channel structs + parameter type aw_chan_t = logic, + parameter type w_chan_t = logic, + parameter type b_chan_t = logic, + parameter type ar_chan_t = logic, + parameter type r_chan_t = logic, + // AXI request & response structs + parameter type axi_req_t = logic, + parameter type axi_resp_t = logic +) ( + input logic clk_i, // Clock + input logic rst_ni, // Asynchronous reset active low + input logic test_i, + // slave port + input axi_req_t slv_req_i, + output axi_resp_t slv_resp_o, + // master port + output axi_req_t mst_req_o, + input axi_resp_t mst_resp_i +); + + if (Depth == '0) begin : gen_no_fifo + // degenerate case, connect input to output + assign mst_req_o = slv_req_i; + assign slv_resp_o = mst_resp_i; + end else begin : gen_axi_fifo + logic aw_fifo_empty, ar_fifo_empty, w_fifo_empty, r_fifo_empty, b_fifo_empty; + logic aw_fifo_full, ar_fifo_full, w_fifo_full, r_fifo_full, b_fifo_full; + + assign mst_req_o.aw_valid = ~aw_fifo_empty; + assign mst_req_o.ar_valid = ~ar_fifo_empty; + assign mst_req_o.w_valid = ~w_fifo_empty; + assign slv_resp_o.r_valid = ~r_fifo_empty; + assign slv_resp_o.b_valid = ~b_fifo_empty; + + assign slv_resp_o.aw_ready = ~aw_fifo_full; + assign slv_resp_o.ar_ready = ~ar_fifo_full; + assign slv_resp_o.w_ready = ~w_fifo_full; + assign mst_req_o.r_ready = ~r_fifo_full; + assign mst_req_o.b_ready = ~b_fifo_full; + + // A FiFo for each channel + fifo_v3 #( + .dtype(aw_chan_t), + .DEPTH(Depth), + .FALL_THROUGH(FallThrough) + ) i_aw_fifo ( + .clk_i, + .rst_ni, + .flush_i (1'b0), + .testmode_i(test_i), + .full_o (aw_fifo_full), + .empty_o (aw_fifo_empty), + .usage_o (), + .data_i (slv_req_i.aw), + .push_i (slv_req_i.aw_valid && slv_resp_o.aw_ready), + .data_o (mst_req_o.aw), + .pop_i (mst_req_o.aw_valid && mst_resp_i.aw_ready) + ); + fifo_v3 #( + .dtype(ar_chan_t), + .DEPTH(Depth), + .FALL_THROUGH(FallThrough) + ) i_ar_fifo ( + .clk_i, + .rst_ni, + .flush_i (1'b0), + .testmode_i(test_i), + .full_o (ar_fifo_full), + .empty_o (ar_fifo_empty), + .usage_o (), + .data_i (slv_req_i.ar), + .push_i (slv_req_i.ar_valid && slv_resp_o.ar_ready), + .data_o (mst_req_o.ar), + .pop_i (mst_req_o.ar_valid && mst_resp_i.ar_ready) + ); + fifo_v3 #( + .dtype(w_chan_t), + .DEPTH(Depth), + .FALL_THROUGH(FallThrough) + ) i_w_fifo ( + .clk_i, + .rst_ni, + .flush_i (1'b0), + .testmode_i(test_i), + .full_o (w_fifo_full), + .empty_o (w_fifo_empty), + .usage_o (), + .data_i (slv_req_i.w), + .push_i (slv_req_i.w_valid && slv_resp_o.w_ready), + .data_o (mst_req_o.w), + .pop_i (mst_req_o.w_valid && mst_resp_i.w_ready) + ); + fifo_v3 #( + .dtype(r_chan_t), + .DEPTH(Depth), + .FALL_THROUGH(FallThrough) + ) i_r_fifo ( + .clk_i, + .rst_ni, + .flush_i (1'b0), + .testmode_i(test_i), + .full_o (r_fifo_full), + .empty_o (r_fifo_empty), + .usage_o (), + .data_i (mst_resp_i.r), + .push_i (mst_resp_i.r_valid && mst_req_o.r_ready), + .data_o (slv_resp_o.r), + .pop_i (slv_resp_o.r_valid && slv_req_i.r_ready) + ); + fifo_v3 #( + .dtype(b_chan_t), + .DEPTH(Depth), + .FALL_THROUGH(FallThrough) + ) i_b_fifo ( + .clk_i, + .rst_ni, + .flush_i (1'b0), + .testmode_i(test_i), + .full_o (b_fifo_full), + .empty_o (b_fifo_empty), + .usage_o (), + .data_i (mst_resp_i.b), + .push_i (mst_resp_i.b_valid && mst_req_o.b_ready), + .data_o (slv_resp_o.b), + .pop_i (slv_resp_o.b_valid && slv_req_i.b_ready) + ); + end + + // Check the invariants + // pragma translate_off +`ifndef VERILATOR + initial begin + assert (Depth >= 0); + end +`endif + // pragma translate_on +endmodule + +`include "axi/assign.svh" +`include "axi/typedef.svh" + +// interface wrapper +module axi_fifo_intf #( + parameter int unsigned ADDR_WIDTH = 0, // The address width. + parameter int unsigned DATA_WIDTH = 0, // The data width. + parameter int unsigned ID_WIDTH = 0, // The ID width. + parameter int unsigned USER_WIDTH = 0, // The user data width. + parameter int unsigned DEPTH = 0, // The number of FiFo slots. + parameter int unsigned FALL_THROUGH = 0 // FiFo in fall-through mode +) ( + input logic clk_i, + input logic rst_ni, + input logic test_i, + AXI_BUS.Slave slv, + AXI_BUS.Master mst +); + + typedef logic [ID_WIDTH-1:0] id_t; + typedef logic [ADDR_WIDTH-1:0] addr_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(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 slv_req, mst_req; + axi_resp_t slv_resp, mst_resp; + + `AXI_ASSIGN_TO_REQ(slv_req, slv) + `AXI_ASSIGN_FROM_RESP(slv, slv_resp) + + `AXI_ASSIGN_FROM_REQ(mst, mst_req) + `AXI_ASSIGN_TO_RESP(mst_resp, mst) + + axi_fifo #( + .Depth (DEPTH), + .FallThrough(FALL_THROUGH), + .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 (axi_req_t), + .axi_resp_t (axi_resp_t) + ) i_axi_fifo ( + .clk_i, + .rst_ni, + .test_i, + .slv_req_i (slv_req), + .slv_resp_o(slv_resp), + .mst_req_o (mst_req), + .mst_resp_i(mst_resp) + ); + + // Check the invariants. + // pragma translate_off +`ifndef VERILATOR + initial begin + assert (ADDR_WIDTH > 0) + else $fatal(1, "Wrong addr width parameter"); + assert (DATA_WIDTH > 0) + else $fatal(1, "Wrong data width parameter"); + assert (ID_WIDTH > 0) + else $fatal(1, "Wrong id width parameter"); + assert (USER_WIDTH > 0) + else $fatal(1, "Wrong user width parameter"); + assert (slv.AXI_ADDR_WIDTH == ADDR_WIDTH) + else $fatal(1, "Wrong interface definition"); + assert (slv.AXI_DATA_WIDTH == DATA_WIDTH) + else $fatal(1, "Wrong interface definition"); + assert (slv.AXI_ID_WIDTH == ID_WIDTH) + else $fatal(1, "Wrong interface definition"); + assert (slv.AXI_USER_WIDTH == USER_WIDTH) + else $fatal(1, "Wrong interface definition"); + assert (mst.AXI_ADDR_WIDTH == ADDR_WIDTH) + else $fatal(1, "Wrong interface definition"); + assert (mst.AXI_DATA_WIDTH == DATA_WIDTH) + else $fatal(1, "Wrong interface definition"); + assert (mst.AXI_ID_WIDTH == ID_WIDTH) + else $fatal(1, "Wrong interface definition"); + assert (mst.AXI_USER_WIDTH == USER_WIDTH) + else $fatal(1, "Wrong interface definition"); + end +`endif + // pragma translate_on +endmodule diff --git a/vendor/pulp-platform/axi/src/axi_from_mem.sv b/vendor/pulp-platform/axi/src/axi_from_mem.sv new file mode 100644 index 0000000000..23ed2d3af9 --- /dev/null +++ b/vendor/pulp-platform/axi/src/axi_from_mem.sv @@ -0,0 +1,124 @@ +// Copyright 2022 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: +// - Christopher Reinwardt +// - Nicole Narr -// Florian Zaruba -// Wolfgang Roenninger +// Authors: +// - Andreas Kurth +// - Wolfgang Roenninger +// - Florian Zaruba `include "common_cells/registers.svh" @@ -238,7 +239,7 @@ module axi_id_remap #( if (slv_req_i.aw_valid) begin // If this is not an ATOP that gives rise to an R response, we can handle it in isolation // on the write direction. - if (!slv_req_i.aw.atop[5]) begin + if (!slv_req_i.aw.atop[axi_pkg::ATOP_R_RESP]) begin // If a burst with the same input ID is already in flight or there are free output IDs: if ((wr_exists && !wr_exists_full) || (!wr_exists && !wr_full)) begin // Determine the output ID: if another in-flight burst had the same input ID, we must @@ -284,13 +285,16 @@ module axi_id_remap #( aw_id_d = wr_push_oup_id; end end - priority casez ({mst_req_o.ar_valid, mst_resp_i.ar_ready, - mst_req_o.aw_valid, mst_resp_i.aw_ready}) - 4'b1010: state_d = HoldAx; - 4'b10??: state_d = HoldAR; - 4'b??10: state_d = HoldAW; - default: state_d = Ready; - endcase + if ({mst_req_o.ar_valid, mst_resp_i.ar_ready, + mst_req_o.aw_valid, mst_resp_i.aw_ready} == 4'b1010) begin + state_d = HoldAx; + end else if ({mst_req_o.ar_valid, mst_resp_i.ar_ready} == 2'b10) begin + state_d = HoldAR; + end else if ({mst_req_o.aw_valid, mst_resp_i.aw_ready} == 2'b10) begin + state_d = HoldAW; + end else begin + state_d = Ready; + end if (mst_req_o.ar_valid && mst_resp_i.ar_ready) begin ar_prio_d = 1'b0; // Reset AR priority, because handshake was successful in this cycle. @@ -378,7 +382,6 @@ module axi_id_remap #( assert ($bits(mst_req_o.ar.id) == AxiMstPortIdWidth); assert ($bits(mst_resp_i.r.id) == AxiMstPortIdWidth); end - `endif default disable iff (!rst_ni); assert property (@(posedge clk_i) slv_req_i.aw_valid && slv_resp_o.aw_ready |-> mst_req_o.aw_valid && mst_resp_i.aw_ready); @@ -394,6 +397,7 @@ module axi_id_remap #( |=> mst_req_o.ar_valid && $stable(mst_req_o.ar.id)); assert property (@(posedge clk_i) mst_req_o.aw_valid && !mst_resp_i.aw_ready |=> mst_req_o.aw_valid && $stable(mst_req_o.aw.id)); + `endif // pragma translate_on endmodule diff --git a/vendor/pulp-platform/axi/src/axi_id_serialize.sv b/vendor/pulp-platform/axi/src/axi_id_serialize.sv index 2ed43458de..b3d28e8390 100644 --- a/vendor/pulp-platform/axi/src/axi_id_serialize.sv +++ b/vendor/pulp-platform/axi/src/axi_id_serialize.sv @@ -10,8 +10,7 @@ // specific language governing permissions and limitations under the License. // // Authors: -// - Wolfgang Roenninger -// - Andreas Kurth +// - Andreas Kurth `include "axi/assign.svh" `include "axi/typedef.svh" @@ -47,6 +46,8 @@ module axi_id_serialize #( parameter int unsigned AxiDataWidth = 32'd0, /// User width of both AXI4+ATOP ports parameter int unsigned AxiUserWidth = 32'd0, + /// Enable support for AXI4+ATOP atomics + parameter bit AtopSupport = 1'b1, /// Request struct type of the AXI4+ATOP slave port parameter type slv_req_t = logic, /// Response struct type of the AXI4+ATOP slave port @@ -156,12 +157,12 @@ module axi_id_serialize #( .b_chan_t ( slv_b_t ), .ar_chan_t ( slv_ar_t ), .r_chan_t ( slv_r_t ), - .req_t ( slv_req_t ), - .resp_t ( slv_resp_t ), + .axi_req_t ( slv_req_t ), + .axi_resp_t ( slv_resp_t ), .NoMstPorts ( AxiMstPortMaxUniqIds ), .MaxTrans ( AxiSlvPortMaxTxns ), .AxiLookBits ( AxiSlvPortIdWidth ), - .FallThrough ( 1'b1 ), + .AtopSupport ( AtopSupport ), .SpillAw ( 1'b1 ), .SpillW ( 1'b0 ), .SpillB ( 1'b0 ), @@ -189,8 +190,8 @@ module axi_id_serialize #( .MaxReadTxns ( AxiMstPortMaxTxnsPerId ), .MaxWriteTxns ( AxiMstPortMaxTxnsPerId ), .AxiIdWidth ( AxiSlvPortIdWidth ), - .req_t ( slv_req_t ), - .resp_t ( slv_resp_t ) + .axi_req_t ( slv_req_t ), + .axi_resp_t ( slv_resp_t ) ) i_axi_serializer ( .clk_i, .rst_ni, diff --git a/vendor/pulp-platform/axi/src/axi_intf.sv b/vendor/pulp-platform/axi/src/axi_intf.sv index 0d43ffbae4..c0257f21c7 100644 --- a/vendor/pulp-platform/axi/src/axi_intf.sv +++ b/vendor/pulp-platform/axi/src/axi_intf.sv @@ -98,6 +98,14 @@ interface AXI_BUS #( output r_id, r_data, r_resp, r_last, r_user, r_valid, input r_ready ); + modport Monitor ( + input aw_id, aw_addr, aw_len, aw_size, aw_burst, aw_lock, aw_cache, aw_prot, aw_qos, aw_region, aw_atop, aw_user, aw_valid, aw_ready, + w_data, w_strb, w_last, w_user, w_valid, w_ready, + b_id, b_resp, b_user, b_valid, b_ready, + ar_id, ar_addr, ar_len, ar_size, ar_burst, ar_lock, ar_cache, ar_prot, ar_qos, ar_region, ar_user, ar_valid, ar_ready, + r_id, r_data, r_resp, r_last, r_user, r_valid, r_ready + ); + endinterface @@ -441,6 +449,14 @@ interface AXI_LITE #( output r_data, r_resp, r_valid, input r_ready ); + modport Monitor ( + input aw_addr, aw_prot, aw_valid, aw_ready, + w_data, w_strb, w_valid, w_ready, + b_resp, b_valid, b_ready, + ar_addr, ar_prot, ar_valid, ar_ready, + r_data, r_resp, r_valid, r_ready + ); + endinterface @@ -499,6 +515,14 @@ interface AXI_LITE_DV #( output r_data, r_resp, r_valid, input r_ready ); + modport Monitor ( + input aw_addr, aw_prot, aw_valid, aw_ready, + w_data, w_strb, w_valid, w_ready, + b_resp, b_valid, b_ready, + ar_addr, ar_prot, ar_valid, ar_ready, + r_data, r_resp, r_valid, r_ready + ); + endinterface diff --git a/vendor/pulp-platform/axi/src/axi_isolate.sv b/vendor/pulp-platform/axi/src/axi_isolate.sv index 1eb484679c..6385b26337 100644 --- a/vendor/pulp-platform/axi/src/axi_isolate.sv +++ b/vendor/pulp-platform/axi/src/axi_isolate.sv @@ -13,37 +13,168 @@ // - Wolfgang Roenninger // - Andreas Kurth -// Description: -// -// This module can isolate the AXI4+ATOPs bus on the master port from the slave port. When the -// isolation is not active, the two ports are directly connected. -// -// This module counts how many open transactions are currently in flight on the read and write -// channels. It is further capable of tracking the amount of open atomic transactions with read -// responses. -// -// The isolation interface has two signals: `isolate_i` and `isolated_o`. When `isolate_i` is -// asserted, all open transactions are gracefully terminated. When no transactions are in flight -// anymore, the `isolated_o` output is asserted. As long as `isolated_o` is asserted, all output -// signals in `mst_req_o` are silenced to `'0`. When isolated, new transactions initiated on the -// slave port are stalled until the isolation is terminated by deasserting `isolate_i`. - +`include "axi/typedef.svh" `include "common_cells/registers.svh" +/// This module can isolate the AXI4+ATOPs bus on the master port from the slave port. When the +/// isolation is not active, the two ports are directly connected. +/// +/// This module counts how many open transactions are currently in flight on the read and write +/// channels. It is further capable of tracking the amount of open atomic transactions with read +/// responses. +/// +/// The isolation interface has two signals: `isolate_i` and `isolated_o`. When `isolate_i` is +/// asserted, all open transactions are gracefully terminated. When no transactions are in flight +/// anymore, the `isolated_o` output is asserted. As long as `isolated_o` is asserted, all output +/// signals in `mst_req_o` are silenced to `'0`. When isolated, new transactions initiated on the +/// slave port are stalled until the isolation is terminated by deasserting `isolate_i`. +/// +/// ## Response +/// +/// If the `TerminateTransaction` parameter is set to `1'b1`, the module will return response errors +/// in case there is an incoming transaction while the module isolates. The data returned on the +/// bus is `1501A7ED` (hexspeak for isolated). +/// +/// If `TerminateTransaction` is set to `1'b0`, the transaction will block indefinitely until the +/// module is de-isolated again. module axi_isolate #( - parameter int unsigned NumPending = 32'd16, // Number of pending requests per channel - parameter type req_t = logic, // AXI request struct definition - parameter type resp_t = logic // AXI response struct definition + /// Maximum number of pending requests per channel + parameter int unsigned NumPending = 32'd16, + /// Gracefully terminate all incoming transactions in case of isolation by returning proper error + /// responses. + parameter bit TerminateTransaction = 1'b0, + /// Support atomic operations (ATOPs) + parameter bit AtopSupport = 1'b1, + /// Address width of all AXI4+ATOP ports + parameter int signed AxiAddrWidth = 32'd0, + /// Data width of all AXI4+ATOP ports + parameter int signed AxiDataWidth = 32'd0, + /// ID width of all AXI4+ATOP ports + parameter int signed AxiIdWidth = 32'd0, + /// User signal width of all AXI4+ATOP ports + parameter int signed AxiUserWidth = 32'd0, + /// Request struct type of all AXI4+ATOP ports + parameter type axi_req_t = logic, + /// Response struct type of all AXI4+ATOP ports + parameter type axi_resp_t = logic ) ( - input logic clk_i, // clock - input logic rst_ni, // reset - input req_t slv_req_i, // slave port request struct - output resp_t slv_resp_o, // slave port response struct - output req_t mst_req_o, // master port request struct - input resp_t mst_resp_i, // master port response struct - input logic isolate_i, // isolate master port from slave port - output logic isolated_o // master port is isolated from slave port + /// Rising-edge clock of all ports + input logic clk_i, + /// Asynchronous reset, active low + input logic rst_ni, + /// Slave port request + input axi_req_t slv_req_i, + /// Slave port response + output axi_resp_t slv_resp_o, + /// Master port request + output axi_req_t mst_req_o, + /// Master port response + input axi_resp_t mst_resp_i, + /// Isolate master port from slave port + input logic isolate_i, + /// Master port is isolated from slave port + output logic isolated_o ); + + typedef logic [AxiIdWidth-1:0] id_t; + typedef logic [AxiAddrWidth-1:0] addr_t; + typedef logic [AxiDataWidth-1:0] data_t; + typedef logic [AxiDataWidth/8-1:0] strb_t; + typedef logic [AxiUserWidth-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_req_t [1:0] demux_req; + axi_resp_t [1:0] demux_rsp; + + if (TerminateTransaction) begin + axi_demux #( + .AxiIdWidth ( AxiIdWidth ), + .AtopSupport ( AtopSupport ), + .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 ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .NoMstPorts ( 2 ), + .MaxTrans ( NumPending ), + // We don't need many bits here as the common case will be to go for the pass-through. + .AxiLookBits ( 1 ), + .UniqueIds ( 1'b0 ), + .SpillAw ( 1'b0 ), + .SpillW ( 1'b0 ), + .SpillB ( 1'b0 ), + .SpillAr ( 1'b0 ), + .SpillR ( 1'b0 ) + ) i_axi_demux ( + .clk_i, + .rst_ni, + .test_i ( 1'b0 ), + .slv_req_i, + .slv_aw_select_i ( isolated_o ), + .slv_ar_select_i ( isolated_o ), + .slv_resp_o, + .mst_reqs_o ( demux_req ), + .mst_resps_i ( demux_rsp ) + ); + + axi_err_slv #( + .AxiIdWidth ( AxiIdWidth ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .Resp ( axi_pkg::RESP_DECERR ), + .RespData ( 'h1501A7ED ), + .ATOPs ( AtopSupport ), + .MaxTrans ( 1 ) + ) i_axi_err_slv ( + .clk_i, + .rst_ni, + .test_i ( 1'b0 ), + .slv_req_i ( demux_req[1] ), + .slv_resp_o ( demux_rsp[1] ) + ); + end else begin + assign demux_req[0] = slv_req_i; + assign slv_resp_o = demux_rsp[0]; + end + + axi_isolate_inner #( + .NumPending ( NumPending ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ) + ) i_axi_isolate ( + .clk_i, + .rst_ni, + .slv_req_i ( demux_req[0] ), + .slv_resp_o ( demux_rsp[0] ), + .mst_req_o, + .mst_resp_i, + .isolate_i, + .isolated_o + ); +endmodule + +module axi_isolate_inner #( + parameter int unsigned NumPending = 32'd16, + parameter type axi_req_t = logic, + parameter type axi_resp_t = logic +) ( + input logic clk_i, + input logic rst_ni, + input axi_req_t slv_req_i, + output axi_resp_t slv_resp_o, + output axi_req_t mst_req_o, + input axi_resp_t mst_resp_i, + input logic isolate_i, + output logic isolated_o +); + // plus 1 in clog for accouning no open transaction, plus one bit for atomic injection localparam int unsigned CounterWidth = $clog2(NumPending + 32'd1) + 32'd1; typedef logic [CounterWidth-1:0] cnt_t; @@ -275,22 +406,26 @@ module axi_isolate #( // pragma translate_on endmodule -`include "axi/typedef.svh" `include "axi/assign.svh" +/// Interface variant of [`axi_isolate`](module.axi_isolate). +/// +/// See the documentation of the main module for the definition of ports and parameters. module axi_isolate_intf #( - parameter int unsigned NUM_PENDING = 32'd16, // Number of pending requests - parameter int unsigned AXI_ID_WIDTH = 32'd0, // AXI ID width - parameter int unsigned AXI_ADDR_WIDTH = 32'd0, // AXI address width - parameter int unsigned AXI_DATA_WIDTH = 32'd0, // AXI data width - parameter int unsigned AXI_USER_WIDTH = 32'd0 // AXI user width + parameter int unsigned NUM_PENDING = 32'd16, + parameter bit TERMINATE_TRANSACTION = 1'b0, + parameter bit ATOP_SUPPORT = 1'b1, + parameter int unsigned AXI_ID_WIDTH = 32'd0, + parameter int unsigned AXI_ADDR_WIDTH = 32'd0, + parameter int unsigned AXI_DATA_WIDTH = 32'd0, + parameter int unsigned AXI_USER_WIDTH = 32'd0 ) ( - input logic clk_i, // clock - input logic rst_ni, // asynchronous reset active low - AXI_BUS.Slave slv, // slave port - AXI_BUS.Master mst, // master port - input logic isolate_i, // isolate master port from slave port - output logic isolated_o // master port is isolated from slave port + input logic clk_i, + input logic rst_ni, + AXI_BUS.Slave slv, + AXI_BUS.Master mst, + input logic isolate_i, + output logic isolated_o ); typedef logic [AXI_ID_WIDTH-1:0] id_t; typedef logic [AXI_ADDR_WIDTH-1:0] addr_t; @@ -305,11 +440,11 @@ module axi_isolate_intf #( `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) + `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) - req_t slv_req, mst_req; - resp_t slv_resp, mst_resp; + axi_req_t slv_req, mst_req; + axi_resp_t slv_resp, mst_resp; `AXI_ASSIGN_TO_REQ(slv_req, slv) `AXI_ASSIGN_FROM_RESP(slv, slv_resp) @@ -318,18 +453,24 @@ module axi_isolate_intf #( `AXI_ASSIGN_TO_RESP(mst_resp, mst) axi_isolate #( - .NumPending ( NUM_PENDING ), // Number of pending requests per channel - .req_t ( req_t ), // AXI request struct definition - .resp_t ( resp_t ) // AXI response struct definition + .NumPending ( NUM_PENDING ), + .TerminateTransaction ( TERMINATE_TRANSACTION ), + .AtopSupport ( ATOP_SUPPORT ), + .AxiAddrWidth ( AXI_ADDR_WIDTH ), + .AxiDataWidth ( AXI_DATA_WIDTH ), + .AxiIdWidth ( AXI_ID_WIDTH ), + .AxiUserWidth ( AXI_USER_WIDTH ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ) ) i_axi_isolate ( - .clk_i, // clock - .rst_ni, // reset - .slv_req_i ( slv_req ), // slave port request struct - .slv_resp_o ( slv_resp ), // slave port response struct - .mst_req_o ( mst_req ), // master port request struct - .mst_resp_i ( mst_resp ), // master port response struct - .isolate_i, // isolate master port from slave port - .isolated_o // master port is isolated from slave port + .clk_i, + .rst_ni, + .slv_req_i ( slv_req ), + .slv_resp_o ( slv_resp ), + .mst_req_o ( mst_req ), + .mst_resp_i ( mst_resp ), + .isolate_i, + .isolated_o ); // pragma translate_off diff --git a/vendor/pulp-platform/axi/src/axi_iw_converter.sv b/vendor/pulp-platform/axi/src/axi_iw_converter.sv index e9c5ebb82f..f29425eb19 100644 --- a/vendor/pulp-platform/axi/src/axi_iw_converter.sv +++ b/vendor/pulp-platform/axi/src/axi_iw_converter.sv @@ -9,9 +9,9 @@ // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. // -// Andreas Kurth -// Florian Zaruba -// Wolfgang Roenninger +// Authors: +// - Andreas Kurth +// - Wolfgang Roenninger `include "axi/typedef.svh" diff --git a/vendor/pulp-platform/axi/src/axi_lfsr.sv b/vendor/pulp-platform/axi/src/axi_lfsr.sv new file mode 100644 index 0000000000..2085a76fee --- /dev/null +++ b/vendor/pulp-platform/axi/src/axi_lfsr.sv @@ -0,0 +1,121 @@ +// Copyright 2022 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: +// - Thomas Benz + +`include "axi/typedef.svh" + +/// AXI4 LFSR Subordinate device. Responds with a pseudo random answer. Serial interface to +/// set the internal state. +module axi_lfsr #( + /// AXI4 Data Width + parameter int unsigned DataWidth = 32'd0, + /// AXI4 Addr Width + parameter int unsigned AddrWidth = 32'd0, + /// AXI4 Id Width + parameter int unsigned IdWidth = 32'd0, + /// AXI4 User Width + parameter int unsigned UserWidth = 32'd0, + /// AXI4 request struct definition + parameter type axi_req_t = logic, + /// AXI4 response struct definition + parameter type axi_rsp_t = logic +)( + /// Rising-edge clock + input logic clk_i, + /// Active-low reset + input logic rst_ni, + /// Testmode + input logic testmode_i, + /// AXI4 request struct + input axi_req_t req_i, + /// AXI4 response struct + output axi_rsp_t rsp_o, + /// Serial shift data in (write) + input logic w_ser_data_i, + /// Serial shift data out (write) + output logic w_ser_data_o, + /// Serial shift enable (write) + input logic w_ser_en_i, + /// Serial shift data in (read) + input logic r_ser_data_i, + /// Serial shift data out (read) + output logic r_ser_data_o, + /// Serial shift enable (read) + input logic r_ser_en_i +); + + /// AXI4 Strobe Width + localparam int unsigned StrbWidth = DataWidth / 8; + + /// Address Type + typedef logic [AddrWidth-1:0] addr_t; + /// Data type + typedef logic [DataWidth-1:0] data_t; + /// Strobe Type + typedef logic [StrbWidth-1:0] strb_t; + + // AXI Lite typedef + `AXI_LITE_TYPEDEF_AW_CHAN_T(axi_lite_aw_chan_t, addr_t) + `AXI_LITE_TYPEDEF_W_CHAN_T(axi_lite_w_chan_t, data_t, strb_t) + `AXI_LITE_TYPEDEF_B_CHAN_T(axi_lite_b_chan_t) + + `AXI_LITE_TYPEDEF_AR_CHAN_T(axi_lite_ar_chan_t, addr_t) + `AXI_LITE_TYPEDEF_R_CHAN_T(axi_lite_r_chan_t, data_t) + + `AXI_LITE_TYPEDEF_REQ_T(axi_lite_req_t, axi_lite_aw_chan_t, axi_lite_w_chan_t, axi_lite_ar_chan_t) + `AXI_LITE_TYPEDEF_RESP_T(axi_lite_rsp_t, axi_lite_b_chan_t, axi_lite_r_chan_t) + + // AXI Lite buses + axi_lite_req_t axi_lite_req; + axi_lite_rsp_t axi_lite_rsp; + + axi_to_axi_lite #( + .AxiAddrWidth ( AddrWidth ), + .AxiDataWidth ( DataWidth ), + .AxiIdWidth ( IdWidth ), + .AxiUserWidth ( UserWidth ), + .AxiMaxWriteTxns ( 'd2 ), // We only have 1 cycle latency; 2 is enough + .AxiMaxReadTxns ( 'd2 ), // We only have 1 cycle latency; 2 is enough + .FallThrough ( 1'b0 ), + .full_req_t ( axi_req_t ), + .full_resp_t ( axi_rsp_t ), + .lite_req_t ( axi_lite_req_t ), + .lite_resp_t ( axi_lite_rsp_t ) + ) i_axi_to_axi_lite ( + .clk_i, + .rst_ni, + .test_i ( testmode_i ), + .slv_req_i ( req_i ), + .slv_resp_o ( rsp_o ), + .mst_req_o ( axi_lite_req ), + .mst_resp_i ( axi_lite_rsp ) + ); + + axi_lite_lfsr #( + .DataWidth ( DataWidth ), + .axi_lite_req_t ( axi_lite_req_t ), + .axi_lite_rsp_t ( axi_lite_rsp_t ) + ) i_axi_lite_lfsr ( + .clk_i, + .rst_ni, + .testmode_i, + .w_ser_data_i, + .w_ser_data_o, + .w_ser_en_i, + .r_ser_data_i, + .r_ser_data_o, + .r_ser_en_i, + .req_i ( axi_lite_req ), + .rsp_o ( axi_lite_rsp ) + ); + +endmodule : axi_lfsr diff --git a/vendor/pulp-platform/axi/src/axi_lite_demux.sv b/vendor/pulp-platform/axi/src/axi_lite_demux.sv index 937f843a1c..5513fb4431 100644 --- a/vendor/pulp-platform/axi/src/axi_lite_demux.sv +++ b/vendor/pulp-platform/axi/src/axi_lite_demux.sv @@ -14,6 +14,12 @@ `include "common_cells/registers.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 + // axi_lite_demux: Demultiplex an AXI4-Lite bus from one slave port to multiple master ports. // The selection signal at the AW and AR channel has to follow the same // stability rules as the corresponding AXI4-Lite channel. @@ -24,8 +30,8 @@ module axi_lite_demux #( parameter type b_chan_t = logic, // AXI4-Lite B channel parameter type ar_chan_t = logic, // AXI4-Lite AR channel parameter type r_chan_t = logic, // AXI4-Lite R channel - parameter type req_t = logic, // AXI4-Lite request struct - parameter type resp_t = logic, // AXI4-Lite response struct + parameter type axi_req_t = logic, // AXI4-Lite request struct + parameter type axi_resp_t = logic, // AXI4-Lite response struct parameter int unsigned NoMstPorts = 32'd0, // Number of instantiated ports parameter int unsigned MaxTrans = 32'd0, // Maximum number of open transactions per channel parameter bit FallThrough = 1'b0, // FIFOs are in fall through mode @@ -37,17 +43,17 @@ module axi_lite_demux #( // Dependent parameters, DO NOT OVERRIDE! parameter type select_t = logic [$clog2(NoMstPorts)-1:0] ) ( - input logic clk_i, - input logic rst_ni, - input logic test_i, + input logic clk_i, + input logic rst_ni, + input logic test_i, // slave port (AXI4-Lite input), connect master module here - input req_t slv_req_i, - input select_t slv_aw_select_i, - input select_t slv_ar_select_i, - output resp_t slv_resp_o, + 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 (AXI4-Lite outputs), connect slave modules here - output req_t [NoMstPorts-1:0] mst_reqs_o, - input resp_t [NoMstPorts-1:0] mst_resps_i + output axi_req_t [NoMstPorts-1:0] mst_reqs_o, + input axi_resp_t [NoMstPorts-1:0] mst_resps_i ); //-------------------------------------- @@ -64,9 +70,72 @@ module axi_lite_demux #( if (NoMstPorts == 32'd1) begin : gen_no_demux // degenerate case, connect slave to master port - // AW channel - assign mst_reqs_o[0] = slv_req_i; - assign slv_resp_o = mst_resps_i[0]; + 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 ) + ); + end else begin : gen_demux // normal non degenerate case @@ -123,9 +192,15 @@ module axi_lite_demux #( //-------------------------------------- // AW Channel //-------------------------------------- - // Workaround for bug in Questa 2021.1: Flatten the struct into a logic vector before + `ifdef TARGET_VSIM + // Workaround for bug in Questa 2020.2 and 2021.1: Flatten the struct into a logic vector before // instantiating `spill_register`. typedef logic [$bits(aw_chan_select_t)-1:0] aw_chan_select_flat_t; + `else + // Other tools, such as VCS, have problems with `$bits()`, so the workaround cannot be used + // generally. + typedef aw_chan_select_t aw_chan_select_flat_t; + `endif aw_chan_select_flat_t slv_aw_chan_select_in_flat, slv_aw_chan_select_out_flat; assign slv_aw_chan_select_in_flat = {slv_req_i.aw, slv_aw_select_i}; @@ -282,9 +357,12 @@ module axi_lite_demux #( //-------------------------------------- // AR Channel //-------------------------------------- - // Workaround for bug in Questa 2021.1: Flatten the struct into a logic vector before - // instantiating `spill_register`. + // Workaround for bug in Questa (see comments on AW channel for details). + `ifdef TARGET_VSIM typedef logic [$bits(ar_chan_select_t)-1:0] ar_chan_select_flat_t; + `else + typedef ar_chan_select_t ar_chan_select_flat_t; + `endif ar_chan_select_flat_t slv_ar_chan_select_in_flat, slv_ar_chan_select_out_flat; assign slv_ar_chan_select_in_flat = {slv_req_i.ar, slv_ar_select_i}; @@ -348,8 +426,15 @@ module axi_lite_demux #( ); // connect the response if the FIFO has valid data in it - assign slv_r_chan = (!r_fifo_empty) ? mst_resps_i[r_select].r : '0; - assign slv_r_valid = ~r_fifo_empty & mst_resps_i[r_select].r_valid; + always_comb begin + slv_r_chan = '0; + slv_r_valid = '0; + if (!r_fifo_empty) begin + slv_r_chan = mst_resps_i[r_select].r; + slv_r_valid = mst_resps_i[r_select].r_valid; + end + end + for (genvar i = 0; i < NoMstPorts; i++) begin : gen_mst_r assign mst_reqs_o[i].r_ready = ~r_fifo_empty & slv_r_ready & (r_select == select_t'(i)); end @@ -425,13 +510,13 @@ module axi_lite_demux_intf #( `AXI_LITE_TYPEDEF_B_CHAN_T(b_chan_t) `AXI_LITE_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t) `AXI_LITE_TYPEDEF_R_CHAN_T(r_chan_t, data_t) - `AXI_LITE_TYPEDEF_REQ_T(req_t, aw_chan_t, w_chan_t, ar_chan_t) - `AXI_LITE_TYPEDEF_RESP_T(resp_t, b_chan_t, r_chan_t) + `AXI_LITE_TYPEDEF_REQ_T(axi_req_t, aw_chan_t, w_chan_t, ar_chan_t) + `AXI_LITE_TYPEDEF_RESP_T(axi_resp_t, b_chan_t, r_chan_t) - req_t slv_req; - resp_t slv_resp; - req_t [NoMstPorts-1:0] mst_reqs; - resp_t [NoMstPorts-1:0] mst_resps; + axi_req_t slv_req; + axi_resp_t slv_resp; + axi_req_t [NoMstPorts-1:0] mst_reqs; + axi_resp_t [NoMstPorts-1:0] mst_resps; `AXI_LITE_ASSIGN_TO_REQ(slv_req, slv) `AXI_LITE_ASSIGN_FROM_RESP(slv, slv_resp) @@ -442,13 +527,13 @@ module axi_lite_demux_intf #( end axi_lite_demux #( - .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 ), - .req_t ( req_t ), - .resp_t ( resp_t ), + .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 ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), .NoMstPorts ( NoMstPorts ), .MaxTrans ( MaxTrans ), .FallThrough ( FallThrough ), diff --git a/vendor/pulp-platform/axi/src/axi_lite_from_mem.sv b/vendor/pulp-platform/axi/src/axi_lite_from_mem.sv new file mode 100644 index 0000000000..6b5446c6b0 --- /dev/null +++ b/vendor/pulp-platform/axi/src/axi_lite_from_mem.sv @@ -0,0 +1,246 @@ +// Copyright 2022 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 + +/// Protocol adapter which translates memory requests to the AXI4-Lite protocol. +/// +/// This module acts like an SRAM and makes AXI4-Lite requests downstream. +/// +/// Supports multiple outstanding requests and will have responses for reads **and** writes. +/// Response latency is not fixed and for sure **not 1** and depends on the AXI4-Lite memory system. +/// The `mem_rsp_valid_o` can have multiple cycles of latency from the corresponding `mem_gnt_o`. +/// (Was called `mem_to_axi_lite` - originating from https://github.com/pulp-platform/snitch) +module axi_lite_from_mem #( + /// Memory request address width. + parameter int unsigned MemAddrWidth = 32'd0, + /// AXI4-Lite address width. + parameter int unsigned AxiAddrWidth = 32'd0, + /// Data width in bit of the memory request data **and** the Axi4-Lite data channels. + parameter int unsigned DataWidth = 32'd0, + /// How many requests can be in flight at the same time. (Depth of the response mux FIFO). + parameter int unsigned MaxRequests = 32'd0, + /// Protection signal the module should emit on the AXI4-Lite transactions. + parameter axi_pkg::prot_t AxiProt = 3'b000, + /// AXI4-Lite request struct definition. + parameter type axi_req_t = logic, + /// AXI4-Lite response struct definition. + parameter type axi_rsp_t = logic, + /// Dependent parameter do **not** overwrite! + /// + /// Memory address type, derived from `MemAddrWidth`. + parameter type mem_addr_t = logic[MemAddrWidth-1:0], + /// Dependent parameter do **not** overwrite! + /// + /// AXI4-Lite address type, derived from `AxiAddrWidth`. + parameter type axi_addr_t = logic[AxiAddrWidth-1:0], + /// Dependent parameter do **not** overwrite! + /// + /// Data type for read and write data, derived from `DataWidth`. + /// This is the same for the memory request side **and** the AXI4-Lite `W` and `R` channels. + parameter type data_t = logic[DataWidth-1:0], + /// Dependent parameter do **not** overwrite! + /// + /// Byte enable / AXI4-Lite strobe type, derived from `DataWidth`. + parameter type strb_t = logic[DataWidth/8-1:0] +) ( + /// Clock input, positive edge triggered. + input logic clk_i, + /// Asynchronous reset, active low. + input logic rst_ni, + /// Memory slave port, request is active. + input logic mem_req_i, + /// Memory slave port, request address. + /// + /// Byte address, will be extended or truncated to match `AxiAddrWidth`. + input mem_addr_t mem_addr_i, + /// Memory slave port, request is a write. + /// + /// `0`: Read request. + /// `1`: Write request. + input logic mem_we_i, + /// Memory salve port, write data for request. + input data_t mem_wdata_i, + /// Memory slave port, write byte enable for request. + /// + /// Active high. + input strb_t mem_be_i, + /// Memory request is granted. + output logic mem_gnt_o, + /// Memory slave port, response is valid. For each request, regardless if read or write, + /// this will be active once for one cycle. + output logic mem_rsp_valid_o, + /// Memory slave port, response read data. This is forwarded directly from the AXI4-Lite + /// `R` channel. Only valid for responses generated by a read request. + output data_t mem_rsp_rdata_o, + /// Memory request encountered an error. This is forwarded from the AXI4-Lite error response. + output logic mem_rsp_error_o, + /// AXI4-Lite master port, request output. + output axi_req_t axi_req_o, + /// AXI4-Lite master port, response input. + input axi_rsp_t axi_rsp_i +); + `include "common_cells/registers.svh" + + // Response FIFO control signals. + logic fifo_full, fifo_empty; + // Bookkeeping for sent write beats. + logic aw_sent_q, aw_sent_d; + logic w_sent_q, w_sent_d; + + // Control for translating request to the AXI4-Lite `AW`, `W` and `AR` channels. + always_comb begin + // Default assignments. + axi_req_o.aw = '0; + axi_req_o.aw.addr = axi_addr_t'(mem_addr_i); + axi_req_o.aw.prot = AxiProt; + axi_req_o.aw_valid = 1'b0; + axi_req_o.w = '0; + axi_req_o.w.data = mem_wdata_i; + axi_req_o.w.strb = mem_be_i; + axi_req_o.w_valid = 1'b0; + axi_req_o.ar = '0; + axi_req_o.ar.addr = axi_addr_t'(mem_addr_i); + axi_req_o.ar.prot = AxiProt; + axi_req_o.ar_valid = 1'b0; + // This is also the push signal for the response FIFO. + mem_gnt_o = 1'b0; + // Bookkeeping about sent write channels. + aw_sent_d = aw_sent_q; + w_sent_d = w_sent_q; + + // Control for Request to AXI4-Lite translation. + if (mem_req_i && !fifo_full) begin + if (!mem_we_i) begin + // It is a read request. + axi_req_o.ar_valid = 1'b1; + mem_gnt_o = axi_rsp_i.ar_ready; + end else begin + // Is is a write request, decouple `AW` and `W` channels. + unique case ({aw_sent_q, w_sent_q}) + 2'b00 : begin + // None of the AXI4-Lite writes have been sent jet. + axi_req_o.aw_valid = 1'b1; + axi_req_o.w_valid = 1'b1; + unique case ({axi_rsp_i.aw_ready, axi_rsp_i.w_ready}) + 2'b01 : begin // W is sent, still needs AW. + w_sent_d = 1'b1; + end + 2'b10 : begin // AW is sent, still needs W. + aw_sent_d = 1'b1; + end + 2'b11 : begin // Both are transmitted, grant the write request. + mem_gnt_o = 1'b1; + end + default : /* do nothing */; + endcase + end + 2'b10 : begin + // W has to be sent. + axi_req_o.w_valid = 1'b1; + if (axi_rsp_i.w_ready) begin + aw_sent_d = 1'b0; + mem_gnt_o = 1'b1; + end + end + 2'b01 : begin + // AW has to be sent. + axi_req_o.aw_valid = 1'b1; + if (axi_rsp_i.aw_ready) begin + w_sent_d = 1'b0; + mem_gnt_o = 1'b1; + end + end + default : begin + // Failsafe go to IDLE. + aw_sent_d = 1'b0; + w_sent_d = 1'b0; + end + endcase + end + end + end + + `FFARN(aw_sent_q, aw_sent_d, 1'b0, clk_i, rst_ni) + `FFARN(w_sent_q, w_sent_d, 1'b0, clk_i, rst_ni) + + // Select which response should be forwarded. `1` write response, `0` read response. + logic rsp_sel; + + fifo_v3 #( + .FALL_THROUGH ( 1'b0 ), // No fallthrough for one cycle delay before ready on AXI. + .DEPTH ( MaxRequests ), + .dtype ( logic ) + ) i_fifo_rsp_mux ( + .clk_i, + .rst_ni, + .flush_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .full_o ( fifo_full ), + .empty_o ( fifo_empty ), + .usage_o ( /*not used*/ ), + .data_i ( mem_we_i ), + .push_i ( mem_gnt_o ), + .data_o ( rsp_sel ), + .pop_i ( mem_rsp_valid_o ) + ); + + // Response selection control. + // If something is in the FIFO, the corresponding channel is ready. + assign axi_req_o.b_ready = !fifo_empty && rsp_sel; + assign axi_req_o.r_ready = !fifo_empty && !rsp_sel; + // Read data is directly forwarded. + assign mem_rsp_rdata_o = axi_rsp_i.r.data; + // Error is taken from the respective channel. + assign mem_rsp_error_o = rsp_sel ? + (axi_rsp_i.b.resp inside {axi_pkg::RESP_SLVERR, axi_pkg::RESP_DECERR}) : + (axi_rsp_i.r.resp inside {axi_pkg::RESP_SLVERR, axi_pkg::RESP_DECERR}); + // Mem response is valid if the handshaking on the respective channel occurs. + // Can not happen at the same time as ready is set from the FIFO. + // This serves as the pop signal for the FIFO. + assign mem_rsp_valid_o = (axi_rsp_i.b_valid && axi_req_o.b_ready) || + (axi_rsp_i.r_valid && axi_req_o.r_ready); + + // pragma translate_off + `ifndef SYNTHESIS + `ifndef VERILATOR + initial begin : proc_assert + assert (MemAddrWidth > 32'd0) else $fatal(1, "MemAddrWidth has to be greater than 0!"); + assert (AxiAddrWidth > 32'd0) else $fatal(1, "AxiAddrWidth has to be greater than 0!"); + assert (DataWidth inside {32'd32, 32'd64}) else + $fatal(1, "DataWidth has to be either 32 or 64 bit!"); + assert (MaxRequests > 32'd0) else $fatal(1, "MaxRequests has to be greater than 0!"); + assert (AxiAddrWidth == $bits(axi_req_o.aw.addr)) else + $fatal(1, "AxiAddrWidth has to match axi_req_o.aw.addr!"); + assert (AxiAddrWidth == $bits(axi_req_o.ar.addr)) else + $fatal(1, "AxiAddrWidth has to match axi_req_o.ar.addr!"); + assert (DataWidth == $bits(axi_req_o.w.data)) else + $fatal(1, "DataWidth has to match axi_req_o.w.data!"); + assert (DataWidth/8 == $bits(axi_req_o.w.strb)) else + $fatal(1, "DataWidth / 8 has to match axi_req_o.w.strb!"); + assert (DataWidth == $bits(axi_rsp_i.r.data)) else + $fatal(1, "DataWidth has to match axi_rsp_i.r.data!"); + end + default disable iff (~rst_ni); + assert property (@(posedge clk_i) (mem_req_i && !mem_gnt_o) |=> mem_req_i) else + $fatal(1, "It is not allowed to deassert the request if it was not granted!"); + assert property (@(posedge clk_i) (mem_req_i && !mem_gnt_o) |=> $stable(mem_addr_i)) else + $fatal(1, "mem_addr_i has to be stable if request is not granted!"); + assert property (@(posedge clk_i) (mem_req_i && !mem_gnt_o) |=> $stable(mem_we_i)) else + $fatal(1, "mem_we_i has to be stable if request is not granted!"); + assert property (@(posedge clk_i) (mem_req_i && !mem_gnt_o) |=> $stable(mem_wdata_i)) else + $fatal(1, "mem_wdata_i has to be stable if request is not granted!"); + assert property (@(posedge clk_i) (mem_req_i && !mem_gnt_o) |=> $stable(mem_be_i)) else + $fatal(1, "mem_be_i has to be stable if request is not granted!"); + `endif + `endif + // pragma translate_on +endmodule diff --git a/vendor/pulp-platform/axi/src/axi_lite_lfsr.sv b/vendor/pulp-platform/axi/src/axi_lite_lfsr.sv new file mode 100644 index 0000000000..bf8e0f5e52 --- /dev/null +++ b/vendor/pulp-platform/axi/src/axi_lite_lfsr.sv @@ -0,0 +1,223 @@ +// Copyright 2022 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: +// - Thomas Benz + +`include "common_cells/registers.svh" + +/// AXI4 Lite LFSR Subordinate device. Responds with a pseudo random answer. Serial interface to +/// set the internal state. +module axi_lite_lfsr #( + /// AXI4 Lite Data Width + parameter int unsigned DataWidth = 32'd0, + /// AXI4 Lite request struct definition + parameter type axi_lite_req_t = logic, + /// AXI4 Lite response struct definition + parameter type axi_lite_rsp_t = logic +)( + /// Rising-edge clock + input logic clk_i, + /// Active-low reset + input logic rst_ni, + /// Testmode + input logic testmode_i, + /// AXI4 Lite request struct + input axi_lite_req_t req_i, + /// AXI4 Lite response struct + output axi_lite_rsp_t rsp_o, + /// Serial shift data in (write) + input logic w_ser_data_i, + /// Serial shift data out (write) + output logic w_ser_data_o, + /// Serial shift enable (write) + input logic w_ser_en_i, + /// Serial shift data in (read) + input logic r_ser_data_i, + /// Serial shift data out (read) + output logic r_ser_data_o, + /// Serial shift enable (read) + input logic r_ser_en_i +); + + /// AXI4 Strobe Width + localparam int unsigned StrbWidth = DataWidth / 8; + + logic w_lfsr_en; + logic r_lfsr_en; + + logic w_b_fifo_ready; + + // LFSR outputs + logic [DataWidth-1:0] w_data_in, w_data_out; + + // AW (ignored) + assign rsp_o.aw_ready = !w_ser_en_i; + + // W + axi_opt_lfsr #( + .Width ( DataWidth ) + ) i_axi_opt_lfsr_w ( + .clk_i, + .rst_ni, + .en_i ( w_lfsr_en ), + .ser_data_i ( w_ser_data_i ), + .ser_data_o ( w_ser_data_o ), + .ser_en_i ( w_ser_en_i ), + .inp_en_i ( w_lfsr_en ), + .data_i ( w_data_in ), + .data_o ( w_data_out ) + ); + assign w_lfsr_en = req_i.w_valid & rsp_o.w_ready; + assign rsp_o.w_ready = !w_ser_en_i & w_b_fifo_ready; + + // only write bytes with strobe signal enabled + always_comb begin : gen_data_strb_connect + for (int unsigned i = 0; i < StrbWidth; i++) begin : gen_strb_en + if (req_i.w.strb[i] == 1'b0) begin + w_data_in[i*8+:8] = w_data_out[i*8+:8]; + end else if (req_i.w.strb[i] == 1'b1) begin + w_data_in[i*8+:8] = req_i.w.data[i*8+:8]; + end else begin + w_data_in[i*8+:8] = 'x; + end + end + end + + // B + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DATA_WIDTH ( 'd1 ), + .DEPTH ( 'd2 ) + ) i_stream_fifo_w_b ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( 1'b0 ), + .usage_o ( /* NOT CONNECTED */ ), + .data_i ( 1'b0 ), + .valid_i ( req_i.w_valid ), + .ready_o ( w_b_fifo_ready ), + .data_o ( /* NOT CONNECTED */ ), + .valid_o ( w_b_fifo_valid ), + .ready_i ( req_i.b_ready ) + ); + assign rsp_o.b.resp = axi_pkg::RESP_OKAY; + assign rsp_o.b_valid = w_b_fifo_valid; + + // AR (ignored) + assign rsp_o.ar_ready = !w_ser_en_i; + + // R + axi_opt_lfsr #( + .Width ( DataWidth ) + ) i_axi_opt_lfsr_r ( + .clk_i, + .rst_ni, + .en_i ( r_lfsr_en ), + .ser_data_i ( r_ser_data_i ), + .ser_data_o ( r_ser_data_o ), + .ser_en_i ( r_ser_en_i ), + .inp_en_i ( 1'b0 ), + .data_i ( /* NOT CONNECTED */ ), + .data_o ( rsp_o.r.data ) + ); + assign rsp_o.r.resp = axi_pkg::RESP_OKAY; + assign r_lfsr_en = req_i.r_ready & rsp_o.r_valid; + assign rsp_o.r_valid = !r_ser_en_i; + +endmodule : axi_lite_lfsr + + +/// XOR LFSR with tabs based on the [lfsr_table](https://datacipy.cz/lfsr_table.pdf). LFSR has +/// a serial interface to set the initial state +module axi_opt_lfsr #( + parameter int unsigned Width = 32'd0 +) ( + /// Rising-edge clock + input logic clk_i, + /// Active-low reset + input logic rst_ni, + input logic en_i, + input logic ser_data_i, + output logic ser_data_o, + input logic ser_en_i, + input logic inp_en_i, + input logic [Width-1:0] data_i, + output logic [Width-1:0] data_o +); + + /// Number of bits required to hold the LFSR tab configuration + localparam int unsigned LfsrIdxWidth = cf_math_pkg::idx_width(Width); + /// Maximum number of tabs + localparam int unsigned MaxNumTabs = 4; + + /// Type specifying the tap positions + typedef logic [LfsrIdxWidth:0] xnor_entry_t [MaxNumTabs-1:0]; + xnor_entry_t XnorFeedback; + + // the shift register + logic [Width-1:0] reg_d, reg_q; + + // the feedback signal + logic xnor_feedback; + + always_comb begin : gen_register + + // get the parameters + case (Width) + 'd8 : XnorFeedback = { 'd8, 'd6, 'd5, 'd4 }; + 'd16 : XnorFeedback = { 'd16, 'd14, 'd13, 'd11 }; + 'd32 : XnorFeedback = { 'd32, 'd30, 'd26, 'd25 }; + 'd64 : XnorFeedback = { 'd64, 'd63, 'd61, 'd60 }; + 'd128 : XnorFeedback = { 'd128, 'd127, 'd126, 'd119 }; + 'd256 : XnorFeedback = { 'd256, 'd256, 'd521, 'd246 }; + 'd512 : XnorFeedback = { 'd512, 'd510, 'd507, 'd504 }; + 'd1024 : XnorFeedback = { 'd1024, 'd1015, 'd1002, 'd1001 }; + default : XnorFeedback = { 'x, 'x, 'x, 'x }; + endcase + + // shift register functionality + // compression mode + if (inp_en_i) begin + for (int unsigned i = 0; i < Width - 1; i++) begin : gen_comp_conection + reg_d[i] = reg_q[i+1] ^ data_i[i]; + end + // generation mode + end else begin + for (int unsigned i = 0; i < Width - 1; i++) begin : gen_gen_conection + reg_d[i] = reg_q[i+1]; + end + end + // serial access mode + if (ser_en_i) begin + // new head element + reg_d[Width-1] = ser_data_i; + // LFSR mode + end else begin + xnor_feedback = reg_q[XnorFeedback[MaxNumTabs-1]-1]; + for (int unsigned t = 0; t < MaxNumTabs - 1; t++) begin : gen_feedback_path + xnor_feedback = xnor_feedback; + if (XnorFeedback[t] != 0) begin + xnor_feedback = xnor_feedback ^ reg_q[XnorFeedback[t]-1]; + end + end + reg_d[Width-1] = inp_en_i ? xnor_feedback ^ data_i[Width-1] : xnor_feedback; + end + end + + // connect outputs + assign ser_data_o = reg_q[0]; + assign data_o = reg_q; + + // state + `FFL(reg_q, reg_d, en_i | ser_en_i, '1, clk_i, rst_ni) + +endmodule : axi_opt_lfsr diff --git a/vendor/pulp-platform/axi/src/axi_lite_mux.sv b/vendor/pulp-platform/axi/src/axi_lite_mux.sv index c115e7888c..5772802790 100644 --- a/vendor/pulp-platform/axi/src/axi_lite_mux.sv +++ b/vendor/pulp-platform/axi/src/axi_lite_mux.sv @@ -27,8 +27,8 @@ module axi_lite_mux #( parameter type b_chan_t = logic, // B LITE Channel Type parameter type ar_chan_t = logic, // AR LITE Channel Type parameter type r_chan_t = logic, // R LITE Channel Type - parameter type req_t = logic, // AXI4-Lite request type - parameter type resp_t = logic, // AXI4-Lite response type + parameter type axi_req_t = logic, // AXI4-Lite request type + parameter type axi_resp_t = logic, // AXI4-Lite response type parameter int unsigned NoSlvPorts = 32'd0, // Number of slave ports // Maximum number of outstanding transactions per write or read parameter int unsigned MaxTrans = 32'd0, @@ -42,20 +42,84 @@ module axi_lite_mux #( parameter bit SpillAr = 1'b1, parameter bit SpillR = 1'b0 ) ( - input logic clk_i, // Clock - input logic rst_ni, // Asynchronous reset active low - input logic test_i, // Test Mode enable + input logic clk_i, // Clock + input logic rst_ni, // Asynchronous reset active low + input logic test_i, // Test Mode enable // slave ports (AXI4-Lite inputs), connect master modules here - input req_t [NoSlvPorts-1:0] slv_reqs_i, - output resp_t [NoSlvPorts-1:0] slv_resps_o, + input axi_req_t [NoSlvPorts-1:0] slv_reqs_i, + output axi_resp_t [NoSlvPorts-1:0] slv_resps_o, // master port (AXI4-Lite output), connect slave module here - output req_t mst_req_o, - input resp_t mst_resp_i + output axi_req_t mst_req_o, + input axi_resp_t mst_resp_i ); // pass through if only one slave port if (NoSlvPorts == 32'h1) begin : gen_no_mux - assign mst_req_o = slv_reqs_i[0]; - assign slv_resps_o[0] = mst_resp_i; + spill_register #( + .T ( aw_chan_t ), + .Bypass ( ~SpillAw ) + ) i_aw_spill_reg ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .valid_i ( slv_reqs_i[0].aw_valid ), + .ready_o ( slv_resps_o[0].aw_ready ), + .data_i ( slv_reqs_i[0].aw ), + .valid_o ( mst_req_o.aw_valid ), + .ready_i ( mst_resp_i.aw_ready ), + .data_o ( mst_req_o.aw ) + ); + spill_register #( + .T ( w_chan_t ), + .Bypass ( ~SpillW ) + ) i_w_spill_reg ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .valid_i ( slv_reqs_i[0].w_valid ), + .ready_o ( slv_resps_o[0].w_ready ), + .data_i ( slv_reqs_i[0].w ), + .valid_o ( mst_req_o.w_valid ), + .ready_i ( mst_resp_i.w_ready ), + .data_o ( mst_req_o.w ) + ); + spill_register #( + .T ( b_chan_t ), + .Bypass ( ~SpillB ) + ) i_b_spill_reg ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .valid_i ( mst_resp_i.b_valid ), + .ready_o ( mst_req_o.b_ready ), + .data_i ( mst_resp_i.b ), + .valid_o ( slv_resps_o[0].b_valid ), + .ready_i ( slv_reqs_i[0].b_ready ), + .data_o ( slv_resps_o[0].b ) + ); + spill_register #( + .T ( ar_chan_t ), + .Bypass ( ~SpillAr ) + ) i_ar_spill_reg ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .valid_i ( slv_reqs_i[0].ar_valid ), + .ready_o ( slv_resps_o[0].ar_ready ), + .data_i ( slv_reqs_i[0].ar ), + .valid_o ( mst_req_o.ar_valid ), + .ready_i ( mst_resp_i.ar_ready ), + .data_o ( mst_req_o.ar ) + ); + spill_register #( + .T ( r_chan_t ), + .Bypass ( ~SpillR ) + ) i_r_spill_reg ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .valid_i ( mst_resp_i.r_valid ), + .ready_o ( mst_req_o.r_ready ), + .data_i ( mst_resp_i.r ), + .valid_o ( slv_resps_o[0].r_valid ), + .ready_i ( slv_reqs_i[0].r_ready ), + .data_o ( slv_resps_o[0].r ) + ); + // other non degenerate cases end else begin : gen_mux // typedef for the FIFO types @@ -219,7 +283,7 @@ module axi_lite_mux #( // W Channel //-------------------------------------- // multiplexer - assign mst_w_chan = (!w_fifo_empty && !b_fifo_full) ? slv_reqs_i[w_select].w : '0; + assign mst_w_chan = slv_reqs_i[w_select].w; assign mst_w_valid = (!w_fifo_empty && !b_fifo_full) ? slv_reqs_i[w_select].w_valid : 1'b0; for (genvar i = 0; i < NoSlvPorts; i++) begin : gen_slv_w_ready assign slv_resps_o[i].w_ready = mst_w_ready & ~w_fifo_empty & @@ -406,11 +470,11 @@ module axi_lite_mux_intf #( parameter bit SpillAr = 1'b1, parameter bit SpillR = 1'b0 ) ( - input logic clk_i, // Clock - input logic rst_ni, // Asynchronous reset active low - input logic test_i, // Testmode enable - AXI_BUS.Slave slv [NoSlvPorts-1:0], // slave ports - AXI_BUS.Master mst // master port + input logic clk_i, // Clock + input logic rst_ni, // Asynchronous reset active low + input logic test_i, // Testmode enable + AXI_LITE.Slave slv [NoSlvPorts-1:0], // slave ports + AXI_LITE.Master mst // master port ); typedef logic [AxiAddrWidth-1:0] addr_t; @@ -422,13 +486,13 @@ module axi_lite_mux_intf #( `AXI_LITE_TYPEDEF_B_CHAN_T(b_chan_t) `AXI_LITE_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t) `AXI_LITE_TYPEDEF_R_CHAN_T(r_chan_t, data_t) - `AXI_LITE_TYPEDEF_REQ_T(req_t, aw_chan_t, w_chan_t, ar_chan_t) - `AXI_LITE_TYPEDEF_RESP_T(resp_t, b_chan_t, r_chan_t) + `AXI_LITE_TYPEDEF_REQ_T(axi_req_t, aw_chan_t, w_chan_t, ar_chan_t) + `AXI_LITE_TYPEDEF_RESP_T(axi_resp_t, b_chan_t, r_chan_t) - req_t [NoSlvPorts-1:0] slv_reqs; - resp_t [NoSlvPorts-1:0] slv_resps; - req_t mst_req; - resp_t mst_resp; + axi_req_t [NoSlvPorts-1:0] slv_reqs; + axi_resp_t [NoSlvPorts-1:0] slv_resps; + axi_req_t mst_req; + axi_resp_t mst_resp; for (genvar i = 0; i < NoSlvPorts; i++) begin : gen_assign_slv_ports `AXI_LITE_ASSIGN_TO_REQ(slv_reqs[i], slv[i]) @@ -444,6 +508,8 @@ module axi_lite_mux_intf #( .b_chan_t ( b_chan_t ), // B Channel Type .ar_chan_t ( ar_chan_t ), // AR Channel Type .r_chan_t ( r_chan_t ), // R Channel Type + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), .NoSlvPorts ( NoSlvPorts ), // Number of slave ports .MaxTrans ( MaxTrans ), .FallThrough ( FallThrough ), diff --git a/vendor/pulp-platform/axi/src/axi_lite_to_axi.sv b/vendor/pulp-platform/axi/src/axi_lite_to_axi.sv index bbeebec10c..f1f59ed02c 100644 --- a/vendor/pulp-platform/axi/src/axi_lite_to_axi.sv +++ b/vendor/pulp-platform/axi/src/axi_lite_to_axi.sv @@ -21,8 +21,8 @@ module axi_lite_to_axi #( parameter type req_lite_t = logic, parameter type resp_lite_t = logic, // FULL AXI structs - parameter type req_t = logic, - parameter type resp_t = logic + parameter type axi_req_t = logic, + parameter type axi_resp_t = logic ) ( // Slave AXI LITE port input req_lite_t slv_req_lite_i, @@ -30,8 +30,8 @@ module axi_lite_to_axi #( input axi_pkg::cache_t slv_aw_cache_i, input axi_pkg::cache_t slv_ar_cache_i, // Master AXI port - output req_t mst_req_o, - input resp_t mst_resp_i + output axi_req_t mst_req_o, + input axi_resp_t mst_resp_i ); localparam int unsigned AxiSize = axi_pkg::size_t'($unsigned($clog2(AxiDataWidth/8))); diff --git a/vendor/pulp-platform/axi/src/axi_lite_xbar.sv b/vendor/pulp-platform/axi/src/axi_lite_xbar.sv index cbacf4ceb6..3c2bb387ce 100644 --- a/vendor/pulp-platform/axi/src/axi_lite_xbar.sv +++ b/vendor/pulp-platform/axi/src/axi_lite_xbar.sv @@ -21,24 +21,24 @@ module axi_lite_xbar #( parameter axi_pkg::xbar_cfg_t Cfg = '0, - parameter type aw_chan_t = logic, - parameter type w_chan_t = logic, - parameter type b_chan_t = logic, - parameter type ar_chan_t = logic, - parameter type r_chan_t = logic, - parameter type req_t = logic, - parameter type resp_t = logic, - parameter type rule_t = axi_pkg::xbar_rule_64_t, + parameter type aw_chan_t = logic, + parameter type w_chan_t = logic, + parameter type b_chan_t = logic, + parameter type ar_chan_t = logic, + parameter type r_chan_t = logic, + parameter type axi_req_t = logic, + parameter type axi_resp_t = logic, + parameter type rule_t = axi_pkg::xbar_rule_64_t, // DEPENDENT PARAMETERS, DO NOT OVERWRITE! parameter int unsigned MstIdxWidth = (Cfg.NoMstPorts > 32'd1) ? $clog2(Cfg.NoMstPorts) : 32'd1 ) ( input logic clk_i, input logic rst_ni, input logic test_i, - input req_t [Cfg.NoSlvPorts-1:0] slv_ports_req_i, - output resp_t [Cfg.NoSlvPorts-1:0] slv_ports_resp_o, - output req_t [Cfg.NoMstPorts-1:0] mst_ports_req_o, - input resp_t [Cfg.NoMstPorts-1:0] mst_ports_resp_i, + input axi_req_t [Cfg.NoSlvPorts-1:0] slv_ports_req_i, + output axi_resp_t [Cfg.NoSlvPorts-1:0] slv_ports_resp_o, + output axi_req_t [Cfg.NoMstPorts-1:0] mst_ports_req_o, + input axi_resp_t [Cfg.NoMstPorts-1:0] mst_ports_resp_i, input rule_t [Cfg.NoAddrRules-1:0] addr_map_i, input logic [Cfg.NoSlvPorts-1:0] en_default_mst_port_i, input logic [Cfg.NoSlvPorts-1:0][MstIdxWidth-1:0] default_mst_port_i @@ -60,12 +60,12 @@ module axi_lite_xbar #( `AXI_TYPEDEF_RESP_T(full_resp_t, full_b_chan_t, full_r_chan_t) // signals from the axi_lite_demuxes, one index more for decode error routing - req_t [Cfg.NoSlvPorts-1:0][Cfg.NoMstPorts:0] slv_reqs; - resp_t [Cfg.NoSlvPorts-1:0][Cfg.NoMstPorts:0] slv_resps; + axi_req_t [Cfg.NoSlvPorts-1:0][Cfg.NoMstPorts:0] slv_reqs; + axi_resp_t [Cfg.NoSlvPorts-1:0][Cfg.NoMstPorts:0] slv_resps; // signals into the axi_lite_muxes, are of type slave as the multiplexer extends the ID - req_t [Cfg.NoMstPorts-1:0][Cfg.NoSlvPorts-1:0] mst_reqs; - resp_t [Cfg.NoMstPorts-1:0][Cfg.NoSlvPorts-1:0] mst_resps; + axi_req_t [Cfg.NoMstPorts-1:0][Cfg.NoSlvPorts-1:0] mst_reqs; + axi_resp_t [Cfg.NoMstPorts-1:0][Cfg.NoSlvPorts-1:0] mst_resps; for (genvar i = 0; i < Cfg.NoSlvPorts; i++) begin : gen_slv_port_demux logic [MstIdxWidth-1:0] dec_aw, dec_ar; @@ -143,8 +143,8 @@ module axi_lite_xbar #( .b_chan_t ( b_chan_t ), // B Channel Type .ar_chan_t ( ar_chan_t ), // AR Channel Type .r_chan_t ( r_chan_t ), // R Channel Type - .req_t ( req_t ), - .resp_t ( resp_t ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), .NoMstPorts ( Cfg.NoMstPorts + 1 ), .MaxTrans ( Cfg.MaxMstTrans ), .FallThrough ( Cfg.FallThrough ), @@ -169,10 +169,10 @@ module axi_lite_xbar #( // typedef as the decode error slave uses full axi axi_lite_to_axi #( .AxiDataWidth ( Cfg.AxiDataWidth ), - .req_lite_t ( req_t ), - .resp_lite_t ( resp_t ), - .req_t ( full_req_t ), - .resp_t ( full_resp_t ) + .req_lite_t ( axi_req_t ), + .resp_lite_t ( axi_resp_t ), + .axi_req_t ( full_req_t ), + .axi_resp_t ( full_resp_t ) ) i_dec_err_conv ( .slv_req_lite_i ( slv_reqs[i][Cfg.NoMstPorts] ), .slv_resp_lite_o ( slv_resps[i][Cfg.NoMstPorts] ), @@ -184,8 +184,8 @@ module axi_lite_xbar #( axi_err_slv #( .AxiIdWidth ( 32'd1 ), // ID width is one as defined as logic above - .req_t ( full_req_t ), // AXI request struct - .resp_t ( full_resp_t ), // AXI response struct + .axi_req_t ( full_req_t ), // AXI request struct + .axi_resp_t ( full_resp_t ), // AXI response struct .Resp ( axi_pkg::RESP_DECERR ), .ATOPs ( 1'b0 ), // no ATOPs in AXI4-Lite .MaxTrans ( 1 ) // Transactions terminate at this slave, and AXI4-Lite @@ -215,8 +215,8 @@ module axi_lite_xbar #( .b_chan_t ( b_chan_t ), // B Channel Type .ar_chan_t ( ar_chan_t ), // AR Channel Type .r_chan_t ( r_chan_t ), // R Channel Type - .req_t ( req_t ), - .resp_t ( resp_t ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), .NoSlvPorts ( Cfg.NoSlvPorts ), // Number of Masters for the module .MaxTrans ( Cfg.MaxSlvTrans ), .FallThrough ( Cfg.FallThrough ), @@ -261,13 +261,13 @@ module axi_lite_xbar_intf #( `AXI_LITE_TYPEDEF_B_CHAN_T(b_chan_t) `AXI_LITE_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t) `AXI_LITE_TYPEDEF_R_CHAN_T(r_chan_t, data_t) - `AXI_LITE_TYPEDEF_REQ_T(req_t, aw_chan_t, w_chan_t, ar_chan_t) - `AXI_LITE_TYPEDEF_RESP_T(resp_t, b_chan_t, r_chan_t) + `AXI_LITE_TYPEDEF_REQ_T(axi_req_t, aw_chan_t, w_chan_t, ar_chan_t) + `AXI_LITE_TYPEDEF_RESP_T(axi_resp_t, b_chan_t, r_chan_t) - req_t [Cfg.NoMstPorts-1:0] mst_reqs; - resp_t [Cfg.NoMstPorts-1:0] mst_resps; - req_t [Cfg.NoSlvPorts-1:0] slv_reqs; - resp_t [Cfg.NoSlvPorts-1:0] slv_resps; + axi_req_t [Cfg.NoMstPorts-1:0] mst_reqs; + axi_resp_t [Cfg.NoMstPorts-1:0] mst_resps; + axi_req_t [Cfg.NoSlvPorts-1:0] slv_reqs; + axi_resp_t [Cfg.NoSlvPorts-1:0] slv_resps; for (genvar i = 0; i < Cfg.NoMstPorts; i++) begin : gen_assign_mst `AXI_LITE_ASSIGN_FROM_REQ(mst_ports[i], mst_reqs[i]) @@ -281,14 +281,14 @@ module axi_lite_xbar_intf #( axi_lite_xbar #( .Cfg (Cfg), - .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 ), - .req_t ( req_t ), - .resp_t ( resp_t ), - .rule_t ( rule_t ) + .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 ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .rule_t ( rule_t ) ) i_xbar ( .clk_i, .rst_ni, diff --git a/vendor/pulp-platform/axi/src/axi_multicut.sv b/vendor/pulp-platform/axi/src/axi_multicut.sv index 8e5dc2f958..64a354a4c1 100644 --- a/vendor/pulp-platform/axi/src/axi_multicut.sv +++ b/vendor/pulp-platform/axi/src/axi_multicut.sv @@ -11,8 +11,8 @@ // // Authors: // - Wolfgang Roenninger -// - Fabian Schuiki // - Andreas Kurth +// - Fabian Schuiki // - Stefan Mach // Multiple AXI4 cuts. @@ -21,23 +21,23 @@ module axi_multicut #( parameter int unsigned NoCuts = 32'd1, // Number of cuts. // AXI channel structs - parameter type aw_chan_t = logic, - parameter type w_chan_t = logic, - parameter type b_chan_t = logic, - parameter type ar_chan_t = logic, - parameter type r_chan_t = logic, + parameter type aw_chan_t = logic, + parameter type w_chan_t = logic, + parameter type b_chan_t = logic, + parameter type ar_chan_t = logic, + parameter type r_chan_t = logic, // AXI request & response structs - parameter type req_t = logic, - parameter type resp_t = logic + parameter type axi_req_t = logic, + parameter type axi_resp_t = logic ) ( - input logic clk_i, // Clock - input logic rst_ni, // Asynchronous reset active low + input logic clk_i, // Clock + input logic rst_ni, // Asynchronous reset active low // slave port - input req_t slv_req_i, - output resp_t slv_resp_o, + input axi_req_t slv_req_i, + output axi_resp_t slv_resp_o, // master port - output req_t mst_req_o, - input resp_t mst_resp_i + output axi_req_t mst_req_o, + input axi_resp_t mst_resp_i ); if (NoCuts == '0) begin : gen_no_cut @@ -46,8 +46,8 @@ module axi_multicut #( assign slv_resp_o = mst_resp_i; end else begin : gen_axi_cut // instantiate all needed cuts - req_t [NoCuts:0] cut_req; - resp_t [NoCuts:0] cut_resp; + axi_req_t [NoCuts:0] cut_req; + axi_resp_t [NoCuts:0] cut_resp; // connect slave to the lowest index assign cut_req[0] = slv_req_i; @@ -56,14 +56,14 @@ module axi_multicut #( // AXI cuts for (genvar i = 0; i < NoCuts; i++) begin : gen_axi_cuts axi_cut #( - .Bypass ( 1'b0 ), - .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 ), - .req_t ( req_t ), - .resp_t ( resp_t ) + .Bypass ( 1'b0 ), + .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 ( axi_req_t ), + .axi_resp_t ( axi_resp_t ) ) i_cut ( .clk_i, .rst_ni, @@ -117,11 +117,11 @@ module axi_multicut_intf #( `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) + `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) - req_t slv_req, mst_req; - resp_t slv_resp, mst_resp; + axi_req_t slv_req, mst_req; + axi_resp_t slv_resp, mst_resp; `AXI_ASSIGN_TO_REQ(slv_req, in) `AXI_ASSIGN_FROM_RESP(in, slv_resp) @@ -130,14 +130,14 @@ module axi_multicut_intf #( `AXI_ASSIGN_TO_RESP(mst_resp, out) axi_multicut #( - .NoCuts ( NUM_CUTS ), - .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 ), - .req_t ( req_t ), - .resp_t ( resp_t ) + .NoCuts ( NUM_CUTS ), + .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 ( axi_req_t ), + .axi_resp_t ( axi_resp_t ) ) i_axi_multicut ( .clk_i, .rst_ni, @@ -191,11 +191,11 @@ module axi_lite_multicut_intf #( `AXI_LITE_TYPEDEF_B_CHAN_T(b_chan_t) `AXI_LITE_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t) `AXI_LITE_TYPEDEF_R_CHAN_T(r_chan_t, data_t) - `AXI_LITE_TYPEDEF_REQ_T(req_t, aw_chan_t, w_chan_t, ar_chan_t) - `AXI_LITE_TYPEDEF_RESP_T(resp_t, b_chan_t, r_chan_t) + `AXI_LITE_TYPEDEF_REQ_T(axi_req_t, aw_chan_t, w_chan_t, ar_chan_t) + `AXI_LITE_TYPEDEF_RESP_T(axi_resp_t, b_chan_t, r_chan_t) - req_t slv_req, mst_req; - resp_t slv_resp, mst_resp; + axi_req_t slv_req, mst_req; + axi_resp_t slv_resp, mst_resp; `AXI_LITE_ASSIGN_TO_REQ(slv_req, in) `AXI_LITE_ASSIGN_FROM_RESP(in, slv_resp) @@ -204,14 +204,14 @@ module axi_lite_multicut_intf #( `AXI_LITE_ASSIGN_TO_RESP(mst_resp, out) axi_multicut #( - .NoCuts ( NUM_CUTS ), - .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 ), - .req_t ( req_t ), - .resp_t ( resp_t ) + .NoCuts ( NUM_CUTS ), + .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 ( axi_req_t ), + .axi_resp_t ( axi_resp_t ) ) i_axi_multicut ( .clk_i, .rst_ni, diff --git a/vendor/pulp-platform/axi/src/axi_mux.sv b/vendor/pulp-platform/axi/src/axi_mux.sv index 59ee3ec465..da17e2b8cc 100644 --- a/vendor/pulp-platform/axi/src/axi_mux.sv +++ b/vendor/pulp-platform/axi/src/axi_mux.sv @@ -22,6 +22,7 @@ // a response with ID `6'b100110` will be forwarded to slave port 2 (`2'b10`). // register macros +`include "common_cells/assertions.svh" `include "common_cells/registers.svh" module axi_mux #( @@ -69,8 +70,83 @@ module axi_mux #( // pass through if only one slave port if (NoSlvPorts == 32'h1) begin : gen_no_mux - assign mst_req_o = slv_reqs_i[0]; - assign slv_resps_o[0] = mst_resp_i; + spill_register #( + .T ( mst_aw_chan_t ), + .Bypass ( ~SpillAw ) + ) i_aw_spill_reg ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .valid_i ( slv_reqs_i[0].aw_valid ), + .ready_o ( slv_resps_o[0].aw_ready ), + .data_i ( slv_reqs_i[0].aw ), + .valid_o ( mst_req_o.aw_valid ), + .ready_i ( mst_resp_i.aw_ready ), + .data_o ( mst_req_o.aw ) + ); + spill_register #( + .T ( w_chan_t ), + .Bypass ( ~SpillW ) + ) i_w_spill_reg ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .valid_i ( slv_reqs_i[0].w_valid ), + .ready_o ( slv_resps_o[0].w_ready ), + .data_i ( slv_reqs_i[0].w ), + .valid_o ( mst_req_o.w_valid ), + .ready_i ( mst_resp_i.w_ready ), + .data_o ( mst_req_o.w ) + ); + spill_register #( + .T ( mst_b_chan_t ), + .Bypass ( ~SpillB ) + ) i_b_spill_reg ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .valid_i ( mst_resp_i.b_valid ), + .ready_o ( mst_req_o.b_ready ), + .data_i ( mst_resp_i.b ), + .valid_o ( slv_resps_o[0].b_valid ), + .ready_i ( slv_reqs_i[0].b_ready ), + .data_o ( slv_resps_o[0].b ) + ); + spill_register #( + .T ( mst_ar_chan_t ), + .Bypass ( ~SpillAr ) + ) i_ar_spill_reg ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .valid_i ( slv_reqs_i[0].ar_valid ), + .ready_o ( slv_resps_o[0].ar_ready ), + .data_i ( slv_reqs_i[0].ar ), + .valid_o ( mst_req_o.ar_valid ), + .ready_i ( mst_resp_i.ar_ready ), + .data_o ( mst_req_o.ar ) + ); + spill_register #( + .T ( mst_r_chan_t ), + .Bypass ( ~SpillR ) + ) i_r_spill_reg ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .valid_i ( mst_resp_i.r_valid ), + .ready_o ( mst_req_o.r_ready ), + .data_i ( mst_resp_i.r ), + .valid_o ( slv_resps_o[0].r_valid ), + .ready_i ( slv_reqs_i[0].r_ready ), + .data_o ( slv_resps_o[0].r ) + ); +// Validate parameters. +// pragma translate_off + `ASSERT_INIT(CorrectIdWidthSlvAw, $bits(slv_reqs_i[0].aw.id) == SlvAxiIDWidth) + `ASSERT_INIT(CorrectIdWidthSlvB, $bits(slv_resps_o[0].b.id) == SlvAxiIDWidth) + `ASSERT_INIT(CorrectIdWidthSlvAr, $bits(slv_reqs_i[0].ar.id) == SlvAxiIDWidth) + `ASSERT_INIT(CorrectIdWidthSlvR, $bits(slv_resps_o[0].r.id) == SlvAxiIDWidth) + `ASSERT_INIT(CorrectIdWidthMstAw, $bits(mst_req_o.aw.id) == SlvAxiIDWidth) + `ASSERT_INIT(CorrectIdWidthMstB, $bits(mst_resp_i.b.id) == SlvAxiIDWidth) + `ASSERT_INIT(CorrectIdWidthMstAr, $bits(mst_req_o.ar.id) == SlvAxiIDWidth) + `ASSERT_INIT(CorrectIdWidthMstR, $bits(mst_resp_i.r.id) == SlvAxiIDWidth) +// pragma translate_on + // other non degenerate cases end else begin : gen_mux diff --git a/vendor/pulp-platform/axi/src/axi_pkg.sv b/vendor/pulp-platform/axi/src/axi_pkg.sv index 92ede558c1..7ef3bbcd7f 100644 --- a/vendor/pulp-platform/axi/src/axi_pkg.sv +++ b/vendor/pulp-platform/axi/src/axi_pkg.sv @@ -393,17 +393,44 @@ package axi_pkg; /// Configuration for `axi_xbar`. typedef struct packed { + /// Number of slave ports of the crossbar. + /// This many master modules are connected to it. int unsigned NoSlvPorts; + /// Number of master ports of the crossbar. + /// This many slave modules are connected to it. int unsigned NoMstPorts; + /// Maximum number of open transactions each master connected to the crossbar can have in + /// flight at the same time. int unsigned MaxMstTrans; + /// Maximum number of open transactions each slave connected to the crossbar can have in + /// flight at the same time. int unsigned MaxSlvTrans; + /// Determine if the internal FIFOs of the crossbar are instantiated in fallthrough mode. + /// 0: No fallthrough + /// 1: Fallthrough bit FallThrough; + /// The Latency mode of the xbar. This determines if the channels on the ports have + /// a spill register instantiated. + /// Example configurations are provided with the enum `xbar_latency_e`. xbar_latency_e LatencyMode; + /// This is the number of `axi_multicut` stages instantiated in the line cross of the channels. + /// Having multiple stages can potentially add a large number of FFs! + int unsigned PipelineStages; + /// AXI ID width of the salve ports. The ID width of the master ports is determined + /// Automatically. See `axi_mux` for details. int unsigned AxiIdWidthSlvPorts; + /// The used ID portion to determine if a different salve is used for the same ID. + /// See `axi_demux` for details. int unsigned AxiIdUsedSlvPorts; + /// Are IDs unique? bit UniqueIds; + /// AXI4+ATOP address field width. int unsigned AxiAddrWidth; + /// AXI4+ATOP data field width. int unsigned AxiDataWidth; + /// The number of address rules defined for routing of the transactions. + /// Each master port can have multiple rules, should have however at least one. + /// If a transaction can not be routed the xbar will answer with an `axi_pkg::RESP_DECERR`. int unsigned NoAddrRules; } xbar_cfg_t; diff --git a/vendor/pulp-platform/axi/src/axi_rw_join.sv b/vendor/pulp-platform/axi/src/axi_rw_join.sv new file mode 100644 index 0000000000..6b7191b38e --- /dev/null +++ b/vendor/pulp-platform/axi/src/axi_rw_join.sv @@ -0,0 +1,110 @@ +// Copyright (c) 2022 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: +// - Tobias Senti + +`include "axi/assign.svh" +`include "common_cells/assertions.svh" + +/// Joins a read and a write slave into one single read / write master +/// +/// Connects the ar and r channel of the read slave to the read / write master +/// and the aw, w and b channel of the write slave to the read / write master +module axi_rw_join #( + parameter type axi_req_t = logic, + parameter type axi_resp_t = logic +) ( + input logic clk_i, + input logic rst_ni, + // Read Slave + input axi_req_t slv_read_req_i, + output axi_resp_t slv_read_resp_o, + + // Write Slave + input axi_req_t slv_write_req_i, + output axi_resp_t slv_write_resp_o, + + // Read / Write Master + output axi_req_t mst_req_o, + input axi_resp_t mst_resp_i +); + + //-------------------------------------- + // Read channel data + //-------------------------------------- + + // Assign Read Structs + `AXI_ASSIGN_AR_STRUCT ( mst_req_o.ar , slv_read_req_i.ar ) + `AXI_ASSIGN_R_STRUCT ( slv_read_resp_o.r , mst_resp_i.r ) + + // Read B channel data + assign slv_read_resp_o.b = '0; + + + //-------------------------------------- + // Read channel handshakes + //-------------------------------------- + + // Read AR channel handshake + assign mst_req_o.ar_valid = slv_read_req_i.ar_valid; + assign slv_read_resp_o.ar_ready = mst_resp_i.ar_ready; + + // Read R channel handshake + assign slv_read_resp_o.r_valid = mst_resp_i.r_valid; + assign mst_req_o.r_ready = slv_read_req_i.r_ready; + + // Read AW, W and B handshake + assign slv_read_resp_o.aw_ready = 1'b0; + assign slv_read_resp_o.w_ready = 1'b0; + assign slv_read_resp_o.b_valid = 1'b0; + + // check for AW and W never to be valid + `ASSERT_NEVER(slv_read_req_aw_valid, slv_read_req_i.aw_valid, clk_i, !rst_ni) + `ASSERT_NEVER(slv_read_req_w_valid, slv_read_req_i.w_valid, clk_i, !rst_ni) + + //-------------------------------------- + // Write channel data + //-------------------------------------- + + // Assign Write Structs + `AXI_ASSIGN_AW_STRUCT ( mst_req_o.aw , slv_write_req_i.aw ) + `AXI_ASSIGN_W_STRUCT ( mst_req_o.w , slv_write_req_i.w ) + `AXI_ASSIGN_B_STRUCT ( slv_write_resp_o.b , mst_resp_i.b ) + + // Write R channel data + assign slv_write_resp_o.r = '0; + + + //-------------------------------------- + // Write channel handshakes + //-------------------------------------- + + // Write AR and R channel handshake + assign slv_write_resp_o.ar_ready = 1'b0; + assign slv_write_resp_o.r_valid = 1'b0; + + // check for AR to never be valid + `ASSERT_NEVER(slv_write_req_ar_valid, slv_write_req_i.ar_valid, clk_i, !rst_ni) + + // Write AW channel handshake + assign mst_req_o.aw_valid = slv_write_req_i.aw_valid; + assign slv_write_resp_o.aw_ready = mst_resp_i.aw_ready; + + // Write W channel handshake + assign mst_req_o.w_valid = slv_write_req_i.w_valid; + assign slv_write_resp_o.w_ready = mst_resp_i.w_ready; + + // Write B channel handshake + assign slv_write_resp_o.b_valid = mst_resp_i.b_valid; + assign mst_req_o.b_ready = slv_write_req_i.b_ready; + +endmodule : axi_rw_join diff --git a/vendor/pulp-platform/axi/src/axi_rw_split.sv b/vendor/pulp-platform/axi/src/axi_rw_split.sv new file mode 100644 index 0000000000..6fa96dfa9a --- /dev/null +++ b/vendor/pulp-platform/axi/src/axi_rw_split.sv @@ -0,0 +1,111 @@ +// Copyright (c) 2022 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: +// - Tobias Senti + +`include "axi/assign.svh" +`include "common_cells/assertions.svh" + +/// Splits a single read / write slave into one read and one write master +/// +/// Connects the ar and r channel of the read / write slave to the read master +/// and the aw, w and b channel of the read / write slave to the write master +module axi_rw_split #( + parameter type axi_req_t = logic, + parameter type axi_resp_t = logic +) ( + input logic clk_i, + input logic rst_ni, + // Read / Write Slave + input axi_req_t slv_req_i, + output axi_resp_t slv_resp_o, + + // Read Master + output axi_req_t mst_read_req_o, + input axi_resp_t mst_read_resp_i, + + // Write Master + output axi_req_t mst_write_req_o, + input axi_resp_t mst_write_resp_i +); + + //-------------------------------------- + // Read channel data + //-------------------------------------- + + // Assign Read channel structs + `AXI_ASSIGN_AR_STRUCT ( mst_read_req_o.ar , slv_req_i.ar ) + `AXI_ASSIGN_R_STRUCT ( slv_resp_o.r , mst_read_resp_i.r ) + + // Read AW and W channel data + assign mst_read_req_o.aw = '0; + assign mst_read_req_o.w = '0; + + + //-------------------------------------- + // Read channel handshakes + //-------------------------------------- + + // Read AR channel handshake + assign mst_read_req_o.ar_valid = slv_req_i.ar_valid; + assign slv_resp_o.ar_ready = mst_read_resp_i.ar_ready; + + // Read R channel handshake + assign slv_resp_o.r_valid = mst_read_resp_i.r_valid; + assign mst_read_req_o.r_ready = slv_req_i.r_ready; + + // Read AW, W and B handshake + assign mst_read_req_o.aw_valid = 1'b0; + assign mst_read_req_o.w_valid = 1'b0; + assign mst_read_req_o.b_ready = 1'b0; + + // check for B never to be valid + `ASSERT_NEVER(mst_read_resp_b_valid, mst_read_resp_i.b_valid, clk_i, !rst_ni) + + + //-------------------------------------- + // Write channel data + //-------------------------------------- + + // Assign Write channel structs + `AXI_ASSIGN_AW_STRUCT ( mst_write_req_o.aw , slv_req_i.aw ) + `AXI_ASSIGN_W_STRUCT ( mst_write_req_o.w , slv_req_i.w ) + `AXI_ASSIGN_B_STRUCT ( slv_resp_o.b , mst_write_resp_i.b ) + + // Write AR channel data + assign mst_write_req_o.ar = '0; + + + //-------------------------------------- + // Write channel handshakes + //-------------------------------------- + + // Write AR and R channel handshake + assign mst_write_req_o.ar_valid = 1'b0; + assign mst_write_req_o.r_ready = 1'b0; + + // check for R never to be valid + `ASSERT_NEVER(mst_read_resp_r_valid, mst_read_resp_i.r_valid, clk_i, !rst_ni) + + // Write AW channel handshake + assign mst_write_req_o.aw_valid = slv_req_i.aw_valid; + assign slv_resp_o.aw_ready = mst_write_resp_i.aw_ready; + + // Write W channel handshake + assign mst_write_req_o.w_valid = slv_req_i.w_valid; + assign slv_resp_o.w_ready = mst_write_resp_i.w_ready; + + // Write B channel handshake + assign slv_resp_o.b_valid = mst_write_resp_i.b_valid; + assign mst_write_req_o.b_ready = slv_req_i.b_ready; + +endmodule : axi_rw_split diff --git a/vendor/pulp-platform/axi/src/axi_serializer.sv b/vendor/pulp-platform/axi/src/axi_serializer.sv index df2a35a728..0b064f29ee 100644 --- a/vendor/pulp-platform/axi/src/axi_serializer.sv +++ b/vendor/pulp-platform/axi/src/axi_serializer.sv @@ -27,22 +27,22 @@ module axi_serializer #( /// AXI4+ATOP ID width. parameter int unsigned AxiIdWidth = 32'd0, /// AXI4+ATOP request struct definition. - parameter type req_t = logic, + parameter type axi_req_t = logic, /// AXI4+ATOP response struct definition. - parameter type resp_t = logic + parameter type axi_resp_t = logic ) ( /// Clock - input logic clk_i, + input logic clk_i, /// Asynchronous reset, active low - input logic rst_ni, + input logic rst_ni, /// Slave port request - input req_t slv_req_i, + input axi_req_t slv_req_i, /// Slave port response - output resp_t slv_resp_o, + output axi_resp_t slv_resp_o, /// Master port request - output req_t mst_req_o, + output axi_req_t mst_req_o, /// Master port response - input resp_t mst_resp_i + input axi_resp_t mst_resp_i ); typedef logic [AxiIdWidth-1:0] id_t; @@ -254,10 +254,10 @@ module axi_serializer_intf #( `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 slv_req, mst_req; - resp_t slv_resp, mst_resp; + `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 slv_req, mst_req; + axi_resp_t slv_resp, mst_resp; `AXI_ASSIGN_TO_REQ(slv_req, slv) `AXI_ASSIGN_FROM_RESP(slv, slv_resp) `AXI_ASSIGN_FROM_REQ(mst, mst_req) @@ -267,8 +267,8 @@ module axi_serializer_intf #( .MaxReadTxns ( MAX_READ_TXNS ), .MaxWriteTxns ( MAX_WRITE_TXNS ), .AxiIdWidth ( AXI_ID_WIDTH ), - .req_t ( req_t ), - .resp_t ( resp_t ) + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ) ) i_axi_serializer ( .clk_i, .rst_ni, diff --git a/vendor/pulp-platform/axi/src/axi_sim_mem.sv b/vendor/pulp-platform/axi/src/axi_sim_mem.sv index 4a9ad57b68..b8b6b12c08 100644 --- a/vendor/pulp-platform/axi/src/axi_sim_mem.sv +++ b/vendor/pulp-platform/axi/src/axi_sim_mem.sv @@ -3,6 +3,9 @@ // // Authors: // - Andreas Kurth +// - Samuel Riedel +// - Michael Rogenmoser +// - Thomas Benz `include "axi/typedef.svh" @@ -14,6 +17,8 @@ /// axi_sim_mem #( ... ) i_sim_mem ( ... ); /// initial begin /// $readmemh("file_with_memory_addrs_and_data.mem", i_sim_mem.mem); +/// $readmemh("file_with_memory_addrs_and_read_errors.mem", i_sim_mem.rerr); +/// $readmemh("file_with_memory_addrs_and_write_errors.mem", i_sim_mem.werr); /// end /// ``` /// `mem` is addressed (or indexed) byte-wise with `AddrWidth`-wide addresses. @@ -29,11 +34,13 @@ module axi_sim_mem #( /// AXI User Width. parameter int unsigned UserWidth = 32'd0, /// AXI4 request struct definition - parameter type req_t = logic, + parameter type axi_req_t = logic, /// AXI4 response struct definition - parameter type rsp_t = logic, + parameter type axi_rsp_t = logic, /// Warn on accesses to uninitialized bytes parameter bit WarnUninitialized = 1'b0, + /// Clear error on access + parameter bit ClearErrOnAccess = 1'b0, /// Application delay (measured after rising clock edge) parameter time ApplDelay = 0ps, /// Acquisition delay (measured after rising clock edge) @@ -44,9 +51,41 @@ module axi_sim_mem #( /// Active-low reset input logic rst_ni, /// AXI4 request struct - input req_t axi_req_i, + input axi_req_t axi_req_i, /// AXI4 response struct - output rsp_t axi_rsp_o + output axi_rsp_t axi_rsp_o, + /// Memory monitor write valid. All `mon_w_*` outputs are only valid if this signal is high. + /// A write to the memory is visible on the `mon_w_*` outputs in the clock cycle after it has + /// happened. + output logic mon_w_valid_o, + /// Memory monitor write address + output logic [AddrWidth-1:0] mon_w_addr_o, + /// Memory monitor write data + output logic [DataWidth-1:0] mon_w_data_o, + /// Memory monitor write ID + output logic [IdWidth-1:0] mon_w_id_o, + /// Memory monitor write user + output logic [UserWidth-1:0] mon_w_user_o, + /// Memory monitor write beat count + output axi_pkg::len_t mon_w_beat_count_o, + /// Memory monitor write last + output logic mon_w_last_o, + /// Memory monitor read valid. All `mon_r_*` outputs are only valid if this signal is high. + /// A read from the memory is visible on the `mon_w_*` outputs in the clock cycle after it has + /// happened. + output logic mon_r_valid_o, + /// Memory monitor read address + output logic [AddrWidth-1:0] mon_r_addr_o, + /// Memory monitor read data + output logic [DataWidth-1:0] mon_r_data_o, + /// Memory monitor read ID + output logic [IdWidth-1:0] mon_r_id_o, + /// Memory monitor read user + output logic [UserWidth-1:0] mon_r_user_o, + /// Memory monitor read beat count + output axi_pkg::len_t mon_r_beat_count_o, + /// Memory monitor read last + output logic mon_r_last_o ); localparam int unsigned StrbWidth = DataWidth / 8; @@ -61,7 +100,23 @@ module axi_sim_mem #( `AXI_TYPEDEF_AR_CHAN_T(ar_t, addr_t, id_t, user_t) `AXI_TYPEDEF_R_CHAN_T(r_t, data_t, id_t, user_t) - logic [7:0] mem[addr_t]; + typedef struct packed { + logic valid; + logic [AddrWidth-1:0] addr; + logic [DataWidth-1:0] data; + logic [IdWidth-1:0] id; + logic [UserWidth-1:0] user; + axi_pkg::len_t beat_count; + logic last; + } monitor_t; + + monitor_t mon_w, mon_r; + logic [7:0] mem[addr_t]; + axi_pkg::resp_t rerr[addr_t] = '{default: axi_pkg::RESP_OKAY}; + axi_pkg::resp_t werr[addr_t] = '{default: axi_pkg::RESP_OKAY}; + + // error happened in write burst + axi_pkg::resp_t error_happened = axi_pkg::RESP_OKAY; initial begin automatic ar_t ar_queue[$]; @@ -69,6 +124,9 @@ module axi_sim_mem #( automatic b_t b_queue[$]; automatic shortint unsigned r_cnt = 0, w_cnt = 0; axi_rsp_o = '0; + // Monitor interface + mon_w = '0; + mon_r = '0; wait (rst_ni); fork // AW @@ -87,6 +145,7 @@ module axi_sim_mem #( @(posedge clk_i); #(ApplDelay); axi_rsp_o.w_ready = 1'b0; + mon_w = '0; if (aw_queue.size() != 0) begin axi_rsp_o.w_ready = 1'b1; #(AcqDelay - ApplDelay); @@ -96,6 +155,12 @@ module axi_sim_mem #( automatic axi_pkg::size_t size = aw_queue[0].size; automatic addr_t addr = axi_pkg::beat_addr(aw_queue[0].addr, size, len, burst, w_cnt); + mon_w.valid = 1'b1; + mon_w.addr = addr; + mon_w.data = axi_req_i.w.data; + mon_w.id = aw_queue[0].id; + mon_w.user = aw_queue[0].user; + mon_w.beat_count = w_cnt; for (shortint unsigned i_byte = axi_pkg::beat_lower_byte(addr, size, len, burst, StrbWidth, w_cnt); i_byte <= axi_pkg::beat_upper_byte(addr, size, len, burst, StrbWidth, w_cnt); @@ -103,15 +168,20 @@ module axi_sim_mem #( if (axi_req_i.w.strb[i_byte]) begin automatic addr_t byte_addr = (addr / StrbWidth) * StrbWidth + i_byte; mem[byte_addr] = axi_req_i.w.data[i_byte*8+:8]; + error_happened = axi_pkg::resp_precedence(werr[byte_addr], error_happened); + if (ClearErrOnAccess) + werr[byte_addr] = axi_pkg::RESP_OKAY; end end if (w_cnt == aw_queue[0].len) begin automatic b_t b_beat = '0; assert (axi_req_i.w.last) else $error("Expected last beat of W burst!"); b_beat.id = aw_queue[0].id; - b_beat.resp = axi_pkg::RESP_OKAY; + b_beat.resp = error_happened; b_queue.push_back(b_beat); w_cnt = 0; + mon_w.last = 1'b1; + error_happened = axi_pkg::RESP_OKAY; void'(aw_queue.pop_front()); end else begin assert (!axi_req_i.w.last) else $error("Did not expect last beat of W burst!"); @@ -150,12 +220,14 @@ module axi_sim_mem #( @(posedge clk_i); #(ApplDelay); axi_rsp_o.r_valid = 1'b0; + mon_r = '0; if (ar_queue.size() != 0) begin automatic axi_pkg::burst_t burst = ar_queue[0].burst; automatic axi_pkg::len_t len = ar_queue[0].len; automatic axi_pkg::size_t size = ar_queue[0].size; automatic addr_t addr = axi_pkg::beat_addr(ar_queue[0].addr, size, len, burst, r_cnt); automatic r_t r_beat = '0; + automatic data_t r_data = 'x; // compatibility reasons r_beat.data = 'x; r_beat.id = ar_queue[0].id; r_beat.resp = axi_pkg::RESP_OKAY; @@ -169,30 +241,85 @@ module axi_sim_mem #( $warning("Access to non-initialized byte at address 0x%016x by ID 0x%x.", byte_addr, r_beat.id); end - r_beat.data[i_byte*8+:8] = 'x; + r_data[i_byte*8+:8] = 'x; end else begin - r_beat.data[i_byte*8+:8] = mem[byte_addr]; + r_data[i_byte*8+:8] = mem[byte_addr]; + end + r_beat.resp = axi_pkg::resp_precedence(rerr[byte_addr], r_beat.resp); + if (ClearErrOnAccess & axi_req_i.r_ready) begin + rerr[byte_addr] = axi_pkg::RESP_OKAY; end end + r_beat.data = r_data; if (r_cnt == ar_queue[0].len) begin r_beat.last = 1'b1; + mon_r.last = 1'b1; end axi_rsp_o.r = r_beat; axi_rsp_o.r_valid = 1'b1; + mon_r.valid = 1'b1; + mon_r.addr = addr; + mon_r.data = r_beat.data; + mon_r.id = r_beat.id; + mon_r.user = ar_queue[0].user; + mon_r.beat_count = r_cnt; #(AcqDelay - ApplDelay); - if (axi_req_i.r_ready) begin - if (r_beat.last) begin - r_cnt = 0; - void'(ar_queue.pop_front()); - end else begin - r_cnt++; - end + while (!axi_req_i.r_ready) begin + @(posedge clk_i); + #(AcqDelay); + mon_r = '0; + end + if (r_beat.last) begin + r_cnt = 0; + void'(ar_queue.pop_front()); + end else begin + r_cnt++; end end end join end + // Assign the monitor output in the next clock cycle. Rationale: We only know whether we are + // writing until after `AcqDelay`. This means we could only provide the monitoring output for + // writes after `AcqDelay` in the same cycle, which is incompatible with ATI timing. Thus, we + // provide the monitoring output for writes (and for uniformity also for reads) in the next clock + // cycle. + initial begin + mon_w_valid_o = '0; + mon_w_addr_o = '0; + mon_w_data_o = '0; + mon_w_id_o = '0; + mon_w_user_o = '0; + mon_w_beat_count_o = '0; + mon_w_last_o = '0; + mon_r_valid_o = '0; + mon_r_addr_o = '0; + mon_r_data_o = '0; + mon_r_id_o = '0; + mon_r_user_o = '0; + mon_r_beat_count_o = '0; + mon_r_last_o = '0; + wait (rst_ni); + forever begin + @(posedge clk_i); + mon_w_valid_o <= #(ApplDelay) mon_w.valid; + mon_w_addr_o <= #(ApplDelay) mon_w.addr; + mon_w_data_o <= #(ApplDelay) mon_w.data; + mon_w_id_o <= #(ApplDelay) mon_w.id; + mon_w_user_o <= #(ApplDelay) mon_w.user; + mon_w_beat_count_o <= #(ApplDelay) mon_w.beat_count; + mon_w_last_o <= #(ApplDelay) mon_w.last; + mon_r_valid_o <= #(ApplDelay) mon_r.valid; + mon_r_addr_o <= #(ApplDelay) mon_r.addr; + mon_r_data_o <= #(ApplDelay) mon_r.data; + mon_r_id_o <= #(ApplDelay) mon_r.id; + mon_r_user_o <= #(ApplDelay) mon_r.user; + mon_r_beat_count_o <= #(ApplDelay) mon_r.beat_count; + mon_r_last_o <= #(ApplDelay) mon_r.last; + end + end + // Parameter Assertions initial begin assert (AddrWidth != 0) else $fatal("AddrWidth must be non-zero!", 1); @@ -202,3 +329,85 @@ module axi_sim_mem #( end endmodule + + +`include "axi/assign.svh" + +/// Interface variant of [`axi_sim_mem`](module.axi_sim_mem). +/// +/// See the documentation of the main module for the definition of ports and parameters. +module axi_sim_mem_intf #( + parameter int unsigned AXI_ADDR_WIDTH = 32'd0, + parameter int unsigned AXI_DATA_WIDTH = 32'd0, + parameter int unsigned AXI_ID_WIDTH = 32'd0, + parameter int unsigned AXI_USER_WIDTH = 32'd0, + parameter bit WARN_UNINITIALIZED = 1'b0, + parameter bit ClearErrOnAccess = 1'b0, + parameter time APPL_DELAY = 0ps, + parameter time ACQ_DELAY = 0ps +) ( + input logic clk_i, + input logic rst_ni, + AXI_BUS.Slave axi_slv, + output logic mon_w_valid_o, + output logic [AXI_ADDR_WIDTH-1:0] mon_w_addr_o, + output logic [AXI_DATA_WIDTH-1:0] mon_w_data_o, + output logic [AXI_ID_WIDTH-1:0] mon_w_id_o, + output logic [AXI_USER_WIDTH-1:0] mon_w_user_o, + output axi_pkg::len_t mon_w_beat_count_o, + output logic mon_w_last_o, + output logic mon_r_valid_o, + output logic [AXI_ADDR_WIDTH-1:0] mon_r_addr_o, + output logic [AXI_DATA_WIDTH-1:0] mon_r_data_o, + output logic [AXI_ID_WIDTH-1:0] mon_r_id_o, + output logic [AXI_USER_WIDTH-1:0] mon_r_user_o, + output axi_pkg::len_t mon_r_beat_count_o, + output logic mon_r_last_o +); + + typedef logic [AXI_ADDR_WIDTH-1:0] axi_addr_t; + typedef logic [AXI_DATA_WIDTH-1:0] axi_data_t; + typedef logic [AXI_ID_WIDTH-1:0] axi_id_t; + typedef logic [AXI_DATA_WIDTH/8-1:0] axi_strb_t; + typedef logic [AXI_USER_WIDTH-1:0] axi_user_t; + `AXI_TYPEDEF_ALL(axi, axi_addr_t, axi_id_t, axi_data_t, axi_strb_t, axi_user_t) + + axi_req_t axi_req; + axi_resp_t axi_rsp; + + `AXI_ASSIGN_TO_REQ(axi_req, axi_slv) + `AXI_ASSIGN_FROM_RESP(axi_slv, axi_rsp) + + axi_sim_mem #( + .AddrWidth (AXI_ADDR_WIDTH), + .DataWidth (AXI_DATA_WIDTH), + .IdWidth (AXI_ID_WIDTH), + .UserWidth (AXI_USER_WIDTH), + .axi_req_t (axi_req_t), + .axi_rsp_t (axi_resp_t), + .WarnUninitialized (WARN_UNINITIALIZED), + .ClearErrOnAccess (ClearErrOnAccess), + .ApplDelay (APPL_DELAY), + .AcqDelay (ACQ_DELAY) + ) i_sim_mem ( + .clk_i, + .rst_ni, + .axi_req_i (axi_req), + .axi_rsp_o (axi_rsp), + .mon_w_valid_o, + .mon_w_addr_o, + .mon_w_data_o, + .mon_w_id_o, + .mon_w_user_o, + .mon_w_beat_count_o, + .mon_w_last_o, + .mon_r_valid_o, + .mon_r_addr_o, + .mon_r_data_o, + .mon_r_id_o, + .mon_r_user_o, + .mon_r_beat_count_o, + .mon_r_last_o + ); + +endmodule diff --git a/vendor/pulp-platform/axi/src/axi_slave_compare.sv b/vendor/pulp-platform/axi/src/axi_slave_compare.sv new file mode 100644 index 0000000000..a4442985c5 --- /dev/null +++ b/vendor/pulp-platform/axi/src/axi_slave_compare.sv @@ -0,0 +1,185 @@ +// Copyright (c) 2019-2020 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 + +`include "axi/assign.svh" +/// Synthesizable test module comparing two AXI slaves of the same type. +/// The reference response is always passed to the master, whereas the test response +/// is discarded after handshaking. +/// This module is meant to be used in FPGA-based verification. +module axi_slave_compare #( + /// ID width of the AXI4+ATOP interface + parameter int unsigned AxiIdWidth = 32'd0, + /// FIFO depth + parameter int unsigned FifoDepth = 32'd0, + /// AW channel type of the AXI4+ATOP interface + parameter type axi_aw_chan_t = logic, + /// W channel type of the AXI4+ATOP interface + parameter type axi_w_chan_t = logic, + /// B channel type of the AXI4+ATOP interface + parameter type axi_b_chan_t = logic, + /// AR channel type of the AXI4+ATOP interface + parameter type axi_ar_chan_t = logic, + /// R channel type of the AXI4+ATOP interface + parameter type axi_r_chan_t = logic, + /// Request struct type of the AXI4+ATOP slave port + parameter type axi_req_t = logic, + /// Response struct type of the AXI4+ATOP slave port + parameter type axi_rsp_t = logic, + /// ID type (*do not overwrite*) + parameter type id_t = logic [2**AxiIdWidth-1:0] +)( + /// Clock + input logic clk_i, + /// Asynchronous reset, active low + input logic rst_ni, + /// Testmode + input logic testmode_i, + /// AXI4+ATOP channel request in + input axi_req_t axi_mst_req_i, + /// AXI4+ATOP channel response out + output axi_rsp_t axi_mst_rsp_o, + /// AXI4+ATOP reference channel request out + output axi_req_t axi_ref_req_o, + /// AXI4+ATOP reference channel response in + input axi_rsp_t axi_ref_rsp_i, + /// AXI4+ATOP test channel request out + output axi_req_t axi_test_req_o, + /// AXI4+ATOP test channel response in + input axi_rsp_t axi_test_rsp_i, + /// AW mismatch + output id_t aw_mismatch_o, + /// W mismatch + output logic w_mismatch_o, + /// B mismatch + output id_t b_mismatch_o, + /// AR mismatch + output id_t ar_mismatch_o, + /// R mismatch + output id_t r_mismatch_o, + /// General mismatch + output logic mismatch_o, + /// Unit is busy + output logic busy_o +); + + axi_req_t axi_ref_req_in, axi_test_req_in; + axi_rsp_t axi_ref_rsp_in, axi_test_rsp_in; + + logic aw_valid_ref, aw_ready_ref; + logic w_valid_ref, w_ready_ref; + logic ar_valid_ref, ar_ready_ref; + + logic aw_valid_test, aw_ready_test; + logic w_valid_test, w_ready_test; + logic ar_valid_test, ar_ready_test; + + logic aw_ready_mst; + logic w_ready_mst; + logic ar_ready_mst; + + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_aw ( + .clk_i, + .rst_ni, + .valid_i ( axi_mst_req_i.aw_valid ), + .ready_o ( aw_ready_mst ), + .valid_o ( { aw_valid_ref, aw_valid_test } ), + .ready_i ( { aw_ready_ref, aw_ready_test } ) + ); + + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_ar ( + .clk_i, + .rst_ni, + .valid_i ( axi_mst_req_i.ar_valid ), + .ready_o ( ar_ready_mst ), + .valid_o ( { ar_valid_ref, ar_valid_test } ), + .ready_i ( { ar_ready_ref, ar_ready_test } ) + ); + + stream_fork #( + .N_OUP ( 32'd2 ) + ) i_stream_fork_w ( + .clk_i, + .rst_ni, + .valid_i ( axi_mst_req_i.w_valid ), + .ready_o ( w_ready_mst ), + .valid_o ( { w_valid_ref, w_valid_test } ), + .ready_i ( { w_ready_ref, w_ready_test } ) + ); + + // assemble buses + always_comb begin + // request + `AXI_SET_REQ_STRUCT(axi_ref_req_in, axi_mst_req_i) + `AXI_SET_REQ_STRUCT(axi_test_req_in, axi_mst_req_i) + // overwrite valids in requests + axi_ref_req_in.aw_valid = aw_valid_ref; + axi_ref_req_in.ar_valid = ar_valid_ref; + axi_ref_req_in.w_valid = w_valid_ref; + axi_test_req_in.aw_valid = aw_valid_test; + axi_test_req_in.ar_valid = ar_valid_test; + axi_test_req_in.w_valid = w_valid_test; + // get readies + aw_ready_ref = axi_ref_rsp_in.aw_ready; + ar_ready_ref = axi_ref_rsp_in.ar_ready; + w_ready_ref = axi_ref_rsp_in.w_ready; + aw_ready_test = axi_test_rsp_in.aw_ready; + ar_ready_test = axi_test_rsp_in.ar_ready; + w_ready_test = axi_test_rsp_in.w_ready; + // response + `AXI_SET_RESP_STRUCT(axi_mst_rsp_o, axi_ref_rsp_in) + // overwrite readies + axi_mst_rsp_o.aw_ready = aw_ready_mst; + axi_mst_rsp_o.w_ready = w_ready_mst; + axi_mst_rsp_o.ar_ready = ar_ready_mst; + // b interface is not used + axi_test_req_in.r_ready = '1; + axi_test_req_in.b_ready = '1; + end + + axi_bus_compare #( + .AxiIdWidth ( AxiIdWidth ), + .FifoDepth ( FifoDepth ), + .axi_aw_chan_t ( axi_aw_chan_t ), + .axi_w_chan_t ( axi_w_chan_t ), + .axi_b_chan_t ( axi_b_chan_t ), + .axi_ar_chan_t ( axi_ar_chan_t ), + .axi_r_chan_t ( axi_r_chan_t ), + .axi_req_t ( axi_req_t ), + .axi_rsp_t ( axi_rsp_t ) + ) i_axi_bus_compare ( + .clk_i, + .rst_ni, + .testmode_i, + .aw_mismatch_o, + .w_mismatch_o, + .b_mismatch_o, + .ar_mismatch_o, + .r_mismatch_o, + .mismatch_o, + .busy_o, + .axi_a_req_i ( axi_ref_req_in ), + .axi_a_rsp_o ( axi_ref_rsp_in ), + .axi_a_req_o ( axi_ref_req_o ), + .axi_a_rsp_i ( axi_ref_rsp_i ), + .axi_b_req_i ( axi_test_req_in ), + .axi_b_rsp_o ( axi_test_rsp_in ), + .axi_b_req_o ( axi_test_req_o ), + .axi_b_rsp_i ( axi_test_rsp_i ) + ); + +endmodule diff --git a/vendor/pulp-platform/axi/src/axi_test.sv b/vendor/pulp-platform/axi/src/axi_test.sv index c62a6d564f..7c7fab63ef 100644 --- a/vendor/pulp-platform/axi/src/axi_test.sv +++ b/vendor/pulp-platform/axi/src/axi_test.sv @@ -10,10 +10,9 @@ // specific language governing permissions and limitations under the License. // // Authors: -// - Wolfgang Roenninger // - Andreas Kurth +// - Wolfgang Roenninger // - Fabian Schuiki -// - Florian Zaruba // - Matheus Cavalcante @@ -591,6 +590,92 @@ package axi_test; axi.r_ready <= #TA 0; endtask + /// Monitor the AW channel and return the next beat. + task mon_aw ( + output ax_beat_t beat + ); + cycle_start(); + while (!(axi.aw_valid && axi.aw_ready)) begin cycle_end(); cycle_start(); end + beat = new; + beat.ax_id = axi.aw_id; + beat.ax_addr = axi.aw_addr; + beat.ax_len = axi.aw_len; + beat.ax_size = axi.aw_size; + beat.ax_burst = axi.aw_burst; + beat.ax_lock = axi.aw_lock; + beat.ax_cache = axi.aw_cache; + beat.ax_prot = axi.aw_prot; + beat.ax_qos = axi.aw_qos; + beat.ax_region = axi.aw_region; + beat.ax_atop = axi.aw_atop; + beat.ax_user = axi.aw_user; + cycle_end(); + endtask + + /// Monitor the W channel and return the next beat. + task mon_w ( + output w_beat_t beat + ); + cycle_start(); + while (!(axi.w_valid && axi.w_ready)) begin cycle_end(); cycle_start(); end + beat = new; + beat.w_data = axi.w_data; + beat.w_strb = axi.w_strb; + beat.w_last = axi.w_last; + beat.w_user = axi.w_user; + cycle_end(); + endtask + + /// Monitor the B channel and return the next beat. + task mon_b ( + output b_beat_t beat + ); + cycle_start(); + while (!(axi.b_valid && axi.b_ready)) begin cycle_end(); cycle_start(); end + beat = new; + beat.b_id = axi.b_id; + beat.b_resp = axi.b_resp; + beat.b_user = axi.b_user; + cycle_end(); + endtask + + /// Monitor the AR channel and return the next beat. + task mon_ar ( + output ax_beat_t beat + ); + cycle_start(); + while (!(axi.ar_valid && axi.ar_ready)) begin cycle_end(); cycle_start(); end + beat = new; + beat.ax_id = axi.ar_id; + beat.ax_addr = axi.ar_addr; + beat.ax_len = axi.ar_len; + beat.ax_size = axi.ar_size; + beat.ax_burst = axi.ar_burst; + beat.ax_lock = axi.ar_lock; + beat.ax_cache = axi.ar_cache; + beat.ax_prot = axi.ar_prot; + beat.ax_qos = axi.ar_qos; + beat.ax_region = axi.ar_region; + beat.ax_atop = 'X; // Not defined on the AR channel. + beat.ax_user = axi.ar_user; + cycle_end(); + endtask + + /// Monitor the R channel and return the next beat. + task mon_r ( + output r_beat_t beat + ); + cycle_start(); + while (!(axi.r_valid && axi.r_ready)) begin cycle_end(); cycle_start(); end + beat = new; + beat.r_id = axi.r_id; + beat.r_data = axi.r_data; + beat.r_resp = axi.r_resp; + beat.r_last = axi.r_last; + beat.r_user = axi.r_user; + cycle_end(); + endtask + endclass class axi_rand_master #( @@ -776,7 +861,9 @@ package axi_test; for (int i = 0; i < traffic_shape.size(); i++) if (traffic_shape[i].cprob > cprob) begin len = traffic_shape[i].len; - assert (ax_beat.ax_burst == BURST_WRAP -> len inside {len_t'(1), len_t'(3), len_t'(7), len_t'(15)}); + if (ax_beat.ax_burst == BURST_WRAP) begin + assert (len inside {len_t'(1), len_t'(3), len_t'(7), len_t'(15)}); + end break; end @@ -871,7 +958,7 @@ package axi_test; end // Determine `ax_size` and `ax_len`. if (2**beat.ax_size < AXI_STRB_WIDTH) begin - // Transaction does *not* occupy full data bus, so we must send just one beat. [E2.1.3] + // Transaction does *not* occupy full data bus, so we must send just one beat. [E1.1.3] beat.ax_len = '0; end else begin automatic int unsigned bytes; @@ -897,10 +984,10 @@ package axi_test; end // Determine `ax_addr` and `ax_burst`. if (beat.ax_atop == axi_pkg::ATOP_ATOMICCMP) begin - // The address must be aligned to half the outbound data size. [E2-337] + // The address must be aligned to half the outbound data size. [E1.1.3] beat.ax_addr = beat.ax_addr & ~((1'b1 << beat.ax_size) - 1); // If the address is aligned to the total size of outgoing data, the burst type must be - // INCR. Otherwise, it must be WRAP. [E2-338] + // INCR. Otherwise, it must be WRAP. [E1.1.3] beat.ax_burst = (beat.ax_addr % ((beat.ax_len+1) * 2**beat.ax_size) == 0) ? axi_pkg::BURST_INCR : axi_pkg::BURST_WRAP; // If we are not allowed to emit WRAP bursts, align the address to the total size of @@ -910,7 +997,7 @@ package axi_test; beat.ax_burst = axi_pkg::BURST_INCR; end end else begin - // The address must be aligned to the data size. [E2-337] + // The address must be aligned to the data size. [E1.1.3] beat.ax_addr = beat.ax_addr & ~((1'b1 << (beat.ax_size+1)) - 1); // Only INCR allowed. beat.ax_burst = axi_pkg::BURST_INCR; @@ -1011,7 +1098,7 @@ package axi_test; if (beat.ax_atop != 2'b00) begin // This is an ATOP, so it gives rise to a write response. atop_resp_b[beat.ax_id] = 1'b1; - if (beat.ax_atop[5]) begin + if (beat.ax_atop[axi_pkg::ATOP_R_RESP]) begin // This ATOP type additionally gives rise to a read response. atop_resp_r[beat.ax_id] = 1'b1; end @@ -1102,12 +1189,19 @@ package axi_test; static logic rand_success; wait (w_queue.size() > 0 || (aw_done && w_queue.size() == 0)); aw_beat = w_queue.pop_front(); - addr = aw_beat.ax_addr; for (int unsigned i = 0; i < aw_beat.ax_len + 1; i++) begin automatic w_beat_t w_beat = new; automatic int unsigned begin_byte, end_byte, n_bytes; automatic logic [AXI_STRB_WIDTH-1:0] rand_strb, strb_mask; + addr = axi_pkg::beat_addr(aw_beat.ax_addr, aw_beat.ax_size, aw_beat.ax_len, + aw_beat.ax_burst, i); +`ifdef XSIM + // std::randomize(w_beat) may behave differently to w_beat.randomize() wrt. limited ranges + // Keeping alternate implementation for XSIM only + rand_success = std::randomize(w_beat); assert (rand_success); +`else rand_success = w_beat.randomize(); assert (rand_success); +`endif // Determine strobe. w_beat.w_strb = '0; n_bytes = 2**aw_beat.ax_size; @@ -1122,8 +1216,6 @@ package axi_test; w_beat.w_last = (i == aw_beat.ax_len); rand_wait(W_MIN_WAIT_CYCLES, W_MAX_WAIT_CYCLES); drv.send_w(w_beat); - if (aw_beat.ax_burst == axi_pkg::BURST_INCR) - addr += n_bytes; end end endtask @@ -1181,7 +1273,11 @@ package axi_test; parameter int R_MIN_WAIT_CYCLES = 0, parameter int R_MAX_WAIT_CYCLES = 5, parameter int RESP_MIN_WAIT_CYCLES = 0, - parameter int RESP_MAX_WAIT_CYCLES = 20 + parameter int RESP_MAX_WAIT_CYCLES = 20, + /// This parameter eneables an internal memory, which gets randomly initialized, if it is read + /// and retains written data. This mode does currently not support `axi_pkg::BURST_WRAP`! + /// All responses are `axi_pkg::RESP_OKAY` when in this mode. + parameter bit MAPPED = 1'b0 ); typedef axi_test::axi_driver #( .AW(AW), .DW(DW), .IW(IW), .UW(UW), .TA(TA), .TT(TT) @@ -1195,11 +1291,17 @@ package axi_test; typedef axi_driver_t::r_beat_t r_beat_t; typedef axi_driver_t::w_beat_t w_beat_t; + typedef logic [AW-1:0] addr_t; + typedef logic [7:0] byte_t; + axi_driver_t drv; rand_ax_beat_queue_t ar_queue; ax_beat_t aw_queue[$]; int unsigned b_wait_cnt; + // Memory array for when the `MAPPED` parameter is set. + byte_t memory_q[addr_t]; + function new( virtual AXI_BUS_DV #( .AXI_ADDR_WIDTH(AW), @@ -1215,7 +1317,8 @@ package axi_test; endfunction function void reset(); - drv.reset_slave(); + this.drv.reset_slave(); + this.memory_q.delete(); endfunction // TODO: The `rand_wait` task exists in `rand_verif_pkg`, but that task cannot be called with @@ -1235,6 +1338,10 @@ package axi_test; automatic ax_beat_t ar_beat; rand_wait(AX_MIN_WAIT_CYCLES, AX_MAX_WAIT_CYCLES); drv.recv_ar(ar_beat); + if (MAPPED) begin + assert (ar_beat.ax_burst != axi_pkg::BURST_WRAP) else + $error("axi_pkg::BURST_WRAP not supported in MAPPED mode."); + end ar_queue.push(ar_beat.ax_id, ar_beat); end endtask @@ -1243,10 +1350,30 @@ package axi_test; forever begin automatic logic rand_success; automatic ax_beat_t ar_beat; - automatic r_beat_t r_beat = new; + automatic r_beat_t r_beat = new; + automatic addr_t byte_addr; wait (ar_queue.size > 0); - ar_beat = ar_queue.peek(); + ar_beat = ar_queue.peek(); + byte_addr = axi_pkg::aligned_addr(ar_beat.ax_addr, axi_pkg::size_t'($clog2(DW/8))); +`ifdef XSIM + // std::randomize(r_beat) may behave differently to r_beat.randomize() wrt. limited ranges + // Keeping alternate implementation for XSIM only + rand_success = std::randomize(r_beat); assert(rand_success); +`else rand_success = r_beat.randomize(); assert(rand_success); +`endif + if (MAPPED) begin + // Either use the actual data, or save the random generated. + for (int unsigned i = 0; i < (DW/8); i++) begin + if (this.memory_q.exists(byte_addr)) begin + r_beat.r_data[i*8+:8] = this.memory_q[byte_addr]; + end else begin + this.memory_q[byte_addr] = r_beat.r_data[i*8+:8]; + end + byte_addr++; + end + r_beat.r_resp = axi_pkg::RESP_OKAY; + end r_beat.r_id = ar_beat.ax_id; if (RAND_RESP && !ar_beat.ax_atop[axi_pkg::ATOP_R_RESP]) r_beat.r_resp[1] = $random(); @@ -1257,6 +1384,10 @@ package axi_test; r_beat.r_last = 1'b1; void'(ar_queue.pop_id(ar_beat.ax_id)); end else begin + if ((ar_beat.ax_burst == axi_pkg::BURST_INCR) && MAPPED) begin + ar_beat.ax_addr = axi_pkg::aligned_addr(ar_beat.ax_addr, ar_beat.ax_size) + + 2**ar_beat.ax_size; + end ar_beat.ax_len--; ar_queue.set(ar_beat.ax_id, ar_beat); end @@ -1269,9 +1400,15 @@ package axi_test; automatic ax_beat_t aw_beat; rand_wait(AX_MIN_WAIT_CYCLES, AX_MAX_WAIT_CYCLES); drv.recv_aw(aw_beat); + if (MAPPED) begin + assert (aw_beat.ax_atop == '0) else + $error("ATOP not supported in MAPPED mode."); + assert (aw_beat.ax_burst != axi_pkg::BURST_WRAP) else + $error("axi_pkg::BURST_WRAP not supported in MAPPED mode."); + end aw_queue.push_back(aw_beat); // Atomic{Load,Swap,Compare}s require an R response. - if (aw_beat.ax_atop[5]) begin + if (aw_beat.ax_atop[axi_pkg::ATOP_R_RESP]) begin ar_queue.push(aw_beat.ax_id, aw_beat); end end @@ -1280,10 +1417,30 @@ package axi_test; task recv_ws(); forever begin automatic ax_beat_t aw_beat; + automatic addr_t byte_addr; forever begin automatic w_beat_t w_beat; rand_wait(RESP_MIN_WAIT_CYCLES, RESP_MAX_WAIT_CYCLES); drv.recv_w(w_beat); + if (MAPPED) begin + wait (aw_queue.size() > 0); + aw_beat = aw_queue[0]; + byte_addr = axi_pkg::aligned_addr(aw_beat.ax_addr, $clog2(DW/8)); + + // Write Data if the strobe is defined + for (int unsigned i = 0; i < (DW/8); i++) begin + if (w_beat.w_strb[i]) begin + this.memory_q[byte_addr] = w_beat.w_data[i*8+:8]; + end + byte_addr++; + end + // Update address in beat + if (aw_beat.ax_burst == axi_pkg::BURST_INCR) begin + aw_beat.ax_addr = axi_pkg::aligned_addr(aw_beat.ax_addr, aw_beat.ax_size) + + 2**aw_beat.ax_size; + end + aw_queue[0] = aw_beat; + end if (w_beat.w_last) break; end @@ -1298,7 +1455,13 @@ package axi_test; automatic logic rand_success; wait (b_wait_cnt > 0 && (aw_queue.size() != 0)); aw_beat = aw_queue.pop_front(); - rand_success = b_beat.randomize(); assert(rand_success); +`ifdef XSIM + // std::randomize(b_beat) may behave differently to b_beat.randomize() wrt. limited ranges + // Keeping alternate implementation for XSIM only + rand_success = std::randomize(b_beat); assert (rand_success); +`else + rand_success = b_beat.randomize(); assert (rand_success); +`endif b_beat.b_id = aw_beat.ax_id; if (RAND_RESP && !aw_beat.ax_atop[axi_pkg::ATOP_R_RESP]) b_beat.b_resp[1] = $random(); @@ -1306,6 +1469,9 @@ package axi_test; b_beat.b_resp[0]= $random(); end rand_wait(RESP_MIN_WAIT_CYCLES, RESP_MAX_WAIT_CYCLES); + if (MAPPED) begin + b_beat.b_resp = axi_pkg::RESP_OKAY; + end drv.send_b(b_beat); b_wait_cnt--; end @@ -1623,6 +1789,80 @@ package axi_test; endtask endclass + /// AXI Monitor. + class axi_monitor #( + /// AXI4+ATOP ID width + parameter int unsigned IW = 0, + /// AXI4+ATOP address width + parameter int unsigned AW = 0, + /// AXI4+ATOP data width + parameter int unsigned DW = 0, + /// AXI4+ATOP user width + parameter int unsigned UW = 0, + /// Stimuli test time + parameter time TT = 0ns + ); + + typedef axi_test::axi_driver #( + .AW(AW), .DW(DW), .IW(IW), .UW(UW), .TA(TT), .TT(TT) + ) axi_driver_t; + + typedef axi_driver_t::ax_beat_t ax_beat_t; + typedef axi_driver_t::w_beat_t w_beat_t; + typedef axi_driver_t::b_beat_t b_beat_t; + typedef axi_driver_t::r_beat_t r_beat_t; + + axi_driver_t drv; + mailbox aw_mbx = new, w_mbx = new, b_mbx = new, + ar_mbx = new, r_mbx = new; + + 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); + endfunction + + task monitor; + fork + // AW + forever begin + automatic ax_beat_t ax; + this.drv.mon_aw(ax); + aw_mbx.put(ax); + end + // W + forever begin + automatic w_beat_t w; + this.drv.mon_w(w); + w_mbx.put(w); + end + // B + forever begin + automatic b_beat_t b; + this.drv.mon_b(b); + b_mbx.put(b); + end + // AR + forever begin + automatic ax_beat_t ax; + this.drv.mon_ar(ax); + ar_mbx.put(ax); + end + // R + forever begin + automatic r_beat_t r; + this.drv.mon_r(r); + r_mbx.put(r); + end + join + endtask + endclass + /// `axi_scoreboard` models a memory that only gets changed by the monitored AXI4+ATOP bus. /// /// This class is only capable of modeling `INCR` burst type, and cannot handle atomic operations. @@ -1777,8 +2017,6 @@ package axi_test; b_beat = b_sample[id].pop_front(); if (check_en[BRespCheck]) begin assert (b_beat.b_id == id); - assert (b_beat.b_resp == axi_pkg::RESP_OKAY) else - $warning("Behavior for b_resp != axi_pkg::RESP_OKAY not modeled."); end // pop all accessed memory locations by this beat for (int unsigned i = 0; i <= aw_beat.ax_len; i++) begin @@ -1786,7 +2024,11 @@ package axi_test; axi_pkg::beat_addr(aw_beat.ax_addr, aw_beat.ax_size, aw_beat.ax_len, aw_beat.ax_burst, i), BUS_SIZE); for (int j = 0; j < axi_pkg::num_bytes(BUS_SIZE); j++) begin - memory_q[bus_address+j].delete(0); + if (b_beat.b_resp inside {axi_pkg::RESP_OKAY, axi_pkg::RESP_EXOKAY}) begin + memory_q[bus_address+j].delete(0); + end else begin + memory_q[bus_address+j].delete(memory_q[bus_address+j].size() - 1); + end end end end @@ -1820,22 +2062,29 @@ package axi_test; end end // Assert that the correct data is read. - if (this.check_en[ReadCheck]) begin + 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 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]; - tst_data = exp_data.find with (item === 8'hxx || item === act_data); - assert (tst_data.size() > 0) else begin - $warning("Unexpected RData ID: %0h Addr: %0h Byte Idx: %0h Exp Data : %0h Data: %h", - r_beat.r_id, beat_address+j, idx_data, exp_data, act_data); + if (exp_data.size() > 0) begin + tst_data = exp_data.find with (item === 8'hxx || item === act_data); + assert (tst_data.size() > 0) else begin + $warning("Unexpected RData ID: %0h \n \ + Addr: %h \n \ + Byte Idx: %h \n \ + Exp Data: %h \n \ + Act Data: %h \n \ + BeatData: %h", + r_beat.r_id, beat_address+j, idx_data, exp_data, act_data, r_beat.r_data); + end end end end end if (this.check_en[RRespCheck]) begin assert (r_beat.r_id == id); - assert (r_beat.r_resp == axi_pkg::RESP_OKAY); assert (r_beat.r_last); end end @@ -2020,6 +2269,40 @@ package axi_test; assert(this.b_queue[i].size() == 0); end endtask : reset + + /// Check that the byte in memory_q is the same as check_data. + task automatic check_byte(axi_addr_t check_addr, byte_t check_data); + assert(this.memory_q[check_addr][0] === check_data) else + $warning("Byte at ADDR: %h does not match: memory_q: %h check_data: %h", + check_addr, this.memory_q[check_addr][0], check_data); + endtask : check_byte + + /// Clear a byte from memoy. Can be used to partially delete mem space. + task clear_byte(axi_addr_t clear_addr); + if (this.memory_q.exists(clear_addr)) begin + this.memory_q.delete(clear_addr); + end + endtask : clear_byte + + /// Clear a memory range. + /// The end address alo gets cleared. + task automatic clear_range(axi_addr_t clear_start_addr, clear_end_addr); + axi_addr_t curr_addr = clear_start_addr; + while (curr_addr <= clear_end_addr) begin + this.clear_byte(curr_addr); + curr_addr++; + end + endtask : clear_range + + /// Get a byte from the modeled memory. + task automatic get_byte(input axi_addr_t byte_addr, output byte_t byte_data); + if (this.memory_q.exists(byte_addr)) begin + byte_data = this.memory_q[byte_addr][0]; + end else begin + byte_data = 8'hxx; + end + endtask : get_byte + endclass : axi_scoreboard endpackage @@ -2095,7 +2378,7 @@ module axi_chan_logger #( end // inject AR into queue, if there is an atomic - if (aw_chan_i.atop[5]) begin + if (aw_chan_i.atop[axi_pkg::ATOP_R_RESP]) begin $display("Atomic detected with response"); ar_beat.id = aw_chan_i.id; ar_beat.addr = aw_chan_i.addr; diff --git a/vendor/pulp-platform/axi/src/axi_throttle.sv b/vendor/pulp-platform/axi/src/axi_throttle.sv new file mode 100644 index 0000000000..3e7973a08f --- /dev/null +++ b/vendor/pulp-platform/axi/src/axi_throttle.sv @@ -0,0 +1,102 @@ +// Copyright 2022 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 +// +// Authors: +// - Thomas Benz + +/// Throttles an AXI4+ATOP bus. The maximum number of outstanding transfers have to +/// be set as a compile-time parameter, whereas the number of outstanding transfers can be set +/// during runtime. This module assumes either in-order processing of the requests or +/// indistinguishability of the request/responses (all ARs and AWs have the same ID respectively). +module axi_throttle #( + /// The maximum amount of allowable outstanding write requests + parameter int unsigned MaxNumAwPending = 1, + /// The maximum amount of allowable outstanding read requests + parameter int unsigned MaxNumArPending = 1, + /// AXI4+ATOP request type + parameter type axi_req_t = logic, + /// AXI4+ATOP response type + parameter type axi_rsp_t = logic, + /// The width of the write credit counter (*DO NOT OVERWRITE*) + parameter int unsigned WCntWidth = cf_math_pkg::idx_width(MaxNumAwPending), + /// The width of the read credit counter (*DO NOT OVERWRITE*) + parameter int unsigned RCntWidth = cf_math_pkg::idx_width(MaxNumArPending), + /// The type of the write credit counter (*DO NOT OVERWRITE*) + parameter type w_credit_t = logic [WCntWidth-1:0], + /// The type of the read credit counter (*DO NOT OVERWRITE*) + parameter type r_credit_t = logic [RCntWidth-1:0] +) ( + /// Clock + input logic clk_i, + /// Asynchronous reset, active low + input logic rst_ni, + + /// AXI4+ATOP request in + input axi_req_t req_i, + /// AXI4+ATOP response out + output axi_rsp_t rsp_o, + /// AXI4+ATOP request out + output axi_req_t req_o, + /// AXI4+ATOP response in + input axi_rsp_t rsp_i, + + /// Amount of write credit (number of outstanding write transfers) + input w_credit_t w_credit_i, + /// Amount of read credit (number of outstanding read transfers) + input r_credit_t r_credit_i +); + + // ax throttled valids + logic throttled_aw_valid; + logic throttled_ar_valid; + + // ax throttled readies + logic throttled_aw_ready; + logic throttled_ar_ready; + + // limit Aw requests -> wait for b + stream_throttle #( + .MaxNumPending ( MaxNumAwPending ) + ) i_stream_throttle_aw ( + .clk_i, + .rst_ni, + .req_valid_i ( req_i.aw_valid ), + .req_valid_o ( throttled_aw_valid ), + .req_ready_i ( rsp_i.aw_ready ), + .req_ready_o ( throttled_aw_ready ), + .rsp_valid_i ( rsp_i.b_valid ), + .rsp_ready_i ( req_i.b_ready ), + .credit_i ( w_credit_i ) + ); + + // limit Ar requests -> wait for r.last + stream_throttle #( + .MaxNumPending ( MaxNumArPending ) + ) i_stream_throttle_ar ( + .clk_i, + .rst_ni, + .req_valid_i ( req_i.ar_valid ), + .req_valid_o ( throttled_ar_valid ), + .req_ready_i ( rsp_i.ar_ready ), + .req_ready_o ( throttled_ar_ready ), + .rsp_valid_i ( rsp_i.r_valid & rsp_i.r.last ), + .rsp_ready_i ( req_i.r_ready ), + .credit_i ( r_credit_i ) + ); + + // connect the throttled request bus (its a through connection - except for the ax valids) + always_comb begin : gen_throttled_req_conn + req_o = req_i; + req_o.aw_valid = throttled_aw_valid; + req_o.ar_valid = throttled_ar_valid; + end + + // connect the throttled response bus (its a through connection - except for the ax readies) + always_comb begin : gen_throttled_rsp_conn + rsp_o = rsp_i; + rsp_o.aw_ready = throttled_aw_ready; + rsp_o.ar_ready = throttled_ar_ready; + end + +endmodule : axi_throttle 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 c75887a615..a0702bb79c 100644 --- a/vendor/pulp-platform/axi/src/axi_to_axi_lite.sv +++ b/vendor/pulp-platform/axi/src/axi_to_axi_lite.sv @@ -13,7 +13,6 @@ // - Wolfgang Roenninger // - Andreas Kurth // - Fabian Schuiki -// - Florian Zaruba /// An AXI4+ATOP to AXI4-Lite converter with atomic transaction and burst support. module axi_to_axi_lite #( @@ -47,8 +46,8 @@ module axi_to_axi_lite #( axi_atop_filter #( .AxiIdWidth ( AxiIdWidth ), .AxiMaxWriteTxns ( AxiMaxWriteTxns ), - .req_t ( full_req_t ), - .resp_t ( full_resp_t ) + .axi_req_t ( full_req_t ), + .axi_resp_t ( full_resp_t ) ) i_axi_atop_filter( .clk_i ( clk_i ), .rst_ni ( rst_ni ), @@ -66,8 +65,8 @@ module axi_to_axi_lite #( .DataWidth ( AxiDataWidth ), .IdWidth ( AxiIdWidth ), .UserWidth ( AxiUserWidth ), - .req_t ( full_req_t ), - .resp_t ( full_resp_t ) + .axi_req_t ( full_req_t ), + .axi_resp_t ( full_resp_t ) ) i_axi_burst_splitter ( .clk_i ( clk_i ), .rst_ni ( rst_ni ), diff --git a/vendor/pulp-platform/axi/src/axi_to_mem.sv b/vendor/pulp-platform/axi/src/axi_to_mem.sv new file mode 100644 index 0000000000..1b8fd590e8 --- /dev/null +++ b/vendor/pulp-platform/axi/src/axi_to_mem.sv @@ -0,0 +1,758 @@ +// 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 + +`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_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, + /// 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] +) ( + /// 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, write enable. Then asserted store of `mem_w_data` is requested. + output logic [NumBanks-1:0] mem_we_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 +); + + 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 ( + .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 }) + ); + + // 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 + + +`include "axi/assign.svh" +`include "axi/typedef.svh" +/// Interface wrapper for module `axi_to_mem`. +module axi_to_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_we_o`. + output logic [NUM_BANKS-1:0] mem_we_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 +); + 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_mem #( + .axi_req_t ( req_t ), + .axi_resp_t ( resp_t ), + .AddrWidth ( ADDR_WIDTH ), + .DataWidth ( DATA_WIDTH ), + .IdWidth ( ID_WIDTH ), + .NumBanks ( NUM_BANKS ), + .BufDepth ( BUF_DEPTH ), + .HideStrb ( HIDE_STRB ), + .OutFifoDepth ( OUT_FIFO_DEPTH ) + ) i_axi_to_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_we_o, + .mem_rvalid_i, + .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 new file mode 100644 index 0000000000..cc7c6198d3 --- /dev/null +++ b/vendor/pulp-platform/axi/src/axi_to_mem_banked.sv @@ -0,0 +1,435 @@ +// Copyright 2020 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// Authors: +// - Wolfgang Rönninger +// - Michael Rogenmoser + +/// AXI4+ATOP to banked SRAM memory slave. Allows for parallel read and write transactions. +/// Has higher throughput than `axi_to_mem`, however needs more hardware. +/// +/// The used address space starts at 0x0 and ends at the capacity of all memory banks combined. +/// The higher address bits are ignored for accesses. +module axi_to_mem_banked #( + /// AXI4+ATOP ID width + parameter int unsigned AxiIdWidth = 32'd0, + /// AXI4+ATOP address width + parameter int unsigned AxiAddrWidth = 32'd0, + /// AXI4+ATOP data width + parameter int unsigned AxiDataWidth = 32'd0, + /// AXI4+ATOP AW channel struct + parameter type axi_aw_chan_t = logic, + /// AXI4+ATOP W channel struct + parameter type axi_w_chan_t = logic, + /// AXI4+ATOP B channel struct + parameter type axi_b_chan_t = logic, + /// AXI4+ATOP AR channel struct + parameter type axi_ar_chan_t = logic, + /// AXI4+ATOP R channel struct + parameter type axi_r_chan_t = logic, + /// AXI4+ATOP request struct + parameter type axi_req_t = logic, + /// AXI4+ATOP response struct + parameter type axi_resp_t = logic, + /// Number of memory banks / macros + /// Has to satisfy: + /// - MemNumBanks >= 2 * AxiDataWidth / MemDataWidth + /// - MemNumBanks is a power of 2. + parameter int unsigned MemNumBanks = 32'd4, + /// Address width of an individual memory bank. This is treated as a word address. + parameter int unsigned MemAddrWidth = 32'd11, + /// Data width of the memory macros. + /// Has to satisfy: + /// - AxiDataWidth % MemDataWidth = 0 + parameter int unsigned MemDataWidth = 32'd32, + /// Read latency of the connected memory in cycles + parameter int unsigned MemLatency = 32'd1, + /// 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 OVERWRITE! Address type of the memory request. + parameter type mem_addr_t = logic [MemAddrWidth-1:0], + /// DEPENDENT PARAMETER, DO NOT OVERWRITE! Atomic operation type for the memory request. + parameter type mem_atop_t = axi_pkg::atop_t, + /// DEPENDENT PARAMETER, DO NOT OVERWRITE! Data type for the memory request. + parameter type mem_data_t = logic [MemDataWidth-1:0], + /// DEPENDENT PARAMETER, DO NOT OVERWRITE! Byte strobe/enable signal for the memory request. + parameter type mem_strb_t = logic [MemDataWidth/8-1:0] +) ( + /// Clock + input logic clk_i, + /// Asynchronous reset, active low + input logic rst_ni, + /// Testmode enable + input logic test_i, + /// AXI4+ATOP slave port, request struct + input axi_req_t axi_req_i, + /// AXI4+ATOP slave port, response struct + output axi_resp_t axi_resp_o, + /// Memory bank request + output logic [MemNumBanks-1:0] mem_req_o, + /// Memory request grant + input logic [MemNumBanks-1:0] mem_gnt_i, + /// Request address + output mem_addr_t [MemNumBanks-1:0] mem_add_o, + /// Write request enable, active high + output logic [MemNumBanks-1:0] mem_we_o, + /// Write data + output mem_data_t [MemNumBanks-1:0] mem_wdata_o, + /// Write data byte enable, active high + output mem_strb_t [MemNumBanks-1:0] mem_be_o, + /// Atomic operation + output mem_atop_t [MemNumBanks-1:0] mem_atop_o, + /// Read data response + input mem_data_t [MemNumBanks-1:0] mem_rdata_i, + /// Status output, busy flag of `axi_to_mem` + output logic [1:0] axi_to_mem_busy_o +); + /// This specifies the number of banks needed to have the full data bandwidth of one + /// AXI data channel. + localparam int unsigned BanksPerAxiChannel = AxiDataWidth / MemDataWidth; + /// Offset of the byte address from AXI to determine, where the selection signal for the + /// memory bank should start. + localparam int unsigned BankSelOffset = $clog2(MemDataWidth / 32'd8); + /// Selection signal width of the xbar. This is the reason for power of two banks, otherwise + /// There are holes in the address mapping. + localparam int unsigned BankSelWidth = cf_math_pkg::idx_width(MemNumBanks); + typedef logic [BankSelWidth-1:0] xbar_sel_t; + + // Typedef for defining the channels + typedef enum logic { + ReadAccess = 1'b0, + WriteAccess = 1'b1 + } access_type_e; + typedef logic [AxiAddrWidth-1:0] axi_addr_t; + + /// Payload definition which is sent over the xbar between the macros and the read/write unit. + typedef struct packed { + /// Address for the memory access + mem_addr_t addr; + /// Write enable, active high + logic we; + /// Write data + mem_data_t wdata; + /// Strobe signal, byte enable + mem_strb_t wstrb; + /// Atomic operation, from AXI + mem_atop_t atop; + } xbar_payload_t; + + /// Read data definition for the shift register, which samples the read response data + typedef struct packed { + /// Selection signal for response routing + xbar_sel_t sel; + /// Selection is valid + logic valid; + } read_sel_t; + + axi_req_t [1:0] mem_axi_reqs; + axi_resp_t [1:0] mem_axi_resps; + + // Fixed select `axi_demux` to split reads and writes to the two `axi_to_mem` + axi_demux #( + .AxiIdWidth ( AxiIdWidth ), + .AtopSupport ( 1'b1 ), + .aw_chan_t ( axi_aw_chan_t ), + .w_chan_t ( axi_w_chan_t ), + .b_chan_t ( axi_b_chan_t ), + .ar_chan_t ( axi_ar_chan_t ), + .r_chan_t ( axi_r_chan_t ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .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 ), + .SpillAw ( 1'b1 ), + .SpillW ( 1'b1 ), + .SpillB ( 1'b1 ), + .SpillAr ( 1'b1 ), + .SpillR ( 1'b1 ) + ) i_axi_demux ( + .clk_i, + .rst_ni, + .test_i, + .slv_req_i ( axi_req_i ), + .slv_aw_select_i ( WriteAccess ), + .slv_ar_select_i ( ReadAccess ), + .slv_resp_o ( axi_resp_o ), + .mst_reqs_o ( mem_axi_reqs ), + .mst_resps_i ( mem_axi_resps ) + ); + + xbar_payload_t [1:0][BanksPerAxiChannel-1:0] inter_payload; + xbar_sel_t [1:0][BanksPerAxiChannel-1:0] inter_sel; + logic [1:0][BanksPerAxiChannel-1:0] inter_valid, inter_ready; + + // axi_to_mem protocol converter + for (genvar i = 0; i < 2; i++) begin : gen_axi_to_mem + axi_addr_t [BanksPerAxiChannel-1:0] req_addr; // This is a byte address + mem_data_t [BanksPerAxiChannel-1:0] req_wdata, res_rdata; + mem_strb_t [BanksPerAxiChannel-1:0] req_wstrb; + mem_atop_t [BanksPerAxiChannel-1:0] req_atop; + + logic [BanksPerAxiChannel-1:0] req_we, res_valid; + + // Careful, request / grant + // Only assert grant, if there is a ready + axi_to_mem #( + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .AddrWidth ( AxiAddrWidth ), + .DataWidth ( AxiDataWidth ), + .IdWidth ( AxiIdWidth ), + .NumBanks ( BanksPerAxiChannel ), + .BufDepth ( MemLatency ), + .HideStrb ( HideStrb ), + .OutFifoDepth ( OutFifoDepth ) + ) i_axi_to_mem ( + .clk_i, + .rst_ni, + .busy_o ( axi_to_mem_busy_o[i] ), + .axi_req_i ( mem_axi_reqs[i] ), + .axi_resp_o ( mem_axi_resps[i] ), + .mem_req_o ( inter_valid[i] ), + .mem_gnt_i ( inter_ready[i] & inter_valid[i] ), // convert valid/ready to req/gnt + .mem_addr_o ( req_addr ), + .mem_wdata_o ( req_wdata ), + .mem_strb_o ( req_wstrb ), + .mem_atop_o ( req_atop ), + .mem_we_o ( req_we ), + .mem_rvalid_i ( res_valid ), + .mem_rdata_i ( res_rdata ) + ); + // Pack the payload data together + for (genvar j = 0; unsigned'(j) < BanksPerAxiChannel; j++) begin : gen_response_mux + // Cut out the bank selection signal. + assign inter_sel[i][j] = req_addr[j][BankSelOffset+:BankSelWidth]; + + // Assign the xbar payload. + assign inter_payload[i][j] = xbar_payload_t'{ + // Cut out the word address for the banks. + addr: req_addr[j][(BankSelOffset+BankSelWidth)+:MemAddrWidth], + we: req_we[j], + wdata: req_wdata[j], + wstrb: req_wstrb[j], + atop: req_atop[j], + default: '0 + }; + + // Cut out the portion of the address for the bank selection, each bank is word addressed! + read_sel_t r_shift_inp, r_shift_oup; + // Pack the selection into the shift register + assign r_shift_inp = read_sel_t'{ + sel: inter_sel[i][j], // Selection for response multiplexer + valid: inter_valid[i][j] & inter_ready[i][j], // Valid when req to SRAM + default: '0 + }; + + // Select the right read response data. + // Writes should also generate a `response`. + assign res_valid[j] = r_shift_oup.valid; + assign res_rdata[j] = mem_rdata_i[r_shift_oup.sel]; + + // Connect for the response data `MemLatency` cycles after a request was made to the xbar. + shift_reg #( + .dtype ( read_sel_t ), + .Depth ( MemLatency ) + ) i_shift_reg_rdata_mux ( + .clk_i, + .rst_ni, + .d_i ( r_shift_inp ), + .d_o ( r_shift_oup ) + ); + end + end + + // Xbar to arbitrate data over the different memory banks + xbar_payload_t [MemNumBanks-1:0] mem_payload; + + stream_xbar #( + .NumInp ( 32'd2 * BanksPerAxiChannel ), + .NumOut ( MemNumBanks ), + .payload_t ( xbar_payload_t ), + .OutSpillReg ( 1'b0 ), + .ExtPrio ( 1'b0 ), + .AxiVldRdy ( 1'b1 ), + .LockIn ( 1'b1 ) + ) i_stream_xbar ( + .clk_i, + .rst_ni, + .flush_i ( 1'b0 ), + .rr_i ( '0 ), + .data_i ( inter_payload ), + .sel_i ( inter_sel ), + .valid_i ( inter_valid ), + .ready_o ( inter_ready ), + .data_o ( mem_payload ), + .idx_o ( /*not used*/ ), + .valid_o ( mem_req_o ), + .ready_i ( mem_gnt_i ) + ); + + // Memory request output assignment + for (genvar i = 0; unsigned'(i) < MemNumBanks; i++) begin : gen_mem_outp + assign mem_add_o[i] = mem_payload[i].addr; + assign mem_we_o[i] = mem_payload[i].we; + assign mem_wdata_o[i] = mem_payload[i].wdata; + assign mem_be_o[i] = mem_payload[i].wstrb; + assign mem_atop_o[i] = mem_payload[i].atop; + end + +// pragma translate_off +`ifndef VERILATOR + initial begin: p_assertions + assert (AxiIdWidth >= 32'd1) else $fatal(1, "AxiIdWidth must be at least 1!"); + assert (AxiAddrWidth >= 32'd1) else $fatal(1, "AxiAddrWidth must be at least 1!"); + assert (AxiDataWidth >= 32'd1) else $fatal(1, "AxiDataWidth must be at least 1!"); + assert (MemNumBanks >= 32'd2 * AxiDataWidth / MemDataWidth) else + $fatal(1, "MemNumBanks has to be >= 2 * AxiDataWidth / MemDataWidth"); + assert (MemLatency >= 32'd1) else $fatal(1, "MemLatency has to be at least 1!"); + assert ($onehot(MemNumBanks)) else $fatal(1, "MemNumBanks has to be a power of 2."); + assert (MemAddrWidth >= 32'd1) else $fatal(1, "MemAddrWidth must be at least 1!"); + assert (MemDataWidth >= 32'd1) else $fatal(1, "MemDataWidth must be at least 1!"); + assert (AxiDataWidth % MemDataWidth == 0) else + $fatal(1, "MemDataWidth has to be a divisor of AxiDataWidth."); + end +`endif +// pragma translate_on +endmodule + +`include "axi/typedef.svh" +`include "axi/assign.svh" +/// AXI4+ATOP interface wrapper for `axi_to_mem` +module axi_to_mem_banked_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 + /// Has to satisfy: + /// - MemNumBanks >= 2 * AxiDataWidth / MemDataWidth + /// - MemNumBanks is a power of 2. + parameter int unsigned MEM_NUM_BANKS = 32'd4, + /// Address width of an individual memory bank. + parameter int unsigned MEM_ADDR_WIDTH = 32'd11, + /// Data width of the memory macros. + /// Has to satisfy: + /// - AxiDataWidth % MemDataWidth = 0 + parameter int unsigned MEM_DATA_WIDTH = 32'd32, + /// Read latency of the connected memory in cycles + parameter int unsigned MEM_LATENCY = 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 PARAMETERS, DO NOT OVERWRITE! + parameter type mem_addr_t = logic [MEM_ADDR_WIDTH-1:0], + parameter type mem_atop_t = logic [5:0], + parameter type mem_data_t = logic [MEM_DATA_WIDTH-1:0], + parameter type mem_strb_t = logic [MEM_DATA_WIDTH/8-1:0] +) ( + /// Clock + input logic clk_i, + /// Asynchronous reset, active low + input logic rst_ni, + /// Testmode enable + input logic test_i, + /// 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_add_o, + /// Write request enable, active high + output logic [MEM_NUM_BANKS-1:0] mem_we_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_be_o, + /// Atomic operation + output mem_atop_t [MEM_NUM_BANKS-1:0] mem_atop_o, + /// Read data response + input mem_data_t [MEM_NUM_BANKS-1:0] mem_rdata_i, + /// Status output, busy flag of `axi_to_mem` + output logic [1:0] axi_to_mem_busy_o +); + 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_banked #( + .AxiIdWidth ( AXI_ID_WIDTH ), + .AxiAddrWidth ( AXI_ADDR_WIDTH ), + .AxiDataWidth ( AXI_DATA_WIDTH ), + .axi_aw_chan_t ( aw_chan_t ), + .axi_w_chan_t ( w_chan_t ), + .axi_b_chan_t ( b_chan_t ), + .axi_ar_chan_t ( ar_chan_t ), + .axi_r_chan_t ( r_chan_t ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .MemNumBanks ( MEM_NUM_BANKS ), + .MemAddrWidth ( MEM_ADDR_WIDTH ), + .MemDataWidth ( MEM_DATA_WIDTH ), + .MemLatency ( MEM_LATENCY ), + .HideStrb ( HIDE_STRB ), + .OutFifoDepth ( OUT_FIFO_DEPTH ) + ) i_axi_to_mem_banked ( + .clk_i, + .rst_ni, + .test_i, + .axi_to_mem_busy_o, + .axi_req_i ( mem_axi_req ), + .axi_resp_o ( mem_axi_resp ), + .mem_req_o, + .mem_gnt_i, + .mem_add_o, + .mem_wdata_o, + .mem_be_o, + .mem_atop_o, + .mem_we_o, + .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 + diff --git a/vendor/pulp-platform/axi/src/axi_to_mem_interleaved.sv b/vendor/pulp-platform/axi/src/axi_to_mem_interleaved.sv new file mode 100644 index 0000000000..a744c1ec0e --- /dev/null +++ b/vendor/pulp-platform/axi/src/axi_to_mem_interleaved.sv @@ -0,0 +1,248 @@ +// Copyright 2018 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 +// - Thomas Benz +// - Michael Rogenmoser + +/// AXI4+ATOP to SRAM memory slave. Allows for parallel read and write transactions. +/// Allows reads to bypass writes, in contrast to `axi_to_mem`, however needs more hardware. +module axi_to_mem_interleaved #( + /// 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, + /// 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. + parameter type addr_t = logic [AddrWidth-1:0], + /// Dependent parameter, do not override. Memory data type. + parameter type mem_data_t = logic [DataWidth/NumBanks-1:0], + /// Dependent parameter, do not override. Memory write strobe type. + parameter type mem_strb_t = logic [DataWidth/NumBanks/8-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, write enable. Then asserted store of `mem_w_data` is requested. + output logic [NumBanks-1:0] mem_we_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 +); + + // internal signals + logic w_busy, r_busy; + logic [NumBanks-1:0] arb_outcome, arb_outcome_head; + + // internal AXI buses + axi_req_t r_axi_req, w_axi_req; + axi_resp_t r_axi_resp, w_axi_resp; + + // internal TCDM buses + logic [NumBanks-1:0] r_mem_req, w_mem_req; + logic [NumBanks-1:0] r_mem_gnt, w_mem_gnt; + addr_t [NumBanks-1:0] r_mem_addr, w_mem_addr; + mem_data_t [NumBanks-1:0] r_mem_wdata, w_mem_wdata; + mem_strb_t [NumBanks-1:0] r_mem_strb, w_mem_strb; + axi_pkg::atop_t [NumBanks-1:0] r_mem_atop, w_mem_atop; + logic [NumBanks-1:0] r_mem_we, w_mem_we; + logic [NumBanks-1:0] r_mem_rvalid, w_mem_rvalid; + 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_to_mem #( + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .AddrWidth ( AddrWidth ), + .DataWidth ( DataWidth ), + .IdWidth ( IdWidth ), + .NumBanks ( NumBanks ), + .BufDepth ( BufDepth ), + .HideStrb ( HideStrb ), + .OutFifoDepth( OutFifoDepth ) + ) i_axi_to_mem_write ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .busy_o ( w_busy ), + .axi_req_i ( w_axi_req ), + .axi_resp_o ( w_axi_resp ), + .mem_req_o ( w_mem_req ), + .mem_gnt_i ( w_mem_gnt ), + .mem_addr_o ( w_mem_addr ), + .mem_wdata_o ( w_mem_wdata ), + .mem_strb_o ( w_mem_strb ), + .mem_atop_o ( w_mem_atop ), + .mem_we_o ( w_mem_we ), + .mem_rvalid_i ( w_mem_rvalid ), + .mem_rdata_i ( w_mem_rdata ) + ); + + axi_to_mem #( + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .AddrWidth ( AddrWidth ), + .DataWidth ( DataWidth ), + .IdWidth ( IdWidth ), + .NumBanks ( NumBanks ), + .BufDepth ( BufDepth ), + .HideStrb ( HideStrb ), + .OutFifoDepth ( OutFifoDepth ) + ) i_axi_to_mem_read ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .busy_o ( r_busy ), + .axi_req_i ( r_axi_req ), + .axi_resp_o ( r_axi_resp ), + .mem_req_o ( r_mem_req ), + .mem_gnt_i ( r_mem_gnt ), + .mem_addr_o ( r_mem_addr ), + .mem_wdata_o ( r_mem_wdata ), + .mem_strb_o ( r_mem_strb ), + .mem_atop_o ( r_mem_atop ), + .mem_we_o ( r_mem_we ), + .mem_rvalid_i ( r_mem_rvalid ), + .mem_rdata_i ( r_mem_rdata ) + ); + + // create a struct for the rr-arb-tree + typedef struct packed { + addr_t addr; + mem_data_t wdata; + mem_strb_t strb; + logic we; + axi_pkg::atop_t atop; + } mem_req_payload_t; + + mem_req_payload_t [NumBanks-1:0] r_payload, w_payload, payload; + + for (genvar i = 0; i < NumBanks; i++) begin + // pack the mem + assign r_payload[i].addr = r_mem_addr[i]; + assign r_payload[i].wdata = r_mem_wdata[i]; + assign r_payload[i].strb = r_mem_strb[i]; + assign r_payload[i].we = r_mem_we[i]; + assign r_payload[i].atop = r_mem_atop[i]; + + assign w_payload[i].addr = w_mem_addr[i]; + assign w_payload[i].wdata = w_mem_wdata[i]; + assign w_payload[i].strb = w_mem_strb[i]; + assign w_payload[i].we = w_mem_we[i]; + assign w_payload[i].atop = w_mem_atop[i]; + + assign mem_addr_o [i] = payload[i].addr; + assign mem_wdata_o[i] = payload[i].wdata; + assign mem_strb_o [i] = payload[i].strb; + assign mem_we_o [i] = payload[i].we; + assign mem_atop_o [i] = payload[i].atop; + + // route data back to both channels + assign w_mem_rdata[i] = mem_rdata_i[i]; + assign r_mem_rdata[i] = mem_rdata_i[i]; + + assign w_mem_rvalid[i] = mem_rvalid_i[i] & !arb_outcome_head[i]; + assign r_mem_rvalid[i] = mem_rvalid_i[i] & arb_outcome_head[i]; + + // fine-grain arbitration + rr_arb_tree #( + .NumIn ( 2 ), + .DataType ( mem_req_payload_t ) + ) i_rr_arb_tree ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .flush_i ( 1'b0 ), + .rr_i ( '0 ), + .req_i ( { r_mem_req[i], w_mem_req[i] } ), + .gnt_o ( { r_mem_gnt[i], w_mem_gnt[i] } ), + .data_i ( { r_payload[i], w_payload[i] } ), + .req_o ( mem_req_o[i] ), + .gnt_i ( mem_gnt_i[i] ), + .data_o ( payload[i] ), + .idx_o ( arb_outcome[i] ) + ); + + // back-routing store + fifo_v3 #( + .DATA_WIDTH ( 1 ), + .DEPTH ( BufDepth + 1 ) + ) i_fifo_v3_response_trgt_store ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .flush_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .full_o ( ), + .empty_o ( ), + .usage_o ( ), + .data_i ( arb_outcome[i] ), + .push_i ( mem_req_o[i] & mem_gnt_i[i] ), + .data_o ( arb_outcome_head[i] ), + .pop_i ( mem_rvalid_i[i] ) + ); + end + +endmodule : axi_to_mem_interleaved diff --git a/vendor/pulp-platform/axi/src/axi_to_mem_split.sv b/vendor/pulp-platform/axi/src/axi_to_mem_split.sv new file mode 100644 index 0000000000..107d3fe753 --- /dev/null +++ b/vendor/pulp-platform/axi/src/axi_to_mem_split.sv @@ -0,0 +1,250 @@ +// Copyright 2022 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 + +`include "axi/assign.svh" +/// AXI4+ATOP to memory-protocol interconnect. Completely separates the read and write channel to +/// individual mem ports. This can only be used when addresses for the same bank are accessible +/// from different memory ports. +module axi_to_mem_split #( + /// 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 AxiDataWidth = 0, + /// AXI4+ATOP ID width. + parameter int unsigned IdWidth = 0, + /// Memory data width, must evenly divide `DataWidth`. + parameter int unsigned MemDataWidth = 0, // must divide `AxiDataWidth` without remainder + /// Depth of memory response buffer. This should be equal to the memory response latency. + parameter int unsigned BufDepth = 0, + /// 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 parameters, do not override. Number of memory ports. + parameter int unsigned NumMemPorts = 2*AxiDataWidth/MemDataWidth, + /// Dependent parameter, do not override. Memory address type. + parameter type addr_t = logic [AddrWidth-1:0], + /// Dependent parameter, do not override. Memory data type. + parameter type mem_data_t = logic [MemDataWidth-1:0], + /// Dependent parameter, do not override. Memory write strobe type. + parameter type mem_strb_t = logic [MemDataWidth/8-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 [NumMemPorts-1:0] mem_req_o, + /// Memory stream master, request can be granted by this bank. + input logic [NumMemPorts-1:0] mem_gnt_i, + /// Memory stream master, byte address of the request. + output addr_t [NumMemPorts-1:0] mem_addr_o, // byte address + /// Memory stream master, write data for this bank. Valid when `mem_req_o`. + output mem_data_t [NumMemPorts-1:0] mem_wdata_o, // write data + /// Memory stream master, byte-wise strobe (byte enable). + output mem_strb_t [NumMemPorts-1:0] mem_strb_o, // byte-wise strobe + /// Memory stream master, `axi_pkg::atop_t` signal associated with this request. + output axi_pkg::atop_t [NumMemPorts-1:0] mem_atop_o, // atomic operation + /// Memory stream master, write enable. Then asserted store of `mem_w_data` is requested. + output logic [NumMemPorts-1:0] mem_we_o, // write enable + /// 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 [NumMemPorts-1:0] mem_rvalid_i, // response valid + /// Memory stream master, read response data. + input mem_data_t [NumMemPorts-1:0] mem_rdata_i // read data +); + + axi_req_t axi_read_req, axi_write_req; + axi_resp_t axi_read_resp, axi_write_resp; + + logic read_busy, write_busy; + + axi_rw_split #( + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ) + ) i_axi_rw_split ( + .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 ) + ); + + assign busy_o = read_busy || write_busy; + + axi_to_mem #( + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .AddrWidth ( AddrWidth ), + .DataWidth ( AxiDataWidth ), + .IdWidth ( IdWidth ), + .NumBanks ( NumMemPorts/2 ), + .BufDepth ( BufDepth ), + .HideStrb ( 1'b0 ), + .OutFifoDepth ( OutFifoDepth ) + ) i_axi_to_mem_read ( + .clk_i, + .rst_ni, + .busy_o ( read_busy ), + .axi_req_i ( axi_read_req ), + .axi_resp_o ( axi_read_resp ), + .mem_req_o ( mem_req_o [NumMemPorts/2-1:0] ), + .mem_gnt_i ( mem_gnt_i [NumMemPorts/2-1:0] ), + .mem_addr_o ( mem_addr_o [NumMemPorts/2-1:0] ), + .mem_wdata_o ( mem_wdata_o [NumMemPorts/2-1:0] ), + .mem_strb_o ( mem_strb_o [NumMemPorts/2-1:0] ), + .mem_atop_o ( mem_atop_o [NumMemPorts/2-1:0] ), + .mem_we_o ( mem_we_o [NumMemPorts/2-1:0] ), + .mem_rvalid_i ( mem_rvalid_i [NumMemPorts/2-1:0] ), + .mem_rdata_i ( mem_rdata_i [NumMemPorts/2-1:0] ) + ); + + axi_to_mem #( + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .AddrWidth ( AddrWidth ), + .DataWidth ( AxiDataWidth ), + .IdWidth ( IdWidth ), + .NumBanks ( NumMemPorts/2 ), + .BufDepth ( BufDepth ), + .HideStrb ( HideStrb ), + .OutFifoDepth ( OutFifoDepth ) + ) i_axi_to_mem_write ( + .clk_i, + .rst_ni, + .busy_o ( write_busy ), + .axi_req_i ( axi_write_req ), + .axi_resp_o ( axi_write_resp ), + .mem_req_o ( mem_req_o [NumMemPorts-1:NumMemPorts/2] ), + .mem_gnt_i ( mem_gnt_i [NumMemPorts-1:NumMemPorts/2] ), + .mem_addr_o ( mem_addr_o [NumMemPorts-1:NumMemPorts/2] ), + .mem_wdata_o ( mem_wdata_o [NumMemPorts-1:NumMemPorts/2] ), + .mem_strb_o ( mem_strb_o [NumMemPorts-1:NumMemPorts/2] ), + .mem_atop_o ( mem_atop_o [NumMemPorts-1:NumMemPorts/2] ), + .mem_we_o ( mem_we_o [NumMemPorts-1:NumMemPorts/2] ), + .mem_rvalid_i ( mem_rvalid_i [NumMemPorts-1:NumMemPorts/2] ), + .mem_rdata_i ( mem_rdata_i [NumMemPorts-1:NumMemPorts/2] ) + ); + +endmodule + +`include "axi/typedef.svh" +/// AXI4+ATOP interface wrapper for `axi_to_mem_split` +module axi_to_mem_split_intf #( + /// AXI4+ATOP ID width + parameter int unsigned AXI_ID_WIDTH = 32'b0, + /// AXI4+ATOP address width + parameter int unsigned AXI_ADDR_WIDTH = 32'b0, + /// AXI4+ATOP data width + parameter int unsigned AXI_DATA_WIDTH = 32'b0, + /// AXI4+ATOP user width + parameter int unsigned AXI_USER_WIDTH = 32'b0, + /// Memory data width, must evenly divide `DataWidth`. + parameter int unsigned MEM_DATA_WIDTH = 32'b0, + /// See `axi_to_mem`, parameter `BufDepth`. + parameter int unsigned BUF_DEPTH = 0, + /// 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 parameters, do not override. Number of memory ports. + parameter int unsigned NUM_MEM_PORTS = 2*AXI_DATA_WIDTH/MEM_DATA_WIDTH, + /// Dependent parameter, do not override. See `axi_to_mem`, parameter `addr_t`. + parameter type addr_t = logic [AXI_ADDR_WIDTH-1:0], + /// Dependent parameter, do not override. See `axi_to_mem`, parameter `mem_data_t`. + parameter type mem_data_t = logic [MEM_DATA_WIDTH-1:0], + /// Dependent parameter, do not override. See `axi_to_mem`, parameter `mem_strb_t`. + parameter type mem_strb_t = logic [MEM_DATA_WIDTH/8-1:0] +) ( + /// Clock input. + input logic clk_i, + /// Asynchronous reset, active low. + input logic rst_ni, + /// See `axi_to_mem_split`, port `busy_o`. + output logic busy_o, + /// AXI4+ATOP slave interface port. + AXI_BUS.Slave axi_bus, + /// See `axi_to_mem_split`, port `mem_req_o`. + output logic [NUM_MEM_PORTS-1:0] mem_req_o, + /// See `axi_to_mem_split`, port `mem_gnt_i`. + input logic [NUM_MEM_PORTS-1:0] mem_gnt_i, + /// See `axi_to_mem_split`, port `mem_addr_o`. + output addr_t [NUM_MEM_PORTS-1:0] mem_addr_o, + /// See `axi_to_mem_split`, port `mem_wdata_o`. + output mem_data_t [NUM_MEM_PORTS-1:0] mem_wdata_o, + /// See `axi_to_mem_split`, port `mem_strb_o`. + output mem_strb_t [NUM_MEM_PORTS-1:0] mem_strb_o, + /// See `axi_to_mem_split`, port `mem_atop_o`. + output axi_pkg::atop_t [NUM_MEM_PORTS-1:0] mem_atop_o, + /// See `axi_to_mem_split`, port `mem_we_o`. + output logic [NUM_MEM_PORTS-1:0] mem_we_o, + /// See `axi_to_mem_split`, port `mem_rvalid_i`. + input logic [NUM_MEM_PORTS-1:0] mem_rvalid_i, + /// See `axi_to_mem_split`, port `mem_rdata_i`. + input mem_data_t [NUM_MEM_PORTS-1:0] mem_rdata_i +); + + typedef logic [AXI_ID_WIDTH-1:0] id_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_ALL(axi, addr_t, id_t, data_t, strb_t, user_t) + + axi_req_t axi_req; + axi_resp_t axi_resp; + `AXI_ASSIGN_TO_REQ(axi_req, axi_bus) + `AXI_ASSIGN_FROM_RESP(axi_bus, axi_resp) + + axi_to_mem_split #( + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .AxiDataWidth ( AXI_DATA_WIDTH ), + .AddrWidth ( AXI_ADDR_WIDTH ), + .IdWidth ( AXI_ID_WIDTH ), + .MemDataWidth ( MEM_DATA_WIDTH ), // must divide `AxiDataWidth` without remainder + .BufDepth ( BUF_DEPTH ), + .HideStrb ( HIDE_STRB ), + .OutFifoDepth ( OUT_FIFO_DEPTH ) + ) i_axi_to_mem_split ( + .clk_i, + .rst_ni, + .busy_o, + .axi_req_i (axi_req), + .axi_resp_o (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 + ); + +endmodule diff --git a/vendor/pulp-platform/axi/src/axi_xbar.sv b/vendor/pulp-platform/axi/src/axi_xbar.sv index d66cd97d5d..52769a66cb 100644 --- a/vendor/pulp-platform/axi/src/axi_xbar.sv +++ b/vendor/pulp-platform/axi/src/axi_xbar.sv @@ -13,41 +13,102 @@ // - Andreas Kurth // - Florian Zaruba -// axi_xbar: Fully-connected AXI4+ATOP crossbar with an arbitrary number of slave and master ports. -// See `doc/axi_xbar.md` for the documentation, including the definition of parameters and ports. -module axi_xbar #( - parameter axi_pkg::xbar_cfg_t Cfg = '0, - parameter bit ATOPs = 1'b1, - parameter type slv_aw_chan_t = logic, - parameter type mst_aw_chan_t = logic, - parameter type w_chan_t = logic, - parameter type slv_b_chan_t = logic, - parameter type mst_b_chan_t = logic, - parameter type slv_ar_chan_t = logic, - parameter type mst_ar_chan_t = logic, - parameter type slv_r_chan_t = logic, - parameter type mst_r_chan_t = logic, - parameter type slv_req_t = logic, - parameter type slv_resp_t = logic, - parameter type mst_req_t = logic, - parameter type mst_resp_t = logic, - parameter type rule_t = axi_pkg::xbar_rule_64_t +/// axi_xbar: Fully-connected AXI4+ATOP crossbar with an arbitrary number of slave and master ports. +/// See `doc/axi_xbar.md` for the documentation, including the definition of parameters and ports. +module axi_xbar +import cf_math_pkg::idx_width; +#( + /// Configuration struct for the crossbar see `axi_pkg` for fields and definitions. + parameter axi_pkg::xbar_cfg_t Cfg = '0, + /// Enable atomic operations support. + parameter bit ATOPs = 1'b1, + /// Connectivity matrix + parameter bit [Cfg.NoSlvPorts-1:0][Cfg.NoMstPorts-1:0] Connectivity = '1, + /// AXI4+ATOP AW channel struct type for the slave ports. + parameter type slv_aw_chan_t = logic, + /// AXI4+ATOP AW channel struct type for the master ports. + parameter type mst_aw_chan_t = logic, + /// AXI4+ATOP W channel struct type for all ports. + parameter type w_chan_t = logic, + /// AXI4+ATOP B channel struct type for the slave ports. + parameter type slv_b_chan_t = logic, + /// AXI4+ATOP B channel struct type for the master ports. + parameter type mst_b_chan_t = logic, + /// AXI4+ATOP AR channel struct type for the slave ports. + parameter type slv_ar_chan_t = logic, + /// AXI4+ATOP AR channel struct type for the master ports. + parameter type mst_ar_chan_t = logic, + /// AXI4+ATOP R channel struct type for the slave ports. + parameter type slv_r_chan_t = logic, + /// AXI4+ATOP R channel struct type for the master ports. + parameter type mst_r_chan_t = logic, + /// AXI4+ATOP request struct type for the slave ports. + parameter type slv_req_t = logic, + /// AXI4+ATOP response struct type for the slave ports. + parameter type slv_resp_t = logic, + /// AXI4+ATOP request struct type for the master ports. + parameter type mst_req_t = logic, + /// AXI4+ATOP response struct type for the master ports + parameter type mst_resp_t = logic, + /// Address rule type for the address decoders from `common_cells:addr_decode`. + /// Example types are provided in `axi_pkg`. + /// Required struct fields: + /// ``` + /// typedef struct packed { + /// int unsigned idx; + /// axi_addr_t start_addr; + /// axi_addr_t end_addr; + /// } rule_t; + /// ``` + parameter type rule_t = axi_pkg::xbar_rule_64_t +`ifdef VCS + , localparam int unsigned MstPortsIdxWidth = + (Cfg.NoMstPorts == 32'd1) ? 32'd1 : unsigned'($clog2(Cfg.NoMstPorts)) +`endif ) ( - input logic clk_i, - input logic rst_ni, - input logic test_i, - input slv_req_t [Cfg.NoSlvPorts-1:0] slv_ports_req_i, - output slv_resp_t [Cfg.NoSlvPorts-1:0] slv_ports_resp_o, - output mst_req_t [Cfg.NoMstPorts-1:0] mst_ports_req_o, - input mst_resp_t [Cfg.NoMstPorts-1:0] mst_ports_resp_i, - input rule_t [Cfg.NoAddrRules-1:0] addr_map_i, - input logic [Cfg.NoSlvPorts-1:0] en_default_mst_port_i, - input logic [Cfg.NoSlvPorts-1:0][$clog2(Cfg.NoMstPorts)-1:0] default_mst_port_i + /// Clock, positive edge triggered. + input logic clk_i, + /// Asynchronous reset, active low. + input logic rst_ni, + /// Testmode enable, active high. + input logic test_i, + /// AXI4+ATOP requests to the slave ports. + input slv_req_t [Cfg.NoSlvPorts-1:0] slv_ports_req_i, + /// AXI4+ATOP responses of the slave ports. + output slv_resp_t [Cfg.NoSlvPorts-1:0] slv_ports_resp_o, + /// AXI4+ATOP requests of the master ports. + output mst_req_t [Cfg.NoMstPorts-1:0] mst_ports_req_o, + /// AXI4+ATOP responses to the master ports. + input mst_resp_t [Cfg.NoMstPorts-1:0] mst_ports_resp_i, + /// Address map array input for the crossbar. This map is global for the whole module. + /// It is used for routing the transactions to the respective master ports. + /// Each master port can have multiple different rules. + input rule_t [Cfg.NoAddrRules-1:0] addr_map_i, + /// Enable default master port. + input logic [Cfg.NoSlvPorts-1:0] en_default_mst_port_i, +`ifdef VCS + /// Enables a default master port for each slave port. When this is enabled unmapped + /// transactions get issued at the master port given by `default_mst_port_i`. + /// When not used, tie to `'0`. + input logic [Cfg.NoSlvPorts-1:0][MstPortsIdxWidth-1:0] default_mst_port_i +`else + /// Enables a default master port for each slave port. When this is enabled unmapped + /// transactions get issued at the master port given by `default_mst_port_i`. + /// When not used, tie to `'0`. + input logic [Cfg.NoSlvPorts-1:0][idx_width(Cfg.NoMstPorts)-1:0] default_mst_port_i +`endif ); - typedef logic [Cfg.AxiAddrWidth-1:0] addr_t; + // Address tpye for inidvidual address signals + typedef logic [Cfg.AxiAddrWidth-1:0] addr_t; // to account for the decoding error slave - typedef logic [$clog2(Cfg.NoMstPorts + 1)-1:0] mst_port_idx_t; +`ifdef VCS + localparam int unsigned MstPortsIdxWidthOne = + (Cfg.NoMstPorts == 32'd1) ? 32'd1 : unsigned'($clog2(Cfg.NoMstPorts + 1)); + typedef logic [MstPortsIdxWidthOne-1:0] mst_port_idx_t; +`else + typedef logic [idx_width(Cfg.NoMstPorts + 1)-1:0] mst_port_idx_t; +`endif // signals from the axi_demuxes, one index more for decode error slv_req_t [Cfg.NoSlvPorts-1:0][Cfg.NoMstPorts:0] slv_reqs; @@ -61,10 +122,14 @@ module axi_xbar #( slv_resp_t [Cfg.NoMstPorts-1:0][Cfg.NoSlvPorts-1:0] mst_resps; for (genvar i = 0; i < Cfg.NoSlvPorts; i++) begin : gen_slv_port_demux - logic [$clog2(Cfg.NoMstPorts)-1:0] dec_aw, dec_ar; - mst_port_idx_t slv_aw_select, slv_ar_select; - logic dec_aw_valid, dec_aw_error; - logic dec_ar_valid, dec_ar_error; +`ifdef VCS + logic [MstPortsIdxWidth-1:0] dec_aw, dec_ar; +`else + logic [idx_width(Cfg.NoMstPorts)-1:0] dec_aw, dec_ar; +`endif + mst_port_idx_t slv_aw_select, slv_ar_select; + logic dec_aw_valid, dec_aw_error; + logic dec_ar_valid, dec_ar_error; addr_decode #( .NoIndices ( Cfg.NoMstPorts ), @@ -131,18 +196,18 @@ module axi_xbar #( // pragma translate_on axi_demux #( .AxiIdWidth ( Cfg.AxiIdWidthSlvPorts ), // ID Width + .AtopSupport ( ATOPs ), .aw_chan_t ( slv_aw_chan_t ), // AW Channel Type .w_chan_t ( w_chan_t ), // W Channel Type .b_chan_t ( slv_b_chan_t ), // B Channel Type .ar_chan_t ( slv_ar_chan_t ), // AR Channel Type .r_chan_t ( slv_r_chan_t ), // R Channel Type - .req_t ( slv_req_t ), - .resp_t ( slv_resp_t ), + .axi_req_t ( slv_req_t ), + .axi_resp_t ( slv_resp_t ), .NoMstPorts ( Cfg.NoMstPorts + 1 ), .MaxTrans ( Cfg.MaxMstTrans ), .AxiLookBits ( Cfg.AxiIdUsedSlvPorts ), .UniqueIds ( Cfg.UniqueIds ), - .FallThrough ( Cfg.FallThrough ), .SpillAw ( Cfg.LatencyMode[9] ), .SpillW ( Cfg.LatencyMode[8] ), .SpillB ( Cfg.LatencyMode[7] ), @@ -162,8 +227,8 @@ module axi_xbar #( axi_err_slv #( .AxiIdWidth ( Cfg.AxiIdWidthSlvPorts ), - .req_t ( slv_req_t ), - .resp_t ( slv_resp_t ), + .axi_req_t ( slv_req_t ), + .axi_resp_t ( slv_resp_t ), .Resp ( axi_pkg::RESP_DECERR ), .ATOPs ( ATOPs ), .MaxTrans ( 4 ) // Transactions terminate at this slave, so minimize @@ -182,8 +247,42 @@ module axi_xbar #( // cross all channels for (genvar i = 0; i < Cfg.NoSlvPorts; i++) begin : gen_xbar_slv_cross for (genvar j = 0; j < Cfg.NoMstPorts; j++) begin : gen_xbar_mst_cross - assign mst_reqs[j][i] = slv_reqs[i][j]; - assign slv_resps[i][j] = mst_resps[j][i]; + if (Connectivity[i][j]) begin : gen_connection + axi_multicut #( + .NoCuts ( Cfg.PipelineStages ), + .aw_chan_t ( slv_aw_chan_t ), + .w_chan_t ( w_chan_t ), + .b_chan_t ( slv_b_chan_t ), + .ar_chan_t ( slv_ar_chan_t ), + .r_chan_t ( slv_r_chan_t ), + .axi_req_t ( slv_req_t ), + .axi_resp_t ( slv_resp_t ) + ) i_axi_multicut_xbar_pipeline ( + .clk_i, + .rst_ni, + .slv_req_i ( slv_reqs[i][j] ), + .slv_resp_o ( slv_resps[i][j] ), + .mst_req_o ( mst_reqs[j][i] ), + .mst_resp_i ( mst_resps[j][i] ) + ); + + end else begin : gen_no_connection + assign mst_reqs[j][i] = '0; + axi_err_slv #( + .AxiIdWidth ( Cfg.AxiIdWidthSlvPorts ), + .axi_req_t ( slv_req_t ), + .axi_resp_t ( slv_resp_t ), + .Resp ( axi_pkg::RESP_DECERR ), + .ATOPs ( ATOPs ), + .MaxTrans ( 1 ) + ) i_axi_err_slv ( + .clk_i, + .rst_ni, + .test_i, + .slv_req_i ( slv_reqs[i][j] ), + .slv_resp_o ( slv_resps[i][j] ) + ); + end end end @@ -239,19 +338,31 @@ endmodule `include "axi/assign.svh" `include "axi/typedef.svh" -module axi_xbar_intf #( +module axi_xbar_intf +import cf_math_pkg::idx_width; +#( parameter int unsigned AXI_USER_WIDTH = 0, parameter axi_pkg::xbar_cfg_t Cfg = '0, + parameter bit ATOPS = 1'b1, + parameter bit [Cfg.NoSlvPorts-1:0][Cfg.NoMstPorts-1:0] CONNECTIVITY = '1, parameter type rule_t = axi_pkg::xbar_rule_64_t +`ifdef VCS + , localparam int unsigned MstPortsIdxWidth = + (Cfg.NoMstPorts == 32'd1) ? 32'd1 : unsigned'($clog2(Cfg.NoMstPorts)) +`endif ) ( - input logic clk_i, - input logic rst_ni, - input logic test_i, - AXI_BUS.Slave slv_ports [Cfg.NoSlvPorts-1:0], - AXI_BUS.Master mst_ports [Cfg.NoMstPorts-1:0], - input rule_t [Cfg.NoAddrRules-1:0] addr_map_i, - input logic [Cfg.NoSlvPorts-1:0] en_default_mst_port_i, - input logic [Cfg.NoSlvPorts-1:0][$clog2(Cfg.NoMstPorts)-1:0] default_mst_port_i + input logic clk_i, + input logic rst_ni, + input logic test_i, + AXI_BUS.Slave slv_ports [Cfg.NoSlvPorts-1:0], + AXI_BUS.Master mst_ports [Cfg.NoMstPorts-1:0], + input rule_t [Cfg.NoAddrRules-1:0] addr_map_i, + input logic [Cfg.NoSlvPorts-1:0] en_default_mst_port_i, +`ifdef VCS + input logic [Cfg.NoSlvPorts-1:0][MstPortsIdxWidth-1:0] default_mst_port_i +`else + input logic [Cfg.NoSlvPorts-1:0][idx_width(Cfg.NoMstPorts)-1:0] default_mst_port_i +`endif ); localparam int unsigned AxiIdWidthMstPorts = Cfg.AxiIdWidthSlvPorts + $clog2(Cfg.NoSlvPorts); @@ -294,6 +405,8 @@ module axi_xbar_intf #( axi_xbar #( .Cfg (Cfg), + .ATOPs ( ATOPS ), + .Connectivity ( CONNECTIVITY ), .slv_aw_chan_t ( slv_aw_chan_t ), .mst_aw_chan_t ( mst_aw_chan_t ), .w_chan_t ( w_chan_t ), diff --git a/vendor/pulp-platform/axi/src/axi_xp.sv b/vendor/pulp-platform/axi/src/axi_xp.sv new file mode 100644 index 0000000000..5ccbfb9ff0 --- /dev/null +++ b/vendor/pulp-platform/axi/src/axi_xp.sv @@ -0,0 +1,256 @@ +// Copyright (c) 2020 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: +// - Tim Fischer +// - Andreas Kurth +// - Vikram Jain + +`include "axi/typedef.svh" + +/// AXI Crosspoint (XP) with homomorphous slave and master ports. +module axi_xp #( + // Atomic operations settings + parameter bit ATOPs = 1'b1, + // xbar configuration + parameter axi_pkg::xbar_cfg_t Cfg = '0, + /// Number of slave ports. + parameter int unsigned NumSlvPorts = 32'd0, + /// Number of master ports. + parameter int unsigned NumMstPorts = 32'd0, + /// Connectivity from a slave port to the master ports. A `1'b1` in `Connectivity[i][j]` means + /// that slave port `i` is connected to master port `j`. By default, all slave ports are + /// connected to all master ports. + parameter bit [NumSlvPorts-1:0][NumMstPorts-1:0] Connectivity = '1, + /// Address width of all ports. + parameter int unsigned AxiAddrWidth = 32'd0, + /// Data width of all ports. + parameter int unsigned AxiDataWidth = 32'd0, + /// ID width of all ports. + parameter int unsigned AxiIdWidth = 32'd0, + /// User signal width of all ports. + parameter int unsigned AxiUserWidth = 32'd0, + /// Maximum number of different IDs that can be in flight at each slave port. Reads and writes + /// are counted separately (except for ATOPs, which count as both read and write). + /// + /// It is legal for upstream to have transactions with more unique IDs than the maximum given by + /// this parameter in flight, but a transaction exceeding the maximum will be stalled until all + /// transactions of another ID complete. + parameter int unsigned AxiSlvPortMaxUniqIds = 32'd0, + /// Maximum number of in-flight transactions with the same ID at the slave port. + /// + /// This parameter is only relevant if `AxiSlvPortMaxUniqIds <= 2**AxiMstPortIdWidth`. In that + /// case, this parameter is passed to [`axi_id_remap` as `AxiMaxTxnsPerId` + /// parameter](module.axi_id_remap#parameter.AxiMaxTxnsPerId). + parameter int unsigned AxiSlvPortMaxTxnsPerId = 32'd0, + /// Maximum number of in-flight transactions at the slave port. Reads and writes are counted + /// separately (except for ATOPs, which count as both read and write). + /// + /// This parameter is only relevant if `AxiSlvPortMaxUniqIds > 2**AxiMstPortIdWidth`. In that + /// case, this parameter is passed to + /// [`axi_id_serialize`](module.axi_id_serialize#parameter.AxiSlvPortMaxTxns). + parameter int unsigned AxiSlvPortMaxTxns = 32'd0, + /// Maximum number of different IDs that can be in flight at the master port. Reads and writes + /// are counted separately (except for ATOPs, which count as both read and write). + /// + /// This parameter is only relevant if `AxiSlvPortMaxUniqIds > 2**AxiMstPortIdWidth`. In that + /// case, this parameter is passed to + /// [`axi_id_serialize`](module.axi_id_serialize#parameter.AxiMstPortMaxUniqIds). + parameter int unsigned AxiMstPortMaxUniqIds = 32'd0, + /// Maximum number of in-flight transactions with the same ID at the master port. + /// + /// This parameter is only relevant if `AxiSlvPortMaxUniqIds > 2**AxiMstPortIdWidth`. In that + /// case, this parameter is passed to + /// [`axi_id_serialize`](module.axi_id_serialize#parameter.AxiMstPortMaxTxnsPerId). + parameter int unsigned AxiMstPortMaxTxnsPerId = 32'd0, + /// Number of rules in the address map. + parameter int unsigned NumAddrRules = 32'd0, + /// Request struct type of the AXI4+ATOP + parameter type axi_req_t = logic, + /// Response struct type of the AXI4+ATOP + parameter type axi_resp_t = logic, + /// Rule type (see documentation of `axi_xbar` for details). + parameter type rule_t = axi_pkg::xbar_rule_64_t +) ( + /// Rising-edge clock of all ports + input logic clk_i, + /// Asynchronous reset, active low + input logic rst_ni, + /// Test mode enable + input logic test_en_i, + /// Slave ports request + input axi_req_t [NumSlvPorts-1:0] slv_req_i, + /// Slave ports response + output axi_resp_t [NumSlvPorts-1:0] slv_resp_o, + /// Master ports request + output axi_req_t [NumMstPorts-1:0] mst_req_o, + /// Master ports response + input axi_resp_t [NumMstPorts-1:0] mst_resp_i, + /// Address map for transferring transactions from slave to master ports + input rule_t [NumAddrRules-1:0] addr_map_i +); + + // The master port of the Xbar has a different ID width than the slave ports. + parameter int unsigned AxiXbarIdWidth = AxiIdWidth + $clog2(NumSlvPorts); + typedef logic [AxiAddrWidth-1:0] addr_t; + typedef logic [AxiDataWidth-1:0] data_t; + typedef logic [AxiIdWidth-1:0] id_t; + typedef logic [AxiXbarIdWidth-1:0] xbar_id_t; + typedef logic [AxiDataWidth/8-1:0] strb_t; + typedef logic [AxiUserWidth-1:0] user_t; + + + `AXI_TYPEDEF_ALL(xp, addr_t, id_t, data_t, strb_t, user_t) + `AXI_TYPEDEF_ALL(xbar, addr_t, xbar_id_t, data_t, strb_t, user_t) + + xbar_req_t [NumMstPorts-1:0] xbar_req; + xbar_resp_t [NumMstPorts-1:0] xbar_resp; + + axi_xbar #( + .Cfg ( Cfg ), + .ATOPs ( ATOPs ), + .Connectivity ( Connectivity ), + .slv_aw_chan_t ( xp_aw_chan_t ), + .mst_aw_chan_t ( xbar_aw_chan_t ), + .w_chan_t ( xp_w_chan_t ), + .slv_b_chan_t ( xp_b_chan_t ), + .mst_b_chan_t ( xbar_b_chan_t ), + .slv_ar_chan_t ( xp_ar_chan_t ), + .mst_ar_chan_t ( xbar_ar_chan_t ), + .slv_r_chan_t ( xp_r_chan_t ), + .mst_r_chan_t ( xbar_r_chan_t ), + .slv_req_t ( axi_req_t ), + .slv_resp_t ( axi_resp_t ), + .mst_req_t ( xbar_req_t ), + .mst_resp_t ( xbar_resp_t ), + .rule_t ( rule_t ) + ) i_xbar ( + .clk_i, + .rst_ni, + .test_i ( test_en_i ), + .slv_ports_req_i ( slv_req_i ), + .slv_ports_resp_o ( slv_resp_o ), + .mst_ports_req_o ( xbar_req ), + .mst_ports_resp_i ( xbar_resp ), + .addr_map_i, + .en_default_mst_port_i ( '0 ), + .default_mst_port_i ( '0 ) + ); + + for (genvar i = 0; i < NumMstPorts; i++) begin : gen_remap + axi_id_remap #( + .AxiSlvPortIdWidth ( AxiXbarIdWidth ), + .AxiSlvPortMaxUniqIds ( AxiSlvPortMaxUniqIds ), + .AxiMaxTxnsPerId ( AxiSlvPortMaxTxnsPerId ), + .AxiMstPortIdWidth ( AxiIdWidth ), + .slv_req_t ( xbar_req_t ), + .slv_resp_t ( xbar_resp_t ), + .mst_req_t ( axi_req_t ), + .mst_resp_t ( axi_resp_t ) + ) i_axi_id_remap ( + .clk_i, + .rst_ni, + .slv_req_i ( xbar_req[i] ), + .slv_resp_o ( xbar_resp[i] ), + .mst_req_o ( mst_req_o[i] ), + .mst_resp_i ( mst_resp_i[i] ) + ); + end + +endmodule + +`include "axi/assign.svh" +`include "axi/typedef.svh" + +module axi_xp_intf +import cf_math_pkg::idx_width; +#( + parameter bit ATOPs = 1'b1, + parameter axi_pkg::xbar_cfg_t Cfg = '0, + parameter int unsigned NumSlvPorts = 32'd0, + parameter int unsigned NumMstPorts = 32'd0, + parameter bit [NumSlvPorts-1:0][NumMstPorts-1:0] Connectivity = '1, + parameter int unsigned AxiAddrWidth = 32'd0, + parameter int unsigned AxiDataWidth = 32'd0, + parameter int unsigned AxiIdWidth = 32'd0, + parameter int unsigned AxiUserWidth = 32'd0, + parameter int unsigned AxiSlvPortMaxUniqIds = 32'd0, + parameter int unsigned AxiSlvPortMaxTxnsPerId = 32'd0, + parameter int unsigned AxiSlvPortMaxTxns = 32'd0, + parameter int unsigned AxiMstPortMaxUniqIds = 32'd0, + parameter int unsigned AxiMstPortMaxTxnsPerId = 32'd0, + parameter int unsigned NumAddrRules = 32'd0, + parameter type rule_t = axi_pkg::xbar_rule_64_t +) ( + input logic clk_i, + input logic rst_ni, + input logic test_en_i, + AXI_BUS.Slave slv_ports [NumSlvPorts-1:0], + AXI_BUS.Master mst_ports [NumMstPorts-1:0], + input rule_t [NumAddrRules-1:0] addr_map_i +); + + // localparam int unsigned AxiIdWidthMstPorts = AxiIdWidth + $clog2(NoSlvPorts); + + typedef logic [AxiIdWidth -1:0] id_t; + typedef logic [AxiAddrWidth -1:0] addr_t; + typedef logic [AxiDataWidth -1:0] data_t; + typedef logic [AxiDataWidth/8 -1:0] strb_t; + typedef logic [AxiUserWidth -1:0] user_t; + + `AXI_TYPEDEF_ALL(axi, addr_t, id_t, data_t, strb_t, user_t) + + axi_req_t [NumMstPorts-1:0] mst_reqs; + axi_resp_t [NumMstPorts-1:0] mst_resps; + axi_req_t [NumSlvPorts-1:0] slv_reqs; + axi_resp_t [NumSlvPorts-1:0] slv_resps; + + for (genvar i = 0; i < NumMstPorts; i++) begin : gen_assign_mst + `AXI_ASSIGN_FROM_REQ(mst_ports[i], mst_reqs[i]) + `AXI_ASSIGN_TO_RESP(mst_resps[i], mst_ports[i]) + end + + for (genvar i = 0; i < NumSlvPorts; i++) begin : gen_assign_slv + `AXI_ASSIGN_TO_REQ(slv_reqs[i], slv_ports[i]) + `AXI_ASSIGN_FROM_RESP(slv_ports[i], slv_resps[i]) + end + + axi_xp #( + .ATOPs ( ATOPs ), + .Cfg ( Cfg ), + .NumSlvPorts ( NumSlvPorts ), + .NumMstPorts ( NumMstPorts ), + .Connectivity ( Connectivity ), + .AxiAddrWidth ( AxiAddrWidth ), + .AxiDataWidth ( AxiDataWidth ), + .AxiIdWidth ( AxiIdWidth ), + .AxiUserWidth ( AxiUserWidth ), + .AxiSlvPortMaxUniqIds ( AxiSlvPortMaxUniqIds ), + .AxiSlvPortMaxTxnsPerId ( AxiSlvPortMaxTxnsPerId ), + .AxiSlvPortMaxTxns ( AxiSlvPortMaxTxns ), + .AxiMstPortMaxUniqIds ( AxiMstPortMaxUniqIds ), + .AxiMstPortMaxTxnsPerId ( AxiMstPortMaxTxnsPerId ), + .NumAddrRules ( NumAddrRules ), + .axi_req_t ( axi_req_t ), + .axi_resp_t ( axi_resp_t ), + .rule_t ( rule_t ) + ) i_xp ( + .clk_i, + .rst_ni, + .test_en_i, + .slv_req_i (slv_reqs ), + .slv_resp_o (slv_resps), + .mst_req_o (mst_reqs ), + .mst_resp_i (mst_resps), + .addr_map_i + ); + +endmodule diff --git a/vendor/pulp-platform_axi.lock.hjson b/vendor/pulp-platform_axi.lock.hjson index 5a52f240a1..0227d9911f 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: 697f13ff67153a5243e347f2d1992a125018b6c2 + rev: af8b0ce2653997301b1b792c4c6d207b95f63a56 } } diff --git a/vendor/pulp-platform_axi.vendor.hjson b/vendor/pulp-platform_axi.vendor.hjson index 55b8626f64..e7f39596cf 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.31.0", + rev: "v0.39.0-beta.2", } // Patch dir for local changes