Vendorize corev_apu submodules referenced by CVA6 core. (#1015)

This commit is contained in:
Zbigniew Chamski 2022-12-13 12:20:36 +01:00 committed by GitHub
parent 4b33e69a10
commit 17ccfc42f4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
72 changed files with 19090 additions and 27 deletions

View file

@ -24,7 +24,7 @@ core/include/riscv_pkg.sv
corev_apu/riscv-dbg/src/dm_pkg.sv
core/include/ariane_pkg.sv
corev_apu/tb/ariane_soc_pkg.sv
corev_apu/axi/src/axi_pkg.sv
vendor/pulp-platform/axi/src/axi_pkg.sv
core/include/ariane_axi_pkg.sv
core/include/wt_cache_pkg.sv
core/include/axi_intf.sv
@ -50,11 +50,11 @@ vendor/pulp-platform/common_cells/src/cdc_2phase.sv
vendor/pulp-platform/common_cells/src/unread.sv
vendor/pulp-platform/common_cells/src/popcount.sv
corev_apu/axi_mem_if/src/axi2mem.sv
corev_apu/src/tech_cells_generic/src/deprecated/cluster_clk_cells.sv
corev_apu/src/tech_cells_generic/src/deprecated/pulp_clk_cells.sv
vendor/pulp-platform/tech_cells_generic/src/deprecated/cluster_clk_cells.sv
vendor/pulp-platform/tech_cells_generic/src/deprecated/pulp_clk_cells.sv
common/local/util/tc_sram_wrapper.sv
corev_apu/src/tech_cells_generic/src/rtl/tc_sram.sv
corev_apu/src/tech_cells_generic/src/rtl/tc_clk.sv
vendor/pulp-platform/tech_cells_generic/src/rtl/tc_sram.sv
vendor/pulp-platform/tech_cells_generic/src/rtl/tc_clk.sv
core/axi_adapter.sv
core/alu.sv
core/fpu_wrap.sv

View file

@ -160,7 +160,7 @@ src := corev_apu/tb/axi_adapter.sv
corev_apu/riscv-dbg/src/dm_top.sv \
corev_apu/riscv-dbg/debug_rom/debug_rom.sv \
corev_apu/register_interface/src/apb_to_reg.sv \
corev_apu/axi/src/axi_multicut.sv \
vendor/pulp-platform/axi/src/axi_multicut.sv \
vendor/pulp-platform/common_cells/src/rstgen_bypass.sv \
vendor/pulp-platform/common_cells/src/rstgen.sv \
vendor/pulp-platform/common_cells/src/stream_mux.sv \
@ -168,16 +168,16 @@ src := corev_apu/tb/axi_adapter.sv
vendor/pulp-platform/common_cells/src/exp_backoff.sv \
vendor/pulp-platform/common_cells/src/addr_decode.sv \
vendor/pulp-platform/common_cells/src/stream_register.sv \
corev_apu/axi/src/axi_cut.sv \
corev_apu/axi/src/axi_join.sv \
corev_apu/axi/src/axi_delayer.sv \
corev_apu/axi/src/axi_to_axi_lite.sv \
corev_apu/axi/src/axi_id_prepend.sv \
corev_apu/axi/src/axi_atop_filter.sv \
corev_apu/axi/src/axi_err_slv.sv \
corev_apu/axi/src/axi_mux.sv \
corev_apu/axi/src/axi_demux.sv \
corev_apu/axi/src/axi_xbar.sv \
vendor/pulp-platform/axi/src/axi_cut.sv \
vendor/pulp-platform/axi/src/axi_join.sv \
vendor/pulp-platform/axi/src/axi_delayer.sv \
vendor/pulp-platform/axi/src/axi_to_axi_lite.sv \
vendor/pulp-platform/axi/src/axi_id_prepend.sv \
vendor/pulp-platform/axi/src/axi_atop_filter.sv \
vendor/pulp-platform/axi/src/axi_err_slv.sv \
vendor/pulp-platform/axi/src/axi_mux.sv \
vendor/pulp-platform/axi/src/axi_demux.sv \
vendor/pulp-platform/axi/src/axi_xbar.sv \
vendor/pulp-platform/common_cells/src/cdc_2phase.sv \
vendor/pulp-platform/common_cells/src/spill_register_flushable.sv \
vendor/pulp-platform/common_cells/src/spill_register.sv \
@ -187,9 +187,9 @@ src := corev_apu/tb/axi_adapter.sv
vendor/pulp-platform/common_cells/src/deprecated/fifo_v2.sv \
vendor/pulp-platform/common_cells/src/stream_delay.sv \
vendor/pulp-platform/common_cells/src/lfsr_16bit.sv \
corev_apu/src/tech_cells_generic/src/deprecated/cluster_clk_cells.sv \
corev_apu/src/tech_cells_generic/src/deprecated/pulp_clk_cells.sv \
corev_apu/src/tech_cells_generic/src/rtl/tc_clk.sv \
vendor/pulp-platform/tech_cells_generic/src/deprecated/cluster_clk_cells.sv \
vendor/pulp-platform/tech_cells_generic/src/deprecated/pulp_clk_cells.sv \
vendor/pulp-platform/tech_cells_generic/src/rtl/tc_clk.sv \
corev_apu/tb/ariane_testharness.sv \
corev_apu/tb/ariane_peripherals.sv \
corev_apu/tb/rvfi_tracer.sv \
@ -236,7 +236,7 @@ riscv-fp-tests := $(shell xargs printf '\n%s' < $(riscv-fp-tests-list
riscv-benchmarks := $(shell xargs printf '\n%s' < $(riscv-benchmarks-list) | cut -b 1-)
# Search here for include files (e.g.: non-standalone components)
incdir := vendor/pulp-platform/common_cells/include/ corev_apu/axi/include/ corev_apu/register_interface/include/
incdir := vendor/pulp-platform/common_cells/include/ vendor/pulp-platform/axi/include/ corev_apu/register_interface/include/
# Compile and sim flags
compile_flag += +cover=bcfst+/dut -incr -64 -nologo -quiet -suppress 13262 -permissive +define+$(defines)
@ -285,9 +285,9 @@ vcs_build: $(dpi-library)/ariane_dpi.so
vlogan $(if $(VERDI), -kdb,) -full64 -nc -sverilog +define+$(defines) -f ../core/Flist.cva6 &&\
vlogan $(if $(VERDI), -kdb,) -full64 -nc -sverilog +define+$(defines) $(filter %.sv,$(ariane_pkg)) +incdir+core/include/+$(VCS_HOME)/etc/uvm-1.2/dpi &&\
vhdlan $(if $(VERDI), -kdb,) -full64 -nc $(filter %.vhd,$(uart_src)) &&\
vlogan $(if $(VERDI), -kdb,) -full64 -nc -sverilog -assert svaext +define+$(defines) $(filter %.sv,$(src)) +incdir+../vendor/pulp-platform/common_cells/include/+../corev_apu/axi/include/+../corev_apu/register_interface/include/ &&\
vlogan $(if $(VERDI), -kdb,) -full64 -nc -sverilog -assert svaext +define+$(defines) $(filter %.sv,$(src)) +incdir+../vendor/pulp-platform/common_cells/include/+../vendor/pulp-platform/axi/include/+../corev_apu/register_interface/include/ &&\
vlogan $(if $(VERDI), -kdb,) -full64 -nc -sverilog -ntb_opts uvm-1.2 &&\
vlogan $(if $(VERDI), -kdb,) -full64 -nc -sverilog -ntb_opts uvm-1.2 $(tbs) +define+$(defines) +incdir+../corev_apu/axi/include/ &&\
vlogan $(if $(VERDI), -kdb,) -full64 -nc -sverilog -ntb_opts uvm-1.2 $(tbs) +define+$(defines) +incdir+../vendor/pulp-platform/axi/include/ &&\
vcs $(if $(VERDI), -kdb -debug_access+all -lca,) -full64 -timescale=1ns/1ns -ntb_opts uvm-1.2 work.ariane_tb
vcs: vcs_build
@ -725,7 +725,7 @@ fpga_filter += $(addprefix $(root-dir), src/util/ex_trace_item.sv)
fpga_filter += $(addprefix $(root-dir), src/util/instr_trace_item.sv)
fpga_filter += $(addprefix $(root-dir), common/local/util/instr_tracer_if.sv)
fpga_filter += $(addprefix $(root-dir), common/local/util/instr_tracer.sv)
fpga_filter += $(addprefix $(root-dir), corev_apu/src/tech_cells_generic/src/rtl/tc_sram.sv)
fpga_filter += $(addprefix $(root-dir), vendor/pulp-platform/tech_cells_generic/src/rtl/tc_sram.sv)
fpga_filter += $(addprefix $(root-dir), common/local/util/tc_sram_wrapper.sv)
fpga: $(ariane_pkg) $(src) $(fpga_src) $(uart_src) $(src_flist)

View file

@ -36,7 +36,7 @@ ${CVA6_REPO_DIR}/core/include/riscv_pkg.sv
${CVA6_REPO_DIR}/core/include/ariane_dm_pkg.sv
${CVA6_REPO_DIR}/core/include/ariane_pkg.sv
// TODO: ariane_axi_pkg is dependent on this.
${CVA6_REPO_DIR}/corev_apu/axi/src/axi_pkg.sv
${CVA6_REPO_DIR}/vendor/pulp-platform/axi/src/axi_pkg.sv
${CVA6_REPO_DIR}/core/include/ariane_rvfi_pkg.sv
// Packages
@ -150,7 +150,7 @@ ${CVA6_REPO_DIR}/core/pmp/src/pmp_entry.sv
${CVA6_REPO_DIR}/common/local/util/instr_tracer_if.sv
${CVA6_REPO_DIR}/common/local/util/instr_tracer.sv
${CVA6_REPO_DIR}/common/local/util/tc_sram_wrapper.sv
${CVA6_REPO_DIR}/corev_apu/src/tech_cells_generic/src/rtl/tc_sram.sv
${CVA6_REPO_DIR}/vendor/pulp-platform/tech_cells_generic/src/rtl/tc_sram.sv
${CVA6_REPO_DIR}/common/local/util/sram.sv
// MMU Sv39

View file

@ -13,7 +13,7 @@ ${CVA6_REPO_DIR}/core/include/riscv_pkg.sv
${CVA6_REPO_DIR}/core/include/ariane_dm_pkg.sv
${CVA6_REPO_DIR}/core/include/ariane_pkg.sv
// TODO: ariane_axi_pkg is dependent on this.
${CVA6_REPO_DIR}/corev_apu/axi/src/axi_pkg.sv
${CVA6_REPO_DIR}/vendor/pulp-platform/axi/src/axi_pkg.sv
${CVA6_REPO_DIR}/core/include/ariane_rvfi_pkg.sv
${CVA6_REPO_DIR}/core/include/cvxif_pkg.sv
@ -29,5 +29,5 @@ ${CVA6_REPO_DIR}/pd/synth/cva6_${TARGET_CFG}_synth_modified.v
${CVA6_REPO_DIR}/pd/synth/tc_sram_wrapper_256_64_00000008_00000001_00000001_none_0.sv
${CVA6_REPO_DIR}/common/local/util/tc_sram_wrapper.sv
${CVA6_REPO_DIR}/corev_apu/src/tech_cells_generic/src/rtl/tc_sram.sv
${CVA6_REPO_DIR}/vendor/pulp-platform/tech_cells_generic/src/rtl/tc_sram.sv
${CVA6_REPO_DIR}/common/local/util/sram.sv

7
vendor/pulp-platform/axi/.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
.*
!.git*
!.ci/
/.git/
/build
/Bender.lock
/Bender.local

735
vendor/pulp-platform/axi/CHANGELOG.md vendored Normal file
View file

@ -0,0 +1,735 @@
# Changelog
All notable changes to this project will be documented in this file.
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).
## 0.31.0 - 2021-12-07
### Added
- Add three modules to convert between any two AXI ID widths under many different concurrency
requirements:
- `axi_iw_converter` is the top-level module that converts between any two AXI ID widths with all
supported parameters. It upsizes IDs by extending the MSBs with zeros and joins two interfaces
with identical ID widths. For downsizing IDs, it instantiates one of the following two modules:
- `axi_id_remap` remaps AXI IDs from wide IDs at the slave port to narrower IDs at the master
port without serializing transactions.
- `axi_id_serialize` reduces AXI IDs by serializing transactions when necessary.
## 0.30.0 - 2021-12-01
### Added
- Add `axi_lite_xbar_intf` interface variant of `axi_lite_xbar`.
### Fixed
- `axi_lite_demux`: Improve compatibility with new version of QuestaSim's optimizer (`vopt`).
Before this workaround, QuestaSim 2021.1 could segfault on instances of `axi_lite_demux`.
## 0.29.2 - 2021-11-12
### Fixed
- `axi_demux`: Improve compatibility with VCS (#187). The workaround of #169 was not compatible
with VCS 2020.12. That workaround is now only active if `TARGET_VSIM` is defined.
- `axi_dw_downsizer` and `axi_dw_upsizer` (part of `axi_dw_converter`): Avoid latch inference on the
Mentor Precision synthesis tool.
- `axi_lite_cdc_src_intf`: Fix `_i` and `_o` suffixes in instantiation of `axi_cdc_src`.
- `axi_test::axi_rand_slave`: Improve compatibility with VCS (#175).
- `axi_test::axi_scoreboard`: Add default value to parameters to improve compatibility with some
tools.
## 0.29.1 - 2021-06-02
### Fixed
- `axi_lite_to_apb_intf`: Add missing parameters, which were added to `axi_lite_to_apb` in v0.28.0.
## 0.29.0 - 2021-05-06
### Changed
- `axi_xbar` and `axi_demux`: Add support for unique IDs by adding a `UniqueIds` parameter to both
modules (#172). If you can guarantee that the ID of each transaction is always unique among all
in-flight transactions in the same direction, setting the `UniqueIds` parameter to `1'b1`
simplifies the demultiplexer (see documentation of `axi_demux` for details). This change is
backward-compatible on `axi_demux` (because the default value of the new parameter is `1'b0`).
As `axi_xbar` is configured with the `xbar_cfg_t` `struct`, this change is *not
backward-compatible* for `axi_xbar` (except for `xbar_cfg_t`s initialized with a `default` part).
### Fixed
- `axi_test::axi_rand_master`: Refactor ID legalization into common function to simplify the
implementation and remove redundant code. No known functional bug was fixed, but the correctness
of the refactored code can be asserted more easily.
## 0.28.0 - 2021-04-15
### Added
- Add source- and destination-clock-domain "halves" for the clock domain crossing (CDC):
`axi_cdc_src` and `axi_cdc_dst`. This is implemented by refactoring the `axi_cdc` module, so the
implementation is reused from the existing `axi_cdc` module. To avoid code duplication, `axi_cdc`
now instantiates an `axi_cdc_src` connected to an `axi_cdc_dst`.
### Changed
- `axi_lite_to_apb`: Make pipeline registers on request and response path optional (can be enabled
with the new `PipelineRequest` and `PipelineResponse` `parameter`s), and disable those pipeline
registers by default.
### Fixed
- `axi_demux`: Improve compatibility with new version of QuestaSim's optimizer (`vopt`) (#169).
Before this workaround, QuestaSim 2020.2 and 2021.1 could segfault on instances of `axi_demux`.
## 0.27.1 - 2021-02-01
### Fixed
- `axi_dw_downsizer` and `axi_dw_upsizer` (part of `axi_dw_converter`): Fix declaration order of
`w_req_t`, `w_req_d`, and `w_req_q` to remove problematic forward references.
- FuseSoC: Fix version of `common_cells` (`1.21.0`).
## 0.27.0 - 2021-02-01
### Added
- `assign.svh`: Add macros for assigning between AXI-Lite `struct`s, both inside a process
(`AXI_LITE_SET_*_STRUCT`) and outside a process (`AXI_LITE_ASSIGN_*_STRUCT`). This is safer than
assigning `struct`s with a simple `=`, because the macros assign individual fields.
- `typedef.svh`: Add `AXI_TYPEDEF_ALL` and `AXI_LITE_TYPEDEF_ALL` macros for defining all channels
and request/response `struct`s of an AXI4+ATOPs and an AXI4-Lite interface, respectively, in a
single macro call.
- `axi_test::axi_rand_slave`: Add parameter `RAND_RESP`, which enables randomization of the `resp`
field in B and R beats.
### Changed
- `axi_test::axi_rand_master`: Randomize the QoS field.
- Update `common_verification` dependency to `0.2.0`, which has been released for more than a year.
- Update `common_cells` dependency to `1.21.0` to align on version `0.2.0` of the
`common_verification` dependency. This includes version `1.20.1` of `common_cells`, which fixes
an out-of-bounds index in `axi_burst_splitter` (#150).
## 0.26.0 - 2021-01-19
### Added
- Add infinite, simulation-only memory `axi_sim_mem`.
- `assign.svh`: Add macros for assigning between `struct`s, both inside a process
(`AXI_SET_*_STRUCT`) and outside a process (`AXI_ASSIGN_*_STRUCT`). This is safer than assigning
`struct`s with a simple `=`, because the macros assign individual fields. (Fields that mismatch
between two `struct`s, e.g., due to different `user` signal widths, should, and in some cases
must, be still assigned separately.)
### Changed
- Rename the following classes in `axi_test` to follow the convention that all user-facing objects
in this repository start with `axi_`:
- `rand_axi_lite_master` to `axi_lite_rand_master`,
- `rand_axi_lite_slave` to `axi_lite_rand_slave`,
- `rand_axi_master` to `axi_rand_master`, and
- `rand_axi_slave` to `axi_rand_slave`.
## 0.25.0 - 2021-01-14
### Added
- `axi_xbar`: Add parameter to disable support for atomic operations (`ATOPs`).
### Changed
- `AXI_BUS`, `AXI_BUS_ASYNC`, `AXI_BUS_DV`, `AXI_LITE`, and `AXI_LITE_DV`: Change type of every
parameter from `int` to `int unsigned`. An unsigned type is more appropriate, because none of
those parameters can actually take a negative value, and it improves compatibility with some
tools.
- `axi_test::rand_axi_lite_slave` and `axi_test::rand_axi_lite_master`: Change type of address and
data width parameters (`AW` and `DW`) from `int` to `int unsigned`. Same rationale as for
`AXI_BUS` (et al.) above.
### Fixed
- `axi_demux`: Break combinatorial simulation loop.
- `axi_xbar`: Improve compatibility with vsim version 10.6c (and earlier) by introducing a
workaround for a tool limitation (#133).
- `tb_axi_lite_regs`: Removed superfluous hardcoded assertion.
- Improve compatibility with Vivado XSim by disabling formal properties in `axi_demux`,
`axi_err_slv`, and `axi_xbar` if `XSIM` is defined.
## 0.24.2 - 2021-01-11
### Changed
- `axi_test::rand_axi_lite_master` and `axi_test::rand_axi_lite_slave`: Specify default values for
parameters to improve compatibility with tools that require a default value for every parameter.
### Fixed
- `axi_lite_demux`: Move `typedef` out of `generate` block to improve compatibility with VCS.
- `axi_test::rand_axi_master` and `axi_test::rand_axi_slave`: Fix call to `randomize` function for
class variables. Prior to this fix, the `std::randomize()` function was used for three class
variables, but class variables must use the `.randomize()` member function.
## 0.24.1 - 2020-11-04
### Changed
- Update `common_cells` dependency to `1.20.0` to fix file order in IPApproX.
### Fixed
- `doc/axi_lite_mailbox`: Fix position of `RFIFOL` and `WFIFOL` in `STATUS` register.
- IPApproX:
- Add missing link against `common_cells_lib`.
- Fix include path for `common_cells`.
- Fix version specification of `common_verification`.
## 0.24.0 - 2020-10-27
### Added
- `axi_pkg`: Add function that defines response precedence.
### Changed
- `axi_dw_downsizer` and `axi_dw_upsizer`: Pipeline injection of atomic AWs into the AR channel to
shorten the critical path.
### Fixed
- `axi_dw_downsizer` and `axi_dw_upsizer`: Improve portability of bit slice assignment constructs.
- `axi_dw_downsizer`:
- Forward worst response among split transactions.
- Fix overflow of B forward FIFO.
- `axi_test`: Remove minimal length constraint from `rand_atop_burst`.
## 0.23.2 - 2020-09-14
### Fixed
- `ips_list.yml`: Add missing `common_verification` dependency.
## 0.23.1 - 2020-06-19
### Fixed
- `axi_lite_demux_intf`: Fix passing of `req_t` and `resp_t` parameters to `axi_lite_demux`.
- `axi_lite_xbar`: Add missing `slv_a{w,r}_cache_i` connections on `axi_lite_to_axi` instance.
## 0.23.0 - 2020-05-11
### Added
- `axi_lite_regs`: Add memory-mapped registers with AXI4-Lite slave port and the option to make
individual bytes read-only.
### Changed
- Interfaces `AXI_LITE` and `AXI_LITE_DV`: add `aw_prot` and `ar_prot` signals.
- The `AXI_LITE_ASSIGN*` and `AXI_LITE_SET*` macros (in `include/axi/assign.svh`) have been
updated to include the two new interface signals.
- `axi_test::axi_lite_driver`: A new `prot` function argument has been added to the `send_aw`,
`send_ar`, `recv_aw`, and `recv_ar` functions.
- `axi_test::rand_axi_lite_master`:
- A new `w_prot` and `r_prot` function argument has been added to the `write` and `read`
function, respectively. The new arguments have a default value of `'0`.
- The `send_aws` and the `send_ars` function now randomizes the `prot` signal of each AW and AR,
respectively.
- `axi_test::rand_axi_slave`: Display `prot` signal (but otherwise still ignore it).
### Fixed
- `rand_axi_master` (in `axi_test`): Another fix to respect burst type restrictions when emitting
ATOPs.
## 0.22.1 - 2020-05-11
### Fixed
- `rand_axi_master` (in `axi_test`): Respect burst type restrictions when emitting ATOPs.
## 0.22.0 - 2020-05-01
### Added
- `axi_pkg`: Add `bufferable` and `modifiable` helper functions.
- `axi_dw_converter`: Add support for single-beat *fixed* bursts in the downsizer and for *fixed*
bursts of any length in the upsizer.
### Changed
- `axi_dw_downsizer` (part of `axi_dw_converter`): Downsize regardless of the *modifiable* bit of
incoming transactions. Previously, non-*modifiable* transactions whose attributes would have to
be modified for downsizing were rejected with a slave error. As of this change, transactions are
downsized and their attributes modified even if their *modifiable* bit is not set. This is
permitted by a note in the AXI specification (page A4-65 of IHI0022H).
### Fixed
- `axi_dw_downsizer` (part of `axi_dw_converter`): Fix condition for keeping transactions that have
a smaller `size` than the master/downstream port unmodified.
## 0.21.0 - 2020-04-27
### Added
- `axi_serializer`: serialize transactions with different IDs to the same ID.
### Changed
- `axi_modify_address`:
- Simplify redundant `slv_resp_t` and `mst_resp_t` parameters to single `axi_resp_t` parameter.
- Remove unnecessary `slv_a{r,w}_addr_o` outputs, which were fed back from the `slv_req_i` inputs.
Those signals can instead be derived outside `axi_modify_address`.
- `axi_modify_address_intf`:
- Change name of slave port to `slv` and master port to `mst` and change name of associated
parameters to align them with repository conventions.
- Change type of parameters to `int unsigned` because their values are unsigned.
- Add parameters for data, ID, and user width to avoid derivation from interface, which is
incompatible with many tools.
- Add missing I/O suffixes to port names and align them with `axi_modify_address`.
### Fixed
- `axi_modify_address_intf`: Fix type parameters passed to actual implementation.
## 0.20.0 - 2020-04-22
### Added
- `axi_pkg`: Add `wrap_boundary` function to calculate the boundary of a wrapping burst.
- `axi_test`: The random AXI master `rand_axi_master` can now emit wrapping bursts (but does not do
so by default). Three new parameters control the burst types of the emitted transactions; not
setting those parameters means the random master behaves as it did before this change.
- Interface `AXI_BUS_DV`: Add `Monitor` modport, in which all signals are inputs.
- `axi/assign.svh`: Add `AXI_ASSIGN_MONITOR` macro, which assigns an `AXI_BUS` to an
`AXI_BUS_DV.Monitor`.
- Package `axi_test`: Add `axi_scoreboard` class, which checks that data read from a memory address
matches data written to that address.
### Changed
- `axi_pkg`:
- The `beat_addr` function now supports all burst types. Due to this, the function has two new
arguments (the length and type of the burst).
- The `beat_upper_byte` and `beat_lower_byte` functions internally call `beat_addr`, so they have
two new arguments as well.
## 0.19.0 - 2020-04-21
### Changed
- `axi_lite_to_axi`: Expose `AxCACHE` signals. It is now possible to define the `cache` signal of
AXI transactions coming out of this module by driving the added `slv_aw_cache_i` and
`slv_ar_cache_i` inputs. To retain the behavior prior to this change, tie those two inputs to
zero.
## 0.18.1 - 2020-04-08
### Fixed
- `axi_modify_address`: Fix unconnected `w_valid`.
- `axi_dw_converter`: Fix internal inversion of up- and downconversion, which led to incorrect lane
steering and serialization.
- `rand_axi_master` (in `axi_test`): In ATOP mode, this module could get stuck receiving an R beat
when only writes (without ATOP read responses) were left to complete. This has been fixed.
- `assign.svh`: Remove spurious semicolons.
- `axi_lite_to_apb`: Fix message of assertion checking the strobe width.
## 0.18.0 - 2020-03-24
### Added
- `axi_dw_converter`: a data width converter between AXI interfaces of any data width. Depending on
its parametrization, this module instantiates one of the following:
- `axi_dw_downsizer`: a data width converter between a wide AXI master and a narrower slave.
- `axi_dw_upsizer`: a data width converter between a narrow AXI master and a wider slave.
## 0.17.0 - 2020-03-23
### Added
- Add `axi_isolate` to isolate downstream slaves from receiving new transactions.
### Changed
- `axi_lite_to_axi`: Add mandatory `AxiDataWidth` parameter to enable fix mentioned below.
### Fixed
- Improve compatibility with Xcelium:
- by removing unsupported hierarchical argument to `$bits()` function in `axi_lite_to_axi`;
- by removing unsupported `struct` assignment in `axi_lite_demux`.
## 0.16.3 - 2020-03-19
### Changed
- `axi_err_slv`: Add optional parameter to define data returned by read response. The parameter
defaults to a 64-bit value, so buses with data width larger than or equal to 64 bit see an
additional 32-bit value in error responses compared to the prior version. Other than that, this
change is fully backward compatible.
## 0.16.2 - 2020-03-16
### Fixed
- `axi_atop_filter`: Fix underflow in counter for `AxiMaxWriteTxns = 1`.
## 0.16.1 - 2020-03-13
### Fixed
- Remove whitespace in and semicolon after macro calls.
- `axi_intf`: Improve Verilator compatibility by disabling unsupported assertions.
## 0.16.0 - 2020-03-11
### Added
- `axi_cdc_intf`: Add interface variant of AXI clock domain crossing.
### Fixed
- `axi_cdc`: Remove unused global `import axi_pkg::*`.
- `axi_intf`: Remove global `import axi_pkg::*` and explicitly use symbols from `axi_pkg`.
- `axi_lite_cut_intf`: Add missing assigns to and from interface ports.
- `tb_axi_cdc`:
- Remove global `import axi_pkg::*`.
- Define channels with `AXI_TYPEDEF` macros instead of local `typedef`s.
### Removed
- Remove unused `AXI_ARBITRATION` and `AXI_ROUTING_RULES` interfaces.
## 0.15.1 - 2020-03-09
### Added
- `axi_intf`: Add single-channel assertions to `AXI_BUS_DV`.
### Fixed
- `axi_lite_to_apb`: Fix the interface version (`axi_lite_to_apb_intf`) to match the changes from
version `0.15.0`.
- `axi_demux`: When `MaxTrans` was 1, the `IdCounterWidth` became 0. This has been fixed.
- `axi_atop_filter`:
- The master interface of this module in one case depended on `aw_ready` before applying
`w_valid`, which is a violation of the AXI specification that can lead to deadlocks. This issue
has been fixed by removing that dependency.
- The slave interface of this module could illegally change the value of B and R beats between
valid and handshake. This has been fixed.
- `rand_axi_master` (in `axi_test`):
- Fix infinite wait in `send_ws` task.
- Decouple generation of AWs from sending them. This allows to apply W beats before or
simultaneous with AW beats.
- `rand_axi_slave` (in `axi_test`):
- Decouple receiving of Ws from receiving of AWs. This allows to receive W beats independent of
AW beats.
- Update `common_cells` to `1.16.4` to fix synthesis warning in `id_queue`.
## 0.15.0 - 2020-02-28
### Added
- `axi_burst_splitter`: Split AXI4 bursts to single-beat transactions.
### Changed
- `axi_lite_to_apb`: The `psel` field of the `apb_req_t` struct is now a single bit. That is, every
APB slave has its own request struct. Accordingly, `apb_req_o` is now an array with `NoApbSlaves`
entries.
- `axi_decerr_slv` has been replaced by a more generic `axi_err_slv`, which takes the kind of error
as parameter. This `axi_err_slv` no longer has a `FallThrough` parameter; instead, a response
(i.e., B or R beat) now always comes one cycle after the AW or AR beat (as required by the AXI
Spec) but the slave can accept a W beat in the same cycle as the corresponding AW beat.
Additionally, `axi_err_slv` got a parameter `ATOPs` that defines if it supports atomic operations.
- `axi_to_axi_lite`: Rework module to structs and add burst support.
### Fixed
- `axi_demux`: The `case` statement controlling the counters had not been specified `unique` even
though it qualified for it. This has been fixed.
- `axi_lite_mux_intf`: Fix signal names in internal assignments, names of parameters of
`axi_lite_mux` instance, and typos in assertion messages.
## 0.14.0 - 2020-02-24
### Added
- Add `axi_lite_mailbox`: AXI4-Lite mailbox.
## 0.13.0 - 2020-02-18
### Added
- `axi_xbar_intf`: Add interface variant of crossbar.
### Fixed
- `axi_atop_filter`: Fix ModelSim warnings by adding `default` statement. The signal in the `case`
has a single bit, and both values were correctly handled in synthesis. However, when starting
simulation, the signal has an undefined value, and ModelSim threw warnings that this violated the
`unique` condition.
- `axi_demux`: Move `typedef` outside `generate` for compatibility with VCS.
- `axi_id_prepend`:
- Fix text of some assertion messages.
- Fix case of prepending a single-bit ID.
- `tb_axi_xbar`: Fix for localparam `AxiIdWidthSlaves` to be dependent on the number of masters.
## 0.12.0 - 2020-02-14
### Added
- `axi_lite_to_apb`: AXI4-Lite to APB4 converter.
## 0.11.0 - 2020-02-13
### Added
- `axi_cdc`: Add a safe AXI clock domain crossing (CDC) implementation.
### Changed
- The interface variants of `axi_demux` and `axi_mux` have been changed to match the convention for
interface variants in this repository:
- `axi_demux_wrap`: Change name to `axi_demux_intf` and change parameter names to ALL_CAPS.
- `axi_mux_wrap`: Change name to `axi_mux_intf`, and change parameter names to ALL_CAPS.
- `axi_demux`: Default parameters to `0`.
### Fixed
- `axi_demux`: Add parameter case for `NoMstPorts == 1`.
## 0.10.2 - 2020-02-13
### Fixed
- `axi_atop_filter`: Remove unreachable `default` in `unique case` block.
- `axi_demux_wrap`: Fix signals passed to demux.
- `axi_lite_demux_intf`: Fix signal passed to demux.
- `axi_lite_mux`: Add missing declaration of `r_fifo_push`.
## 0.10.1 - 2020-02-12
### Fixed
- `axi_lite_xbar`: Fix synthesis for `NoMstPorts == 1`.
## 0.10.0 - 2020-02-11
### Added
- `axi_lite_xbar`: fully-connected AXI4-Lite crossbar.
- `axi_lite_demux`: AXI4-Lite demultiplexer from one slave port to a configurable number of master
ports.
- `axi_lite_mux`: AXI4-Lite multiplexer from a configurable number of slave ports to one master
port.
### Changed
- `axi_test`: Extended package with random AXI4-Lite master and slave test bench classes.
## 0.9.2 - 2020-02-11
### Fixed
- `axi_pkg`: Fix value of `CUT_ALL_PORTS` (in `xbar_latency_e`) in Vivado synthesis.
## 0.9.1 - 2020-01-18
### Fixed
- `axi_decerr_slv`: Fix parameter to be UpperCamelCase
## 0.9.0 - 2020-01-16
### Added
- `axi_test`: Constrained randomizing AXI master (`rand_axi_master`) and slave (`rand_axi_slave`).
- `rand_axi_master` issues a configurable number of read and write transactions to configurable
memory regions (address ranges with associated memory types) and with random properties within
constraints (e.g., burst length, exclusive accesses, atomic operations).
- `rand_axi_slave` responds to transactions with random delays and data.
- `axi_pkg`: AXI memory types (`mem_type_t`) and functions `get_arcache` and `get_awcache` to
calculate `AxCACHE` bits for a given memory type.
- Add `axi_decerr_slv`.
- Add `axi_id_prepend`.
- Add fully compliant `axi_xbar`.
- Add documentation on `axi_mux`, `axi_demux` and `axi_xbar`
- Module overview to `README.md`
### Changed
- `axi_test`: The `reset` tasks in `axi_driver` and `axi_lite_driver` are now functions.
- Bump `common_cells` to `1.16.0` which contains the address decoding logic used in `axi_xbar`.
### Fixed
- `axi_intf` move import into interface bodies.
- `axi_pkg` make functions automatic, fixing a problem with Synopsys.
## 0.8.2 - 2019-12-20
### Fixed
- `src_files.yml`: Add `only_local` flag for `axi_test`.
- `axi_test`:
- Add missing default parameters to `axi_lite_driver`.
- Move wildcard import from `axi_test` into package to prevent pollution of compilation unit.
## 0.8.1 - 2019-12-19
### Added
- `axi_pkg`: Functions to calculate addresses and byte positions within a beat.
## 0.8.0 - 2019-12-19
All modules have been changed from SystemVerilog interfaces to struct ports. Thus, all modules in
this repository are now available in tools that do not support interfaces. Interfaces are now
opt-in: every module has a variant with `_intf` suffix that is functionally equivalent but has
interfaces instead of struct ports. If you would like to keep using interfaces, please add an
`_intf` suffix to any module you are using from this repository. Some `_intf` variants require more
parameters (e.g., to define the ID width) than the module prior to this release, but otherwise the
`_intf` variants are drop-in replacements.
We encourage the use of structs to build AXI infrastructure, and we have added a set of `typdef`
macros and have extended the `assign` macros to keep designers productive and prevent mismatches.
Additionally, we have removed a set of modules that had known issues. We will provide new
implementations for these modules in near-term releases and no longer support the removed modules.
The individual changes for each module follow.
### Added
- `assign.svh`:
- Macros for setting an AXI or AXI-Lite interface from channel or request/response structs inside
a process (`AXI_SET_FROM_*` and `AXI_LITE_SET_FROM_*`) and outside a process like an assignment
(`AXI_ASSIGN_FROM_*` and `AXI_LITE_ASSIGN_FROM_*`).
- Macros for setting channel or request/response structs to the signals of an AXI or AXI-Lite
interface inside a process (`AXI_SET_TO_*` and `AXI_LITE_SET_TO_*`) and outside a process like
an assignment (`AXI_ASSIGN_TO_*`, `AXI_LITE_ASSIGN_TO_*`).
- `typedef.svh`: Macros for defining AXI or AXI-Lite channel (`AXI_TYPEDEF_*_CHAN_T` and
`AXI_LITE_TYPEDEF_*_CHAN_T`) and request/response structs (`AXI_TYPEDEF_RE{Q,SP}_T` and
`AXI_LITE_TYPEDEF_RE{Q,SP}_T`).
### Changed
- `axi_atop_filter` has been changed from interfaces to struct ports. Please use the newly added
`axi_atop_filter_intf` module if you prefer interfaces.
- `axi_cut` has been changed from interfaces to struct ports. Please use the newly added
`axi_cut_intf` module if you prefer interfaces.
- `axi_delayer` has been changed from interfaces to struct ports. Please use the newly added
`axi_delayer_intf` module if you prefer interfaces.
- `axi_join` has been renamed to `axi_join_intf`, and `axi_lite_join` has been renamed to
`axi_lite_join_intf`. To join two structs, simply assign them instead.
- `axi_multicut` has been changed from interfaces to struct ports. Please use the newly added
`axi_multicut_intf` module if you prefer interfaces.
- `axi_modify_address` has been changed from interfaces to struct ports. Please use the newly added
`axi_modify_address_intf` module if you prefer interfaces.
- `axi_lite_to_axi` has been changed from interfaces to struct ports. Please use the newly added
`axi_lite_to_axi_intf` module if you prefer interfaces.
### Removed
- `axi_lite_xbar`: This interconnect module was not a full crossbar and its routing rules interface
no longer fits our demands. A replacement will be provided in a near-term release.
- `axi_address_resolver` was used together with `axi_lite_xbar` and is removed along with it. If a
standalone replacement for this module is required, please use `addr_decoder` from `common_cells`.
- `axi_arbiter` was used together with `axi_lite_xbar` and is removed along with it. If a
standalone replacement of this module is required, please use `rr_arb_tree` from `common_cells`.
A near-term release will introduce an AXI multiplexer and demultiplexer to suit protocol-specific
needs.
- `axi_id_remap` had problems with ordering and ATOPs. A new, correct implementation will be
provided in a near-term release.
- `axi_lite_cut` has been rendered unnecessary by changing `axi_cut` to struct ports. To get a cut
with AXI-Lite ports, simply pass AXI-Lite channels and request/response structs as parameters. If
you prefer interfaces, please replace any `axi_lite_cut` with the newly added `axi_lite_cut_intf`
module.
- `axi_lite_multicut`: same rationale and transition procedure as for `axi_lite_cut`.
- In `axi_pkg`, the `*Width` `localparam`s and the `id_t`, `addr_t`, etc. `typedef`s have been
removed. There is no one-fits-all value of these parameters, so we cannot provide a generic
definition for them in this package. Please use the added macros in `typedef.svh` to define your
own types with a few lines of code (which you can put into your own package, for example).
## 0.7.2 - 2019-12-03
### Fixed
- axi_to_axi_lite: Fix underflow in internal buffers.
- axi_to_axi_lite: Remove restriction on size of internal buffers.
## 0.7.1 - 2019-11-19
### Changed
- axi_multicut: Simplified implementation without changing I/O behavior.
### Fixed
- src_files: Removed `axi_test.sv` from synthesized files.
- tb_axi_lite_xbar: Fixed AW->W dependency.
## 0.7.0 - 2019-05-28
### Changed
- The `in` and `out` modports have been removed from the interface definition of both AXI and AXI
Lite. These modports were "aliases" of `Slave` and `Master`, respectively, and caused problems
because many tools did not recognize the aliases as being identical to `Slave` and `Master`.
## 0.6.0 - 2019-02-27
### Changed
- AXI interfaces now include the `aw_atop` signal. Interfaces, macros, and existing modules and
TBs in this repository have been updated. The ReadMe has been updated to guide users of this
repository on how to deal with the `aw_atop` signal.
### Added
- Add AXI atomic operations (ATOPs) filter.
### Fixed
- Replace non-ASCII characters in Solderpad license text.
- Add a trailing semicolon to the `AXI_ASSIGN()` and `AXI_LITE_ASSIGN()` macros in `assign.svh`
(#8). Those macros can now be used without a semicolon. Existing code that uses the macros with a
semicolon do not break.
## 0.5.0 - 2018-12-18
- Add axi channel delayer
### Changed
- Remove clock from `AXI_BUS` and `AXI_LITE`. Such a clock signal is useful for testing purposes
but confusing (or even harmful) in hardware designs. For testing purposes, the `AXI_BUS_DV` and
`AXI_LITE_DV` (suffix for "design verification") interfaces have been defined instead.
### Fixed
- Update `src_files.yml` to match `Bender.yml`.
- Add missing `axi_test` to compile script.
## 0.4.5 - 2018-09-12
### Fixed
- Fix `common_cells` dependency to open-source repo
## 0.4.4 - 2018-09-06
### Changed
- Make `axi_cut` and `axi_multicut` verilator compatible
## 0.4.3 - 2018-08-01
### Changed
- Add license file and adjust copyright headers.
## 0.4.2 - 2018-06-02
### Fixed
- Add test mode signal to `axi_to_axi_lite` adapter, used in the FIFOs.
- Remove `axi_find_first_one` from src_files.yml
- Fix release ID issue in ID `axi_id_remap`
## 0.4.1 - 2018-03-23
### Fixed
- Remove time unit from test package. Fixes an issue in the AXI driver.
## 0.4.0 - 2018-03-20
### Added
- Add AXI ID remapper.
### Fixed
- Fixed typos in the AXI and AXI-Lite multicuts.
- Fixed ID width in AXI ID remapper.
- AXI join now asserts if width of outgoing ID is larger or equal to width of incoming ID.
## 0.3.0 - 2018-03-09
### Added
- AXI and AXI-Lite multicuts
## 0.2.1 - 2018-03-09
### Fixed
- Remove `axi_find_first_one.sv` from manifest
## 0.2.0 - 2018-03-09
### Added
- AXI cut
## 0.1.0 - 2018-03-09
- Initial release with various interfaces, drivers for testbenches, and utility modules.

View file

@ -0,0 +1,26 @@
# Contribution Guidelines
## Coding Style
All SystemVerilog code in this repository _must_ adhere to the [SystemVerilog Coding Style Guide by
lowRISC](https://github.com/lowRISC/style-guides/blob/master/VerilogCodingStyle.md) and the
following rules:
- All module names _must_ start with `axi_`.
- User-facing modules _must_ have SystemVerilog `struct`s as AXI ports. The concrete `struct` type
_must_ be defined as `parameter` to the module. The fields of the `struct` _must_ correspond to
those defined by our [`typedef`
macros](https://github.com/pulp-platform/axi/blob/master/include/axi/typedef.svh).
- User-facing modules _may_ come with a variant that has SystemVerilog interfaces as AXI ports.
- Such an interface variant module _must not_ implement any functionality except wiring its
interfaces to the `struct` ports of the original module.
- The name of an interface variant _must_ be the name of the original module suffixed by `_intf`.
- The parameters of an interface variant must be formatted `ALL_CAPS`.
## Collaboration Guidelines
We follow [`pulp-platform`'s Collaboration
Guidelines](https://github.com/pulp-platform/style-guidelines/blob/master/CONTRIBUTING.md).

176
vendor/pulp-platform/axi/LICENSE vendored Normal file
View file

@ -0,0 +1,176 @@
SOLDERPAD HARDWARE LICENSE version 0.51
This license is based closely on the Apache License Version 2.0, but is not
approved or endorsed by the Apache Foundation. A copy of the non-modified
Apache License 2.0 can be found at http://www.apache.org/licenses/LICENSE-2.0.
As this license is not currently OSI or FSF approved, the Licensor permits any
Work licensed under this License, at the option of the Licensee, to be treated
as licensed under the Apache License Version 2.0 (which is so approved).
This License is licensed under the terms of this License and in particular
clause 7 below (Disclaimer of Warranties) applies in relation to its use.
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and
distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the Rights owner or entity authorized by the Rights owner
that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other entities
that control, are controlled by, or are under common control with that entity.
For the purposes of this definition, "control" means (i) the power, direct or
indirect, to cause the direction or management of such entity, whether by
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising
permissions granted by this License.
"Rights" means copyright and any similar right including design right (whether
registered or unregistered), semiconductor topography (mask) rights and
database rights (but excluding Patents and Trademarks).
"Source" form shall mean the preferred form for making modifications, including
but not limited to source code, net lists, board layouts, CAD files,
documentation source, and configuration files.
"Object" form shall mean any form resulting from mechanical transformation or
translation of a Source form, including but not limited to compiled object
code, generated documentation, the instantiation of a hardware design and
conversions to other media types, including intermediate forms such as
bytecodes, FPGA bitstreams, artwork and semiconductor topographies (mask
works).
"Work" shall mean the work of authorship, whether in Source form or other
Object form, made available under the License, as indicated by a Rights notice
that is included in or attached to the work (an example is provided in the
Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object form, that
is based on (or derived from) the Work and for which the editorial revisions,
annotations, elaborations, or other modifications represent, as a whole, an
original work of authorship. For the purposes of this License, Derivative Works
shall not include works that remain separable from, or merely link (or bind by
name) or physically connect to or interoperate with the interfaces of, the Work
and Derivative Works thereof.
"Contribution" shall mean any design or work of authorship, including the
original version of the Work and any modifications or additions to that Work or
Derivative Works thereof, that is intentionally submitted to Licensor for
inclusion in the Work by the Rights owner or by an individual or Legal Entity
authorized to submit on behalf of the Rights owner. For the purposes of this
definition, "submitted" means any form of electronic, verbal, or written
communication sent to the Licensor or its representatives, including but not
limited to communication on electronic mailing lists, source code control
systems, and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but excluding
communication that is conspicuously marked or otherwise designated in writing
by the Rights owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
of whom a Contribution has been received by Licensor and subsequently
incorporated within the Work.
2. Grant of License. Subject to the terms and conditions of this License, each
Contributor hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable license under the Rights to reproduce,
prepare Derivative Works of, publicly display, publicly perform, sublicense,
and distribute the Work and such Derivative Works in Source or Object form and
do anything in relation to the Work as if the Rights did not exist.
3. Grant of Patent License. Subject to the terms and conditions of this
License, each Contributor hereby grants to You a perpetual, worldwide,
non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this
section) patent license to make, have made, use, offer to sell, sell, import,
and otherwise transfer the Work, where such license applies only to those
patent claims licensable by such Contributor that are necessarily infringed by
their Contribution(s) alone or by combination of their Contribution(s) with the
Work to which such Contribution(s) was submitted. If You institute patent
litigation against any entity (including a cross-claim or counterclaim in a
lawsuit) alleging that the Work or a Contribution incorporated within the Work
constitutes direct or contributory patent infringement, then any patent
licenses granted to You under this License for that Work shall terminate as of
the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the Work or
Derivative Works thereof in any medium, with or without modifications, and in
Source or Object form, provided that You meet the following conditions:
You must give any other recipients of the Work or Derivative Works a copy
of this License; and
You must cause any modified files to carry prominent notices stating that
You changed the files; and
You must retain, in the Source form of any Derivative Works that You
distribute, all copyright, patent, trademark, and attribution notices from
the Source form of the Work, excluding those notices that do not pertain to
any part of the Derivative Works; and
If the Work includes a "NOTICE" text file as part of its distribution, then
any Derivative Works that You distribute must include a readable copy of
the attribution notices contained within such NOTICE file, excluding those
notices that do not pertain to any part of the Derivative Works, in at
least one of the following places: within a NOTICE text file distributed as
part of the Derivative Works; within the Source form or documentation, if
provided along with the Derivative Works; or, within a display generated by
the Derivative Works, if and wherever such third-party notices normally
appear. The contents of the NOTICE file are for informational purposes only
and do not modify the License. You may add Your own attribution notices
within Derivative Works that You distribute, alongside or as an addendum to
the NOTICE text from the Work, provided that such additional attribution
notices cannot be construed as modifying the License. You may add Your own
copyright statement to Your modifications and may provide additional or
different license terms and conditions for use, reproduction, or
distribution of Your modifications, or for any such Derivative Works as a
whole, provided Your use, reproduction, and distribution of the Work
otherwise complies with the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise, any
Contribution intentionally submitted for inclusion in the Work by You to the
Licensor shall be under the terms and conditions of this License, without any
additional terms or conditions. Notwithstanding the above, nothing herein shall
supersede or modify the terms of any separate license agreement you may have
executed with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade names,
trademarks, service marks, or product names of the Licensor, except as required
for reasonable and customary use in describing the origin of the Work and
reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in
writing, Licensor provides the Work (and each Contributor provides its
Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied, including, without limitation, any warranties
or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any risks
associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory, whether in
tort (including negligence), contract, or otherwise, unless required by
applicable law (such as deliberate and grossly negligent acts) or agreed to in
writing, shall any Contributor be liable to You for damages, including any
direct, indirect, special, incidental, or consequential damages of any
character arising as a result of this License or out of the use or inability to
use the Work (including but not limited to damages for loss of goodwill, work
stoppage, computer failure or malfunction, or any and all other commercial
damages or losses), even if such Contributor has been advised of the
possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing the Work or
Derivative Works thereof, You may choose to offer, and charge a fee for,
acceptance of support, warranty, indemnity, or other liability obligations
and/or rights consistent with this License. However, in accepting such
obligations, You may act only on Your own behalf and on Your sole
responsibility, not on behalf of any other Contributor, and only if You agree
to indemnify, defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason of your
accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

106
vendor/pulp-platform/axi/README.md vendored Normal file
View file

@ -0,0 +1,106 @@
# AXI SystemVerilog Modules for High-Performance On-Chip Communication
[![CI status](https://akurth.net/usrv/ig/shields/pipeline/akurth/axi/master.svg)](https://iis-git.ee.ethz.ch/akurth/axi/commits/master)
[![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.
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).
- **Modularity**: We favor design by composition over design by configuration where possible. We strive to apply the *Unix philosophy* to hardware: make each module do one thing well. This means you will more often instantiate our modules back-to-back than change a parameter value to build more specialized networks.
- **Fit for Heterogeneous Networks**: Our modules are parametrizable in terms of data width and transaction concurrency. This allows to create optimized networks for a wide range of performance (e.g., bandwidth, concurrency, timing), power, and area requirements. We provide modules such as [data width converters](src/axi_dw_converter.sv) and [ID width converters](src/axi_iw_converter.sv) that allow to join subnetworks with different properties, creating heterogeneous on-chip networks.
- **Full AXI Standard Compliance**.
- **Compatibility** with a [wide range of (recent versions of) EDA tools](#which-eda-tools-are-supported) and implementation in standardized synthesizable SystemVerilog.
The **design and microarchitecture** of the modules in this repository is described in [**this paper**](https://ieeexplore.ieee.org/document/9522037) ([preprint](https://arxiv.org/pdf/2009.05334)). If you use our work in your research, please cite it.
## List of Modules
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) |
### Simulation-Only Modules
In addition to the modules above, which are available in synthesis and simulation, the following modules are available only in simulation. Those modules are widely used in our testbenches, but they are also suitable to build testbenches for AXI modules and systems outside this repository.
| Name | Description |
|------------------------------------------------------|--------------------------------------------------------------------------------------------------------|
| [`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_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. |
| [`axi_rand_master`](src/axi_test.sv) | AXI4(+ATOPs) master component that issues random transactions within user-defined constraints. |
| [`axi_rand_slave`](src/axi_test.sv) | AXI4(+ATOPs) slave component that responds to transactions with constrainable random delays and data. |
| [`axi_scoreboard`](src/axi_test.sv) | Scoreboard that models a memory that only gets changed by the monitored AXI4(+ATOPs) port. |
| [`axi_sim_mem`](src/axi_sim_mem.sv) | Infinite memory with AXI4 slave port. |
## 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:
- 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.
- System designers are responsible for ensuring that
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.
## Which EDA Tools Are Supported?
Our code is written in standard SystemVerilog ([IEEE 1800-2012][], to be precise), so the more important question is: Which subset of SystemVerilog does your EDA tool support?
We aim to be compatible with a wide range of EDA tools. For this reason, we strive to use as simple language constructs as possible, especially for our synthesizable modules. We encourage contributions that further simplify our code to make it compatible with even more EDA tools. We also welcome contributions that work around problems that specific EDA tools may have with our code, as long as:
- the EDA tool is reasonably widely used,
- recent versions of the EDA tool are affected,
- the workaround does not break functionality in other tools, and
- the workaround does not significantly complicate code or add maintenance overhead.
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.
[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
[doc.axi_iw_converter]: https://pulp-platform.github.io/axi/master/module.axi_iw_converter
[doc.axi_lite_regs]: https://pulp-platform.github.io/axi/master/module.axi_lite_regs

1
vendor/pulp-platform/axi/VERSION vendored Normal file
View file

@ -0,0 +1 @@
0.31.0

View file

@ -0,0 +1,541 @@
// Copyright (c) 2014-2018 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:
// - Andreas Kurth <akurth@iis.ee.ethz.ch>
// - Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>
// Macros to assign AXI Interfaces and Structs
`ifndef AXI_ASSIGN_SVH_
`define AXI_ASSIGN_SVH_
////////////////////////////////////////////////////////////////////////////////////////////////////
// Internal implementation for assigning one AXI struct or interface to another struct or interface.
// The path to the signals on each side is defined by the `__sep*` arguments. The `__opt_as`
// argument allows to use this standalone (with `__opt_as = assign`) or in assignments inside
// processes (with `__opt_as` void).
`define __AXI_TO_AW(__opt_as, __lhs, __lhs_sep, __rhs, __rhs_sep) \
__opt_as __lhs``__lhs_sep``id = __rhs``__rhs_sep``id; \
__opt_as __lhs``__lhs_sep``addr = __rhs``__rhs_sep``addr; \
__opt_as __lhs``__lhs_sep``len = __rhs``__rhs_sep``len; \
__opt_as __lhs``__lhs_sep``size = __rhs``__rhs_sep``size; \
__opt_as __lhs``__lhs_sep``burst = __rhs``__rhs_sep``burst; \
__opt_as __lhs``__lhs_sep``lock = __rhs``__rhs_sep``lock; \
__opt_as __lhs``__lhs_sep``cache = __rhs``__rhs_sep``cache; \
__opt_as __lhs``__lhs_sep``prot = __rhs``__rhs_sep``prot; \
__opt_as __lhs``__lhs_sep``qos = __rhs``__rhs_sep``qos; \
__opt_as __lhs``__lhs_sep``region = __rhs``__rhs_sep``region; \
__opt_as __lhs``__lhs_sep``atop = __rhs``__rhs_sep``atop; \
__opt_as __lhs``__lhs_sep``user = __rhs``__rhs_sep``user;
`define __AXI_TO_W(__opt_as, __lhs, __lhs_sep, __rhs, __rhs_sep) \
__opt_as __lhs``__lhs_sep``data = __rhs``__rhs_sep``data; \
__opt_as __lhs``__lhs_sep``strb = __rhs``__rhs_sep``strb; \
__opt_as __lhs``__lhs_sep``last = __rhs``__rhs_sep``last; \
__opt_as __lhs``__lhs_sep``user = __rhs``__rhs_sep``user;
`define __AXI_TO_B(__opt_as, __lhs, __lhs_sep, __rhs, __rhs_sep) \
__opt_as __lhs``__lhs_sep``id = __rhs``__rhs_sep``id; \
__opt_as __lhs``__lhs_sep``resp = __rhs``__rhs_sep``resp; \
__opt_as __lhs``__lhs_sep``user = __rhs``__rhs_sep``user;
`define __AXI_TO_AR(__opt_as, __lhs, __lhs_sep, __rhs, __rhs_sep) \
__opt_as __lhs``__lhs_sep``id = __rhs``__rhs_sep``id; \
__opt_as __lhs``__lhs_sep``addr = __rhs``__rhs_sep``addr; \
__opt_as __lhs``__lhs_sep``len = __rhs``__rhs_sep``len; \
__opt_as __lhs``__lhs_sep``size = __rhs``__rhs_sep``size; \
__opt_as __lhs``__lhs_sep``burst = __rhs``__rhs_sep``burst; \
__opt_as __lhs``__lhs_sep``lock = __rhs``__rhs_sep``lock; \
__opt_as __lhs``__lhs_sep``cache = __rhs``__rhs_sep``cache; \
__opt_as __lhs``__lhs_sep``prot = __rhs``__rhs_sep``prot; \
__opt_as __lhs``__lhs_sep``qos = __rhs``__rhs_sep``qos; \
__opt_as __lhs``__lhs_sep``region = __rhs``__rhs_sep``region; \
__opt_as __lhs``__lhs_sep``user = __rhs``__rhs_sep``user;
`define __AXI_TO_R(__opt_as, __lhs, __lhs_sep, __rhs, __rhs_sep) \
__opt_as __lhs``__lhs_sep``id = __rhs``__rhs_sep``id; \
__opt_as __lhs``__lhs_sep``data = __rhs``__rhs_sep``data; \
__opt_as __lhs``__lhs_sep``resp = __rhs``__rhs_sep``resp; \
__opt_as __lhs``__lhs_sep``last = __rhs``__rhs_sep``last; \
__opt_as __lhs``__lhs_sep``user = __rhs``__rhs_sep``user;
`define __AXI_TO_REQ(__opt_as, __lhs, __lhs_sep, __rhs, __rhs_sep) \
`__AXI_TO_AW(__opt_as, __lhs.aw, __lhs_sep, __rhs.aw, __rhs_sep) \
__opt_as __lhs.aw_valid = __rhs.aw_valid; \
`__AXI_TO_W(__opt_as, __lhs.w, __lhs_sep, __rhs.w, __rhs_sep) \
__opt_as __lhs.w_valid = __rhs.w_valid; \
__opt_as __lhs.b_ready = __rhs.b_ready; \
`__AXI_TO_AR(__opt_as, __lhs.ar, __lhs_sep, __rhs.ar, __rhs_sep) \
__opt_as __lhs.ar_valid = __rhs.ar_valid; \
__opt_as __lhs.r_ready = __rhs.r_ready;
`define __AXI_TO_RESP(__opt_as, __lhs, __lhs_sep, __rhs, __rhs_sep) \
__opt_as __lhs.aw_ready = __rhs.aw_ready; \
__opt_as __lhs.ar_ready = __rhs.ar_ready; \
__opt_as __lhs.w_ready = __rhs.w_ready; \
__opt_as __lhs.b_valid = __rhs.b_valid; \
`__AXI_TO_B(__opt_as, __lhs.b, __lhs_sep, __rhs.b, __rhs_sep) \
__opt_as __lhs.r_valid = __rhs.r_valid; \
`__AXI_TO_R(__opt_as, __lhs.r, __lhs_sep, __rhs.r, __rhs_sep)
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// Assigning one AXI4+ATOP interface to another, as if you would do `assign slv = mst;`
//
// The channel assignments `AXI_ASSIGN_XX(dst, src)` assign all payload and the valid signal of the
// `XX` channel from the `src` to the `dst` interface and they assign the ready signal from the
// `src` to the `dst` interface.
// The interface assignment `AXI_ASSIGN(dst, src)` assigns all channels including handshakes as if
// `src` was the master of `dst`.
//
// Usage Example:
// `AXI_ASSIGN(slv, mst)
// `AXI_ASSIGN_AW(dst, src)
// `AXI_ASSIGN_R(dst, src)
`define AXI_ASSIGN_AW(dst, src) \
`__AXI_TO_AW(assign, dst.aw, _, src.aw, _) \
assign dst.aw_valid = src.aw_valid; \
assign src.aw_ready = dst.aw_ready;
`define AXI_ASSIGN_W(dst, src) \
`__AXI_TO_W(assign, dst.w, _, src.w, _) \
assign dst.w_valid = src.w_valid; \
assign src.w_ready = dst.w_ready;
`define AXI_ASSIGN_B(dst, src) \
`__AXI_TO_B(assign, dst.b, _, src.b, _) \
assign dst.b_valid = src.b_valid; \
assign src.b_ready = dst.b_ready;
`define AXI_ASSIGN_AR(dst, src) \
`__AXI_TO_AR(assign, dst.ar, _, src.ar, _) \
assign dst.ar_valid = src.ar_valid; \
assign src.ar_ready = dst.ar_ready;
`define AXI_ASSIGN_R(dst, src) \
`__AXI_TO_R(assign, dst.r, _, src.r, _) \
assign dst.r_valid = src.r_valid; \
assign src.r_ready = dst.r_ready;
`define AXI_ASSIGN(slv, mst) \
`AXI_ASSIGN_AW(slv, mst) \
`AXI_ASSIGN_W(slv, mst) \
`AXI_ASSIGN_B(mst, slv) \
`AXI_ASSIGN_AR(slv, mst) \
`AXI_ASSIGN_R(mst, slv)
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// Assigning a AXI4+ATOP interface to a monitor modport, as if you would do `assign mon = axi_if;`
//
// The channel assignment `AXI_ASSIGN_MONITOR(mon_dv, axi_if)` assigns all signals from `axi_if`
// to the `mon_dv` interface.
//
// Usage Example:
// `AXI_ASSIGN_MONITOR(mon_dv, axi_if)
`define AXI_ASSIGN_MONITOR(mon_dv, axi_if) \
`__AXI_TO_AW(assign, mon_dv.aw, _, axi_if.aw, _) \
assign mon_dv.aw_valid = axi_if.aw_valid; \
assign mon_dv.aw_ready = axi_if.aw_ready; \
`__AXI_TO_W(assign, mon_dv.w, _, axi_if.w, _) \
assign mon_dv.w_valid = axi_if.w_valid; \
assign mon_dv.w_ready = axi_if.w_ready; \
`__AXI_TO_B(assign, mon_dv.b, _, axi_if.b, _) \
assign mon_dv.b_valid = axi_if.b_valid; \
assign mon_dv.b_ready = axi_if.b_ready; \
`__AXI_TO_AR(assign, mon_dv.ar, _, axi_if.ar, _) \
assign mon_dv.ar_valid = axi_if.ar_valid; \
assign mon_dv.ar_ready = axi_if.ar_ready; \
`__AXI_TO_R(assign, mon_dv.r, _, axi_if.r, _) \
assign mon_dv.r_valid = axi_if.r_valid; \
assign mon_dv.r_ready = axi_if.r_ready;
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// Setting an interface from channel or request/response structs inside a process.
//
// The channel macros `AXI_SET_FROM_XX(axi_if, xx_struct)` set the payload signals of the `axi_if`
// interface from the signals in `xx_struct`. They do not set the handshake signals.
// The request macro `AXI_SET_FROM_REQ(axi_if, req_struct)` sets all request channels (AW, W, AR)
// and the request-side handshake signals (AW, W, and AR valid and B and R ready) of the `axi_if`
// interface from the signals in `req_struct`.
// The response macro `AXI_SET_FROM_RESP(axi_if, resp_struct)` sets both response channels (B and R)
// and the response-side handshake signals (B and R valid and AW, W, and AR ready) of the `axi_if`
// interface from the signals in `resp_struct`.
//
// Usage Example:
// always_comb begin
// `AXI_SET_FROM_REQ(my_if, my_req_struct)
// end
`define AXI_SET_FROM_AW(axi_if, aw_struct) `__AXI_TO_AW(, axi_if.aw, _, aw_struct, .)
`define AXI_SET_FROM_W(axi_if, w_struct) `__AXI_TO_W(, axi_if.w, _, w_struct, .)
`define AXI_SET_FROM_B(axi_if, b_struct) `__AXI_TO_B(, axi_if.b, _, b_struct, .)
`define AXI_SET_FROM_AR(axi_if, ar_struct) `__AXI_TO_AR(, axi_if.ar, _, ar_struct, .)
`define AXI_SET_FROM_R(axi_if, r_struct) `__AXI_TO_R(, axi_if.r, _, r_struct, .)
`define AXI_SET_FROM_REQ(axi_if, req_struct) `__AXI_TO_REQ(, axi_if, _, req_struct, .)
`define AXI_SET_FROM_RESP(axi_if, resp_struct) `__AXI_TO_RESP(, axi_if, _, resp_struct, .)
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// Assigning an interface from channel or request/response structs outside a process.
//
// The channel macros `AXI_ASSIGN_FROM_XX(axi_if, xx_struct)` assign the payload signals of the
// `axi_if` interface from the signals in `xx_struct`. They do not assign the handshake signals.
// The request macro `AXI_ASSIGN_FROM_REQ(axi_if, req_struct)` assigns all request channels (AW, W,
// AR) and the request-side handshake signals (AW, W, and AR valid and B and R ready) of the
// `axi_if` interface from the signals in `req_struct`.
// The response macro `AXI_ASSIGN_FROM_RESP(axi_if, resp_struct)` assigns both response channels (B
// and R) and the response-side handshake signals (B and R valid and AW, W, and AR ready) of the
// `axi_if` interface from the signals in `resp_struct`.
//
// Usage Example:
// `AXI_ASSIGN_FROM_REQ(my_if, my_req_struct)
`define AXI_ASSIGN_FROM_AW(axi_if, aw_struct) `__AXI_TO_AW(assign, axi_if.aw, _, aw_struct, .)
`define AXI_ASSIGN_FROM_W(axi_if, w_struct) `__AXI_TO_W(assign, axi_if.w, _, w_struct, .)
`define AXI_ASSIGN_FROM_B(axi_if, b_struct) `__AXI_TO_B(assign, axi_if.b, _, b_struct, .)
`define AXI_ASSIGN_FROM_AR(axi_if, ar_struct) `__AXI_TO_AR(assign, axi_if.ar, _, ar_struct, .)
`define AXI_ASSIGN_FROM_R(axi_if, r_struct) `__AXI_TO_R(assign, axi_if.r, _, r_struct, .)
`define AXI_ASSIGN_FROM_REQ(axi_if, req_struct) `__AXI_TO_REQ(assign, axi_if, _, req_struct, .)
`define AXI_ASSIGN_FROM_RESP(axi_if, resp_struct) `__AXI_TO_RESP(assign, axi_if, _, resp_struct, .)
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// Setting channel or request/response structs from an interface inside a process.
//
// The channel macros `AXI_SET_TO_XX(xx_struct, axi_if)` set the signals of `xx_struct` to the
// payload signals of that channel in the `axi_if` interface. They do not set the handshake
// signals.
// The request macro `AXI_SET_TO_REQ(axi_if, req_struct)` sets all signals of `req_struct` (i.e.,
// request channel (AW, W, AR) payload and request-side handshake signals (AW, W, and AR valid and
// B and R ready)) to the signals in the `axi_if` interface.
// The response macro `AXI_SET_TO_RESP(axi_if, resp_struct)` sets all signals of `resp_struct`
// (i.e., response channel (B and R) payload and response-side handshake signals (B and R valid and
// AW, W, and AR ready)) to the signals in the `axi_if` interface.
//
// Usage Example:
// always_comb begin
// `AXI_SET_TO_REQ(my_req_struct, my_if)
// end
`define AXI_SET_TO_AW(aw_struct, axi_if) `__AXI_TO_AW(, aw_struct, ., axi_if.aw, _)
`define AXI_SET_TO_W(w_struct, axi_if) `__AXI_TO_W(, w_struct, ., axi_if.w, _)
`define AXI_SET_TO_B(b_struct, axi_if) `__AXI_TO_B(, b_struct, ., axi_if.b, _)
`define AXI_SET_TO_AR(ar_struct, axi_if) `__AXI_TO_AR(, ar_struct, ., axi_if.ar, _)
`define AXI_SET_TO_R(r_struct, axi_if) `__AXI_TO_R(, r_struct, ., axi_if.r, _)
`define AXI_SET_TO_REQ(req_struct, axi_if) `__AXI_TO_REQ(, req_struct, ., axi_if, _)
`define AXI_SET_TO_RESP(resp_struct, axi_if) `__AXI_TO_RESP(, resp_struct, ., axi_if, _)
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// Assigning channel or request/response structs from an interface outside a process.
//
// The channel macros `AXI_ASSIGN_TO_XX(xx_struct, axi_if)` assign the signals of `xx_struct` to the
// payload signals of that channel in the `axi_if` interface. They do not assign the handshake
// signals.
// The request macro `AXI_ASSIGN_TO_REQ(axi_if, req_struct)` assigns all signals of `req_struct`
// (i.e., request channel (AW, W, AR) payload and request-side handshake signals (AW, W, and AR
// valid and B and R ready)) to the signals in the `axi_if` interface.
// The response macro `AXI_ASSIGN_TO_RESP(axi_if, resp_struct)` assigns all signals of `resp_struct`
// (i.e., response channel (B and R) payload and response-side handshake signals (B and R valid and
// AW, W, and AR ready)) to the signals in the `axi_if` interface.
//
// Usage Example:
// `AXI_ASSIGN_TO_REQ(my_req_struct, my_if)
`define AXI_ASSIGN_TO_AW(aw_struct, axi_if) `__AXI_TO_AW(assign, aw_struct, ., axi_if.aw, _)
`define AXI_ASSIGN_TO_W(w_struct, axi_if) `__AXI_TO_W(assign, w_struct, ., axi_if.w, _)
`define AXI_ASSIGN_TO_B(b_struct, axi_if) `__AXI_TO_B(assign, b_struct, ., axi_if.b, _)
`define AXI_ASSIGN_TO_AR(ar_struct, axi_if) `__AXI_TO_AR(assign, ar_struct, ., axi_if.ar, _)
`define AXI_ASSIGN_TO_R(r_struct, axi_if) `__AXI_TO_R(assign, r_struct, ., axi_if.r, _)
`define AXI_ASSIGN_TO_REQ(req_struct, axi_if) `__AXI_TO_REQ(assign, req_struct, ., axi_if, _)
`define AXI_ASSIGN_TO_RESP(resp_struct, axi_if) `__AXI_TO_RESP(assign, resp_struct, ., axi_if, _)
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// Setting channel or request/response structs from another struct inside a process.
//
// The channel macros `AXI_SET_XX_STRUCT(lhs, rhs)` set the fields of the `lhs` channel struct to
// the fields of the `rhs` channel struct. They do not set the handshake signals, which are not
// part of channel structs.
// The request macro `AXI_SET_REQ_STRUCT(lhs, rhs)` sets all fields of the `lhs` request struct to
// the fields of the `rhs` request struct. This includes all request channel (AW, W, AR) payload
// and request-side handshake signals (AW, W, and AR valid and B and R ready).
// The response macro `AXI_SET_RESP_STRUCT(lhs, rhs)` sets all fields of the `lhs` response struct
// to the fields of the `rhs` response struct. This includes all response channel (B and R) payload
// and response-side handshake signals (B and R valid and AW, W, and R ready).
//
// Usage Example:
// always_comb begin
// `AXI_SET_REQ_STRUCT(my_req_struct, another_req_struct)
// end
`define AXI_SET_AW_STRUCT(lhs, rhs) `__AXI_TO_AW(, lhs, ., rhs, .)
`define AXI_SET_W_STRUCT(lhs, rhs) `__AXI_TO_W(, lhs, ., rhs, .)
`define AXI_SET_B_STRUCT(lhs, rhs) `__AXI_TO_B(, lhs, ., rhs, .)
`define AXI_SET_AR_STRUCT(lhs, rhs) `__AXI_TO_AR(, lhs, ., rhs, .)
`define AXI_SET_R_STRUCT(lhs, rhs) `__AXI_TO_R(, lhs, ., rhs, .)
`define AXI_SET_REQ_STRUCT(lhs, rhs) `__AXI_TO_REQ(, lhs, ., rhs, .)
`define AXI_SET_RESP_STRUCT(lhs, rhs) `__AXI_TO_RESP(, lhs, ., rhs, .)
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// Assigning channel or request/response structs from another struct outside a process.
//
// The channel macros `AXI_ASSIGN_XX_STRUCT(lhs, rhs)` assign the fields of the `lhs` channel struct
// to the fields of the `rhs` channel struct. They do not assign the handshake signals, which are
// not part of the channel structs.
// The request macro `AXI_ASSIGN_REQ_STRUCT(lhs, rhs)` assigns all fields of the `lhs` request
// struct to the fields of the `rhs` request struct. This includes all request channel (AW, W, AR)
// payload and request-side handshake signals (AW, W, and AR valid and B and R ready).
// The response macro `AXI_ASSIGN_RESP_STRUCT(lhs, rhs)` assigns all fields of the `lhs` response
// struct to the fields of the `rhs` response struct. This includes all response channel (B and R)
// payload and response-side handshake signals (B and R valid and AW, W, and R ready).
//
// Usage Example:
// `AXI_ASSIGN_REQ_STRUCT(my_req_struct, another_req_struct)
`define AXI_ASSIGN_AW_STRUCT(lhs, rhs) `__AXI_TO_AW(assign, lhs, ., rhs, .)
`define AXI_ASSIGN_W_STRUCT(lhs, rhs) `__AXI_TO_W(assign, lhs, ., rhs, .)
`define AXI_ASSIGN_B_STRUCT(lhs, rhs) `__AXI_TO_B(assign, lhs, ., rhs, .)
`define AXI_ASSIGN_AR_STRUCT(lhs, rhs) `__AXI_TO_AR(assign, lhs, ., rhs, .)
`define AXI_ASSIGN_R_STRUCT(lhs, rhs) `__AXI_TO_R(assign, lhs, ., rhs, .)
`define AXI_ASSIGN_REQ_STRUCT(lhs, rhs) `__AXI_TO_REQ(assign, lhs, ., rhs, .)
`define AXI_ASSIGN_RESP_STRUCT(lhs, rhs) `__AXI_TO_RESP(assign, lhs, ., rhs, .)
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// Internal implementation for assigning one Lite structs or interface to another struct or
// interface. The path to the signals on each side is defined by the `__sep*` arguments. The
// `__opt_as` argument allows to use this standalne (with `__opt_as = assign`) or in assignments
// inside processes (with `__opt_as` void).
`define __AXI_LITE_TO_AX(__opt_as, __lhs, __lhs_sep, __rhs, __rhs_sep) \
__opt_as __lhs``__lhs_sep``addr = __rhs``__rhs_sep``addr; \
__opt_as __lhs``__lhs_sep``prot = __rhs``__rhs_sep``prot;
`define __AXI_LITE_TO_W(__opt_as, __lhs, __lhs_sep, __rhs, __rhs_sep) \
__opt_as __lhs``__lhs_sep``data = __rhs``__rhs_sep``data; \
__opt_as __lhs``__lhs_sep``strb = __rhs``__rhs_sep``strb;
`define __AXI_LITE_TO_B(__opt_as, __lhs, __lhs_sep, __rhs, __rhs_sep) \
__opt_as __lhs``__lhs_sep``resp = __rhs``__rhs_sep``resp;
`define __AXI_LITE_TO_R(__opt_as, __lhs, __lhs_sep, __rhs, __rhs_sep) \
__opt_as __lhs``__lhs_sep``data = __rhs``__rhs_sep``data; \
__opt_as __lhs``__lhs_sep``resp = __rhs``__rhs_sep``resp;
`define __AXI_LITE_TO_REQ(__opt_as, __lhs, __lhs_sep, __rhs, __rhs_sep) \
`__AXI_LITE_TO_AX(__opt_as, __lhs.aw, __lhs_sep, __rhs.aw, __rhs_sep) \
__opt_as __lhs.aw_valid = __rhs.aw_valid; \
`__AXI_LITE_TO_W(__opt_as, __lhs.w, __lhs_sep, __rhs.w, __rhs_sep) \
__opt_as __lhs.w_valid = __rhs.w_valid; \
__opt_as __lhs.b_ready = __rhs.b_ready; \
`__AXI_LITE_TO_AX(__opt_as, __lhs.ar, __lhs_sep, __rhs.ar, __rhs_sep) \
__opt_as __lhs.ar_valid = __rhs.ar_valid; \
__opt_as __lhs.r_ready = __rhs.r_ready;
`define __AXI_LITE_TO_RESP(__opt_as, __lhs, __lhs_sep, __rhs, __rhs_sep) \
__opt_as __lhs.aw_ready = __rhs.aw_ready; \
__opt_as __lhs.ar_ready = __rhs.ar_ready; \
__opt_as __lhs.w_ready = __rhs.w_ready; \
__opt_as __lhs.b_valid = __rhs.b_valid; \
`__AXI_LITE_TO_B(__opt_as, __lhs.b, __lhs_sep, __rhs.b, __rhs_sep) \
__opt_as __lhs.r_valid = __rhs.r_valid; \
`__AXI_LITE_TO_R(__opt_as, __lhs.r, __lhs_sep, __rhs.r, __rhs_sep)
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// Assigning one AXI-Lite interface to another, as if you would do `assign slv = mst;`
//
// The channel assignments `AXI_LITE_ASSIGN_XX(dst, src)` assign all payload and the valid signal of
// the `XX` channel from the `src` to the `dst` interface and they assign the ready signal from the
// `src` to the `dst` interface.
// The interface assignment `AXI_LITE_ASSIGN(dst, src)` assigns all channels including handshakes as
// if `src` was the master of `dst`.
//
// Usage Example:
// `AXI_LITE_ASSIGN(slv, mst)
// `AXI_LITE_ASSIGN_AW(dst, src)
// `AXI_LITE_ASSIGN_R(dst, src)
`define AXI_LITE_ASSIGN_AW(dst, src) \
`__AXI_LITE_TO_AX(assign, dst.aw, _, src.aw, _) \
assign dst.aw_valid = src.aw_valid; \
assign src.aw_ready = dst.aw_ready;
`define AXI_LITE_ASSIGN_W(dst, src) \
`__AXI_LITE_TO_W(assign, dst.w, _, src.w, _) \
assign dst.w_valid = src.w_valid; \
assign src.w_ready = dst.w_ready;
`define AXI_LITE_ASSIGN_B(dst, src) \
`__AXI_LITE_TO_B(assign, dst.b, _, src.b, _) \
assign dst.b_valid = src.b_valid; \
assign src.b_ready = dst.b_ready;
`define AXI_LITE_ASSIGN_AR(dst, src) \
`__AXI_LITE_TO_AX(assign, dst.ar, _, src.ar, _) \
assign dst.ar_valid = src.ar_valid; \
assign src.ar_ready = dst.ar_ready;
`define AXI_LITE_ASSIGN_R(dst, src) \
`__AXI_LITE_TO_R(assign, dst.r, _, src.r, _) \
assign dst.r_valid = src.r_valid; \
assign src.r_ready = dst.r_ready;
`define AXI_LITE_ASSIGN(slv, mst) \
`AXI_LITE_ASSIGN_AW(slv, mst) \
`AXI_LITE_ASSIGN_W(slv, mst) \
`AXI_LITE_ASSIGN_B(mst, slv) \
`AXI_LITE_ASSIGN_AR(slv, mst) \
`AXI_LITE_ASSIGN_R(mst, slv)
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// Setting a Lite interface from channel or request/response structs inside a process.
//
// The channel macros `AXI_LITE_SET_FROM_XX(axi_if, xx_struct)` set the payload signals of the
// `axi_if` interface from the signals in `xx_struct`. They do not set the handshake signals.
// The request macro `AXI_LITE_SET_FROM_REQ(axi_if, req_struct)` sets all request channels (AW, W,
// AR) and the request-side handshake signals (AW, W, and AR valid and B and R ready) of the
// `axi_if` interface from the signals in `req_struct`.
// The response macro `AXI_LITE_SET_FROM_RESP(axi_if, resp_struct)` sets both response channels (B
// and R) and the response-side handshake signals (B and R valid and AW, W, and AR ready) of the
// `axi_if` interface from the signals in `resp_struct`.
//
// Usage Example:
// always_comb begin
// `AXI_LITE_SET_FROM_REQ(my_if, my_req_struct)
// end
`define AXI_LITE_SET_FROM_AW(axi_if, aw_struct) `__AXI_LITE_TO_AX(, axi_if.aw, _, aw_struct, .)
`define AXI_LITE_SET_FROM_W(axi_if, w_struct) `__AXI_LITE_TO_W(, axi_if.w, _, w_struct, .)
`define AXI_LITE_SET_FROM_B(axi_if, b_struct) `__AXI_LITE_TO_B(, axi_if.b, _, b_struct, .)
`define AXI_LITE_SET_FROM_AR(axi_if, ar_struct) `__AXI_LITE_TO_AX(, axi_if.ar, _, ar_struct, .)
`define AXI_LITE_SET_FROM_R(axi_if, r_struct) `__AXI_LITE_TO_R(, axi_if.r, _, r_struct, .)
`define AXI_LITE_SET_FROM_REQ(axi_if, req_struct) `__AXI_LITE_TO_REQ(, axi_if, _, req_struct, .)
`define AXI_LITE_SET_FROM_RESP(axi_if, resp_struct) `__AXI_LITE_TO_RESP(, axi_if, _, resp_struct, .)
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// Assigning a Lite interface from channel or request/response structs outside a process.
//
// The channel macros `AXI_LITE_ASSIGN_FROM_XX(axi_if, xx_struct)` assign the payload signals of the
// `axi_if` interface from the signals in `xx_struct`. They do not assign the handshake signals.
// The request macro `AXI_LITE_ASSIGN_FROM_REQ(axi_if, req_struct)` assigns all request channels
// (AW, W, AR) and the request-side handshake signals (AW, W, and AR valid and B and R ready) of the
// `axi_if` interface from the signals in `req_struct`.
// The response macro `AXI_LITE_ASSIGN_FROM_RESP(axi_if, resp_struct)` assigns both response
// channels (B and R) and the response-side handshake signals (B and R valid and AW, W, and AR
// ready) of the `axi_if` interface from the signals in `resp_struct`.
//
// Usage Example:
// `AXI_LITE_ASSIGN_FROM_REQ(my_if, my_req_struct)
`define AXI_LITE_ASSIGN_FROM_AW(axi_if, aw_struct) `__AXI_LITE_TO_AX(assign, axi_if.aw, _, aw_struct, .)
`define AXI_LITE_ASSIGN_FROM_W(axi_if, w_struct) `__AXI_LITE_TO_W(assign, axi_if.w, _, w_struct, .)
`define AXI_LITE_ASSIGN_FROM_B(axi_if, b_struct) `__AXI_LITE_TO_B(assign, axi_if.b, _, b_struct, .)
`define AXI_LITE_ASSIGN_FROM_AR(axi_if, ar_struct) `__AXI_LITE_TO_AX(assign, axi_if.ar, _, ar_struct, .)
`define AXI_LITE_ASSIGN_FROM_R(axi_if, r_struct) `__AXI_LITE_TO_R(assign, axi_if.r, _, r_struct, .)
`define AXI_LITE_ASSIGN_FROM_REQ(axi_if, req_struct) `__AXI_LITE_TO_REQ(assign, axi_if, _, req_struct, .)
`define AXI_LITE_ASSIGN_FROM_RESP(axi_if, resp_struct) `__AXI_LITE_TO_RESP(assign, axi_if, _, resp_struct, .)
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// Setting channel or request/response structs from an interface inside a process.
//
// The channel macros `AXI_LITE_SET_TO_XX(xx_struct, axi_if)` set the signals of `xx_struct` to the
// payload signals of that channel in the `axi_if` interface. They do not set the handshake
// signals.
// The request macro `AXI_LITE_SET_TO_REQ(axi_if, req_struct)` sets all signals of `req_struct`
// (i.e., request channel (AW, W, AR) payload and request-side handshake signals (AW, W, and AR
// valid and B and R ready)) to the signals in the `axi_if` interface.
// The response macro `AXI_LITE_SET_TO_RESP(axi_if, resp_struct)` sets all signals of `resp_struct`
// (i.e., response channel (B and R) payload and response-side handshake signals (B and R valid and
// AW, W, and AR ready)) to the signals in the `axi_if` interface.
//
// Usage Example:
// always_comb begin
// `AXI_LITE_SET_TO_REQ(my_req_struct, my_if)
// end
`define AXI_LITE_SET_TO_AW(aw_struct, axi_if) `__AXI_LITE_TO_AX(, aw_struct, ., axi_if.aw, _)
`define AXI_LITE_SET_TO_W(w_struct, axi_if) `__AXI_LITE_TO_W(, w_struct, ., axi_if.w, _)
`define AXI_LITE_SET_TO_B(b_struct, axi_if) `__AXI_LITE_TO_B(, b_struct, ., axi_if.b, _)
`define AXI_LITE_SET_TO_AR(ar_struct, axi_if) `__AXI_LITE_TO_AX(, ar_struct, ., axi_if.ar, _)
`define AXI_LITE_SET_TO_R(r_struct, axi_if) `__AXI_LITE_TO_R(, r_struct, ., axi_if.r, _)
`define AXI_LITE_SET_TO_REQ(req_struct, axi_if) `__AXI_LITE_TO_REQ(, req_struct, ., axi_if, _)
`define AXI_LITE_SET_TO_RESP(resp_struct, axi_if) `__AXI_LITE_TO_RESP(, resp_struct, ., axi_if, _)
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// Assigning channel or request/response structs from an interface outside a process.
//
// The channel macros `AXI_LITE_ASSIGN_TO_XX(xx_struct, axi_if)` assign the signals of `xx_struct`
// to the payload signals of that channel in the `axi_if` interface. They do not assign the
// handshake signals.
// The request macro `AXI_LITE_ASSIGN_TO_REQ(axi_if, req_struct)` assigns all signals of
// `req_struct` (i.e., request channel (AW, W, AR) payload and request-side handshake signals (AW,
// W, and AR valid and B and R ready)) to the signals in the `axi_if` interface.
// The response macro `AXI_LITE_ASSIGN_TO_RESP(axi_if, resp_struct)` assigns all signals of
// `resp_struct` (i.e., response channel (B and R) payload and response-side handshake signals (B
// and R valid and AW, W, and AR ready)) to the signals in the `axi_if` interface.
//
// Usage Example:
// `AXI_LITE_ASSIGN_TO_REQ(my_req_struct, my_if)
`define AXI_LITE_ASSIGN_TO_AW(aw_struct, axi_if) `__AXI_LITE_TO_AX(assign, aw_struct, ., axi_if.aw, _)
`define AXI_LITE_ASSIGN_TO_W(w_struct, axi_if) `__AXI_LITE_TO_W(assign, w_struct, ., axi_if.w, _)
`define AXI_LITE_ASSIGN_TO_B(b_struct, axi_if) `__AXI_LITE_TO_B(assign, b_struct, ., axi_if.b, _)
`define AXI_LITE_ASSIGN_TO_AR(ar_struct, axi_if) `__AXI_LITE_TO_AX(assign, ar_struct, ., axi_if.ar, _)
`define AXI_LITE_ASSIGN_TO_R(r_struct, axi_if) `__AXI_LITE_TO_R(assign, r_struct, ., axi_if.r, _)
`define AXI_LITE_ASSIGN_TO_REQ(req_struct, axi_if) `__AXI_LITE_TO_REQ(assign, req_struct, ., axi_if, _)
`define AXI_LITE_ASSIGN_TO_RESP(resp_struct, axi_if) `__AXI_LITE_TO_RESP(assign, resp_struct, ., axi_if, _)
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// Setting channel or request/response structs from another struct inside a process.
//
// The channel macros `AXI_LITE_SET_XX_STRUCT(lhs, rhs)` set the fields of the `lhs` channel struct
// to the fields of the `rhs` channel struct. They do not set the handshake signals, which are not
// part of channel structs.
// The request macro `AXI_LITE_SET_REQ_STRUCT(lhs, rhs)` sets all fields of the `lhs` request struct
// to the fields of the `rhs` request struct. This includes all request channel (AW, W, AR) payload
// and request-side handshake signals (AW, W, and AR valid and B and R ready).
// The response macro `AXI_LITE_SET_RESP_STRUCT(lhs, rhs)` sets all fields of the `lhs` response
// struct to the fields of the `rhs` response struct. This includes all response channel (B and R)
// payload and response-side handshake signals (B and R valid and AW, W, and R ready).
//
// Usage Example:
// always_comb begin
// `AXI_LITE_SET_REQ_STRUCT(my_req_struct, another_req_struct)
// end
`define AXI_LITE_SET_AW_STRUCT(lhs, rhs) `__AXI_LITE_TO_AX(, lhs, ., rhs, .)
`define AXI_LITE_SET_W_STRUCT(lhs, rhs) `__AXI_LITE_TO_W(, lhs, ., rhs, .)
`define AXI_LITE_SET_B_STRUCT(lhs, rhs) `__AXI_LITE_TO_B(, lhs, ., rhs, .)
`define AXI_LITE_SET_AR_STRUCT(lhs, rhs) `__AXI_LITE_TO_AX(, lhs, ., rhs, .)
`define AXI_LITE_SET_R_STRUCT(lhs, rhs) `__AXI_LITE_TO_R(, lhs, ., rhs, .)
`define AXI_LITE_SET_REQ_STRUCT(lhs, rhs) `__AXI_LITE_TO_REQ(, lhs, ., rhs, .)
`define AXI_LITE_SET_RESP_STRUCT(lhs, rhs) `__AXI_LITE_TO_RESP(, lhs, ., rhs, .)
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// Assigning channel or request/response structs from another struct outside a process.
//
// The channel macros `AXI_LITE_ASSIGN_XX_STRUCT(lhs, rhs)` assign the fields of the `lhs` channel
// struct to the fields of the `rhs` channel struct. They do not assign the handshake signals,
// which are not part of the channel structs.
// The request macro `AXI_LITE_ASSIGN_REQ_STRUCT(lhs, rhs)` assigns all fields of the `lhs` request
// struct to the fields of the `rhs` request struct. This includes all request channel (AW, W, AR)
// payload and request-side handshake signals (AW, W, and AR valid and B and R ready).
// The response macro `AXI_LITE_ASSIGN_RESP_STRUCT(lhs, rhs)` assigns all fields of the `lhs`
// response struct to the fields of the `rhs` response struct. This includes all response channel
// (B and R) payload and response-side handshake signals (B and R valid and AW, W, and R ready).
//
// Usage Example:
// `AXI_LITE_ASSIGN_REQ_STRUCT(my_req_struct, another_req_struct)
`define AXI_LITE_ASSIGN_AW_STRUCT(lhs, rhs) `__AXI_LITE_TO_AX(assign, lhs, ., rhs, .)
`define AXI_LITE_ASSIGN_W_STRUCT(lhs, rhs) `__AXI_LITE_TO_W(assign, lhs, ., rhs, .)
`define AXI_LITE_ASSIGN_B_STRUCT(lhs, rhs) `__AXI_LITE_TO_B(assign, lhs, ., rhs, .)
`define AXI_LITE_ASSIGN_AR_STRUCT(lhs, rhs) `__AXI_LITE_TO_AX(assign, lhs, ., rhs, .)
`define AXI_LITE_ASSIGN_R_STRUCT(lhs, rhs) `__AXI_LITE_TO_R(assign, lhs, ., rhs, .)
`define AXI_LITE_ASSIGN_REQ_STRUCT(lhs, rhs) `__AXI_LITE_TO_REQ(assign, lhs, ., rhs, .)
`define AXI_LITE_ASSIGN_RESP_STRUCT(lhs, rhs) `__AXI_LITE_TO_RESP(assign, lhs, ., rhs, .)
////////////////////////////////////////////////////////////////////////////////////////////////////
`endif

View file

@ -0,0 +1,211 @@
// 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:
// - Andreas Kurth <akurth@iis.ee.ethz.ch>
// - Florian Zaruba <zarubaf@iis.ee.ethz.ch>
// - Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>
// Macros to define AXI and AXI-Lite Channel and Request/Response Structs
`ifndef AXI_TYPEDEF_SVH_
`define AXI_TYPEDEF_SVH_
////////////////////////////////////////////////////////////////////////////////////////////////////
// AXI4+ATOP Channel and Request/Response Structs
//
// Usage Example:
// `AXI_TYPEDEF_AW_CHAN_T(axi_aw_t, axi_addr_t, axi_id_t, axi_user_t)
// `AXI_TYPEDEF_W_CHAN_T(axi_w_t, axi_data_t, axi_strb_t, axi_user_t)
// `AXI_TYPEDEF_B_CHAN_T(axi_b_t, axi_id_t, axi_user_t)
// `AXI_TYPEDEF_AR_CHAN_T(axi_ar_t, axi_addr_t, axi_id_t, axi_user_t)
// `AXI_TYPEDEF_R_CHAN_T(axi_r_t, axi_data_t, axi_id_t, axi_user_t)
// `AXI_TYPEDEF_REQ_T(axi_req_t, axi_aw_t, axi_w_t, axi_ar_t)
// `AXI_TYPEDEF_RESP_T(axi_resp_t, axi_b_t, axi_r_t)
`define AXI_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t, id_t, user_t) \
typedef struct packed { \
id_t id; \
addr_t addr; \
axi_pkg::len_t len; \
axi_pkg::size_t size; \
axi_pkg::burst_t burst; \
logic lock; \
axi_pkg::cache_t cache; \
axi_pkg::prot_t prot; \
axi_pkg::qos_t qos; \
axi_pkg::region_t region; \
axi_pkg::atop_t atop; \
user_t user; \
} aw_chan_t;
`define AXI_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t, user_t) \
typedef struct packed { \
data_t data; \
strb_t strb; \
logic last; \
user_t user; \
} w_chan_t;
`define AXI_TYPEDEF_B_CHAN_T(b_chan_t, id_t, user_t) \
typedef struct packed { \
id_t id; \
axi_pkg::resp_t resp; \
user_t user; \
} b_chan_t;
`define AXI_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t, id_t, user_t) \
typedef struct packed { \
id_t id; \
addr_t addr; \
axi_pkg::len_t len; \
axi_pkg::size_t size; \
axi_pkg::burst_t burst; \
logic lock; \
axi_pkg::cache_t cache; \
axi_pkg::prot_t prot; \
axi_pkg::qos_t qos; \
axi_pkg::region_t region; \
user_t user; \
} ar_chan_t;
`define AXI_TYPEDEF_R_CHAN_T(r_chan_t, data_t, id_t, user_t) \
typedef struct packed { \
id_t id; \
data_t data; \
axi_pkg::resp_t resp; \
logic last; \
user_t user; \
} r_chan_t;
`define AXI_TYPEDEF_REQ_T(req_t, aw_chan_t, w_chan_t, ar_chan_t) \
typedef struct packed { \
aw_chan_t aw; \
logic aw_valid; \
w_chan_t w; \
logic w_valid; \
logic b_ready; \
ar_chan_t ar; \
logic ar_valid; \
logic r_ready; \
} req_t;
`define AXI_TYPEDEF_RESP_T(resp_t, b_chan_t, r_chan_t) \
typedef struct packed { \
logic aw_ready; \
logic ar_ready; \
logic w_ready; \
logic b_valid; \
b_chan_t b; \
logic r_valid; \
r_chan_t r; \
} resp_t;
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// All AXI4+ATOP Channels and Request/Response Structs in One Macro
//
// 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(axi, addr_t, id_t, data_t, strb_t, user_t)
//
// 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)
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// AXI4-Lite Channel and Request/Response Structs
//
// Usage Example:
// `AXI_LITE_TYPEDEF_AW_CHAN_T(axi_lite_aw_t, axi_lite_addr_t)
// `AXI_LITE_TYPEDEF_W_CHAN_T(axi_lite_w_t, axi_lite_data_t, axi_lite_strb_t)
// `AXI_LITE_TYPEDEF_B_CHAN_T(axi_lite_b_t)
// `AXI_LITE_TYPEDEF_AR_CHAN_T(axi_lite_ar_t, axi_lite_addr_t)
// `AXI_LITE_TYPEDEF_R_CHAN_T(axi_lite_r_t, axi_lite_data_t)
// `AXI_LITE_TYPEDEF_REQ_T(axi_lite_req_t, axi_lite_aw_t, axi_lite_w_t, axi_lite_ar_t)
// `AXI_LITE_TYPEDEF_RESP_T(axi_lite_resp_t, axi_lite_b_t, axi_lite_r_t)
`define AXI_LITE_TYPEDEF_AW_CHAN_T(aw_chan_lite_t, addr_t) \
typedef struct packed { \
addr_t addr; \
axi_pkg::prot_t prot; \
} aw_chan_lite_t;
`define AXI_LITE_TYPEDEF_W_CHAN_T(w_chan_lite_t, data_t, strb_t) \
typedef struct packed { \
data_t data; \
strb_t strb; \
} w_chan_lite_t;
`define AXI_LITE_TYPEDEF_B_CHAN_T(b_chan_lite_t) \
typedef struct packed { \
axi_pkg::resp_t resp; \
} b_chan_lite_t;
`define AXI_LITE_TYPEDEF_AR_CHAN_T(ar_chan_lite_t, addr_t) \
typedef struct packed { \
addr_t addr; \
axi_pkg::prot_t prot; \
} ar_chan_lite_t;
`define AXI_LITE_TYPEDEF_R_CHAN_T(r_chan_lite_t, data_t) \
typedef struct packed { \
data_t data; \
axi_pkg::resp_t resp; \
} r_chan_lite_t;
`define AXI_LITE_TYPEDEF_REQ_T(req_lite_t, aw_chan_lite_t, w_chan_lite_t, ar_chan_lite_t) \
typedef struct packed { \
aw_chan_lite_t aw; \
logic aw_valid; \
w_chan_lite_t w; \
logic w_valid; \
logic b_ready; \
ar_chan_lite_t ar; \
logic ar_valid; \
logic r_ready; \
} req_lite_t;
`define AXI_LITE_TYPEDEF_RESP_T(resp_lite_t, b_chan_lite_t, r_chan_lite_t) \
typedef struct packed { \
logic aw_ready; \
logic w_ready; \
b_chan_lite_t b; \
logic b_valid; \
logic ar_ready; \
r_chan_lite_t r; \
logic r_valid; \
} resp_lite_t;
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// All AXI4-Lite Channels and Request/Response Structs in One Macro
//
// 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(axi_lite, 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(__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)
////////////////////////////////////////////////////////////////////////////////////////////////////
`endif

View file

@ -0,0 +1,444 @@
// 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:
// - Andreas Kurth <akurth@iis.ee.ethz.ch>
// - Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>
/// Filter atomic operations (ATOPs) in a protocol-compliant manner.
///
/// This module filters atomic operations (ATOPs), i.e., write transactions that have a non-zero
/// `aw_atop` value, from its `slv` to its `mst` port. This module guarantees that:
///
/// 1) `aw_atop` is always zero on the `mst` port;
///
/// 2) write transactions with non-zero `aw_atop` on the `slv` port are handled in conformance with
/// the AXI standard by replying to such write transactions with the proper B and R responses.
/// The response code on atomic operations that reach this module is always SLVERR
/// (implementation-specific, not defined in the AXI standard).
///
/// ## Intended usage
/// This module is intended to be placed between masters that may issue ATOPs and slaves that do not
/// support ATOPs. That way, this module ensures that the AXI protocol remains in a defined state on
/// systems with mixed ATOP capabilities.
///
/// ## Specification reminder
/// The AXI standard specifies that there may be no ordering requirements between different atomic
/// bursts (i.e., a burst started by an AW with ATOP other than 0) and none between atomic bursts
/// and non-atomic bursts [E2.1.4]. That is, **an atomic burst may never have the same ID as any
/// other write or read burst that is in-flight at the same time**.
module axi_atop_filter #(
/// AXI ID width
parameter int unsigned AxiIdWidth = 0,
/// Maximum number of in-flight AXI write transactions
parameter int unsigned AxiMaxWriteTxns = 0,
/// AXI request type
parameter type req_t = logic,
/// AXI response type
parameter type resp_t = logic
) (
/// Rising-edge clock of both ports
input logic clk_i,
/// Asynchronous reset, active low
input logic rst_ni,
/// Slave port request
input req_t slv_req_i,
/// Slave port response
output resp_t slv_resp_o,
/// Master port request
output req_t mst_req_o,
/// Master port response
input resp_t mst_resp_i
);
// Minimum counter width is 2 to detect underflows.
localparam int unsigned COUNTER_WIDTH = (AxiMaxWriteTxns == 1) ? 2 : $clog2(AxiMaxWriteTxns+1);
typedef struct packed {
logic underflow;
logic [COUNTER_WIDTH-1:0] cnt;
} cnt_t;
cnt_t w_cnt_d, w_cnt_q;
typedef enum logic [2:0] {
W_FEEDTHROUGH, BLOCK_AW, ABSORB_W, HOLD_B, INJECT_B, WAIT_R
} w_state_e;
w_state_e w_state_d, w_state_q;
typedef enum logic [1:0] { R_FEEDTHROUGH, INJECT_R, R_HOLD } r_state_e;
r_state_e r_state_d, r_state_q;
typedef logic [AxiIdWidth-1:0] id_t;
id_t id_d, id_q;
typedef logic [7:0] len_t;
len_t r_beats_d, r_beats_q;
typedef struct packed {
len_t len;
} r_resp_cmd_t;
r_resp_cmd_t r_resp_cmd_push, r_resp_cmd_pop;
logic aw_without_complete_w_downstream,
complete_w_without_aw_downstream,
r_resp_cmd_push_valid, r_resp_cmd_push_ready,
r_resp_cmd_pop_valid, r_resp_cmd_pop_ready;
// An AW without a complete W burst is in-flight downstream if the W counter is > 0 and not
// underflowed.
assign aw_without_complete_w_downstream = !w_cnt_q.underflow && (w_cnt_q.cnt > 0);
// A complete W burst without AW is in-flight downstream if the W counter is -1.
assign complete_w_without_aw_downstream = w_cnt_q.underflow && &(w_cnt_q.cnt);
// Manage AW, W, and B channels.
always_comb begin
// Defaults:
// Disable AW and W handshakes.
mst_req_o.aw_valid = 1'b0;
slv_resp_o.aw_ready = 1'b0;
mst_req_o.w_valid = 1'b0;
slv_resp_o.w_ready = 1'b0;
// Feed write responses through.
mst_req_o.b_ready = slv_req_i.b_ready;
slv_resp_o.b_valid = mst_resp_i.b_valid;
slv_resp_o.b = mst_resp_i.b;
// Keep ID stored for B and R response.
id_d = id_q;
// Do not push R response commands.
r_resp_cmd_push_valid = 1'b0;
// Keep the current state.
w_state_d = w_state_q;
unique case (w_state_q)
W_FEEDTHROUGH: begin
// Feed AW channel through if the maximum number of outstanding bursts is not reached.
if (complete_w_without_aw_downstream || (w_cnt_q.cnt < AxiMaxWriteTxns)) begin
mst_req_o.aw_valid = slv_req_i.aw_valid;
slv_resp_o.aw_ready = mst_resp_i.aw_ready;
end
// Feed W channel through if ..
if (aw_without_complete_w_downstream // .. downstream is missing W bursts ..
// .. or a new non-ATOP AW is being applied and there is not already a complete W burst
// downstream (to prevent underflows of w_cnt).
|| ((slv_req_i.aw_valid && slv_req_i.aw.atop[5:4] == axi_pkg::ATOP_NONE)
&& !complete_w_without_aw_downstream)
) begin
mst_req_o.w_valid = slv_req_i.w_valid;
slv_resp_o.w_ready = mst_resp_i.w_ready;
end
// Filter out AWs that are atomic operations.
if (slv_req_i.aw_valid && slv_req_i.aw.atop[5:4] != axi_pkg::ATOP_NONE) begin
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
// 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.
r_resp_cmd_push_valid = 1'b1;
end
// If downstream is missing W beats, block the AW channel and let the W bursts complete.
if (aw_without_complete_w_downstream) begin
w_state_d = BLOCK_AW;
// If downstream is not missing W beats, absorb the W beats for this atomic AW.
end else begin
mst_req_o.w_valid = 1'b0; // Do not let W beats pass to master port.
slv_resp_o.w_ready = 1'b1; // Absorb W beats on slave port.
if (slv_req_i.w_valid && slv_req_i.w.last) begin
// If the W beat is valid and the last, proceed by injecting the B response.
// However, if there is a non-handshaked B on our response port, we must let that
// complete first.
if (slv_resp_o.b_valid && !slv_req_i.b_ready) begin
w_state_d = HOLD_B;
end else begin
w_state_d = INJECT_B;
end
end else begin
// Otherwise continue with absorbing W beats.
w_state_d = ABSORB_W;
end
end
end
end
BLOCK_AW: begin
// Feed W channel through to let outstanding bursts complete.
if (aw_without_complete_w_downstream) begin
mst_req_o.w_valid = slv_req_i.w_valid;
slv_resp_o.w_ready = mst_resp_i.w_ready;
end else begin
// If there are no more outstanding W bursts, start absorbing the next W burst.
slv_resp_o.w_ready = 1'b1;
if (slv_req_i.w_valid && slv_req_i.w.last) begin
// If the W beat is valid and the last, proceed by injecting the B response.
if (slv_resp_o.b_valid && !slv_req_i.b_ready) begin
w_state_d = HOLD_B;
end else begin
w_state_d = INJECT_B;
end
end else begin
// Otherwise continue with absorbing W beats.
w_state_d = ABSORB_W;
end
end
end
ABSORB_W: begin
// Absorb all W beats of the current burst.
slv_resp_o.w_ready = 1'b1;
if (slv_req_i.w_valid && slv_req_i.w.last) begin
if (slv_resp_o.b_valid && !slv_req_i.b_ready) begin
w_state_d = HOLD_B;
end else begin
w_state_d = INJECT_B;
end
end
end
HOLD_B: begin
// Proceed with injection of B response upon handshake.
if (slv_resp_o.b_valid && slv_req_i.b_ready) begin
w_state_d = INJECT_B;
end
end
INJECT_B: begin
// Pause forwarding of B response.
mst_req_o.b_ready = 1'b0;
// Inject error response instead. Since the B channel has an ID and the atomic burst we are
// replying to is guaranteed to be the only burst with this ID in flight, we do not have to
// observe any ordering and can immediately inject on the B channel.
slv_resp_o.b = '0;
slv_resp_o.b.id = id_q;
slv_resp_o.b.resp = axi_pkg::RESP_SLVERR;
slv_resp_o.b_valid = 1'b1;
if (slv_req_i.b_ready) begin
// If not all beats of the R response have been injected, wait for them. Otherwise, return
// to `W_FEEDTHROUGH`.
if (r_resp_cmd_pop_valid && !r_resp_cmd_pop_ready) begin
w_state_d = WAIT_R;
end else begin
w_state_d = W_FEEDTHROUGH;
end
end
end
WAIT_R: begin
// Wait with returning to `W_FEEDTHROUGH` until all beats of the R response have been
// injected.
if (!r_resp_cmd_pop_valid) begin
w_state_d = W_FEEDTHROUGH;
end
end
default: w_state_d = W_FEEDTHROUGH;
endcase
end
// Connect signals on AW and W channel that are not managed by the control FSM from slave port to
// master port.
// Feed-through of the AW and W vectors, make sure that downstream aw.atop is always zero
always_comb begin
// overwrite the atop signal
mst_req_o.aw = slv_req_i.aw;
mst_req_o.aw.atop = '0;
end
assign mst_req_o.w = slv_req_i.w;
// Manage R channel.
always_comb begin
// Defaults:
// Feed read responses through.
slv_resp_o.r = mst_resp_i.r;
slv_resp_o.r_valid = mst_resp_i.r_valid;
mst_req_o.r_ready = slv_req_i.r_ready;
// Do not pop R response command.
r_resp_cmd_pop_ready = 1'b0;
// Keep the current value of the beats counter.
r_beats_d = r_beats_q;
// Keep the current state.
r_state_d = r_state_q;
unique case (r_state_q)
R_FEEDTHROUGH: begin
if (mst_resp_i.r_valid && !slv_req_i.r_ready) begin
r_state_d = R_HOLD;
end else if (r_resp_cmd_pop_valid) begin
// Upon a command to inject an R response, immediately proceed with doing so because there
// are no ordering requirements with other bursts that may be ongoing on the R channel at
// this moment.
r_beats_d = r_resp_cmd_pop.len;
r_state_d = INJECT_R;
end
end
INJECT_R: begin
mst_req_o.r_ready = 1'b0;
slv_resp_o.r = '0;
slv_resp_o.r.id = id_q;
slv_resp_o.r.resp = axi_pkg::RESP_SLVERR;
slv_resp_o.r.last = (r_beats_q == '0);
slv_resp_o.r_valid = 1'b1;
if (slv_req_i.r_ready) begin
if (slv_resp_o.r.last) begin
r_resp_cmd_pop_ready = 1'b1;
r_state_d = R_FEEDTHROUGH;
end else begin
r_beats_d -= 1;
end
end
end
R_HOLD: begin
if (mst_resp_i.r_valid && slv_req_i.r_ready) begin
r_state_d = R_FEEDTHROUGH;
end
end
default: r_state_d = R_FEEDTHROUGH;
endcase
end
// Feed all signals on AR through.
assign mst_req_o.ar = slv_req_i.ar;
assign mst_req_o.ar_valid = slv_req_i.ar_valid;
assign slv_resp_o.ar_ready = mst_resp_i.ar_ready;
// Keep track of outstanding downstream write bursts and responses.
always_comb begin
w_cnt_d = w_cnt_q;
if (mst_req_o.aw_valid && mst_resp_i.aw_ready) begin
w_cnt_d.cnt += 1;
end
if (mst_req_o.w_valid && mst_resp_i.w_ready && mst_req_o.w.last) begin
w_cnt_d.cnt -= 1;
end
if (w_cnt_q.underflow && (w_cnt_d.cnt == '0)) begin
w_cnt_d.underflow = 1'b0;
end else if (w_cnt_q.cnt == '0 && &(w_cnt_d.cnt)) begin
w_cnt_d.underflow = 1'b1;
end
end
always_ff @(posedge clk_i, negedge rst_ni) begin
if (!rst_ni) begin
id_q <= '0;
r_beats_q <= '0;
r_state_q <= R_FEEDTHROUGH;
w_cnt_q <= '{default: '0};
w_state_q <= W_FEEDTHROUGH;
end else begin
id_q <= id_d;
r_beats_q <= r_beats_d;
r_state_q <= r_state_d;
w_cnt_q <= w_cnt_d;
w_state_q <= w_state_d;
end
end
stream_register #(
.T(r_resp_cmd_t)
) r_resp_cmd (
.clk_i (clk_i),
.rst_ni (rst_ni),
.clr_i (1'b0),
.testmode_i (1'b0),
.valid_i (r_resp_cmd_push_valid),
.ready_o (r_resp_cmd_push_ready),
.data_i (r_resp_cmd_push),
.valid_o (r_resp_cmd_pop_valid),
.ready_i (r_resp_cmd_pop_ready),
.data_o (r_resp_cmd_pop)
);
assign r_resp_cmd_push.len = slv_req_i.aw.len;
// pragma translate_off
`ifndef VERILATOR
initial begin: p_assertions
assert (AxiIdWidth >= 1) else $fatal(1, "AXI ID width must be at least 1!");
assert (AxiMaxWriteTxns >= 1)
else $fatal(1, "Maximum number of outstanding write transactions must be at least 1!");
end
`endif
// pragma translate_on
endmodule
`include "axi/assign.svh"
`include "axi/typedef.svh"
/// Interface variant of [`axi_atop_filter`](module.axi_atop_filter).
module axi_atop_filter_intf #(
/// AXI ID width
parameter int unsigned AXI_ID_WIDTH = 0,
/// AXI address width
parameter int unsigned AXI_ADDR_WIDTH = 0,
/// AXI data width
parameter int unsigned AXI_DATA_WIDTH = 0,
/// AXI user signal width
parameter int unsigned AXI_USER_WIDTH = 0,
/// Maximum number of in-flight AXI write transactions
parameter int unsigned AXI_MAX_WRITE_TXNS = 0
) (
/// Rising-edge clock of both ports
input logic clk_i,
/// Asynchronous reset, active low
input logic rst_ni,
/// Slave interface port
AXI_BUS.Slave slv,
/// Master interface port
AXI_BUS.Master mst
);
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(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_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_atop_filter #(
.AxiIdWidth ( AXI_ID_WIDTH ),
// 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 )
) i_axi_atop_filter (
.clk_i,
.rst_ni,
.slv_req_i ( slv_req ),
.slv_resp_o ( slv_resp ),
.mst_req_o ( mst_req ),
.mst_resp_i ( mst_resp )
);
// pragma translate_off
`ifndef VERILATOR
initial begin: p_assertions
assert (AXI_ADDR_WIDTH >= 1) else $fatal(1, "AXI ADDR width must be at least 1!");
assert (AXI_DATA_WIDTH >= 1) else $fatal(1, "AXI DATA 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

View file

@ -0,0 +1,579 @@
// 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:
// - Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>
// - Andreas Kurth <akurth@iis.ee.ethz.ch>
`include "axi/typedef.svh"
`include "common_cells/registers.svh"
/// Split AXI4 bursts into single-beat transactions.
///
/// ## Limitations
///
/// - This module does not support wrapping ([`axi_pkg::BURST_WRAP`](package.axi_pkg)) bursts and
/// responds to such bursts with slave error(s).
/// - This module does not support atomic operations (ATOPs) and responds to ATOPs with a slave
/// error. Place an [`axi_atop_filter`](module.axi_atop_filter) before this module if upstream
/// modules can generate ATOPs.
module axi_burst_splitter #(
// Maximum number of AXI read bursts outstanding at the same time
parameter int unsigned MaxReadTxns = 32'd0,
// Maximum number of AXI write bursts outstanding at the same time
parameter int unsigned MaxWriteTxns = 32'd0,
// AXI Bus Types
parameter int unsigned AddrWidth = 32'd0,
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
) (
input logic clk_i,
input logic rst_ni,
// Input / Slave Port
input req_t slv_req_i,
output resp_t slv_resp_o,
// Output / Master Port
output req_t mst_req_o,
input resp_t mst_resp_i
);
typedef logic [AddrWidth-1:0] addr_t;
typedef logic [DataWidth-1:0] data_t;
typedef logic [IdWidth-1:0] id_t;
typedef logic [DataWidth/8-1:0] strb_t;
typedef logic [UserWidth-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)
// 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;
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 )
) i_demux_supported_vs_unsupported (
.clk_i,
.rst_ni,
.test_i ( 1'b0 ),
.slv_req_i,
.slv_aw_select_i ( sel_aw_unsupported ),
.slv_ar_select_i ( sel_ar_unsupported ),
.slv_resp_o,
.mst_reqs_o ( {unsupported_req, act_req} ),
.mst_resps_i ( {unsupported_resp, act_resp} )
);
// Define supported transactions.
function bit txn_supported(axi_pkg::atop_t atop, axi_pkg::burst_t burst, axi_pkg::cache_t cache,
axi_pkg::len_t len);
// Single-beat transactions do not need splitting, so all are supported.
if (len == '0) return 1'b1;
// Wrapping bursts are currently not supported.
if (burst == axi_pkg::BURST_WRAP) return 1'b0;
// ATOPs are not supported.
if (atop != '0) return 1'b0;
// The AXI Spec (A3.4.1) only allows splitting non-modifiable transactions ..
if (!axi_pkg::modifiable(cache)) begin
// .. if they are INCR bursts and longer than 16 beats.
return (burst == axi_pkg::BURST_INCR) & (len > 16);
end
// All other transactions are supported.
return 1'b1;
endfunction
assign sel_aw_unsupported = ~txn_supported(slv_req_i.aw.atop, slv_req_i.aw.burst,
slv_req_i.aw.cache, slv_req_i.aw.len);
assign sel_ar_unsupported = ~txn_supported('0, slv_req_i.ar.burst,
slv_req_i.ar.cache, slv_req_i.ar.len);
// Respond to unsupported transactions with slave errors.
axi_err_slv #(
.AxiIdWidth ( IdWidth ),
.req_t ( req_t ),
.resp_t ( 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.
) i_err_slv (
.clk_i,
.rst_ni,
.test_i ( 1'b0 ),
.slv_req_i ( unsupported_req ),
.slv_resp_o ( unsupported_resp )
);
// --------------------------------------------------
// AW Channel
// --------------------------------------------------
logic w_cnt_dec, w_cnt_req, w_cnt_gnt, w_cnt_err;
axi_pkg::len_t w_cnt_len;
axi_burst_splitter_ax_chan #(
.chan_t ( aw_chan_t ),
.IdWidth ( IdWidth ),
.MaxTxns ( MaxWriteTxns )
) i_axi_burst_splitter_aw_chan (
.clk_i,
.rst_ni,
.ax_i ( act_req.aw ),
.ax_valid_i ( act_req.aw_valid ),
.ax_ready_o ( act_resp.aw_ready ),
.ax_o ( mst_req_o.aw ),
.ax_valid_o ( mst_req_o.aw_valid ),
.ax_ready_i ( mst_resp_i.aw_ready ),
.cnt_id_i ( mst_resp_i.b.id ),
.cnt_len_o ( w_cnt_len ),
.cnt_set_err_i ( mst_resp_i.b.resp[1] ),
.cnt_err_o ( w_cnt_err ),
.cnt_dec_i ( w_cnt_dec ),
.cnt_req_i ( w_cnt_req ),
.cnt_gnt_o ( w_cnt_gnt )
);
// --------------------------------------------------
// W Channel
// --------------------------------------------------
// Feed through, except `last`, which is always set.
always_comb begin
mst_req_o.w = act_req.w;
mst_req_o.w.last = 1'b1; // overwrite last flag
end
assign mst_req_o.w_valid = act_req.w_valid;
assign act_resp.w_ready = mst_resp_i.w_ready;
// --------------------------------------------------
// B Channel
// --------------------------------------------------
// Filter B response, except for the last one
enum logic {BReady, BWait} b_state_d, b_state_q;
logic b_err_d, b_err_q;
always_comb begin
mst_req_o.b_ready = 1'b0;
act_resp.b = '0;
act_resp.b_valid = 1'b0;
w_cnt_dec = 1'b0;
w_cnt_req = 1'b0;
b_err_d = b_err_q;
b_state_d = b_state_q;
unique case (b_state_q)
BReady: begin
if (mst_resp_i.b_valid) begin
w_cnt_req = 1'b1;
if (w_cnt_gnt) begin
if (w_cnt_len == 8'd0) begin
act_resp.b = mst_resp_i.b;
if (w_cnt_err) begin
act_resp.b.resp = axi_pkg::RESP_SLVERR;
end
act_resp.b_valid = 1'b1;
w_cnt_dec = 1'b1;
if (act_req.b_ready) begin
mst_req_o.b_ready = 1'b1;
end else begin
b_state_d = BWait;
b_err_d = w_cnt_err;
end
end else begin
mst_req_o.b_ready = 1'b1;
w_cnt_dec = 1'b1;
end
end
end
end
BWait: begin
act_resp.b = mst_resp_i.b;
if (b_err_q) begin
act_resp.b.resp = axi_pkg::RESP_SLVERR;
end
act_resp.b_valid = 1'b1;
if (mst_resp_i.b_valid && act_req.b_ready) begin
mst_req_o.b_ready = 1'b1;
b_state_d = BReady;
end
end
default: /*do nothing*/;
endcase
end
// --------------------------------------------------
// AR Channel
// --------------------------------------------------
// See description of `ax_chan` module.
logic r_cnt_dec, r_cnt_req, r_cnt_gnt;
axi_pkg::len_t r_cnt_len;
axi_burst_splitter_ax_chan #(
.chan_t ( ar_chan_t ),
.IdWidth ( IdWidth ),
.MaxTxns ( MaxReadTxns )
) i_axi_burst_splitter_ar_chan (
.clk_i,
.rst_ni,
.ax_i ( act_req.ar ),
.ax_valid_i ( act_req.ar_valid ),
.ax_ready_o ( act_resp.ar_ready ),
.ax_o ( mst_req_o.ar ),
.ax_valid_o ( mst_req_o.ar_valid ),
.ax_ready_i ( mst_resp_i.ar_ready ),
.cnt_id_i ( mst_resp_i.r.id ),
.cnt_len_o ( r_cnt_len ),
.cnt_set_err_i ( 1'b0 ),
.cnt_err_o ( ),
.cnt_dec_i ( r_cnt_dec ),
.cnt_req_i ( r_cnt_req ),
.cnt_gnt_o ( r_cnt_gnt )
);
// --------------------------------------------------
// R Channel
// --------------------------------------------------
// Reconstruct `last`, feed rest through.
logic r_last_d, r_last_q;
enum logic {RFeedthrough, RWait} r_state_d, r_state_q;
always_comb begin
r_cnt_dec = 1'b0;
r_cnt_req = 1'b0;
r_last_d = r_last_q;
r_state_d = r_state_q;
mst_req_o.r_ready = 1'b0;
act_resp.r = mst_resp_i.r;
act_resp.r.last = 1'b0;
act_resp.r_valid = 1'b0;
unique case (r_state_q)
RFeedthrough: begin
// If downstream has an R beat and the R counters can give us the remaining length of
// that burst, ...
if (mst_resp_i.r_valid) begin
r_cnt_req = 1'b1;
if (r_cnt_gnt) begin
r_last_d = (r_cnt_len == 8'd0);
act_resp.r.last = r_last_d;
// Decrement the counter.
r_cnt_dec = 1'b1;
// Try to forward the beat upstream.
act_resp.r_valid = 1'b1;
if (act_req.r_ready) begin
// Acknowledge downstream.
mst_req_o.r_ready = 1'b1;
end else begin
// Wait for upstream to become ready.
r_state_d = RWait;
end
end
end
end
RWait: begin
act_resp.r.last = r_last_q;
act_resp.r_valid = mst_resp_i.r_valid;
if (mst_resp_i.r_valid && act_req.r_ready) begin
mst_req_o.r_ready = 1'b1;
r_state_d = RFeedthrough;
end
end
default: /*do nothing*/;
endcase
end
// --------------------------------------------------
// Flip-Flops
// --------------------------------------------------
`FFARN(b_err_q, b_err_d, 1'b0, clk_i, rst_ni)
`FFARN(b_state_q, b_state_d, BReady, clk_i, rst_ni)
`FFARN(r_last_q, r_last_d, 1'b0, clk_i, rst_ni)
`FFARN(r_state_q, r_state_d, RFeedthrough, clk_i, rst_ni)
// --------------------------------------------------
// Assumptions and assertions
// --------------------------------------------------
`ifndef VERILATOR
// pragma translate_off
default disable iff (!rst_ni);
// Inputs
assume property (@(posedge clk_i) slv_req_i.aw_valid |->
txn_supported(slv_req_i.aw.atop, slv_req_i.aw.burst, slv_req_i.aw.cache, slv_req_i.aw.len)
) else $warning("Unsupported AW transaction received, returning slave error!");
assume property (@(posedge clk_i) slv_req_i.ar_valid |->
txn_supported('0, slv_req_i.ar.burst, slv_req_i.ar.cache, slv_req_i.ar.len)
) else $warning("Unsupported AR transaction received, returning slave error!");
assume property (@(posedge clk_i) slv_req_i.aw_valid |->
slv_req_i.aw.atop == '0 || slv_req_i.aw.atop[5:4] == axi_pkg::ATOP_ATOMICSTORE
) else $fatal(1, "Unsupported ATOP that gives rise to a R response received,\
cannot respond in protocol-compliant manner!");
// Outputs
assert property (@(posedge clk_i) mst_req_o.aw_valid |-> mst_req_o.aw.len == '0)
else $fatal(1, "AW burst longer than a single beat emitted!");
assert property (@(posedge clk_i) mst_req_o.ar_valid |-> mst_req_o.ar.len == '0)
else $fatal(1, "AR burst longer than a single beat emitted!");
// pragma translate_on
`endif
endmodule
/// Internal module of [`axi_burst_splitter`](module.axi_burst_splitter) to control Ax channels.
///
/// Store burst lengths in counters, which are associated to AXI IDs through ID queues (to allow
/// reordering of responses w.r.t. requests).
module axi_burst_splitter_ax_chan #(
parameter type chan_t = logic,
parameter int unsigned IdWidth = 0,
parameter int unsigned MaxTxns = 0,
parameter type id_t = logic[IdWidth-1:0]
) (
input logic clk_i,
input logic rst_ni,
input chan_t ax_i,
input logic ax_valid_i,
output logic ax_ready_o,
output chan_t ax_o,
output logic ax_valid_o,
input logic ax_ready_i,
input id_t cnt_id_i,
output axi_pkg::len_t cnt_len_o,
input logic cnt_set_err_i,
output logic cnt_err_o,
input logic cnt_dec_i,
input logic cnt_req_i,
output logic cnt_gnt_o
);
typedef logic[IdWidth-1:0] cnt_id_t;
logic cnt_alloc_req, cnt_alloc_gnt;
axi_burst_splitter_counters #(
.MaxTxns ( MaxTxns ),
.IdWidth ( IdWidth )
) i_axi_burst_splitter_counters (
.clk_i,
.rst_ni,
.alloc_id_i ( ax_i.id ),
.alloc_len_i ( ax_i.len ),
.alloc_req_i ( cnt_alloc_req ),
.alloc_gnt_o ( cnt_alloc_gnt ),
.cnt_id_i ( cnt_id_i ),
.cnt_len_o ( cnt_len_o ),
.cnt_set_err_i ( cnt_set_err_i ),
.cnt_err_o ( cnt_err_o ),
.cnt_dec_i ( cnt_dec_i ),
.cnt_req_i ( cnt_req_i ),
.cnt_gnt_o ( cnt_gnt_o )
);
chan_t ax_d, ax_q;
enum logic {Idle, Busy} state_d, state_q;
always_comb begin
cnt_alloc_req = 1'b0;
ax_d = ax_q;
state_d = state_q;
ax_o = '0;
ax_valid_o = 1'b0;
ax_ready_o = 1'b0;
unique case (state_q)
Idle: begin
if (ax_valid_i && cnt_alloc_gnt) begin
if (ax_i.len == '0) begin // No splitting required -> feed through.
ax_o = ax_i;
ax_valid_o = 1'b1;
// As soon as downstream is ready, allocate a counter and acknowledge upstream.
if (ax_ready_i) begin
cnt_alloc_req = 1'b1;
ax_ready_o = 1'b1;
end
end else begin // Splitting required.
// Store Ax, allocate a counter, and acknowledge upstream.
ax_d = ax_i;
cnt_alloc_req = 1'b1;
ax_ready_o = 1'b1;
// Try to feed first burst through.
ax_o = ax_d;
ax_o.len = '0;
ax_valid_o = 1'b1;
if (ax_ready_i) begin
// Reduce number of bursts still to be sent by one and increment address.
ax_d.len--;
if (ax_d.burst == axi_pkg::BURST_INCR) begin
ax_d.addr += (1 << ax_d.size);
end
end
state_d = Busy;
end
end
end
Busy: begin
// Sent next burst from split.
ax_o = ax_q;
ax_o.len = '0;
ax_valid_o = 1'b1;
if (ax_ready_i) begin
if (ax_q.len == '0) begin
// If this was the last burst, go back to idle.
state_d = Idle;
end else begin
// Otherwise, continue with the next burst.
ax_d.len--;
if (ax_q.burst == axi_pkg::BURST_INCR) begin
ax_d.addr += (1 << ax_q.size);
end
end
end
end
default: /*do nothing*/;
endcase
end
// registers
`FFARN(ax_q, ax_d, '0, clk_i, rst_ni)
`FFARN(state_q, state_d, Idle, clk_i, rst_ni)
endmodule
/// Internal module of [`axi_burst_splitter`](module.axi_burst_splitter) to order transactions.
module axi_burst_splitter_counters #(
parameter int unsigned MaxTxns = 0,
parameter int unsigned IdWidth = 0,
parameter type id_t = logic [IdWidth-1:0]
) (
input logic clk_i,
input logic rst_ni,
input id_t alloc_id_i,
input axi_pkg::len_t alloc_len_i,
input logic alloc_req_i,
output logic alloc_gnt_o,
input id_t cnt_id_i,
output axi_pkg::len_t cnt_len_o,
input logic cnt_set_err_i,
output logic cnt_err_o,
input logic cnt_dec_i,
input logic cnt_req_i,
output logic cnt_gnt_o
);
localparam int unsigned CntIdxWidth = (MaxTxns > 1) ? $clog2(MaxTxns) : 32'd1;
typedef logic [CntIdxWidth-1:0] cnt_idx_t;
typedef logic [$bits(axi_pkg::len_t):0] cnt_t;
logic [MaxTxns-1:0] cnt_dec, cnt_free, cnt_set, err_d, err_q;
cnt_t cnt_inp;
cnt_t [MaxTxns-1:0] cnt_oup;
cnt_idx_t cnt_free_idx, cnt_r_idx;
for (genvar i = 0; i < MaxTxns; i++) begin : gen_cnt
counter #(
.WIDTH ( $bits(cnt_t) )
) i_cnt (
.clk_i,
.rst_ni,
.clear_i ( 1'b0 ),
.en_i ( cnt_dec[i] ),
.load_i ( cnt_set[i] ),
.down_i ( 1'b1 ),
.d_i ( cnt_inp ),
.q_o ( cnt_oup[i] ),
.overflow_o ( ) // not used
);
assign cnt_free[i] = (cnt_oup[i] == '0);
end
assign cnt_inp = {1'b0, alloc_len_i} + 1;
lzc #(
.WIDTH ( MaxTxns ),
.MODE ( 1'b0 ) // start counting at index 0
) i_lzc (
.in_i ( cnt_free ),
.cnt_o ( cnt_free_idx ),
.empty_o ( )
);
logic idq_inp_req, idq_inp_gnt,
idq_oup_gnt, idq_oup_valid, idq_oup_pop;
id_queue #(
.ID_WIDTH ( $bits(id_t) ),
.CAPACITY ( MaxTxns ),
.data_t ( cnt_idx_t )
) i_idq (
.clk_i,
.rst_ni,
.inp_id_i ( alloc_id_i ),
.inp_data_i ( cnt_free_idx ),
.inp_req_i ( idq_inp_req ),
.inp_gnt_o ( idq_inp_gnt ),
.exists_data_i ( '0 ),
.exists_mask_i ( '0 ),
.exists_req_i ( 1'b0 ),
.exists_o (/* keep open */),
.exists_gnt_o (/* keep open */),
.oup_id_i ( cnt_id_i ),
.oup_pop_i ( idq_oup_pop ),
.oup_req_i ( cnt_req_i ),
.oup_data_o ( cnt_r_idx ),
.oup_data_valid_o ( idq_oup_valid ),
.oup_gnt_o ( idq_oup_gnt )
);
assign idq_inp_req = alloc_req_i & alloc_gnt_o;
assign alloc_gnt_o = idq_inp_gnt & |(cnt_free);
assign cnt_gnt_o = idq_oup_gnt & idq_oup_valid;
logic [8:0] read_len;
assign read_len = cnt_oup[cnt_r_idx] - 1;
assign cnt_len_o = read_len[7:0];
assign idq_oup_pop = cnt_req_i & cnt_gnt_o & cnt_dec_i & (cnt_len_o == 8'd0);
always_comb begin
cnt_dec = '0;
cnt_dec[cnt_r_idx] = cnt_req_i & cnt_gnt_o & cnt_dec_i;
end
always_comb begin
cnt_set = '0;
cnt_set[cnt_free_idx] = alloc_req_i & alloc_gnt_o;
end
always_comb begin
err_d = err_q;
cnt_err_o = err_q[cnt_r_idx];
if (cnt_req_i && cnt_gnt_o && cnt_set_err_i) begin
err_d[cnt_r_idx] = 1'b1;
cnt_err_o = 1'b1;
end
if (alloc_req_i && alloc_gnt_o) begin
err_d[cnt_free_idx] = 1'b0;
end
end
// registers
`FFARN(err_q, err_d, '0, clk_i, rst_ni)
`ifndef VERILATOR
// pragma translate_off
assume property (@(posedge clk_i) idq_oup_gnt |-> idq_oup_valid)
else $warning("Invalid output at ID queue, read not granted!");
// pragma translate_on
`endif
endmodule

246
vendor/pulp-platform/axi/src/axi_cdc.sv vendored Normal file
View file

@ -0,0 +1,246 @@
// 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:
// - Andreas Kurth <akurth@iis.ee.ethz.ch>
// - Fabian Schuiki <fschuiki@iis.ee.ethz.ch>
// - Florian Zaruba <zarubaf@iis.ee.ethz.ch>
// - Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>
// - Luca Valente <luca.valente2@unibo.it>
`include "axi/assign.svh"
/// A clock domain crossing on an AXI interface.
///
/// For each of the five AXI channels, this module instantiates a CDC FIFO, whose push and pop
/// ports are in separate clock domains. IMPORTANT: For each AXI channel, you MUST properly
/// constrain three paths through the FIFO; see the header of `cdc_fifo_gray` for instructions.
module axi_cdc #(
parameter type aw_chan_t = logic, // AW Channel Type
parameter type w_chan_t = logic, // W Channel Type
parameter type b_chan_t = logic, // B Channel Type
parameter type ar_chan_t = logic, // AR Channel Type
parameter type r_chan_t = logic, // R Channel Type
parameter type axi_req_t = logic, // encapsulates request channels
parameter type axi_resp_t = logic, // encapsulates request channels
/// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH.
parameter int unsigned LogDepth = 1
) (
// slave side - clocked by `src_clk_i`
input logic src_clk_i,
input logic src_rst_ni,
input axi_req_t src_req_i,
output axi_resp_t src_resp_o,
// master side - clocked by `dst_clk_i`
input logic dst_clk_i,
input logic dst_rst_ni,
output axi_req_t dst_req_o,
input axi_resp_t dst_resp_i
);
aw_chan_t [2**LogDepth-1:0] async_data_aw_data;
w_chan_t [2**LogDepth-1:0] async_data_w_data;
b_chan_t [2**LogDepth-1:0] async_data_b_data;
ar_chan_t [2**LogDepth-1:0] async_data_ar_data;
r_chan_t [2**LogDepth-1:0] async_data_r_data;
logic [LogDepth:0] async_data_aw_wptr, async_data_aw_rptr,
async_data_w_wptr, async_data_w_rptr,
async_data_b_wptr, async_data_b_rptr,
async_data_ar_wptr, async_data_ar_rptr,
async_data_r_wptr, async_data_r_rptr;
axi_cdc_src #(
.aw_chan_t ( aw_chan_t ),
.w_chan_t ( w_chan_t ),
.b_chan_t ( b_chan_t ),
.ar_chan_t ( ar_chan_t ),
.r_chan_t ( r_chan_t ),
.axi_req_t ( axi_req_t ),
.axi_resp_t ( axi_resp_t ),
.LogDepth ( LogDepth )
) i_axi_cdc_src (
.src_clk_i,
.src_rst_ni,
.src_req_i,
.src_resp_o,
(* async *) .async_data_master_aw_data_o ( async_data_aw_data ),
(* async *) .async_data_master_aw_wptr_o ( async_data_aw_wptr ),
(* async *) .async_data_master_aw_rptr_i ( async_data_aw_rptr ),
(* async *) .async_data_master_w_data_o ( async_data_w_data ),
(* async *) .async_data_master_w_wptr_o ( async_data_w_wptr ),
(* async *) .async_data_master_w_rptr_i ( async_data_w_rptr ),
(* async *) .async_data_master_b_data_i ( async_data_b_data ),
(* async *) .async_data_master_b_wptr_i ( async_data_b_wptr ),
(* async *) .async_data_master_b_rptr_o ( async_data_b_rptr ),
(* async *) .async_data_master_ar_data_o ( async_data_ar_data ),
(* async *) .async_data_master_ar_wptr_o ( async_data_ar_wptr ),
(* async *) .async_data_master_ar_rptr_i ( async_data_ar_rptr ),
(* async *) .async_data_master_r_data_i ( async_data_r_data ),
(* async *) .async_data_master_r_wptr_i ( async_data_r_wptr ),
(* async *) .async_data_master_r_rptr_o ( async_data_r_rptr )
);
axi_cdc_dst #(
.aw_chan_t ( aw_chan_t ),
.w_chan_t ( w_chan_t ),
.b_chan_t ( b_chan_t ),
.ar_chan_t ( ar_chan_t ),
.r_chan_t ( r_chan_t ),
.axi_req_t ( axi_req_t ),
.axi_resp_t ( axi_resp_t ),
.LogDepth ( LogDepth )
) i_axi_cdc_dst (
.dst_clk_i,
.dst_rst_ni,
.dst_req_o,
.dst_resp_i,
(* async *) .async_data_slave_aw_wptr_i ( async_data_aw_wptr ),
(* async *) .async_data_slave_aw_rptr_o ( async_data_aw_rptr ),
(* async *) .async_data_slave_aw_data_i ( async_data_aw_data ),
(* async *) .async_data_slave_w_wptr_i ( async_data_w_wptr ),
(* async *) .async_data_slave_w_rptr_o ( async_data_w_rptr ),
(* async *) .async_data_slave_w_data_i ( async_data_w_data ),
(* async *) .async_data_slave_b_wptr_o ( async_data_b_wptr ),
(* async *) .async_data_slave_b_rptr_i ( async_data_b_rptr ),
(* async *) .async_data_slave_b_data_o ( async_data_b_data ),
(* async *) .async_data_slave_ar_wptr_i ( async_data_ar_wptr ),
(* async *) .async_data_slave_ar_rptr_o ( async_data_ar_rptr ),
(* async *) .async_data_slave_ar_data_i ( async_data_ar_data ),
(* async *) .async_data_slave_r_wptr_o ( async_data_r_wptr ),
(* async *) .async_data_slave_r_rptr_i ( async_data_r_rptr ),
(* async *) .async_data_slave_r_data_o ( async_data_r_data )
);
endmodule
`include "axi/assign.svh"
`include "axi/typedef.svh"
// interface wrapper
module axi_cdc_intf #(
parameter int unsigned AXI_ID_WIDTH = 0,
parameter int unsigned AXI_ADDR_WIDTH = 0,
parameter int unsigned AXI_DATA_WIDTH = 0,
parameter int unsigned AXI_USER_WIDTH = 0,
/// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH.
parameter int unsigned LOG_DEPTH = 1
) (
// slave side - clocked by `src_clk_i`
input logic src_clk_i,
input logic src_rst_ni,
AXI_BUS.Slave src,
// master side - clocked by `dst_clk_i`
input logic dst_clk_i,
input logic dst_rst_ni,
AXI_BUS.Master dst
);
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(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 src_req, dst_req;
resp_t src_resp, dst_resp;
`AXI_ASSIGN_TO_REQ(src_req, src)
`AXI_ASSIGN_FROM_RESP(src, src_resp)
`AXI_ASSIGN_FROM_REQ(dst, dst_req)
`AXI_ASSIGN_TO_RESP(dst_resp, dst)
axi_cdc #(
.aw_chan_t ( aw_chan_t ),
.w_chan_t ( w_chan_t ),
.b_chan_t ( b_chan_t ),
.ar_chan_t ( ar_chan_t ),
.r_chan_t ( r_chan_t ),
.axi_req_t ( req_t ),
.axi_resp_t ( resp_t ),
.LogDepth ( LOG_DEPTH )
) i_axi_cdc (
.src_clk_i,
.src_rst_ni,
.src_req_i ( src_req ),
.src_resp_o ( src_resp ),
.dst_clk_i,
.dst_rst_ni,
.dst_req_o ( dst_req ),
.dst_resp_i ( dst_resp )
);
endmodule
module axi_lite_cdc_intf #(
parameter int unsigned AXI_ADDR_WIDTH = 0,
parameter int unsigned AXI_DATA_WIDTH = 0,
/// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH.
parameter int unsigned LOG_DEPTH = 1
) (
// slave side - clocked by `src_clk_i`
input logic src_clk_i,
input logic src_rst_ni,
AXI_LITE.Slave src,
// master side - clocked by `dst_clk_i`
input logic dst_clk_i,
input logic dst_rst_ni,
AXI_LITE.Master dst
);
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;
`AXI_LITE_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t)
`AXI_LITE_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t)
`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)
req_t src_req, dst_req;
resp_t src_resp, dst_resp;
`AXI_LITE_ASSIGN_TO_REQ(src_req, src)
`AXI_LITE_ASSIGN_FROM_RESP(src, src_resp)
`AXI_LITE_ASSIGN_FROM_REQ(dst, dst_req)
`AXI_LITE_ASSIGN_TO_RESP(dst_resp, dst)
axi_cdc #(
.aw_chan_t ( aw_chan_t ),
.w_chan_t ( w_chan_t ),
.b_chan_t ( b_chan_t ),
.ar_chan_t ( ar_chan_t ),
.r_chan_t ( r_chan_t ),
.axi_req_t ( req_t ),
.axi_resp_t ( resp_t ),
.LogDepth ( LOG_DEPTH )
) i_axi_cdc (
.src_clk_i,
.src_rst_ni,
.src_req_i ( src_req ),
.src_resp_o ( src_resp ),
.dst_clk_i,
.dst_rst_ni,
.dst_req_o ( dst_req ),
.dst_resp_i ( dst_resp )
);
endmodule

View file

@ -0,0 +1,264 @@
// 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:
// - Andreas Kurth <akurth@iis.ee.ethz.ch>
// - Fabian Schuiki <fschuiki@iis.ee.ethz.ch>
// - Florian Zaruba <zarubaf@iis.ee.ethz.ch>
// - Luca Valente <luca.valente2@unibo.it>
`include "axi/assign.svh"
`include "axi/typedef.svh"
/// Destination-clock-domain half of the AXI CDC crossing.
///
/// For each of the five AXI channels, this module instantiates the source or destination half of
/// a CDC FIFO. IMPORTANT: For each AXI channel, you MUST properly constrain three paths through
/// the FIFO; see the header of `cdc_fifo_gray` for instructions.
module axi_cdc_dst #(
/// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH.
parameter int unsigned LogDepth = 1,
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
) (
// asynchronous slave port
input aw_chan_t [2**LogDepth-1:0] async_data_slave_aw_data_i,
input logic [LogDepth:0] async_data_slave_aw_wptr_i,
output logic [LogDepth:0] async_data_slave_aw_rptr_o,
input w_chan_t [2**LogDepth-1:0] async_data_slave_w_data_i,
input logic [LogDepth:0] async_data_slave_w_wptr_i,
output logic [LogDepth:0] async_data_slave_w_rptr_o,
output b_chan_t [2**LogDepth-1:0] async_data_slave_b_data_o,
output logic [LogDepth:0] async_data_slave_b_wptr_o,
input logic [LogDepth:0] async_data_slave_b_rptr_i,
input ar_chan_t [2**LogDepth-1:0] async_data_slave_ar_data_i,
input logic [LogDepth:0] async_data_slave_ar_wptr_i,
output logic [LogDepth:0] async_data_slave_ar_rptr_o,
output r_chan_t [2**LogDepth-1:0] async_data_slave_r_data_o,
output logic [LogDepth:0] async_data_slave_r_wptr_o,
input logic [LogDepth:0] async_data_slave_r_rptr_i,
// synchronous master port - clocked by `dst_clk_i`
input logic dst_clk_i,
input logic dst_rst_ni,
output axi_req_t dst_req_o,
input axi_resp_t dst_resp_i
);
cdc_fifo_gray_dst #(
.T ( logic [$bits(aw_chan_t)-1:0] ),
.LOG_DEPTH ( LogDepth )
) i_cdc_fifo_gray_dst_aw (
.async_data_i ( async_data_slave_aw_data_i ),
.async_wptr_i ( async_data_slave_aw_wptr_i ),
.async_rptr_o ( async_data_slave_aw_rptr_o ),
.dst_clk_i,
.dst_rst_ni,
.dst_data_o ( dst_req_o.aw ),
.dst_valid_o ( dst_req_o.aw_valid ),
.dst_ready_i ( dst_resp_i.aw_ready )
);
cdc_fifo_gray_dst #(
.T ( logic [$bits(w_chan_t)-1:0] ),
.LOG_DEPTH ( LogDepth )
) i_cdc_fifo_gray_dst_w (
.async_data_i ( async_data_slave_w_data_i ),
.async_wptr_i ( async_data_slave_w_wptr_i ),
.async_rptr_o ( async_data_slave_w_rptr_o ),
.dst_clk_i,
.dst_rst_ni,
.dst_data_o ( dst_req_o.w ),
.dst_valid_o ( dst_req_o.w_valid ),
.dst_ready_i ( dst_resp_i.w_ready )
);
cdc_fifo_gray_src #(
.T ( logic [$bits(b_chan_t)-1:0] ),
.LOG_DEPTH ( LogDepth )
) i_cdc_fifo_gray_src_b (
.src_clk_i ( dst_clk_i ),
.src_rst_ni ( dst_rst_ni ),
.src_data_i ( dst_resp_i.b ),
.src_valid_i ( dst_resp_i.b_valid ),
.src_ready_o ( dst_req_o.b_ready ),
.async_data_o ( async_data_slave_b_data_o ),
.async_wptr_o ( async_data_slave_b_wptr_o ),
.async_rptr_i ( async_data_slave_b_rptr_i )
);
cdc_fifo_gray_dst #(
.T ( logic [$bits(ar_chan_t)-1:0] ),
.LOG_DEPTH ( LogDepth )
) i_cdc_fifo_gray_dst_ar (
.dst_clk_i,
.dst_rst_ni,
.dst_data_o ( dst_req_o.ar ),
.dst_valid_o ( dst_req_o.ar_valid ),
.dst_ready_i ( dst_resp_i.ar_ready ),
.async_data_i ( async_data_slave_ar_data_i ),
.async_wptr_i ( async_data_slave_ar_wptr_i ),
.async_rptr_o ( async_data_slave_ar_rptr_o )
);
cdc_fifo_gray_src #(
.T ( logic [$bits(r_chan_t)-1:0] ),
.LOG_DEPTH ( LogDepth )
) i_cdc_fifo_gray_src_r (
.src_clk_i ( dst_clk_i ),
.src_rst_ni ( dst_rst_ni ),
.src_data_i ( dst_resp_i.r ),
.src_valid_i ( dst_resp_i.r_valid ),
.src_ready_o ( dst_req_o.r_ready ),
.async_data_o ( async_data_slave_r_data_o ),
.async_wptr_o ( async_data_slave_r_wptr_o ),
.async_rptr_i ( async_data_slave_r_rptr_i )
);
endmodule
module axi_cdc_dst_intf #(
parameter int unsigned AXI_ID_WIDTH = 0,
parameter int unsigned AXI_ADDR_WIDTH = 0,
parameter int unsigned AXI_DATA_WIDTH = 0,
parameter int unsigned AXI_USER_WIDTH = 0,
/// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH.
parameter int unsigned LOG_DEPTH = 1
) (
// asynchronous slave port
AXI_BUS_ASYNC_GRAY.Slave src,
// synchronous master port - clocked by `dst_clk_i`
input logic dst_clk_i,
input logic dst_rst_ni,
AXI_BUS.Master dst
);
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(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 dst_req;
resp_t dst_resp;
axi_cdc_dst #(
.aw_chan_t ( aw_chan_t ),
.w_chan_t ( w_chan_t ),
.b_chan_t ( b_chan_t ),
.ar_chan_t ( ar_chan_t ),
.r_chan_t ( r_chan_t ),
.axi_req_t ( req_t ),
.axi_resp_t ( resp_t ),
.LogDepth ( LOG_DEPTH )
) i_axi_cdc_dst (
.async_data_slave_aw_data_i ( src.aw_data ),
.async_data_slave_aw_wptr_i ( src.aw_wptr ),
.async_data_slave_aw_rptr_o ( src.aw_rptr ),
.async_data_slave_w_data_i ( src.w_data ),
.async_data_slave_w_wptr_i ( src.w_wptr ),
.async_data_slave_w_rptr_o ( src.w_rptr ),
.async_data_slave_b_data_o ( src.b_data ),
.async_data_slave_b_wptr_o ( src.b_wptr ),
.async_data_slave_b_rptr_i ( src.b_rptr ),
.async_data_slave_ar_data_i ( src.ar_data ),
.async_data_slave_ar_wptr_i ( src.ar_wptr ),
.async_data_slave_ar_rptr_o ( src.ar_rptr ),
.async_data_slave_r_data_o ( src.r_data ),
.async_data_slave_r_wptr_o ( src.r_wptr ),
.async_data_slave_r_rptr_i ( src.r_rptr ),
.dst_clk_i,
.dst_rst_ni,
.dst_req_o ( dst_req ),
.dst_resp_i ( dst_resp )
);
`AXI_ASSIGN_FROM_REQ(dst, dst_req)
`AXI_ASSIGN_TO_RESP(dst_resp, dst)
endmodule
module axi_lite_cdc_dst_intf #(
parameter int unsigned AXI_ADDR_WIDTH = 0,
parameter int unsigned AXI_DATA_WIDTH = 0,
/// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH.
parameter int unsigned LOG_DEPTH = 1
) (
// asynchronous slave port
AXI_LITE_ASYNC_GRAY.Slave src,
// synchronous master port - clocked by `dst_clk_i`
input logic dst_clk_i,
input logic dst_rst_ni,
AXI_LITE.Master dst
);
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;
`AXI_LITE_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t)
`AXI_LITE_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t)
`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)
req_t dst_req;
resp_t dst_resp;
axi_cdc_dst #(
.aw_chan_t ( aw_chan_t ),
.w_chan_t ( w_chan_t ),
.b_chan_t ( b_chan_t ),
.ar_chan_t ( ar_chan_t ),
.r_chan_t ( r_chan_t ),
.axi_req_t ( req_t ),
.axi_resp_t ( resp_t ),
.LogDepth ( LOG_DEPTH )
) i_axi_cdc_dst (
.async_data_slave_aw_data_i ( src.aw_data ),
.async_data_slave_aw_wptr_i ( src.aw_wptr ),
.async_data_slave_aw_rptr_o ( src.aw_rptr ),
.async_data_slave_w_data_i ( src.w_data ),
.async_data_slave_w_wptr_i ( src.w_wptr ),
.async_data_slave_w_rptr_o ( src.w_rptr ),
.async_data_slave_b_data_o ( src.b_data ),
.async_data_slave_b_wptr_o ( src.b_wptr ),
.async_data_slave_b_rptr_i ( src.b_rptr ),
.async_data_slave_ar_data_i ( src.ar_data ),
.async_data_slave_ar_wptr_i ( src.ar_wptr ),
.async_data_slave_ar_rptr_o ( src.ar_rptr ),
.async_data_slave_r_data_o ( src.r_data ),
.async_data_slave_r_wptr_o ( src.r_wptr ),
.async_data_slave_r_rptr_i ( src.r_rptr ),
.dst_clk_i,
.dst_rst_ni,
.dst_req_o ( dst_req ),
.dst_resp_i ( dst_resp )
);
`AXI_LITE_ASSIGN_FROM_REQ(dst, dst_req)
`AXI_LITE_ASSIGN_TO_RESP(dst_resp, dst)
endmodule

View file

@ -0,0 +1,264 @@
// 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:
// - Andreas Kurth <akurth@iis.ee.ethz.ch>
// - Fabian Schuiki <fschuiki@iis.ee.ethz.ch>
// - Florian Zaruba <zarubaf@iis.ee.ethz.ch>
// - Luca Valente <luca.valente2@unibo.it>
`include "axi/assign.svh"
`include "axi/typedef.svh"
/// Source-clock-domain half of the AXI CDC crossing.
///
/// For each of the five AXI channels, this module instantiates the source or destination half of
/// a CDC FIFO. IMPORTANT: For each AXI channel, you MUST properly constrain three paths through
/// the FIFO; see the header of `cdc_fifo_gray` for instructions.
module axi_cdc_src #(
/// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH.
parameter int unsigned LogDepth = 1,
parameter 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
) (
// synchronous slave port - clocked by `src_clk_i`
input logic src_clk_i,
input logic src_rst_ni,
input axi_req_t src_req_i,
output axi_resp_t src_resp_o,
// asynchronous master port
output aw_chan_t [2**LogDepth-1:0] async_data_master_aw_data_o,
output logic [LogDepth:0] async_data_master_aw_wptr_o,
input logic [LogDepth:0] async_data_master_aw_rptr_i,
output w_chan_t [2**LogDepth-1:0] async_data_master_w_data_o,
output logic [LogDepth:0] async_data_master_w_wptr_o,
input logic [LogDepth:0] async_data_master_w_rptr_i,
input b_chan_t [2**LogDepth-1:0] async_data_master_b_data_i,
input logic [LogDepth:0] async_data_master_b_wptr_i,
output logic [LogDepth:0] async_data_master_b_rptr_o,
output ar_chan_t [2**LogDepth-1:0] async_data_master_ar_data_o,
output logic [LogDepth:0] async_data_master_ar_wptr_o,
input logic [LogDepth:0] async_data_master_ar_rptr_i,
input r_chan_t [2**LogDepth-1:0] async_data_master_r_data_i,
input logic [LogDepth:0] async_data_master_r_wptr_i,
output logic [LogDepth:0] async_data_master_r_rptr_o
);
cdc_fifo_gray_src #(
.T ( logic [$bits(aw_chan_t)-1:0] ),
.LOG_DEPTH ( LogDepth )
) i_cdc_fifo_gray_src_aw (
.src_clk_i,
.src_rst_ni,
.src_data_i ( src_req_i.aw ),
.src_valid_i ( src_req_i.aw_valid ),
.src_ready_o ( src_resp_o.aw_ready ),
.async_data_o ( async_data_master_aw_data_o ),
.async_wptr_o ( async_data_master_aw_wptr_o ),
.async_rptr_i ( async_data_master_aw_rptr_i )
);
cdc_fifo_gray_src #(
.T ( logic [$bits(w_chan_t)-1:0] ),
.LOG_DEPTH ( LogDepth )
) i_cdc_fifo_gray_src_w (
.src_clk_i,
.src_rst_ni,
.src_data_i ( src_req_i.w ),
.src_valid_i ( src_req_i.w_valid ),
.src_ready_o ( src_resp_o.w_ready ),
.async_data_o ( async_data_master_w_data_o ),
.async_wptr_o ( async_data_master_w_wptr_o ),
.async_rptr_i ( async_data_master_w_rptr_i )
);
cdc_fifo_gray_dst #(
.T ( logic [$bits(b_chan_t)-1:0] ),
.LOG_DEPTH ( LogDepth )
) i_cdc_fifo_gray_dst_b (
.dst_clk_i ( src_clk_i ),
.dst_rst_ni ( src_rst_ni ),
.dst_data_o ( src_resp_o.b ),
.dst_valid_o ( src_resp_o.b_valid ),
.dst_ready_i ( src_req_i.b_ready ),
.async_data_i ( async_data_master_b_data_i ),
.async_wptr_i ( async_data_master_b_wptr_i ),
.async_rptr_o ( async_data_master_b_rptr_o )
);
cdc_fifo_gray_src #(
.T ( logic [$bits(ar_chan_t)-1:0] ),
.LOG_DEPTH ( LogDepth )
) i_cdc_fifo_gray_src_ar (
.src_clk_i,
.src_rst_ni,
.src_data_i ( src_req_i.ar ),
.src_valid_i ( src_req_i.ar_valid ),
.src_ready_o ( src_resp_o.ar_ready ),
.async_data_o ( async_data_master_ar_data_o ),
.async_wptr_o ( async_data_master_ar_wptr_o ),
.async_rptr_i ( async_data_master_ar_rptr_i )
);
cdc_fifo_gray_dst #(
.T ( logic [$bits(r_chan_t)-1:0] ),
.LOG_DEPTH ( LogDepth )
) i_cdc_fifo_gray_dst_r (
.dst_clk_i ( src_clk_i ),
.dst_rst_ni ( src_rst_ni ),
.dst_data_o ( src_resp_o.r ),
.dst_valid_o ( src_resp_o.r_valid ),
.dst_ready_i ( src_req_i.r_ready ),
.async_data_i ( async_data_master_r_data_i ),
.async_wptr_i ( async_data_master_r_wptr_i ),
.async_rptr_o ( async_data_master_r_rptr_o )
);
endmodule
module axi_cdc_src_intf #(
parameter int unsigned AXI_ID_WIDTH = 0,
parameter int unsigned AXI_ADDR_WIDTH = 0,
parameter int unsigned AXI_DATA_WIDTH = 0,
parameter int unsigned AXI_USER_WIDTH = 0,
/// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH.
parameter int unsigned LOG_DEPTH = 1
) (
// synchronous slave port - clocked by `src_clk_i`
input logic src_clk_i,
input logic src_rst_ni,
AXI_BUS.Slave src,
// asynchronous master port
AXI_BUS_ASYNC_GRAY.Master dst
);
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(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 src_req;
resp_t src_resp;
`AXI_ASSIGN_TO_REQ(src_req, src)
`AXI_ASSIGN_FROM_RESP(src, src_resp)
axi_cdc_src #(
.aw_chan_t ( aw_chan_t ),
.w_chan_t ( w_chan_t ),
.b_chan_t ( b_chan_t ),
.ar_chan_t ( ar_chan_t ),
.r_chan_t ( r_chan_t ),
.axi_req_t ( req_t ),
.axi_resp_t ( resp_t ),
.LogDepth ( LOG_DEPTH )
) i_axi_cdc_src (
.src_clk_i,
.src_rst_ni,
.src_req_i ( src_req ),
.src_resp_o ( src_resp ),
.async_data_master_aw_data_o ( dst.aw_data ),
.async_data_master_aw_wptr_o ( dst.aw_wptr ),
.async_data_master_aw_rptr_i ( dst.aw_rptr ),
.async_data_master_w_data_o ( dst.w_data ),
.async_data_master_w_wptr_o ( dst.w_wptr ),
.async_data_master_w_rptr_i ( dst.w_rptr ),
.async_data_master_b_data_i ( dst.b_data ),
.async_data_master_b_wptr_i ( dst.b_wptr ),
.async_data_master_b_rptr_o ( dst.b_rptr ),
.async_data_master_ar_data_o ( dst.ar_data ),
.async_data_master_ar_wptr_o ( dst.ar_wptr ),
.async_data_master_ar_rptr_i ( dst.ar_rptr ),
.async_data_master_r_data_i ( dst.r_data ),
.async_data_master_r_wptr_i ( dst.r_wptr ),
.async_data_master_r_rptr_o ( dst.r_rptr )
);
endmodule
module axi_lite_cdc_src_intf #(
parameter int unsigned AXI_ADDR_WIDTH = 0,
parameter int unsigned AXI_DATA_WIDTH = 0,
/// Depth of the FIFO crossing the clock domain, given as 2**LOG_DEPTH.
parameter int unsigned LOG_DEPTH = 1
) (
// synchronous slave port - clocked by `src_clk_i`
input logic src_clk_i,
input logic src_rst_ni,
AXI_BUS.Slave src,
// asynchronous master port
AXI_LITE_ASYNC_GRAY.Master dst
);
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;
`AXI_LITE_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t)
`AXI_LITE_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t)
`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)
req_t src_req;
resp_t src_resp;
`AXI_LITE_ASSIGN_TO_REQ(src_req, src)
`AXI_LITE_ASSIGN_FROM_RESP(src, src_resp)
axi_cdc_src #(
.aw_chan_t ( aw_chan_t ),
.w_chan_t ( w_chan_t ),
.b_chan_t ( b_chan_t ),
.ar_chan_t ( ar_chan_t ),
.r_chan_t ( r_chan_t ),
.axi_req_t ( req_t ),
.axi_resp_t ( resp_t ),
.LogDepth ( LOG_DEPTH )
) i_axi_cdc_src (
.src_clk_i,
.src_rst_ni,
.src_req_i ( src_req ),
.src_resp_o ( src_resp ),
.async_data_master_aw_data_o ( dst.aw_data ),
.async_data_master_aw_wptr_o ( dst.aw_wptr ),
.async_data_master_aw_rptr_i ( dst.aw_rptr ),
.async_data_master_w_data_o ( dst.w_data ),
.async_data_master_w_wptr_o ( dst.w_wptr ),
.async_data_master_w_rptr_i ( dst.w_rptr ),
.async_data_master_b_data_i ( dst.b_data ),
.async_data_master_b_wptr_i ( dst.b_wptr ),
.async_data_master_b_rptr_o ( dst.b_rptr ),
.async_data_master_ar_data_o ( dst.ar_data ),
.async_data_master_ar_wptr_o ( dst.ar_wptr ),
.async_data_master_ar_rptr_i ( dst.ar_rptr ),
.async_data_master_r_data_i ( dst.r_data ),
.async_data_master_r_wptr_i ( dst.r_wptr ),
.async_data_master_r_rptr_o ( dst.r_rptr )
);
endmodule

265
vendor/pulp-platform/axi/src/axi_cut.sv vendored Normal file
View file

@ -0,0 +1,265 @@
// Copyright (c) 2014-2018 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:
// - Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>
// - Fabian Schuiki <fschuiki@iis.ee.ethz.ch>
// - Andreas Kurth <akurth@iis.ee.ethz.ch>
/// An AXI4 cut.
///
/// Breaks all combinatorial paths between its input and output.
module axi_cut #(
// bypass enable
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,
// AXI request & response structs
parameter type req_t = logic,
parameter type resp_t = logic
) (
input logic clk_i,
input logic rst_ni,
// salve port
input req_t slv_req_i,
output resp_t slv_resp_o,
// master port
output req_t mst_req_o,
input resp_t mst_resp_i
);
// a spill register for each channel
spill_register #(
.T ( aw_chan_t ),
.Bypass ( Bypass )
) i_reg_aw (
.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_req_o.aw_valid ),
.ready_i ( mst_resp_i.aw_ready ),
.data_o ( mst_req_o.aw )
);
spill_register #(
.T ( w_chan_t ),
.Bypass ( Bypass )
) i_reg_w (
.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_req_o.w_valid ),
.ready_i ( mst_resp_i.w_ready ),
.data_o ( mst_req_o.w )
);
spill_register #(
.T ( b_chan_t ),
.Bypass ( Bypass )
) i_reg_b (
.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_resp_o.b_valid ),
.ready_i ( slv_req_i.b_ready ),
.data_o ( slv_resp_o.b )
);
spill_register #(
.T ( ar_chan_t ),
.Bypass ( Bypass )
) i_reg_ar (
.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_req_o.ar_valid ),
.ready_i ( mst_resp_i.ar_ready ),
.data_o ( mst_req_o.ar )
);
spill_register #(
.T ( r_chan_t ),
.Bypass ( Bypass )
) i_reg_r (
.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_resp_o.r_valid ),
.ready_i ( slv_req_i.r_ready ),
.data_o ( slv_resp_o.r )
);
endmodule
`include "axi/assign.svh"
`include "axi/typedef.svh"
// interface wrapper
module axi_cut_intf #(
// Bypass eneable
parameter bit BYPASS = 1'b0,
// The address width.
parameter int unsigned ADDR_WIDTH = 0,
// The data width.
parameter int unsigned DATA_WIDTH = 0,
// The ID width.
parameter int unsigned ID_WIDTH = 0,
// The user data width.
parameter int unsigned USER_WIDTH = 0
) (
input logic clk_i ,
input logic rst_ni ,
AXI_BUS.Slave in ,
AXI_BUS.Master out
);
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(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_ASSIGN_TO_REQ(slv_req, in)
`AXI_ASSIGN_FROM_RESP(in, slv_resp)
`AXI_ASSIGN_FROM_REQ(out, mst_req)
`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 )
) i_axi_cut (
.clk_i,
.rst_ni,
.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 (in.AXI_ADDR_WIDTH == ADDR_WIDTH) else $fatal(1, "Wrong interface definition");
assert (in.AXI_DATA_WIDTH == DATA_WIDTH) else $fatal(1, "Wrong interface definition");
assert (in.AXI_ID_WIDTH == ID_WIDTH) else $fatal(1, "Wrong interface definition");
assert (in.AXI_USER_WIDTH == USER_WIDTH) else $fatal(1, "Wrong interface definition");
assert (out.AXI_ADDR_WIDTH == ADDR_WIDTH) else $fatal(1, "Wrong interface definition");
assert (out.AXI_DATA_WIDTH == DATA_WIDTH) else $fatal(1, "Wrong interface definition");
assert (out.AXI_ID_WIDTH == ID_WIDTH) else $fatal(1, "Wrong interface definition");
assert (out.AXI_USER_WIDTH == USER_WIDTH) else $fatal(1, "Wrong interface definition");
end
`endif
// pragma translate_on
endmodule
module axi_lite_cut_intf #(
// bypass enable
parameter bit BYPASS = 1'b0,
/// The address width.
parameter int unsigned ADDR_WIDTH = 0,
/// The data width.
parameter int unsigned DATA_WIDTH = 0
) (
input logic clk_i ,
input logic rst_ni ,
AXI_LITE.Slave in ,
AXI_LITE.Master out
);
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;
`AXI_LITE_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t)
`AXI_LITE_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t)
`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)
req_t slv_req, mst_req;
resp_t slv_resp, mst_resp;
`AXI_LITE_ASSIGN_TO_REQ(slv_req, in)
`AXI_LITE_ASSIGN_FROM_RESP(in, slv_resp)
`AXI_LITE_ASSIGN_FROM_REQ(out, mst_req)
`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 )
) i_axi_cut (
.clk_i,
.rst_ni,
.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 (in.AXI_ADDR_WIDTH == ADDR_WIDTH) else $fatal(1, "Wrong interface definition");
assert (in.AXI_DATA_WIDTH == DATA_WIDTH) else $fatal(1, "Wrong interface definition");
assert (out.AXI_ADDR_WIDTH == ADDR_WIDTH) else $fatal(1, "Wrong interface definition");
assert (out.AXI_DATA_WIDTH == DATA_WIDTH) else $fatal(1, "Wrong interface definition");
end
`endif
// pragma translate_on
endmodule

View file

@ -0,0 +1,198 @@
// 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 <wroennin@iis.ee.ethz.ch>
// - Florian Zaruba <zarubaf@iis.ee.ethz.ch>
// - Andreas Kurth <akurth@iis.ee.ethz.ch>
/// 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,
// AXI request & response types
parameter type req_t = logic,
parameter type 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
// slave port
input req_t slv_req_i,
output resp_t slv_resp_o,
// master port
output req_t mst_req_o,
input resp_t mst_resp_i
);
// AW
stream_delay #(
.StallRandom ( StallRandomInput ),
.FixedDelay ( FixedDelayInput ),
.payload_t ( aw_chan_t )
) i_stream_delay_aw (
.clk_i,
.rst_ni,
.payload_i ( slv_req_i.aw ),
.ready_o ( slv_resp_o.aw_ready ),
.valid_i ( slv_req_i.aw_valid ),
.payload_o ( mst_req_o.aw ),
.ready_i ( mst_resp_i.aw_ready ),
.valid_o ( mst_req_o.aw_valid )
);
// AR
stream_delay #(
.StallRandom ( StallRandomInput ),
.FixedDelay ( FixedDelayInput ),
.payload_t ( ar_chan_t )
) i_stream_delay_ar (
.clk_i,
.rst_ni,
.payload_i ( slv_req_i.ar ),
.ready_o ( slv_resp_o.ar_ready ),
.valid_i ( slv_req_i.ar_valid ),
.payload_o ( mst_req_o.ar ),
.ready_i ( mst_resp_i.ar_ready ),
.valid_o ( mst_req_o.ar_valid )
);
// W
stream_delay #(
.StallRandom ( StallRandomInput ),
.FixedDelay ( FixedDelayInput ),
.payload_t ( w_chan_t )
) i_stream_delay_w (
.clk_i,
.rst_ni,
.payload_i ( slv_req_i.w ),
.ready_o ( slv_resp_o.w_ready ),
.valid_i ( slv_req_i.w_valid ),
.payload_o ( mst_req_o.w ),
.ready_i ( mst_resp_i.w_ready ),
.valid_o ( mst_req_o.w_valid )
);
// B
stream_delay #(
.StallRandom ( StallRandomOutput ),
.FixedDelay ( FixedDelayOutput ),
.payload_t ( b_chan_t )
) i_stream_delay_b (
.clk_i,
.rst_ni,
.payload_i ( mst_resp_i.b ),
.ready_o ( mst_req_o.b_ready ),
.valid_i ( mst_resp_i.b_valid ),
.payload_o ( slv_resp_o.b ),
.ready_i ( slv_req_i.b_ready ),
.valid_o ( slv_resp_o.b_valid )
);
// R
stream_delay #(
.StallRandom ( StallRandomOutput ),
.FixedDelay ( FixedDelayOutput ),
.payload_t ( r_chan_t )
) i_stream_delay_r (
.clk_i,
.rst_ni,
.payload_i ( mst_resp_i.r ),
.ready_o ( mst_req_o.r_ready ),
.valid_i ( mst_resp_i.r_valid ),
.payload_o ( slv_resp_o.r ),
.ready_i ( slv_req_i.r_ready ),
.valid_o ( slv_resp_o.r_valid )
);
endmodule
`include "axi/typedef.svh"
`include "axi/assign.svh"
// interface wrapper
module axi_delayer_intf #(
// Synopsys DC requires a default value for parameters.
parameter int unsigned AXI_ID_WIDTH = 0,
parameter int unsigned AXI_ADDR_WIDTH = 0,
parameter int unsigned AXI_DATA_WIDTH = 0,
parameter int unsigned AXI_USER_WIDTH = 0,
parameter bit STALL_RANDOM_INPUT = 0,
parameter bit STALL_RANDOM_OUTPUT = 0,
parameter int unsigned FIXED_DELAY_INPUT = 1,
parameter int unsigned FIXED_DELAY_OUTPUT = 1
) (
input logic clk_i,
input logic rst_ni,
AXI_BUS.Slave slv,
AXI_BUS.Master mst
);
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(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_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_delayer #(
.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 ),
.StallRandomInput ( STALL_RANDOM_INPUT ),
.StallRandomOutput ( STALL_RANDOM_OUTPUT ),
.FixedDelayInput ( FIXED_DELAY_INPUT ),
.FixedDelayOutput ( FIXED_DELAY_OUTPUT )
) i_axi_delayer (
.clk_i, // Clock
.rst_ni, // Asynchronous reset active low
.slv_req_i ( slv_req ),
.slv_resp_o ( slv_resp ),
.mst_req_o ( mst_req ),
.mst_resp_i ( mst_resp )
);
// pragma translate_off
`ifndef VERILATOR
initial begin: p_assertions
assert (AXI_ID_WIDTH >= 1) else $fatal(1, "AXI ID width must be at least 1!");
assert (AXI_ADDR_WIDTH >= 1) else $fatal(1, "AXI ADDR width must be at least 1!");
assert (AXI_DATA_WIDTH >= 1) else $fatal(1, "AXI DATA 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

View file

@ -0,0 +1,786 @@
// Copyright (c) 2019 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
//
// Authors:
// - Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>
// - Andreas Kurth <akurth@iis.ee.ethz.ch>
`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.
module axi_demux #(
parameter int unsigned AxiIdWidth = 32'd0,
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 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,
parameter bit SpillAr = 1'b1,
parameter bit SpillR = 1'b0,
// Dependent parameters, DO NOT OVERRIDE!
parameter int unsigned SelectWidth = (NoMstPorts > 32'd1) ? $clog2(NoMstPorts) : 32'd1,
parameter type select_t = logic [SelectWidth-1:0]
) (
input logic clk_i,
input logic rst_ni,
input logic test_i,
// Slave Port
input 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,
// Master Ports
output req_t [NoMstPorts-1:0] mst_reqs_o,
input resp_t [NoMstPorts-1:0] mst_resps_i
);
localparam int unsigned IdCounterWidth = MaxTrans > 1 ? $clog2(MaxTrans) : 1;
//--------------------------------------
// 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;
// other non degenerate cases
end else begin : gen_demux
//--------------------------------------
//--------------------------------------
// Signal Declarations
//--------------------------------------
//--------------------------------------
//--------------------------------------
// Write Transaction
//--------------------------------------
// comes from spill register at input
aw_chan_select_t slv_aw_chan_select;
logic slv_aw_valid, slv_aw_ready;
// 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;
// Register which locks the AW valid signal
logic lock_aw_valid_d, lock_aw_valid_q, load_aw_lock;
logic aw_valid, aw_ready;
// W channel from spill reg
w_chan_t slv_w_chan;
logic slv_w_valid, slv_w_ready;
// B channles input into the arbitration
b_chan_t [NoMstPorts-1:0] mst_b_chans;
logic [NoMstPorts-1:0] mst_b_valids, mst_b_readies;
// B channel to spill register
b_chan_t slv_b_chan;
logic slv_b_valid, slv_b_ready;
//--------------------------------------
// Read Transaction
//--------------------------------------
// comes from spill register at input
ar_chan_select_t slv_ar_chan_select;
logic slv_ar_valid, slv_ar_ready;
// AR ID counter
select_t lookup_ar_select;
logic ar_select_occupied, ar_id_cnt_full;
logic ar_push;
// Register which locks the AR valid signel
logic lock_ar_valid_d, lock_ar_valid_q, load_ar_lock;
logic ar_valid, ar_ready;
// R channles input into the arbitration
r_chan_t [NoMstPorts-1:0] mst_r_chans;
logic [NoMstPorts-1:0] mst_r_valids, mst_r_readies;
// R channel to spill register
r_chan_t slv_r_chan;
logic slv_r_valid, slv_r_ready;
//--------------------------------------
//--------------------------------------
// Channel Control
//--------------------------------------
//--------------------------------------
//--------------------------------------
// AW Channel
//--------------------------------------
// spill register at the channel input
`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 )
);
assign slv_aw_chan_select = slv_aw_chan_select_out_flat;
// Control of the AW handshake
always_comb begin
// AXI Handshakes
slv_aw_ready = 1'b0;
aw_valid = 1'b0;
// `lock_aw_valid`, used to be protocol conform as it is not allowed to deassert
// a valid if there was no corresponding ready. As this process has to be able to inject
// an AXI ID into the counter of the AR channel on an ATOP, there could be a case where
// this process waits on `aw_ready` but in the mean time on the AR channel the counter gets
// full.
lock_aw_valid_d = lock_aw_valid_q;
load_aw_lock = 1'b0;
// AW ID counter and W FIFO
aw_push = 1'b0;
// ATOP injection into ar counter
atop_inject = 1'b0;
// we had an arbitration decision, the valid is locked, wait for the transaction
if (lock_aw_valid_q) begin
aw_valid = 1'b1;
// transaction
if (aw_ready) begin
slv_aw_ready = 1'b1;
lock_aw_valid_d = 1'b0;
load_aw_lock = 1'b1;
atop_inject = slv_aw_chan_select.aw_chan.atop[5]; // inject the ATOP if necessary
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
// 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;
// on AW transaction
if (aw_ready) begin
slv_aw_ready = 1'b1;
atop_inject = slv_aw_chan_select.aw_chan.atop[5];
// no AW transaction this cycle, lock the decision
end else begin
lock_aw_valid_d = 1'b1;
load_aw_lock = 1'b1;
end
end
end
end
end
// lock the valid signal, as the selection gets pushed into the W FIFO on first assertion,
// prevent further pushing
`FFLARN(lock_aw_valid_q, lock_aw_valid_d, load_aw_lock, '0, clk_i, rst_ni)
if (UniqueIds) begin : gen_unique_ids_aw
// If the `UniqueIds` parameter is set, each write transaction has an ID that is unique among
// all in-flight write transactions, or all write transactions with a given ID target the same
// master port as all write transactions with the same ID, or both. This means that the
// signals that are driven by the ID counters if this parameter is not set can instead be
// derived from existing signals. The ID counters can therefore be omitted.
assign lookup_aw_select = slv_aw_chan_select.aw_select;
assign aw_select_occupied = 1'b0;
assign aw_id_cnt_full = 1'b0;
end else begin : gen_aw_id_counter
axi_demux_id_counters #(
.AxiIdBits ( AxiLookBits ),
.CounterWidth ( IdCounterWidth ),
.mst_port_select_t ( select_t )
) i_aw_id_counter (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.lookup_axi_id_i ( slv_aw_chan_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 )
);
// 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
);
//--------------------------------------
// W Channel
//--------------------------------------
spill_register #(
.T ( w_chan_t ),
.Bypass ( ~SpillW )
) i_w_spill_reg(
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.valid_i ( slv_req_i.w_valid ),
.ready_o ( slv_resp_o.w_ready ),
.data_i ( slv_req_i.w ),
.valid_o ( slv_w_valid ),
.ready_i ( slv_w_ready ),
.data_o ( slv_w_chan )
);
//--------------------------------------
// B Channel
//--------------------------------------
// optional spill register
spill_register #(
.T ( b_chan_t ),
.Bypass ( ~SpillB )
) i_b_spill_reg (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.valid_i ( slv_b_valid ),
.ready_o ( slv_b_ready ),
.data_i ( slv_b_chan ),
.valid_o ( slv_resp_o.b_valid ),
.ready_i ( slv_req_i.b_ready ),
.data_o ( slv_resp_o.b )
);
// Arbitration of the different B responses
rr_arb_tree #(
.NumIn ( NoMstPorts ),
.DataType ( b_chan_t ),
.AxiVldRdy( 1'b1 ),
.LockIn ( 1'b1 )
) i_b_mux (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i( 1'b0 ),
.rr_i ( '0 ),
.req_i ( mst_b_valids ),
.gnt_o ( mst_b_readies ),
.data_i ( mst_b_chans ),
.gnt_i ( slv_b_ready ),
.req_o ( slv_b_valid ),
.data_o ( slv_b_chan ),
.idx_o ( )
);
//--------------------------------------
// AR Channel
//--------------------------------------
`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};
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 )
);
assign slv_ar_chan_select = slv_ar_chan_select_out_flat;
// control of the AR handshake
always_comb begin
// AXI Handshakes
slv_ar_ready = 1'b0;
ar_valid = 1'b0;
// `lock_ar_valid`: Used to be protocol conform as it is not allowed to deassert `ar_valid`
// if there was no corresponding `ar_ready`. There is the possibility that an injection
// of a R response from an `atop` from the AW channel can change the occupied flag of the
// `i_ar_id_counter`, even if it was previously empty. This FF prevents the deassertion.
lock_ar_valid_d = lock_ar_valid_q;
load_ar_lock = 1'b0;
// AR id counter
ar_push = 1'b0;
// The process had an arbitration decision in a previous cycle, the valid is locked,
// wait for the AR transaction.
if (lock_ar_valid_q) begin
ar_valid = 1'b1;
// transaction
if (ar_ready) begin
slv_ar_ready = 1'b1;
ar_push = 1'b1;
lock_ar_valid_d = 1'b0;
load_ar_lock = 1'b1;
end
end else begin
// The process can start handling AR transaction if `i_ar_id_counter` has space.
if (!ar_id_cnt_full) begin
// There is a valid AR, so look the ID up.
if (slv_ar_valid && (!ar_select_occupied ||
(slv_ar_chan_select.ar_select == lookup_ar_select))) begin
// connect the AR handshake
ar_valid = 1'b1;
// on transaction
if (ar_ready) begin
slv_ar_ready = 1'b1;
ar_push = 1'b1;
// no transaction this cycle, lock the valid decision!
end else begin
lock_ar_valid_d = 1'b1;
load_ar_lock = 1'b1;
end
end
end
end
end
// this ff is needed so that ar does not get de-asserted if an atop gets injected
`FFLARN(lock_ar_valid_q, lock_ar_valid_d, load_ar_lock, '0, clk_i, rst_ni)
if (UniqueIds) begin : gen_unique_ids_ar
// If the `UniqueIds` parameter is set, each read transaction has an ID that is unique among
// all in-flight read transactions, or all read transactions with a given ID target the same
// master port as all read transactions with the same ID, or both. This means that the
// signals that are driven by the ID counters if this parameter is not set can instead be
// derived from existing signals. The ID counters can therefore be omitted.
assign lookup_ar_select = slv_ar_chan_select.ar_select;
assign ar_select_occupied = 1'b0;
assign ar_id_cnt_full = 1'b0;
end else begin : gen_ar_id_counter
axi_demux_id_counters #(
.AxiIdBits ( AxiLookBits ),
.CounterWidth ( IdCounterWidth ),
.mst_port_select_t ( select_t )
) i_ar_id_counter (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.lookup_axi_id_i ( slv_ar_chan_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 )
);
end
//--------------------------------------
// R Channel
//--------------------------------------
// optional spill register
spill_register #(
.T ( r_chan_t ),
.Bypass ( ~SpillR )
) i_r_spill_reg (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.valid_i ( slv_r_valid ),
.ready_o ( slv_r_ready ),
.data_i ( slv_r_chan ),
.valid_o ( slv_resp_o.r_valid ),
.ready_i ( slv_req_i.r_ready ),
.data_o ( slv_resp_o.r )
);
// Arbitration of the different r responses
rr_arb_tree #(
.NumIn ( NoMstPorts ),
.DataType ( r_chan_t ),
.AxiVldRdy( 1'b1 ),
.LockIn ( 1'b1 )
) i_r_mux (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i( 1'b0 ),
.rr_i ( '0 ),
.req_i ( mst_r_valids ),
.gnt_o ( mst_r_readies ),
.data_i ( mst_r_chans ),
.gnt_i ( slv_r_ready ),
.req_o ( slv_r_valid ),
.data_o ( slv_r_chan ),
.idx_o ( )
);
assign ar_ready = ar_valid & mst_resps_i[slv_ar_chan_select.ar_select].ar_ready;
assign aw_ready = aw_valid & mst_resps_i[slv_aw_chan_select.aw_select].aw_ready;
// process that defines the individual demuxes and assignments for the arbitration
// as mst_reqs_o has to be drivem from the same always comb block!
always_comb begin
// default assignments
mst_reqs_o = '0;
slv_w_ready = 1'b0;
w_fifo_pop = 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_valid = 1'b0;
if (aw_valid && (slv_aw_chan_select.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
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;
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_valid = 1'b0;
if (ar_valid && (slv_ar_chan_select.ar_select == i)) begin
mst_reqs_o[i].ar_valid = 1'b1;
end
// R channel
mst_reqs_o[i].r_ready = mst_r_readies[i];
end
end
// unpack the response B and R channels for the arbitration
for (genvar i = 0; i < NoMstPorts; i++) begin : gen_b_channels
assign mst_b_chans[i] = mst_resps_i[i].b;
assign mst_b_valids[i] = mst_resps_i[i].b_valid;
assign mst_r_chans[i] = mst_resps_i[i].r;
assign mst_r_valids[i] = mst_resps_i[i].r_valid;
end
// Validate parameters.
// pragma translate_off
`ifndef VERILATOR
`ifndef XSIM
initial begin: validate_params
no_mst_ports: assume (NoMstPorts > 0) else
$fatal(1, "The Number of slaves (NoMstPorts) has to be at least 1");
AXI_ID_BITS: assume (AxiIdWidth >= AxiLookBits) else
$fatal(1, "AxiIdBits has to be equal or smaller than AxiIdWidth.");
end
default disable iff (!rst_ni);
aw_select: assume property( @(posedge clk_i) (slv_req_i.aw_valid |->
(slv_aw_select_i < NoMstPorts))) else
$fatal(1, "slv_aw_select_i is %d: AW has selected a slave that is not defined.\
NoMstPorts: %d", slv_aw_select_i, NoMstPorts);
ar_select: assume property( @(posedge clk_i) (slv_req_i.ar_valid |->
(slv_ar_select_i < NoMstPorts))) else
$fatal(1, "slv_ar_select_i is %d: AR has selected a slave that is not defined.\
NoMstPorts: %d", slv_ar_select_i, NoMstPorts);
aw_valid_stable: assert property( @(posedge clk_i) (aw_valid && !aw_ready) |=> aw_valid) else
$fatal(1, "aw_valid was deasserted, when aw_ready = 0 in last cycle.");
ar_valid_stable: assert property( @(posedge clk_i)
(ar_valid && !ar_ready) |=> ar_valid) else
$fatal(1, "ar_valid was deasserted, when ar_ready = 0 in last cycle.");
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.");
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.");
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.");
`endif
`endif
// pragma translate_on
end
endmodule
module axi_demux_id_counters #(
// the lower bits of the AXI ID that should be considered, results in 2**AXI_ID_BITS counters
parameter int unsigned AxiIdBits = 2,
parameter int unsigned CounterWidth = 4,
parameter type mst_port_select_t = logic
) (
input clk_i, // Clock
input rst_ni, // Asynchronous reset active low
// lookup
input logic [AxiIdBits-1:0] lookup_axi_id_i,
output mst_port_select_t lookup_mst_select_o,
output logic lookup_mst_select_occupied_o,
// push
output logic full_o,
input logic [AxiIdBits-1:0] push_axi_id_i,
input mst_port_select_t push_mst_select_i,
input logic push_i,
// inject ATOPs in AR channel
input logic [AxiIdBits-1:0] inject_axi_id_i,
input logic inject_i,
// pop
input logic [AxiIdBits-1:0] pop_axi_id_i,
input logic pop_i
);
localparam int unsigned NoCounters = 2**AxiIdBits;
typedef logic [CounterWidth-1:0] cnt_t;
// registers, each gets loaded when push_en[i]
mst_port_select_t [NoCounters-1:0] mst_select_q;
// counter signals
logic [NoCounters-1:0] push_en, inject_en, pop_en, occupied, cnt_full;
//-----------------------------------
// Lookup
//-----------------------------------
assign lookup_mst_select_o = mst_select_q[lookup_axi_id_i];
assign lookup_mst_select_occupied_o = occupied[lookup_axi_id_i];
//-----------------------------------
// Push and Pop
//-----------------------------------
assign push_en = (push_i) ? (1 << push_axi_id_i) : '0;
assign inject_en = (inject_i) ? (1 << inject_axi_id_i) : '0;
assign pop_en = (pop_i) ? (1 << pop_axi_id_i) : '0;
assign full_o = |cnt_full;
// counters
for (genvar i = 0; i < NoCounters; i++) begin : gen_counters
logic cnt_en, cnt_down, overflow;
cnt_t cnt_delta, in_flight;
always_comb begin
unique case ({push_en[i], inject_en[i], pop_en[i]})
3'b001 : begin // pop_i = -1
cnt_en = 1'b1;
cnt_down = 1'b1;
cnt_delta = cnt_t'(1);
end
3'b010 : begin // inject_i = +1
cnt_en = 1'b1;
cnt_down = 1'b0;
cnt_delta = cnt_t'(1);
end
// 3'b011, inject_i & pop_i = 0 --> use default
3'b100 : begin // push_i = +1
cnt_en = 1'b1;
cnt_down = 1'b0;
cnt_delta = cnt_t'(1);
end
// 3'b101, push_i & pop_i = 0 --> use default
3'b110 : begin // push_i & inject_i = +2
cnt_en = 1'b1;
cnt_down = 1'b0;
cnt_delta = cnt_t'(2);
end
3'b111 : begin // push_i & inject_i & pop_i = +1
cnt_en = 1'b1;
cnt_down = 1'b0;
cnt_delta = cnt_t'(1);
end
default : begin // do nothing to the counters
cnt_en = 1'b0;
cnt_down = 1'b0;
cnt_delta = cnt_t'(0);
end
endcase
end
delta_counter #(
.WIDTH ( CounterWidth ),
.STICKY_OVERFLOW ( 1'b0 )
) i_in_flight_cnt (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.clear_i ( 1'b0 ),
.en_i ( cnt_en ),
.load_i ( 1'b0 ),
.down_i ( cnt_down ),
.delta_i ( cnt_delta ),
.d_i ( '0 ),
.q_o ( in_flight ),
.overflow_o ( overflow )
);
assign occupied[i] = |in_flight;
assign cnt_full[i] = overflow | (&in_flight);
// holds the selection signal for this id
`FFLARN(mst_select_q[i], push_mst_select_i, push_en[i], '0, clk_i, rst_ni)
// pragma translate_off
`ifndef VERILATOR
`ifndef XSIM
// Validate parameters.
cnt_underflow: assert property(
@(posedge clk_i) disable iff (~rst_ni) (pop_en[i] |=> !overflow)) else
$fatal(1, "axi_demux_id_counters > Counter: %0d underflowed.\
The reason is probably a faulty AXI response.", i);
`endif
`endif
// pragma translate_on
end
endmodule
// interface wrapper
`include "axi/assign.svh"
`include "axi/typedef.svh"
module axi_demux_intf #(
parameter int unsigned AXI_ID_WIDTH = 32'd0, // Synopsys DC requires default value for params
parameter int unsigned AXI_ADDR_WIDTH = 32'd0,
parameter int unsigned AXI_DATA_WIDTH = 32'd0,
parameter int unsigned AXI_USER_WIDTH = 32'd0,
parameter int unsigned NO_MST_PORTS = 32'd3,
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,
parameter bit SPILL_AR = 1'b1,
parameter bit SPILL_R = 1'b0,
// Dependent parameters, DO NOT OVERRIDE!
parameter int unsigned SELECT_WIDTH = (NO_MST_PORTS > 32'd1) ? $clog2(NO_MST_PORTS) : 32'd1,
parameter type select_t = logic [SELECT_WIDTH-1:0] // MST port select type
) (
input logic clk_i, // Clock
input logic rst_ni, // Asynchronous reset active low
input logic test_i, // Testmode enable
input select_t slv_aw_select_i, // has to be stable, when aw_valid
input select_t slv_ar_select_i, // has to be stable, when ar_valid
AXI_BUS.Slave slv, // slave port
AXI_BUS.Master mst [NO_MST_PORTS-1:0] // master ports
);
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(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;
resp_t slv_resp;
req_t [NO_MST_PORTS-1:0] mst_req;
resp_t [NO_MST_PORTS-1:0] mst_resp;
`AXI_ASSIGN_TO_REQ(slv_req, slv)
`AXI_ASSIGN_FROM_RESP(slv, slv_resp)
for (genvar i = 0; i < NO_MST_PORTS; i++) begin : gen_assign_mst_ports
`AXI_ASSIGN_FROM_REQ(mst[i], mst_req[i])
`AXI_ASSIGN_TO_RESP(mst_resp[i], mst[i])
end
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 ),
.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 ),
.SpillAr ( SPILL_AR ),
.SpillR ( SPILL_R )
) i_axi_demux (
.clk_i, // Clock
.rst_ni, // Asynchronous reset active low
.test_i, // Testmode enable
// slave port
.slv_req_i ( slv_req ),
.slv_aw_select_i ( slv_aw_select_i ),
.slv_ar_select_i ( slv_ar_select_i ),
.slv_resp_o ( slv_resp ),
// master port
.mst_reqs_o ( mst_req ),
.mst_resps_i ( mst_resp )
);
endmodule

View file

@ -0,0 +1,190 @@
// 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:
// - Matheus Cavalcante <matheusd@iis.ee.ethz.ch>
// NOTE: The upsizer does not support WRAP bursts, and will answer with SLVERR
// upon receiving a burst of such type. In addition to that, the downsizer also
// does not support FIXED bursts with incoming axlen != 0.
module axi_dw_converter #(
parameter int unsigned AxiMaxReads = 1 , // Number of outstanding reads
parameter int unsigned AxiSlvPortDataWidth = 8 , // Data width of the slv port
parameter int unsigned AxiMstPortDataWidth = 8 , // Data width of the mst port
parameter int unsigned AxiAddrWidth = 1 , // Address width
parameter int unsigned AxiIdWidth = 1 , // ID width
parameter type aw_chan_t = logic, // AW Channel Type
parameter type mst_w_chan_t = logic, // W Channel Type for the mst port
parameter type slv_w_chan_t = logic, // W Channel Type for the slv port
parameter type b_chan_t = logic, // B Channel Type
parameter type ar_chan_t = logic, // AR Channel Type
parameter type mst_r_chan_t = logic, // R Channel Type for the mst port
parameter type slv_r_chan_t = logic, // R Channel Type for the slv port
parameter type axi_mst_req_t = logic, // AXI Request Type for mst ports
parameter type axi_mst_resp_t = logic, // AXI Response Type for mst ports
parameter type axi_slv_req_t = logic, // AXI Request Type for slv ports
parameter type axi_slv_resp_t = logic // AXI Response Type for slv ports
) (
input logic clk_i,
input logic rst_ni,
// Slave interface
input axi_slv_req_t slv_req_i,
output axi_slv_resp_t slv_resp_o,
// Master interface
output axi_mst_req_t mst_req_o,
input axi_mst_resp_t mst_resp_i
);
if (AxiMstPortDataWidth == AxiSlvPortDataWidth) begin: gen_no_dw_conversion
assign mst_req_o = slv_req_i ;
assign slv_resp_o = mst_resp_i;
end : gen_no_dw_conversion
if (AxiMstPortDataWidth > AxiSlvPortDataWidth) begin: gen_dw_upsize
axi_dw_upsizer #(
.AxiMaxReads (AxiMaxReads ),
.AxiSlvPortDataWidth(AxiSlvPortDataWidth),
.AxiMstPortDataWidth(AxiMstPortDataWidth),
.AxiAddrWidth (AxiAddrWidth ),
.AxiIdWidth (AxiIdWidth ),
.aw_chan_t (aw_chan_t ),
.mst_w_chan_t (mst_w_chan_t ),
.slv_w_chan_t (slv_w_chan_t ),
.b_chan_t (b_chan_t ),
.ar_chan_t (ar_chan_t ),
.mst_r_chan_t (mst_r_chan_t ),
.slv_r_chan_t (slv_r_chan_t ),
.axi_mst_req_t (axi_mst_req_t ),
.axi_mst_resp_t (axi_mst_resp_t ),
.axi_slv_req_t (axi_slv_req_t ),
.axi_slv_resp_t (axi_slv_resp_t )
) i_axi_dw_upsizer (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
// Slave interface
.slv_req_i (slv_req_i ),
.slv_resp_o(slv_resp_o),
// Master interface
.mst_req_o (mst_req_o ),
.mst_resp_i(mst_resp_i)
);
end : gen_dw_upsize
if (AxiMstPortDataWidth < AxiSlvPortDataWidth) begin: gen_dw_downsize
axi_dw_downsizer #(
.AxiMaxReads (AxiMaxReads ),
.AxiSlvPortDataWidth(AxiSlvPortDataWidth),
.AxiMstPortDataWidth(AxiMstPortDataWidth),
.AxiAddrWidth (AxiAddrWidth ),
.AxiIdWidth (AxiIdWidth ),
.aw_chan_t (aw_chan_t ),
.mst_w_chan_t (mst_w_chan_t ),
.slv_w_chan_t (slv_w_chan_t ),
.b_chan_t (b_chan_t ),
.ar_chan_t (ar_chan_t ),
.mst_r_chan_t (mst_r_chan_t ),
.slv_r_chan_t (slv_r_chan_t ),
.axi_mst_req_t (axi_mst_req_t ),
.axi_mst_resp_t (axi_mst_resp_t ),
.axi_slv_req_t (axi_slv_req_t ),
.axi_slv_resp_t (axi_slv_resp_t )
) i_axi_dw_downsizer (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
// Slave interface
.slv_req_i (slv_req_i ),
.slv_resp_o(slv_resp_o),
// Master interface
.mst_req_o (mst_req_o ),
.mst_resp_i(mst_resp_i)
);
end : gen_dw_downsize
endmodule : axi_dw_converter
// Interface wrapper
`include "axi/assign.svh"
`include "axi/typedef.svh"
module axi_dw_converter_intf #(
parameter int unsigned AXI_ID_WIDTH = 1,
parameter int unsigned AXI_ADDR_WIDTH = 1,
parameter int unsigned AXI_SLV_PORT_DATA_WIDTH = 8,
parameter int unsigned AXI_MST_PORT_DATA_WIDTH = 8,
parameter int unsigned AXI_USER_WIDTH = 0,
parameter int unsigned AXI_MAX_READS = 8
) (
input logic clk_i,
input logic rst_ni,
AXI_BUS.Slave slv,
AXI_BUS.Master mst
);
typedef logic [AXI_ID_WIDTH-1:0] id_t ;
typedef logic [AXI_ADDR_WIDTH-1:0] addr_t ;
typedef logic [AXI_MST_PORT_DATA_WIDTH-1:0] mst_data_t ;
typedef logic [AXI_MST_PORT_DATA_WIDTH/8-1:0] mst_strb_t;
typedef logic [AXI_SLV_PORT_DATA_WIDTH-1:0] slv_data_t ;
typedef logic [AXI_SLV_PORT_DATA_WIDTH/8-1:0] slv_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(mst_w_chan_t, mst_data_t, mst_strb_t, user_t)
`AXI_TYPEDEF_W_CHAN_T(slv_w_chan_t, slv_data_t, slv_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(mst_r_chan_t, mst_data_t, id_t, user_t)
`AXI_TYPEDEF_R_CHAN_T(slv_r_chan_t, slv_data_t, id_t, user_t)
`AXI_TYPEDEF_REQ_T(mst_req_t, aw_chan_t, mst_w_chan_t, ar_chan_t)
`AXI_TYPEDEF_RESP_T(mst_resp_t, b_chan_t, mst_r_chan_t)
`AXI_TYPEDEF_REQ_T(slv_req_t, aw_chan_t, slv_w_chan_t, ar_chan_t)
`AXI_TYPEDEF_RESP_T(slv_resp_t, b_chan_t, slv_r_chan_t)
slv_req_t slv_req;
slv_resp_t slv_resp;
mst_req_t mst_req;
mst_resp_t 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_dw_converter #(
.AxiMaxReads ( AXI_MAX_READS ),
.AxiSlvPortDataWidth( AXI_SLV_PORT_DATA_WIDTH ),
.AxiMstPortDataWidth( AXI_MST_PORT_DATA_WIDTH ),
.AxiAddrWidth ( AXI_ADDR_WIDTH ),
.AxiIdWidth ( AXI_ID_WIDTH ),
.aw_chan_t ( aw_chan_t ),
.mst_w_chan_t ( mst_w_chan_t ),
.slv_w_chan_t ( slv_w_chan_t ),
.b_chan_t ( b_chan_t ),
.ar_chan_t ( ar_chan_t ),
.mst_r_chan_t ( mst_r_chan_t ),
.slv_r_chan_t ( slv_r_chan_t ),
.axi_mst_req_t ( mst_req_t ),
.axi_mst_resp_t ( mst_resp_t ),
.axi_slv_req_t ( slv_req_t ),
.axi_slv_resp_t ( slv_resp_t )
) i_axi_dw_converter (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
// slave port
.slv_req_i ( slv_req ),
.slv_resp_o ( slv_resp ),
// master port
.mst_req_o ( mst_req ),
.mst_resp_i ( mst_resp )
);
endmodule : axi_dw_converter_intf

View file

@ -0,0 +1,890 @@
// 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:
// - Matheus Cavalcante <matheusd@iis.ee.ethz.ch>
// Description:
// Data width downsize conversion.
// Connects a wide master to a narrower slave.
// NOTE: The downsizer does not support WRAP bursts, and will answer with SLVERR
// upon receiving a burst of such type. The downsizer does support FIXED
// bursts, but only if they consist of a single beat; it will answer with SLVERR
// on multi-beat FIXED bursts.
module axi_dw_downsizer #(
parameter int unsigned AxiMaxReads = 1 , // Number of outstanding reads
parameter int unsigned AxiSlvPortDataWidth = 8 , // Data width of the slv port
parameter int unsigned AxiMstPortDataWidth = 8 , // Data width of the mst port
parameter int unsigned AxiAddrWidth = 1 , // Address width
parameter int unsigned AxiIdWidth = 1 , // ID width
parameter type aw_chan_t = logic, // AW Channel Type
parameter type mst_w_chan_t = logic, // W Channel Type for mst port
parameter type slv_w_chan_t = logic, // W Channel Type for slv port
parameter type b_chan_t = logic, // B Channel Type
parameter type ar_chan_t = logic, // AR Channel Type
parameter type mst_r_chan_t = logic, // R Channel Type for mst port
parameter type slv_r_chan_t = logic, // R Channel Type for slv port
parameter type axi_mst_req_t = logic, // AXI Request Type for mst ports
parameter type axi_mst_resp_t = logic, // AXI Response Type for mst ports
parameter type axi_slv_req_t = logic, // AXI Request Type for slv ports
parameter type axi_slv_resp_t = logic // AXI Response Type for slv ports
) (
input logic clk_i,
input logic rst_ni,
// Slave interface
input axi_slv_req_t slv_req_i,
output axi_slv_resp_t slv_resp_o,
// Master interface
output axi_mst_req_t mst_req_o,
input axi_mst_resp_t mst_resp_i
);
/*****************
* DEFINITIONS *
*****************/
import axi_pkg::aligned_addr;
import axi_pkg::modifiable ;
import cf_math_pkg::idx_width;
// Type used to index which adapter is handling each outstanding transaction.
localparam TranIdWidth = AxiMaxReads > 1 ? $clog2(AxiMaxReads) : 1;
typedef logic [TranIdWidth-1:0] tran_id_t;
// Data width
localparam AxiSlvPortStrbWidth = AxiSlvPortDataWidth / 8;
localparam AxiMstPortStrbWidth = AxiMstPortDataWidth / 8;
localparam AxiSlvPortMaxSize = $clog2(AxiSlvPortStrbWidth);
localparam AxiMstPortMaxSize = $clog2(AxiMstPortStrbWidth);
localparam SlvPortByteMask = AxiSlvPortStrbWidth - 1;
localparam MstPortByteMask = AxiMstPortStrbWidth - 1;
// Byte-grouped data words
typedef logic [AxiMstPortStrbWidth-1:0][7:0] mst_data_t;
typedef logic [AxiSlvPortStrbWidth-1:0][7:0] slv_data_t;
// Address width
typedef logic [AxiAddrWidth-1:0] addr_t;
// ID width
typedef logic [AxiIdWidth-1:0] id_t;
// Length of burst after upsizing
typedef logic [$clog2(AxiSlvPortStrbWidth/AxiMstPortStrbWidth) + 7:0] burst_len_t;
// Internal AXI bus
axi_mst_req_t mst_req;
axi_mst_resp_t mst_resp;
/**************
* ARBITERS *
**************/
// R
slv_r_chan_t [AxiMaxReads-1:0] slv_r_tran;
logic [AxiMaxReads-1:0] slv_r_valid_tran;
logic [AxiMaxReads-1:0] slv_r_ready_tran;
rr_arb_tree #(
.NumIn (AxiMaxReads ),
.DataType (slv_r_chan_t),
.AxiVldRdy(1'b1 ),
.ExtPrio (1'b0 ),
.LockIn (1'b1 )
) i_slv_r_arb (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
.flush_i(1'b0 ),
.rr_i ('0 ),
.req_i (slv_r_valid_tran ),
.gnt_o (slv_r_ready_tran ),
.data_i (slv_r_tran ),
.gnt_i (slv_req_i.r_ready ),
.req_o (slv_resp_o.r_valid),
.data_o (slv_resp_o.r ),
.idx_o (/* Unused */ )
);
logic [AxiMaxReads-1:0] mst_r_ready_tran;
assign mst_req.r_ready = |mst_r_ready_tran;
// AR
id_t arb_slv_ar_id;
logic arb_slv_ar_req;
logic arb_slv_ar_gnt;
logic [AxiMaxReads-1:0] arb_slv_ar_gnt_tran;
// Multiplex AR slave between AR and AW for the injection of atomic operations with an R response.
logic inject_aw_into_ar;
logic inject_aw_into_ar_req;
logic inject_aw_into_ar_gnt;
assign arb_slv_ar_gnt = |arb_slv_ar_gnt_tran;
rr_arb_tree #(
.NumIn (2 ),
.DataWidth (AxiIdWidth),
.ExtPrio (1'b0 ),
.AxiVldRdy (1'b1 ),
.LockIn (1'b0 )
) i_slv_ar_arb (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
.flush_i (1'b0 ),
.rr_i ('0 ),
.req_i ({inject_aw_into_ar_req, slv_req_i.ar_valid} ),
.gnt_o ({inject_aw_into_ar_gnt, slv_resp_o.ar_ready}),
.data_i ({slv_req_i.aw.id, slv_req_i.ar.id} ),
.req_o (arb_slv_ar_req ),
.gnt_i (arb_slv_ar_gnt ),
.data_o (arb_slv_ar_id ),
.idx_o (inject_aw_into_ar )
);
ar_chan_t [AxiMaxReads-1:0] mst_ar_tran;
id_t [AxiMaxReads-1:0] mst_ar_id;
logic [AxiMaxReads-1:0] mst_ar_valid_tran;
logic [AxiMaxReads-1:0] mst_ar_ready_tran;
tran_id_t mst_req_idx;
rr_arb_tree #(
.NumIn (AxiMaxReads),
.DataType (ar_chan_t ),
.AxiVldRdy(1'b1 ),
.ExtPrio (1'b0 ),
.LockIn (1'b1 )
) i_mst_ar_arb (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
.flush_i(1'b0 ),
.rr_i ('0 ),
.req_i (mst_ar_valid_tran),
.gnt_o (mst_ar_ready_tran),
.data_i (mst_ar_tran ),
.gnt_i (mst_resp.ar_ready),
.req_o (mst_req.ar_valid ),
.data_o (mst_req.ar ),
.idx_o (mst_req_idx )
);
/*****************
* ERROR SLAVE *
*****************/
axi_mst_req_t axi_err_req;
axi_mst_resp_t axi_err_resp;
axi_err_slv #(
.AxiIdWidth(AxiIdWidth ),
.Resp (axi_pkg::RESP_SLVERR),
.req_t (axi_mst_req_t ),
.resp_t (axi_mst_resp_t )
) i_axi_err_slv (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
.test_i (1'b0 ),
.slv_req_i (axi_err_req ),
.slv_resp_o(axi_err_resp)
);
/***********
* DEMUX *
***********/
// Requests can be sent either to the error slave,
// or to the DWC's master port.
logic [AxiMaxReads-1:0] mst_req_ar_err;
logic mst_req_aw_err;
axi_demux #(
.AxiIdWidth (AxiIdWidth ),
.AxiLookBits(AxiIdWidth ),
.aw_chan_t (aw_chan_t ),
.w_chan_t (mst_w_chan_t ),
.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),
.NoMstPorts (2 ),
.MaxTrans (AxiMaxReads ),
.SpillAw (1'b1 ) // Required to break dependency between AW and W channels
) i_axi_demux (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
.test_i (1'b0 ),
.mst_reqs_o ({axi_err_req, mst_req_o} ),
.mst_resps_i ({axi_err_resp, mst_resp_i} ),
.slv_ar_select_i(mst_req_ar_err[mst_req_idx]),
.slv_aw_select_i(mst_req_aw_err ),
.slv_req_i (mst_req ),
.slv_resp_o (mst_resp )
);
/**********
* READ *
**********/
typedef enum logic [2:0] {
R_IDLE ,
R_INJECT_AW ,
R_PASSTHROUGH ,
R_INCR_DOWNSIZE,
R_SPLIT_INCR_DOWNSIZE
} r_state_e;
typedef struct packed {
ar_chan_t ar ;
logic ar_valid ;
logic ar_throw_error ;
slv_r_chan_t r ;
logic r_valid ;
burst_len_t burst_len ;
axi_pkg::size_t orig_ar_size;
logic injected_aw ;
} r_req_t;
// Write-related type, but w_req_q is referenced from Read logic
typedef struct packed {
aw_chan_t aw ;
logic aw_valid ;
logic aw_throw_error ;
burst_len_t burst_len ;
axi_pkg::len_t orig_aw_len ;
axi_pkg::burst_t orig_aw_burst;
axi_pkg::resp_t burst_resp ;
axi_pkg::size_t orig_aw_size ;
} w_req_t;
w_req_t w_req_d, w_req_q;
// Decide which downsizer will handle the incoming AXI transaction
logic [AxiMaxReads-1:0] idle_read_downsizer;
tran_id_t idx_ar_downsizer;
// Find an idle downsizer to handle this transaction
tran_id_t idx_idle_downsizer;
lzc #(
.WIDTH(AxiMaxReads)
) i_idle_lzc (
.in_i (idle_read_downsizer),
.cnt_o (idx_idle_downsizer ),
.empty_o(/* Unused */ )
);
// Is there already another downsizer handling a transaction with the same id
logic [AxiMaxReads-1:0] id_clash_downsizer;
tran_id_t idx_id_clash_downsizer;
for (genvar t = 0; t < AxiMaxReads; t++) begin: gen_id_clash
assign id_clash_downsizer[t] = arb_slv_ar_id == mst_ar_id[t] && !idle_read_downsizer[t];
end
onehot_to_bin #(
.ONEHOT_WIDTH(AxiMaxReads)
) i_id_clash_onehot_to_bin (
.onehot(id_clash_downsizer ),
.bin (idx_id_clash_downsizer)
);
// Choose an idle downsizer, unless there is an id clash
assign idx_ar_downsizer = (|id_clash_downsizer) ? idx_id_clash_downsizer : idx_idle_downsizer;
// This ID queue is used to resolve which downsizer is handling
// each outstanding read transaction.
logic [AxiMaxReads-1:0] idqueue_push;
logic [AxiMaxReads-1:0] idqueue_pop;
tran_id_t idqueue_id;
logic idqueue_valid;
id_queue #(
.ID_WIDTH(AxiIdWidth ),
.CAPACITY(AxiMaxReads),
.data_t (tran_id_t )
) i_read_id_queue (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
.inp_id_i (arb_slv_ar_id ),
.inp_data_i (idx_ar_downsizer),
.inp_req_i (|idqueue_push ),
.inp_gnt_o (/* Unused */ ),
.oup_id_i (mst_resp.r.id ),
.oup_pop_i (|idqueue_pop ),
.oup_req_i (1'b1 ),
.oup_data_o (idqueue_id ),
.oup_data_valid_o(idqueue_valid ),
.oup_gnt_o (/* Unused */ ),
.exists_data_i ('0 ),
.exists_mask_i ('0 ),
.exists_req_i ('0 ),
.exists_o (/* Unused */ ),
.exists_gnt_o (/* Unused */ )
);
for (genvar t = 0; unsigned'(t) < AxiMaxReads; t++) begin: gen_read_downsizer
r_state_e r_state_d, r_state_q;
r_req_t r_req_d , r_req_q ;
// Are we idle?
assign idle_read_downsizer[t] = (r_state_q == R_IDLE) || (r_state_q == R_INJECT_AW);
// Byte-grouped data signal for the serialization step
slv_data_t r_data;
always_comb begin
// Maintain state
r_state_d = r_state_q;
r_req_d = r_req_q ;
// AR Channel
mst_ar_tran[t] = r_req_q.ar ;
mst_ar_id[t] = r_req_q.ar.id ;
mst_ar_valid_tran[t] = r_req_q.ar_valid;
// Throw an error
mst_req_ar_err[t] = r_req_q.ar_throw_error;
// R Channel
slv_r_tran[t] = r_req_q.r ;
slv_r_valid_tran[t] = r_req_q.r_valid;
idqueue_push[t] = '0;
idqueue_pop[t] = '0;
arb_slv_ar_gnt_tran[t] = 1'b0;
mst_r_ready_tran[t] = 1'b0;
// Got a grant on the AR channel
if (mst_ar_valid_tran[t] && mst_ar_ready_tran[t]) begin
r_req_d.ar_valid = 1'b0;
r_req_d.ar_throw_error = 1'b0;
end
// Initialize r_data
r_data = r_req_q.r.data;
case (r_state_q)
R_IDLE : begin
// Reset channels
r_req_d.ar = '0;
r_req_d.r = '0;
// New read request
if (arb_slv_ar_req && (idx_ar_downsizer == t)) begin
arb_slv_ar_gnt_tran[t] = 1'b1;
// Push to ID queue
idqueue_push[t] = 1'b1;
// Must inject an AW request into this upsizer
if (inject_aw_into_ar) begin
r_state_d = R_INJECT_AW;
end else begin
// Default state
r_state_d = R_PASSTHROUGH;
// Save beat
r_req_d.ar = slv_req_i.ar ;
r_req_d.ar_valid = 1'b1 ;
r_req_d.burst_len = slv_req_i.ar.len ;
r_req_d.orig_ar_size = slv_req_i.ar.size;
r_req_d.injected_aw = 1'b0 ;
case (r_req_d.ar.burst)
axi_pkg::BURST_INCR : begin
// Evaluate downsize ratio
automatic addr_t size_mask = (1 << r_req_d.ar.size) - 1 ;
automatic addr_t conv_ratio = ((1 << r_req_d.ar.size) + AxiMstPortStrbWidth - 1) / AxiMstPortStrbWidth;
// Evaluate output burst length
automatic addr_t align_adj = (r_req_d.ar.addr & size_mask & ~MstPortByteMask) / AxiMstPortStrbWidth;
r_req_d.burst_len = (r_req_d.ar.len + 1) * conv_ratio - align_adj - 1 ;
if (conv_ratio != 1) begin
r_req_d.ar.size = AxiMstPortMaxSize;
if (r_req_d.burst_len <= 255) begin
r_state_d = R_INCR_DOWNSIZE ;
r_req_d.ar.len = r_req_d.burst_len;
end else begin
r_state_d = R_SPLIT_INCR_DOWNSIZE;
r_req_d.ar.len = 255 - align_adj ;
end
end
end
axi_pkg::BURST_FIXED: begin
// Single transaction
if (r_req_d.ar.len == '0) begin
// Evaluate downsize ratio
automatic addr_t size_mask = (1 << r_req_d.ar.size) - 1 ;
automatic addr_t conv_ratio = ((1 << r_req_d.ar.size) + AxiMstPortStrbWidth - 1) / AxiMstPortStrbWidth;
// Evaluate output burst length
automatic addr_t align_adj = (r_req_d.ar.addr & size_mask & ~MstPortByteMask) / AxiMstPortStrbWidth;
r_req_d.burst_len = (conv_ratio >= align_adj + 1) ? (conv_ratio - align_adj - 1) : 0;
if (conv_ratio != 1) begin
r_state_d = R_INCR_DOWNSIZE ;
r_req_d.ar.len = r_req_d.burst_len ;
r_req_d.ar.size = AxiMstPortMaxSize ;
r_req_d.ar.burst = axi_pkg::BURST_INCR;
end
end else begin
// The downsizer does not support fixed burts
r_req_d.ar_throw_error = 1'b1;
end
end
axi_pkg::BURST_WRAP: begin
// The DW converter does not support this type of burst.
r_state_d = R_PASSTHROUGH;
r_req_d.ar_throw_error = 1'b1 ;
end
endcase
end
end
end
R_INJECT_AW : begin
// Save beat
r_req_d.ar.id = w_req_q.aw.id ;
r_req_d.ar.addr = w_req_q.aw.addr ;
r_req_d.ar.size = w_req_q.orig_aw_size ;
r_req_d.ar.burst = w_req_q.orig_aw_burst;
r_req_d.ar.len = w_req_q.orig_aw_len ;
r_req_d.ar.lock = w_req_q.aw.lock ;
r_req_d.ar.cache = w_req_q.aw.cache ;
r_req_d.ar.prot = w_req_q.aw.prot ;
r_req_d.ar.qos = w_req_q.aw.qos ;
r_req_d.ar.region = w_req_q.aw.region ;
r_req_d.ar.user = w_req_q.aw.user ;
r_req_d.ar_valid = 1'b0 ; // Injected "AR"s from AW are not valid.
r_req_d.burst_len = w_req_q.orig_aw_len ;
r_req_d.orig_ar_size = w_req_q.orig_aw_size ;
r_req_d.injected_aw = 1'b1 ;
// Default state
r_state_d = R_PASSTHROUGH;
case (r_req_d.ar.burst)
axi_pkg::BURST_INCR : begin
// Evaluate downsize ratio
automatic addr_t size_mask = (1 << r_req_d.ar.size) - 1 ;
automatic addr_t conv_ratio = ((1 << r_req_d.ar.size) + AxiMstPortStrbWidth - 1) / AxiMstPortStrbWidth;
// Evaluate output burst length
automatic addr_t align_adj = (r_req_d.ar.addr & size_mask & ~MstPortByteMask) / AxiMstPortStrbWidth;
r_req_d.burst_len = (r_req_d.ar.len + 1) * conv_ratio - align_adj - 1 ;
if (conv_ratio != 1) begin
r_req_d.ar.size = AxiMstPortMaxSize;
if (r_req_d.burst_len <= 255) begin
r_state_d = R_INCR_DOWNSIZE ;
r_req_d.ar.len = r_req_d.burst_len;
end else begin
r_state_d = R_SPLIT_INCR_DOWNSIZE;
r_req_d.ar.len = 255 - align_adj ;
end
end
end
axi_pkg::BURST_FIXED: begin
// Single transaction
if (r_req_d.ar.len == '0) begin
// Evaluate downsize ratio
automatic addr_t size_mask = (1 << r_req_d.ar.size) - 1 ;
automatic addr_t conv_ratio = ((1 << r_req_d.ar.size) + AxiMstPortStrbWidth - 1) / AxiMstPortStrbWidth;
// Evaluate output burst length
automatic addr_t align_adj = (r_req_d.ar.addr & size_mask & ~MstPortByteMask) / AxiMstPortStrbWidth;
r_req_d.burst_len = (conv_ratio >= align_adj + 1) ? (conv_ratio - align_adj - 1) : 0;
if (conv_ratio != 1) begin
r_state_d = R_INCR_DOWNSIZE ;
r_req_d.ar.len = r_req_d.burst_len ;
r_req_d.ar.size = AxiMstPortMaxSize ;
r_req_d.ar.burst = axi_pkg::BURST_INCR;
end
end else begin
// The downsizer does not support fixed burts
r_req_d.ar_throw_error = 1'b1;
end
end
axi_pkg::BURST_WRAP: begin
// The DW converter does not support this type of burst.
r_state_d = R_PASSTHROUGH;
r_req_d.ar_throw_error = 1'b1 ;
end
endcase
end
R_PASSTHROUGH, R_INCR_DOWNSIZE, R_SPLIT_INCR_DOWNSIZE: begin
// Got a grant on the R channel
if (slv_r_valid_tran[t] && slv_r_ready_tran[t]) begin
r_req_d.r = '0 ;
r_req_d.r_valid = 1'b0;
r_data = '0 ;
end
// Request was accepted
if (!r_req_q.ar_valid)
// Our turn
if ((idqueue_id == t) && idqueue_valid)
// Ready to accept more data
if (!slv_r_valid_tran[t] || (slv_r_valid_tran[t] && slv_r_ready_tran[t])) begin
mst_r_ready_tran[t] = 1'b1;
if (mst_resp.r_valid) begin
automatic addr_t mst_port_offset = AxiMstPortStrbWidth == 1 ? '0 : r_req_q.ar.addr[idx_width(AxiMstPortStrbWidth)-1:0];
automatic addr_t slv_port_offset = AxiSlvPortStrbWidth == 1 ? '0 : r_req_q.ar.addr[idx_width(AxiSlvPortStrbWidth)-1:0];
// Serialization
for (int b = 0; b < AxiSlvPortStrbWidth; b++)
if ((b >= slv_port_offset) &&
(b - slv_port_offset < (1 << r_req_q.orig_ar_size)) &&
(b + mst_port_offset - slv_port_offset < AxiMstPortStrbWidth)) begin
r_data[b] = mst_resp.r.data[8*(b + mst_port_offset - slv_port_offset) +: 8];
end
r_req_d.burst_len = r_req_q.burst_len - 1 ;
r_req_d.ar.len = r_req_q.ar.len - 1 ;
r_req_d.r.data = r_data ;
r_req_d.r.last = (r_req_q.burst_len == 0);
r_req_d.r.id = mst_resp.r.id ;
r_req_d.r.user = mst_resp.r.user ;
// Merge response of this beat with prior one according to precedence rules.
r_req_d.r.resp = axi_pkg::resp_precedence(r_req_q.r.resp, mst_resp.r.resp);
case (r_req_d.ar.burst)
axi_pkg::BURST_INCR: begin
r_req_d.ar.addr = aligned_addr(r_req_q.ar.addr, r_req_q.ar.size) + (1 << r_req_q.ar.size);
end
axi_pkg::BURST_FIXED: begin
r_req_d.ar.addr = r_req_q.ar.addr;
end
endcase
if (r_req_q.burst_len == 0)
idqueue_pop[t] = 1'b1;
case (r_state_q)
R_PASSTHROUGH :
// Forward data as soon as we can
r_req_d.r_valid = 1'b1;
R_INCR_DOWNSIZE, R_SPLIT_INCR_DOWNSIZE:
// Forward when the burst is finished, or after filling up a word
if (r_req_q.burst_len == 0 || (aligned_addr(r_req_d.ar.addr, r_req_q.orig_ar_size) != aligned_addr(r_req_q.ar.addr, r_req_q.orig_ar_size)))
r_req_d.r_valid = 1'b1;
endcase
// Trigger another burst request, if needed
if (r_state_q == R_SPLIT_INCR_DOWNSIZE)
// Finished current burst, but whole transaction hasn't finished
if (r_req_q.ar.len == '0 && r_req_q.burst_len != '0) begin
r_req_d.ar_valid = !r_req_q.injected_aw;
r_req_d.ar.len = (r_req_d.burst_len <= 255) ? r_req_d.burst_len : 255;
end
end
end
if (slv_r_valid_tran[t] && slv_r_ready_tran[t])
if (r_req_q.burst_len == '1)
r_state_d = R_IDLE;
end
endcase
end
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
r_state_q <= R_IDLE;
r_req_q <= '0 ;
end else begin
r_state_q <= r_state_d;
r_req_q <= r_req_d ;
end
end
end : gen_read_downsizer
/***********
* WRITE *
***********/
typedef enum logic [1:0] {
W_IDLE ,
W_PASSTHROUGH ,
W_INCR_DOWNSIZE,
W_SPLIT_INCR_DOWNSIZE
} w_state_e;
w_state_e w_state_d, w_state_q;
// This FIFO holds the number of bursts generated by each write transactions handled by this downsizer.
// This is used to forward only the correct B beats to the slave.
logic forward_b_beat_i;
logic forward_b_beat_o;
logic forward_b_beat_push;
logic forward_b_beat_pop;
logic forward_b_beat_full;
fifo_v3 #(
.DATA_WIDTH (1 ),
.DEPTH (AxiMaxReads),
.FALL_THROUGH(1'b1 )
) i_forward_b_beats_queue (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
.flush_i (1'b0 ),
.testmode_i(1'b0 ),
.data_i (forward_b_beat_i ),
.push_i (forward_b_beat_push ),
.full_o (forward_b_beat_full ),
.data_o (forward_b_beat_o ),
.pop_i (forward_b_beat_pop ),
.empty_o (/* Unused */ ),
.usage_o (/* Unused */ )
);
// Byte-grouped data signal for the lane steering step
mst_data_t w_data;
always_comb begin
inject_aw_into_ar_req = 1'b0;
// i_num_b_beats default state
forward_b_beat_i = '0 ;
forward_b_beat_push = 1'b0;
forward_b_beat_pop = 1'b0;
// Maintain state
w_state_d = w_state_q;
w_req_d = w_req_q ;
// AW Channel
mst_req.aw = w_req_q.aw ;
mst_req.aw_valid = w_req_q.aw_valid;
slv_resp_o.aw_ready = '0 ;
// Throw an error.
mst_req_aw_err = w_req_q.aw_throw_error;
// W Channel
mst_req.w = '0;
mst_req.w_valid = '0;
slv_resp_o.w_ready = '0;
// Initialize w_data
w_data = '0;
// B Channel (No latency)
if (mst_resp.b_valid) begin
// Merge response of this burst with prior one according to precedence rules.
w_req_d.burst_resp = axi_pkg::resp_precedence(w_req_q.burst_resp, mst_resp.b.resp);
end
slv_resp_o.b = mst_resp.b ;
slv_resp_o.b.resp = w_req_d.burst_resp;
// Each write transaction might trigger several B beats on the master (narrow) side.
// Only forward the last B beat of each transaction.
if (forward_b_beat_o) begin
slv_resp_o.b_valid = mst_resp.b_valid ;
mst_req.b_ready = slv_req_i.b_ready;
// Got an ack on the B channel. Pop transaction.
if (mst_req.b_ready && mst_resp.b_valid)
forward_b_beat_pop = 1'b1;
end else begin
// Otherwise, just acknowlegde the B beats
slv_resp_o.b_valid = 1'b0 ;
mst_req.b_ready = 1'b1 ;
forward_b_beat_pop = mst_resp.b_valid;
end
// Got a grant on the AW channel
if (mst_req.aw_valid & mst_resp.aw_ready) begin
w_req_d.aw_valid = 1'b0;
w_req_d.aw_throw_error = 1'b0;
end
case (w_state_q)
W_PASSTHROUGH, W_INCR_DOWNSIZE, W_SPLIT_INCR_DOWNSIZE: begin
// Request was accepted
if (!w_req_q.aw_valid)
if (slv_req_i.w_valid) begin
automatic addr_t mst_port_offset = AxiMstPortStrbWidth == 1 ? '0 : w_req_q.aw.addr[idx_width(AxiMstPortStrbWidth)-1:0];
automatic addr_t slv_port_offset = AxiSlvPortStrbWidth == 1 ? '0 : w_req_q.aw.addr[idx_width(AxiSlvPortStrbWidth)-1:0];
// Valid output
mst_req.w_valid = 1'b1 ;
mst_req.w.last = w_req_q.aw.len == 0;
mst_req.w.user = slv_req_i.w.user ;
// Lane steering
for (int b = 0; b < AxiSlvPortStrbWidth; b++)
if ((b >= slv_port_offset) &&
(b - slv_port_offset < (1 << w_req_q.orig_aw_size)) &&
(b + mst_port_offset - slv_port_offset < AxiMstPortStrbWidth)) begin
w_data[b + mst_port_offset - slv_port_offset] = slv_req_i.w.data[8*b +: 8];
mst_req.w.strb[b + mst_port_offset - slv_port_offset] = slv_req_i.w.strb[b] ;
end
mst_req.w.data = w_data;
end
// Acknowledgment
if (mst_resp.w_ready && mst_req.w_valid) begin
w_req_d.burst_len = w_req_q.burst_len - 1;
w_req_d.aw.len = w_req_q.aw.len - 1 ;
case (w_req_d.aw.burst)
axi_pkg::BURST_INCR: begin
w_req_d.aw.addr = aligned_addr(w_req_q.aw.addr, w_req_q.aw.size) + (1 << w_req_q.aw.size);
end
axi_pkg::BURST_FIXED: begin
w_req_d.aw.addr = w_req_q.aw.addr;
end
endcase
case (w_state_q)
W_PASSTHROUGH:
slv_resp_o.w_ready = 1'b1;
W_INCR_DOWNSIZE, W_SPLIT_INCR_DOWNSIZE:
if (w_req_q.burst_len == 0 || (aligned_addr(w_req_d.aw.addr, w_req_q.orig_aw_size) != aligned_addr(w_req_q.aw.addr, w_req_q.orig_aw_size)))
slv_resp_o.w_ready = 1'b1;
endcase
// Trigger another burst request, if needed
if (w_state_q == W_SPLIT_INCR_DOWNSIZE)
// Finished current burst, but whole transaction hasn't finished
if (w_req_q.aw.len == '0 && w_req_q.burst_len != '0 && !forward_b_beat_full) begin
w_req_d.aw_valid = 1'b1;
w_req_d.aw.len = (w_req_d.burst_len <= 255) ? w_req_d.burst_len : 255;
// We will receive an extraneous B beat. Ignore it.
forward_b_beat_i = 1'b0;
forward_b_beat_push = 1'b1;
end
if (w_req_q.burst_len == 0 && !forward_b_beat_full) begin
w_state_d = W_IDLE;
forward_b_beat_push = 1'b1;
forward_b_beat_i = 1'b1;
end
end
end
endcase
// Can start a new request as soon as w_state_d is W_IDLE
if (w_state_d == W_IDLE) begin
// Reset channels
w_req_d.aw = '0 ;
w_req_d.aw_valid = 1'b0 ;
w_req_d.aw_throw_error = 1'b0 ;
w_req_d.burst_resp = axi_pkg::RESP_OKAY;
if (!forward_b_beat_full) begin
if (slv_req_i.aw_valid && slv_req_i.aw.atop[5]) 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
slv_resp_o.aw_ready = 1'b1;
end
// New write request
if (slv_req_i.aw_valid && slv_resp_o.aw_ready) begin
// Default state
w_state_d = W_PASSTHROUGH;
// Save beat
w_req_d.aw = slv_req_i.aw ;
w_req_d.aw_valid = 1'b1 ;
w_req_d.burst_len = slv_req_i.aw.len ;
w_req_d.orig_aw_len = slv_req_i.aw.len ;
w_req_d.orig_aw_size = slv_req_i.aw.size ;
w_req_d.orig_aw_burst = slv_req_i.aw.burst;
case (slv_req_i.aw.burst)
axi_pkg::BURST_INCR: begin
// Evaluate downsize ratio
automatic addr_t size_mask = (1 << slv_req_i.aw.size) - 1 ;
automatic addr_t conv_ratio = ((1 << slv_req_i.aw.size) + AxiMstPortStrbWidth - 1) / AxiMstPortStrbWidth;
// Evaluate output burst length
automatic addr_t align_adj = (slv_req_i.aw.addr & size_mask & ~MstPortByteMask) / AxiMstPortStrbWidth;
w_req_d.burst_len = (slv_req_i.aw.len + 1) * conv_ratio - align_adj - 1 ;
if (conv_ratio != 1) begin
w_req_d.aw.size = AxiMstPortMaxSize;
if (w_req_d.burst_len <= 255) begin
w_state_d = W_INCR_DOWNSIZE ;
w_req_d.aw.len = w_req_d.burst_len;
end else begin
w_state_d = W_SPLIT_INCR_DOWNSIZE;
w_req_d.aw.len = 255 - align_adj ;
end
end
end
axi_pkg::BURST_FIXED: begin
// Single transaction
if (slv_req_i.aw.len == '0) begin
// Evaluate downsize ratio
automatic addr_t size_mask = (1 << slv_req_i.aw.size) - 1 ;
automatic addr_t conv_ratio = ((1 << slv_req_i.aw.size) + AxiMstPortStrbWidth - 1) / AxiMstPortStrbWidth;
// Evaluate output burst length
automatic addr_t align_adj = (slv_req_i.aw.addr & size_mask & ~MstPortByteMask) / AxiMstPortStrbWidth;
w_req_d.burst_len = (conv_ratio >= align_adj + 1) ? (conv_ratio - align_adj - 1) : 0;
if (conv_ratio != 1) begin
w_state_d = W_INCR_DOWNSIZE ;
w_req_d.aw.len = w_req_d.burst_len ;
w_req_d.aw.size = AxiMstPortMaxSize ;
w_req_d.aw.burst = axi_pkg::BURST_INCR;
end
end else begin
// The downsizer does not support fixed bursts
w_req_d.aw_throw_error = 1'b1;
end
end
axi_pkg::BURST_WRAP: begin
// The DW converter does not support this type of burst.
w_state_d = W_PASSTHROUGH;
w_req_d.aw_throw_error = 1'b1 ;
end
endcase
end
end
end
end
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
w_state_q <= W_IDLE;
w_req_q <= '0 ;
end else begin
w_state_q <= w_state_d;
w_req_q <= w_req_d ;
end
end
endmodule : axi_dw_downsizer

View file

@ -0,0 +1,725 @@
// 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:
// - Matheus Cavalcante <matheusd@iis.ee.ethz.ch>
// Description:
// Data width upsize conversion.
// Connects a narrow master to a wider slave.
// NOTE: The upsizer does not support WRAP bursts, and will answer with SLVERR
// upon receiving a burst of such type.
module axi_dw_upsizer #(
parameter int unsigned AxiMaxReads = 1 , // Number of outstanding reads
parameter int unsigned AxiSlvPortDataWidth = 8 , // Data width of the slv port
parameter int unsigned AxiMstPortDataWidth = 8 , // Data width of the mst port
parameter int unsigned AxiAddrWidth = 1 , // Address width
parameter int unsigned AxiIdWidth = 1 , // ID width
parameter type aw_chan_t = logic, // AW Channel Type
parameter type mst_w_chan_t = logic, // W Channel Type for mst port
parameter type slv_w_chan_t = logic, // W Channel Type for slv port
parameter type b_chan_t = logic, // B Channel Type
parameter type ar_chan_t = logic, // AR Channel Type
parameter type mst_r_chan_t = logic, // R Channel Type for mst port
parameter type slv_r_chan_t = logic, // R Channel Type for slv port
parameter type axi_mst_req_t = logic, // AXI Request Type for mst ports
parameter type axi_mst_resp_t = logic, // AXI Response Type for mst ports
parameter type axi_slv_req_t = logic, // AXI Request Type for slv ports
parameter type axi_slv_resp_t = logic // AXI Response Type for slv ports
) (
input logic clk_i,
input logic rst_ni,
// Slave interface
input axi_slv_req_t slv_req_i,
output axi_slv_resp_t slv_resp_o,
// Master interface
output axi_mst_req_t mst_req_o,
input axi_mst_resp_t mst_resp_i
);
/*****************
* DEFINITIONS *
*****************/
import axi_pkg::aligned_addr;
import axi_pkg::beat_addr ;
import axi_pkg::modifiable ;
import cf_math_pkg::idx_width;
// Type used to index which adapter is handling each outstanding transaction.
localparam TranIdWidth = AxiMaxReads > 1 ? $clog2(AxiMaxReads) : 1;
typedef logic [TranIdWidth-1:0] tran_id_t;
// Data width
localparam AxiSlvPortStrbWidth = AxiSlvPortDataWidth / 8;
localparam AxiMstPortStrbWidth = AxiMstPortDataWidth / 8;
localparam AxiSlvPortMaxSize = $clog2(AxiSlvPortStrbWidth);
localparam AxiMstPortMaxSize = $clog2(AxiMstPortStrbWidth);
// Byte-grouped data words
typedef logic [AxiMstPortStrbWidth-1:0][7:0] mst_data_t;
typedef logic [AxiSlvPortStrbWidth-1:0][7:0] slv_data_t;
// Address width
typedef logic [AxiAddrWidth-1:0] addr_t;
// ID width
typedef logic [AxiIdWidth-1:0] id_t;
// Length of burst after upsizing
typedef logic [$clog2(AxiMstPortStrbWidth/AxiSlvPortStrbWidth) + 7:0] burst_len_t;
// Internal AXI bus
axi_mst_req_t mst_req;
axi_mst_resp_t mst_resp;
/**************
* ARBITERS *
**************/
// R
slv_r_chan_t [AxiMaxReads-1:0] slv_r_tran;
logic [AxiMaxReads-1:0] slv_r_valid_tran;
logic [AxiMaxReads-1:0] slv_r_ready_tran;
rr_arb_tree #(
.NumIn (AxiMaxReads ),
.DataType (slv_r_chan_t),
.AxiVldRdy(1'b1 ),
.ExtPrio (1'b0 ),
.LockIn (1'b1 )
) i_slv_r_arb (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
.flush_i(1'b0 ),
.rr_i ('0 ),
.req_i (slv_r_valid_tran ),
.gnt_o (slv_r_ready_tran ),
.data_i (slv_r_tran ),
.gnt_i (slv_req_i.r_ready ),
.req_o (slv_resp_o.r_valid),
.data_o (slv_resp_o.r ),
.idx_o (/* Unused */ )
);
logic [AxiMaxReads-1:0] mst_r_ready_tran;
assign mst_req.r_ready = |mst_r_ready_tran;
// AR
id_t arb_slv_ar_id;
logic arb_slv_ar_req;
logic arb_slv_ar_gnt;
logic [AxiMaxReads-1:0] arb_slv_ar_gnt_tran;
// Multiplex AR slave between AR and AW for the injection of atomic operations with an R response.
logic inject_aw_into_ar;
logic inject_aw_into_ar_req;
logic inject_aw_into_ar_gnt;
assign arb_slv_ar_gnt = |arb_slv_ar_gnt_tran;
rr_arb_tree #(
.NumIn (2 ),
.DataWidth (AxiIdWidth),
.ExtPrio (1'b0 ),
.AxiVldRdy (1'b1 ),
.LockIn (1'b0 )
) i_slv_ar_arb (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
.flush_i(1'b0 ),
.rr_i ('0 ),
.req_i ({inject_aw_into_ar_req, slv_req_i.ar_valid} ),
.gnt_o ({inject_aw_into_ar_gnt, slv_resp_o.ar_ready}),
.data_i ({slv_req_i.aw.id, slv_req_i.ar.id} ),
.req_o (arb_slv_ar_req ),
.gnt_i (arb_slv_ar_gnt ),
.data_o (arb_slv_ar_id ),
.idx_o (inject_aw_into_ar )
);
ar_chan_t [AxiMaxReads-1:0] mst_ar_tran;
id_t [AxiMaxReads-1:0] mst_ar_id;
logic [AxiMaxReads-1:0] mst_ar_valid_tran;
logic [AxiMaxReads-1:0] mst_ar_ready_tran;
tran_id_t mst_req_idx;
rr_arb_tree #(
.NumIn (AxiMaxReads),
.DataType (ar_chan_t ),
.AxiVldRdy(1'b1 ),
.ExtPrio (1'b0 ),
.LockIn (1'b1 )
) i_mst_ar_arb (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
.flush_i(1'b0 ),
.rr_i ('0 ),
.req_i (mst_ar_valid_tran),
.gnt_o (mst_ar_ready_tran),
.data_i (mst_ar_tran ),
.gnt_i (mst_resp.ar_ready),
.req_o (mst_req.ar_valid ),
.data_o (mst_req.ar ),
.idx_o (mst_req_idx )
);
/*****************
* ERROR SLAVE *
*****************/
axi_mst_req_t axi_err_req;
axi_mst_resp_t axi_err_resp;
axi_err_slv #(
.AxiIdWidth(AxiIdWidth ),
.Resp (axi_pkg::RESP_SLVERR),
.req_t (axi_mst_req_t ),
.resp_t (axi_mst_resp_t )
) i_axi_err_slv (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
.test_i (1'b0 ),
.slv_req_i (axi_err_req ),
.slv_resp_o(axi_err_resp)
);
/***********
* DEMUX *
***********/
// Requests can be sent either to the error slave,
// or to the DWC's master port.
logic [AxiMaxReads-1:0] mst_req_ar_err;
logic mst_req_aw_err;
axi_demux #(
.AxiIdWidth (AxiIdWidth ),
.AxiLookBits(AxiIdWidth ),
.aw_chan_t (aw_chan_t ),
.w_chan_t (mst_w_chan_t ),
.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),
.NoMstPorts (2 ),
.MaxTrans (AxiMaxReads ),
.SpillAw (1'b1 ) // Required to break dependency between AW and W channels
) i_axi_demux (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
.test_i (1'b0 ),
.mst_reqs_o ({axi_err_req, mst_req_o} ),
.mst_resps_i ({axi_err_resp, mst_resp_i} ),
.slv_ar_select_i(mst_req_ar_err[mst_req_idx]),
.slv_aw_select_i(mst_req_aw_err ),
.slv_req_i (mst_req ),
.slv_resp_o (mst_resp )
);
/**********
* READ *
**********/
typedef enum logic [1:0] {
R_IDLE ,
R_INJECT_AW ,
R_PASSTHROUGH,
R_INCR_UPSIZE
} r_state_e;
// Write-related type, but w_req_q is referenced from Read logic
typedef struct packed {
aw_chan_t aw ;
logic aw_valid ;
logic aw_throw_error ;
mst_w_chan_t w ;
logic w_valid ;
axi_pkg::len_t burst_len ;
axi_pkg::size_t orig_aw_size;
} w_req_t;
w_req_t w_req_d, w_req_q;
// Decide which upsizer will handle the incoming AXI transaction
logic [AxiMaxReads-1:0] idle_read_upsizer;
tran_id_t idx_ar_upsizer ;
// Find an idle upsizer to handle this transaction
tran_id_t idx_idle_upsizer;
lzc #(
.WIDTH(AxiMaxReads)
) i_idle_lzc (
.in_i (idle_read_upsizer),
.cnt_o (idx_idle_upsizer ),
.empty_o(/* Unused */ )
);
// Is there already another upsizer handling a transaction with the same id
logic [AxiMaxReads-1:0] id_clash_upsizer;
tran_id_t idx_id_clash_upsizer ;
for (genvar t = 0; t < AxiMaxReads; t++) begin: gen_id_clash
assign id_clash_upsizer[t] = arb_slv_ar_id == mst_ar_id[t] && !idle_read_upsizer[t];
end
onehot_to_bin #(
.ONEHOT_WIDTH(AxiMaxReads)
) i_id_clash_onehot_to_bin (
.onehot(id_clash_upsizer ),
.bin (idx_id_clash_upsizer)
);
// Choose an idle upsizer, unless there is an id clash
assign idx_ar_upsizer = (|id_clash_upsizer) ? idx_id_clash_upsizer : idx_idle_upsizer;
// This logic is used to resolve which upsizer is handling
// each outstanding read transaction
logic r_upsizer_valid;
tran_id_t idx_r_upsizer;
logic [AxiMaxReads-1:0] rid_upsizer_match;
// Is there a upsizer handling this transaction?
assign r_upsizer_valid = |rid_upsizer_match;
for (genvar t = 0; t < AxiMaxReads; t++) begin: gen_rid_match
assign rid_upsizer_match[t] = (mst_resp.r.id == mst_ar_id[t]) && !idle_read_upsizer[t];
end
onehot_to_bin #(
.ONEHOT_WIDTH(AxiMaxReads)
) i_rid_upsizer_lzc (
.onehot(rid_upsizer_match),
.bin (idx_r_upsizer )
);
typedef struct packed {
ar_chan_t ar ;
logic ar_valid ;
logic ar_throw_error ;
axi_pkg::len_t burst_len ;
axi_pkg::size_t orig_ar_size;
} r_req_t;
for (genvar t = 0; unsigned'(t) < AxiMaxReads; t++) begin: gen_read_upsizer
r_state_e r_state_d, r_state_q;
r_req_t r_req_d , r_req_q ;
// Are we idle?
assign idle_read_upsizer[t] = (r_state_q == R_IDLE) || (r_state_q == R_INJECT_AW);
// Byte-grouped data signal for the lane steering step
slv_data_t r_data;
always_comb begin
// Maintain state
r_state_d = r_state_q;
r_req_d = r_req_q ;
// AR Channel
mst_ar_tran[t] = r_req_q.ar ;
mst_ar_id[t] = r_req_q.ar.id ;
mst_ar_valid_tran[t] = r_req_q.ar_valid;
// Throw an error
mst_req_ar_err[t] = r_req_q.ar_throw_error;
// R Channel
// No latency
slv_r_tran[t] = '0 ;
slv_r_tran[t].id = mst_resp.r.id ;
slv_r_tran[t].resp = mst_resp.r.resp;
slv_r_tran[t].user = mst_resp.r.user;
arb_slv_ar_gnt_tran[t] = 1'b0;
mst_r_ready_tran[t] = 1'b0;
slv_r_valid_tran[t] = 1'b0;
// Got a grant on the AR channel
if (mst_ar_valid_tran[t] && mst_ar_ready_tran[t]) begin
r_req_d.ar_valid = 1'b0;
r_req_d.ar_throw_error = 1'b0;
end
// Initialize r_data
r_data = '0;
case (r_state_q)
R_IDLE : begin
// Reset channels
r_req_d.ar = '0;
// New read request
if (arb_slv_ar_req && (idx_ar_upsizer == t)) begin
arb_slv_ar_gnt_tran[t] = 1'b1;
// Must inject an AW request into this upsizer
if (inject_aw_into_ar) begin
r_state_d = R_INJECT_AW;
end else begin
// Default state
r_state_d = R_PASSTHROUGH;
// Save beat
r_req_d.ar = slv_req_i.ar ;
r_req_d.ar_valid = 1'b1 ;
r_req_d.burst_len = slv_req_i.ar.len ;
r_req_d.orig_ar_size = slv_req_i.ar.size;
case (r_req_d.ar.burst)
axi_pkg::BURST_INCR: begin
// Modifiable transaction
if (modifiable(r_req_d.ar.cache)) begin
// No need to upsize single-beat transactions.
if (r_req_d.ar.len != '0) begin
// Evaluate output burst length
automatic addr_t start_addr = aligned_addr(r_req_d.ar.addr, AxiMstPortMaxSize);
automatic addr_t end_addr = aligned_addr(beat_addr(r_req_d.ar.addr,
r_req_d.orig_ar_size, r_req_d.burst_len, r_req_d.ar.burst,
r_req_d.burst_len), AxiMstPortMaxSize);
r_req_d.ar.len = (end_addr - start_addr) >> AxiMstPortMaxSize;
r_req_d.ar.size = AxiMstPortMaxSize ;
r_state_d = R_INCR_UPSIZE ;
end
end
end
axi_pkg::BURST_FIXED: begin
// Passes through the upsizer without any changes
r_state_d = R_PASSTHROUGH;
end
axi_pkg::BURST_WRAP: begin
// The DW converter does not support this kind of burst ...
r_state_d = R_PASSTHROUGH;
r_req_d.ar_throw_error = 1'b1 ;
// ... but might if this is a single-beat transaction
if (r_req_d.ar.len == '0)
r_req_d.ar_throw_error = 1'b0;
end
endcase
end
end
end
R_INJECT_AW : begin
// Save beat
// During this cycle, w_req_q stores the original AW request
r_req_d.ar.id = w_req_q.aw.id ;
r_req_d.ar.addr = w_req_q.aw.addr ;
r_req_d.ar.size = w_req_q.orig_aw_size;
r_req_d.ar.burst = w_req_q.aw.burst ;
r_req_d.ar.len = w_req_q.burst_len ;
r_req_d.ar.lock = w_req_q.aw.lock ;
r_req_d.ar.cache = w_req_q.aw.cache ;
r_req_d.ar.prot = w_req_q.aw.prot ;
r_req_d.ar.qos = w_req_q.aw.qos ;
r_req_d.ar.region = w_req_q.aw.region ;
r_req_d.ar.user = w_req_q.aw.user ;
r_req_d.ar_valid = 1'b0 ; // Injected "AR"s from AW are not valid.
r_req_d.burst_len = w_req_q.burst_len ;
r_req_d.orig_ar_size = w_req_q.orig_aw_size;
// Default state
r_state_d = R_PASSTHROUGH;
case (r_req_d.ar.burst)
axi_pkg::BURST_INCR: begin
// Modifiable transaction
if (modifiable(r_req_d.ar.cache)) begin
// No need to upsize single-beat transactions.
if (r_req_d.ar.len != '0) begin
// Evaluate output burst length
automatic addr_t start_addr = aligned_addr(r_req_d.ar.addr, AxiMstPortMaxSize);
automatic addr_t end_addr = aligned_addr(beat_addr(r_req_d.ar.addr,
r_req_d.orig_ar_size, r_req_d.burst_len, r_req_d.ar.burst,
r_req_d.burst_len), AxiMstPortMaxSize);
r_req_d.ar.len = (end_addr - start_addr) >> AxiMstPortMaxSize;
r_req_d.ar.size = AxiMstPortMaxSize ;
r_state_d = R_INCR_UPSIZE ;
end
end
end
axi_pkg::BURST_FIXED: begin
// Passes through the upsizer without any changes
r_state_d = R_PASSTHROUGH;
end
axi_pkg::BURST_WRAP: begin
// The DW converter does not support this kind of burst ...
r_state_d = R_PASSTHROUGH;
r_req_d.ar_throw_error = 1'b1 ;
// ... but might if this is a single-beat transaction
if (r_req_d.ar.len == '0)
r_req_d.ar_throw_error = 1'b0;
end
endcase
end
R_PASSTHROUGH, R_INCR_UPSIZE: begin
// Request was accepted
if (!r_req_q.ar_valid)
if (mst_resp.r_valid && (idx_r_upsizer == t) && r_upsizer_valid) begin
automatic addr_t mst_port_offset = AxiMstPortStrbWidth == 1 ? '0 : r_req_q.ar.addr[idx_width(AxiMstPortStrbWidth)-1:0];
automatic addr_t slv_port_offset = AxiSlvPortStrbWidth == 1 ? '0 : r_req_q.ar.addr[idx_width(AxiSlvPortStrbWidth)-1:0];
// Valid output
slv_r_valid_tran[t] = 1'b1 ;
slv_r_tran[t].last = mst_resp.r.last && (r_req_q.burst_len == 0);
// Lane steering
for (int b = 0; b < AxiMstPortStrbWidth; b++) begin
if ((b >= mst_port_offset) &&
(b - mst_port_offset < (1 << r_req_q.orig_ar_size)) &&
(b + slv_port_offset - mst_port_offset < AxiSlvPortStrbWidth)) begin
r_data[b + slv_port_offset - mst_port_offset] = mst_resp.r.data[8*b +: 8];
end
end
slv_r_tran[t].data = r_data;
// Acknowledgment
if (slv_r_ready_tran[t]) begin
r_req_d.burst_len = r_req_q.burst_len - 1;
case (r_req_q.ar.burst)
axi_pkg::BURST_INCR: begin
r_req_d.ar.addr = aligned_addr(r_req_q.ar.addr, r_req_q.orig_ar_size) + (1 << r_req_q.orig_ar_size);
end
axi_pkg::BURST_FIXED: begin
r_req_d.ar.addr = r_req_q.ar.addr;
end
endcase
case (r_state_q)
R_PASSTHROUGH:
mst_r_ready_tran[t] = 1'b1;
R_INCR_UPSIZE:
if (r_req_q.burst_len == 0 || (aligned_addr(r_req_d.ar.addr, AxiMstPortMaxSize) != aligned_addr(r_req_q.ar.addr, AxiMstPortMaxSize)))
mst_r_ready_tran[t] = 1'b1;
endcase
if (r_req_q.burst_len == '0)
r_state_d = R_IDLE;
end
end
end
endcase
end
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
r_state_q <= R_IDLE;
r_req_q <= '0 ;
end else begin
r_state_q <= r_state_d;
r_req_q <= r_req_d ;
end
end
end : gen_read_upsizer
/***********
* WRITE *
***********/
typedef enum logic [1:0] {
W_IDLE ,
W_PASSTHROUGH,
W_INCR_UPSIZE
} w_state_e;
w_state_e w_state_d, w_state_q;
// Byte-grouped data signal for the serialization step
mst_data_t w_data;
always_comb begin
inject_aw_into_ar_req = 1'b0;
// Maintain state
w_state_d = w_state_q;
w_req_d = w_req_q ;
// AW Channel
mst_req.aw = w_req_q.aw ;
mst_req.aw_valid = w_req_q.aw_valid;
slv_resp_o.aw_ready = '0 ;
// Throw an error.
mst_req_aw_err = w_req_q.aw_throw_error;
// W Channel
mst_req.w = w_req_q.w ;
mst_req.w_valid = w_req_q.w_valid;
slv_resp_o.w_ready = '0 ;
// Initialize w_data
w_data = w_req_q.w.data;
// B Channel (No latency)
slv_resp_o.b = mst_resp.b ;
slv_resp_o.b_valid = mst_resp.b_valid ;
mst_req.b_ready = slv_req_i.b_ready;
// Got a grant on the AW channel
if (mst_req.aw_valid && mst_resp.aw_ready) begin
w_req_d.aw_valid = 1'b0;
w_req_d.aw_throw_error = 1'b0;
end
case (w_state_q)
W_PASSTHROUGH, W_INCR_UPSIZE: begin
// Got a grant on the W channel
if (mst_req.w_valid && mst_resp.w_ready) begin
w_data = '0 ;
w_req_d.w = '0 ;
w_req_d.w_valid = 1'b0;
end
// Request was accepted
if (!w_req_q.aw_valid) begin
// Ready if downstream interface is idle, or if it is ready
slv_resp_o.w_ready = ~mst_req.w_valid || mst_resp.w_ready;
if (slv_req_i.w_valid && slv_resp_o.w_ready) begin
automatic addr_t mst_port_offset = AxiMstPortStrbWidth == 1 ? '0 : w_req_q.aw.addr[idx_width(AxiMstPortStrbWidth)-1:0];
automatic addr_t slv_port_offset = AxiSlvPortStrbWidth == 1 ? '0 : w_req_q.aw.addr[idx_width(AxiSlvPortStrbWidth)-1:0];
// Serialization
for (int b = 0; b < AxiMstPortStrbWidth; b++)
if ((b >= mst_port_offset) &&
(b - mst_port_offset < (1 << w_req_q.orig_aw_size)) &&
(b + slv_port_offset - mst_port_offset < AxiSlvPortStrbWidth)) begin
w_data[b] = slv_req_i.w.data[8*(b + slv_port_offset - mst_port_offset) +: 8];
w_req_d.w.strb[b] = slv_req_i.w.strb[b + slv_port_offset - mst_port_offset] ;
end
w_req_d.burst_len = w_req_q.burst_len - 1 ;
w_req_d.w.data = w_data ;
w_req_d.w.last = (w_req_q.burst_len == 0);
w_req_d.w.user = slv_req_i.w.user ;
case (w_req_q.aw.burst)
axi_pkg::BURST_INCR: begin
w_req_d.aw.addr = aligned_addr(w_req_q.aw.addr, w_req_q.orig_aw_size) + (1 << w_req_q.orig_aw_size);
end
axi_pkg::BURST_FIXED: begin
w_req_d.aw.addr = w_req_q.aw.addr;
end
endcase
case (w_state_q)
W_PASSTHROUGH:
// Forward data as soon as we can
w_req_d.w_valid = 1'b1;
W_INCR_UPSIZE:
// Forward when the burst is finished, or after filling up a word
if (w_req_q.burst_len == 0 || (aligned_addr(w_req_d.aw.addr, AxiMstPortMaxSize) != aligned_addr(w_req_q.aw.addr, AxiMstPortMaxSize)))
w_req_d.w_valid = 1'b1;
endcase
end
end
if (mst_req.w_valid && mst_resp.w_ready)
if (w_req_q.burst_len == '1) begin
slv_resp_o.w_ready = 1'b0 ;
w_state_d = W_IDLE;
end
end
endcase
// Can start a new request as soon as w_state_d is W_IDLE
if (w_state_d == W_IDLE) begin
// Reset channels
w_req_d.aw = '0 ;
w_req_d.aw_valid = 1'b0;
w_req_d.aw_throw_error = 1'b0;
w_req_d.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
inject_aw_into_ar_req = 1'b1 ;
slv_resp_o.aw_ready = inject_aw_into_ar_gnt;
end else begin // Regular AW
slv_resp_o.aw_ready = 1'b1;
end
// New write request
if (slv_req_i.aw_valid & slv_resp_o.aw_ready) begin
// Default state
w_state_d = W_PASSTHROUGH;
// Save beat
w_req_d.aw = slv_req_i.aw;
w_req_d.aw_valid = 1'b1 ;
w_req_d.burst_len = slv_req_i.aw.len ;
w_req_d.orig_aw_size = slv_req_i.aw.size;
case (slv_req_i.aw.burst)
axi_pkg::BURST_INCR: begin
// Modifiable transaction
if (modifiable(slv_req_i.aw.cache))
// No need to upsize single-beat transactions.
if (slv_req_i.aw.len != '0) begin
// Evaluate output burst length
automatic addr_t start_addr = aligned_addr(slv_req_i.aw.addr, AxiMstPortMaxSize);
automatic addr_t end_addr = aligned_addr(beat_addr(slv_req_i.aw.addr,
slv_req_i.aw.size, slv_req_i.aw.len, slv_req_i.aw.burst, slv_req_i.aw.len),
AxiMstPortMaxSize);
w_req_d.aw.len = (end_addr - start_addr) >> AxiMstPortMaxSize;
w_req_d.aw.size = AxiMstPortMaxSize ;
w_state_d = W_INCR_UPSIZE ;
end
end
axi_pkg::BURST_FIXED: begin
// Passes through the upsizer without any changes
w_state_d = W_PASSTHROUGH;
end
axi_pkg::BURST_WRAP: begin
// The DW converter does not support this kind of burst ...
w_state_d = W_PASSTHROUGH;
w_req_d.aw_throw_error = 1'b1 ;
// ... but might if this is a single-beat transaction
if (slv_req_i.aw.len == '0)
w_req_d.aw_throw_error = 1'b0;
end
endcase
end
end
end
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
w_state_q <= W_IDLE;
w_req_q <= '0 ;
end else begin
w_state_q <= w_state_d;
w_req_q <= w_req_d ;
end
end
endmodule : axi_dw_upsizer

View file

@ -0,0 +1,261 @@
// Copyright 2019 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
//
// Authors:
// - Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>
// - Andreas Kurth <akurth@iis.ee.ethz.ch>
// - Matheus Cavalcante <matheusd@iis.ee.ethz.ch>
// AXI Error Slave: This module always responds with an AXI error for transactions that are sent to
// it. This module optionally supports ATOPs if the `ATOPs` parameter is set.
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 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
// slave port
input req_t slv_req_i,
output resp_t slv_resp_o
);
typedef logic [AxiIdWidth-1:0] id_t;
typedef struct packed {
id_t id;
axi_pkg::len_t len;
} r_data_t;
req_t err_req;
resp_t err_resp;
if (ATOPs) begin
axi_atop_filter #(
.AxiIdWidth ( AxiIdWidth ),
.AxiMaxWriteTxns ( MaxTrans ),
.req_t ( req_t ),
.resp_t ( resp_t )
) i_atop_filter (
.clk_i,
.rst_ni,
.slv_req_i ( slv_req_i ),
.slv_resp_o ( slv_resp_o ),
.mst_req_o ( err_req ),
.mst_resp_i ( err_resp )
);
end else begin
assign err_req = slv_req_i;
assign slv_resp_o = err_resp;
end
// w fifo
logic w_fifo_full, w_fifo_empty;
logic w_fifo_push, w_fifo_pop;
id_t w_fifo_data;
// b fifo
logic b_fifo_full, b_fifo_empty;
logic b_fifo_push, b_fifo_pop;
id_t b_fifo_data;
// r fifo
r_data_t r_fifo_inp;
logic r_fifo_full, r_fifo_empty;
logic r_fifo_push, r_fifo_pop;
r_data_t r_fifo_data;
// r counter
logic r_cnt_clear, r_cnt_en, r_cnt_load;
axi_pkg::len_t r_current_beat;
// r status
logic r_busy_d, r_busy_q, r_busy_load;
//--------------------------------------
// Write Transactions
//--------------------------------------
// push, when there is room in the fifo
assign w_fifo_push = err_req.aw_valid & ~w_fifo_full;
assign err_resp.aw_ready = ~w_fifo_full;
fifo_v3 #(
.FALL_THROUGH ( 1'b1 ),
.DEPTH ( MaxTrans ),
.dtype ( id_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 ( err_req.aw.id ),
.push_i ( w_fifo_push ),
.data_o ( w_fifo_data ),
.pop_i ( w_fifo_pop )
);
always_comb begin : proc_w_channel
err_resp.w_ready = 1'b0;
w_fifo_pop = 1'b0;
b_fifo_push = 1'b0;
if (!w_fifo_empty && !b_fifo_full) begin
// eat the beats
err_resp.w_ready = 1'b1;
// on the last w transaction
if (err_req.w_valid && err_req.w.last) begin
w_fifo_pop = 1'b1;
b_fifo_push = 1'b1;
end
end
end
fifo_v3 #(
.FALL_THROUGH ( 1'b0 ),
.DEPTH ( unsigned'(2) ), // two placed so that w can eat beats if b is not sent
.dtype ( id_t )
) i_b_fifo (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( 1'b0 ),
.testmode_i ( test_i ),
.full_o ( b_fifo_full ),
.empty_o ( b_fifo_empty ),
.usage_o ( ),
.data_i ( w_fifo_data ),
.push_i ( b_fifo_push ),
.data_o ( b_fifo_data ),
.pop_i ( b_fifo_pop )
);
always_comb begin : proc_b_channel
b_fifo_pop = 1'b0;
err_resp.b = '0;
err_resp.b.id = b_fifo_data;
err_resp.b.resp = Resp;
err_resp.b_valid = 1'b0;
if (!b_fifo_empty) begin
err_resp.b_valid = 1'b1;
// b transaction
b_fifo_pop = err_req.b_ready;
end
end
//--------------------------------------
// Read Transactions
//--------------------------------------
// push if there is room in the fifo
assign r_fifo_push = err_req.ar_valid & ~r_fifo_full;
assign err_resp.ar_ready = ~r_fifo_full;
// fifo data assignment
assign r_fifo_inp.id = err_req.ar.id;
assign r_fifo_inp.len = err_req.ar.len;
fifo_v3 #(
.FALL_THROUGH ( 1'b0 ),
.DEPTH ( MaxTrans ),
.dtype ( r_data_t )
) i_r_fifo (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( 1'b0 ),
.testmode_i( test_i ),
.full_o ( r_fifo_full ),
.empty_o ( r_fifo_empty ),
.usage_o ( ),
.data_i ( r_fifo_inp ),
.push_i ( r_fifo_push ),
.data_o ( r_fifo_data ),
.pop_i ( r_fifo_pop )
);
always_comb begin : proc_r_channel
// default assignments
r_busy_d = r_busy_q;
r_busy_load = 1'b0;
// r fifo signals
r_fifo_pop = 1'b0;
// r counter signals
r_cnt_clear = 1'b0;
r_cnt_en = 1'b0;
r_cnt_load = 1'b0;
// r_channel
err_resp.r = '0;
err_resp.r.id = r_fifo_data.id;
err_resp.r.data = RespData;
err_resp.r.resp = Resp;
err_resp.r_valid = 1'b0;
// control
if (r_busy_q) begin
err_resp.r_valid = 1'b1;
err_resp.r.last = (r_current_beat == '0);
// r transaction
if (err_req.r_ready) begin
r_cnt_en = 1'b1;
if (r_current_beat == '0) begin
r_busy_d = 1'b0;
r_busy_load = 1'b1;
r_cnt_clear = 1'b1;
r_fifo_pop = 1'b1;
end
end
end else begin
// when not busy and fifo not empty, start counter err gen
if (!r_fifo_empty) begin
r_busy_d = 1'b1;
r_busy_load = 1'b1;
r_cnt_load = 1'b1;
end
end
end
always_ff @(posedge clk_i, negedge rst_ni) begin
if (!rst_ni) begin
r_busy_q <= '0;
end else if (r_busy_load) begin
r_busy_q <= r_busy_d;
end
end
counter #(
.WIDTH ($bits(axi_pkg::len_t))
) i_r_counter (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.clear_i ( r_cnt_clear ),
.en_i ( r_cnt_en ),
.load_i ( r_cnt_load ),
.down_i ( 1'b1 ),
.d_i ( r_fifo_data.len ),
.q_o ( r_current_beat ),
.overflow_o( )
);
// pragma translate_off
`ifndef VERILATOR
`ifndef XSIM
initial begin
assert (Resp == axi_pkg::RESP_DECERR || Resp == axi_pkg::RESP_SLVERR) else
$fatal(1, "This module may only generate RESP_DECERR or RESP_SLVERR responses!");
end
default disable iff (!rst_ni);
if (!ATOPs) begin : gen_assert_atops_unsupported
assume property( @(posedge clk_i) (slv_req_i.aw_valid |-> slv_req_i.aw.atop == '0)) else
$fatal(1, "Got ATOP but not configured to support ATOPs!");
end
`endif
`endif
// pragma translate_on
endmodule

View file

@ -0,0 +1,161 @@
// Copyright 2019 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
//
// Authors:
// - Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>
// - Andreas Kurth <akurth@iis.ee.ethz.ch>
// AXI ID Prepend: This module prepends/strips the MSB from the AXI IDs.
// Constraints enforced through assertions: ID width of slave and master port
module axi_id_prepend #(
parameter int unsigned NoBus = 1, // Can take multiple axi busses
parameter int unsigned AxiIdWidthSlvPort = 4, // AXI ID Width of the Slave Ports
parameter int unsigned AxiIdWidthMstPort = 6, // AXI ID Width of the Master Ports
parameter type slv_aw_chan_t = logic, // AW Channel Type for slv port
parameter type slv_w_chan_t = logic, // W Channel Type for slv port
parameter type slv_b_chan_t = logic, // B Channel Type for slv port
parameter type slv_ar_chan_t = logic, // AR Channel Type for slv port
parameter type slv_r_chan_t = logic, // R Channel Type for slv port
parameter type mst_aw_chan_t = logic, // AW Channel Type for mst port
parameter type mst_w_chan_t = logic, // W Channel Type for mst port
parameter type mst_b_chan_t = logic, // B Channel Type for mst port
parameter type mst_ar_chan_t = logic, // AR Channel Type for mst port
parameter type mst_r_chan_t = logic, // R Channel Type for mst port
// DEPENDENT PARAMETER DO NOT OVERWRITE!
parameter int unsigned PreIdWidth = AxiIdWidthMstPort - AxiIdWidthSlvPort
) (
input logic [PreIdWidth-1:0] pre_id_i, // ID to be prepended
// slave port (input), connect master modules here
// AW channel
input slv_aw_chan_t [NoBus-1:0] slv_aw_chans_i,
input logic [NoBus-1:0] slv_aw_valids_i,
output logic [NoBus-1:0] slv_aw_readies_o,
// W channel
input slv_w_chan_t [NoBus-1:0] slv_w_chans_i,
input logic [NoBus-1:0] slv_w_valids_i,
output logic [NoBus-1:0] slv_w_readies_o,
// B channel
output slv_b_chan_t [NoBus-1:0] slv_b_chans_o,
output logic [NoBus-1:0] slv_b_valids_o,
input logic [NoBus-1:0] slv_b_readies_i,
// AR channel
input slv_ar_chan_t [NoBus-1:0] slv_ar_chans_i,
input logic [NoBus-1:0] slv_ar_valids_i,
output logic [NoBus-1:0] slv_ar_readies_o,
// R channel
output slv_r_chan_t [NoBus-1:0] slv_r_chans_o,
output logic [NoBus-1:0] slv_r_valids_o,
input logic [NoBus-1:0] slv_r_readies_i,
// master ports (output), connect slave modules here
// AW channel
output mst_aw_chan_t [NoBus-1:0] mst_aw_chans_o,
output logic [NoBus-1:0] mst_aw_valids_o,
input logic [NoBus-1:0] mst_aw_readies_i,
// W channel
output mst_w_chan_t [NoBus-1:0] mst_w_chans_o,
output logic [NoBus-1:0] mst_w_valids_o,
input logic [NoBus-1:0] mst_w_readies_i,
// B channel
input mst_b_chan_t [NoBus-1:0] mst_b_chans_i,
input logic [NoBus-1:0] mst_b_valids_i,
output logic [NoBus-1:0] mst_b_readies_o,
// AR channel
output mst_ar_chan_t [NoBus-1:0] mst_ar_chans_o,
output logic [NoBus-1:0] mst_ar_valids_o,
input logic [NoBus-1:0] mst_ar_readies_i,
// R channel
input mst_r_chan_t [NoBus-1:0] mst_r_chans_i,
input logic [NoBus-1:0] mst_r_valids_i,
output logic [NoBus-1:0] mst_r_readies_o
);
// prepend the ID
for (genvar i = 0; i < NoBus; i++) begin : gen_id_prepend
if (PreIdWidth == 0) begin : gen_no_prepend
assign mst_aw_chans_o[i] = slv_aw_chans_i[i];
assign mst_ar_chans_o[i] = slv_ar_chans_i[i];
end else begin : gen_prepend
always_comb begin
mst_aw_chans_o[i] = slv_aw_chans_i[i];
mst_ar_chans_o[i] = slv_ar_chans_i[i];
mst_aw_chans_o[i].id = {pre_id_i, slv_aw_chans_i[i].id[AxiIdWidthSlvPort-1:0]};
mst_ar_chans_o[i].id = {pre_id_i, slv_ar_chans_i[i].id[AxiIdWidthSlvPort-1:0]};
end
end
// The ID is in the highest bits of the struct, so an assignment from a channel with a wide ID
// to a channel with a shorter ID correctly cuts the prepended ID.
assign slv_b_chans_o[i] = mst_b_chans_i[i];
assign slv_r_chans_o[i] = mst_r_chans_i[i];
end
// assign the handshaking's and w channel
assign mst_w_chans_o = slv_w_chans_i;
assign mst_aw_valids_o = slv_aw_valids_i;
assign slv_aw_readies_o = mst_aw_readies_i;
assign mst_w_valids_o = slv_w_valids_i;
assign slv_w_readies_o = mst_w_readies_i;
assign slv_b_valids_o = mst_b_valids_i;
assign mst_b_readies_o = slv_b_readies_i;
assign mst_ar_valids_o = slv_ar_valids_i;
assign slv_ar_readies_o = mst_ar_readies_i;
assign slv_r_valids_o = mst_r_valids_i;
assign mst_r_readies_o = slv_r_readies_i;
// pragma translate_off
`ifndef VERILATOR
initial begin : p_assert
assert(NoBus > 0)
else $fatal(1, "Input must be at least one element wide.");
assert(PreIdWidth == ($bits(mst_aw_chans_o[0].id) - $bits(slv_aw_chans_i[0].id)))
else $fatal(1, "Prepend ID Width must be: $bits(mst_aw_chans_o.id)-$bits(slv_aw_chans_i.id)");
assert ($bits(mst_aw_chans_o[0].id) > $bits(slv_aw_chans_i[0].id))
else $fatal(1, "The master AXI port has to have a wider ID than the slave port.");
end
aw_id : assert final(
mst_aw_chans_o[0].id[$bits(slv_aw_chans_i[0].id)-1:0] === slv_aw_chans_i[0].id)
else $fatal (1, "Something with the AW channel ID prepending went wrong.");
aw_addr : assert final(mst_aw_chans_o[0].addr === slv_aw_chans_i[0].addr)
else $fatal (1, "Something with the AW channel ID prepending went wrong.");
aw_len : assert final(mst_aw_chans_o[0].len === slv_aw_chans_i[0].len)
else $fatal (1, "Something with the AW channel ID prepending went wrong.");
aw_size : assert final(mst_aw_chans_o[0].size === slv_aw_chans_i[0].size)
else $fatal (1, "Something with the AW channel ID prepending went wrong.");
aw_qos : assert final(mst_aw_chans_o[0].qos === slv_aw_chans_i[0].qos)
else $fatal (1, "Something with the AW channel ID prepending went wrong.");
b_id : assert final(
mst_b_chans_i[0].id[$bits(slv_b_chans_o[0].id)-1:0] === slv_b_chans_o[0].id)
else $fatal (1, "Something with the B channel ID stripping went wrong.");
b_resp : assert final(mst_b_chans_i[0].resp === slv_b_chans_o[0].resp)
else $fatal (1, "Something with the B channel ID stripping went wrong.");
ar_id : assert final(
mst_ar_chans_o[0].id[$bits(slv_ar_chans_i[0].id)-1:0] === slv_ar_chans_i[0].id)
else $fatal (1, "Something with the AR channel ID prepending went wrong.");
ar_addr : assert final(mst_ar_chans_o[0].addr === slv_ar_chans_i[0].addr)
else $fatal (1, "Something with the AR channel ID prepending went wrong.");
ar_len : assert final(mst_ar_chans_o[0].len === slv_ar_chans_i[0].len)
else $fatal (1, "Something with the AR channel ID prepending went wrong.");
ar_size : assert final(mst_ar_chans_o[0].size === slv_ar_chans_i[0].size)
else $fatal (1, "Something with the AR channel ID prepending went wrong.");
ar_qos : assert final(mst_ar_chans_o[0].qos === slv_ar_chans_i[0].qos)
else $fatal (1, "Something with the AR channel ID prepending went wrong.");
r_id : assert final(mst_r_chans_i[0].id[$bits(slv_r_chans_o[0].id)-1:0] === slv_r_chans_o[0].id)
else $fatal (1, "Something with the R channel ID stripping went wrong.");
r_data : assert final(mst_r_chans_i[0].data === slv_r_chans_o[0].data)
else $fatal (1, "Something with the R channel ID stripping went wrong.");
r_resp : assert final(mst_r_chans_i[0].resp === slv_r_chans_o[0].resp)
else $fatal (1, "Something with the R channel ID stripping went wrong.");
`endif
// pragma translate_on
endmodule

View file

@ -0,0 +1,657 @@
// Copyright (c) 2014-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.
//
// Andreas Kurth <akurth@iis.ee.ethz.ch>
// Florian Zaruba <zarubaf@iis.ee.ethz.ch>
// Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>
`include "common_cells/registers.svh"
/// Remap AXI IDs from wide IDs at the slave port to narrower IDs at the master port.
///
/// This module is designed to remap an overly wide, sparsely used ID space to a narrower, densely
/// used ID space. This scenario occurs, for example, when an AXI master has wide ID ports but
/// effectively only uses a (not necessarily contiguous) subset of IDs.
///
/// This module retains the independence of IDs. That is, if two transactions have different IDs at
/// the slave port of this module, they are guaranteed to have different IDs at the master port of
/// this module. This implies a lower bound on the [width of IDs on the master
/// port](#parameter.AxiMstPortIdWidth). If you require narrower master port IDs and can forgo ID
/// independence, use [`axi_id_serialize`](module.axi_id_serialize) instead.
///
/// Internally, a [table is used for remapping IDs](module.axi_id_remap_table).
module axi_id_remap #(
/// ID width of the AXI4+ATOP slave port.
parameter int unsigned AxiSlvPortIdWidth = 32'd0,
/// Maximum number of different IDs that can be in flight at the 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.
///
/// The maximum value of this parameter is `2**AxiSlvPortIdWidth`.
parameter int unsigned AxiSlvPortMaxUniqIds = 32'd0,
/// Maximum number of in-flight transactions with the same ID.
///
/// It is legal for upstream to have more transactions than the maximum given by this parameter in
/// flight for any ID, but a transaction exceeding the maximum will be stalled until another
/// transaction with the same ID completes.
parameter int unsigned AxiMaxTxnsPerId = 32'd0,
/// ID width of the AXI4+ATOP master port.
///
/// The minimum value of this parameter is the ceiled binary logarithm of `AxiSlvPortMaxUniqIds`,
/// because IDs at the master port must be wide enough to represent IDs up to
/// `AxiSlvPortMaxUniqIds-1`.
///
/// If master IDs are wider than the minimum, they are extended by prepending zeros.
parameter int unsigned AxiMstPortIdWidth = 32'd0,
/// Request struct type of the AXI4+ATOP slave port.
///
/// The width of all IDs in this struct must match `AxiSlvPortIdWidth`.
parameter type slv_req_t = logic,
/// Response struct type of the AXI4+ATOP slave port.
///
/// The width of all IDs in this struct must match `AxiSlvPortIdWidth`.
parameter type slv_resp_t = logic,
/// Request struct type of the AXI4+ATOP master port
///
/// The width of all IDs in this struct must match `AxiMstPortIdWidth`.
parameter type mst_req_t = logic,
/// Response struct type of the AXI4+ATOP master port
///
/// The width of all IDs in this struct must match `AxiMstPortIdWidth`.
parameter type mst_resp_t = logic
) (
/// Rising-edge clock of all ports
input logic clk_i,
/// Asynchronous reset, active low
input logic rst_ni,
/// Slave port request
input slv_req_t slv_req_i,
/// Slave port response
output slv_resp_t slv_resp_o,
/// Master port request
output mst_req_t mst_req_o,
/// Master port response
input mst_resp_t mst_resp_i
);
// Feed all signals that are not ID or flow control of AW and AR through.
assign mst_req_o.aw.addr = slv_req_i.aw.addr;
assign mst_req_o.aw.len = slv_req_i.aw.len;
assign mst_req_o.aw.size = slv_req_i.aw.size;
assign mst_req_o.aw.burst = slv_req_i.aw.burst;
assign mst_req_o.aw.lock = slv_req_i.aw.lock;
assign mst_req_o.aw.cache = slv_req_i.aw.cache;
assign mst_req_o.aw.prot = slv_req_i.aw.prot;
assign mst_req_o.aw.qos = slv_req_i.aw.qos;
assign mst_req_o.aw.region = slv_req_i.aw.region;
assign mst_req_o.aw.atop = slv_req_i.aw.atop;
assign mst_req_o.aw.user = slv_req_i.aw.user;
assign mst_req_o.w = slv_req_i.w;
assign mst_req_o.w_valid = slv_req_i.w_valid;
assign slv_resp_o.w_ready = mst_resp_i.w_ready;
assign slv_resp_o.b.resp = mst_resp_i.b.resp;
assign slv_resp_o.b.user = mst_resp_i.b.user;
assign slv_resp_o.b_valid = mst_resp_i.b_valid;
assign mst_req_o.b_ready = slv_req_i.b_ready;
assign mst_req_o.ar.addr = slv_req_i.ar.addr;
assign mst_req_o.ar.len = slv_req_i.ar.len;
assign mst_req_o.ar.size = slv_req_i.ar.size;
assign mst_req_o.ar.burst = slv_req_i.ar.burst;
assign mst_req_o.ar.lock = slv_req_i.ar.lock;
assign mst_req_o.ar.cache = slv_req_i.ar.cache;
assign mst_req_o.ar.prot = slv_req_i.ar.prot;
assign mst_req_o.ar.qos = slv_req_i.ar.qos;
assign mst_req_o.ar.region = slv_req_i.ar.region;
assign mst_req_o.ar.user = slv_req_i.ar.user;
assign slv_resp_o.r.data = mst_resp_i.r.data;
assign slv_resp_o.r.resp = mst_resp_i.r.resp;
assign slv_resp_o.r.last = mst_resp_i.r.last;
assign slv_resp_o.r.user = mst_resp_i.r.user;
assign slv_resp_o.r_valid = mst_resp_i.r_valid;
assign mst_req_o.r_ready = slv_req_i.r_ready;
// Remap tables keep track of in-flight bursts and their input and output IDs.
localparam int unsigned IdxWidth = cf_math_pkg::idx_width(AxiSlvPortMaxUniqIds);
typedef logic [AxiSlvPortMaxUniqIds-1:0] field_t;
typedef logic [AxiSlvPortIdWidth-1:0] id_inp_t;
typedef logic [IdxWidth-1:0] idx_t;
field_t wr_free, rd_free, both_free;
id_inp_t rd_push_inp_id;
idx_t wr_free_oup_id, rd_free_oup_id, both_free_oup_id,
wr_push_oup_id, rd_push_oup_id,
wr_exists_id, rd_exists_id;
logic wr_exists, rd_exists,
wr_exists_full, rd_exists_full,
wr_full, rd_full,
wr_push, rd_push;
axi_id_remap_table #(
.InpIdWidth ( AxiSlvPortIdWidth ),
.MaxUniqInpIds ( AxiSlvPortMaxUniqIds ),
.MaxTxnsPerId ( AxiMaxTxnsPerId )
) i_wr_table (
.clk_i,
.rst_ni,
.free_o ( wr_free ),
.free_oup_id_o ( wr_free_oup_id ),
.full_o ( wr_full ),
.push_i ( wr_push ),
.push_inp_id_i ( slv_req_i.aw.id ),
.push_oup_id_i ( wr_push_oup_id ),
.exists_inp_id_i ( slv_req_i.aw.id ),
.exists_o ( wr_exists ),
.exists_oup_id_o ( wr_exists_id ),
.exists_full_o ( wr_exists_full ),
.pop_i ( slv_resp_o.b_valid && slv_req_i.b_ready ),
.pop_oup_id_i ( mst_resp_i.b.id[IdxWidth-1:0] ),
.pop_inp_id_o ( slv_resp_o.b.id )
);
axi_id_remap_table #(
.InpIdWidth ( AxiSlvPortIdWidth ),
.MaxUniqInpIds ( AxiSlvPortMaxUniqIds ),
.MaxTxnsPerId ( AxiMaxTxnsPerId )
) i_rd_table (
.clk_i,
.rst_ni,
.free_o ( rd_free ),
.free_oup_id_o ( rd_free_oup_id ),
.full_o ( rd_full ),
.push_i ( rd_push ),
.push_inp_id_i ( rd_push_inp_id ),
.push_oup_id_i ( rd_push_oup_id ),
.exists_inp_id_i ( slv_req_i.ar.id ),
.exists_o ( rd_exists ),
.exists_oup_id_o ( rd_exists_id ),
.exists_full_o ( rd_exists_full ),
.pop_i ( slv_resp_o.r_valid && slv_req_i.r_ready && slv_resp_o.r.last ),
.pop_oup_id_i ( mst_resp_i.r.id[IdxWidth-1:0] ),
.pop_inp_id_o ( slv_resp_o.r.id )
);
assign both_free = wr_free & rd_free;
lzc #(
.WIDTH ( AxiSlvPortMaxUniqIds ),
.MODE ( 1'b0 )
) i_lzc (
.in_i ( both_free ),
.cnt_o ( both_free_oup_id ),
.empty_o ( /* unused */ )
);
// Zero-extend output IDs if the output IDs is are wider than the IDs from the tables.
localparam ZeroWidth = AxiMstPortIdWidth - IdxWidth;
assign mst_req_o.ar.id = {{ZeroWidth{1'b0}}, rd_push_oup_id};
assign mst_req_o.aw.id = {{ZeroWidth{1'b0}}, wr_push_oup_id};
// Handle requests.
enum logic [1:0] {Ready, HoldAR, HoldAW, HoldAx} state_d, state_q;
idx_t ar_id_d, ar_id_q,
aw_id_d, aw_id_q;
logic ar_prio_d, ar_prio_q;
always_comb begin
mst_req_o.aw_valid = 1'b0;
slv_resp_o.aw_ready = 1'b0;
wr_push = 1'b0;
wr_push_oup_id = '0;
mst_req_o.ar_valid = 1'b0;
slv_resp_o.ar_ready = 1'b0;
rd_push = 1'b0;
rd_push_inp_id = '0;
rd_push_oup_id = '0;
ar_id_d = ar_id_q;
aw_id_d = aw_id_q;
ar_prio_d = ar_prio_q;
state_d = state_q;
unique case (state_q)
Ready: begin
// Reads
if (slv_req_i.ar_valid) begin
// If a burst with the same input ID is already in flight or there are free output IDs:
if ((rd_exists && !rd_exists_full) || (!rd_exists && !rd_full)) begin
// Determine the output ID: if another in-flight burst had the same input ID, we must
// reuse its output ID to maintain ordering; else, we assign the next free ID.
rd_push_inp_id = slv_req_i.ar.id;
rd_push_oup_id = rd_exists ? rd_exists_id : rd_free_oup_id;
// Forward the AR and push a new entry to the read table.
mst_req_o.ar_valid = 1'b1;
rd_push = 1'b1;
end
end
// Writes
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 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
// reuse its output ID to maintain ordering; else, we assign the next free ID.
wr_push_oup_id = wr_exists ? wr_exists_id : wr_free_oup_id;
// Forward the AW and push a new entry to the write table.
mst_req_o.aw_valid = 1'b1;
wr_push = 1'b1;
end
// If this is an ATOP that gives rise to an R response, we must remap to an ID that is
// free on both read and write direction and push also to the read table.
// Only allowed if AR does not have arbitration priority
end else if (!(ar_prio_q && mst_req_o.ar_valid)) begin
// Nullify a potential AR at our output. This is legal in this state.
mst_req_o.ar_valid = 1'b0;
slv_resp_o.ar_ready = 1'b0;
rd_push = 1'b0;
if ((|both_free)) begin
// Use an output ID that is free in both directions.
wr_push_oup_id = both_free_oup_id;
rd_push_inp_id = slv_req_i.aw.id;
rd_push_oup_id = both_free_oup_id;
// Forward the AW and push a new entry to both tables.
mst_req_o.aw_valid = 1'b1;
rd_push = 1'b1;
wr_push = 1'b1;
// Give AR priority in the next cycle (so ATOPs cannot infinitely preempt ARs).
ar_prio_d = 1'b1;
end
end
end
// Hold AR, AW, or both if they are valid but not yet ready.
if (mst_req_o.ar_valid) begin
slv_resp_o.ar_ready = mst_resp_i.ar_ready;
if (!mst_resp_i.ar_ready) begin
ar_id_d = rd_push_oup_id;
end
end
if (mst_req_o.aw_valid) begin
slv_resp_o.aw_ready = mst_resp_i.aw_ready;
if (!mst_resp_i.aw_ready) begin
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) begin
ar_prio_d = 1'b0; // Reset AR priority, because handshake was successful in this cycle.
end
end
HoldAR: begin
// Drive `mst_req_o.ar.id` through `rd_push_oup_id`.
rd_push_oup_id = ar_id_q;
mst_req_o.ar_valid = 1'b1;
slv_resp_o.ar_ready = mst_resp_i.ar_ready;
if (mst_resp_i.ar_ready) begin
state_d = Ready;
ar_prio_d = 1'b0; // Reset AR priority, because handshake was successful in this cycle.
end
end
HoldAW: begin
// Drive mst_req_o.aw.id through `wr_push_oup_id`.
wr_push_oup_id = aw_id_q;
mst_req_o.aw_valid = 1'b1;
slv_resp_o.aw_ready = mst_resp_i.aw_ready;
if (mst_resp_i.aw_ready) begin
state_d = Ready;
end
end
HoldAx: begin
rd_push_oup_id = ar_id_q;
mst_req_o.ar_valid = 1'b1;
slv_resp_o.ar_ready = mst_resp_i.ar_ready;
wr_push_oup_id = aw_id_q;
mst_req_o.aw_valid = 1'b1;
slv_resp_o.aw_ready = mst_resp_i.aw_ready;
unique case ({mst_resp_i.ar_ready, mst_resp_i.aw_ready})
2'b01: state_d = HoldAR;
2'b10: state_d = HoldAW;
2'b11: state_d = Ready;
default: /*do nothing / stay in this state*/;
endcase
if (mst_resp_i.ar_ready) begin
ar_prio_d = 1'b0; // Reset AR priority, because handshake was successful in this cycle.
end
end
default: state_d = Ready;
endcase
end
// Registers
`FFARN(ar_id_q, ar_id_d, '0, clk_i, rst_ni)
`FFARN(ar_prio_q, ar_prio_d, 1'b0, clk_i, rst_ni)
`FFARN(aw_id_q, aw_id_d, '0, clk_i, rst_ni)
`FFARN(state_q, state_d, Ready, clk_i, rst_ni)
// pragma translate_off
`ifndef VERILATOR
initial begin : p_assert
assert(AxiSlvPortIdWidth > 32'd0)
else $fatal(1, "Parameter AxiSlvPortIdWidth has to be larger than 0!");
assert(AxiMstPortIdWidth >= IdxWidth)
else $fatal(1, "Parameter AxiMstPortIdWidth has to be at least IdxWidth!");
assert (AxiSlvPortMaxUniqIds > 0)
else $fatal(1, "Parameter AxiSlvPortMaxUniqIds has to be larger than 0!");
assert (AxiSlvPortMaxUniqIds <= 2**AxiSlvPortIdWidth)
else $fatal(1, "Parameter AxiSlvPortMaxUniqIds may be at most 2**AxiSlvPortIdWidth!");
assert (AxiMaxTxnsPerId > 0)
else $fatal(1, "Parameter AxiMaxTxnsPerId has to be larger than 0!");
assert($bits(slv_req_i.aw.addr) == $bits(mst_req_o.aw.addr))
else $fatal(1, "AXI AW address widths are not equal!");
assert($bits(slv_req_i.w.data) == $bits(mst_req_o.w.data))
else $fatal(1, "AXI W data widths are not equal!");
assert($bits(slv_req_i.w.user) == $bits(mst_req_o.w.user))
else $fatal(1, "AXI W user widths are not equal!");
assert($bits(slv_req_i.ar.addr) == $bits(mst_req_o.ar.addr))
else $fatal(1, "AXI AR address widths are not equal!");
assert($bits(slv_resp_o.r.data) == $bits(mst_resp_i.r.data))
else $fatal(1, "AXI R data widths are not equal!");
assert ($bits(slv_req_i.aw.id) == AxiSlvPortIdWidth);
assert ($bits(slv_resp_o.b.id) == AxiSlvPortIdWidth);
assert ($bits(slv_req_i.ar.id) == AxiSlvPortIdWidth);
assert ($bits(slv_resp_o.r.id) == AxiSlvPortIdWidth);
assert ($bits(mst_req_o.aw.id) == AxiMstPortIdWidth);
assert ($bits(mst_resp_i.b.id) == AxiMstPortIdWidth);
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);
assert property (@(posedge clk_i) mst_resp_i.b_valid && mst_req_o.b_ready
|-> slv_resp_o.b_valid && slv_req_i.b_ready);
assert property (@(posedge clk_i) slv_req_i.ar_valid && slv_resp_o.ar_ready
|-> mst_req_o.ar_valid && mst_resp_i.ar_ready);
assert property (@(posedge clk_i) mst_resp_i.r_valid && mst_req_o.r_ready
|-> slv_resp_o.r_valid && slv_req_i.r_ready);
assert property (@(posedge clk_i) slv_resp_o.r_valid
|-> slv_resp_o.r.last == mst_resp_i.r.last);
assert property (@(posedge clk_i) mst_req_o.ar_valid && !mst_resp_i.ar_ready
|=> 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));
// pragma translate_on
endmodule
/// Internal module of [`axi_id_remap`](module.axi_id_remap): Table to remap input to output IDs.
///
/// This module contains a table indexed by the output ID (type `idx_t`). Each table entry has two
/// fields: the input ID and a counter that records how many transactions with the input and output
/// ID of the entry are in-flight.
///
/// The mapping from input and output IDs is injective. Therefore, when the table contains an entry
/// for an input ID with non-zero counter value, subsequent input IDs must use the same entry and
/// thus the same output ID.
///
/// ## Relation of types and table layout
/// ![diagram of table](axi_id_remap_table.svg)
///
/// ## Complexity
/// This module has:
/// - `MaxUniqInpIds * InpIdWidth * clog2(MaxTxnsPerId)` flip flops;
/// - `MaxUniqInpIds` comparators of width `InpIdWidth`;
/// - 2 leading-zero counters of width `MaxUniqInpIds`.
module axi_id_remap_table #(
/// Width of input IDs, therefore width of `id_inp_t`.
parameter int unsigned InpIdWidth = 32'd0,
/// Maximum number of different input IDs that can be in-flight. This defines the number of remap
/// table entries.
///
/// The maximum value of this parameter is `2**InpIdWidth`.
parameter int unsigned MaxUniqInpIds = 32'd0,
/// Maximum number of in-flight transactions with the same ID.
parameter int unsigned MaxTxnsPerId = 32'd0,
/// Derived (**=do not override**) type of input IDs.
localparam type id_inp_t = logic [InpIdWidth-1:0],
/// Derived (**=do not override**) width of table index (ceiled binary logarithm of
/// `MaxUniqInpIds`).
localparam int unsigned IdxWidth = cf_math_pkg::idx_width(MaxUniqInpIds),
/// Derived (**=do not override**) type of table index (width = `IdxWidth`).
localparam type idx_t = logic [IdxWidth-1:0],
/// Derived (**=do not override**) type with one bit per table entry (thus also output ID).
localparam type field_t = logic [MaxUniqInpIds-1:0]
) (
/// Rising-edge clock of all ports
input logic clk_i,
/// Asynchronous reset, active low
input logic rst_ni,
/// One bit per table entry / output ID that indicates whether the entry is free.
output field_t free_o,
/// Lowest free output ID. Only valid if the table is not full (i.e., `!full_o`).
output idx_t free_oup_id_o,
/// Indicates whether the table is full.
output logic full_o,
/// Push an input/output ID pair to the table.
input logic push_i,
/// Input ID to be pushed. If the table already contains this ID, its counter must be smaller than
/// `MaxTxnsPerId`.
input id_inp_t push_inp_id_i,
/// Output ID to be pushed. If the table already contains the input ID to be pushed, the output
/// ID **must** match the output ID of the existing entry with the same input ID.
input idx_t push_oup_id_i,
/// Input ID to look up in the table.
input id_inp_t exists_inp_id_i,
/// Indicates whether the given input ID exists in the table.
output logic exists_o,
/// The output ID of the given input ID. Only valid if the input ID exists (i.e., `exists_o`).
output idx_t exists_oup_id_o,
/// Indicates whether the maximum number of transactions for the given input ID is reached. Only
/// valid if the input ID exists (i.e., `exists_o`).
output logic exists_full_o,
/// Pop an output ID from the table. This reduces the counter for the table index given in
/// `pop_oup_id_i` by one.
input logic pop_i,
/// Output ID to be popped. The counter for this ID must be larger than `0`.
input idx_t pop_oup_id_i,
/// Input ID corresponding to the popped output ID.
output id_inp_t pop_inp_id_o
);
/// Counter width, derived to hold numbers up to `MaxTxnsPerId`.
localparam int unsigned CntWidth = $clog2(MaxTxnsPerId+1);
/// Counter that tracks the number of in-flight transactions with an ID.
typedef logic [CntWidth-1:0] cnt_t;
/// Type of a table entry.
typedef struct packed {
id_inp_t inp_id;
cnt_t cnt;
} entry_t;
// Table indexed by output IDs that contains the corresponding input IDs
entry_t [MaxUniqInpIds-1:0] table_d, table_q;
// Determine lowest free output ID.
for (genvar i = 0; i < MaxUniqInpIds; i++) begin : gen_free_o
assign free_o[i] = table_q[i].cnt == '0;
end
lzc #(
.WIDTH ( MaxUniqInpIds ),
.MODE ( 1'b0 )
) i_lzc_free (
.in_i ( free_o ),
.cnt_o ( free_oup_id_o ),
.empty_o ( full_o )
);
// Determine the input ID for a given output ID.
if (MaxUniqInpIds == 1) begin : gen_pop_for_single_unique_inp_id
assign pop_inp_id_o = table_q[0].inp_id;
end else begin : gen_pop_for_multiple_unique_inp_ids
assign pop_inp_id_o = table_q[pop_oup_id_i].inp_id;
end
// Determine if given output ID is already used and, if it is, by which input ID.
field_t match;
for (genvar i = 0; i < MaxUniqInpIds; i++) begin : gen_match
assign match[i] = table_q[i].cnt > 0 && table_q[i].inp_id == exists_inp_id_i;
end
logic no_match;
lzc #(
.WIDTH ( MaxUniqInpIds ),
.MODE ( 1'b0 )
) i_lzc_match (
.in_i ( match ),
.cnt_o ( exists_oup_id_o ),
.empty_o ( no_match )
);
assign exists_o = ~no_match;
if (MaxUniqInpIds == 1) begin : gen_exists_full_for_single_unique_inp_id
assign exists_full_o = table_q[0].cnt == MaxTxnsPerId;
end else begin : gen_exists_full_for_multiple_unique_inp_ids
assign exists_full_o = table_q[exists_oup_id_o].cnt == MaxTxnsPerId;
end
// Push and pop table entries.
always_comb begin
table_d = table_q;
if (push_i) begin
table_d[push_oup_id_i].inp_id = push_inp_id_i;
table_d[push_oup_id_i].cnt += 1;
end
if (pop_i) begin
table_d[pop_oup_id_i].cnt -= 1;
end
end
// Registers
`FFARN(table_q, table_d, '0, clk_i, rst_ni)
// Assertions
// pragma translate_off
`ifndef VERILATOR
default disable iff (!rst_ni);
assume property (@(posedge clk_i) push_i |->
table_q[push_oup_id_i].cnt == '0 || table_q[push_oup_id_i].inp_id == push_inp_id_i)
else $error("Push must be to empty output ID or match existing input ID!");
assume property (@(posedge clk_i) push_i |-> table_q[push_oup_id_i].cnt < MaxTxnsPerId)
else $error("Maximum number of in-flight bursts must not be exceeded!");
assume property (@(posedge clk_i) pop_i |-> table_q[pop_oup_id_i].cnt > 0)
else $error("Pop must target output ID with non-zero counter!");
assume property (@(posedge clk_i) $onehot0(match))
else $error("Input ID in table must be unique!");
initial begin
assert (InpIdWidth > 0);
assert (MaxUniqInpIds > 0);
assert (MaxUniqInpIds <= (1 << InpIdWidth));
assert (MaxTxnsPerId > 0);
assert (IdxWidth >= 1);
end
`endif
// pragma translate_on
endmodule
`include "axi/typedef.svh"
`include "axi/assign.svh"
/// Interface variant of [`axi_id_remap`](module.axi_id_remap).
///
/// See the documentation of the main module for the definition of ports and parameters.
module axi_id_remap_intf #(
parameter int unsigned AXI_SLV_PORT_ID_WIDTH = 32'd0,
parameter int unsigned AXI_SLV_PORT_MAX_UNIQ_IDS = 32'd0,
parameter int unsigned AXI_MAX_TXNS_PER_ID = 32'd0,
parameter int unsigned AXI_MST_PORT_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.Slave slv,
AXI_BUS.Master mst
);
typedef logic [AXI_SLV_PORT_ID_WIDTH-1:0] slv_id_t;
typedef logic [AXI_MST_PORT_ID_WIDTH-1:0] mst_id_t;
typedef logic [AXI_ADDR_WIDTH-1:0] axi_addr_t;
typedef logic [AXI_DATA_WIDTH-1:0] axi_data_t;
typedef logic [AXI_DATA_WIDTH/8-1:0] axi_strb_t;
typedef logic [AXI_USER_WIDTH-1:0] axi_user_t;
`AXI_TYPEDEF_AW_CHAN_T(slv_aw_chan_t, axi_addr_t, slv_id_t, axi_user_t)
`AXI_TYPEDEF_W_CHAN_T(slv_w_chan_t, axi_data_t, axi_strb_t, axi_user_t)
`AXI_TYPEDEF_B_CHAN_T(slv_b_chan_t, slv_id_t, axi_user_t)
`AXI_TYPEDEF_AR_CHAN_T(slv_ar_chan_t, axi_addr_t, slv_id_t, axi_user_t)
`AXI_TYPEDEF_R_CHAN_T(slv_r_chan_t, axi_data_t, slv_id_t, axi_user_t)
`AXI_TYPEDEF_REQ_T(slv_req_t, slv_aw_chan_t, slv_w_chan_t, slv_ar_chan_t)
`AXI_TYPEDEF_RESP_T(slv_resp_t, slv_b_chan_t, slv_r_chan_t)
`AXI_TYPEDEF_AW_CHAN_T(mst_aw_chan_t, axi_addr_t, mst_id_t, axi_user_t)
`AXI_TYPEDEF_W_CHAN_T(mst_w_chan_t, axi_data_t, axi_strb_t, axi_user_t)
`AXI_TYPEDEF_B_CHAN_T(mst_b_chan_t, mst_id_t, axi_user_t)
`AXI_TYPEDEF_AR_CHAN_T(mst_ar_chan_t, axi_addr_t, mst_id_t, axi_user_t)
`AXI_TYPEDEF_R_CHAN_T(mst_r_chan_t, axi_data_t, mst_id_t, axi_user_t)
`AXI_TYPEDEF_REQ_T(mst_req_t, mst_aw_chan_t, mst_w_chan_t, mst_ar_chan_t)
`AXI_TYPEDEF_RESP_T(mst_resp_t, mst_b_chan_t, mst_r_chan_t)
slv_req_t slv_req;
slv_resp_t slv_resp;
mst_req_t mst_req;
mst_resp_t 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_id_remap #(
.AxiSlvPortIdWidth ( AXI_SLV_PORT_ID_WIDTH ),
.AxiSlvPortMaxUniqIds ( AXI_SLV_PORT_MAX_UNIQ_IDS ),
.AxiMaxTxnsPerId ( AXI_MAX_TXNS_PER_ID ),
.AxiMstPortIdWidth ( AXI_MST_PORT_ID_WIDTH ),
.slv_req_t ( slv_req_t ),
.slv_resp_t ( slv_resp_t ),
.mst_req_t ( mst_req_t ),
.mst_resp_t ( mst_resp_t )
) i_axi_id_remap (
.clk_i,
.rst_ni,
.slv_req_i ( slv_req ),
.slv_resp_o ( slv_resp ),
.mst_req_o ( mst_req ),
.mst_resp_i ( mst_resp )
);
// pragma translate_off
`ifndef VERILATOR
initial begin
assert (slv.AXI_ID_WIDTH == AXI_SLV_PORT_ID_WIDTH);
assert (slv.AXI_ADDR_WIDTH == AXI_ADDR_WIDTH);
assert (slv.AXI_DATA_WIDTH == AXI_DATA_WIDTH);
assert (slv.AXI_USER_WIDTH == AXI_USER_WIDTH);
assert (mst.AXI_ID_WIDTH == AXI_MST_PORT_ID_WIDTH);
assert (mst.AXI_ADDR_WIDTH == AXI_ADDR_WIDTH);
assert (mst.AXI_DATA_WIDTH == AXI_DATA_WIDTH);
assert (mst.AXI_USER_WIDTH == AXI_USER_WIDTH);
end
`endif
// pragma translate_on
endmodule

View file

@ -0,0 +1,424 @@
// 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:
// - Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>
// - Andreas Kurth <akurth@iis.ee.ethz.ch>
`include "axi/assign.svh"
`include "axi/typedef.svh"
/// Reduce AXI IDs by serializing transactions when necessary.
///
/// This module is designed to remap a wide ID space to an arbitrarily narrow ID space. If
/// necessary, this module maps two different IDs at its slave port to the same ID at its master
/// port, thereby constraining the order of those transactions and in this sense *serializing* them.
/// If the independence of IDs needs to be retained at the cost of a wider ID space at the master
/// port, use [`axi_id_remap`](module.axi_id_remap) instead.
///
/// This module contains one [`axi_serializer`](module.axi_serializer) per master port ID (given by
/// the `AxiMstPortMaxUniqIds parameter`).
module axi_id_serialize #(
/// ID width of the AXI4+ATOP slave port
parameter int unsigned AxiSlvPortIdWidth = 32'd0,
/// Maximum number of transactions that can be in flight at the slave port. Reads and writes are
/// counted separately (except for ATOPs, which count as both read and write).
parameter int unsigned AxiSlvPortMaxTxns = 32'd0,
/// ID width of the AXI4+ATOP master port
parameter int unsigned AxiMstPortIdWidth = 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).
///
/// The maximum value of this parameter is `2**AxiMstPortIdWidth`.
parameter int unsigned AxiMstPortMaxUniqIds = 32'd0,
/// Maximum number of in-flight transactions with the same ID at the master port.
parameter int unsigned AxiMstPortMaxTxnsPerId = 32'd0,
/// Address width of both AXI4+ATOP ports
parameter int unsigned AxiAddrWidth = 32'd0,
/// Data width of both AXI4+ATOP ports
parameter int unsigned AxiDataWidth = 32'd0,
/// User width of both AXI4+ATOP ports
parameter int unsigned AxiUserWidth = 32'd0,
/// Request struct type of the AXI4+ATOP slave port
parameter type slv_req_t = logic,
/// Response struct type of the AXI4+ATOP slave port
parameter type slv_resp_t = logic,
/// Request struct type of the AXI4+ATOP master port
parameter type mst_req_t = logic,
/// Response struct type of the AXI4+ATOP master port
parameter type mst_resp_t = logic
) (
/// Rising-edge clock of both ports
input logic clk_i,
/// Asynchronous reset, active low
input logic rst_ni,
/// Slave port request
input slv_req_t slv_req_i,
/// Slave port response
output slv_resp_t slv_resp_o,
/// Master port request
output mst_req_t mst_req_o,
/// Master port response
input mst_resp_t mst_resp_i
);
/// Number of bits of the slave port ID that determine the mapping to the master port ID
localparam int unsigned SelectWidth = cf_math_pkg::idx_width(AxiMstPortMaxUniqIds);
/// Slice of slave port IDs that determines the master port ID
typedef logic [SelectWidth-1:0] select_t;
/// ID width after the multiplexer
localparam int unsigned MuxIdWidth = (AxiMstPortMaxUniqIds > 32'd1) ? SelectWidth + 32'd1 : 32'd1;
/// ID after serializer (i.e., with a constant value of zero)
typedef logic [0:0] ser_id_t;
/// ID after the multiplexer
typedef logic [MuxIdWidth-1:0] mux_id_t;
/// ID at the slave port
typedef logic [AxiSlvPortIdWidth-1:0] slv_id_t;
/// ID at the master port
typedef logic [AxiMstPortIdWidth-1:0] mst_id_t;
/// Address in any AXI channel
typedef logic [AxiAddrWidth-1:0] addr_t;
/// Data in any AXI channel
typedef logic [AxiDataWidth-1:0] data_t;
/// Strobe in any AXI channel
typedef logic [AxiDataWidth/8-1:0] strb_t;
/// User signal in any AXI channel
typedef logic [AxiUserWidth-1:0] user_t;
/// W channel at any interface
`AXI_TYPEDEF_W_CHAN_T(w_t, data_t, strb_t, user_t)
/// AW channel at slave port
`AXI_TYPEDEF_AW_CHAN_T(slv_aw_t, addr_t, slv_id_t, user_t)
/// B channel at slave port
`AXI_TYPEDEF_B_CHAN_T(slv_b_t, slv_id_t, user_t)
/// AR channel at slave port
`AXI_TYPEDEF_AR_CHAN_T(slv_ar_t, addr_t, slv_id_t, user_t)
/// R channel at slave port
`AXI_TYPEDEF_R_CHAN_T(slv_r_t, data_t, slv_id_t, user_t)
/// AW channel after serializer
`AXI_TYPEDEF_AW_CHAN_T(ser_aw_t, addr_t, ser_id_t, user_t)
/// B channel after serializer
`AXI_TYPEDEF_B_CHAN_T(ser_b_t, ser_id_t, user_t)
/// AR channel after serializer
`AXI_TYPEDEF_AR_CHAN_T(ser_ar_t, addr_t, ser_id_t, user_t)
/// R channel after serializer
`AXI_TYPEDEF_R_CHAN_T(ser_r_t, data_t, ser_id_t, user_t)
/// AXI Requests from serializer
`AXI_TYPEDEF_REQ_T(ser_req_t, ser_aw_t, w_t, ser_ar_t)
/// AXI responses to serializer
`AXI_TYPEDEF_RESP_T(ser_resp_t, ser_b_t, ser_r_t)
/// AW channel after the multiplexer
`AXI_TYPEDEF_AW_CHAN_T(mux_aw_t, addr_t, mux_id_t, user_t)
/// B channel after the multiplexer
`AXI_TYPEDEF_B_CHAN_T(mux_b_t, mux_id_t, user_t)
/// AR channel after the multiplexer
`AXI_TYPEDEF_AR_CHAN_T(mux_ar_t, addr_t, mux_id_t, user_t)
/// R channel after the multiplexer
`AXI_TYPEDEF_R_CHAN_T(mux_r_t, data_t, mux_id_t, user_t)
/// AXI requests from the multiplexer
`AXI_TYPEDEF_REQ_T(mux_req_t, mux_aw_t, w_t, mux_ar_t)
/// AXI responses to the multiplexer
`AXI_TYPEDEF_RESP_T(mux_resp_t, mux_b_t, mux_r_t)
/// AW channel at master port
`AXI_TYPEDEF_AW_CHAN_T(mst_aw_t, addr_t, mst_id_t, user_t)
/// B channel at master port
`AXI_TYPEDEF_B_CHAN_T(mst_b_t, mst_id_t, user_t)
/// AR channel at master port
`AXI_TYPEDEF_AR_CHAN_T(mst_ar_t, addr_t, mst_id_t, user_t)
/// R channel at master port
`AXI_TYPEDEF_R_CHAN_T(mst_r_t, data_t, mst_id_t, user_t)
select_t slv_aw_select, slv_ar_select;
assign slv_aw_select = select_t'(slv_req_i.aw.id % AxiMstPortMaxUniqIds); // TODO: customizable base
assign slv_ar_select = select_t'(slv_req_i.ar.id % AxiMstPortMaxUniqIds);
slv_req_t [AxiMstPortMaxUniqIds-1:0] to_serializer_reqs;
slv_resp_t [AxiMstPortMaxUniqIds-1:0] to_serializer_resps;
axi_demux #(
.AxiIdWidth ( AxiSlvPortIdWidth ),
.aw_chan_t ( slv_aw_t ),
.w_chan_t ( w_t ),
.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 ),
.NoMstPorts ( AxiMstPortMaxUniqIds ),
.MaxTrans ( AxiSlvPortMaxTxns ),
.AxiLookBits ( AxiSlvPortIdWidth ),
.FallThrough ( 1'b1 ),
.SpillAw ( 1'b1 ),
.SpillW ( 1'b0 ),
.SpillB ( 1'b0 ),
.SpillAr ( 1'b1 ),
.SpillR ( 1'b0 )
) i_axi_demux (
.clk_i,
.rst_ni,
.test_i ( 1'b0 ),
.slv_req_i ( slv_req_i ),
.slv_aw_select_i ( slv_aw_select ),
.slv_ar_select_i ( slv_ar_select ),
.slv_resp_o ( slv_resp_o ),
.mst_reqs_o ( to_serializer_reqs ),
.mst_resps_i ( to_serializer_resps )
);
slv_req_t [AxiMstPortMaxUniqIds-1:0] tmp_serializer_reqs;
slv_resp_t [AxiMstPortMaxUniqIds-1:0] tmp_serializer_resps;
ser_req_t [AxiMstPortMaxUniqIds-1:0] from_serializer_reqs;
ser_resp_t [AxiMstPortMaxUniqIds-1:0] from_serializer_resps;
for (genvar i = 0; i < AxiMstPortMaxUniqIds; i++) begin : gen_serializers
axi_serializer #(
.MaxReadTxns ( AxiMstPortMaxTxnsPerId ),
.MaxWriteTxns ( AxiMstPortMaxTxnsPerId ),
.AxiIdWidth ( AxiSlvPortIdWidth ),
.req_t ( slv_req_t ),
.resp_t ( slv_resp_t )
) i_axi_serializer (
.clk_i,
.rst_ni,
.slv_req_i ( to_serializer_reqs[i] ),
.slv_resp_o ( to_serializer_resps[i] ),
.mst_req_o ( tmp_serializer_reqs[i] ),
.mst_resp_i ( tmp_serializer_resps[i] )
);
always_comb begin
`AXI_SET_REQ_STRUCT(from_serializer_reqs[i], tmp_serializer_reqs[i])
// Truncate to ID width 1 as all requests have ID '0.
from_serializer_reqs[i].aw.id = tmp_serializer_reqs[i].aw.id[0];
from_serializer_reqs[i].ar.id = tmp_serializer_reqs[i].ar.id[0];
`AXI_SET_RESP_STRUCT(tmp_serializer_resps[i], from_serializer_resps[i])
// Zero-extend response IDs.
tmp_serializer_resps[i].b.id = {{AxiSlvPortIdWidth-1{1'b0}}, from_serializer_resps[i].b.id};
tmp_serializer_resps[i].r.id = {{AxiSlvPortIdWidth-1{1'b0}}, from_serializer_resps[i].r.id};
end
end
mux_req_t axi_mux_req;
mux_resp_t axi_mux_resp;
axi_mux #(
.SlvAxiIDWidth ( 32'd1 ),
.slv_aw_chan_t ( ser_aw_t ),
.mst_aw_chan_t ( mux_aw_t ),
.w_chan_t ( w_t ),
.slv_b_chan_t ( ser_b_t ),
.mst_b_chan_t ( mux_b_t ),
.slv_ar_chan_t ( ser_ar_t ),
.mst_ar_chan_t ( mux_ar_t ),
.slv_r_chan_t ( ser_r_t ),
.mst_r_chan_t ( mux_r_t ),
.slv_req_t ( ser_req_t ),
.slv_resp_t ( ser_resp_t ),
.mst_req_t ( mux_req_t ),
.mst_resp_t ( mux_resp_t ),
.NoSlvPorts ( AxiMstPortMaxUniqIds ),
.MaxWTrans ( AxiMstPortMaxTxnsPerId ),
.FallThrough ( 1'b0 ),
.SpillAw ( 1'b1 ),
.SpillW ( 1'b0 ),
.SpillB ( 1'b0 ),
.SpillAr ( 1'b1 ),
.SpillR ( 1'b0 )
) i_axi_mux (
.clk_i,
.rst_ni,
.test_i ( 1'b0 ),
.slv_reqs_i ( from_serializer_reqs ),
.slv_resps_o ( from_serializer_resps ),
.mst_req_o ( axi_mux_req ),
.mst_resp_i ( axi_mux_resp )
);
// Shift the ID one down if needed, as mux prepends IDs
if (MuxIdWidth > 32'd1) begin : gen_id_shift
always_comb begin
`AXI_SET_REQ_STRUCT(mst_req_o, axi_mux_req)
mst_req_o.aw.id = mst_id_t'(axi_mux_req.aw.id >> 32'd1);
mst_req_o.ar.id = mst_id_t'(axi_mux_req.ar.id >> 32'd1);
`AXI_SET_RESP_STRUCT(axi_mux_resp, mst_resp_i)
axi_mux_resp.b.id = mux_id_t'(mst_resp_i.b.id << 32'd1);
axi_mux_resp.r.id = mux_id_t'(mst_resp_i.r.id << 32'd1);
end
end else begin : gen_no_id_shift
axi_id_prepend #(
.NoBus ( 32'd1 ),
.AxiIdWidthSlvPort ( MuxIdWidth ),
.AxiIdWidthMstPort ( AxiMstPortIdWidth ),
.slv_aw_chan_t ( mux_aw_t ),
.slv_w_chan_t ( w_t ),
.slv_b_chan_t ( mux_b_t ),
.slv_ar_chan_t ( mux_ar_t ),
.slv_r_chan_t ( mux_r_t ),
.mst_aw_chan_t ( mst_aw_t ),
.mst_w_chan_t ( w_t ),
.mst_b_chan_t ( mst_b_t ),
.mst_ar_chan_t ( mst_ar_t ),
.mst_r_chan_t ( mst_r_t )
) i_axi_id_prepend (
.pre_id_i ( '0 ),
.slv_aw_chans_i ( axi_mux_req.aw ),
.slv_aw_valids_i ( axi_mux_req.aw_valid ),
.slv_aw_readies_o ( axi_mux_resp.aw_ready ),
.slv_w_chans_i ( axi_mux_req.w ),
.slv_w_valids_i ( axi_mux_req.w_valid ),
.slv_w_readies_o ( axi_mux_resp.w_ready ),
.slv_b_chans_o ( axi_mux_resp.b ),
.slv_b_valids_o ( axi_mux_resp.b_valid ),
.slv_b_readies_i ( axi_mux_req.b_ready ),
.slv_ar_chans_i ( axi_mux_req.ar ),
.slv_ar_valids_i ( axi_mux_req.ar_valid ),
.slv_ar_readies_o ( axi_mux_resp.ar_ready ),
.slv_r_chans_o ( axi_mux_resp.r ),
.slv_r_valids_o ( axi_mux_resp.r_valid ),
.slv_r_readies_i ( axi_mux_req.r_ready ),
.mst_aw_chans_o ( mst_req_o.aw ),
.mst_aw_valids_o ( mst_req_o.aw_valid ),
.mst_aw_readies_i ( mst_resp_i.aw_ready ),
.mst_w_chans_o ( mst_req_o.w ),
.mst_w_valids_o ( mst_req_o.w_valid ),
.mst_w_readies_i ( mst_resp_i.w_ready ),
.mst_b_chans_i ( mst_resp_i.b ),
.mst_b_valids_i ( mst_resp_i.b_valid ),
.mst_b_readies_o ( mst_req_o.b_ready ),
.mst_ar_chans_o ( mst_req_o.ar ),
.mst_ar_valids_o ( mst_req_o.ar_valid ),
.mst_ar_readies_i ( mst_resp_i.ar_ready ),
.mst_r_chans_i ( mst_resp_i.r ),
.mst_r_valids_i ( mst_resp_i.r_valid ),
.mst_r_readies_o ( mst_req_o.r_ready )
);
end
// pragma translate_off
`ifndef VERILATOR
initial begin : p_assert
assert(AxiMstPortMaxUniqIds > 32'd0)
else $fatal(1, "AxiMstPortMaxUniqIds has to be > 0.");
assert(2**(AxiMstPortIdWidth) >= AxiMstPortMaxUniqIds)
else $fatal(1, "Not enought Id width on MST port to map all ID's.");
assert(AxiSlvPortIdWidth > 32'd0)
else $fatal(1, "Parameter AxiSlvPortIdWidth has to be larger than 0!");
assert(AxiMstPortIdWidth)
else $fatal(1, "Parameter AxiMstPortIdWidth has to be larger than 0!");
assert(AxiMstPortIdWidth <= AxiSlvPortIdWidth)
else $fatal(1, "Downsize implies that AxiMstPortIdWidth <= AxiSlvPortIdWidth!");
assert($bits(slv_req_i.aw.addr) == $bits(mst_req_o.aw.addr))
else $fatal(1, "AXI AW address widths are not equal!");
assert($bits(slv_req_i.w.data) == $bits(mst_req_o.w.data))
else $fatal(1, "AXI W data widths are not equal!");
assert($bits(slv_req_i.ar.addr) == $bits(mst_req_o.ar.addr))
else $fatal(1, "AXI AR address widths are not equal!");
assert($bits(slv_resp_o.r.data) == $bits(mst_resp_i.r.data))
else $fatal(1, "AXI R data widths are not equal!");
end
`endif
// pragma translate_on
endmodule
/// Interface variant of [`axi_id_serialize`](module.axi_id_serialize).
///
/// See the documentation of the main module for the definition of ports and parameters.
module axi_id_serialize_intf #(
parameter int unsigned AXI_SLV_PORT_ID_WIDTH = 32'd0,
parameter int unsigned AXI_SLV_PORT_MAX_TXNS = 32'd0,
parameter int unsigned AXI_MST_PORT_ID_WIDTH = 32'd0,
parameter int unsigned AXI_MST_PORT_MAX_UNIQ_IDS = 32'd0,
parameter int unsigned AXI_MST_PORT_MAX_TXNS_PER_ID = 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.Slave slv,
AXI_BUS.Master mst
);
typedef logic [AXI_SLV_PORT_ID_WIDTH-1:0] slv_id_t;
typedef logic [AXI_MST_PORT_ID_WIDTH-1:0] mst_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(slv_aw_t, addr_t, slv_id_t, user_t)
`AXI_TYPEDEF_W_CHAN_T(w_t, data_t, strb_t, user_t)
`AXI_TYPEDEF_B_CHAN_T(slv_b_t, slv_id_t, user_t)
`AXI_TYPEDEF_AR_CHAN_T(slv_ar_t, addr_t, slv_id_t, user_t)
`AXI_TYPEDEF_R_CHAN_T(slv_r_t, data_t, slv_id_t, user_t)
`AXI_TYPEDEF_REQ_T(slv_req_t, slv_aw_t, w_t, slv_ar_t)
`AXI_TYPEDEF_RESP_T(slv_resp_t, slv_b_t, slv_r_t)
`AXI_TYPEDEF_AW_CHAN_T(mst_aw_t, addr_t, mst_id_t, user_t)
`AXI_TYPEDEF_B_CHAN_T(mst_b_t, mst_id_t, user_t)
`AXI_TYPEDEF_AR_CHAN_T(mst_ar_t, addr_t, mst_id_t, user_t)
`AXI_TYPEDEF_R_CHAN_T(mst_r_t, data_t, mst_id_t, user_t)
`AXI_TYPEDEF_REQ_T(mst_req_t, mst_aw_t, w_t, mst_ar_t)
`AXI_TYPEDEF_RESP_T(mst_resp_t, mst_b_t, mst_r_t)
slv_req_t slv_req;
slv_resp_t slv_resp;
mst_req_t mst_req;
mst_resp_t 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_id_serialize #(
.AxiSlvPortIdWidth ( AXI_SLV_PORT_ID_WIDTH ),
.AxiSlvPortMaxTxns ( AXI_SLV_PORT_MAX_TXNS ),
.AxiMstPortIdWidth ( AXI_MST_PORT_ID_WIDTH ),
.AxiMstPortMaxUniqIds ( AXI_MST_PORT_MAX_UNIQ_IDS ),
.AxiMstPortMaxTxnsPerId ( AXI_MST_PORT_MAX_TXNS_PER_ID ),
.AxiAddrWidth ( AXI_ADDR_WIDTH ),
.AxiDataWidth ( AXI_DATA_WIDTH ),
.AxiUserWidth ( AXI_USER_WIDTH ),
.slv_req_t ( slv_req_t ),
.slv_resp_t ( slv_resp_t ),
.mst_req_t ( mst_req_t ),
.mst_resp_t ( mst_resp_t )
) i_axi_id_serialize (
.clk_i,
.rst_ni,
.slv_req_i ( slv_req ),
.slv_resp_o ( slv_resp ),
.mst_req_o ( mst_req ),
.mst_resp_i ( mst_resp )
);
// pragma translate_off
`ifndef VERILATOR
initial begin
assert (slv.AXI_ID_WIDTH == AXI_SLV_PORT_ID_WIDTH);
assert (slv.AXI_ADDR_WIDTH == AXI_ADDR_WIDTH);
assert (slv.AXI_DATA_WIDTH == AXI_DATA_WIDTH);
assert (slv.AXI_USER_WIDTH == AXI_USER_WIDTH);
assert (mst.AXI_ID_WIDTH == AXI_MST_PORT_ID_WIDTH);
assert (mst.AXI_ADDR_WIDTH == AXI_ADDR_WIDTH);
assert (mst.AXI_DATA_WIDTH == AXI_DATA_WIDTH);
assert (mst.AXI_USER_WIDTH == AXI_USER_WIDTH);
end
`endif
// pragma translate_on
endmodule

549
vendor/pulp-platform/axi/src/axi_intf.sv vendored Normal file
View file

@ -0,0 +1,549 @@
// Copyright (c) 2014-2018 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:
// - Fabian Schuiki <fschuiki@iis.ee.ethz.ch>
// - Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>
// - Andreas Kurth <akurth@iis.ee.ethz.ch>
/// An AXI4 interface.
interface AXI_BUS #(
parameter int unsigned AXI_ADDR_WIDTH = 0,
parameter int unsigned AXI_DATA_WIDTH = 0,
parameter int unsigned AXI_ID_WIDTH = 0,
parameter int unsigned AXI_USER_WIDTH = 0
);
localparam int unsigned AXI_STRB_WIDTH = AXI_DATA_WIDTH / 8;
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_STRB_WIDTH-1:0] strb_t;
typedef logic [AXI_USER_WIDTH-1:0] user_t;
id_t aw_id;
addr_t aw_addr;
axi_pkg::len_t aw_len;
axi_pkg::size_t aw_size;
axi_pkg::burst_t aw_burst;
logic aw_lock;
axi_pkg::cache_t aw_cache;
axi_pkg::prot_t aw_prot;
axi_pkg::qos_t aw_qos;
axi_pkg::region_t aw_region;
axi_pkg::atop_t aw_atop;
user_t aw_user;
logic aw_valid;
logic aw_ready;
data_t w_data;
strb_t w_strb;
logic w_last;
user_t w_user;
logic w_valid;
logic w_ready;
id_t b_id;
axi_pkg::resp_t b_resp;
user_t b_user;
logic b_valid;
logic b_ready;
id_t ar_id;
addr_t ar_addr;
axi_pkg::len_t ar_len;
axi_pkg::size_t ar_size;
axi_pkg::burst_t ar_burst;
logic ar_lock;
axi_pkg::cache_t ar_cache;
axi_pkg::prot_t ar_prot;
axi_pkg::qos_t ar_qos;
axi_pkg::region_t ar_region;
user_t ar_user;
logic ar_valid;
logic ar_ready;
id_t r_id;
data_t r_data;
axi_pkg::resp_t r_resp;
logic r_last;
user_t r_user;
logic r_valid;
logic r_ready;
modport Master (
output 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, input aw_ready,
output w_data, w_strb, w_last, w_user, w_valid, input w_ready,
input b_id, b_resp, b_user, b_valid, output b_ready,
output ar_id, ar_addr, ar_len, ar_size, ar_burst, ar_lock, ar_cache, ar_prot, ar_qos, ar_region, ar_user, ar_valid, input ar_ready,
input r_id, r_data, r_resp, r_last, r_user, r_valid, output r_ready
);
modport Slave (
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, output aw_ready,
input w_data, w_strb, w_last, w_user, w_valid, output w_ready,
output b_id, b_resp, b_user, b_valid, input b_ready,
input ar_id, ar_addr, ar_len, ar_size, ar_burst, ar_lock, ar_cache, ar_prot, ar_qos, ar_region, ar_user, ar_valid, output ar_ready,
output r_id, r_data, r_resp, r_last, r_user, r_valid, input r_ready
);
endinterface
/// A clocked AXI4 interface for use in design verification.
interface AXI_BUS_DV #(
parameter int unsigned AXI_ADDR_WIDTH = 0,
parameter int unsigned AXI_DATA_WIDTH = 0,
parameter int unsigned AXI_ID_WIDTH = 0,
parameter int unsigned AXI_USER_WIDTH = 0
)(
input logic clk_i
);
localparam int unsigned AXI_STRB_WIDTH = AXI_DATA_WIDTH / 8;
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_STRB_WIDTH-1:0] strb_t;
typedef logic [AXI_USER_WIDTH-1:0] user_t;
id_t aw_id;
addr_t aw_addr;
axi_pkg::len_t aw_len;
axi_pkg::size_t aw_size;
axi_pkg::burst_t aw_burst;
logic aw_lock;
axi_pkg::cache_t aw_cache;
axi_pkg::prot_t aw_prot;
axi_pkg::qos_t aw_qos;
axi_pkg::region_t aw_region;
axi_pkg::atop_t aw_atop;
user_t aw_user;
logic aw_valid;
logic aw_ready;
data_t w_data;
strb_t w_strb;
logic w_last;
user_t w_user;
logic w_valid;
logic w_ready;
id_t b_id;
axi_pkg::resp_t b_resp;
user_t b_user;
logic b_valid;
logic b_ready;
id_t ar_id;
addr_t ar_addr;
axi_pkg::len_t ar_len;
axi_pkg::size_t ar_size;
axi_pkg::burst_t ar_burst;
logic ar_lock;
axi_pkg::cache_t ar_cache;
axi_pkg::prot_t ar_prot;
axi_pkg::qos_t ar_qos;
axi_pkg::region_t ar_region;
user_t ar_user;
logic ar_valid;
logic ar_ready;
id_t r_id;
data_t r_data;
axi_pkg::resp_t r_resp;
logic r_last;
user_t r_user;
logic r_valid;
logic r_ready;
modport Master (
output 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, input aw_ready,
output w_data, w_strb, w_last, w_user, w_valid, input w_ready,
input b_id, b_resp, b_user, b_valid, output b_ready,
output ar_id, ar_addr, ar_len, ar_size, ar_burst, ar_lock, ar_cache, ar_prot, ar_qos, ar_region, ar_user, ar_valid, input ar_ready,
input r_id, r_data, r_resp, r_last, r_user, r_valid, output r_ready
);
modport Slave (
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, output aw_ready,
input w_data, w_strb, w_last, w_user, w_valid, output w_ready,
output b_id, b_resp, b_user, b_valid, input b_ready,
input ar_id, ar_addr, ar_len, ar_size, ar_burst, ar_lock, ar_cache, ar_prot, ar_qos, ar_region, ar_user, ar_valid, output ar_ready,
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
);
// pragma translate_off
`ifndef VERILATOR
// Single-Channel Assertions: Signals including valid must not change between valid and handshake.
// AW
assert property (@(posedge clk_i) (aw_valid && !aw_ready |=> $stable(aw_id)));
assert property (@(posedge clk_i) (aw_valid && !aw_ready |=> $stable(aw_addr)));
assert property (@(posedge clk_i) (aw_valid && !aw_ready |=> $stable(aw_len)));
assert property (@(posedge clk_i) (aw_valid && !aw_ready |=> $stable(aw_size)));
assert property (@(posedge clk_i) (aw_valid && !aw_ready |=> $stable(aw_burst)));
assert property (@(posedge clk_i) (aw_valid && !aw_ready |=> $stable(aw_lock)));
assert property (@(posedge clk_i) (aw_valid && !aw_ready |=> $stable(aw_cache)));
assert property (@(posedge clk_i) (aw_valid && !aw_ready |=> $stable(aw_prot)));
assert property (@(posedge clk_i) (aw_valid && !aw_ready |=> $stable(aw_qos)));
assert property (@(posedge clk_i) (aw_valid && !aw_ready |=> $stable(aw_region)));
assert property (@(posedge clk_i) (aw_valid && !aw_ready |=> $stable(aw_atop)));
assert property (@(posedge clk_i) (aw_valid && !aw_ready |=> $stable(aw_user)));
assert property (@(posedge clk_i) (aw_valid && !aw_ready |=> aw_valid));
// W
assert property (@(posedge clk_i) ( w_valid && ! w_ready |=> $stable(w_data)));
assert property (@(posedge clk_i) ( w_valid && ! w_ready |=> $stable(w_strb)));
assert property (@(posedge clk_i) ( w_valid && ! w_ready |=> $stable(w_last)));
assert property (@(posedge clk_i) ( w_valid && ! w_ready |=> $stable(w_user)));
assert property (@(posedge clk_i) ( w_valid && ! w_ready |=> w_valid));
// B
assert property (@(posedge clk_i) ( b_valid && ! b_ready |=> $stable(b_id)));
assert property (@(posedge clk_i) ( b_valid && ! b_ready |=> $stable(b_resp)));
assert property (@(posedge clk_i) ( b_valid && ! b_ready |=> $stable(b_user)));
assert property (@(posedge clk_i) ( b_valid && ! b_ready |=> b_valid));
// AR
assert property (@(posedge clk_i) (ar_valid && !ar_ready |=> $stable(ar_id)));
assert property (@(posedge clk_i) (ar_valid && !ar_ready |=> $stable(ar_addr)));
assert property (@(posedge clk_i) (ar_valid && !ar_ready |=> $stable(ar_len)));
assert property (@(posedge clk_i) (ar_valid && !ar_ready |=> $stable(ar_size)));
assert property (@(posedge clk_i) (ar_valid && !ar_ready |=> $stable(ar_burst)));
assert property (@(posedge clk_i) (ar_valid && !ar_ready |=> $stable(ar_lock)));
assert property (@(posedge clk_i) (ar_valid && !ar_ready |=> $stable(ar_cache)));
assert property (@(posedge clk_i) (ar_valid && !ar_ready |=> $stable(ar_prot)));
assert property (@(posedge clk_i) (ar_valid && !ar_ready |=> $stable(ar_qos)));
assert property (@(posedge clk_i) (ar_valid && !ar_ready |=> $stable(ar_region)));
assert property (@(posedge clk_i) (ar_valid && !ar_ready |=> $stable(ar_user)));
assert property (@(posedge clk_i) (ar_valid && !ar_ready |=> ar_valid));
// R
assert property (@(posedge clk_i) ( r_valid && ! r_ready |=> $stable(r_id)));
assert property (@(posedge clk_i) ( r_valid && ! r_ready |=> $stable(r_data)));
assert property (@(posedge clk_i) ( r_valid && ! r_ready |=> $stable(r_resp)));
assert property (@(posedge clk_i) ( r_valid && ! r_ready |=> $stable(r_last)));
assert property (@(posedge clk_i) ( r_valid && ! r_ready |=> $stable(r_user)));
assert property (@(posedge clk_i) ( r_valid && ! r_ready |=> r_valid));
`endif
// pragma translate_on
endinterface
/// An asynchronous AXI4 interface.
interface AXI_BUS_ASYNC
#(
parameter int unsigned AXI_ADDR_WIDTH = 0,
parameter int unsigned AXI_DATA_WIDTH = 0,
parameter int unsigned AXI_ID_WIDTH = 0,
parameter int unsigned AXI_USER_WIDTH = 0,
parameter int unsigned BUFFER_WIDTH = 0
);
localparam int unsigned AXI_STRB_WIDTH = AXI_DATA_WIDTH / 8;
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_STRB_WIDTH-1:0] strb_t;
typedef logic [AXI_USER_WIDTH-1:0] user_t;
typedef logic [BUFFER_WIDTH-1:0] buffer_t;
id_t aw_id;
addr_t aw_addr;
axi_pkg::len_t aw_len;
axi_pkg::size_t aw_size;
axi_pkg::burst_t aw_burst;
logic aw_lock;
axi_pkg::cache_t aw_cache;
axi_pkg::prot_t aw_prot;
axi_pkg::qos_t aw_qos;
axi_pkg::region_t aw_region;
axi_pkg::atop_t aw_atop;
user_t aw_user;
buffer_t aw_writetoken;
buffer_t aw_readpointer;
data_t w_data;
strb_t w_strb;
logic w_last;
user_t w_user;
buffer_t w_writetoken;
buffer_t w_readpointer;
id_t b_id;
axi_pkg::resp_t b_resp;
user_t b_user;
buffer_t b_writetoken;
buffer_t b_readpointer;
id_t ar_id;
addr_t ar_addr;
axi_pkg::len_t ar_len;
axi_pkg::size_t ar_size;
axi_pkg::burst_t ar_burst;
logic ar_lock;
axi_pkg::cache_t ar_cache;
axi_pkg::prot_t ar_prot;
axi_pkg::qos_t ar_qos;
axi_pkg::region_t ar_region;
user_t ar_user;
buffer_t ar_writetoken;
buffer_t ar_readpointer;
id_t r_id;
data_t r_data;
axi_pkg::resp_t r_resp;
logic r_last;
user_t r_user;
buffer_t r_writetoken;
buffer_t r_readpointer;
modport Master (
output 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_writetoken, input aw_readpointer,
output w_data, w_strb, w_last, w_user, w_writetoken, input w_readpointer,
input b_id, b_resp, b_user, b_writetoken, output b_readpointer,
output ar_id, ar_addr, ar_len, ar_size, ar_burst, ar_lock, ar_cache, ar_prot, ar_qos, ar_region, ar_user, ar_writetoken, input ar_readpointer,
input r_id, r_data, r_resp, r_last, r_user, r_writetoken, output r_readpointer
);
modport Slave (
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_writetoken, output aw_readpointer,
input w_data, w_strb, w_last, w_user, w_writetoken, output w_readpointer,
output b_id, b_resp, b_user, b_writetoken, input b_readpointer,
input ar_id, ar_addr, ar_len, ar_size, ar_burst, ar_lock, ar_cache, ar_prot, ar_qos, ar_region, ar_user, ar_writetoken, output ar_readpointer,
output r_id, r_data, r_resp, r_last, r_user, r_writetoken, input r_readpointer
);
endinterface
`include "axi/typedef.svh"
/// An asynchronous AXI4 interface for Gray CDCs.
interface AXI_BUS_ASYNC_GRAY #(
parameter int unsigned AXI_ADDR_WIDTH = 0,
parameter int unsigned AXI_DATA_WIDTH = 0,
parameter int unsigned AXI_ID_WIDTH = 0,
parameter int unsigned AXI_USER_WIDTH = 0,
parameter int unsigned LOG_DEPTH = 0
);
localparam int unsigned AXI_STRB_WIDTH = AXI_DATA_WIDTH / 8;
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_STRB_WIDTH-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)
aw_chan_t [2**LOG_DEPTH-1:0] aw_data;
w_chan_t [2**LOG_DEPTH-1:0] w_data;
b_chan_t [2**LOG_DEPTH-1:0] b_data;
ar_chan_t [2**LOG_DEPTH-1:0] ar_data;
r_chan_t [2**LOG_DEPTH-1:0] r_data;
logic [LOG_DEPTH:0] aw_wptr, aw_rptr,
w_wptr, w_rptr,
b_wptr, b_rptr,
ar_wptr, ar_rptr,
r_wptr, r_rptr;
modport Master (
output aw_data, aw_wptr, input aw_rptr,
output w_data, w_wptr, input w_rptr,
input b_data, b_wptr, output b_rptr,
output ar_data, ar_wptr, input ar_rptr,
input r_data, r_wptr, output r_rptr);
modport Slave (
input aw_data, aw_wptr, output aw_rptr,
input w_data, w_wptr, output w_rptr,
output b_data, b_wptr, input b_rptr,
input ar_data, ar_wptr, output ar_rptr,
output r_data, r_wptr, input r_rptr);
endinterface
/// An AXI4-Lite interface.
interface AXI_LITE #(
parameter int unsigned AXI_ADDR_WIDTH = 0,
parameter int unsigned AXI_DATA_WIDTH = 0
);
localparam int unsigned AXI_STRB_WIDTH = AXI_DATA_WIDTH / 8;
typedef logic [AXI_ADDR_WIDTH-1:0] addr_t;
typedef logic [AXI_DATA_WIDTH-1:0] data_t;
typedef logic [AXI_STRB_WIDTH-1:0] strb_t;
// AW channel
addr_t aw_addr;
axi_pkg::prot_t aw_prot;
logic aw_valid;
logic aw_ready;
data_t w_data;
strb_t w_strb;
logic w_valid;
logic w_ready;
axi_pkg::resp_t b_resp;
logic b_valid;
logic b_ready;
addr_t ar_addr;
axi_pkg::prot_t ar_prot;
logic ar_valid;
logic ar_ready;
data_t r_data;
axi_pkg::resp_t r_resp;
logic r_valid;
logic r_ready;
modport Master (
output aw_addr, aw_prot, aw_valid, input aw_ready,
output w_data, w_strb, w_valid, input w_ready,
input b_resp, b_valid, output b_ready,
output ar_addr, ar_prot, ar_valid, input ar_ready,
input r_data, r_resp, r_valid, output r_ready
);
modport Slave (
input aw_addr, aw_prot, aw_valid, output aw_ready,
input w_data, w_strb, w_valid, output w_ready,
output b_resp, b_valid, input b_ready,
input ar_addr, ar_prot, ar_valid, output ar_ready,
output r_data, r_resp, r_valid, input r_ready
);
endinterface
/// A clocked AXI4-Lite interface for use in design verification.
interface AXI_LITE_DV #(
parameter int unsigned AXI_ADDR_WIDTH = 0,
parameter int unsigned AXI_DATA_WIDTH = 0
)(
input logic clk_i
);
localparam AXI_STRB_WIDTH = AXI_DATA_WIDTH / 8;
typedef logic [AXI_ADDR_WIDTH-1:0] addr_t;
typedef logic [AXI_DATA_WIDTH-1:0] data_t;
typedef logic [AXI_STRB_WIDTH-1:0] strb_t;
// AW channel
addr_t aw_addr;
axi_pkg::prot_t aw_prot;
logic aw_valid;
logic aw_ready;
data_t w_data;
strb_t w_strb;
logic w_valid;
logic w_ready;
axi_pkg::resp_t b_resp;
logic b_valid;
logic b_ready;
addr_t ar_addr;
axi_pkg::prot_t ar_prot;
logic ar_valid;
logic ar_ready;
data_t r_data;
axi_pkg::resp_t r_resp;
logic r_valid;
logic r_ready;
modport Master (
output aw_addr, aw_prot, aw_valid, input aw_ready,
output w_data, w_strb, w_valid, input w_ready,
input b_resp, b_valid, output b_ready,
output ar_addr, ar_prot, ar_valid, input ar_ready,
input r_data, r_resp, r_valid, output r_ready
);
modport Slave (
input aw_addr, aw_prot, aw_valid, output aw_ready,
input w_data, w_strb, w_valid, output w_ready,
output b_resp, b_valid, input b_ready,
input ar_addr, ar_prot, ar_valid, output ar_ready,
output r_data, r_resp, r_valid, input r_ready
);
endinterface
/// An asynchronous AXI4-Lite interface for Gray CDCs.
interface AXI_LITE_ASYNC_GRAY #(
parameter int unsigned AXI_ADDR_WIDTH = 0,
parameter int unsigned AXI_DATA_WIDTH = 0,
parameter int unsigned LOG_DEPTH = 0
);
localparam int unsigned AXI_STRB_WIDTH = AXI_DATA_WIDTH / 8;
typedef logic [AXI_ADDR_WIDTH-1:0] addr_t;
typedef logic [AXI_DATA_WIDTH-1:0] data_t;
typedef logic [AXI_STRB_WIDTH-1:0] strb_t;
`AXI_LITE_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t)
`AXI_LITE_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t)
`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)
aw_chan_t [2**LOG_DEPTH-1:0] aw_data;
w_chan_t [2**LOG_DEPTH-1:0] w_data;
b_chan_t [2**LOG_DEPTH-1:0] b_data;
ar_chan_t [2**LOG_DEPTH-1:0] ar_data;
r_chan_t [2**LOG_DEPTH-1:0] r_data;
logic [LOG_DEPTH:0] aw_wptr, aw_rptr,
w_wptr, w_rptr,
b_wptr, b_rptr,
ar_wptr, ar_rptr,
r_wptr, r_rptr;
modport Master (
output aw_data, aw_wptr, input aw_rptr,
output w_data, w_wptr, input w_rptr,
input b_data, b_wptr, output b_rptr,
output ar_data, ar_wptr, input ar_rptr,
input r_data, r_wptr, output r_rptr);
modport Slave (
input aw_data, aw_wptr, output aw_rptr,
input w_data, w_wptr, output w_rptr,
output b_data, b_wptr, input b_rptr,
input ar_data, ar_wptr, output ar_rptr,
output r_data, r_wptr, input r_rptr);
endinterface

View file

@ -0,0 +1,346 @@
// 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:
// - Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>
// - Andreas Kurth <akurth@iis.ee.ethz.ch>
// 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 "common_cells/registers.svh"
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
) (
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
);
// 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;
typedef enum logic [1:0] {
Normal,
Hold,
Drain,
Isolate
} isolate_state_e;
isolate_state_e state_aw_d, state_aw_q, state_ar_d, state_ar_q;
logic update_aw_state, update_ar_state;
cnt_t pending_aw_d, pending_aw_q;
logic update_aw_cnt;
cnt_t pending_w_d, pending_w_q;
logic update_w_cnt, connect_w;
cnt_t pending_ar_d, pending_ar_q;
logic update_ar_cnt;
`FFLARN(pending_aw_q, pending_aw_d, update_aw_cnt, '0, clk_i, rst_ni)
`FFLARN(pending_w_q, pending_w_d, update_w_cnt, '0, clk_i, rst_ni)
`FFLARN(pending_ar_q, pending_ar_d, update_ar_cnt, '0, clk_i, rst_ni)
`FFLARN(state_aw_q, state_aw_d, update_aw_state, Isolate, clk_i, rst_ni)
`FFLARN(state_ar_q, state_ar_d, update_ar_state, Isolate, clk_i, rst_ni)
// Update counters.
always_comb begin
pending_aw_d = pending_aw_q;
update_aw_cnt = 1'b0;
pending_w_d = pending_w_q;
update_w_cnt = 1'b0;
connect_w = 1'b0;
pending_ar_d = pending_ar_q;
update_ar_cnt = 1'b0;
// write counters
if (mst_req_o.aw_valid && (state_aw_q == Normal)) begin
pending_aw_d++;
update_aw_cnt = 1'b1;
pending_w_d++;
update_w_cnt = 1'b1;
connect_w = 1'b1;
if (mst_req_o.aw.atop[axi_pkg::ATOP_R_RESP]) begin
pending_ar_d++; // handle atomic with read response by injecting a count in AR
update_ar_cnt = 1'b1;
end
end
if (mst_req_o.w_valid && mst_resp_i.w_ready && mst_req_o.w.last) begin
pending_w_d--;
update_w_cnt = 1'b1;
end
if (mst_resp_i.b_valid && mst_req_o.b_ready) begin
pending_aw_d--;
update_aw_cnt = 1'b1;
end
// read counters
if (mst_req_o.ar_valid && (state_ar_q == Normal)) begin
pending_ar_d++;
update_ar_cnt = 1'b1;
end
if (mst_resp_i.r_valid && mst_req_o.r_ready && mst_resp_i.r.last) begin
pending_ar_d--;
update_ar_cnt = 1'b1;
end
end
// Perform isolation.
always_comb begin
// Default assignments
state_aw_d = state_aw_q;
update_aw_state = 1'b0;
state_ar_d = state_ar_q;
update_ar_state = 1'b0;
// Connect channel per default
mst_req_o = slv_req_i;
slv_resp_o = mst_resp_i;
/////////////////////////////////////////////////////////////
// Write transaction
/////////////////////////////////////////////////////////////
unique case (state_aw_q)
Normal: begin // Normal operation
// Cut valid handshake if a counter capacity is reached. It has to check AR counter in case
// of atomics. Counters are wide enough to account for injected count in the read response
// counter.
if (pending_aw_q >= cnt_t'(NumPending) || pending_ar_q >= cnt_t'(2*NumPending)
|| (pending_w_q >= cnt_t'(NumPending))) begin
mst_req_o.aw_valid = 1'b0;
slv_resp_o.aw_ready = 1'b0;
if (isolate_i) begin
state_aw_d = Drain;
update_aw_state = 1'b1;
end
end else begin
// here the AW handshake is connected normally
if (slv_req_i.aw_valid && !mst_resp_i.aw_ready) begin
state_aw_d = Hold;
update_aw_state = 1'b1;
end else begin
if (isolate_i) begin
state_aw_d = Drain;
update_aw_state = 1'b1;
end
end
end
end
Hold: begin // Hold the valid signal on 1'b1 if there was no transfer
mst_req_o.aw_valid = 1'b1;
// aw_ready normal connected
if (mst_resp_i.aw_ready) begin
update_aw_state = 1'b1;
state_aw_d = isolate_i ? Drain : Normal;
end
end
Drain: begin // cut the AW channel until counter is zero
mst_req_o.aw = '0;
mst_req_o.aw_valid = 1'b0;
slv_resp_o.aw_ready = 1'b0;
if (pending_aw_q == '0) begin
state_aw_d = Isolate;
update_aw_state = 1'b1;
end
end
Isolate: begin // Cut the signals to the outputs
mst_req_o.aw = '0;
mst_req_o.aw_valid = 1'b0;
slv_resp_o.aw_ready = 1'b0;
slv_resp_o.b = '0;
slv_resp_o.b_valid = 1'b0;
mst_req_o.b_ready = 1'b0;
if (!isolate_i) begin
state_aw_d = Normal;
update_aw_state = 1'b1;
end
end
default: /*do nothing*/;
endcase
// W channel is cut as long the counter is zero and not explicitly unlocked through an AW.
if ((pending_w_q == '0) && !connect_w ) begin
mst_req_o.w = '0;
mst_req_o.w_valid = 1'b0;
slv_resp_o.w_ready = 1'b0;
end
/////////////////////////////////////////////////////////////
// Read transaction
/////////////////////////////////////////////////////////////
unique case (state_ar_q)
Normal: begin
// cut handshake if counter capacity is reached
if (pending_ar_q >= NumPending) begin
mst_req_o.ar_valid = 1'b0;
slv_resp_o.ar_ready = 1'b0;
if (isolate_i) begin
state_ar_d = Drain;
update_ar_state = 1'b1;
end
end else begin
// here the AR handshake is connected normally
if (slv_req_i.ar_valid && !mst_resp_i.ar_ready) begin
state_ar_d = Hold;
update_ar_state = 1'b1;
end else begin
if (isolate_i) begin
state_ar_d = Drain;
update_ar_state = 1'b1;
end
end
end
end
Hold: begin // Hold the valid signal on 1'b1 if there was no transfer
mst_req_o.ar_valid = 1'b1;
// ar_ready normal connected
if (mst_resp_i.ar_ready) begin
update_ar_state = 1'b1;
state_ar_d = isolate_i ? Drain : Normal;
end
end
Drain: begin
mst_req_o.ar = '0;
mst_req_o.ar_valid = 1'b0;
slv_resp_o.ar_ready = 1'b0;
if (pending_ar_q == '0) begin
state_ar_d = Isolate;
update_ar_state = 1'b1;
end
end
Isolate: begin
mst_req_o.ar = '0;
mst_req_o.ar_valid = 1'b0;
slv_resp_o.ar_ready = 1'b0;
slv_resp_o.r = '0;
slv_resp_o.r_valid = 1'b0;
mst_req_o.r_ready = 1'b0;
if (!isolate_i) begin
state_ar_d = Normal;
update_ar_state = 1'b1;
end
end
default: /*do nothing*/;
endcase
end
// the isolated output signal
assign isolated_o = (state_aw_q == Isolate && state_ar_q == Isolate);
// pragma translate_off
`ifndef VERILATOR
initial begin
assume (NumPending > 0) else $fatal(1, "At least one pending transaction required.");
end
default disable iff (!rst_ni);
aw_overflow: assert property (@(posedge clk_i)
(pending_aw_q == '1) |=> (pending_aw_q != '0)) else
$fatal(1, "pending_aw_q overflowed");
ar_overflow: assert property (@(posedge clk_i)
(pending_ar_q == '1) |=> (pending_ar_q != '0)) else
$fatal(1, "pending_ar_q overflowed");
aw_underflow: assert property (@(posedge clk_i)
(pending_aw_q == '0) |=> (pending_aw_q != '1)) else
$fatal(1, "pending_aw_q underflowed");
ar_underflow: assert property (@(posedge clk_i)
(pending_ar_q == '0) |=> (pending_ar_q != '1)) else
$fatal(1, "pending_ar_q underflowed");
`endif
// pragma translate_on
endmodule
`include "axi/typedef.svh"
`include "axi/assign.svh"
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
) (
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
);
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(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_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_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
) 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
);
// pragma translate_off
`ifndef VERILATOR
initial begin
assume (AXI_ID_WIDTH > 0) else $fatal(1, "AXI_ID_WIDTH has to be > 0.");
assume (AXI_ADDR_WIDTH > 0) else $fatal(1, "AXI_ADDR_WIDTH has to be > 0.");
assume (AXI_DATA_WIDTH > 0) else $fatal(1, "AXI_DATA_WIDTH has to be > 0.");
assume (AXI_USER_WIDTH > 0) else $fatal(1, "AXI_USER_WIDTH has to be > 0.");
end
`endif
// pragma translate_on
endmodule

View file

@ -0,0 +1,350 @@
// Copyright (c) 2014-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.
//
// Andreas Kurth <akurth@iis.ee.ethz.ch>
// Florian Zaruba <zarubaf@iis.ee.ethz.ch>
// Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>
`include "axi/typedef.svh"
/// Convert between any two AXI ID widths.
///
/// Any combination of slave and master port ID width is valid. When the master port ID width is
/// larger than or equal to the slave port ID width, slave port IDs are simply prepended with zeros
/// to the width of master port IDs. For *reducing* the ID width, i.e., when the master port ID
/// width is smaller than the slave port ID width, there are two options.
///
/// ## Options for reducing the ID width
///
/// The two options for reducing ID widths differ in the maximum number of different IDs that can be
/// in flight at the slave port of this module, given in the `AxiSlvPortMaxUniqIds` parameter.
///
/// ### Fewer unique slave port IDs than master port IDs
///
/// If `AxiSlvPortMaxUniqIds <= 2**AxiMstPortIdWidth`, there are fewer unique slave port IDs than
/// master port IDs. Therefore, IDs that are different at the slave port of this module can remain
/// different at the reduced-ID-width master port and thus remain *independently reorderable*.
/// Since the IDs are master port are nonetheless shorter than at the slave port, they need to be
/// *remapped*. An instance of [`axi_id_remap`](module.axi_id_remap) handles this case.
///
/// ### More unique slave port IDs than master port IDs
///
/// If `AxiSlvPortMaxUniqIds > 2**AxiMstPortIdWidth`, there are more unique slave port IDs than
/// master port IDs. Therefore, some IDs that are different at the slave port need to be assigned
/// to the same master port ID and thus become ordered with respect to each other. An instance of
/// [`axi_id_serialize`](module.axi_id_serialize) handles this case.
module axi_iw_converter #(
/// ID width of the AXI4+ATOP slave port
parameter int unsigned AxiSlvPortIdWidth = 32'd0,
/// ID width of the AXI4+ATOP master port
parameter int unsigned AxiMstPortIdWidth = 32'd0,
/// Maximum number of different IDs that can be in flight at the 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,
/// Address width of both AXI4+ATOP ports
parameter int unsigned AxiAddrWidth = 32'd0,
/// Data width of both AXI4+ATOP ports
parameter int unsigned AxiDataWidth = 32'd0,
/// User signal width of both AXI4+ATOP ports
parameter int unsigned AxiUserWidth = 32'd0,
/// Request struct type of the AXI4+ATOP slave port
parameter type slv_req_t = logic,
/// Response struct type of the AXI4+ATOP slave port
parameter type slv_resp_t = logic,
/// Request struct type of the AXI4+ATOP master port
parameter type mst_req_t = logic,
/// Response struct type of the AXI4+ATOP master port
parameter type mst_resp_t = logic
) (
/// Rising-edge clock of both ports
input logic clk_i,
/// Asynchronous reset, active low
input logic rst_ni,
/// Slave port request
input slv_req_t slv_req_i,
/// Slave port response
output slv_resp_t slv_resp_o,
/// Master port request
output mst_req_t mst_req_o,
/// Master port response
input mst_resp_t mst_resp_i
);
typedef logic [AxiAddrWidth-1:0] addr_t;
typedef logic [AxiDataWidth-1:0] data_t;
typedef logic [AxiSlvPortIdWidth-1:0] slv_id_t;
typedef logic [AxiMstPortIdWidth-1:0] mst_id_t;
typedef logic [AxiDataWidth/8-1:0] strb_t;
typedef logic [AxiUserWidth-1:0] user_t;
`AXI_TYPEDEF_AW_CHAN_T(slv_aw_t, addr_t, slv_id_t, user_t)
`AXI_TYPEDEF_AW_CHAN_T(mst_aw_t, addr_t, mst_id_t, user_t)
`AXI_TYPEDEF_W_CHAN_T(w_t, data_t, strb_t, user_t)
`AXI_TYPEDEF_B_CHAN_T(slv_b_t, slv_id_t, user_t)
`AXI_TYPEDEF_B_CHAN_T(mst_b_t, mst_id_t, user_t)
`AXI_TYPEDEF_AR_CHAN_T(slv_ar_t, addr_t, slv_id_t, user_t)
`AXI_TYPEDEF_AR_CHAN_T(mst_ar_t, addr_t, mst_id_t, user_t)
`AXI_TYPEDEF_R_CHAN_T(slv_r_t, data_t, slv_id_t, user_t)
`AXI_TYPEDEF_R_CHAN_T(mst_r_t, data_t, mst_id_t, user_t)
if (AxiMstPortIdWidth < AxiSlvPortIdWidth) begin : gen_downsize
if (AxiSlvPortMaxUniqIds <= 2**AxiMstPortIdWidth) begin : gen_remap
axi_id_remap #(
.AxiSlvPortIdWidth ( AxiSlvPortIdWidth ),
.AxiMstPortIdWidth ( AxiMstPortIdWidth ),
.AxiSlvPortMaxUniqIds ( AxiSlvPortMaxUniqIds ),
.AxiMaxTxnsPerId ( AxiSlvPortMaxTxnsPerId ),
.slv_req_t ( slv_req_t ),
.slv_resp_t ( slv_resp_t ),
.mst_req_t ( mst_req_t ),
.mst_resp_t ( mst_resp_t )
) i_axi_id_remap (
.clk_i,
.rst_ni,
.slv_req_i ( slv_req_i ),
.slv_resp_o ( slv_resp_o ),
.mst_req_o ( mst_req_o ),
.mst_resp_i ( mst_resp_i )
);
end else begin : gen_serialize
axi_id_serialize #(
.AxiSlvPortIdWidth ( AxiSlvPortIdWidth ),
.AxiSlvPortMaxTxns ( AxiSlvPortMaxTxns ),
.AxiMstPortIdWidth ( AxiMstPortIdWidth ),
.AxiMstPortMaxUniqIds ( AxiMstPortMaxUniqIds ),
.AxiMstPortMaxTxnsPerId ( AxiMstPortMaxTxnsPerId ),
.AxiAddrWidth ( AxiAddrWidth ),
.AxiDataWidth ( AxiDataWidth ),
.AxiUserWidth ( AxiUserWidth ),
.slv_req_t ( slv_req_t ),
.slv_resp_t ( slv_resp_t ),
.mst_req_t ( mst_req_t ),
.mst_resp_t ( mst_resp_t )
) i_axi_id_serialize (
.clk_i,
.rst_ni,
.slv_req_i ( slv_req_i ),
.slv_resp_o ( slv_resp_o ),
.mst_req_o ( mst_req_o ),
.mst_resp_i ( mst_resp_i )
);
end
end else if (AxiMstPortIdWidth > AxiSlvPortIdWidth) begin : gen_upsize
axi_id_prepend #(
.NoBus ( 32'd1 ),
.AxiIdWidthSlvPort ( AxiSlvPortIdWidth ),
.AxiIdWidthMstPort ( AxiMstPortIdWidth ),
.slv_aw_chan_t ( slv_aw_t ),
.slv_w_chan_t ( w_t ),
.slv_b_chan_t ( slv_b_t ),
.slv_ar_chan_t ( slv_ar_t ),
.slv_r_chan_t ( slv_r_t ),
.mst_aw_chan_t ( mst_aw_t ),
.mst_w_chan_t ( w_t ),
.mst_b_chan_t ( mst_b_t ),
.mst_ar_chan_t ( mst_ar_t ),
.mst_r_chan_t ( mst_r_t )
) i_axi_id_prepend (
.pre_id_i ( '0 ),
.slv_aw_chans_i ( slv_req_i.aw ),
.slv_aw_valids_i ( slv_req_i.aw_valid ),
.slv_aw_readies_o ( slv_resp_o.aw_ready ),
.slv_w_chans_i ( slv_req_i.w ),
.slv_w_valids_i ( slv_req_i.w_valid ),
.slv_w_readies_o ( slv_resp_o.w_ready ),
.slv_b_chans_o ( slv_resp_o.b ),
.slv_b_valids_o ( slv_resp_o.b_valid ),
.slv_b_readies_i ( slv_req_i.b_ready ),
.slv_ar_chans_i ( slv_req_i.ar ),
.slv_ar_valids_i ( slv_req_i.ar_valid ),
.slv_ar_readies_o ( slv_resp_o.ar_ready ),
.slv_r_chans_o ( slv_resp_o.r ),
.slv_r_valids_o ( slv_resp_o.r_valid ),
.slv_r_readies_i ( slv_req_i.r_ready ),
.mst_aw_chans_o ( mst_req_o.aw ),
.mst_aw_valids_o ( mst_req_o.aw_valid ),
.mst_aw_readies_i ( mst_resp_i.aw_ready ),
.mst_w_chans_o ( mst_req_o.w ),
.mst_w_valids_o ( mst_req_o.w_valid ),
.mst_w_readies_i ( mst_resp_i.w_ready ),
.mst_b_chans_i ( mst_resp_i.b ),
.mst_b_valids_i ( mst_resp_i.b_valid ),
.mst_b_readies_o ( mst_req_o.b_ready ),
.mst_ar_chans_o ( mst_req_o.ar ),
.mst_ar_valids_o ( mst_req_o.ar_valid ),
.mst_ar_readies_i ( mst_resp_i.ar_ready ),
.mst_r_chans_i ( mst_resp_i.r ),
.mst_r_valids_i ( mst_resp_i.r_valid ),
.mst_r_readies_o ( mst_req_o.r_ready )
);
end else begin : gen_passthrough
assign mst_req_o = slv_req_i;
assign slv_resp_o = mst_resp_i;
end
// pragma translate_off
`ifndef VERILATOR
initial begin : p_assert
assert(AxiAddrWidth > 32'd0)
else $fatal(1, "Parameter AxiAddrWidth has to be larger than 0!");
assert(AxiDataWidth > 32'd0)
else $fatal(1, "Parameter AxiDataWidth has to be larger than 0!");
assert(AxiUserWidth > 32'd0)
else $fatal(1, "Parameter AxiUserWidth has to be larger than 0!");
assert(AxiSlvPortIdWidth > 32'd0)
else $fatal(1, "Parameter AxiSlvPortIdWidth has to be larger than 0!");
assert(AxiMstPortIdWidth > 32'd0)
else $fatal(1, "Parameter AxiMstPortIdWidth has to be larger than 0!");
if (AxiSlvPortMaxUniqIds <= 2**AxiMstPortIdWidth) begin
assert(AxiSlvPortMaxTxnsPerId > 32'd0)
else $fatal(1, "Parameter AxiSlvPortMaxTxnsPerId has to be larger than 0!");
end else begin
assert(AxiMstPortMaxUniqIds > 32'd0)
else $fatal(1, "Parameter AxiMstPortMaxUniqIds has to be larger than 0!");
assert(AxiMstPortMaxTxnsPerId > 32'd0)
else $fatal(1, "Parameter AxiMstPortMaxTxnsPerId has to be larger than 0!");
end
assert($bits(slv_req_i.aw.addr) == $bits(mst_req_o.aw.addr))
else $fatal(1, "AXI AW address widths are not equal!");
assert($bits(slv_req_i.w.data) == $bits(mst_req_o.w.data))
else $fatal(1, "AXI W data widths are not equal!");
assert($bits(slv_req_i.ar.addr) == $bits(mst_req_o.ar.addr))
else $fatal(1, "AXI AR address widths are not equal!");
assert($bits(slv_resp_o.r.data) == $bits(mst_resp_i.r.data))
else $fatal(1, "AXI R data widths are not equal!");
end
`endif
// pragma translate_on
endmodule
`include "axi/assign.svh"
/// Interface variant of [`axi_iw_converter`](module.axi_iw_converter).
///
/// See the documentation of the main module for the definition of ports and parameters.
module axi_iw_converter_intf #(
parameter int unsigned AXI_SLV_PORT_ID_WIDTH = 32'd0,
parameter int unsigned AXI_MST_PORT_ID_WIDTH = 32'd0,
parameter int unsigned AXI_SLV_PORT_MAX_UNIQ_IDS = 32'd0,
parameter int unsigned AXI_SLV_PORT_MAX_TXNS_PER_ID = 32'd0,
parameter int unsigned AXI_SLV_PORT_MAX_TXNS = 32'd0,
parameter int unsigned AXI_MST_PORT_MAX_UNIQ_IDS = 32'd0,
parameter int unsigned AXI_MST_PORT_MAX_TXNS_PER_ID = 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.Slave slv,
AXI_BUS.Master mst
);
typedef logic [AXI_SLV_PORT_ID_WIDTH-1:0] slv_id_t;
typedef logic [AXI_MST_PORT_ID_WIDTH-1:0] mst_id_t;
typedef logic [AXI_ADDR_WIDTH-1:0] axi_addr_t;
typedef logic [AXI_DATA_WIDTH-1:0] axi_data_t;
typedef logic [AXI_DATA_WIDTH/8-1:0] axi_strb_t;
typedef logic [AXI_USER_WIDTH-1:0] axi_user_t;
`AXI_TYPEDEF_AW_CHAN_T(slv_aw_chan_t, axi_addr_t, slv_id_t, axi_user_t)
`AXI_TYPEDEF_W_CHAN_T(slv_w_chan_t, axi_data_t, axi_strb_t, axi_user_t)
`AXI_TYPEDEF_B_CHAN_T(slv_b_chan_t, slv_id_t, axi_user_t)
`AXI_TYPEDEF_AR_CHAN_T(slv_ar_chan_t, axi_addr_t, slv_id_t, axi_user_t)
`AXI_TYPEDEF_R_CHAN_T(slv_r_chan_t, axi_data_t, slv_id_t, axi_user_t)
`AXI_TYPEDEF_REQ_T(slv_req_t, slv_aw_chan_t, slv_w_chan_t, slv_ar_chan_t)
`AXI_TYPEDEF_RESP_T(slv_resp_t, slv_b_chan_t, slv_r_chan_t)
`AXI_TYPEDEF_AW_CHAN_T(mst_aw_chan_t, axi_addr_t, mst_id_t, axi_user_t)
`AXI_TYPEDEF_W_CHAN_T(mst_w_chan_t, axi_data_t, axi_strb_t, axi_user_t)
`AXI_TYPEDEF_B_CHAN_T(mst_b_chan_t, mst_id_t, axi_user_t)
`AXI_TYPEDEF_AR_CHAN_T(mst_ar_chan_t, axi_addr_t, mst_id_t, axi_user_t)
`AXI_TYPEDEF_R_CHAN_T(mst_r_chan_t, axi_data_t, mst_id_t, axi_user_t)
`AXI_TYPEDEF_REQ_T(mst_req_t, mst_aw_chan_t, mst_w_chan_t, mst_ar_chan_t)
`AXI_TYPEDEF_RESP_T(mst_resp_t, mst_b_chan_t, mst_r_chan_t)
slv_req_t slv_req;
slv_resp_t slv_resp;
mst_req_t mst_req;
mst_resp_t 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_iw_converter #(
.AxiSlvPortIdWidth ( AXI_SLV_PORT_ID_WIDTH ),
.AxiMstPortIdWidth ( AXI_MST_PORT_ID_WIDTH ),
.AxiSlvPortMaxUniqIds ( AXI_SLV_PORT_MAX_UNIQ_IDS ),
.AxiSlvPortMaxTxnsPerId ( AXI_SLV_PORT_MAX_TXNS_PER_ID ),
.AxiSlvPortMaxTxns ( AXI_SLV_PORT_MAX_TXNS ),
.AxiMstPortMaxUniqIds ( AXI_MST_PORT_MAX_UNIQ_IDS ),
.AxiMstPortMaxTxnsPerId ( AXI_MST_PORT_MAX_TXNS_PER_ID ),
.AxiAddrWidth ( AXI_ADDR_WIDTH ),
.AxiDataWidth ( AXI_DATA_WIDTH ),
.AxiUserWidth ( AXI_USER_WIDTH ),
.slv_req_t ( slv_req_t ),
.slv_resp_t ( slv_resp_t ),
.mst_req_t ( mst_req_t ),
.mst_resp_t ( mst_resp_t )
) i_axi_iw_converter (
.clk_i,
.rst_ni,
.slv_req_i ( slv_req ),
.slv_resp_o ( slv_resp ),
.mst_req_o ( mst_req ),
.mst_resp_i ( mst_resp )
);
// pragma translate_off
`ifndef VERILATOR
initial begin
assert (slv.AXI_ID_WIDTH == AXI_SLV_PORT_ID_WIDTH);
assert (slv.AXI_ADDR_WIDTH == AXI_ADDR_WIDTH);
assert (slv.AXI_DATA_WIDTH == AXI_DATA_WIDTH);
assert (slv.AXI_USER_WIDTH == AXI_USER_WIDTH);
assert (mst.AXI_ID_WIDTH == AXI_MST_PORT_ID_WIDTH);
assert (mst.AXI_ADDR_WIDTH == AXI_ADDR_WIDTH);
assert (mst.AXI_DATA_WIDTH == AXI_DATA_WIDTH);
assert (mst.AXI_USER_WIDTH == AXI_USER_WIDTH);
end
`endif
// pragma translate_on
endmodule

View file

@ -0,0 +1,37 @@
// Copyright (c) 2014-2018 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:
// - Fabian Schuiki <fschuiki@iis.ee.ethz.ch>
// - Andreas Kurth <akurth@iis.ee.ethz.ch>
`include "axi/assign.svh"
/// A connector that joins two AXI interfaces.
module axi_join_intf (
AXI_BUS.Slave in,
AXI_BUS.Master out
);
`AXI_ASSIGN(out, in)
// pragma translate_off
`ifndef VERILATOR
initial begin
assert(in.AXI_ADDR_WIDTH == out.AXI_ADDR_WIDTH);
assert(in.AXI_DATA_WIDTH == out.AXI_DATA_WIDTH);
assert(in.AXI_ID_WIDTH <= out.AXI_ID_WIDTH );
assert(in.AXI_USER_WIDTH == out.AXI_USER_WIDTH);
end
`endif
// pragma translate_on
endmodule

View file

@ -0,0 +1,482 @@
// Copyright (c) 2020 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
//
// Authors:
// - Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>
// - Andreas Kurth <akurth@iis.ee.ethz.ch>
`include "common_cells/registers.svh"
// 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.
module axi_lite_demux #(
parameter type aw_chan_t = logic, // AXI4-Lite AW channel
parameter type w_chan_t = logic, // AXI4-Lite W channel
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 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
parameter bit SpillAw = 1'b1, // insert one cycle latency on slave AW
parameter bit SpillW = 1'b0, // insert one cycle latency on slave W
parameter bit SpillB = 1'b0, // insert one cycle latency on slave B
parameter bit SpillAr = 1'b1, // insert one cycle latency on slave AR
parameter bit SpillR = 1'b0, // insert one cycle latency on slave R
// 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,
// 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,
// 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
);
//--------------------------------------
// Typedefs for the spill registers
//--------------------------------------
typedef struct packed {
aw_chan_t aw;
select_t select;
} aw_chan_select_t;
typedef struct packed {
ar_chan_t ar;
select_t select;
} ar_chan_select_t;
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];
end else begin : gen_demux
// normal non degenerate case
//--------------------------------------
//--------------------------------------
// Signal Declarations
//--------------------------------------
//--------------------------------------
//--------------------------------------
// Write Transaction
//--------------------------------------
aw_chan_select_t slv_aw_chan;
logic slv_aw_valid, slv_aw_ready;
logic [NoMstPorts-1:0] mst_aw_valids, mst_aw_readies;
logic lock_aw_valid_d, lock_aw_valid_q, load_aw_lock;
logic w_fifo_push, w_fifo_pop;
logic w_fifo_full, w_fifo_empty;
w_chan_t slv_w_chan;
select_t w_select;
logic slv_w_valid, slv_w_ready;
logic /*w_pop*/ b_fifo_pop;
logic b_fifo_full, b_fifo_empty;
b_chan_t slv_b_chan;
select_t b_select;
logic slv_b_valid, slv_b_ready;
//--------------------------------------
// Read Transaction
//--------------------------------------
ar_chan_select_t slv_ar_chan;
logic slv_ar_valid, slv_ar_ready;
logic r_fifo_push, r_fifo_pop;
logic r_fifo_full, r_fifo_empty;
r_chan_t slv_r_chan;
select_t r_select;
logic slv_r_valid, slv_r_ready;
//--------------------------------------
//--------------------------------------
// Channel control
//--------------------------------------
//--------------------------------------
//--------------------------------------
// AW Channel
//--------------------------------------
// Workaround for bug in Questa 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;
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 )
) 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 )
);
assign slv_aw_chan = slv_aw_chan_select_out_flat;
// replicate AW channel to the request output
for (genvar i = 0; i < NoMstPorts; i++) begin : gen_mst_aw
assign mst_reqs_o[i].aw = slv_aw_chan.aw;
assign mst_reqs_o[i].aw_valid = mst_aw_valids[i];
assign mst_aw_readies[i] = mst_resps_i[i].aw_ready;
end
// AW channel handshake control
always_comb begin
// default assignments
lock_aw_valid_d = lock_aw_valid_q;
load_aw_lock = 1'b0;
// handshake
slv_aw_ready = 1'b0;
mst_aw_valids = '0;
// W FIFO input control
w_fifo_push = 1'b0;
// control
if (lock_aw_valid_q) begin
// AW channel is locked and has valid output, fifo was pushed, as the new request was issued
mst_aw_valids[slv_aw_chan.select] = 1'b1;
if (mst_aw_readies[slv_aw_chan.select]) begin
// transaction, go back to IDLE
slv_aw_ready = 1'b1;
lock_aw_valid_d = 1'b0;
load_aw_lock = 1'b1;
end
end else begin
if (!w_fifo_full && slv_aw_valid) begin
// new transaction, push select in the FIFO and then look if transaction happened
w_fifo_push = 1'b1;
mst_aw_valids[slv_aw_chan.select] = 1'b1; // only set the valid when FIFO is not full
if (mst_aw_readies[slv_aw_chan.select]) begin
// transaction, notify slave port
slv_aw_ready = 1'b1;
end else begin
// no transaction, lock valid
lock_aw_valid_d = 1'b1;
load_aw_lock = 1'b1;
end
end
end
end
// lock the valid signal, as the selection gets pushed into the W FIFO on first assertion,
// prevent further pushing
`FFLARN(lock_aw_valid_q, lock_aw_valid_d, load_aw_lock, '0, clk_i, rst_ni)
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 ), // not used, because AXI4-Lite no preemtion rule
.testmode_i ( test_i ),
.full_o ( w_fifo_full ),
.empty_o ( w_fifo_empty ),
.usage_o ( /*not used*/ ),
.data_i ( slv_aw_chan.select ),
.push_i ( w_fifo_push ),
.data_o ( w_select ),
.pop_i ( w_fifo_pop )
);
//--------------------------------------
// W Channel
//--------------------------------------
spill_register #(
.T ( w_chan_t ),
.Bypass ( ~SpillW )
) i_w_spill_reg (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.valid_i ( slv_req_i.w_valid ),
.ready_o ( slv_resp_o.w_ready ),
.data_i ( slv_req_i.w ),
.valid_o ( slv_w_valid ),
.ready_i ( slv_w_ready ),
.data_o ( slv_w_chan )
);
// replicate W channel
for (genvar i = 0; i < NoMstPorts; i++) begin : gen_mst_w
assign mst_reqs_o[i].w = slv_w_chan;
assign mst_reqs_o[i].w_valid = ~w_fifo_empty & ~b_fifo_full &
slv_w_valid & (w_select == select_t'(i));
end
assign slv_w_ready = ~w_fifo_empty & ~b_fifo_full & mst_resps_i[w_select].w_ready;
assign w_fifo_pop = slv_w_valid & slv_w_ready;
fifo_v3 #(
.FALL_THROUGH( FallThrough ),
.DEPTH ( MaxTrans ),
.dtype ( select_t )
) i_b_fifo (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( 1'b0 ), // not used, because AXI4-Lite no preemption
.testmode_i ( test_i ),
.full_o ( b_fifo_full ),
.empty_o ( b_fifo_empty ),
.usage_o ( /*not used*/ ),
.data_i ( w_select ),
.push_i ( w_fifo_pop ), // w beat was transferred, push selection to b channel
.data_o ( b_select ),
.pop_i ( b_fifo_pop )
);
//--------------------------------------
// B Channel
//--------------------------------------
spill_register #(
.T ( b_chan_t ),
.Bypass ( ~SpillB )
) i_b_spill_reg (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.valid_i ( slv_b_valid ),
.ready_o ( slv_b_ready ),
.data_i ( slv_b_chan ),
.valid_o ( slv_resp_o.b_valid ),
.ready_i ( slv_req_i.b_ready ),
.data_o ( slv_resp_o.b )
);
// connect the response if the FIFO has valid data in it
assign slv_b_chan = (!b_fifo_empty) ? mst_resps_i[b_select].b : '0;
assign slv_b_valid = ~b_fifo_empty & mst_resps_i[b_select].b_valid;
for (genvar i = 0; i < NoMstPorts; i++) begin : gen_mst_b
assign mst_reqs_o[i].b_ready = ~b_fifo_empty & slv_b_ready & (b_select == select_t'(i));
end
assign b_fifo_pop = slv_b_valid & slv_b_ready;
//--------------------------------------
// AR Channel
//--------------------------------------
// Workaround for bug in Questa 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;
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};
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 )
);
assign slv_ar_chan = slv_ar_chan_select_out_flat;
// replicate AR channel
for (genvar i = 0; i < NoMstPorts; i++) begin : gen_mst_ar
assign mst_reqs_o[i].ar = slv_ar_chan.ar;
assign mst_reqs_o[i].ar_valid = ~r_fifo_full & slv_ar_valid &
(slv_ar_chan.select == select_t'(i));
end
assign slv_ar_ready = ~r_fifo_full & mst_resps_i[slv_ar_chan.select].ar_ready;
assign r_fifo_push = slv_ar_valid & slv_ar_ready;
fifo_v3 #(
.FALL_THROUGH( FallThrough ),
.DEPTH ( MaxTrans ),
.dtype ( select_t )
) i_r_fifo (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( 1'b0 ), // not used, because AXI4-Lite no preemption rule
.testmode_i ( test_i ),
.full_o ( r_fifo_full ),
.empty_o ( r_fifo_empty ),
.usage_o ( /*not used*/ ),
.data_i ( slv_ar_chan.select ),
.push_i ( r_fifo_push ),
.data_o ( r_select ),
.pop_i ( r_fifo_pop )
);
//--------------------------------------
// R Channel
//--------------------------------------
spill_register #(
.T ( r_chan_t ),
.Bypass ( ~SpillR )
) i_r_spill_reg (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.valid_i ( slv_r_valid ),
.ready_o ( slv_r_ready ),
.data_i ( slv_r_chan ),
.valid_o ( slv_resp_o.r_valid ),
.ready_i ( slv_req_i.r_ready ),
.data_o ( slv_resp_o.r )
);
// 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;
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
assign r_fifo_pop = slv_r_valid & slv_r_ready;
// pragma translate_off
`ifndef VERILATOR
default disable iff (!rst_ni);
aw_select: assume property( @(posedge clk_i) (slv_req_i.aw_valid |->
(slv_aw_select_i < NoMstPorts))) else
$fatal(1, "slv_aw_select_i is %d: AW has selected a slave that is not defined.\
NoMstPorts: %d", slv_aw_select_i, NoMstPorts);
ar_select: assume property( @(posedge clk_i) (slv_req_i.ar_valid |->
(slv_ar_select_i < NoMstPorts))) else
$fatal(1, "slv_ar_select_i is %d: AR has selected a slave that is not defined.\
NoMstPorts: %d", slv_ar_select_i, NoMstPorts);
aw_valid_stable: assert property( @(posedge clk_i) (slv_aw_valid && !slv_aw_ready)
|=> slv_aw_valid) else
$fatal(1, "aw_valid was deasserted, when aw_ready = 0 in last cycle.");
ar_valid_stable: assert property( @(posedge clk_i) (slv_ar_valid && !slv_ar_ready)
|=> slv_ar_valid) else
$fatal(1, "ar_valid was deasserted, when ar_ready = 0 in last cycle.");
aw_stable: assert property( @(posedge clk_i) (slv_aw_valid && !slv_aw_ready)
|=> $stable(slv_aw_chan)) else
$fatal(1, "slv_aw_chan_select unstable with valid set.");
ar_stable: assert property( @(posedge clk_i) (slv_ar_valid && !slv_ar_ready)
|=> $stable(slv_ar_chan)) else
$fatal(1, "slv_aw_chan_select unstable with valid set.");
`endif
// pragma translate_on
end
// pragma translate_off
`ifndef VERILATOR
initial begin: p_assertions
NoPorts: assert (NoMstPorts > 0) else $fatal("Number of master ports must be at least 1!");
MaxTnx: assert (MaxTrans > 0) else $fatal("Number of transactions must be at least 1!");
end
`endif
// pragma translate_on
endmodule
`include "axi/assign.svh"
`include "axi/typedef.svh"
module axi_lite_demux_intf #(
parameter int unsigned AxiAddrWidth = 32'd0,
parameter int unsigned AxiDataWidth = 32'd0,
parameter int unsigned NoMstPorts = 32'd0,
parameter int unsigned MaxTrans = 32'd0,
parameter bit FallThrough = 1'b0,
parameter bit SpillAw = 1'b1,
parameter bit SpillW = 1'b0,
parameter bit SpillB = 1'b0,
parameter bit SpillAr = 1'b1,
parameter bit SpillR = 1'b0,
// Dependent parameters, DO NOT OVERRIDE!
parameter type select_t = logic [$clog2(NoMstPorts)-1:0]
) (
input logic clk_i, // Clock
input logic rst_ni, // Asynchronous reset active low
input logic test_i, // Testmode enable
input select_t slv_aw_select_i, // has to be stable, when aw_valid
input select_t slv_ar_select_i, // has to be stable, when ar_valid
AXI_LITE.Slave slv, // slave port
AXI_LITE.Master mst [NoMstPorts-1:0] // master ports
);
typedef logic [AxiAddrWidth-1:0] addr_t;
typedef logic [AxiDataWidth-1:0] data_t;
typedef logic [AxiDataWidth/8-1:0] strb_t;
`AXI_LITE_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t)
`AXI_LITE_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t)
`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)
req_t slv_req;
resp_t slv_resp;
req_t [NoMstPorts-1:0] mst_reqs;
resp_t [NoMstPorts-1:0] mst_resps;
`AXI_LITE_ASSIGN_TO_REQ(slv_req, slv)
`AXI_LITE_ASSIGN_FROM_RESP(slv, slv_resp)
for (genvar i = 0; i < NoMstPorts; i++) begin : gen_assign_mst_ports
`AXI_LITE_ASSIGN_FROM_REQ(mst[i], mst_reqs[i])
`AXI_LITE_ASSIGN_TO_RESP(mst_resps[i], mst[i])
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 ),
.NoMstPorts ( NoMstPorts ),
.MaxTrans ( MaxTrans ),
.FallThrough ( FallThrough ),
.SpillAw ( SpillAw ),
.SpillW ( SpillW ),
.SpillB ( SpillB ),
.SpillAr ( SpillAr ),
.SpillR ( SpillR )
) i_axi_demux (
.clk_i,
.rst_ni,
.test_i,
// slave Port
.slv_req_i ( slv_req ),
.slv_aw_select_i ( slv_aw_select_i ), // must be stable while slv_aw_valid_i
.slv_ar_select_i ( slv_ar_select_i ), // must be stable while slv_ar_valid_i
.slv_resp_o ( slv_resp ),
// mster ports
.mst_reqs_o ( mst_reqs ),
.mst_resps_i ( mst_resps )
);
// pragma translate_off
`ifndef VERILATOR
initial begin: p_assertions
AddrWidth: assert (AxiAddrWidth > 0) else $fatal("Axi Parmeter has to be > 0!");
DataWidth: assert (AxiDataWidth > 0) else $fatal("Axi Parmeter has to be > 0!");
end
`endif
// pragma translate_on
endmodule

View file

@ -0,0 +1,35 @@
// Copyright (c) 2014-2018 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:
// - Fabian Schuiki <fschuiki@iis.ee.ethz.ch>
// - Andreas Kurth <akurth@iis.ee.ethz.ch>
`include "axi/assign.svh"
/// A connector that joins two AXI-Lite interfaces.
module axi_lite_join_intf (
AXI_LITE.Slave in,
AXI_LITE.Master out
);
`AXI_LITE_ASSIGN(out, in)
// pragma translate_off
`ifndef VERILATOR
initial begin
assert(in.AXI_ADDR_WIDTH == out.AXI_ADDR_WIDTH);
assert(in.AXI_DATA_WIDTH == out.AXI_DATA_WIDTH);
end
`endif
// pragma translate_on
endmodule

View file

@ -0,0 +1,625 @@
// Copyright (c) 2020 ETH Zurich and University of Bologna
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
//
// Authors:
// - Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>
// - Andreas Kurth <akurth@iis.ee.ethz.ch>
// Description: A mailbox with two AXI4-Lite slave ports and associated interrupt requests.
// See `doc/axi_lite_mailbox.md` for the documentation, including the definition
// of parameters and ports.
`include "common_cells/registers.svh"
module axi_lite_mailbox #(
parameter int unsigned MailboxDepth = 32'd0,
parameter bit unsigned IrqEdgeTrig = 1'b0,
parameter bit unsigned IrqActHigh = 1'b1,
parameter int unsigned AxiAddrWidth = 32'd0,
parameter int unsigned AxiDataWidth = 32'd0,
parameter type req_lite_t = logic,
parameter type resp_lite_t = logic,
// DEPENDENT PARAMETERS, DO NOT OVERRIDE!
parameter type addr_t = logic [AxiAddrWidth-1:0]
) (
input logic clk_i, // Clock
input logic rst_ni, // Asynchronous reset active low
input logic test_i, // Testmode enable
// slave ports [1:0]
input req_lite_t [1:0] slv_reqs_i,
output resp_lite_t [1:0] slv_resps_o,
output logic [1:0] irq_o, // interrupt output for each port
input addr_t [1:0] base_addr_i // base address for each port
);
localparam int unsigned FifoUsageWidth = $clog2(MailboxDepth);
typedef logic [AxiDataWidth-1:0] data_t;
// usage type of the mailbox FIFO, also the type of the threshold comparison
// is one bit wider, MSB is the fifo_full flag of the respective fifo
typedef logic [FifoUsageWidth:0] usage_t;
// signal declaration for the mailbox FIFO's, signal index is the port
logic [1:0] mbox_full, mbox_empty; // index is the instantiated mailbox FIFO
logic [1:0] mbox_push, mbox_pop; // index is port
logic [1:0] w_mbox_flush, r_mbox_flush; // index is port
data_t [1:0] mbox_w_data, mbox_r_data; // index is port
usage_t [1:0] mbox_usage; // index is the instantiated mailbox FIFO
// interrupt request from this slave port, level triggered, active high --> convert
logic [1:0] slv_irq;
logic [1:0] clear_irq;
axi_lite_mailbox_slave #(
.MailboxDepth ( MailboxDepth ),
.AxiAddrWidth ( AxiAddrWidth ),
.AxiDataWidth ( AxiDataWidth ),
.req_lite_t ( req_lite_t ),
.resp_lite_t ( resp_lite_t ),
.addr_t ( addr_t ),
.data_t ( data_t ),
.usage_t ( usage_t ) // fill pointer from MBOX FIFO
) i_slv_port_0 (
.clk_i, // Clock
.rst_ni, // Asynchronous reset active low
// slave port
.slv_req_i ( slv_reqs_i[0] ),
.slv_resp_o ( slv_resps_o[0] ),
.base_addr_i ( base_addr_i[0] ), // base address for the slave port
// write FIFO port
.mbox_w_data_o ( mbox_w_data[0] ),
.mbox_w_full_i ( mbox_full[0] ),
.mbox_w_push_o ( mbox_push[0] ),
.mbox_w_flush_o ( w_mbox_flush[0] ),
.mbox_w_usage_i ( mbox_usage[0] ),
// read FIFO port
.mbox_r_data_i ( mbox_r_data[0] ),
.mbox_r_empty_i ( mbox_empty[1] ),
.mbox_r_pop_o ( mbox_pop[0] ),
.mbox_r_flush_o ( r_mbox_flush[0] ),
.mbox_r_usage_i ( mbox_usage[1] ),
// interrupt output, level triggered, active high, conversion in top
.irq_o ( slv_irq[0] ),
.clear_irq_o ( clear_irq[0] )
);
axi_lite_mailbox_slave #(
.MailboxDepth ( MailboxDepth ),
.AxiAddrWidth ( AxiAddrWidth ),
.AxiDataWidth ( AxiDataWidth ),
.req_lite_t ( req_lite_t ),
.resp_lite_t ( resp_lite_t ),
.addr_t ( addr_t ),
.data_t ( data_t ),
.usage_t ( usage_t ) // fill pointer from MBOX FIFO
) i_slv_port_1 (
.clk_i, // Clock
.rst_ni, // Asynchronous reset active low
// slave port
.slv_req_i ( slv_reqs_i[1] ),
.slv_resp_o ( slv_resps_o[1] ),
.base_addr_i ( base_addr_i[1] ), // base address for the slave port
// write FIFO port
.mbox_w_data_o ( mbox_w_data[1] ),
.mbox_w_full_i ( mbox_full[1] ),
.mbox_w_push_o ( mbox_push[1] ),
.mbox_w_flush_o ( w_mbox_flush[1] ),
.mbox_w_usage_i ( mbox_usage[1] ),
// read FIFO port
.mbox_r_data_i ( mbox_r_data[1] ),
.mbox_r_empty_i ( mbox_empty[0] ),
.mbox_r_pop_o ( mbox_pop[1] ),
.mbox_r_flush_o ( r_mbox_flush[1] ),
.mbox_r_usage_i ( mbox_usage[0] ),
// interrupt output, level triggered, active high, conversion in top
.irq_o ( slv_irq[1] ),
.clear_irq_o ( clear_irq[1] )
);
// the usage gets concatinated with the full flag to have consistent threshold detection
logic [FifoUsageWidth-1:0] mbox_0_to_1_usage, mbox_1_to_0_usage;
fifo_v3 #(
.FALL_THROUGH ( 1'b0 ),
.DEPTH ( MailboxDepth ),
.dtype ( data_t )
) i_mbox_0_to_1 (
.clk_i,
.rst_ni,
.testmode_i( test_i ),
.flush_i ( w_mbox_flush[0] | r_mbox_flush[1] ),
.full_o ( mbox_full[0] ),
.empty_o ( mbox_empty[0] ),
.usage_o ( mbox_0_to_1_usage ),
.data_i ( mbox_w_data[0] ),
.push_i ( mbox_push[0] ),
.data_o ( mbox_r_data[1] ),
.pop_i ( mbox_pop[1] )
);
// assign the MSB of the FIFO to the correct usage signal
assign mbox_usage[0] = {mbox_full[0], mbox_0_to_1_usage};
fifo_v3 #(
.FALL_THROUGH ( 1'b0 ),
.DEPTH ( MailboxDepth ),
.dtype ( data_t )
) i_mbox_1_to_0 (
.clk_i,
.rst_ni,
.testmode_i( test_i ),
.flush_i ( w_mbox_flush[1] | r_mbox_flush[0] ),
.full_o ( mbox_full[1] ),
.empty_o ( mbox_empty[1] ),
.usage_o ( mbox_1_to_0_usage ),
.data_i ( mbox_w_data[1] ),
.push_i ( mbox_push[1] ),
.data_o ( mbox_r_data[0] ),
.pop_i ( mbox_pop[0] )
);
assign mbox_usage[1] = {mbox_full[1], mbox_1_to_0_usage};
for (genvar i = 0; i < 2; i++) begin : gen_irq_conversion
if (IrqEdgeTrig) begin : gen_irq_edge
logic irq_q, irq_d, update_irq;
always_comb begin
// default assignments
irq_d = irq_q;
update_irq = 1'b0;
// init the irq and pulse only on update
irq_o[i] = ~IrqActHigh;
if (clear_irq[i]) begin
irq_d = 1'b0;
update_irq = 1'b1;
end else if (!irq_q && slv_irq[i]) begin
irq_d = 1'b1;
update_irq = 1'b1;
irq_o[i] = IrqActHigh; // on update of the register pulse the irq signal
end
end
`FFLARN(irq_q, irq_d, update_irq, '0, clk_i, rst_ni)
end else begin : gen_irq_level
assign irq_o[i] = (IrqActHigh) ? slv_irq[i] : ~slv_irq[i];
end
end
// pragma translate_off
`ifndef VERILATOR
initial begin : proc_check_params
mailbox_depth: assert (MailboxDepth > 1) else $fatal(1, "MailboxDepth has to be at least 2");
axi_addr_width: assert (AxiAddrWidth > 0) else $fatal(1, "AxiAddrWidth has to be > 0");
axi_data_width: assert (AxiDataWidth > 0) else $fatal(1, "AxiDataWidth has to be > 0");
end
`endif
// pragma translate_on
endmodule
`include "axi/typedef.svh"
// slave port module
module axi_lite_mailbox_slave #(
parameter int unsigned MailboxDepth = 32'd16,
parameter int unsigned AxiAddrWidth = 32'd32,
parameter int unsigned AxiDataWidth = 32'd32,
parameter type req_lite_t = logic,
parameter type resp_lite_t = logic,
parameter type addr_t = logic [AxiAddrWidth-1:0],
parameter type data_t = logic [AxiDataWidth-1:0],
parameter type usage_t = logic // fill pointer from MBOX FIFO
) (
input logic clk_i, // Clock
input logic rst_ni, // Asynchronous reset active low
// slave port
input req_lite_t slv_req_i,
output resp_lite_t slv_resp_o,
input addr_t base_addr_i, // base address for the slave port
// write FIFO port
output data_t mbox_w_data_o,
input logic mbox_w_full_i,
output logic mbox_w_push_o,
output logic mbox_w_flush_o,
input usage_t mbox_w_usage_i,
// read FIFO port
input data_t mbox_r_data_i,
input logic mbox_r_empty_i,
output logic mbox_r_pop_o,
output logic mbox_r_flush_o,
input usage_t mbox_r_usage_i,
// interrupt output, level triggered, active high, conversion in top
output logic irq_o,
output logic clear_irq_o // clear the edge trigger irq register in `axi_lite_mailbox`
);
`AXI_LITE_TYPEDEF_B_CHAN_T(b_chan_lite_t)
`AXI_LITE_TYPEDEF_R_CHAN_T(r_chan_lite_t, data_t)
localparam int unsigned NoRegs = 32'd10;
typedef enum logic [3:0] {
MBOXW = 4'd0, // Mailbox write register
MBOXR = 4'd1, // Mailbox read register
STATUS = 4'd2, // Mailbox status register
ERROR = 4'd3, // Mailbox error register
WIRQT = 4'd4, // Write interrupt request threshold register
RIRQT = 4'd5, // Read interrupt request threshold register
IRQS = 4'd6, // Interrupt request status register
IRQEN = 4'd7, // Interrupt request enable register
IRQP = 4'd8, // Interrupt request pending register
CTRL = 4'd9 // Mailbox control register
} reg_e;
// address map rule struct, as required from `addr_decode` from `common_cells`
typedef struct packed {
int unsigned idx;
addr_t start_addr;
addr_t end_addr;
} rule_t;
// output type of the address decoders, to be casted onto the enum type `reg_e`
typedef logic [$clog2(NoRegs)-1:0] idx_t;
// LITE response signals, go into the output spill registers to prevent combinational response
logic b_valid, b_ready;
b_chan_lite_t b_chan;
logic r_valid, r_ready;
r_chan_lite_t r_chan;
// address map generation
rule_t [NoRegs-1:0] addr_map;
for (genvar i = 0; i < NoRegs; i++) begin : gen_addr_map
assign addr_map[i] = '{
idx: i,
start_addr: base_addr_i + i * (AxiDataWidth / 8),
end_addr: base_addr_i + (i + 1) * (AxiDataWidth / 8),
default: '0
};
end
// address decode flags
idx_t w_reg_idx, r_reg_idx;
logic dec_w_valid, dec_r_valid;
// mailbox register signal declarations, get extended when read, some of these regs
// are build combinationally, indicated by the absence of the `*_d` signal
logic [3:0] status_q; // mailbox status register (read only)
logic [1:0] error_q, error_d; // mailbox error register
data_t wirqt_q, wirqt_d; // write interrupt request threshold register
data_t rirqt_q, rirqt_d; // read interrupt request threshold register
logic [2:0] irqs_q, irqs_d; // interrupt request status register
logic [2:0] irqen_q, irqen_d; // interrupt request enable register
logic [2:0] irqp_q; // interrupt request pending register (read only)
logic [1:0] ctrl_q; // mailbox control register
logic update_regs; // register enable signal
// register instantiation
`FFLARN(error_q, error_d, update_regs, '0, clk_i, rst_ni)
`FFLARN(wirqt_q, wirqt_d, update_regs, '0, clk_i, rst_ni)
`FFLARN(rirqt_q, rirqt_d, update_regs, '0, clk_i, rst_ni)
`FFLARN(irqs_q, irqs_d, update_regs, '0, clk_i, rst_ni)
`FFLARN(irqen_q, irqen_d, update_regs, '0, clk_i, rst_ni)
// Mailbox FIFO data assignments
for (genvar i = 0; i < (AxiDataWidth/8); i++) begin : gen_w_mbox_data
assign mbox_w_data_o[i*8+:8] = slv_req_i.w.strb[i] ? slv_req_i.w.data[i*8+:8] : '0;
end
// combinational mailbox register assignments, for the read only registers
assign status_q = { mbox_r_usage_i > usage_t'(rirqt_q),
mbox_w_usage_i > usage_t'(wirqt_q),
mbox_w_full_i,
mbox_r_empty_i };
assign irqp_q = irqs_q & irqen_q; // interrupt request pending is bit wise and
assign ctrl_q = {mbox_r_flush_o, mbox_w_flush_o}; // read ctrl_q is flush signals
assign irq_o = |irqp_q; // generate an active-high level irq
always_comb begin
// slave port channel outputs for the AW, W and R channel, other driven from spill register
slv_resp_o.aw_ready = 1'b0;
slv_resp_o.w_ready = 1'b0;
b_chan = '{resp: axi_pkg::RESP_SLVERR};
b_valid = 1'b0;
slv_resp_o.ar_ready = 1'b0;
r_chan = '{data: '0, resp: axi_pkg::RESP_SLVERR};
r_valid = 1'b0;
// Default assignments for the internal registers
error_d = error_q; // mailbox error register
wirqt_d = wirqt_q; // write interrupt request threshold register
rirqt_d = rirqt_q; // read interrupt request threshold register
irqs_d = irqs_q; // interrupt request status register
irqen_d = irqen_q; // interrupt request enable register
update_regs = 1'b0; // register update enable signal
// MBOX FIFO control signals
mbox_w_push_o = 1'b0;
mbox_w_flush_o = 1'b0;
mbox_r_pop_o = 1'b0;
mbox_r_flush_o = 1'b0;
// clear the edge triggered irq register if it is instantiated
clear_irq_o = 1'b0;
// -------------------------------------------
// Set the read and write interrupt FF (irqs), when the threshold triggers
// -------------------------------------------
// strict threshold interrupt these fields get cleared by acknowledge on write onto the register
// read trigger, see status_q above
if (!irqs_q[1] && status_q[3]) begin
irqs_d[1] = 1'b1;
update_regs = 1'b1;
end
// write trigger, see status_q above
if (!irqs_q[0] && status_q[2]) begin
irqs_d[0] = 1'b1;
update_regs = 1'b1;
end
// -------------------------------------------
// Read registers
// -------------------------------------------
// The logic of the read and write channels have to be in the same `always_comb` block.
// The reason is that the error register could be cleared in the same cycle as a read from
// the mailbox FIFO generates a new error. In this case the error is NOT cleared. Instead
// it will generate a new irq when it is edge triggered, or the level will stay at its
// active state.
// Check if there is a pending read request on the slave port.
if (slv_req_i.ar_valid) begin
// set the right read channel output depending on the address decoding
if (dec_r_valid) begin
// when decode not valid, send the default slaveerror
// read the right register when the transfer happens and decode is valid
unique case (reg_e'(r_reg_idx))
MBOXW: r_chan = '{data: data_t'( 32'hFEEDC0DE ), resp: axi_pkg::RESP_OKAY};
MBOXR: begin
if (!mbox_r_empty_i) begin
r_chan = '{data: data_t'( mbox_r_data_i ), resp: axi_pkg::RESP_OKAY};
mbox_r_pop_o = 1'b1;
end else begin
// read mailbox is empty, set the read error Flip flop and respond with error
r_chan = '{data: data_t'( 32'hFEEDDEAD ), resp: axi_pkg::RESP_SLVERR};
error_d[0] = 1'b1;
irqs_d[2] = 1'b1;
update_regs = 1'b1;
end
end
STATUS: r_chan = '{data: data_t'( status_q ), resp: axi_pkg::RESP_OKAY};
ERROR: begin // clear the error register
r_chan = '{data: data_t'( error_q ), resp: axi_pkg::RESP_OKAY};
error_d = '0;
update_regs = 1'b1;
end
WIRQT: r_chan = '{data: data_t'( wirqt_q ), resp: axi_pkg::RESP_OKAY};
RIRQT: r_chan = '{data: data_t'( rirqt_q ), resp: axi_pkg::RESP_OKAY};
IRQS: r_chan = '{data: data_t'( irqs_q ), resp: axi_pkg::RESP_OKAY};
IRQEN: r_chan = '{data: data_t'( irqen_q ), resp: axi_pkg::RESP_OKAY};
IRQP: r_chan = '{data: data_t'( irqp_q ), resp: axi_pkg::RESP_OKAY};
CTRL: r_chan = '{data: data_t'( ctrl_q ), resp: axi_pkg::RESP_OKAY};
default: /*do nothing*/;
endcase
end
r_valid = 1'b1;
if (r_ready) begin
slv_resp_o.ar_ready = 1'b1;
end
end // read register
// -------------------------------------------
// Write registers
// -------------------------------------------
// Wait for control and write data to be valid.
if (slv_req_i.aw_valid && slv_req_i.w_valid) begin
// Can do the handshake here as the b response goes into a spill register with latency one.
// Without the B spill register, the B channel would violate the AXI stable requirement.
b_valid = 1'b1;
if (b_ready) begin
// write to the register if required
if (dec_w_valid) begin
unique case (reg_e'(w_reg_idx))
MBOXW: begin
if (!mbox_w_full_i) begin
mbox_w_push_o = 1'b1;
b_chan = '{resp: axi_pkg::RESP_OKAY};
end else begin
// response with default error and set the error FF
error_d[1] = 1'b1;
irqs_d[2] = 1'b1;
update_regs = 1'b1;
end
end
// MBOXR: read only
// STATUS: read only
// ERROR: read only
WIRQT: begin
for (int unsigned i = 0; i < AxiDataWidth/8; i++) begin
wirqt_d[i*8+:8] = slv_req_i.w.strb[i] ? slv_req_i.w.data[i*8+:8] : 8'b0000_0000;
end
if (wirqt_d >= data_t'(MailboxDepth)) begin
// the `-1` is to have the interrupt fireing when the FIFO is comletely full
wirqt_d = data_t'(MailboxDepth) - data_t'(32'd1); // Threshold to maximal value
end
update_regs = 1'b1;
b_chan = '{resp: axi_pkg::RESP_OKAY};
end
RIRQT: begin
for (int unsigned i = 0; i < AxiDataWidth/8; i++) begin
rirqt_d[i*8+:8] = slv_req_i.w.strb[i] ? slv_req_i.w.data[i*8+:8] : 8'b0000_0000;
end
if (rirqt_d >= data_t'(MailboxDepth)) begin
// Threshold to maximal value, minus two to prevent overflow in usage
rirqt_d = data_t'(MailboxDepth) - data_t'(32'd1);
end
update_regs = 1'b1;
b_chan = '{resp: axi_pkg::RESP_OKAY};
end
IRQS: begin
// Acknowledge and clear the register by asserting the respective one
if (slv_req_i.w.strb[0]) begin
// *_d signal is set in the beginning of this process, prevent accidental
// overwrite of not acknowledged irq
irqs_d[2] = slv_req_i.w.data[2] ? 1'b0 : irqs_d[2]; // Error irq status
irqs_d[1] = slv_req_i.w.data[1] ? 1'b0 : irqs_d[1]; // Read irq status
irqs_d[0] = slv_req_i.w.data[0] ? 1'b0 : irqs_d[0]; // Write irq status
clear_irq_o = 1'b1;
update_regs = 1'b1;
end
b_chan = '{resp: axi_pkg::RESP_OKAY};
end
IRQEN: begin
if (slv_req_i.w.strb[0]) begin
irqen_d[2:0] = slv_req_i.w.data[2:0]; // set the irq enable bits
update_regs = 1'b1;
end
b_chan = '{resp: axi_pkg::RESP_OKAY};
end
// IRQP: read only
CTRL: begin
if (slv_req_i.w.strb[0]) begin
mbox_r_flush_o = slv_req_i.w.data[1]; // Flush read FIFO
mbox_w_flush_o = slv_req_i.w.data[0]; // Flush write FIFO
end
b_chan = '{resp: axi_pkg::RESP_OKAY};
end
default : /* use default b_chan */;
endcase
end
slv_resp_o.aw_ready = 1'b1;
slv_resp_o.w_ready = 1'b1;
end // if (b_ready): Does not violate AXI spec, because the ready comes from an internal
// spill register and does not propagate the ready from the b channel.
end // write register
end
// address decoder and response FIFOs for the LITE channel, the port can take a new transaction if
// these FIFOs are not full, not fall through to prevent combinational paths to the return path
addr_decode #(
.NoIndices( NoRegs ),
.NoRules ( NoRegs ),
.addr_t ( addr_t ),
.rule_t ( rule_t )
) i_waddr_decode (
.addr_i ( slv_req_i.aw.addr ),
.addr_map_i ( addr_map ),
.idx_o ( w_reg_idx ),
.dec_valid_o ( dec_w_valid ),
.dec_error_o ( /*not used*/ ),
.en_default_idx_i ( 1'b0 ),
.default_idx_i ( '0 )
);
spill_register #(
.T ( b_chan_lite_t )
) i_b_chan_outp (
.clk_i,
.rst_ni,
.valid_i ( b_valid ),
.ready_o ( b_ready ),
.data_i ( b_chan ),
.valid_o ( slv_resp_o.b_valid ),
.ready_i ( slv_req_i.b_ready ),
.data_o ( slv_resp_o.b )
);
addr_decode #(
.NoIndices( NoRegs ),
.NoRules ( NoRegs ),
.addr_t ( addr_t ),
.rule_t ( rule_t )
) i_raddr_decode (
.addr_i ( slv_req_i.ar.addr ),
.addr_map_i ( addr_map ),
.idx_o ( r_reg_idx ),
.dec_valid_o ( dec_r_valid ),
.dec_error_o ( /*not used*/ ),
.en_default_idx_i ( 1'b0 ),
.default_idx_i ( '0 )
);
spill_register #(
.T ( r_chan_lite_t )
) i_r_chan_outp (
.clk_i,
.rst_ni,
.valid_i ( r_valid ),
.ready_o ( r_ready ),
.data_i ( r_chan ),
.valid_o ( slv_resp_o.r_valid ),
.ready_i ( slv_req_i.r_ready ),
.data_o ( slv_resp_o.r )
);
// pragma translate_off
`ifndef VERILATOR
initial begin : proc_check_params
assert (AxiAddrWidth == $bits(slv_req_i.aw.addr)) else $fatal(1, "AW AxiAddrWidth mismatch");
assert (AxiDataWidth == $bits(slv_req_i.w.data)) else $fatal(1, " W AxiDataWidth mismatch");
assert (AxiAddrWidth == $bits(slv_req_i.ar.addr)) else $fatal(1, "AR AxiAddrWidth mismatch");
assert (AxiDataWidth == $bits(slv_resp_o.r.data)) else $fatal(1, " R AxiDataWidth mismatch");
end
`endif
// pragma translate_on
endmodule
`include "axi/assign.svh"
module axi_lite_mailbox_intf #(
parameter int unsigned MAILBOX_DEPTH = 32'd0,
parameter bit unsigned IRQ_EDGE_TRIG = 1'b0,
parameter bit unsigned IRQ_ACT_HIGH = 1'b1,
parameter int unsigned AXI_ADDR_WIDTH = 32'd0,
parameter int unsigned AXI_DATA_WIDTH = 32'd0,
// DEPENDENT PARAMETERS, DO NOT OVERRIDE!
parameter type addr_t = logic [AXI_ADDR_WIDTH-1:0]
) (
input logic clk_i, // Clock
input logic rst_ni, // Asynchronous reset active low
input logic test_i, // Testmode enable
AXI_LITE.Slave slv [1:0], // slave ports [1:0]
output logic [1:0] irq_o, // interrupt output for each port
input addr_t [1:0] base_addr_i // base address for each port
);
typedef logic [AXI_DATA_WIDTH-1:0] data_t;
typedef logic [AXI_DATA_WIDTH/8-1:0] strb_t;
`AXI_LITE_TYPEDEF_AW_CHAN_T(aw_chan_lite_t, addr_t)
`AXI_LITE_TYPEDEF_W_CHAN_T(w_chan_lite_t, data_t, strb_t)
`AXI_LITE_TYPEDEF_B_CHAN_T(b_chan_lite_t)
`AXI_LITE_TYPEDEF_AR_CHAN_T(ar_chan_lite_t, addr_t)
`AXI_LITE_TYPEDEF_R_CHAN_T(r_chan_lite_t, data_t)
`AXI_LITE_TYPEDEF_REQ_T(req_lite_t, aw_chan_lite_t, w_chan_lite_t, ar_chan_lite_t)
`AXI_LITE_TYPEDEF_RESP_T(resp_lite_t, b_chan_lite_t, r_chan_lite_t)
req_lite_t [1:0] slv_reqs;
resp_lite_t [1:0] slv_resps;
for (genvar i = 0; i < 2; i++) begin : gen_port_assign
`AXI_LITE_ASSIGN_TO_REQ(slv_reqs[i], slv[i])
`AXI_LITE_ASSIGN_FROM_RESP(slv[i], slv_resps[i])
end
axi_lite_mailbox #(
.MailboxDepth ( MAILBOX_DEPTH ),
.IrqEdgeTrig ( IRQ_EDGE_TRIG ),
.IrqActHigh ( IRQ_ACT_HIGH ),
.AxiAddrWidth ( AXI_ADDR_WIDTH ),
.AxiDataWidth ( AXI_DATA_WIDTH ),
.req_lite_t ( req_lite_t ),
.resp_lite_t ( resp_lite_t )
) i_axi_lite_mailbox (
.clk_i, // Clock
.rst_ni, // Asynchronous reset active low
.test_i, // Testmode enable
// slave ports [1:0]
.slv_reqs_i ( slv_reqs ),
.slv_resps_o ( slv_resps ),
.irq_o, // interrupt output for each port
.base_addr_i // base address for each port
);
// pragma translate_off
`ifndef VERILATOR
initial begin
assert (slv[0].AXI_ADDR_WIDTH == AXI_ADDR_WIDTH)
else $fatal(1, "LITE Interface [0] AXI_ADDR_WIDTH missmatch!");
assert (slv[1].AXI_ADDR_WIDTH == AXI_ADDR_WIDTH)
else $fatal(1, "LITE Interface [1] AXI_ADDR_WIDTH missmatch!");
assert (slv[0].AXI_DATA_WIDTH == AXI_DATA_WIDTH)
else $fatal(1, "LITE Interface [0] AXI_DATA_WIDTH missmatch!");
assert (slv[1].AXI_DATA_WIDTH == AXI_DATA_WIDTH)
else $fatal(1, "LITE Interface [1] AXI_DATA_WIDTH missmatch!");
end
`endif
// pragma translate_on
endmodule

View file

@ -0,0 +1,473 @@
// 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:
// - Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>
// - Andreas Kurth <akurth@iis.ee.ethz.ch>
// AXI4-Lite Multiplexer: This module multiplexes the AXI4-Lite slave ports down to one master port.
// The multiplexing happens in a round robin fashion, responses get
// sent back in order.
// register macros
`include "common_cells/registers.svh"
module axi_lite_mux #(
// AXI4-Lite parameter and channel types
parameter type aw_chan_t = logic, // AW LITE Channel Type
parameter type w_chan_t = logic, // W LITE Channel Type
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 int unsigned NoSlvPorts = 32'd0, // Number of slave ports
// Maximum number of outstanding transactions per write or read
parameter int unsigned MaxTrans = 32'd0,
// If enabled, this multiplexer is purely combinatorial
parameter bit FallThrough = 1'b0,
// add spill register on write master port, adds a cycle latency on write channels
parameter bit SpillAw = 1'b1,
parameter bit SpillW = 1'b0,
parameter bit SpillB = 1'b0,
// add spill register on read master port, adds a cycle latency on read channels
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
// 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,
// master port (AXI4-Lite output), connect slave module here
output req_t mst_req_o,
input 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;
// other non degenerate cases
end else begin : gen_mux
// typedef for the FIFO types
typedef logic [$clog2(NoSlvPorts)-1:0] select_t;
// input to the AW arbitration tree, unpacked from request struct
aw_chan_t [NoSlvPorts-1:0] slv_aw_chans;
logic [NoSlvPorts-1:0] slv_aw_valids, slv_aw_readies;
// AW channel arb tree decision
select_t aw_select;
aw_chan_t mst_aw_chan;
logic mst_aw_valid, mst_aw_ready;
// AW master handshake internal, so that we are able to stall, if w_fifo is full
logic aw_valid, aw_ready;
// FF to lock the AW valid signal, when a new arbitration decision is made the decision
// gets pushed into the W FIFO, when it now stalls prevent subsequent pushing
// This FF removes AW to W dependency
logic lock_aw_valid_d, lock_aw_valid_q;
logic load_aw_lock;
// signals for the FIFO that holds the last switching decision of the AW channel
logic w_fifo_full, w_fifo_empty;
logic w_fifo_push, w_fifo_pop;
// W channel spill reg
select_t w_select;
w_chan_t mst_w_chan;
logic mst_w_valid, mst_w_ready;
// switching decision for the B response back routing
select_t b_select;
// signals for the FIFO that holds the last switching decision of the AW channel
logic b_fifo_full, b_fifo_empty;
logic /*w_fifo_pop*/b_fifo_pop;
// B channel spill reg
b_chan_t mst_b_chan;
logic mst_b_valid, mst_b_ready;
// input to the AR arbitration tree, unpacked from request struct
ar_chan_t [NoSlvPorts-1:0] slv_ar_chans;
logic [NoSlvPorts-1:0] slv_ar_valids, slv_ar_readies;
// AR channel for when spill is enabled
select_t ar_select;
ar_chan_t mst_ar_chan;
logic mst_ar_valid, mst_ar_ready;
// AR master handshake internal, so that we are able to stall, if R_fifo is full
logic ar_valid, ar_ready;
// master ID in the r_id
select_t r_select;
// signals for the FIFO that holds the last switching decision of the AR channel
logic r_fifo_full, r_fifo_empty;
logic r_fifo_push, r_fifo_pop;
// R channel spill reg
r_chan_t mst_r_chan;
logic mst_r_valid, mst_r_ready;
//--------------------------------------
// AW Channel
//--------------------------------------
// unpach AW channel from request/response array
for (genvar i = 0; i < NoSlvPorts; i++) begin : gen_aw_arb_input
assign slv_aw_chans[i] = slv_reqs_i[i].aw;
assign slv_aw_valids[i] = slv_reqs_i[i].aw_valid;
assign slv_resps_o[i].aw_ready = slv_aw_readies[i];
end
rr_arb_tree #(
.NumIn ( NoSlvPorts ),
.DataType ( aw_chan_t ),
.AxiVldRdy( 1'b1 ),
.LockIn ( 1'b1 )
) i_aw_arbiter (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i( 1'b0 ),
.rr_i ( '0 ),
.req_i ( slv_aw_valids ),
.gnt_o ( slv_aw_readies ),
.data_i ( slv_aw_chans ),
.gnt_i ( aw_ready ),
.req_o ( aw_valid ),
.data_o ( mst_aw_chan ),
.idx_o ( aw_select )
);
// control of the AW channel
always_comb begin
// default assignments
lock_aw_valid_d = lock_aw_valid_q;
load_aw_lock = 1'b0;
w_fifo_push = 1'b0;
mst_aw_valid = 1'b0;
aw_ready = 1'b0;
// had a downstream stall, be valid and send the AW along
if (lock_aw_valid_q) begin
mst_aw_valid = 1'b1;
// transaction
if (mst_aw_ready) begin
aw_ready = 1'b1;
lock_aw_valid_d = 1'b0;
load_aw_lock = 1'b1;
end
end else begin
if (!w_fifo_full && aw_valid) begin
mst_aw_valid = 1'b1;
w_fifo_push = 1'b1;
if (mst_aw_ready) begin
aw_ready = 1'b1;
end else begin
// go to lock if transaction not in this cycle
lock_aw_valid_d = 1'b1;
load_aw_lock = 1'b1;
end
end
end
end
`FFLARN(lock_aw_valid_q, lock_aw_valid_d, load_aw_lock, '0, clk_i, rst_ni)
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 ( aw_select ),
.push_i ( w_fifo_push ),
.data_o ( w_select ),
.pop_i ( w_fifo_pop )
);
spill_register #(
.T ( aw_chan_t ),
.Bypass ( ~SpillAw ) // Param indicated that we want a spill reg
) i_aw_spill_reg (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.valid_i ( mst_aw_valid ),
.ready_o ( mst_aw_ready ),
.data_i ( mst_aw_chan ),
.valid_o ( mst_req_o.aw_valid ),
.ready_i ( mst_resp_i.aw_ready ),
.data_o ( mst_req_o.aw )
);
//--------------------------------------
// W Channel
//--------------------------------------
// multiplexer
assign mst_w_chan = (!w_fifo_empty && !b_fifo_full) ? slv_reqs_i[w_select].w : '0;
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 &
~b_fifo_full & (w_select == select_t'(i));
end
assign w_fifo_pop = mst_w_valid & mst_w_ready;
fifo_v3 #(
.FALL_THROUGH ( FallThrough ),
.DEPTH ( MaxTrans ),
.dtype ( select_t )
) i_b_fifo (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( 1'b0 ),
.testmode_i( test_i ),
.full_o ( b_fifo_full ),
.empty_o ( b_fifo_empty ),
.usage_o ( ),
.data_i ( w_select ),
.push_i ( w_fifo_pop ), // push the selection for the B channel on W transaction
.data_o ( b_select ),
.pop_i ( b_fifo_pop )
);
spill_register #(
.T ( w_chan_t ),
.Bypass ( ~SpillW )
) i_w_spill_reg (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.valid_i ( mst_w_valid ),
.ready_o ( mst_w_ready ),
.data_i ( mst_w_chan ),
.valid_o ( mst_req_o.w_valid ),
.ready_i ( mst_resp_i.w_ready ),
.data_o ( mst_req_o.w )
);
//--------------------------------------
// B Channel
//--------------------------------------
// replicate B channels
for (genvar i = 0; i < NoSlvPorts; i++) begin : gen_slv_resps_b
assign slv_resps_o[i].b = mst_b_chan;
assign slv_resps_o[i].b_valid = mst_b_valid & ~b_fifo_empty & (b_select == select_t'(i));
end
assign mst_b_ready = ~b_fifo_empty & slv_reqs_i[b_select].b_ready;
assign b_fifo_pop = mst_b_valid & mst_b_ready;
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 ( mst_b_valid ),
.ready_i ( mst_b_ready ),
.data_o ( mst_b_chan )
);
//--------------------------------------
// AR Channel
//--------------------------------------
// unpack AR channel from request/response struct
for (genvar i = 0; i < NoSlvPorts; i++) begin : gen_ar_arb_input
assign slv_ar_chans[i] = slv_reqs_i[i].ar;
assign slv_ar_valids[i] = slv_reqs_i[i].ar_valid;
assign slv_resps_o[i].ar_ready = slv_ar_readies[i];
end
rr_arb_tree #(
.NumIn ( NoSlvPorts ),
.DataType ( ar_chan_t ),
.AxiVldRdy( 1'b1 ),
.LockIn ( 1'b1 )
) i_ar_arbiter (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i( 1'b0 ),
.rr_i ( '0 ),
.req_i ( slv_ar_valids ),
.gnt_o ( slv_ar_readies ),
.data_i ( slv_ar_chans ),
.gnt_i ( ar_ready ),
.req_o ( ar_valid ),
.data_o ( mst_ar_chan ),
.idx_o ( ar_select )
);
// connect the handshake if there is space in the FIFO, no need for valid locking
// as the R response is only allowed, when AR is transferred
assign mst_ar_valid = (!r_fifo_full) ? ar_valid : 1'b0;
assign ar_ready = (!r_fifo_full) ? mst_ar_ready : 1'b0;
assign r_fifo_push = mst_ar_valid & mst_ar_ready;
fifo_v3 #(
.FALL_THROUGH ( FallThrough ),
.DEPTH ( MaxTrans ),
.dtype ( select_t )
) i_r_fifo (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( 1'b0 ),
.testmode_i( test_i ),
.full_o ( r_fifo_full ),
.empty_o ( r_fifo_empty ),
.usage_o ( ),
.data_i ( ar_select ),
.push_i ( r_fifo_push ), // push the selection when w transaction happens
.data_o ( r_select ),
.pop_i ( r_fifo_pop )
);
spill_register #(
.T ( ar_chan_t ),
.Bypass ( ~SpillAr )
) i_ar_spill_reg (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.valid_i ( mst_ar_valid ),
.ready_o ( mst_ar_ready ),
.data_i ( mst_ar_chan ),
.valid_o ( mst_req_o.ar_valid ),
.ready_i ( mst_resp_i.ar_ready ),
.data_o ( mst_req_o.ar )
);
//--------------------------------------
// R Channel
//--------------------------------------
// replicate R channels
for (genvar i = 0; i < NoSlvPorts; i++) begin : gen_slv_resps_r
assign slv_resps_o[i].r = mst_r_chan;
assign slv_resps_o[i].r_valid = mst_r_valid & ~r_fifo_empty & (r_select == select_t'(i));
end
assign mst_r_ready = ~r_fifo_empty & slv_reqs_i[r_select].r_ready;
assign r_fifo_pop = mst_r_valid & mst_r_ready;
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 ( mst_r_valid ),
.ready_i ( mst_r_ready ),
.data_o ( mst_r_chan )
);
end
// pragma translate_off
`ifndef VERILATOR
initial begin: p_assertions
NoPorts: assert (NoSlvPorts > 0) else $fatal("Number of slave ports must be at least 1!");
MaxTnx: assert (MaxTrans > 0) else $fatal("Number of transactions must be at least 1!");
end
`endif
// pragma translate_on
endmodule
// interface wrap
`include "axi/assign.svh"
`include "axi/typedef.svh"
module axi_lite_mux_intf #(
parameter int unsigned AxiAddrWidth = 32'd0,
parameter int unsigned AxiDataWidth = 32'd0,
parameter int unsigned NoSlvPorts = 32'd0, // Number of slave ports
// Maximum number of outstanding transactions per write
parameter int unsigned MaxTrans = 32'd0,
// if enabled, this multiplexer is purely combinatorial
parameter bit FallThrough = 1'b0,
// add spill register on write master ports, adds a cycle latency on write channels
parameter bit SpillAw = 1'b1,
parameter bit SpillW = 1'b0,
parameter bit SpillB = 1'b0,
// add spill register on read master ports, adds a cycle latency on read channels
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
);
typedef logic [AxiAddrWidth-1:0] addr_t;
typedef logic [AxiDataWidth-1:0] data_t;
typedef logic [AxiDataWidth/8-1:0] strb_t;
// channels typedef
`AXI_LITE_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t)
`AXI_LITE_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t)
`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)
req_t [NoSlvPorts-1:0] slv_reqs;
resp_t [NoSlvPorts-1:0] slv_resps;
req_t mst_req;
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])
`AXI_LITE_ASSIGN_FROM_RESP(slv[i], slv_resps[i])
end
`AXI_LITE_ASSIGN_FROM_REQ(mst, mst_req)
`AXI_LITE_ASSIGN_TO_RESP(mst_resp, mst)
axi_lite_mux #(
.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
.NoSlvPorts ( NoSlvPorts ), // Number of slave ports
.MaxTrans ( MaxTrans ),
.FallThrough ( FallThrough ),
.SpillAw ( SpillAw ),
.SpillW ( SpillW ),
.SpillB ( SpillB ),
.SpillAr ( SpillAr ),
.SpillR ( SpillR )
) i_axi_mux (
.clk_i, // Clock
.rst_ni, // Asynchronous reset active low
.test_i, // Test Mode enable
.slv_reqs_i ( slv_reqs ),
.slv_resps_o ( slv_resps ),
.mst_req_o ( mst_req ),
.mst_resp_i ( mst_resp )
);
// pragma translate_off
`ifndef VERILATOR
initial begin: p_assertions
AddrWidth: assert (AxiAddrWidth > 0) else $fatal("Axi Parameter has to be > 0!");
DataWidth: assert (AxiDataWidth > 0) else $fatal("Axi Parameter has to be > 0!");
end
`endif
// pragma translate_on
endmodule

View file

@ -0,0 +1,483 @@
// Copyright (c) 2020 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
//
// Authors:
// - Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>
// - Andreas Kurth <akurth@iis.ee.ethz.ch>
`include "axi/typedef.svh"
`include "common_cells/registers.svh"
/// AXI4-Lite registers with optional read-only and protection features.
///
/// This module contains a parametrizable number of bytes in flip-flops (FFs) and makes them
/// accessible on two interfaces:
/// - as memory-mapped AXI4-Lite slave (ports `axi_req_i` and `axi_resp_o`), and
/// - as wires to directly attach other hardware logic (ports `reg_d_i`, `reg_load_i`, `reg_q_o`,
/// `wr_active_o`, `rd_active_o`).
///
/// ## Address Map
///
/// The address range covered by this module is defined by `RegNumBytes`. The base address of this
/// module *must* be aligned to `RegNumBytes`. The first byte is accessible at offset `0`, the last
/// byte is accessible at offset `RegNumBytes-1`. The slice `[$clog2(RegNumBytes)-1:0]` of a given
/// address is used to decode the accessed byte within this module. Address bits outside that slice
/// are ignored. Accesses to addresses within the slice but with an offset above the last byte are
/// responded with `SLVERR`.
///
/// ## Read-Only Bytes
///
/// Any set of bytes can be configured as read-only by setting the `AxiReadOnly` parameter
/// accordingly. A read-only byte cannot be written via the AXI interface, but it can be changed
/// from the logic interface.
///
/// When one or multiple bytes in a write transaction are read-only, they are not modified. A write
/// transaction is responded with `OKAY` if it wrote at least one byte. Write transactions / that
/// have `wstrb` set *only* for read-only bytes are responded with `SLVERR`.
///
/// This read-only mechanism can be used to expose constants (lookup-table data) as follows.
///
/// ### Exposing Constants
///
/// To make a byte with constant value (e.g., implemented as LUT instead of FF after synthesis)
/// readable from the AXI4-Lite port:
/// - Make the byte read-only from the AXI4-Lite port by setting its `AxiReadOnly` bit to `1`.
/// - Disable loading the byte from logic by driving its `reg_load_i` bit to `0`.
/// - Define the value of the byte by setting its `RegRstVal` entry.
///
/// ## Protection
///
/// This module can be configured to only allow *privileged* and/or *secure* accesses (see A4.7
/// of the AXI4 specification) by setting the `PrivProtOnly` and/or `SecuProtOnly` parameter,
/// respectively.
module axi_lite_regs #(
/// The size of the register field in bytes.
parameter int unsigned RegNumBytes = 32'd0,
/// Address width of the AXI4-Lite port.
///
/// The minimum value of this parameter is `$clog2(RegNumBytes)`.
parameter int unsigned AxiAddrWidth = 32'd0,
/// Data width of the AXI4-Lite port.
parameter int unsigned AxiDataWidth = 32'd0,
/// Only allow *privileged* accesses on the AXI4-Lite port.
///
/// If this parameter is set to `1`, this module only allows reads and writes that have the
/// `AxProt[0]` bit set. If a transaction does not have the `AxProt[0]` bit set, this module
/// replies with `SLVERR` and does not read or write register data.
parameter bit PrivProtOnly = 1'b0,
/// Only allow *secure* accesses on the AXI4-Lite port.
///
/// If this parameter is set to `1`, this module only allows reads and writes that have the
/// `AxProt[1]` bit set. If a transaction does not have the `AxProt[1]` bit set, this module
/// replies with `SLVERR` and does not read or write register data.
parameter bit SecuProtOnly = 1'b0,
/// Define individual bytes as *read-only from the AXI4-Lite port*.
///
/// This parameter is an array with one bit for each byte. If that bit is `0`, the byte can be
/// read and written on the AXI4-Lite port; if that bit is `1`, the byte can only be read on the
/// AXI4-Lite port.
parameter logic [RegNumBytes-1:0] AxiReadOnly = {RegNumBytes{1'b0}},
/// Constant (=**do not overwrite!**); type of a byte is 8 bit.
parameter type byte_t = logic [7:0],
/// Reset value for the whole register array.
///
/// This parameter is an array with one byte value for each byte. At reset, each byte is
/// assigned its value from this array.
parameter byte_t [RegNumBytes-1:0] RegRstVal = {RegNumBytes{8'h00}},
/// Request struct of the AXI4-Lite port.
parameter type req_lite_t = logic,
/// Response struct of the AXI4-Lite port.
parameter type resp_lite_t = logic
) (
/// Rising-edge clock of all ports
input logic clk_i,
/// Asynchronous reset, active low
input logic rst_ni,
/// AXI4-Lite slave request
input req_lite_t axi_req_i,
/// AXI4-Lite slave response
output resp_lite_t axi_resp_o,
/// Signals that a byte is being written from the AXI4-Lite port in the current clock cycle. This
/// signal is asserted regardless of the value of `AxiReadOnly` and can therefore be used by
/// surrounding logic to react to write-on-read-only-byte errors.
output logic [RegNumBytes-1:0] wr_active_o,
/// Signals that a byte is being read from the AXI4-Lite port in the current clock cycle.
output logic [RegNumBytes-1:0] rd_active_o,
/// Input value of each byte. If `reg_load_i` is `1` for a byte in the current clock cycle, the
/// byte register in this module is set to the value of the byte in `reg_d_i` at the next clock
/// edge.
input byte_t [RegNumBytes-1:0] reg_d_i,
/// Load enable of each byte.
///
/// If `reg_load_i` is `1` for a byte defined as non-read-only in a clock cycle, an AXI4-Lite
/// write transaction is stalled when it tries to write the same byte. That is, a write
/// transaction is stalled if all of the following conditions are true for the byte at index `i`:
/// - `AxiReadOnly[i]` is `0`,
/// - `reg_load_i[i]` is `1`,
/// - the bit in `axi_req_i.w.strb` that affects the byte is `1`.
///
/// If unused, set this input to `'0`.
input logic [RegNumBytes-1:0] reg_load_i,
/// The registered value of each byte.
output byte_t [RegNumBytes-1:0] reg_q_o
);
// Define the number of register chunks needed to map all `RegNumBytes` to the AXI channel.
// Eg: `AxiDataWidth == 32'd32`
// AXI strb: 3 2 1 0
// | | | |
// *---------*---------* | | |
// | *-------|-*-------|-* | |
// | | *-----|-|-*-----|-|-* |
// | | | *---|-|-|-*---|-|-|-*
// | | | | | | | | | | | |
// Reg byte: B A 9 8 7 6 5 4 3 2 1 0
// | chunk_2 | chunk_1 | chunk_0 |
localparam int unsigned AxiStrbWidth = AxiDataWidth / 32'd8;
localparam int unsigned NumChunks = cf_math_pkg::ceil_div(RegNumBytes, AxiStrbWidth);
localparam int unsigned ChunkIdxWidth = (NumChunks > 32'd1) ? $clog2(NumChunks) : 32'd1;
// Type of the index to identify a specific register chunk.
typedef logic [ChunkIdxWidth-1:0] chunk_idx_t;
// Find out how many bits of the address are applicable for this module.
// Look at the `AddrWidth` number of LSBs to calculate the multiplexer index of the AXI.
localparam int unsigned AddrWidth = (RegNumBytes > 32'd1) ? ($clog2(RegNumBytes)+1) : 32'd2;
typedef logic [AddrWidth-1:0] addr_t;
// Define the address map which maps each register chunk onto an AXI address.
typedef struct packed {
int unsigned idx;
addr_t start_addr;
addr_t end_addr;
} axi_rule_t;
axi_rule_t [NumChunks-1:0] addr_map;
for (genvar i = 0; i < NumChunks; i++) begin : gen_addr_map
assign addr_map[i] = axi_rule_t'{
idx: i,
start_addr: addr_t'( i * AxiStrbWidth),
end_addr: addr_t'((i+1)* AxiStrbWidth)
};
end
// Channel definitions for spill register
typedef logic [AxiDataWidth-1:0] axi_data_t;
`AXI_LITE_TYPEDEF_B_CHAN_T(b_chan_lite_t)
`AXI_LITE_TYPEDEF_R_CHAN_T(r_chan_lite_t, axi_data_t)
// Register array declarations
byte_t [RegNumBytes-1:0] reg_q, reg_d;
logic [RegNumBytes-1:0] reg_update;
// Write logic
chunk_idx_t aw_chunk_idx;
logic aw_dec_valid;
b_chan_lite_t b_chan;
logic b_valid, b_ready;
logic aw_prot_ok;
logic chunk_loaded, chunk_ro;
// Flag for telling that the protection level is the right one.
assign aw_prot_ok = (PrivProtOnly ? axi_req_i.aw.prot[0] : 1'b1) &
(SecuProtOnly ? axi_req_i.aw.prot[1] : 1'b1);
// Have a flag which is true if any of the bytes inside a chunk are directly loaded.
logic [AxiStrbWidth-1:0] load;
logic [AxiStrbWidth-1:0] read_only;
// Address of the lowest byte byte of a chunk accessed by an AXI write transaction.
addr_t byte_w_addr;
assign byte_w_addr = addr_t'(aw_chunk_idx * AxiStrbWidth);
for (genvar i = 0; i < AxiStrbWidth; i++) begin : gen_load_assign
// Indexed byte address
addr_t reg_w_idx;
assign reg_w_idx = byte_w_addr + addr_t'(i);
// Only assert load flag for non read only bytes.
assign load[i] = (reg_w_idx < RegNumBytes) ?
(reg_load_i[reg_w_idx] && !AxiReadOnly[reg_w_idx]) : 1'b0;
// Flag to find out that all bytes of the chunk are read only.
assign read_only[i] = (reg_w_idx < RegNumBytes) ? AxiReadOnly[reg_w_idx] : 1'b1;
end
// Only assert the loaded flag if there could be a load conflict between a strobe and load
// signal.
assign chunk_loaded = |(load & axi_req_i.w.strb);
assign chunk_ro = &read_only;
// Register write logic.
always_comb begin
automatic addr_t reg_byte_idx = '0;
// default assignments
reg_d = reg_q;
reg_update = '0;
// Channel handshake
axi_resp_o.aw_ready = 1'b0;
axi_resp_o.w_ready = 1'b0;
// Response
b_chan = b_chan_lite_t'{resp: axi_pkg::RESP_SLVERR, default: '0};
b_valid = 1'b0;
// write active flag
wr_active_o = '0;
// Control
// Handle all non AXI register loads.
for (int unsigned i = 0; i < RegNumBytes; i++) begin
if (reg_load_i[i]) begin
reg_d[i] = reg_d_i[i];
reg_update[i] = 1'b1;
end
end
// Handle load from AXI write.
// `b_ready` is allowed to be a condition as it comes from a spill register.
if (axi_req_i.aw_valid && axi_req_i.w_valid && b_ready) begin
// The write can be performed when these conditions are true:
// - AW decode is valid.
// - `axi_req_i.aw.prot` has the right value.
if (aw_dec_valid && aw_prot_ok) begin
// Stall write as long as any direct load is going on in the current chunk.
// Read-only bytes within a chunk have no influence on stalling.
if (!chunk_loaded) begin
// Go through all bytes on the W channel.
for (int unsigned i = 0; i < AxiStrbWidth; i++) begin
reg_byte_idx = byte_w_addr + addr_t'(i);
// Only execute if the byte is mapped onto the register array.
if (reg_byte_idx < RegNumBytes) begin
// Only update the reg from an AXI write if it is not `ReadOnly`.
// Only connect the data and load to the reg, if the byte is written from AXI.
// This allows for simultaneous direct load onto unwritten bytes.
if (!AxiReadOnly[reg_byte_idx] && axi_req_i.w.strb[i]) begin
reg_d[reg_byte_idx] = axi_req_i.w.data[8*i+:8];
reg_update[reg_byte_idx] = 1'b1;
end
wr_active_o[reg_byte_idx] = axi_req_i.w.strb[i];
end
end
b_chan.resp = chunk_ro ? axi_pkg::RESP_SLVERR : axi_pkg::RESP_OKAY;
b_valid = 1'b1;
axi_resp_o.aw_ready = 1'b1;
axi_resp_o.w_ready = 1'b1;
end
end else begin
// Send default B error response on each not allowed write transaction.
b_valid = 1'b1;
axi_resp_o.aw_ready = 1'b1;
axi_resp_o.w_ready = 1'b1;
end
end
end
// Read logic
chunk_idx_t ar_chunk_idx;
logic ar_dec_valid;
r_chan_lite_t r_chan;
logic r_valid, r_ready;
logic ar_prot_ok;
assign ar_prot_ok = (PrivProtOnly ? axi_req_i.ar.prot[0] : 1'b1) &
(SecuProtOnly ? axi_req_i.ar.prot[1] : 1'b1);
// Multiplexer to determine R channel
always_comb begin
automatic int unsigned reg_byte_idx = '0;
// Default R channel throws an error.
r_chan = r_chan_lite_t'{
data: axi_data_t'(32'hBA5E1E55),
resp: axi_pkg::RESP_SLVERR,
default: '0
};
// Default nothing is reading the registers
rd_active_o = '0;
// Read is valid on a chunk
if (ar_dec_valid && ar_prot_ok) begin
// Calculate the corresponding byte index from `ar_chunk_idx`.
for (int unsigned i = 0; i < AxiStrbWidth; i++) begin
reg_byte_idx = unsigned'(ar_chunk_idx) * AxiStrbWidth + i;
// Guard to not index outside the `reg_q_o` array.
if (reg_byte_idx < RegNumBytes) begin
r_chan.data[8*i+:8] = reg_q_o[reg_byte_idx];
rd_active_o[reg_byte_idx] = r_valid & r_ready;
end else begin
r_chan.data[8*i+:8] = 8'h00;
end
end
r_chan.resp = axi_pkg::RESP_OKAY;
end
end
assign r_valid = axi_req_i.ar_valid; // to spill register
assign axi_resp_o.ar_ready = r_ready; // from spill register
// Register array mapping, even read only register can be loaded over `reg_load_i`.
for (genvar i = 0; i < RegNumBytes; i++) begin : gen_rw_regs
`FFLARN(reg_q[i], reg_d[i], reg_update[i], RegRstVal[i], clk_i, rst_ni)
assign reg_q_o[i] = reg_q[i];
end
addr_decode #(
.NoIndices ( NumChunks ),
.NoRules ( NumChunks ),
.addr_t ( addr_t ),
.rule_t ( axi_rule_t )
) i_aw_decode (
.addr_i ( addr_t'(axi_req_i.aw.addr) ), // Only look at the `AddrWidth` LSBs.
.addr_map_i ( addr_map ),
.idx_o ( aw_chunk_idx ),
.dec_valid_o ( aw_dec_valid ),
.dec_error_o ( /*not used*/ ),
.en_default_idx_i ( '0 ),
.default_idx_i ( '0 )
);
addr_decode #(
.NoIndices ( NumChunks ),
.NoRules ( NumChunks ),
.addr_t ( addr_t ),
.rule_t ( axi_rule_t )
) i_ar_decode (
.addr_i ( addr_t'(axi_req_i.ar.addr) ), // Only look at the `AddrWidth` LSBs.
.addr_map_i ( addr_map ),
.idx_o ( ar_chunk_idx ),
.dec_valid_o ( ar_dec_valid ),
.dec_error_o ( /*not used*/ ),
.en_default_idx_i ( '0 ),
.default_idx_i ( '0 )
);
// Add a cycle delay on AXI response, cut all comb paths between slave port inputs and outputs.
spill_register #(
.T ( b_chan_lite_t ),
.Bypass ( 1'b0 )
) i_b_spill_register (
.clk_i,
.rst_ni,
.valid_i ( b_valid ),
.ready_o ( b_ready ),
.data_i ( b_chan ),
.valid_o ( axi_resp_o.b_valid ),
.ready_i ( axi_req_i.b_ready ),
.data_o ( axi_resp_o.b )
);
// Add a cycle delay on AXI response, cut all comb paths between slave port inputs and outputs.
spill_register #(
.T ( r_chan_lite_t ),
.Bypass ( 1'b0 )
) i_r_spill_register (
.clk_i,
.rst_ni,
.valid_i ( r_valid ),
.ready_o ( r_ready ),
.data_i ( r_chan ),
.valid_o ( axi_resp_o.r_valid ),
.ready_i ( axi_req_i.r_ready ),
.data_o ( axi_resp_o.r )
);
// Validate parameters.
// pragma translate_off
`ifndef VERILATOR
initial begin: p_assertions
assert (RegNumBytes > 32'd0) else
$fatal(1, "The number of bytes must be at least 1!");
assert (AxiAddrWidth >= AddrWidth) else
$fatal(1, "AxiAddrWidth is not wide enough, has to be at least %0d-bit wide!", AddrWidth);
assert ($bits(axi_req_i.aw.addr) == AxiAddrWidth) else
$fatal(1, "AddrWidth does not match req_i.aw.addr!");
assert ($bits(axi_req_i.ar.addr) == AxiAddrWidth) else
$fatal(1, "AddrWidth does not match req_i.ar.addr!");
assert (AxiDataWidth == $bits(axi_req_i.w.data)) else
$fatal(1, "AxiDataWidth has to be: AxiDataWidth == $bits(axi_req_i.w.data)!");
assert (AxiDataWidth == $bits(axi_resp_o.r.data)) else
$fatal(1, "AxiDataWidth has to be: AxiDataWidth == $bits(axi_resp_o.r.data)!");
assert (RegNumBytes == $bits(AxiReadOnly)) else
$fatal(1, "Each register needs a `ReadOnly` flag!");
end
default disable iff (~rst_ni);
for (genvar i = 0; i < RegNumBytes; i++) begin
assert property (@(posedge clk_i) (!reg_load_i[i] && AxiReadOnly[i] |=> $stable(reg_q_o[i])))
else $fatal(1, "Read-only register at `byte_index: %0d` was changed by AXI!", i);
end
`endif
// pragma translate_on
endmodule
`include "axi/assign.svh"
/// Interface variant of [`axi_lite_regs`](module.axi_lite_regs).
///
/// See the documentation of the main module for the definition of ports and parameters.
module axi_lite_regs_intf #(
parameter type byte_t = logic [7:0],
parameter int unsigned REG_NUM_BYTES = 32'd0,
parameter int unsigned AXI_ADDR_WIDTH = 32'd0,
parameter int unsigned AXI_DATA_WIDTH = 32'd0,
parameter bit PRIV_PROT_ONLY = 1'd0,
parameter bit SECU_PROT_ONLY = 1'd0,
parameter logic [REG_NUM_BYTES-1:0] AXI_READ_ONLY = {REG_NUM_BYTES{1'b0}},
parameter byte_t [REG_NUM_BYTES-1:0] REG_RST_VAL = {REG_NUM_BYTES{8'h00}}
) (
input logic clk_i,
input logic rst_ni,
AXI_LITE.Slave slv,
output logic [REG_NUM_BYTES-1:0] wr_active_o,
output logic [REG_NUM_BYTES-1:0] rd_active_o,
input byte_t [REG_NUM_BYTES-1:0] reg_d_i,
input logic [REG_NUM_BYTES-1:0] reg_load_i,
output byte_t [REG_NUM_BYTES-1:0] reg_q_o
);
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;
`AXI_LITE_TYPEDEF_AW_CHAN_T(aw_chan_lite_t, addr_t)
`AXI_LITE_TYPEDEF_W_CHAN_T(w_chan_lite_t, data_t, strb_t)
`AXI_LITE_TYPEDEF_B_CHAN_T(b_chan_lite_t)
`AXI_LITE_TYPEDEF_AR_CHAN_T(ar_chan_lite_t, addr_t)
`AXI_LITE_TYPEDEF_R_CHAN_T(r_chan_lite_t, data_t)
`AXI_LITE_TYPEDEF_REQ_T(req_lite_t, aw_chan_lite_t, w_chan_lite_t, ar_chan_lite_t)
`AXI_LITE_TYPEDEF_RESP_T(resp_lite_t, b_chan_lite_t, r_chan_lite_t)
req_lite_t axi_lite_req;
resp_lite_t axi_lite_resp;
`AXI_LITE_ASSIGN_TO_REQ(axi_lite_req, slv)
`AXI_LITE_ASSIGN_FROM_RESP(slv, axi_lite_resp)
axi_lite_regs #(
.RegNumBytes ( REG_NUM_BYTES ),
.AxiAddrWidth ( AXI_ADDR_WIDTH ),
.AxiDataWidth ( AXI_DATA_WIDTH ),
.PrivProtOnly ( PRIV_PROT_ONLY ),
.SecuProtOnly ( SECU_PROT_ONLY ),
.AxiReadOnly ( AXI_READ_ONLY ),
.RegRstVal ( REG_RST_VAL ),
.req_lite_t ( req_lite_t ),
.resp_lite_t ( resp_lite_t )
) i_axi_lite_regs (
.clk_i,
.rst_ni,
.axi_req_i ( axi_lite_req ),
.axi_resp_o ( axi_lite_resp ),
.wr_active_o,
.rd_active_o,
.reg_d_i,
.reg_load_i,
.reg_q_o
);
// Validate parameters.
// pragma translate_off
`ifndef VERILATOR
initial begin: p_assertions
assert (AXI_ADDR_WIDTH == $bits(slv.aw_addr))
else $fatal(1, "AXI_ADDR_WIDTH does not match slv interface!");
assert (AXI_DATA_WIDTH == $bits(slv.w_data))
else $fatal(1, "AXI_DATA_WIDTH does not match slv interface!");
end
`endif
// pragma translate_on
endmodule

View file

@ -0,0 +1,494 @@
// Copyright (c) 2020 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
//
// Authors:
// - Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>
// - Andreas Kurth <akurth@iis.ee.ethz.ch>
// - Samuel Riedel <sriedel@iis.ee.ethz.ch>
// Description: AXI4-Lite to APB4 bridge
//
// This module has one AXI4-Lite slave port and is capable of generating
// APB4 requests for multiple APB4 slave modules. The address and data widths
// of AXI4-Lite and APB4 have to be the same for both ports and are enforced over assertions.
// The selection of the APB4 slave is handled by the `addr_decode` module from `common_cells`.
// This module will answer with a `axi_pkg::RESP_DECERR` when a AXI4-Lite AW or AR request
// is not in the address map of the module and `no` APB4 request is made.
//
// The type of the APB4 request is required to look like:
// typedef struct packed {
// addr_t paddr; // same as AXI4-Lite
// prot_t pprot; // same as AXI4-Lite, specification is the same
// logic psel; // each APB4 slave has its own single-bit psel
// logic penable; // enable signal shows second APB4 cycle
// logic pwrite; // write enable
// data_t pwdata; // write data, comes from W channel
// strb_t pstrb; // write strb, comes from W channel
// } apb_req_t;
// For every APB4 slave, the `psel` field is only asserted when the decoded address matches that
// slave. If `psel` is deasserted, the value of the other fields may be undefined.
//
// The type of the APB4 response is required to look like:
// typedef struct packed {
// logic pready; // slave signals that it is ready
// data_t prdata; // read data, connects to R channel
// logic pslverr; // gets translated into either `axi_pkg::RESP_OK` or `axi_pkg::RESP_SLVERR`
// } apb_resp_t;
// Each connected `apb_resp`, has to be connected to the corresponding port index. The module
// routes the response depending on the `apb_req.psel` bit and `apb_req.pwrite` either to the
// AXI4Lite B channel for writes and to the R channel for reads.
`include "common_cells/registers.svh"
module axi_lite_to_apb #(
parameter int unsigned NoApbSlaves = 32'd1, // Number of connected APB slaves
parameter int unsigned NoRules = 32'd1, // Number of APB address rules
parameter int unsigned AddrWidth = 32'd32, // Address width
parameter int unsigned DataWidth = 32'd32, // Data width
parameter bit PipelineRequest = 1'b0, // Pipeline request path
parameter bit PipelineResponse = 1'b0, // Pipeline response path
parameter type axi_lite_req_t = logic, // AXI4-Lite request struct
parameter type axi_lite_resp_t = logic, // AXI4-Lite response sruct
parameter type apb_req_t = logic, // APB4 request struct
parameter type apb_resp_t = logic, // APB4 response struct
parameter type rule_t = logic // Address Decoder rule from `common_cells`
) (
input logic clk_i, // Clock
input logic rst_ni, // Asynchronous reset active low
// AXI LITE slave port
input axi_lite_req_t axi_lite_req_i,
output axi_lite_resp_t axi_lite_resp_o,
// APB master port
output apb_req_t [NoApbSlaves-1:0] apb_req_o,
input apb_resp_t [NoApbSlaves-1:0] apb_resp_i,
// APB Slave Address Map
input rule_t [NoRules-1:0] addr_map_i
);
localparam logic RD = 1'b0; // Encode index of a read request
localparam logic WR = 1'b1; // Encode index of a write request
localparam int unsigned SelIdxWidth = (NoApbSlaves > 32'd1) ? $clog2(NoApbSlaves) : 32'd1;
typedef logic [AddrWidth-1:0] addr_t; // AXI4-Lite, APB4 and rule_t addr width
typedef logic [DataWidth-1:0] data_t; // AXI4-Lite and APB4 data width
typedef logic [DataWidth/8-1:0] strb_t; // AXI4-Lite and APB4 strb width
typedef logic [SelIdxWidth-1:0] sel_idx_t; // Selection index from addr_decode
typedef struct packed {
addr_t addr;
axi_pkg::prot_t prot; // prot has the exact same bit mapping in AXI4-Lite as In APB v2.0
data_t data;
strb_t strb;
logic write;
} int_req_t; // request type generated by the read and write channel, internal
typedef struct packed {
data_t data; // read data from APB
axi_pkg::resp_t resp; // response bit from APB
} int_resp_t; // internal
typedef enum logic {
Setup = 1'b0, // APB in Idle or Setup
Access = 1'b1 // APB in Access
} apb_state_e;
// Signals from AXI4-Lite slave to arbitration tree
int_req_t [1:0] axi_req;
logic [1:0] axi_req_valid, axi_req_ready;
// Signals from response spill registers
axi_pkg::resp_t axi_bresp;
logic axi_bresp_valid, axi_bresp_ready;
int_resp_t axi_rresp;
logic axi_rresp_valid, axi_rresp_ready;
// -----------------------------------------------------------------------------------------------
// AXI4-Lite slave
// -----------------------------------------------------------------------------------------------
// read request
assign axi_req[RD] = '{
addr: axi_lite_req_i.ar.addr,
prot: axi_lite_req_i.ar.prot,
data: '0,
strb: '0,
write: RD
};
assign axi_req_valid[RD] = axi_lite_req_i.ar_valid;
// write request
assign axi_req[WR] = '{
addr: axi_lite_req_i.aw.addr,
prot: axi_lite_req_i.aw.prot,
data: axi_lite_req_i.w.data,
strb: axi_lite_req_i.w.strb,
write: WR
};
assign axi_req_valid[WR] = axi_lite_req_i.aw_valid & axi_lite_req_i.w_valid;
assign axi_lite_resp_o = '{
aw_ready: axi_req_valid[WR] & axi_req_ready[WR], // if AXI AW & W valid & tree gnt_o[WR]
w_ready: axi_req_valid[WR] & axi_req_ready[WR], // if AXI AW & W valid & tree gnt_o[WR]
b: '{resp: axi_bresp}, // from spill reg
b_valid: axi_bresp_valid, // from spill reg
ar_ready: axi_req_valid[RD] & axi_req_ready[RD], // if AXI AR valid and tree gnt[RD]
r: '{data: axi_rresp.data, resp: axi_rresp.resp}, // from spill reg
r_valid: axi_rresp_valid // from spill reg
};
// -----------------------------------------------------------------------------------------------
// Arbitration between write and read plus spill register for request and response
// -----------------------------------------------------------------------------------------------
int_req_t arb_req, apb_req;
logic arb_req_valid, arb_req_ready, apb_req_valid, apb_req_ready;
axi_pkg::resp_t apb_wresp;
logic apb_wresp_valid, apb_wresp_ready;
int_resp_t apb_rresp;
logic apb_rresp_valid, apb_rresp_ready;
rr_arb_tree #(
.NumIn ( 32'd2 ),
.DataType ( int_req_t ),
.ExtPrio ( 1'b0 ),
.AxiVldRdy( 1'b1 ),
.LockIn ( 1'b1 )
) i_req_arb (
.clk_i,
.rst_ni,
.flush_i ( '0 ),
.rr_i ( '0 ),
.req_i ( axi_req_valid ),
.gnt_o ( axi_req_ready ),
.data_i ( axi_req ),
.gnt_i ( arb_req_ready ),
.req_o ( arb_req_valid ),
.data_o ( arb_req ),
.idx_o ( /*not used*/ )
);
if (PipelineRequest) begin : gen_req_spill
spill_register #(
.T ( int_req_t ),
.Bypass ( 1'b0 )
) i_req_spill (
.clk_i,
.rst_ni,
.valid_i ( arb_req_valid ),
.ready_o ( arb_req_ready ),
.data_i ( arb_req ),
.valid_o ( apb_req_valid ),
.ready_i ( apb_req_ready ),
.data_o ( apb_req )
);
end else begin : gen_req_ft_reg
fall_through_register #(
.T ( int_req_t )
) i_req_ft_reg (
.clk_i,
.rst_ni,
.clr_i ( 1'b0 ),
.testmode_i ( 1'b0 ),
.valid_i ( arb_req_valid ),
.ready_o ( arb_req_ready ),
.data_i ( arb_req ),
.valid_o ( apb_req_valid ),
.ready_i ( apb_req_ready ),
.data_o ( apb_req )
);
end
if (PipelineResponse) begin : gen_resp_spill
spill_register #(
.T ( axi_pkg::resp_t ),
.Bypass ( 1'b0 )
) i_write_resp_spill (
.clk_i,
.rst_ni,
.valid_i ( apb_wresp_valid ),
.ready_o ( apb_wresp_ready ),
.data_i ( apb_wresp ),
.valid_o ( axi_bresp_valid ),
.ready_i ( axi_lite_req_i.b_ready ),
.data_o ( axi_bresp )
);
spill_register #(
.T ( int_resp_t ),
.Bypass ( 1'b0 )
) i_read_resp_spill (
.clk_i,
.rst_ni,
.valid_i ( apb_rresp_valid ),
.ready_o ( apb_rresp_ready ),
.data_i ( apb_rresp ),
.valid_o ( axi_rresp_valid ),
.ready_i ( axi_lite_req_i.r_ready ),
.data_o ( axi_rresp )
);
end else begin : gen_resp_ft_reg
fall_through_register #(
.T ( axi_pkg::resp_t )
) i_write_resp_ft_reg (
.clk_i,
.rst_ni,
.clr_i ( 1'b0 ),
.testmode_i ( 1'b0 ),
.valid_i ( apb_wresp_valid ),
.ready_o ( apb_wresp_ready ),
.data_i ( apb_wresp ),
.valid_o ( axi_bresp_valid ),
.ready_i ( axi_lite_req_i.b_ready ),
.data_o ( axi_bresp )
);
fall_through_register #(
.T ( int_resp_t )
) i_read_resp_ft_reg (
.clk_i,
.rst_ni,
.clr_i ( 1'b0 ),
.testmode_i ( 1'b0 ),
.valid_i ( apb_rresp_valid ),
.ready_o ( apb_rresp_ready ),
.data_i ( apb_rresp ),
.valid_o ( axi_rresp_valid ),
.ready_i ( axi_lite_req_i.r_ready ),
.data_o ( axi_rresp )
);
end
// -----------------------------------------------------------------------------------------------
// APB master FSM
// -----------------------------------------------------------------------------------------------
// APB access state machine
apb_state_e apb_state_q, apb_state_d;
logic apb_update;
// output of address decoder to determine PSELx signal
logic apb_dec_valid;
sel_idx_t apb_sel_idx;
addr_decode #(
.NoIndices( NoApbSlaves ),
.NoRules ( NoRules ),
.addr_t ( addr_t ),
.rule_t ( rule_t )
) i_apb_decode (
.addr_i ( apb_req.addr ),
.addr_map_i ( addr_map_i ),
.idx_o ( apb_sel_idx ),
.dec_valid_o ( apb_dec_valid ), // when not valid -> decode error
.dec_error_o ( /*not used*/ ),
.en_default_idx_i ( '0 ),
.default_idx_i ( '0 )
);
always_comb begin
// default assignments
apb_state_d = apb_state_q;
apb_update = 1'b0;
apb_req_o = '0;
apb_req_ready = 1'b0;
// response defaults to the two response spill registers
apb_wresp = axi_pkg::RESP_SLVERR;
apb_wresp_valid = 1'b0;
apb_rresp = '{data: data_t'(32'hDEA110C8), resp: axi_pkg::RESP_SLVERR};
apb_rresp_valid = 1'b0;
unique case (apb_state_q)
Setup: begin
// `Idle` and `Setup` steps
// can check here for readiness, because the response goes into spill_registers
if (apb_req_valid && apb_wresp_ready && apb_rresp_ready) begin
if (apb_dec_valid) begin
// `Setup` step
// set the request output
apb_req_o[apb_sel_idx] = '{
paddr: apb_req.addr,
pprot: apb_req.prot,
psel: 1'b1,
penable: 1'b0,
pwrite: apb_req.write,
pwdata: apb_req.data,
pstrb: apb_req.strb
};
apb_state_d = Access;
apb_update = 1'b1;
end else begin
// decode error, generate error and do not generate APB request, pop it
apb_req_ready = 1'b1;
if (apb_req.write) begin
apb_wresp = axi_pkg::RESP_DECERR;
apb_wresp_valid = 1'b1;
end else begin
apb_rresp.resp = axi_pkg::RESP_DECERR;
apb_rresp_valid = 1'b1;
end
end
end
end
Access: begin
// `Access` step
apb_req_o[apb_sel_idx] = '{
paddr: apb_req.addr,
pprot: apb_req.prot,
psel: 1'b1,
penable: 1'b1,
pwrite: apb_req.write,
pwdata: apb_req.data,
pstrb: apb_req.strb
};
if (apb_resp_i[apb_sel_idx].pready) begin
// transfer, pop the request, generate response and update state
apb_req_ready = 1'b1;
// we are only in this state if the response spill registers are ready anyway
if (apb_req.write) begin
apb_wresp = apb_resp_i[apb_sel_idx].pslverr ?
axi_pkg::RESP_SLVERR : axi_pkg::RESP_OKAY;
apb_wresp_valid = 1'b1;
end else begin
apb_rresp.data = apb_resp_i[apb_sel_idx].prdata;
apb_rresp.resp = apb_resp_i[apb_sel_idx].pslverr ?
axi_pkg::RESP_SLVERR : axi_pkg::RESP_OKAY;
apb_rresp_valid = 1'b1;
end
apb_state_d = Setup;
apb_update = 1'b1;
end
end
default: /* do nothing */ ;
endcase
end
`FFLARN(apb_state_q, apb_state_d, apb_update, Setup, clk_i, rst_ni)
// parameter check
// pragma translate_off
`ifndef VERILATOR
initial begin : check_params
addr_width: assert ($bits(axi_lite_req_i.aw.addr ) == $bits(apb_req_o[0].paddr)) else
$fatal(1, $sformatf("AXI4-Lite and APB address width not equal"));
wdata_width: assert ($bits(axi_lite_req_i.w.data ) == $bits(apb_req_o[0].pwdata)) else
$fatal(1, $sformatf("AXI4-Lite and APB write data width not equal"));
strb_width: assert ($bits(axi_lite_req_i.w.strb ) == $bits(apb_req_o[0].pstrb)) else
$fatal(1, $sformatf("AXI4-Lite and APB strobe width not equal"));
rdata_width: assert ($bits(axi_lite_resp_o.r.data ) == $bits(apb_resp_i[0].prdata)) else
$fatal(1, $sformatf("AXI4-Lite and APB read data width not equal"));
sel_width: assert ($bits(apb_req_o[0].psel) == 32'd1) else
$fatal(1, $sformatf("APB psel signal has to have a width of 1'b1"));
end
`endif
// pragma translate_on
endmodule
`include "axi/typedef.svh"
`include "axi/assign.svh"
module axi_lite_to_apb_intf #(
parameter int unsigned NoApbSlaves = 32'd1, // Number of connected APB slaves
parameter int unsigned NoRules = 32'd1, // Number of APB address rules
parameter int unsigned AddrWidth = 32'd32, // Address width
parameter int unsigned DataWidth = 32'd32, // Data width
parameter bit PipelineRequest = 1'b0, // Pipeline request path
parameter bit PipelineResponse = 1'b0, // Pipeline response path
parameter type rule_t = logic, // Address Decoder rule from `common_cells`
// DEPENDENT PARAMERETS, DO NOT OVERWRITE!
parameter type addr_t = logic [AddrWidth-1:0],
parameter type data_t = logic [DataWidth-1:0],
parameter type strb_t = logic [DataWidth/8-1:0],
parameter type sel_t = logic [NoApbSlaves-1:0]
) (
input logic clk_i, // Clock
input logic rst_ni, // Asynchronous reset active low
// AXI LITE slave port
AXI_LITE.Slave slv,
// APB master port
output addr_t paddr_o,
output logic [2:0] pprot_o,
output sel_t pselx_o,
output logic penable_o,
output logic pwrite_o,
output data_t pwdata_o,
output strb_t pstrb_o,
input logic [NoApbSlaves-1:0] pready_i,
input data_t [NoApbSlaves-1:0] prdata_i,
input [NoApbSlaves-1:0] pslverr_i,
// APB Slave Address Map
input rule_t [NoRules-1:0] addr_map_i
);
localparam int unsigned SelIdxWidth = NoApbSlaves > 1 ? $clog2(NoApbSlaves) : 1;
typedef struct packed {
addr_t paddr; // same as AXI4-Lite
axi_pkg::prot_t pprot; // same as AXI4-Lite, specification is the same
logic psel; // onehot, one psel line per connected APB4 slave
logic penable; // enable signal shows second APB4 cycle
logic pwrite; // write enable
data_t pwdata; // write data, comes from W channel
strb_t pstrb; // write strb, comes from W channel
} apb_req_t;
typedef struct packed {
logic pready; // slave signals that it is ready
data_t prdata; // read data, connects to R channel
logic pslverr; // gets translated into either `axi_pkg::RESP_OK` or `axi_pkg::RESP_SLVERR`
} apb_resp_t;
`AXI_LITE_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t)
`AXI_LITE_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t)
`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(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)
axi_req_t axi_req;
axi_resp_t axi_resp;
apb_req_t [NoApbSlaves-1:0] apb_req;
apb_resp_t [NoApbSlaves-1:0] apb_resp;
logic [SelIdxWidth-1:0] apb_sel;
`AXI_LITE_ASSIGN_TO_REQ(axi_req, slv)
`AXI_LITE_ASSIGN_FROM_RESP(slv, axi_resp)
onehot_to_bin #(
.ONEHOT_WIDTH ( NoApbSlaves )
) i_onehot_to_bin (
.onehot ( pselx_o ),
.bin ( apb_sel )
);
assign paddr_o = apb_req[apb_sel].paddr;
assign pprot_o = apb_req[apb_sel].pprot;
assign penable_o = apb_req[apb_sel].penable;
assign pwrite_o = apb_req[apb_sel].pwrite;
assign pwdata_o = apb_req[apb_sel].pwdata;
assign pstrb_o = apb_req[apb_sel].pstrb;
for (genvar i = 0; i < NoApbSlaves; i++) begin : gen_apb_resp_assign
assign pselx_o[i] = apb_req[i].psel;
assign apb_resp[i].pready = pready_i[i];
assign apb_resp[i].prdata = prdata_i[i];
assign apb_resp[i].pslverr = pslverr_i[i];
end
axi_lite_to_apb #(
.NoApbSlaves ( NoApbSlaves ),
.NoRules ( NoRules ),
.AddrWidth ( AddrWidth ),
.DataWidth ( DataWidth ),
.PipelineRequest ( PipelineRequest ),
.PipelineResponse ( PipelineResponse ),
.axi_lite_req_t ( axi_req_t ),
.axi_lite_resp_t ( axi_resp_t ),
.apb_req_t ( apb_req_t ),
.apb_resp_t ( apb_resp_t ),
.rule_t ( rule_t )
) i_axi_lite_to_apb (
.clk_i, // Clock
.rst_ni, // Asynchronous reset active low
// AXI LITE slave port
.axi_lite_req_i ( axi_req ),
.axi_lite_resp_o ( axi_resp ),
// APB master port
.apb_req_o ( apb_req ),
.apb_resp_i ( apb_resp ),
// APB Slave Address Map
.addr_map_i
);
endmodule

View file

@ -0,0 +1,160 @@
// Copyright (c) 2014-2018 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:
// - Fabian Schuiki <fschuiki@iis.ee.ethz.ch>
// - Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>
// - Andreas Kurth <akurth@iis.ee.ethz.ch>
/// An AXI4-Lite to AXI4 adapter.
module axi_lite_to_axi #(
parameter int unsigned AxiDataWidth = 32'd0,
// LITE AXI structs
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
) (
// Slave AXI LITE port
input req_lite_t slv_req_lite_i,
output resp_lite_t slv_resp_lite_o,
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
);
localparam int unsigned AxiSize = axi_pkg::size_t'($unsigned($clog2(AxiDataWidth/8)));
// request assign
assign mst_req_o = '{
aw: '{
addr: slv_req_lite_i.aw.addr,
prot: slv_req_lite_i.aw.prot,
size: AxiSize,
burst: axi_pkg::BURST_FIXED,
cache: slv_aw_cache_i,
default: '0
},
aw_valid: slv_req_lite_i.aw_valid,
w: '{
data: slv_req_lite_i.w.data,
strb: slv_req_lite_i.w.strb,
last: 1'b1,
default: '0
},
w_valid: slv_req_lite_i.w_valid,
b_ready: slv_req_lite_i.b_ready,
ar: '{
addr: slv_req_lite_i.ar.addr,
prot: slv_req_lite_i.ar.prot,
size: AxiSize,
burst: axi_pkg::BURST_FIXED,
cache: slv_ar_cache_i,
default: '0
},
ar_valid: slv_req_lite_i.ar_valid,
r_ready: slv_req_lite_i.r_ready,
default: '0
};
// response assign
assign slv_resp_lite_o = '{
aw_ready: mst_resp_i.aw_ready,
w_ready: mst_resp_i.w_ready,
b: '{
resp: mst_resp_i.b.resp,
default: '0
},
b_valid: mst_resp_i.b_valid,
ar_ready: mst_resp_i.ar_ready,
r: '{
data: mst_resp_i.r.data,
resp: mst_resp_i.r.resp,
default: '0
},
r_valid: mst_resp_i.r_valid,
default: '0
};
// pragma translate_off
`ifndef VERILATOR
initial begin
assert (AxiDataWidth > 0) else $fatal(1, "Data width must be non-zero!");
end
`endif
// pragma translate_on
endmodule
module axi_lite_to_axi_intf #(
parameter int unsigned AXI_DATA_WIDTH = 32'd0
) (
AXI_LITE.Slave in,
input axi_pkg::cache_t slv_aw_cache_i,
input axi_pkg::cache_t slv_ar_cache_i,
AXI_BUS.Master out
);
localparam int unsigned AxiSize = axi_pkg::size_t'($unsigned($clog2(AXI_DATA_WIDTH/8)));
// pragma translate_off
initial begin
assert(in.AXI_ADDR_WIDTH == out.AXI_ADDR_WIDTH);
assert(in.AXI_DATA_WIDTH == out.AXI_DATA_WIDTH);
assert(AXI_DATA_WIDTH == out.AXI_DATA_WIDTH);
end
// pragma translate_on
assign out.aw_id = '0;
assign out.aw_addr = in.aw_addr;
assign out.aw_len = '0;
assign out.aw_size = AxiSize;
assign out.aw_burst = axi_pkg::BURST_FIXED;
assign out.aw_lock = '0;
assign out.aw_cache = slv_aw_cache_i;
assign out.aw_prot = '0;
assign out.aw_qos = '0;
assign out.aw_region = '0;
assign out.aw_atop = '0;
assign out.aw_user = '0;
assign out.aw_valid = in.aw_valid;
assign in.aw_ready = out.aw_ready;
assign out.w_data = in.w_data;
assign out.w_strb = in.w_strb;
assign out.w_last = '1;
assign out.w_user = '0;
assign out.w_valid = in.w_valid;
assign in.w_ready = out.w_ready;
assign in.b_resp = out.b_resp;
assign in.b_valid = out.b_valid;
assign out.b_ready = in.b_ready;
assign out.ar_id = '0;
assign out.ar_addr = in.ar_addr;
assign out.ar_len = '0;
assign out.ar_size = AxiSize;
assign out.ar_burst = axi_pkg::BURST_FIXED;
assign out.ar_lock = '0;
assign out.ar_cache = slv_ar_cache_i;
assign out.ar_prot = '0;
assign out.ar_qos = '0;
assign out.ar_region = '0;
assign out.ar_user = '0;
assign out.ar_valid = in.ar_valid;
assign in.ar_ready = out.ar_ready;
assign in.r_data = out.r_data;
assign in.r_resp = out.r_resp;
assign in.r_valid = out.r_valid;
assign out.r_ready = in.r_ready;
endmodule

View file

@ -0,0 +1,305 @@
// Copyright (c) 2020 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
//
// Authors:
// - Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>
// - Fabian Schuiki <fschuiki@iis.ee.ethz.ch>
// - Andreas Kurth <akurth@iis.ee.ethz.ch>
// axi_lite_xbar: Fully-connected AXI4-Lite crossbar.
// See `doc/axi_lite_xbar.md` for the documentation,
// including the definition of parameters and ports.
`include "axi/typedef.svh"
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,
// 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 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
);
typedef logic [Cfg.AxiAddrWidth-1:0] addr_t;
typedef logic [Cfg.AxiDataWidth-1:0] data_t;
typedef logic [Cfg.AxiDataWidth/8-1:0] strb_t;
// to account for the decoding error slave
typedef logic [$clog2(Cfg.NoMstPorts + 1)-1:0] mst_port_idx_t;
// full AXI typedef for the decode error slave, id_t and user_t are logic and will be
// removed during logic optimization as they are stable
`AXI_TYPEDEF_AW_CHAN_T(full_aw_chan_t, addr_t, logic, logic)
`AXI_TYPEDEF_W_CHAN_T(full_w_chan_t, data_t, strb_t, logic)
`AXI_TYPEDEF_B_CHAN_T(full_b_chan_t, logic, logic)
`AXI_TYPEDEF_AR_CHAN_T(full_ar_chan_t, addr_t, logic, logic)
`AXI_TYPEDEF_R_CHAN_T(full_r_chan_t, data_t, logic, logic)
`AXI_TYPEDEF_REQ_T(full_req_t, full_aw_chan_t, full_w_chan_t, full_ar_chan_t)
`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;
// 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;
for (genvar i = 0; i < Cfg.NoSlvPorts; i++) begin : gen_slv_port_demux
logic [MstIdxWidth-1:0] dec_aw, dec_ar;
mst_port_idx_t slv_aw_select, slv_ar_select;
logic dec_aw_error;
logic dec_ar_error;
full_req_t decerr_req;
full_resp_t decerr_resp;
addr_decode #(
.NoIndices ( Cfg.NoMstPorts ),
.NoRules ( Cfg.NoAddrRules ),
.addr_t ( addr_t ),
.rule_t ( rule_t )
) i_axi_aw_decode (
.addr_i ( slv_ports_req_i[i].aw.addr ),
.addr_map_i ( addr_map_i ),
.idx_o ( dec_aw ),
.dec_valid_o ( /*not used*/ ),
.dec_error_o ( dec_aw_error ),
.en_default_idx_i ( en_default_mst_port_i[i] ),
.default_idx_i ( default_mst_port_i[i] )
);
addr_decode #(
.NoIndices ( Cfg.NoMstPorts ),
.addr_t ( addr_t ),
.NoRules ( Cfg.NoAddrRules ),
.rule_t ( rule_t )
) i_axi_ar_decode (
.addr_i ( slv_ports_req_i[i].ar.addr ),
.addr_map_i ( addr_map_i ),
.idx_o ( dec_ar ),
.dec_valid_o ( /*not used*/ ),
.dec_error_o ( dec_ar_error ),
.en_default_idx_i ( en_default_mst_port_i[i] ),
.default_idx_i ( default_mst_port_i[i] )
);
assign slv_aw_select = (dec_aw_error) ?
mst_port_idx_t'(Cfg.NoMstPorts) : mst_port_idx_t'(dec_aw);
assign slv_ar_select = (dec_ar_error) ?
mst_port_idx_t'(Cfg.NoMstPorts) : mst_port_idx_t'(dec_ar);
// make sure that the default slave does not get changed, if there is an unserved Ax
// pragma translate_off
`ifndef VERILATOR
default disable iff (~rst_ni);
default_aw_mst_port_en: assert property(
@(posedge clk_i) (slv_ports_req_i[i].aw_valid && !slv_ports_resp_o[i].aw_ready)
|=> $stable(en_default_mst_port_i[i]))
else $fatal (1, $sformatf("It is not allowed to change the default mst port\
enable, when there is an unserved Aw beat. Slave Port: %0d", i));
default_aw_mst_port: assert property(
@(posedge clk_i) (slv_ports_req_i[i].aw_valid && !slv_ports_resp_o[i].aw_ready)
|=> $stable(default_mst_port_i[i]))
else $fatal (1, $sformatf("It is not allowed to change the default mst port\
when there is an unserved Aw beat. Slave Port: %0d", i));
default_ar_mst_port_en: assert property(
@(posedge clk_i) (slv_ports_req_i[i].ar_valid && !slv_ports_resp_o[i].ar_ready)
|=> $stable(en_default_mst_port_i[i]))
else $fatal (1, $sformatf("It is not allowed to change the enable, when\
there is an unserved Ar beat. Slave Port: %0d", i));
default_ar_mst_port: assert property(
@(posedge clk_i) (slv_ports_req_i[i].ar_valid && !slv_ports_resp_o[i].ar_ready)
|=> $stable(default_mst_port_i[i]))
else $fatal (1, $sformatf("It is not allowed to change the default mst port\
when there is an unserved Ar beat. Slave Port: %0d", i));
`endif
// pragma translate_on
axi_lite_demux #(
.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 ),
.NoMstPorts ( Cfg.NoMstPorts + 1 ),
.MaxTrans ( Cfg.MaxMstTrans ),
.FallThrough ( Cfg.FallThrough ),
.SpillAw ( Cfg.LatencyMode[9] ),
.SpillW ( Cfg.LatencyMode[8] ),
.SpillB ( Cfg.LatencyMode[7] ),
.SpillAr ( Cfg.LatencyMode[6] ),
.SpillR ( Cfg.LatencyMode[5] )
) i_axi_lite_demux (
.clk_i, // Clock
.rst_ni, // Asynchronous reset active low
.test_i, // Testmode enable
.slv_req_i ( slv_ports_req_i[i] ),
.slv_aw_select_i ( slv_aw_select ),
.slv_ar_select_i ( slv_ar_select ),
.slv_resp_o ( slv_ports_resp_o[i] ),
.mst_reqs_o ( slv_reqs[i] ),
.mst_resps_i ( slv_resps[i] )
);
// connect the decode error module to the last index of the demux master port
// 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 )
) i_dec_err_conv (
.slv_req_lite_i ( slv_reqs[i][Cfg.NoMstPorts] ),
.slv_resp_lite_o ( slv_resps[i][Cfg.NoMstPorts] ),
.slv_aw_cache_i ( 4'd0 ),
.slv_ar_cache_i ( 4'd0 ),
.mst_req_o ( decerr_req ),
.mst_resp_i ( decerr_resp )
);
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
.Resp ( axi_pkg::RESP_DECERR ),
.ATOPs ( 1'b0 ), // no ATOPs in AXI4-Lite
.MaxTrans ( 1 ) // Transactions terminate at this slave, and AXI4-Lite
// transactions have only a single beat.
) i_axi_err_slv (
.clk_i ( clk_i ), // Clock
.rst_ni ( rst_ni ), // Asynchronous reset active low
.test_i ( test_i ), // Testmode enable
// slave port
.slv_req_i ( decerr_req ),
.slv_resp_o ( decerr_resp )
);
end
// 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];
end
end
for (genvar i = 0; i < Cfg.NoMstPorts; i++) begin : gen_mst_port_mux
axi_lite_mux #(
.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 ),
.NoSlvPorts ( Cfg.NoSlvPorts ), // Number of Masters for the module
.MaxTrans ( Cfg.MaxSlvTrans ),
.FallThrough ( Cfg.FallThrough ),
.SpillAw ( Cfg.LatencyMode[4] ),
.SpillW ( Cfg.LatencyMode[3] ),
.SpillB ( Cfg.LatencyMode[2] ),
.SpillAr ( Cfg.LatencyMode[1] ),
.SpillR ( Cfg.LatencyMode[0] )
) i_axi_lite_mux (
.clk_i, // Clock
.rst_ni, // Asynchronous reset active low
.test_i, // Test Mode enable
.slv_reqs_i ( mst_reqs[i] ),
.slv_resps_o ( mst_resps[i] ),
.mst_req_o ( mst_ports_req_o[i] ),
.mst_resp_i ( mst_ports_resp_i[i] )
);
end
endmodule
`include "axi/assign.svh"
module axi_lite_xbar_intf #(
parameter axi_pkg::xbar_cfg_t Cfg = '0,
parameter type rule_t = axi_pkg::xbar_rule_64_t
) (
input logic clk_i,
input logic rst_ni,
input logic test_i,
AXI_LITE.Slave slv_ports [Cfg.NoSlvPorts-1:0],
AXI_LITE.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
);
typedef logic [Cfg.AxiAddrWidth -1:0] addr_t;
typedef logic [Cfg.AxiDataWidth -1:0] data_t;
typedef logic [Cfg.AxiDataWidth/8 -1:0] strb_t;
`AXI_LITE_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t)
`AXI_LITE_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t)
`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)
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;
for (genvar i = 0; i < Cfg.NoMstPorts; i++) begin : gen_assign_mst
`AXI_LITE_ASSIGN_FROM_REQ(mst_ports[i], mst_reqs[i])
`AXI_LITE_ASSIGN_TO_RESP(mst_resps[i], mst_ports[i])
end
for (genvar i = 0; i < Cfg.NoSlvPorts; i++) begin : gen_assign_slv
`AXI_LITE_ASSIGN_TO_REQ(slv_reqs[i], slv_ports[i])
`AXI_LITE_ASSIGN_FROM_RESP(slv_ports[i], slv_resps[i])
end
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 )
) i_xbar (
.clk_i,
.rst_ni,
.test_i,
.slv_ports_req_i (slv_reqs ),
.slv_ports_resp_o (slv_resps),
.mst_ports_req_o (mst_reqs ),
.mst_ports_resp_i (mst_resps),
.addr_map_i,
.en_default_mst_port_i,
.default_mst_port_i
);
endmodule

View file

@ -0,0 +1,164 @@
// Copyright (c) 2014-2018 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:
// - Andreas Kurth <akurth@iis.ee.ethz.ch>
// - Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>
// - Fabian Schuiki <fschuiki@iis.ee.ethz.ch>
/// Modify addresses on an AXI4 bus
module axi_modify_address #(
/// Request type of the slave port
parameter type slv_req_t = logic,
/// Address type of the master port
parameter type mst_addr_t = logic,
/// Request type of the master port
parameter type mst_req_t = logic,
/// Response type of slave and master port
parameter type axi_resp_t = logic
) (
/// Slave port request
input slv_req_t slv_req_i,
/// Slave port response
output axi_resp_t slv_resp_o,
/// AW address on master port; must remain stable while an AW handshake is pending.
input mst_addr_t mst_aw_addr_i,
/// AR address on master port; must remain stable while an AR handshake is pending.
input mst_addr_t mst_ar_addr_i,
/// Master port request
output mst_req_t mst_req_o,
/// Master port response
input axi_resp_t mst_resp_i
);
assign mst_req_o = '{
aw: '{
id: slv_req_i.aw.id,
addr: mst_aw_addr_i,
len: slv_req_i.aw.len,
size: slv_req_i.aw.size,
burst: slv_req_i.aw.burst,
lock: slv_req_i.aw.lock,
cache: slv_req_i.aw.cache,
prot: slv_req_i.aw.prot,
qos: slv_req_i.aw.qos,
region: slv_req_i.aw.region,
atop: slv_req_i.aw.atop,
user: slv_req_i.aw.user,
default: '0
},
aw_valid: slv_req_i.aw_valid,
w: slv_req_i.w,
w_valid: slv_req_i.w_valid,
b_ready: slv_req_i.b_ready,
ar: '{
id: slv_req_i.ar.id,
addr: mst_ar_addr_i,
len: slv_req_i.ar.len,
size: slv_req_i.ar.size,
burst: slv_req_i.ar.burst,
lock: slv_req_i.ar.lock,
cache: slv_req_i.ar.cache,
prot: slv_req_i.ar.prot,
qos: slv_req_i.ar.qos,
region: slv_req_i.ar.region,
user: slv_req_i.ar.user,
default: '0
},
ar_valid: slv_req_i.ar_valid,
r_ready: slv_req_i.r_ready,
default: '0
};
assign slv_resp_o = mst_resp_i;
endmodule
`include "axi/typedef.svh"
`include "axi/assign.svh"
/// Interface variant of [`axi_modify_address`](module.axi_modify_address)
module axi_modify_address_intf #(
/// Address width of slave port
parameter int unsigned AXI_SLV_PORT_ADDR_WIDTH = 0,
/// Address width of master port
parameter int unsigned AXI_MST_PORT_ADDR_WIDTH = AXI_SLV_PORT_ADDR_WIDTH,
/// Data width of slave and master port
parameter int unsigned AXI_DATA_WIDTH = 0,
/// ID width of slave and master port
parameter int unsigned AXI_ID_WIDTH = 0,
/// User signal width of slave and master port
parameter int unsigned AXI_USER_WIDTH = 0,
/// Derived (=DO NOT OVERRIDE) type of master port addresses
type mst_addr_t = logic [AXI_MST_PORT_ADDR_WIDTH-1:0]
) (
/// Slave port
AXI_BUS.Slave slv,
/// AW address on master port; must remain stable while an AW handshake is pending.
input mst_addr_t mst_aw_addr_i,
/// AR address on master port; must remain stable while an AR handshake is pending.
input mst_addr_t mst_ar_addr_i,
/// Master port
AXI_BUS.Master mst
);
typedef logic [AXI_ID_WIDTH-1:0] id_t;
typedef logic [AXI_SLV_PORT_ADDR_WIDTH-1:0] slv_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(slv_aw_chan_t, slv_addr_t, id_t, user_t)
`AXI_TYPEDEF_AW_CHAN_T(mst_aw_chan_t, mst_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(slv_ar_chan_t, slv_addr_t, id_t, user_t)
`AXI_TYPEDEF_AR_CHAN_T(mst_ar_chan_t, mst_addr_t, id_t, user_t)
`AXI_TYPEDEF_R_CHAN_T(r_chan_t, data_t, id_t, user_t)
`AXI_TYPEDEF_REQ_T(slv_req_t, slv_aw_chan_t, w_chan_t, slv_ar_chan_t)
`AXI_TYPEDEF_REQ_T(mst_req_t, mst_aw_chan_t, w_chan_t, mst_ar_chan_t)
`AXI_TYPEDEF_RESP_T(axi_resp_t, b_chan_t, r_chan_t)
slv_req_t slv_req;
mst_req_t 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_modify_address #(
.slv_req_t ( slv_req_t ),
.mst_addr_t ( mst_addr_t ),
.mst_req_t ( mst_req_t ),
.axi_resp_t ( axi_resp_t )
) i_axi_modify_address (
.slv_req_i ( slv_req ),
.slv_resp_o ( slv_resp ),
.mst_req_o ( mst_req ),
.mst_resp_i ( mst_resp ),
.mst_aw_addr_i,
.mst_ar_addr_i
);
// pragma translate_off
`ifndef VERILATOR
initial begin
assert(AXI_SLV_PORT_ADDR_WIDTH > 0);
assert(AXI_MST_PORT_ADDR_WIDTH > 0);
assert(AXI_DATA_WIDTH > 0);
assert(AXI_ID_WIDTH > 0);
end
`endif
// pragma translate_on
endmodule

View file

@ -0,0 +1,237 @@
// Copyright (c) 2014-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:
// - Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>
// - Fabian Schuiki <fschuiki@iis.ee.ethz.ch>
// - Andreas Kurth <akurth@iis.ee.ethz.ch>
// - Stefan Mach <smach@iis.ee.ethz.ch>
// Multiple AXI4 cuts.
//
// These can be used to relax timing pressure on very long AXI busses.
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,
// AXI request & response structs
parameter type req_t = logic,
parameter type resp_t = logic
) (
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,
// master port
output req_t mst_req_o,
input resp_t mst_resp_i
);
if (NoCuts == '0) begin : gen_no_cut
// 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_cut
// instantiate all needed cuts
req_t [NoCuts:0] cut_req;
resp_t [NoCuts:0] cut_resp;
// connect slave to the lowest index
assign cut_req[0] = slv_req_i;
assign slv_resp_o = cut_resp[0];
// 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 )
) i_cut (
.clk_i,
.rst_ni,
.slv_req_i ( cut_req[i] ),
.slv_resp_o ( cut_resp[i] ),
.mst_req_o ( cut_req[i+1] ),
.mst_resp_i ( cut_resp[i+1] )
);
end
// connect master to the highest index
assign mst_req_o = cut_req[NoCuts];
assign cut_resp[NoCuts] = mst_resp_i;
end
// Check the invariants
// pragma translate_off
`ifndef VERILATOR
initial begin
assert(NoCuts >= 0);
end
`endif
// pragma translate_on
endmodule
`include "axi/assign.svh"
`include "axi/typedef.svh"
// interface wrapper
module axi_multicut_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 NUM_CUTS = 0 // The number of cuts.
) (
input logic clk_i,
input logic rst_ni,
AXI_BUS.Slave in,
AXI_BUS.Master out
);
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(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_ASSIGN_TO_REQ(slv_req, in)
`AXI_ASSIGN_FROM_RESP(in, slv_resp)
`AXI_ASSIGN_FROM_REQ(out, mst_req)
`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 )
) i_axi_multicut (
.clk_i,
.rst_ni,
.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 (in.AXI_ADDR_WIDTH == ADDR_WIDTH) else $fatal(1, "Wrong interface definition");
assert (in.AXI_DATA_WIDTH == DATA_WIDTH) else $fatal(1, "Wrong interface definition");
assert (in.AXI_ID_WIDTH == ID_WIDTH) else $fatal(1, "Wrong interface definition");
assert (in.AXI_USER_WIDTH == USER_WIDTH) else $fatal(1, "Wrong interface definition");
assert (out.AXI_ADDR_WIDTH == ADDR_WIDTH) else $fatal(1, "Wrong interface definition");
assert (out.AXI_DATA_WIDTH == DATA_WIDTH) else $fatal(1, "Wrong interface definition");
assert (out.AXI_ID_WIDTH == ID_WIDTH) else $fatal(1, "Wrong interface definition");
assert (out.AXI_USER_WIDTH == USER_WIDTH) else $fatal(1, "Wrong interface definition");
end
`endif
// pragma translate_on
endmodule
module axi_lite_multicut_intf #(
// The address width.
parameter int unsigned ADDR_WIDTH = 0,
// The data width.
parameter int unsigned DATA_WIDTH = 0,
// The number of cuts.
parameter int unsigned NUM_CUTS = 0
) (
input logic clk_i ,
input logic rst_ni ,
AXI_LITE.Slave in ,
AXI_LITE.Master out
);
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;
`AXI_LITE_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t)
`AXI_LITE_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t)
`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)
req_t slv_req, mst_req;
resp_t slv_resp, mst_resp;
`AXI_LITE_ASSIGN_TO_REQ(slv_req, in)
`AXI_LITE_ASSIGN_FROM_RESP(in, slv_resp)
`AXI_LITE_ASSIGN_FROM_REQ(out, mst_req)
`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 )
) i_axi_multicut (
.clk_i,
.rst_ni,
.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 (in.AXI_ADDR_WIDTH == ADDR_WIDTH) else $fatal(1, "Wrong interface definition");
assert (in.AXI_DATA_WIDTH == DATA_WIDTH) else $fatal(1, "Wrong interface definition");
assert (out.AXI_ADDR_WIDTH == ADDR_WIDTH) else $fatal(1, "Wrong interface definition");
assert (out.AXI_DATA_WIDTH == DATA_WIDTH) else $fatal(1, "Wrong interface definition");
end
`endif
// pragma translate_on
endmodule

522
vendor/pulp-platform/axi/src/axi_mux.sv vendored Normal file
View file

@ -0,0 +1,522 @@
// 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:
// - Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>
// - Andreas Kurth <akurth@iis.ee.ethz.ch>
// AXI Multiplexer: This module multiplexes the AXI4 slave ports down to one master port.
// The AXI IDs from the slave ports get extended with the respective slave port index.
// The extension width can be calculated with `$clog2(NoSlvPorts)`. This means the AXI
// ID for the master port has to be this `$clog2(NoSlvPorts)` wider than the ID for the
// slave ports.
// Responses are switched based on these bits. For example, with 4 slave ports
// a response with ID `6'b100110` will be forwarded to slave port 2 (`2'b10`).
// register macros
`include "common_cells/registers.svh"
module axi_mux #(
// AXI parameter and channel types
parameter int unsigned SlvAxiIDWidth = 32'd0, // AXI ID width, slave ports
parameter type slv_aw_chan_t = logic, // AW Channel Type, slave ports
parameter type mst_aw_chan_t = logic, // AW Channel Type, master port
parameter type w_chan_t = logic, // W Channel Type, all ports
parameter type slv_b_chan_t = logic, // B Channel Type, slave ports
parameter type mst_b_chan_t = logic, // B Channel Type, master port
parameter type slv_ar_chan_t = logic, // AR Channel Type, slave ports
parameter type mst_ar_chan_t = logic, // AR Channel Type, master port
parameter type slv_r_chan_t = logic, // R Channel Type, slave ports
parameter type mst_r_chan_t = logic, // R Channel Type, master port
parameter type slv_req_t = logic, // Slave port request type
parameter type slv_resp_t = logic, // Slave port response type
parameter type mst_req_t = logic, // Master ports request type
parameter type mst_resp_t = logic, // Master ports response type
parameter int unsigned NoSlvPorts = 32'd0, // Number of slave ports
// Maximum number of outstanding transactions per write
parameter int unsigned MaxWTrans = 32'd8,
// If enabled, this multiplexer is purely combinatorial
parameter bit FallThrough = 1'b0,
// add spill register on write master ports, adds a cycle latency on write channels
parameter bit SpillAw = 1'b1,
parameter bit SpillW = 1'b0,
parameter bit SpillB = 1'b0,
// add spill register on read master ports, adds a cycle latency on read channels
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
// slave ports (AXI inputs), connect master modules here
input slv_req_t [NoSlvPorts-1:0] slv_reqs_i,
output slv_resp_t [NoSlvPorts-1:0] slv_resps_o,
// master port (AXI outputs), connect slave modules here
output mst_req_t mst_req_o,
input mst_resp_t mst_resp_i
);
localparam int unsigned MstIdxBits = $clog2(NoSlvPorts);
localparam int unsigned MstAxiIDWidth = SlvAxiIDWidth + MstIdxBits;
// 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;
// other non degenerate cases
end else begin : gen_mux
typedef logic [MstIdxBits-1:0] switch_id_t;
// AXI channels between the ID prepend unit and the rest of the multiplexer
mst_aw_chan_t [NoSlvPorts-1:0] slv_aw_chans;
logic [NoSlvPorts-1:0] slv_aw_valids, slv_aw_readies;
w_chan_t [NoSlvPorts-1:0] slv_w_chans;
logic [NoSlvPorts-1:0] slv_w_valids, slv_w_readies;
mst_b_chan_t [NoSlvPorts-1:0] slv_b_chans;
logic [NoSlvPorts-1:0] slv_b_valids, slv_b_readies;
mst_ar_chan_t [NoSlvPorts-1:0] slv_ar_chans;
logic [NoSlvPorts-1:0] slv_ar_valids, slv_ar_readies;
mst_r_chan_t [NoSlvPorts-1:0] slv_r_chans;
logic [NoSlvPorts-1:0] slv_r_valids, slv_r_readies;
// These signals are all ID prepended
// AW channel
mst_aw_chan_t mst_aw_chan;
logic mst_aw_valid, mst_aw_ready;
// AW master handshake internal, so that we are able to stall, if w_fifo is full
logic aw_valid, aw_ready;
// FF to lock the AW valid signal, when a new arbitration decision is made the decision
// gets pushed into the W FIFO, when it now stalls prevent subsequent pushing
// This FF removes AW to W dependency
logic lock_aw_valid_d, lock_aw_valid_q;
logic load_aw_lock;
// signals for the FIFO that holds the last switching decision of the AW channel
logic w_fifo_full, w_fifo_empty;
logic w_fifo_push, w_fifo_pop;
switch_id_t w_fifo_data;
// W channel spill reg
w_chan_t mst_w_chan;
logic mst_w_valid, mst_w_ready;
// master ID in the b_id
switch_id_t switch_b_id;
// B channel spill reg
mst_b_chan_t mst_b_chan;
logic mst_b_valid;
// AR channel for when spill is enabled
mst_ar_chan_t mst_ar_chan;
logic ar_valid, ar_ready;
// master ID in the r_id
switch_id_t switch_r_id;
// R channel spill reg
mst_r_chan_t mst_r_chan;
logic mst_r_valid;
//--------------------------------------
// ID prepend for all slave ports
//--------------------------------------
for (genvar i = 0; i < NoSlvPorts; i++) begin : gen_id_prepend
axi_id_prepend #(
.NoBus ( 32'd1 ), // one AXI bus per slave port
.AxiIdWidthSlvPort( SlvAxiIDWidth ),
.AxiIdWidthMstPort( MstAxiIDWidth ),
.slv_aw_chan_t ( slv_aw_chan_t ),
.slv_w_chan_t ( w_chan_t ),
.slv_b_chan_t ( slv_b_chan_t ),
.slv_ar_chan_t ( slv_ar_chan_t ),
.slv_r_chan_t ( slv_r_chan_t ),
.mst_aw_chan_t ( mst_aw_chan_t ),
.mst_w_chan_t ( w_chan_t ),
.mst_b_chan_t ( mst_b_chan_t ),
.mst_ar_chan_t ( mst_ar_chan_t ),
.mst_r_chan_t ( mst_r_chan_t )
) i_id_prepend (
.pre_id_i ( switch_id_t'(i) ),
.slv_aw_chans_i ( slv_reqs_i[i].aw ),
.slv_aw_valids_i ( slv_reqs_i[i].aw_valid ),
.slv_aw_readies_o ( slv_resps_o[i].aw_ready ),
.slv_w_chans_i ( slv_reqs_i[i].w ),
.slv_w_valids_i ( slv_reqs_i[i].w_valid ),
.slv_w_readies_o ( slv_resps_o[i].w_ready ),
.slv_b_chans_o ( slv_resps_o[i].b ),
.slv_b_valids_o ( slv_resps_o[i].b_valid ),
.slv_b_readies_i ( slv_reqs_i[i].b_ready ),
.slv_ar_chans_i ( slv_reqs_i[i].ar ),
.slv_ar_valids_i ( slv_reqs_i[i].ar_valid ),
.slv_ar_readies_o ( slv_resps_o[i].ar_ready ),
.slv_r_chans_o ( slv_resps_o[i].r ),
.slv_r_valids_o ( slv_resps_o[i].r_valid ),
.slv_r_readies_i ( slv_reqs_i[i].r_ready ),
.mst_aw_chans_o ( slv_aw_chans[i] ),
.mst_aw_valids_o ( slv_aw_valids[i] ),
.mst_aw_readies_i ( slv_aw_readies[i] ),
.mst_w_chans_o ( slv_w_chans[i] ),
.mst_w_valids_o ( slv_w_valids[i] ),
.mst_w_readies_i ( slv_w_readies[i] ),
.mst_b_chans_i ( slv_b_chans[i] ),
.mst_b_valids_i ( slv_b_valids[i] ),
.mst_b_readies_o ( slv_b_readies[i] ),
.mst_ar_chans_o ( slv_ar_chans[i] ),
.mst_ar_valids_o ( slv_ar_valids[i] ),
.mst_ar_readies_i ( slv_ar_readies[i] ),
.mst_r_chans_i ( slv_r_chans[i] ),
.mst_r_valids_i ( slv_r_valids[i] ),
.mst_r_readies_o ( slv_r_readies[i] )
);
end
//--------------------------------------
// AW Channel
//--------------------------------------
rr_arb_tree #(
.NumIn ( NoSlvPorts ),
.DataType ( mst_aw_chan_t ),
.AxiVldRdy( 1'b1 ),
.LockIn ( 1'b1 )
) i_aw_arbiter (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i( 1'b0 ),
.rr_i ( '0 ),
.req_i ( slv_aw_valids ),
.gnt_o ( slv_aw_readies ),
.data_i ( slv_aw_chans ),
.gnt_i ( aw_ready ),
.req_o ( aw_valid ),
.data_o ( mst_aw_chan ),
.idx_o ( )
);
// control of the AW channel
always_comb begin
// default assignments
lock_aw_valid_d = lock_aw_valid_q;
load_aw_lock = 1'b0;
w_fifo_push = 1'b0;
mst_aw_valid = 1'b0;
aw_ready = 1'b0;
// had a downstream stall, be valid and send the AW along
if (lock_aw_valid_q) begin
mst_aw_valid = 1'b1;
// transaction
if (mst_aw_ready) begin
aw_ready = 1'b1;
lock_aw_valid_d = 1'b0;
load_aw_lock = 1'b1;
end
end else begin
if (!w_fifo_full && aw_valid) begin
mst_aw_valid = 1'b1;
w_fifo_push = 1'b1;
if (mst_aw_ready) begin
aw_ready = 1'b1;
end else begin
// go to lock if transaction not in this cycle
lock_aw_valid_d = 1'b1;
load_aw_lock = 1'b1;
end
end
end
end
`FFLARN(lock_aw_valid_q, lock_aw_valid_d, load_aw_lock, '0, clk_i, rst_ni)
fifo_v3 #(
.FALL_THROUGH ( FallThrough ),
.DEPTH ( MaxWTrans ),
.dtype ( switch_id_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 ( mst_aw_chan.id[SlvAxiIDWidth+:MstIdxBits] ),
.push_i ( w_fifo_push ),
.data_o ( w_fifo_data ),
.pop_i ( w_fifo_pop )
);
spill_register #(
.T ( mst_aw_chan_t ),
.Bypass ( ~SpillAw ) // Param indicated that we want a spill reg
) i_aw_spill_reg (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.valid_i ( mst_aw_valid ),
.ready_o ( mst_aw_ready ),
.data_i ( mst_aw_chan ),
.valid_o ( mst_req_o.aw_valid ),
.ready_i ( mst_resp_i.aw_ready ),
.data_o ( mst_req_o.aw )
);
//--------------------------------------
// W Channel
//--------------------------------------
// multiplexer
assign mst_w_chan = slv_w_chans[w_fifo_data];
always_comb begin
// default assignments
mst_w_valid = 1'b0;
slv_w_readies = '0;
w_fifo_pop = 1'b0;
// control
if (!w_fifo_empty) begin
// connect the handshake
mst_w_valid = slv_w_valids[w_fifo_data];
slv_w_readies[w_fifo_data] = mst_w_ready;
// pop FIFO on a last transaction
w_fifo_pop = slv_w_valids[w_fifo_data] & mst_w_ready & mst_w_chan.last;
end
end
spill_register #(
.T ( w_chan_t ),
.Bypass ( ~SpillW )
) i_w_spill_reg (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.valid_i ( mst_w_valid ),
.ready_o ( mst_w_ready ),
.data_i ( mst_w_chan ),
.valid_o ( mst_req_o.w_valid ),
.ready_i ( mst_resp_i.w_ready ),
.data_o ( mst_req_o.w )
);
//--------------------------------------
// B Channel
//--------------------------------------
// replicate B channels
assign slv_b_chans = {NoSlvPorts{mst_b_chan}};
// control B channel handshake
assign switch_b_id = mst_b_chan.id[SlvAxiIDWidth+:MstIdxBits];
assign slv_b_valids = (mst_b_valid) ? (1 << switch_b_id) : '0;
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 ( mst_b_valid ),
.ready_i ( slv_b_readies[switch_b_id] ),
.data_o ( mst_b_chan )
);
//--------------------------------------
// AR Channel
//--------------------------------------
rr_arb_tree #(
.NumIn ( NoSlvPorts ),
.DataType ( mst_ar_chan_t ),
.AxiVldRdy( 1'b1 ),
.LockIn ( 1'b1 )
) i_ar_arbiter (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i( 1'b0 ),
.rr_i ( '0 ),
.req_i ( slv_ar_valids ),
.gnt_o ( slv_ar_readies ),
.data_i ( slv_ar_chans ),
.gnt_i ( ar_ready ),
.req_o ( ar_valid ),
.data_o ( mst_ar_chan ),
.idx_o ( )
);
spill_register #(
.T ( mst_ar_chan_t ),
.Bypass ( ~SpillAr )
) i_ar_spill_reg (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.valid_i ( ar_valid ),
.ready_o ( ar_ready ),
.data_i ( mst_ar_chan ),
.valid_o ( mst_req_o.ar_valid ),
.ready_i ( mst_resp_i.ar_ready ),
.data_o ( mst_req_o.ar )
);
//--------------------------------------
// R Channel
//--------------------------------------
// replicate R channels
assign slv_r_chans = {NoSlvPorts{mst_r_chan}};
// R channel handshake control
assign switch_r_id = mst_r_chan.id[SlvAxiIDWidth+:MstIdxBits];
assign slv_r_valids = (mst_r_valid) ? (1 << switch_r_id) : '0;
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 ( mst_r_valid ),
.ready_i ( slv_r_readies[switch_r_id] ),
.data_o ( mst_r_chan )
);
end
// pragma translate_off
`ifndef VERILATOR
initial begin
assert (SlvAxiIDWidth > 0) else $fatal(1, "AXI ID width of slave ports must be non-zero!");
assert (NoSlvPorts > 0) else $fatal(1, "Number of slave ports must be non-zero!");
assert (MaxWTrans > 0)
else $fatal(1, "Maximum number of outstanding writes must be non-zero!");
assert (MstAxiIDWidth >= SlvAxiIDWidth + $clog2(NoSlvPorts))
else $fatal(1, "AXI ID width of master ports must be wide enough to identify slave ports!");
// Assert ID widths (one slave is sufficient since they all have the same type).
assert ($unsigned($bits(slv_reqs_i[0].aw.id)) == SlvAxiIDWidth)
else $fatal(1, "ID width of AW channel of slave ports does not match parameter!");
assert ($unsigned($bits(slv_reqs_i[0].ar.id)) == SlvAxiIDWidth)
else $fatal(1, "ID width of AR channel of slave ports does not match parameter!");
assert ($unsigned($bits(slv_resps_o[0].b.id)) == SlvAxiIDWidth)
else $fatal(1, "ID width of B channel of slave ports does not match parameter!");
assert ($unsigned($bits(slv_resps_o[0].r.id)) == SlvAxiIDWidth)
else $fatal(1, "ID width of R channel of slave ports does not match parameter!");
assert ($unsigned($bits(mst_req_o.aw.id)) == MstAxiIDWidth)
else $fatal(1, "ID width of AW channel of master port is wrong!");
assert ($unsigned($bits(mst_req_o.ar.id)) == MstAxiIDWidth)
else $fatal(1, "ID width of AR channel of master port is wrong!");
assert ($unsigned($bits(mst_resp_i.b.id)) == MstAxiIDWidth)
else $fatal(1, "ID width of B channel of master port is wrong!");
assert ($unsigned($bits(mst_resp_i.r.id)) == MstAxiIDWidth)
else $fatal(1, "ID width of R channel of master port is wrong!");
end
`endif
// pragma translate_on
endmodule
// interface wrap
`include "axi/assign.svh"
`include "axi/typedef.svh"
module axi_mux_intf #(
parameter int unsigned SLV_AXI_ID_WIDTH = 32'd0, // Synopsys DC requires default value for params
parameter int unsigned MST_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,
parameter int unsigned NO_SLV_PORTS = 32'd0, // Number of slave ports
// Maximum number of outstanding transactions per write
parameter int unsigned MAX_W_TRANS = 32'd8,
// if enabled, this multiplexer is purely combinatorial
parameter bit FALL_THROUGH = 1'b0,
// add spill register on write master ports, adds a cycle latency on write channels
parameter bit SPILL_AW = 1'b1,
parameter bit SPILL_W = 1'b0,
parameter bit SPILL_B = 1'b0,
// add spill register on read master ports, adds a cycle latency on read channels
parameter bit SPILL_AR = 1'b1,
parameter bit SPILL_R = 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 [NO_SLV_PORTS-1:0], // slave ports
AXI_BUS.Master mst // master port
);
typedef logic [SLV_AXI_ID_WIDTH-1:0] slv_id_t;
typedef logic [MST_AXI_ID_WIDTH-1:0] mst_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;
// channels typedef
`AXI_TYPEDEF_AW_CHAN_T(slv_aw_chan_t, addr_t, slv_id_t, user_t)
`AXI_TYPEDEF_AW_CHAN_T(mst_aw_chan_t, addr_t, mst_id_t, user_t)
`AXI_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t, user_t)
`AXI_TYPEDEF_B_CHAN_T(slv_b_chan_t, slv_id_t, user_t)
`AXI_TYPEDEF_B_CHAN_T(mst_b_chan_t, mst_id_t, user_t)
`AXI_TYPEDEF_AR_CHAN_T(slv_ar_chan_t, addr_t, slv_id_t, user_t)
`AXI_TYPEDEF_AR_CHAN_T(mst_ar_chan_t, addr_t, mst_id_t, user_t)
`AXI_TYPEDEF_R_CHAN_T(slv_r_chan_t, data_t, slv_id_t, user_t)
`AXI_TYPEDEF_R_CHAN_T(mst_r_chan_t, data_t, mst_id_t, user_t)
`AXI_TYPEDEF_REQ_T(slv_req_t, slv_aw_chan_t, w_chan_t, slv_ar_chan_t)
`AXI_TYPEDEF_RESP_T(slv_resp_t, slv_b_chan_t, slv_r_chan_t)
`AXI_TYPEDEF_REQ_T(mst_req_t, mst_aw_chan_t, w_chan_t, mst_ar_chan_t)
`AXI_TYPEDEF_RESP_T(mst_resp_t, mst_b_chan_t, mst_r_chan_t)
slv_req_t [NO_SLV_PORTS-1:0] slv_reqs;
slv_resp_t [NO_SLV_PORTS-1:0] slv_resps;
mst_req_t mst_req;
mst_resp_t mst_resp;
for (genvar i = 0; i < NO_SLV_PORTS; i++) begin : gen_assign_slv_ports
`AXI_ASSIGN_TO_REQ(slv_reqs[i], slv[i])
`AXI_ASSIGN_FROM_RESP(slv[i], slv_resps[i])
end
`AXI_ASSIGN_FROM_REQ(mst, mst_req)
`AXI_ASSIGN_TO_RESP(mst_resp, mst)
axi_mux #(
.SlvAxiIDWidth ( SLV_AXI_ID_WIDTH ),
.slv_aw_chan_t ( slv_aw_chan_t ), // AW Channel Type, slave ports
.mst_aw_chan_t ( mst_aw_chan_t ), // AW Channel Type, master port
.w_chan_t ( w_chan_t ), // W Channel Type, all ports
.slv_b_chan_t ( slv_b_chan_t ), // B Channel Type, slave ports
.mst_b_chan_t ( mst_b_chan_t ), // B Channel Type, master port
.slv_ar_chan_t ( slv_ar_chan_t ), // AR Channel Type, slave ports
.mst_ar_chan_t ( mst_ar_chan_t ), // AR Channel Type, master port
.slv_r_chan_t ( slv_r_chan_t ), // R Channel Type, slave ports
.mst_r_chan_t ( mst_r_chan_t ), // R Channel Type, master port
.slv_req_t ( slv_req_t ),
.slv_resp_t ( slv_resp_t ),
.mst_req_t ( mst_req_t ),
.mst_resp_t ( mst_resp_t ),
.NoSlvPorts ( NO_SLV_PORTS ), // Number of slave ports
.MaxWTrans ( MAX_W_TRANS ),
.FallThrough ( FALL_THROUGH ),
.SpillAw ( SPILL_AW ),
.SpillW ( SPILL_W ),
.SpillB ( SPILL_B ),
.SpillAr ( SPILL_AR ),
.SpillR ( SPILL_R )
) i_axi_mux (
.clk_i ( clk_i ), // Clock
.rst_ni ( rst_ni ), // Asynchronous reset active low
.test_i ( test_i ), // Test Mode enable
.slv_reqs_i ( slv_reqs ),
.slv_resps_o ( slv_resps ),
.mst_req_o ( mst_req ),
.mst_resp_i ( mst_resp )
);
endmodule

423
vendor/pulp-platform/axi/src/axi_pkg.sv vendored Normal file
View file

@ -0,0 +1,423 @@
// Copyright (c) 2014-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:
// - Andreas Kurth <akurth@iis.ee.ethz.ch>
// - Florian Zaruba <zarubaf@iis.ee.ethz.ch>
// - Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>
// - Fabian Schuiki <fschuiki@iis.ee.ethz.ch>
// - Matheus Cavalcante <matheusd@iis.ee.ethz.ch>
//! AXI Package
/// Contains all necessary type definitions, constants, and generally useful functions.
package axi_pkg;
/// AXI Transaction Burst Type.
typedef logic [1:0] burst_t;
/// AXI Transaction Response Type.
typedef logic [1:0] resp_t;
/// AXI Transaction Cacheability Type.
typedef logic [3:0] cache_t;
/// AXI Transaction Protection Type.
typedef logic [2:0] prot_t;
/// AXI Transaction Quality of Service Type.
typedef logic [3:0] qos_t;
/// AXI Transaction Region Type.
typedef logic [3:0] region_t;
/// AXI Transaction Length Type.
typedef logic [7:0] len_t;
/// AXI Transaction Size Type.
typedef logic [2:0] size_t;
/// AXI5 Atomic Operation Type.
typedef logic [5:0] atop_t; // atomic operations
/// AXI5 Non-Secure Address Identifier.
typedef logic [3:0] nsaid_t;
/// In a fixed burst:
/// - The address is the same for every transfer in the burst.
/// - The byte lanes that are valid are constant for all beats in the burst. However, within
/// those byte lanes, the actual bytes that have `wstrb` asserted can differ for each beat in
/// the burst.
/// This burst type is used for repeated accesses to the same location such as when loading or
/// emptying a FIFO.
localparam BURST_FIXED = 2'b00;
/// In an incrementing burst, the address for each transfer in the burst is an increment of the
/// address for the previous transfer. The increment value depends on the size of the transfer.
/// For example, the address for each transfer in a burst with a size of 4 bytes is the previous
/// address plus four.
/// This burst type is used for accesses to normal sequential memory.
localparam BURST_INCR = 2'b01;
/// A wrapping burst is similar to an incrementing burst, except that the address wraps around to
/// a lower address if an upper address limit is reached.
/// The following restrictions apply to wrapping bursts:
/// - The start address must be aligned to the size of each transfer.
/// - The length of the burst must be 2, 4, 8, or 16 transfers.
localparam BURST_WRAP = 2'b10;
/// Normal access success. Indicates that a normal access has been successful. Can also indicate
/// that an exclusive access has failed.
localparam RESP_OKAY = 2'b00;
/// Exclusive access okay. Indicates that either the read or write portion of an exclusive access
/// has been successful.
localparam RESP_EXOKAY = 2'b01;
/// Slave error. Used when the access has reached the slave successfully, but the slave wishes to
/// return an error condition to the originating master.
localparam RESP_SLVERR = 2'b10;
/// Decode error. Generated, typically by an interconnect component, to indicate that there is no
/// slave at the transaction address.
localparam RESP_DECERR = 2'b11;
/// When this bit is asserted, the interconnect, or any component, can delay the transaction
/// reaching its final destination for any number of cycles.
localparam CACHE_BUFFERABLE = 4'b0001;
/// When HIGH, Modifiable indicates that the characteristics of the transaction can be modified.
/// When Modifiable is LOW, the transaction is Non-modifiable.
localparam CACHE_MODIFIABLE = 4'b0010;
/// When this bit is asserted, read allocation of the transaction is recommended but is not
/// mandatory.
localparam CACHE_RD_ALLOC = 4'b0100;
/// When this bit is asserted, write allocation of the transaction is recommended but is not
/// mandatory.
localparam CACHE_WR_ALLOC = 4'b1000;
/// Maximum number of bytes per burst, as specified by `size` (see Table A3-2).
function automatic shortint unsigned num_bytes(size_t size);
return 1 << size;
endfunction
/// An overly long address type.
/// It lets us define functions that work generically for shorter addresses. We rely on the
/// synthesizer to optimize the unused bits away.
typedef logic [127:0] largest_addr_t;
/// Aligned address of burst (see A3-51).
function automatic largest_addr_t aligned_addr(largest_addr_t addr, size_t size);
return (addr >> size) << size;
endfunction
/// Warp boundary of a `BURST_WRAP` transfer (see A3-51).
/// This is the lowest address accessed within a wrapping burst.
/// This address is aligned to the size and length of the burst.
/// The length of a `BURST_WRAP` has to be 2, 4, 8, or 16 transfers.
function automatic largest_addr_t wrap_boundary (largest_addr_t addr, size_t size, len_t len);
largest_addr_t wrap_addr;
// pragma translate_off
`ifndef VERILATOR
assume (len == len_t'(4'b1) || len == len_t'(4'b11) || len == len_t'(4'b111) ||
len == len_t'(4'b1111)) else
$error("AXI BURST_WRAP with not allowed len of: %0h", len);
`endif
// pragma translate_on
// In A3-51 the wrap boundary is defined as:
// `Wrap_Boundary = (INT(Start_Address / (Number_Bytes × Burst_Length))) ×
// (Number_Bytes × Burst_Length)`
// Whereas the aligned address is defined as:
// `Aligned_Address = (INT(Start_Address / Number_Bytes)) × Number_Bytes`
// This leads to the wrap boundary using the same calculation as the aligned address, difference
// being the additional dependency on the burst length. The addition in the case statement
// is equal to the multiplication with `Burst_Length` as a shift (used by `aligned_addr`) is
// equivalent with multiplication and division by a power of two, which conveniently are the
// only allowed values for `len` of a `BURST_WRAP`.
unique case (len)
4'b1 : wrap_addr = (addr >> (unsigned'(size) + 1)) << (unsigned'(size) + 1); // multiply `Number_Bytes` by `2`
4'b11 : wrap_addr = (addr >> (unsigned'(size) + 2)) << (unsigned'(size) + 2); // multiply `Number_Bytes` by `4`
4'b111 : wrap_addr = (addr >> (unsigned'(size) + 3)) << (unsigned'(size) + 3); // multiply `Number_Bytes` by `8`
4'b1111 : wrap_addr = (addr >> (unsigned'(size) + 4)) << (unsigned'(size) + 4); // multiply `Number_Bytes` by `16`
default : wrap_addr = '0;
endcase
return wrap_addr;
endfunction
/// Address of beat (see A3-51).
function automatic largest_addr_t
beat_addr(largest_addr_t addr, size_t size, len_t len, burst_t burst, shortint unsigned i_beat);
largest_addr_t ret_addr = addr;
largest_addr_t wrp_bond = '0;
if (burst == BURST_WRAP) begin
// do not trigger the function if there is no wrapping burst, to prevent assumptions firing
wrp_bond = wrap_boundary(addr, size, len);
end
if (i_beat != 0 && burst != BURST_FIXED) begin
// From A3-51:
// For an INCR burst, and for a WRAP burst for which the address has not wrapped, this
// equation determines the address of any transfer after the first transfer in a burst:
// `Address_N = Aligned_Address + (N 1) × Number_Bytes` (N counts from 1 to len!)
ret_addr = aligned_addr(addr, size) + i_beat * num_bytes(size);
// From A3-51:
// For a WRAP burst, if Address_N = Wrap_Boundary + (Number_Bytes × Burst_Length), then:
// * Use this equation for the current transfer:
// `Address_N = Wrap_Boundary`
// * Use this equation for any subsequent transfers:
// `Address_N = Start_Address + ((N 1) × Number_Bytes) (Number_Bytes × Burst_Length)`
// This means that the address calculation of a `BURST_WRAP` fundamentally works the same
// as for a `BURST_INC`, the difference is when the calculated address increments
// over the wrap threshold, the address wraps around by subtracting the accessed address
// space from the normal `BURST_INCR` address. The lower wrap boundary is equivalent to
// The wrap trigger condition minus the container size (`num_bytes(size) * (len + 1)`).
if (burst == BURST_WRAP && ret_addr >= wrp_bond + (num_bytes(size) * (len + 1))) begin
ret_addr = ret_addr - (num_bytes(size) * (len + 1));
end
end
return ret_addr;
endfunction
/// Index of lowest byte in beat (see A3-51).
function automatic shortint unsigned
beat_lower_byte(largest_addr_t addr, size_t size, len_t len, burst_t burst,
shortint unsigned strobe_width, shortint unsigned i_beat);
largest_addr_t _addr = beat_addr(addr, size, len, burst, i_beat);
return _addr - (_addr / strobe_width) * strobe_width;
endfunction
/// Index of highest byte in beat (see A3-51).
function automatic shortint unsigned
beat_upper_byte(largest_addr_t addr, size_t size, len_t len, burst_t burst,
shortint unsigned strobe_width, shortint unsigned i_beat);
if (i_beat == 0) begin
return aligned_addr(addr, size) + (num_bytes(size) - 1) - (addr / strobe_width) * strobe_width;
end else begin
return beat_lower_byte(addr, size, len, burst, strobe_width, i_beat) + num_bytes(size) - 1;
end
endfunction
/// Is the bufferable bit set?
function automatic logic bufferable(cache_t cache);
return |(cache & CACHE_BUFFERABLE);
endfunction
/// Is the modifiable bit set?
function automatic logic modifiable(cache_t cache);
return |(cache & CACHE_MODIFIABLE);
endfunction
/// Memory Type.
typedef enum logic [3:0] {
DEVICE_NONBUFFERABLE,
DEVICE_BUFFERABLE,
NORMAL_NONCACHEABLE_NONBUFFERABLE,
NORMAL_NONCACHEABLE_BUFFERABLE,
WTHRU_NOALLOCATE,
WTHRU_RALLOCATE,
WTHRU_WALLOCATE,
WTHRU_RWALLOCATE,
WBACK_NOALLOCATE,
WBACK_RALLOCATE,
WBACK_WALLOCATE,
WBACK_RWALLOCATE
} mem_type_t;
/// Create an `AR_CACHE` field from a `mem_type_t` type.
function automatic logic [3:0] get_arcache(mem_type_t mtype);
unique case (mtype)
DEVICE_NONBUFFERABLE : return 4'b0000;
DEVICE_BUFFERABLE : return 4'b0001;
NORMAL_NONCACHEABLE_NONBUFFERABLE : return 4'b0010;
NORMAL_NONCACHEABLE_BUFFERABLE : return 4'b0011;
WTHRU_NOALLOCATE : return 4'b1010;
WTHRU_RALLOCATE : return 4'b1110;
WTHRU_WALLOCATE : return 4'b1010;
WTHRU_RWALLOCATE : return 4'b1110;
WBACK_NOALLOCATE : return 4'b1011;
WBACK_RALLOCATE : return 4'b1111;
WBACK_WALLOCATE : return 4'b1011;
WBACK_RWALLOCATE : return 4'b1111;
endcase // mtype
endfunction
/// Create an `AW_CACHE` field from a `mem_type_t` type.
function automatic logic [3:0] get_awcache(mem_type_t mtype);
unique case (mtype)
DEVICE_NONBUFFERABLE : return 4'b0000;
DEVICE_BUFFERABLE : return 4'b0001;
NORMAL_NONCACHEABLE_NONBUFFERABLE : return 4'b0010;
NORMAL_NONCACHEABLE_BUFFERABLE : return 4'b0011;
WTHRU_NOALLOCATE : return 4'b0110;
WTHRU_RALLOCATE : return 4'b0110;
WTHRU_WALLOCATE : return 4'b1110;
WTHRU_RWALLOCATE : return 4'b1110;
WBACK_NOALLOCATE : return 4'b0111;
WBACK_RALLOCATE : return 4'b0111;
WBACK_WALLOCATE : return 4'b1111;
WBACK_RWALLOCATE : return 4'b1111;
endcase // mtype
endfunction
/// RESP precedence: DECERR > SLVERR > OKAY > EXOKAY. This is not defined in the AXI standard but
/// depends on the implementation. We consistently use the precedence above. Rationale:
/// - EXOKAY means an exclusive access was successful, whereas OKAY means it was not. Thus, if
/// OKAY and EXOKAY are to be merged, OKAY precedes because the exclusive access was not fully
/// successful.
/// - Both DECERR and SLVERR mean (part of) a transaction were unsuccessful, whereas OKAY means an
/// entire transaction was successful. Thus both DECERR and SLVERR precede OKAY.
/// - DECERR means (part of) a transactions could not be routed to a slave component, whereas
/// SLVERR means the transaction reached a slave component but lead to an error condition there.
/// Thus DECERR precedes SLVERR because DECERR happens earlier in the handling of a transaction.
function automatic resp_t resp_precedence(resp_t resp_a, resp_t resp_b);
unique case (resp_a)
RESP_OKAY: begin
// Any response except EXOKAY precedes OKAY.
if (resp_b == RESP_EXOKAY) begin
return resp_a;
end else begin
return resp_b;
end
end
RESP_EXOKAY: begin
// Any response precedes EXOKAY.
return resp_b;
end
RESP_SLVERR: begin
// Only DECERR precedes SLVERR.
if (resp_b == RESP_DECERR) begin
return resp_b;
end else begin
return resp_a;
end
end
RESP_DECERR: begin
// No response precedes DECERR.
return resp_a;
end
endcase
endfunction
// ATOP[5:0]
/// - Sends a single data value with an address.
/// - The target swaps the value at the addressed location with the data value that is supplied in
/// the transaction.
/// - The original data value at the addressed location is returned.
/// - Outbound data size is 1, 2, 4, or 8 bytes.
/// - Inbound data size is the same as the outbound data size.
localparam ATOP_ATOMICSWAP = 6'b110000;
/// - Sends two data values, the compare value and the swap value, to the addressed location.
/// The compare and swap values are of equal size.
/// - The data value at the addressed location is checked against the compare value:
/// - If the values match, the swap value is written to the addressed location.
/// - If the values do not match, the swap value is not written to the addressed location.
/// - The original data value at the addressed location is returned.
/// - Outbound data size is 2, 4, 8, 16, or 32 bytes.
/// - Inbound data size is half of the outbound data size because the outbound data contains both
/// compare and swap values, whereas the inbound data has only the original data value.
localparam ATOP_ATOMICCMP = 6'b110001;
// ATOP[5:4]
/// Perform no atomic operation.
localparam ATOP_NONE = 2'b00;
/// - Sends a single data value with an address and the atomic operation to be performed.
/// - The target performs the operation using the sent data and value at the addressed location as
/// operands.
/// - The result is stored in the address location.
/// - A single response is given without data.
/// - Outbound data size is 1, 2, 4, or 8 bytes.
localparam ATOP_ATOMICSTORE = 2'b01;
/// Sends a single data value with an address and the atomic operation to be performed.
/// - The original data value at the addressed location is returned.
/// - The target performs the operation using the sent data and value at the addressed location as
/// operands.
/// - The result is stored in the address location.
/// - Outbound data size is 1, 2, 4, or 8 bytes.
/// - Inbound data size is the same as the outbound data size.
localparam ATOP_ATOMICLOAD = 2'b10;
// ATOP[3]
/// For AtomicStore and AtomicLoad transactions `AWATOP[3]` indicates the endianness that is
/// required for the atomic operation. The value of `AWATOP[3]` applies to arithmetic operations
/// only and is ignored for bitwise logical operations.
/// When deasserted, this bit indicates that the operation is little-endian.
localparam ATOP_LITTLE_END = 1'b0;
/// When asserted, this bit indicates that the operation is big-endian.
localparam ATOP_BIG_END = 1'b1;
// ATOP[2:0]
/// The value in memory is added to the sent data and the result stored in memory.
localparam ATOP_ADD = 3'b000;
/// Every set bit in the sent data clears the corresponding bit of the data in memory.
localparam ATOP_CLR = 3'b001;
/// Bitwise exclusive OR of the sent data and value in memory.
localparam ATOP_EOR = 3'b010;
/// Every set bit in the sent data sets the corresponding bit of the data in memory.
localparam ATOP_SET = 3'b011;
/// The value stored in memory is the maximum of the existing value and sent data. This operation
/// assumes signed data.
localparam ATOP_SMAX = 3'b100;
/// The value stored in memory is the minimum of the existing value and sent data. This operation
/// assumes signed data.
localparam ATOP_SMIN = 3'b101;
/// The value stored in memory is the maximum of the existing value and sent data. This operation
/// assumes unsigned data.
localparam ATOP_UMAX = 3'b110;
/// The value stored in memory is the minimum of the existing value and sent data. This operation
/// assumes unsigned data.
localparam ATOP_UMIN = 3'b111;
// ATOP[5] == 1'b1 indicated that an atomic transaction has a read response
// Ussage eg: if (req_i.aw.atop[axi_pkg::ATOP_R_RESP]) begin
localparam ATOP_R_RESP = 32'd5;
// `xbar_latency_e` and `xbar_cfg_t` are documented in `doc/axi_xbar.md`.
/// Slice on Demux AW channel.
localparam logic [9:0] DemuxAw = (1 << 9);
/// Slice on Demux W channel.
localparam logic [9:0] DemuxW = (1 << 8);
/// Slice on Demux B channel.
localparam logic [9:0] DemuxB = (1 << 7);
/// Slice on Demux AR channel.
localparam logic [9:0] DemuxAr = (1 << 6);
/// Slice on Demux R channel.
localparam logic [9:0] DemuxR = (1 << 5);
/// Slice on Mux AW channel.
localparam logic [9:0] MuxAw = (1 << 4);
/// Slice on Mux W channel.
localparam logic [9:0] MuxW = (1 << 3);
/// Slice on Mux B channel.
localparam logic [9:0] MuxB = (1 << 2);
/// Slice on Mux AR channel.
localparam logic [9:0] MuxAr = (1 << 1);
/// Slice on Mux R channel.
localparam logic [9:0] MuxR = (1 << 0);
/// Latency configuration for `axi_xbar`.
typedef enum logic [9:0] {
NO_LATENCY = 10'b000_00_000_00,
CUT_SLV_AX = DemuxAw | DemuxAr,
CUT_MST_AX = MuxAw | MuxAr,
CUT_ALL_AX = DemuxAw | DemuxAr | MuxAw | MuxAr,
CUT_SLV_PORTS = DemuxAw | DemuxW | DemuxB | DemuxAr | DemuxR,
CUT_MST_PORTS = MuxAw | MuxW | MuxB | MuxAr | MuxR,
CUT_ALL_PORTS = 10'b111_11_111_11
} xbar_latency_e;
/// Configuration for `axi_xbar`.
typedef struct packed {
int unsigned NoSlvPorts;
int unsigned NoMstPorts;
int unsigned MaxMstTrans;
int unsigned MaxSlvTrans;
bit FallThrough;
xbar_latency_e LatencyMode;
int unsigned AxiIdWidthSlvPorts;
int unsigned AxiIdUsedSlvPorts;
bit UniqueIds;
int unsigned AxiAddrWidth;
int unsigned AxiDataWidth;
int unsigned NoAddrRules;
} xbar_cfg_t;
/// Commonly used rule types for `axi_xbar` (64-bit addresses).
typedef struct packed {
int unsigned idx;
logic [63:0] start_addr;
logic [63:0] end_addr;
} xbar_rule_64_t;
/// Commonly used rule types for `axi_xbar` (32-bit addresses).
typedef struct packed {
int unsigned idx;
logic [31:0] start_addr;
logic [31:0] end_addr;
} xbar_rule_32_t;
endpackage

View file

@ -0,0 +1,295 @@
// Copyright (c) 2020 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
//
// Authors:
// - Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>
// - Andreas Kurth <akurth@iis.ee.ethz.ch>
`include "common_cells/registers.svh"
/// Serialize all AXI transactions to a single ID (zero).
///
/// This module contains one queue with slave port IDs for the read direction and one for the write
/// direction. These queues are used to reconstruct the ID of responses at the slave port. The
/// depth of each queue is defined by `MaxReadTxns` and `MaxWriteTxns`, respectively.
module axi_serializer #(
/// Maximum number of in flight read transactions.
parameter int unsigned MaxReadTxns = 32'd0,
/// Maximum number of in flight write transactions.
parameter int unsigned MaxWriteTxns = 32'd0,
/// AXI4+ATOP ID width.
parameter int unsigned AxiIdWidth = 32'd0,
/// AXI4+ATOP request struct definition.
parameter type req_t = logic,
/// AXI4+ATOP response struct definition.
parameter type resp_t = logic
) (
/// Clock
input logic clk_i,
/// Asynchronous reset, active low
input logic rst_ni,
/// Slave port request
input req_t slv_req_i,
/// Slave port response
output resp_t slv_resp_o,
/// Master port request
output req_t mst_req_o,
/// Master port response
input resp_t mst_resp_i
);
typedef logic [AxiIdWidth-1:0] id_t;
typedef enum logic [1:0] {
AtopIdle = 2'b00,
AtopDrain = 2'b01,
AtopExecute = 2'b10
} state_e;
logic rd_fifo_full, rd_fifo_empty, rd_fifo_push, rd_fifo_pop,
wr_fifo_full, wr_fifo_empty, wr_fifo_push, wr_fifo_pop;
id_t b_id,
r_id, ar_id;
state_e state_q, state_d;
always_comb begin
// Default assignments
state_d = state_q;
rd_fifo_push = 1'b0;
wr_fifo_push = 1'b0;
// Default, connect the channels
mst_req_o = slv_req_i;
slv_resp_o = mst_resp_i;
// Serialize transactions -> tie downstream IDs to zero.
mst_req_o.aw.id = '0;
mst_req_o.ar.id = '0;
// Reflect upstream ID in response.
ar_id = slv_req_i.ar.id;
slv_resp_o.b.id = b_id;
slv_resp_o.r.id = r_id;
// Default, cut the AW/AR handshaking
mst_req_o.ar_valid = 1'b0;
slv_resp_o.ar_ready = 1'b0;
mst_req_o.aw_valid = 1'b0;
slv_resp_o.aw_ready = 1'b0;
unique case (state_q)
AtopIdle, AtopExecute: begin
// Wait until the ATOP response(s) have been sent back upstream.
if (state_q == AtopExecute) begin
if ((wr_fifo_empty && rd_fifo_empty) || (wr_fifo_pop && rd_fifo_pop) ||
(wr_fifo_empty && rd_fifo_pop) || (wr_fifo_pop && rd_fifo_empty)) begin
state_d = AtopIdle;
end
end
// This part lets new Transactions through, if no ATOP is underway or the last ATOP
// response has been transmitted.
if ((state_q == AtopIdle) || (state_d == AtopIdle)) begin
// Gate AR handshake with ready output of Read FIFO.
mst_req_o.ar_valid = slv_req_i.ar_valid & ~rd_fifo_full;
slv_resp_o.ar_ready = mst_resp_i.ar_ready & ~rd_fifo_full;
rd_fifo_push = mst_req_o.ar_valid & mst_resp_i.ar_ready;
if (slv_req_i.aw_valid) begin
if (slv_req_i.aw.atop[5:4] == axi_pkg::ATOP_NONE) begin
// Normal operation
// Gate AW handshake with ready output of Write FIFO.
mst_req_o.aw_valid = ~wr_fifo_full;
slv_resp_o.aw_ready = mst_resp_i.aw_ready & ~wr_fifo_full;
wr_fifo_push = mst_req_o.aw_valid & mst_resp_i.aw_ready;
end else begin
// Atomic Operation received, go to drain state, when both channels are ready
// Wait for finished or no AR beat
if (!mst_req_o.ar_valid || (mst_req_o.ar_valid && mst_resp_i.ar_ready)) begin
state_d = AtopDrain;
end
end
end
end
end
AtopDrain: begin
// Send the ATOP AW when the last open transaction terminates
if (wr_fifo_empty && rd_fifo_empty) begin
mst_req_o.aw_valid = 1'b1;
slv_resp_o.aw_ready = mst_resp_i.aw_ready;
wr_fifo_push = mst_resp_i.aw_ready;
if (slv_req_i.aw.atop[axi_pkg::ATOP_R_RESP]) begin
// Overwrite the read ID with the one from AW
ar_id = slv_req_i.aw.id;
rd_fifo_push = mst_resp_i.aw_ready;
end
if (mst_resp_i.aw_ready) begin
state_d = AtopExecute;
end
end
end
default : /* do nothing */;
endcase
// Gate B handshake with empty flag output of Write FIFO.
slv_resp_o.b_valid = mst_resp_i.b_valid & ~wr_fifo_empty;
mst_req_o.b_ready = slv_req_i.b_ready & ~wr_fifo_empty;
// Gate R handshake with empty flag output of Read FIFO.
slv_resp_o.r_valid = mst_resp_i.r_valid & ~rd_fifo_empty;
mst_req_o.r_ready = slv_req_i.r_ready & ~rd_fifo_empty;
end
fifo_v3 #(
.FALL_THROUGH ( 1'b0 ), // No fall-through as response has to come a cycle later anyway
.DEPTH ( MaxReadTxns ),
.dtype ( id_t )
) i_rd_id_fifo (
.clk_i,
.rst_ni,
.flush_i ( 1'b0 ),
.testmode_i ( 1'b0 ),
.data_i ( ar_id ),
.push_i ( rd_fifo_push ),
.full_o ( rd_fifo_full ),
.data_o ( r_id ),
.empty_o ( rd_fifo_empty ),
.pop_i ( rd_fifo_pop ),
.usage_o ( /*not used*/ )
);
// Assign as this condition is needed in FSM
assign rd_fifo_pop = slv_resp_o.r_valid & slv_req_i.r_ready & slv_resp_o.r.last;
fifo_v3 #(
.FALL_THROUGH ( 1'b0 ),
.DEPTH ( MaxWriteTxns ),
.dtype ( id_t )
) i_wr_id_fifo (
.clk_i,
.rst_ni,
.flush_i ( 1'b0 ),
.testmode_i ( 1'b0 ),
.data_i ( slv_req_i.aw.id ),
.push_i ( wr_fifo_push ),
.full_o ( wr_fifo_full ),
.data_o ( b_id ),
.empty_o ( wr_fifo_empty ),
.pop_i ( wr_fifo_pop ),
.usage_o ( /*not used*/ )
);
// Assign as this condition is needed in FSM
assign wr_fifo_pop = slv_resp_o.b_valid & slv_req_i.b_ready;
`FFARN(state_q, state_d, AtopIdle, clk_i, rst_ni)
// pragma translate_off
`ifndef VERILATOR
initial begin: p_assertions
assert (AxiIdWidth >= 1) else $fatal(1, "AXI ID width must be at least 1!");
assert (MaxReadTxns >= 1)
else $fatal(1, "Maximum number of read transactions must be >= 1!");
assert (MaxWriteTxns >= 1)
else $fatal(1, "Maximum number of write transactions must be >= 1!");
end
default disable iff (~rst_ni);
aw_lost : 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))
else $error("AW beat lost.");
w_lost : assert property( @(posedge clk_i)
(slv_req_i.w_valid & slv_resp_o.w_ready |-> mst_req_o.w_valid & mst_resp_i.w_ready))
else $error("W beat lost.");
b_lost : assert property( @(posedge clk_i)
(mst_resp_i.b_valid & mst_req_o.b_ready |-> slv_resp_o.b_valid & slv_req_i.b_ready))
else $error("B beat lost.");
ar_lost : assert property( @(posedge clk_i)
(slv_req_i.ar_valid & slv_resp_o.ar_ready |-> mst_req_o.ar_valid & mst_resp_i.ar_ready))
else $error("AR beat lost.");
r_lost : assert property( @(posedge clk_i)
(mst_resp_i.r_valid & mst_req_o.r_ready |-> slv_resp_o.r_valid & slv_req_i.r_ready))
else $error("R beat lost.");
`endif
// pragma translate_on
endmodule
`include "axi/typedef.svh"
`include "axi/assign.svh"
/// Serialize all AXI transactions to a single ID (zero), interface version.
module axi_serializer_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,
/// Maximum number of in flight read transactions.
parameter int unsigned MAX_READ_TXNS = 32'd0,
/// Maximum number of in flight write transactions.
parameter int unsigned MAX_WRITE_TXNS = 32'd0
) (
/// Clock
input logic clk_i,
/// Asynchronous reset, active low
input logic rst_ni,
/// AXI4+ATOP Slave modport
AXI_BUS.Slave slv,
/// AXI4+ATOP Master modport
AXI_BUS.Master mst
);
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(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_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_serializer #(
.MaxReadTxns ( MAX_READ_TXNS ),
.MaxWriteTxns ( MAX_WRITE_TXNS ),
.AxiIdWidth ( AXI_ID_WIDTH ),
.req_t ( req_t ),
.resp_t ( resp_t )
) i_axi_serializer (
.clk_i,
.rst_ni,
.slv_req_i ( slv_req ),
.slv_resp_o ( slv_resp ),
.mst_req_o ( mst_req ),
.mst_resp_i ( mst_resp )
);
// 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!");
assert (MAX_READ_TXNS >= 1)
else $fatal(1, "Maximum number of read transactions must be >= 1!");
assert (MAX_WRITE_TXNS >= 1)
else $fatal(1, "Maximum number of write transactions must be >= 1!");
end
`endif
// pragma translate_on
endmodule

View file

@ -0,0 +1,204 @@
// Copyright (c) 2020 ETH Zurich and University of Bologna
// SPDX-License-Identifier: SHL-0.51
//
// Authors:
// - Andreas Kurth <akurth@iis.ee.ethz.ch>
`include "axi/typedef.svh"
/// Infinite (Simulation-Only) Memory with AXI Slave Port
///
/// The memory array is named `mem`, and it is *not* initialized or reset. This makes it possible to
/// load the memory of this module in simulation with an external `$readmem*` command, e.g.,
/// ```sv
/// axi_sim_mem #( ... ) i_sim_mem ( ... );
/// initial begin
/// $readmemh("file_with_memory_addrs_and_data.mem", i_sim_mem.mem);
/// end
/// ```
/// `mem` is addressed (or indexed) byte-wise with `AddrWidth`-wide addresses.
///
/// This module does not support atomic operations (ATOPs).
module axi_sim_mem #(
/// AXI Address Width
parameter int unsigned AddrWidth = 32'd0,
/// AXI Data Width
parameter int unsigned DataWidth = 32'd0,
/// AXI ID Width
parameter int unsigned IdWidth = 32'd0,
/// AXI User Width.
parameter int unsigned UserWidth = 32'd0,
/// AXI4 request struct definition
parameter type req_t = logic,
/// AXI4 response struct definition
parameter type rsp_t = logic,
/// Warn on accesses to uninitialized bytes
parameter bit WarnUninitialized = 1'b0,
/// Application delay (measured after rising clock edge)
parameter time ApplDelay = 0ps,
/// Acquisition delay (measured after rising clock edge)
parameter time AcqDelay = 0ps
) (
/// Rising-edge clock
input logic clk_i,
/// Active-low reset
input logic rst_ni,
/// AXI4 request struct
input req_t axi_req_i,
/// AXI4 response struct
output rsp_t axi_rsp_o
);
localparam int unsigned StrbWidth = DataWidth / 8;
typedef logic [AddrWidth-1:0] addr_t;
typedef logic [DataWidth-1:0] data_t;
typedef logic [IdWidth-1:0] id_t;
typedef logic [StrbWidth-1:0] strb_t;
typedef logic [UserWidth-1:0] user_t;
`AXI_TYPEDEF_AW_CHAN_T(aw_t, addr_t, id_t, user_t)
`AXI_TYPEDEF_W_CHAN_T(w_t, data_t, strb_t, user_t)
`AXI_TYPEDEF_B_CHAN_T(b_t, id_t, user_t)
`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];
initial begin
automatic ar_t ar_queue[$];
automatic aw_t aw_queue[$];
automatic b_t b_queue[$];
automatic shortint unsigned r_cnt = 0, w_cnt = 0;
axi_rsp_o = '0;
wait (rst_ni);
fork
// AW
forever begin
@(posedge clk_i);
#(ApplDelay);
axi_rsp_o.aw_ready = 1'b1;
#(AcqDelay - ApplDelay);
if (axi_req_i.aw_valid) begin
automatic aw_t aw = axi_req_i.aw;
aw_queue.push_back(aw);
end
end
// W
forever begin
@(posedge clk_i);
#(ApplDelay);
axi_rsp_o.w_ready = 1'b0;
if (aw_queue.size() != 0) begin
axi_rsp_o.w_ready = 1'b1;
#(AcqDelay - ApplDelay);
if (axi_req_i.w_valid) begin
automatic axi_pkg::burst_t burst = aw_queue[0].burst;
automatic axi_pkg::len_t len = aw_queue[0].len;
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);
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);
i_byte++) begin
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];
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_queue.push_back(b_beat);
w_cnt = 0;
void'(aw_queue.pop_front());
end else begin
assert (!axi_req_i.w.last) else $error("Did not expect last beat of W burst!");
w_cnt++;
end
end
end
end
// B
forever begin
@(posedge clk_i);
#(ApplDelay);
axi_rsp_o.b_valid = 1'b0;
if (b_queue.size() != 0) begin
axi_rsp_o.b = b_queue[0];
axi_rsp_o.b_valid = 1'b1;
#(AcqDelay - ApplDelay);
if (axi_req_i.b_ready) begin
void'(b_queue.pop_front());
end
end
end
// AR
forever begin
@(posedge clk_i);
#(ApplDelay);
axi_rsp_o.ar_ready = 1'b1;
#(AcqDelay - ApplDelay);
if (axi_req_i.ar_valid) begin
automatic ar_t ar = axi_req_i.ar;
ar_queue.push_back(ar);
end
end
// R
forever begin
@(posedge clk_i);
#(ApplDelay);
axi_rsp_o.r_valid = 1'b0;
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;
r_beat.data = 'x;
r_beat.id = ar_queue[0].id;
r_beat.resp = axi_pkg::RESP_OKAY;
for (shortint unsigned
i_byte = axi_pkg::beat_lower_byte(addr, size, len, burst, StrbWidth, r_cnt);
i_byte <= axi_pkg::beat_upper_byte(addr, size, len, burst, StrbWidth, r_cnt);
i_byte++) begin
automatic addr_t byte_addr = (addr / StrbWidth) * StrbWidth + i_byte;
if (!mem.exists(byte_addr)) begin
if (WarnUninitialized) begin
$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;
end else begin
r_beat.data[i_byte*8+:8] = mem[byte_addr];
end
end
if (r_cnt == ar_queue[0].len) begin
r_beat.last = 1'b1;
end
axi_rsp_o.r = r_beat;
axi_rsp_o.r_valid = 1'b1;
#(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
end
end
end
join
end
// Parameter Assertions
initial begin
assert (AddrWidth != 0) else $fatal("AddrWidth must be non-zero!", 1);
assert (DataWidth != 0) else $fatal("DataWidth must be non-zero!", 1);
assert (IdWidth != 0) else $fatal("IdWidth must be non-zero!", 1);
assert (UserWidth != 0) else $fatal("UserWidth must be non-zero!", 1);
end
endmodule

2280
vendor/pulp-platform/axi/src/axi_test.sv vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,323 @@
// Copyright (c) 2014-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:
// - Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>
// - Andreas Kurth <akurth@iis.ee.ethz.ch>
// - Fabian Schuiki <fschuiki@iis.ee.ethz.ch>
// - Florian Zaruba <zarubaf@iis.ee.ethz.ch>
/// An AXI4+ATOP to AXI4-Lite converter with atomic transaction and burst support.
module axi_to_axi_lite #(
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 AxiMaxWriteTxns = 32'd0,
parameter int unsigned AxiMaxReadTxns = 32'd0,
parameter bit FallThrough = 1'b1, // FIFOs in Fall through mode in ID reflect
parameter type full_req_t = logic,
parameter type full_resp_t = logic,
parameter type lite_req_t = logic,
parameter type lite_resp_t = logic
) (
input logic clk_i, // Clock
input logic rst_ni, // Asynchronous reset active low
input logic test_i, // Testmode enable
// slave port full AXI4+ATOP
input full_req_t slv_req_i,
output full_resp_t slv_resp_o,
// master port AXI4-Lite
output lite_req_t mst_req_o,
input lite_resp_t mst_resp_i
);
// full bus declarations
full_req_t filtered_req, splitted_req;
full_resp_t filtered_resp, splitted_resp;
// atomics adapter so that atomics can be resolved
axi_atop_filter #(
.AxiIdWidth ( AxiIdWidth ),
.AxiMaxWriteTxns ( AxiMaxWriteTxns ),
.req_t ( full_req_t ),
.resp_t ( full_resp_t )
) i_axi_atop_filter(
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.slv_req_i ( slv_req_i ),
.slv_resp_o ( slv_resp_o ),
.mst_req_o ( filtered_req ),
.mst_resp_i ( filtered_resp )
);
// burst splitter so that the id reflect module has no burst accessing it
axi_burst_splitter #(
.MaxReadTxns ( AxiMaxReadTxns ),
.MaxWriteTxns ( AxiMaxWriteTxns ),
.AddrWidth ( AxiAddrWidth ),
.DataWidth ( AxiDataWidth ),
.IdWidth ( AxiIdWidth ),
.UserWidth ( AxiUserWidth ),
.req_t ( full_req_t ),
.resp_t ( full_resp_t )
) i_axi_burst_splitter (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.slv_req_i ( filtered_req ),
.slv_resp_o ( filtered_resp ),
.mst_req_o ( splitted_req ),
.mst_resp_i ( splitted_resp )
);
// ID reflect module handles the conversion from the full AXI to AXI lite on the wireing
axi_to_axi_lite_id_reflect #(
.AxiIdWidth ( AxiIdWidth ),
.AxiMaxWriteTxns ( AxiMaxWriteTxns ),
.AxiMaxReadTxns ( AxiMaxReadTxns ),
.FallThrough ( FallThrough ),
.full_req_t ( full_req_t ),
.full_resp_t ( full_resp_t ),
.lite_req_t ( lite_req_t ),
.lite_resp_t ( lite_resp_t )
) i_axi_to_axi_lite_id_reflect (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.test_i ( test_i ),
.slv_req_i ( splitted_req ),
.slv_resp_o ( splitted_resp ),
.mst_req_o ( mst_req_o ),
.mst_resp_i ( mst_resp_i )
);
// Assertions, check params
// pragma translate_off
`ifndef VERILATOR
initial begin
assume (AxiIdWidth > 0) else $fatal(1, "AXI ID width has to be > 0");
assume (AxiAddrWidth > 0) else $fatal(1, "AXI address width has to be > 0");
assume (AxiDataWidth > 0) else $fatal(1, "AXI data width has to be > 0");
end
`endif
// pragma translate_on
endmodule
// Description: This module does the translation of the full AXI4+ATOP to AXI4-Lite signals.
// It reflects the ID of the incoming transaction and crops all signals not used
// in AXI4-Lite. It requires that incoming AXI4+ATOP transactions have a
// `axi_pkg::len_t` of `'0` and an `axi_pkg::atop_t` of `'0`.
module axi_to_axi_lite_id_reflect #(
parameter int unsigned AxiIdWidth = 32'd0,
parameter int unsigned AxiMaxWriteTxns = 32'd0,
parameter int unsigned AxiMaxReadTxns = 32'd0,
parameter bit FallThrough = 1'b1, // FIFOs in fall through mode
parameter type full_req_t = logic,
parameter type full_resp_t = logic,
parameter type lite_req_t = logic,
parameter type lite_resp_t = logic
) (
input logic clk_i, // Clock
input logic rst_ni, // Asynchronous reset active low
input logic test_i, // Testmode enable
// slave port full AXI
input full_req_t slv_req_i,
output full_resp_t slv_resp_o,
// master port AXI LITE
output lite_req_t mst_req_o,
input lite_resp_t mst_resp_i
);
typedef logic [AxiIdWidth-1:0] id_t;
// FIFO status and control signals
logic aw_full, aw_empty, aw_push, aw_pop, ar_full, ar_empty, ar_push, ar_pop;
id_t aw_reflect_id, ar_reflect_id;
assign slv_resp_o = '{
aw_ready: mst_resp_i.aw_ready & ~aw_full,
w_ready: mst_resp_i.w_ready,
b: '{
id: aw_reflect_id,
resp: mst_resp_i.b.resp,
default: '0
},
b_valid: mst_resp_i.b_valid & ~aw_empty,
ar_ready: mst_resp_i.ar_ready & ~ar_full,
r: '{
id: ar_reflect_id,
data: mst_resp_i.r.data,
resp: mst_resp_i.r.resp,
last: 1'b1,
default: '0
},
r_valid: mst_resp_i.r_valid & ~ar_empty,
default: '0
};
// Write ID reflection
assign aw_push = mst_req_o.aw_valid & slv_resp_o.aw_ready;
assign aw_pop = slv_resp_o.b_valid & mst_req_o.b_ready;
fifo_v3 #(
.FALL_THROUGH ( FallThrough ),
.DEPTH ( AxiMaxWriteTxns ),
.dtype ( id_t )
) i_aw_id_fifo (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( 1'b0 ),
.testmode_i( test_i ),
.full_o ( aw_full ),
.empty_o ( aw_empty ),
.usage_o ( /*not used*/ ),
.data_i ( slv_req_i.aw.id ),
.push_i ( aw_push ),
.data_o ( aw_reflect_id ),
.pop_i ( aw_pop )
);
// Read ID reflection
assign ar_push = mst_req_o.ar_valid & slv_resp_o.ar_ready;
assign ar_pop = slv_resp_o.r_valid & mst_req_o.r_ready;
fifo_v3 #(
.FALL_THROUGH ( FallThrough ),
.DEPTH ( AxiMaxReadTxns ),
.dtype ( id_t )
) i_ar_id_fifo (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.flush_i ( 1'b0 ),
.testmode_i( test_i ),
.full_o ( ar_full ),
.empty_o ( ar_empty ),
.usage_o ( /*not used*/ ),
.data_i ( slv_req_i.ar.id ),
.push_i ( ar_push ),
.data_o ( ar_reflect_id ),
.pop_i ( ar_pop )
);
assign mst_req_o = '{
aw: '{
addr: slv_req_i.aw.addr,
prot: slv_req_i.aw.prot
},
aw_valid: slv_req_i.aw_valid & ~aw_full,
w: '{
data: slv_req_i.w.data,
strb: slv_req_i.w.strb
},
w_valid: slv_req_i.w_valid,
b_ready: slv_req_i.b_ready & ~aw_empty,
ar: '{
addr: slv_req_i.ar.addr,
prot: slv_req_i.ar.prot
},
ar_valid: slv_req_i.ar_valid & ~ar_full,
r_ready: slv_req_i.r_ready & ~ar_empty,
default: '0
};
// Assertions
// pragma translate_off
`ifndef VERILATOR
aw_atop: assume property( @(posedge clk_i) disable iff (~rst_ni)
slv_req_i.aw_valid |-> (slv_req_i.aw.atop == '0)) else
$fatal(1, "Module does not support atomics. Value observed: %0b", slv_req_i.aw.atop);
aw_axi_len: assume property( @(posedge clk_i) disable iff (~rst_ni)
slv_req_i.aw_valid |-> (slv_req_i.aw.len == '0)) else
$fatal(1, "AW request length has to be zero. Value observed: %0b", slv_req_i.aw.len);
w_axi_last: assume property( @(posedge clk_i) disable iff (~rst_ni)
slv_req_i.w_valid |-> (slv_req_i.w.last == 1'b1)) else
$fatal(1, "W last signal has to be one. Value observed: %0b", slv_req_i.w.last);
ar_axi_len: assume property( @(posedge clk_i) disable iff (~rst_ni)
slv_req_i.ar_valid |-> (slv_req_i.ar.len == '0)) else
$fatal(1, "AR request length has to be zero. Value observed: %0b", slv_req_i.ar.len);
`endif
// pragma translate_on
endmodule
// interface wrapper
`include "axi/assign.svh"
`include "axi/typedef.svh"
module axi_to_axi_lite_intf #(
/// AXI bus parameters
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,
/// Maximum number of outstanding writes.
parameter int unsigned AXI_MAX_WRITE_TXNS = 32'd1,
/// Maximum number of outstanding reads.
parameter int unsigned AXI_MAX_READ_TXNS = 32'd1,
parameter bit FALL_THROUGH = 1'b1
) (
input logic clk_i,
input logic rst_ni,
input logic testmode_i,
AXI_BUS.Slave slv,
AXI_LITE.Master mst
);
typedef logic [AXI_ADDR_WIDTH-1:0] addr_t;
typedef logic [AXI_DATA_WIDTH-1:0] data_t;
typedef logic [AXI_ID_WIDTH-1:0] id_t;
typedef logic [AXI_DATA_WIDTH/8-1:0] strb_t;
typedef logic [AXI_USER_WIDTH-1:0] user_t;
// full channels typedefs
`AXI_TYPEDEF_AW_CHAN_T(full_aw_chan_t, addr_t, id_t, user_t)
`AXI_TYPEDEF_W_CHAN_T(full_w_chan_t, data_t, strb_t, user_t)
`AXI_TYPEDEF_B_CHAN_T(full_b_chan_t, id_t, user_t)
`AXI_TYPEDEF_AR_CHAN_T(full_ar_chan_t, addr_t, id_t, user_t)
`AXI_TYPEDEF_R_CHAN_T(full_r_chan_t, data_t, id_t, user_t)
`AXI_TYPEDEF_REQ_T(full_req_t, full_aw_chan_t, full_w_chan_t, full_ar_chan_t)
`AXI_TYPEDEF_RESP_T(full_resp_t, full_b_chan_t, full_r_chan_t)
// LITE channels typedef
`AXI_LITE_TYPEDEF_AW_CHAN_T(lite_aw_chan_t, addr_t)
`AXI_LITE_TYPEDEF_W_CHAN_T(lite_w_chan_t, data_t, strb_t)
`AXI_LITE_TYPEDEF_B_CHAN_T(lite_b_chan_t)
`AXI_LITE_TYPEDEF_AR_CHAN_T(lite_ar_chan_t, addr_t)
`AXI_LITE_TYPEDEF_R_CHAN_T (lite_r_chan_t, data_t)
`AXI_LITE_TYPEDEF_REQ_T(lite_req_t, lite_aw_chan_t, lite_w_chan_t, lite_ar_chan_t)
`AXI_LITE_TYPEDEF_RESP_T(lite_resp_t, lite_b_chan_t, lite_r_chan_t)
full_req_t full_req;
full_resp_t full_resp;
lite_req_t lite_req;
lite_resp_t lite_resp;
`AXI_ASSIGN_TO_REQ(full_req, slv)
`AXI_ASSIGN_FROM_RESP(slv, full_resp)
`AXI_LITE_ASSIGN_FROM_REQ(mst, lite_req)
`AXI_LITE_ASSIGN_TO_RESP(lite_resp, mst)
axi_to_axi_lite #(
.AxiAddrWidth ( AXI_ADDR_WIDTH ),
.AxiDataWidth ( AXI_DATA_WIDTH ),
.AxiIdWidth ( AXI_ID_WIDTH ),
.AxiUserWidth ( AXI_USER_WIDTH ),
.AxiMaxWriteTxns ( AXI_MAX_WRITE_TXNS ),
.AxiMaxReadTxns ( AXI_MAX_READ_TXNS ),
.FallThrough ( FALL_THROUGH ), // FIFOs in Fall through mode in ID reflect
.full_req_t ( full_req_t ),
.full_resp_t ( full_resp_t ),
.lite_req_t ( lite_req_t ),
.lite_resp_t ( lite_resp_t )
) i_axi_to_axi_lite (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.test_i ( testmode_i ),
// slave port full AXI4+ATOP
.slv_req_i ( full_req ),
.slv_resp_o ( full_resp ),
// master port AXI4-Lite
.mst_req_o ( lite_req ),
.mst_resp_i ( lite_resp )
);
endmodule

324
vendor/pulp-platform/axi/src/axi_xbar.sv vendored Normal file
View file

@ -0,0 +1,324 @@
// Copyright (c) 2019 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
//
// Authors:
// - Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>
// - Andreas Kurth <akurth@iis.ee.ethz.ch>
// - Florian Zaruba <zarubaf@iis.ee.ethz.ch>
// 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
) (
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
);
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;
// signals from the axi_demuxes, one index more for decode error
slv_req_t [Cfg.NoSlvPorts-1:0][Cfg.NoMstPorts:0] slv_reqs;
slv_resp_t [Cfg.NoSlvPorts-1:0][Cfg.NoMstPorts:0] slv_resps;
// workaround for issue #133 (problem with vsim 10.6c)
localparam int unsigned cfg_NoMstPorts = Cfg.NoMstPorts;
// signals into the axi_muxes, are of type slave as the multiplexer extends the ID
slv_req_t [Cfg.NoMstPorts-1:0][Cfg.NoSlvPorts-1:0] mst_reqs;
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;
addr_decode #(
.NoIndices ( Cfg.NoMstPorts ),
.NoRules ( Cfg.NoAddrRules ),
.addr_t ( addr_t ),
.rule_t ( rule_t )
) i_axi_aw_decode (
.addr_i ( slv_ports_req_i[i].aw.addr ),
.addr_map_i ( addr_map_i ),
.idx_o ( dec_aw ),
.dec_valid_o ( dec_aw_valid ),
.dec_error_o ( dec_aw_error ),
.en_default_idx_i ( en_default_mst_port_i[i] ),
.default_idx_i ( default_mst_port_i[i] )
);
addr_decode #(
.NoIndices ( Cfg.NoMstPorts ),
.addr_t ( addr_t ),
.NoRules ( Cfg.NoAddrRules ),
.rule_t ( rule_t )
) i_axi_ar_decode (
.addr_i ( slv_ports_req_i[i].ar.addr ),
.addr_map_i ( addr_map_i ),
.idx_o ( dec_ar ),
.dec_valid_o ( dec_ar_valid ),
.dec_error_o ( dec_ar_error ),
.en_default_idx_i ( en_default_mst_port_i[i] ),
.default_idx_i ( default_mst_port_i[i] )
);
assign slv_aw_select = (dec_aw_error) ?
mst_port_idx_t'(Cfg.NoMstPorts) : mst_port_idx_t'(dec_aw);
assign slv_ar_select = (dec_ar_error) ?
mst_port_idx_t'(Cfg.NoMstPorts) : mst_port_idx_t'(dec_ar);
// make sure that the default slave does not get changed, if there is an unserved Ax
// pragma translate_off
`ifndef VERILATOR
`ifndef XSIM
default disable iff (~rst_ni);
default_aw_mst_port_en: assert property(
@(posedge clk_i) (slv_ports_req_i[i].aw_valid && !slv_ports_resp_o[i].aw_ready)
|=> $stable(en_default_mst_port_i[i]))
else $fatal (1, $sformatf("It is not allowed to change the default mst port\
enable, when there is an unserved Aw beat. Slave Port: %0d", i));
default_aw_mst_port: assert property(
@(posedge clk_i) (slv_ports_req_i[i].aw_valid && !slv_ports_resp_o[i].aw_ready)
|=> $stable(default_mst_port_i[i]))
else $fatal (1, $sformatf("It is not allowed to change the default mst port\
when there is an unserved Aw beat. Slave Port: %0d", i));
default_ar_mst_port_en: assert property(
@(posedge clk_i) (slv_ports_req_i[i].ar_valid && !slv_ports_resp_o[i].ar_ready)
|=> $stable(en_default_mst_port_i[i]))
else $fatal (1, $sformatf("It is not allowed to change the enable, when\
there is an unserved Ar beat. Slave Port: %0d", i));
default_ar_mst_port: assert property(
@(posedge clk_i) (slv_ports_req_i[i].ar_valid && !slv_ports_resp_o[i].ar_ready)
|=> $stable(default_mst_port_i[i]))
else $fatal (1, $sformatf("It is not allowed to change the default mst port\
when there is an unserved Ar beat. Slave Port: %0d", i));
`endif
`endif
// pragma translate_on
axi_demux #(
.AxiIdWidth ( Cfg.AxiIdWidthSlvPorts ), // ID Width
.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 ),
.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] ),
.SpillAr ( Cfg.LatencyMode[6] ),
.SpillR ( Cfg.LatencyMode[5] )
) i_axi_demux (
.clk_i, // Clock
.rst_ni, // Asynchronous reset active low
.test_i, // Testmode enable
.slv_req_i ( slv_ports_req_i[i] ),
.slv_aw_select_i ( slv_aw_select ),
.slv_ar_select_i ( slv_ar_select ),
.slv_resp_o ( slv_ports_resp_o[i] ),
.mst_reqs_o ( slv_reqs[i] ),
.mst_resps_i ( slv_resps[i] )
);
axi_err_slv #(
.AxiIdWidth ( Cfg.AxiIdWidthSlvPorts ),
.req_t ( slv_req_t ),
.resp_t ( slv_resp_t ),
.Resp ( axi_pkg::RESP_DECERR ),
.ATOPs ( ATOPs ),
.MaxTrans ( 4 ) // Transactions terminate at this slave, so minimize
// resource consumption by accepting only a few
// transactions at a time.
) i_axi_err_slv (
.clk_i, // Clock
.rst_ni, // Asynchronous reset active low
.test_i, // Testmode enable
// slave port
.slv_req_i ( slv_reqs[i][Cfg.NoMstPorts] ),
.slv_resp_o ( slv_resps[i][cfg_NoMstPorts] )
);
end
// 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];
end
end
for (genvar i = 0; i < Cfg.NoMstPorts; i++) begin : gen_mst_port_mux
axi_mux #(
.SlvAxiIDWidth ( Cfg.AxiIdWidthSlvPorts ), // ID width of the slave ports
.slv_aw_chan_t ( slv_aw_chan_t ), // AW Channel Type, slave ports
.mst_aw_chan_t ( mst_aw_chan_t ), // AW Channel Type, master port
.w_chan_t ( w_chan_t ), // W Channel Type, all ports
.slv_b_chan_t ( slv_b_chan_t ), // B Channel Type, slave ports
.mst_b_chan_t ( mst_b_chan_t ), // B Channel Type, master port
.slv_ar_chan_t ( slv_ar_chan_t ), // AR Channel Type, slave ports
.mst_ar_chan_t ( mst_ar_chan_t ), // AR Channel Type, master port
.slv_r_chan_t ( slv_r_chan_t ), // R Channel Type, slave ports
.mst_r_chan_t ( mst_r_chan_t ), // R Channel Type, master port
.slv_req_t ( slv_req_t ),
.slv_resp_t ( slv_resp_t ),
.mst_req_t ( mst_req_t ),
.mst_resp_t ( mst_resp_t ),
.NoSlvPorts ( Cfg.NoSlvPorts ), // Number of Masters for the module
.MaxWTrans ( Cfg.MaxSlvTrans ),
.FallThrough ( Cfg.FallThrough ),
.SpillAw ( Cfg.LatencyMode[4] ),
.SpillW ( Cfg.LatencyMode[3] ),
.SpillB ( Cfg.LatencyMode[2] ),
.SpillAr ( Cfg.LatencyMode[1] ),
.SpillR ( Cfg.LatencyMode[0] )
) i_axi_mux (
.clk_i, // Clock
.rst_ni, // Asynchronous reset active low
.test_i, // Test Mode enable
.slv_reqs_i ( mst_reqs[i] ),
.slv_resps_o ( mst_resps[i] ),
.mst_req_o ( mst_ports_req_o[i] ),
.mst_resp_i ( mst_ports_resp_i[i] )
);
end
// pragma translate_off
`ifndef VERILATOR
`ifndef XSIM
initial begin : check_params
id_slv_req_ports: assert ($bits(slv_ports_req_i[0].aw.id ) == Cfg.AxiIdWidthSlvPorts) else
$fatal(1, $sformatf("Slv_req and aw_chan id width not equal."));
id_slv_resp_ports: assert ($bits(slv_ports_resp_o[0].r.id) == Cfg.AxiIdWidthSlvPorts) else
$fatal(1, $sformatf("Slv_req and aw_chan id width not equal."));
end
`endif
`endif
// pragma translate_on
endmodule
`include "axi/assign.svh"
`include "axi/typedef.svh"
module axi_xbar_intf #(
parameter int unsigned AXI_USER_WIDTH = 0,
parameter axi_pkg::xbar_cfg_t Cfg = '0,
parameter type rule_t = axi_pkg::xbar_rule_64_t
) (
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
);
localparam int unsigned AxiIdWidthMstPorts = Cfg.AxiIdWidthSlvPorts + $clog2(Cfg.NoSlvPorts);
typedef logic [AxiIdWidthMstPorts -1:0] id_mst_t;
typedef logic [Cfg.AxiIdWidthSlvPorts -1:0] id_slv_t;
typedef logic [Cfg.AxiAddrWidth -1:0] addr_t;
typedef logic [Cfg.AxiDataWidth -1:0] data_t;
typedef logic [Cfg.AxiDataWidth/8 -1:0] strb_t;
typedef logic [AXI_USER_WIDTH -1:0] user_t;
`AXI_TYPEDEF_AW_CHAN_T(mst_aw_chan_t, addr_t, id_mst_t, user_t)
`AXI_TYPEDEF_AW_CHAN_T(slv_aw_chan_t, addr_t, id_slv_t, user_t)
`AXI_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t, user_t)
`AXI_TYPEDEF_B_CHAN_T(mst_b_chan_t, id_mst_t, user_t)
`AXI_TYPEDEF_B_CHAN_T(slv_b_chan_t, id_slv_t, user_t)
`AXI_TYPEDEF_AR_CHAN_T(mst_ar_chan_t, addr_t, id_mst_t, user_t)
`AXI_TYPEDEF_AR_CHAN_T(slv_ar_chan_t, addr_t, id_slv_t, user_t)
`AXI_TYPEDEF_R_CHAN_T(mst_r_chan_t, data_t, id_mst_t, user_t)
`AXI_TYPEDEF_R_CHAN_T(slv_r_chan_t, data_t, id_slv_t, user_t)
`AXI_TYPEDEF_REQ_T(mst_req_t, mst_aw_chan_t, w_chan_t, mst_ar_chan_t)
`AXI_TYPEDEF_REQ_T(slv_req_t, slv_aw_chan_t, w_chan_t, slv_ar_chan_t)
`AXI_TYPEDEF_RESP_T(mst_resp_t, mst_b_chan_t, mst_r_chan_t)
`AXI_TYPEDEF_RESP_T(slv_resp_t, slv_b_chan_t, slv_r_chan_t)
mst_req_t [Cfg.NoMstPorts-1:0] mst_reqs;
mst_resp_t [Cfg.NoMstPorts-1:0] mst_resps;
slv_req_t [Cfg.NoSlvPorts-1:0] slv_reqs;
slv_resp_t [Cfg.NoSlvPorts-1:0] slv_resps;
for (genvar i = 0; i < Cfg.NoMstPorts; 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 < Cfg.NoSlvPorts; 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_xbar #(
.Cfg (Cfg),
.slv_aw_chan_t ( slv_aw_chan_t ),
.mst_aw_chan_t ( mst_aw_chan_t ),
.w_chan_t ( w_chan_t ),
.slv_b_chan_t ( slv_b_chan_t ),
.mst_b_chan_t ( mst_b_chan_t ),
.slv_ar_chan_t ( slv_ar_chan_t ),
.mst_ar_chan_t ( mst_ar_chan_t ),
.slv_r_chan_t ( slv_r_chan_t ),
.mst_r_chan_t ( mst_r_chan_t ),
.slv_req_t ( slv_req_t ),
.slv_resp_t ( slv_resp_t ),
.mst_req_t ( mst_req_t ),
.mst_resp_t ( mst_resp_t ),
.rule_t ( rule_t )
) i_xbar (
.clk_i,
.rst_ni,
.test_i,
.slv_ports_req_i (slv_reqs ),
.slv_ports_resp_o (slv_resps),
.mst_ports_req_o (mst_reqs ),
.mst_ports_resp_i (mst_resps),
.addr_map_i,
.en_default_mst_port_i,
.default_mst_port_i
);
endmodule

View file

@ -0,0 +1,6 @@
.*
!.git*
build
scripts/vivado/add_sources.tcl
/Bender.lock
/Bender.local

View file

@ -0,0 +1,73 @@
# Changelog
All notable changes to this project will be documented in this file.
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
## 0.2.9 - 2022-03-17
### Changed
- Added optional `IS_FUNCTIONAL` flag to `tc_clk_gating` cell to optionally mark them as *not required for functionality*.
## 0.2.8
*Skipped*
## 0.2.7
*Skipped*
## 0.2.6 - 2021-10-04
### Added
- Add `pad_functional_xilinx
### Fixed
- Bender targets
### Removed
- Deprecated xilinx `clk_cell`s replaced by wrappers
## 0.2.5
*Skipped*
## 0.2.4 - 2021-02-04
- Add `deprecated/pulp_clk_cells_xilinx.sv` to `Bender.yml`
## 0.2.3 - 2021-01-28
### Fixed
- `tc_sram_xilinx`: Remove unsupported `string` type from `SimInit` parameter.
- `IPApproX:` Add `tc_sram` to `src_files.yml` for proper compilation with IPApproX
## 0.2.2 - 2020-11-11
### Fixed
- `Bender:` Add deprecated `pulp_clock_gating_async` for compatibility to `udma_core`.
## 0.2.1 - 2020-06-23
### Added
- `Bender:` Add `rtl/tc_sram` to target `rtl`, to prevent overwriting of target specific implementations.
### Fixed
- `tc_sram`: Drop string literal from parameter `SimInit` definition as synopsys throws an elaboration error.
- `tc_clk:tc_clk_delay`: Add Verilator and synthesis guards.
## 0.2.0 - 2020-03-18
### Added
- Add `tc_sram` and `tc_sram_xilinx`, with testbench for verifying technology specific implementations.
## 0.1.6 - 2019-11-18
### Added
- Add Readme
- Add Contribution Guide
### Changed
- Move modules of similar topic to a single file. This makes it easier to add new modules.
- Move separation between `cluster` and `pulp` to `deprecated` folder. There should be a single solution to a tech-cell.
## 0.1.1 - 2018-09-12
### Changed
- Polish release
- Keep Changelog
- Move to sources subfolder
## 0.1.0 - 2018-09-12
### Added
- Initial commit.

View file

@ -0,0 +1,3 @@
# Contributing
See [our style and contribution guidelines](https://github.com/pulp-platform/style-guidelines).

View file

@ -0,0 +1,176 @@
SOLDERPAD HARDWARE LICENSE version 0.51
This license is based closely on the Apache License Version 2.0, but is not
approved or endorsed by the Apache Foundation. A copy of the non-modified
Apache License 2.0 can be found at http://www.apache.org/licenses/LICENSE-2.0.
As this license is not currently OSI or FSF approved, the Licensor permits any
Work licensed under this License, at the option of the Licensee, to be treated
as licensed under the Apache License Version 2.0 (which is so approved).
This License is licensed under the terms of this License and in particular
clause 7 below (Disclaimer of Warranties) applies in relation to its use.
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and
distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the Rights owner or entity authorized by the Rights owner
that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other entities
that control, are controlled by, or are under common control with that entity.
For the purposes of this definition, "control" means (i) the power, direct or
indirect, to cause the direction or management of such entity, whether by
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising
permissions granted by this License.
"Rights" means copyright and any similar right including design right (whether
registered or unregistered), semiconductor topography (mask) rights and
database rights (but excluding Patents and Trademarks).
"Source" form shall mean the preferred form for making modifications, including
but not limited to source code, net lists, board layouts, CAD files,
documentation source, and configuration files.
"Object" form shall mean any form resulting from mechanical transformation or
translation of a Source form, including but not limited to compiled object
code, generated documentation, the instantiation of a hardware design and
conversions to other media types, including intermediate forms such as
bytecodes, FPGA bitstreams, artwork and semiconductor topographies (mask
works).
"Work" shall mean the work of authorship, whether in Source form or other
Object form, made available under the License, as indicated by a Rights notice
that is included in or attached to the work (an example is provided in the
Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object form, that
is based on (or derived from) the Work and for which the editorial revisions,
annotations, elaborations, or other modifications represent, as a whole, an
original work of authorship. For the purposes of this License, Derivative Works
shall not include works that remain separable from, or merely link (or bind by
name) or physically connect to or interoperate with the interfaces of, the Work
and Derivative Works thereof.
"Contribution" shall mean any design or work of authorship, including the
original version of the Work and any modifications or additions to that Work or
Derivative Works thereof, that is intentionally submitted to Licensor for
inclusion in the Work by the Rights owner or by an individual or Legal Entity
authorized to submit on behalf of the Rights owner. For the purposes of this
definition, "submitted" means any form of electronic, verbal, or written
communication sent to the Licensor or its representatives, including but not
limited to communication on electronic mailing lists, source code control
systems, and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but excluding
communication that is conspicuously marked or otherwise designated in writing
by the Rights owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
of whom a Contribution has been received by Licensor and subsequently
incorporated within the Work.
2. Grant of License. Subject to the terms and conditions of this License, each
Contributor hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable license under the Rights to reproduce,
prepare Derivative Works of, publicly display, publicly perform, sublicense,
and distribute the Work and such Derivative Works in Source or Object form and
do anything in relation to the Work as if the Rights did not exist.
3. Grant of Patent License. Subject to the terms and conditions of this
License, each Contributor hereby grants to You a perpetual, worldwide,
non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this
section) patent license to make, have made, use, offer to sell, sell, import,
and otherwise transfer the Work, where such license applies only to those
patent claims licensable by such Contributor that are necessarily infringed by
their Contribution(s) alone or by combination of their Contribution(s) with the
Work to which such Contribution(s) was submitted. If You institute patent
litigation against any entity (including a cross-claim or counterclaim in a
lawsuit) alleging that the Work or a Contribution incorporated within the Work
constitutes direct or contributory patent infringement, then any patent
licenses granted to You under this License for that Work shall terminate as of
the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the Work or
Derivative Works thereof in any medium, with or without modifications, and in
Source or Object form, provided that You meet the following conditions:
You must give any other recipients of the Work or Derivative Works a copy
of this License; and
You must cause any modified files to carry prominent notices stating that
You changed the files; and
You must retain, in the Source form of any Derivative Works that You
distribute, all copyright, patent, trademark, and attribution notices from
the Source form of the Work, excluding those notices that do not pertain to
any part of the Derivative Works; and
If the Work includes a "NOTICE" text file as part of its distribution, then
any Derivative Works that You distribute must include a readable copy of
the attribution notices contained within such NOTICE file, excluding those
notices that do not pertain to any part of the Derivative Works, in at
least one of the following places: within a NOTICE text file distributed as
part of the Derivative Works; within the Source form or documentation, if
provided along with the Derivative Works; or, within a display generated by
the Derivative Works, if and wherever such third-party notices normally
appear. The contents of the NOTICE file are for informational purposes only
and do not modify the License. You may add Your own attribution notices
within Derivative Works that You distribute, alongside or as an addendum to
the NOTICE text from the Work, provided that such additional attribution
notices cannot be construed as modifying the License. You may add Your own
copyright statement to Your modifications and may provide additional or
different license terms and conditions for use, reproduction, or
distribution of Your modifications, or for any such Derivative Works as a
whole, provided Your use, reproduction, and distribution of the Work
otherwise complies with the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise, any
Contribution intentionally submitted for inclusion in the Work by You to the
Licensor shall be under the terms and conditions of this License, without any
additional terms or conditions. Notwithstanding the above, nothing herein shall
supersede or modify the terms of any separate license agreement you may have
executed with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade names,
trademarks, service marks, or product names of the Licensor, except as required
for reasonable and customary use in describing the origin of the Work and
reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in
writing, Licensor provides the Work (and each Contributor provides its
Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied, including, without limitation, any warranties
or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any risks
associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory, whether in
tort (including negligence), contract, or otherwise, unless required by
applicable law (such as deliberate and grossly negligent acts) or agreed to in
writing, shall any Contributor be liable to You for damages, including any
direct, indirect, special, incidental, or consequential damages of any
character arising as a result of this License or out of the use or inability to
use the Work (including but not limited to damages for loss of goodwill, work
stoppage, computer failure or malfunction, or any and all other commercial
damages or losses), even if such Contributor has been advised of the
possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing the Work or
Derivative Works thereof, You may choose to offer, and charge a fee for,
acceptance of support, warranty, indemnity, or other liability obligations
and/or rights consistent with this License. However, in accepting such
obligations, You may act only on Your own behalf and on Your sole
responsibility, not on behalf of any other Contributor, and only if You agree
to indemnify, defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason of your
accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

View file

@ -0,0 +1,52 @@
# Generic Technology Cells (API to Cell Library)
Maintainer: Nils Wistoff <nwistoff@iis.ee.ethz.ch>
This repository contains technology related cells like SRAMs, clock-gating cells, power management cells. The description here are:
- **Behavioral**: Meant for RTL simulation only.
- **FPGA**: Meant for FPGA implementation. Currently we only support Xilinx FPGAs. But we are happy to accept patches for Altera or other devices.
It is the purpose of your technology specific file to include the cells with correct drive strength. We do not make the assumption in the front-end anymore.
> As this layer needs to be re-implemented for every new technology keep it thin!
## Cell Contents
If you want to get started in your own technology (either an unsupported FPGA or an ASIC technology) please provide implementations for the cells in this repository.
### Clock Cells
Clock cells usually are care-fully designed cells which do not exhibit any glitches. Therefore they need to be manually instantiated in ASIC designs. All clock cells can be found in `tc_clk.sv`.
| Name | Description | Status | Xilinx |
|-------------------|------------------------------|--------|--------------------|
| `tc_clk_and2` | Clock and gate | active | :white_check_mark: |
| `tc_clk_buffer` | Clock buffer | active | :white_check_mark: |
| `tc_clk_gating` | Integrated clock gating cell | active | :white_check_mark: |
| `tc_clk_inverter` | Clock inverter | active | :white_check_mark: |
| `tc_clk_mux2` | Clock Mux with two inputs | active | :white_check_mark: |
| `tc_clk_xor2` | Clock Xor | active | :white_check_mark: |
| `tc_clk_delay` | Programmable clock-delay | active | |
### Memory
| Name | Description | Status | Xilinx |
|-----------|----------------------------------------------------------|--------|--------------------|
| `tc_sram` | Configurable SRAM | active | :white_check_mark: |
### Power Cells
Power cells are mostly used for advanced power gating features and not used in any of our open-source IPs. However, feel-free to re-use them. All clock cells can be found in `tc_pwr.sv`.
| Name | Description | Status |
|-------------------------------------|--------------------------------------|--------|
| `tc_pwr_level_shifter_in` | Level Shifter | active |
| `tc_pwr_level_shifter_in_clamp_lo` | Level Shifter w/ clamp to `1'b0` | active |
| `tc_pwr_level_shifter_in_clamp_hi` | Level Shifter w/ clamp to `1'b1` | active |
| `tc_pwr_level_shifter_out` | Level Shifter | active |
| `tc_pwr_level_shifter_out_clamp_lo` | Level Shifter w/ clamp to `1'b0` | active |
| `tc_pwr_level_shifter_out_clamp_hi` | Level Shifter w/ clamp to `1'b1` | active |
| `tc_pwr_power_gating` | Power Gate with ctrl and status pins | active |
| `tc_pwr_isolation_lo` | Isolation Cell w/ isolate to `1'b0` | active |
| `tc_pwr_isolation_hi` | Isolation Cell w/ isolate to `1'b1` | active |

View file

@ -0,0 +1,94 @@
// Copyright 2019 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
module cluster_clock_and2 (
input logic clk0_i,
input logic clk1_i,
output logic clk_o
);
tc_clk_and2 i_tc_clk_and2 (
.clk0_i,
.clk1_i,
.clk_o
);
endmodule
module cluster_clock_buffer (
input logic clk_i,
output logic clk_o
);
tc_clk_buffer i_tc_clk_buffer (
.clk_i,
.clk_o
);
endmodule
// Description: Behavioral model of an integrated clock-gating cell (ICG)
module cluster_clock_gating (
input logic clk_i,
input logic en_i,
input logic test_en_i,
output logic clk_o
);
tc_clk_gating i_tc_clk_gating (
.clk_i,
.en_i,
.test_en_i,
.clk_o
);
endmodule
module cluster_clock_inverter (
input logic clk_i,
output logic clk_o
);
tc_clk_inverter i_tc_clk_inverter (
.clk_i,
.clk_o
);
endmodule
module cluster_clock_mux2 (
input logic clk0_i,
input logic clk1_i,
input logic clk_sel_i,
output logic clk_o
);
tc_clk_mux2 i_tc_clk_mux2 (
.clk0_i,
.clk1_i,
.clk_sel_i,
.clk_o
);
endmodule
module cluster_clock_xor2 (
input logic clk0_i,
input logic clk1_i,
output logic clk_o
);
tc_clk_xor2 i_tc_clk_xor2 (
.clk0_i,
.clk1_i,
.clk_o
);
endmodule

View file

@ -0,0 +1,59 @@
// 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.
// Description: This file contains power-related cells
// Mainly shifters at the moment.
module cluster_level_shifter_in (
input logic in_i,
output logic out_o
);
assign out_o = in_i;
endmodule
module cluster_level_shifter_in_clamp (
input logic in_i,
output logic out_o,
input logic clamp_i
);
assign out_o = clamp_i ? 1'b0 : in_i;
endmodule
module cluster_level_shifter_inout (
input logic data_i,
output logic data_o
);
assign data_o = data_i;
endmodule
module cluster_level_shifter_out (
input logic in_i,
output logic out_o
);
assign out_o = in_i;
endmodule
module cluster_level_shifter_out_clamp (
input logic in_i,
output logic out_o,
input logic clamp_i
);
assign out_o = clamp_i ? 1'b0 : in_i;
endmodule

View file

@ -0,0 +1,72 @@
// 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.
module generic_memory
#(
parameter ADDR_WIDTH = 12,
parameter DATA_WIDTH = 32,
parameter BE_WIDTH = DATA_WIDTH/8
)
(
input logic CLK,
input logic INITN,
input logic CEN,
input logic [ADDR_WIDTH-1:0] A,
input logic WEN,
input logic [DATA_WIDTH-1:0] D,
input logic [BE_WIDTH-1:0] BEN,
output logic [DATA_WIDTH-1:0] Q
);
localparam NUM_WORDS = 2**ADDR_WIDTH;
logic [DATA_WIDTH-1:0] MEM [NUM_WORDS-1:0];
logic [DATA_WIDTH-1:0] M;
genvar i,j;
generate
for (i=0; i<BE_WIDTH; i++)
begin
for (j=0; j<8; j++)
begin
assign M[i*8+j] = BEN[i];
end
end
endgenerate
generate
for (i=0; i < DATA_WIDTH ; i++)
begin
always @ (posedge CLK)
begin
if ( INITN == 1'b1 )
begin
if ( CEN == 1'b0 )
begin
if ( WEN == 1'b0 )
begin
if ( M[i] == 1'b0 )
begin
MEM[A][i] <= D[i];
end
end
else if(WEN == 1'b1)
begin
Q[i] <= MEM[A][i];
end
end
end
end
end
endgenerate
endmodule

View file

@ -0,0 +1,42 @@
// 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.
module generic_rom
#(
parameter ADDR_WIDTH = 32,
parameter DATA_WIDTH = 32,
parameter FILE_NAME = "./boot/boot_code.cde"
)
(
input logic CLK,
input logic CEN,
input logic [ADDR_WIDTH-1:0] A,
output logic [DATA_WIDTH-1:0] Q
);
localparam NUM_WORDS = 2**ADDR_WIDTH;
logic [DATA_WIDTH-1:0] MEM [NUM_WORDS-1:0];
logic [ADDR_WIDTH-1:0] A_Q;
initial
begin
$readmemb(FILE_NAME, MEM);
end
always_ff @(posedge CLK)
begin
if (CEN == 1'b0)
A_Q <= A;
end
assign Q = MEM[A_Q];
endmodule

View file

@ -0,0 +1,82 @@
// 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.
module pad_functional_pd (
input logic OEN,
input logic I,
output logic O,
input logic PEN,
inout wire PAD
);
/*
X Unknown
Z Hi-Z
H Pull High
L Pull Low
*/
/*
OEN I PAD PEN | PAD O
|
0 0 - 0/1 | 0 0
0 1 - 0/1 | 1 1
1 0/1 0 0/1 | - 0
1 0/1 1 0/1 | - 1
1 0/1 Z 0 | L L
1 0/1 Z 1 | - X
*/
wire PAD_wi;
bufif0 (PAD, I, OEN);
buf (O, PAD);
bufif0 (PAD_wi, 1'b0, PEN);
rpmos (PAD, PAD_wi, 1'b0);
endmodule
module pad_functional_pu (
input logic OEN,
input logic I,
output logic O,
input logic PEN,
inout wire PAD
);
/*
X Unknown
Z Hi-Z
H Pull High
L Pull Low
*/
/*
OEN I PAD PEN | PAD O
|
0 0 - 0/1 | 0 0
0 1 - 0/1 | 1 1
1 0/1 0 0/1 | - 0
1 0/1 1 0/1 | - 1
1 0/1 Z 0 | H H
1 0/1 Z 1 | - X
*/
wire PAD_wi;
bufif0 (PAD, I, OEN);
buf (O, PAD);
bufif0 (PAD_wi, 1'b1, PEN);
rpmos (PAD, PAD_wi, 1'b0);
endmodule

View file

@ -0,0 +1,19 @@
// 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.
module pulp_buffer
(
input logic in_i,
output logic out_o
);
assign out_o = in_i;
endmodule

View file

@ -0,0 +1,107 @@
// Copyright 2019 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
module pulp_clock_and2 (
input logic clk0_i,
input logic clk1_i,
output logic clk_o
);
tc_clk_and2 i_tc_clk_and2 (
.clk0_i,
.clk1_i,
.clk_o
);
endmodule
module pulp_clock_buffer (
input logic clk_i,
output logic clk_o
);
tc_clk_buffer i_tc_clk_buffer (
.clk_i,
.clk_o
);
endmodule
// Description: Behavioral model of an integrated clock-gating cell (ICG)
module pulp_clock_gating (
input logic clk_i,
input logic en_i,
input logic test_en_i,
output logic clk_o
);
tc_clk_gating i_tc_clk_gating (
.clk_i,
.en_i,
.test_en_i,
.clk_o
);
endmodule
module pulp_clock_inverter (
input logic clk_i,
output logic clk_o
);
tc_clk_inverter i_tc_clk_inverter (
.clk_i,
.clk_o
);
endmodule
module pulp_clock_mux2 (
input logic clk0_i,
input logic clk1_i,
input logic clk_sel_i,
output logic clk_o
);
tc_clk_mux2 i_tc_clk_mux2 (
.clk0_i,
.clk1_i,
.clk_sel_i,
.clk_o
);
endmodule
module pulp_clock_xor2 (
input logic clk0_i,
input logic clk1_i,
output logic clk_o
);
tc_clk_xor2 i_tc_clk_xor2 (
.clk0_i,
.clk1_i,
.clk_o
);
endmodule
`ifndef SYNTHESIS
module pulp_clock_delay(
input logic in_i,
output logic out_o
);
assign #(300ps) out_o = in_i;
endmodule
`endif

View file

@ -0,0 +1,44 @@
// Copyright 2019 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
// TODO(zarubaf): This is not really a tech cell - move it to common cells
module pulp_clock_gating_async #(
parameter int unsigned STAGES = 2
) (
input logic clk_i,
input logic rstn_i,
input logic en_async_i,
output logic en_ack_o,
input logic test_en_i,
output logic clk_o
);
logic [STAGES-1:0] r_reg;
assign en_ack_o = r_reg[STAGES-1];
// synchronize enable signal
always_ff @ (posedge clk_i or negedge rstn_i) begin
if (!rstn_i) begin
r_reg <= '0;
end else begin
r_reg <= {r_reg[STAGES-2:0], en_async_i};
end
end
pulp_clock_gating i_clk_gate (
.clk_i,
.en_i ( r_reg[STAGES-1] ),
.test_en_i,
.clk_o
);
endmodule

View file

@ -0,0 +1,88 @@
// 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.
// Description: This file contains power-related cells
// Mainly shifters at the moment.
module pulp_level_shifter_in (
input logic in_i,
output logic out_o
);
assign out_o = in_i;
endmodule
module pulp_level_shifter_in_clamp (
input logic in_i,
output logic out_o,
input logic clamp_i
);
assign out_o = clamp_i ? 1'b0 : in_i;
endmodule
module pulp_level_shifter_inout (
input logic data_i,
output logic data_o
);
assign data_o = data_i;
endmodule
module pulp_level_shifter_out (
input logic in_i,
output logic out_o
);
assign out_o = in_i;
endmodule
module pulp_level_shifter_out_clamp (
input logic in_i,
output logic out_o,
input logic clamp_i
);
assign out_o = clamp_i ? 1'b0 : in_i;
endmodule
module pulp_power_gating (
input logic sleep_i,
output logic sleepout_o
);
assign sleepout_o = sleep_i;
endmodule
module pulp_isolation_0 (
input logic data_i,
input logic ena_i,
output logic data_o
);
assign data_o = ena_i ? data_i : 1'b0;
endmodule
module pulp_isolation_1 (
input logic data_i,
input logic ena_i,
output logic data_o
);
assign data_o = ena_i ? data_i : 1'b1;
endmodule

View file

@ -0,0 +1,48 @@
// 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.
module pad_functional_pd
(
input logic OEN,
input logic I,
output logic O,
input logic PEN,
inout logic PAD
);
(* PULLDOWN = "YES" *)
IOBUF iobuf_i (
.T ( OEN ),
.I ( I ),
.O ( O ),
.IO( PAD )
);
endmodule
module pad_functional_pu
(
input logic OEN,
input logic I,
output logic O,
input logic PEN,
inout logic PAD
);
(* PULLUP = "YES" *)
IOBUF iobuf_i (
.T ( OEN ),
.I ( I ),
.O ( O ),
.IO( PAD )
);
endmodule

View file

@ -0,0 +1,78 @@
// Copyright 2019 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
// Cells to be used for Xilinx FPGA mappings
module tc_clk_and2 (
input logic clk0_i,
input logic clk1_i,
output logic clk_o
);
assign clk_o = clk0_i & clk1_i;
endmodule
module tc_clk_buffer (
input logic clk_i,
output logic clk_o
);
assign clk_o = clk_i;
endmodule
// Disable clock gating on FPGA as it behaves differently than expected
module tc_clk_gating (
input logic clk_i,
input logic en_i,
input logic test_en_i,
output logic clk_o
);
assign clk_o = clk_i;
endmodule
module tc_clk_inverter (
input logic clk_i,
output logic clk_o
);
assign clk_o = ~clk_i;
endmodule
module tc_clk_mux2 (
input logic clk0_i,
input logic clk1_i,
input logic clk_sel_i,
output logic clk_o
);
BUFGMUX i_BUFGMUX (
.S ( clk_sel_i ),
.I0 ( clk0_i ),
.I1 ( clk1_i ),
.O ( clk_o )
);
endmodule
module tc_clk_xor2 (
input logic clk0_i,
input logic clk1_i,
output logic clk_o
);
assign clk_o = clk0_i ^ clk1_i;
endmodule

View file

@ -0,0 +1,210 @@
// 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.
//
// Author: Wolfgang Roenninger <wroennin@ethz.ch>, ETH Zurich
//
// Description: Xilinx implementation using the XPM constructs for `tc_sram`
// Make sure that Vivado can detect the XPM macros by issuing
// the `auto_detect_xpm` or `set_property XPM_LIBRARIES XPM_MEMORY [current_project]`
// command. Currently the Xilinx macros are always initialized to all zero!
// The behaviour, parameters and ports are described in the header of `rtl/tc_sram.sv`.
module tc_sram #(
parameter int unsigned NumWords = 32'd1024, // Number of Words in data array
parameter int unsigned DataWidth = 32'd128, // Data signal width (in bits)
parameter int unsigned ByteWidth = 32'd8, // Width of a data byte (in bits)
parameter int unsigned NumPorts = 32'd2, // Number of read and write ports
parameter int unsigned Latency = 32'd1, // Latency when the read data is available
parameter SimInit = "zeros", // Simulation initialization, fixed to zero here!
parameter bit PrintSimCfg = 1'b0, // Print configuration
// DEPENDENT PARAMETERS, DO NOT OVERWRITE!
parameter int unsigned AddrWidth = (NumWords > 32'd1) ? $clog2(NumWords) : 32'd1,
parameter int unsigned BeWidth = (DataWidth + ByteWidth - 32'd1) / ByteWidth, // ceil_div
parameter type addr_t = logic [AddrWidth-1:0],
parameter type data_t = logic [DataWidth-1:0],
parameter type be_t = logic [BeWidth-1:0]
) (
input logic clk_i, // Clock
input logic rst_ni, // Asynchronous reset active low
// input ports
input logic [NumPorts-1:0] req_i, // request
input logic [NumPorts-1:0] we_i, // write enable
input addr_t [NumPorts-1:0] addr_i, // request address
input data_t [NumPorts-1:0] wdata_i, // write data
input be_t [NumPorts-1:0] be_i, // write byte enable
// output ports
output data_t [NumPorts-1:0] rdata_o // read data
);
localparam int unsigned DataWidthAligned = ByteWidth * BeWidth;
localparam int unsigned Size = NumWords * DataWidthAligned;
typedef logic [DataWidthAligned-1:0] data_aligned_t;
data_aligned_t [NumPorts-1:0] wdata_al;
data_aligned_t [NumPorts-1:0] rdata_al;
be_t [NumPorts-1:0] we;
// pad with 0 to next byte for inferable macro below, as the macro wants
// READ_DATA_WIDTH_A be a multiple of BYTE_WRITE_WIDTH_A
always_comb begin : p_align
wdata_al = '0;
for (int unsigned i = 0; i < NumPorts; i++) begin
wdata_al[i][DataWidth-1:0] = wdata_i[i];
end
end
for (genvar i = 0; i < NumPorts; i++) begin : gen_port_assign
for (genvar j = 0; j < BeWidth; j++) begin : gen_we_assign
assign we[i][j] = be_i[i][j] & we_i[i];
end
assign rdata_o[i] = data_t'(rdata_al[i]);
end
if (NumPorts == 32'd1) begin : gen_1_ports
// xpm_memory_spram: Single Port RAM
// XilinxParameterizedMacro, version 2018.1
xpm_memory_spram#(
.ADDR_WIDTH_A ( AddrWidth ), // DECIMAL
.AUTO_SLEEP_TIME ( 0 ), // DECIMAL
.BYTE_WRITE_WIDTH_A ( ByteWidth ), // DECIMAL
.ECC_MODE ( "no_ecc" ), // String
.MEMORY_INIT_FILE ( "none" ), // String
.MEMORY_INIT_PARAM ( "0" ), // String
.MEMORY_OPTIMIZATION ( "true" ), // String
.MEMORY_PRIMITIVE ( "auto" ), // String
.MEMORY_SIZE ( Size ), // DECIMAL in bit!
.MESSAGE_CONTROL ( 0 ), // DECIMAL
.READ_DATA_WIDTH_A ( DataWidthAligned ), // DECIMAL
.READ_LATENCY_A ( Latency ), // DECIMAL
.READ_RESET_VALUE_A ( "0" ), // String
.USE_MEM_INIT ( 1 ), // DECIMAL
.WAKEUP_TIME ( "disable_sleep" ), // String
.WRITE_DATA_WIDTH_A ( DataWidthAligned ), // DECIMAL
.WRITE_MODE_A ( "no_change" ) // String
) i_xpm_memory_spram (
.dbiterra ( /*not used*/ ), // 1-bit output: Status signal to indicate double biterror
.douta ( rdata_al[0] ), // READ_DATA_WIDTH_A-bitoutput: Data output for port A
.sbiterra ( /*not used*/ ), // 1-bit output: Status signal to indicate single biterror
.addra ( addr_i[0] ), // ADDR_WIDTH_A-bit input: Address for port A
.clka ( clk_i ), // 1-bit input: Clock signal for port A.
.dina ( wdata_al[0] ), // WRITE_DATA_WIDTH_A-bitinput: Data input for port A
.ena ( req_i[0] ), // 1-bit input: Memory enable signal for port A.
.injectdbiterra ( 1'b0 ), // 1-bit input: Controls double biterror injection
.injectsbiterra ( 1'b0 ), // 1-bit input: Controls single biterror injection
.regcea ( 1'b1 ), // 1-bit input: Clock Enable for the last register
.rsta ( ~rst_ni ), // 1-bit input: Reset signal for the final port A output
.sleep ( 1'b0 ), // 1-bit input: sleep signal to enable the dynamic power save
.wea ( we[0] )
);
end else if (NumPorts == 32'd2) begin : gen_2_ports
// xpm_memory_tdpram: True Dual Port RAM
// XilinxParameterizedMacro, version 2018.1
xpm_memory_tdpram#(
.ADDR_WIDTH_A ( AddrWidth ), // DECIMAL
.ADDR_WIDTH_B ( AddrWidth ), // DECIMAL
.AUTO_SLEEP_TIME ( 0 ), // DECIMAL
.BYTE_WRITE_WIDTH_A ( ByteWidth ), // DECIMAL
.BYTE_WRITE_WIDTH_B ( ByteWidth ), // DECIMAL
.CLOCKING_MODE ( "common_clock" ), // String
.ECC_MODE ( "no_ecc" ), // String
.MEMORY_INIT_FILE ( "none" ), // String
.MEMORY_INIT_PARAM ( "0" ), // String
.MEMORY_OPTIMIZATION ( "true" ), // String
.MEMORY_PRIMITIVE ( "auto" ), // String
.MEMORY_SIZE ( Size ), // DECIMAL in bits!
.MESSAGE_CONTROL ( 0 ), // DECIMAL
.READ_DATA_WIDTH_A ( DataWidthAligned ), // DECIMAL
.READ_DATA_WIDTH_B ( DataWidthAligned ), // DECIMAL
.READ_LATENCY_A ( Latency ), // DECIMAL
.READ_LATENCY_B ( Latency ), // DECIMAL
.READ_RESET_VALUE_A ( "0" ), // String
.READ_RESET_VALUE_B ( "0" ), // String
.USE_EMBEDDED_CONSTRAINT ( 0 ), // DECIMAL
.USE_MEM_INIT ( 1 ), // DECIMAL
.WAKEUP_TIME ( "disable_sleep" ), // String
.WRITE_DATA_WIDTH_A ( DataWidthAligned ), // DECIMAL
.WRITE_DATA_WIDTH_B ( DataWidthAligned ), // DECIMAL
.WRITE_MODE_A ( "no_change" ), // String
.WRITE_MODE_B ( "no_change" ) // String
) i_xpm_memory_tdpram (
.dbiterra ( /*not used*/ ), // 1-bit output: Doubble bit error A
.dbiterrb ( /*not used*/ ), // 1-bit output: Doubble bit error B
.sbiterra ( /*not used*/ ), // 1-bit output: Single bit error A
.sbiterrb ( /*not used*/ ), // 1-bit output: Single bit error B
.addra ( addr_i[0] ), // ADDR_WIDTH_A-bit input: Address for port A
.addrb ( addr_i[1] ), // ADDR_WIDTH_B-bit input: Address for port B
.clka ( clk_i ), // 1-bit input: Clock signal for port A
.clkb ( clk_i ), // 1-bit input: Clock signal for port B
.dina ( wdata_al[0] ), // WRITE_DATA_WIDTH_A-bit input: Data input for port A
.dinb ( wdata_al[1] ), // WRITE_DATA_WIDTH_B-bit input: Data input for port B
.douta ( rdata_al[0] ), // READ_DATA_WIDTH_A-bit output: Data output for port A
.doutb ( rdata_al[1] ), // READ_DATA_WIDTH_B-bit output: Data output for port B
.ena ( req_i[0] ), // 1-bit input: Memory enable signal for port A
.enb ( req_i[1] ), // 1-bit input: Memory enable signal for port B
.injectdbiterra ( 1'b0 ), // 1-bit input: Controls doublebiterror injection on input data
.injectdbiterrb ( 1'b0 ), // 1-bit input: Controls doublebiterror injection on input data
.injectsbiterra ( 1'b0 ), // 1-bit input: Controls singlebiterror injection on input data
.injectsbiterrb ( 1'b0 ), // 1-bit input: Controls singlebiterror injection on input data
.regcea ( 1'b1 ), // 1-bit input: Clock Enable for the last register stage
.regceb ( 1'b1 ), // 1-bit input: Clock Enable for the last register stage
.rsta ( ~rst_ni ), // 1-bit input: Reset signal for the final port A output
.rstb ( ~rst_ni ), // 1-bit input: Reset signal for the final port B output
.sleep ( 1'b0 ), // 1-bit input: sleep signal to enable the dynamic power
.wea ( we[0] ), // WRITE_DATA_WIDTH_A-bit input: Write enable vector for port A
.web ( we[1] ) // WRITE_DATA_WIDTH_B-bit input: Write enable vector for port B
);
end else begin : gen_err_ports
$fatal(1, "Not supported port parametrization for NumPorts: %0d", NumPorts);
end
// Validate parameters.
// pragma translate_off
`ifndef VERILATOR
`ifndef TARGET_SYNTHESIS
initial begin: p_assertions
assert (SimInit == "zeros") else $fatal(1, "The Xilinx `tc_sram` has fixed SimInit: zeros");
assert ($bits(addr_i) == NumPorts * AddrWidth) else $fatal(1, "AddrWidth problem on `addr_i`");
assert ($bits(wdata_i) == NumPorts * DataWidth) else $fatal(1, "DataWidth problem on `wdata_i`");
assert ($bits(be_i) == NumPorts * BeWidth) else $fatal(1, "BeWidth problem on `be_i`" );
assert ($bits(rdata_o) == NumPorts * DataWidth) else $fatal(1, "DataWidth problem on `rdata_o`");
assert (NumWords >= 32'd1) else $fatal(1, "NumWords has to be > 0");
assert (DataWidth >= 32'd1) else $fatal(1, "DataWidth has to be > 0");
assert (ByteWidth >= 32'd1) else $fatal(1, "ByteWidth has to be > 0");
assert (NumPorts >= 32'd1) else $fatal(1, "The number of ports must be at least 1!");
end
initial begin: p_sim_hello
if (PrintSimCfg) begin
$display("#################################################################################");
$display("tc_sram functional instantiated with the configuration:" );
$display("Instance: %m" );
$display("Number of ports (dec): %0d", NumPorts );
$display("Number of words (dec): %0d", NumWords );
$display("Address width (dec): %0d", AddrWidth );
$display("Data width (dec): %0d", DataWidth );
$display("Byte width (dec): %0d", ByteWidth );
$display("Byte enable width (dec): %0d", BeWidth );
$display("Latency Cycles (dec): %0d", Latency );
$display("Simulation init (str): %0s", SimInit );
$display("#################################################################################");
end
end
for (genvar i = 0; i < NumPorts; i++) begin : gen_assertions
assert property ( @(posedge clk_i) disable iff (!rst_ni)
(req_i[i] |-> (addr_i[i] < NumWords))) else
$warning("Request address %0h not mapped, port %0d, expect random write or read behavior!",
addr_i[i], i);
end
`endif
`endif
// pragma translate_on
endmodule

View file

@ -0,0 +1,102 @@
// Copyright 2019 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
module tc_clk_and2 (
input logic clk0_i,
input logic clk1_i,
output logic clk_o
);
assign clk_o = clk0_i & clk1_i;
endmodule
module tc_clk_buffer (
input logic clk_i,
output logic clk_o
);
assign clk_o = clk_i;
endmodule
// Description: Behavioral model of an integrated clock-gating cell (ICG)
module tc_clk_gating #(
/// This paramaeter is a hint for tool/technology specific mappings of this
/// tech_cell. It indicates wether this particular clk gate instance is
/// required for functional correctness or just instantiated for power
/// savings. If IS_FUNCTIONAL == 0, technology specific mappings might
/// replace this cell with a feedthrough connection without any gating.
parameter bit IS_FUNCTIONAL = 1'b1
)(
input logic clk_i,
input logic en_i,
input logic test_en_i,
output logic clk_o
);
logic clk_en;
always_latch begin
if (clk_i == 1'b0) clk_en <= en_i | test_en_i;
end
assign clk_o = clk_i & clk_en;
endmodule
module tc_clk_inverter (
input logic clk_i,
output logic clk_o
);
assign clk_o = ~clk_i;
endmodule
module tc_clk_mux2 (
input logic clk0_i,
input logic clk1_i,
input logic clk_sel_i,
output logic clk_o
);
assign clk_o = (clk_sel_i) ? clk1_i : clk0_i;
endmodule
module tc_clk_xor2 (
input logic clk0_i,
input logic clk1_i,
output logic clk_o
);
assign clk_o = clk0_i ^ clk1_i;
endmodule
`ifndef SYNTHESIS
module tc_clk_delay #(
parameter int unsigned Delay = 300ps
) (
input logic in_i,
output logic out_o
);
// pragma translate_off
`ifndef VERILATOR
assign #(Delay) out_o = in_i;
`endif
// pragma translate_on
endmodule
`endif

View file

@ -0,0 +1,241 @@
// Copyright (c) 2020 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
// Author: Wolfgang Roenninger <wroennin@ethz.ch>
// Description: Functional module of a generic SRAM
//
// Parameters:
// - NumWords: Number of words in the macro. Address width can be calculated with:
// `AddrWidth = (NumWords > 32'd1) ? $clog2(NumWords) : 32'd1`
// The module issues a warning if there is a request on an address which is
// not in range.
// - DataWidth: Width of the ports `wdata_i` and `rdata_o`.
// - ByteWidth: Width of a byte, the byte enable signal `be_i` can be calculated with the
// ceiling division `ceil(DataWidth, ByteWidth)`.
// - NumPorts: Number of read and write ports. Each is a full port. Ports with a higher
// index read and write after the ones with lower indices.
// - Latency: Read latency, the read data is available this many cycles after a request.
// - SimInit: Macro simulation initialization. Values are:
// "zeros": Each bit gets initialized with 1'b0.
// "ones": Each bit gets initialized with 1'b1.
// "random": Each bit gets random initialized with 1'b0 or 1'b1.
// "none": Each bit gets initialized with 1'bx. (default)
// - PrintSimCfg: Prints at the beginning of the simulation a `Hello` message with
// the instantiated parameters and signal widths.
//
// Ports:
// - `clk_i`: Clock
// - `rst_ni`: Asynchronous reset, active low
// - `req_i`: Request, active high
// - `we_i`: Write request, active high
// - `addr_i`: Request address
// - `wdata_i`: Write data, has to be valid on request
// - `be_i`: Byte enable, active high
// - `rdata_o`: Read data, valid `Latency` cycles after a request with `we_i` low.
//
// Behaviour:
// - Address collision: When Ports are making a write access onto the same address,
// the write operation will start at the port with the lowest address
// index, each port will overwrite the changes made by the previous ports
// according how the respective `be_i` signal is set.
// - Read data on write: This implementation will not produce a read data output on the signal
// `rdata_o` when `req_i` and `we_i` are asserted. The output data is stable
// on write requests.
module tc_sram #(
parameter int unsigned NumWords = 32'd1024, // Number of Words in data array
parameter int unsigned DataWidth = 32'd128, // Data signal width
parameter int unsigned ByteWidth = 32'd8, // Width of a data byte
parameter int unsigned NumPorts = 32'd2, // Number of read and write ports
parameter int unsigned Latency = 32'd1, // Latency when the read data is available
parameter SimInit = "none", // Simulation initialization
parameter bit PrintSimCfg = 1'b0, // Print configuration
// DEPENDENT PARAMETERS, DO NOT OVERWRITE!
parameter int unsigned AddrWidth = (NumWords > 32'd1) ? $clog2(NumWords) : 32'd1,
parameter int unsigned BeWidth = (DataWidth + ByteWidth - 32'd1) / ByteWidth, // ceil_div
parameter type addr_t = logic [AddrWidth-1:0],
parameter type data_t = logic [DataWidth-1:0],
parameter type be_t = logic [BeWidth-1:0]
) (
input logic clk_i, // Clock
input logic rst_ni, // Asynchronous reset active low
// input ports
input logic [NumPorts-1:0] req_i, // request
input logic [NumPorts-1:0] we_i, // write enable
input addr_t [NumPorts-1:0] addr_i, // request address
input data_t [NumPorts-1:0] wdata_i, // write data
input be_t [NumPorts-1:0] be_i, // write byte enable
// output ports
output data_t [NumPorts-1:0] rdata_o // read data
);
// memory array
data_t sram [NumWords-1:0];
// hold the read address when no read access is made
addr_t [NumPorts-1:0] r_addr_q;
// SRAM simulation initialization
data_t init_val[NumWords-1:0];
initial begin : proc_sram_init
for (int unsigned i = 0; i < NumWords; i++) begin
case (SimInit)
"zeros": init_val[i] = {DataWidth{1'b0}};
"ones": init_val[i] = {DataWidth{1'b1}};
"random": init_val[i] = {DataWidth{$urandom()}};
default: init_val[i] = {DataWidth{1'bx}};
endcase
end
end
// set the read output if requested
// The read data at the highest array index is set combinational.
// It gets then delayed for a number of cycles until it gets available at the output at
// array index 0.
// read data output assignment
data_t [NumPorts-1:0][Latency-1:0] rdata_q, rdata_d;
if (Latency == 32'd0) begin : gen_no_read_lat
for (genvar i = 0; i < NumPorts; i++) begin : gen_port
assign rdata_o[i] = (req_i[i] && !we_i[i]) ? sram[addr_i[i]] : sram[r_addr_q[i]];
end
end else begin : gen_read_lat
always_comb begin
for (int unsigned i = 0; i < NumPorts; i++) begin
rdata_o[i] = rdata_q[i][0];
for (int unsigned j = 0; j < (Latency-1); j++) begin
rdata_d[i][j] = rdata_q[i][j+1];
end
rdata_d[i][Latency-1] = (req_i[i] && !we_i[i]) ? sram[addr_i[i]] : sram[r_addr_q[i]];
end
end
end
// In case simulation initialization is disabled (SimInit == 'none'), don't assign to the sram
// content at all. This improves simulation performance in tools like verilator
if (SimInit == "none") begin
// write memory array without initialization
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
for (int i = 0; i < NumPorts; i++) begin
r_addr_q[i] <= {AddrWidth{1'b0}};
end
end else begin
// read value latch happens before new data is written to the sram
for (int unsigned i = 0; i < NumPorts; i++) begin
if (Latency != 0) begin
for (int unsigned j = 0; j < Latency; j++) begin
rdata_q[i][j] <= rdata_d[i][j];
end
end
end
// there is a request for the SRAM, latch the required register
for (int unsigned i = 0; i < NumPorts; i++) begin
if (req_i[i]) begin
if (we_i[i]) begin
// update value when write is set at clock
for (int unsigned j = 0; j < BeWidth; j++) begin
if (be_i[i][j]) begin
sram[addr_i[i]][j*ByteWidth+:ByteWidth] <= wdata_i[i][j*ByteWidth+:ByteWidth];
end
end
end else begin
// otherwise update read address for subsequent non request cycles
r_addr_q[i] <= addr_i[i];
end
end // if req_i
end // for ports
end // if !rst_ni
end
end else begin
// write memory array
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
sram <= init_val;
for (int i = 0; i < NumPorts; i++) begin
r_addr_q[i] <= {AddrWidth{1'b0}};
// initialize the read output register for each port
if (Latency != 32'd0) begin
for (int unsigned j = 0; j < Latency; j++) begin
rdata_q[i][j] <= init_val[{AddrWidth{1'b0}}];
end
end
end
end else begin
// read value latch happens before new data is written to the sram
for (int unsigned i = 0; i < NumPorts; i++) begin
if (Latency != 0) begin
for (int unsigned j = 0; j < Latency; j++) begin
rdata_q[i][j] <= rdata_d[i][j];
end
end
end
// there is a request for the SRAM, latch the required register
for (int unsigned i = 0; i < NumPorts; i++) begin
if (req_i[i]) begin
if (we_i[i]) begin
// update value when write is set at clock
for (int unsigned j = 0; j < BeWidth; j++) begin
if (be_i[i][j]) begin
sram[addr_i[i]][j*ByteWidth+:ByteWidth] <= wdata_i[i][j*ByteWidth+:ByteWidth];
end
end
end else begin
// otherwise update read address for subsequent non request cycles
r_addr_q[i] <= addr_i[i];
end
end // if req_i
end // for ports
end // if !rst_ni
end
end
// Validate parameters.
// pragma translate_off
`ifndef VERILATOR
`ifndef TARGET_SYNTHESIS
initial begin: p_assertions
assert ($bits(addr_i) == NumPorts * AddrWidth) else $fatal(1, "AddrWidth problem on `addr_i`");
assert ($bits(wdata_i) == NumPorts * DataWidth) else $fatal(1, "DataWidth problem on `wdata_i`");
assert ($bits(be_i) == NumPorts * BeWidth) else $fatal(1, "BeWidth problem on `be_i`" );
assert ($bits(rdata_o) == NumPorts * DataWidth) else $fatal(1, "DataWidth problem on `rdata_o`");
assert (NumWords >= 32'd1) else $fatal(1, "NumWords has to be > 0");
assert (DataWidth >= 32'd1) else $fatal(1, "DataWidth has to be > 0");
assert (ByteWidth >= 32'd1) else $fatal(1, "ByteWidth has to be > 0");
assert (NumPorts >= 32'd1) else $fatal(1, "The number of ports must be at least 1!");
end
initial begin: p_sim_hello
if (PrintSimCfg) begin
$display("#################################################################################");
$display("tc_sram functional instantiated with the configuration:" );
$display("Instance: %m" );
$display("Number of ports (dec): %0d", NumPorts );
$display("Number of words (dec): %0d", NumWords );
$display("Address width (dec): %0d", AddrWidth );
$display("Data width (dec): %0d", DataWidth );
$display("Byte width (dec): %0d", ByteWidth );
$display("Byte enable width (dec): %0d", BeWidth );
$display("Latency Cycles (dec): %0d", Latency );
$display("Simulation init (str): %0s", SimInit );
$display("#################################################################################");
end
end
for (genvar i = 0; i < NumPorts; i++) begin : gen_assertions
assert property ( @(posedge clk_i) disable iff (!rst_ni)
(req_i[i] |-> (addr_i[i] < NumWords))) else
$warning("Request address %0h not mapped, port %0d, expect random write or read behavior!",
addr_i[i], i);
end
`endif
`endif
// pragma translate_on
endmodule

View file

@ -0,0 +1,100 @@
// 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.
// Description: This file contains power-related cells
// Mainly shifters at the moment.
module tc_pwr_level_shifter_in (
input logic in_i,
output logic out_o
);
assign out_o = in_i;
endmodule
module tc_pwr_level_shifter_in_clamp_lo (
input logic in_i,
output logic out_o,
input logic clamp_i
);
assign out_o = clamp_i ? 1'b0 : in_i;
endmodule
module tc_pwr_level_shifter_in_clamp_hi (
input logic in_i,
output logic out_o,
input logic clamp_i
);
assign out_o = clamp_i ? 1'b1 : in_i;
endmodule
module tc_pwr_level_shifter_out (
input logic in_i,
output logic out_o
);
assign out_o = in_i;
endmodule
module tc_pwr_level_shifter_out_clamp_lo (
input logic in_i,
output logic out_o,
input logic clamp_i
);
assign out_o = clamp_i ? 1'b0 : in_i;
endmodule
module tc_pwr_level_shifter_out_clamp_hi (
input logic in_i,
output logic out_o,
input logic clamp_i
);
assign out_o = clamp_i ? 1'b1 : in_i;
endmodule
module tc_pwr_power_gating (
input logic sleep_i,
output logic sleepout_o
);
assign sleepout_o = sleep_i;
endmodule
module tc_pwr_isolation_lo (
input logic data_i,
input logic ena_i,
output logic data_o
);
assign data_o = ena_i ? data_i : 1'b0;
endmodule
module tc_pwr_isolation_hi (
input logic data_i,
input logic ena_i,
output logic data_o
);
assign data_o = ena_i ? data_i : 1'b1;
endmodule

14
vendor/pulp-platform_axi.lock.hjson vendored Normal file
View file

@ -0,0 +1,14 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// This file is generated by the util/vendor script. Please do not modify it
// manually.
{
upstream:
{
url: https://github.com/pulp-platform/axi.git
rev: 697f13ff67153a5243e347f2d1992a125018b6c2
}
}

39
vendor/pulp-platform_axi.vendor.hjson vendored Normal file
View file

@ -0,0 +1,39 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2022 Thales DIS France SAS
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.0.
// Original Author: Zbigniew Chamski (zbigniew.chamski@thalesgroup.com)
{
// Name of the project
name: "pulp_axi",
// Target directory: relative to the location of this script.
target_dir: "pulp-platform/axi",
// Upstream repository
upstream: {
// URL
url: "https://github.com/pulp-platform/axi.git",
// revision
rev: "v0.31.0",
}
// Patch dir for local changes
patch_dir: "patches/pulp-platform/axi",
// Exclusions from upstream content
exclude_from_upstream: [
".ci",
".github",
".gitlab-ci.d",
".gitlab-ci.yml",
"axi.core",
"Bender.yml",
"doc",
"docs",
"ips_list.yml",
"scripts",
"src_files.yml",
"test",
]
}

View file

@ -0,0 +1,14 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// This file is generated by the util/vendor script. Please do not modify it
// manually.
{
upstream:
{
url: https://github.com/pulp-platform/tech_cells_generic.git
rev: b2a68114302af1d8191ddf34ea0e07b471911866
}
}

View file

@ -0,0 +1,32 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2022 Thales DIS France SAS
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.0.
// Original Author: Zbigniew Chamski (zbigniew.chamski@thalesgroup.com)
{
// Name of the project
name: "pulp_tech_cells_generic",
// Target directory: relative to the location of this script.
target_dir: "pulp-platform/tech_cells_generic",
// Upstream repository
upstream: {
// URL
url: "https://github.com/pulp-platform/tech_cells_generic.git",
// revision
rev: "b2a68114302af1d8191ddf34ea0e07b471911866",
}
// Patch dir for local changes
patch_dir: "patches/pulp-platform/tech_cells_generic",
// Exclusions from upstream content
exclude_from_upstream: [
"Bender.yml",
"scripts",
"src_files.yml",
"test",
]
}