mirror of
https://github.com/openhwgroup/cva6.git
synced 2025-04-19 03:44:46 -04:00
Vendorize corev_apu submodules referenced by CVA6 core. (#1015)
This commit is contained in:
parent
4b33e69a10
commit
17ccfc42f4
72 changed files with 19090 additions and 27 deletions
10
Flist.ariane
10
Flist.ariane
|
@ -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
|
||||
|
|
36
Makefile
36
Makefile
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
7
vendor/pulp-platform/axi/.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
.*
|
||||
!.git*
|
||||
!.ci/
|
||||
/.git/
|
||||
/build
|
||||
/Bender.lock
|
||||
/Bender.local
|
735
vendor/pulp-platform/axi/CHANGELOG.md
vendored
Normal file
735
vendor/pulp-platform/axi/CHANGELOG.md
vendored
Normal 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.
|
26
vendor/pulp-platform/axi/CONTRIBUTING.md
vendored
Normal file
26
vendor/pulp-platform/axi/CONTRIBUTING.md
vendored
Normal 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
176
vendor/pulp-platform/axi/LICENSE
vendored
Normal 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
106
vendor/pulp-platform/axi/README.md
vendored
Normal file
|
@ -0,0 +1,106 @@
|
|||
# AXI SystemVerilog Modules for High-Performance On-Chip Communication
|
||||
[](https://iis-git.ee.ethz.ch/akurth/axi/commits/master)
|
||||
[](CHANGELOG.md)
|
||||
[](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
1
vendor/pulp-platform/axi/VERSION
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
0.31.0
|
541
vendor/pulp-platform/axi/include/axi/assign.svh
vendored
Normal file
541
vendor/pulp-platform/axi/include/axi/assign.svh
vendored
Normal 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
|
211
vendor/pulp-platform/axi/include/axi/typedef.svh
vendored
Normal file
211
vendor/pulp-platform/axi/include/axi/typedef.svh
vendored
Normal 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
|
444
vendor/pulp-platform/axi/src/axi_atop_filter.sv
vendored
Normal file
444
vendor/pulp-platform/axi/src/axi_atop_filter.sv
vendored
Normal 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
|
579
vendor/pulp-platform/axi/src/axi_burst_splitter.sv
vendored
Normal file
579
vendor/pulp-platform/axi/src/axi_burst_splitter.sv
vendored
Normal 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
246
vendor/pulp-platform/axi/src/axi_cdc.sv
vendored
Normal 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
|
264
vendor/pulp-platform/axi/src/axi_cdc_dst.sv
vendored
Normal file
264
vendor/pulp-platform/axi/src/axi_cdc_dst.sv
vendored
Normal 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
|
264
vendor/pulp-platform/axi/src/axi_cdc_src.sv
vendored
Normal file
264
vendor/pulp-platform/axi/src/axi_cdc_src.sv
vendored
Normal 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
265
vendor/pulp-platform/axi/src/axi_cut.sv
vendored
Normal 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
|
198
vendor/pulp-platform/axi/src/axi_delayer.sv
vendored
Normal file
198
vendor/pulp-platform/axi/src/axi_delayer.sv
vendored
Normal 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
|
786
vendor/pulp-platform/axi/src/axi_demux.sv
vendored
Normal file
786
vendor/pulp-platform/axi/src/axi_demux.sv
vendored
Normal 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
|
190
vendor/pulp-platform/axi/src/axi_dw_converter.sv
vendored
Normal file
190
vendor/pulp-platform/axi/src/axi_dw_converter.sv
vendored
Normal 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
|
890
vendor/pulp-platform/axi/src/axi_dw_downsizer.sv
vendored
Normal file
890
vendor/pulp-platform/axi/src/axi_dw_downsizer.sv
vendored
Normal 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
|
725
vendor/pulp-platform/axi/src/axi_dw_upsizer.sv
vendored
Normal file
725
vendor/pulp-platform/axi/src/axi_dw_upsizer.sv
vendored
Normal 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
|
261
vendor/pulp-platform/axi/src/axi_err_slv.sv
vendored
Normal file
261
vendor/pulp-platform/axi/src/axi_err_slv.sv
vendored
Normal 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
|
161
vendor/pulp-platform/axi/src/axi_id_prepend.sv
vendored
Normal file
161
vendor/pulp-platform/axi/src/axi_id_prepend.sv
vendored
Normal 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
|
657
vendor/pulp-platform/axi/src/axi_id_remap.sv
vendored
Normal file
657
vendor/pulp-platform/axi/src/axi_id_remap.sv
vendored
Normal 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
|
||||
/// 
|
||||
///
|
||||
/// ## 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
|
424
vendor/pulp-platform/axi/src/axi_id_serialize.sv
vendored
Normal file
424
vendor/pulp-platform/axi/src/axi_id_serialize.sv
vendored
Normal 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
549
vendor/pulp-platform/axi/src/axi_intf.sv
vendored
Normal 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
|
346
vendor/pulp-platform/axi/src/axi_isolate.sv
vendored
Normal file
346
vendor/pulp-platform/axi/src/axi_isolate.sv
vendored
Normal 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
|
||||
|
350
vendor/pulp-platform/axi/src/axi_iw_converter.sv
vendored
Normal file
350
vendor/pulp-platform/axi/src/axi_iw_converter.sv
vendored
Normal 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
|
37
vendor/pulp-platform/axi/src/axi_join.sv
vendored
Normal file
37
vendor/pulp-platform/axi/src/axi_join.sv
vendored
Normal 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
|
482
vendor/pulp-platform/axi/src/axi_lite_demux.sv
vendored
Normal file
482
vendor/pulp-platform/axi/src/axi_lite_demux.sv
vendored
Normal 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
|
35
vendor/pulp-platform/axi/src/axi_lite_join.sv
vendored
Normal file
35
vendor/pulp-platform/axi/src/axi_lite_join.sv
vendored
Normal 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
|
625
vendor/pulp-platform/axi/src/axi_lite_mailbox.sv
vendored
Normal file
625
vendor/pulp-platform/axi/src/axi_lite_mailbox.sv
vendored
Normal 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
|
473
vendor/pulp-platform/axi/src/axi_lite_mux.sv
vendored
Normal file
473
vendor/pulp-platform/axi/src/axi_lite_mux.sv
vendored
Normal 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
|
483
vendor/pulp-platform/axi/src/axi_lite_regs.sv
vendored
Normal file
483
vendor/pulp-platform/axi/src/axi_lite_regs.sv
vendored
Normal 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
|
494
vendor/pulp-platform/axi/src/axi_lite_to_apb.sv
vendored
Normal file
494
vendor/pulp-platform/axi/src/axi_lite_to_apb.sv
vendored
Normal 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
|
160
vendor/pulp-platform/axi/src/axi_lite_to_axi.sv
vendored
Normal file
160
vendor/pulp-platform/axi/src/axi_lite_to_axi.sv
vendored
Normal 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
|
305
vendor/pulp-platform/axi/src/axi_lite_xbar.sv
vendored
Normal file
305
vendor/pulp-platform/axi/src/axi_lite_xbar.sv
vendored
Normal 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
|
164
vendor/pulp-platform/axi/src/axi_modify_address.sv
vendored
Normal file
164
vendor/pulp-platform/axi/src/axi_modify_address.sv
vendored
Normal 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
|
237
vendor/pulp-platform/axi/src/axi_multicut.sv
vendored
Normal file
237
vendor/pulp-platform/axi/src/axi_multicut.sv
vendored
Normal 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
522
vendor/pulp-platform/axi/src/axi_mux.sv
vendored
Normal 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
423
vendor/pulp-platform/axi/src/axi_pkg.sv
vendored
Normal 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
|
295
vendor/pulp-platform/axi/src/axi_serializer.sv
vendored
Normal file
295
vendor/pulp-platform/axi/src/axi_serializer.sv
vendored
Normal 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
|
204
vendor/pulp-platform/axi/src/axi_sim_mem.sv
vendored
Normal file
204
vendor/pulp-platform/axi/src/axi_sim_mem.sv
vendored
Normal 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
2280
vendor/pulp-platform/axi/src/axi_test.sv
vendored
Normal file
File diff suppressed because it is too large
Load diff
323
vendor/pulp-platform/axi/src/axi_to_axi_lite.sv
vendored
Normal file
323
vendor/pulp-platform/axi/src/axi_to_axi_lite.sv
vendored
Normal 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
324
vendor/pulp-platform/axi/src/axi_xbar.sv
vendored
Normal 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
|
6
vendor/pulp-platform/tech_cells_generic/.gitignore
vendored
Normal file
6
vendor/pulp-platform/tech_cells_generic/.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
.*
|
||||
!.git*
|
||||
build
|
||||
scripts/vivado/add_sources.tcl
|
||||
/Bender.lock
|
||||
/Bender.local
|
73
vendor/pulp-platform/tech_cells_generic/CHANGELOG.md
vendored
Normal file
73
vendor/pulp-platform/tech_cells_generic/CHANGELOG.md
vendored
Normal 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.
|
3
vendor/pulp-platform/tech_cells_generic/CONTRIBUTING.md
vendored
Normal file
3
vendor/pulp-platform/tech_cells_generic/CONTRIBUTING.md
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Contributing
|
||||
|
||||
See [our style and contribution guidelines](https://github.com/pulp-platform/style-guidelines).
|
176
vendor/pulp-platform/tech_cells_generic/LICENSE
vendored
Normal file
176
vendor/pulp-platform/tech_cells_generic/LICENSE
vendored
Normal 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
|
52
vendor/pulp-platform/tech_cells_generic/README.md
vendored
Normal file
52
vendor/pulp-platform/tech_cells_generic/README.md
vendored
Normal 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 |
|
94
vendor/pulp-platform/tech_cells_generic/src/deprecated/cluster_clk_cells.sv
vendored
Normal file
94
vendor/pulp-platform/tech_cells_generic/src/deprecated/cluster_clk_cells.sv
vendored
Normal 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
|
59
vendor/pulp-platform/tech_cells_generic/src/deprecated/cluster_pwr_cells.sv
vendored
Normal file
59
vendor/pulp-platform/tech_cells_generic/src/deprecated/cluster_pwr_cells.sv
vendored
Normal 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
|
72
vendor/pulp-platform/tech_cells_generic/src/deprecated/generic_memory.sv
vendored
Normal file
72
vendor/pulp-platform/tech_cells_generic/src/deprecated/generic_memory.sv
vendored
Normal 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
|
42
vendor/pulp-platform/tech_cells_generic/src/deprecated/generic_rom.sv
vendored
Normal file
42
vendor/pulp-platform/tech_cells_generic/src/deprecated/generic_rom.sv
vendored
Normal 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
|
82
vendor/pulp-platform/tech_cells_generic/src/deprecated/pad_functional.sv
vendored
Normal file
82
vendor/pulp-platform/tech_cells_generic/src/deprecated/pad_functional.sv
vendored
Normal 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
|
19
vendor/pulp-platform/tech_cells_generic/src/deprecated/pulp_buffer.sv
vendored
Normal file
19
vendor/pulp-platform/tech_cells_generic/src/deprecated/pulp_buffer.sv
vendored
Normal 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
|
107
vendor/pulp-platform/tech_cells_generic/src/deprecated/pulp_clk_cells.sv
vendored
Normal file
107
vendor/pulp-platform/tech_cells_generic/src/deprecated/pulp_clk_cells.sv
vendored
Normal 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
|
||||
|
||||
|
44
vendor/pulp-platform/tech_cells_generic/src/deprecated/pulp_clock_gating_async.sv
vendored
Normal file
44
vendor/pulp-platform/tech_cells_generic/src/deprecated/pulp_clock_gating_async.sv
vendored
Normal 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
|
88
vendor/pulp-platform/tech_cells_generic/src/deprecated/pulp_pwr_cells.sv
vendored
Normal file
88
vendor/pulp-platform/tech_cells_generic/src/deprecated/pulp_pwr_cells.sv
vendored
Normal 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
|
48
vendor/pulp-platform/tech_cells_generic/src/fpga/pad_functional_xilinx.sv
vendored
Normal file
48
vendor/pulp-platform/tech_cells_generic/src/fpga/pad_functional_xilinx.sv
vendored
Normal 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
|
78
vendor/pulp-platform/tech_cells_generic/src/fpga/tc_clk_xilinx.sv
vendored
Normal file
78
vendor/pulp-platform/tech_cells_generic/src/fpga/tc_clk_xilinx.sv
vendored
Normal 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
|
||||
|
210
vendor/pulp-platform/tech_cells_generic/src/fpga/tc_sram_xilinx.sv
vendored
Normal file
210
vendor/pulp-platform/tech_cells_generic/src/fpga/tc_sram_xilinx.sv
vendored
Normal 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
|
102
vendor/pulp-platform/tech_cells_generic/src/rtl/tc_clk.sv
vendored
Normal file
102
vendor/pulp-platform/tech_cells_generic/src/rtl/tc_clk.sv
vendored
Normal 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
|
||||
|
||||
|
241
vendor/pulp-platform/tech_cells_generic/src/rtl/tc_sram.sv
vendored
Normal file
241
vendor/pulp-platform/tech_cells_generic/src/rtl/tc_sram.sv
vendored
Normal 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
|
100
vendor/pulp-platform/tech_cells_generic/src/tc_pwr.sv
vendored
Normal file
100
vendor/pulp-platform/tech_cells_generic/src/tc_pwr.sv
vendored
Normal 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
14
vendor/pulp-platform_axi.lock.hjson
vendored
Normal 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
39
vendor/pulp-platform_axi.vendor.hjson
vendored
Normal 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",
|
||||
]
|
||||
}
|
14
vendor/pulp-platform_tech_cells_generic.lock.hjson
vendored
Normal file
14
vendor/pulp-platform_tech_cells_generic.lock.hjson
vendored
Normal 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
|
||||
}
|
||||
}
|
32
vendor/pulp-platform_tech_cells_generic.vendor.hjson
vendored
Normal file
32
vendor/pulp-platform_tech_cells_generic.vendor.hjson
vendored
Normal 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",
|
||||
]
|
||||
}
|
||||
|
Loading…
Add table
Reference in a new issue