diff --git a/.gitignore b/.gitignore index 8265b48..42eddf9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,34 +1,27 @@ -work-dpi/ -dw* -site/* -*.ucdb -work/* +# Simulation transcript *.ini *.wlf -*.vstf -wlft* -*nfs* -*.sig -*.dtb -*.dasm -obj_dir/* -/tmp* -*.dasm -build/ -trace_* -mems -*log -.bender -.dvt -.settings -*.o -install -.projectBender.lock -Bender.lock -logs -bin -hardware/deps/* *.tdb +# Toolchain +install +# Vis4Mesh (pinned clone, see util/vis4mesh.version -- not tracked here) +util/vis4mesh +# Utilization logs util/lint/sg_projects util/lint/tmp +# Dependency files +.bender +hardware/deps/* +# Bootrom auto-generated files +hardware/bootrom/bootdata.cc +hardware/bootrom/bootdata_bootrom.cc +hardware/bootrom/bootrom.bin +hardware/bootrom/bootrom.dump +hardware/bootrom/bootrom.elf +hardware/bootrom/bootrom.sv +# Configuration auto-generated files +config/cachepool.hjson +*noc_profiling +# CI venv (created by iis-env.sh) +/cachepool \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 567242b..bc708ce 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,41 +8,91 @@ variables: GIT_SUBMODULE_STRATEGY: none ROOT_DIR: '$CI_PROJECT_DIR' APPS: "tests" - PATH: '/home/gitlabci/.cargo/bin:/usr/local/bin:/usr/bin:/usr/sbin:/sbin:/usr/local/condor/bin:/usr/sepp/bin:$CI_PROJECT_DIR/install/verilator/bin:/home/gitlabci/.local/bin' OBJCACHE: '' CC: '/usr/pack/gcc-11.2.0-af/linux-x64/bin/gcc' CXX: '/usr/pack/gcc-11.2.0-af/linux-x64/bin/g++' CMAKE: 'cmake-3.28.3' python: 'python3' python3: 'python3' + # Config to build and test + CI_CONFIG: 'cachepool_fpu_4g' + SW_PREFIX: 'test-cachepool-' default: - tags: [dolent] + tags: [shared] stages: - build + - test -.base: - artifacts: - when: always - expire_in: 1 day - -build-vsim: - extends: .base +# --------------------------------------------------------------------------- +# Build stage: compile RTL and software for CI_CONFIG. +# Sources iis-env.sh, which points the toolchain at the prebuilt install dir +# (replaces `make quick-tool`'s symlink) and sets up the venv for code-gen deps; +# test-stage jobs run prebuilt artifacts and don't need either, so iis-env.sh is +# only sourced here. +# --------------------------------------------------------------------------- +build: stage: build - timeout: 5h script: - echo "Using CC=$CC" - echo "Using CXX=$CXX" - test -x "$CC" - test -x "$CXX" - - make quick-tool + - source iis-env.sh - make init - make dram-build - - cd util/auto-benchmark - - chmod +x ./run_ci.sh - - ./run_ci.sh + - make clean generate update-floonoc bootrom vsim sw config=$CI_CONFIG DEBUG=0 noc_profiling=0 + artifacts: + when: always + expire_in: 2h + paths: + # QuestaSim compiled work library + - sim/work/ + # vsim wrapper scripts (exclude sim/bin/logs/ — not needed by test jobs) + - sim/bin/cachepool_cluster.vsim + - sim/bin/cachepool_cluster.vsim.gui + # DPI shared library + - sim/work-dpi/ + # Software binaries for all kernels + - software/build/CachePoolTests/ + # DRAMSys shared libraries and config files (referenced by vsim at runtime) + - hardware/deps/dram_rtl_sim/dramsys_lib/DRAMSys/build/lib/ + - hardware/deps/dram_rtl_sim/dramsys_lib/DRAMSys/configs/ +# --------------------------------------------------------------------------- +# Test stage: run each kernel in parallel on a separate runner. +# Each job downloads the build artifacts, runs one simulation, and checks +# the output log for failures. +# --------------------------------------------------------------------------- +test: + stage: test + needs: [build] + parallel: + matrix: + - KERNEL: + - spin-lock + - load-store + - fdotp-32b_M32768 + - gemv_M512_N128_K32 + - fmatmul-32b_M64_N64_K64 + - fft-32b_M1024_N16 + - byte-enable + - multi_producer_single_consumer_double_linked_list_M1_N1350_K10 + script: + # The vsim script writes a .rtlbinary marker here; monitor logs need the + # core/noc/others subfolders. Build artifacts exclude sim/bin/logs/, so + # these must be recreated in the test job too. + - mkdir -p sim/bin/logs sim/bin/logs/core sim/bin/logs/noc sim/bin/logs/others + - chmod +x sim/bin/cachepool_cluster.vsim + - BIN="${SW_PREFIX}${KERNEL}" + - sim/bin/cachepool_cluster.vsim software/build/CachePoolTests/$BIN 2>&1 | tee test_${KERNEL}.log + - python3 util/auto-benchmark/check-ci.py test_${KERNEL}.log artifacts: + when: always + expire_in: 1 week paths: - - util/auto-benchmark/logs + # Full simulation log + - test_*.log + # Performance-monitor trace files written by the simulator + - sim/bin/logs/ diff --git a/Bender.lock b/Bender.lock index 9684f42..4321fe3 100644 --- a/Bender.lock +++ b/Bender.lock @@ -16,17 +16,34 @@ packages: - common_verification - tech_cells_generic axi_riscv_atomics: - revision: 97dcb14ef057cbe5bd70dda2060b5bb9e7e04c6d - version: 0.7.0 + revision: 97a1dd2ac643c276880420a0cf8eea697f228aa9 + version: 0.8.3 source: Git: https://github.com/pulp-platform/axi_riscv_atomics.git dependencies: - axi - common_cells - common_verification + axi_stream: + revision: 54891ff40455ca94a37641b9da4604647878cc07 + version: 0.1.1 + source: + Git: https://github.com/pulp-platform/axi_stream.git + dependencies: + - common_cells + cluster_icache: + revision: ce0ed94a5b95f5c76b9fa51940303fcce53f56e5 + version: null + source: + Git: https://github.com/pulp-platform/cluster_icache.git + dependencies: + - axi + - common_cells + - scm + - tech_cells_generic common_cells: - revision: 9afda9abb565971649c2aa0985639c096f351171 - version: 1.38.0 + revision: 9ca8a7655f741e7dd5736669a20a301325194c28 + version: 1.39.0 source: Git: https://github.com/pulp-platform/common_cells.git dependencies: @@ -45,8 +62,27 @@ packages: Git: https://github.com/pulp-platform/dram_rtl_sim.git dependencies: - axi + floo_noc: + revision: 8413d9b044a51f958a7d4ef261e2b01e73db4544 + version: null + source: + Git: https://github.com/pulp-platform/FlooNoC.git + dependencies: + - axi + - axi_riscv_atomics + - common_cells + - common_verification + - floo_noc_pd + - fpnew + - idma + floo_noc_pd: + revision: null + version: null + source: + Path: hardware/deps/floo_noc/./pd + dependencies: [] fpnew: - revision: a8e0cba6dd50f357ece73c2c955d96efc3c6c315 + revision: e5aa6a01b5bbe1675c3aa8872e1203413ded83d1 version: null source: Git: https://github.com/pulp-platform/cvfpu.git @@ -61,14 +97,16 @@ packages: dependencies: - common_cells idma: - revision: b31e8f019c657eff4126bc789f0336d403da6766 - version: 0.4.2 + revision: 28a36e5e07705549e59fc33db96ab681bc1ca88e + version: 0.6.5 source: Git: https://github.com/pulp-platform/iDMA.git dependencies: - axi + - axi_stream - common_cells - common_verification + - obi - register_interface insitu-cache: revision: fa761ddebc946f9b46509d84945bf41ee1a9ec49 @@ -79,6 +117,14 @@ packages: - axi - common_cells - register_interface + obi: + revision: 0155fc34e900c7c884e081c0a1114a247937ff69 + version: 0.1.7 + source: + Git: https://github.com/pulp-platform/obi.git + dependencies: + - common_cells + - common_verification register_interface: revision: 146501d80052b61475cdc333d3aab4cd769fd5dc version: 0.3.9 @@ -96,8 +142,15 @@ packages: dependencies: - common_cells - tech_cells_generic + scm: + revision: 1976c7efb4979271eee2abe262fde0f9a20e2557 + version: 1.2.1 + source: + Git: https://github.com/pulp-platform/scm.git + dependencies: + - tech_cells_generic spatz: - revision: ed25c78dd72d839db8141287f9516d78ee399b93 + revision: 3a6e6bed2a774a449997e426d893d15f84420518 version: null source: Git: https://github.com/pulp-platform/spatz.git diff --git a/Bender.yml b/Bender.yml index 45b01da..df22222 100644 --- a/Bender.yml +++ b/Bender.yml @@ -10,13 +10,14 @@ dependencies: axi_riscv_atomics: { git: "https://github.com/pulp-platform/axi_riscv_atomics.git", version: 0.7.0 } common_cells: { git: "https://github.com/pulp-platform/common_cells.git", version: 1.28.0 } FPnew: { git: "https://github.com/pulp-platform/cvfpu.git", rev: pulp-v0.1.3 } - idma: { git: "https://github.com/pulp-platform/iDMA.git", version: 0.4.2 } register_interface: { git: "https://github.com/pulp-platform/register_interface.git", version: 0.3.8 } riscv-dbg: { git: "https://github.com/pulp-platform/riscv-dbg.git", version: 0.7.0 } tech_cells_generic: { git: "https://github.com/pulp-platform/tech_cells_generic.git", version: 0.2.11 } Insitu-Cache: { git: "https://github.com/pulp-platform/Insitu-Cache.git", rev: zexin/cachepool_dev } - spatz: { git: "https://github.com/pulp-platform/spatz.git", rev: cachepool-32b } dram_rtl_sim: { git: "https://github.com/pulp-platform/dram_rtl_sim.git", rev: cachepool } + floo_noc: { git: "https://github.com/pulp-platform/FlooNoC.git", rev: cachepool } + cluster_icache: { git: "https://github.com/pulp-platform/cluster_icache.git", rev: main } + spatz: { git: "https://github.com/pulp-platform/spatz.git", rev: cachepool-32b } workspace: checkout_dir: "./hardware/deps" @@ -25,6 +26,8 @@ export_include_dirs: sources: # Level 0 + - hardware/src/reqrsp_to_reg.sv + - hardware/src/reqrsp_w2n_converter.sv - hardware/src/reqrsp_xbar.sv - hardware/src/tcdm_cache_interco.sv - hardware/src/tcdm_id_remapper.sv @@ -35,16 +38,20 @@ sources: - hardware/cachepool_peripheral/cachepool_peripheral.sv # Bootrom - hardware/bootrom/bootrom.sv - # Barrier - - hardware/src/cachepool_tile_barrier.sv - - hardware/src/cachepool_cluster_barrier.sv # Level 1 + - hardware/generated/floo_cachepool_noc_pkg.sv - hardware/src/cachepool_pkg.sv - hardware/src/cachepool_cc.sv + # Barrier + - hardware/src/cachepool_tile_barrier.sv + - hardware/src/cachepool_cluster_barrier.sv + # ICache + - hardware/src/axi_hier_interco.sv # Level 2 - hardware/src/cachepool_tile.sv # Level 3 - hardware/src/cachepool_group.sv + - hardware/src/cachepool_group_noc_wrapper.sv - hardware/src/cachepool_cluster.sv # Level 4 @@ -53,4 +60,6 @@ sources: - target: cachepool_test files: - hardware/tb/axi_uart.sv + - hardware/tb/cachepool_monitor.sv + - hardware/tb/cachepool_noc_profiling.sv - hardware/tb/tb_cachepool.sv diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4947287 --- /dev/null +++ b/LICENSE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + 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 copyright owner or entity authorized by + the copyright 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. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, 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, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright 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) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any 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 copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright 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 copyright 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 Copyright 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 + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 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: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) 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 + + (d) 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 \ No newline at end of file diff --git a/Makefile b/Makefile index 588cf0f..af516b3 100644 --- a/Makefile +++ b/Makefile @@ -67,7 +67,7 @@ CACHE_PATH := $(shell [ -x "$(BENDER)" ] && $(BENDER) path insitu-cac # Configurations CFG_DIR ?= ${CACHEPOOL_DIR}/config -config ?= cachepool_512 +config ?= cachepool_fpu_4g # Compiler choice for SW cmake COMPILER ?= llvm @@ -122,7 +122,7 @@ quick-tool: ln -sf /home/dishen/cachepool-32b/install $(CACHEPOOL_DIR)/install .PHONY: generate -generate: update_opcodes gen-spatz-cfg +generate: gen-spatz-cfg update_opcodes update-floonoc $(MAKE) -C $(SPZ_CLS_DIR) generate SPATZ_CLUSTER_CFG=${CFG_DIR}/cachepool.hjson PYTHON=${PYTHON} .PHONY: cache-init @@ -146,8 +146,13 @@ $(BOOTROM_DIR)/bootdata_bootrom.cc: $(SCRIPTS_DIR)/generate_bootdata.py $(HJSON_ $(BOOTROM_DIR)/bootdata.cc: $(SCRIPTS_DIR)/generate_bootdata.py $(HJSON_OUT) ${PYTHON} $< -c $(HJSON_OUT) -d $(BOOTROM_DIR) -t bootdata.cc.tpl -o $@ +SNRT_BOOTINFO_H := $(SOFTWARE_DIR)/snRuntime/include/snrt_bootinfo.h + +$(SNRT_BOOTINFO_H): $(SCRIPTS_DIR)/generate_bootdata.py $(HJSON_OUT) $(BOOTROM_DIR)/snrt_bootinfo.h.tpl + ${PYTHON} $< -c $(HJSON_OUT) -d $(BOOTROM_DIR) -t snrt_bootinfo.h.tpl -o $@ + $(BOOTROM_DIR)/bootrom.elf $(BOOTROM_DIR)/bootrom.dump $(BOOTROM_DIR)/bootrom.bin: \ - $(BOOTROM_DIR)/bootrom.S $(BOOTROM_DIR)/bootdata_bootrom.cc $(BOOTROM_DIR)/bootrom.ld Makefile + $(BOOTROM_DIR)/bootrom.S $(BOOTROM_DIR)/bootdata_bootrom.cc $(BOOTROM_DIR)/bootrom.ld $(SNRT_BOOTINFO_H) riscv -riscv64-gcc-9.5.0 riscv64-unknown-elf-gcc \ -mabi=ilp32 -march=rv32imaf -static -nostartfiles \ -T$(BOOTROM_DIR)/bootrom.ld \ @@ -163,6 +168,38 @@ $(BOOTROM_DIR)/bootrom.sv: $(BOOTROM_DIR)/bootrom.bin $(BOOTROM_DIR)/bootdata.cc ${PYTHON} $(SCRIPTS_DIR)/generate_bootrom.py \ $< -c $(HJSON_OUT) --output $@ +########### +# FlooNoC # +########### +FLOO_DIR ?= $(shell $(BENDER_INSTALL_DIR)/bender path floo_noc) +FLOO_GEN_OUTDIR ?= $(ROOT_DIR)/hardware/generated + +# Auto-select FlooNoC YAML based on config name +ifneq ($(filter %_16g_tiny,$(config)),) + FLOO_CFG ?= $(ROOT_DIR)/config/floonoc_cachepool_16g_tiny.yml +else ifneq ($(filter %_16g,$(config)),) + FLOO_CFG ?= $(ROOT_DIR)/config/floonoc_cachepool_16g.yml +else + FLOO_CFG ?= $(ROOT_DIR)/config/floonoc_cachepool_4g.yml +endif +FLOO_NAME = cachepool +FLOO_NOC ?= $(FLOO_GEN_OUTDIR)/floo_$(FLOO_NAME)_noc_pkg.sv + +$(info FLOO_DIR: $(FLOO_DIR)) + +# Generates the sources for FlooNoC +.PHONY: update-floonoc install-floogen clean-floonoc +install-floogen: + pip install -e $(FLOO_DIR) --quiet + +update-floonoc: $(FLOO_NOC) +$(FLOO_NOC): $(FLOO_CFG) + mkdir -p $(FLOO_GEN_OUTDIR) + PATH="$(HOME)/.local/bin:$(PATH)" floogen pkg -c $(FLOO_CFG) -o $(FLOO_GEN_OUTDIR) --no-format + +clean-floonoc: + rm -f $(FLOO_NOC) + ########### # DramSys # ########### @@ -171,7 +208,7 @@ VSIM_FLAGS := VSIM_BENDER = .PHONY: dram-build -dram-build: $(DRAMSYS_PATH)/README.md dram-clean dram-config +dram-build: $(DRAMSYS_PATH)/README.md dram-config cd $(DRAMSYS_PATH) && \ if [ ! -d "build" ]; then \ mkdir build && cd build; \ @@ -232,33 +269,32 @@ VLOG_FLAGS += -64 VLOG_DEFS = -DCACHEPOOL # Cluster configuration +VLOG_DEFS += -DNUM_GROUPS=$(num_groups) +VLOG_DEFS += -DNUM_GROUPS_X=$(num_groups_x) VLOG_DEFS += -DNUM_TILES=$(num_tiles) VLOG_DEFS += -DNUM_CORES=$(num_cores) VLOG_DEFS += -DDATA_WIDTH=$(data_width) VLOG_DEFS += -DADDR_WIDTH=$(addr_width) # Tile configuration -VLOG_DEFS += -DNUM_CORES_PER_TILE=$(num_cores_per_tile) VLOG_DEFS += -DREFILL_DATA_WIDTH=$(refill_data_width) # L1 Data Cache VLOG_DEFS += -DL1D_CACHELINE_WIDTH=$(l1d_cacheline_width) -VLOG_DEFS += -DL1D_SIZE=$(l1d_size) -VLOG_DEFS += -DL1D_BANK_FACTOR=$(l1d_bank_factor) VLOG_DEFS += -DL1D_COAL_WINDOW=$(l1d_coal_window) VLOG_DEFS += -DL1D_NUM_WAY=$(l1d_num_way) -VLOG_DEFS += -DL1D_TILE_SIZE=$(l1d_tile_size) VLOG_DEFS += -DL1D_TAG_DATA_WIDTH=$(l1d_tag_data_width) VLOG_DEFS += -DL1D_NUM_BANKS=$(l1d_num_banks) VLOG_DEFS += -DL1D_DEPTH=$(l1d_depth) # CachePool CC / core cluster -VLOG_DEFS += -DSPATZ_FPU_EN=$(spatz_fpu_en) VLOG_DEFS += -DSPATZ_NUM_FPU=$(spatz_num_fpu) VLOG_DEFS += -DSPATZ_NUM_IPU=$(spatz_num_ipu) VLOG_DEFS += -DSPATZ_MAX_TRANS=$(spatz_max_trans) VLOG_DEFS += -DSNITCH_MAX_TRANS=$(snitch_max_trans) -VLOG_DEFS += -DREMOTE_PORT_PER_CORE=$(num_remote_ports_per_tile) +VLOG_DEFS += -DLG_PORT_PER_CORE=$(num_lg_ports_per_core) +VLOG_DEFS += -DRG_PORT_PER_CORE=$(num_rg_ports_per_core) +VLOG_DEFS += -DNOC_PORT_PER_TILE=$(num_noc_ports_per_tile) # AXI configuration VLOG_DEFS += -DAXI_USER_WIDTH=$(axi_user_width) @@ -267,15 +303,28 @@ VLOG_DEFS += -DAXI_USER_WIDTH=$(axi_user_width) VLOG_DEFS += -DL2_CHANNEL=$(l2_channel) VLOG_DEFS += -DL2_BANK_WIDTH=$(l2_bank_width) VLOG_DEFS += -DL2_INTERLEAVE=$(l2_interleave) +# DRAM type string for DRAMSys (passed as a quoted SV string literal) +VLOG_DEFS += -DDRAM_TYPE='"$(dram_type)"' -# Peripherals / memory map -VLOG_DEFS += -DSTACK_ADDR=$(stack_addr) +# Stack / SPM (boot_addr, stack_addr, periph_start_addr, uart_addr used by hjson +# generator via environment; not consumed as SV defines) VLOG_DEFS += -DSTACK_HW_SIZE=$(stack_hw_size) VLOG_DEFS += -DSTACK_HW_DEPTH=$(stack_hw_depth) VLOG_DEFS += -DSTACK_TOT_SIZE=$(stack_tot_size) -VLOG_DEFS += -DPERIPH_START_ADDR=$(periph_start_addr) -VLOG_DEFS += -DBOOT_ADDR=$(boot_addr) -VLOG_DEFS += -DUART_ADDR=$(uart_addr) +VLOG_DEFS += -DSTACK_TOT_DEPTH=$(stack_tot_depth) + +# Simulation-only debug/profiling toggles (opt-in: add simulation overhead) + +# Cycle-accurate per-router/tile/core NoC traffic logs from +# hardware/tb/cachepool_noc_profiling.sv (noc_profiling/*.log), consumed by +# util/scripts/noc_profiling_to_vis4mesh.py (see README's "NoC +# Visualization" section). L1 router/tile logs only produce data with +# num_rg_ports_per_core > 0 (multi-group configs); L2 logs are always +# populated. +noc_profiling ?= 1 +ifeq ($(noc_profiling),1) +VLOG_DEFS += -DNOC_PROFILING +endif ENABLE_CACHEPOOL_TESTS ?= 1 @@ -288,46 +337,84 @@ include sim/sim.mk # SW # ###### +##################### +## Data Generation ## +##################### + +TESTS_DIR := $(SOFTWARE_DIR)/tests + +# Auto-discover every test that has a script/gen_data.py. +# Convention: script/data_.json → data/data_.h +# Adding a new test or a new data variant needs no Makefile changes: +# - new test: drop gen_data.py + data_.json into its script/ dir +# - new variant: add data_.json to an existing test's script/ dir +DATA_TESTS := $(patsubst $(TESTS_DIR)/%/script/gen_data.py,%, \ + $(wildcard $(TESTS_DIR)/*/script/gen_data.py)) + +define gen_data_rules +$(1)_DATA := $$(patsubst $(TESTS_DIR)/$(1)/script/data_%.json, \ + $(TESTS_DIR)/$(1)/data/data_%.h, \ + $$(wildcard $(TESTS_DIR)/$(1)/script/data_*.json)) +ALL_GEN_DATA += $$($(1)_DATA) + +$(TESTS_DIR)/$(1)/data/data_%.h: \ + $(TESTS_DIR)/$(1)/script/data_%.json \ + $(TESTS_DIR)/$(1)/script/gen_data.py + $$(PYTHON) $(TESTS_DIR)/$(1)/script/gen_data.py -c $$< +endef + +$(foreach test,$(DATA_TESTS),$(eval $(call gen_data_rules,$(test)))) + +# bandwidth: always outputs data/data.h (fixed name); handled separately. +BANDWIDTH_DATA := $(TESTS_DIR)/bandwidth/data/data.h +ALL_GEN_DATA += $(BANDWIDTH_DATA) + +$(BANDWIDTH_DATA): $(TESTS_DIR)/bandwidth/script/data.json \ + $(TESTS_DIR)/bandwidth/script/gen_data.py + $(PYTHON) $(TESTS_DIR)/bandwidth/script/gen_data.py -c $< + +.PHONY: gen-data +gen-data: $(ALL_GEN_DATA) + +.PHONY: clean.data +clean.data: + rm -f $(ALL_GEN_DATA) + .PHONY: clean.sw clean.sw: rm -rf ${SOFTWARE_DIR}/build +.PHONY: clean +clean: clean.sw clean.vsim clean.data + rm -rf $(HJSON_OUT) $(BOOTROM_DIR)/bootdata.cc \ + $(BOOTROM_DIR)/bootdata_bootrom.cc \ + $(BOOTROM_DIR)/bootrom.sv \ + $(BOOTROM_DIR)/bootrom.dump \ + $(BOOTROM_DIR)/bootrom.elf \ + $(SNRT_BOOTINFO_H) + +# Common CMake flags shared by sw and vsim targets. +# vsim appends -DSNITCH_SIMULATOR to point tests at the compiled binary. +SW_CMAKE_FLAGS = \ + -DENABLE_CACHEPOOL_TESTS=${ENABLE_CACHEPOOL_TESTS} \ + -DCACHEPOOL_DIR=$(CACHEPOOL_DIR) \ + -DRUNTIME_DIR=${SOFTWARE_DIR} \ + -DSPATZ_SW_DIR=$(SPATZ_SW_DIR) \ + -DLLVM_PATH=${LLVM_INSTALL_DIR} \ + -DGCC_PATH=${GCC_INSTALL_DIR} \ + -DPYTHON=${PYTHON} \ + -DBUILD_TESTS=ON + .PHONY: sw -sw: generate bootrom clean.sw - echo ${SOFTWARE_DIR} +sw: generate bootrom gen-data mkdir -p ${SOFTWARE_DIR}/build - cd ${SOFTWARE_DIR}/build && ${CMAKE} \ - -DENABLE_CACHEPOOL_TESTS=${ENABLE_CACHEPOOL_TESTS} \ - -DCACHEPOOL_DIR=$(CACHEPOOL_DIR) \ - -DRUNTIME_DIR=${SOFTWARE_DIR} \ - -DSPATZ_SW_DIR=$(SPATZ_SW_DIR) \ - -DLLVM_PATH=${LLVM_INSTALL_DIR} \ - -DGCC_PATH=${GCC_INSTALL_DIR} \ - -DPYTHON=${PYTHON} \ - -DBUILD_TESTS=ON .. && $(MAKE) + cd ${SOFTWARE_DIR}/build && ${CMAKE} ${SW_CMAKE_FLAGS} \ + $(if $(wildcard ${SIMBIN_DIR}/cachepool_cluster.vsim),-DSNITCH_SIMULATOR=${SIMBIN_DIR}/cachepool_cluster.vsim) \ + .. && $(MAKE) .PHONY: vsim vsim: generate bootrom dpi ${SIMBIN_DIR}/cachepool_cluster.vsim - echo ${SOFTWARE_DIR} - mkdir -p ${SOFTWARE_DIR}/build - cd ${SOFTWARE_DIR}/build && ${CMAKE} \ - -DENABLE_CACHEPOOL_TESTS=${ENABLE_CACHEPOOL_TESTS} \ - -DCACHEPOOL_DIR=$(CACHEPOOL_DIR) \ - -DRUNTIME_DIR=${SOFTWARE_DIR} \ - -DSPATZ_SW_DIR=$(SPATZ_SW_DIR) \ - -DLLVM_PATH=${LLVM_INSTALL_DIR} \ - -DGCC_PATH=${GCC_INSTALL_DIR} \ - -DPYTHON=${PYTHON} \ - -DSNITCH_SIMULATOR=${SIMBIN_DIR}/cachepool_cluster.vsim \ - -DBUILD_TESTS=ON .. && $(MAKE) -.PHONY: clean -clean: clean.sw clean.vsim - rm -rf $(HJSON_OUT) $(BOOTROM_DIR)/bootdata.cc \ - $(BOOTROM_DIR)/bootdata_bootrom.cc \ - $(BOOTROM_DIR)/bootrom.sv \ - $(BOOTROM_DIR)/bootrom.dump \ - $(BOOTROM_DIR)/bootrom.elf ######## # Lint # @@ -345,6 +432,58 @@ ${LINT_PATH}/tmp/files: @if [ ! -x "$(BENDER)" ]; then echo "bender not installed; run 'make bender'"; exit 1; fi ${BENDER} script verilator $(VLOG_DEFS) -t rtl -t spatz -t cachepool -t dramsys --define COMMON_CELLS_ASSERTS_OFF > ${LINT_PATH}/tmp/files +######## +# Logs # +######## +LOGS_DIR ?= ${SIMBIN_DIR}/logs + +.PHONY: avg-log +avg-log: + rm -rf $(LOGS_DIR)/average + $(PYTHON) $(CACHEPOOL_DIR)/util/scripts/generate_average_log.py --logs-dir $(LOGS_DIR) + +############# +# Vis4Mesh # +############# +# NoC traffic visualization frontend (fork of https://github.com/ueqri/vis4mesh +# with CachePool-specific fixes/features). Consumes the Vis4Mesh dataset +# directories produced by util/scripts/noc_profiling_to_vis4mesh.py from +# hardware/tb/cachepool_noc_profiling.sv's per-cycle NoC logs. Fetched as a +# plain pinned clone (same pattern as toolchain.mk's riscv-gnu-toolchain/ +# llvm-project targets), not a git submodule. +NPM ?= npm +VIS4MESH_DIR ?= ${CACHEPOOL_DIR}/util/vis4mesh +VIS4MESH_REPO ?= https://github.com/DiyouS/vis4mesh.git +VIS4MESH_VERSION := $(shell cat ${CACHEPOOL_DIR}/util/vis4mesh.version) + +.PHONY: vis4mesh vis4mesh-serve +vis4mesh: ${VIS4MESH_DIR}/dist/index.bundle.js + +${VIS4MESH_DIR}/.git: + git clone ${VIS4MESH_REPO} ${VIS4MESH_DIR} + cd ${VIS4MESH_DIR} && git checkout ${VIS4MESH_VERSION} + +${VIS4MESH_DIR}/dist/index.bundle.js: ${VIS4MESH_DIR}/.git + cd ${VIS4MESH_DIR} && ${NPM} install && ${NPM} run build + +vis4mesh-serve: vis4mesh + cd ${VIS4MESH_DIR}/dist && ${PYTHON} -m http.server + +NOC_VIS_INPUT_DIR ?= noc_profiling +NOC_VIS_OUTPUT_PREFIX ?= ${VIS4MESH_DIR}/visdata/$(config) +NOC_VIS_SLICE_CYCLES ?= 500 +NOC_VIS_CLK_FREQ ?= 1000 + +.PHONY: vis4mesh-data +vis4mesh-data: + @for level in l1 l2; do \ + $(PYTHON) $(CACHEPOOL_DIR)/util/scripts/noc_profiling_to_vis4mesh.py --level $$level \ + --input-dir $(NOC_VIS_INPUT_DIR) --output-dir $(NOC_VIS_OUTPUT_PREFIX)-$$level \ + --num-groups-x $(num_groups_x) --num-groups-y $$(( $(num_groups) / $(num_groups_x) )) \ + --num-tiles-per-group $(num_tiles_per_group) --num-noc-ports-per-tile $(num_noc_ports_per_tile) \ + --slice-cycles $(NOC_VIS_SLICE_CYCLES) --clk-freq $(NOC_VIS_CLK_FREQ); \ + done + ######## # Help # ######## @@ -365,6 +504,9 @@ help: @echo "*generate*: generate the Spatz package and opcodes, and the cluster config HJSON" @echo "*cache-init*: source the insitu-cache environment (requires bender checkout)" @echo "*bootrom*: compile and generate the bootrom SystemVerilog module" + @echo "*update-floonoc*: regenerate FlooNoC package from FLOO_CFG (run after changing group count)" + @echo "*install-floogen*: install the floogen Python tool (required by update-floonoc)" + @echo "*clean-floonoc*: remove the generated FlooNoC package" @echo "" @echo "DRAMSys:" @echo "" @@ -378,7 +520,7 @@ help: @echo "" @echo "Simulation:" @echo "" - @echo "*vsim*: build hardware and software for QuestaSim simulation" + @echo "*vsim*: build hardware for QuestaSim simulation (use 'sw' to build software separately)" @echo "*clean.vsim*: remove the hardware simulation build [from sim/sim.mk]" @echo "*clean*: remove SW build, vsim build, and all generated HW files" @echo "" @@ -386,9 +528,30 @@ help: @echo "" @echo "*lint*: run SpyGlass lint (requires bender + SpyGlass in PATH)" @echo "" + @echo "Logs:" + @echo "" + @echo "*avg-log*: average the per-core/per-tile/per-group monitor_*.txt logs under LOGS_DIR" + @echo "" + @echo "--------------------------------------------------------------------------------------------------------" + @echo "Vis4Mesh (NoC visualization):" + @echo "" + @echo "*vis4mesh*: clone/build the Vis4Mesh frontend into util/vis4mesh (pinned, see util/vis4mesh.version)" + @echo "*vis4mesh-serve*: build (if needed) and serve Vis4Mesh at http://localhost:8000" + @echo " upload a directory from util/scripts/noc_profiling_to_vis4mesh.py's --output-dir" + @echo "*vis4mesh-data*: convert noc_profiling/*.log (see 'noc_profiling' above) into Vis4Mesh datasets" + @echo " (l1/l2) under NOC_VIS_OUTPUT_PREFIX-, using the current config's" + @echo " group/tile/port counts; one dataset per session_/ kernel session if present" + @echo "" @echo "--------------------------------------------------------------------------------------------------------" @echo "Settings:" @echo "*config*: cluster configuration name (default: $(config))" @echo "*CMAKE*: CMake binary (default: $(CMAKE)); must be >= 3.28 for DRAMSys" @echo "*DEBUG*: enable +acc for waveform visibility in vsim (default: $(DEBUG))" + @echo "*LOGS_DIR*: logs directory used by avg-log (default: $(LOGS_DIR))" + @echo "*VIS4MESH_DIR*: Vis4Mesh checkout directory (default: $(VIS4MESH_DIR))" + @echo "*VIS4MESH_REPO*: Vis4Mesh git remote (default: $(VIS4MESH_REPO))" + @echo "*NOC_VIS_INPUT_DIR*: vis4mesh-data --input-dir (default: $(NOC_VIS_INPUT_DIR))" + @echo "*NOC_VIS_OUTPUT_PREFIX*: vis4mesh-data output dir prefix, - appended (default: $(NOC_VIS_OUTPUT_PREFIX))" + @echo "*NOC_VIS_SLICE_CYCLES*: vis4mesh-data --slice-cycles (default: $(NOC_VIS_SLICE_CYCLES))" + @echo "*NOC_VIS_CLK_FREQ*: vis4mesh-data --clk-freq in MHz (default: $(NOC_VIS_CLK_FREQ))" @echo "" diff --git a/README.md b/README.md index 48c83e9..9bbb466 100644 --- a/README.md +++ b/README.md @@ -8,20 +8,28 @@ CachePool is a Snitch–Spatz–based many-core system with a shared L1 data cac - SystemVerilog (via `VLOG_DEFS` at compile time) - The Spatz cluster generator (via an auto-generated `config/cachepool.hjson`) +![CachePool block diagram](util/figures/CachePool_block_diagram.png) + ## System Hierarchy | Level | Module | Description | |-------|--------|-------------| | 1 | Core Complex (CC) | One 32-bit Snitch + one Spatz RVV accelerator | -| 2 | Tile | 4 CCs + 4 × 64 KiB 4-way InSitu-Cache banks | -| 3 | Group | 4 Tiles connected via crossbar | -| 4 | Cluster (WIP) | Multiple Groups connected via NoC (currently one Group) | +| 2 | Tile | 4 CCs + 4 × InSitu-Cache banks | +| 3 | Group | 4 Tiles connected via crossbar + shared L2 ICache | +| 4 | Cluster | Multiple Groups connected via a FlooNoC XY mesh (L1 request/response) plus a separate TCDM-based L2 refill mesh | + +All tiles across all groups share one unified L1 data cache, interleaved across cache banks. The bank-selection offset is configurable at runtime via `l1d_xbar_config(...)`. -All tiles in a cluster share one unified L1 cache, interleaved across cache banks. The bank-selection offset is configurable at runtime via `l1d_xbar_config(...)`. +For `NumGroups > 1`, two independent FlooNoC-based meshes connect the groups: +- **L1 group mesh** (`cachepool_group_noc_wrapper.sv`): XY-routed mesh of `floo_router` instances carrying inter-group cache-line requests/responses between tiles. +- **L2 refill mesh** (`cachepool_cluster.sv`, `gen_l2_refill_mesh`): a separate torus-like mesh (distinct flit/header types) that routes DRAM refill traffic from each group to `floo_tcdm_chimney` edge nodes, which bridge into the DRAMSys/HBM channels. + +Single-group configurations bypass both meshes; the intra-group crossbar path is unchanged. ## Requirements -- Linux environment with: `make`, `git`, `python3`, `wget`, `curl` +- Linux environment with: `make`, `git`, `python3`, `curl` - **CMake ≥ 3.28**, **GCC/G++ ≥ 11.2** - **QuestaSim** (tested with `questa-2023.4`) - Optional: SpyGlass for lint @@ -42,11 +50,12 @@ Build the RISC-V toolchains (LLVM + GCC). Spike (`riscv-isa-sim`) is also availa make toolchain ``` -For ETH users, a **pre-built toolchain** is available for faster setup: +For ETH users, a **pre-built toolchain** is available for faster setup. Source `iis-env.sh` +to point the toolchain at it and set up the Python venv needed for RTL/config generation: ```bash -# ETH only: link a prebuilt toolchain -make quick-tool +# ETH only: point at a prebuilt toolchain and set up the venv +source iis-env.sh ``` ### Initialize Submodules @@ -68,34 +77,40 @@ make dram-build CMAKE=/path/to/cmake-3.28.x CC=/path/to/gcc-11.2 CXX=/path/to/g+ ### Generate Required RTL Some RTL components (e.g., package headers) must be generated prior to simulation. -Generation requires specifying a **configuration**. If none is provided, the default is `cachepool_512`. +Generation requires specifying a **configuration**. If none is provided, the default is `cachepool_fpu_4g`. ```bash -make generate config=cachepool_fpu_512 +make generate config=cachepool_fpu_4g ``` +`make generate` now also regenerates the FlooNoC package (`update-floonoc`) as a prerequisite, so the correct mesh topology for the selected `config` is always built automatically. Run `make update-floonoc config=` standalone if you only need to refresh the NoC package (e.g. after editing a `config/floonoc_*.yml` topology file). + ### Build the BootROM The BootROM is built separately from the RTL generation step: ```bash -make bootrom config=cachepool_fpu_512 +make bootrom config=cachepool_fpu_4g ``` ### Compilation and Simulation +Hardware and software builds are decoupled: `make sw` builds software only, `make vsim` builds hardware only. Build whichever you need (or both) independently. + #### Build Software Only ```bash -make sw config=cachepool_fpu_512 +make sw config=cachepool_fpu_4g ``` -#### Build Hardware + Software (QuestaSim) +#### Build Hardware Only (QuestaSim) ```bash -make vsim config=cachepool_fpu_512 +make vsim config=cachepool_fpu_4g ``` +Set `DEBUG=0` to disable `+acc` waveform visibility and speed up simulation (used by CI); default is `DEBUG=1`. + #### Run the Simulation The wrapper script launches the simulation (GUI or CLI) and expects a software ELF path as argument: @@ -117,15 +132,17 @@ A lightweight benchmarking automation flow is provided under `util/auto-benchmar | File | Description | |------|-------------| | `configs.sh` | Defines configurations (`CONFIGS`) and kernel suffixes (`KERNELS`) to test, along with optional `PREFIX` and `ROOT_PATH`. | +| `configs-ci.sh` | CI-specific variant of `configs.sh`, used by `run_ci.sh`. | | `run_all.sh` | Main automation script that builds each configuration, runs all kernels, saves logs, and generates summaries. | +| `run_ci.sh` | CI entry point: sources `configs-ci.sh`, builds with `DEBUG=0`, and runs `check-ci.py` on each kernel's log. | | `write_results.py` | Extracts `[UART]` lines from simulator logs and appends them to per-configuration summary files. | -| `check_ci.py` | Scans a simulation log for failures and exits non-zero if any are found (see [CI Checking](#ci-checking)). | +| `check-ci.py` | Scans a simulation log for failures and exits non-zero if any are found (see [CI Checking](#ci-checking)). | ### Usage 1. Edit `configs.sh` to list the desired configurations and kernels: - CONFIGS="cachepool_fpu_512 cachepool_fpu_256 cachepool_fpu_128" + CONFIGS="cachepool_fpu_4g cachepool_fpu_16g" KERNELS="fdotp-32b_M32768 ffft-64b_M16384 fmatmul-64b_M2048" PREFIX="test-cachepool-" ROOT_PATH=../.. @@ -147,10 +164,10 @@ A lightweight benchmarking automation flow is provided under `util/auto-benchmar Example directory after a run: logs/20251028-1230/ - ├── cachepool_fpu_512_fdotp-32b_M32768.log - ├── cachepool_fpu_512_fdotp-32b_M32768_pm/ - ├── cachepool_fpu_512_summary.txt - ├── cachepool_fpu_256_summary.txt + ├── cachepool_fpu_4g_fdotp-32b_M32768.log + ├── cachepool_fpu_4g_fdotp-32b_M32768_pm/ + ├── cachepool_fpu_4g_summary.txt + ├── cachepool_fpu_16g_summary.txt └── ... Each run includes: @@ -162,7 +179,7 @@ This setup allows quick reproducible benchmarks with all results neatly organize ### CI Checking -`check_ci.py` scans a simulation log and exits non-zero if any failure is detected, making it suitable for integration into CI pipelines. It flags the following patterns: +`check-ci.py` scans a simulation log and exits non-zero if any failure is detected, making it suitable for integration into CI pipelines. It flags the following patterns: - Any line containing `FAIL` or `[FAIL]` (case-insensitive) - Any line matching `error ` where N is non-zero (`error 0` is treated as pass) @@ -170,46 +187,44 @@ This setup allows quick reproducible benchmarks with all results neatly organize Usage: ```bash -python3 check_ci.py logs/latest/cachepool_fpu_512_load-store.log +python3 check-ci.py logs/latest/cachepool_fpu_4g_load-store.log ``` Exit code 0 means all tests passed; exit code 1 means at least one failure was detected. On failure the offending lines and their line numbers are printed for manual inspection. +GitLab CI (`.gitlab-ci.yml`) uses this same flow directly: a `build` stage runs `make clean generate update-floonoc bootrom vsim sw config=$CI_CONFIG DEBUG=0` (default `CI_CONFIG=cachepool_fpu_4g`), then a `test` stage runs each kernel in parallel and pipes its log through `check-ci.py`. + ## Configurations -All hardware knobs live in **`config/config.mk`** (and flavor files it includes). The default configuration is **4 tiles, 16 cores**. +All hardware knobs live in **`config/config.mk`** (and flavor files it includes). The default configuration is **`cachepool_fpu_4g`: 4 groups (2×2 mesh), 4 tiles/group, 4 cores/tile = 64 cores total**. 2-group configurations have been removed — they are no longer compatible with the L2 mesh interconnect. -| Flavor file | Description | -|-------------|-------------| -| `cachepool.mk` | No floating-point support | -| `cachepool_fpu.mk` | Enables single/half precision in the Spatz vector core | +Configuration names encode the number of groups and whether the FPU is enabled: -Available named configurations (passed as `config=`): +| Name | Groups | Mesh | FPU | Tiles/group | Cores/tile | Cores | +|------|--------|------|-----|-------------|------------|-------| +| `cachepool_4g` | 4 | 2×2 | No | 4 | 4 | 64 | +| `cachepool_fpu_4g` | 4 | 2×2 | Yes | 4 | 4 | 64 | +| `cachepool_fpu_16g` | 16 | 4×4 | Yes | 4 | 4 | 256 | +| `cachepool_fpu_16g_tiny` | 16 | 4×4 | Yes | 2 | 2 | 64 | -| Name | Cacheline | FPU | -|------|-----------|-----| -| `cachepool_512` | 512b | No | -| `cachepool_128` | 128b | No | -| `cachepool_fpu_512` | 512b | Yes | -| `cachepool_fpu_256` | 256b | Yes | -| `cachepool_fpu_128` | 128b | Yes | +`cachepool_fpu_16g_tiny` shrinks tiles/group and cores/tile for a faster-to-build, faster-to-simulate smoke test of the full 16-group mesh topology. The Spatz cluster consumes **`config/cachepool.hjson`**, which is **generated** from: - `config/cachepool.hjson.tmpl` (skeleton with comments) - `config/config.mk` (source of truth) -To switch flavors, set `config=` (or export `CACHEPOOL_CONFIGURATION=`), then rebuild: +Multi-group configurations also require a FlooNoC topology file (e.g. `config/floonoc_cachepool_4g.yml`, `config/floonoc_cachepool_16g.yml`, `config/floonoc_cachepool_16g_tiny.yml`), auto-selected by the config name suffix. `make generate` regenerates the FlooNoC package automatically; run `make update-floonoc` standalone only if you need to refresh it without a full generate. + +To switch configurations, always clean first: ```bash make clean -make generate config=cachepool_fpu_512 +make generate config=cachepool_fpu_4g ``` -> `make clean` is recommended when changing configurations. - ### How configuration flows -1. **`config/config.mk`** defines all parameters (e.g., `num_tiles`, `num_cores`, `l1d_cacheline_width`, `axi_user_width`, addresses, etc.). Derived values (like `axi_user_width`) are pre-computed so tools receive integers, not expressions. +1. **`config/config.mk`** defines all parameters (e.g., `num_groups`, `num_groups_x`, `num_tiles_per_group`, `num_cores_per_tile`, `l1d_cacheline_width`, `axi_user_width`, etc.). Derived values are pre-computed so tools receive integers, not expressions. 2. `make generate` calls the Python generator to produce **`config/cachepool.hjson`** from the template. 3. The Makefile passes the same values to **QuestaSim** via `VLOG_DEFS`, keeping RTL, sim, and HJSON in sync. @@ -265,29 +280,94 @@ For `insn != 2'b00`, the peripheral sets the tile-select mask to all-ones for co ### Software API +Cluster-wide entry points (`software/snRuntime/src/l1cache.c`) — each of these must be called by **all** cores; they fence, barrier, have core 0 issue the register write(s), and barrier again before returning, so callers don't need to gate by core ID themselves: + ```c -// Flush all banks in all tiles (existing behaviour) -l1d_flush(); +// Flush all banks in all tiles +l1d_cluster_flush(); -// Flush private banks in selected tiles (one-hot tile mask) -l1d_private_flush(uint32_t tile_mask); +// Flush private banks in selected tiles (one-hot tile mask, bits 0-63) +l1d_cluster_private_flush(uint64_t tile_mask); // Flush shared banks in all tiles -l1d_shared_flush(); +l1d_cluster_shared_flush(); -// Set the private/shared address boundary +// Set the number of private cache banks per tile (0..NumCache) +l1d_part(uint32_t size); + +// Set the private/shared address boundary (addr >= boundary is private) l1d_addr(uint32_t addr); -// Poll until all pending flush operations complete -l1d_wait(); +// Set the crossbar bank-selection bit offset (clamped to >= cacheline width) +l1d_xbar_config(uint32_t offset); ``` +Lower-level, single-shot primitives (`l1d_flush()`, `l1d_private_flush()`, `l1d_shared_flush()`, `l1d_commit()`, `l1d_wait()`) exist for building custom sequences but issue only the register write — the caller is responsible for cluster-wide fence/barrier synchronization around them. + +`l1d_addr()` has no hardware completion status bit (unlike flush, which polls `L1D_FLUSH_STATUS` via `l1d_wait()`); its peripheral register write is posted over the interconnect, so it bridges the gap with a fixed-cycle delay on core 0 before the final barrier. + +> Changing the partition mode, boundary address, or crossbar offset while the cache contains valid data requires a flush first — `l1d_xbar_config()` and `l1d_part()` flush internally; `l1d_addr()` does not (call a cluster flush before it if needed). + ### Flush completion Each tile tracks completion per cache controller using a `cache_flush_q` register (one bit per controller). The tile asserts a one-cycle ready pulse to the cluster controller when all targeted controllers have finished. The cluster controller uses a per-tile lock register to track in-flight flushes; a new instruction cannot be issued until all selected tiles report completion. Cache accesses from cores and remote tiles are gated (`l1d_busy`) while a flush is in progress, preventing stale hits during the flush window. +## Performance Monitor + +A simulation-only monitor (`hardware/tb/cachepool_monitor.sv`), instantiated alongside the DUT in the testbench, tracks per-**session** traffic and performance counters — a session is the interval between toggles of the `SPATZ_STATUS.SPATZ_CLUSTER_PROBE` peripheral bit. Software brackets a region of interest with: + +```c +start_kernel(); // opens a new monitoring session +// ... code to profile ... +stop_kernel(); // closes the session and dumps counters to file +``` + +(call from a single core, e.g. `if (cid == 0) { ... }`; both are declared in `software/tests/include/benchmark.h`). + +Each session dumps counter files under `sim/bin/logs/`, split into three subfolders: + +| Subfolder | Contents | +|-----------|----------| +| `core/` | Per-core statistics (`monitor_core_g__c.txt`) | +| `noc/` | Inter-group L1 NoC, L2 refill mesh, and DRAM channel traffic | +| `others/` | Per-tile local/group/remote request locality and congestion | + +CI test jobs recreate these subfolders before running (build artifacts exclude `sim/bin/logs/`) and archive the whole `sim/bin/logs/` tree as job artifacts. + +### NoC Visualization (Vis4Mesh) + +For a spatial, time-scrubbable view of NoC traffic (as opposed to `cachepool_monitor.sv`'s per-session text summaries above), a second simulation-only tap, `hardware/tb/cachepool_noc_profiling.sv`, records cycle-accurate per-router/tile/core state and packet traces to `noc_profiling/*.log`. `util/scripts/noc_profiling_to_vis4mesh.py` converts those logs into a dataset for [Vis4Mesh](https://github.com/DiyouS/vis4mesh) (a fork of [ueqri/vis4mesh](https://github.com/ueqri/vis4mesh) with CachePool-specific fixes), a browser-based NoC traffic visualizer. + +1. **Enable the profiler and run a kernel** (default `noc_profiling ?= 1`, so this is on unless you passed `noc_profiling=0`): + ```sh + make vsim config= noc_profiling=1 + ./sim/bin/cachepool_cluster.vsim software/build/CachePoolTests/test-cachepool- + ``` + L1 (inter-group cache-access mesh) logs (`router_g*.log`, `tile_g*.log`) only populate with `num_rg_ports_per_core > 0` (a multi-group config); L2 (DRAM-refill mesh) logs (`l2_router_g*.log`) and per-core PE logs (`pe_g*.log`) are always populated. + +2. **Convert to a Vis4Mesh dataset**: + ```sh + python3 util/scripts/noc_profiling_to_vis4mesh.py --level l1 \ + --num-groups-x --num-groups-y \ + --num-tiles-per-group --num-noc-ports-per-tile \ + --output-dir util/vis4mesh/visdata/ + ``` + `--level` selects the network and channel breakdown: + | Level | Network | Channel breakdown | + |---|---|---| + | `l1` (default) | Inter-group cache-access mesh | Per physical link (tile × NoC port) | + | `l2` | DRAM-refill mesh | Single (per your read/write/resp split via message type) | + + `--num-tiles-per-group`/`--num-noc-ports-per-tile` are only needed for `l1`. See the script's module docstring for the full data-shape/limitation notes (e.g. hop-distance and transfer-type filters aren't populated yet). + +3. **Build and serve Vis4Mesh, then upload the dataset directory**: + ```sh + make vis4mesh-serve # clones/builds util/vis4mesh (pinned, see util/vis4mesh.version), serves at http://localhost:8000 + ``` + Open the URL, click the upload button, and select the `--output-dir` directory from step 2. + ## Snitch–Spatz Core Complex The default system uses a 32-bit Snitch core with a Spatz RVV accelerator. Double-precision is disabled by default for scalability; enable the FPU flavor (`cachepool_fpu.mk`) for single/half precision support. @@ -319,7 +399,7 @@ Cluster peripherals (including the BootROM and memory-mapped registers) are inst SpyGlass lint (optional): ```bash -make lint config=cachepool_fpu_512 +make lint config=cachepool_fpu_4g ``` --- @@ -328,6 +408,13 @@ make lint config=cachepool_fpu_512 - To see the exact macros passed to vlog, check `VLOG_DEFS` in the Makefile and `sim/work/compile.vsim.tcl`. - If you change cacheline width, `AXI_USER_WIDTH` is derived (supported widths: 128→19, 256→18, 512→17). Unsupported widths error out at generation time. -- Use `make clean` when switching flavors/configs to prevent stale build artifacts. +- `make generate` regenerates the FlooNoC package automatically; only run `make update-floonoc` standalone if you're iterating on a `config/floonoc_*.yml` topology file without a full generate. +- `make sw` and `make vsim` are decoupled (hw/sw build independently); rebuild whichever side you changed. +- Use `make clean` when switching configs to prevent stale build artifacts. - Runtime functions `snrt_tile_id()` and `snrt_num_tiles()` are available to query tile topology from software. -- Changing the partition mode or boundary address while the cache holds valid data requires a flush (`l1d_flush()` or the appropriate partition flush) before reconfiguring. +- Changing the partition mode or boundary address while the cache holds valid data requires a flush (`l1d_cluster_flush()` or the appropriate cluster-wide partition flush) before reconfiguring. +- Set `DEBUG=0` to disable `+acc` and speed up simulation (used by CI); default is `DEBUG=1` for waveform visibility. + +## License + +CachePool is released under permissive open source licenses. Most of CachePool's source code is released under the Apache License 2.0 (`Apache-2.0`), see [LICENSE](LICENSE). The code in `hardware/` is released under Solderpad v0.51 (`SHL-0.51`), see [hardware/LICENSE](hardware/LICENSE). diff --git a/config/cachepool.hjson b/config/cachepool.hjson deleted file mode 100644 index 2ac0947..0000000 --- a/config/cachepool.hjson +++ /dev/null @@ -1,108 +0,0 @@ -// CachePool cluster configuration (generated from config.mk) -// Edit this skeleton for fixed values - -{ - cluster: { - mempool: 0, - boot_addr: 4096, // e.g., 4096 (0x1000) - cluster_base_addr: 3221223424, // 0xBFFF_F800 typically - cluster_base_offset: 0, - cluster_base_hartid: 0, - addr_width: 32, - data_width: 32, - id_width_in: 6, // fixed for now - id_width_out: 2, // fixed for now - user_width: 17, - axi_cdc_enable: false, - sw_rst_enable: true, - axi_isolate_enable: false, - - tcdm: { - size: 2, // kB (fixed for now) - banks: 1, - }, - - cluster_periph_size: 64, // kB (fixed) - dma_data_width: 128, - dma_axi_req_fifo_depth: 3, - dma_req_fifo_depth: 3, - - // Spatz parameters - vlen: 512, - n_fpu: 4, - n_ipu: 4, - spatz_fpu: true, - norvd: true, - - // Timing parameters (fixed defaults; tweak in template if needed) - timing: { - lat_comp_fp32: 1, - lat_comp_fp64: 2, - lat_comp_fp16: 1, - lat_comp_fp16_alt: 1, - lat_comp_fp8: 0, - lat_comp_fp8_alt: 0, - lat_noncomp: 1, - lat_conv: 2, - lat_sdotp: 4, - fpu_pipe_config: "BEFORE", - xbar_latency: "CUT_ALL_PORTS", - register_tcdm_cuts: true, - register_core_req: true, - register_core_rsp: true, - register_offload_rsp: true - }, - - nr_tiles: 4, - - // Repeat the compute core template N times (driven by 16) - cores: [ - { $ref: "#/compute_core_template" },{ $ref: "#/compute_core_template" },{ $ref: "#/compute_core_template" },{ $ref: "#/compute_core_template" },{ $ref: "#/compute_core_template" },{ $ref: "#/compute_core_template" },{ $ref: "#/compute_core_template" },{ $ref: "#/compute_core_template" },{ $ref: "#/compute_core_template" },{ $ref: "#/compute_core_template" },{ $ref: "#/compute_core_template" },{ $ref: "#/compute_core_template" },{ $ref: "#/compute_core_template" },{ $ref: "#/compute_core_template" },{ $ref: "#/compute_core_template" },{ $ref: "#/compute_core_template" } - ], - - icache: { - size: 16, // kB (fixed) - sets: 4, // ways - cacheline: 128 // bits - } - }, - - dram: { - address: 2147483648, // 0x8000_0000 - length: 536870912 // 0x2000_0000 - }, - - uncached_region: { - address: 3221225472, // 0xC000_0000 - length: 536870912 // 0x2000_0000 - }, - - peripherals: { }, - - // Templates - compute_core_template: { - isa: "rv32imafd", - xf16: false, - xf8: false, - xfdotp: false, - xdma: false, - num_int_outstanding_loads: 32, - num_int_outstanding_mem: 32, - num_spatz_outstanding_loads: 32, - num_dtlb_entries: 1, - num_itlb_entries: 1 - }, - - dma_core_template: { - isa: "rv32imafd", - xdma: true, - xf16: false, - xf8: false, - xfdotp: false, - num_int_outstanding_loads: 32, - num_int_outstanding_mem: 32, - num_spatz_outstanding_loads: 32, - num_dtlb_entries: 1, - num_itlb_entries: 1 - } -} diff --git a/config/cachepool_128.mk b/config/cachepool_128.mk deleted file mode 100644 index df52dab..0000000 --- a/config/cachepool_128.mk +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright 2025 ETH Zurich and University of Bologna. -# Licensed under the Apache License, Version 2.0, see LICENSE for details. -# SPDX-License-Identifier: Apache-2.0 - -# Author: Diyou Shen, ETH Zurich - -######################### -## CachePool Cluster ## -######################### - -# Number of tiles -num_tiles ?= 4 - -# Number of cores -num_cores ?= 16 - -# Core datawidth -data_width ?= 32 - -# Core addrwidth -addr_width ?= 32 - - -###################### -## CachePool Tile ## -###################### - -# Number of cores per CachePool tile -num_cores_per_tile ?= 4 - -# Refill interconnection data width -refill_data_width ?= 128 - -##### L1 Data Cache ##### - -# L1 data cacheline width (in Bit) -l1d_cacheline_width ?= 128 - -# L1 data cache size (in KiB) -l1d_size ?= 256 - -# L1 data cache banking factor (how many banks per core?) -l1d_bank_factor ?= 1 - -# L1 coalecsing window -l1d_coal_window ?= 2 - -# L1 data cache number of ways per -l1d_num_way ?= 4 - -# L1 data cache size per tile (KiB) -l1d_tile_size ?= 256 - -# L1 data cache tag width (TODO: should be calcualted) -l1d_tag_data_width ?= 52 - -#################### -## CachePool CC ## -#################### -# Spatz fpu support? -spatz_fpu_en ?= 0 - -# Spatz number of FPU -spatz_num_fpu ?= 0 - -# Spatz number of IPU -spatz_num_ipu ?= 4 - -# Spatz max outstanding transactions -spatz_max_trans ?= 32 - -# Snitch/FPU max outstanding transactions -snitch_max_trans ?= 16 - - -##################### -## L2 Main Memory ## -##################### -# L2 number of channels -l2_channel ?= 4 - -# L2 bank width (DRAM width, change with care) -l2_bank_width ?= 512 - -# L2 interleaving factor (in order of bank_width) -l2_interleave ?= 16 - - -################## -## Peripherals ## -################## -# Hardware stack size (in Byte) -stack_hw_size ?= 1024 - -# Stack size (total, including share and private, 32'h800) -stack_tot_size ?= 2048 diff --git a/config/cachepool_512.mk b/config/cachepool_4g.mk similarity index 82% rename from config/cachepool_512.mk rename to config/cachepool_4g.mk index 6d04a68..f2237c4 100644 --- a/config/cachepool_512.mk +++ b/config/cachepool_4g.mk @@ -8,11 +8,17 @@ ## CachePool Cluster ## ######################### +# Number of groups +num_groups ?= 4 + +# 2×2 mesh +num_groups_x ?= 2 + # Number of tiles -num_tiles ?= 4 +num_tiles_per_group ?= 4 # Number of cores -num_cores ?= 16 +num_cores_per_tile ?= 4 # Core datawidth data_width ?= 32 @@ -20,25 +26,22 @@ data_width ?= 32 # Core addrwidth addr_width ?= 32 +num_lg_ports_per_core ?= 4 + +num_rg_ports_per_core ?= 1 + +num_noc_ports_per_tile ?= 4 + ###################### ## CachePool Tile ## ###################### -# Number of cores per CachePool tile -num_cores_per_tile ?= 4 - -# Refill interconnection data width -refill_data_width ?= 128 - ##### L1 Data Cache ##### # L1 data cacheline width (in Bit) l1d_cacheline_width ?= 512 -# L1 data cache size (in KiB) -l1d_size ?= 256 - # L1 data cache banking factor (how many banks per core?) l1d_bank_factor ?= 1 @@ -48,7 +51,7 @@ l1d_coal_window ?= 2 # L1 data cache number of ways per l1d_num_way ?= 4 -# L1 data cache size **per tile** (KiB) +# L1 data cache size per tile (KiB) l1d_tile_size ?= 256 # L1 data cache tag width (TODO: should be calcualted) @@ -76,6 +79,9 @@ snitch_max_trans ?= 16 ##################### ## L2 Main Memory ## ##################### +# DRAM type (selects DRAMSys config and sets default refill_data_width) +dram_type ?= HBM2 + # L2 number of channels l2_channel ?= 4 diff --git a/config/cachepool_fpu_256.mk b/config/cachepool_fpu_16g.mk similarity index 80% rename from config/cachepool_fpu_256.mk rename to config/cachepool_fpu_16g.mk index 279dc80..aee1634 100644 --- a/config/cachepool_fpu_256.mk +++ b/config/cachepool_fpu_16g.mk @@ -8,11 +8,17 @@ ## CachePool Cluster ## ######################### +# Number of groups +num_groups ?= 16 + +# 4×4 mesh +num_groups_x ?= 4 + # Number of tiles -num_tiles ?= 4 +num_tiles_per_group ?= 4 # Number of cores -num_cores ?= 16 +num_cores_per_tile ?= 4 # Core datawidth data_width ?= 32 @@ -20,24 +26,21 @@ data_width ?= 32 # Core addrwidth addr_width ?= 32 +num_lg_ports_per_core ?= 2 + +num_rg_ports_per_core ?= 1 + +num_noc_ports_per_tile ?= 4 + ###################### ## CachePool Tile ## ###################### -# Number of cores per CachePool tile -num_cores_per_tile ?= 4 - -# Refill interconnection data width -refill_data_width ?= 128 - ##### L1 Data Cache ##### # L1 data cacheline width (in Bit) -l1d_cacheline_width ?= 256 - -# L1 data cache size (in KiB) -l1d_size ?= 256 +l1d_cacheline_width ?= 512 # L1 data cache banking factor (how many banks per core?) l1d_bank_factor ?= 1 @@ -52,7 +55,7 @@ l1d_num_way ?= 4 l1d_tile_size ?= 256 # L1 data cache tag width (TODO: should be calcualted) -l1d_tag_data_width ?= 64 +l1d_tag_data_width ?= 92 #################### ## CachePool CC ## @@ -76,14 +79,17 @@ snitch_max_trans ?= 16 ##################### ## L2 Main Memory ## ##################### +# DRAM type (selects DRAMSys config and sets default refill_data_width) +dram_type ?= HBM2 + # L2 number of channels -l2_channel ?= 4 +l2_channel ?= 8 # L2 bank width (DRAM width, change with care) l2_bank_width ?= 512 # L2 interleaving factor (in order of bank_width) -l2_interleave ?= 8 +l2_interleave ?= 16 ################## diff --git a/config/cachepool_fpu_128.mk b/config/cachepool_fpu_16g_tiny.mk similarity index 79% rename from config/cachepool_fpu_128.mk rename to config/cachepool_fpu_16g_tiny.mk index e60aad4..abdea66 100644 --- a/config/cachepool_fpu_128.mk +++ b/config/cachepool_fpu_16g_tiny.mk @@ -8,11 +8,17 @@ ## CachePool Cluster ## ######################### +# Number of groups +num_groups ?= 16 + +# 4×4 mesh +num_groups_x ?= 4 + # Number of tiles -num_tiles ?= 4 +num_tiles_per_group ?= 2 # Number of cores -num_cores ?= 16 +num_cores_per_tile ?= 2 # Core datawidth data_width ?= 32 @@ -20,30 +26,27 @@ data_width ?= 32 # Core addrwidth addr_width ?= 32 +num_lg_ports_per_core ?= 1 + +num_rg_ports_per_core ?= 1 + +num_noc_ports_per_tile ?= 2 + ###################### ## CachePool Tile ## ###################### -# Number of cores per CachePool tile -num_cores_per_tile ?= 4 - -# Refill interconnection data width -refill_data_width ?= 128 - ##### L1 Data Cache ##### # L1 data cacheline width (in Bit) -l1d_cacheline_width ?= 128 - -# L1 data cache size (in KiB) -l1d_size ?= 256 +l1d_cacheline_width ?= 512 # L1 data cache banking factor (how many banks per core?) l1d_bank_factor ?= 1 # L1 coalecsing window -l1d_coal_window ?= 1 +l1d_coal_window ?= 2 # L1 data cache number of ways per l1d_num_way ?= 4 @@ -52,7 +55,7 @@ l1d_num_way ?= 4 l1d_tile_size ?= 256 # L1 data cache tag width (TODO: should be calcualted) -l1d_tag_data_width ?= 64 +l1d_tag_data_width ?= 92 #################### ## CachePool CC ## @@ -76,8 +79,11 @@ snitch_max_trans ?= 16 ##################### ## L2 Main Memory ## ##################### +# DRAM type (selects DRAMSys config and sets default refill_data_width) +dram_type ?= HBM2 + # L2 number of channels -l2_channel ?= 4 +l2_channel ?= 8 # L2 bank width (DRAM width, change with care) l2_bank_width ?= 512 @@ -85,6 +91,8 @@ l2_bank_width ?= 512 # L2 interleaving factor (in order of bank_width) l2_interleave ?= 16 +axi_user_width ?= 16 + ################## ## Peripherals ## diff --git a/config/cachepool_fpu_512.mk b/config/cachepool_fpu_4g.mk similarity index 84% rename from config/cachepool_fpu_512.mk rename to config/cachepool_fpu_4g.mk index 2e4c3ca..349d6fc 100644 --- a/config/cachepool_fpu_512.mk +++ b/config/cachepool_fpu_4g.mk @@ -8,11 +8,17 @@ ## CachePool Cluster ## ######################### +# Number of groups +num_groups ?= 4 + +# 2×2 mesh +num_groups_x ?= 2 + # Number of tiles -num_tiles ?= 4 +num_tiles_per_group ?= 4 # Number of cores -num_cores ?= 16 +num_cores_per_tile ?= 4 # Core datawidth data_width ?= 32 @@ -20,27 +26,22 @@ data_width ?= 32 # Core addrwidth addr_width ?= 32 -num_remote_ports_per_tile ?= 2 +num_lg_ports_per_core ?= 1 + +num_rg_ports_per_core ?= 1 + +num_noc_ports_per_tile ?= 2 ###################### ## CachePool Tile ## ###################### -# Number of cores per CachePool tile -num_cores_per_tile ?= 4 - -# Refill interconnection data width -refill_data_width ?= 128 - ##### L1 Data Cache ##### # L1 data cacheline width (in Bit) l1d_cacheline_width ?= 512 -# L1 data cache size (in KiB) -l1d_size ?= 256 - # L1 data cache banking factor (how many banks per core?) l1d_bank_factor ?= 1 @@ -78,6 +79,9 @@ snitch_max_trans ?= 16 ##################### ## L2 Main Memory ## ##################### +# DRAM type (selects DRAMSys config and sets default refill_data_width) +dram_type ?= HBM2 + # L2 number of channels l2_channel ?= 4 diff --git a/config/config.mk b/config/config.mk index 9eee8cb..a4280d7 100644 --- a/config/config.mk +++ b/config/config.mk @@ -22,17 +22,51 @@ endif include $(CACHEPOOL_DIR)/config/$(config).mk +##################### +## DRAM Type ## +##################### + +# DRAM type used by DRAMSys and to auto-set refill_data_width. +# Supported values: DDR3, DDR4, LPDDR4, HBM2 +dram_type ?= HBM2 + +# Auto-derive default refill data width from dram_type. +# HBM variants: 512b (3600*16/1000 = 57.6B/cyc) +# DDR variants: 128b (1866*8/1000 = 15B/cyc) +ifeq ($(dram_type),HBM2) + refill_data_width_default := 512 +else ifeq ($(dram_type),LPDDR4) + refill_data_width_default := 256 +else + refill_data_width_default := 128 +endif + ######################### ## CachePool Cluster ## ######################### +# Number of groups +num_groups ?= 1 + +# X dimension of the group mesh (Y = num_groups / num_groups_x) +num_groups_x ?= 1 + # Number of tiles -num_tiles ?= 1 +num_tiles_per_group ?= 4 +num_tiles = $(shell echo $$(( $(num_groups) * $(num_tiles_per_group)))) -num_remote_ports_per_tile ?= 1 +# Intra-group remote ports per core (to other tiles in the same group) +num_lg_ports_per_core ?= 1 # Number of cores -num_cores ?= 4 +num_cores_per_tile ?= 4 +num_cores ?= $(shell echo $$(( $(num_tiles) * $(num_cores_per_tile)))) + +# Inter-group remote ports per core (to remote groups, via the L1 NoC) +num_rg_ports_per_core ?= 0 + +# NoC router channels per tile (concentrates each tile's inter-group traffic) +num_noc_ports_per_tile ?= 1 # Core datawidth data_width ?= 32 @@ -40,25 +74,22 @@ data_width ?= 32 # Core addrwidth addr_width ?= 32 +# Bootrom datawidth +bootrom_data_width ?= 32 + ###################### ## CachePool Tile ## ###################### -# Number of cores per CachePool tile -num_cores_per_tile ?= 4 - -# Refill interconnection data width -refill_data_width ?= 128 +# Refill interconnection data width (auto-derived from dram_type; override in flavor files) +refill_data_width ?= $(refill_data_width_default) ##### L1 Data Cache ##### # L1 data cacheline width (in Bit) l1d_cacheline_width ?= 512 -# L1 data cache size (in KiB) -l1d_size ?= 256 - # L1 data cache banking factor (how many banks per core?) l1d_bank_factor ?= 1 @@ -106,16 +137,25 @@ snitch_max_trans ?= 16 ######################### ifeq ($(l1d_cacheline_width),512) - axi_user_width := 17 + axi_user_width ?= 17 else ifeq ($(l1d_cacheline_width),256) - axi_user_width := 18 + axi_user_width ?= 18 else ifeq ($(l1d_cacheline_width),128) - axi_user_width := 21 + axi_user_width ?= 21 endif ##################### ## L2 Main Memory ## ##################### + +# DRAM base address and size (hex: 0x8000_0000, 0x2000_0000) +dram_addr ?= 2147483648 +dram_len ?= 536870912 + +# Uncached region base address and size (hex: 0xC000_0000, 0x2000_0000) +uncached_addr ?= 3221225472 +uncached_len ?= 536870912 + # L2 number of channels l2_channel ?= 4 diff --git a/config/floonoc_cachepool_16g.yml b/config/floonoc_cachepool_16g.yml new file mode 100644 index 0000000..4ef03ee --- /dev/null +++ b/config/floonoc_cachepool_16g.yml @@ -0,0 +1,156 @@ +# Copyright 2025 ETH Zurich and University of Bologna. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +# Author: Diyou Shen, ETH Zurich + +# FlooNoC topology for CachePool 16-group (4x4 mesh) with AXI-only links. +# +# Wide channel: dCache refill (512b data) +# +# 4x4 group router mesh, 8 HBM on West/East edges: +# West: hbm0-3 at routers [0,0]-[0,3] +# East: hbm4-7 at routers [3,0]-[3,3] +# Peripheral traffic routed through hbm0 chimney (demuxed at cluster output) +# +# Width calculation notes (for cachepool_fpu_16g config, 4 tiles/group): +# wide_in id_width = GroupWideIdWidth + 1 = TileAxiIdWidth + clog2(4) + 1 + 1 = 7 +# wide_out id_width = SpatzAxiIdOutWidth = clog2(32*2) = 6 +# user_width = SpatzAxiUserWidth = axi_user_width + clog2(NumTiles) = 17 + 6 = 23 +# HBM per-channel = DramSize / l2_channel = 0x4000_0000 / 8 = 0x0800_0000 +# DRAM total: 0x8000_0000 - 0xBFFF_FFFF (1 GB) + +name: cachepool +description: "CachePool AXI NoC - 16 groups" +network_type: "axi" + +routing: + route_algo: "SRC" + use_id_table: true + +protocols: + - name: "wide_in" + type: "wide" + protocol: "AXI4" + data_width: 512 + addr_width: 32 + id_width: 7 + user_width: 23 + - name: "wide_out" + type: "wide" + protocol: "AXI4" + data_width: 512 + addr_width: 32 + id_width: 6 + user_width: 23 + +endpoints: + - name: "group" + array: [4, 4] + mgr_port_protocol: + - "wide_in" + # HBM West (channels 0-3, scrambled contiguous addresses) + # HBM0 also handles peripheral traffic (demuxed at cluster output) + - name: "hbm0" + addr_range: + - start: 0x0000_0000 + end: 0x7FFF_FFFF + - start: 0x8000_0000 + end: 0x87FF_FFFF + - start: 0xC000_0000 + end: 0xFFFF_FFFF + sbr_port_protocol: + - "wide_out" + - name: "hbm1" + addr_range: + - start: 0x8800_0000 + end: 0x8FFF_FFFF + sbr_port_protocol: + - "wide_out" + - name: "hbm2" + addr_range: + - start: 0x9000_0000 + end: 0x97FF_FFFF + sbr_port_protocol: + - "wide_out" + - name: "hbm3" + addr_range: + - start: 0x9800_0000 + end: 0x9FFF_FFFF + sbr_port_protocol: + - "wide_out" + # HBM East (channels 4-7) + - name: "hbm4" + addr_range: + - start: 0xA000_0000 + end: 0xA7FF_FFFF + sbr_port_protocol: + - "wide_out" + - name: "hbm5" + addr_range: + - start: 0xA800_0000 + end: 0xAFFF_FFFF + sbr_port_protocol: + - "wide_out" + - name: "hbm6" + addr_range: + - start: 0xB000_0000 + end: 0xB7FF_FFFF + sbr_port_protocol: + - "wide_out" + - name: "hbm7" + addr_range: + - start: 0xB800_0000 + end: 0xBFFF_FFFF + sbr_port_protocol: + - "wide_out" + +routers: + - name: "group_router" + array: [4, 4] + degree: 5 + +connections: + - src: "group" + dst: "group_router" + src_range: + - [0, 3] + - [0, 3] + dst_range: + - [0, 3] + - [0, 3] + dst_dir: "Eject" + # HBM West + - src: "hbm0" + dst: "group_router" + dst_idx: [0, 0] + dst_dir: "West" + - src: "hbm1" + dst: "group_router" + dst_idx: [0, 1] + dst_dir: "West" + - src: "hbm2" + dst: "group_router" + dst_idx: [0, 2] + dst_dir: "West" + - src: "hbm3" + dst: "group_router" + dst_idx: [0, 3] + dst_dir: "West" + # HBM East + - src: "hbm4" + dst: "group_router" + dst_idx: [3, 0] + dst_dir: "East" + - src: "hbm5" + dst: "group_router" + dst_idx: [3, 1] + dst_dir: "East" + - src: "hbm6" + dst: "group_router" + dst_idx: [3, 2] + dst_dir: "East" + - src: "hbm7" + dst: "group_router" + dst_idx: [3, 3] + dst_dir: "East" diff --git a/config/floonoc_cachepool_16g_tiny.yml b/config/floonoc_cachepool_16g_tiny.yml new file mode 100644 index 0000000..80f8b2a --- /dev/null +++ b/config/floonoc_cachepool_16g_tiny.yml @@ -0,0 +1,156 @@ +# Copyright 2025 ETH Zurich and University of Bologna. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +# Author: Diyou Shen, ETH Zurich + +# FlooNoC topology for CachePool 16-group tiny (4x4 mesh) with AXI-only links. +# +# Wide channel: dCache refill (512b data) +# +# 4x4 group router mesh, 8 HBM on West/East edges: +# West: hbm0-3 at routers [0,0]-[0,3] +# East: hbm4-7 at routers [3,0]-[3,3] +# Peripheral traffic routed through hbm0 chimney (demuxed at cluster output) +# +# Width calculation notes (for cachepool_fpu_16g_tiny config, 2 tiles/group): +# wide_in id_width = GroupWideIdWidth + 1 = TileAxiIdWidth + clog2(2) + 1 + 1 = 6 +# wide_out id_width = SpatzAxiIdOutWidth = clog2(32*2) = 6 +# user_width = SpatzAxiUserWidth = axi_user_width + clog2(NumTiles) = 16 + 5 = 21 +# HBM per-channel = DramSize / l2_channel = 0x4000_0000 / 8 = 0x0800_0000 +# DRAM total: 0x8000_0000 - 0xBFFF_FFFF (1 GB) + +name: cachepool +description: "CachePool AXI NoC - 16 groups (tiny)" +network_type: "axi" + +routing: + route_algo: "SRC" + use_id_table: true + +protocols: + - name: "wide_in" + type: "wide" + protocol: "AXI4" + data_width: 512 + addr_width: 32 + id_width: 6 + user_width: 21 + - name: "wide_out" + type: "wide" + protocol: "AXI4" + data_width: 512 + addr_width: 32 + id_width: 6 + user_width: 21 + +endpoints: + - name: "group" + array: [4, 4] + mgr_port_protocol: + - "wide_in" + # HBM West (channels 0-3, scrambled contiguous addresses) + # HBM0 also handles peripheral traffic (demuxed at cluster output) + - name: "hbm0" + addr_range: + - start: 0x0000_0000 + end: 0x7FFF_FFFF + - start: 0x8000_0000 + end: 0x87FF_FFFF + - start: 0xC000_0000 + end: 0xFFFF_FFFF + sbr_port_protocol: + - "wide_out" + - name: "hbm1" + addr_range: + - start: 0x8800_0000 + end: 0x8FFF_FFFF + sbr_port_protocol: + - "wide_out" + - name: "hbm2" + addr_range: + - start: 0x9000_0000 + end: 0x97FF_FFFF + sbr_port_protocol: + - "wide_out" + - name: "hbm3" + addr_range: + - start: 0x9800_0000 + end: 0x9FFF_FFFF + sbr_port_protocol: + - "wide_out" + # HBM East (channels 4-7) + - name: "hbm4" + addr_range: + - start: 0xA000_0000 + end: 0xA7FF_FFFF + sbr_port_protocol: + - "wide_out" + - name: "hbm5" + addr_range: + - start: 0xA800_0000 + end: 0xAFFF_FFFF + sbr_port_protocol: + - "wide_out" + - name: "hbm6" + addr_range: + - start: 0xB000_0000 + end: 0xB7FF_FFFF + sbr_port_protocol: + - "wide_out" + - name: "hbm7" + addr_range: + - start: 0xB800_0000 + end: 0xBFFF_FFFF + sbr_port_protocol: + - "wide_out" + +routers: + - name: "group_router" + array: [4, 4] + degree: 5 + +connections: + - src: "group" + dst: "group_router" + src_range: + - [0, 3] + - [0, 3] + dst_range: + - [0, 3] + - [0, 3] + dst_dir: "Eject" + # HBM West + - src: "hbm0" + dst: "group_router" + dst_idx: [0, 0] + dst_dir: "West" + - src: "hbm1" + dst: "group_router" + dst_idx: [0, 1] + dst_dir: "West" + - src: "hbm2" + dst: "group_router" + dst_idx: [0, 2] + dst_dir: "West" + - src: "hbm3" + dst: "group_router" + dst_idx: [0, 3] + dst_dir: "West" + # HBM East + - src: "hbm4" + dst: "group_router" + dst_idx: [3, 0] + dst_dir: "East" + - src: "hbm5" + dst: "group_router" + dst_idx: [3, 1] + dst_dir: "East" + - src: "hbm6" + dst: "group_router" + dst_idx: [3, 2] + dst_dir: "East" + - src: "hbm7" + dst: "group_router" + dst_idx: [3, 3] + dst_dir: "East" diff --git a/config/floonoc_cachepool_4g.yml b/config/floonoc_cachepool_4g.yml new file mode 100644 index 0000000..9f06f15 --- /dev/null +++ b/config/floonoc_cachepool_4g.yml @@ -0,0 +1,96 @@ +# Copyright 2025 ETH Zurich and University of Bologna. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name: cachepool +description: "CachePool AXI NoC" +network_type: "axi" + +routing: + route_algo: "SRC" + use_id_table: true + +protocols: + - name: "wide_in" + type: "wide" + protocol: "AXI4" + data_width: 512 + addr_width: 32 + id_width: 7 + user_width: 21 + - name: "wide_out" + type: "wide" + protocol: "AXI4" + data_width: 512 + addr_width: 32 + id_width: 6 + user_width: 21 + +endpoints: + - name: "group" + array: [2, 2] + mgr_port_protocol: + - "wide_in" + # HBM0 also handles peripheral traffic (demuxed at cluster output) + - name: "hbm0" + addr_range: + - start: 0x0000_0000 + end: 0x7FFF_FFFF + - start: 0x8000_0000 + end: 0x8FFF_FFFF + - start: 0xC000_0000 + end: 0xFFFF_FFFF + sbr_port_protocol: + - "wide_out" + - name: "hbm1" + addr_range: + - start: 0x9000_0000 + end: 0x9FFF_FFFF + sbr_port_protocol: + - "wide_out" + - name: "hbm2" + addr_range: + - start: 0xA000_0000 + end: 0xAFFF_FFFF + sbr_port_protocol: + - "wide_out" + - name: "hbm3" + addr_range: + - start: 0xB000_0000 + end: 0xBFFF_FFFF + sbr_port_protocol: + - "wide_out" + +routers: + - name: "group_router" + array: [2, 2] + degree: 5 + +connections: + - src: "group" + dst: "group_router" + src_range: + - [0, 1] + - [0, 1] + dst_range: + - [0, 1] + - [0, 1] + dst_dir: "Eject" + # HBM West (hbm0 at [0,0], hbm1 at [0,1]) + - src: "hbm0" + dst: "group_router" + dst_idx: [0, 0] + dst_dir: "West" + - src: "hbm1" + dst: "group_router" + dst_idx: [0, 1] + dst_dir: "West" + # HBM East (hbm2 at [1,0], hbm3 at [1,1]) + - src: "hbm2" + dst: "group_router" + dst_idx: [1, 0] + dst_dir: "East" + - src: "hbm3" + dst: "group_router" + dst_idx: [1, 1] + dst_dir: "East" diff --git a/hardware/LICENSE b/hardware/LICENSE new file mode 100644 index 0000000..3a2b87e --- /dev/null +++ b/hardware/LICENSE @@ -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 diff --git a/hardware/bootrom/bootdata.cc b/hardware/bootrom/bootdata.cc deleted file mode 100644 index f96b8ba..0000000 --- a/hardware/bootrom/bootdata.cc +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2021 ETH Zurich and University of Bologna. -// Solderpad Hardware License, Version 0.51, see LICENSE for details. -// SPDX-License-Identifier: SHL-0.51 - -#include - -namespace sim { - -const BootData BOOTDATA = {.boot_addr = 0x1000, - .core_count = 16, - .hartid_base = 0, - .tcdm_start = 0xbffff800, - .tcdm_size = 0x800, - .tcdm_offset = 0x0, - .global_mem_start = 0x80000000, - .global_mem_end = 0xa0000000, - .tile_count = 4}; - -} // namespace sim diff --git a/hardware/bootrom/bootdata_bootrom.cc b/hardware/bootrom/bootdata_bootrom.cc deleted file mode 100644 index d578d55..0000000 --- a/hardware/bootrom/bootdata_bootrom.cc +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2021 ETH Zurich and University of Bologna. -// Solderpad Hardware License, Version 0.51, see LICENSE for details. -// SPDX-License-Identifier: SHL-0.51 - -#include - -// The boot data generated along with the system RTL. -struct BootData { - uint32_t boot_addr; - uint32_t core_count; - uint32_t hartid_base; - uint32_t tcdm_start; - uint32_t tcdm_size; - uint32_t tcdm_offset; - uint64_t global_mem_start; - uint64_t global_mem_end; - uint32_t tile_count; -}; - -extern "C" const BootData BOOTDATA = {.boot_addr = 0x1000, - .core_count = 16, - .hartid_base = 0, - .tcdm_start = 0xbffff800, - .tcdm_size = 0x800, - .tcdm_offset = 0x0, - .global_mem_start = 0x80000000, - .global_mem_end = 0xa0000000, - .tile_count = 4}; diff --git a/hardware/bootrom/bootrom.S b/hardware/bootrom/bootrom.S index 22beca1..20749fb 100644 --- a/hardware/bootrom/bootrom.S +++ b/hardware/bootrom/bootrom.S @@ -3,6 +3,7 @@ # SPDX-License-Identifier: SHL-0.51 #include +#include .global _start .global BOOTDATA @@ -12,24 +13,24 @@ _start: csrw mtvec, t1 csrr a0, mhartid la a1, BOOTDATA - - // Enable MSI interrupt + + // Enable MSI interrupt csrw mie, 0xF // Wait for the wakeup interrupt wfi - // Load the start address of the TCDM - lw t2, 12(a1) - // Load the size of the TCDM - lw t3, 16(a1) - // Final address of the TCDM - add t2, t2, t3 + // Peripheral base = TCDM start + TCDM size. These are chip-wide constants + // fixed by the hardware config (see snrt_bootinfo.h), so we use immediates + // here instead of reading BOOTDATA: every core in every group would + // otherwise serialize on this read through the single shared bootrom. + li t2, (SNRT_BOOT_TCDM_START + SNRT_BOOT_TCDM_SIZE) // Find the entry point address addi t2, t2, CACHEPOOL_PERIPHERAL_CLUSTER_BOOT_CONTROL_REG_OFFSET - // Load the entry point + // Load the entry point. This one is genuinely dynamic (written once by + // the testbench/host per test binary), so it cannot be a constant. lw t2, 0(t2) // Jump to the entry point diff --git a/hardware/bootrom/bootrom.bin b/hardware/bootrom/bootrom.bin deleted file mode 100755 index d4a9322..0000000 Binary files a/hardware/bootrom/bootrom.bin and /dev/null differ diff --git a/hardware/bootrom/bootrom.dump b/hardware/bootrom/bootrom.dump deleted file mode 100644 index dad90e3..0000000 --- a/hardware/bootrom/bootrom.dump +++ /dev/null @@ -1,127 +0,0 @@ - -/scratch2/diyou/cachepool/ManyRVData/hardware/bootrom/bootrom.elf: file format elf32-littleriscv - - -Disassembly of section .text: - -00001000 <_start>: - 1000: 00000317 auipc t1,0x0 - 1004: 07832303 lw t1,120(t1) # 1078 <_GLOBAL_OFFSET_TABLE_+0x4> - 1008: 30531073 csrw mtvec,t1 - 100c: f1402573 csrr a0,mhartid - 1010: 00000597 auipc a1,0x0 - 1014: 06c5a583 lw a1,108(a1) # 107c <_GLOBAL_OFFSET_TABLE_+0x8> - 1018: 3047d073 csrwi mie,15 - 101c: 10500073 wfi - 1020: 00c5a383 lw t2,12(a1) - 1024: 0105ae03 lw t3,16(a1) - 1028: 01c383b3 add t2,t2,t3 - 102c: 02038393 addi t2,t2,32 - 1030: 0003a383 lw t2,0(t2) - 1034: 00038067 jr t2 - -00001038 : - 1038: 10500073 wfi - 103c: ffdff06f j 1038 - -Disassembly of section .rodata: - -00001040 : - 1040: 1000 .2byte 0x1000 - 1042: 0000 .2byte 0x0 - 1044: 0010 .2byte 0x10 - 1046: 0000 .2byte 0x0 - 1048: 0000 .2byte 0x0 - 104a: 0000 .2byte 0x0 - 104c: f800 .2byte 0xf800 - 104e: bfff .2byte 0xbfff - 1050: 0800 .2byte 0x800 - ... - 105a: 8000 .2byte 0x8000 - 105c: 0000 .2byte 0x0 - 105e: 0000 .2byte 0x0 - 1060: 0000 .2byte 0x0 - 1062: a000 .2byte 0xa000 - 1064: 0000 .2byte 0x0 - 1066: 0000 .2byte 0x0 - 1068: 0004 .2byte 0x4 - 106a: 0000 .2byte 0x0 - 106c: 0000 .2byte 0x0 - ... - -Disassembly of section .boot_section: - -00001070 : - 1070: 1038 .2byte 0x1038 - ... - -Disassembly of section .got: - -00001074 <_GLOBAL_OFFSET_TABLE_>: - 1074: 0000 .2byte 0x0 - 1076: 0000 .2byte 0x0 - 1078: 1038 .2byte 0x1038 - 107a: 0000 .2byte 0x0 - 107c: 1040 .2byte 0x1040 - ... - -Disassembly of section .got.plt: - -00001080 <.got.plt>: - 1080: ffff .2byte 0xffff - 1082: ffff .2byte 0xffff - 1084: 0000 .2byte 0x0 - ... - -Disassembly of section .riscv.attributes: - -00000000 <.riscv.attributes>: - 0: 4341 .2byte 0x4341 - 2: 0000 .2byte 0x0 - 4: 7200 .2byte 0x7200 - 6: 7369 .2byte 0x7369 - 8: 01007663 bgeu zero,a6,14 <_start-0xfec> - c: 0039 .2byte 0x39 - e: 0000 .2byte 0x0 - 10: 1004 .2byte 0x1004 - 12: 7205 .2byte 0x7205 - 14: 3376 .2byte 0x3376 - 16: 6932 .2byte 0x6932 - 18: 7032 .2byte 0x7032 - 1a: 5f31 .2byte 0x5f31 - 1c: 326d .2byte 0x326d - 1e: 3070 .2byte 0x3070 - 20: 615f 7032 5f31 .byte 0x5f, 0x61, 0x32, 0x70, 0x31, 0x5f - 26: 3266 .2byte 0x3266 - 28: 3270 .2byte 0x3270 - 2a: 7a5f 6369 7273 .byte 0x5f, 0x7a, 0x69, 0x63, 0x73, 0x72 - 30: 7032 .2byte 0x7032 - 32: 5f30 .2byte 0x5f30 - 34: 697a .2byte 0x697a - 36: 6566 .2byte 0x6566 - 38: 636e .2byte 0x636e - 3a: 6965 .2byte 0x6965 - 3c: 7032 .2byte 0x7032 - 3e: 0030 .2byte 0x30 - 40: 0108 .2byte 0x108 - 42: 0b0a .2byte 0xb0a - -Disassembly of section .comment: - -00000000 <.comment>: - 0: 3a434347 .4byte 0x3a434347 - 4: 2820 .2byte 0x2820 - 6: 736f7263 bgeu t5,s6,72a <_start-0x8d6> - a: 6f6f7473 csrrci s0,0x6f6,30 - e: 2d6c .2byte 0x2d6c - 10: 474e .2byte 0x474e - 12: 3120 .2byte 0x3120 - 14: 322e .2byte 0x322e - 16: 2e35 .2byte 0x2e35 - 18: 2e30 .2byte 0x2e30 - 1a: 3538 .2byte 0x3538 - 1c: 365f 6331 6334 .byte 0x5f, 0x36, 0x31, 0x63, 0x34, 0x63 - 22: 20296163 bltu s2,sp,224 <_start-0xddc> - 26: 2e39 .2byte 0x2e39 - 28: 2e35 .2byte 0x2e35 - 2a: 0030 .2byte 0x30 diff --git a/hardware/bootrom/bootrom.elf b/hardware/bootrom/bootrom.elf deleted file mode 100755 index 8c26b6e..0000000 Binary files a/hardware/bootrom/bootrom.elf and /dev/null differ diff --git a/hardware/bootrom/bootrom.sv b/hardware/bootrom/bootrom.sv deleted file mode 100644 index c3b8995..0000000 --- a/hardware/bootrom/bootrom.sv +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2023 ETH Zurich and University of Bologna. -// Solderpad Hardware License, Version 0.51, see LICENSE for details. -// SPDX-License-Identifier: SHL-0.51 -// -// Description: Automatically generated bootrom -// -// Generated by util/scripts/generate_bootrom.py - -module bootrom #( - /* Automatically generated. DO NOT CHANGE! */ - parameter int unsigned DataWidth = 128, - parameter int unsigned AddrWidth = 32 -) ( - input logic clk_i, - input logic req_i, - input logic [AddrWidth-1:0] addr_i, - output logic [DataWidth-1:0] rdata_o -); - localparam int RomSize = 8; - localparam int AddrBits = RomSize > 1 ? $clog2(RomSize) : 1; - - const logic [RomSize-1:0][DataWidth-1:0] mem = { - 128'h00001040000010380000000000001038, - 128'h000000000000000400000000a0000000, - 128'h00000000800000000000000000000800, - 128'hbffff800000000000000001000001000, - 128'hffdff06f10500073000380670003a383, - 128'h0203839301c383b30105ae0300c5a383, - 128'h105000733047d07306c5a58300000597, - 128'hf1402573305310730783230300000317 - }; - - logic [AddrBits-1:0] addr_q; - - always_ff @(posedge clk_i) begin - if (req_i) begin - addr_q <= addr_i[AddrBits-1+4:4]; - end - end - - // this prevents spurious Xes from propagating into - // the speculative fetch stage of the core - assign rdata_o = (addr_q < RomSize) ? mem[addr_q] : '0; -endmodule diff --git a/hardware/bootrom/snrt_bootinfo.h.tpl b/hardware/bootrom/snrt_bootinfo.h.tpl new file mode 100644 index 0000000..2575149 --- /dev/null +++ b/hardware/bootrom/snrt_bootinfo.h.tpl @@ -0,0 +1,25 @@ +// Copyright 2026 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 +// +// Auto-generated chip-wide boot constants. These are identical for every +// core/tile/group in a given hardware build, so they are baked in at compile +// time instead of being read from the shared bootrom at runtime by every +// core. Regenerated from the cluster config whenever the config changes. +// +// Generated by util/scripts/generate_bootdata.py from this hjson's `cluster` +// and `dram` sections. DO NOT EDIT BY HAND. + +#ifndef SNRT_BOOTINFO_H_ +#define SNRT_BOOTINFO_H_ + +#define SNRT_BOOT_CORE_COUNT ${cfg['cluster']['nr_cores']} +#define SNRT_BOOT_HARTID_BASE ${cfg['cluster']['cluster_base_hartid']} +#define SNRT_BOOT_TCDM_START ${hex(cfg['cluster']['cluster_base_addr'])} +#define SNRT_BOOT_TCDM_SIZE ${hex(cfg['cluster']['tcdm']['size'] * 1024)} +#define SNRT_BOOT_TCDM_OFFSET ${hex(cfg['cluster']['cluster_base_offset'])} +#define SNRT_BOOT_GLOBAL_MEM_START ${hex(cfg['dram']['address'])} +#define SNRT_BOOT_GLOBAL_MEM_END ${hex(cfg['dram']['address'] + cfg['dram']['length'])} +#define SNRT_BOOT_TILE_COUNT ${cfg['cluster']['nr_tiles']} + +#endif // SNRT_BOOTINFO_H_ diff --git a/hardware/cachepool_peripheral/cachepool_peripheral.sv b/hardware/cachepool_peripheral/cachepool_peripheral.sv index 6326cfa..8cde188 100644 --- a/hardware/cachepool_peripheral/cachepool_peripheral.sv +++ b/hardware/cachepool_peripheral/cachepool_peripheral.sv @@ -18,8 +18,6 @@ module cachepool_peripheral parameter type reg_req_t = logic, parameter type reg_rsp_t = logic, parameter type cache_insn_t = logic, - // Nr of course in the cluster - parameter logic [31:0] NrCores = 0, /// Derived parameter *Do not override* parameter type addr_t = logic [AddrWidth-1:0], parameter type spm_size_t = logic [SPMWidth-1:0] @@ -35,7 +33,6 @@ module cachepool_peripheral input addr_t tcdm_end_address_i, output addr_t private_start_addr_o, output logic icache_prefetch_enable_o, - output logic [NrCores-1:0] cl_clint_o, output logic cluster_probe_o, input logic [9:0] cluster_hart_base_id_i, /// For cache xbar dynamic configuration @@ -45,7 +42,8 @@ module cachepool_peripheral output cache_insn_t l1d_insn_o, output logic l1d_insn_valid_o, input logic [NumTiles-1:0] l1d_insn_ready_i, - output logic [NumTiles-1:0] l1d_busy_o + output logic [NumTiles-1:0] l1d_busy_o, + output logic [NumTiles-1:0] barrier_participation_mask_o ); cachepool_peripheral_reg2hw_t reg2hw; @@ -88,8 +86,6 @@ module cachepool_peripheral //////////// L1 DCache //////////// - logic [NumPerfCounters-1:0][47:0] perf_counter_d, perf_counter_q; - logic [31:0] cl_clint_d, cl_clint_q; logic [9:0] l1d_spm_size_d, l1d_spm_size_q; logic [3:0] l1d_private_d, l1d_private_q; addr_t private_start_addr_d, private_start_addr_q; @@ -126,6 +122,26 @@ module cachepool_peripheral assign l1d_private_o = l1d_private_q; assign private_start_addr_o = private_start_addr_q; + // Concatenate all tile-select register words into one wide vector and slice + // to NumTiles. Unused upper bits (for small configs) are optimised away. + localparam int unsigned NumTileSelWords = cachepool_peripheral_reg_pkg::NumTileSelRegs; + logic [NumTileSelWords*32-1:0] tile_sel_raw; + always_comb begin : tile_sel_concat + for (int i = 0; i < NumTileSelWords; i++) begin + tile_sel_raw[i*32 +: 32] = reg2hw.cfg_l1d_tile_sel[i].q; + end + end + + // Concatenate all barrier-participation register words into one wide vector + // and slice to NumTiles. Unused upper bits (for small configs) are optimised away. + logic [NumTileSelWords*32-1:0] barrier_participation_mask_raw; + always_comb begin : barrier_participation_mask_concat + for (int i = 0; i < NumTileSelWords; i++) begin + barrier_participation_mask_raw[i*32 +: 32] = reg2hw.hw_barrier_participation_mask[i].q; + end + end + assign barrier_participation_mask_o = barrier_participation_mask_raw[NumTiles-1:0]; + // Cache Flush Controller // Operates at tile granularity. l1d_lock_q[t] is set when tile t is // issued an instruction and cleared when tile t returns ready. @@ -151,7 +167,7 @@ module cachepool_peripheral // For non-private modes (shared/all/init), tile_sel is forced to '1. l1d_insn_o.insn = reg2hw.cfg_l1d_insn.q; l1d_insn_o.tile_sel = (reg2hw.cfg_l1d_insn.q == 2'b00) - ? reg2hw.cfg_l1d_tile_sel.q[NumTiles-1:0] + ? tile_sel_raw[NumTiles-1:0] : {NumTiles{1'b1}}; l1d_insn_valid_o = 1'b1; // Lock only the tiles that will receive the instruction. @@ -172,25 +188,12 @@ module cachepool_peripheral end `FF(private_start_addr_q, private_start_addr_d, 32'hA000_0000, clk_i, rst_ni) - `FF(l1d_private_q, l1d_private_d, '0, clk_i, rst_ni) + `FF(l1d_private_q, l1d_private_d, 0, clk_i, rst_ni) `FF(l1d_lock_q, l1d_lock_d, '0, clk_i, rst_ni) // To show if the current flush/invalidation is complete assign hw2reg.l1d_flush_status.d = (l1d_lock_q != '0); // assign l1d_busy_o = (l1d_lock_q != '0); - // Wake-up logic: Bits in cl_clint_q can be set/cleared with writes to - // cl_clint_set/cl_clint_clear - always_comb begin - cl_clint_d = cl_clint_q; - if (reg2hw.cl_clint_set.qe) begin - cl_clint_d = cl_clint_q | reg2hw.cl_clint_set.q; - end else if (reg2hw.cl_clint_clear.qe) begin - cl_clint_d = cl_clint_q & ~reg2hw.cl_clint_clear.q; - end - end - `FF(cl_clint_q, cl_clint_d, '0, clk_i, rst_ni) - assign cl_clint_o = cl_clint_q[NrCores-1:0]; - // Enable icache prefetch assign icache_prefetch_enable_o = reg2hw.icache_prefetch_enable.q; diff --git a/hardware/cachepool_peripheral/cachepool_peripheral_reg.hjson b/hardware/cachepool_peripheral/cachepool_peripheral_reg.hjson index 79d7cda..02aadb6 100644 --- a/hardware/cachepool_peripheral/cachepool_peripheral_reg.hjson +++ b/hardware/cachepool_peripheral/cachepool_peripheral_reg.hjson @@ -4,8 +4,8 @@ // Licensed under Solderpad Hardware License, Version 0.51, see LICENSE for details. { param_list: [ - { name: "NumPerfCounters", - desc: "Number of performance counters", + { name: "NumTileSelRegs", + desc: "Number of 32-bit tile-select registers. Default 2 covers up to 64 tiles (16 groups x 4 tiles).", type: "int", default: "2" }, @@ -17,59 +17,6 @@ ] regwidth: 32, registers: [ - { - multireg: { - name: "HART_SELECT", - desc: '''Select from which hart in the cluster, starting from `0`, - the event should be counted. For each performance counter - the cores can be selected individually. If a hart greater - than the clusters total hart size is selected the selection - will wrap and the hart corresponding to `hart_select % total_harts_in_cluster` - will be selected.''' - swaccess: "rw", - hwaccess: "hro", - count: "NumPerfCounters", - cname: "hart_select", - compact: "false", - fields: [{ - bits: "9:0", - name: "HART_SELECT", - desc: "Select source of per-hart performance counter" - }] - } - }, - { - name: "CL_CLINT_SET", - desc: ''' - Set bits in the cluster-local CLINT. Writing a 1 at location i sets the cluster-local interrupt - of hart i, where i is relative to the first hart in the cluster, ignoring the cluster base hart ID. - ''' - hwext: "true", - hwqe: "true", - swaccess: "wo", - hwaccess: "hro", - fields: [{ - bits: "31:0", - name: "CL_CLINT_SET", - desc: "Set cluster-local interrupt of hart i" - }] - }, - { - name: "CL_CLINT_CLEAR", - desc: ''' - Clear bits in the cluster-local CLINT. Writing a 1 at location i clears the cluster-local interrupt - of hart i, where i is relative to the first hart in the cluster, ignoring the cluster base hart ID. - ''' - hwext: "true", - hwqe: "true", - swaccess: "wo", - hwaccess: "hro", - fields: [{ - bits: "31:0", - name: "CL_CLINT_CLEAR", - desc: "Clear cluster-local interrupt of hart i" - }] - }, { name: "HW_BARRIER", desc: '''Hardware barrier register. Loads to this register will block until all cores have @@ -173,18 +120,43 @@ }] }, { - name: "CFG_L1D_TILE_SEL", - desc: '''Controls Tiles to be flushed''' - // hwext: "true", - // hwqe: "true", - swaccess: "rw", - hwaccess: "hro", - resval: "0", - fields: [{ - bits: "31:0", - name: "TILE", - desc: "Select the tile to be flush, only used in private partition flush." - }] + multireg: { + name: "CFG_L1D_TILE_SEL", + desc: '''One-hot tile selection mask for private-partition flush. + Each 32-bit register covers 32 tiles: register 0 → tiles 0-31, + register 1 → tiles 32-63. Only used when CFG_L1D_INSN == 2'b00.''' + swaccess: "rw", + hwaccess: "hro", + count: "NumTileSelRegs", + cname: "cfg_l1d_tile_sel", + compact: "false", + resval: "0", + fields: [{ + bits: "31:0", + name: "TILE", + desc: "Tile select bits for this 32-tile bank." + }] + } + }, + { + multireg: { + name: "HW_BARRIER_PARTICIPATION_MASK", + desc: '''Tile participation mask for the cluster-level hardware barrier. + Each 32-bit register covers 32 tiles: register 0 -> tiles 0-31, + register 1 -> tiles 32-63. A tile whose bit is clear is excluded + from the next hardware barrier round.''' + swaccess: "rw", + hwaccess: "hro", + count: "NumTileSelRegs", + cname: "hw_barrier_participation_mask", + compact: "false", + resval: "0xFFFFFFFF", + fields: [{ + bits: "31:0", + name: "TILE", + desc: "Tile participation bits for this 32-tile bank." + }] + } }, { name: "L1D_SPM_COMMIT", diff --git a/hardware/cachepool_peripheral/cachepool_peripheral_reg_pkg.sv b/hardware/cachepool_peripheral/cachepool_peripheral_reg_pkg.sv index df8c6e6..52b2d3b 100644 --- a/hardware/cachepool_peripheral/cachepool_peripheral_reg_pkg.sv +++ b/hardware/cachepool_peripheral/cachepool_peripheral_reg_pkg.sv @@ -7,7 +7,7 @@ package cachepool_peripheral_reg_pkg; // Param list - parameter int NumPerfCounters = 2; + parameter int NumTileSelRegs = 2; // Address widths within the block parameter int BlockAw = 7; @@ -16,20 +16,6 @@ package cachepool_peripheral_reg_pkg; // Typedefs for registers // //////////////////////////// - typedef struct packed { - logic [9:0] q; - } cachepool_peripheral_reg2hw_hart_select_mreg_t; - - typedef struct packed { - logic [31:0] q; - logic qe; - } cachepool_peripheral_reg2hw_cl_clint_set_reg_t; - - typedef struct packed { - logic [31:0] q; - logic qe; - } cachepool_peripheral_reg2hw_cl_clint_clear_reg_t; - typedef struct packed { logic [31:0] q; } cachepool_peripheral_reg2hw_hw_barrier_reg_t; @@ -64,7 +50,11 @@ package cachepool_peripheral_reg_pkg; typedef struct packed { logic [31:0] q; - } cachepool_peripheral_reg2hw_cfg_l1d_tile_sel_reg_t; + } cachepool_peripheral_reg2hw_cfg_l1d_tile_sel_mreg_t; + + typedef struct packed { + logic [31:0] q; + } cachepool_peripheral_reg2hw_hw_barrier_participation_mask_mreg_t; typedef struct packed { logic q; @@ -115,18 +105,16 @@ package cachepool_peripheral_reg_pkg; // Register -> HW type typedef struct packed { - cachepool_peripheral_reg2hw_hart_select_mreg_t [1:0] hart_select; // [275:256] - cachepool_peripheral_reg2hw_cl_clint_set_reg_t cl_clint_set; // [255:223] - cachepool_peripheral_reg2hw_cl_clint_clear_reg_t cl_clint_clear; // [222:190] - cachepool_peripheral_reg2hw_hw_barrier_reg_t hw_barrier; // [189:158] - cachepool_peripheral_reg2hw_icache_prefetch_enable_reg_t icache_prefetch_enable; // [157:157] - cachepool_peripheral_reg2hw_spatz_status_reg_t spatz_status; // [156:156] - cachepool_peripheral_reg2hw_spatz_cycle_reg_t spatz_cycle; // [155:124] - cachepool_peripheral_reg2hw_cluster_boot_control_reg_t cluster_boot_control; // [123:92] - cachepool_peripheral_reg2hw_cluster_eoc_exit_reg_t cluster_eoc_exit; // [91:88] - cachepool_peripheral_reg2hw_cfg_l1d_spm_reg_t cfg_l1d_spm; // [87:78] - cachepool_peripheral_reg2hw_cfg_l1d_insn_reg_t cfg_l1d_insn; // [77:76] - cachepool_peripheral_reg2hw_cfg_l1d_tile_sel_reg_t cfg_l1d_tile_sel; // [75:44] + cachepool_peripheral_reg2hw_hw_barrier_reg_t hw_barrier; // [285:254] + cachepool_peripheral_reg2hw_icache_prefetch_enable_reg_t icache_prefetch_enable; // [253:253] + cachepool_peripheral_reg2hw_spatz_status_reg_t spatz_status; // [252:252] + cachepool_peripheral_reg2hw_spatz_cycle_reg_t spatz_cycle; // [251:220] + cachepool_peripheral_reg2hw_cluster_boot_control_reg_t cluster_boot_control; // [219:188] + cachepool_peripheral_reg2hw_cluster_eoc_exit_reg_t cluster_eoc_exit; // [187:184] + cachepool_peripheral_reg2hw_cfg_l1d_spm_reg_t cfg_l1d_spm; // [183:174] + cachepool_peripheral_reg2hw_cfg_l1d_insn_reg_t cfg_l1d_insn; // [173:172] + cachepool_peripheral_reg2hw_cfg_l1d_tile_sel_mreg_t [1:0] cfg_l1d_tile_sel; // [171:108] + cachepool_peripheral_reg2hw_hw_barrier_participation_mask_mreg_t [1:0] hw_barrier_participation_mask; // [107:44] cachepool_peripheral_reg2hw_l1d_spm_commit_reg_t l1d_spm_commit; // [43:43] cachepool_peripheral_reg2hw_l1d_insn_commit_reg_t l1d_insn_commit; // [42:42] cachepool_peripheral_reg2hw_l1d_private_reg_t l1d_private; // [41:38] @@ -145,40 +133,33 @@ package cachepool_peripheral_reg_pkg; } cachepool_peripheral_hw2reg_t; // Register offsets - parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_HART_SELECT_0_OFFSET = 7'h 0; - parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_HART_SELECT_1_OFFSET = 7'h 4; - parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_CL_CLINT_SET_OFFSET = 7'h 8; - parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_CL_CLINT_CLEAR_OFFSET = 7'h c; - parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_HW_BARRIER_OFFSET = 7'h 10; - parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_ICACHE_PREFETCH_ENABLE_OFFSET = 7'h 14; - parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_SPATZ_STATUS_OFFSET = 7'h 18; - parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_SPATZ_CYCLE_OFFSET = 7'h 1c; - parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_CLUSTER_BOOT_CONTROL_OFFSET = 7'h 20; - parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_CLUSTER_EOC_EXIT_OFFSET = 7'h 24; - parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_CFG_L1D_SPM_OFFSET = 7'h 28; - parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_CFG_L1D_INSN_OFFSET = 7'h 2c; - parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_CFG_L1D_TILE_SEL_OFFSET = 7'h 30; - parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_L1D_SPM_COMMIT_OFFSET = 7'h 34; - parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_L1D_INSN_COMMIT_OFFSET = 7'h 38; - parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_L1D_FLUSH_STATUS_OFFSET = 7'h 3c; - parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_L1D_PRIVATE_OFFSET = 7'h 40; - parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_L1D_ADDR_OFFSET = 7'h 44; - parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_XBAR_OFFSET_OFFSET = 7'h 48; - parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_XBAR_OFFSET_COMMIT_OFFSET = 7'h 4c; + parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_HW_BARRIER_OFFSET = 7'h 0; + parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_ICACHE_PREFETCH_ENABLE_OFFSET = 7'h 4; + parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_SPATZ_STATUS_OFFSET = 7'h 8; + parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_SPATZ_CYCLE_OFFSET = 7'h c; + parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_CLUSTER_BOOT_CONTROL_OFFSET = 7'h 10; + parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_CLUSTER_EOC_EXIT_OFFSET = 7'h 14; + parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_CFG_L1D_SPM_OFFSET = 7'h 18; + parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_CFG_L1D_INSN_OFFSET = 7'h 1c; + parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_CFG_L1D_TILE_SEL_0_OFFSET = 7'h 20; + parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_CFG_L1D_TILE_SEL_1_OFFSET = 7'h 24; + parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_HW_BARRIER_PARTICIPATION_MASK_0_OFFSET = 7'h 28; + parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_HW_BARRIER_PARTICIPATION_MASK_1_OFFSET = 7'h 2c; + parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_L1D_SPM_COMMIT_OFFSET = 7'h 30; + parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_L1D_INSN_COMMIT_OFFSET = 7'h 34; + parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_L1D_FLUSH_STATUS_OFFSET = 7'h 38; + parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_L1D_PRIVATE_OFFSET = 7'h 3c; + parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_L1D_ADDR_OFFSET = 7'h 40; + parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_XBAR_OFFSET_OFFSET = 7'h 44; + parameter logic [BlockAw-1:0] CACHEPOOL_PERIPHERAL_XBAR_OFFSET_COMMIT_OFFSET = 7'h 48; // Reset values for hwext registers and their fields - parameter logic [31:0] CACHEPOOL_PERIPHERAL_CL_CLINT_SET_RESVAL = 32'h 0; - parameter logic [31:0] CACHEPOOL_PERIPHERAL_CL_CLINT_CLEAR_RESVAL = 32'h 0; parameter logic [31:0] CACHEPOOL_PERIPHERAL_HW_BARRIER_RESVAL = 32'h 0; parameter logic [0:0] CACHEPOOL_PERIPHERAL_L1D_FLUSH_STATUS_RESVAL = 1'h 0; parameter logic [0:0] CACHEPOOL_PERIPHERAL_L1D_FLUSH_STATUS_STATUS_RESVAL = 1'h 0; // Register index typedef enum int { - CACHEPOOL_PERIPHERAL_HART_SELECT_0, - CACHEPOOL_PERIPHERAL_HART_SELECT_1, - CACHEPOOL_PERIPHERAL_CL_CLINT_SET, - CACHEPOOL_PERIPHERAL_CL_CLINT_CLEAR, CACHEPOOL_PERIPHERAL_HW_BARRIER, CACHEPOOL_PERIPHERAL_ICACHE_PREFETCH_ENABLE, CACHEPOOL_PERIPHERAL_SPATZ_STATUS, @@ -187,7 +168,10 @@ package cachepool_peripheral_reg_pkg; CACHEPOOL_PERIPHERAL_CLUSTER_EOC_EXIT, CACHEPOOL_PERIPHERAL_CFG_L1D_SPM, CACHEPOOL_PERIPHERAL_CFG_L1D_INSN, - CACHEPOOL_PERIPHERAL_CFG_L1D_TILE_SEL, + CACHEPOOL_PERIPHERAL_CFG_L1D_TILE_SEL_0, + CACHEPOOL_PERIPHERAL_CFG_L1D_TILE_SEL_1, + CACHEPOOL_PERIPHERAL_HW_BARRIER_PARTICIPATION_MASK_0, + CACHEPOOL_PERIPHERAL_HW_BARRIER_PARTICIPATION_MASK_1, CACHEPOOL_PERIPHERAL_L1D_SPM_COMMIT, CACHEPOOL_PERIPHERAL_L1D_INSN_COMMIT, CACHEPOOL_PERIPHERAL_L1D_FLUSH_STATUS, @@ -198,27 +182,26 @@ package cachepool_peripheral_reg_pkg; } cachepool_peripheral_id_e; // Register width information to check illegal writes - parameter logic [3:0] CACHEPOOL_PERIPHERAL_PERMIT [20] = '{ - 4'b 0011, // index[ 0] CACHEPOOL_PERIPHERAL_HART_SELECT_0 - 4'b 0011, // index[ 1] CACHEPOOL_PERIPHERAL_HART_SELECT_1 - 4'b 1111, // index[ 2] CACHEPOOL_PERIPHERAL_CL_CLINT_SET - 4'b 1111, // index[ 3] CACHEPOOL_PERIPHERAL_CL_CLINT_CLEAR - 4'b 1111, // index[ 4] CACHEPOOL_PERIPHERAL_HW_BARRIER - 4'b 0001, // index[ 5] CACHEPOOL_PERIPHERAL_ICACHE_PREFETCH_ENABLE - 4'b 0001, // index[ 6] CACHEPOOL_PERIPHERAL_SPATZ_STATUS - 4'b 1111, // index[ 7] CACHEPOOL_PERIPHERAL_SPATZ_CYCLE - 4'b 1111, // index[ 8] CACHEPOOL_PERIPHERAL_CLUSTER_BOOT_CONTROL - 4'b 0001, // index[ 9] CACHEPOOL_PERIPHERAL_CLUSTER_EOC_EXIT - 4'b 0011, // index[10] CACHEPOOL_PERIPHERAL_CFG_L1D_SPM - 4'b 0001, // index[11] CACHEPOOL_PERIPHERAL_CFG_L1D_INSN - 4'b 1111, // index[12] CACHEPOOL_PERIPHERAL_CFG_L1D_TILE_SEL - 4'b 0001, // index[13] CACHEPOOL_PERIPHERAL_L1D_SPM_COMMIT - 4'b 0001, // index[14] CACHEPOOL_PERIPHERAL_L1D_INSN_COMMIT - 4'b 0001, // index[15] CACHEPOOL_PERIPHERAL_L1D_FLUSH_STATUS - 4'b 0001, // index[16] CACHEPOOL_PERIPHERAL_L1D_PRIVATE - 4'b 1111, // index[17] CACHEPOOL_PERIPHERAL_L1D_ADDR - 4'b 0001, // index[18] CACHEPOOL_PERIPHERAL_XBAR_OFFSET - 4'b 0001 // index[19] CACHEPOOL_PERIPHERAL_XBAR_OFFSET_COMMIT + parameter logic [3:0] CACHEPOOL_PERIPHERAL_PERMIT [19] = '{ + 4'b 1111, // index[ 0] CACHEPOOL_PERIPHERAL_HW_BARRIER + 4'b 0001, // index[ 1] CACHEPOOL_PERIPHERAL_ICACHE_PREFETCH_ENABLE + 4'b 0001, // index[ 2] CACHEPOOL_PERIPHERAL_SPATZ_STATUS + 4'b 1111, // index[ 3] CACHEPOOL_PERIPHERAL_SPATZ_CYCLE + 4'b 1111, // index[ 4] CACHEPOOL_PERIPHERAL_CLUSTER_BOOT_CONTROL + 4'b 0001, // index[ 5] CACHEPOOL_PERIPHERAL_CLUSTER_EOC_EXIT + 4'b 0011, // index[ 6] CACHEPOOL_PERIPHERAL_CFG_L1D_SPM + 4'b 0001, // index[ 7] CACHEPOOL_PERIPHERAL_CFG_L1D_INSN + 4'b 1111, // index[ 8] CACHEPOOL_PERIPHERAL_CFG_L1D_TILE_SEL_0 + 4'b 1111, // index[ 9] CACHEPOOL_PERIPHERAL_CFG_L1D_TILE_SEL_1 + 4'b 1111, // index[10] CACHEPOOL_PERIPHERAL_HW_BARRIER_PARTICIPATION_MASK_0 + 4'b 1111, // index[11] CACHEPOOL_PERIPHERAL_HW_BARRIER_PARTICIPATION_MASK_1 + 4'b 0001, // index[12] CACHEPOOL_PERIPHERAL_L1D_SPM_COMMIT + 4'b 0001, // index[13] CACHEPOOL_PERIPHERAL_L1D_INSN_COMMIT + 4'b 0001, // index[14] CACHEPOOL_PERIPHERAL_L1D_FLUSH_STATUS + 4'b 0001, // index[15] CACHEPOOL_PERIPHERAL_L1D_PRIVATE + 4'b 1111, // index[16] CACHEPOOL_PERIPHERAL_L1D_ADDR + 4'b 0001, // index[17] CACHEPOOL_PERIPHERAL_XBAR_OFFSET + 4'b 0001 // index[18] CACHEPOOL_PERIPHERAL_XBAR_OFFSET_COMMIT }; endpackage diff --git a/hardware/cachepool_peripheral/cachepool_peripheral_reg_top.sv b/hardware/cachepool_peripheral/cachepool_peripheral_reg_top.sv index c6ece73..8552d9e 100644 --- a/hardware/cachepool_peripheral/cachepool_peripheral_reg_top.sv +++ b/hardware/cachepool_peripheral/cachepool_peripheral_reg_top.sv @@ -68,16 +68,6 @@ module cachepool_peripheral_reg_top #( // Define SW related signals // Format: __{wd|we|qs} // or _{wd|we|qs} if field == 1 or 0 - logic [9:0] hart_select_0_qs; - logic [9:0] hart_select_0_wd; - logic hart_select_0_we; - logic [9:0] hart_select_1_qs; - logic [9:0] hart_select_1_wd; - logic hart_select_1_we; - logic [31:0] cl_clint_set_wd; - logic cl_clint_set_we; - logic [31:0] cl_clint_clear_wd; - logic cl_clint_clear_we; logic [31:0] hw_barrier_qs; logic hw_barrier_re; logic icache_prefetch_enable_wd; @@ -99,9 +89,18 @@ module cachepool_peripheral_reg_top #( logic [1:0] cfg_l1d_insn_qs; logic [1:0] cfg_l1d_insn_wd; logic cfg_l1d_insn_we; - logic [31:0] cfg_l1d_tile_sel_qs; - logic [31:0] cfg_l1d_tile_sel_wd; - logic cfg_l1d_tile_sel_we; + logic [31:0] cfg_l1d_tile_sel_0_qs; + logic [31:0] cfg_l1d_tile_sel_0_wd; + logic cfg_l1d_tile_sel_0_we; + logic [31:0] cfg_l1d_tile_sel_1_qs; + logic [31:0] cfg_l1d_tile_sel_1_wd; + logic cfg_l1d_tile_sel_1_we; + logic [31:0] hw_barrier_participation_mask_0_qs; + logic [31:0] hw_barrier_participation_mask_0_wd; + logic hw_barrier_participation_mask_0_we; + logic [31:0] hw_barrier_participation_mask_1_qs; + logic [31:0] hw_barrier_participation_mask_1_wd; + logic hw_barrier_participation_mask_1_we; logic l1d_spm_commit_qs; logic l1d_spm_commit_wd; logic l1d_spm_commit_we; @@ -124,94 +123,6 @@ module cachepool_peripheral_reg_top #( logic xbar_offset_commit_we; // Register instances - - // Subregister 0 of Multireg hart_select - // R[hart_select_0]: V(False) - - prim_subreg #( - .DW (10), - .SWACCESS("RW"), - .RESVAL (10'h0) - ) u_hart_select_0 ( - .clk_i (clk_i ), - .rst_ni (rst_ni ), - - // from register interface - .we (hart_select_0_we), - .wd (hart_select_0_wd), - - // from internal hardware - .de (1'b0), - .d ('0 ), - - // to internal hardware - .qe (), - .q (reg2hw.hart_select[0].q ), - - // to register interface (read) - .qs (hart_select_0_qs) - ); - - // Subregister 1 of Multireg hart_select - // R[hart_select_1]: V(False) - - prim_subreg #( - .DW (10), - .SWACCESS("RW"), - .RESVAL (10'h0) - ) u_hart_select_1 ( - .clk_i (clk_i ), - .rst_ni (rst_ni ), - - // from register interface - .we (hart_select_1_we), - .wd (hart_select_1_wd), - - // from internal hardware - .de (1'b0), - .d ('0 ), - - // to internal hardware - .qe (), - .q (reg2hw.hart_select[1].q ), - - // to register interface (read) - .qs (hart_select_1_qs) - ); - - - // R[cl_clint_set]: V(True) - - prim_subreg_ext #( - .DW (32) - ) u_cl_clint_set ( - .re (1'b0), - .we (cl_clint_set_we), - .wd (cl_clint_set_wd), - .d ('0), - .qre (), - .qe (reg2hw.cl_clint_set.qe), - .q (reg2hw.cl_clint_set.q ), - .qs () - ); - - - // R[cl_clint_clear]: V(True) - - prim_subreg_ext #( - .DW (32) - ) u_cl_clint_clear ( - .re (1'b0), - .we (cl_clint_clear_we), - .wd (cl_clint_clear_wd), - .d ('0), - .qre (), - .qe (reg2hw.cl_clint_clear.qe), - .q (reg2hw.cl_clint_clear.q ), - .qs () - ); - - // R[hw_barrier]: V(True) prim_subreg_ext #( @@ -415,19 +326,48 @@ module cachepool_peripheral_reg_top #( ); - // R[cfg_l1d_tile_sel]: V(False) + + // Subregister 0 of Multireg cfg_l1d_tile_sel + // R[cfg_l1d_tile_sel_0]: V(False) + + prim_subreg #( + .DW (32), + .SWACCESS("RW"), + .RESVAL (32'h0) + ) u_cfg_l1d_tile_sel_0 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (cfg_l1d_tile_sel_0_we), + .wd (cfg_l1d_tile_sel_0_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.cfg_l1d_tile_sel[0].q ), + + // to register interface (read) + .qs (cfg_l1d_tile_sel_0_qs) + ); + + // Subregister 1 of Multireg cfg_l1d_tile_sel + // R[cfg_l1d_tile_sel_1]: V(False) prim_subreg #( .DW (32), .SWACCESS("RW"), .RESVAL (32'h0) - ) u_cfg_l1d_tile_sel ( + ) u_cfg_l1d_tile_sel_1 ( .clk_i (clk_i ), .rst_ni (rst_ni ), // from register interface - .we (cfg_l1d_tile_sel_we), - .wd (cfg_l1d_tile_sel_wd), + .we (cfg_l1d_tile_sel_1_we), + .wd (cfg_l1d_tile_sel_1_wd), // from internal hardware .de (1'b0), @@ -435,10 +375,66 @@ module cachepool_peripheral_reg_top #( // to internal hardware .qe (), - .q (reg2hw.cfg_l1d_tile_sel.q ), + .q (reg2hw.cfg_l1d_tile_sel[1].q ), // to register interface (read) - .qs (cfg_l1d_tile_sel_qs) + .qs (cfg_l1d_tile_sel_1_qs) + ); + + + + // Subregister 0 of Multireg hw_barrier_participation_mask + // R[hw_barrier_participation_mask_0]: V(False) + + prim_subreg #( + .DW (32), + .SWACCESS("RW"), + .RESVAL (32'hffffffff) + ) u_hw_barrier_participation_mask_0 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (hw_barrier_participation_mask_0_we), + .wd (hw_barrier_participation_mask_0_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.hw_barrier_participation_mask[0].q ), + + // to register interface (read) + .qs (hw_barrier_participation_mask_0_qs) + ); + + // Subregister 1 of Multireg hw_barrier_participation_mask + // R[hw_barrier_participation_mask_1]: V(False) + + prim_subreg #( + .DW (32), + .SWACCESS("RW"), + .RESVAL (32'hffffffff) + ) u_hw_barrier_participation_mask_1 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (hw_barrier_participation_mask_1_we), + .wd (hw_barrier_participation_mask_1_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.hw_barrier_participation_mask[1].q ), + + // to register interface (read) + .qs (hw_barrier_participation_mask_1_qs) ); @@ -622,29 +618,28 @@ module cachepool_peripheral_reg_top #( - logic [19:0] addr_hit; + logic [18:0] addr_hit; always_comb begin addr_hit = '0; - addr_hit[ 0] = (reg_addr == CACHEPOOL_PERIPHERAL_HART_SELECT_0_OFFSET); - addr_hit[ 1] = (reg_addr == CACHEPOOL_PERIPHERAL_HART_SELECT_1_OFFSET); - addr_hit[ 2] = (reg_addr == CACHEPOOL_PERIPHERAL_CL_CLINT_SET_OFFSET); - addr_hit[ 3] = (reg_addr == CACHEPOOL_PERIPHERAL_CL_CLINT_CLEAR_OFFSET); - addr_hit[ 4] = (reg_addr == CACHEPOOL_PERIPHERAL_HW_BARRIER_OFFSET); - addr_hit[ 5] = (reg_addr == CACHEPOOL_PERIPHERAL_ICACHE_PREFETCH_ENABLE_OFFSET); - addr_hit[ 6] = (reg_addr == CACHEPOOL_PERIPHERAL_SPATZ_STATUS_OFFSET); - addr_hit[ 7] = (reg_addr == CACHEPOOL_PERIPHERAL_SPATZ_CYCLE_OFFSET); - addr_hit[ 8] = (reg_addr == CACHEPOOL_PERIPHERAL_CLUSTER_BOOT_CONTROL_OFFSET); - addr_hit[ 9] = (reg_addr == CACHEPOOL_PERIPHERAL_CLUSTER_EOC_EXIT_OFFSET); - addr_hit[10] = (reg_addr == CACHEPOOL_PERIPHERAL_CFG_L1D_SPM_OFFSET); - addr_hit[11] = (reg_addr == CACHEPOOL_PERIPHERAL_CFG_L1D_INSN_OFFSET); - addr_hit[12] = (reg_addr == CACHEPOOL_PERIPHERAL_CFG_L1D_TILE_SEL_OFFSET); - addr_hit[13] = (reg_addr == CACHEPOOL_PERIPHERAL_L1D_SPM_COMMIT_OFFSET); - addr_hit[14] = (reg_addr == CACHEPOOL_PERIPHERAL_L1D_INSN_COMMIT_OFFSET); - addr_hit[15] = (reg_addr == CACHEPOOL_PERIPHERAL_L1D_FLUSH_STATUS_OFFSET); - addr_hit[16] = (reg_addr == CACHEPOOL_PERIPHERAL_L1D_PRIVATE_OFFSET); - addr_hit[17] = (reg_addr == CACHEPOOL_PERIPHERAL_L1D_ADDR_OFFSET); - addr_hit[18] = (reg_addr == CACHEPOOL_PERIPHERAL_XBAR_OFFSET_OFFSET); - addr_hit[19] = (reg_addr == CACHEPOOL_PERIPHERAL_XBAR_OFFSET_COMMIT_OFFSET); + addr_hit[ 0] = (reg_addr == CACHEPOOL_PERIPHERAL_HW_BARRIER_OFFSET); + addr_hit[ 1] = (reg_addr == CACHEPOOL_PERIPHERAL_ICACHE_PREFETCH_ENABLE_OFFSET); + addr_hit[ 2] = (reg_addr == CACHEPOOL_PERIPHERAL_SPATZ_STATUS_OFFSET); + addr_hit[ 3] = (reg_addr == CACHEPOOL_PERIPHERAL_SPATZ_CYCLE_OFFSET); + addr_hit[ 4] = (reg_addr == CACHEPOOL_PERIPHERAL_CLUSTER_BOOT_CONTROL_OFFSET); + addr_hit[ 5] = (reg_addr == CACHEPOOL_PERIPHERAL_CLUSTER_EOC_EXIT_OFFSET); + addr_hit[ 6] = (reg_addr == CACHEPOOL_PERIPHERAL_CFG_L1D_SPM_OFFSET); + addr_hit[ 7] = (reg_addr == CACHEPOOL_PERIPHERAL_CFG_L1D_INSN_OFFSET); + addr_hit[ 8] = (reg_addr == CACHEPOOL_PERIPHERAL_CFG_L1D_TILE_SEL_0_OFFSET); + addr_hit[ 9] = (reg_addr == CACHEPOOL_PERIPHERAL_CFG_L1D_TILE_SEL_1_OFFSET); + addr_hit[10] = (reg_addr == CACHEPOOL_PERIPHERAL_HW_BARRIER_PARTICIPATION_MASK_0_OFFSET); + addr_hit[11] = (reg_addr == CACHEPOOL_PERIPHERAL_HW_BARRIER_PARTICIPATION_MASK_1_OFFSET); + addr_hit[12] = (reg_addr == CACHEPOOL_PERIPHERAL_L1D_SPM_COMMIT_OFFSET); + addr_hit[13] = (reg_addr == CACHEPOOL_PERIPHERAL_L1D_INSN_COMMIT_OFFSET); + addr_hit[14] = (reg_addr == CACHEPOOL_PERIPHERAL_L1D_FLUSH_STATUS_OFFSET); + addr_hit[15] = (reg_addr == CACHEPOOL_PERIPHERAL_L1D_PRIVATE_OFFSET); + addr_hit[16] = (reg_addr == CACHEPOOL_PERIPHERAL_L1D_ADDR_OFFSET); + addr_hit[17] = (reg_addr == CACHEPOOL_PERIPHERAL_XBAR_OFFSET_OFFSET); + addr_hit[18] = (reg_addr == CACHEPOOL_PERIPHERAL_XBAR_OFFSET_COMMIT_OFFSET); end assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0 ; @@ -670,66 +665,62 @@ module cachepool_peripheral_reg_top #( (addr_hit[15] & (|(CACHEPOOL_PERIPHERAL_PERMIT[15] & ~reg_be))) | (addr_hit[16] & (|(CACHEPOOL_PERIPHERAL_PERMIT[16] & ~reg_be))) | (addr_hit[17] & (|(CACHEPOOL_PERIPHERAL_PERMIT[17] & ~reg_be))) | - (addr_hit[18] & (|(CACHEPOOL_PERIPHERAL_PERMIT[18] & ~reg_be))) | - (addr_hit[19] & (|(CACHEPOOL_PERIPHERAL_PERMIT[19] & ~reg_be))))); + (addr_hit[18] & (|(CACHEPOOL_PERIPHERAL_PERMIT[18] & ~reg_be))))); end - assign hart_select_0_we = addr_hit[0] & reg_we & !reg_error; - assign hart_select_0_wd = reg_wdata[9:0]; - - assign hart_select_1_we = addr_hit[1] & reg_we & !reg_error; - assign hart_select_1_wd = reg_wdata[9:0]; - - assign cl_clint_set_we = addr_hit[2] & reg_we & !reg_error; - assign cl_clint_set_wd = reg_wdata[31:0]; - - assign cl_clint_clear_we = addr_hit[3] & reg_we & !reg_error; - assign cl_clint_clear_wd = reg_wdata[31:0]; - - assign hw_barrier_re = addr_hit[4] & reg_re & !reg_error; + assign hw_barrier_re = addr_hit[0] & reg_re & !reg_error; - assign icache_prefetch_enable_we = addr_hit[5] & reg_we & !reg_error; + assign icache_prefetch_enable_we = addr_hit[1] & reg_we & !reg_error; assign icache_prefetch_enable_wd = reg_wdata[0]; - assign spatz_status_we = addr_hit[6] & reg_we & !reg_error; + assign spatz_status_we = addr_hit[2] & reg_we & !reg_error; assign spatz_status_wd = reg_wdata[0]; - assign spatz_cycle_we = addr_hit[7] & reg_we & !reg_error; + assign spatz_cycle_we = addr_hit[3] & reg_we & !reg_error; assign spatz_cycle_wd = reg_wdata[31:0]; - assign cluster_boot_control_we = addr_hit[8] & reg_we & !reg_error; + assign cluster_boot_control_we = addr_hit[4] & reg_we & !reg_error; assign cluster_boot_control_wd = reg_wdata[31:0]; - assign cluster_eoc_exit_we = addr_hit[9] & reg_we & !reg_error; + assign cluster_eoc_exit_we = addr_hit[5] & reg_we & !reg_error; assign cluster_eoc_exit_wd = reg_wdata[3:0]; - assign cfg_l1d_spm_we = addr_hit[10] & reg_we & !reg_error; + assign cfg_l1d_spm_we = addr_hit[6] & reg_we & !reg_error; assign cfg_l1d_spm_wd = reg_wdata[9:0]; - assign cfg_l1d_insn_we = addr_hit[11] & reg_we & !reg_error; + assign cfg_l1d_insn_we = addr_hit[7] & reg_we & !reg_error; assign cfg_l1d_insn_wd = reg_wdata[1:0]; - assign cfg_l1d_tile_sel_we = addr_hit[12] & reg_we & !reg_error; - assign cfg_l1d_tile_sel_wd = reg_wdata[31:0]; + assign cfg_l1d_tile_sel_0_we = addr_hit[8] & reg_we & !reg_error; + assign cfg_l1d_tile_sel_0_wd = reg_wdata[31:0]; - assign l1d_spm_commit_we = addr_hit[13] & reg_we & !reg_error; + assign cfg_l1d_tile_sel_1_we = addr_hit[9] & reg_we & !reg_error; + assign cfg_l1d_tile_sel_1_wd = reg_wdata[31:0]; + + assign hw_barrier_participation_mask_0_we = addr_hit[10] & reg_we & !reg_error; + assign hw_barrier_participation_mask_0_wd = reg_wdata[31:0]; + + assign hw_barrier_participation_mask_1_we = addr_hit[11] & reg_we & !reg_error; + assign hw_barrier_participation_mask_1_wd = reg_wdata[31:0]; + + assign l1d_spm_commit_we = addr_hit[12] & reg_we & !reg_error; assign l1d_spm_commit_wd = reg_wdata[0]; - assign l1d_insn_commit_we = addr_hit[14] & reg_we & !reg_error; + assign l1d_insn_commit_we = addr_hit[13] & reg_we & !reg_error; assign l1d_insn_commit_wd = reg_wdata[0]; - assign l1d_flush_status_re = addr_hit[15] & reg_re & !reg_error; + assign l1d_flush_status_re = addr_hit[14] & reg_re & !reg_error; - assign l1d_private_we = addr_hit[16] & reg_we & !reg_error; + assign l1d_private_we = addr_hit[15] & reg_we & !reg_error; assign l1d_private_wd = reg_wdata[3:0]; - assign l1d_addr_we = addr_hit[17] & reg_we & !reg_error; + assign l1d_addr_we = addr_hit[16] & reg_we & !reg_error; assign l1d_addr_wd = reg_wdata[31:0]; - assign xbar_offset_we = addr_hit[18] & reg_we & !reg_error; + assign xbar_offset_we = addr_hit[17] & reg_we & !reg_error; assign xbar_offset_wd = reg_wdata[4:0]; - assign xbar_offset_commit_we = addr_hit[19] & reg_we & !reg_error; + assign xbar_offset_commit_we = addr_hit[18] & reg_we & !reg_error; assign xbar_offset_commit_wd = reg_wdata[0]; // Read data return @@ -737,82 +728,78 @@ module cachepool_peripheral_reg_top #( reg_rdata_next = '0; unique case (1'b1) addr_hit[0]: begin - reg_rdata_next[9:0] = hart_select_0_qs; + reg_rdata_next[31:0] = hw_barrier_qs; end addr_hit[1]: begin - reg_rdata_next[9:0] = hart_select_1_qs; + reg_rdata_next[0] = '0; end addr_hit[2]: begin - reg_rdata_next[31:0] = '0; + reg_rdata_next[0] = '0; end addr_hit[3]: begin - reg_rdata_next[31:0] = '0; + reg_rdata_next[31:0] = spatz_cycle_qs; end addr_hit[4]: begin - reg_rdata_next[31:0] = hw_barrier_qs; + reg_rdata_next[31:0] = cluster_boot_control_qs; end addr_hit[5]: begin - reg_rdata_next[0] = '0; + reg_rdata_next[3:0] = cluster_eoc_exit_qs; end addr_hit[6]: begin - reg_rdata_next[0] = '0; + reg_rdata_next[9:0] = cfg_l1d_spm_qs; end addr_hit[7]: begin - reg_rdata_next[31:0] = spatz_cycle_qs; + reg_rdata_next[1:0] = cfg_l1d_insn_qs; end addr_hit[8]: begin - reg_rdata_next[31:0] = cluster_boot_control_qs; + reg_rdata_next[31:0] = cfg_l1d_tile_sel_0_qs; end addr_hit[9]: begin - reg_rdata_next[3:0] = cluster_eoc_exit_qs; + reg_rdata_next[31:0] = cfg_l1d_tile_sel_1_qs; end addr_hit[10]: begin - reg_rdata_next[9:0] = cfg_l1d_spm_qs; + reg_rdata_next[31:0] = hw_barrier_participation_mask_0_qs; end addr_hit[11]: begin - reg_rdata_next[1:0] = cfg_l1d_insn_qs; + reg_rdata_next[31:0] = hw_barrier_participation_mask_1_qs; end addr_hit[12]: begin - reg_rdata_next[31:0] = cfg_l1d_tile_sel_qs; - end - - addr_hit[13]: begin reg_rdata_next[0] = l1d_spm_commit_qs; end - addr_hit[14]: begin + addr_hit[13]: begin reg_rdata_next[0] = l1d_insn_commit_qs; end - addr_hit[15]: begin + addr_hit[14]: begin reg_rdata_next[0] = l1d_flush_status_qs; end - addr_hit[16]: begin + addr_hit[15]: begin reg_rdata_next[3:0] = l1d_private_qs; end - addr_hit[17]: begin + addr_hit[16]: begin reg_rdata_next[31:0] = l1d_addr_qs; end - addr_hit[18]: begin + addr_hit[17]: begin reg_rdata_next[4:0] = xbar_offset_qs; end - addr_hit[19]: begin + addr_hit[18]: begin reg_rdata_next[0] = xbar_offset_commit_qs; end diff --git a/hardware/generated/floo_cachepool_noc_pkg.sv b/hardware/generated/floo_cachepool_noc_pkg.sv new file mode 100644 index 0000000..e6f5ea6 --- /dev/null +++ b/hardware/generated/floo_cachepool_noc_pkg.sv @@ -0,0 +1,219 @@ +// Copyright 2026 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +// AUTOMATICALLY GENERATED! DO NOT EDIT! + +`include "axi/typedef.svh" +`include "floo_noc/typedef.svh" + +package floo_cachepool_noc_pkg; + + import floo_pkg::*; + + ///////////////////// + // Address Map // + ///////////////////// + + typedef enum logic[3:0] { + GroupX0Y0 = 0, + GroupX0Y1 = 1, + GroupX1Y0 = 2, + GroupX1Y1 = 3, + Hbm0 = 4, + Hbm1 = 5, + Hbm2 = 6, + Hbm3 = 7, + NumEndpoints = 8} ep_id_e; + + + + typedef enum logic[2:0] { + Hbm0SamIdx = 2, + Hbm1SamIdx = 3, + Hbm2SamIdx = 4, + Hbm3SamIdx = 5} sam_idx_e; + + + + typedef logic[0:0] rob_idx_t; +typedef logic[0:0] port_id_t; +typedef logic[2:0] id_t; +typedef logic[8:0] route_t; + + + typedef struct packed { + id_t idx; + id_t start_addr; + id_t end_addr; + } route_map_rule_t; + + localparam int unsigned SamNumRules = 6; + +typedef struct packed { + id_t idx; + logic [31:0] start_addr; + logic [31:0] end_addr; +} sam_rule_t; + +localparam sam_rule_t[SamNumRules-1:0] Sam = '{ +'{ idx: 7, + start_addr: 32'hb0000000, + end_addr: 32'hbfffffff},// Hbm3 +'{ idx: 6, + start_addr: 32'ha0000000, + end_addr: 32'hafffffff},// Hbm2 +'{ idx: 5, + start_addr: 32'h90000000, + end_addr: 32'h9fffffff},// Hbm1 +'{ idx: 4, + start_addr: 32'h00000000, + end_addr: 32'h7fffffff},// Hbm0 +'{ idx: 4, + start_addr: 32'h80000000, + end_addr: 32'h8fffffff},// Hbm0 +'{ idx: 4, + start_addr: 32'hc0000000, + end_addr: 32'hffffffff} // Hbm0 + +}; + + + localparam route_t[NumEndpoints-1:0][NumEndpoints-1:0] RoutingTables = '{ +'{ +9'b000000000,// -> hbm3_ni +9'b000000000,// -> hbm2_ni +9'b000000000,// -> hbm1_ni +9'b000000000,// -> hbm0_ni +9'b000000100,// -> group_ni_1_1 +9'b000100010,// -> group_ni_1_0 +9'b000100011,// -> group_ni_0_1 +9'b100010011 // -> group_ni_0_0 +}, +'{ +9'b000000000,// -> hbm3_ni +9'b000000000,// -> hbm2_ni +9'b000000000,// -> hbm1_ni +9'b000000000,// -> hbm0_ni +9'b000100000,// -> group_ni_1_1 +9'b000000100,// -> group_ni_1_0 +9'b100000011,// -> group_ni_0_1 +9'b000100011 // -> group_ni_0_0 +}, +'{ +9'b000000000,// -> hbm3_ni +9'b000000000,// -> hbm2_ni +9'b000000000,// -> hbm1_ni +9'b000000000,// -> hbm0_ni +9'b000100001,// -> group_ni_1_1 +9'b100001010,// -> group_ni_1_0 +9'b000000100,// -> group_ni_0_1 +9'b000100010 // -> group_ni_0_0 +}, +'{ +9'b000000000,// -> hbm3_ni +9'b000000000,// -> hbm2_ni +9'b000000000,// -> hbm1_ni +9'b000000000,// -> hbm0_ni +9'b100001000,// -> group_ni_1_1 +9'b000100001,// -> group_ni_1_0 +9'b000100000,// -> group_ni_0_1 +9'b000000100 // -> group_ni_0_0 +}, +'{ +9'b000000001,// -> hbm3_ni +9'b000001010,// -> hbm2_ni +9'b000011011,// -> hbm1_ni +9'b011010011,// -> hbm0_ni +9'b000000000,// -> group_ni_1_1 +9'b000000000,// -> group_ni_1_0 +9'b000000000,// -> group_ni_0_1 +9'b000000000 // -> group_ni_0_0 +}, +'{ +9'b000001000,// -> hbm3_ni +9'b000000001,// -> hbm2_ni +9'b011000011,// -> hbm1_ni +9'b000011011,// -> hbm0_ni +9'b000000000,// -> group_ni_1_1 +9'b000000000,// -> group_ni_1_0 +9'b000000000,// -> group_ni_0_1 +9'b000000000 // -> group_ni_0_0 +}, +'{ +9'b000001001,// -> hbm3_ni +9'b001001010,// -> hbm2_ni +9'b000000011,// -> hbm1_ni +9'b000011010,// -> hbm0_ni +9'b000000000,// -> group_ni_1_1 +9'b000000000,// -> group_ni_1_0 +9'b000000000,// -> group_ni_0_1 +9'b000000000 // -> group_ni_0_0 +}, +'{ +9'b001001000,// -> hbm3_ni +9'b000001001,// -> hbm2_ni +9'b000011000,// -> hbm1_ni +9'b000000011,// -> hbm0_ni +9'b000000000,// -> group_ni_1_1 +9'b000000000,// -> group_ni_1_0 +9'b000000000,// -> group_ni_0_1 +9'b000000000 // -> group_ni_0_0 +}} +; + + + localparam route_cfg_t RouteCfg = '{ RouteAlgo: SourceRouting, + UseIdTable: 1'b1, + XYAddrOffsetX: 0, + XYAddrOffsetY: 0, + IdAddrOffset: 0, + NumSamRules: 6, + NumRoutes: 8, + CollectiveCfg: '{ OpCfg: '{ EnNarrowMulticast: 1'b0, + EnWideMulticast: 1'b0, + EnLsbAnd: 1'b0, + EnFpAdd: 1'b0, + EnFpMul: 1'b0, + EnFpMin: 1'b0, + EnFpMax: 1'b0, + EnIntAdd: 1'b0, + EnIntMul: 1'b0, + EnIntMinS: 1'b0, + EnIntMinU: 1'b0, + EnIntMaxS: 1'b0, + EnIntMaxU: 1'b0}, + NarrRedCfg: RedDefaultCfg, + WideRedCfg: RedDefaultCfg}}; + + + + typedef logic[31:0] axi_wide_in_addr_t; +typedef logic[511:0] axi_wide_in_data_t; +typedef logic[63:0] axi_wide_in_strb_t; +typedef logic[6:0] axi_wide_in_id_t; +typedef logic[20:0] axi_wide_in_user_t; +`AXI_TYPEDEF_ALL_CT(axi_wide_in, axi_wide_in_req_t, axi_wide_in_rsp_t, axi_wide_in_addr_t, axi_wide_in_id_t, axi_wide_in_data_t, axi_wide_in_strb_t, axi_wide_in_user_t) + + + typedef logic[31:0] axi_wide_out_addr_t; +typedef logic[511:0] axi_wide_out_data_t; +typedef logic[63:0] axi_wide_out_strb_t; +typedef logic[5:0] axi_wide_out_id_t; +typedef logic[20:0] axi_wide_out_user_t; +`AXI_TYPEDEF_ALL_CT(axi_wide_out, axi_wide_out_req_t, axi_wide_out_rsp_t, axi_wide_out_addr_t, axi_wide_out_id_t, axi_wide_out_data_t, axi_wide_out_strb_t, axi_wide_out_user_t) + + + + `FLOO_TYPEDEF_HDR_T(hdr_t, route_t, id_t, axi_ch_e, rob_idx_t) + localparam axi_cfg_t AxiCfg = '{ AddrWidth: 32, + DataWidth: 512, + InIdWidth: 7, + OutIdWidth: 6, + UserWidth: 21}; +`FLOO_TYPEDEF_AXI_CHAN_ALL(axi, req, rsp, axi_wide_in, AxiCfg, hdr_t) + +`FLOO_TYPEDEF_AXI_LINK_ALL(req, rsp, req, rsp) + + +endpackage diff --git a/hardware/src/axi_hier_interco.sv b/hardware/src/axi_hier_interco.sv new file mode 100644 index 0000000..dfc4d99 --- /dev/null +++ b/hardware/src/axi_hier_interco.sv @@ -0,0 +1,322 @@ +// Copyright 2021 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +// Author: Samuel Riedel + +// Implement a hierarchical AXI interconnect. Below shows one level of the interconnect. This module +// recursively instantiates itself and creates a tree of interconnects, each node with `Radix` slave +// ports. +// +// AXI Mux Read-only ID Width +// Cache Converter +// |‾╲ +// +-------->| ╲ +// | + +-------+ +-------+ +// +-------->| M | | | | | +// | U |---->| $ |---->| > |----> +// | X | | | | | +// | + +-------+ +-------+ +// +-------->| ╱ +// |_╱ +// Internal Cache +// Slave type type type Master type + +module axi_hier_interco + import cachepool_pkg::ro_cache_ctrl_t; +#( + parameter int unsigned NumSlvPorts = 0, + parameter int unsigned NumMstPorts = 0, + parameter int unsigned Radix = 2, + parameter int unsigned EnableCache = 0, + parameter int unsigned CacheLineWidth = 0, + parameter int unsigned CacheSizeByte = 0, + parameter int unsigned CacheSets = 0, + parameter int unsigned AddrWidth = 0, + parameter int unsigned DataWidth = 0, + parameter int unsigned SlvIdWidth = 0, + parameter int unsigned MstIdWidth = 0, + parameter int unsigned UserWidth = 0, + parameter type slv_req_t = logic, + parameter type slv_resp_t = logic, + parameter type mst_req_t = logic, + parameter type mst_resp_t = logic +) ( + input logic clk_i, + input logic rst_ni, + input logic test_i, + input ro_cache_ctrl_t ro_cache_ctrl_i, + input slv_req_t [NumSlvPorts-1:0] slv_req_i, + output slv_resp_t [NumSlvPorts-1:0] slv_resp_o, + output mst_req_t [NumMstPorts-1:0] mst_req_o, + input mst_resp_t [NumMstPorts-1:0] mst_resp_i +); + + //////////////// + // Typedefs // + //////////////// + + localparam int unsigned IntIdWidth = SlvIdWidth + $clog2(NumSlvPorts); + localparam int unsigned CacheIdWidth = EnableCache[0] ? IntIdWidth + 1: IntIdWidth; + localparam int unsigned NrAddrRules = cachepool_pkg::ROCacheNumAddrRules; + + typedef logic [AddrWidth-1:0] addr_t; + typedef logic [DataWidth-1:0] data_t; + typedef logic [DataWidth/8-1:0] strb_t; + typedef logic [SlvIdWidth-1:0] slv_id_t; + typedef logic [MstIdWidth-1:0] mst_id_t; + typedef logic [IntIdWidth-1:0] int_id_t; + typedef logic [CacheIdWidth-1:0] cache_id_t; + typedef logic [UserWidth-1:0] user_t; + + `include "axi/typedef.svh" + // Common AXI types + `AXI_TYPEDEF_W_CHAN_T(w_t, data_t, strb_t, user_t); + // Slave AXI types + `AXI_TYPEDEF_AW_CHAN_T(slv_aw_t, addr_t, slv_id_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); + // Intermediate AXI types + `AXI_TYPEDEF_AW_CHAN_T(int_aw_t, addr_t, int_id_t, user_t); + `AXI_TYPEDEF_B_CHAN_T(int_b_t, int_id_t, user_t); + `AXI_TYPEDEF_AR_CHAN_T(int_ar_t, addr_t, int_id_t, user_t); + `AXI_TYPEDEF_R_CHAN_T(int_r_t, data_t, int_id_t, user_t); + `AXI_TYPEDEF_REQ_T(int_req_t, int_aw_t, w_t, int_ar_t); + `AXI_TYPEDEF_RESP_T(int_resp_t, int_b_t, int_r_t ); + // Cache AXI types + `AXI_TYPEDEF_AW_CHAN_T(cache_aw_t, addr_t, cache_id_t, user_t); + `AXI_TYPEDEF_B_CHAN_T(cache_b_t, cache_id_t, user_t); + `AXI_TYPEDEF_AR_CHAN_T(cache_ar_t, addr_t, cache_id_t, user_t); + `AXI_TYPEDEF_R_CHAN_T(cache_r_t, data_t, cache_id_t, user_t); + `AXI_TYPEDEF_REQ_T(cache_req_t, cache_aw_t, w_t, cache_ar_t); + `AXI_TYPEDEF_RESP_T(cache_resp_t, cache_b_t, cache_r_t ); + + /////////////// + // Interco // + /////////////// + + // Recursive module to implement multiple hierarchy levels at once + + if (NumMstPorts > NumSlvPorts) begin : gen_error + $error("[axi_hier_interco] `NumMstPorts` must be bigger than `NumSlvPorts`."); + end else if (NumMstPorts == NumSlvPorts) begin : gen_top_level + // Top-level, connect the ports to the master ports + for (genvar i = 0; i < NumMstPorts; i++) begin : gen_bypasses + assign mst_req_o[i] = slv_req_i[i]; + assign slv_resp_o[i] = mst_resp_i[i]; + end + end else if (Radix <= 1) begin : gen_error + $error("[axi_hier_interco] `Radix` must be bigger than 1."); + end else if (NumSlvPorts > Radix) begin : gen_axi_level_recursive + // More than one level missing. --> Recursively call this module + // This level will contain `NumMuxes` interconnects + localparam int unsigned NumMuxes = NumSlvPorts / Radix; + if (NumMuxes * Radix != NumSlvPorts) begin : gen_error + $error("[axi_hier_interco] `NumSlvPorts` mod `Radix` must be 0."); + end else begin : gen_level + slv_req_t [NumMuxes-1:0] int_req; + slv_resp_t [NumMuxes-1:0] int_resp; + + for (genvar i = 0; i < NumMuxes; i++) begin : gen_lower_level + axi_hier_interco #( + .NumSlvPorts (Radix ), + .NumMstPorts (1 ), + .Radix (Radix ), + .EnableCache (EnableCache ), + .CacheLineWidth (CacheLineWidth), + .CacheSizeByte (CacheSizeByte ), + .CacheSets (CacheSets ), + .AddrWidth (AddrWidth ), + .DataWidth (DataWidth ), + .SlvIdWidth (SlvIdWidth ), + .MstIdWidth (SlvIdWidth ), + .UserWidth (UserWidth ), + .slv_req_t (slv_req_t ), + .slv_resp_t (slv_resp_t ), + .mst_req_t (slv_req_t ), + .mst_resp_t (slv_resp_t ) + ) i_axi_interco ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + .test_i (test_i ), + .ro_cache_ctrl_i (ro_cache_ctrl_i ), + .slv_req_i (slv_req_i[i*Radix +: Radix] ), + .slv_resp_o (slv_resp_o[i*Radix +: Radix]), + .mst_req_o (int_req[i] ), + .mst_resp_i (int_resp[i] ) + ); + end + + axi_hier_interco #( + .NumSlvPorts (NumMuxes ), + .NumMstPorts (NumMstPorts ), + .Radix (Radix ), + .EnableCache (EnableCache>>1), + .CacheLineWidth (CacheLineWidth), + .CacheSizeByte (CacheSizeByte ), + .CacheSets (CacheSets ), + .AddrWidth (AddrWidth ), + .DataWidth (DataWidth ), + .SlvIdWidth (SlvIdWidth ), + .MstIdWidth (MstIdWidth ), + .UserWidth (UserWidth ), + .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_interco ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + .test_i (test_i ), + .ro_cache_ctrl_i (ro_cache_ctrl_i), + .slv_req_i (int_req ), + .slv_resp_o (int_resp ), + .mst_req_o (mst_req_o ), + .mst_resp_i (mst_resp_i ) + ); + end + end else if (NumSlvPorts <= Radix && NumMstPorts == 1) begin : gen_bottom_level + + // Intermediate AXI channel + int_req_t int_req; + int_resp_t int_resp; + cache_req_t cache_req; + cache_resp_t cache_resp; + + axi_mux #( + // AXI parameter and channel types + .SlvAxiIDWidth (SlvIdWidth ), // AXI ID width, slave ports + .slv_aw_chan_t (slv_aw_t ), // AW Channel Type, slave ports + .mst_aw_chan_t (int_aw_t ), // AW Channel Type, master port + .w_chan_t (w_t ), // W Channel Type, all ports + .slv_b_chan_t (slv_b_t ), // B Channel Type, slave ports + .mst_b_chan_t (int_b_t ), // B Channel Type, master port + .slv_ar_chan_t (slv_ar_t ), // AR Channel Type, slave ports + .mst_ar_chan_t (int_ar_t ), // AR Channel Type, master port + .slv_r_chan_t (slv_r_t ), // R Channel Type, slave ports + .mst_r_chan_t (int_r_t ), // R Channel Type, master port + .slv_req_t (slv_req_t ), // Slave port request type + .slv_resp_t (slv_resp_t ), // Slave port response type + .mst_req_t (int_req_t ), // Master ports request type + .mst_resp_t (int_resp_t ), // Master ports response type + .NoSlvPorts (NumSlvPorts), // Number of slave ports + // Maximum number of outstanding transactions per write + .MaxWTrans (8 ), + // If enabled, this multiplexer is purely combinatorial + .FallThrough (1'b0 ), + // add spill register on write master ports, adds a cycle latency on write channels + .SpillAw (1'b1 ), + .SpillW (1'b1 ), + .SpillB (1'b1 ), + // add spill register on read master ports, adds a cycle latency on read channels + .SpillAr (1'b1 ), + .SpillR (1'b1 ) + ) i_axi_mux ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + .test_i (test_i ), + .slv_reqs_i (slv_req_i ), + .slv_resps_o (slv_resp_o), + .mst_req_o (int_req ), + .mst_resp_i (int_resp ) + ); + + if (EnableCache[0]) begin: gen_ro_cache + localparam int unsigned LineCount = CacheSizeByte/(CacheSets*CacheLineWidth/8); + snitch_read_only_cache #( + .LineWidth (CacheLineWidth), + .LineCount (LineCount ), + .WayCount (CacheSets ), + .AxiAddrWidth (AddrWidth ), + .AxiDataWidth (DataWidth ), + .AxiIdWidth (IntIdWidth ), + .AxiUserWidth (UserWidth ), + .MaxTrans (32'd8 ), + .NrAddrRules (NrAddrRules ), + .SerialLookup (0 ), + .slv_req_t (int_req_t ), + .slv_rsp_t (int_resp_t ), + .mst_req_t (cache_req_t ), + .mst_rsp_t (cache_resp_t ) + ) i_snitch_read_only_cache ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + .enable_i (ro_cache_ctrl_i.enable ), + .flush_valid_i (ro_cache_ctrl_i.flush_valid), + .flush_ready_o (/* unused */ ), + .icache_events_o (/* unused */ ), + .start_addr_i (ro_cache_ctrl_i.start_addr ), + .end_addr_i (ro_cache_ctrl_i.end_addr ), + .axi_slv_req_i (int_req ), + .axi_slv_rsp_o (int_resp ), + .axi_mst_req_o (cache_req ), + .axi_mst_rsp_i (cache_resp ), + .sram_cfg_data_i ('0 ), + .sram_cfg_tag_i ('0 ), + .sram_cfg_out_data_o (/* unused */ ), + .sram_cfg_out_tag_o (/* unused */ ) + ); + end else begin: gen_no_ro_cache + assign cache_req = int_req; + assign int_resp = cache_resp; + end + + axi_id_remap #( + .AxiSlvPortIdWidth (CacheIdWidth ), + .AxiSlvPortMaxUniqIds (2**MstIdWidth), + .AxiMaxTxnsPerId (8 ), + .AxiMstPortIdWidth (MstIdWidth ), + .slv_req_t (cache_req_t ), + .slv_resp_t (cache_resp_t ), + .mst_req_t (mst_req_t ), + .mst_resp_t (mst_resp_t ) + ) i_axi_id_remap ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + .slv_req_i (cache_req ), + .slv_resp_o (cache_resp), + .mst_req_o (mst_req_o ), + .mst_resp_i (mst_resp_i) + ); + + // Check all the AXI widths + if ($bits(slv_req_i[0].aw.addr) != AddrWidth) + $error("[axi_hier_interco] `slv_req_i.aw.addr` does not match AddrWidth."); + if ($bits(slv_req_i[0].w.data) != DataWidth) + $error("[axi_hier_interco] `slv_req_i.w.data` does not match DataWidth."); + if ($bits(slv_req_i[0].aw.id) != SlvIdWidth) + $error("[axi_hier_interco] `slv_req_i.aw.id` does not match SlvIdWidth."); + if ($bits(slv_req_i[0].aw.user) != UserWidth) + $error("[axi_hier_interco] `slv_req_i.aw.user` does not match UserWidth."); + + if ($bits(mst_req_o[0].aw.addr) != AddrWidth) + $error("[axi_hier_interco] `mst_req_o.aw.addr` does not match AddrWidth."); + if ($bits(mst_req_o[0].w.data) != DataWidth) + $error("[axi_hier_interco] `mst_req_o.w.data` does not match DataWidth."); + if ($bits(mst_req_o[0].aw.id) != MstIdWidth) + $error("[axi_hier_interco] `mst_req_o.aw.id` does not match MstIdWidth."); + if ($bits(mst_req_o[0].aw.user) != UserWidth) + $error("[axi_hier_interco] `mst_req_o.aw.user` does not match UserWidth."); + + if ($bits(int_req.aw.addr) != AddrWidth) + $error("[axi_hier_interco] `int_req.aw.addr` does not match AddrWidth."); + if ($bits(int_req.w.data) != DataWidth) + $error("[axi_hier_interco] `int_req.w.data` does not match DataWidth."); + if ($bits(int_req.aw.id) != IntIdWidth) + $error("[axi_hier_interco] `int_req.aw.id` does not match IntIdWidth."); + if ($bits(int_req.aw.user) != UserWidth) + $error("[axi_hier_interco] `int_req.aw.user` does not match UserWidth."); + + if ($bits(cache_req.aw.addr) != AddrWidth) + $error("[axi_hier_interco] `cache_req.aw.addr` does not match AddrWidth."); + if ($bits(cache_req.w.data) != DataWidth) + $error("[axi_hier_interco] `cache_req.w.data` does not match DataWidth."); + if ($bits(cache_req.aw.id) != CacheIdWidth) + $error("[axi_hier_interco] `cache_req.aw.id` does not match CacheIdWidth."); + if ($bits(cache_req.aw.user) != UserWidth) + $error("[axi_hier_interco] `cache_req.aw.user` does not match UserWidth."); + end else begin: gen_error + $error("[axi_hier_interco] Cannot build a tree with those parameters."); + end +endmodule diff --git a/hardware/src/cachepool_cc.sv b/hardware/src/cachepool_cc.sv index 86c8d7e..2844816 100644 --- a/hardware/src/cachepool_cc.sv +++ b/hardware/src/cachepool_cc.sv @@ -23,12 +23,6 @@ module cachepool_cc parameter int unsigned DataWidth = 0, /// User width of the buses. parameter int unsigned UserWidth = 0, - /// Data width of the AXI DMA buses. - parameter int unsigned DMADataWidth = 0, - /// Id width of the AXI DMA bus. - parameter int unsigned DMAIdWidth = 0, - parameter int unsigned DMAAxiReqFifoDepth = 0, - parameter int unsigned DMAReqFifoDepth = 0, parameter int unsigned SpmStackDepth = 512, /// Data port request type. @@ -75,7 +69,6 @@ module cachepool_cc parameter bit XF16ALT = 0, parameter bit XF8ALT = 0, /// Enable Snitch DMA - parameter bit Xdma = 0, parameter int unsigned NumIntOutstandingLoads = 0, parameter int unsigned NumIntOutstandingMem = 0, parameter int unsigned NumSpatzOutstandingLoads = 0, @@ -176,7 +169,7 @@ module cachepool_cc .VMSupport (1'b0 ), .RVE (RVE ), .FP_EN (FPEn ), - .Xdma (Xdma ), + .Xdma (1'b0 ), .RVF (RVF ), .RVD (RVD ), .RVV (RVV ), @@ -682,9 +675,6 @@ module cachepool_cc `ASSERT(stack_overflow, mem_req_valid[TotStack] |-> (&stack_addr_check == 1'b1), clk_i, !rst_ni, "Core ID bits cannot be used for stack") - int spatz_f; - string spatz_f_name; - initial begin // We need to schedule the assignment into a safe region, otherwise // `hart_id_i` won't have a value assigned at the beginning of the first @@ -692,13 +682,9 @@ module cachepool_cc /* verilator lint_off STMTDLY */ @(posedge clk_i); /* verilator lint_on STMTDLY */ - $system("mkdir sim/bin/logs -p"); - $sformat(fn, "sim/bin/logs/trace_hart_%05x.dasm", hart_id_i); + $sformat(fn, "sim/bin/logs/core/trace_hart_%05x.dasm", hart_id_i); f = $fopen(fn, "w"); $display("[Tracer] Logging Hart %d to %s", hart_id_i, fn); - - $sformat(spatz_f_name, "sim/bin/logs/monitor_spatz_%05x.txt", hart_id_i); - spatz_f = $fopen(spatz_f_name, "w"); end // verilog_lint: waive-start always-ff-non-blocking @@ -780,167 +766,7 @@ module cachepool_cc end final begin - `ifndef TARGET_SYNTHESIS - /***** Controller Report *****/ - automatic real ctrl_vlsu_insn = i_spatz.i_controller.ctrl_vlsu_insn_q; - automatic real ctrl_vlsu_stall = i_spatz.i_controller.ctrl_vlsu_stall_q; - automatic real ctrl_vlsu_active = i_spatz.i_controller.ctrl_vlsu_active_q; - automatic real ctrl_vlsu_wvalid = i_spatz.i_controller.ctrl_wvalid_cnt_q[1]; - automatic real ctrl_vlsu_wtrans = i_spatz.i_controller.ctrl_wtrans_cnt_q[1]; - automatic real ctrl_vlsu_wstall = ctrl_vlsu_wvalid - ctrl_vlsu_wtrans; - - automatic real ctrl_vfu_insn = i_spatz.i_controller.ctrl_vfu_insn_q; - automatic real ctrl_vfu_stall = i_spatz.i_controller.ctrl_vfu_stall_q; - automatic real ctrl_vfu_active = i_spatz.i_controller.ctrl_vfu_active_q; - automatic real ctrl_vfu_wvalid = i_spatz.i_controller.ctrl_wvalid_cnt_q[0]; - automatic real ctrl_vfu_wtrans = i_spatz.i_controller.ctrl_wtrans_cnt_q[0]; - automatic real ctrl_vfu_wstall = ctrl_vfu_wvalid - ctrl_vfu_wtrans; - - automatic real ctrl_vsldu_insn = i_spatz.i_controller.ctrl_vsldu_insn_q; - automatic real ctrl_vsldu_stall = i_spatz.i_controller.ctrl_vsldu_stall_q; - automatic real ctrl_vsldu_active = i_spatz.i_controller.ctrl_vsldu_active_q; - automatic real ctrl_vsldu_wvalid = i_spatz.i_controller.ctrl_wvalid_cnt_q[2]; - automatic real ctrl_vsldu_wtrans = i_spatz.i_controller.ctrl_wtrans_cnt_q[2]; - automatic real ctrl_vsldu_wstall = ctrl_vsldu_wvalid - ctrl_vsldu_wtrans; - - - /***** VLSU Report *****/ - automatic real vlsu_wvalid = i_spatz.i_vlsu.vrf_wvalid_cnt_q; - automatic real vlsu_wtrans = i_spatz.i_vlsu.vrf_wtrans_cnt_q; - automatic real vlsu_vrf_w_util = i_spatz.i_vlsu.vrf_wvalid_cnt_q == 0 ? - 0 : 100 * i_spatz.i_vlsu.vrf_wtrans_cnt_q / i_spatz.i_vlsu.vrf_wvalid_cnt_q; - automatic real vlsu_vrf_w_avg_cyc = i_spatz.i_vlsu.vrf_wtrans_cnt_q == 0 ? - 0 : i_spatz.i_vlsu.vrf_wvalid_cnt_q / i_spatz.i_vlsu.vrf_wtrans_cnt_q; - - automatic real vlsu_mem_cnt_tot = i_spatz.i_vlsu.mem_valid_cnt_q; - automatic real vlsu_mem_tran_tot = i_spatz.i_vlsu.mem_trans_cnt_q; - automatic real vlsu_mem_util = vlsu_mem_cnt_tot == 0 ? - 0 : 100 * vlsu_mem_tran_tot / vlsu_mem_cnt_tot; - automatic real vlsu_mem_avg_cyc = vlsu_mem_tran_tot == 0 ? - 0 : vlsu_mem_cnt_tot / vlsu_mem_tran_tot; - - // ROB in use for x cycle, total usage Y: Utilization = Y/(X*depth) - automatic real vlsu_rob_usage_avg = i_spatz.i_vlsu.rob_use_cyc_q == 0 ? - 0 : 100 * i_spatz.i_vlsu.rob_usage_q / i_spatz.i_vlsu.rob_use_cyc_q / NumSpatzOutstandingLoads; - automatic real vlsu_rob_peak_util = 100 * i_spatz.i_vlsu.rob_peak_q / NumSpatzOutstandingLoads; - - automatic real vlsu_stall_cyc = i_spatz.i_vlsu.rob_full_cyc_q; - - // Average instruction cycle can be used to caclulate the latency from VLSU to memory - // AVG_insn_cyc / VLEN = AVG_LD_cyc_per_elem - automatic real vlsu_insn_cnt = i_spatz.i_vlsu.vlsu_insn_cnt_q; - automatic real vlsu_avg_insn_cyc = i_spatz.i_vlsu.vlsu_insn_valid_cyc_q / i_spatz.i_vlsu.vlsu_insn_cnt_q; - - /***** VFU Report *****/ - automatic real vfu_wvalid = i_spatz.i_vfu.vrf_wvalid_cnt_q; - automatic real vfu_wtrans = i_spatz.i_vfu.vrf_wtrans_cnt_q; - automatic real vfu_vrf_w_util = i_spatz.i_vfu.vrf_wvalid_cnt_q == 0 ? - 0 : (100 * i_spatz.i_vfu.vrf_wtrans_cnt_q / i_spatz.i_vfu.vrf_wvalid_cnt_q); - automatic real vfu_vrf_w_avg_cyc = i_spatz.i_vfu.vrf_wtrans_cnt_q == 0 ? - 0 : (i_spatz.i_vfu.vrf_wvalid_cnt_q / i_spatz.i_vfu.vrf_wtrans_cnt_q); - - automatic real vfu_rvalid0 = i_spatz.i_vfu.vrf_rvalid_cnt_q[0]; - automatic real vfu_rtrans0 = i_spatz.i_vfu.vrf_rtrans_cnt_q[0]; - automatic real vfu_vrf_r_util0 = i_spatz.i_vfu.vrf_rvalid_cnt_q[0] == 0 ? - 0 : (100 * i_spatz.i_vfu.vrf_rtrans_cnt_q[0] / i_spatz.i_vfu.vrf_rvalid_cnt_q[0]); - automatic real vfu_vrf_r_avg_cyc0 = i_spatz.i_vfu.vrf_rtrans_cnt_q[0] == 0 ? - 0 : (i_spatz.i_vfu.vrf_rvalid_cnt_q[0] / i_spatz.i_vfu.vrf_rtrans_cnt_q[0]); - - automatic real vfu_rvalid1 = i_spatz.i_vfu.vrf_rvalid_cnt_q[1]; - automatic real vfu_rtrans1 = i_spatz.i_vfu.vrf_rtrans_cnt_q[1]; - automatic real vfu_vrf_r_util1 = i_spatz.i_vfu.vrf_rvalid_cnt_q[1] == 0 ? - 0 : (100 * i_spatz.i_vfu.vrf_rtrans_cnt_q[1] / i_spatz.i_vfu.vrf_rvalid_cnt_q[1]); - automatic real vfu_vrf_r_avg_cyc1 = i_spatz.i_vfu.vrf_rvalid_cnt_q[1] == 0 ? - 0 : (i_spatz.i_vfu.vrf_rvalid_cnt_q[1] / i_spatz.i_vfu.vrf_rtrans_cnt_q[1]); - - automatic real vfu_rvalid2 = i_spatz.i_vfu.vrf_rvalid_cnt_q[2]; - automatic real vfu_rtrans2 = i_spatz.i_vfu.vrf_rtrans_cnt_q[2]; - automatic real vfu_vrf_r_util2 = i_spatz.i_vfu.vrf_rvalid_cnt_q[2] == 0 ? - 0 : (100 * i_spatz.i_vfu.vrf_rtrans_cnt_q[2] / i_spatz.i_vfu.vrf_rvalid_cnt_q[2]); - automatic real vfu_vrf_r_avg_cyc2 = i_spatz.i_vfu.vrf_rvalid_cnt_q[2] == 0 ? - 0 : (i_spatz.i_vfu.vrf_rvalid_cnt_q[2] / i_spatz.i_vfu.vrf_rtrans_cnt_q[2]); - - automatic real vfu_insn_cnt = i_spatz.i_vfu.vfu_insn_cnt_q; - automatic real vfu_avg_insn_cyc = i_spatz.i_vfu.vfu_insn_cnt_q == 0 ? - 0 : (i_spatz.i_vfu.vfu_insn_valid_cyc_q / i_spatz.i_vfu.vfu_insn_cnt_q); - - $fwrite(spatz_f, "*********************************************************************\n"); - $fwrite(spatz_f, "*** Spatz Controller Utilization Report ***\n"); - $fwrite(spatz_f, "*********************************************************************\n"); - $fwrite(spatz_f, " VLSU:\n" ); - $fwrite(spatz_f, " VLSU Active (not accurate): %32d\n", ctrl_vlsu_active ); - $fwrite(spatz_f, " VLSU Num Instructions: %32d\n", ctrl_vlsu_insn ); - $fwrite(spatz_f, " VLSU Stall Cycles: %32d\n", ctrl_vlsu_stall ); - $fwrite(spatz_f, " VLSU WR Stalls: %32d\n", ctrl_vlsu_wstall ); - $fwrite(spatz_f, "\n" ); - $fwrite(spatz_f, " VFU:\n" ); - $fwrite(spatz_f, " VFU Active (not accurate): %32d\n", ctrl_vfu_active ); - $fwrite(spatz_f, " VFU Num Instructions: %32d\n", ctrl_vfu_insn ); - $fwrite(spatz_f, " VFU Stall Cycles: %32d\n", ctrl_vfu_stall ); - $fwrite(spatz_f, " VFU WR Stalls: %32d\n", ctrl_vfu_wstall ); - $fwrite(spatz_f, "\n" ); - $fwrite(spatz_f, " VSLDU:\n" ); - $fwrite(spatz_f, " VSLDU Active (not accurate): %32d\n", ctrl_vsldu_active ); - $fwrite(spatz_f, " VSLDU Num Instructions: %32d\n", ctrl_vsldu_insn ); - $fwrite(spatz_f, " VSLDU Stall Cycles: %32d\n", ctrl_vsldu_stall ); - $fwrite(spatz_f, " VSLDU WR Stalls: %32d\n", ctrl_vsldu_wstall ); - $fwrite(spatz_f, "\n" ); - $fwrite(spatz_f, "*********************************************************************\n"); - $fwrite(spatz_f, "*** Spatz VLSU Utilization Report ***\n"); - $fwrite(spatz_f, "*********************************************************************\n"); - $fwrite(spatz_f, " Number of VRF Valid Cycles: %32d\n", vlsu_wvalid ); - $fwrite(spatz_f, " Number of VRF Transaction Counts: %32d\n", vlsu_wtrans ); - $fwrite(spatz_f, " VRF W Utilization: %32.2f\n", vlsu_vrf_w_util ); - $fwrite(spatz_f, " VRF W AVG Cycles: %32.2f\n", vlsu_vrf_w_avg_cyc ); - $fwrite(spatz_f, "\n" ); - $fwrite(spatz_f, " Number of Mem Valid Cycles: %32d\n", vlsu_mem_cnt_tot ); - $fwrite(spatz_f, " Number of Mem Transaction Counts: %32d\n", vlsu_mem_tran_tot ); - $fwrite(spatz_f, " Mem Utilization: %32.2f\n",vlsu_mem_util ); - $fwrite(spatz_f, " Mem AVG Req Accept Cycles: %32.2f\n",vlsu_mem_avg_cyc ); - $fwrite(spatz_f, "\n" ); - $fwrite(spatz_f, " VLSU Stall Cycles (ROB Full): %32d\n", vlsu_stall_cyc ); - $fwrite(spatz_f, " ROB AVG Utilization: %32.2f\n", vlsu_rob_usage_avg ); - $fwrite(spatz_f, " ROB Peak Utilization: %32.2f\n", vlsu_rob_peak_util ); - $fwrite(spatz_f, "\n" ); - $fwrite(spatz_f, " Total Insn Count: %32.2f\n", vlsu_insn_cnt ); - $fwrite(spatz_f, " AVG Insn Cycles: %32.2f\n", vlsu_avg_insn_cyc ); - $fwrite(spatz_f, "\n" ); - $fwrite(spatz_f, "*********************************************************************\n"); - $fwrite(spatz_f, "*** Spatz VFU Utilization Report ***\n"); - $fwrite(spatz_f, "*********************************************************************\n"); - $fwrite(spatz_f, " VRF Write Port:\n" ); - $fwrite(spatz_f, " Number of VRF Valid Cycles: %32d\n", vfu_wvalid ); - $fwrite(spatz_f, " Number of VRF Transaction Counts: %32d\n", vfu_wtrans ); - $fwrite(spatz_f, " VRF W Utilization: %32.2f\n", vfu_vrf_w_util ); - $fwrite(spatz_f, " VRF W AVG Cycles: %32.2f\n", vfu_vrf_w_avg_cyc ); - $fwrite(spatz_f, "\n" ); - $fwrite(spatz_f, " VRF Read Port 0:\n" ); - $fwrite(spatz_f, " Number of VRF Valid Cycles: %32d\n", vfu_rvalid0 ); - $fwrite(spatz_f, " Number of VRF Transaction Counts: %32d\n", vfu_rtrans0 ); - $fwrite(spatz_f, " VRF W Utilization: %32.2f\n", vfu_vrf_r_util0 ); - $fwrite(spatz_f, " VRF W AVG Cycles: %32.2f\n", vfu_vrf_r_avg_cyc0 ); - $fwrite(spatz_f, "\n" ); - $fwrite(spatz_f, " VRF Read Port 1:\n" ); - $fwrite(spatz_f, " Number of VRF Valid Cycles: %32d\n", vfu_rvalid1 ); - $fwrite(spatz_f, " Number of VRF Transaction Counts: %32d\n", vfu_rtrans1 ); - $fwrite(spatz_f, " VRF W Utilization: %32.2f\n", vfu_vrf_r_util1 ); - $fwrite(spatz_f, " VRF W AVG Cycles: %32.2f\n", vfu_vrf_r_avg_cyc1 ); - $fwrite(spatz_f, "\n" ); - $fwrite(spatz_f, " VRF Read Port 2:\n" ); - $fwrite(spatz_f, " Number of VRF Valid Cycles: %32d\n", vfu_rvalid2 ); - $fwrite(spatz_f, " Number of VRF Transaction Counts: %32d\n", vfu_rtrans2 ); - $fwrite(spatz_f, " VRF W Utilization: %32.2f\n", vfu_vrf_r_util2 ); - $fwrite(spatz_f, " VRF W AVG Cycles: %32.2f\n", vfu_vrf_r_avg_cyc2 ); - $fwrite(spatz_f, "\n" ); - $fwrite(spatz_f, "\n" ); - $fwrite(spatz_f, " Total Insn Count: %32.2f\n", vfu_insn_cnt ); - $fwrite(spatz_f, " AVG Insn Cycles: %32.2f\n", vfu_avg_insn_cyc ); - $fwrite(spatz_f, "*********************************************************************\n"); - - `endif $fclose(f); - $fclose(spatz_f); - end // verilog_lint: waive-stop always-ff-non-blocking diff --git a/hardware/src/cachepool_cluster.sv b/hardware/src/cachepool_cluster.sv index df687a3..d4f4982 100644 --- a/hardware/src/cachepool_cluster.sv +++ b/hardware/src/cachepool_cluster.sv @@ -4,21 +4,13 @@ // Author: Diyou Shen -`include "axi/assign.svh" `include "axi/typedef.svh" -`include "common_cells/assertions.svh" -`include "common_cells/registers.svh" -`include "mem_interface/assign.svh" -`include "mem_interface/typedef.svh" -`include "register_interface//assign.svh" `include "register_interface/typedef.svh" -`include "reqrsp_interface/assign.svh" -`include "reqrsp_interface/typedef.svh" -`include "snitch_vm/typedef.svh" -`include "tcdm_interface/assign.svh" -`include "tcdm_interface/typedef.svh" +`include "common_cells/registers.svh" +`include "common_cells/assertions.svh" -/// A single-tile cluster implementation for CachePool +/// CachePool cluster: instantiates NumGroups groups connected via FlooNoC mesh, +/// with shared L2 memory and peripheral fabric. module cachepool_cluster import cachepool_pkg::*; import spatz_pkg::*; @@ -47,10 +39,6 @@ module cachepool_cluster parameter int unsigned ClusterPeriphSize = 64, /// Number of TCDM Banks. parameter int unsigned NrBanks = 2 * NrCores, - /// Size of DMA AXI buffer. - parameter int unsigned DMAAxiReqFifoDepth = 3, - /// Size of DMA request fifo. - parameter int unsigned DMAReqFifoDepth = 3, /// Width of a single icache line. parameter unsigned ICacheLineWidth = 0, /// Number of icache lines per set. @@ -65,8 +53,6 @@ module cachepool_cluster /// Spatz FPU/IPU Configuration parameter int unsigned NumSpatzFPUs = 4, parameter int unsigned NumSpatzIPUs = 1, - /// Per-core enabling of the custom `Xdma` ISA extensions. - parameter bit [NrCores-1:0] Xdma = '{default: '0}, /// # Per-core parameters /// Per-core integer outstanding loads parameter int unsigned NumIntOutstandingLoads = 0, @@ -91,10 +77,6 @@ module cachepool_cluster parameter int unsigned MaxSlvTrans = 4, /// # Interface /// AXI Ports - parameter type axi_in_req_t = logic, - parameter type axi_in_resp_t = logic, - parameter type axi_narrow_req_t = logic, - parameter type axi_narrow_resp_t = logic, parameter type axi_out_req_t = logic, parameter type axi_out_resp_t = logic, /// SRAM configuration @@ -115,20 +97,20 @@ module cachepool_cluster input logic rst_ni, /// Per-core debug request signal. Asserting this signals puts the /// corresponding core into debug mode. This signal is assumed to be _async_. - input logic [NrCores-1:0] debug_req_i, + input logic debug_req_i, /// End of Computing indicator to notify the host/tb output logic [3:0] eoc_o, /// Machine external interrupt pending. Usually those interrupts come from a /// platform-level interrupt controller. This signal is assumed to be _async_. - input logic [NrCores-1:0] meip_i, + input logic meip_i, /// Machine timer interrupt pending. Usually those interrupts come from a /// core-local interrupt controller such as a timer/RTC. This signal is /// assumed to be _async_. - input logic [NrCores-1:0] mtip_i, + input logic mtip_i, /// Core software interrupt pending. Usually those interrupts come from /// another core to facilitate inter-processor-interrupts. This signal is /// assumed to be _async_. - input logic [NrCores-1:0] msip_i, + input logic msip_i, /// First hartid of the cluster. Cores of a cluster are monotonically /// increasing without a gap, i.e., a cluster with 8 cores and a /// `hart_base_id_i` of 5 get the hartids 5 - 12. @@ -139,9 +121,9 @@ module cachepool_cluster /// Per-cluster probe on the cluster status. Can be written by the cores to indicate /// to the overall system that the cluster is executing something. output logic cluster_probe_o, - /// AXI Core cluster in-port. - input axi_in_req_t axi_in_req_i, - output axi_in_resp_t axi_in_resp_o, + /// External peripheral REQRSP in-port (from TB/SoC, 32b narrow, refill_user_t). + input peri_narrow_req_t peri_ext_req_i, + output peri_narrow_rsp_t peri_ext_rsp_o, /// AXI Narrow out-port (UART) output axi_uart_req_t axi_narrow_req_o, input axi_uart_resp_t axi_narrow_resp_i, @@ -157,39 +139,14 @@ module cachepool_cluster // Imports // --------- import snitch_pkg::*; - import snitch_icache_pkg::icache_events_t; // --------- // Constants // --------- - /// Minimum width to hold the core number. - localparam int unsigned CoreIDWidth = cf_math_pkg::idx_width(NrCores); - - // Enlarge the address width for Spatz due to cache - localparam int unsigned TCDMAddrWidth = 32; - - // Core Request, SoC Request - localparam int unsigned NrNarrowMasters = 2; - localparam int unsigned WideIdWidthOut = AxiIdWidthOut; - localparam int unsigned WideIdWidthIn = WideIdWidthOut - $clog2(NumClusterMst); - - // Cache XBar configuration struct - localparam axi_pkg::xbar_cfg_t CacheXbarCfg = '{ - NoSlvPorts : NumClusterMst*NumTiles, - NoMstPorts : ClusterWideOutAxiPorts, - MaxMstTrans : MaxMstTrans, - MaxSlvTrans : MaxSlvTrans, - FallThrough : 1'b0, - LatencyMode : XbarLatency, - AxiIdWidthSlvPorts: WideIdWidthIn, - AxiIdUsedSlvPorts : WideIdWidthIn, - UniqueIds : 1'b0, - AxiAddrWidth : AxiAddrWidth, - AxiDataWidth : AxiDataWidth, - NoAddrRules : ClusterWideOutAxiPorts - 1, - default : '0 - }; + // Per-group wide AXI ID width, passed as AxiIdWidthOut to each group. + // With the FlooNoC mesh, this is independent of WideIdWidthOut (no star mux). + localparam int unsigned WideIdWidthIn = GroupWideIdWidth; // -------- // Typedefs @@ -197,548 +154,841 @@ module cachepool_cluster typedef logic [AxiAddrWidth-1:0] addr_t; typedef logic [AxiDataWidth-1:0] data_cache_t; typedef logic [AxiDataWidth/8-1:0] strb_cache_t; - typedef logic [WideIdWidthIn-1:0] id_cache_mst_t; typedef logic [WideIdWidthOut-1:0] id_cache_slv_t; typedef logic [AxiUserWidth-1:0] user_cache_t; - `AXI_TYPEDEF_ALL(axi_mst_cache, addr_t, id_cache_mst_t, data_cache_t, strb_cache_t, user_cache_t) - `AXI_TYPEDEF_ALL(axi_slv_cache, addr_t, id_cache_slv_t, data_cache_t, strb_cache_t, user_cache_t) - - `REG_BUS_TYPEDEF_ALL(reg_cache, addr_t, data_cache_t, strb_cache_t) - - typedef struct packed { - int unsigned idx; - addr_t start_addr; - addr_t end_addr; - } xbar_rule_t; - - `SNITCH_VM_TYPEDEF(AxiAddrWidth) + // Post-chimney AXI types (used for axi_cut and DRAM output). + `AXI_TYPEDEF_ALL(axi_slv_cache, addr_t, id_cache_slv_t, data_cache_t, strb_cache_t, user_cache_t) // ---------------- // Wire Definitions // ---------------- - // 1. AXI - axi_mst_cache_req_t [NumTiles-1:0][TileNarrowAxiPorts-1:0] axi_tile_req; - axi_mst_cache_resp_t [NumTiles-1:0][TileNarrowAxiPorts-1:0] axi_tile_rsp; - axi_slv_cache_req_t [ClusterWideOutAxiPorts-1 :0] wide_axi_slv_req; - axi_slv_cache_resp_t [ClusterWideOutAxiPorts-1 :0] wide_axi_slv_rsp; - axi_narrow_req_t [NumTiles-1:0][1:0] axi_out_req; - axi_narrow_resp_t [NumTiles-1:0][1:0] axi_out_resp; - - // 2. BootROM - reg_cache_req_t [NumTiles-1:0] bootrom_reg_req; - reg_cache_rsp_t [NumTiles-1:0] bootrom_reg_rsp; - - // 3. Peripherals - axi_addr_t private_start_addr; - icache_events_t [NrCores-1:0] icache_events; - logic icache_prefetch_enable; - logic [NrCores-1:0] cl_interrupt; - logic [$clog2(L1AddrWidth)-1:0] dynamic_offset; - logic [3:0] l1d_private; - cache_insn_t l1d_insn; - logic l1d_insn_valid; - logic [NumTiles-1:0] l1d_insn_ready; - logic [NumTiles-1:0] l1d_busy; - - // --------------- - // CachePool Tile - // --------------- - - cache_trans_req_t [NumL1CacheCtrl-1 :0] cache_refill_req; - cache_trans_rsp_t [NumL1CacheCtrl-1 :0] cache_refill_rsp; - - cache_trans_req_t [NumTiles-1 :0] cache_core_req; - cache_trans_rsp_t [NumTiles-1 :0] cache_core_rsp; + // 1. AXI — post-chimney wide AXI (one per L2/HBM channel). + axi_slv_cache_req_t [ClusterWideOutAxiPorts-1:0] wide_axi_slv_req; + axi_slv_cache_resp_t [ClusterWideOutAxiPorts-1:0] wide_axi_slv_rsp; + + // HBM0 peripheral demux port: narrowed 32b REQRSP with refill_user_t (same user as wide side). + peri_narrow_req_t hbm0_peri_narrow_req; + peri_narrow_rsp_t hbm0_peri_narrow_rsp; + + // 2. Peripherals + axi_addr_t private_start_addr; + logic icache_prefetch_enable; + logic [$clog2(L1AddrWidth)-1:0] dynamic_offset; + cache_insn_t l1d_insn; + logic l1d_insn_valid; + logic [NumTiles-1:0] l1d_insn_ready; + logic [NumTiles-1:0] l1d_busy; + logic [$clog2(NumL1CtrlTile):0] l1d_private; + + // Per-group error signals. + logic [NumGroups-1:0] group_error; + + // Direct-wire barrier: one bit per tile across all groups + logic [NumGroups-1:0][NumTilesPerGroup-1:0] tile_barrier; + logic barrier_done; + // Tile participation mask for the cluster-level barrier, software-configured + // via the HW_BARRIER_PARTICIPATION_MASK peripheral CSR (reset value: all tiles). + logic [NumTiles-1:0] barrier_participation_mask; - cache_trans_req_chan_t [NumTiles*NumClusterMst-1 :0] tile_req_chan; - cache_trans_rsp_chan_t [NumTiles*NumClusterMst-1 :0] tile_rsp_chan; - logic [NumTiles*NumClusterMst-1 :0] tile_req_valid, tile_req_ready, tile_rsp_valid, tile_rsp_ready; - - l2_req_t [ClusterWideOutAxiPorts-1 :0] l2_req; - l2_rsp_t [ClusterWideOutAxiPorts-1 :0] l2_rsp; + cachepool_cluster_barrier #( + .NrTiles ( NumTiles ) + ) i_cluster_barrier ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .tile_barrier_i ( tile_barrier ), + .barrier_done_o ( barrier_done ), + .barrier_mask_i ( barrier_participation_mask ) + ); - cache_trans_req_chan_t [ClusterWideOutAxiPorts-1 :0] l2_req_chan; - cache_trans_rsp_chan_t [ClusterWideOutAxiPorts-1 :0] l2_rsp_chan; - logic [ClusterWideOutAxiPorts-1 :0] l2_req_valid, l2_req_ready , l2_rsp_valid, l2_rsp_ready; + // Inter-group NoC mesh signals (indexed by group, then direction, then port) + noc_group_req_t [NumGroups-1:0][3:0][NumTilesPerGroup*NumNoCPortsPerTile-1:0] noc_req_out; + logic [NumGroups-1:0][3:0][NumTilesPerGroup*NumNoCPortsPerTile-1:0] noc_req_out_valid; + logic [NumGroups-1:0][3:0][NumTilesPerGroup*NumNoCPortsPerTile-1:0] noc_req_out_ready; + noc_group_req_t [NumGroups-1:0][3:0][NumTilesPerGroup*NumNoCPortsPerTile-1:0] noc_req_in; + logic [NumGroups-1:0][3:0][NumTilesPerGroup*NumNoCPortsPerTile-1:0] noc_req_in_valid; + logic [NumGroups-1:0][3:0][NumTilesPerGroup*NumNoCPortsPerTile-1:0] noc_req_in_ready; + noc_group_rsp_t [NumGroups-1:0][3:0][NumTilesPerGroup*NumNoCPortsPerTile-1:0] noc_rsp_out; + logic [NumGroups-1:0][3:0][NumTilesPerGroup*NumNoCPortsPerTile-1:0] noc_rsp_out_valid; + logic [NumGroups-1:0][3:0][NumTilesPerGroup*NumNoCPortsPerTile-1:0] noc_rsp_out_ready; + noc_group_rsp_t [NumGroups-1:0][3:0][NumTilesPerGroup*NumNoCPortsPerTile-1:0] noc_rsp_in; + logic [NumGroups-1:0][3:0][NumTilesPerGroup*NumNoCPortsPerTile-1:0] noc_rsp_in_valid; + logic [NumGroups-1:0][3:0][NumTilesPerGroup*NumNoCPortsPerTile-1:0] noc_rsp_in_ready; - typedef logic [$clog2(NumClusterMst*NumTiles)-1:0] l2_sel_t; - // one more bit for out-of-range alert - typedef logic [$clog2(ClusterWideOutAxiPorts) :0] tile_sel_err_t; - typedef logic [$clog2(ClusterWideOutAxiPorts)-1:0] tile_sel_t; + // --------------- + // CachePool Group + // --------------- - // Which l2 we want to select for each req - tile_sel_err_t [NumTiles*NumClusterMst-1 :0] tile_sel_err; - tile_sel_t [NumTiles*NumClusterMst-1 :0] tile_sel; - // Which tile we selected for each req - l2_sel_t [ClusterWideOutAxiPorts-1 :0] tile_selected; - // which tile we want to select for each rsp - l2_sel_t [ClusterWideOutAxiPorts-1 :0] l2_sel; - // What is the priority for response wiring? - // Here we want to make sure the responses from one burst - // continues until done - // If the rsp is a burst with blen != 0, then we will keep - // the rr same, until got a burst rsp with blen == 0 - tile_sel_t [NumTiles*NumClusterMst-1 :0] l2_rsp_rr; + // Per-group L2 refill TCDM mesh signals [NumGroupsX][NumGroupsY][direction 0..3] + l2_noc_req_t [NumGroupsX-1:0][NumGroupsY-1:0][3:0] l2_req_out; + logic [NumGroupsX-1:0][NumGroupsY-1:0][3:0] l2_req_out_valid; + logic [NumGroupsX-1:0][NumGroupsY-1:0][3:0] l2_req_out_ready; + l2_noc_req_t [NumGroupsX-1:0][NumGroupsY-1:0][3:0] l2_req_in; + logic [NumGroupsX-1:0][NumGroupsY-1:0][3:0] l2_req_in_valid; + logic [NumGroupsX-1:0][NumGroupsY-1:0][3:0] l2_req_in_ready; + l2_noc_rsp_t [NumGroupsX-1:0][NumGroupsY-1:0][3:0] l2_rsp_out; + logic [NumGroupsX-1:0][NumGroupsY-1:0][3:0] l2_rsp_out_valid; + logic [NumGroupsX-1:0][NumGroupsY-1:0][3:0] l2_rsp_out_ready; + l2_noc_rsp_t [NumGroupsX-1:0][NumGroupsY-1:0][3:0] l2_rsp_in; + logic [NumGroupsX-1:0][NumGroupsY-1:0][3:0] l2_rsp_in_valid; + logic [NumGroupsX-1:0][NumGroupsY-1:0][3:0] l2_rsp_in_ready; + + assign error_o = |group_error; + + for (genvar gy = 0; gy < NumGroupsY; gy++) begin : gen_group_y + for (genvar gx = 0; gx < NumGroupsX; gx++) begin : gen_group_x + // Flat group index: row-major (matches L1 mesh wiring which uses gx + gy*NumGroupsX) + localparam int unsigned g = gy * NumGroupsX + gx; + cachepool_group_noc_wrapper #( + .AxiAddrWidth ( AxiAddrWidth ), + .AxiDataWidth ( AxiDataWidth ), + .AxiIdWidthIn ( AxiIdWidthIn ), + .AxiIdWidthOut ( WideIdWidthIn ), + .AxiUserWidth ( AxiUserWidth ), + .BootAddr ( BootAddr ), + .UartAddr ( UartAddr ), + .ClusterPeriphSize ( ClusterPeriphSize ), + .NrCores ( NumCoreGroup ), + .TCDMDepth ( TCDMDepth ), + .NrBanks ( NrBanks / NumGroups ), + .ICacheLineWidth ( ICacheLineWidth ), + .ICacheLineCount ( ICacheLineCount ), + .ICacheSets ( ICacheSets ), + .FPUImplementation ( FPUImplementation ), + .NumSpatzFPUs ( NumSpatzFPUs ), + .NumSpatzIPUs ( NumSpatzIPUs ), + .SnitchPMACfg ( SnitchPMACfg ), + .NumIntOutstandingLoads ( NumIntOutstandingLoads ), + .NumIntOutstandingMem ( NumIntOutstandingMem ), + .NumSpatzOutstandingLoads ( NumSpatzOutstandingLoads ), + .axi_out_req_t ( logic ), + .axi_out_resp_t ( logic ), + .RegisterOffloadRsp ( RegisterOffloadRsp ), + .RegisterCoreReq ( RegisterCoreReq ), + .RegisterCoreRsp ( RegisterCoreRsp ), + .RegisterTCDMCuts ( RegisterTCDMCuts ), + .RegisterExt ( RegisterExt ), + .XbarLatency ( XbarLatency ), + .MaxMstTrans ( MaxMstTrans ), + .MaxSlvTrans ( MaxSlvTrans ) + ) i_group ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .impl_i ( impl_i ), + .error_o ( group_error[g] ), + .debug_req_i ( debug_req_i ), + .meip_i ( meip_i ), + .mtip_i ( mtip_i ), + .msip_i ( msip_i ), + .hart_base_id_i ( hart_base_id_i + 10'(g * NumCoreGroup) ), + .tile_base_id_i ( TileIDWidth'(g * NumTilesPerGroup) ), + .cluster_base_addr_i ( cluster_base_addr_i ), + .private_start_addr_i ( private_start_addr ), + // L2 refill TCDM mesh (4 directions) + .l2_req_o ( l2_req_out [gx][gy] ), + .l2_req_valid_o ( l2_req_out_valid [gx][gy] ), + .l2_req_ready_i ( l2_req_out_ready [gx][gy] ), + .l2_req_i ( l2_req_in [gx][gy] ), + .l2_req_valid_i ( l2_req_in_valid [gx][gy] ), + .l2_req_ready_o ( l2_req_in_ready [gx][gy] ), + .l2_rsp_o ( l2_rsp_out [gx][gy] ), + .l2_rsp_valid_o ( l2_rsp_out_valid [gx][gy] ), + .l2_rsp_ready_i ( l2_rsp_out_ready [gx][gy] ), + .l2_rsp_i ( l2_rsp_in [gx][gy] ), + .l2_rsp_valid_i ( l2_rsp_in_valid [gx][gy] ), + .l2_rsp_ready_o ( l2_rsp_in_ready [gx][gy] ), + .l2_id_i ( floo_cachepool_noc_pkg::id_t'(gx * NumGroupsY + gy) ), + .l2_route_table_i ( floo_cachepool_noc_pkg::RoutingTables[gx * NumGroupsY + gy] ), + // Peripherals + .icache_events_o ( /* unused */ ), + .icache_prefetch_enable_i ( icache_prefetch_enable ), + .cl_interrupt_i ( '0 ), + .dynamic_offset_i ( dynamic_offset ), + .l1d_private_i ( l1d_private ), + .l1d_insn_i ( l1d_insn ), + .l1d_insn_valid_i ( l1d_insn_valid ), + .l1d_insn_ready_o ( l1d_insn_ready[g*NumTilesPerGroup +: NumTilesPerGroup]), + .l1d_busy_i ( l1d_busy [g*NumTilesPerGroup +: NumTilesPerGroup]), + .group_xy_id_i ( group_xy_id_t'{x: gx, + y: gy, + port_id: 1'b0} ), + .noc_req_o ( noc_req_out [g] ), + .noc_req_valid_o ( noc_req_out_valid[g] ), + .noc_req_ready_i ( noc_req_out_ready[g] ), + .noc_req_i ( noc_req_in [g] ), + .noc_req_valid_i ( noc_req_in_valid [g] ), + .noc_req_ready_o ( noc_req_in_ready [g] ), + .noc_rsp_o ( noc_rsp_out [g] ), + .noc_rsp_valid_o ( noc_rsp_out_valid[g] ), + .noc_rsp_ready_i ( noc_rsp_out_ready[g] ), + .noc_rsp_i ( noc_rsp_in [g] ), + .noc_rsp_valid_i ( noc_rsp_in_valid [g] ), + .noc_rsp_ready_o ( noc_rsp_in_ready [g] ), + // Direct-wire barrier + .tile_barrier_o ( tile_barrier [g] ), + .barrier_done_i ( barrier_done ) + ); + end + end - logic [NumTiles*NumClusterMst-1 :0] rr_lock_d, rr_lock_q; - tile_sel_t [NumTiles*NumClusterMst-1 :0] l2_prio_d, l2_prio_q; + // ---------------------------- + // Inter-group NoC mesh wiring + // ---------------------------- + + // East-West (horizontal) interior connections + for (genvar gx = 0; gx < NumGroupsX-1; gx++) begin : gen_ew_conn + for (genvar gy = 0; gy < NumGroupsY; gy++) begin : gen_ew_conn_y + // East output of (gx,gy) → West input of (gx+1,gy) + assign noc_req_in [gx+1 + gy*NumGroupsX][3] = noc_req_out [gx + gy*NumGroupsX][1]; + assign noc_req_in_valid [gx+1 + gy*NumGroupsX][3] = noc_req_out_valid[gx + gy*NumGroupsX][1]; + assign noc_req_out_ready[gx + gy*NumGroupsX][1] = noc_req_in_ready [gx+1 + gy*NumGroupsX][3]; + assign noc_rsp_in [gx+1 + gy*NumGroupsX][3] = noc_rsp_out [gx + gy*NumGroupsX][1]; + assign noc_rsp_in_valid [gx+1 + gy*NumGroupsX][3] = noc_rsp_out_valid[gx + gy*NumGroupsX][1]; + assign noc_rsp_out_ready[gx + gy*NumGroupsX][1] = noc_rsp_in_ready [gx+1 + gy*NumGroupsX][3]; + // West output of (gx+1,gy) → East input of (gx,gy) + assign noc_req_in [gx + gy*NumGroupsX][1] = noc_req_out [gx+1 + gy*NumGroupsX][3]; + assign noc_req_in_valid [gx + gy*NumGroupsX][1] = noc_req_out_valid[gx+1 + gy*NumGroupsX][3]; + assign noc_req_out_ready[gx+1 + gy*NumGroupsX][3] = noc_req_in_ready [gx + gy*NumGroupsX][1]; + assign noc_rsp_in [gx + gy*NumGroupsX][1] = noc_rsp_out [gx+1 + gy*NumGroupsX][3]; + assign noc_rsp_in_valid [gx + gy*NumGroupsX][1] = noc_rsp_out_valid[gx+1 + gy*NumGroupsX][3]; + assign noc_rsp_out_ready[gx+1 + gy*NumGroupsX][3] = noc_rsp_in_ready [gx + gy*NumGroupsX][1]; + end + end + // North-South (vertical) interior connections + for (genvar gx = 0; gx < NumGroupsX; gx++) begin : gen_ns_conn + for (genvar gy = 0; gy < NumGroupsY-1; gy++) begin : gen_ns_conn_y + // North output of (gx,gy) (dir 0) → South input of (gx,gy+1) (dir 2) + assign noc_req_in [gx + (gy+1)*NumGroupsX][2] = noc_req_out [gx + gy *NumGroupsX][0]; + assign noc_req_in_valid [gx + (gy+1)*NumGroupsX][2] = noc_req_out_valid[gx + gy *NumGroupsX][0]; + assign noc_req_out_ready[gx + gy *NumGroupsX][0] = noc_req_in_ready [gx + (gy+1)*NumGroupsX][2]; + assign noc_rsp_in [gx + (gy+1)*NumGroupsX][2] = noc_rsp_out [gx + gy *NumGroupsX][0]; + assign noc_rsp_in_valid [gx + (gy+1)*NumGroupsX][2] = noc_rsp_out_valid[gx + gy *NumGroupsX][0]; + assign noc_rsp_out_ready[gx + gy *NumGroupsX][0] = noc_rsp_in_ready [gx + (gy+1)*NumGroupsX][2]; + // South output of (gx,gy+1) (dir 2) → North input of (gx,gy) (dir 0) + assign noc_req_in [gx + gy *NumGroupsX][0] = noc_req_out [gx + (gy+1)*NumGroupsX][2]; + assign noc_req_in_valid [gx + gy *NumGroupsX][0] = noc_req_out_valid[gx + (gy+1)*NumGroupsX][2]; + assign noc_req_out_ready[gx + (gy+1)*NumGroupsX][2] = noc_req_in_ready [gx + gy *NumGroupsX][0]; + assign noc_rsp_in [gx + gy *NumGroupsX][0] = noc_rsp_out [gx + (gy+1)*NumGroupsX][2]; + assign noc_rsp_in_valid [gx + gy *NumGroupsX][0] = noc_rsp_out_valid[gx + (gy+1)*NumGroupsX][2]; + assign noc_rsp_out_ready[gx + (gy+1)*NumGroupsX][2] = noc_rsp_in_ready [gx + gy *NumGroupsX][0]; + end + end - l2_sel_t [ClusterWideOutAxiPorts-1:0] port_id; + // West boundary: gx=0 has no West neighbor (dir 3) + for (genvar gy = 0; gy < NumGroupsY; gy++) begin : gen_west_bnd + assign noc_req_in [gy*NumGroupsX][3] = '0; + assign noc_req_in_valid[gy*NumGroupsX][3] = '0; + assign noc_req_out_ready[gy*NumGroupsX][3] = '1; + assign noc_rsp_in [gy*NumGroupsX][3] = '0; + assign noc_rsp_in_valid[gy*NumGroupsX][3] = '0; + assign noc_rsp_out_ready[gy*NumGroupsX][3] = '1; +`ifndef SYNTHESIS + for (genvar p = 0; p < NumTilesPerGroup*NumNoCPortsPerTile; p++) begin : gen_west_chk + always_ff @(posedge clk_i) begin + if (rst_ni && noc_req_out_valid[gy*NumGroupsX][3][p]) + $error("[L1_MESH] REQ lost at West G(0,%0d) p%0d | src=(%0d,%0d) t%0d dst=(%0d,%0d) addr=%08x wen=%0b dst_tile=%0d", + gy, p, + noc_req_out[gy*NumGroupsX][3][p].hdr.src_id.x, + noc_req_out[gy*NumGroupsX][3][p].hdr.src_id.y, + noc_req_out[gy*NumGroupsX][3][p].hdr.src_tile_id, + noc_req_out[gy*NumGroupsX][3][p].hdr.dst_id.x, + noc_req_out[gy*NumGroupsX][3][p].hdr.dst_id.y, + noc_req_out[gy*NumGroupsX][3][p].payload.addr, + noc_req_out[gy*NumGroupsX][3][p].payload.write, + noc_req_out[gy*NumGroupsX][3][p].payload.user.dst_tile_id); + if (rst_ni && noc_rsp_out_valid[gy*NumGroupsX][3][p]) + $error("[L1_MESH] RSP lost at West G(0,%0d) p%0d | src=(%0d,%0d) t%0d dst=(%0d,%0d) data=%08x", + gy, p, + noc_rsp_out[gy*NumGroupsX][3][p].hdr.src_id.x, + noc_rsp_out[gy*NumGroupsX][3][p].hdr.src_id.y, + noc_rsp_out[gy*NumGroupsX][3][p].hdr.src_tile_id, + noc_rsp_out[gy*NumGroupsX][3][p].hdr.dst_id.x, + noc_rsp_out[gy*NumGroupsX][3][p].hdr.dst_id.y, + noc_rsp_out[gy*NumGroupsX][3][p].payload.data); + end + end +`endif + end - for (genvar i = 0; i < ClusterWideOutAxiPorts; i ++) begin - assign port_id[i] = l2_rsp[i].p.user.tile_id * NumClusterMst + l2_rsp[i].p.user.bank_id; + // East boundary: gx=NumGroupsX-1 has no East neighbor (dir 1) + for (genvar gy = 0; gy < NumGroupsY; gy++) begin : gen_east_bnd + assign noc_req_in [(NumGroupsX-1) + gy*NumGroupsX][1] = '0; + assign noc_req_in_valid [(NumGroupsX-1) + gy*NumGroupsX][1] = '0; + assign noc_req_out_ready[(NumGroupsX-1) + gy*NumGroupsX][1] = '1; + assign noc_rsp_in [(NumGroupsX-1) + gy*NumGroupsX][1] = '0; + assign noc_rsp_in_valid [(NumGroupsX-1) + gy*NumGroupsX][1] = '0; + assign noc_rsp_out_ready[(NumGroupsX-1) + gy*NumGroupsX][1] = '1; +`ifndef SYNTHESIS + for (genvar p = 0; p < NumTilesPerGroup*NumNoCPortsPerTile; p++) begin : gen_east_chk + always_ff @(posedge clk_i) begin + if (rst_ni && noc_req_out_valid[(NumGroupsX-1) + gy*NumGroupsX][1][p]) + $error("[L1_MESH] REQ lost at East G(%0d,%0d) p%0d | src=(%0d,%0d) t%0d dst=(%0d,%0d) addr=%08x wen=%0b dst_tile=%0d", + NumGroupsX-1, gy, p, + noc_req_out[(NumGroupsX-1) + gy*NumGroupsX][1][p].hdr.src_id.x, + noc_req_out[(NumGroupsX-1) + gy*NumGroupsX][1][p].hdr.src_id.y, + noc_req_out[(NumGroupsX-1) + gy*NumGroupsX][1][p].hdr.src_tile_id, + noc_req_out[(NumGroupsX-1) + gy*NumGroupsX][1][p].hdr.dst_id.x, + noc_req_out[(NumGroupsX-1) + gy*NumGroupsX][1][p].hdr.dst_id.y, + noc_req_out[(NumGroupsX-1) + gy*NumGroupsX][1][p].payload.addr, + noc_req_out[(NumGroupsX-1) + gy*NumGroupsX][1][p].payload.write, + noc_req_out[(NumGroupsX-1) + gy*NumGroupsX][1][p].payload.user.dst_tile_id); + if (rst_ni && noc_rsp_out_valid[(NumGroupsX-1) + gy*NumGroupsX][1][p]) + $error("[L1_MESH] RSP lost at East G(%0d,%0d) p%0d | src=(%0d,%0d) t%0d dst=(%0d,%0d) data=%08x", + NumGroupsX-1, gy, p, + noc_rsp_out[(NumGroupsX-1) + gy*NumGroupsX][1][p].hdr.src_id.x, + noc_rsp_out[(NumGroupsX-1) + gy*NumGroupsX][1][p].hdr.src_id.y, + noc_rsp_out[(NumGroupsX-1) + gy*NumGroupsX][1][p].hdr.src_tile_id, + noc_rsp_out[(NumGroupsX-1) + gy*NumGroupsX][1][p].hdr.dst_id.x, + noc_rsp_out[(NumGroupsX-1) + gy*NumGroupsX][1][p].hdr.dst_id.y, + noc_rsp_out[(NumGroupsX-1) + gy*NumGroupsX][1][p].payload.data); + end + end +`endif end + // South boundary: gy=0 has no South neighbor (dir 2) + for (genvar gx = 0; gx < NumGroupsX; gx++) begin : gen_south_bnd + assign noc_req_in [gx][2] = '0; + assign noc_req_in_valid [gx][2] = '0; + assign noc_req_out_ready[gx][2] = '1; + assign noc_rsp_in [gx][2] = '0; + assign noc_rsp_in_valid [gx][2] = '0; + assign noc_rsp_out_ready[gx][2] = '1; +`ifndef SYNTHESIS + for (genvar p = 0; p < NumTilesPerGroup*NumNoCPortsPerTile; p++) begin : gen_south_chk + always_ff @(posedge clk_i) begin + if (rst_ni && noc_req_out_valid[gx][2][p]) + $error("[L1_MESH] REQ lost at South G(%0d,0) p%0d | src=(%0d,%0d) t%0d dst=(%0d,%0d) addr=%08x wen=%0b dst_tile=%0d", + gx, p, + noc_req_out[gx][2][p].hdr.src_id.x, + noc_req_out[gx][2][p].hdr.src_id.y, + noc_req_out[gx][2][p].hdr.src_tile_id, + noc_req_out[gx][2][p].hdr.dst_id.x, + noc_req_out[gx][2][p].hdr.dst_id.y, + noc_req_out[gx][2][p].payload.addr, + noc_req_out[gx][2][p].payload.write, + noc_req_out[gx][2][p].payload.user.dst_tile_id); + if (rst_ni && noc_rsp_out_valid[gx][2][p]) + $error("[L1_MESH] RSP lost at South G(%0d,0) p%0d | src=(%0d,%0d) t%0d dst=(%0d,%0d) data=%08x", + gx, p, + noc_rsp_out[gx][2][p].hdr.src_id.x, + noc_rsp_out[gx][2][p].hdr.src_id.y, + noc_rsp_out[gx][2][p].hdr.src_tile_id, + noc_rsp_out[gx][2][p].hdr.dst_id.x, + noc_rsp_out[gx][2][p].hdr.dst_id.y, + noc_rsp_out[gx][2][p].payload.data); + end + end +`endif + end - if (Burst_Enable) begin : gen_burst_ext_sel - `FF(rr_lock_q, rr_lock_d, 1'b0) - `FF(l2_prio_q, l2_prio_d, 1'b0) + // North boundary: gy=NumGroupsY-1 has no North neighbor (dir 0) + for (genvar gx = 0; gx < NumGroupsX; gx++) begin : gen_north_bnd + assign noc_req_in [gx + (NumGroupsY-1)*NumGroupsX][0] = '0; + assign noc_req_in_valid [gx + (NumGroupsY-1)*NumGroupsX][0] = '0; + assign noc_req_out_ready[gx + (NumGroupsY-1)*NumGroupsX][0] = '1; + assign noc_rsp_in [gx + (NumGroupsY-1)*NumGroupsX][0] = '0; + assign noc_rsp_in_valid [gx + (NumGroupsY-1)*NumGroupsX][0] = '0; + assign noc_rsp_out_ready[gx + (NumGroupsY-1)*NumGroupsX][0] = '1; +`ifndef SYNTHESIS + for (genvar p = 0; p < NumTilesPerGroup*NumNoCPortsPerTile; p++) begin : gen_north_chk + always_ff @(posedge clk_i) begin + if (rst_ni && noc_req_out_valid[gx + (NumGroupsY-1)*NumGroupsX][0][p]) + $error("[L1_MESH] REQ lost at North G(%0d,%0d) p%0d | src=(%0d,%0d) t%0d dst=(%0d,%0d) addr=%08x wen=%0b dst_tile=%0d", + gx, NumGroupsY-1, p, + noc_req_out[gx + (NumGroupsY-1)*NumGroupsX][0][p].hdr.src_id.x, + noc_req_out[gx + (NumGroupsY-1)*NumGroupsX][0][p].hdr.src_id.y, + noc_req_out[gx + (NumGroupsY-1)*NumGroupsX][0][p].hdr.src_tile_id, + noc_req_out[gx + (NumGroupsY-1)*NumGroupsX][0][p].hdr.dst_id.x, + noc_req_out[gx + (NumGroupsY-1)*NumGroupsX][0][p].hdr.dst_id.y, + noc_req_out[gx + (NumGroupsY-1)*NumGroupsX][0][p].payload.addr, + noc_req_out[gx + (NumGroupsY-1)*NumGroupsX][0][p].payload.write, + noc_req_out[gx + (NumGroupsY-1)*NumGroupsX][0][p].payload.user.dst_tile_id); + if (rst_ni && noc_rsp_out_valid[gx + (NumGroupsY-1)*NumGroupsX][0][p]) + $error("[L1_MESH] RSP lost at North G(%0d,%0d) p%0d | src=(%0d,%0d) t%0d dst=(%0d,%0d) data=%08x", + gx, NumGroupsY-1, p, + noc_rsp_out[gx + (NumGroupsY-1)*NumGroupsX][0][p].hdr.src_id.x, + noc_rsp_out[gx + (NumGroupsY-1)*NumGroupsX][0][p].hdr.src_id.y, + noc_rsp_out[gx + (NumGroupsY-1)*NumGroupsX][0][p].hdr.src_tile_id, + noc_rsp_out[gx + (NumGroupsY-1)*NumGroupsX][0][p].hdr.dst_id.x, + noc_rsp_out[gx + (NumGroupsY-1)*NumGroupsX][0][p].hdr.dst_id.y, + noc_rsp_out[gx + (NumGroupsY-1)*NumGroupsX][0][p].payload.data); + end + end +`endif + end - for (genvar port = 0; port < NumTiles*NumClusterMst; port ++) begin : gen_rsp_rr - tile_sel_t l2_rr; - logic [ClusterWideOutAxiPorts-1:0] arb_valid; - for (genvar i = 0; i < ClusterWideOutAxiPorts; i ++) begin - // Used to check the round-robin selection - assign arb_valid[i] = (port_id[i] == port) & l2_rsp_valid[i]; + // ------------------------------------------------------- + // L2 Refill TCDM Mesh + HBM Edge Adapters + // ------------------------------------------------------- + + if (NumGroups > 1) begin : gen_l2_refill_mesh + + // -------------------------------------------------- + // L2 mesh interior cross-connections (valid/ready) + // -------------------------------------------------- + + // East-West connections + for (genvar gx = 0; gx < NumGroupsX - 1; gx++) begin : gen_l2_ew + for (genvar gy = 0; gy < NumGroupsY; gy++) begin : gen_l2_ew_y + // East(1) output of (gx,gy) → West(3) input of (gx+1,gy) + assign l2_req_in [gx+1][gy][3] = l2_req_out [gx] [gy][1]; + assign l2_req_in_valid [gx+1][gy][3] = l2_req_out_valid [gx] [gy][1]; + assign l2_req_out_ready[gx] [gy][1] = l2_req_in_ready [gx+1][gy][3]; + assign l2_rsp_in [gx+1][gy][3] = l2_rsp_out [gx] [gy][1]; + assign l2_rsp_in_valid [gx+1][gy][3] = l2_rsp_out_valid [gx] [gy][1]; + assign l2_rsp_out_ready[gx] [gy][1] = l2_rsp_in_ready [gx+1][gy][3]; + // West(3) output of (gx+1,gy) → East(1) input of (gx,gy) + assign l2_req_in [gx] [gy][1] = l2_req_out [gx+1][gy][3]; + assign l2_req_in_valid [gx] [gy][1] = l2_req_out_valid [gx+1][gy][3]; + assign l2_req_out_ready[gx+1][gy][3] = l2_req_in_ready [gx] [gy][1]; + assign l2_rsp_in [gx] [gy][1] = l2_rsp_out [gx+1][gy][3]; + assign l2_rsp_in_valid [gx] [gy][1] = l2_rsp_out_valid [gx+1][gy][3]; + assign l2_rsp_out_ready[gx+1][gy][3] = l2_rsp_in_ready [gx] [gy][1]; end + end - always_comb begin - l2_prio_d[port] = l2_prio_q[port]; - rr_lock_d[port] = rr_lock_q[port]; - - // Determine the priority we give - // round-robin or locked to previous value? - if (|arb_valid) begin - if (rr_lock_q[port]) begin - // rr is locked because of burst - l2_prio_d[port] = l2_prio_q[port]; - end else begin - l2_prio_d[port] = l2_rr; - end - end - // assigned to xbar rr_i - l2_rsp_rr[port] = l2_prio_d[port]; - - // Lock judgement - // Should it work on the l2_rsp instead of tile_rsp? - if (tile_rsp_chan[port].user.burst.is_burst & |arb_valid) begin - // We got a burst response - if (tile_rsp_chan[port].user.burst.burst_len == 0) begin - // this is the last transaction within a burt, remove lock - rr_lock_d[port] = 1'b0; - end else begin - // the burst response is not finished yet, lock the rr - rr_lock_d[port] = 1'b1; - end - end + // North-South connections + for (genvar gx = 0; gx < NumGroupsX; gx++) begin : gen_l2_ns + for (genvar gy = 0; gy < NumGroupsY - 1; gy++) begin : gen_l2_ns_y + // North(0) output of (gx,gy) → South(2) input of (gx,gy+1) + assign l2_req_in [gx][gy+1][2] = l2_req_out [gx][gy] [0]; + assign l2_req_in_valid [gx][gy+1][2] = l2_req_out_valid [gx][gy] [0]; + assign l2_req_out_ready[gx][gy] [0] = l2_req_in_ready [gx][gy+1][2]; + assign l2_rsp_in [gx][gy+1][2] = l2_rsp_out [gx][gy] [0]; + assign l2_rsp_in_valid [gx][gy+1][2] = l2_rsp_out_valid [gx][gy] [0]; + assign l2_rsp_out_ready[gx][gy] [0] = l2_rsp_in_ready [gx][gy+1][2]; + // South(2) output of (gx,gy+1) → North(0) input of (gx,gy) + assign l2_req_in [gx][gy] [0] = l2_req_out [gx][gy+1][2]; + assign l2_req_in_valid [gx][gy] [0] = l2_req_out_valid [gx][gy+1][2]; + assign l2_req_out_ready[gx][gy+1][2] = l2_req_in_ready [gx][gy] [0]; + assign l2_rsp_in [gx][gy] [0] = l2_rsp_out [gx][gy+1][2]; + assign l2_rsp_in_valid [gx][gy] [0] = l2_rsp_out_valid [gx][gy+1][2]; + assign l2_rsp_out_ready[gx][gy+1][2] = l2_rsp_in_ready [gx][gy] [0]; end + end - // We use the rr_arb_tree to get the round-robin selection - // No data is needed here, only need the handshaking - rr_arb_tree #( - .NumIn ( ClusterWideOutAxiPorts ), - .DataType ( logic ), - .ExtPrio ( 1'b0 ), - .AxiVldRdy ( 1'b1 ), - .LockIn ( 1'b1 ) - ) i_rr_arb_tree ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .flush_i ( '0 ), - .rr_i ( '0 ), - .req_i ( arb_valid ), - .gnt_o ( /*not used*/ ), - .data_i ( '0 ), - .req_o ( /*not used*/ ), - .gnt_i ( tile_rsp_ready[port] ), - .data_o ( /*not used*/ ), - .idx_o ( l2_rr ) - ); + // -------------------------------------------------- + // Boundary tie-offs (North/South edges) + // -------------------------------------------------- + + // North boundary (gy=NumGroupsY-1, direction North=0) + for (genvar gx = 0; gx < NumGroupsX; gx++) begin : gen_l2_north_bnd + assign l2_req_in [gx][NumGroupsY-1][0] = '0; + assign l2_req_in_valid [gx][NumGroupsY-1][0] = 1'b0; + assign l2_req_out_ready[gx][NumGroupsY-1][0] = 1'b1; + assign l2_rsp_in [gx][NumGroupsY-1][0] = '0; + assign l2_rsp_in_valid [gx][NumGroupsY-1][0] = 1'b0; + assign l2_rsp_out_ready[gx][NumGroupsY-1][0] = 1'b1; end - end else begin - assign l2_prio_d = '0; - assign l2_prio_q = '0; - assign rr_lock_d = '0; - assign rr_lock_q = '0; - assign l2_rsp_rr = '0; - end - if (NumTiles > 1) begin : gen_group - cachepool_group #( - .AxiAddrWidth ( AxiAddrWidth ), - .AxiDataWidth ( AxiDataWidth ), - .AxiIdWidthIn ( AxiIdWidthIn ), - .AxiIdWidthOut ( WideIdWidthIn ), - .AxiUserWidth ( AxiUserWidth ), - .BootAddr ( BootAddr ), - .UartAddr ( UartAddr ), - .ClusterPeriphSize ( ClusterPeriphSize ), - .NrCores ( NrCores ), - .TCDMDepth ( TCDMDepth ), - .NrBanks ( NrBanks ), - .ICacheLineWidth ( ICacheLineWidth ), - .ICacheLineCount ( ICacheLineCount ), - .ICacheSets ( ICacheSets ), - .FPUImplementation ( FPUImplementation ), - .NumSpatzFPUs ( NumSpatzFPUs ), - .NumSpatzIPUs ( NumSpatzIPUs ), - .SnitchPMACfg ( SnitchPMACfg ), - .NumIntOutstandingLoads ( NumIntOutstandingLoads ), - .NumIntOutstandingMem ( NumIntOutstandingMem ), - .NumSpatzOutstandingLoads ( NumSpatzOutstandingLoads ), - .axi_in_req_t ( axi_in_req_t ), - .axi_in_resp_t ( axi_in_resp_t ), - .axi_narrow_req_t ( axi_narrow_req_t ), - .axi_narrow_resp_t ( axi_narrow_resp_t ), - .axi_out_req_t ( axi_mst_cache_req_t ), - .axi_out_resp_t ( axi_mst_cache_resp_t ), - .Xdma ( Xdma ), - .DMAAxiReqFifoDepth ( DMAAxiReqFifoDepth ), - .DMAReqFifoDepth ( DMAReqFifoDepth ), - .RegisterOffloadRsp ( RegisterOffloadRsp ), - .RegisterCoreReq ( RegisterCoreReq ), - .RegisterCoreRsp ( RegisterCoreRsp ), - .RegisterTCDMCuts ( RegisterTCDMCuts ), - .RegisterExt ( RegisterExt ), - .XbarLatency ( XbarLatency ), - .MaxMstTrans ( MaxMstTrans ), - .MaxSlvTrans ( MaxSlvTrans ) - ) i_group ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .impl_i ( impl_i ), - .error_o ( error_o ), - .debug_req_i ( debug_req_i ), - .meip_i ( meip_i ), - .mtip_i ( mtip_i ), - .msip_i ( msip_i ), - .hart_base_id_i ( hart_base_id_i ), - .cluster_base_addr_i ( cluster_base_addr_i ), - .private_start_addr_i ( private_start_addr ), - .axi_narrow_req_o ( axi_out_req ), - .axi_narrow_rsp_i ( axi_out_resp ), - .axi_wide_req_o ( axi_tile_req ), - .axi_wide_rsp_i ( axi_tile_rsp ), - // Cache Refill Ports - .cache_refill_req_o ( cache_refill_req ), - .cache_refill_rsp_i ( cache_refill_rsp ), - // Peripherals - .icache_events_o ( icache_events ), - .icache_prefetch_enable_i ( icache_prefetch_enable ), - .cl_interrupt_i ( cl_interrupt ), - .dynamic_offset_i ( dynamic_offset ), - .l1d_private_i ( l1d_private ), - .l1d_insn_i ( l1d_insn ), - .l1d_insn_valid_i ( l1d_insn_valid ), - .l1d_insn_ready_o ( l1d_insn_ready ), - .l1d_busy_i ( l1d_busy ) - ); - // TODO: 2 axi ports converted lost correct assignments - // 1. tile id? - // 2. mux then convert? - for (genvar t = 0; t < NumTiles; t ++) begin : gen_axi_converter - axi_to_reqrsp #( - .axi_req_t ( axi_mst_cache_req_t ), - .axi_rsp_t ( axi_mst_cache_resp_t ), - .AddrWidth ( AxiAddrWidth ), - .DataWidth ( AxiDataWidth ), - .UserWidth ( $bits(refill_user_t) ), - .IdWidth ( AxiIdWidthIn ), - .BufDepth ( NumSpatzOutstandingLoads ), - .reqrsp_req_t ( cache_trans_req_t ), - .reqrsp_rsp_t ( cache_trans_rsp_t ) - ) i_axi2reqrsp ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .busy_o ( ), - .axi_req_i ( axi_tile_req [t][TileMem] ), - .axi_rsp_o ( axi_tile_rsp [t][TileMem] ), - .reqrsp_req_o ( cache_core_req[t] ), - .reqrsp_rsp_i ( cache_core_rsp[t] ) - ); + // South boundary (gy=0, direction South=2) + for (genvar gx = 0; gx < NumGroupsX; gx++) begin : gen_l2_south_bnd + assign l2_req_in [gx][0][2] = '0; + assign l2_req_in_valid [gx][0][2] = 1'b0; + assign l2_req_out_ready[gx][0][2] = 1'b1; + assign l2_rsp_in [gx][0][2] = '0; + assign l2_rsp_in_valid [gx][0][2] = 1'b0; + assign l2_rsp_out_ready[gx][0][2] = 1'b1; end - end else begin : gen_tile - cachepool_tile #( - .AxiAddrWidth ( AxiAddrWidth ), - .AxiDataWidth ( AxiDataWidth ), - .AxiIdWidthIn ( AxiIdWidthIn ), - .AxiIdWidthOut ( WideIdWidthIn ), - .AxiUserWidth ( AxiUserWidth ), - .BootAddr ( BootAddr ), - .UartAddr ( UartAddr ), - .ClusterPeriphSize ( ClusterPeriphSize ), - .NrCores ( NrCores ), - .TCDMDepth ( TCDMDepth ), - .NrBanks ( NrBanks ), - .ICacheLineWidth ( ICacheLineWidth ), - .ICacheLineCount ( ICacheLineCount ), - .ICacheSets ( ICacheSets ), - .FPUImplementation ( FPUImplementation ), - .NumSpatzFPUs ( NumSpatzFPUs ), - .NumSpatzIPUs ( NumSpatzIPUs ), - .SnitchPMACfg ( SnitchPMACfg ), - .TileIDWidth ( 1 ), - .NumIntOutstandingLoads ( NumIntOutstandingLoads ), - .NumIntOutstandingMem ( NumIntOutstandingMem ), - .NumSpatzOutstandingLoads ( NumSpatzOutstandingLoads ), - .axi_in_req_t ( axi_in_req_t ), - .axi_in_resp_t ( axi_in_resp_t ), - .axi_narrow_req_t ( axi_narrow_req_t ), - .axi_narrow_resp_t ( axi_narrow_resp_t ), - .axi_out_req_t ( axi_mst_cache_req_t ), - .axi_out_resp_t ( axi_mst_cache_resp_t ), - .Xdma ( Xdma ), - .DMAAxiReqFifoDepth ( DMAAxiReqFifoDepth ), - .DMAReqFifoDepth ( DMAReqFifoDepth ), - .RegisterOffloadRsp ( RegisterOffloadRsp ), - .RegisterCoreReq ( RegisterCoreReq ), - .RegisterCoreRsp ( RegisterCoreRsp ), - .RegisterTCDMCuts ( RegisterTCDMCuts ), - .RegisterExt ( RegisterExt ), - .XbarLatency ( XbarLatency ), - .MaxMstTrans ( MaxMstTrans ), - .MaxSlvTrans ( MaxSlvTrans ) - ) i_tile ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .impl_i ( impl_i ), - .error_o ( error_o ), - .debug_req_i ( debug_req_i ), - .meip_i ( meip_i ), - .mtip_i ( mtip_i ), - .msip_i ( msip_i ), - .hart_base_id_i ( hart_base_id_i ), - .cluster_base_addr_i ( cluster_base_addr_i ), - .tile_id_i ( '0 ), - .private_start_addr_i ( private_start_addr ), - .axi_out_req_o ( axi_out_req [0] ), - .axi_out_resp_i ( axi_out_resp [0] ), - // Remote Ports (not used) - .remote_req_o ( ), - .remote_req_dst_o ( ), - .remote_rsp_i ( '0 ), - .remote_rsp_ready_i ( '0 ), - .remote_req_i ( '0 ), - .remote_rsp_o ( ), - .remote_rsp_ready_o ( ), - // Cache Refill Ports - .cache_refill_req_o ( cache_refill_req ), - .cache_refill_rsp_i ( cache_refill_rsp ), - .axi_wide_req_o ( axi_tile_req[0] ), - .axi_wide_rsp_i ( axi_tile_rsp[0] ), - // Peripherals - .icache_events_o ( icache_events ), - .icache_prefetch_enable_i ( icache_prefetch_enable ), - .cl_interrupt_i ( cl_interrupt ), - .dynamic_offset_i ( dynamic_offset ), - .l1d_private_i ( l1d_private ), - .l1d_insn_i ( l1d_insn ), - .l1d_insn_valid_i ( l1d_insn_valid ), - .l1d_insn_ready_o ( l1d_insn_ready ), - .l1d_busy_i ( l1d_busy ) - ); + // -------------------------------------------------- + // West HBM ejection points (HBM channels 0..NumGroupsY-1) + // floo_tcdm_chimney (SbrPort, no router) at mesh edge: + // unpack req flit → reqrsp_to_axi → DRAM + // response → chimney packs flit with source route back to requester + // HBM0: axi_demux splits DRAM from peripheral traffic. + // -------------------------------------------------- + + for (genvar gy = 0; gy < NumGroupsY; gy++) begin : gen_hbm_west + localparam int unsigned HbmIdx = gy; + localparam int unsigned HbmEndpointId = NumGroups + HbmIdx; + + // Chimney subordinate port ↔ REQRSP bundle bridge + cache_trans_req_chan_t hbm_sbr_req; + logic hbm_sbr_req_valid; + logic hbm_sbr_req_ready; + cache_trans_rsp_chan_t hbm_sbr_rsp; + logic hbm_sbr_rsp_valid; + logic hbm_sbr_rsp_ready; + + floo_tcdm_chimney #( + .RouteCfg ( floo_cachepool_noc_pkg::RouteCfg ), + .EnMgrPort ( 1'b0 ), + .EnSbrPort ( 1'b1 ), + .id_t ( floo_cachepool_noc_pkg::id_t ), + .route_t ( floo_cachepool_noc_pkg::route_t ), + .dst_t ( floo_cachepool_noc_pkg::route_t ), + .hdr_t ( l2_noc_hdr_t ), + .req_chan_t ( cache_trans_req_chan_t ), + .rsp_chan_t ( cache_trans_rsp_chan_t ), + .floo_req_t ( l2_noc_req_t ), + .floo_rsp_t ( l2_noc_rsp_t ), + .addr_t ( axi_addr_t ), + .sam_rule_t ( floo_cachepool_noc_pkg::sam_rule_t ), + .Sam ( floo_cachepool_noc_pkg::Sam ) + ) i_hbm_chimney ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .test_enable_i ( 1'b0 ), + // Manager port: unused + .mgr_req_i ( '0 ), + .mgr_req_valid_i ( 1'b0 ), + .mgr_req_ready_o ( ), + .mgr_rsp_o ( ), + .mgr_rsp_valid_o ( ), + .mgr_rsp_ready_i ( 1'b0 ), + // Subordinate port: drives reqrsp_to_axi + .sbr_req_o ( hbm_sbr_req ), + .sbr_req_valid_o ( hbm_sbr_req_valid ), + .sbr_req_ready_i ( hbm_sbr_req_ready ), + .sbr_rsp_i ( hbm_sbr_rsp ), + .sbr_rsp_valid_i ( hbm_sbr_rsp_valid ), + .sbr_rsp_ready_o ( hbm_sbr_rsp_ready ), + .sbr_txn_id_i ( hbm_sbr_rsp.user.l2_src_id ), + // Routing + .id_i ( floo_cachepool_noc_pkg::id_t'(HbmEndpointId) ), + .route_table_i ( floo_cachepool_noc_pkg::RoutingTables[HbmEndpointId]), + // Request flit from mesh West(3) edge (no router) + .floo_req_o ( ), + .floo_req_valid_o ( ), + .floo_req_ready_i ( 1'b1 ), + .floo_req_i ( l2_req_out[0][gy][3] ), + .floo_req_valid_i ( l2_req_out_valid[0][gy][3] ), + .floo_req_ready_o ( l2_req_out_ready[0][gy][3] ), + // Response flit into mesh West(3) edge + .floo_rsp_o ( l2_rsp_in[0][gy][3] ), + .floo_rsp_valid_o ( l2_rsp_in_valid[0][gy][3] ), + .floo_rsp_ready_i ( l2_rsp_in_ready[0][gy][3] ), + .floo_rsp_i ( '0 ), + .floo_rsp_valid_i ( 1'b0 ), + .floo_rsp_ready_o ( ) + ); - axi_to_reqrsp #( - .axi_req_t ( axi_mst_cache_req_t ), - .axi_rsp_t ( axi_mst_cache_resp_t ), - .AddrWidth ( AxiAddrWidth ), - .DataWidth ( AxiDataWidth ), - .UserWidth ( $bits(refill_user_t) ), - .IdWidth ( AxiIdWidthIn ), - .BufDepth ( NumSpatzOutstandingLoads ), - .reqrsp_req_t ( cache_trans_req_t ), - .reqrsp_rsp_t ( cache_trans_rsp_t ) - ) i_axi2reqrsp ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .busy_o ( ), - .axi_req_i ( axi_tile_req [0][TileMem] ), - .axi_rsp_o ( axi_tile_rsp [0][TileMem] ), - .reqrsp_req_o ( cache_core_req[0] ), - .reqrsp_rsp_i ( cache_core_rsp[0] ) - ); - end + // Tie off: no requests injected from HBM, drain stray outgoing responses + assign l2_req_in [0][gy][3] = '0; + assign l2_req_in_valid [0][gy][3] = 1'b0; + assign l2_rsp_out_ready[0][gy][3] = 1'b1; - // Additional one port for iCache connection - localparam int unsigned ReqrspPortsTile = NumL1CtrlTile + 1; - always_comb begin - for (int t = 0; t < NumTiles; t++) begin - for (int p = 0; p < ReqrspPortsTile; p++) begin - automatic int unsigned xbar_idx = t*ReqrspPortsTile + p; - automatic int unsigned refill_idx = t*NumL1CtrlTile + p-1; - - if (p == 0) begin - // connect_icache_path - tile_req_chan [xbar_idx] = cache_core_req [t].q; - // Scrmable address - tile_req_chan [xbar_idx].addr = scrambleAddr(cache_core_req[t].q.addr); - tile_req_valid [xbar_idx] = cache_core_req [t].q_valid; - cache_core_rsp [t].q_ready = tile_req_ready [xbar_idx]; - - cache_core_rsp [t].p = tile_rsp_chan [xbar_idx]; - cache_core_rsp [t].p_valid = tile_rsp_valid [xbar_idx]; - tile_rsp_ready [xbar_idx] = cache_core_req [t].p_ready; - // Tile ID assignment - tile_req_chan [xbar_idx].user.tile_id = t; - end else begin - // connect_refill_path - tile_req_chan [xbar_idx] = cache_refill_req[refill_idx].q; - // Scramble address - tile_req_chan [xbar_idx].addr = scrambleAddr(cache_refill_req[refill_idx].q.addr); - tile_req_valid [xbar_idx] = cache_refill_req[refill_idx].q_valid; - cache_refill_rsp[refill_idx].q_ready = tile_req_ready [xbar_idx]; - - cache_refill_rsp[refill_idx].p = tile_rsp_chan [xbar_idx]; - cache_refill_rsp[refill_idx].p_valid = tile_rsp_valid [xbar_idx]; - tile_rsp_ready [xbar_idx] = cache_refill_req[refill_idx].p_ready; - // Tile ID assignment - tile_req_chan [xbar_idx].user.tile_id = t; + // Pack chimney sbr signals into REQRSP bundle + cache_trans_req_t hbm_reqrsp_req; + cache_trans_rsp_t hbm_reqrsp_rsp; + + assign hbm_reqrsp_req = '{ + q: hbm_sbr_req, + q_valid: hbm_sbr_req_valid, + p_ready: hbm_sbr_rsp_ready + }; + assign hbm_sbr_req_ready = hbm_reqrsp_rsp.q_ready; + assign hbm_sbr_rsp = hbm_reqrsp_rsp.p; + assign hbm_sbr_rsp_valid = hbm_reqrsp_rsp.p_valid; + + // HBM0: splits DRAM vs peripheral before AXI conversion. Uses + // reqrsp_xbar rather than reqrsp_demux: the demux tracked every + // dispatched request (regardless of target) in a single shared + // in-order ID FIFO sized RespDepth, capping outstanding HBM0 refills + // far below the windows available at the chimney/reqrsp_to_axi. The + // xbar lets each downstream path track its own outstanding requests + // independently. Reordering across the two paths is safe because + // miss/request info lives in refill_user_t, not in arrival order. + // Each path still has its own reqrsp_to_axi so the user-field FIFO + // stays in-order per path and refill_user_t is preserved end-to-end. + if (HbmIdx == 0) begin : gen_hbm0_demux + + // Address-based select: DRAM = port 0, Peripheral = port 1 + logic hbm0_reqrsp_sel; + assign hbm0_reqrsp_sel = !((hbm_reqrsp_req.q.addr >= DramAddr) && + (hbm_reqrsp_req.q.addr < DramAddr + DramPerChSize)); + + cache_trans_req_t [1:0] hbm0_demux_req; + cache_trans_rsp_t [1:0] hbm0_demux_rsp; + + cache_trans_req_chan_t [1:0] hbm0_xbar_mst_req; + logic [1:0] hbm0_xbar_mst_req_valid; + logic [1:0] hbm0_xbar_mst_req_ready; + cache_trans_rsp_chan_t [1:0] hbm0_xbar_mst_rsp; + logic [1:0] hbm0_xbar_mst_rsp_valid; + logic [1:0] hbm0_xbar_mst_rsp_ready; + + logic hbm0_xbar_slv_req_ready; + cache_trans_rsp_chan_t hbm0_xbar_slv_rsp; + logic hbm0_xbar_slv_rsp_valid; + + reqrsp_xbar #( + .NumInp ( 1 ), + .NumOut ( 2 ), + .PipeReg ( 1'b1 ), + .RspReg ( 1'b1 ), + .tcdm_req_chan_t ( cache_trans_req_chan_t ), + .tcdm_rsp_chan_t ( cache_trans_rsp_chan_t ) + ) i_hbm0_reqrsp_xbar ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .slv_req_i ( hbm_reqrsp_req.q ), + .slv_rr_i ( '0 ), + .slv_req_valid_i ( hbm_reqrsp_req.q_valid ), + .slv_req_ready_o ( hbm0_xbar_slv_req_ready ), + .slv_rsp_o ( hbm0_xbar_slv_rsp ), + .slv_rsp_valid_o ( hbm0_xbar_slv_rsp_valid ), + .slv_rsp_ready_i ( hbm_reqrsp_req.p_ready ), + .slv_sel_i ( hbm0_reqrsp_sel ), + .slv_selected_o ( ), + .mst_req_o ( hbm0_xbar_mst_req ), + .mst_req_valid_o ( hbm0_xbar_mst_req_valid ), + .mst_req_ready_i ( hbm0_xbar_mst_req_ready ), + .mst_rsp_i ( hbm0_xbar_mst_rsp ), + .mst_rr_i ( '0 ), + .mst_rsp_valid_i ( hbm0_xbar_mst_rsp_valid ), + .mst_rsp_ready_o ( hbm0_xbar_mst_rsp_ready ), + .mst_sel_i ( '0 ) + ); + + assign hbm_reqrsp_rsp = '{ + q_ready: hbm0_xbar_slv_req_ready, + p: hbm0_xbar_slv_rsp, + p_valid: hbm0_xbar_slv_rsp_valid + }; + + for (genvar p = 0; p < 2; p++) begin : gen_hbm0_xbar_bundle + assign hbm0_demux_req[p] = '{ + q: hbm0_xbar_mst_req[p], + q_valid: hbm0_xbar_mst_req_valid[p], + p_ready: hbm0_xbar_mst_rsp_ready[p] + }; + assign hbm0_xbar_mst_req_ready[p] = hbm0_demux_rsp[p].q_ready; + assign hbm0_xbar_mst_rsp[p] = hbm0_demux_rsp[p].p; + assign hbm0_xbar_mst_rsp_valid[p] = hbm0_demux_rsp[p].p_valid; end + + // Port 0: DRAM — reqrsp_to_axi → axi_cut → TB + axi_slv_cache_req_t hbm0_dram_axi_req; + axi_slv_cache_resp_t hbm0_dram_axi_rsp; + + reqrsp_to_axi #( + .MaxTrans ( L2RefillMaxTrans ), + .ID ( 0 ), + .EnBurst ( 1 ), + .ShuffleId ( 1 ), + .AxiIdWidth ( WideIdWidthOut ), + .DataWidth ( AxiDataWidth ), + .UserWidth ( $bits(refill_user_t) ), + .AxiUserWidth ( AxiUserWidth ), + .reqrsp_req_t ( cache_trans_req_t ), + .reqrsp_rsp_t ( cache_trans_rsp_t ), + .axi_req_t ( axi_slv_cache_req_t ), + .axi_rsp_t ( axi_slv_cache_resp_t ) + ) i_hbm0_dram_reqrsp_to_axi ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .user_i ( '0 ), + .reqrsp_req_i ( hbm0_demux_req[0] ), + .reqrsp_rsp_o ( hbm0_demux_rsp[0] ), + .axi_req_o ( hbm0_dram_axi_req ), + .axi_rsp_i ( hbm0_dram_axi_rsp ) + ); + + assign wide_axi_slv_req[0] = hbm0_dram_axi_req; + assign hbm0_dram_axi_rsp = wide_axi_slv_rsp[0]; + + + // Port 1: Non-DRAM — revert VA back to PA, then DW convert to 32b + // All paths through the interconnect use scrambled (VA) addresses. + // revertAddr recovers the physical address for the peripheral endpoint. + cache_trans_req_t hbm0_peri_reqrsp_req; + always_comb begin + hbm0_peri_reqrsp_req = hbm0_demux_req[1]; + hbm0_peri_reqrsp_req.q.addr = revertAddr(hbm0_demux_req[1].q.addr); + end + + cache_trans_rsp_t hbm0_peri_reqrsp_rsp; + assign hbm0_demux_rsp[1] = hbm0_peri_reqrsp_rsp; + + // W2N converter: 512b REQRSP → 32b REQRSP + // Same user type (refill_user_t) on both sides. + // We add a small 1-entry buffer with MSHR inside to reduce boot time + reqrsp_w2n_converter #( + .SlvDataWidth ( AxiDataWidth ), + .MstDataWidth ( 32 ), + .AddrWidth ( AxiAddrWidth ), + .SlvUserWidth ( $bits(refill_user_t) ), + .MstUserWidth ( $bits(refill_user_t) ), + .MaxTrans ( 4 ), + .EnRdBuf ( 1'b1 ), + .CacheableBase ( BootAddr ), + .CacheableSize ( 32'h1_0000 ), + .slv_req_t ( cache_trans_req_t ), + .slv_rsp_t ( cache_trans_rsp_t ), + .mst_req_t ( peri_narrow_req_t ), + .mst_rsp_t ( peri_narrow_rsp_t ), + .slv_user_t ( refill_user_t ) + ) i_hbm0_peri_dw ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .slv_req_i ( hbm0_peri_reqrsp_req ), + .slv_rsp_o ( hbm0_peri_reqrsp_rsp ), + .mst_req_o ( hbm0_peri_narrow_req ), + .mst_rsp_i ( hbm0_peri_narrow_rsp ) + ); + + end else begin : gen_hbm_direct + // Non-HBM0: single reqrsp_to_axi → DRAM + axi_slv_cache_req_t hbm_axi_req; + axi_slv_cache_resp_t hbm_axi_rsp; + + reqrsp_to_axi #( + .MaxTrans ( L2RefillMaxTrans ), + .ID ( 0 ), + .EnBurst ( 1 ), + .ShuffleId ( 1 ), + .AxiIdWidth ( WideIdWidthOut ), + .DataWidth ( AxiDataWidth ), + .UserWidth ( $bits(refill_user_t) ), + .AxiUserWidth ( AxiUserWidth ), + .reqrsp_req_t ( cache_trans_req_t ), + .reqrsp_rsp_t ( cache_trans_rsp_t ), + .axi_req_t ( axi_slv_cache_req_t ), + .axi_rsp_t ( axi_slv_cache_resp_t ) + ) i_hbm_reqrsp_to_axi ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .user_i ( '0 ), + .reqrsp_req_i ( hbm_reqrsp_req ), + .reqrsp_rsp_o ( hbm_reqrsp_rsp ), + .axi_req_o ( hbm_axi_req ), + .axi_rsp_i ( hbm_axi_rsp ) + ); + + assign wide_axi_slv_req[HbmIdx] = hbm_axi_req; + assign hbm_axi_rsp = wide_axi_slv_rsp[HbmIdx]; end end - end - typedef struct packed { - int unsigned idx; - logic [AxiAddrWidth-1:0] base; - logic [AxiAddrWidth-1:0] mask; - } reqrsp_rule_t; - - reqrsp_rule_t [ClusterWideOutAxiPorts-1:0] xbar_rule; + // -------------------------------------------------- + // East HBM ejection points (HBM channels NumGroupsY..2*NumGroupsY-1) + // floo_tcdm_chimney (SbrPort, no router) at mesh edge. + // -------------------------------------------------- + + for (genvar gy = 0; gy < NumGroupsY; gy++) begin : gen_hbm_east + localparam int unsigned HbmIdx = NumGroupsY + gy; + localparam int unsigned HbmEndpointId = NumGroups + HbmIdx; + + // Chimney subordinate port ↔ REQRSP bundle bridge + cache_trans_req_chan_t hbm_sbr_req; + logic hbm_sbr_req_valid; + logic hbm_sbr_req_ready; + cache_trans_rsp_chan_t hbm_sbr_rsp; + logic hbm_sbr_rsp_valid; + logic hbm_sbr_rsp_ready; + + floo_tcdm_chimney #( + .RouteCfg ( floo_cachepool_noc_pkg::RouteCfg ), + .EnMgrPort ( 1'b0 ), + .EnSbrPort ( 1'b1 ), + .id_t ( floo_cachepool_noc_pkg::id_t ), + .route_t ( floo_cachepool_noc_pkg::route_t ), + .dst_t ( floo_cachepool_noc_pkg::route_t ), + .hdr_t ( l2_noc_hdr_t ), + .req_chan_t ( cache_trans_req_chan_t ), + .rsp_chan_t ( cache_trans_rsp_chan_t ), + .floo_req_t ( l2_noc_req_t ), + .floo_rsp_t ( l2_noc_rsp_t ), + .addr_t ( axi_addr_t ), + .sam_rule_t ( floo_cachepool_noc_pkg::sam_rule_t ), + .Sam ( floo_cachepool_noc_pkg::Sam ) + ) i_hbm_chimney ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .test_enable_i ( 1'b0 ), + // Manager port: unused + .mgr_req_i ( '0 ), + .mgr_req_valid_i ( 1'b0 ), + .mgr_req_ready_o ( ), + .mgr_rsp_o ( ), + .mgr_rsp_valid_o ( ), + .mgr_rsp_ready_i ( 1'b0 ), + // Subordinate port: drives reqrsp_to_axi + .sbr_req_o ( hbm_sbr_req ), + .sbr_req_valid_o ( hbm_sbr_req_valid ), + .sbr_req_ready_i ( hbm_sbr_req_ready ), + .sbr_rsp_i ( hbm_sbr_rsp ), + .sbr_rsp_valid_i ( hbm_sbr_rsp_valid ), + .sbr_rsp_ready_o ( hbm_sbr_rsp_ready ), + .sbr_txn_id_i ( hbm_sbr_rsp.user.l2_src_id ), + // Routing + .id_i ( floo_cachepool_noc_pkg::id_t'(HbmEndpointId) ), + .route_table_i ( floo_cachepool_noc_pkg::RoutingTables[HbmEndpointId] ), + // Request flit from mesh East(1) edge (no router) + .floo_req_o ( ), + .floo_req_valid_o ( ), + .floo_req_ready_i ( 1'b1 ), + .floo_req_i ( l2_req_out[NumGroupsX-1][gy][1] ), + .floo_req_valid_i ( l2_req_out_valid[NumGroupsX-1][gy][1] ), + .floo_req_ready_o ( l2_req_out_ready[NumGroupsX-1][gy][1] ), + // Response flit into mesh East(1) edge + .floo_rsp_o ( l2_rsp_in[NumGroupsX-1][gy][1] ), + .floo_rsp_valid_o ( l2_rsp_in_valid[NumGroupsX-1][gy][1] ), + .floo_rsp_ready_i ( l2_rsp_in_ready[NumGroupsX-1][gy][1] ), + .floo_rsp_i ( '0 ), + .floo_rsp_valid_i ( 1'b0 ), + .floo_rsp_ready_o ( ) + ); - for (genvar i = 0; i < ClusterWideOutAxiPorts; i ++) begin - assign xbar_rule[i] = '{ - idx : i, - base : DramAddr + DramPerChSize * i, - mask : ({AxiAddrWidth{1'b1}} << $clog2(DramPerChSize)) - }; - end + // Tie off unused + assign l2_req_in [NumGroupsX-1][gy][1] = '0; + assign l2_req_in_valid [NumGroupsX-1][gy][1] = 1'b0; + assign l2_rsp_out_ready[NumGroupsX-1][gy][1] = 1'b1; - logic [$clog2(ClusterWideOutAxiPorts):0] default_idx; - assign default_idx = ClusterWideOutAxiPorts; - - for (genvar inp = 0; inp < NumClusterMst*NumTiles; inp ++) begin : gen_xbar_sel - addr_decode_napot #( - .NoIndices (ClusterWideOutAxiPorts+1 ), - .NoRules (ClusterWideOutAxiPorts ), - .addr_t (axi_addr_t ), - .rule_t (reqrsp_rule_t ) - ) i_snitch_decode_napot ( - .addr_i (tile_req_chan[inp].addr), - .addr_map_i (xbar_rule ), - .idx_o (tile_sel_err[inp] ), - .dec_valid_o (/* Unused */ ), - .dec_error_o (/* Unused */ ), - .en_default_idx_i (1'b1 ), - .default_idx_i (default_idx ) - ); + // Pack chimney sbr signals into REQRSP bundle for reqrsp_to_axi + cache_trans_req_t hbm_reqrsp_req; + cache_trans_rsp_t hbm_reqrsp_rsp; - assign tile_sel[inp] = tile_sel_err[inp][$clog2(ClusterWideOutAxiPorts)-1:0]; - -`ifndef TARGET_SYNTHESIS - // Alert the system that we have illegal memory access - IllegalMemAccess : assert property( - @(posedge clk_i) disable iff (!rst_ni) (tile_req_valid[inp] |-> !tile_sel_err[inp][$clog2(ClusterWideOutAxiPorts)])) - else $error("Visited illegal address: time=%0t, port=%0d, addr=0x%08h", $time, inp, tile_req_chan[inp].addr); - // else $fatal (1, "Visited address is not mapped"); -`endif - end + assign hbm_reqrsp_req = '{ + q: hbm_sbr_req, + q_valid: hbm_sbr_req_valid, + p_ready: hbm_sbr_rsp_ready + }; + assign hbm_sbr_req_ready = hbm_reqrsp_rsp.q_ready; + assign hbm_sbr_rsp = hbm_reqrsp_rsp.p; + assign hbm_sbr_rsp_valid = hbm_reqrsp_rsp.p_valid; + + // REQRSP → AXI conversion + axi_slv_cache_req_t hbm_axi_req; + axi_slv_cache_resp_t hbm_axi_rsp; + + reqrsp_to_axi #( + .MaxTrans ( L2RefillMaxTrans ), + .ID ( 0 ), + .EnBurst ( 1 ), + .ShuffleId ( 1 ), + .AxiIdWidth ( WideIdWidthOut ), + .DataWidth ( AxiDataWidth ), + .UserWidth ( $bits(refill_user_t) ), + .AxiUserWidth ( AxiUserWidth ), + .reqrsp_req_t ( cache_trans_req_t ), + .reqrsp_rsp_t ( cache_trans_rsp_t ), + .axi_req_t ( axi_slv_cache_req_t ), + .axi_rsp_t ( axi_slv_cache_resp_t ) + ) i_hbm_reqrsp_to_axi ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .user_i ( '0 ), + .reqrsp_req_i ( hbm_reqrsp_req ), + .reqrsp_rsp_o ( hbm_reqrsp_rsp ), + .axi_req_o ( hbm_axi_req ), + .axi_rsp_i ( hbm_axi_rsp ) + ); - reqrsp_xbar #( - .NumInp (NumClusterMst*NumTiles ), - .NumOut (ClusterWideOutAxiPorts ), - .PipeReg (1'b1 ), - .ExtReqPrio (1'b0 ), - .ExtRspPrio (Burst_Enable ), - .tcdm_req_chan_t (cache_trans_req_chan_t ), - .tcdm_rsp_chan_t (cache_trans_rsp_chan_t ) - ) i_cluster_xbar ( - .clk_i (clk_i ), - .rst_ni (rst_ni ), - .slv_req_i (tile_req_chan ), - .slv_req_valid_i (tile_req_valid ), - .slv_req_ready_o (tile_req_ready ), - .slv_rsp_o (tile_rsp_chan ), - .slv_rsp_valid_o (tile_rsp_valid ), - .slv_rsp_ready_i (tile_rsp_ready ), - .slv_sel_i (tile_sel[NumTiles*NumClusterMst-1:0] ), - .slv_rr_i ('0 ), - .slv_selected_o (tile_selected ), - .mst_req_o (l2_req_chan ), - .mst_req_valid_o (l2_req_valid ), - .mst_req_ready_i (l2_req_ready ), - .mst_rsp_i (l2_rsp_chan ), - .mst_rr_i (l2_rsp_rr ), - .mst_rsp_valid_i (l2_rsp_valid ), - .mst_rsp_ready_o (l2_rsp_ready ), - .mst_sel_i (l2_sel ) - ); + assign wide_axi_slv_req[HbmIdx] = hbm_axi_req; + assign hbm_axi_rsp = wide_axi_slv_rsp[HbmIdx]; - for (genvar ch = 0; ch < ClusterWideOutAxiPorts; ch++) begin - // To L2 Channels - always_comb begin - l2_req[ch].q = '{ - addr : l2_req_chan[ch].addr, - write: l2_req_chan[ch].write, - amo : l2_req_chan[ch].amo, - data : l2_req_chan[ch].data, - strb : l2_req_chan[ch].strb, - size : l2_req_chan[ch].size, - default: '0 - }; - l2_req[ch].q.user = l2_req_chan[ch].user; - l2_req[ch].q_valid = l2_req_valid[ch] ; - l2_req_ready[ch] = l2_rsp[ch].q_ready; - - l2_rsp_chan [ch] = '{ - data : l2_rsp[ch].p.data, - error: l2_rsp[ch].p.error, - write: l2_rsp[ch].p.write, - default: '0 - }; - l2_rsp_chan [ch].user = l2_rsp[ch].p.user; - l2_rsp_valid[ch] = l2_rsp[ch].p_valid; - l2_req[ch].p_ready = l2_rsp_ready[ch]; - // calculate the port from the tile id and bank id - // bank_id == 0 --- bypass - // bank_id == 1-4 --- cache bank 0-3 - l2_sel[ch] = l2_rsp[ch].p.user.tile_id * NumClusterMst + l2_rsp[ch].p.user.bank_id; end - end - for (genvar ch = 0; ch < ClusterWideOutAxiPorts; ch ++) begin : gen_output_axi - reqrsp_to_axi #( - .MaxTrans (NumSpatzOutstandingLoads*2 ), - .ID ('0 ), - .EnBurst (1 ), - .ShuffleId (1 ), - .UserWidth ($bits(refill_user_t) ), - .ReqUserFallThrough (1'b0 ), - .DataWidth (AxiDataWidth ), - .AxiUserWidth (AxiUserWidth ), - .reqrsp_req_t (l2_req_t ), - .reqrsp_rsp_t (l2_rsp_t ), - .axi_req_t (axi_slv_cache_req_t ), - .axi_rsp_t (axi_slv_cache_resp_t ) - ) i_reqrsp2axi ( - .clk_i (clk_i ), - .rst_ni (rst_ni ), - .user_i (l2_req[ch].q.user ), - .reqrsp_req_i (l2_req[ch] ), - .reqrsp_rsp_o (l2_rsp[ch] ), - .axi_req_o (wide_axi_slv_req[ch] ), - .axi_rsp_i (wide_axi_slv_rsp[ch] ) - ); end - - // ------------- - // To Main Memory - // ------------- - // Optionally decouple the external wide AXI master port. + // -------------------------------------------------- + // AXI Cut: HBM → DRAM Output + // -------------------------------------------------- for (genvar port = 0; port < ClusterWideOutAxiPorts; port ++) begin : gen_axi_out_cut axi_cut #( .Bypass (0 ), @@ -759,251 +1009,294 @@ module cachepool_cluster ); end - // --------- - // Slaves - // --------- - - /***** UART ****/ - axi_narrow_req_t [NumTiles-1:0] axi_uart_mux_req; - axi_narrow_resp_t [NumTiles-1:0] axi_uart_mux_rsp; - - if (NumTiles > 1) begin : gen_uart_mux - for (genvar tile = 0; tile < NumTiles; tile++) begin - assign axi_uart_mux_req[tile] = axi_out_req[tile][ClusterUart]; - assign axi_out_resp[tile][ClusterUart] = axi_uart_mux_rsp[tile]; - end - - axi_mux #( - .SlvAxiIDWidth ( CsrAxiMstIdWidth ), - .slv_aw_chan_t ( axi_csr_mst_aw_chan_t ), // AW Channel Type, slave ports - .mst_aw_chan_t ( axi_uart_aw_chan_t ), // AW Channel Type, master port - .w_chan_t ( axi_uart_w_chan_t ), // W Channel Type, all ports - .slv_b_chan_t ( axi_csr_mst_b_chan_t ), // B Channel Type, slave ports - .mst_b_chan_t ( axi_uart_b_chan_t ), // B Channel Type, master port - .slv_ar_chan_t ( axi_csr_mst_ar_chan_t ), // AR Channel Type, slave ports - .mst_ar_chan_t ( axi_uart_ar_chan_t ), // AR Channel Type, master port - .slv_r_chan_t ( axi_csr_mst_r_chan_t ), // R Channel Type, slave ports - .mst_r_chan_t ( axi_uart_r_chan_t ), // R Channel Type, master port - .slv_req_t ( axi_csr_mst_req_t ), - .slv_resp_t ( axi_csr_mst_resp_t ), - .mst_req_t ( axi_uart_req_t ), - .mst_resp_t ( axi_uart_resp_t ), - .NoSlvPorts ( NumTiles ), // Number of Masters for the module - .FallThrough ( 0 ), - .SpillAw ( XbarLatency[4] ), - .SpillW ( XbarLatency[3] ), - .SpillB ( XbarLatency[2] ), - .SpillAr ( XbarLatency[1] ), - .SpillR ( XbarLatency[0] ), - .MaxWTrans ( 2 ) - ) i_axi_uart_mux ( - .clk_i ( clk_i ), // Clock - .rst_ni ( rst_ni ), // Asynchronous reset active low - .test_i ( '0 ), // Test Mode enable - .slv_reqs_i ( axi_uart_mux_req ), - .slv_resps_o ( axi_uart_mux_rsp ), - .mst_req_o ( axi_narrow_req_o ), - .mst_resp_i ( axi_narrow_resp_i ) - ); - end else begin : gen_uart_connect - assign axi_narrow_req_o = axi_out_req[0][ClusterUart]; - assign axi_out_resp[0][ClusterUart] = axi_narrow_resp_i; - end - - /***** BootROM ****/ - for (genvar t = 0; t < NumTiles; t++) begin : gen_bootrom - axi_to_reg #( - .ADDR_WIDTH (AxiAddrWidth ), - .DATA_WIDTH (AxiDataWidth ), - .AXI_MAX_WRITE_TXNS (1 ), - .AXI_MAX_READ_TXNS (1 ), - .DECOUPLE_W (0 ), - .ID_WIDTH (WideIdWidthIn ), - .USER_WIDTH (AxiUserWidth ), - .axi_req_t (axi_mst_cache_req_t ), - .axi_rsp_t (axi_mst_cache_resp_t), - .reg_req_t (reg_cache_req_t ), - .reg_rsp_t (reg_cache_rsp_t ) - ) i_axi_to_reg_bootrom ( - .clk_i (clk_i ), - .rst_ni (rst_ni ), - .testmode_i (1'b0 ), - .axi_req_i (axi_tile_req[t][TileBootROM] ), - .axi_rsp_o (axi_tile_rsp[t][TileBootROM] ), - .reg_req_o (bootrom_reg_req[t] ), - .reg_rsp_i (bootrom_reg_rsp[t] ) - ); - - bootrom i_bootrom ( - .clk_i (clk_i ), - .req_i (bootrom_reg_req[t].valid ), - .addr_i (addr_t'(bootrom_reg_req[t].addr) ), - .rdata_o(bootrom_reg_rsp[t].rdata ) - ); - - `FF(bootrom_reg_rsp[t].ready, bootrom_reg_req[t].valid, 1'b0) - - assign bootrom_reg_rsp[t].error = 1'b0; - end - - - /***** CSR/Peripherals *****/ - - `REG_BUS_TYPEDEF_ALL(reg, narrow_addr_t, narrow_data_t, narrow_strb_t) - - reg_req_t reg_req; - reg_rsp_t reg_rsp; - - axi_csr_slv_req_t axi_csr_req; - axi_csr_slv_resp_t axi_csr_rsp; - - axi_narrow_req_t [NumTiles-1:0] axi_core_csr_req, axi_barrier_req; - axi_narrow_resp_t [NumTiles-1:0] axi_core_csr_rsp, axi_barrier_rsp; - - - for (genvar t = 0; t < NumTiles; t++) begin - assign axi_barrier_req[t] = axi_out_req [t][ClusterPeriph]; - assign axi_out_resp [t][ClusterPeriph] = axi_barrier_rsp[t]; - end - - // Calculate the peripheral base address - localparam logic [AxiAddrWidth-1:0] TCDMMask = ~(TCDMSize-1); + // -------------------------------------------------- + // Peripheral Fabric (REQRSP-based 2×3 xbar) + // -------------------------------------------------- + // + // Data path (all 32b REQRSP): + // Source [0]: HBM0 demux port 1 -> reqrsp_w2n_converter (512b-32b) -> pad src_id=0 + // Source [1]: peri_ext_req_i (TB/SoC, already 32b REQRSP) -> pad src_id=1 + // + // Target [0]: BootROM -> reqrsp_to_reg -> bootrom + // Target [1]: CSR -> reqrsp_to_reg -> cachepool_peripheral + // Target [2]: UART -> reqrsp_to_axi -> axi_narrow_req_o + // + // Response routing: mst_sel_i = mst_rsp.user.src_id (1-bit, 0=NoC, 1=TB) + + localparam int unsigned PeriXbarNumSrc = 2; + localparam int unsigned PeriXbarNumTgt = 3; + + typedef enum int unsigned { + PeriTgtBootROM = 0, + PeriTgtCSR = 1, + PeriTgtUART = 2 + } peri_tgt_e; + + // Calculate the peripheral base address (needed for xbar rules). + localparam logic [AxiAddrWidth-1:0] TCDMMask = ~(TCDMSize-1); addr_t tcdm_start_address, tcdm_end_address; assign tcdm_start_address = (cluster_base_addr_i & TCDMMask); assign tcdm_end_address = (tcdm_start_address + TCDMSize) & TCDMMask; + typedef logic [$clog2(PeriXbarNumTgt)-1:0] peri_tgt_sel_t; + typedef logic peri_src_sel_t; + + function automatic peri_tgt_sel_t peri_addr_decode(input addr_t addr); + if (addr >= BootAddr && addr < BootAddr + 32'h1_0000) + return peri_tgt_sel_t'(PeriTgtBootROM); + else if (addr >= UartAddr && addr < UartAddr + 32'h1_0000) + return peri_tgt_sel_t'(PeriTgtUART); + else + return peri_tgt_sel_t'(PeriTgtCSR); + endfunction + + // ---- Inline src_id packing: peri_narrow → peri_xbar (add 1-bit src_id) ---- + + peri_xbar_req_chan_t [PeriXbarNumSrc-1:0] peri_xbar_src_req; + logic [PeriXbarNumSrc-1:0] peri_xbar_src_req_valid; + logic [PeriXbarNumSrc-1:0] peri_xbar_src_req_ready; + peri_xbar_rsp_chan_t [PeriXbarNumSrc-1:0] peri_xbar_src_rsp; + logic [PeriXbarNumSrc-1:0] peri_xbar_src_rsp_valid; + logic [PeriXbarNumSrc-1:0] peri_xbar_src_rsp_ready; + + peri_xbar_req_chan_t [PeriXbarNumTgt-1:0] peri_xbar_tgt_req; + logic [PeriXbarNumTgt-1:0] peri_xbar_tgt_req_valid; + logic [PeriXbarNumTgt-1:0] peri_xbar_tgt_req_ready; + peri_xbar_rsp_chan_t [PeriXbarNumTgt-1:0] peri_xbar_tgt_rsp; + logic [PeriXbarNumTgt-1:0] peri_xbar_tgt_rsp_valid; + logic [PeriXbarNumTgt-1:0] peri_xbar_tgt_rsp_ready; + + // Source [0]: HBM0 (src_id = 0) + always_comb begin + peri_xbar_src_req[0] = '0; + peri_xbar_src_req[0].addr = hbm0_peri_narrow_req.q.addr; + peri_xbar_src_req[0].write = hbm0_peri_narrow_req.q.write; + peri_xbar_src_req[0].amo = hbm0_peri_narrow_req.q.amo; + peri_xbar_src_req[0].data = hbm0_peri_narrow_req.q.data; + peri_xbar_src_req[0].strb = hbm0_peri_narrow_req.q.strb; + peri_xbar_src_req[0].size = hbm0_peri_narrow_req.q.size; + peri_xbar_src_req[0].user = '{src_id: 1'b0, refill: hbm0_peri_narrow_req.q.user}; + end + assign peri_xbar_src_req_valid[0] = hbm0_peri_narrow_req.q_valid; + assign peri_xbar_src_rsp_ready[0] = hbm0_peri_narrow_req.p_ready; - logic [NumTiles-1:0] use_barrier; - // TODO: Connect to CSR - assign use_barrier = {NumTiles{1'b1}}; + assign hbm0_peri_narrow_rsp.q_ready = peri_xbar_src_req_ready[0]; + assign hbm0_peri_narrow_rsp.p.data = peri_xbar_src_rsp[0].data; + assign hbm0_peri_narrow_rsp.p.error = peri_xbar_src_rsp[0].error; + assign hbm0_peri_narrow_rsp.p.write = peri_xbar_src_rsp[0].write; + assign hbm0_peri_narrow_rsp.p.user = peri_xbar_src_rsp[0].user.refill; + assign hbm0_peri_narrow_rsp.p_valid = peri_xbar_src_rsp_valid[0]; - cachepool_cluster_barrier #( - .AddrWidth (AxiAddrWidth ), - .NrPorts (NumTiles ), - .axi_req_t (axi_narrow_req_t ), - .axi_rsp_t (axi_narrow_resp_t ), - .axi_id_t (axi_id_in_t ), - .axi_user_t (axi_user_t ) - ) i_cachepool_cluster_barrier ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .axi_slv_req_i ( axi_barrier_req ), - .axi_slv_rsp_o ( axi_barrier_rsp ), - .axi_mst_req_o ( axi_core_csr_req ), - .axi_mst_rsp_i ( axi_core_csr_rsp ), - .barrier_i ( use_barrier ), - .cluster_periph_start_address_i ( tcdm_end_address ) + // Source [1]: TB/SoC external (src_id = 1) + always_comb begin + peri_xbar_src_req[1] = '0; + peri_xbar_src_req[1].addr = peri_ext_req_i.q.addr; + peri_xbar_src_req[1].write = peri_ext_req_i.q.write; + peri_xbar_src_req[1].amo = peri_ext_req_i.q.amo; + peri_xbar_src_req[1].data = peri_ext_req_i.q.data; + peri_xbar_src_req[1].strb = peri_ext_req_i.q.strb; + peri_xbar_src_req[1].size = peri_ext_req_i.q.size; + peri_xbar_src_req[1].user = '{src_id: 1'b1, refill: peri_ext_req_i.q.user}; + end + assign peri_xbar_src_req_valid[1] = peri_ext_req_i.q_valid; + assign peri_xbar_src_rsp_ready[1] = peri_ext_req_i.p_ready; + + assign peri_ext_rsp_o.q_ready = peri_xbar_src_req_ready[1]; + assign peri_ext_rsp_o.p.data = peri_xbar_src_rsp[1].data; + assign peri_ext_rsp_o.p.error = peri_xbar_src_rsp[1].error; + assign peri_ext_rsp_o.p.write = peri_xbar_src_rsp[1].write; + assign peri_ext_rsp_o.p.user = peri_xbar_src_rsp[1].user.refill; + assign peri_ext_rsp_o.p_valid = peri_xbar_src_rsp_valid[1]; + + // ---- Address decode per source ---- + peri_tgt_sel_t [PeriXbarNumSrc-1:0] peri_slv_sel; + assign peri_slv_sel[0] = peri_addr_decode(hbm0_peri_narrow_req.q.addr); + assign peri_slv_sel[1] = peri_addr_decode(peri_ext_req_i.q.addr); + + // ---- Response routing per target: use src_id from user field ---- + peri_src_sel_t [PeriXbarNumTgt-1:0] peri_mst_sel; + assign peri_mst_sel[0] = peri_xbar_tgt_rsp[0].user.src_id; + assign peri_mst_sel[1] = peri_xbar_tgt_rsp[1].user.src_id; + assign peri_mst_sel[2] = peri_xbar_tgt_rsp[2].user.src_id; + + // ---- 2×3 REQRSP Xbar ---- + reqrsp_xbar #( + .NumInp ( PeriXbarNumSrc ), + .NumOut ( PeriXbarNumTgt ), + .PipeReg ( 1'b1 ), + .RspReg ( 1'b1 ), + .tcdm_req_chan_t ( peri_xbar_req_chan_t ), + .tcdm_rsp_chan_t ( peri_xbar_rsp_chan_t ) + ) i_peri_xbar ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .slv_req_i ( peri_xbar_src_req ), + .slv_rr_i ( '0 ), + .slv_req_valid_i ( peri_xbar_src_req_valid ), + .slv_req_ready_o ( peri_xbar_src_req_ready ), + .slv_rsp_o ( peri_xbar_src_rsp ), + .slv_rsp_valid_o ( peri_xbar_src_rsp_valid ), + .slv_rsp_ready_i ( peri_xbar_src_rsp_ready ), + .slv_sel_i ( peri_slv_sel ), + .slv_selected_o ( /* unused */ ), + .mst_req_o ( peri_xbar_tgt_req ), + .mst_req_valid_o ( peri_xbar_tgt_req_valid ), + .mst_req_ready_i ( peri_xbar_tgt_req_ready ), + .mst_rsp_i ( peri_xbar_tgt_rsp ), + .mst_rr_i ( '0 ), + .mst_rsp_valid_i ( peri_xbar_tgt_rsp_valid ), + .mst_rsp_ready_o ( peri_xbar_tgt_rsp_ready ), + .mst_sel_i ( peri_mst_sel ) ); - - axi_mux #( - .SlvAxiIDWidth ( CsrAxiMstIdWidth ), - .slv_aw_chan_t ( axi_csr_mst_aw_chan_t ), // AW Channel Type, slave ports - .mst_aw_chan_t ( axi_csr_slv_aw_chan_t ), // AW Channel Type, master port - .w_chan_t ( axi_csr_slv_w_chan_t ), // W Channel Type, all ports - .slv_b_chan_t ( axi_csr_mst_b_chan_t ), // B Channel Type, slave ports - .mst_b_chan_t ( axi_csr_slv_b_chan_t ), // B Channel Type, master port - .slv_ar_chan_t ( axi_csr_mst_ar_chan_t ), // AR Channel Type, slave ports - .mst_ar_chan_t ( axi_csr_slv_ar_chan_t ), // AR Channel Type, master port - .slv_r_chan_t ( axi_csr_mst_r_chan_t ), // R Channel Type, slave ports - .mst_r_chan_t ( axi_csr_slv_r_chan_t ), // R Channel Type, master port - .slv_req_t ( axi_csr_mst_req_t ), - .slv_resp_t ( axi_csr_mst_resp_t ), - .mst_req_t ( axi_csr_slv_req_t ), - .mst_resp_t ( axi_csr_slv_resp_t ), - .NoSlvPorts ( NumTiles + 1 ), // Number of Masters for the module - .FallThrough ( 0 ), - .SpillAw ( XbarLatency[4] ), - .SpillW ( XbarLatency[3] ), - .SpillB ( XbarLatency[2] ), - .SpillAr ( XbarLatency[1] ), - .SpillR ( XbarLatency[0] ), - .MaxWTrans ( 2 ) - ) i_axi_csr_mux ( - .clk_i ( clk_i ), // Clock - .rst_ni ( rst_ni ), // Asynchronous reset active low - .test_i ('0 ), // Test Mode enable - .slv_reqs_i ( {axi_in_req_i, axi_core_csr_req} ), - .slv_resps_o ( {axi_in_resp_o, axi_core_csr_rsp} ), - .mst_req_o ( axi_csr_req ), - .mst_resp_i ( axi_csr_rsp ) + // ---- Target [0]: BootROM → reqrsp_to_reg → bootrom ---- + + `REG_BUS_TYPEDEF_ALL(reg_bootrom, addr_t, narrow_data_t, narrow_strb_t) + + peri_xbar_req_t peri_bootrom_req; + peri_xbar_rsp_t peri_bootrom_rsp; + + // Bundle xbar split channels into REQRSP struct + assign peri_bootrom_req.q = peri_xbar_tgt_req[PeriTgtBootROM]; + assign peri_bootrom_req.q_valid = peri_xbar_tgt_req_valid[PeriTgtBootROM]; + assign peri_bootrom_req.p_ready = peri_xbar_tgt_rsp_ready[PeriTgtBootROM]; + assign peri_xbar_tgt_req_ready[PeriTgtBootROM] = peri_bootrom_rsp.q_ready; + assign peri_xbar_tgt_rsp[PeriTgtBootROM] = peri_bootrom_rsp.p; + assign peri_xbar_tgt_rsp_valid[PeriTgtBootROM] = peri_bootrom_rsp.p_valid; + + reg_bootrom_req_t bootrom_reg_req; + reg_bootrom_rsp_t bootrom_reg_rsp; + + reqrsp_to_reg #( + .AddrWidth ( AxiAddrWidth ), + .DataWidth ( 32 ), + .UserWidth ( $bits(peri_xbar_user_t) ), + .FifoDepth ( 2 ), + .ShiftResponse( 1'b0 ), + .user_t ( peri_xbar_user_t ), + .reqrsp_req_t ( peri_xbar_req_t ), + .reqrsp_rsp_t ( peri_xbar_rsp_t ), + .reg_req_t ( reg_bootrom_req_t ), + .reg_rsp_t ( reg_bootrom_rsp_t ) + ) i_reqrsp_to_reg_bootrom ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .reqrsp_req_i ( peri_bootrom_req ), + .reqrsp_rsp_o ( peri_bootrom_rsp ), + .reg_req_o ( bootrom_reg_req ), + .reg_rsp_i ( bootrom_reg_rsp ) ); - axi_to_reg #( - .ADDR_WIDTH (AxiAddrWidth ), - .DATA_WIDTH (SpatzAxiNarrowDataWidth ), - .AXI_MAX_WRITE_TXNS (1 ), - .AXI_MAX_READ_TXNS (1 ), - .DECOUPLE_W (0 ), - .ID_WIDTH (CsrAxiSlvIdWidth ), - .USER_WIDTH (SpatzAxiUserWidth ), - .axi_req_t (axi_csr_slv_req_t ), - .axi_rsp_t (axi_csr_slv_resp_t ), - .reg_req_t (reg_req_t ), - .reg_rsp_t (reg_rsp_t ) - ) i_csr_axi_to_reg ( - .clk_i (clk_i ), - .rst_ni (rst_ni ), - .testmode_i (1'b0 ), - .axi_req_i (axi_csr_req ), - .axi_rsp_o (axi_csr_rsp ), - .reg_req_o (reg_req ), - .reg_rsp_i (reg_rsp ) + bootrom #( + .DataWidth ( 32 ), + .AddrWidth ( AxiAddrWidth ) + ) i_bootrom ( + .clk_i ( clk_i ), + .req_i ( bootrom_reg_req.valid ), + .addr_i ( addr_t'(bootrom_reg_req.addr) ), + .rdata_o ( bootrom_reg_rsp.rdata ) + ); + `FF(bootrom_reg_rsp.ready, bootrom_reg_req.valid, 1'b0) + assign bootrom_reg_rsp.error = 1'b0; + + // ---- Target [1]: CSR → reqrsp_to_reg → cachepool_peripheral ---- + + `REG_BUS_TYPEDEF_ALL(reg_csr, addr_t, narrow_data_t, narrow_strb_t) + + peri_xbar_req_t peri_csr_req; + peri_xbar_rsp_t peri_csr_rsp; + + // Bundle xbar split channels into REQRSP struct + assign peri_csr_req.q = peri_xbar_tgt_req[PeriTgtCSR]; + assign peri_csr_req.q_valid = peri_xbar_tgt_req_valid[PeriTgtCSR]; + assign peri_csr_req.p_ready = peri_xbar_tgt_rsp_ready[PeriTgtCSR]; + assign peri_xbar_tgt_req_ready[PeriTgtCSR] = peri_csr_rsp.q_ready; + assign peri_xbar_tgt_rsp[PeriTgtCSR] = peri_csr_rsp.p; + assign peri_xbar_tgt_rsp_valid[PeriTgtCSR] = peri_csr_rsp.p_valid; + + reg_csr_req_t reg_req; + reg_csr_rsp_t reg_rsp; + + reqrsp_to_reg #( + .AddrWidth ( AxiAddrWidth ), + .DataWidth ( 32 ), + .UserWidth ( $bits(peri_xbar_user_t) ), + .FifoDepth ( 2 ), + .ShiftResponse( 1'b0 ), + .user_t ( peri_xbar_user_t ), + .reqrsp_req_t ( peri_xbar_req_t ), + .reqrsp_rsp_t ( peri_xbar_rsp_t ), + .reg_req_t ( reg_csr_req_t ), + .reg_rsp_t ( reg_csr_rsp_t ) + ) i_reqrsp_to_reg_csr ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .reqrsp_req_i ( peri_csr_req ), + .reqrsp_rsp_o ( peri_csr_rsp ), + .reg_req_o ( reg_req ), + .reg_rsp_i ( reg_rsp ) ); - - - // Event counter increments for the TCDM. - typedef struct packed { - /// Number requests going in - logic [$clog2(5):0] inc_accessed; - /// Number of requests stalled due to congestion - logic [$clog2(5):0] inc_congested; - } tcdm_events_t; - - // Event counter increments for DMA. - typedef struct packed { - logic aw_stall, ar_stall, r_stall, w_stall, - buf_w_stall, buf_r_stall; - logic aw_valid, aw_ready, aw_done, aw_bw; - logic ar_valid, ar_ready, ar_done, ar_bw; - logic r_valid, r_ready, r_done, r_bw; - logic w_valid, w_ready, w_done, w_bw; - logic b_valid, b_ready, b_done; - logic dma_busy; - axi_pkg::len_t aw_len, ar_len; - axi_pkg::size_t aw_size, ar_size; - logic [$clog2(SpatzAxiNarrowDataWidth/8):0] num_bytes_written; - } dma_events_t; cachepool_peripheral #( - .AddrWidth (AxiAddrWidth ), - .SPMWidth ($clog2(L1NumSet)), - .NumTiles (NumTiles ), - .reg_req_t (reg_req_t ), - .reg_rsp_t (reg_rsp_t ), - .cache_insn_t (cache_insn_t ), - .NrCores (NrCores ) + .AddrWidth ( AxiAddrWidth ), + .SPMWidth ( $clog2(L1NumSet)), + .NumTiles ( NumTiles ), + .reg_req_t ( reg_csr_req_t ), + .reg_rsp_t ( reg_csr_rsp_t ), + .cache_insn_t ( cache_insn_t ) ) i_cachepool_cluster_peripheral ( - .clk_i (clk_i ), - .rst_ni (rst_ni ), - .eoc_o (eoc_o ), - .reg_req_i (reg_req ), - .reg_rsp_o (reg_rsp ), - /// The TCDM always starts at the cluster base. - .tcdm_start_address_i (tcdm_start_address ), - .tcdm_end_address_i (tcdm_end_address ), - .icache_prefetch_enable_o (icache_prefetch_enable), - .cl_clint_o (cl_interrupt ), - .cluster_hart_base_id_i (hart_base_id_i ), - .cluster_probe_o (cluster_probe_o ), - .dynamic_offset_o (dynamic_offset ), - .private_start_addr_o (private_start_addr ), - .l1d_spm_size_o ( ), - .l1d_private_o (l1d_private ), - .l1d_insn_o (l1d_insn ), - .l1d_insn_valid_o (l1d_insn_valid ), - .l1d_insn_ready_i (l1d_insn_ready ), - .l1d_busy_o (l1d_busy ) + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .eoc_o ( eoc_o ), + .reg_req_i ( reg_req ), + .reg_rsp_o ( reg_rsp ), + .tcdm_start_address_i ( tcdm_start_address ), + .tcdm_end_address_i ( tcdm_end_address ), + .icache_prefetch_enable_o ( icache_prefetch_enable ), + .cluster_hart_base_id_i ( hart_base_id_i ), + .cluster_probe_o ( cluster_probe_o ), + .dynamic_offset_o ( dynamic_offset ), + .private_start_addr_o ( private_start_addr ), + .l1d_spm_size_o ( ), + .l1d_private_o ( l1d_private ), + .l1d_insn_o ( l1d_insn ), + .l1d_insn_valid_o ( l1d_insn_valid ), + .l1d_insn_ready_i ( l1d_insn_ready ), + .l1d_busy_o ( l1d_busy ), + .barrier_participation_mask_o ( barrier_participation_mask ) + ); + + // ---- Target [2]: UART → reqrsp_to_axi → axi_narrow_req_o ---- + + peri_xbar_req_t peri_uart_req; + peri_xbar_rsp_t peri_uart_rsp; + + // Bundle xbar split channels into REQRSP struct + assign peri_uart_req.q = peri_xbar_tgt_req[PeriTgtUART]; + assign peri_uart_req.q_valid = peri_xbar_tgt_req_valid[PeriTgtUART]; + assign peri_uart_req.p_ready = peri_xbar_tgt_rsp_ready[PeriTgtUART]; + assign peri_xbar_tgt_req_ready[PeriTgtUART] = peri_uart_rsp.q_ready; + assign peri_xbar_tgt_rsp[PeriTgtUART] = peri_uart_rsp.p; + assign peri_xbar_tgt_rsp_valid[PeriTgtUART] = peri_uart_rsp.p_valid; + + reqrsp_to_axi #( + .MaxTrans ( 4 ), + .AxiIdWidth ( SpatzAxiUartIdWidth ), + .DataWidth ( 32 ), + .UserWidth ( $bits(peri_xbar_user_t) ), + .AxiUserWidth ( AxiUserWidth ), + .reqrsp_req_t ( peri_xbar_req_t ), + .reqrsp_rsp_t ( peri_xbar_rsp_t ), + .axi_req_t ( axi_uart_req_t ), + .axi_rsp_t ( axi_uart_resp_t ) + ) i_reqrsp_to_axi_uart ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .user_i ( '0 ), + .reqrsp_req_i ( peri_uart_req ), + .reqrsp_rsp_o ( peri_uart_rsp ), + .axi_req_o ( axi_narrow_req_o ), + .axi_rsp_i ( axi_narrow_resp_i ) ); endmodule diff --git a/hardware/src/cachepool_cluster_barrier.sv b/hardware/src/cachepool_cluster_barrier.sv index 7ed2ec6..f71c866 100644 --- a/hardware/src/cachepool_cluster_barrier.sv +++ b/hardware/src/cachepool_cluster_barrier.sv @@ -6,138 +6,79 @@ `include "common_cells/registers.svh" -/// Last level of barrier in CachePool -/// Can perform partial/full barrier based on tile numbers +/// Cluster-level barrier controller. +/// Collects per-tile barrier signals (direct wires, no AXI/NoC) and broadcasts +/// a done signal when all participating tiles have reached the barrier. +/// Supports partial barriers via `barrier_mask_i`. module cachepool_cluster_barrier - import snitch_pkg::*; - import cachepool_peripheral_reg_pkg::*; #( - parameter int unsigned AddrWidth = 0, - parameter int NrPorts = 0, - parameter type axi_req_t = logic, - parameter type axi_rsp_t = logic, - // Used to generate response - parameter type axi_id_t = logic, - parameter type axi_user_t = logic, - /// Derived parameter *Do not override* - parameter type addr_t = logic [AddrWidth-1:0] + parameter int unsigned NrTiles = 0 ) ( - input logic clk_i, - input logic rst_ni, - // AXI input - input axi_req_t [NrPorts-1:0] axi_slv_req_i, - output axi_rsp_t [NrPorts-1:0] axi_slv_rsp_o, - // AXI output - output axi_req_t [NrPorts-1:0] axi_mst_req_o, - input axi_rsp_t [NrPorts-1:0] axi_mst_rsp_i, - - input logic [NrPorts-1:0] barrier_i, - input addr_t cluster_periph_start_address_i + input logic clk_i, + input logic rst_ni, + // Per-tile barrier request (active-high, held until barrier completes) + input logic [NrTiles-1:0] tile_barrier_i, + // Broadcast barrier completion to all tiles + output logic barrier_done_o, + // Which tiles participate in the barrier (active-high mask). + // Active-low bits mark tiles that are excluded from synchronization. + input logic [NrTiles-1:0] barrier_mask_i ); typedef enum logic [1:0] { Idle, Wait, - Take, - Global + Done } barrier_state_e; - // FSM State of the barrier - barrier_state_e [NrPorts-1:0] state_d, state_q; - // the tiles participate in global barrier - logic [NrPorts-1:0] barrier_d, barrier_q; - - // Infomation stored for response generation - typedef struct packed { - axi_id_t id; - axi_user_t user; - } info_t; - - info_t [NrPorts-1:0] info_d, info_q; - - addr_t barrier_addr; - assign barrier_addr = cluster_periph_start_address_i + CACHEPOOL_PERIPHERAL_HW_BARRIER_OFFSET; + barrier_state_e state_d, state_q; - logic [NrPorts-1:0] is_barrier; - logic take_barrier; + // Latch the mask when barrier starts, so it stays stable during the sequence + logic [NrTiles-1:0] mask_d, mask_q; - // xnor between the is_barrier and the barrier needs to be taken - assign take_barrier = ~(|(barrier_q ^ is_barrier)); + // Masked barrier: only participating tiles matter + logic all_arrived; + assign all_arrived = (tile_barrier_i & mask_q) == mask_q; always_comb begin - state_d = state_q; - barrier_d = barrier_q; - info_d = info_q; - is_barrier = '0; - - // Pass through the signals by default - axi_mst_req_o = axi_slv_req_i; - axi_slv_rsp_o = axi_mst_rsp_i; - - // Maximum barrier counter may only be configured in Idle state - barrier_d = (|is_barrier == '0) ? barrier_i : barrier_q; - - for (int i = 0; i < NrPorts; i++) begin - - case (state_q[i]) - Idle: begin - if (axi_slv_req_i[i].ar_valid & axi_slv_req_i[i].ar.addr == barrier_addr) begin - // We have received a barrier request - // Do not forward it further - axi_mst_req_o[i].ar_valid = 0; - // Accept the request - axi_slv_rsp_o[i].ar_ready = 1; - // Record the info need for response - info_d[i] = '{ - id: axi_slv_req_i[i].ar.id, - user: axi_slv_req_i[i].ar.user - }; - // Switch to next state - if (barrier_q[i]) begin - // Wait for global barrier - state_d[i] = Wait; - end else begin - // Local tile barrier, no need to sync - state_d[i] = Take; - end + state_d = state_q; + mask_d = mask_q; + barrier_done_o = 1'b0; + + case (state_q) + Idle: begin + // When any participating tile asserts barrier, latch the mask and wait. + // Use barrier_mask_i directly here since mask_q may hold the previous mask. + if (|(tile_barrier_i & barrier_mask_i)) begin + mask_d = barrier_mask_i; + if ((tile_barrier_i & barrier_mask_i) == barrier_mask_i) begin + state_d = Done; + end else begin + state_d = Wait; end end + end - Wait: begin - is_barrier[i] = 1; - - if (take_barrier) begin - // Means all tile participate in the barrier have reach the point - state_d[i] = Take; - end - end - - Take: begin - if (!axi_mst_rsp_i[i].r_valid) begin - // Make sure no r packet is in transition - axi_slv_rsp_o[i].r = '0; - axi_slv_rsp_o[i].r_valid = 1'b1; - axi_slv_rsp_o[i].r.last = 1'b1; - axi_slv_rsp_o[i].r.id = info_q[i].id; - axi_slv_rsp_o[i].r.user = info_q[i].user; - - if (axi_slv_req_i[i].r_ready) begin - // response accepted, switch back state - state_d[i] = Idle; - info_d[i] = '0; - end - end + Wait: begin + // Wait until all participating tiles have reached the barrier + if (all_arrived) begin + state_d = Done; end - - default: state_d[i] = Idle; - endcase - end + end + + Done: begin + // Assert barrier_done for one cycle, then return to Idle. + // Tiles deassert barrier_o the cycle after seeing barrier_done_i, + // and no new barrier can start until cores complete the Take sequence. + barrier_done_o = 1'b1; + state_d = Idle; + end + + default: state_d = Idle; + endcase end - for (genvar i = 0; i < NrPorts; i++) begin : gen_ff - `FFARN(state_q[i], state_d[i], Idle, clk_i, rst_ni) - `FFARN(barrier_q[i], barrier_d[i], '0, clk_i, rst_ni) - `FFARN(info_q[i], info_d[i], '0, clk_i, rst_ni) - end + `FFARN(state_q, state_d, Idle, clk_i, rst_ni) + `FFARN(mask_q, mask_d, '0, clk_i, rst_ni) endmodule diff --git a/hardware/src/cachepool_group.sv b/hardware/src/cachepool_group.sv index b14d1ac..64779e7 100644 --- a/hardware/src/cachepool_group.sv +++ b/hardware/src/cachepool_group.sv @@ -4,19 +4,9 @@ // Author: Diyou Shen -`include "axi/assign.svh" `include "axi/typedef.svh" -`include "common_cells/assertions.svh" `include "common_cells/registers.svh" -`include "mem_interface/assign.svh" -`include "mem_interface/typedef.svh" -`include "register_interface//assign.svh" `include "register_interface/typedef.svh" -`include "reqrsp_interface/assign.svh" -`include "reqrsp_interface/typedef.svh" -`include "snitch_vm/typedef.svh" -`include "tcdm_interface/assign.svh" -`include "tcdm_interface/typedef.svh" /// Group implementation for CachePool module cachepool_group @@ -24,7 +14,7 @@ module cachepool_group import spatz_pkg::*; import fpnew_pkg::fpu_implementation_t; import snitch_pma_pkg::snitch_pma_t; - import snitch_icache_pkg::icache_events_t; + import snitch_icache_pkg::icache_l1_events_t; #( /// Width of physical address. parameter int unsigned AxiAddrWidth = 48, @@ -48,10 +38,6 @@ module cachepool_group parameter int unsigned ClusterPeriphSize = 64, /// Number of TCDM Banks. parameter int unsigned NrBanks = 2 * NrCores, - /// Size of DMA AXI buffer. - parameter int unsigned DMAAxiReqFifoDepth = 3, - /// Size of DMA request fifo. - parameter int unsigned DMAReqFifoDepth = 3, /// Width of a single icache line. parameter unsigned ICacheLineWidth = 0, /// Number of icache lines per set. @@ -65,8 +51,6 @@ module cachepool_group /// Spatz FPU/IPU Configuration parameter int unsigned NumSpatzFPUs = 4, parameter int unsigned NumSpatzIPUs = 1, - /// Per-core enabling of the custom `Xdma` ISA extensions. - parameter bit [NrCores-1:0] Xdma = '{default: '0}, /// # Per-core parameters /// Per-core integer outstanding loads parameter int unsigned NumIntOutstandingLoads = 0, @@ -107,7 +91,12 @@ module cachepool_group parameter int unsigned MemoryMacroLatency = 1 + RegisterTCDMCuts, /// # SRAM Configuration rules needed: L1D Tag + L1D Data + L1D FIFO + L1I Tag + L1I Data /*** ATTENTION: `NrSramCfg` should be changed if `L1NumDataBank` and `L1NumTagBank` is changed ***/ - parameter int unsigned NrSramCfg = 1 + parameter int unsigned NrSramCfg = 1, + + localparam int unsigned TotRGPorts = (NumRemoteGroupPortCore == 0) ? 0 : + NumTilesPerGroup*NumRemoteGroupPortCore*NrTCDMPortsPerCore-1, + localparam int unsigned NumRemoteGroupPortTile = (NumRemoteGroupPortCore == 0) ? 1 : + NumRemoteGroupPortCore * NrTCDMPortsPerCore ) ( /// System clock. input logic clk_i, @@ -115,48 +104,65 @@ module cachepool_group input logic rst_ni, /// Per-core debug request signal. Asserting this signals puts the /// corresponding core into debug mode. This signal is assumed to be _async_. - input logic [NrCores-1:0] debug_req_i, + input logic debug_req_i, /// Machine external interrupt pending. Usually those interrupts come from a /// platform-level interrupt controller. This signal is assumed to be _async_. - input logic [NrCores-1:0] meip_i, + input logic meip_i, /// Machine timer interrupt pending. Usually those interrupts come from a /// core-local interrupt controller such as a timer/RTC. This signal is /// assumed to be _async_. - input logic [NrCores-1:0] mtip_i, + input logic mtip_i, /// Core software interrupt pending. Usually those interrupts come from /// another core to facilitate inter-processor-interrupts. This signal is /// assumed to be _async_. - input logic [NrCores-1:0] msip_i, + input logic msip_i, /// First hartid of the cluster. Cores of a cluster are monotonically /// increasing without a gap, i.e., a cluster with 8 cores and a /// `hart_base_id_i` of 5 get the hartids 5 - 12. input logic [9:0] hart_base_id_i, + /// Globally-unique tile ID of the first tile in this group (= group_index * NumTilesPerGroup). + input logic [TileIDWidth-1:0] tile_base_id_i, /// Base address of cluster. TCDM and cluster peripheral location are derived from /// it. This signal is pseudo-static. input axi_addr_t cluster_base_addr_i, /// Partitioning address input axi_addr_t private_start_addr_i, - /// AXI Narrow out-port (UART/Peripheral) - output axi_narrow_req_t [GroupNarrowAxiPorts-1:0] axi_narrow_req_o, - input axi_narrow_resp_t [GroupNarrowAxiPorts-1:0] axi_narrow_rsp_i, - /// Wide AXI ports to cluster level - output axi_out_req_t [GroupWideAxiPorts-1:0] axi_wide_req_o, - input axi_out_resp_t [GroupWideAxiPorts-1:0] axi_wide_rsp_i, - - /// Cache refill ports - output cache_trans_req_t [NumL1CacheCtrl-1:0] cache_refill_req_o, - input cache_trans_rsp_t [NumL1CacheCtrl-1:0] cache_refill_rsp_i, + /// L2 refill reqrsp port (single merged output: cache refill + iCache/peripheral) + output l2_req_t l2_req_o, + input l2_rsp_t l2_rsp_i, + /// This group's L2 refill mesh floo endpoint ID. Stamped into + /// refill_user_t.l2_src_id on every outgoing L2 request so the HBM + /// ejection chimney can route responses back without a local src_id + /// FIFO (which assumed in-order HBM completion). + input floo_cachepool_noc_pkg::id_t l2_group_id_i, /// Peripheral signals - output icache_events_t [NrCores-1:0] icache_events_o, + output icache_l1_events_t [NrCores-1:0] icache_events_o, input logic icache_prefetch_enable_i, input logic [NrCores-1:0] cl_interrupt_i, input logic [$clog2(AxiAddrWidth)-1:0] dynamic_offset_i, - input logic [3:0] l1d_private_i, - input cache_insn_t l1d_insn_i, + input logic [$clog2(NumL1CtrlTile):0] l1d_private_i, + input cache_insn_t l1d_insn_i, input logic l1d_insn_valid_i, - output logic [NumTiles-1:0] l1d_insn_ready_o, - input logic [NumTiles-1:0] l1d_busy_i, + output logic [NumTilesPerGroup-1:0] l1d_insn_ready_o, + input logic [NumTilesPerGroup-1:0] l1d_busy_i, + + /// Inter-group remote access ports (to other groups). + /// Layout: [NumTilesPerGroup-1:0][NumRemoteGroupPortTile-1:0] flattened to + /// [NumTilesPerGroup * NumRemoteGroupPortTile - 1 : 0]. + /// Per-tile flat index: j + r * NrTCDMPortsPerCore (j = interco instance, + /// r = inter-group slot within that instance). + /// NumRemoteGroupPortTile = NumRemoteGroupPortCore * NrTCDMPortsPerCore. + /// Uses REQRSP-style types with built-in ready and remote_group_user_t. + output remote_group_req_t [TotRGPorts:0] remote_group_req_o, + input remote_group_rsp_t [TotRGPorts:0] remote_group_rsp_i, + /// Inter-group remote access ports (from other groups) + input remote_group_req_t [TotRGPorts:0] remote_group_req_i, + output remote_group_rsp_t [TotRGPorts:0] remote_group_rsp_o, + + // Direct-wire barrier: one bit per tile + output logic [NumTilesPerGroup-1:0] tile_barrier_o, + input logic barrier_done_i, /// SRAM Configuration input impl_in_t [NrSramCfg-1:0] impl_i, @@ -173,17 +179,9 @@ module cachepool_group // --------- // Constants // --------- - /// Minimum width to hold the core number. - localparam int unsigned CoreIDWidth = cf_math_pkg::idx_width(NrCores); - localparam int unsigned TileIDWidth = cf_math_pkg::idx_width(NumTiles); - - // Enlarge the address width for Spatz due to cache - localparam int unsigned TCDMAddrWidth = L1AddrWidth; - - // Core Request, SoC Request - localparam int unsigned NrNarrowMasters = 2; + // Per-group overrides of package-level constants that depend on NumTiles/NumCores. + localparam int unsigned NumL1CacheCtrlLocal = NrCores; - localparam int unsigned WideIdWidthOut = AxiIdWidthOut; localparam int unsigned WideIdWidthIn = AxiIdWidthOut; @@ -194,13 +192,9 @@ module cachepool_group typedef logic [AxiDataWidth-1:0] data_cache_t; typedef logic [AxiDataWidth/8-1:0] strb_cache_t; typedef logic [WideIdWidthIn-1:0] id_cache_mst_t; - typedef logic [WideIdWidthOut-1:0] id_cache_slv_t; typedef logic [AxiUserWidth-1:0] user_cache_t; `AXI_TYPEDEF_ALL(axi_mst_cache, addr_t, id_cache_mst_t, data_cache_t, strb_cache_t, user_cache_t) - `AXI_TYPEDEF_ALL(axi_slv_cache, addr_t, id_cache_slv_t, data_cache_t, strb_cache_t, user_cache_t) - - `REG_BUS_TYPEDEF_ALL(reg_cache, addr_t, data_cache_t, strb_cache_t) typedef struct packed { int unsigned idx; @@ -208,170 +202,514 @@ module cachepool_group addr_t end_addr; } xbar_rule_t; - `SNITCH_VM_TYPEDEF(AxiAddrWidth) - // --------------- // CachePool Tile // --------------- - logic [NumTiles-1:0] error; + logic [NumTilesPerGroup-1:0] error; assign error_o = |error; - // Tile remote access signals - // In/Out relative to the tile (out--leave a tile; in--enter a tile) - // Tile-side flat layout: index = j + r*NrTCDMPortsPerCore (j=xbar idx, r=remote slot within xbar) - tcdm_req_t [NumTiles-1:0][NumRemotePortTile-1:0] tile_remote_out_req; - tcdm_rsp_t [NumTiles-1:0][NumRemotePortTile-1:0] tile_remote_out_rsp; - logic [NumTiles-1:0][NumRemotePortTile-1:0] tile_remote_in_ready, tile_remote_out_ready; + // Per-tile iCache AXI (single port, BootROM moved to cluster level) + axi_mst_cache_req_t [NumTilesPerGroup-1:0] axi_tile_mem_req; + axi_mst_cache_resp_t [NumTilesPerGroup-1:0] axi_tile_mem_rsp; + + // Cache refill ports from tiles (NumL1CacheCtrlLocal = NumCores total) + cache_trans_req_t [NumL1CacheCtrlLocal-1:0] cache_refill_req; + cache_trans_rsp_t [NumL1CacheCtrlLocal-1:0] cache_refill_rsp; + + // Per-tile peripheral REQRSP (narrow 32b, from tile core mux) + periph_req_t [NumTilesPerGroup-1:0] tile_periph_req; + periph_rsp_t [NumTilesPerGroup-1:0] tile_periph_rsp; + + // L2 Group ICache AXI master output (from axi_hier_interco) + axi_mst_cache_req_t axi_l2icache_mst_req; + axi_mst_cache_resp_t axi_l2icache_mst_rsp; + // L2 Group ICache reqrsp output (to refill xbar port 0) + cache_trans_req_t cache_l2icache_req; + cache_trans_rsp_t cache_l2icache_rsp; + // L2 Group ICache control (hardwired) + ro_cache_ctrl_t l2icache_ctrl; + + // Flat xbar input channels: NumClusterMst ports per tile (1 iCache + NumL1CtrlTile refill) + cache_trans_req_chan_t [NumTilesPerGroup*NumClusterMst-1:0] tile_req_chan; + cache_trans_rsp_chan_t [NumTilesPerGroup*NumClusterMst-1:0] tile_rsp_chan; + logic [NumTilesPerGroup*NumClusterMst-1:0] tile_req_valid, tile_req_ready, + tile_rsp_valid, tile_rsp_ready; + + // Xbar output channel: single merged output (N:1 mux) + cache_trans_req_chan_t l2_req_chan; + cache_trans_rsp_chan_t l2_rsp_chan; + logic l2_req_valid, l2_req_ready, + l2_rsp_valid, l2_rsp_ready; + + // Response demux selection: which xbar input port does the response target + typedef logic [$clog2(NumClusterMst*NumTilesPerGroup)-1:0] l2_sel_t; + l2_sel_t l2_sel; + + // --------------------- + // L2 Group ICache: 4-to-1 AXI mux + read-only cache + ID remap + // --------------------- + always_comb begin + l2icache_ctrl = '0; + l2icache_ctrl.enable = 1'b1; + l2icache_ctrl.flush_valid = 1'b0; + // Rule 0: DRAM range + l2icache_ctrl.start_addr[0] = DramAddr; + l2icache_ctrl.end_addr[0] = DramAddr + DramSize; + // Rule 1: BootROM range — routes through Cache path, not Bypass, + // to avoid axi_mux MSB ID corruption in snitch_read_only_cache + l2icache_ctrl.start_addr[1] = BootAddr; + l2icache_ctrl.end_addr[1] = BootAddr + 'h1000; + end - tcdm_req_t [NumTiles-1:0][NumRemotePortTile-1:0] tile_remote_in_req; - tcdm_rsp_t [NumTiles-1:0][NumRemotePortTile-1:0] tile_remote_in_rsp; + axi_hier_interco #( + .NumSlvPorts ( NumTilesPerGroup ), + .NumMstPorts ( 1 ), + .Radix ( NumTilesPerGroup ), + .EnableCache ( 1 ), + .CacheLineWidth ( L2ICacheLineWidth ), + .CacheSizeByte ( L2ICacheSizeByte ), + .CacheSets ( L2ICacheSets ), + .AddrWidth ( AxiAddrWidth ), + .DataWidth ( AxiDataWidth ), + .SlvIdWidth ( WideIdWidthIn ), + .MstIdWidth ( WideIdWidthIn ), + .UserWidth ( AxiUserWidth ), + .slv_req_t ( axi_mst_cache_req_t ), + .slv_resp_t ( axi_mst_cache_resp_t ), + .mst_req_t ( axi_mst_cache_req_t ), + .mst_resp_t ( axi_mst_cache_resp_t ) + ) i_l2icache_interco ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .test_i ( 1'b0 ), + .ro_cache_ctrl_i ( l2icache_ctrl ), + .slv_req_i ( axi_tile_mem_req ), + .slv_resp_o ( axi_tile_mem_rsp ), + .mst_req_o ( axi_l2icache_mst_req ), + .mst_resp_i ( axi_l2icache_mst_rsp ) + ); - // Xbar-side: NrTCDMPortsPerCore xbars, each with NumTiles*NumRemotePortCore ports - // Xbar port index = t*NumRemotePortCore + r - tcdm_req_chan_t [NrTCDMPortsPerCore-1:0][NumTiles*NumRemotePortCore-1:0] tile_remote_out_req_chan; - logic [NrTCDMPortsPerCore-1:0][NumTiles*NumRemotePortCore-1:0] tile_remote_out_req_valid, tile_remote_out_req_ready; - tcdm_rsp_chan_t [NrTCDMPortsPerCore-1:0][NumTiles*NumRemotePortCore-1:0] tile_remote_out_rsp_chan; - logic [NrTCDMPortsPerCore-1:0][NumTiles*NumRemotePortCore-1:0] tile_remote_out_rsp_valid, tile_remote_out_rsp_ready; + // Convert L2 ICache AXI output to REQRSP for merging into refill xbar + axi_to_reqrsp #( + .axi_req_t ( axi_mst_cache_req_t ), + .axi_rsp_t ( axi_mst_cache_resp_t ), + .AddrWidth ( AxiAddrWidth ), + .DataWidth ( AxiDataWidth ), + .UserWidth ( $bits(refill_user_t) ), + .IdWidth ( WideIdWidthIn ), + .BufDepth ( 0 ), + .EnUserIdPassthrough ( 1'b1 ), + .reqrsp_req_t ( cache_trans_req_t ), + .reqrsp_rsp_t ( cache_trans_rsp_t ) + ) i_l2icache_axi2reqrsp ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .busy_o ( ), + .axi_req_i ( axi_l2icache_mst_req ), + .axi_rsp_o ( axi_l2icache_mst_rsp ), + .reqrsp_req_o ( cache_l2icache_req ), + .reqrsp_rsp_i ( cache_l2icache_rsp ) + ); - tcdm_req_chan_t [NrTCDMPortsPerCore-1:0][NumTiles*NumRemotePortCore-1:0] tile_remote_in_req_chan; - logic [NrTCDMPortsPerCore-1:0][NumTiles*NumRemotePortCore-1:0] tile_remote_in_req_valid, tile_remote_in_req_ready; - tcdm_rsp_chan_t [NrTCDMPortsPerCore-1:0][NumTiles*NumRemotePortCore-1:0] tile_remote_in_rsp_chan; - logic [NrTCDMPortsPerCore-1:0][NumTiles*NumRemotePortCore-1:0] tile_remote_in_rsp_valid, tile_remote_in_rsp_ready; + // --------------------- + // Wiring: assemble flat xbar input from refill + peripheral + iCache paths + // --------------------- + // Port layout per tile (NumClusterMst = NumL1CtrlTile + 2): + // p = 0..NumL1CtrlTile-1 → refill (bank_id = p, set by tile) + // p = NumL1CtrlTile → peripheral (bank_id = BankIdPeriph) + // p = NumL1CtrlTile+1 → iCache (bank_id = BankIdICache, only tile 0) + always_comb begin + for (int t = 0; t < NumTilesPerGroup; t++) begin + for (int p = 0; p < NumClusterMst; p++) begin + automatic int unsigned xbar_idx = t * NumClusterMst + p; + + if (p < NumL1CtrlTile) begin + // Refill path: p maps directly to cache controller index + automatic int unsigned refill_idx = t * NumL1CtrlTile + p; + tile_req_chan [xbar_idx] = cache_refill_req[refill_idx].q; + tile_req_chan [xbar_idx].addr = scrambleAddr(cache_refill_req[refill_idx].q.addr); + tile_req_valid [xbar_idx] = cache_refill_req[refill_idx].q_valid; + cache_refill_rsp[refill_idx].q_ready = tile_req_ready[xbar_idx]; + + cache_refill_rsp[refill_idx].p = tile_rsp_chan [xbar_idx]; + cache_refill_rsp[refill_idx].p_valid = tile_rsp_valid[xbar_idx]; + tile_rsp_ready [xbar_idx] = cache_refill_req[refill_idx].p_ready; + tile_req_chan [xbar_idx].user.tile_id = t; + tile_req_chan [xbar_idx].user.l2_src_id = l2_group_id_i; + + end else if (p == BankIdPeriph) begin + // Peripheral path: narrow REQRSP directly connected (data width mismatch — placeholder) + tile_req_chan [xbar_idx] = '0; + tile_req_chan [xbar_idx].addr = scrambleAddr(tile_periph_req[t].q.addr); + tile_req_chan [xbar_idx].write = tile_periph_req[t].q.write; + tile_req_chan [xbar_idx].amo = tile_periph_req[t].q.amo; + tile_req_chan [xbar_idx].size = tile_periph_req[t].q.size; + tile_req_chan [xbar_idx].data = axi_wide_data_t'(tile_periph_req[t].q.data); + tile_req_chan [xbar_idx].strb = axi_wide_strb_t'(tile_periph_req[t].q.strb); + tile_req_chan [xbar_idx].user.tile_id = t; + tile_req_chan [xbar_idx].user.bank_id = BankIdPeriph; + tile_req_chan [xbar_idx].user.l2_src_id = l2_group_id_i; + // Borrow the info field to carry req_id through the mesh + tile_req_chan [xbar_idx].user.info = cache_info_t'(tile_periph_req[t].q.user.req_id); + tile_req_valid [xbar_idx] = tile_periph_req[t].q_valid; + tile_periph_rsp[t].q_ready = tile_req_ready[xbar_idx]; + + tile_periph_rsp[t].p.data = tile_rsp_chan[xbar_idx].data[31:0]; + tile_periph_rsp[t].p.error = tile_rsp_chan[xbar_idx].error; + tile_periph_rsp[t].p.write = tile_rsp_chan[xbar_idx].write; + // Restore req_id from the info field; zero other tcdm_user fields + tile_periph_rsp[t].p.user = '0; + tile_periph_rsp[t].p.user.req_id = reqid_t'(tile_rsp_chan[xbar_idx].user.info); + tile_periph_rsp[t].p_valid = tile_rsp_valid[xbar_idx]; + tile_rsp_ready [xbar_idx] = tile_periph_req[t].p_ready; + + end else begin + // iCache path: only tile 0 active + if (t == 0) begin + tile_req_chan [xbar_idx] = cache_l2icache_req.q; + tile_req_chan [xbar_idx].addr = scrambleAddr(cache_l2icache_req.q.addr); + tile_req_valid [xbar_idx] = cache_l2icache_req.q_valid; + cache_l2icache_rsp.q_ready = tile_req_ready[xbar_idx]; + + cache_l2icache_rsp.p = tile_rsp_chan [xbar_idx]; + cache_l2icache_rsp.p_valid = tile_rsp_valid[xbar_idx]; + tile_rsp_ready [xbar_idx] = cache_l2icache_req.p_ready; + tile_req_chan [xbar_idx].user.tile_id = '0; + tile_req_chan [xbar_idx].user.bank_id = BankIdICache; + tile_req_chan [xbar_idx].user.l2_src_id = l2_group_id_i; + // axi_to_reqrsp with EnUserIdPassthrough packs the AXI ID into + // the LSBs of the user field (spanning burst + info). Move the + // ID into info and zero burst so that reqrsp_to_axi (EnBurst+ + // ShuffleId) sees is_burst=0 → keeps ID=0 for iCache requests. + tile_req_chan [xbar_idx].user.info = cache_info_t'(cache_l2icache_req.q.user); + tile_req_chan [xbar_idx].user.burst = '0; + end else begin + tile_req_chan [xbar_idx] = '0; + tile_req_valid [xbar_idx] = 1'b0; + tile_rsp_ready [xbar_idx] = 1'b0; + end + end + end + end + end + + // --------------------- + // Refill xbar: N:1 mux (all requests go to single output) + // No address decoder needed — the mesh chimney handles routing. + // No burst protection needed — single output, no multi-channel response demux. + // --------------------- + reqrsp_xbar #( + .NumInp ( NumClusterMst*NumTilesPerGroup ), + .NumOut ( 1 ), + .PipeReg ( 1'b1 ), + .ExtReqPrio ( 1'b0 ), + .ExtRspPrio ( 1'b0 ), + .tcdm_req_chan_t ( cache_trans_req_chan_t ), + .tcdm_rsp_chan_t ( cache_trans_rsp_chan_t ) + ) i_refill_xbar ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .slv_req_i ( tile_req_chan ), + .slv_req_valid_i ( tile_req_valid ), + .slv_req_ready_o ( tile_req_ready ), + .slv_rsp_o ( tile_rsp_chan ), + .slv_rsp_valid_o ( tile_rsp_valid ), + .slv_rsp_ready_i ( tile_rsp_ready ), + .slv_sel_i ( '{default: '0} ), + .slv_rr_i ( '0 ), + .slv_selected_o ( /* unused */ ), + .mst_req_o ( l2_req_chan ), + .mst_req_valid_o ( l2_req_valid ), + .mst_req_ready_i ( l2_req_ready ), + .mst_rsp_i ( l2_rsp_chan ), + .mst_rr_i ( '0 ), + .mst_rsp_valid_i ( l2_rsp_valid ), + .mst_rsp_ready_o ( l2_rsp_ready ), + .mst_sel_i ( l2_sel ) + ); + + // --------------------- + // l2_req/rsp packing: bridge xbar channels <-> l2_req_t/l2_rsp_t port + // --------------------- + always_comb begin + // Request: xbar -> group output port + l2_req_o.q = '{ + addr : l2_req_chan.addr, + write : l2_req_chan.write, + amo : l2_req_chan.amo, + data : l2_req_chan.data, + strb : l2_req_chan.strb, + size : l2_req_chan.size, + default: '0 + }; + l2_req_o.q.user = l2_req_chan.user; + l2_req_o.q_valid = l2_req_valid; + l2_req_ready = l2_rsp_i.q_ready; + + // Response: group input port -> xbar + l2_rsp_chan = '{ + data : l2_rsp_i.p.data, + error : l2_rsp_i.p.error, + write : l2_rsp_i.p.write, + default: '0 + }; + l2_rsp_chan.user = l2_rsp_i.p.user; + l2_rsp_valid = l2_rsp_i.p_valid; + l2_req_o.p_ready = l2_rsp_ready; + + // Response demux: which xbar input port does this response target? + l2_sel = l2_rsp_i.p.user.tile_id * NumClusterMst + + l2_rsp_i.p.user.bank_id; + end + + + // Tile remote access signals + // In/Out relative to the tile (out--leave a tile; in--enter a tile) + // Tile-side flat layout: index = j + r*NrTCDMPortsPerCore (j=xbar idx, r=remote slot within xbar) + tcdm_req_t [NumTilesPerGroup-1:0][NumLGPortTile-1:0] tile_remote_out_req; + tcdm_rsp_t [NumTilesPerGroup-1:0][NumLGPortTile-1:0] tile_remote_out_rsp; + logic [NumTilesPerGroup-1:0][NumLGPortTile-1:0] tile_remote_in_ready, tile_remote_out_ready; + + tcdm_req_t [NumTilesPerGroup-1:0][NumLGPortTile-1:0] tile_remote_in_req; + tcdm_rsp_t [NumTilesPerGroup-1:0][NumLGPortTile-1:0] tile_remote_in_rsp; + + // Xbar-side: NrTCDMPortsPerCore xbars, each with NumTilesPerGroup*NumLGPortCore ports + // Xbar port index = t*NumLGPortCore + r + tcdm_req_chan_t [NrTCDMPortsPerCore-1:0][NumTilesPerGroup*NumLGPortCore-1:0] tile_remote_out_req_chan; + logic [NrTCDMPortsPerCore-1:0][NumTilesPerGroup*NumLGPortCore-1:0] tile_remote_out_req_valid, tile_remote_out_req_ready; + tcdm_rsp_chan_t [NrTCDMPortsPerCore-1:0][NumTilesPerGroup*NumLGPortCore-1:0] tile_remote_out_rsp_chan; + logic [NrTCDMPortsPerCore-1:0][NumTilesPerGroup*NumLGPortCore-1:0] tile_remote_out_rsp_valid, tile_remote_out_rsp_ready; + + tcdm_req_chan_t [NrTCDMPortsPerCore-1:0][NumTilesPerGroup*NumLGPortCore-1:0] tile_remote_in_req_chan; + logic [NrTCDMPortsPerCore-1:0][NumTilesPerGroup*NumLGPortCore-1:0] tile_remote_in_req_valid, tile_remote_in_req_ready; + tcdm_rsp_chan_t [NrTCDMPortsPerCore-1:0][NumTilesPerGroup*NumLGPortCore-1:0] tile_remote_in_rsp_chan; + logic [NrTCDMPortsPerCore-1:0][NumTilesPerGroup*NumLGPortCore-1:0] tile_remote_in_rsp_valid, tile_remote_in_rsp_ready; + + // Per-group override of package-level remote xbar selection width. + // The package uses NumTiles (total), but the group's xbar is sized per-group. + localparam int unsigned LocalRemoteXbarSelWidth = $clog2(NumTilesPerGroup * NumLGPortCore); + typedef logic [LocalRemoteXbarSelWidth-1:0] local_remote_xbar_sel_t; // Tile-side selection: narrow type, only carries tile_id - remote_tile_sel_t [NumTiles-1:0][NumRemotePortTile-1:0] remote_out_sel_tile; - // Xbar-side selection: wider type, encodes tile_id*NumRemotePortCore + core_id%NumRemotePortCore - remote_xbar_sel_t [NrTCDMPortsPerCore-1:0][NumTiles*NumRemotePortCore-1:0] remote_out_sel_xbar, remote_in_sel_xbar; + remote_tile_sel_t [NumTilesPerGroup-1:0][NumLGPortTile-1:0] remote_out_sel_tile; + // Xbar-side selection: wider type, encodes tile_id*NumLGPortCore + core_id%NumLGPortCore + local_remote_xbar_sel_t [NrTCDMPortsPerCore-1:0][NumTilesPerGroup*NumLGPortCore-1:0] remote_out_sel_xbar, remote_in_sel_xbar; - for (genvar t = 0; t < NumTiles; t++) begin + for (genvar t = 0; t < NumTilesPerGroup; t++) begin for (genvar j = 0; j < NrTCDMPortsPerCore; j++) begin - for (genvar r = 0; r < NumRemotePortCore; r++) begin + for (genvar r = 0; r < NumLGPortCore; r++) begin // tile flat index: j + r*NrTCDMPortsPerCore - // xbar port index: t*NumRemotePortCore + r - assign tile_remote_out_req_chan [j][t*NumRemotePortCore+r] = tile_remote_out_req[t][j+r*NrTCDMPortsPerCore].q; - assign tile_remote_out_req_valid[j][t*NumRemotePortCore+r] = tile_remote_out_req[t][j+r*NrTCDMPortsPerCore].q_valid; - assign tile_remote_out_rsp_ready[j][t*NumRemotePortCore+r] = tile_remote_in_ready[t][j+r*NrTCDMPortsPerCore]; - - assign tile_remote_out_rsp[t][j+r*NrTCDMPortsPerCore].p = tile_remote_out_rsp_chan [j][t*NumRemotePortCore+r]; - assign tile_remote_out_rsp[t][j+r*NrTCDMPortsPerCore].p_valid = tile_remote_out_rsp_valid[j][t*NumRemotePortCore+r]; - assign tile_remote_out_rsp[t][j+r*NrTCDMPortsPerCore].q_ready = tile_remote_out_req_ready[j][t*NumRemotePortCore+r]; - - assign tile_remote_in_req[t][j+r*NrTCDMPortsPerCore].q = tile_remote_in_req_chan [j][t*NumRemotePortCore+r]; - assign tile_remote_in_req[t][j+r*NrTCDMPortsPerCore].q_valid = tile_remote_in_req_valid[j][t*NumRemotePortCore+r]; - assign tile_remote_out_ready[t][j+r*NrTCDMPortsPerCore] = tile_remote_in_rsp_ready[j][t*NumRemotePortCore+r]; - - assign tile_remote_in_rsp_chan [j][t*NumRemotePortCore+r] = tile_remote_in_rsp[t][j+r*NrTCDMPortsPerCore].p; - assign tile_remote_in_rsp_valid[j][t*NumRemotePortCore+r] = tile_remote_in_rsp[t][j+r*NrTCDMPortsPerCore].p_valid; - assign tile_remote_in_req_ready[j][t*NumRemotePortCore+r] = tile_remote_in_rsp[t][j+r*NrTCDMPortsPerCore].q_ready; - - // Request selection: route to target tile's remote-in slot based on - // target tile ID, so that all requests to the same destination tile - // travel through one pipeline — preserving write-before-read ordering. - assign remote_out_sel_xbar[j][t*NumRemotePortCore+r] = remote_xbar_sel_t'( - remote_out_sel_tile[t][j+r*NrTCDMPortsPerCore] * NumRemotePortCore - + remote_out_sel_tile[t][j+r*NrTCDMPortsPerCore] % NumRemotePortCore); - - // Response selection: route back to source tile's remote-out slot. - // The originator (tile_id in user field) sent on slot - // (target_tile % NumRemotePortCore). The responding tile is `t` - // (genvar), so target_tile = t. - assign remote_in_sel_xbar[j][t*NumRemotePortCore+r] = remote_xbar_sel_t'( - tile_remote_in_rsp_chan[j][t*NumRemotePortCore+r].user.tile_id * NumRemotePortCore - + t % NumRemotePortCore); + // xbar port index: t*NumLGPortCore + r + assign tile_remote_out_req_chan [j][t*NumLGPortCore+r] = tile_remote_out_req[t][j+r*NrTCDMPortsPerCore].q; + assign tile_remote_out_req_valid[j][t*NumLGPortCore+r] = tile_remote_out_req[t][j+r*NrTCDMPortsPerCore].q_valid; + assign tile_remote_out_rsp_ready[j][t*NumLGPortCore+r] = tile_remote_in_ready[t][j+r*NrTCDMPortsPerCore]; + + assign tile_remote_out_rsp[t][j+r*NrTCDMPortsPerCore].p = tile_remote_out_rsp_chan [j][t*NumLGPortCore+r]; + assign tile_remote_out_rsp[t][j+r*NrTCDMPortsPerCore].p_valid = tile_remote_out_rsp_valid[j][t*NumLGPortCore+r]; + assign tile_remote_out_rsp[t][j+r*NrTCDMPortsPerCore].q_ready = tile_remote_out_req_ready[j][t*NumLGPortCore+r]; + + assign tile_remote_in_req[t][j+r*NrTCDMPortsPerCore].q = tile_remote_in_req_chan [j][t*NumLGPortCore+r]; + assign tile_remote_in_req[t][j+r*NrTCDMPortsPerCore].q_valid = tile_remote_in_req_valid[j][t*NumLGPortCore+r]; + assign tile_remote_out_ready[t][j+r*NrTCDMPortsPerCore] = tile_remote_in_rsp_ready[j][t*NumLGPortCore+r]; + + assign tile_remote_in_rsp_chan [j][t*NumLGPortCore+r] = tile_remote_in_rsp[t][j+r*NrTCDMPortsPerCore].p; + assign tile_remote_in_rsp_valid[j][t*NumLGPortCore+r] = tile_remote_in_rsp[t][j+r*NrTCDMPortsPerCore].p_valid; + assign tile_remote_in_req_ready[j][t*NumLGPortCore+r] = tile_remote_in_rsp[t][j+r*NrTCDMPortsPerCore].q_ready; + + assign remote_out_sel_xbar[j][t*NumLGPortCore+r] = local_remote_xbar_sel_t'( + remote_out_sel_tile[t][j+r*NrTCDMPortsPerCore] * NumLGPortCore + + tile_remote_out_req_chan[j][t*NumLGPortCore+r].user.core_id % NumLGPortCore); + + assign remote_in_sel_xbar[j][t*NumLGPortCore+r] = local_remote_xbar_sel_t'( + tile_remote_in_rsp_chan[j][t*NumLGPortCore+r].user.tile_id * NumLGPortCore + + tile_remote_in_rsp_chan[j][t*NumLGPortCore+r].user.core_id % NumLGPortCore); end end end - for (genvar t = 0; t < NumTiles; t ++) begin : gen_tiles + for (genvar t = 0; t < NumTilesPerGroup; t ++) begin : gen_tiles logic [9:0] hart_base_id; assign hart_base_id = hart_base_id_i + t * NumCoresTile; logic [TileIDWidth-1:0] tile_id; - assign tile_id = t; - - cachepool_tile #( - .AxiAddrWidth ( AxiAddrWidth ), - .AxiDataWidth ( AxiDataWidth ), - .AxiIdWidthIn ( AxiIdWidthIn ), - .AxiIdWidthOut ( WideIdWidthIn ), - .AxiUserWidth ( AxiUserWidth ), - .BootAddr ( BootAddr ), - .UartAddr ( UartAddr ), - .ClusterPeriphSize ( ClusterPeriphSize ), - .NrCores ( NumCoresTile ), - .TCDMDepth ( TCDMDepth ), - .NrBanks ( NrBanks ), - .ICacheLineWidth ( ICacheLineWidth ), - .ICacheLineCount ( ICacheLineCount ), - .ICacheSets ( ICacheSets ), - .FPUImplementation ( FPUImplementation ), - .NumSpatzFPUs ( NumSpatzFPUs ), - .NumSpatzIPUs ( NumSpatzIPUs ), - .SnitchPMACfg ( SnitchPMACfg ), - .NumIntOutstandingLoads ( NumIntOutstandingLoads ), - .NumIntOutstandingMem ( NumIntOutstandingMem ), - .NumSpatzOutstandingLoads ( NumSpatzOutstandingLoads ), - .axi_in_req_t ( axi_in_req_t ), - .axi_in_resp_t ( axi_in_resp_t ), - .axi_narrow_req_t ( axi_narrow_req_t ), - .axi_narrow_resp_t ( axi_narrow_resp_t ), - .axi_out_req_t ( axi_mst_cache_req_t ), - .axi_out_resp_t ( axi_mst_cache_resp_t ), - .Xdma ( Xdma ), - .TileIDWidth ( TileIDWidth ), - .DMAAxiReqFifoDepth ( DMAAxiReqFifoDepth ), - .DMAReqFifoDepth ( DMAReqFifoDepth ), - .RegisterOffloadRsp ( RegisterOffloadRsp ), - .RegisterCoreReq ( RegisterCoreReq ), - .RegisterCoreRsp ( RegisterCoreRsp ), - .RegisterTCDMCuts ( RegisterTCDMCuts ), - .RegisterExt ( RegisterExt ), - .XbarLatency ( XbarLatency ), - .MaxMstTrans ( MaxMstTrans ), - .MaxSlvTrans ( MaxSlvTrans ) - ) i_tile ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .impl_i ( impl_i ), - .error_o ( error[t] ), - // TODO: remove hardcode - .debug_req_i ( debug_req_i [t*NumCoresTile+:NumCoresTile] ), - .meip_i ( meip_i [t*NumCoresTile+:NumCoresTile] ), - .mtip_i ( mtip_i [t*NumCoresTile+:NumCoresTile] ), - .msip_i ( msip_i [t*NumCoresTile+:NumCoresTile] ), - .hart_base_id_i ( hart_base_id ), - .cluster_base_addr_i ( cluster_base_addr_i ), - .tile_id_i ( tile_id ), - .private_start_addr_i ( private_start_addr_i ), - // AXI out for UART - .axi_out_req_o ( axi_narrow_req_o [t*TileNarrowAxiPorts+:TileNarrowAxiPorts]), - .axi_out_resp_i ( axi_narrow_rsp_i [t*TileNarrowAxiPorts+:TileNarrowAxiPorts]), - // Remote Access Ports - .remote_req_o ( tile_remote_out_req[t] ), - .remote_req_dst_o ( remote_out_sel_tile[t] ), - .remote_rsp_i ( tile_remote_out_rsp[t] ), - .remote_rsp_ready_i ( tile_remote_out_ready[t] ), - .remote_req_i ( tile_remote_in_req [t] ), - .remote_rsp_o ( tile_remote_in_rsp [t] ), - .remote_rsp_ready_o ( tile_remote_in_ready[t] ), - // Cache Refill Ports - .cache_refill_req_o ( cache_refill_req_o[t*NumL1CtrlTile+:NumL1CtrlTile] ), - .cache_refill_rsp_i ( cache_refill_rsp_i[t*NumL1CtrlTile+:NumL1CtrlTile] ), - // BootROM / Core-side Cache Bypass - .axi_wide_req_o ( axi_wide_req_o [t*TileWideAxiPorts+:TileWideAxiPorts] ), - .axi_wide_rsp_i ( axi_wide_rsp_i [t*TileWideAxiPorts+:TileWideAxiPorts] ), - // Peripherals - .icache_events_o ( /* unused */ ), - .icache_prefetch_enable_i ( icache_prefetch_enable_i ), - .cl_interrupt_i ( cl_interrupt_i [t*NumCoresTile+:NumCoresTile] ), - .dynamic_offset_i ( dynamic_offset_i ), - .l1d_insn_i ( l1d_insn_i ), - .l1d_private_i ( l1d_private_i ), - .l1d_insn_valid_i ( l1d_insn_valid_i ), - .l1d_insn_ready_o ( l1d_insn_ready_o [t] ), - .l1d_busy_i ( l1d_busy_i [t] ) - ); + assign tile_id = tile_base_id_i + TileIDWidth'(t); + + if (NumRemoteGroupPortCore == 0) begin : gen_tile + cachepool_tile #( + .AxiAddrWidth ( AxiAddrWidth ), + .AxiDataWidth ( AxiDataWidth ), + .AxiIdWidthIn ( AxiIdWidthIn ), + .AxiIdWidthOut ( WideIdWidthIn ), + .AxiUserWidth ( AxiUserWidth ), + .BootAddr ( BootAddr ), + .UartAddr ( UartAddr ), + .ClusterPeriphSize ( ClusterPeriphSize ), + .NrCores ( NumCoresTile ), + .TCDMDepth ( TCDMDepth ), + .NrBanks ( NrBanks ), + .ICacheLineWidth ( ICacheLineWidth ), + .ICacheLineCount ( ICacheLineCount ), + .ICacheSets ( ICacheSets ), + .FPUImplementation ( FPUImplementation ), + .NumSpatzFPUs ( NumSpatzFPUs ), + .NumSpatzIPUs ( NumSpatzIPUs ), + .SnitchPMACfg ( SnitchPMACfg ), + .NumIntOutstandingLoads ( NumIntOutstandingLoads ), + .NumIntOutstandingMem ( NumIntOutstandingMem ), + .NumSpatzOutstandingLoads ( NumSpatzOutstandingLoads ), + .axi_narrow_req_t ( axi_narrow_req_t ), + .axi_narrow_resp_t ( axi_narrow_resp_t ), + .axi_out_req_t ( axi_mst_cache_req_t ), + .axi_out_resp_t ( axi_mst_cache_resp_t ), + .TileIDWidth ( TileIDWidth ), + .NumRemoteGroupPortCore ( NumRemoteGroupPortCore ), + .NumTilesPerGroup ( NumTilesPerGroup ), + .RegisterOffloadRsp ( RegisterOffloadRsp ), + .RegisterCoreReq ( RegisterCoreReq ), + .RegisterCoreRsp ( RegisterCoreRsp ), + .RegisterTCDMCuts ( RegisterTCDMCuts ), + .RegisterExt ( RegisterExt ), + .XbarLatency ( XbarLatency ), + .MaxMstTrans ( MaxMstTrans ), + .MaxSlvTrans ( MaxSlvTrans ) + ) i_tile ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .impl_i ( impl_i ), + .error_o ( error[t] ), + .debug_req_i ( debug_req_i ), + .meip_i ( meip_i ), + .mtip_i ( mtip_i ), + .msip_i ( msip_i ), + .hart_base_id_i ( hart_base_id ), + .cluster_base_addr_i ( cluster_base_addr_i ), + .tile_id_i ( tile_id ), + .private_start_addr_i ( private_start_addr_i ), + // Remote Access Ports + .remote_req_o ( tile_remote_out_req [t] ), + .remote_req_dst_o ( remote_out_sel_tile [t] ), + .remote_rsp_i ( tile_remote_out_rsp [t] ), + .remote_rsp_ready_i ( tile_remote_out_ready[t] ), + .remote_req_i ( tile_remote_in_req [t] ), + .remote_rsp_o ( tile_remote_in_rsp [t] ), + .remote_rsp_ready_o ( tile_remote_in_ready [t] ), + // Inter-group Remote Access Ports (directly exposed to group I/O) + .remote_group_req_o ( ), + .remote_group_rsp_i ( '0 ), + .remote_group_req_i ( '0 ), + .remote_group_rsp_o ( ), + // Cache Refill Ports (now internal, connected to group-level xbar) + .cache_refill_req_o ( cache_refill_req[t*NumL1CtrlTile+:NumL1CtrlTile] ), + .cache_refill_rsp_i ( cache_refill_rsp[t*NumL1CtrlTile+:NumL1CtrlTile] ), + // Peripheral REQRSP (narrow, bypasses wide xbar) + .periph_req_o ( tile_periph_req [t] ), + .periph_rsp_i ( tile_periph_rsp [t] ), + // iCache L2 (single wide AXI port, BootROM at cluster level) + .axi_wide_req_o ( axi_tile_mem_req[t] ), + .axi_wide_rsp_i ( axi_tile_mem_rsp[t] ), + // Direct-wire barrier + .barrier_o ( tile_barrier_o [t] ), + .barrier_done_i ( barrier_done_i ), + // Peripherals + .icache_events_o ( /* unused */ ), + .icache_prefetch_enable_i ( icache_prefetch_enable_i ), + .cl_interrupt_i ( cl_interrupt_i [t*NumCoresTile+:NumCoresTile] ), + .dynamic_offset_i ( dynamic_offset_i ), + .l1d_insn_i ( l1d_insn_i ), + .l1d_private_i ( l1d_private_i ), + .l1d_insn_valid_i ( l1d_insn_valid_i ), + .l1d_insn_ready_o ( l1d_insn_ready_o[t] ), + .l1d_busy_i ( l1d_busy_i [t] ) + ); + end else begin : gen_tile + cachepool_tile #( + .AxiAddrWidth ( AxiAddrWidth ), + .AxiDataWidth ( AxiDataWidth ), + .AxiIdWidthIn ( AxiIdWidthIn ), + .AxiIdWidthOut ( WideIdWidthIn ), + .AxiUserWidth ( AxiUserWidth ), + .BootAddr ( BootAddr ), + .UartAddr ( UartAddr ), + .ClusterPeriphSize ( ClusterPeriphSize ), + .NrCores ( NumCoresTile ), + .TCDMDepth ( TCDMDepth ), + .NrBanks ( NrBanks ), + .ICacheLineWidth ( ICacheLineWidth ), + .ICacheLineCount ( ICacheLineCount ), + .ICacheSets ( ICacheSets ), + .FPUImplementation ( FPUImplementation ), + .NumSpatzFPUs ( NumSpatzFPUs ), + .NumSpatzIPUs ( NumSpatzIPUs ), + .SnitchPMACfg ( SnitchPMACfg ), + .NumIntOutstandingLoads ( NumIntOutstandingLoads ), + .NumIntOutstandingMem ( NumIntOutstandingMem ), + .NumSpatzOutstandingLoads ( NumSpatzOutstandingLoads ), + .axi_narrow_req_t ( axi_narrow_req_t ), + .axi_narrow_resp_t ( axi_narrow_resp_t ), + .axi_out_req_t ( axi_mst_cache_req_t ), + .axi_out_resp_t ( axi_mst_cache_resp_t ), + .TileIDWidth ( TileIDWidth ), + .NumRemoteGroupPortCore ( NumRemoteGroupPortCore ), + .NumTilesPerGroup ( NumTilesPerGroup ), + .RegisterOffloadRsp ( RegisterOffloadRsp ), + .RegisterCoreReq ( RegisterCoreReq ), + .RegisterCoreRsp ( RegisterCoreRsp ), + .RegisterTCDMCuts ( RegisterTCDMCuts ), + .RegisterExt ( RegisterExt ), + .XbarLatency ( XbarLatency ), + .MaxMstTrans ( MaxMstTrans ), + .MaxSlvTrans ( MaxSlvTrans ) + ) i_tile ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .impl_i ( impl_i ), + .error_o ( error[t] ), + .debug_req_i ( debug_req_i ), + .meip_i ( meip_i ), + .mtip_i ( mtip_i ), + .msip_i ( msip_i ), + .hart_base_id_i ( hart_base_id ), + .cluster_base_addr_i ( cluster_base_addr_i ), + .tile_id_i ( tile_id ), + .private_start_addr_i ( private_start_addr_i ), + // Remote Access Ports + .remote_req_o ( tile_remote_out_req [t] ), + .remote_req_dst_o ( remote_out_sel_tile [t] ), + .remote_rsp_i ( tile_remote_out_rsp [t] ), + .remote_rsp_ready_i ( tile_remote_out_ready[t] ), + .remote_req_i ( tile_remote_in_req [t] ), + .remote_rsp_o ( tile_remote_in_rsp [t] ), + .remote_rsp_ready_o ( tile_remote_in_ready [t] ), + // Inter-group Remote Access Ports (directly exposed to group I/O) + .remote_group_req_o ( remote_group_req_o [t*NumRemoteGroupPortTile+:NumRemoteGroupPortTile]), + .remote_group_rsp_i ( remote_group_rsp_i [t*NumRemoteGroupPortTile+:NumRemoteGroupPortTile]), + .remote_group_req_i ( remote_group_req_i [t*NumRemoteGroupPortTile+:NumRemoteGroupPortTile]), + .remote_group_rsp_o ( remote_group_rsp_o [t*NumRemoteGroupPortTile+:NumRemoteGroupPortTile]), + // Cache Refill Ports (now internal, connected to group-level xbar) + .cache_refill_req_o ( cache_refill_req[t*NumL1CtrlTile+:NumL1CtrlTile] ), + .cache_refill_rsp_i ( cache_refill_rsp[t*NumL1CtrlTile+:NumL1CtrlTile] ), + // Peripheral REQRSP (narrow, bypasses wide xbar) + .periph_req_o ( tile_periph_req [t] ), + .periph_rsp_i ( tile_periph_rsp [t] ), + // iCache L2 (single wide AXI port, BootROM at cluster level) + .axi_wide_req_o ( axi_tile_mem_req[t] ), + .axi_wide_rsp_i ( axi_tile_mem_rsp[t] ), + // Direct-wire barrier + .barrier_o ( tile_barrier_o [t] ), + .barrier_done_i ( barrier_done_i ), + // Peripherals + .icache_events_o ( /* unused */ ), + .icache_prefetch_enable_i ( icache_prefetch_enable_i ), + .cl_interrupt_i ( cl_interrupt_i [t*NumCoresTile+:NumCoresTile] ), + .dynamic_offset_i ( dynamic_offset_i ), + .l1d_insn_i ( l1d_insn_i ), + .l1d_private_i ( l1d_private_i ), + .l1d_insn_valid_i ( l1d_insn_valid_i ), + .l1d_insn_ready_o ( l1d_insn_ready_o [t] ), + .l1d_busy_i ( l1d_busy_i [t] ) + ); + end end // ------------ @@ -379,11 +717,10 @@ module cachepool_group // ------------ for (genvar p = 0; p < NrTCDMPortsPerCore; p++) begin : gen_remote_tile_xbar - // Decide which tile to go reqrsp_xbar #( - .NumInp (NumTiles * NumRemotePortCore ), - .NumOut (NumTiles * NumRemotePortCore ), + .NumInp (NumTilesPerGroup * NumLGPortCore ), + .NumOut (NumTilesPerGroup * NumLGPortCore ), .PipeReg (1'b1 ), .RspReg (1'b1 ), .ExtReqPrio (1'b0 ), diff --git a/hardware/src/cachepool_group_noc_wrapper.sv b/hardware/src/cachepool_group_noc_wrapper.sv new file mode 100644 index 0000000..e41464d --- /dev/null +++ b/hardware/src/cachepool_group_noc_wrapper.sv @@ -0,0 +1,794 @@ +// Copyright 2025 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +// Description: Wrapper around cachepool_group that handles inter-group +// interconnection: master-side concentration xbar, flit packing, floo_router +// instances (req + rsp), and a slave-side dispatch xbar. +// +// Author: Diyou Shen + + +`include "common_cells/registers.svh" +`include "axi/typedef.svh" + +module cachepool_group_noc_wrapper + import cachepool_pkg::*; + import floo_pkg::*; + import spatz_pkg::*; + import fpnew_pkg::fpu_implementation_t; + import snitch_pma_pkg::snitch_pma_t; + import snitch_icache_pkg::icache_l1_events_t; + #( + parameter int unsigned AxiAddrWidth = 48, + parameter int unsigned AxiDataWidth = 512, + parameter int unsigned AxiIdWidthIn = 2, + parameter int unsigned AxiIdWidthOut = 2, + parameter int unsigned AxiUserWidth = 1, + parameter logic [31:0] BootAddr = 32'h0, + parameter logic [31:0] UartAddr = 32'h0, + parameter int unsigned NrCores = 0, + parameter int unsigned TCDMDepth = 1024, + parameter int unsigned ClusterPeriphSize = 64, + parameter int unsigned NrBanks = 2 * NrCores, + parameter int unsigned ICacheLineWidth = 0, + parameter int unsigned ICacheLineCount = 0, + parameter int unsigned ICacheSets = 0, + parameter fpu_implementation_t FPUImplementation = fpu_implementation_t'(0), + parameter int unsigned NumSpatzFPUs = 1, + parameter int unsigned NumSpatzIPUs = 1, + parameter snitch_pma_t SnitchPMACfg = '0, + parameter int unsigned NumIntOutstandingLoads = 1, + parameter int unsigned NumIntOutstandingMem = 4, + parameter int unsigned NumSpatzOutstandingLoads = 4, + parameter bit RegisterOffloadRsp = 1, + parameter bit RegisterCoreReq = 0, + parameter bit RegisterCoreRsp = 0, + parameter bit RegisterTCDMCuts = 1'b0, + parameter bit RegisterExt = 1'b0, + parameter axi_pkg::xbar_latency_e XbarLatency = axi_pkg::CUT_ALL_PORTS, + parameter int unsigned MaxMstTrans = 4, + parameter int unsigned MaxSlvTrans = 4, + parameter type axi_in_req_t = logic, + parameter type axi_in_resp_t = logic, + parameter type axi_out_req_t = logic, + parameter type axi_out_resp_t = logic, + parameter type impl_in_t = logic, + parameter int unsigned MemoryMacroLatency = 1 + RegisterTCDMCuts, + parameter int unsigned NrSramCfg = 1 + ) ( + input logic clk_i, + input logic rst_ni, + input logic debug_req_i, + input logic meip_i, + input logic mtip_i, + input logic msip_i, + input logic [9:0] hart_base_id_i, + input logic [TileIDWidth-1:0] tile_base_id_i, + input axi_addr_t cluster_base_addr_i, + input axi_addr_t private_start_addr_i, + // L2 refill TCDM mesh: 4 directions (North=0, East=1, South=2, West=3) + output l2_noc_req_t [3:0] l2_req_o, + output logic [3:0] l2_req_valid_o, + input logic [3:0] l2_req_ready_i, + input l2_noc_req_t [3:0] l2_req_i, + input logic [3:0] l2_req_valid_i, + output logic [3:0] l2_req_ready_o, + output l2_noc_rsp_t [3:0] l2_rsp_o, + output logic [3:0] l2_rsp_valid_o, + input logic [3:0] l2_rsp_ready_i, + input l2_noc_rsp_t [3:0] l2_rsp_i, + input logic [3:0] l2_rsp_valid_i, + output logic [3:0] l2_rsp_ready_o, + // L2 mesh: endpoint ID and source-routing table from floogen + input floo_cachepool_noc_pkg::id_t l2_id_i, + input floo_cachepool_noc_pkg::route_t [floo_cachepool_noc_pkg::RouteCfg.NumRoutes-1:0] l2_route_table_i, + output icache_l1_events_t [NrCores-1:0] icache_events_o, + input logic icache_prefetch_enable_i, + input logic [NrCores-1:0] cl_interrupt_i, + input logic [$clog2(AxiAddrWidth)-1:0] dynamic_offset_i, + input logic [$clog2(NumL1CtrlTile):0] l1d_private_i, + input cache_insn_t l1d_insn_i, + input logic l1d_insn_valid_i, + output logic [NumTilesPerGroup-1:0] l1d_insn_ready_o, + input logic [NumTilesPerGroup-1:0] l1d_busy_i, + input impl_in_t [NrSramCfg-1:0] impl_i, + output logic error_o, + // XY coordinates of this group in the inter-group mesh + input group_xy_id_t group_xy_id_i, + // Inter-group req mesh: 4 directions (N=0,E=1,S=2,W=3) + // dim1: direction, dim2: tile*NumNoCPortsPerTile+channel + output noc_group_req_t [3:0][NumTilesPerGroup*NumNoCPortsPerTile-1:0] noc_req_o, + output logic [3:0][NumTilesPerGroup*NumNoCPortsPerTile-1:0] noc_req_valid_o, + input logic [3:0][NumTilesPerGroup*NumNoCPortsPerTile-1:0] noc_req_ready_i, + input noc_group_req_t [3:0][NumTilesPerGroup*NumNoCPortsPerTile-1:0] noc_req_i, + input logic [3:0][NumTilesPerGroup*NumNoCPortsPerTile-1:0] noc_req_valid_i, + output logic [3:0][NumTilesPerGroup*NumNoCPortsPerTile-1:0] noc_req_ready_o, + // Inter-group rsp mesh + output noc_group_rsp_t [3:0][NumTilesPerGroup*NumNoCPortsPerTile-1:0] noc_rsp_o, + output logic [3:0][NumTilesPerGroup*NumNoCPortsPerTile-1:0] noc_rsp_valid_o, + input logic [3:0][NumTilesPerGroup*NumNoCPortsPerTile-1:0] noc_rsp_ready_i, + input noc_group_rsp_t [3:0][NumTilesPerGroup*NumNoCPortsPerTile-1:0] noc_rsp_i, + input logic [3:0][NumTilesPerGroup*NumNoCPortsPerTile-1:0] noc_rsp_valid_i, + output logic [3:0][NumTilesPerGroup*NumNoCPortsPerTile-1:0] noc_rsp_ready_o, + // Direct-wire barrier: one bit per tile in this group + output logic [NumTilesPerGroup-1:0] tile_barrier_o, + input logic barrier_done_i + ); + + + // ------------------------------------------------------------------------- + // Localparams + // ------------------------------------------------------------------------- + localparam int unsigned NumRemoteGroupPortTile = (NumRemoteGroupPortCore == 0) ? 1 + : NumRemoteGroupPortCore * NrTCDMPortsPerCore; + localparam int unsigned NumRemoteGroupPortGroup = NumRemoteGroupPortTile * NumTilesPerGroup; + localparam int unsigned NumNoCPortsGroup = NumNoCPortsPerTile * NumTilesPerGroup; + localparam int unsigned SlvXbarSelW = (NumRemoteGroupPortGroup > 1) ? $clog2(NumRemoteGroupPortGroup) : 1; + localparam int unsigned MstXbarSelW = (NumNoCPortsGroup > 1) ? $clog2(NumNoCPortsGroup) : 1; + + // -- Struct / xbar field widths (always >= 1 to avoid zero-width ports) ------ + localparam int unsigned NocCacheBankBits = $clog2(NrBanks); + localparam int unsigned NocAddrTileWidth = (NumTilesPerGroup > 1) ? $clog2(NumTilesPerGroup) : 1; + // -- Actual bit counts inside dst_tile_id (can be 0 when that dimension = 1) - + // Flat group_id = gy * NumGroupsX + gx (row-major, set in cachepool_cluster.sv). + // dst_tile_id layout: [ group_y (NocGroupBitsY) | group_x (NocGroupBitsX) | local_tile (NocGroupOffset) ] + // Lower group bits are X, upper group bits are Y. + localparam int unsigned NocGroupOffset = $clog2(NumTilesPerGroup); + localparam int unsigned NocGroupBitsX = (NumGroupsX > 1) ? $clog2(NumGroupsX) : 0; + localparam int unsigned NocGroupBitsY = (NumGroupsY > 1) ? $clog2(NumGroupsY) : 0; + + + // ------------------------------------------------------------------------- + // Group ↔ wrapper boundary signals + // ------------------------------------------------------------------------- + remote_group_req_t [NumRemoteGroupPortGroup-1:0] remote_group_req_to_group; + remote_group_rsp_t [NumRemoteGroupPortGroup-1:0] remote_group_rsp_from_group; + remote_group_req_t [NumRemoteGroupPortGroup-1:0] remote_group_req_from_group; + remote_group_rsp_t [NumRemoteGroupPortGroup-1:0] remote_group_rsp_to_group; + + + // Input cuts on some registered signals + logic [$clog2(AxiAddrWidth)-1:0] dynamic_offset_d, dynamic_offset_q; + logic barrier_done_d, barrier_done_q; + logic [NumTilesPerGroup-1:0] tile_barrier_d, tile_barrier_q; + logic [$clog2(NumL1CtrlTile):0] l1d_private_d, l1d_private_q; + cache_insn_t l1d_insn_d, l1d_insn_q; + logic l1d_insn_valid_d, l1d_insn_valid_q; + logic [NumTilesPerGroup-1:0] l1d_insn_ready_d, l1d_insn_ready_q; + logic [NumTilesPerGroup-1:0] l1d_busy_d, l1d_busy_q; + axi_addr_t private_start_addr_d, private_start_addr_q; + + `FF(dynamic_offset_q, dynamic_offset_d, '0, clk_i, rst_ni) + `FF(barrier_done_q, barrier_done_d, '0, clk_i, rst_ni) + `FF(tile_barrier_q, tile_barrier_d, '0, clk_i, rst_ni) + `FF(l1d_private_q, l1d_private_d, '0, clk_i, rst_ni) + `FF(l1d_insn_q, l1d_insn_d, '0, clk_i, rst_ni) + `FF(l1d_insn_valid_q, l1d_insn_valid_d, '0, clk_i, rst_ni) + `FF(l1d_insn_ready_q, l1d_insn_ready_d, '0, clk_i, rst_ni) + `FF(l1d_busy_q, l1d_busy_d, '0, clk_i, rst_ni) + `FF(private_start_addr_q, private_start_addr_d, '0, clk_i, rst_ni) + + assign dynamic_offset_d = dynamic_offset_i; + assign barrier_done_d = barrier_done_i; + assign tile_barrier_o = tile_barrier_q; + assign l1d_private_d = l1d_private_i; + assign l1d_insn_d = l1d_insn_i; + assign l1d_insn_valid_d = l1d_insn_valid_i; + assign l1d_insn_ready_o = l1d_insn_ready_q; + assign l1d_busy_d = l1d_busy_i; + assign private_start_addr_d = private_start_addr_i; + + // ------------------------------------------------------------------------- + // Mesh signals [tile][ch][dir=3:0] and transposition to/from ports + // ------------------------------------------------------------------------- + noc_group_req_t [NumTilesPerGroup-1:0][NumNoCPortsPerTile-1:0][3:0] req_mesh_out; + logic [NumTilesPerGroup-1:0][NumNoCPortsPerTile-1:0][3:0] req_mesh_out_valid; + logic [NumTilesPerGroup-1:0][NumNoCPortsPerTile-1:0][3:0] req_mesh_out_ready; + noc_group_req_t [NumTilesPerGroup-1:0][NumNoCPortsPerTile-1:0][3:0] req_mesh_in; + logic [NumTilesPerGroup-1:0][NumNoCPortsPerTile-1:0][3:0] req_mesh_in_valid; + logic [NumTilesPerGroup-1:0][NumNoCPortsPerTile-1:0][3:0] req_mesh_in_ready; + + noc_group_rsp_t [NumTilesPerGroup-1:0][NumNoCPortsPerTile-1:0][3:0] rsp_mesh_out; + logic [NumTilesPerGroup-1:0][NumNoCPortsPerTile-1:0][3:0] rsp_mesh_out_valid; + logic [NumTilesPerGroup-1:0][NumNoCPortsPerTile-1:0][3:0] rsp_mesh_out_ready; + noc_group_rsp_t [NumTilesPerGroup-1:0][NumNoCPortsPerTile-1:0][3:0] rsp_mesh_in; + logic [NumTilesPerGroup-1:0][NumNoCPortsPerTile-1:0][3:0] rsp_mesh_in_valid; + logic [NumTilesPerGroup-1:0][NumNoCPortsPerTile-1:0][3:0] rsp_mesh_in_ready; + + for (genvar t = 0; t < NumTilesPerGroup; t++) begin : gen_mesh_trans_t + for (genvar n = 0; n < NumNoCPortsPerTile; n++) begin : gen_mesh_trans_n + for (genvar d = 0; d < 4; d++) begin : gen_mesh_trans_d + // Mute the channel when not valid for debugging + assign noc_req_o[d][t*NumNoCPortsPerTile+n] = req_mesh_out_valid[t][n][d] ? req_mesh_out[t][n][d] : '0; + assign noc_req_valid_o[d][t*NumNoCPortsPerTile+n] = req_mesh_out_valid[t][n][d]; + assign req_mesh_out_ready[t][n][d] = noc_req_ready_i[d][t*NumNoCPortsPerTile+n]; + assign req_mesh_in[t][n][d] = noc_req_i[d][t*NumNoCPortsPerTile+n]; + assign req_mesh_in_valid[t][n][d] = noc_req_valid_i[d][t*NumNoCPortsPerTile+n]; + assign noc_req_ready_o[d][t*NumNoCPortsPerTile+n] = req_mesh_in_ready[t][n][d]; + + assign noc_rsp_o[d][t*NumNoCPortsPerTile+n] = rsp_mesh_out_valid[t][n][d] ? rsp_mesh_out[t][n][d] : '0; + assign noc_rsp_valid_o[d][t*NumNoCPortsPerTile+n] = rsp_mesh_out_valid[t][n][d]; + assign rsp_mesh_out_ready[t][n][d] = noc_rsp_ready_i[d][t*NumNoCPortsPerTile+n]; + assign rsp_mesh_in[t][n][d] = noc_rsp_i[d][t*NumNoCPortsPerTile+n]; + assign rsp_mesh_in_valid[t][n][d] = noc_rsp_valid_i[d][t*NumNoCPortsPerTile+n]; + assign noc_rsp_ready_o[d][t*NumNoCPortsPerTile+n] = rsp_mesh_in_ready[t][n][d]; + end + end + end + + + if (NumRemoteGroupPortCore > 0) begin : gen_noc + + // ----------------------------------------------------------------------- + // Router inject/eject signals (flat 1D index noc_port = t*NumNoCPortsPerTile+n) + // ----------------------------------------------------------------------- + noc_group_req_t [NumNoCPortsGroup-1:0] packed_req; + logic [NumNoCPortsGroup-1:0] packed_req_valid; + logic [NumNoCPortsGroup-1:0] packed_req_ready; + + noc_group_req_t [NumNoCPortsGroup-1:0] eject_req; + logic [NumNoCPortsGroup-1:0] eject_req_valid; + logic [NumNoCPortsGroup-1:0] eject_req_ready; + + noc_group_rsp_t [NumNoCPortsGroup-1:0] inject_rsp; + logic [NumNoCPortsGroup-1:0] inject_rsp_valid; + logic [NumNoCPortsGroup-1:0] inject_rsp_ready; + + noc_group_rsp_t [NumNoCPortsGroup-1:0] eject_rsp; + logic [NumNoCPortsGroup-1:0] eject_rsp_valid; + logic [NumNoCPortsGroup-1:0] eject_rsp_ready; + + // Master xbar output (one concentrated req/rsp channel per tile/channel) + remote_group_req_chan_t [NumNoCPortsGroup-1:0] mst_xbar_req; + logic [NumNoCPortsGroup-1:0] mst_xbar_req_valid; + logic [NumNoCPortsGroup-1:0] mst_xbar_req_ready; + + // Slave xbar signals + noc_group_req_t [NumRemoteGroupPortGroup-1:0] slv_xbar_mst_req; + logic [NumRemoteGroupPortGroup-1:0] slv_xbar_mst_req_valid; + logic [NumRemoteGroupPortGroup-1:0] slv_xbar_mst_req_ready; + noc_group_rsp_t [NumRemoteGroupPortGroup-1:0] slv_xbar_mst_rsp; + logic [NumRemoteGroupPortGroup-1:0] slv_xbar_mst_rsp_valid; + logic [NumRemoteGroupPortGroup-1:0] slv_xbar_mst_rsp_ready; + noc_group_rsp_t [NumNoCPortsGroup-1:0] slv_xbar_slv_rsp; + logic [NumNoCPortsGroup-1:0] slv_xbar_slv_rsp_valid; + logic [NumNoCPortsGroup-1:0] slv_xbar_slv_rsp_ready; + + logic [NumNoCPortsGroup-1:0][SlvXbarSelW-1:0] slv_xbar_slv_sel; + logic [NumRemoteGroupPortGroup-1:0][MstXbarSelW-1:0] slv_xbar_mst_sel; + + + // ----------------------------------------------------------------------- + // Master-side per-tile concentration xbar + flit packing + // ----------------------------------------------------------------------- + for (genvar t = 0; t < NumTilesPerGroup; t++) begin : gen_mst_t + + remote_group_req_chan_t [NumRemoteGroupPortTile-1:0] mst_slv_req; + logic [NumRemoteGroupPortTile-1:0] mst_slv_req_valid; + logic [NumRemoteGroupPortTile-1:0] mst_slv_req_ready; + remote_group_rsp_chan_t [NumRemoteGroupPortTile-1:0] mst_slv_rsp; + logic [NumRemoteGroupPortTile-1:0] mst_slv_rsp_valid; + logic [NumRemoteGroupPortTile-1:0] mst_slv_rsp_ready; + remote_group_rsp_chan_t [NumNoCPortsPerTile-1:0] eject_rsp_payload; + portid_t [NumNoCPortsPerTile-1:0] mst_xbar_mst_sel; + portid_t [NumNoCPortsPerTile-1:0] mst_xbar_slv_selected; + + for (genvar p = 0; p < NumRemoteGroupPortTile; p++) begin : gen_mst_port_p + assign mst_slv_req[p] = remote_group_req_from_group[t*NumRemoteGroupPortTile+p].q; + assign mst_slv_req_valid[p] = remote_group_req_from_group[t*NumRemoteGroupPortTile+p].q_valid; + assign remote_group_rsp_to_group[t*NumRemoteGroupPortTile+p].q_ready = mst_slv_req_ready[p]; + assign remote_group_rsp_to_group[t*NumRemoteGroupPortTile+p].p = mst_slv_rsp[p]; + assign remote_group_rsp_to_group[t*NumRemoteGroupPortTile+p].p_valid = mst_slv_rsp_valid[p]; + assign mst_slv_rsp_ready[p] = remote_group_req_from_group[t*NumRemoteGroupPortTile+p].p_ready; + end + + for (genvar n = 0; n < NumNoCPortsPerTile; n++) begin : gen_mst_eject_n + localparam int unsigned noc_port = t * NumNoCPortsPerTile + n; + assign eject_rsp_payload[n] = eject_rsp[noc_port].payload; + assign mst_xbar_mst_sel[n] = eject_rsp[noc_port].hdr.src_port_id; + end + + // Static port-to-NoC-channel mapping: each flat port p has xbar index + // j = p % NrTCDMPortsPerCore, and is steered to NoC channel j % NumNoCPortsPerTile. + // Spatz ports (j=0..NrTCDMPortsPerCore-2) divide evenly across channels; + // Snitch (j=NrTCDMPortsPerCore-1) maps by the same modulo. + localparam int unsigned NocMstSelWidth = (NumNoCPortsPerTile > 1) + ? $clog2(NumNoCPortsPerTile) : 1; + logic [NumRemoteGroupPortTile-1:0][NocMstSelWidth-1:0] noc_mst_sel; + for (genvar p = 0; p < NumRemoteGroupPortTile; p++) begin : gen_noc_mst_sel + assign noc_mst_sel[p] = NocMstSelWidth'((p % NrTCDMPortsPerCore) % NumNoCPortsPerTile); + end + + reqrsp_xbar #( + .NumInp ( NumRemoteGroupPortTile ), + .NumOut ( NumNoCPortsPerTile ), + .tcdm_req_chan_t ( remote_group_req_chan_t ), + .tcdm_rsp_chan_t ( remote_group_rsp_chan_t ) + ) i_noc_mst_xbar ( + .clk_i, + .rst_ni, + .slv_req_i ( mst_slv_req ), + .slv_rr_i ( '0 ), + .slv_req_valid_i ( mst_slv_req_valid ), + .slv_req_ready_o ( mst_slv_req_ready ), + .slv_rsp_o ( mst_slv_rsp ), + .slv_rsp_valid_o ( mst_slv_rsp_valid ), + .slv_rsp_ready_i ( mst_slv_rsp_ready ), + .slv_sel_i ( noc_mst_sel ), + .slv_selected_o ( mst_xbar_slv_selected ), + .mst_req_o ( mst_xbar_req [t*NumNoCPortsPerTile+:NumNoCPortsPerTile] ), + .mst_req_valid_o ( mst_xbar_req_valid[t*NumNoCPortsPerTile+:NumNoCPortsPerTile] ), + .mst_req_ready_i ( mst_xbar_req_ready[t*NumNoCPortsPerTile+:NumNoCPortsPerTile] ), + .mst_rsp_i ( eject_rsp_payload ), + .mst_rr_i ( '0 ), + .mst_rsp_valid_i ( eject_rsp_valid [t*NumNoCPortsPerTile+:NumNoCPortsPerTile] ), + .mst_rsp_ready_o ( eject_rsp_ready [t*NumNoCPortsPerTile+:NumNoCPortsPerTile] ), + .mst_sel_i ( mst_xbar_mst_sel ) + ); + + for (genvar n = 0; n < NumNoCPortsPerTile; n++) begin : gen_pack_n + localparam int unsigned noc_port = t * NumNoCPortsPerTile + n; + assign packed_req[noc_port].hdr.collective_op = '0; + assign packed_req[noc_port].hdr.src_id = group_xy_id_i; + // Flat group_id = gy * NumGroupsX + gx (row-major), so in the + // dst_tile_id bit field the lower group bits are X and the upper are Y: + // dst_tile_id = [ gy (NocGroupBitsY) | gx (NocGroupBitsX) | local_tile ] + if (NumGroupsX > 1) begin : gen_dst_x + assign packed_req[noc_port].hdr.dst_id.x = + mst_xbar_req[noc_port].user.dst_tile_id[NocGroupOffset +: NocGroupBitsX]; + end else begin : gen_dst_x + assign packed_req[noc_port].hdr.dst_id.x = '0; + end + if (NumGroupsY > 1) begin : gen_dst_y + assign packed_req[noc_port].hdr.dst_id.y = + mst_xbar_req[noc_port].user.dst_tile_id[(NocGroupOffset + NocGroupBitsX) +: NocGroupBitsY]; + end else begin : gen_dst_y + assign packed_req[noc_port].hdr.dst_id.y = '0; + end + assign packed_req[noc_port].hdr.dst_id.port_id = '0; + assign packed_req[noc_port].hdr.src_tile_id = group_tile_sel_t'(t); + assign packed_req[noc_port].hdr.src_port_id = mst_xbar_slv_selected[n]; + assign packed_req[noc_port].hdr.last = 1'b1; + assign packed_req[noc_port].payload = mst_xbar_req[noc_port]; + assign packed_req_valid[noc_port] = mst_xbar_req_valid[noc_port]; + assign mst_xbar_req_ready[noc_port] = packed_req_ready[noc_port]; + + end + + end : gen_mst_t + + + // ----------------------------------------------------------------------- + // Per-tile per-channel req floo_router + // ----------------------------------------------------------------------- + for (genvar t = 0; t < NumTilesPerGroup; t++) begin : gen_req_router_t + for (genvar n = 0; n < NumNoCPortsPerTile; n++) begin : gen_req_router_n + localparam int unsigned noc_port = t * NumNoCPortsPerTile + n; + floo_router #( + .NumRoutes ( 5 ), + .NumVirtChannels ( 1 ), + .InFifoDepth ( 2 ), + .OutFifoDepth ( 2 ), + .RouteAlgo ( XYRouting ), + .IdWidth ( $bits(group_xy_id_t) ), + .id_t ( group_xy_id_t ), + .NumAddrRules ( 1 ), + .addr_rule_t ( logic ), + .flit_t ( noc_group_req_t ), + .hdr_t ( noc_group_hdr_t ) + ) i_req_router ( + .clk_i, + .rst_ni, + .test_enable_i ( 1'b0 ), + .xy_id_i ( group_xy_id_i ), + .id_route_map_i ( '0 ), + .valid_i ( {packed_req_valid[noc_port], + req_mesh_in_valid[t][n][3:0]} ), + .ready_o ( {packed_req_ready[noc_port], + req_mesh_in_ready[t][n][3:0]} ), + .data_i ( {packed_req[noc_port], + req_mesh_in[t][n][3:0]} ), + .credit_o ( ), + .valid_o ( {eject_req_valid[noc_port], + req_mesh_out_valid[t][n][3:0]} ), + .ready_i ( {eject_req_ready[noc_port], + req_mesh_out_ready[t][n][3:0]} ), + .data_o ( {eject_req[noc_port], + req_mesh_out[t][n][3:0]} ), + .credit_i ( '1 ), + .offload_req_o ( ), + .offload_rsp_i ( '0 ) + ); + end + end + + + // ----------------------------------------------------------------------- + // Per-tile per-channel rsp floo_router + // ----------------------------------------------------------------------- + for (genvar t = 0; t < NumTilesPerGroup; t++) begin : gen_rsp_router_t + for (genvar n = 0; n < NumNoCPortsPerTile; n++) begin : gen_rsp_router_n + localparam int unsigned noc_port = t * NumNoCPortsPerTile + n; + floo_router #( + .NumRoutes ( 5 ), + .NumVirtChannels ( 1 ), + .InFifoDepth ( 2 ), + .OutFifoDepth ( 2 ), + .RouteAlgo ( XYRouting ), + .IdWidth ( $bits(group_xy_id_t) ), + .id_t ( group_xy_id_t ), + .NumAddrRules ( 1 ), + .addr_rule_t ( logic ), + .flit_t ( noc_group_rsp_t ), + .hdr_t ( noc_group_hdr_t ) + ) i_rsp_router ( + .clk_i, + .rst_ni, + .test_enable_i ( 1'b0 ), + .xy_id_i ( group_xy_id_i ), + .id_route_map_i ( '0 ), + .valid_i ( {inject_rsp_valid[noc_port], + rsp_mesh_in_valid[t][n][3:0]} ), + .ready_o ( {inject_rsp_ready[noc_port], + rsp_mesh_in_ready[t][n][3:0]} ), + .data_i ( {inject_rsp[noc_port], + rsp_mesh_in[t][n][3:0]} ), + .credit_o ( ), + .valid_o ( {eject_rsp_valid[noc_port], + rsp_mesh_out_valid[t][n][3:0]} ), + .ready_i ( {eject_rsp_ready[noc_port], + rsp_mesh_out_ready[t][n][3:0]} ), + .data_o ( {eject_rsp[noc_port], + rsp_mesh_out[t][n][3:0]} ), + .credit_i ( '1 ), + .offload_req_o ( ), + .offload_rsp_i ( '0 ) + ); + end + end + + + // ----------------------------------------------------------------------- + // Slave xbar selection signals + inject_rsp ↔ slv_xbar_slv_rsp + // ----------------------------------------------------------------------- + for (genvar noc_port = 0; noc_port < NumNoCPortsGroup; noc_port++) begin : gen_slv_sel + assign slv_xbar_slv_sel[noc_port] = (NumTilesPerGroup == 1) + ? SlvXbarSelW'(eject_req[noc_port].hdr.src_port_id) + : SlvXbarSelW'(eject_req[noc_port].payload.addr[(dynamic_offset_q + NocCacheBankBits) +: NocAddrTileWidth] + * NumRemoteGroupPortTile + eject_req[noc_port].hdr.src_port_id); + + end + + assign inject_rsp = slv_xbar_slv_rsp; + assign inject_rsp_valid = slv_xbar_slv_rsp_valid; + assign slv_xbar_slv_rsp_ready = inject_rsp_ready; + + + // ----------------------------------------------------------------------- + // Slave-side group-wide dispatch xbar + // ----------------------------------------------------------------------- + reqrsp_xbar #( + .NumInp ( NumNoCPortsGroup ), + .NumOut ( NumRemoteGroupPortGroup), + .tcdm_req_chan_t ( noc_group_req_t ), + .tcdm_rsp_chan_t ( noc_group_rsp_t ) + ) i_noc_slv_xbar ( + .clk_i, + .rst_ni, + .slv_req_i ( eject_req ), + .slv_rr_i ( '0 ), + .slv_req_valid_i ( eject_req_valid ), + .slv_req_ready_o ( eject_req_ready ), + .slv_rsp_o ( slv_xbar_slv_rsp ), + .slv_rsp_valid_o ( slv_xbar_slv_rsp_valid ), + .slv_rsp_ready_i ( slv_xbar_slv_rsp_ready ), + .slv_sel_i ( slv_xbar_slv_sel ), + .slv_selected_o ( ), + .mst_req_o ( slv_xbar_mst_req ), + .mst_req_valid_o ( slv_xbar_mst_req_valid ), + .mst_req_ready_i ( slv_xbar_mst_req_ready ), + .mst_rsp_i ( slv_xbar_mst_rsp ), + .mst_rr_i ( '0 ), + .mst_rsp_valid_i ( slv_xbar_mst_rsp_valid ), + .mst_rsp_ready_o ( slv_xbar_mst_rsp_ready ), + .mst_sel_i ( slv_xbar_mst_sel ) + ); + + + // ----------------------------------------------------------------------- + // Slave delivery: unpack xbar output → group slave ports + rsp packing + // ----------------------------------------------------------------------- + for (genvar t = 0; t < NumTilesPerGroup; t++) begin : gen_slv_deliver_t + for (genvar p = 0; p < NumRemoteGroupPortTile; p++) begin : gen_slv_deliver_p + localparam int unsigned port = t * NumRemoteGroupPortTile + p; + + assign slv_xbar_mst_sel[port] = MstXbarSelW'( + remote_group_rsp_from_group[port].p.user.tile_id[NocGroupOffset-1:0] + * NumNoCPortsPerTile); + + always_comb begin : proc_req_unpack + remote_group_req_to_group[port].q = slv_xbar_mst_req[port].payload; + remote_group_req_to_group[port].q.user.src_group_x = + slv_xbar_mst_req[port].hdr.src_id.x; + remote_group_req_to_group[port].q.user.src_group_y = + slv_xbar_mst_req[port].hdr.src_id.y; + end + + assign remote_group_req_to_group[port].q_valid = slv_xbar_mst_req_valid[port]; + assign slv_xbar_mst_req_ready[port] = + remote_group_rsp_from_group[port].q_ready; + assign remote_group_req_to_group[port].p_ready = slv_xbar_mst_rsp_ready[port]; + + + assign slv_xbar_mst_rsp[port].payload = + remote_group_rsp_from_group[port].p; + assign slv_xbar_mst_rsp[port].hdr.collective_op = '0; + assign slv_xbar_mst_rsp[port].hdr.src_id = group_xy_id_i; + // Row-major: lower group bits are X, upper are Y + if (NumGroupsX > 1) begin : gen_rsp_dst_x + assign slv_xbar_mst_rsp[port].hdr.dst_id.x = + remote_group_rsp_from_group[port].p.user.tile_id[NocGroupOffset +: NocGroupBitsX]; + end else begin : gen_rsp_dst_x + assign slv_xbar_mst_rsp[port].hdr.dst_id.x = '0; + end + if (NumGroupsY > 1) begin : gen_rsp_dst_y + assign slv_xbar_mst_rsp[port].hdr.dst_id.y = + remote_group_rsp_from_group[port].p.user.tile_id[(NocGroupOffset + NocGroupBitsX) +: NocGroupBitsY]; + end else begin : gen_rsp_dst_y + assign slv_xbar_mst_rsp[port].hdr.dst_id.y = '0; + end + assign slv_xbar_mst_rsp[port].hdr.dst_id.port_id = '0; + assign slv_xbar_mst_rsp[port].hdr.src_tile_id = group_tile_sel_t'(t); + assign slv_xbar_mst_rsp[port].hdr.src_port_id = remote_group_rsp_from_group[port].p.user.port_id; + assign slv_xbar_mst_rsp[port].hdr.last = 1'b1; + assign slv_xbar_mst_rsp_valid[port] = + remote_group_rsp_from_group[port].p_valid; + end + end + + + end else begin : gen_noc_disabled + + assign remote_group_req_to_group = '0; + assign remote_group_rsp_to_group = '0; + assign req_mesh_out = '0; + assign req_mesh_out_valid = '0; + assign req_mesh_in_ready = '0; + assign rsp_mesh_out = '0; + assign rsp_mesh_out_valid = '0; + assign rsp_mesh_in_ready = '0; + + end + + + // ------------------------------------------------------------------------- + // L2 path: floo_tcdm_chimney (MgrPort) + floo_router (source routing) + // ------------------------------------------------------------------------- + // The chimney translates request addresses to destination IDs via SAM, + // looks up source routes in the routing table, and packs/unpacks flits. + // Two floo_router instances (req + rsp) handle 5-port source-routed mesh. + + // Internal L2 reqrsp between group and chimney + l2_req_t l2_group_req; + l2_rsp_t l2_group_rsp; + + // Router port signals [Eject:North] = [4:0] + l2_noc_req_t [floo_pkg::Eject:floo_pkg::North] l2_router_req_in; + logic [floo_pkg::Eject:floo_pkg::North] l2_router_req_valid_in; + logic [floo_pkg::Eject:floo_pkg::North] l2_router_req_ready_out; + l2_noc_req_t [floo_pkg::Eject:floo_pkg::North] l2_router_req_out; + logic [floo_pkg::Eject:floo_pkg::North] l2_router_req_valid_out; + logic [floo_pkg::Eject:floo_pkg::North] l2_router_req_ready_in; + + l2_noc_rsp_t [floo_pkg::Eject:floo_pkg::North] l2_router_rsp_in; + logic [floo_pkg::Eject:floo_pkg::North] l2_router_rsp_valid_in; + logic [floo_pkg::Eject:floo_pkg::North] l2_router_rsp_ready_out; + l2_noc_rsp_t [floo_pkg::Eject:floo_pkg::North] l2_router_rsp_out; + logic [floo_pkg::Eject:floo_pkg::North] l2_router_rsp_valid_out; + logic [floo_pkg::Eject:floo_pkg::North] l2_router_rsp_ready_in; + + // Manager chimney: group REQRSP ↔ flit inject/eject at router Eject port + floo_tcdm_chimney #( + .RouteCfg ( floo_cachepool_noc_pkg::RouteCfg ), + .EnMgrPort ( 1'b1 ), + .EnSbrPort ( 1'b0 ), + .id_t ( floo_cachepool_noc_pkg::id_t ), + .route_t ( floo_cachepool_noc_pkg::route_t ), + .dst_t ( floo_cachepool_noc_pkg::route_t ), + .hdr_t ( l2_noc_hdr_t ), + .req_chan_t ( cache_trans_req_chan_t ), + .rsp_chan_t ( cache_trans_rsp_chan_t ), + .floo_req_t ( l2_noc_req_t ), + .floo_rsp_t ( l2_noc_rsp_t ), + .addr_t ( axi_addr_t ), + .sam_rule_t ( floo_cachepool_noc_pkg::sam_rule_t ), + .Sam ( floo_cachepool_noc_pkg::Sam ) + ) i_l2_mgr_chimney ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .test_enable_i ( 1'b0 ), + // Manager port: group REQRSP + .mgr_req_i ( l2_group_req.q ), + .mgr_req_valid_i ( l2_group_req.q_valid ), + .mgr_req_ready_o ( l2_group_rsp.q_ready ), + .mgr_rsp_o ( l2_group_rsp.p ), + .mgr_rsp_valid_o ( l2_group_rsp.p_valid ), + .mgr_rsp_ready_i ( l2_group_req.p_ready ), + // Subordinate port: unused + .sbr_req_o ( ), + .sbr_req_valid_o ( ), + .sbr_req_ready_i ( 1'b0 ), + .sbr_rsp_i ( '0 ), + .sbr_rsp_valid_i ( 1'b0 ), + .sbr_rsp_ready_o ( ), + .sbr_txn_id_i ( '0 ), + // Routing + .id_i ( l2_id_i ), + .route_table_i ( l2_route_table_i ), + // Request flit: inject into request router Eject port + .floo_req_o ( l2_router_req_in[floo_pkg::Eject] ), + .floo_req_valid_o ( l2_router_req_valid_in[floo_pkg::Eject] ), + .floo_req_ready_i ( l2_router_req_ready_out[floo_pkg::Eject] ), + // Request flit: eject from request router (unused for MgrPort) + .floo_req_i ( l2_router_req_out[floo_pkg::Eject] ), + .floo_req_valid_i ( l2_router_req_valid_out[floo_pkg::Eject] ), + .floo_req_ready_o ( l2_router_req_ready_in[floo_pkg::Eject] ), + // Response flit: inject into response router (unused for MgrPort) + .floo_rsp_o ( l2_router_rsp_in[floo_pkg::Eject] ), + .floo_rsp_valid_o ( l2_router_rsp_valid_in[floo_pkg::Eject] ), + .floo_rsp_ready_i ( l2_router_rsp_ready_out[floo_pkg::Eject] ), + // Response flit: eject from response router + .floo_rsp_i ( l2_router_rsp_out[floo_pkg::Eject] ), + .floo_rsp_valid_i ( l2_router_rsp_valid_out[floo_pkg::Eject] ), + .floo_rsp_ready_o ( l2_router_rsp_ready_in[floo_pkg::Eject] ) + ); + + // Request router (source routing, 5 ports: N/E/S/W/Eject) + floo_router #( + .NumRoutes ( 5 ), + .NumVirtChannels( 1 ), + .InFifoDepth ( 2 ), + .OutFifoDepth ( 2 ), + .RouteAlgo ( floo_pkg::SourceRouting ), + .id_t ( floo_cachepool_noc_pkg::id_t), + .flit_t ( l2_noc_req_t ), + .hdr_t ( l2_noc_hdr_t ), + .addr_rule_t ( logic ), + .red_req_t ( logic ), + .red_rsp_t ( logic ) + ) i_l2_req_router ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .test_enable_i ( 1'b0 ), + .xy_id_i ( '0 ), + .id_route_map_i ( '0 ), + .valid_i ( l2_router_req_valid_in ), + .ready_o ( l2_router_req_ready_out ), + .data_i ( l2_router_req_in ), + .credit_o ( ), + .valid_o ( l2_router_req_valid_out ), + .ready_i ( l2_router_req_ready_in ), + .data_o ( l2_router_req_out ), + .credit_i ( '0 ), + .offload_req_o ( ), + .offload_rsp_i ( '0 ) + ); + + // Response router (source routing, 5 ports: N/E/S/W/Eject) + floo_router #( + .NumRoutes ( 5 ), + .NumVirtChannels( 1 ), + .InFifoDepth ( 2 ), + .OutFifoDepth ( 2 ), + .RouteAlgo ( floo_pkg::SourceRouting ), + .id_t ( floo_cachepool_noc_pkg::id_t), + .flit_t ( l2_noc_rsp_t ), + .hdr_t ( l2_noc_hdr_t ), + .addr_rule_t ( logic ), + .red_req_t ( logic ), + .red_rsp_t ( logic ) + ) i_l2_rsp_router ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .test_enable_i ( 1'b0 ), + .xy_id_i ( '0 ), + .id_route_map_i ( '0 ), + .valid_i ( l2_router_rsp_valid_in ), + .ready_o ( l2_router_rsp_ready_out ), + .data_i ( l2_router_rsp_in ), + .credit_o ( ), + .valid_o ( l2_router_rsp_valid_out ), + .ready_i ( l2_router_rsp_ready_in ), + .data_o ( l2_router_rsp_out ), + .credit_i ( '0 ), + .offload_req_o ( ), + .offload_rsp_i ( '0 ) + ); + + // Expose [West:North] router ports (indices 3:0) to cluster for mesh wiring + for (genvar d = 0; d < 4; d++) begin : gen_l2_mesh_ports + assign l2_req_o[d] = l2_router_req_out[d]; + assign l2_req_valid_o[d] = l2_router_req_valid_out[d]; + assign l2_router_req_ready_in[d] = l2_req_ready_i[d]; + assign l2_router_req_in[d] = l2_req_i[d]; + assign l2_router_req_valid_in[d] = l2_req_valid_i[d]; + assign l2_req_ready_o[d] = l2_router_req_ready_out[d]; + + assign l2_rsp_o[d] = l2_router_rsp_out[d]; + assign l2_rsp_valid_o[d] = l2_router_rsp_valid_out[d]; + assign l2_router_rsp_ready_in[d] = l2_rsp_ready_i[d]; + assign l2_router_rsp_in[d] = l2_rsp_i[d]; + assign l2_router_rsp_valid_in[d] = l2_rsp_valid_i[d]; + assign l2_rsp_ready_o[d] = l2_router_rsp_ready_out[d]; + end + + // ------------------------------------------------------------------------- + // Group instantiation + // ------------------------------------------------------------------------- + cachepool_group #( + .AxiAddrWidth ( AxiAddrWidth ), + .AxiDataWidth ( AxiDataWidth ), + .AxiIdWidthIn ( AxiIdWidthIn ), + .AxiIdWidthOut ( AxiIdWidthOut ), + .AxiUserWidth ( AxiUserWidth ), + .BootAddr ( BootAddr ), + .UartAddr ( UartAddr ), + .ClusterPeriphSize ( ClusterPeriphSize ), + .NrCores ( NrCores ), + .TCDMDepth ( TCDMDepth ), + .NrBanks ( NrBanks ), + .ICacheLineWidth ( ICacheLineWidth ), + .ICacheLineCount ( ICacheLineCount ), + .ICacheSets ( ICacheSets ), + .FPUImplementation ( FPUImplementation ), + .NumSpatzFPUs ( NumSpatzFPUs ), + .NumSpatzIPUs ( NumSpatzIPUs ), + .SnitchPMACfg ( SnitchPMACfg ), + .NumIntOutstandingLoads ( NumIntOutstandingLoads ), + .NumIntOutstandingMem ( NumIntOutstandingMem ), + .NumSpatzOutstandingLoads ( NumSpatzOutstandingLoads ), + .axi_out_req_t ( axi_out_req_t ), + .axi_out_resp_t ( axi_out_resp_t ), + .RegisterOffloadRsp ( RegisterOffloadRsp ), + .RegisterCoreReq ( RegisterCoreReq ), + .RegisterCoreRsp ( RegisterCoreRsp ), + .RegisterTCDMCuts ( RegisterTCDMCuts ), + .RegisterExt ( RegisterExt ), + .XbarLatency ( XbarLatency ), + .MaxMstTrans ( MaxMstTrans ), + .MaxSlvTrans ( MaxSlvTrans ) + ) i_group ( + .clk_i, + .rst_ni, + .impl_i ( impl_i ), + .error_o ( error_o ), + .debug_req_i ( debug_req_i ), + .meip_i ( meip_i ), + .mtip_i ( mtip_i ), + .msip_i ( msip_i ), + .hart_base_id_i ( hart_base_id_i ), + .tile_base_id_i ( tile_base_id_i ), + .cluster_base_addr_i ( cluster_base_addr_i ), + .private_start_addr_i ( private_start_addr_q ), + .l2_req_o ( l2_group_req ), + .l2_rsp_i ( l2_group_rsp ), + .l2_group_id_i ( l2_id_i ), + .remote_group_req_o ( remote_group_req_from_group ), + .remote_group_rsp_i ( remote_group_rsp_to_group ), + .remote_group_req_i ( remote_group_req_to_group ), + .remote_group_rsp_o ( remote_group_rsp_from_group ), + .tile_barrier_o ( tile_barrier_d ), + .barrier_done_i ( barrier_done_q ), + .icache_events_o ( icache_events_o ), + .icache_prefetch_enable_i ( icache_prefetch_enable_i ), + .cl_interrupt_i ( cl_interrupt_i ), + .dynamic_offset_i ( dynamic_offset_q ), + .l1d_private_i ( l1d_private_q ), + .l1d_insn_i ( l1d_insn_q ), + .l1d_insn_valid_i ( l1d_insn_valid_q ), + .l1d_insn_ready_o ( l1d_insn_ready_d ), + .l1d_busy_i ( l1d_busy_q ) + ); + +endmodule diff --git a/hardware/src/cachepool_pkg.sv b/hardware/src/cachepool_pkg.sv index 737bc70..a7b38cd 100644 --- a/hardware/src/cachepool_pkg.sv +++ b/hardware/src/cachepool_pkg.sv @@ -46,25 +46,45 @@ package cachepool_pkg; localparam int unsigned NumIntOutstandingMem = `ifdef SNITCH_MAX_TRANS `SNITCH_MAX_TRANS `else 0 `endif; localparam int unsigned NumSpatzOutstandingLoads = `ifdef SPATZ_MAX_TRANS `SPATZ_MAX_TRANS `else 0 `endif; - localparam int unsigned NumAxiMaxTrans = 32; + localparam int unsigned NumAxiMaxTrans = 64; /////////////////// // TILE CONFIG // /////////////////// // How many cores for each tile? - localparam int unsigned NumCoresTile = NumCores / NumTiles; + localparam int unsigned NumCoresTile = NumCores / NumTiles; - // How many remote ports for each tile per core's port? - localparam int unsigned NumRemotePortCore = `ifdef REMOTE_PORT_PER_CORE `REMOTE_PORT_PER_CORE `else 0 `endif; + // Intra-group remote ports per core (to other tiles in the same group). + localparam int unsigned NumLGPortCore = `ifdef LG_PORT_PER_CORE `LG_PORT_PER_CORE `else 0 `endif; // How many cores within a tile? This is used to select the ports within a tile. - localparam int unsigned LogNumCoresTile = $clog2(NumCoresTile); + localparam int unsigned LogNumCoresTile = $clog2(NumCoresTile); // 4 ports from Spatz + 1 shared port from Snitch/FPU - localparam int unsigned NrTCDMPortsPerCore = 5; + localparam int unsigned NrTCDMPortsPerCore = 5; + + // Intra-group remote ports per tile, in total. + localparam int unsigned NumLGPortTile = NumLGPortCore * NrTCDMPortsPerCore; + + //////////////////// + // GROUP CONFIG // + //////////////////// + // How many tiles for each group? + localparam int unsigned NumTilesPerGroup = NumTiles / NumGroups; + + // How many cores for each group? + localparam int unsigned NumCoreGroup = NumCores / NumGroups; + + // How many remote group ports for each tile? + localparam int unsigned NumRemoteGroupPortCore = `ifdef RG_PORT_PER_CORE `RG_PORT_PER_CORE `else 0 `endif; + + // Number of inter-group NoC router channels per tile (x in the 5-to-x concentration xbar). + localparam int unsigned NumNoCPortsPerTile = `ifdef NOC_PORT_PER_TILE `NOC_PORT_PER_TILE `else 1 `endif; + + // Group mesh dimensions. NumGroupsY is derived; NumGroupsX must be set via config. + localparam int unsigned NumGroupsX = `ifdef NUM_GROUPS_X `NUM_GROUPS_X `else 1 `endif; + localparam int unsigned NumGroupsY = NumGroups / NumGroupsX; - // How many remote ports for each tile in total? - localparam int unsigned NumRemotePortTile = NumRemotePortCore * NrTCDMPortsPerCore; //////////////////// // CLUSTER HW // @@ -76,6 +96,12 @@ package cachepool_pkg; localparam int unsigned ICacheLineCount = 128; localparam int unsigned ICacheSets = 4; + // Group-level L2 ICache (shared read-only cache, primarily for coalescing) + localparam int unsigned L2ICacheLineWidth = 512; + localparam int unsigned L2ICacheSets = 4; + localparam int unsigned L2ICacheSizeByte = 16384; + localparam int unsigned L2ICacheLineCount = L2ICacheSizeByte / (L2ICacheSets * L2ICacheLineWidth / 8); + // Be careful on unsigned long int passed in from configuration. // Currently use fixed values. localparam int unsigned TCDMStartAddr = 32'hBFFF_F800; @@ -137,8 +163,8 @@ package cachepool_pkg; // Core id width within a tile => tile ID will be calculated separatly localparam int unsigned CoreIDWidth = idx_width(NumCoresTile); localparam int unsigned TileIDWidth = idx_width(NumTiles); - // Each bank inside a tile needs an unique id, plus one reserved for icache - localparam int unsigned BankIDWidth = idx_width(NumL1CtrlTile + 1); + // Each bank inside a tile needs an unique id, plus two reserved for iCache and peripheral + localparam int unsigned BankIDWidth = idx_width(NumL1CtrlTile + 2); localparam int unsigned RefillDataWidth = `ifdef REFILL_DATA_WIDTH `REFILL_DATA_WIDTH `else 0 `endif; localparam int unsigned RefillStrbWidth = RefillDataWidth / 8; @@ -160,76 +186,121 @@ package cachepool_pkg; // AXI Address Width localparam int unsigned SpatzAxiAddrWidth = `ifdef ADDR_WIDTH `ADDR_WIDTH `else 0 `endif; // AXI User Width - localparam int unsigned SpatzAxiUserWidth = `ifdef AXI_USER_WIDTH `AXI_USER_WIDTH `else 0 `endif + $clog2(NumTiles); + // The `+ $bits(floo_cachepool_noc_pkg::id_t)` term accounts for + // refill_user_t.l2_src_id, whose width scales with NumEndpoints (groups + + // HBM channels) rather than being a fixed constant across configs -- do + // not fold this into the per-config axi_user_width define instead, that + // was tried and breaks silently whenever NumEndpoints changes width. + localparam int unsigned SpatzAxiUserWidth = `ifdef AXI_USER_WIDTH `AXI_USER_WIDTH `else 0 `endif + + $clog2(NumTiles) + + $bits(floo_cachepool_noc_pkg::id_t); // ----------------------- // AXI ID field structure // ----------------------- // ClusterAxiIdWidth is composed of: // [cluster_route_bits][tile_index_bits][tile_local_bits] - localparam int unsigned NumClusterMst = 1 + NumL1CtrlTile; + // Per-tile xbar ports: NumL1CtrlTile refill + 1 peripheral + 1 iCache + localparam int unsigned NumClusterMst = 2 + NumL1CtrlTile; + // Bank ID constants for the refill xbar response demux + // Layout per tile: [0..NumL1CtrlTile-1] = refill, NumL1CtrlTile = peripheral, NumL1CtrlTile+1 = iCache + localparam int unsigned BankIdPeriph = NumL1CtrlTile; + localparam int unsigned BankIdICache = NumL1CtrlTile + 1; localparam int unsigned ClusterRouteIdWidth = $clog2(NumClusterMst); /***** ID Width Topology (Tile -> Group -> Cluster) *****/ + // TileAxiIdWidth: base iCache/DMA AXI ID bits per tile before tile-index bits are added. + // Determines how many outstanding refills the iCache can track (2^TileAxiIdWidth = 8). + // This is the "tile_local_bits" field described above. localparam int unsigned TileAxiIdWidth = 3; localparam int unsigned GroupAxiIdWidth = TileAxiIdWidth + $clog2(NumTiles); localparam int unsigned ClusterAxiIdWidth = GroupAxiIdWidth + ClusterRouteIdWidth; - - // legacy naming + // Alias used by the Spatz-generated wrapper and testbench templates. localparam int unsigned SpatzAxiIdInWidth = ClusterAxiIdWidth; - // localparam int unsigned SpatzAxiIdInWidth = TileAxiIdWidth; - localparam int unsigned SpatzAxiIdOutWidth = ClusterAxiIdWidth + 1; - // Fixed AXI ID width for IWC - localparam int unsigned IwcAxiIdOutWidth = SpatzAxiIdOutWidth + 1; + // Tile wide xbar inputs (iCache only; peripheral bypasses the xbar) + localparam int unsigned TileWideXbarInputs = 1; + localparam int unsigned TileWideXbarIdExtraBits = (TileWideXbarInputs > 1) ? $clog2(TileWideXbarInputs) : 0; + + // Tile-internal wide AXI ID width (passed to groups/tiles as AxiIdWidthOut). + // Sized for the tile's wide xbar: iCache needs TileAxiIdWidth bits, + // group-level BootROM mux needs $clog2(NumTilesPerGroup) bits on top, + // and the tile xbar adds TileWideXbarIdExtraBits (0 with 1 master). + localparam int unsigned GroupWideIdWidth = TileAxiIdWidth + $clog2(NumTilesPerGroup) + TileWideXbarIdExtraBits; + + // Max outstanding transactions tracked by the L2 refill mesh's DRAM-facing + // reqrsp_to_axi converters (HBM0 DRAM path, direct HBM paths). Sized for + // the mesh's aggregate in-flight refill window, not per-core outstanding + // loads -- deliberately decoupled from NumSpatzOutstandingLoads (that + // bounds one core's own outstanding requests, whereas this bounds how many + // requests from all cores/groups can be in flight at a single DRAM channel + // at once). Chosen to match the previous NumSpatzOutstandingLoads*4 value. + localparam int unsigned L2RefillMaxTrans = 128; + + // Cluster-level AXI output ID width (chimney → DRAM). + // With the FlooNoC mesh, reqrsp_to_axi generates fresh IDs with + // $clog2(MaxTrans) bits. The chimney carries these through unchanged. + // No multi-group mux → no extra bits needed. + localparam int unsigned SpatzAxiIdOutWidth = $clog2(L2RefillMaxTrans); + + // Cluster wrapper external output AXI ID width. + // Equals SpatzAxiIdOutWidth (no compression needed), but kept as a + // separate parameter for interface stability. Must track + // SpatzAxiIdOutWidth: if narrower, the id_remap in cachepool_cluster_wrapper + // (i_out_id_remap) would reintroduce the same ID-reuse collision problem + // (right before DRAM) that widening SpatzAxiIdOutWidth was meant to fix. + localparam int unsigned WrapperAxiIdOutWidth = SpatzAxiIdOutWidth; + // External narrow output AXI ID width for the UART port (cluster → SoC direction). + // axi_id_remap in the wrapper compresses SpatzAxiUartIdWidth to this. + localparam int unsigned WrapperAxiNarrowIdOutWidth = 4; localparam int unsigned CsrAxiMstIdWidth = ClusterAxiIdWidth; - localparam int unsigned CsrAxiSlvIdWidth = ClusterAxiIdWidth + $clog2(NumTiles+1); + // ID width after per-master serialization (legacy, kept for barrier compatibility). + localparam int unsigned CsrSerIdWidth = 2; + // CSR slave ID width: 2×2 peripheral xbar adds 1 bit to CsrSerIdWidth. + // Both the HBM0 peripheral path (serialized) and TB axi_in path (serialized) + // feed into the xbar at CsrSerIdWidth; output is CsrSerIdWidth + 1. + localparam int unsigned CsrAxiSlvIdWidth = CsrSerIdWidth + 1; - // Base ID width 6, plus tile mux => adding clog(tile) - localparam int unsigned SpatzAxiNarrowIdWidth = 6 + $clog2(NumTiles); - // UART ID width, with an extra xbar - localparam int unsigned SpatzAxiUartIdWidth = SpatzAxiNarrowIdWidth + $clog2(NumTiles); + // Narrow AXI ID width = ClusterAxiIdWidth (used for barrier types). + localparam int unsigned SpatzAxiNarrowIdWidth = ClusterAxiIdWidth; + // UART ID width: same as CSR slave ID width (both are xbar master outputs). + localparam int unsigned SpatzAxiUartIdWidth = CsrSerIdWidth + 1; - /***** Tile Ports *****/ - // We have three sets of AXI ports for each tile: - // 1) Wide output bus for BootRom & L2 (from ICache) - // 2) Narrow output bus for UART/Periph - // 3) Narrow input bus for SoC control + // BootROM is at cluster level (HBM0 peripheral path), no per-group BootROM ID width needed. - // Narrow AXI Ports: 1 UART + 1 Periph - localparam int unsigned TileNarrowAxiPorts = 2; + /***** Tile Ports *****/ + // Each tile has: + // 1) Wide AXI output: iCache L2 refill only (single port, no xbar) + // 2) Narrow REQRSP output: peripheral traffic (UART, CSR, BootROM) + // 3) Wide input bus for SoC control (enters via mesh) - // Wide AXI Ports: 1 BootROM + 1 Data (I$) - localparam int unsigned TileWideAxiPorts = 2; - localparam int unsigned TileWideXbarInputs = 2; // iCache + narrow2wide - localparam int unsigned TileWideXbarIdExtraBits = $clog2(TileWideXbarInputs); // = 1 + // Wide AXI Ports: iCache L2 output only (BootROM moved to cluster level) + localparam int unsigned TileWideAxiPorts = 1; // Wide Data Ports: 1 for each controller localparam int unsigned TileWideDataPorts = NumL1CtrlTile; /***** Group Ports *****/ - // Narrow AXI ports - localparam int unsigned GroupNarrowAxiPorts = TileNarrowAxiPorts * NumTiles; // Wide AXI ports localparam int unsigned GroupWideAxiPorts = TileWideAxiPorts * NumTiles; // Wide Data ports localparam int unsigned GroupWideDataPorts = NumL1CtrlTile; // Correct selection width for remote xbar at group level - localparam int unsigned RemoteXbarSelWidth = $clog2(NumTiles * NumRemotePortCore); + localparam int unsigned RemoteXbarSelWidth = $clog2(NumTiles * NumLGPortCore); /***** Cluster Ports *****/ - // Narrow AXI ports: 1 In from SoC, 1 Out to UART + // Narrow AXI ports: + // In: 1 from SoC (enters mesh via HBM0 chimney input, upsized 32→512) + // Out: 1 to UART (exits HBM0 chimney output → demux → downsizer 512→32) localparam int unsigned ClusterNarrowInAxiPorts = 1; localparam int unsigned ClusterNarrowOutAxiPorts = 1; - // Wide AXI ports: X to DRAM (X=4 for now) + // Wide AXI ports: one per HBM channel (HBM0 is shared with peripheral demux) localparam int unsigned ClusterWideOutAxiPorts = NumL2Channel; - // TODO: multi-tile support - // One more from the Snitch core ////////////////// // L2 / DRAM // @@ -238,8 +309,11 @@ package cachepool_pkg; localparam int unsigned L2BankWidth = `ifdef L2_BANK_WIDTH `L2_BANK_WIDTH `else 0 `endif; localparam int unsigned L2BankBeWidth = L2BankWidth / 8; - parameter DramType = "DDR4"; // "DDR4", "DDR3", "HBM2", "LPDDR4" +`ifndef TARGET_SYNTHESIS + // Supported values (must match DRAMSys config names): DDR3, DDR4, LPDDR4, HBM2 + parameter string DramType = `ifdef DRAM_TYPE `DRAM_TYPE `else "DDR4" `endif; parameter int unsigned DramBase = 32'h8000_0000; +`endif // One more for UART? localparam int unsigned NumClusterSlv = NumL2Channel; @@ -282,25 +356,21 @@ package cachepool_pkg; typedef logic [SpatzAxiUartIdWidth-1:0] axi_uart_id_t; typedef logic [CsrAxiMstIdWidth-1:0] axi_id_csr_mst_t; + typedef logic [CsrSerIdWidth-1:0] axi_id_csr_ser_t; typedef logic [CsrAxiSlvIdWidth-1:0] axi_id_csr_slv_t; - typedef logic [IwcAxiIdOutWidth-1:0] axi_id_out_iwc_t; + typedef logic [WrapperAxiIdOutWidth-1:0] axi_id_wrapper_out_t; + typedef logic [WrapperAxiNarrowIdOutWidth-1:0] axi_id_wrapper_narrow_out_t; ////////////////// // TILE TYPES // ////////////////// typedef logic [TileIDWidth-1:0] remote_tile_sel_t; - // Naming the port for easier connection - typedef enum integer { - TilePeriph = 0, - TileUart = 1 - } tile_narrow_e; - - // Naming the port for easier connection + // Tile-level wide AXI external port indices (group-facing) + // Single port: iCache L2 output only (BootROM moved to cluster level) typedef enum integer { - TileBootROM = 0, - TileMem = 1 + TileMem = 0 } tile_wide_e; ////////////////////// @@ -352,27 +422,108 @@ package cachepool_pkg; } tcdm_user_t; typedef struct packed { - logic [BankIDWidth-1:0] bank_id; - logic [TileIDWidth-1:0] tile_id; - cache_info_t info; - burst_req_t burst; + logic [BankIDWidth-1:0] bank_id; + logic [TileIDWidth-1:0] tile_id; + // L2 refill mesh source group (floo endpoint ID), stamped at request + // formation and read back at the HBM ejection chimney to route the + // response without a local src_id FIFO (which assumed in-order HBM + // completion). Distinct from tile_id, which stays local to the group + // and is used as a routing index for intra-group response delivery. + // Placed above info/burst (not appended at the end) because the iCache + // path's EnUserIdPassthrough workaround (cachepool_group.sv) truncates + // this struct down to cache_info_t width and depends on info/burst + // remaining the bottom (LSB) fields. + floo_cachepool_noc_pkg::id_t l2_src_id; + cache_info_t info; + burst_req_t burst; } refill_user_t; /////////////////// // GROUP TYPES // /////////////////// - typedef logic [RemoteXbarSelWidth-1:0] remote_xbar_sel_t; + typedef logic [RemoteXbarSelWidth-1:0] remote_xbar_sel_t; + typedef logic [$clog2(NrTCDMPortsPerCore)-1:0] portid_t; + + typedef struct packed { + logic [CoreIDWidth-1:0] core_id; + logic [TileIDWidth-1:0] tile_id; + reqid_t req_id; + logic is_fpu; + portid_t port_id; + logic [idx_width(NumGroupsX)-1:0] src_group_x; + logic [idx_width(NumGroupsY)-1:0] src_group_y; + // Globally-unique destination tile ID, set by tcdm_cache_interco for + // inter-group requests. Upper bits (above $clog2(NumTilesPerGroup)) are + // the linear group index; lower bits are the local tile within the group. + logic [TileIDWidth-1:0] dst_tile_id; + } remote_group_user_t; + + `REQRSP_TYPEDEF_ALL(remote_group, narrow_addr_t, narrow_data_t, narrow_strb_t, remote_group_user_t) + + // XY mesh coordinates for a group. port_id selects the eject port (always 0 for single-link). + typedef struct packed { + logic [idx_width(NumGroupsX)-1:0] x; + logic [idx_width(NumGroupsY)-1:0] y; + logic port_id; + } group_xy_id_t; + + // Per-group tile index used by dispatch xbar selection. + typedef logic [idx_width(NumTilesPerGroup)-1:0] group_tile_sel_t; + + // Routing header embedded in every inter-group NoC flit. + typedef struct packed { + logic [3:0] collective_op; + group_xy_id_t src_id; + group_xy_id_t dst_id; + group_tile_sel_t src_tile_id; + portid_t src_port_id; + logic last; + } noc_group_hdr_t; + + // Inter-group NoC flit types (payload + routing header). + typedef struct packed { + remote_group_req_chan_t payload; + noc_group_hdr_t hdr; + } noc_group_req_t; + + typedef struct packed { + remote_group_rsp_chan_t payload; + noc_group_hdr_t hdr; + } noc_group_rsp_t; + + ////////////////////////////// + // L2 Refill Mesh Types // + ////////////////////////////// + // Types imported from floogen-generated floo_cachepool_noc_pkg: + // id_t = logic[3:0] — endpoint ID (GroupX0Y0..Hbm3, HostPeri) + // route_t = logic[8:0] — packed source route (3 bits/hop, consumed LSB-first) + // The header carries route_t as dst_id (for source routing) and id_t as src_id + // (for return-path lookup). floo_tcdm_chimney uses SAM for address→id translation + // and RoutingTables for id→route lookup. + + typedef struct packed { + logic [3:0] collective_op; + floo_cachepool_noc_pkg::id_t src_id; + floo_cachepool_noc_pkg::route_t dst_id; + logic last; + } l2_noc_hdr_t; + // l2_noc_req_t / l2_noc_rsp_t defined after REQRSP_TYPEDEF_ALL macros below. + + // Group ICache (L2 read-only cache control) + // 2 rules: DRAM (cacheable) + BootROM (cacheable, avoids Bypass path) + localparam int unsigned ROCacheNumAddrRules = 2; + typedef struct packed { + logic enable; + logic flush_valid; + axi_addr_t [ROCacheNumAddrRules-1:0] start_addr; + axi_addr_t [ROCacheNumAddrRules-1:0] end_addr; + } ro_cache_ctrl_t; ///////////////////// // CLUSTER TYPES // ///////////////////// - typedef enum integer { - ClusterUart = 0, - ClusterPeriph = 1 - } cluster_narrow_e; - typedef enum integer { L2Channel0 = 0, L2Channel1 = 1, @@ -414,6 +565,32 @@ package cachepool_pkg; // REQRSP: cache transaction (same payload type as L2 in current code) `REQRSP_TYPEDEF_ALL (cache_trans, axi_addr_t, axi_wide_data_t, axi_wide_strb_t, refill_user_t) + // L2 refill mesh flit types (payload = cache_trans channel + routing header) + typedef struct packed { + cache_trans_req_chan_t payload; + l2_noc_hdr_t hdr; + } l2_noc_req_t; + + typedef struct packed { + cache_trans_rsp_chan_t payload; + l2_noc_hdr_t hdr; + } l2_noc_rsp_t; + + // REQRSP: peripheral path (narrow 32b, tcdm_user_t) + `REQRSP_TYPEDEF_ALL (periph, axi_addr_t, narrow_data_t, narrow_strb_t, tcdm_user_t) + + // REQRSP: narrow 32b with refill_user_t (DW converter output, peripheral path) + `REQRSP_TYPEDEF_ALL (peri_narrow, axi_addr_t, narrow_data_t, narrow_strb_t, refill_user_t) + + // Peripheral xbar user: refill_user_t + 1-bit source ID (0=NoC, 1=TB) + typedef struct packed { + logic src_id; + refill_user_t refill; + } peri_xbar_user_t; + + // REQRSP: peripheral xbar path (narrow 32b, peri_xbar_user_t) + `REQRSP_TYPEDEF_ALL (peri_xbar, axi_addr_t, narrow_data_t, narrow_strb_t, peri_xbar_user_t) + // TCDM req/rsp bus => core to L1 `TCDM_TYPEDEF_ALL(tcdm, narrow_addr_t, narrow_data_t, narrow_strb_t, tcdm_user_t) `TCDM_TYPEDEF_ALL(spm, spm_addr_t, narrow_data_t, narrow_strb_t, tcdm_user_t) @@ -421,13 +598,17 @@ package cachepool_pkg; // AXI typedef bundles `AXI_TYPEDEF_ALL(spatz_axi_narrow, axi_addr_t, axi_narrow_id_t, axi_narrow_data_t, axi_narrow_strb_t, axi_user_t) `AXI_TYPEDEF_ALL(spatz_axi_in, axi_addr_t, axi_id_in_t, axi_narrow_data_t, axi_narrow_strb_t, axi_user_t) - `AXI_TYPEDEF_ALL(spatz_axi_out, axi_addr_t, axi_id_out_t, axi_wide_data_t, axi_wide_strb_t, axi_user_t) - `AXI_TYPEDEF_ALL(spatz_axi_iwc_out, axi_addr_t, axi_id_out_iwc_t, axi_wide_data_t, axi_wide_strb_t, axi_user_t) - - `AXI_TYPEDEF_ALL(axi_uart, axi_addr_t, axi_uart_id_t, axi_narrow_data_t, axi_narrow_strb_t, axi_user_t) - `AXI_TYPEDEF_ALL(axi_csr_mst, axi_addr_t, axi_id_csr_mst_t, axi_narrow_data_t, axi_narrow_strb_t, axi_user_t) - `AXI_TYPEDEF_ALL(axi_csr_slv, axi_addr_t, axi_id_csr_slv_t, axi_narrow_data_t, axi_narrow_strb_t, axi_user_t) - + `AXI_TYPEDEF_ALL(spatz_axi_out, axi_addr_t, axi_id_out_t, axi_wide_data_t, axi_wide_strb_t, axi_user_t) + // Wrapper-level external output type: ID from SpatzAxiIdOutWidth to WrapperAxiIdOutWidth. + `AXI_TYPEDEF_ALL(spatz_axi_wrapper_out, axi_addr_t, axi_id_wrapper_out_t, axi_wide_data_t, axi_wide_strb_t, axi_user_t) + // Wrapper-level external narrow output type: ID compressed from SpatzAxiUartIdWidth to WrapperAxiNarrowIdOutWidth. + `AXI_TYPEDEF_ALL(spatz_axi_wrapper_narrow_out, axi_addr_t, axi_id_wrapper_narrow_out_t, axi_narrow_data_t, axi_narrow_strb_t, axi_user_t) + + `AXI_TYPEDEF_ALL(axi_uart, axi_addr_t, axi_uart_id_t, axi_narrow_data_t, axi_narrow_strb_t, axi_user_t) + `AXI_TYPEDEF_ALL(axi_csr_mst, axi_addr_t, axi_id_csr_mst_t, axi_narrow_data_t, axi_narrow_strb_t, axi_user_t) + // Serialized CSR type: 1-bit ID output of axi_id_serialize, fed into the CSR mux slave ports. + `AXI_TYPEDEF_ALL(axi_csr_ser, axi_addr_t, axi_id_csr_ser_t, axi_narrow_data_t, axi_narrow_strb_t, axi_user_t) + `AXI_TYPEDEF_ALL(axi_csr_slv, axi_addr_t, axi_id_csr_slv_t, axi_narrow_data_t, axi_narrow_strb_t, axi_user_t) /************************************************************** * FUNCTIONS * Order: Core -> Tile -> Group -> Cluster -> TB/L2 @@ -465,11 +646,13 @@ package cachepool_pkg; automatic snitch_pma_pkg::rule_t [snitch_pma_pkg::NrMaxRules-1:0] cached_regions; cached_regions = '{default: '0}; cached_regions[0] = '{base: 32'h80000000, mask: 32'hfc000000}; + // Bootrom at 0x1000: make low 64KB cacheable for ICache + cached_regions[1] = '{base: 32'h00000000, mask: 32'hffff0000}; return cached_regions; endfunction localparam snitch_pma_pkg::snitch_pma_t SnitchPMACfg = '{ - NrCachedRegionRules: 1, + NrCachedRegionRules: 2, CachedRegion: get_cached_regions(), default: 0 }; @@ -478,6 +661,9 @@ package cachepool_pkg; // L2 / DRAM // ////////////////// + // getHbmIdx / hbmIdxToXY removed — replaced by SAM-based address→ID + // translation inside floo_tcdm_chimney (uses floo_cachepool_noc_pkg::Sam). + /************* System Functions ************/ function automatic dram_ctrl_interleave_t getDramCTRLInfo(axi_addr_t addr); automatic dram_ctrl_interleave_t res; diff --git a/hardware/src/cachepool_tile.sv b/hardware/src/cachepool_tile.sv index e01c0ac..1a3fec2 100644 --- a/hardware/src/cachepool_tile.sv +++ b/hardware/src/cachepool_tile.sv @@ -4,19 +4,11 @@ // Author: Diyou Shen -`include "axi/assign.svh" `include "axi/typedef.svh" `include "common_cells/assertions.svh" `include "common_cells/registers.svh" -`include "mem_interface/assign.svh" -`include "mem_interface/typedef.svh" -`include "register_interface//assign.svh" -`include "register_interface/typedef.svh" -`include "reqrsp_interface/assign.svh" `include "reqrsp_interface/typedef.svh" `include "snitch_vm/typedef.svh" -`include "tcdm_interface/assign.svh" -`include "tcdm_interface/typedef.svh" /// Tile implementation for CachePool module cachepool_tile @@ -24,7 +16,7 @@ module cachepool_tile import spatz_pkg::*; import fpnew_pkg::fpu_implementation_t; import snitch_pma_pkg::snitch_pma_t; - import snitch_icache_pkg::icache_events_t; + import snitch_icache_pkg::icache_l1_events_t; #( /// Width of physical address. parameter int unsigned AxiAddrWidth = 48, @@ -48,10 +40,6 @@ module cachepool_tile parameter int unsigned ClusterPeriphSize = 64, /// Number of TCDM Banks. parameter int unsigned NrBanks = 2 * NrCores, - /// Size of DMA AXI buffer. - parameter int unsigned DMAAxiReqFifoDepth = 3, - /// Size of DMA request fifo. - parameter int unsigned DMAReqFifoDepth = 3, /// Width of a single icache line. parameter unsigned ICacheLineWidth = 0, /// Number of icache lines per set. @@ -66,10 +54,14 @@ module cachepool_tile /// Spatz FPU/IPU Configuration parameter int unsigned NumSpatzFPUs = 4, parameter int unsigned NumSpatzIPUs = 1, - /// Per-core enabling of the custom `Xdma` ISA extensions. - parameter bit [NrCores-1:0] Xdma = '{default: '0}, /// Tile ID Width parameter int unsigned TileIDWidth = 0, + /// Number of dedicated inter-group remote ports per xbar plane. + /// When 0, no inter-group ports are generated (single-group mode). + parameter int unsigned NumRemoteGroupPortCore = 0, + /// Number of tiles within a single group (passed to interco for + /// group-id extraction from the address). + parameter int unsigned NumTilesPerGroup = 0, /// # Per-core parameters /// Per-core integer outstanding loads parameter int unsigned NumIntOutstandingLoads = '0, @@ -110,70 +102,83 @@ module cachepool_tile parameter int unsigned MemoryMacroLatency = 1 + RegisterTCDMCuts, /// # SRAM Configuration rules needed: L1D Tag + L1D Data + L1D FIFO + L1I Tag + L1I Data /*** ATTENTION: `NrSramCfg` should be changed if `L1NumDataBank` and `L1NumTagBank` is changed ***/ - parameter int unsigned NrSramCfg = 1 + parameter int unsigned NrSramCfg = 1, + localparam int unsigned TotRGPorts = (NumRemoteGroupPortCore == 0) ? 0 : NumRemoteGroupPortCore*NrTCDMPortsPerCore-1 ) ( /// System clock. - input logic clk_i, + input logic clk_i, /// Asynchronous active high reset. This signal is assumed to be _async_. - input logic rst_ni, + input logic rst_ni, /// Per-core debug request signal. Asserting this signals puts the /// corresponding core into debug mode. This signal is assumed to be _async_. - input logic [NrCores-1:0] debug_req_i, - /// End of Computing indicator to notify the host/tb - // output logic eoc_o, + input logic debug_req_i, /// Machine external interrupt pending. Usually those interrupts come from a /// platform-level interrupt controller. This signal is assumed to be _async_. - input logic [NrCores-1:0] meip_i, + input logic meip_i, /// Machine timer interrupt pending. Usually those interrupts come from a /// core-local interrupt controller such as a timer/RTC. This signal is /// assumed to be _async_. - input logic [NrCores-1:0] mtip_i, + input logic mtip_i, /// Core software interrupt pending. Usually those interrupts come from /// another core to facilitate inter-processor-interrupts. This signal is /// assumed to be _async_. - input logic [NrCores-1:0] msip_i, + input logic msip_i, /// First hartid of the cluster. Cores of a cluster are monotonically /// increasing without a gap, i.e., a cluster with 8 cores and a /// `hart_base_id_i` of 5 get the hartids 5 - 12. - input logic [9:0] hart_base_id_i, + input logic [9:0] hart_base_id_i, /// Base address of cluster. TCDM and cluster peripheral location are derived from /// it. This signal is pseudo-static. - input axi_addr_t cluster_base_addr_i, + input axi_addr_t cluster_base_addr_i, /// Tile ID, internal ID, the base is always 0, in theory should not change during use - input remote_tile_sel_t tile_id_i, + input remote_tile_sel_t tile_id_i, /// Partitioning address - input axi_addr_t private_start_addr_i, - /// AXI Narrow out-port (UART/Peripheral) - output axi_narrow_req_t [1:0] axi_out_req_o, - input axi_narrow_resp_t [1:0] axi_out_resp_i, + input axi_addr_t private_start_addr_i, /// Cache Refill ports - output cache_trans_req_t [NumL1CtrlTile-1:0] cache_refill_req_o, - input cache_trans_rsp_t [NumL1CtrlTile-1:0] cache_refill_rsp_i, - /// Wide AXI ports to cluster level - output axi_out_req_t [TileNarrowAxiPorts-1:0] axi_wide_req_o, - input axi_out_resp_t [TileNarrowAxiPorts-1:0] axi_wide_rsp_i, + output cache_trans_req_t [NumL1CtrlTile-1:0] cache_refill_req_o, + input cache_trans_rsp_t [NumL1CtrlTile-1:0] cache_refill_rsp_i, + /// Peripheral REQRSP port (narrow, 32b, bypasses wide AXI xbar) + output periph_req_t periph_req_o, + input periph_rsp_t periph_rsp_i, + /// Wide AXI port: iCache L2 refill only (BootROM at cluster level) + output axi_out_req_t [TileWideAxiPorts-1:0] axi_wide_req_o, + input axi_out_resp_t [TileWideAxiPorts-1:0] axi_wide_rsp_i, /// Remote Tile access ports (to remote tiles) - output tcdm_req_t [NumRemotePortTile-1:0] remote_req_o, - output remote_tile_sel_t [NumRemotePortTile-1:0] remote_req_dst_o, - input tcdm_rsp_t [NumRemotePortTile-1:0] remote_rsp_i, - input logic [NumRemotePortTile-1:0] remote_rsp_ready_i, + output tcdm_req_t [NumLGPortTile-1:0] remote_req_o, + output remote_tile_sel_t [NumLGPortTile-1:0] remote_req_dst_o, + input tcdm_rsp_t [NumLGPortTile-1:0] remote_rsp_i, + input logic [NumLGPortTile-1:0] remote_rsp_ready_i, /// Remote Tile access ports (from remote tiles) - input tcdm_req_t [NumRemotePortTile-1:0] remote_req_i, - output tcdm_rsp_t [NumRemotePortTile-1:0] remote_rsp_o, - output logic [NumRemotePortTile-1:0] remote_rsp_ready_o, + input tcdm_req_t [NumLGPortTile-1:0] remote_req_i, + output tcdm_rsp_t [NumLGPortTile-1:0] remote_rsp_o, + output logic [NumLGPortTile-1:0] remote_rsp_ready_o, + /// Inter-group remote access ports (to other groups). + /// Flat layout: flat index = j + r * NrTCDMPortsPerCore, + /// where j is the interco instance and r is the inter-group remote slot. + /// Total count: NumRemoteGroupPortCore * NrTCDMPortsPerCore. + /// Uses REQRSP-style types with built-in ready and remote_group_user_t. + output remote_group_req_t [TotRGPorts:0] remote_group_req_o, + input remote_group_rsp_t [TotRGPorts:0] remote_group_rsp_i, + /// Inter-group remote access ports (from other groups) + input remote_group_req_t [TotRGPorts:0] remote_group_req_i, + output remote_group_rsp_t [TotRGPorts:0] remote_group_rsp_o, /// Peripheral signals - output icache_events_t [NrCores-1:0] icache_events_o, - input logic icache_prefetch_enable_i, - input logic [NrCores-1:0] cl_interrupt_i, - input logic [$clog2(AxiAddrWidth)-1:0] dynamic_offset_i, - input cache_insn_t l1d_insn_i, - input logic [3:0] l1d_private_i, - input logic l1d_insn_valid_i, - output logic l1d_insn_ready_o, - input logic l1d_busy_i, + output icache_l1_events_t [NrCores-1:0] icache_events_o, + input logic icache_prefetch_enable_i, + input logic [NrCores-1:0] cl_interrupt_i, + input logic [$clog2(AxiAddrWidth)-1:0] dynamic_offset_i, + input cache_insn_t l1d_insn_i, + input logic [$clog2(NumL1CtrlTile):0] l1d_private_i, + input logic l1d_insn_valid_i, + output logic l1d_insn_ready_o, + input logic l1d_busy_i, + // Direct-wire barrier interface (bypasses NoC) + output logic barrier_o, + input logic barrier_done_i, + /// SRAM Configuration Ports, usually not used. input impl_in_t [NrSramCfg-1:0] impl_i, /// Indicate the program execution is error @@ -188,9 +193,8 @@ module cachepool_tile // Constants // --------- // TODO: Should be imported from Memory-mapped Reg - logic [2:0] num_private_cache; - // half-half - assign num_private_cache = l1d_private_i[2:0]; + logic [$clog2(NumL1CtrlTile):0] num_private_cache; + assign num_private_cache = l1d_private_i [$clog2(NumL1CtrlTile):0]; /// Minimum width to hold the core number. // localparam int unsigned CoreIDWidth = cf_math_pkg::idx_width(NrCores); @@ -215,59 +219,15 @@ module cachepool_tile localparam int unsigned NumTCDMIn = NrTCDMPortsCores + 1; localparam logic [AxiAddrWidth-1:0] TCDMMask = ~(TCDMSize-1); - // Core Request, SoC Request - localparam int unsigned NrNarrowMasters = 1; - - // Narrow AXI network parameters + // Narrow data path parameters (core reqrsp → upsizer) localparam int unsigned NarrowIdWidthIn = AxiIdWidthIn; - localparam int unsigned NarrowIdWidthOut = NarrowIdWidthIn + $clog2(NrNarrowMasters); + localparam int unsigned NarrowIdWidthOut = NarrowIdWidthIn; localparam int unsigned NarrowDataWidth = ELEN; localparam int unsigned NarrowUserWidth = AxiUserWidth; - // Peripherals, SoC Request, UART - localparam int unsigned NrNarrowSlaves = 3; - localparam int unsigned NrNarrowRules = NrNarrowSlaves - 1; - - // Core Request, Instruction cache - localparam int unsigned NrWideMasters = 2; + // ICache AXI output: no xbar needed, single master direct to port localparam int unsigned WideIdWidthOut = AxiIdWidthOut; - localparam int unsigned WideIdWidthIn = AxiIdWidthOut - $clog2(NrWideMasters); - // Wide X-BAR configuration: Core Request, ICache - localparam int unsigned NrWideSlaves = 2; - - // AXI Configuration - localparam axi_pkg::xbar_cfg_t ClusterXbarCfg = '{ - NoSlvPorts : NrNarrowMasters, - NoMstPorts : NrNarrowSlaves, - MaxMstTrans : MaxMstTrans, - MaxSlvTrans : MaxSlvTrans, - FallThrough : 1'b0, - LatencyMode : XbarLatency, - AxiIdWidthSlvPorts: NarrowIdWidthIn, - AxiIdUsedSlvPorts : NarrowIdWidthIn, - UniqueIds : 1'b0, - AxiAddrWidth : AxiAddrWidth, - AxiDataWidth : NarrowDataWidth, - NoAddrRules : NrNarrowRules, - default : '0 - }; - - // DMA configuration struct - localparam axi_pkg::xbar_cfg_t WideXbarCfg = '{ - NoSlvPorts : NrWideMasters, - NoMstPorts : NrWideSlaves, - MaxMstTrans : MaxMstTrans, - MaxSlvTrans : MaxSlvTrans, - FallThrough : 1'b0, - LatencyMode : XbarLatency, - AxiIdWidthSlvPorts: WideIdWidthIn, - AxiIdUsedSlvPorts : WideIdWidthIn, - UniqueIds : 1'b0, - AxiAddrWidth : AxiAddrWidth, - AxiDataWidth : AxiDataWidth, - NoAddrRules : NrWideSlaves - 1, - default : '0 - }; + localparam int unsigned WideIdWidthIn = AxiIdWidthOut; // -------- // Typedefs @@ -300,21 +260,9 @@ module cachepool_tile `AXI_TYPEDEF_ALL(axi_mst, addr_t, id_mst_t, data_t, strb_t, user_t) `AXI_TYPEDEF_ALL(axi_slv, addr_t, id_slv_t, data_t, strb_t, user_t) `AXI_TYPEDEF_ALL(axi_mst_tile_wide, addr_t, id_wide_mst_t, data_wide_t, strb_wide_t, user_wide_t) - `AXI_TYPEDEF_ALL(axi_slv_tile_wide, addr_t, id_wide_slv_t, data_wide_t, strb_wide_t, user_wide_t) `REQRSP_TYPEDEF_ALL(reqrsp, addr_t, data_t, strb_t, tcdm_user_t) - `MEM_TYPEDEF_ALL(mem, tcdm_mem_addr_t, data_t, strb_t, tcdm_user_t) - - `REG_BUS_TYPEDEF_ALL(reg, addr_t, data_t, strb_t) - - - typedef struct packed { - int unsigned idx; - addr_t start_addr; - addr_t end_addr; - } xbar_rule_t; - typedef struct packed { acc_addr_e addr; logic [5:0] id; @@ -387,17 +335,9 @@ module cachepool_tile // ---------------- // Wire Definitions // ---------------- - // 1. AXI - axi_slv_req_t [NrNarrowSlaves-1:0] narrow_axi_slv_req; - axi_slv_resp_t [NrNarrowSlaves-1:0] narrow_axi_slv_rsp; - axi_mst_req_t [NrNarrowMasters-1:0] narrow_axi_mst_req; - axi_mst_resp_t [NrNarrowMasters-1:0] narrow_axi_mst_rsp; - - // DMA AXI buses - axi_mst_tile_wide_req_t [NrWideMasters-1:0] wide_axi_mst_req; - axi_mst_tile_wide_resp_t [NrWideMasters-1:0] wide_axi_mst_rsp; - axi_slv_tile_wide_req_t [NrWideSlaves-1 :0] wide_axi_slv_req; - axi_slv_tile_wide_resp_t [NrWideSlaves-1 :0] wide_axi_slv_rsp; + // iCache AXI output (512b, direct to port, no xbar) + axi_mst_tile_wide_req_t icache_axi_req; + axi_mst_tile_wide_resp_t icache_axi_rsp; // 3. Memory Subsystem (Interconnect) tcdm_req_t [NrTCDMPortsCores-1:0] tcdm_req; @@ -405,7 +345,7 @@ module cachepool_tile core_events_t [NrCores-1:0] core_events; - snitch_icache_pkg::icache_events_t [NrCores-1:0] icache_events; + // snitch_icache_pkg::icache_events_t [NrCores-1:0] icache_events; // 4. Memory Subsystem (Core side). reqrsp_req_t [NrCores-1:0] core_req, filtered_core_req; @@ -418,6 +358,12 @@ module cachepool_tile tcdm_req_t [NrTCDMPortsPerCore-1:0][NumL1CtrlTile-1:0] cache_req, cache_xbar_req; tcdm_rsp_t [NrTCDMPortsPerCore-1:0][NumL1CtrlTile-1:0] cache_rsp, cache_xbar_rsp; + // Post-xbar gated copies. + // cache_ctrl_req : xbar output with q_valid suppressed during flush. + // cache_bank_rsp : raw response from the bank/AMO stage; q_ready is gated before + // being returned to the interco as cache_xbar_rsp. + tcdm_req_t [NrTCDMPortsPerCore-1:0][NumL1CtrlTile-1:0] cache_ctrl_req; + tcdm_rsp_t [NrTCDMPortsPerCore-1:0][NumL1CtrlTile-1:0] cache_bank_rsp; tcdm_req_t [NumL1CtrlTile-1:0] cache_amo_req; tcdm_rsp_t [NumL1CtrlTile-1:0] cache_amo_rsp; @@ -458,56 +404,12 @@ module cachepool_tile assign error_o = 1'b0; - // ------------- - // DMA Subsystem - // ------------- - // Optionally decouple the external wide AXI master port. - - assign axi_wide_req_o[TileMem] = wide_axi_slv_req[SoCDMAOut]; - assign wide_axi_slv_rsp[SoCDMAOut] = axi_wide_rsp_i[TileMem]; - - logic [WideXbarCfg.NoSlvPorts-1:0][$clog2(WideXbarCfg.NoMstPorts)-1:0] dma_xbar_default_port; - xbar_rule_t [WideXbarCfg.NoAddrRules-1:0] dma_xbar_rule; - - assign dma_xbar_default_port = '{default: SoCDMAOut}; - assign dma_xbar_rule = '{ - '{ - idx : BootROM, - start_addr: BootAddr, - end_addr : BootAddr + 'h1000 - } - }; - - localparam bit [WideXbarCfg.NoSlvPorts-1:0] DMAEnableDefaultMstPort = '1; - axi_xbar #( - .Cfg (WideXbarCfg ), - .ATOPs (0 ), - .slv_aw_chan_t (axi_mst_tile_wide_aw_chan_t), - .mst_aw_chan_t (axi_slv_tile_wide_aw_chan_t), - .w_chan_t (axi_mst_tile_wide_w_chan_t ), - .slv_b_chan_t (axi_mst_tile_wide_b_chan_t ), - .mst_b_chan_t (axi_slv_tile_wide_b_chan_t ), - .slv_ar_chan_t (axi_mst_tile_wide_ar_chan_t), - .mst_ar_chan_t (axi_slv_tile_wide_ar_chan_t), - .slv_r_chan_t (axi_mst_tile_wide_r_chan_t ), - .mst_r_chan_t (axi_slv_tile_wide_r_chan_t ), - .slv_req_t (axi_mst_tile_wide_req_t ), - .slv_resp_t (axi_mst_tile_wide_resp_t ), - .mst_req_t (axi_slv_tile_wide_req_t ), - .mst_resp_t (axi_slv_tile_wide_resp_t ), - .rule_t (xbar_rule_t ) - ) i_axi_wide_xbar ( - .clk_i (clk_i ), - .rst_ni (rst_ni ), - .test_i (1'b0 ), - .slv_ports_req_i (wide_axi_mst_req ), - .slv_ports_resp_o (wide_axi_mst_rsp ), - .mst_ports_req_o (wide_axi_slv_req ), - .mst_ports_resp_i (wide_axi_slv_rsp ), - .addr_map_i (dma_xbar_rule ), - .en_default_mst_port_i (DMAEnableDefaultMstPort), - .default_mst_port_i (dma_xbar_default_port ) - ); + // ----------------------- + // iCache AXI Direct Wire + // ----------------------- + // No xbar needed: iCache is the sole wide AXI master, directly wired to output port. + assign axi_wide_req_o[0] = axi_out_req_t'(icache_axi_req); + assign icache_axi_rsp = axi_mst_tile_wide_resp_t'(axi_wide_rsp_i[0]); logic [NrTCDMPortsCores-1:0] unmerge_pready; @@ -517,16 +419,14 @@ module cachepool_tile always_comb begin : cache_flush_protection for (int j = 0; unsigned'(j) < NrTCDMPortsCores; j++) begin /***** REQ *****/ - // Wire to Cache outputs unmerge_req[j].q = tcdm_req[j].q; - // invalidate the request when cache is busy - unmerge_req[j].q_valid = tcdm_req[j].q_valid && !l1d_busy_i; + unmerge_req[j].q_valid = tcdm_req[j].q_valid; unmerge_pready[j] = 1'b1; /***** RSP *****/ tcdm_rsp[j].p = unmerge_rsp[j].p; tcdm_rsp[j].p_valid = unmerge_rsp[j].p_valid; - tcdm_rsp[j].q_ready = unmerge_rsp[j].q_ready && !l1d_busy_i; + tcdm_rsp[j].q_ready = unmerge_rsp[j].q_ready; end end @@ -545,40 +445,32 @@ module cachepool_tile // Used to determine the mapping policy between different cache banks. // Set through CSR - logic [$clog2(TCDMAddrWidth)-1:0] dynamic_offset; + logic [$clog2(AxiAddrWidth)-1:0] dynamic_offset; assign dynamic_offset = dynamic_offset_i; // One entry per flat remote port: flat index = j + r*NrTCDMPortsPerCore // where j is the xbar index and r is the remote slot within that xbar. - logic [NumRemotePortTile-1:0] remote_out_pready, remote_in_pready; + logic [NumLGPortTile-1:0] remote_out_pready, remote_in_pready; - // Flush protection for remote ports. - // - // During a flush (l1d_busy_i) remote tiles must be fully stalled: - // - q_valid gated : stops new requests being presented to the xbar - // - q_ready gated : stops the xbar accepting a request that is already - // sitting at the input (spill register would otherwise - // pop it, and the transaction would be lost because the - // cache is unavailable) - // - remote_in_pready gated : stops response-ready from propagating back, - // preventing in-flight completions during the flush window - - tcdm_req_t [NumRemotePortTile-1:0] remote_req_gated; - // Intermediate response signals from the xbar before q_ready gating. - tcdm_rsp_t [NumRemotePortTile-1:0] remote_rsp_xbar; + // Intra-group remote port wiring. + // q_valid and q_ready for incoming requests are passed through without gating: + // the after-xbar flush gate (cache_xbar_flush_gate) provides the authoritative + // protection at the cache bank boundary and naturally back-pressures through + // the interco to the remote sender. + // response-ready (remote_in_pready) is still gated to prevent draining in-flight + // completions during the flush window. + + tcdm_req_t [NumLGPortTile-1:0] remote_req_gated; + tcdm_rsp_t [NumLGPortTile-1:0] remote_rsp_xbar; always_comb begin : remote_flush_protection for (int j = 0; j < NrTCDMPortsPerCore; j++) begin - for (int r = 0; r < NumRemotePortCore; r++) begin + for (int r = 0; r < NumLGPortCore; r++) begin automatic int unsigned flat = j + r * NrTCDMPortsPerCore; - // Gate q_valid: prevent new requests entering the xbar. remote_req_gated[flat].q = remote_req_i[flat].q; - remote_req_gated[flat].q_valid = remote_req_i[flat].q_valid && !l1d_busy_i; + remote_req_gated[flat].q_valid = remote_req_i[flat].q_valid; - // Pass the full xbar response through, then gate only q_ready so the - // remote tile cannot complete a handshake during a flush. remote_rsp_o[flat] = remote_rsp_xbar[flat]; - remote_rsp_o[flat].q_ready = remote_rsp_xbar[flat].q_ready && !l1d_busy_i; // Gate response-ready back to us: prevent draining completions // of requests that arrived just before the flush. @@ -589,20 +481,161 @@ module cachepool_tile assign remote_rsp_ready_o = remote_out_pready; + // ------------------------------------------------------------------------- + // Inter-group remote ports – type conversion and flush protection + // ------------------------------------------------------------------------- + // External ports use REQRSP-style remote_group_req_t / remote_group_rsp_t + // (with built-in ready and remote_group_user_t). + // Internal interco uses TCDM-style tcdm_req_t / tcdm_rsp_t. + // This section bridges the two and applies flush gating. + // + // Same flat layout as remote ports: flat = j + r * NrTCDMPortsPerCore. + // Total count: NumRemoteGroupPortCore * NrTCDMPortsPerCore. + + localparam int unsigned NumRemoteGroupPortTile = NumRemoteGroupPortCore * NrTCDMPortsPerCore; + + // Internal TCDM-style signals going to/from the interco. + tcdm_req_t [NumRemoteGroupPortTile-1:0] rg_interco_in_req; // incoming requests to interco + tcdm_rsp_t [NumRemoteGroupPortTile-1:0] rg_interco_in_rsp; // responses from interco (for incoming) + logic [NumRemoteGroupPortTile-1:0] rg_interco_in_pready; // response ready for incoming + + tcdm_req_t [NumRemoteGroupPortTile-1:0] rg_interco_out_req; // outgoing requests from interco + tcdm_rsp_t [NumRemoteGroupPortTile-1:0] rg_interco_out_rsp; // responses returning (for outgoing) + logic [NumRemoteGroupPortTile-1:0] rg_interco_out_pready;// response ready for outgoing + remote_tile_sel_t [NumRemoteGroupPortTile-1:0] rg_interco_out_dst; // target tile from interco + + if (NumRemoteGroupPortCore > 0) begin : gen_remote_group_ports + always_comb begin + for (int j = 0; j < NrTCDMPortsPerCore; j++) begin + for (int r = 0; r < NumRemoteGroupPortCore; r++) begin + automatic int unsigned flat = j + r * NrTCDMPortsPerCore; + + // ----------------------------------------------------------- + // Incoming: REQRSP → TCDM conversion → interco + // q_valid and q_ready are passed through without gating; the + // after-xbar flush gate (cache_xbar_flush_gate) is the authoritative + // protection point and naturally back-pressures through the interco. + // ----------------------------------------------------------- + rg_interco_in_req[flat] = '{ + q: '{ + addr: remote_group_req_i[flat].q.addr, + write: remote_group_req_i[flat].q.write, + data: remote_group_req_i[flat].q.data, + strb: remote_group_req_i[flat].q.strb, + amo: remote_group_req_i[flat].q.amo, + user: '{ + core_id: remote_group_req_i[flat].q.user.core_id, + tile_id: remote_group_req_i[flat].q.user.tile_id, + req_id: remote_group_req_i[flat].q.user.req_id, + is_fpu: remote_group_req_i[flat].q.user.is_fpu, + default: '0 + }, + default: '0 + }, + q_valid: remote_group_req_i[flat].q_valid, + default: '0 + }; + + // Interco response (TCDM) → REQRSP for remote_group_rsp_o. + remote_group_rsp_o[flat] = '{ + p: '{ + data: rg_interco_in_rsp[flat].p.data, + write: rg_interco_in_rsp[flat].p.write, + user: '{ + core_id: rg_interco_in_rsp[flat].p.user.core_id, + tile_id: rg_interco_in_rsp[flat].p.user.tile_id, + req_id: rg_interco_in_rsp[flat].p.user.req_id, + is_fpu: rg_interco_in_rsp[flat].p.user.is_fpu, + port_id: portid_t'(j), + default: '0 + }, + default: '0 + }, + p_valid: rg_interco_in_rsp[flat].p_valid, + q_ready: rg_interco_in_rsp[flat].q_ready, + default: '0 + }; + + // Response ready from the external port (REQRSP p_ready). + rg_interco_in_pready[flat] = remote_group_req_i[flat].p_ready && !l1d_busy_i; + + // ----------------------------------------------------------- + // Outgoing: interco → flush gating → TCDM to REQRSP → output + // ----------------------------------------------------------- + remote_group_req_o[flat] = '{ + q: '{ + addr: rg_interco_out_req[flat].q.addr, + write: rg_interco_out_req[flat].q.write, + data: rg_interco_out_req[flat].q.data, + strb: rg_interco_out_req[flat].q.strb, + amo: rg_interco_out_req[flat].q.amo, + user: '{ + core_id: rg_interco_out_req[flat].q.user.core_id, + tile_id: rg_interco_out_req[flat].q.user.tile_id, + req_id: rg_interco_out_req[flat].q.user.req_id, + is_fpu: rg_interco_out_req[flat].q.user.is_fpu, + port_id: portid_t'(j), + dst_tile_id: rg_interco_out_dst[flat], + default: '0 + }, + default: '0 + }, + q_valid: rg_interco_out_req[flat].q_valid && !l1d_busy_i, + p_ready: rg_interco_out_pready[flat] && !l1d_busy_i, + default: '0 + }; + + // Returning response (REQRSP) → TCDM for the interco. + rg_interco_out_rsp[flat] = '{ + p: '{ + data: remote_group_rsp_i[flat].p.data, + write: remote_group_rsp_i[flat].p.write, + user: '{ + core_id: remote_group_rsp_i[flat].p.user.core_id, + tile_id: remote_group_rsp_i[flat].p.user.tile_id, + req_id: remote_group_rsp_i[flat].p.user.req_id, + is_fpu: remote_group_rsp_i[flat].p.user.is_fpu, + default: '0 + }, + default: '0 + }, + p_valid: remote_group_rsp_i[flat].p_valid, + q_ready: remote_group_rsp_i[flat].q_ready, + default: '0 + }; + end + end + end + + end else begin : gen_remote_group_no_ports + // No inter-group remote ports: tie off outputs. + assign remote_group_rsp_o = '0; + assign remote_group_req_o = '0; + assign rg_interco_in_req = '0; + assign rg_interco_in_pready = '0; + assign rg_interco_out_rsp = '0; + assign rg_interco_out_pready = '0; + assign rg_interco_in_rsp = '0; + assign rg_interco_out_req = '0; + assign rg_interco_out_dst = '0; + end + /// Wire requests after strb handling to the cache controller. - /// Each xbar j handles NumRemotePortCore remote slots at flat indices - /// j + r*NrTCDMPortsPerCore for r in [0, NumRemotePortCore). + /// Each xbar j handles NumLGPortCore remote slots at flat indices + /// j + r*NrTCDMPortsPerCore for r in [0, NumLGPortCore). + /// Similarly, each xbar j handles NumRemoteGroupPortCore inter-group remote slots at flat indices + /// j + r*NrTCDMPortsPerCore for r in [0, NumRemoteGroupPortCore). for (genvar j = 0; j < NrTCDMPortsPerCore; j++) begin : gen_cache_xbar - // Collect the NumRemotePortCore remote slots for this xbar. - tcdm_req_t [NumRemotePortCore-1:0] xbar_remote_req_gated; - tcdm_rsp_t [NumRemotePortCore-1:0] xbar_remote_rsp_xbar; - logic [NumRemotePortCore-1:0] xbar_remote_in_pready; - logic [NumRemotePortCore-1:0] xbar_remote_out_pready; - tcdm_rsp_t [NumRemotePortCore-1:0] xbar_remote_rsp_i; - remote_tile_sel_t [NumRemotePortCore-1:0] xbar_remote_req_dst; - tcdm_req_t [NumRemotePortCore-1:0] xbar_remote_req_o; - - for (genvar r = 0; r < NumRemotePortCore; r++) begin : gen_remote_slice + // Collect the NumLGPortCore remote slots for this xbar. + tcdm_req_t [NumLGPortCore-1:0] xbar_remote_req_gated; + tcdm_rsp_t [NumLGPortCore-1:0] xbar_remote_rsp_xbar; + logic [NumLGPortCore-1:0] xbar_remote_in_pready; + logic [NumLGPortCore-1:0] xbar_remote_out_pready; + tcdm_rsp_t [NumLGPortCore-1:0] xbar_remote_rsp_i; + remote_tile_sel_t [NumLGPortCore-1:0] xbar_remote_req_dst; + tcdm_req_t [NumLGPortCore-1:0] xbar_remote_req_o; + + for (genvar r = 0; r < NumLGPortCore; r++) begin : gen_remote_slice localparam int unsigned flat = j + r * NrTCDMPortsPerCore; assign xbar_remote_req_gated [r] = remote_req_gated [flat]; assign xbar_remote_in_pready [r] = remote_in_pready [flat]; @@ -613,33 +646,92 @@ module cachepool_tile assign remote_req_o [flat] = xbar_remote_req_o [r]; end - tcdm_cache_interco #( - .NumTiles (NumTiles ), - .NumCores (NrCores ), - .NumCache (NumL1CtrlTile ), - .NumTotCache (NumL1CacheCtrl ), - .NumRemotePort (NumRemotePortCore ), - .AddrWidth (TCDMAddrWidth ), - .TileIDWidth (TileIDWidth ), - .tcdm_req_t (tcdm_req_t ), - .tcdm_rsp_t (tcdm_rsp_t ), - .tcdm_req_chan_t (tcdm_req_chan_t ), - .tcdm_rsp_chan_t (tcdm_rsp_chan_t ) - ) i_cache_xbar ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .tile_id_i ( tile_id_i ), - .dynamic_offset_i ( dynamic_offset ), - .private_start_addr_i ( private_start_addr_i ), - .num_private_cache_i ( num_private_cache ), - .core_req_i ({xbar_remote_req_gated, cache_req [j]} ), - .core_rsp_ready_i ({xbar_remote_in_pready, cache_pready [j]} ), - .core_rsp_o ({xbar_remote_rsp_xbar, cache_rsp [j]} ), - .tile_sel_o ( xbar_remote_req_dst ), - .mem_req_o ({xbar_remote_req_o, cache_xbar_req [j]} ), - .mem_rsp_ready_o ({xbar_remote_out_pready, cache_xbar_pready[j]} ), - .mem_rsp_i ({xbar_remote_rsp_i, cache_xbar_rsp [j]} ) - ); + // Collect the NumRemoteGroupPortCore inter-group remote slots for this xbar (same flat layout). + // When NumRemoteGroupPortCore == 0, no inter-group remote signals exist and the interco is + // instantiated without inter-group remote ports (backward-compatible). + if (NumRemoteGroupPortCore > 0) begin : gen_remote_group_slice + tcdm_req_t [NumRemoteGroupPortCore-1:0] xbar_remote_group_in_req; + tcdm_rsp_t [NumRemoteGroupPortCore-1:0] xbar_remote_group_in_rsp; + logic [NumRemoteGroupPortCore-1:0] xbar_remote_group_in_pready; + tcdm_req_t [NumRemoteGroupPortCore-1:0] xbar_remote_group_out_req; + tcdm_rsp_t [NumRemoteGroupPortCore-1:0] xbar_remote_group_out_rsp; + logic [NumRemoteGroupPortCore-1:0] xbar_remote_group_out_pready; + remote_tile_sel_t [NumRemoteGroupPortCore-1:0] xbar_remote_group_out_dst; + + for (genvar r = 0; r < NumRemoteGroupPortCore; r++) begin : gen_remote_group_slice_r + localparam int unsigned flat = j + r * NrTCDMPortsPerCore; + // Incoming: from conversion/flush → interco input + assign xbar_remote_group_in_req [r] = rg_interco_in_req [flat]; + assign xbar_remote_group_in_pready [r] = rg_interco_in_pready [flat]; + assign rg_interco_in_rsp [flat] = xbar_remote_group_in_rsp [r]; + // Outgoing: interco output → conversion/flush + assign rg_interco_out_req [flat] = xbar_remote_group_out_req [r]; + assign rg_interco_out_dst [flat] = xbar_remote_group_out_dst [r]; + assign xbar_remote_group_out_rsp [r] = rg_interco_out_rsp [flat]; + assign rg_interco_out_pready [flat] = xbar_remote_group_out_pready[r]; + end + + tcdm_cache_interco #( + .NumTiles (NumTiles ), + .NumCores (NrCores ), + .NumCache (NumL1CtrlTile ), + .NumTotCache (NumL1CacheCtrl ), + .NumLGPort (NumLGPortCore ), + .NumRemoteGroupPort (NumRemoteGroupPortCore ), + .NumTilesPerGroup (NumTilesPerGroup ), + .AddrWidth (TCDMAddrWidth ), + .TileIDWidth (TileIDWidth ), + .tcdm_req_t (tcdm_req_t ), + .tcdm_rsp_t (tcdm_rsp_t ), + .tcdm_req_chan_t (tcdm_req_chan_t ), + .tcdm_rsp_chan_t (tcdm_rsp_chan_t ) + ) i_cache_xbar ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .tile_id_i ( tile_id_i ), + .dynamic_offset_i ( dynamic_offset ), + .private_start_addr_i ( private_start_addr_i ), + .num_private_cache_i ( num_private_cache ), + .core_req_i ({xbar_remote_group_in_req, xbar_remote_req_gated, cache_req [j]}), + .core_rsp_ready_i ({xbar_remote_group_in_pready, xbar_remote_in_pready, cache_pready [j]}), + .core_rsp_o ({xbar_remote_group_in_rsp, xbar_remote_rsp_xbar, cache_rsp [j]}), + .tile_sel_o ( xbar_remote_req_dst ), + .remote_group_sel_o ( xbar_remote_group_out_dst ), + .mem_req_o ({xbar_remote_group_out_req, xbar_remote_req_o, cache_xbar_req [j]}), + .mem_rsp_ready_o ({xbar_remote_group_out_pready, xbar_remote_out_pready, cache_xbar_pready[j]}), + .mem_rsp_i ({xbar_remote_group_out_rsp, xbar_remote_rsp_i, cache_xbar_rsp [j]}) + ); + end else begin : gen_no_remote_group + // No inter-group remote ports: instantiate interco without inter-group remote ports (backward-compatible). + tcdm_cache_interco #( + .NumTiles (NumTiles ), + .NumCores (NrCores ), + .NumCache (NumL1CtrlTile ), + .NumTotCache (NumL1CacheCtrl ), + .NumLGPort (NumLGPortCore ), + .AddrWidth (TCDMAddrWidth ), + .TileIDWidth (TileIDWidth ), + .tcdm_req_t (tcdm_req_t ), + .tcdm_rsp_t (tcdm_rsp_t ), + .tcdm_req_chan_t (tcdm_req_chan_t ), + .tcdm_rsp_chan_t (tcdm_rsp_chan_t ) + ) i_cache_xbar ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .tile_id_i ( tile_id_i ), + .dynamic_offset_i ( dynamic_offset ), + .private_start_addr_i ( private_start_addr_i ), + .num_private_cache_i ( num_private_cache ), + .core_req_i ({xbar_remote_req_gated, cache_req [j]} ), + .core_rsp_ready_i ({xbar_remote_in_pready, cache_pready [j]} ), + .core_rsp_o ({xbar_remote_rsp_xbar, cache_rsp [j]} ), + .tile_sel_o ( xbar_remote_req_dst ), + .remote_group_sel_o ( ), + .mem_req_o ({xbar_remote_req_o, cache_xbar_req [j]}), + .mem_rsp_ready_o ({xbar_remote_out_pready, cache_xbar_pready[j]}), + .mem_rsp_i ({xbar_remote_rsp_i, cache_xbar_rsp [j]}) + ); + end end for (genvar cb = 0; cb < NumL1CtrlTile; cb++) begin : gen_cache_connect @@ -659,9 +751,9 @@ module cachepool_tile ) i_cache_amo ( .clk_i (clk_i ), .rst_ni (rst_ni ), - .core_req_i (cache_xbar_req [j][cb] ), + .core_req_i (cache_ctrl_req [j][cb] ), .core_rsp_ready_i (cache_xbar_pready[j][cb] ), - .core_rsp_o (cache_xbar_rsp [j][cb] ), + .core_rsp_o (cache_bank_rsp [j][cb] ), .mem_req_o (cache_amo_req [cb] ), .mem_rsp_ready_o (cache_amo_pready [cb] ), .mem_rsp_i (cache_amo_rsp [cb] ) @@ -713,22 +805,67 @@ module cachepool_tile assign cache_rsp_reg.p.write = cache_rsp_write[cb][j]; end else begin : gen_no_amo - // Bypass AMO and registers - assign cache_req_valid[cb][j] = cache_xbar_req [j][cb].q_valid; - assign cache_rsp_ready[cb][j] = cache_xbar_pready[j][cb]; - assign cache_req_addr [cb][j] = cache_xbar_req [j][cb].q.addr; - assign cache_req_meta [cb][j] = cache_xbar_req [j][cb].q.user; - assign cache_req_write[cb][j] = cache_xbar_req [j][cb].q.write; - assign cache_req_data [cb][j] = cache_xbar_req [j][cb].q.data; - assign cache_req_strb [cb][j] = cache_xbar_req [j][cb].q.strb; - - assign cache_xbar_rsp[j][cb].p_valid = cache_rsp_valid[cb][j]; - assign cache_xbar_rsp[j][cb].q_ready = cache_req_ready[cb][j]; - assign cache_xbar_rsp[j][cb].p.data = cache_rsp_data [cb][j]; - assign cache_xbar_rsp[j][cb].p.user = cache_rsp_meta [cb][j]; - - assign cache_xbar_rsp[j][cb].p.write = cache_rsp_write[cb][j]; + // Spill registers to cut the L1 xbar → coalescer critical path, + // matching the timing budget of the Snitch AMO path above. + tcdm_req_t cache_req_reg; + tcdm_rsp_t cache_rsp_reg; + spill_register #( + .T ( tcdm_req_chan_t ), + .Bypass ( 1'b0 ) + ) i_spill_reg_cache_req ( + .clk_i , + .rst_ni ( rst_ni ), + .valid_i ( cache_ctrl_req[j][cb].q_valid ), + .ready_o ( cache_bank_rsp[j][cb].q_ready ), + .data_i ( cache_ctrl_req[j][cb].q ), + .valid_o ( cache_req_reg.q_valid ), + .ready_i ( cache_rsp_reg.q_ready ), + .data_o ( cache_req_reg.q ) + ); + + spill_register #( + .T ( tcdm_rsp_chan_t ), + .Bypass ( 1'b1 ) + ) i_spill_reg_cache_rsp ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .valid_i ( cache_rsp_reg.p_valid ), + .ready_o ( cache_rsp_ready[cb][j] ), + .data_i ( cache_rsp_reg.p ), + .valid_o ( cache_bank_rsp[j][cb].p_valid ), + .ready_i ( cache_xbar_pready[j][cb] ), + .data_o ( cache_bank_rsp[j][cb].p ) + ); + + assign cache_req_valid[cb][j] = cache_req_reg.q_valid; + assign cache_req_addr [cb][j] = cache_req_reg.q.addr; + assign cache_req_meta [cb][j] = cache_req_reg.q.user; + assign cache_req_write[cb][j] = cache_req_reg.q.write; + assign cache_req_data [cb][j] = cache_req_reg.q.data; + assign cache_req_strb [cb][j] = cache_req_reg.q.strb; + + assign cache_rsp_reg.p_valid = cache_rsp_valid[cb][j]; + assign cache_rsp_reg.q_ready = cache_req_ready[cb][j]; + assign cache_rsp_reg.p.data = cache_rsp_data [cb][j]; + assign cache_rsp_reg.p.user = cache_rsp_meta [cb][j]; + assign cache_rsp_reg.p.write = cache_rsp_write[cb][j]; + + end + end + end + + // Post-xbar flush gate (applied uniformly across all ports). + // Suppresses q_valid going into the bank so no new cache accesses are processed + // while a flush is in progress, and gates q_ready going back to the interco so the + // xbar cannot dequeue a buffered request that is already sitting at its output. + always_comb begin : cache_xbar_flush_gate + for (int j = 0; j < NrTCDMPortsPerCore; j++) begin + for (int cb = 0; cb < NumL1CtrlTile; cb++) begin + cache_ctrl_req[j][cb] = cache_xbar_req[j][cb]; + cache_ctrl_req[j][cb].q_valid = cache_xbar_req[j][cb].q_valid && !l1d_busy_i; + cache_xbar_rsp[j][cb] = cache_bank_rsp[j][cb]; + cache_xbar_rsp[j][cb].q_ready = cache_bank_rsp[j][cb].q_ready && !l1d_busy_i; end end end @@ -745,6 +882,7 @@ module cachepool_tile localparam NumWordPerLine = L1LineWidth / DataWidth; localparam int unsigned WordBytes = DataWidth / 8; +`ifndef TARGET_SYNTHESIS initial begin $display("Cache Configuration:"); $display(" NumCtrl : %0d", NumL1CtrlTile); @@ -759,6 +897,7 @@ module cachepool_tile $display(" RefillDataWidth: %0d", RefillDataWidth); $display(" DynamicOffset : %0d", dynamic_offset); end +`endif // CL-offset mask: bits below dynamic_offset, verbatim in both directions. logic [SpatzAxiAddrWidth-1:0] bitmask_lo; @@ -885,6 +1024,7 @@ module cachepool_tile .CacheLineWidth (L1LineWidth ), .SetAssociativity (L1AssoPerCtrl ), .BankFactor (L1BankFactor ), + // .LogDebug (0 ), .RefillDataWidth (RefillDataWidth ), // Type .core_meta_t (tcdm_user_t ), @@ -1002,10 +1142,8 @@ module cachepool_tile default : '0 }; - // ID 0 reserved for bypass cache cache_refill_req_o[cb].q.user = '{ - // The first bit is reserved for iCache identifier - bank_id : cb + 1, + bank_id : cb, info : cache_refill_req[cb].info, burst : cache_refill_burst[cb], default : '0 @@ -1043,6 +1181,7 @@ module cachepool_tile end end + for (genvar j = 0; j < NumTagBankPerCtrl; j++) begin tc_sram_impl #( .NumWords (L1CacheWayEntry/L1BankFactor), @@ -1056,7 +1195,7 @@ module cachepool_tile .clk_i (clk_i ), .rst_ni (rst_ni ), .impl_i ('0 ), - .impl_o (/* unsed */ ), + .impl_o (/* unused */ ), .req_i (l1_tag_bank_req [cb][j]), .we_i (l1_tag_bank_we [cb][j]), .addr_i (l1_tag_bank_addr [cb][j]), @@ -1087,7 +1226,7 @@ module cachepool_tile .clk_i (clk_i ), .rst_ni (rst_ni ), .impl_i ('0 ), - .impl_o (/* unsed */ ), + .impl_o (/* unused */ ), .req_i ( l1_data_bank_req [cb][BaseIdx] ), .we_i ( l1_data_bank_we [cb][BaseIdx] ), .addr_i ( l1_data_bank_addr [cb][BaseIdx] ), @@ -1111,7 +1250,7 @@ module cachepool_tile // .clk_i (clk_i ), // .rst_ni (rst_ni ), // .impl_i ('0 ), - // .impl_o (/* unsed */ ), + // .impl_o (/* unused */ ), // .req_i (l1_data_bank_req [cb][j]), // .we_i (l1_data_bank_we [cb][j]), // .addr_i (l1_data_bank_addr [cb][j]), @@ -1134,13 +1273,13 @@ module cachepool_tile interrupts_t irq; sync #(.STAGES (2)) - i_sync_debug (.clk_i, .rst_ni, .serial_i (debug_req_i[i]), .serial_o (irq.debug)); + i_sync_debug (.clk_i, .rst_ni, .serial_i (debug_req_i), .serial_o (irq.debug)); sync #(.STAGES (2)) - i_sync_meip (.clk_i, .rst_ni, .serial_i (meip_i[i]), .serial_o (irq.meip)); + i_sync_meip (.clk_i, .rst_ni, .serial_i (meip_i), .serial_o (irq.meip)); sync #(.STAGES (2)) - i_sync_mtip (.clk_i, .rst_ni, .serial_i (mtip_i[i]), .serial_o (irq.mtip)); + i_sync_mtip (.clk_i, .rst_ni, .serial_i (mtip_i), .serial_o (irq.mtip)); sync #(.STAGES (2)) - i_sync_msip (.clk_i, .rst_ni, .serial_i (msip_i[i]), .serial_o (irq.msip)); + i_sync_msip (.clk_i, .rst_ni, .serial_i (msip_i), .serial_o (irq.msip)); assign irq.mcip = cl_interrupt_i[i]; tcdm_req_t [TcdmPorts-1:0] tcdm_req_wo_user; @@ -1155,15 +1294,10 @@ module cachepool_tile .RVF (RVF ), .RVD (RVD ), .RVV (RVV ), - .Xdma (Xdma[i] ), .AddrWidth (AxiAddrWidth ), .DataWidth (NarrowDataWidth ), .UserWidth (AxiUserWidth ), - .DMADataWidth (AxiDataWidth ), - .DMAIdWidth (AxiIdWidthIn ), .SnitchPMACfg (SnitchPMACfg ), - .DMAAxiReqFifoDepth (DMAAxiReqFifoDepth ), - .DMAReqFifoDepth (DMAReqFifoDepth ), .dreq_t (reqrsp_req_t ), .drsp_t (reqrsp_rsp_t ), .dreq_chan_t (reqrsp_req_chan_t ), @@ -1255,14 +1389,14 @@ module cachepool_tile .L0_LINE_COUNT ( 8 ), .LINE_WIDTH ( ICacheLineWidth ), .LINE_COUNT ( ICacheLineCount ), - .SET_COUNT ( ICacheSets ), + .WAY_COUNT ( ICacheSets ), .FETCH_AW ( AxiAddrWidth ), .FETCH_DW ( 32 ), .FILL_AW ( AxiAddrWidth ), .FILL_DW ( AxiDataWidth ), .EARLY_LATCH ( 0 ), .L0_EARLY_TAG_WIDTH ( snitch_pkg::PAGE_SHIFT - $clog2(ICacheLineWidth/8) ), - .ISO_CROSSING ( 1'b0 ), + .ISO_CROSSING ( 1'b1 ), .axi_req_t ( axi_mst_tile_wide_req_t ), .axi_rsp_t ( axi_mst_tile_wide_resp_t ), .sram_cfg_data_t ( impl_in_t ), @@ -1272,7 +1406,9 @@ module cachepool_tile .clk_d2_i ( clk_i ), .rst_ni ( rst_ni ), .enable_prefetching_i ( icache_prefetch_enable_i ), - .icache_events_o ( icache_events_o ), + .enable_branch_pred_i ( '0 ), + .icache_l0_events_o ( ), + .icache_l1_events_o ( ), .flush_valid_i ( flush_valid ), .flush_ready_o ( flush_ready ), .inst_addr_i ( inst_addr ), @@ -1283,8 +1419,10 @@ module cachepool_tile .inst_error_o ( inst_error ), .sram_cfg_tag_i ( '0 ), .sram_cfg_data_i ( '0 ), - .axi_req_o ( wide_axi_mst_req[ICache] ), - .axi_rsp_i ( wide_axi_mst_rsp[ICache] ) + .sram_cfg_out_data_o (), + .sram_cfg_out_tag_o (), + .axi_req_o ( icache_axi_req ), + .axi_rsp_i ( icache_axi_rsp ) ); // -------- @@ -1305,15 +1443,13 @@ module cachepool_tile .in_rsp_o (core_rsp ), .out_req_o (filtered_core_req ), .out_rsp_i (filtered_core_rsp ), + .barrier_o (barrier_o ), + .barrier_done_i (barrier_done_i ), .cluster_periph_start_address_i (cluster_periph_start_address) ); - reqrsp_req_t core_to_axi_req; - reqrsp_rsp_t core_to_axi_rsp; - user_t cluster_user; - // Atomic ID, needs to be unique ID of cluster - // cluster_id + HartIdOffset + 1 (because 0 is for non-atomic masters) - assign cluster_user = (hart_base_id_i / NrCores) + (hart_base_id_i % NrCores) + 1'b1; + reqrsp_req_t core_to_periph_req; + reqrsp_rsp_t core_to_periph_rsp; reqrsp_mux #( .NrPorts (NrCores ), @@ -1324,159 +1460,23 @@ module cachepool_tile .rsp_t (reqrsp_rsp_t ), .RespDepth (2 ) ) i_reqrsp_mux_core ( - .clk_i (clk_i ), - .rst_ni (rst_ni ), - .slv_req_i (filtered_core_req), - .slv_rsp_o (filtered_core_rsp), - .mst_req_o (core_to_axi_req ), - .mst_rsp_i (core_to_axi_rsp ), - .idx_o (/*unused*/ ) - ); - - reqrsp_to_axi #( - .DataWidth (NarrowDataWidth ), - .AxiUserWidth (NarrowUserWidth ), - .UserWidth ($bits(tcdm_user_t) ), - .ID ( 1 ), - .reqrsp_req_t (reqrsp_req_t ), - .reqrsp_rsp_t (reqrsp_rsp_t ), - .axi_req_t (axi_mst_req_t ), - .axi_rsp_t (axi_mst_resp_t ) - ) i_reqrsp_to_axi_core ( - .clk_i (clk_i ), - .rst_ni (rst_ni ), - .user_i (cluster_user ), - .reqrsp_req_i (core_to_axi_req ), - .reqrsp_rsp_o (core_to_axi_rsp ), - .axi_req_o (narrow_axi_mst_req[CoreReq]), - .axi_rsp_i (narrow_axi_mst_rsp[CoreReq]) - ); - - xbar_rule_t [NrNarrowRules-1:0] cluster_xbar_rules; - - assign cluster_xbar_rules = '{ - '{ - idx : ClusterPeripherals, - start_addr: cluster_periph_start_address, - end_addr : cluster_periph_end_address - }, - '{ - idx : UART, - start_addr: UartAddr, - end_addr : UartAddr + 32'h1000 - } - }; - - localparam bit [ClusterXbarCfg.NoSlvPorts-1:0] ClusterEnableDefaultMstPort = '1; - localparam logic [ClusterXbarCfg.NoSlvPorts-1:0][cf_math_pkg::idx_width(ClusterXbarCfg.NoMstPorts)-1:0] ClusterXbarDefaultPort = '{default: SoC}; - - axi_xbar #( - .Cfg (ClusterXbarCfg ), - .slv_aw_chan_t (axi_mst_aw_chan_t), - .mst_aw_chan_t (axi_slv_aw_chan_t), - .w_chan_t (axi_mst_w_chan_t ), - .slv_b_chan_t (axi_mst_b_chan_t ), - .mst_b_chan_t (axi_slv_b_chan_t ), - .slv_ar_chan_t (axi_mst_ar_chan_t), - .mst_ar_chan_t (axi_slv_ar_chan_t), - .slv_r_chan_t (axi_mst_r_chan_t ), - .mst_r_chan_t (axi_slv_r_chan_t ), - .slv_req_t (axi_mst_req_t ), - .slv_resp_t (axi_mst_resp_t ), - .mst_req_t (axi_slv_req_t ), - .mst_resp_t (axi_slv_resp_t ), - .rule_t (xbar_rule_t ) - ) i_axi_narrow_xbar ( - .clk_i (clk_i ), - .rst_ni (rst_ni ), - .test_i (1'b0 ), - .slv_ports_req_i (narrow_axi_mst_req ), - .slv_ports_resp_o (narrow_axi_mst_rsp ), - .mst_ports_req_o (narrow_axi_slv_req ), - .mst_ports_resp_i (narrow_axi_slv_rsp ), - .addr_map_i (cluster_xbar_rules ), - .en_default_mst_port_i (ClusterEnableDefaultMstPort), - .default_mst_port_i (ClusterXbarDefaultPort ) - ); - - // 3. BootROM - assign axi_wide_req_o[TileBootROM] = wide_axi_slv_req[BootROM]; - assign wide_axi_slv_rsp[BootROM] = axi_wide_rsp_i[TileBootROM]; - - // 4. UART - assign axi_out_req_o[0] = narrow_axi_slv_req[UART]; - assign narrow_axi_slv_rsp[UART] = axi_out_resp_i[0]; - - assign axi_out_req_o[1] = narrow_axi_slv_req[ClusterPeripherals]; - assign narrow_axi_slv_rsp[ClusterPeripherals] = axi_out_resp_i[1]; - - - // Upsize the narrow SoC connection - `AXI_TYPEDEF_ALL(axi_mst_core_narrow, addr_t, id_wide_mst_t, data_t, strb_t, user_t) - axi_mst_core_narrow_req_t narrow_axi_slv_req_soc; - axi_mst_core_narrow_resp_t narrow_axi_slv_resp_soc; - - axi_iw_converter #( - .AxiAddrWidth (AxiAddrWidth ), - .AxiDataWidth (NarrowDataWidth ), - .AxiUserWidth (AxiUserWidth ), - .AxiSlvPortIdWidth (NarrowIdWidthOut ), - .AxiSlvPortMaxUniqIds (1 ), - .AxiSlvPortMaxTxnsPerId(1 ), - .AxiSlvPortMaxTxns (1 ), - .AxiMstPortIdWidth (WideIdWidthIn ), - .AxiMstPortMaxUniqIds (1 ), - .AxiMstPortMaxTxnsPerId(1 ), - .slv_req_t (axi_slv_req_t ), - .slv_resp_t (axi_slv_resp_t ), - .mst_req_t (axi_mst_core_narrow_req_t ), - .mst_resp_t (axi_mst_core_narrow_resp_t) - ) i_soc_port_iw_convert ( - .clk_i (clk_i ), - .rst_ni (rst_ni ), - .slv_req_i (narrow_axi_slv_req[SoC] ), - .slv_resp_o (narrow_axi_slv_rsp[SoC] ), - .mst_req_o (narrow_axi_slv_req_soc ), - .mst_resp_i (narrow_axi_slv_resp_soc ) + .clk_i (clk_i ), + .rst_ni (rst_ni ), + .slv_req_i (filtered_core_req ), + .slv_rsp_o (filtered_core_rsp ), + .mst_req_o (core_to_periph_req ), + .mst_rsp_i (core_to_periph_rsp ), + .idx_o (/*unused*/ ) ); - // TODO: Do we need this data path? - // core will never use it as wide destination is only BootRom and main memory - axi_dw_converter #( - .AxiAddrWidth (AxiAddrWidth ), - .AxiIdWidth (WideIdWidthIn ), - .AxiMaxReads (2 ), - .AxiSlvPortDataWidth(NarrowDataWidth ), - .AxiMstPortDataWidth(AxiDataWidth ), - .ar_chan_t (axi_mst_tile_wide_ar_chan_t), - .aw_chan_t (axi_mst_tile_wide_aw_chan_t), - .b_chan_t (axi_mst_tile_wide_b_chan_t ), - .slv_r_chan_t (axi_mst_core_narrow_r_chan_t), - .slv_w_chan_t (axi_mst_core_narrow_b_chan_t), - .axi_slv_req_t (axi_mst_core_narrow_req_t ), - .axi_slv_resp_t (axi_mst_core_narrow_resp_t ), - .mst_r_chan_t (axi_mst_tile_wide_r_chan_t ), - .mst_w_chan_t (axi_mst_tile_wide_w_chan_t ), - .axi_mst_req_t (axi_mst_tile_wide_req_t ), - .axi_mst_resp_t (axi_mst_tile_wide_resp_t ) - ) i_soc_port_dw_upsize ( - .clk_i (clk_i ), - .rst_ni (rst_ni ), - .slv_req_i (narrow_axi_slv_req_soc ), - .slv_resp_o (narrow_axi_slv_resp_soc ), - .mst_req_o (wide_axi_mst_req[CoreReqWide]), - .mst_resp_i (wide_axi_mst_rsp[CoreReqWide]) - ); + // Peripheral REQRSP: directly exposed to group level (no AXI conversion) + assign periph_req_o = core_to_periph_req; + assign core_to_periph_rsp = periph_rsp_i; // ------------- // Sanity Checks // ------------- - // Sanity check the parameters. Not every configuration makes sense. - `ASSERT_INIT(CheckSuperBankSanity, NrBanks >= BanksPerSuperBank); - `ASSERT_INIT(CheckSuperBankFactor, (NrBanks % BanksPerSuperBank) == 0); // Check that the cluster base address aligns to the TCDMSize. `ASSERT(ClusterBaseAddrAlign, ((TCDMSize - 1) & cluster_base_addr_i) == 0) - // Make sure we only have one DMA in the system. - `ASSERT_INIT(NumberDMA, $onehot0(Xdma)) endmodule diff --git a/hardware/src/cachepool_tile_barrier.sv b/hardware/src/cachepool_tile_barrier.sv index 7dfd1e9..559b5e9 100644 --- a/hardware/src/cachepool_tile_barrier.sv +++ b/hardware/src/cachepool_tile_barrier.sv @@ -8,9 +8,12 @@ `include "common_cells/registers.svh" -/// Hardware barrier to synchronize all cores in a cluster. -/// Adapted from Spatz cluster's barrier to fit for a NUMA system -/// The barrier will notify the CSR for global sync +/// Hardware barrier to synchronize cores in a tile. +/// When all participating cores reach the barrier address, asserts `barrier_o`. +/// Waits for `barrier_done_i` from cluster level before releasing cores. +/// Supports partial barriers: a write to the barrier address carries a +/// per-core participant mask as its write data; a read is treated as the +/// legacy full-tile barrier (all cores participate). // This barrier is designed by halting the q_ready signal of barrier request // IMPORTANT: spill registers on the path to barrier may bring in unwanted behavior @@ -34,6 +37,10 @@ module cachepool_tile_barrier output dreq_t [NrPorts-1:0] out_req_o, input drsp_t [NrPorts-1:0] out_rsp_i, + // Direct-wire barrier interface (bypasses NoC) + output logic barrier_o, + input logic barrier_done_i, + input addr_t cluster_periph_start_address_i ); @@ -50,18 +57,73 @@ module cachepool_tile_barrier addr_t barrier_addr; assign barrier_addr = cluster_periph_start_address_i + CACHEPOOL_PERIPHERAL_HW_BARRIER_OFFSET; - logic [NrPorts-1:0] is_barrier, done_barrier; - logic take_barrier; + logic [NrPorts-1:0] is_barrier; logic local_barrier; + // barrier_o is registered: asserted when all local cores reach the barrier + logic barrier_d, barrier_q; + + // Partial-barrier participant mask, encoded in the barrier request itself: + // a write to barrier_addr carries the participant mask as its write data + // (one bit per local core port); a read (the legacy full-barrier access) + // is treated as "all ports participate". Whichever port's request is + // first to arrive in a round has its mask latched for that round. + typedef enum logic { + MaskIdle, + MaskActive + } mask_state_e; + + mask_state_e mask_state_d, mask_state_q; + logic [NrPorts-1:0] core_mask_d, core_mask_q; + logic [NrPorts-1:0] barrier_hit; + logic [NrPorts-1:0] req_mask [NrPorts]; + logic [$clog2(NrPorts)-1:0] first_hit_idx; + + always_comb begin + for (int i = 0; i < NrPorts; i++) begin + barrier_hit[i] = (state_q[i] == Idle) && in_req_i[i].q_valid + && (in_req_i[i].q.addr == barrier_addr); + req_mask[i] = in_req_i[i].q.write ? in_req_i[i].q.data[NrPorts-1:0] + : {NrPorts{1'b1}}; + end + end + + always_comb begin + first_hit_idx = '0; + for (int i = NrPorts-1; i >= 0; i--) begin + if (barrier_hit[i]) first_hit_idx = i[$clog2(NrPorts)-1:0]; + end + end + + always_comb begin + mask_state_d = mask_state_q; + core_mask_d = core_mask_q; + case (mask_state_q) + MaskIdle: begin + // A new round starts once some local core hits the barrier address. + if (|barrier_hit) begin + core_mask_d = req_mask[first_hit_idx]; + mask_state_d = MaskActive; + end + end + MaskActive: begin + // Round ends once the (masked) set of participants has all arrived. + if (local_barrier) mask_state_d = MaskIdle; + end + default: mask_state_d = MaskIdle; + endcase + end - assign local_barrier = &is_barrier; - assign take_barrier = &done_barrier; + // Masked barrier: only cores selected by core_mask_q must arrive. + // core_mask_q == '0 can only happen before any round has started + // (MaskIdle), so it can never trivially satisfy this comparison. + assign local_barrier = (is_barrier & core_mask_q) == core_mask_q; + assign barrier_o = barrier_q; always_comb begin state_d = state_q; user_d = user_q; + barrier_d = barrier_q; is_barrier = '0; - done_barrier = '0; out_req_o = in_req_i; in_rsp_o = out_rsp_i; @@ -72,7 +134,7 @@ module cachepool_tile_barrier // If we have a barrier request => start to wait for other cores if (in_req_i[i].q_valid && (in_req_i[i].q.addr == barrier_addr)) begin state_d[i] = Wait; - // we do not send the request to CSR + // Do not forward barrier request upstream out_req_o[i].q_valid = 0; in_rsp_o[i].q_ready = 0; end @@ -82,51 +144,26 @@ module cachepool_tile_barrier in_rsp_o[i].q_ready = 0; out_req_o[i].q_valid = 0; - // Pause the request to CSR until all cores have enter the barrier + // When all participating cores have reached the barrier, assert barrier_o and move to Global if (local_barrier) begin - if (i == 0) begin - // Port 0 will be used to sync with other tiles - // Sned out the barrier request - out_req_o[i].q_valid = 1; - if (out_rsp_i[i].q_ready) begin - // Global barrier accepted, waiting for response - state_d[i] = Global; - // out_req_o[i].q_valid = 0; - end - end else begin - // Other ports can directly enter Global state - state_d[i] = Global; - end + barrier_d = 1'b1; + state_d[i] = Global; end end Global: begin - // The local barrier of the tile has complete - // Waiting for CSR to grant the global one => configurable in CSR for barrier size - // We will now send **ONE** request to CSR for cleaerance - - // Do not release the barrier yet + // All local cores are at the barrier. + // Wait for cluster-level barrier_done_i signal. in_rsp_o[i].q_ready = 0; out_req_o[i].q_valid = 0; - if (i == 0) begin - // Waiting for response for global barrier - if (out_rsp_i[i].p_valid) begin - // Mute the response for now - in_rsp_o[i].p_valid = 0; - - done_barrier[i] = 1; - end - end else begin - // Waiting for port 0 finish global barrier - done_barrier[i] = 1; - end - - if (take_barrier) begin + if (barrier_done_i) begin state_d[i] = Take; // Release the barrier by accepting all requests in_rsp_o[i].q_ready = 1; // Record the user for response generating user_d[i] = out_req_o[i].q.user; + // Deassert barrier_o + barrier_d = 1'b0; end end Take: begin @@ -147,6 +184,10 @@ module cachepool_tile_barrier end end + `FFARN(barrier_q, barrier_d, 1'b0, clk_i, rst_ni) + `FFARN(mask_state_q, mask_state_d, MaskIdle, clk_i, rst_ni) + `FFARN(core_mask_q, core_mask_d, {NrPorts{1'b1}}, clk_i, rst_ni) + for (genvar i = 0; i < NrPorts; i++) begin : gen_ff `FFARN(state_q[i], state_d[i], Idle, clk_i, rst_ni) `FFARN(user_q[i], user_d[i], '0, clk_i, rst_ni) diff --git a/hardware/src/reqrsp_to_reg.sv b/hardware/src/reqrsp_to_reg.sv new file mode 100644 index 0000000..b37c930 --- /dev/null +++ b/hardware/src/reqrsp_to_reg.sv @@ -0,0 +1,117 @@ +// Copyright 2025 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 +// +// Based on periph_to_reg from register_interface. +// Converts REQRSP protocol to register interface with 1-cycle response latency. +// Assumes in-order, constant-latency target (e.g., BootROM). +// A FIFO preserves the REQRSP user field from request to response. + +`include "common_cells/registers.svh" +`include "common_cells/assertions.svh" + +module reqrsp_to_reg #( + parameter int unsigned AddrWidth = 32, + parameter int unsigned DataWidth = 32, + parameter int unsigned UserWidth = 1, + // FIFO depth for buffering user field (should match max outstanding) + parameter int unsigned FifoDepth = 2, + // When enabled, shift response data right by the request byte offset + // so that the addressed bytes land at [31:0]. Useful when the regbus + // target is wider than the REQRSP requester (e.g., 512b BootROM + // accessed by 32b core data loads). Aligned requests (offset=0) + // see no change. + parameter bit ShiftResponse = 1'b0, + parameter type user_t = logic [UserWidth-1:0], + parameter type reqrsp_req_t = logic, + parameter type reqrsp_rsp_t = logic, + parameter type reg_req_t = logic, + parameter type reg_rsp_t = logic +) ( + input logic clk_i, + input logic rst_ni, + input reqrsp_req_t reqrsp_req_i, + output reqrsp_rsp_t reqrsp_rsp_o, + output reg_req_t reg_req_o, + input reg_rsp_t reg_rsp_i +); + + localparam int unsigned ByteOffset = $clog2(DataWidth / 8); + + // Request grant: accepted when both sides are valid/ready + logic gnt; + assign gnt = reqrsp_req_i.q_valid & reg_rsp_i.ready; + + // Forward request to regbus (combinational) + assign reg_req_o.addr = reqrsp_req_i.q.addr; + assign reg_req_o.write = reqrsp_req_i.q.write; + assign reg_req_o.wdata = reqrsp_req_i.q.data; + assign reg_req_o.wstrb = reqrsp_req_i.q.strb; + assign reg_req_o.valid = reqrsp_req_i.q_valid; + + // REQRSP q_ready: grant the request + assign reqrsp_rsp_o.q_ready = gnt; + + // Register response (1-cycle latency, same as periph_to_reg) + logic rsp_valid_q; + logic [DataWidth-1:0] rsp_rdata_d, rsp_rdata_q; + logic rsp_error_q; + logic rsp_write_q; + + `FF(rsp_valid_q, gnt, 1'b0) + `FF(rsp_rdata_q, rsp_rdata_d, '0) + `FF(rsp_error_q, reg_rsp_i.error, 1'b0) + `FF(rsp_write_q, reqrsp_req_i.q.write, 1'b0) + + // Optional response data shift: use the combinational request address + // to shift rdata right so the addressed bytes land at the LSB. + // Safe because REQRSP holds q.addr stable until q_ready (= gnt), + // and gnt fires on the same cycle rdata becomes valid (regbus + // ready follows valid by 1 cycle for constant-latency targets). + if (ShiftResponse) begin : gen_shift + assign rsp_rdata_d = reg_rsp_i.rdata >> (reqrsp_req_i.q.addr[ByteOffset-1:0] * 8); + end else begin : gen_no_shift + assign rsp_rdata_d = reg_rsp_i.rdata; + end + + // User FIFO: push on grant, pop when response is consumed + user_t user_fifo_out; + logic user_fifo_full, user_fifo_empty; + logic user_fifo_push, user_fifo_pop; + + assign user_fifo_push = gnt; + assign user_fifo_pop = rsp_valid_q & reqrsp_req_i.p_ready; + + fifo_v3 #( + .FALL_THROUGH ( 1'b0 ), + .DEPTH ( FifoDepth ), + .dtype ( user_t ) + ) i_user_fifo ( + .clk_i, + .rst_ni, + .flush_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .full_o ( user_fifo_full ), + .empty_o ( user_fifo_empty ), + .usage_o ( /* unused */ ), + .data_i ( reqrsp_req_i.q.user ), + .push_i ( user_fifo_push ), + .data_o ( user_fifo_out ), + .pop_i ( user_fifo_pop ) + ); + + // REQRSP response channel + assign reqrsp_rsp_o.p.data = rsp_rdata_q; + assign reqrsp_rsp_o.p.error = rsp_error_q; + assign reqrsp_rsp_o.p.write = rsp_write_q; + assign reqrsp_rsp_o.p.user = user_fifo_out; + assign reqrsp_rsp_o.p_valid = rsp_valid_q; + + // Target is read-only when ShiftResponse is enabled (e.g., BootROM) + `ifndef TARGET_SYNTHESIS + if (ShiftResponse) begin : gen_no_write_assert + `ASSERT(NoWrite, !(reg_req_o.valid && reg_req_o.write), clk_i, rst_ni) + end + `endif + +endmodule diff --git a/hardware/src/reqrsp_w2n_converter.sv b/hardware/src/reqrsp_w2n_converter.sv new file mode 100644 index 0000000..5921a0d --- /dev/null +++ b/hardware/src/reqrsp_w2n_converter.sv @@ -0,0 +1,363 @@ +// Copyright 2026 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 +// +// Author: Diyou Shen +// +// REQRSP data-width converter (wide -> narrow). +// One-at-a-time: only one narrow beat is outstanding at any moment. +// A single wide request is serialized into Ratio = SlvDataWidth/MstDataWidth +// narrow beats. The next beat is not issued until the current beat's +// response has been received. +// +// Optional read buffer + MSHR (EnRdBuf): a single-entry, whole-request, +// read-only cache plus a small merge FIFO, both keyed on an exact full +// address match (no tag/offset decomposition, to sidestep any alignment +// assumptions -- some hit opportunities are given up for simplicity): +// +// - Post-completion cache: once a read finishes with no error, its full +// address and assembled data are latched. A later read in IDLE for the +// exact same address is returned immediately, skipping the narrow-beat +// serialization entirely. +// - Mid-flight merge (MSHR): while a read is being serialized (SEND_BEAT/ +// WAIT_RSP), a second read for the exact same address is accepted too +// (instead of being backpressured) and its `.user` is pushed into a +// depth-MSHRDepth FIFO. Once the in-flight read completes, its response +// is replayed once per queued FIFO entry (same data/error, each with its +// own `.user`), in FIFO (arrival) order, before returning to IDLE. +// +// Either mechanism is only ever populated/consulted for reads; any write +// (in IDLE) invalidates the cache outright (no per-address comparison), and +// writes never participate in merging. +// +// The post-completion cache is further restricted to an explicit allowlisted +// address window (CacheableBase/CacheableSize, default empty = nothing +// cacheable). This is deliberately an allowlist, not a blocklist: some +// registers behind this converter (e.g. status bits driven by direct wires +// from hardware state, not by writes that pass through here) can change +// without this converter ever observing a write, so caching them would make +// a polling loop see a stale value forever. Only address ranges known to be +// pure, bus-write-invalidated read data (e.g. a ROM) should be allowlisted. +// The mid-flight MSHR merge is unaffected by this window: it never persists +// data across separate transactions, so it cannot go stale. + +module reqrsp_w2n_converter #( + parameter int unsigned SlvDataWidth = 512, + parameter int unsigned MstDataWidth = 32, + parameter int unsigned AddrWidth = 32, + parameter int unsigned SlvUserWidth = 1, + parameter int unsigned MstUserWidth = 1, + parameter int unsigned MaxTrans = 4, + parameter bit EnRdBuf = 1'b0, + parameter int unsigned MSHRDepth = 16, + parameter logic [AddrWidth-1:0] CacheableBase = '0, + parameter logic [AddrWidth-1:0] CacheableSize = '0, + parameter type slv_req_t = logic, + parameter type slv_rsp_t = logic, + parameter type mst_req_t = logic, + parameter type mst_rsp_t = logic, + parameter type slv_user_t = logic +) ( + input logic clk_i, + input logic rst_ni, + input slv_req_t slv_req_i, + output slv_rsp_t slv_rsp_o, + output mst_req_t mst_req_o, + input mst_rsp_t mst_rsp_i +); + + // --------------------------------------------------------------------------- + // Constants + // --------------------------------------------------------------------------- + localparam int unsigned Ratio = SlvDataWidth / MstDataWidth; + localparam int unsigned BeatCntW = (Ratio == 1) ? 1 : $clog2(Ratio); + localparam int unsigned MstBytes = MstDataWidth / 8; + localparam int unsigned SlvBytes = SlvDataWidth / 8; + + // --------------------------------------------------------------------------- + // FSM states + // --------------------------------------------------------------------------- + typedef enum logic [2:0] { + IDLE, + SEND_BEAT, + WAIT_RSP, + SLV_RESP, + MSHR_DRAIN + } state_e; + + // --------------------------------------------------------------------------- + // Registers: _d (next) / _q (current) + // --------------------------------------------------------------------------- + state_e state_d, state_q; + logic [BeatCntW-1:0] beat_d, beat_q; + // When set, the request fits in a single narrow beat (size <= MstBytes). + logic narrow_d, narrow_q; + + // Latched slave-side request fields + logic [AddrWidth-1:0] slv_addr_d, slv_addr_q; + logic slv_write_d, slv_write_q; + reqrsp_pkg::amo_op_e slv_amo_d, slv_amo_q; + logic [SlvDataWidth-1:0] slv_data_d, slv_data_q; + logic [SlvBytes-1:0] slv_strb_d, slv_strb_q; + slv_user_t slv_user_d, slv_user_q; + + // Assembled wide read-data and sticky error + logic [SlvDataWidth-1:0] rdata_d, rdata_q; + logic error_d, error_q; + + // --------------------------------------------------------------------------- + // Read buffer + MSHR (only meaningful when EnRdBuf = 1) + // --------------------------------------------------------------------------- + // Post-completion, whole-request, exact-address cache. + logic buf_valid_d, buf_valid_q; + logic [AddrWidth-1:0] buf_addr_d, buf_addr_q; + logic [SlvDataWidth-1:0] buf_data_d, buf_data_q; + + // Mid-flight merge FIFO: holds the `.user` of reads that matched the + // address currently being serialized, to be replayed once it completes. + localparam int unsigned MSHRAddrDepth = (MSHRDepth > 1) ? $clog2(MSHRDepth) : 1; + logic mshr_push, mshr_pop, mshr_full, mshr_empty; + logic [MSHRAddrDepth-1:0] mshr_usage; + slv_user_t mshr_push_data, mshr_pop_data; + + fifo_v3 #( + .FALL_THROUGH ( 1'b0 ), + .DEPTH ( MSHRDepth ), + .dtype ( slv_user_t ) + ) i_mshr_fifo ( + .clk_i, + .rst_ni, + .flush_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .full_o ( mshr_full ), + .empty_o ( mshr_empty ), + .usage_o ( mshr_usage ), + .data_i ( mshr_push_data ), + .push_i ( mshr_push ), + .data_o ( mshr_pop_data ), + .pop_i ( mshr_pop ) + ); + + // A read is being serialized right now, and can still accept a merge + // (once the last beat is issued/answered there is nothing left to merge + // into -- SLV_RESP/MSHR_DRAIN only ever backpressure new requests). + logic mergeable_in_flight; + assign mergeable_in_flight = EnRdBuf && !slv_write_q + && (state_q == SEND_BEAT || state_q == WAIT_RSP); + logic merge_hit; + assign merge_hit = mergeable_in_flight && slv_req_i.q_valid + && !slv_req_i.q.write && (slv_req_i.q.addr == slv_addr_q) + && !mshr_full; + + assign mshr_push = merge_hit; + assign mshr_push_data = slv_req_i.q.user; + + // Address allowlist for the post-completion cache (see header comment). + function automatic logic addr_is_cacheable(input logic [AddrWidth-1:0] addr); + addr_is_cacheable = (CacheableSize != '0) + && (addr >= CacheableBase) + && (addr < CacheableBase + CacheableSize); + endfunction + + // --------------------------------------------------------------------------- + // Combinational datapath + next-state logic + // --------------------------------------------------------------------------- + always_comb begin + // Hold all registers by default + state_d = state_q; + beat_d = beat_q; + narrow_d = narrow_q; + slv_addr_d = slv_addr_q; + slv_write_d = slv_write_q; + slv_amo_d = slv_amo_q; + slv_data_d = slv_data_q; + slv_strb_d = slv_strb_q; + slv_user_d = slv_user_q; + rdata_d = rdata_q; + error_d = error_q; + + // Read buffer/MSHR defaults: hold + buf_valid_d = buf_valid_q; + buf_addr_d = buf_addr_q; + buf_data_d = buf_data_q; + mshr_pop = 1'b0; + + // Slave interface defaults (no handshake) + slv_rsp_o = '0; + + // Master interface defaults (no request) + mst_req_o = '0; + + unique case (state_q) + // ------------------------------------------------------------------ + // IDLE: accept one wide request + // ------------------------------------------------------------------ + IDLE: begin + slv_rsp_o.q_ready = 1'b1; + if (slv_req_i.q_valid) begin + slv_addr_d = slv_req_i.q.addr; + slv_write_d = slv_req_i.q.write; + slv_amo_d = slv_req_i.q.amo; + slv_data_d = slv_req_i.q.data; + slv_strb_d = slv_req_i.q.strb; + slv_user_d = slv_req_i.q.user; + rdata_d = '0; + error_d = 1'b0; + beat_d = '0; + narrow_d = (slv_req_i.q.size <= reqrsp_pkg::size_t'($clog2(MstBytes))); + state_d = SEND_BEAT; + + if (EnRdBuf) begin + if (slv_req_i.q.write) begin + // Any write invalidates the cache outright. + buf_valid_d = 1'b0; + end else if (buf_valid_q && (slv_req_i.q.addr == buf_addr_q)) begin + // Exact-address hit: bypass the narrow-beat serialization. + // (Only ever reachable for a cacheable address -- the buffer + // is only ever filled for addresses that passed the allowlist.) + rdata_d = buf_data_q; + error_d = 1'b0; + state_d = SLV_RESP; + end else if (addr_is_cacheable(slv_req_i.q.addr)) begin + // Cacheable read miss: evict the old line, this fetch refills it. + buf_valid_d = 1'b0; + buf_addr_d = slv_req_i.q.addr; + end + // Non-cacheable read: leave the buffer entirely untouched -- + // must not evict a legitimately cached line, and must never be + // cached itself (see header comment on the allowlist). + end + end + end + + // ------------------------------------------------------------------ + // SEND_BEAT: present the current narrow beat on the master port + // ------------------------------------------------------------------ + SEND_BEAT: begin + mst_req_o.q_valid = 1'b1; + // Address increments by MstBytes per beat + mst_req_o.q.addr = slv_addr_q + + AddrWidth'({beat_q, {$clog2(MstBytes){1'b0}}}); + mst_req_o.q.write = slv_write_q; + mst_req_o.q.amo = slv_amo_q; + mst_req_o.q.size = reqrsp_pkg::size_t'($clog2(MstBytes)); + mst_req_o.q.user = slv_user_q; + + // Write data/strb slicing + mst_req_o.q.data = slv_data_q[beat_q*MstDataWidth +: MstDataWidth]; + mst_req_o.q.strb = slv_strb_q[beat_q*MstBytes +: MstBytes]; + + // Merge a same-address read into this in-flight fetch instead of + // backpressuring it. + slv_rsp_o.q_ready = merge_hit; + + if (mst_rsp_i.q_ready) begin + state_d = WAIT_RSP; + end + end + + // ------------------------------------------------------------------ + // WAIT_RSP: wait for the narrow response + // ------------------------------------------------------------------ + WAIT_RSP: begin + mst_req_o.p_ready = 1'b1; + // Merge a same-address read into this in-flight fetch instead of + // backpressuring it. + slv_rsp_o.q_ready = merge_hit; + + if (mst_rsp_i.p_valid) begin + // Accumulate read data + rdata_d[beat_q*MstDataWidth +: MstDataWidth] = mst_rsp_i.p.data; + // Sticky error + error_d = error_q | mst_rsp_i.p.error; + + if (narrow_q || beat_q == BeatCntW'(Ratio - 1)) begin + // Last beat — deliver aggregated response + state_d = SLV_RESP; + end else begin + beat_d = beat_q + 1; + state_d = SEND_BEAT; + end + end + end + + // ------------------------------------------------------------------ + // SLV_RESP: return the assembled wide response to the slave port + // ------------------------------------------------------------------ + SLV_RESP: begin + slv_rsp_o.p_valid = 1'b1; + slv_rsp_o.p.data = rdata_q; + slv_rsp_o.p.error = error_q; + slv_rsp_o.p.write = slv_write_q; + slv_rsp_o.p.user = slv_user_q; + if (slv_req_i.p_ready) begin + if (EnRdBuf && !slv_write_q && !error_q && addr_is_cacheable(slv_addr_q)) begin + // Cache the completed read for later exact-address hits. + buf_valid_d = 1'b1; + buf_addr_d = slv_addr_q; + buf_data_d = rdata_q; + end + state_d = (EnRdBuf && !mshr_empty) ? MSHR_DRAIN : IDLE; + end + end + + // ------------------------------------------------------------------ + // MSHR_DRAIN: replay the just-completed response once per merged + // request queued in the MSHR FIFO, in arrival order. + // ------------------------------------------------------------------ + MSHR_DRAIN: begin + slv_rsp_o.p_valid = 1'b1; + slv_rsp_o.p.data = rdata_q; + slv_rsp_o.p.error = error_q; + slv_rsp_o.p.write = 1'b0; + slv_rsp_o.p.user = mshr_pop_data; + if (slv_req_i.p_ready) begin + mshr_pop = 1'b1; + // mshr_usage still reflects the pre-pop count: 1 means this is + // the last queued entry, so the FIFO will be empty after this pop. + state_d = (mshr_usage == MSHRAddrDepth'(1)) ? IDLE : MSHR_DRAIN; + end + end + + default: state_d = IDLE; + endcase + end + + // --------------------------------------------------------------------------- + // Sequential — pure register updates, no logic + // --------------------------------------------------------------------------- + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + state_q <= IDLE; + beat_q <= '0; + narrow_q <= 1'b0; + slv_addr_q <= '0; + slv_write_q <= 1'b0; + slv_amo_q <= reqrsp_pkg::AMONone; + slv_data_q <= '0; + slv_strb_q <= '0; + slv_user_q <= slv_user_t'('0); + rdata_q <= '0; + error_q <= 1'b0; + buf_valid_q <= 1'b0; + buf_addr_q <= '0; + buf_data_q <= '0; + end else begin + state_q <= state_d; + beat_q <= beat_d; + narrow_q <= narrow_d; + slv_addr_q <= slv_addr_d; + slv_write_q <= slv_write_d; + slv_amo_q <= slv_amo_d; + slv_data_q <= slv_data_d; + slv_strb_q <= slv_strb_d; + slv_user_q <= slv_user_d; + rdata_q <= rdata_d; + error_q <= error_d; + buf_valid_q <= buf_valid_d; + buf_addr_q <= buf_addr_d; + buf_data_q <= buf_data_d; + end + end + +endmodule : reqrsp_w2n_converter diff --git a/hardware/src/spatz_cache_amo.sv b/hardware/src/spatz_cache_amo.sv index ddcd512..bf8c8a8 100644 --- a/hardware/src/spatz_cache_amo.sv +++ b/hardware/src/spatz_cache_amo.sv @@ -133,6 +133,7 @@ module spatz_cache_amo assign sc_set = amo_req_valid & amo_req_ready & (amo_insn == AMOSC); assign is_sc_rsp = amo_rsp_valid & sc_q & + (sc_user_q.tile_id == amo_rsp.user.tile_id) & (sc_user_q.core_id == amo_rsp.user.core_id) & (sc_user_q.req_id == amo_rsp.user.req_id); @@ -237,6 +238,7 @@ module spatz_cache_amo core_ready = 1'b0; if (amo_rsp_valid && // In case a response of a previous sc from other request trigger this transition + (amo_user_q.tile_id == amo_rsp.user.tile_id) && (amo_user_q.core_id == amo_rsp.user.core_id) && (amo_user_q.req_id == amo_rsp.user.req_id) ) begin diff --git a/hardware/src/tcdm_cache_interco.sv b/hardware/src/tcdm_cache_interco.sv index ba49e11..e06998d 100644 --- a/hardware/src/tcdm_cache_interco.sv +++ b/hardware/src/tcdm_cache_interco.sv @@ -21,24 +21,57 @@ // private_bank = addr_bank_bits % num_private_cache_q // shared_bank = num_private_cache_q + (addr_bank_bits % num_shared_cache_q) // For non-power-of-2 partition sizes this causes uneven bank utilisation. +// +// Multi-group support (NumRemoteGroupPort > 0): +// +// When the cluster contains multiple groups, tile IDs are globally unique +// and encode both the group and tile-within-group: +// tile_id = {group_id, local_tile_id} +// +// The xbar performs three-way routing for shared (non-private) requests: +// 1. Local : same tile -> local cache bank +// 2. Intra-group : same group, diff tile -> remote port (existing xbar) +// 3. Inter-group : different group -> inter-group remote port (new) +// +// inter-group remote ports are appended after the remote ports on both input and output +// sides of the xbar, preserving full backward compatibility when +// NumRemoteGroupPort == 0. `include "common_cells/registers.svh" +`include "common_cells/assertions.svh" module tcdm_cache_interco #( /// Number of Tiles ('>= 1') parameter int unsigned NumTiles = 32'd1, /// Number of inputs into the interconnect (Cores per Tile) (`> 0`). parameter int unsigned NumCores = 32'd0, - /// Number of remote ports added to xbar ('>= 0'). - parameter int unsigned NumRemotePort = 32'd0, + /// Number of remote ports added to xbar for intra-group traffic ('>= 0'). + parameter int unsigned NumLGPort = 32'd0, + /// Number of dedicated inter-group remote ports ('>= 0'). + /// When 0, the module behaves identically to the single-group configuration. + /// Each inter-group remote port serves as both an output (requests to other groups) and an + /// input (requests arriving from other groups), mirroring NumLGPort. + parameter int unsigned NumRemoteGroupPort = 32'd0, /// Number of outputs from the interconnect (Cache banks per Tile) (`> 0`). parameter int unsigned NumCache = 32'd0, /// Number of total cache banks across all tiles (used for address scramble). + /// For multi-group, this must cover all tiles across all groups. parameter int unsigned NumTotCache = 32'd0, /// Address width in bits (cacheline offset: 512b => 6 bits). parameter int unsigned AddrWidth = 32'd32, /// Tile ID width ('> 0'). + /// In multi-group configurations, TileIDWidth covers the globally unique + /// tile ID which encodes both group and tile-within-group: + /// tile_id = {group_id, local_tile_id} parameter int unsigned TileIDWidth = 32'd1, + /// DRAM base address, used to check if we get illegal access + parameter int unsigned DramBaseAddr = 32'h8000_0000, + /// Number of tiles within a single group. + /// Used to extract the group portion from the address tile field: + /// group_id = addr_tile_bits / NumTilesPerGroup + /// Only relevant when NumRemoteGroupPort > 0. Defaults to NumTiles for + /// backward compatibility (single-group: all tiles are in one group). + parameter int unsigned NumTilesPerGroup = NumTiles, /// Port type of the data request ports. parameter type tcdm_req_t = logic, @@ -52,7 +85,9 @@ module tcdm_cache_interco #( parameter snitch_pkg::topo_e Topology = snitch_pkg::LogarithmicInterconnect, /// Dependency parameters – do not override. parameter type tile_id_t = logic [TileIDWidth-1:0], - parameter type addr_t = logic [AddrWidth-1:0] + parameter type addr_t = logic [AddrWidth-1:0], + localparam TotInPorts = NumCores+NumLGPort+NumRemoteGroupPort, + localparam TotOutPorts = NumCache+NumLGPort+NumRemoteGroupPort ) ( /// Clock, positive edge triggered. @@ -68,69 +103,79 @@ module tcdm_cache_interco #( input logic [$clog2(NumCache):0] num_private_cache_i, /// Partitioning address input addr_t private_start_addr_i, - /// Request port (cores + remote-in) ---------------------------------- - input tcdm_req_t [NumCores+NumRemotePort-1:0] core_req_i, + /// Request port (cores + intra-group remote-in + inter-group remote-in). + input tcdm_req_t [TotInPorts-1:0] core_req_i, /// Response ready in. - input logic [NumCores+NumRemotePort-1:0] core_rsp_ready_i, - /// Response port (cores + remote-in). - output tcdm_rsp_t [NumCores+NumRemotePort-1:0] core_rsp_o, + input logic [TotInPorts-1:0] core_rsp_ready_i, + /// Response port (cores + intra-group remote-in + inter-group remote-in). + output tcdm_rsp_t [TotInPorts-1:0] core_rsp_o, /// Memory side ------------------------------------------------------- - /// Which remote tile is targeted (one entry per remote output port). - output tile_id_t [NumRemotePort-1:0] tile_sel_o, - // output logic remote_group_o, - /// Requests to cache banks and remote output ports. - output tcdm_req_t [NumCache+NumRemotePort-1:0] mem_req_o, + /// Which remote tile is targeted (one entry per intra-group remote output). + output tile_id_t [NumLGPort-1:0] tile_sel_o, + /// Which tile is targeted via inter-group remote (one entry per inter-group remote output). + /// Carries the full globally-unique tile ID; the wrapper decomposes it + /// into group XY coordinates for the router and local tile ID for the + /// receiving-side xbar. + output tile_id_t [NumRemoteGroupPort-1:0] remote_group_sel_o, + /// Requests to cache banks, intra-group remote, and inter-group remote ports. + output tcdm_req_t [TotOutPorts-1:0] mem_req_o, /// Response ready out. - output logic [NumCache+NumRemotePort-1:0] mem_rsp_ready_o, - /// Responses from cache banks and remote output ports. - input tcdm_rsp_t [NumCache+NumRemotePort-1:0] mem_rsp_i + output logic [TotOutPorts-1:0] mem_rsp_ready_o, + /// Responses from cache banks, intra-group remote, and inter-group remote ports. + input tcdm_rsp_t [TotOutPorts-1:0] mem_rsp_i ); // ------------------------------------------------------------------------- // Local parameters // ------------------------------------------------------------------------- - // Bits to index into xbar outputs (local banks + one remote slot). - localparam int unsigned NumOutSelBits = $clog2(NumCache + NumRemotePort); + // Total number of xbar input and output ports. + localparam int unsigned NumInp = NumCores + NumLGPort + NumRemoteGroupPort; + localparam int unsigned NumOut = NumCache + NumLGPort + NumRemoteGroupPort; + // Bits to index into xbar outputs. + localparam int unsigned NumOutSelBits = $clog2(NumOut); // Bits to index into xbar inputs. - localparam int unsigned NumInpSelBits = $clog2(NumCores + NumRemotePort); + localparam int unsigned NumInpSelBits = $clog2(NumInp); // Bits needed to select among local cache banks. - localparam int unsigned CacheBankBits = $clog2(NumCache); + localparam int unsigned CacheBankBits = $clog2(NumCache); // Bits needed to select the tile in the shared address space. - // Equals TileIDWidth by construction (NumTotCache / NumCache == NumTiles). - localparam int unsigned TileBits = $clog2(NumTotCache / NumCache); + // Equals TileIDWidth by construction (NumTotCache / NumCache == NumTotalTiles). + localparam int unsigned TileBits = $clog2(NumTotCache / NumCache); + + // Group extraction: number of bits to identify the group within TileID. + // LocalTileBits = $clog2(NumTilesPerGroup); GroupBits = TileBits - LocalTileBits. + // Only meaningful when NumRemoteGroupPort > 0. + localparam int unsigned LocalTileBits = $clog2(NumTilesPerGroup); // ------------------------------------------------------------------------- // Types // ------------------------------------------------------------------------- typedef logic [NumInpSelBits-1:0] mem_sel_t; - typedef logic [NumOutSelBits -1:0] core_sel_t; + typedef logic [NumOutSelBits-1:0] core_sel_t; // ------------------------------------------------------------------------- // Internal signals // ------------------------------------------------------------------------- // Xbar routing signals. - core_sel_t [NumCores+NumRemotePort-1:0] core_req_sel; - mem_sel_t [NumCache+NumRemotePort-1:0] mem_rsp_sel; - // '1' when this request stays on local banks. - logic [NumCores+NumRemotePort-1:0] local_sel; + core_sel_t [NumInp-1:0] core_req_sel; + mem_sel_t [NumOut-1:0] mem_rsp_sel; // '1' when a request targets the private partition. - logic [NumCores+NumRemotePort-1:0] is_private; + logic [NumInp-1:0] is_private; // Xbar channel signals. - tcdm_req_chan_t [NumCores+NumRemotePort-1:0] core_req; - logic [NumCores+NumRemotePort-1:0] core_req_valid, core_req_ready; + tcdm_req_chan_t [NumInp-1:0] core_req; + logic [NumInp-1:0] core_req_valid, core_req_ready; - tcdm_req_chan_t [NumCache+NumRemotePort-1:0] mem_req; - logic [NumCache+NumRemotePort-1:0] mem_req_valid, mem_req_ready; + tcdm_req_chan_t [NumOut-1:0] mem_req; + logic [NumOut-1:0] mem_req_valid, mem_req_ready; - tcdm_rsp_chan_t [NumCores+NumRemotePort-1:0] core_rsp; - logic [NumCores+NumRemotePort-1:0] core_rsp_valid, core_rsp_ready; + tcdm_rsp_chan_t [NumInp-1:0] core_rsp; + logic [NumInp-1:0] core_rsp_valid, core_rsp_ready; - tcdm_rsp_chan_t [NumCache+NumRemotePort-1:0] mem_rsp; - logic [NumCache+NumRemotePort-1:0] mem_rsp_valid, mem_rsp_ready; + tcdm_rsp_chan_t [NumOut-1:0] mem_rsp; + logic [NumOut-1:0] mem_rsp_valid, mem_rsp_ready; // ------------------------------------------------------------------------- // Partition control – registered to ease timing @@ -155,7 +200,7 @@ module tcdm_cache_interco #( // Private/shared classification (request side, before xbar) // ------------------------------------------------------------------------- - for (genvar inp = 0; inp < NumCores+NumRemotePort; inp++) begin : gen_is_private + for (genvar inp = 0; inp < NumInp; inp++) begin : gen_is_private assign is_private[inp] = (core_req[inp].addr >= private_start_addr_q); end @@ -164,103 +209,131 @@ module tcdm_cache_interco #( // ------------------------------------------------------------------------- reqrsp_xbar #( - .NumInp (NumCores + NumRemotePort), - .NumOut (NumCache + NumRemotePort), + .NumInp (NumInp ), + .NumOut (NumOut ), .PipeReg (1'b0 ), .ExtReqPrio (1'b0 ), .ExtRspPrio (1'b0 ), .tcdm_req_chan_t (tcdm_req_chan_t ), .tcdm_rsp_chan_t (tcdm_rsp_chan_t ) ) i_cache_xbar ( - .clk_i (clk_i ), - .rst_ni (rst_ni ), - .slv_req_i (core_req ), - .slv_rr_i ('0 ), - .slv_req_valid_i (core_req_valid ), - .slv_req_ready_o (core_req_ready ), - .slv_rsp_o (core_rsp ), - .slv_rsp_valid_o (core_rsp_valid ), - .slv_rsp_ready_i (core_rsp_ready ), - .slv_sel_i (core_req_sel ), - .slv_selected_o (/* unused */ ), - .mst_req_o (mem_req ), - .mst_rr_i ('0 ), - .mst_req_valid_o (mem_req_valid ), - .mst_req_ready_i (mem_req_ready ), - .mst_rsp_i (mem_rsp ), - .mst_rsp_valid_i (mem_rsp_valid ), - .mst_rsp_ready_o (mem_rsp_ready ), - .mst_sel_i (mem_rsp_sel ) + .clk_i (clk_i ), + .rst_ni (rst_ni ), + .slv_req_i (core_req ), + .slv_rr_i ('0 ), + .slv_req_valid_i (core_req_valid ), + .slv_req_ready_o (core_req_ready ), + .slv_rsp_o (core_rsp ), + .slv_rsp_valid_o (core_rsp_valid ), + .slv_rsp_ready_i (core_rsp_ready ), + .slv_sel_i (core_req_sel ), + .slv_selected_o (/* unused */ ), + .mst_req_o (mem_req ), + .mst_rr_i ('0 ), + .mst_req_valid_o (mem_req_valid ), + .mst_req_ready_i (mem_req_ready ), + .mst_rsp_i (mem_rsp ), + .mst_rsp_valid_i (mem_rsp_valid ), + .mst_rsp_ready_o (mem_rsp_ready ), + .mst_sel_i (mem_rsp_sel ) ); // ------------------------------------------------------------------------- // Request routing (xbar input-side selection) // ------------------------------------------------------------------------- // - // Address layout (example: offset=6, CacheBankBits=2, TileBits=2): + // Address layout (example: offset=6, CacheBankBits=2, TileBits=4 with + // LocalTileBits=2 and GroupBits=2): // - // 31 14 | 13 12 | 11 10 | 9 7 | 5 0 - // Tag | TileID | BankSel | Index | CL offset - // ^-- [offset+CacheBankBits+TileBits-1 : offset+CacheBankBits] - // ^-- [offset+CacheBankBits-1 : offset] + // 31 16 | 15 14 | 13 12 | 11 10 | 9 7 | 5 0 + // Tag | GroupID | LclTID | BankSel | Index | CL offset + // ^-- [offset+CacheBankBits+TileBits-1 : offset+CacheBankBits+LocalTileBits] + // ^-- [offset+CacheBankBits+LocalTileBits-1 : offset+CacheBankBits] + // ^-- [offset+CacheBankBits-1 : offset] // - // Partitioning supports any num_private_cache_q in [0..NumCache]: - // Private banks : ports [0 .. num_private_cache_q-1] - // Shared banks : ports [num_private_cache_q .. NumCache-1] + // Three-way routing classification: + // 1. Local : addr tile == my tile -> route to cache bank + // 2. Intra-group : same group, different tile -> route to remote port + // 3. Inter-group : different group -> route to inter-group remote port // - // Bank selection uses modulo folding: - // private_bank = (addr_bank_bits % num_private_cache_q) - // shared_bank = num_private_cache_q + (addr_bank_bits % num_shared_cache_q) + // Partitioning (private/shared) interacts as follows: + // - Private requests are always local (same as before). + // - Shared requests use the full three-way classification. // - // For power-of-2 partition sizes this reduces to a simple bit mask. - // For non-power-of-2 sizes (e.g. 3) the modulo is a small comparator since - // addr_bank_bits is only CacheBankBits wide. + // The original two-way classification (local vs. remote) is preserved + // when NumRemoteGroupPort == 0, ensuring backward compatibility. + + // Derive this tile's group ID from the globally-unique tile_id_i. + logic [TileBits-1:0] my_group_id; + if (NumRemoteGroupPort == 0) begin + assign my_group_id = tile_id_i; + end else begin + assign my_group_id = tile_id_i[TileBits-1:LocalTileBits]; + end - for (genvar port = 0; port < NumCores+NumRemotePort; port++) begin : gen_req_sel + for (genvar port = 0; port < NumInp; port++) begin : gen_req_sel logic [CacheBankBits-1:0] addr_bank; - logic [TileIDWidth-1:0] addr_tile; + // Full tile ID extracted from the address (covers group + local tile). + logic [TileBits-1:0] addr_tile_id; + // Group portion of the address tile field. + logic [TileBits-1:0] addr_group_id; + // Whether the addressed group matches this tile's group. + logic same_group; always_comb begin // Defaults. - local_sel[port] = 1'b1; core_req_sel[port] = '0; // Extract the raw BankSel field from the address. - addr_bank = core_req[port].addr[dynamic_offset_i +: CacheBankBits]; - // Extract the target TileID from the address (used for remote port selection). - addr_tile = core_req[port].addr[(dynamic_offset_i + CacheBankBits) +: TileIDWidth]; - - if (num_private_cache_q == ($clog2(NumCache)+1)'(NumCache) || NumTiles == 1) begin - // All-private or single-tile: every request is local. + addr_bank = core_req[port].addr[dynamic_offset_i +: CacheBankBits]; + // Extract the full tile ID (group + local) from the address. + addr_tile_id = core_req[port].addr[(dynamic_offset_i + CacheBankBits) +: TileBits]; + // Extract group portion (upper bits of tile ID). + addr_group_id = addr_tile_id >> LocalTileBits; + // Compare group IDs. + same_group = (addr_group_id == my_group_id); + + if (num_private_cache_q == ($clog2(NumCache)+1)'(NumCache) + || (NumTiles == 1 && NumRemoteGroupPort == 0)) begin + // All-private, or single-tile single-group: every request is local. // Use the full BankSel field directly (no folding needed). - local_sel[port] = 1'b1; core_req_sel[port] = core_sel_t'(addr_bank); end else if (num_private_cache_q == '0) begin - // All-shared: check TileID to decide local vs. remote. - // Use the full BankSel field directly (no folding needed). - local_sel[port] = (addr_tile == tile_id_i); - // Route remote requests by target tile ID so that all accesses to the - // same tile share a single pipeline, preserving write-before-read - // ordering across barriers. - core_req_sel[port] = local_sel[port] - ? core_sel_t'(addr_bank) - : core_sel_t'(NumCache + (addr_tile % NumRemotePort)); + // All-shared: full three-way classification. + if (NumRemoteGroupPort > 0 && !same_group) begin + // Inter-group: route to inter-group remote port. + core_req_sel[port] = core_sel_t'(NumCache + NumLGPort + + (port % NumRemoteGroupPort)); + end else if (addr_tile_id[LocalTileBits-1:0] != tile_id_i[LocalTileBits-1:0] + && !(NumTiles == 1)) begin + // Intra-group remote: different tile, same group. + core_req_sel[port] = core_sel_t'(NumCache + (port % NumLGPort)); + end else begin + // Local: same tile. + core_req_sel[port] = core_sel_t'(addr_bank); + end end else begin - // Mixed: fold addr_bank into the appropriate partition via modulo. + // Mixed partition: fold addr_bank into the appropriate partition. if (is_private[port]) begin // Private request: always local. - // bank = addr_bank % num_private_cache_q, offset from bank 0. - local_sel[port] = 1'b1; core_req_sel[port] = core_sel_t'(addr_bank % num_private_cache_q); end else begin - // Shared request: check TileID to decide local vs. remote. - // bank = num_private_cache_q + (addr_bank % num_shared_cache_q). - local_sel[port] = (addr_tile == tile_id_i); - core_req_sel[port] = local_sel[port] - ? core_sel_t'(num_private_cache_q + (addr_bank % num_shared_cache_q)) - : core_sel_t'(NumCache + (addr_tile % NumRemotePort)); + // Shared request: three-way classification. + if (NumRemoteGroupPort > 0 && !same_group) begin + // Inter-group: route to inter-group remote port. + core_req_sel[port] = core_sel_t'(NumCache + NumLGPort + + (port % NumRemoteGroupPort)); + end else if (addr_tile_id[LocalTileBits-1:0] != tile_id_i[LocalTileBits-1:0] + && !(NumTiles == 1)) begin + // Intra-group remote: different tile, same group. + core_req_sel[port] = core_sel_t'(NumCache + (port % NumLGPort)); + end else begin + // Local: same tile. + core_req_sel[port] = core_sel_t'(num_private_cache_q + + (addr_bank % num_shared_cache_q)); + end end end end @@ -269,16 +342,35 @@ module tcdm_cache_interco #( // ------------------------------------------------------------------------- // Response routing (xbar output-side selection) // ------------------------------------------------------------------------- + // + // Responses from local cache banks are routed back to the originating + // core using core_id. Responses from intra-group remote tiles and + // inter-group remote ports carry a tile_id that differs from tile_id_i; + // these are forwarded to the corresponding remote-in or inter-group remote-in port. + + for (genvar port = 0; port < NumOut; port++) begin : gen_rsp_sel + logic [TileBits-1:0] rsp_group_id; + if (NumRemoteGroupPort == 0) begin + assign rsp_group_id = my_group_id; + end else begin + assign rsp_group_id = mem_rsp[port].user.tile_id[TileBits-1:LocalTileBits]; + end - for (genvar port = 0; port < NumCache+NumRemotePort; port++) begin : gen_rsp_sel always_comb begin mem_rsp_sel[port] = mem_rsp[port].user.core_id; if (mem_rsp[port].user.tile_id != tile_id_i) begin - // Response destined for a remote tile: forward to the remote interco - // port that matches the incoming request path. The group-level xbar - // routes requests from source tile S to our remote-in slot - // (S % NumRemotePort), so responses must return via the same slot. - mem_rsp_sel[port] = mem_sel_t'(NumCores + (mem_rsp[port].user.tile_id % NumRemotePort)); + // Response originates from a different tile (intra-group remote or + // inter-group remote). Determine which input port set it came from. + if (NumRemoteGroupPort > 0 + && rsp_group_id != my_group_id) begin + // Inter-group: forward to the inter-group remote-in input port. + mem_rsp_sel[port] = mem_sel_t'(NumCores + NumLGPort + + (mem_rsp[port].user.core_id % NumRemoteGroupPort)); + end else begin + // Intra-group: forward to the remote-in input port. + mem_rsp_sel[port] = mem_sel_t'(NumCores + + (mem_rsp[port].user.core_id % NumLGPort)); + end end end end @@ -287,7 +379,7 @@ module tcdm_cache_interco #( // Input-side pipeline registers // ------------------------------------------------------------------------- - for (genvar port = 0; port < NumCores+NumRemotePort; port++) begin : gen_cache_interco_reg + for (genvar port = 0; port < NumInp; port++) begin : gen_cache_interco_reg spill_register #( .T (tcdm_req_chan_t ) ) i_tcdm_req_reg ( @@ -349,11 +441,11 @@ module tcdm_cache_interco #( // // lower = addr & ((1 << offset) - 1) // CLoffset, verbatim // rot_field = (addr >> offset) & ((1 << N) - 1) // N routing bits - // upper = addr >> (offset + N) // Tag+Index + // upper = addr >> (offset + N) // Tag+Index // // addr_rot = lower - // | (upper << offset) // close the hole - // | (rot_field << (AddrWidth - N)) // park at MSB + // | (upper << offset) // close the hole + // | (rot_field << (AddrWidth - N)) // park at MSB // Width of bits_to_rotate signal: must hold values up to CacheBankBits+TileBits. localparam int unsigned RotWidth = $clog2(CacheBankBits + TileBits + 1) + 1; @@ -408,7 +500,7 @@ module tcdm_cache_interco #( // Output assignment // ------------------------------------------------------------------------- - for (genvar port = 0; port < NumCache + NumRemotePort; port++) begin : gen_cache_io + for (genvar port = 0; port < NumOut; port++) begin : gen_cache_io always_comb begin mem_req_o[port] = '{ q : mem_req[port], @@ -419,10 +511,14 @@ module tcdm_cache_interco #( if (port < NumCache) begin // Local bank: forward address with routing bits rotated to MSB. mem_req_o[port].q.addr = addr_rot[port]; - end else begin - // Remote port: pass address untouched; extract target tile ID. + end else if (port < NumCache + NumLGPort) begin + // Intra-group remote port: pass address untouched; extract target tile ID. tile_sel_o[port - NumCache] = - mem_req[port].addr[(dynamic_offset_i + CacheBankBits) +: TileIDWidth]; + mem_req[port].addr[(dynamic_offset_i + CacheBankBits) +: TileBits]; + end else begin + // Inter-group remote port: pass address untouched; extract target tile ID. + remote_group_sel_o[port - NumCache - NumLGPort] = + mem_req[port].addr[(dynamic_offset_i + CacheBankBits) +: TileBits]; end end @@ -433,4 +529,21 @@ module tcdm_cache_interco #( assign mem_rsp_ready_o = mem_rsp_ready; + // ------------------------------------------------------------------------- + // Assertions + // ------------------------------------------------------------------------- +`ifndef TARGET_SYNTHESIS + // This is used to ensure we will not have illegal visits to DRAM + // This kind of error can be latent in the system until the entry is evicted + for (genvar x = 0; x < TotInPorts; x++) begin : gen_addr_assert + CoreReqAddrAboveDram: assert property ( + @(posedge clk_i) disable iff (!rst_ni !== '0) + core_req_i[x].q_valid |-> core_req_i[x].q.addr >= addr_t'(DramBaseAddr) + ) else begin + $error("[%m] port %0d: addr 0x%08x is below DramBaseAddr 0x%08x", + x, core_req_i[x].q.addr, DramBaseAddr); + end + end +`endif + endmodule diff --git a/hardware/tb/cachepool_cluster_wrapper.sv b/hardware/tb/cachepool_cluster_wrapper.sv index a9dba20..b80ab07 100644 --- a/hardware/tb/cachepool_cluster_wrapper.sv +++ b/hardware/tb/cachepool_cluster_wrapper.sv @@ -13,50 +13,56 @@ module cachepool_cluster_wrapper import fpnew_pkg::fpu_implementation_t; import snitch_pma_pkg::snitch_pma_t; #( - parameter int unsigned AxiAddrWidth = SpatzAxiAddrWidth, - parameter int unsigned AxiDataWidth = SpatzAxiDataWidth, - parameter int unsigned AxiUserWidth = SpatzAxiUserWidth, - parameter int unsigned AxiInIdWidth = SpatzAxiIdInWidth, - parameter int unsigned AxiOutIdWidth = SpatzAxiIdOutWidth, - - parameter type axi_in_resp_t = spatz_axi_in_resp_t, - parameter type axi_in_req_t = spatz_axi_in_req_t, - - parameter type axi_out_resp_t = spatz_axi_out_resp_t, - parameter type axi_out_req_t = spatz_axi_out_req_t, - - parameter type axi_narrow_req_t = spatz_axi_narrow_req_t, - parameter type axi_narrow_resp_t = spatz_axi_narrow_resp_t + parameter int unsigned AxiAddrWidth = SpatzAxiAddrWidth, + parameter int unsigned AxiDataWidth = SpatzAxiDataWidth, + parameter int unsigned AxiUserWidth = SpatzAxiUserWidth, + // External wide output ID width (wrapper → DRAM); remapped from SpatzAxiIdOutWidth inside. + parameter int unsigned AxiOutIdWidth = WrapperAxiIdOutWidth, + // External narrow output ID width (UART, wrapper → SoC); remapped from SpatzAxiUartIdWidth inside. + parameter int unsigned AxiNarrowOutIdWidth = WrapperAxiNarrowIdOutWidth, + + // External wide output types use the wrapper-narrowed ID (WrapperAxiIdOutWidth). + parameter type axi_out_req_t = spatz_axi_wrapper_out_req_t, + parameter type axi_out_resp_t = spatz_axi_wrapper_out_resp_t, + + // External narrow output types use the wrapper-narrowed ID (WrapperAxiNarrowIdOutWidth). + parameter type axi_narrow_out_req_t = spatz_axi_wrapper_narrow_out_req_t, + parameter type axi_narrow_out_resp_t = spatz_axi_wrapper_narrow_out_resp_t )( - input logic clk_i, - input logic rst_ni, - output logic [3:0] eoc_o, - input logic debug_req_i, - - input logic meip_i, - input logic mtip_i, - input logic msip_i, - output logic cluster_probe_o, - input axi_in_req_t axi_in_req_i, - output axi_in_resp_t axi_in_resp_o, - /// AXI Narrow out-port (UART) - output axi_uart_req_t axi_narrow_req_o, - input axi_uart_resp_t axi_narrow_resp_i, - output axi_out_req_t [NumClusterSlv-1:0] axi_out_req_o, - input axi_out_resp_t [NumClusterSlv-1:0] axi_out_resp_i + input logic clk_i, + input logic rst_ni, + output logic [3:0] eoc_o, + input logic debug_req_i, + + input logic meip_i, + input logic mtip_i, + input logic msip_i, + output logic cluster_probe_o, + // REQRSP peripheral in-port (from SoC/testbench); 32b narrow with refill_user_t. + input peri_narrow_req_t peri_ext_req_i, + output peri_narrow_rsp_t peri_ext_rsp_o, + /// AXI Narrow out-port (UART); external ID = AxiNarrowOutIdWidth. + output axi_narrow_out_req_t axi_narrow_req_o, + input axi_narrow_out_resp_t axi_narrow_resp_i, + // AXI wide master ports (to DRAM); external ID = AxiOutIdWidth. + output axi_out_req_t [NumClusterSlv-1:0] axi_out_req_o, + input axi_out_resp_t [NumClusterSlv-1:0] axi_out_resp_i ); - - spatz_axi_iwc_out_req_t [NumClusterSlv-1:0] axi_from_cluster_iwc_req; - spatz_axi_iwc_out_resp_t [NumClusterSlv-1:0] axi_from_cluster_iwc_resp; + // Internal signals between wrapper remappers and cluster (fat IDs). + axi_uart_req_t axi_cluster_narrow_req; + axi_uart_resp_t axi_cluster_narrow_resp; + spatz_axi_out_req_t [NumClusterSlv-1:0] axi_cluster_out_req; + spatz_axi_out_resp_t [NumClusterSlv-1:0] axi_cluster_out_resp; // Spatz cluster under test. + // Internal AXI types are fixed (full-width IDs); the wrapper remaps at both boundaries. cachepool_cluster #( .AxiAddrWidth (AxiAddrWidth ), .AxiDataWidth (AxiDataWidth ), - .AxiIdWidthIn (AxiInIdWidth ), - .AxiIdWidthOut (AxiOutIdWidth ), + .AxiIdWidthIn (SpatzAxiIdInWidth ), + .AxiIdWidthOut (SpatzAxiIdOutWidth ), .AxiUserWidth (AxiUserWidth ), .BootAddr (BootAddr ), .UartAddr (UartAddr ), @@ -74,15 +80,8 @@ module cachepool_cluster_wrapper .NumIntOutstandingLoads (NumIntOutstandingLoads ), .NumIntOutstandingMem (NumIntOutstandingMem ), .NumSpatzOutstandingLoads (NumSpatzOutstandingLoads ), - .axi_in_req_t (axi_in_req_t ), - .axi_in_resp_t (axi_in_resp_t ), - .axi_narrow_req_t (axi_narrow_req_t ), - .axi_narrow_resp_t (axi_narrow_resp_t ), - .axi_out_req_t (axi_out_req_t ), - .axi_out_resp_t (axi_out_resp_t ), - .Xdma (4'h0 ), - .DMAAxiReqFifoDepth (3 ), - .DMAReqFifoDepth (3 ), + .axi_out_req_t (spatz_axi_out_req_t ), + .axi_out_resp_t (spatz_axi_out_resp_t ), .RegisterOffloadRsp (1 ), .RegisterCoreReq (1 ), .RegisterCoreRsp (1 ), @@ -97,22 +96,71 @@ module cachepool_cluster_wrapper .eoc_o (eoc_o ), .impl_i ('0 ), .error_o ( ), - .debug_req_i ({NumCores{debug_req_i}} ), - .meip_i ({NumCores{meip_i}} ), - .mtip_i ({NumCores{mtip_i}} ), - .msip_i ({NumCores{msip_i}} ), + .debug_req_i (debug_req_i ), + .meip_i (meip_i ), + .mtip_i (mtip_i ), + .msip_i (msip_i ), .hart_base_id_i (10'h0 ), .cluster_base_addr_i (TCDMStartAddr ), .cluster_probe_o (cluster_probe_o ), - .axi_in_req_i , - .axi_in_resp_o , - .axi_narrow_req_o , - .axi_narrow_resp_i , - // AXI Master Port - .axi_out_req_o ( axi_out_req_o ), - .axi_out_resp_i ( axi_out_resp_i ) + // REQRSP peripheral in-port: passed straight through from wrapper port. + .peri_ext_req_i (peri_ext_req_i ), + .peri_ext_rsp_o (peri_ext_rsp_o ), + .axi_narrow_req_o (axi_cluster_narrow_req ), + .axi_narrow_resp_i (axi_cluster_narrow_resp ), + // AXI Master Port (fat IDs; wrapper remaps before external port). + .axi_out_req_o (axi_cluster_out_req ), + .axi_out_resp_i (axi_cluster_out_resp ) ); + + // Remap SpatzAxiUartIdWidth -> WrapperAxiNarrowIdOutWidth on the UART master port. + axi_id_remap #( + .AxiSlvPortIdWidth ( SpatzAxiUartIdWidth ), + // MaxUniqIds capped by the slave port's ID space. + .AxiSlvPortMaxUniqIds ( 2**SpatzAxiUartIdWidth ), + .AxiMaxTxnsPerId ( NumAxiMaxTrans ), + .AxiMstPortIdWidth ( WrapperAxiNarrowIdOutWidth ), + .slv_req_t ( axi_uart_req_t ), + .slv_resp_t ( axi_uart_resp_t ), + .mst_req_t ( axi_narrow_out_req_t ), + .mst_resp_t ( axi_narrow_out_resp_t ) + ) i_narrow_out_id_remap ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .slv_req_i ( axi_cluster_narrow_req ), + .slv_resp_o ( axi_cluster_narrow_resp ), + .mst_req_o ( axi_narrow_req_o ), + .mst_resp_i ( axi_narrow_resp_i ) + ); + + // Remap SpatzAxiIdOutWidth -> WrapperAxiIdOutWidth per DRAM channel. + // With FlooNoC mesh, both are 6 bits (no-op remap); kept for interface stability. + for (genvar ch = 0; ch < NumClusterSlv; ch++) begin : gen_out_id_remap + if (SpatzAxiIdOutWidth != WrapperAxiIdOutWidth) begin + axi_id_remap #( + .AxiSlvPortIdWidth ( SpatzAxiIdOutWidth ), + .AxiSlvPortMaxUniqIds ( NumAxiMaxTrans ), + .AxiMaxTxnsPerId ( NumAxiMaxTrans ), + .AxiMstPortIdWidth ( WrapperAxiIdOutWidth ), + .slv_req_t ( spatz_axi_out_req_t ), + .slv_resp_t ( spatz_axi_out_resp_t ), + .mst_req_t ( spatz_axi_wrapper_out_req_t ), + .mst_resp_t ( spatz_axi_wrapper_out_resp_t ) + ) i_out_id_remap ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .slv_req_i ( axi_cluster_out_req [ch] ), + .slv_resp_o ( axi_cluster_out_resp [ch] ), + .mst_req_o ( axi_out_req_o [ch] ), + .mst_resp_i ( axi_out_resp_i [ch] ) + ); + end else begin + assign axi_out_req_o[ch] = axi_cluster_out_req[ch]; + assign axi_cluster_out_resp[ch] = axi_out_resp_i[ch]; + end + end + // AXI utilization monitor `ifndef TARGET_SYNTHESIS typedef logic [31:0] cnt_t; @@ -241,11 +289,12 @@ module cachepool_cluster_wrapper if (AxiUserWidth != SpatzAxiUserWidth) $error("[spatz_cluster_wrapper] AXI User Width does not match the configuration."); - if (AxiInIdWidth != SpatzAxiIdInWidth) - $error("[spatz_cluster_wrapper] AXI Id Width (In) does not match the configuration."); - if (AxiOutIdWidth != SpatzAxiIdOutWidth) + if (AxiOutIdWidth != WrapperAxiIdOutWidth) $error("[spatz_cluster_wrapper] AXI Id Width (Out) does not match the configuration."); + + if (AxiNarrowOutIdWidth != WrapperAxiNarrowIdOutWidth) + $error("[spatz_cluster_wrapper] AXI Narrow Id Width (Out) does not match the configuration."); `endif endmodule diff --git a/hardware/tb/cachepool_monitor.sv b/hardware/tb/cachepool_monitor.sv new file mode 100644 index 0000000..0ddd0d4 --- /dev/null +++ b/hardware/tb/cachepool_monitor.sv @@ -0,0 +1,872 @@ +// Copyright 2025 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +// Author: Diyou Shen + +`include "common_cells/registers.svh" + +/// Simulation-only, central performance/session monitor for CachePool. +/// Instantiated as a sibling of the DUT at the testbench level and reaches +/// into it purely via hierarchical (cross-module) references, so none of +/// this needs to be threaded through the synthesizable RTL. +/// +/// The whole run is split into "sessions" delimited by edges of +/// cluster_probe_i (the SPATZ_CLUSTER_PROBE peripheral bit toggled by +/// start_kernel()/stop_kernel() in software): each edge closes the current +/// session (dumped to file) and opens a new one. +module cachepool_monitor + import cachepool_pkg::*; +( + input logic clk_i, + input logic rst_ni, + input logic cluster_probe_i +); + +`ifndef TARGET_SYNTHESIS + typedef logic [31:0] cnt_t; + + `define TILE_PATH(gy, gx, t) \ + i_cluster_wrapper.i_cluster.gen_group_y[gy].gen_group_x[gx].i_group.i_group.gen_tiles[t].gen_tile.i_tile + + `define CC_PATH(gy, gx, t, c) \ + `TILE_PATH(gy, gx, t).gen_core[c].i_cachepool_cc + + `define CLUSTER_PATH i_cluster_wrapper.i_cluster + + // --------------------------------------------------------------------- + // Session bookkeeping shared by every sub-monitor below. + // --------------------------------------------------------------------- + logic probe_q; + `FF(probe_q, cluster_probe_i, 1'b0) + logic session_edge; + assign session_edge = cluster_probe_i ^ probe_q; + + cnt_t session_idx_d, session_idx_q; + assign session_idx_d = session_edge ? session_idx_q + 1 : session_idx_q; + `FF(session_idx_q, session_idx_d, '0) + + cnt_t cycle_d, cycle_q; + assign cycle_d = cycle_q + 1; + `FF(cycle_q, cycle_d, '0) + + cnt_t session_start_cycle_d, session_start_cycle_q; + assign session_start_cycle_d = session_edge ? cycle_q : session_start_cycle_q; + `FF(session_start_cycle_q, session_start_cycle_d, '0) + + // Labels a session by whether cluster_probe was high (a kernel actually + // running) or low (idle/setup time) during it. At the moment session_edge + // fires, probe_q still holds the phase of the session that just closed; + // for the trailing/incomplete session dumped from `final`, use the live + // cluster_probe_i instead since no edge has fired to register it yet. + function automatic string session_tag(input logic phase, input bit is_final); + session_tag = is_final ? (phase ? "kernel-final" : "idle-final") + : (phase ? "kernel" : "idle"); + endfunction + + // --------------------------------------------------------------------- + // Per-(group, tile) Traffic Locality Monitor. + // Counts accepted TCDM requests per port slot, classified by the + // interco's own routing decision: local cache bank / local-group / + // remote-group (inter-group L1 NoC). + // --------------------------------------------------------------------- + for (genvar gy = 0; gy < NumGroupsY; gy++) begin : gen_mon_gy + for (genvar gx = 0; gx < NumGroupsX; gx++) begin : gen_mon_gx + for (genvar t = 0; t < NumTilesPerGroup; t++) begin : gen_mon_tile + + cnt_t [NrTCDMPortsPerCore-1:0] local_cnt_d, local_cnt_q; + cnt_t [NrTCDMPortsPerCore-1:0] group_cnt_d, group_cnt_q; + cnt_t [NrTCDMPortsPerCore-1:0] remote_cnt_d, remote_cnt_q; + `FF(local_cnt_q, local_cnt_d, '0) + `FF(group_cnt_q, group_cnt_d, '0) + `FF(remote_cnt_q, remote_cnt_d, '0) + + // Congestion: cycles a request sat with q_valid asserted (accepted + // or not). avg_acceptance_cycle = valid_cyc / accepted_cnt, mirroring + // spatz_vlsu.sv's existing "Mem AVG Req Accept Cycles" metric. + cnt_t [NrTCDMPortsPerCore-1:0] local_valid_cyc_d, local_valid_cyc_q; + cnt_t [NrTCDMPortsPerCore-1:0] group_valid_cyc_d, group_valid_cyc_q; + cnt_t [NrTCDMPortsPerCore-1:0] remote_valid_cyc_d, remote_valid_cyc_q; + `FF(local_valid_cyc_q, local_valid_cyc_d, '0) + `FF(group_valid_cyc_q, group_valid_cyc_d, '0) + `FF(remote_valid_cyc_q, remote_valid_cyc_d, '0) + + for (genvar j = 0; j < NrTCDMPortsPerCore; j++) begin : gen_mon_port + always_comb begin + automatic cnt_t local_inc, group_inc, local_valid_inc, group_valid_inc; + local_inc = '0; + group_inc = '0; + local_valid_inc = '0; + group_valid_inc = '0; + + for (int unsigned cb = 0; cb < NumL1CtrlTile; cb++) begin + if (`TILE_PATH(gy, gx, t).cache_xbar_req[j][cb].q_valid) begin + local_valid_inc++; + if (`TILE_PATH(gy, gx, t).cache_xbar_rsp[j][cb].q_ready) + local_inc++; + end + end + + for (int unsigned r = 0; r < NumLGPortCore; r++) begin + if (`TILE_PATH(gy, gx, t).gen_cache_xbar[j].xbar_remote_req_o[r].q_valid) begin + group_valid_inc++; + if (`TILE_PATH(gy, gx, t).gen_cache_xbar[j].xbar_remote_rsp_i[r].q_ready) + group_inc++; + end + end + + local_cnt_d[j] = session_edge ? '0 : local_cnt_q[j] + local_inc; + group_cnt_d[j] = session_edge ? '0 : group_cnt_q[j] + group_inc; + local_valid_cyc_d[j] = session_edge ? '0 : local_valid_cyc_q[j] + local_valid_inc; + group_valid_cyc_d[j] = session_edge ? '0 : group_valid_cyc_q[j] + group_valid_inc; + end + + if (NumRemoteGroupPortCore > 0) begin : gen_mon_remote + always_comb begin + automatic cnt_t remote_inc, remote_valid_inc; + remote_inc = '0; + remote_valid_inc = '0; + for (int unsigned r = 0; r < NumRemoteGroupPortCore; r++) begin + if (`TILE_PATH(gy, gx, t).gen_cache_xbar[j].gen_remote_group_slice.xbar_remote_group_out_req[r].q_valid) begin + remote_valid_inc++; + if (`TILE_PATH(gy, gx, t).gen_cache_xbar[j].gen_remote_group_slice.xbar_remote_group_out_rsp[r].q_ready) + remote_inc++; + end + end + remote_cnt_d[j] = session_edge ? '0 : remote_cnt_q[j] + remote_inc; + remote_valid_cyc_d[j] = session_edge ? '0 : remote_valid_cyc_q[j] + remote_valid_inc; + end + end else begin : gen_mon_no_remote + assign remote_cnt_d[j] = session_edge ? '0 : remote_cnt_q[j]; + assign remote_valid_cyc_d[j] = session_edge ? '0 : remote_valid_cyc_q[j]; + end + end + + int tile_f; + string tile_f_name; + + initial begin + @(posedge clk_i); + $sformat(tile_f_name, "sim/bin/logs/others/monitor_tile_traffic_g%0d_%0d_t%0d.txt", gy, gx, t); + tile_f = $fopen(tile_f_name, "w"); + end + + // A function (never a task) so it can legally be called from the + // `final` block below: functions cannot contain timing controls by + // construction, so simulators never warn about calling them there. + function automatic void dump_tile_session(input cnt_t idx, input string tag); + $fwrite(tile_f, "*** Session %0d (%s): cycles [%0d, %0d) ***\n", + idx, tag, session_start_cycle_q, cycle_q); + for (int unsigned j = 0; j < NrTCDMPortsPerCore; j++) begin + automatic real tot = local_cnt_q[j] + group_cnt_q[j] + remote_cnt_q[j]; + automatic real l_pct = tot == 0 ? 0 : 100 * local_cnt_q[j] / tot; + automatic real g_pct = tot == 0 ? 0 : 100 * group_cnt_q[j] / tot; + automatic real r_pct = tot == 0 ? 0 : 100 * remote_cnt_q[j] / tot; + automatic real l_avg = local_cnt_q[j] == 0 ? 0 : real'(local_valid_cyc_q[j]) / real'(local_cnt_q[j]); + automatic real g_avg = group_cnt_q[j] == 0 ? 0 : real'(group_valid_cyc_q[j]) / real'(group_cnt_q[j]); + automatic real r_avg = remote_cnt_q[j] == 0 ? 0 : real'(remote_valid_cyc_q[j]) / real'(remote_cnt_q[j]); + $fwrite(tile_f, " Port %0d: local=%0d (%0.2f%%) group=%0d (%0.2f%%) remote=%0d (%0.2f%%)\n", + j, local_cnt_q[j], l_pct, group_cnt_q[j], g_pct, remote_cnt_q[j], r_pct); + $fwrite(tile_f, " avg req accept cycles: local=%0.2f group=%0.2f remote=%0.2f\n", + l_avg, g_avg, r_avg); + end + $fwrite(tile_f, "\n"); + endfunction + + always_ff @(posedge clk_i) begin + if (session_edge) dump_tile_session(session_idx_q, session_tag(probe_q, 1'b0)); + end + + final begin + dump_tile_session(session_idx_q, session_tag(cluster_probe_i, 1'b1)); + $fclose(tile_f); + end + end + end + end + + // --------------------------------------------------------------------- + // L1 Inter-Group NoC Monitor. + // Cluster-level mesh-link handshakes (noc_req/rsp_out/in), tracked + // per-(group, direction, tile-port) so that each physical tile-level NoC + // link can be told apart -- one router pair exists per (group, direction, + // tile-port) inside cachepool_group_noc_wrapper.sv, and aggregating them + // away (as before) hides load imbalance across tiles sharing a direction. + // Direction index (floo_pkg order): 0=North, 1=East, 2=South, 3=West. + // "out" = this group sending on that link, "in" = this group receiving. + // + // One file is produced per group: + // - monitor_l1noc_g.txt : human-readable, aggregated over ports. + // (The per-link link_l1noc_g.csv export was removed -- superseded by + // hardware/tb/cachepool_noc_profiling.sv's cycle-accurate per-flit logs, + // consumed by util/scripts/noc_profiling_to_vis4mesh.py.) + // --------------------------------------------------------------------- + localparam int unsigned NumNoCLinkPorts = NumTilesPerGroup * NumNoCPortsPerTile; + + for (genvar g = 0; g < NumGroups; g++) begin : gen_l1noc_g + cnt_t [3:0][NumNoCLinkPorts-1:0] req_out_cnt_d, req_out_cnt_q, req_out_vcyc_d, req_out_vcyc_q; + cnt_t [3:0][NumNoCLinkPorts-1:0] req_in_cnt_d, req_in_cnt_q, req_in_vcyc_d, req_in_vcyc_q; + cnt_t [3:0][NumNoCLinkPorts-1:0] rsp_out_cnt_d, rsp_out_cnt_q, rsp_out_vcyc_d, rsp_out_vcyc_q; + cnt_t [3:0][NumNoCLinkPorts-1:0] rsp_in_cnt_d, rsp_in_cnt_q, rsp_in_vcyc_d, rsp_in_vcyc_q; + `FF(req_out_cnt_q, req_out_cnt_d, '0) + `FF(req_out_vcyc_q, req_out_vcyc_d, '0) + `FF(req_in_cnt_q, req_in_cnt_d, '0) + `FF(req_in_vcyc_q, req_in_vcyc_d, '0) + `FF(rsp_out_cnt_q, rsp_out_cnt_d, '0) + `FF(rsp_out_vcyc_q, rsp_out_vcyc_d, '0) + `FF(rsp_in_cnt_q, rsp_in_cnt_d, '0) + `FF(rsp_in_vcyc_q, rsp_in_vcyc_d, '0) + + for (genvar dir = 0; dir < 4; dir++) begin : gen_l1noc_dir + for (genvar p = 0; p < NumNoCLinkPorts; p++) begin : gen_l1noc_port + always_comb begin + automatic cnt_t ro_c, ro_v, ri_c, ri_v, so_c, so_v, si_c, si_v; + ro_c = '0; ro_v = '0; ri_c = '0; ri_v = '0; + so_c = '0; so_v = '0; si_c = '0; si_v = '0; + if (`CLUSTER_PATH.noc_req_out_valid[g][dir][p]) begin + ro_v = 1; + if (`CLUSTER_PATH.noc_req_out_ready[g][dir][p]) ro_c = 1; + end + if (`CLUSTER_PATH.noc_req_in_valid[g][dir][p]) begin + ri_v = 1; + if (`CLUSTER_PATH.noc_req_in_ready[g][dir][p]) ri_c = 1; + end + if (`CLUSTER_PATH.noc_rsp_out_valid[g][dir][p]) begin + so_v = 1; + if (`CLUSTER_PATH.noc_rsp_out_ready[g][dir][p]) so_c = 1; + end + if (`CLUSTER_PATH.noc_rsp_in_valid[g][dir][p]) begin + si_v = 1; + if (`CLUSTER_PATH.noc_rsp_in_ready[g][dir][p]) si_c = 1; + end + req_out_cnt_d[dir][p] = session_edge ? '0 : req_out_cnt_q[dir][p] + ro_c; + req_out_vcyc_d[dir][p] = session_edge ? '0 : req_out_vcyc_q[dir][p] + ro_v; + req_in_cnt_d[dir][p] = session_edge ? '0 : req_in_cnt_q[dir][p] + ri_c; + req_in_vcyc_d[dir][p] = session_edge ? '0 : req_in_vcyc_q[dir][p] + ri_v; + rsp_out_cnt_d[dir][p] = session_edge ? '0 : rsp_out_cnt_q[dir][p] + so_c; + rsp_out_vcyc_d[dir][p] = session_edge ? '0 : rsp_out_vcyc_q[dir][p] + so_v; + rsp_in_cnt_d[dir][p] = session_edge ? '0 : rsp_in_cnt_q[dir][p] + si_c; + rsp_in_vcyc_d[dir][p] = session_edge ? '0 : rsp_in_vcyc_q[dir][p] + si_v; + end + end + end + + int l1noc_f; + string l1noc_f_name; + + initial begin + @(posedge clk_i); + $sformat(l1noc_f_name, "sim/bin/logs/noc/monitor_l1noc_g%0d.txt", g); + l1noc_f = $fopen(l1noc_f_name, "w"); + end + + function automatic void dump_l1noc_session(input cnt_t idx, input string tag); + automatic string dir_name [4] = '{"North", "East", "South", "West"}; + $fwrite(l1noc_f, "*** Session %0d (%s): cycles [%0d, %0d) ***\n", + idx, tag, session_start_cycle_q, cycle_q); + for (int unsigned dir = 0; dir < 4; dir++) begin + automatic cnt_t ro_c, ro_v, ri_c, ri_v, so_c, so_v, si_c, si_v; + automatic real ro_avg, ri_avg, so_avg, si_avg; + ro_c = '0; ro_v = '0; ri_c = '0; ri_v = '0; + so_c = '0; so_v = '0; si_c = '0; si_v = '0; + for (int unsigned p = 0; p < NumNoCLinkPorts; p++) begin + ro_c += req_out_cnt_q[dir][p]; ro_v += req_out_vcyc_q[dir][p]; + ri_c += req_in_cnt_q[dir][p]; ri_v += req_in_vcyc_q[dir][p]; + so_c += rsp_out_cnt_q[dir][p]; so_v += rsp_out_vcyc_q[dir][p]; + si_c += rsp_in_cnt_q[dir][p]; si_v += rsp_in_vcyc_q[dir][p]; + end + ro_avg = ro_c == 0 ? 0 : real'(ro_v) / real'(ro_c); + ri_avg = ri_c == 0 ? 0 : real'(ri_v) / real'(ri_c); + so_avg = so_c == 0 ? 0 : real'(so_v) / real'(so_c); + si_avg = si_c == 0 ? 0 : real'(si_v) / real'(si_c); + $fwrite(l1noc_f, " %s:\n", dir_name[dir]); + $fwrite(l1noc_f, " REQ out: accepted=%0d valid_cyc=%0d avg_accept_cyc=%0.2f\n", + ro_c, ro_v, ro_avg); + $fwrite(l1noc_f, " REQ in : accepted=%0d valid_cyc=%0d avg_accept_cyc=%0.2f\n", + ri_c, ri_v, ri_avg); + $fwrite(l1noc_f, " RSP out: accepted=%0d valid_cyc=%0d avg_accept_cyc=%0.2f\n", + so_c, so_v, so_avg); + $fwrite(l1noc_f, " RSP in : accepted=%0d valid_cyc=%0d avg_accept_cyc=%0.2f\n", + si_c, si_v, si_avg); + end + $fwrite(l1noc_f, "\n"); + endfunction + + always_ff @(posedge clk_i) begin + if (session_edge) begin + dump_l1noc_session(session_idx_q, session_tag(probe_q, 1'b0)); + end + end + + final begin + dump_l1noc_session(session_idx_q, session_tag(cluster_probe_i, 1'b1)); + $fclose(l1noc_f); + end + end + + // --------------------------------------------------------------------- + // L2 (DRAM) NoC Monitor. + // Cluster-level mesh-link handshakes (l2_req/rsp_out/in). Unlike L1, one + // router pair exists per group (not per tile), so there is no port + // dimension to aggregate over. Direction index matches L1: 0=North, + // 1=East, 2=South, 3=West. + // --------------------------------------------------------------------- + for (genvar gx = 0; gx < NumGroupsX; gx++) begin : gen_l2noc_gx + for (genvar gy = 0; gy < NumGroupsY; gy++) begin : gen_l2noc_gy + + cnt_t [3:0] req_out_cnt_d, req_out_cnt_q, req_out_vcyc_d, req_out_vcyc_q; + cnt_t [3:0] req_in_cnt_d, req_in_cnt_q, req_in_vcyc_d, req_in_vcyc_q; + cnt_t [3:0] rsp_out_cnt_d, rsp_out_cnt_q, rsp_out_vcyc_d, rsp_out_vcyc_q; + cnt_t [3:0] rsp_in_cnt_d, rsp_in_cnt_q, rsp_in_vcyc_d, rsp_in_vcyc_q; + `FF(req_out_cnt_q, req_out_cnt_d, '0) + `FF(req_out_vcyc_q, req_out_vcyc_d, '0) + `FF(req_in_cnt_q, req_in_cnt_d, '0) + `FF(req_in_vcyc_q, req_in_vcyc_d, '0) + `FF(rsp_out_cnt_q, rsp_out_cnt_d, '0) + `FF(rsp_out_vcyc_q, rsp_out_vcyc_d, '0) + `FF(rsp_in_cnt_q, rsp_in_cnt_d, '0) + `FF(rsp_in_vcyc_q, rsp_in_vcyc_d, '0) + + for (genvar dir = 0; dir < 4; dir++) begin : gen_l2noc_dir + always_comb begin + automatic cnt_t ro_c, ro_v, ri_c, ri_v, so_c, so_v, si_c, si_v; + ro_c = '0; ro_v = '0; ri_c = '0; ri_v = '0; + so_c = '0; so_v = '0; si_c = '0; si_v = '0; + if (`CLUSTER_PATH.l2_req_out_valid[gx][gy][dir]) begin + ro_v = 1; + if (`CLUSTER_PATH.l2_req_out_ready[gx][gy][dir]) ro_c = 1; + end + if (`CLUSTER_PATH.l2_req_in_valid[gx][gy][dir]) begin + ri_v = 1; + if (`CLUSTER_PATH.l2_req_in_ready[gx][gy][dir]) ri_c = 1; + end + if (`CLUSTER_PATH.l2_rsp_out_valid[gx][gy][dir]) begin + so_v = 1; + if (`CLUSTER_PATH.l2_rsp_out_ready[gx][gy][dir]) so_c = 1; + end + if (`CLUSTER_PATH.l2_rsp_in_valid[gx][gy][dir]) begin + si_v = 1; + if (`CLUSTER_PATH.l2_rsp_in_ready[gx][gy][dir]) si_c = 1; + end + req_out_cnt_d[dir] = session_edge ? '0 : req_out_cnt_q[dir] + ro_c; + req_out_vcyc_d[dir] = session_edge ? '0 : req_out_vcyc_q[dir] + ro_v; + req_in_cnt_d[dir] = session_edge ? '0 : req_in_cnt_q[dir] + ri_c; + req_in_vcyc_d[dir] = session_edge ? '0 : req_in_vcyc_q[dir] + ri_v; + rsp_out_cnt_d[dir] = session_edge ? '0 : rsp_out_cnt_q[dir] + so_c; + rsp_out_vcyc_d[dir] = session_edge ? '0 : rsp_out_vcyc_q[dir] + so_v; + rsp_in_cnt_d[dir] = session_edge ? '0 : rsp_in_cnt_q[dir] + si_c; + rsp_in_vcyc_d[dir] = session_edge ? '0 : rsp_in_vcyc_q[dir] + si_v; + end + end + + int l2noc_f; + string l2noc_f_name; + + initial begin + @(posedge clk_i); + $sformat(l2noc_f_name, "sim/bin/logs/noc/monitor_l2noc_g%0d_%0d.txt", gx, gy); + l2noc_f = $fopen(l2noc_f_name, "w"); + end + + function automatic void dump_l2noc_session(input cnt_t idx, input string tag); + automatic string dir_name [4] = '{"North", "East", "South", "West"}; + $fwrite(l2noc_f, "*** Session %0d (%s): cycles [%0d, %0d) ***\n", + idx, tag, session_start_cycle_q, cycle_q); + for (int unsigned dir = 0; dir < 4; dir++) begin + automatic real ro_avg = req_out_cnt_q[dir] == 0 ? 0 : real'(req_out_vcyc_q[dir]) / real'(req_out_cnt_q[dir]); + automatic real ri_avg = req_in_cnt_q[dir] == 0 ? 0 : real'(req_in_vcyc_q[dir]) / real'(req_in_cnt_q[dir]); + automatic real so_avg = rsp_out_cnt_q[dir] == 0 ? 0 : real'(rsp_out_vcyc_q[dir]) / real'(rsp_out_cnt_q[dir]); + automatic real si_avg = rsp_in_cnt_q[dir] == 0 ? 0 : real'(rsp_in_vcyc_q[dir]) / real'(rsp_in_cnt_q[dir]); + $fwrite(l2noc_f, " %s:\n", dir_name[dir]); + $fwrite(l2noc_f, " REQ out: accepted=%0d valid_cyc=%0d avg_accept_cyc=%0.2f\n", + req_out_cnt_q[dir], req_out_vcyc_q[dir], ro_avg); + $fwrite(l2noc_f, " REQ in : accepted=%0d valid_cyc=%0d avg_accept_cyc=%0.2f\n", + req_in_cnt_q[dir], req_in_vcyc_q[dir], ri_avg); + $fwrite(l2noc_f, " RSP out: accepted=%0d valid_cyc=%0d avg_accept_cyc=%0.2f\n", + rsp_out_cnt_q[dir], rsp_out_vcyc_q[dir], so_avg); + $fwrite(l2noc_f, " RSP in : accepted=%0d valid_cyc=%0d avg_accept_cyc=%0.2f\n", + rsp_in_cnt_q[dir], rsp_in_vcyc_q[dir], si_avg); + end + $fwrite(l2noc_f, "\n"); + endfunction + + always_ff @(posedge clk_i) begin + if (session_edge) begin + dump_l2noc_session(session_idx_q, session_tag(probe_q, 1'b0)); + end + end + + final begin + dump_l2noc_session(session_idx_q, session_tag(cluster_probe_i, 1'b1)); + $fclose(l2noc_f); + end + end + end + + // --------------------------------------------------------------------- + // L2 DRAM Channel Monitor. + // Per-HBM-channel AXI congestion at the DRAM endpoint, one level beyond + // the L2 NoC mesh above: taps tb_cachepool.sv's own axi_dram_req/ + // axi_from_cluster_resp arrays (the AXI req/resp feeding each + // axi_dram_sim instance), so it shows whether the NoC or the DRAM model + // itself is the bottleneck. + // --------------------------------------------------------------------- + for (genvar mem = 0; mem < NumL2Channel; mem++) begin : gen_dram_mon + + cnt_t aw_cnt_d, aw_cnt_q, aw_vcyc_d, aw_vcyc_q; + cnt_t ar_cnt_d, ar_cnt_q, ar_vcyc_d, ar_vcyc_q; + cnt_t w_cnt_d, w_cnt_q, w_vcyc_d, w_vcyc_q; + cnt_t r_cnt_d, r_cnt_q, r_vcyc_d, r_vcyc_q; + cnt_t b_cnt_d, b_cnt_q, b_vcyc_d, b_vcyc_q; + `FF(aw_cnt_q, aw_cnt_d, '0) `FF(aw_vcyc_q, aw_vcyc_d, '0) + `FF(ar_cnt_q, ar_cnt_d, '0) `FF(ar_vcyc_q, ar_vcyc_d, '0) + `FF(w_cnt_q, w_cnt_d, '0) `FF(w_vcyc_q, w_vcyc_d, '0) + `FF(r_cnt_q, r_cnt_d, '0) `FF(r_vcyc_q, r_vcyc_d, '0) + `FF(b_cnt_q, b_cnt_d, '0) `FF(b_vcyc_q, b_vcyc_d, '0) + + always_comb begin + automatic cnt_t aw_c, aw_v, ar_c, ar_v, w_c, w_v, r_c, r_v, b_c, b_v; + aw_c = '0; aw_v = '0; ar_c = '0; ar_v = '0; w_c = '0; + w_v = '0; r_c = '0; r_v = '0; b_c = '0; b_v = '0; + + if ($root.tb_cachepool.axi_dram_req[mem].aw_valid) begin + aw_v = 1; + if ($root.tb_cachepool.axi_from_cluster_resp[mem].aw_ready) aw_c = 1; + end + if ($root.tb_cachepool.axi_dram_req[mem].ar_valid) begin + ar_v = 1; + if ($root.tb_cachepool.axi_from_cluster_resp[mem].ar_ready) ar_c = 1; + end + if ($root.tb_cachepool.axi_dram_req[mem].w_valid) begin + w_v = 1; + if ($root.tb_cachepool.axi_from_cluster_resp[mem].w_ready) w_c = 1; + end + if ($root.tb_cachepool.axi_from_cluster_resp[mem].r_valid) begin + r_v = 1; + if ($root.tb_cachepool.axi_dram_req[mem].r_ready) r_c = 1; + end + if ($root.tb_cachepool.axi_from_cluster_resp[mem].b_valid) begin + b_v = 1; + if ($root.tb_cachepool.axi_dram_req[mem].b_ready) b_c = 1; + end + + aw_cnt_d = session_edge ? '0 : aw_cnt_q + aw_c; + aw_vcyc_d = session_edge ? '0 : aw_vcyc_q + aw_v; + ar_cnt_d = session_edge ? '0 : ar_cnt_q + ar_c; + ar_vcyc_d = session_edge ? '0 : ar_vcyc_q + ar_v; + w_cnt_d = session_edge ? '0 : w_cnt_q + w_c; + w_vcyc_d = session_edge ? '0 : w_vcyc_q + w_v; + r_cnt_d = session_edge ? '0 : r_cnt_q + r_c; + r_vcyc_d = session_edge ? '0 : r_vcyc_q + r_v; + b_cnt_d = session_edge ? '0 : b_cnt_q + b_c; + b_vcyc_d = session_edge ? '0 : b_vcyc_q + b_v; + end + + int dram_f; + string dram_f_name; + + initial begin + @(posedge clk_i); + $sformat(dram_f_name, "sim/bin/logs/noc/monitor_dram_ch%0d.txt", mem); + dram_f = $fopen(dram_f_name, "w"); + end + + function automatic void dump_dram_session(input cnt_t idx, input string tag); + automatic real aw_avg = aw_cnt_q == 0 ? 0 : real'(aw_vcyc_q) / real'(aw_cnt_q); + automatic real ar_avg = ar_cnt_q == 0 ? 0 : real'(ar_vcyc_q) / real'(ar_cnt_q); + automatic real w_avg = w_cnt_q == 0 ? 0 : real'(w_vcyc_q) / real'(w_cnt_q); + automatic real r_avg = r_cnt_q == 0 ? 0 : real'(r_vcyc_q) / real'(r_cnt_q); + automatic real b_avg = b_cnt_q == 0 ? 0 : real'(b_vcyc_q) / real'(b_cnt_q); + $fwrite(dram_f, "*** Session %0d (%s): cycles [%0d, %0d) ***\n", + idx, tag, session_start_cycle_q, cycle_q); + $fwrite(dram_f, " AW: accepted=%0d valid_cyc=%0d avg_accept_cyc=%0.2f\n", aw_cnt_q, aw_vcyc_q, aw_avg); + $fwrite(dram_f, " AR: accepted=%0d valid_cyc=%0d avg_accept_cyc=%0.2f\n", ar_cnt_q, ar_vcyc_q, ar_avg); + $fwrite(dram_f, " W : accepted=%0d valid_cyc=%0d avg_accept_cyc=%0.2f\n", w_cnt_q, w_vcyc_q, w_avg); + $fwrite(dram_f, " R : accepted=%0d valid_cyc=%0d avg_accept_cyc=%0.2f\n", r_cnt_q, r_vcyc_q, r_avg); + $fwrite(dram_f, " B : accepted=%0d valid_cyc=%0d avg_accept_cyc=%0.2f\n", b_cnt_q, b_vcyc_q, b_avg); + $fwrite(dram_f, "\n"); + endfunction + + always_ff @(posedge clk_i) begin + if (session_edge) dump_dram_session(session_idx_q, session_tag(probe_q, 1'b0)); + end + + final begin + dump_dram_session(session_idx_q, session_tag(cluster_probe_i, 1'b1)); + $fclose(dram_f); + end + end + + // --------------------------------------------------------------------- + // Per-core Monitor: one unified, session-cut report merging the VLSU + // Load Latency Monitor with the Spatz Controller/VLSU/VFU Utilization + // Report (migrated from cachepool_cc.sv). All the underlying counters + // inside spatz_controller.sv/spatz_vlsu.sv/spatz_vfu.sv are cumulative + // (never reset); a single struct snapshot is taken at every session edge + // and every printed number is the delta against that snapshot — i.e. that + // session's own stats, not the whole run's. + // + // Exception: ROB peak utilization is a running maximum, not an additive + // counter, so a delta of two peaks would be meaningless. It is reported + // as-is (the peak reached so far over the whole run, not session-local), + // and clearly labeled below. + // --------------------------------------------------------------------- + typedef struct packed { + cnt_t [2:0] ctrl_wvalid; // [0]=VFU [1]=VLSU [2]=VSLDU + cnt_t [2:0] ctrl_wtrans; + cnt_t ctrl_vlsu_insn, ctrl_vlsu_stall, ctrl_vlsu_active; + cnt_t ctrl_vfu_insn, ctrl_vfu_stall, ctrl_vfu_active; + cnt_t ctrl_vsldu_insn, ctrl_vsldu_stall, ctrl_vsldu_active; + cnt_t vlsu_wvalid, vlsu_wtrans; + cnt_t vlsu_mem_valid, vlsu_mem_trans; + cnt_t vlsu_rob_usage, vlsu_rob_use_cyc, vlsu_rob_full_cyc; + cnt_t vlsu_insn_valid_cyc, vlsu_insn_cnt; + cnt_t vlsu_load_lat_sum, vlsu_load_lat_cnt; + cnt_t vfu_wvalid, vfu_wtrans; + cnt_t [2:0] vfu_rvalid; + cnt_t [2:0] vfu_rtrans; + cnt_t vfu_insn_valid_cyc, vfu_insn_cnt; + } spatz_stats_t; + + for (genvar gy = 0; gy < NumGroupsY; gy++) begin : gen_core_gy + for (genvar gx = 0; gx < NumGroupsX; gx++) begin : gen_core_gx + for (genvar t = 0; t < NumTilesPerGroup; t++) begin : gen_core_tile + for (genvar c = 0; c < NumCoresTile; c++) begin : gen_core_core + + // Reads every cumulative Spatz counter for this core in one shot. + function automatic spatz_stats_t read_spatz_stats(); + read_spatz_stats.ctrl_wvalid[0] = `CC_PATH(gy, gx, t, c).i_spatz.i_controller.ctrl_wvalid_cnt_q[0]; + read_spatz_stats.ctrl_wvalid[1] = `CC_PATH(gy, gx, t, c).i_spatz.i_controller.ctrl_wvalid_cnt_q[1]; + read_spatz_stats.ctrl_wvalid[2] = `CC_PATH(gy, gx, t, c).i_spatz.i_controller.ctrl_wvalid_cnt_q[2]; + read_spatz_stats.ctrl_wtrans[0] = `CC_PATH(gy, gx, t, c).i_spatz.i_controller.ctrl_wtrans_cnt_q[0]; + read_spatz_stats.ctrl_wtrans[1] = `CC_PATH(gy, gx, t, c).i_spatz.i_controller.ctrl_wtrans_cnt_q[1]; + read_spatz_stats.ctrl_wtrans[2] = `CC_PATH(gy, gx, t, c).i_spatz.i_controller.ctrl_wtrans_cnt_q[2]; + + read_spatz_stats.ctrl_vlsu_insn = `CC_PATH(gy, gx, t, c).i_spatz.i_controller.ctrl_vlsu_insn_q; + read_spatz_stats.ctrl_vlsu_stall = `CC_PATH(gy, gx, t, c).i_spatz.i_controller.ctrl_vlsu_stall_q; + read_spatz_stats.ctrl_vlsu_active = `CC_PATH(gy, gx, t, c).i_spatz.i_controller.ctrl_vlsu_active_q; + read_spatz_stats.ctrl_vfu_insn = `CC_PATH(gy, gx, t, c).i_spatz.i_controller.ctrl_vfu_insn_q; + read_spatz_stats.ctrl_vfu_stall = `CC_PATH(gy, gx, t, c).i_spatz.i_controller.ctrl_vfu_stall_q; + read_spatz_stats.ctrl_vfu_active = `CC_PATH(gy, gx, t, c).i_spatz.i_controller.ctrl_vfu_active_q; + read_spatz_stats.ctrl_vsldu_insn = `CC_PATH(gy, gx, t, c).i_spatz.i_controller.ctrl_vsldu_insn_q; + read_spatz_stats.ctrl_vsldu_stall = `CC_PATH(gy, gx, t, c).i_spatz.i_controller.ctrl_vsldu_stall_q; + read_spatz_stats.ctrl_vsldu_active = `CC_PATH(gy, gx, t, c).i_spatz.i_controller.ctrl_vsldu_active_q; + + read_spatz_stats.vlsu_wvalid = `CC_PATH(gy, gx, t, c).i_spatz.i_vlsu.vrf_wvalid_cnt_q; + read_spatz_stats.vlsu_wtrans = `CC_PATH(gy, gx, t, c).i_spatz.i_vlsu.vrf_wtrans_cnt_q; + read_spatz_stats.vlsu_mem_valid = `CC_PATH(gy, gx, t, c).i_spatz.i_vlsu.mem_valid_cnt_q; + read_spatz_stats.vlsu_mem_trans = `CC_PATH(gy, gx, t, c).i_spatz.i_vlsu.mem_trans_cnt_q; + read_spatz_stats.vlsu_rob_usage = `CC_PATH(gy, gx, t, c).i_spatz.i_vlsu.rob_usage_q; + read_spatz_stats.vlsu_rob_use_cyc = `CC_PATH(gy, gx, t, c).i_spatz.i_vlsu.rob_use_cyc_q; + read_spatz_stats.vlsu_rob_full_cyc = `CC_PATH(gy, gx, t, c).i_spatz.i_vlsu.rob_full_cyc_q; + read_spatz_stats.vlsu_insn_valid_cyc = `CC_PATH(gy, gx, t, c).i_spatz.i_vlsu.vlsu_insn_valid_cyc_q; + read_spatz_stats.vlsu_insn_cnt = `CC_PATH(gy, gx, t, c).i_spatz.i_vlsu.vlsu_insn_cnt_q; + read_spatz_stats.vlsu_load_lat_sum = `CC_PATH(gy, gx, t, c).i_spatz.i_vlsu.load_lat_sum_q; + read_spatz_stats.vlsu_load_lat_cnt = `CC_PATH(gy, gx, t, c).i_spatz.i_vlsu.load_lat_cnt_q; + + read_spatz_stats.vfu_wvalid = `CC_PATH(gy, gx, t, c).i_spatz.i_vfu.vrf_wvalid_cnt_q; + read_spatz_stats.vfu_wtrans = `CC_PATH(gy, gx, t, c).i_spatz.i_vfu.vrf_wtrans_cnt_q; + read_spatz_stats.vfu_rvalid[0] = `CC_PATH(gy, gx, t, c).i_spatz.i_vfu.vrf_rvalid_cnt_q[0]; + read_spatz_stats.vfu_rvalid[1] = `CC_PATH(gy, gx, t, c).i_spatz.i_vfu.vrf_rvalid_cnt_q[1]; + read_spatz_stats.vfu_rvalid[2] = `CC_PATH(gy, gx, t, c).i_spatz.i_vfu.vrf_rvalid_cnt_q[2]; + read_spatz_stats.vfu_rtrans[0] = `CC_PATH(gy, gx, t, c).i_spatz.i_vfu.vrf_rtrans_cnt_q[0]; + read_spatz_stats.vfu_rtrans[1] = `CC_PATH(gy, gx, t, c).i_spatz.i_vfu.vrf_rtrans_cnt_q[1]; + read_spatz_stats.vfu_rtrans[2] = `CC_PATH(gy, gx, t, c).i_spatz.i_vfu.vrf_rtrans_cnt_q[2]; + read_spatz_stats.vfu_insn_valid_cyc = `CC_PATH(gy, gx, t, c).i_spatz.i_vfu.vfu_insn_valid_cyc_q; + read_spatz_stats.vfu_insn_cnt = `CC_PATH(gy, gx, t, c).i_spatz.i_vfu.vfu_insn_cnt_q; + endfunction + + spatz_stats_t stats_base_d, stats_base_q; + `FF(stats_base_q, stats_base_d, '0) + assign stats_base_d = session_edge ? read_spatz_stats() : stats_base_q; + + // ------------------------------------------------------------- + // Snitch scalar-core frontend/LSU monitor. Unlike the Spatz + // counters above (cumulative RTL registers, snapshotted and + // diffed), these are computed here from live combinational + // signals inside i_snitch and reset every session directly, same + // pattern as the NoC/tile monitors elsewhere in this file. + // + // Stall-cause breakdown mutually exclusive and summing to + // i_snitch.stall's own assertion count: priority-classified in + // the same order as snitch.sv's own `stall` OR-expression -- + // "no valid instruction" causes (frontend not ready, RAW hazard, + // itlb miss) are checked first since they all gate valid_instr + // there, then the independent lsu/acc/fence stall sources. + // ------------------------------------------------------------- + cnt_t inst_valid_cyc_d, inst_valid_cyc_q; + cnt_t inst_accepted_d, inst_accepted_q; + `FF(inst_valid_cyc_q, inst_valid_cyc_d, '0) + `FF(inst_accepted_q, inst_accepted_d, '0) + + cnt_t stall_total_d, stall_total_q; + cnt_t stall_ifetch_d, stall_ifetch_q; + cnt_t stall_itlb_d, stall_itlb_q; + cnt_t stall_hazard_d, stall_hazard_q; + cnt_t stall_lsu_d, stall_lsu_q; + cnt_t stall_acc_d, stall_acc_q; + cnt_t stall_fence_d, stall_fence_q; + `FF(stall_total_q, stall_total_d, '0) + `FF(stall_ifetch_q, stall_ifetch_d, '0) + `FF(stall_itlb_q, stall_itlb_d, '0) + `FF(stall_hazard_q, stall_hazard_d, '0) + `FF(stall_lsu_q, stall_lsu_d, '0) + `FF(stall_acc_q, stall_acc_d, '0) + `FF(stall_fence_q, stall_fence_d, '0) + + always_comb begin + automatic logic inst_valid, inst_ready, stall; + automatic logic is_no_instr, is_itlb, is_hazard, is_lsu, is_acc, is_fence; + + inst_valid = `CC_PATH(gy, gx, t, c).i_snitch.inst_valid_o; + inst_ready = `CC_PATH(gy, gx, t, c).i_snitch.inst_ready_i; + stall = `CC_PATH(gy, gx, t, c).i_snitch.stall; + + is_no_instr = ~(inst_valid & inst_ready); + is_itlb = `CC_PATH(gy, gx, t, c).i_snitch.trans_active & + ~(`CC_PATH(gy, gx, t, c).i_snitch.itlb_valid & + `CC_PATH(gy, gx, t, c).i_snitch.itlb_ready); + is_hazard = ~(`CC_PATH(gy, gx, t, c).i_snitch.operands_ready & + `CC_PATH(gy, gx, t, c).i_snitch.dst_ready); + is_lsu = `CC_PATH(gy, gx, t, c).i_snitch.lsu_stall; + is_acc = `CC_PATH(gy, gx, t, c).i_snitch.acc_stall; + is_fence = `CC_PATH(gy, gx, t, c).i_snitch.fence_snitch_stall | + `CC_PATH(gy, gx, t, c).i_snitch.fence_spatz_stall | + `CC_PATH(gy, gx, t, c).i_snitch.fence_stall; + + inst_valid_cyc_d = session_edge ? '0 : inst_valid_cyc_q + (inst_valid ? 1 : 0); + inst_accepted_d = session_edge ? '0 : + inst_accepted_q + ((inst_valid & inst_ready) ? 1 : 0); + + stall_total_d = session_edge ? '0 : stall_total_q + (stall ? 1 : 0); + stall_ifetch_d = session_edge ? '0 : + stall_ifetch_q + ((stall & is_no_instr) ? 1 : 0); + stall_itlb_d = session_edge ? '0 : + stall_itlb_q + ((stall & ~is_no_instr & is_itlb) ? 1 : 0); + stall_hazard_d = session_edge ? '0 : + stall_hazard_q + ((stall & ~is_no_instr & ~is_itlb & is_hazard) ? 1 : 0); + stall_lsu_d = session_edge ? '0 : + stall_lsu_q + ((stall & ~is_no_instr & ~is_itlb & ~is_hazard & is_lsu) ? 1 : 0); + stall_acc_d = session_edge ? '0 : + stall_acc_q + ((stall & ~is_no_instr & ~is_itlb & ~is_hazard & ~is_lsu & is_acc) ? 1 : 0); + stall_fence_d = session_edge ? '0 : + stall_fence_q + ((stall & ~is_no_instr & ~is_itlb & ~is_hazard & ~is_lsu & ~is_acc & is_fence) ? 1 : 0); + end + + // Scalar LSU load request-to-commit latency (data_req_o port, + // loads only). snitch_lsu.sv guarantees in-order responses, so a + // FIFO of accepted load requests' cycle-stamps correctly pairs + // each commit with its own request even with multiple loads + // outstanding. + logic snitch_load_req_accept, snitch_load_resp_commit; + assign snitch_load_req_accept = + `CC_PATH(gy, gx, t, c).i_snitch.data_req_o.q_valid & + `CC_PATH(gy, gx, t, c).i_snitch.data_rsp_i.q_ready & + ~`CC_PATH(gy, gx, t, c).i_snitch.data_req_o.q.write; + assign snitch_load_resp_commit = + `CC_PATH(gy, gx, t, c).i_snitch.data_rsp_i.p_valid & + `CC_PATH(gy, gx, t, c).i_snitch.data_req_o.p_ready & + ~`CC_PATH(gy, gx, t, c).i_snitch.data_rsp_i.p.write; + + cnt_t load_req_ts_q[$]; + cnt_t snitch_load_lat_sum_q, snitch_load_lat_cnt_q; + + always_ff @(posedge clk_i) begin + if (rst_ni) begin + if (snitch_load_req_accept) load_req_ts_q.push_back(cycle_q); + + if (session_edge) begin + snitch_load_lat_sum_q <= '0; + snitch_load_lat_cnt_q <= '0; + end else if (snitch_load_resp_commit && load_req_ts_q.size() > 0) begin + snitch_load_lat_sum_q <= snitch_load_lat_sum_q + (cycle_q - load_req_ts_q[0]); + snitch_load_lat_cnt_q <= snitch_load_lat_cnt_q + 1; + end + + if (snitch_load_resp_commit && load_req_ts_q.size() > 0) void'(load_req_ts_q.pop_front()); + end else begin + load_req_ts_q.delete(); + snitch_load_lat_sum_q <= '0; + snitch_load_lat_cnt_q <= '0; + end + end + + int core_f; + string core_f_name; + + initial begin + @(posedge clk_i); + $sformat(core_f_name, "sim/bin/logs/core/monitor_core_g%0d_%0d_t%0d_c%0d.txt", gy, gx, t, c); + core_f = $fopen(core_f_name, "w"); + end + + // A function (never a task), see dump_tile_session above for why. + function automatic void dump_core_session(input cnt_t idx, input string tag); + automatic spatz_stats_t now = read_spatz_stats(); + automatic spatz_stats_t d; + automatic real snitch_load_lat_avg, inst_serve_avg; + automatic real load_lat_avg, ctrl_vlsu_wstall, ctrl_vfu_wstall, ctrl_vsldu_wstall; + automatic real vlsu_vrf_w_util, vlsu_vrf_w_avg_cyc, vlsu_mem_util, vlsu_mem_avg_cyc; + automatic real vlsu_rob_usage_avg, vlsu_rob_peak_util, vlsu_avg_insn_cyc; + automatic real vfu_vrf_w_util, vfu_vrf_w_avg_cyc, vfu_avg_insn_cyc; + automatic real vfu_vrf_r_util [3], vfu_vrf_r_avg_cyc [3]; + + d.ctrl_wvalid[0] = now.ctrl_wvalid[0] - stats_base_q.ctrl_wvalid[0]; + d.ctrl_wvalid[1] = now.ctrl_wvalid[1] - stats_base_q.ctrl_wvalid[1]; + d.ctrl_wvalid[2] = now.ctrl_wvalid[2] - stats_base_q.ctrl_wvalid[2]; + d.ctrl_wtrans[0] = now.ctrl_wtrans[0] - stats_base_q.ctrl_wtrans[0]; + d.ctrl_wtrans[1] = now.ctrl_wtrans[1] - stats_base_q.ctrl_wtrans[1]; + d.ctrl_wtrans[2] = now.ctrl_wtrans[2] - stats_base_q.ctrl_wtrans[2]; + d.ctrl_vlsu_insn = now.ctrl_vlsu_insn - stats_base_q.ctrl_vlsu_insn; + d.ctrl_vlsu_stall = now.ctrl_vlsu_stall - stats_base_q.ctrl_vlsu_stall; + d.ctrl_vlsu_active = now.ctrl_vlsu_active - stats_base_q.ctrl_vlsu_active; + d.ctrl_vfu_insn = now.ctrl_vfu_insn - stats_base_q.ctrl_vfu_insn; + d.ctrl_vfu_stall = now.ctrl_vfu_stall - stats_base_q.ctrl_vfu_stall; + d.ctrl_vfu_active = now.ctrl_vfu_active - stats_base_q.ctrl_vfu_active; + d.ctrl_vsldu_insn = now.ctrl_vsldu_insn - stats_base_q.ctrl_vsldu_insn; + d.ctrl_vsldu_stall = now.ctrl_vsldu_stall - stats_base_q.ctrl_vsldu_stall; + d.ctrl_vsldu_active = now.ctrl_vsldu_active - stats_base_q.ctrl_vsldu_active; + + d.vlsu_wvalid = now.vlsu_wvalid - stats_base_q.vlsu_wvalid; + d.vlsu_wtrans = now.vlsu_wtrans - stats_base_q.vlsu_wtrans; + d.vlsu_mem_valid = now.vlsu_mem_valid - stats_base_q.vlsu_mem_valid; + d.vlsu_mem_trans = now.vlsu_mem_trans - stats_base_q.vlsu_mem_trans; + d.vlsu_rob_usage = now.vlsu_rob_usage - stats_base_q.vlsu_rob_usage; + d.vlsu_rob_use_cyc = now.vlsu_rob_use_cyc - stats_base_q.vlsu_rob_use_cyc; + d.vlsu_rob_full_cyc = now.vlsu_rob_full_cyc - stats_base_q.vlsu_rob_full_cyc; + d.vlsu_insn_valid_cyc = now.vlsu_insn_valid_cyc - stats_base_q.vlsu_insn_valid_cyc; + d.vlsu_insn_cnt = now.vlsu_insn_cnt - stats_base_q.vlsu_insn_cnt; + d.vlsu_load_lat_sum = now.vlsu_load_lat_sum - stats_base_q.vlsu_load_lat_sum; + d.vlsu_load_lat_cnt = now.vlsu_load_lat_cnt - stats_base_q.vlsu_load_lat_cnt; + + d.vfu_wvalid = now.vfu_wvalid - stats_base_q.vfu_wvalid; + d.vfu_wtrans = now.vfu_wtrans - stats_base_q.vfu_wtrans; + d.vfu_rvalid[0] = now.vfu_rvalid[0] - stats_base_q.vfu_rvalid[0]; + d.vfu_rvalid[1] = now.vfu_rvalid[1] - stats_base_q.vfu_rvalid[1]; + d.vfu_rvalid[2] = now.vfu_rvalid[2] - stats_base_q.vfu_rvalid[2]; + d.vfu_rtrans[0] = now.vfu_rtrans[0] - stats_base_q.vfu_rtrans[0]; + d.vfu_rtrans[1] = now.vfu_rtrans[1] - stats_base_q.vfu_rtrans[1]; + d.vfu_rtrans[2] = now.vfu_rtrans[2] - stats_base_q.vfu_rtrans[2]; + d.vfu_insn_valid_cyc = now.vfu_insn_valid_cyc - stats_base_q.vfu_insn_valid_cyc; + d.vfu_insn_cnt = now.vfu_insn_cnt - stats_base_q.vfu_insn_cnt; + + load_lat_avg = d.vlsu_load_lat_cnt == 0 ? 0 : real'(d.vlsu_load_lat_sum) / real'(d.vlsu_load_lat_cnt); + + ctrl_vlsu_wstall = real'(d.ctrl_wvalid[1]) - real'(d.ctrl_wtrans[1]); + ctrl_vfu_wstall = real'(d.ctrl_wvalid[0]) - real'(d.ctrl_wtrans[0]); + ctrl_vsldu_wstall = real'(d.ctrl_wvalid[2]) - real'(d.ctrl_wtrans[2]); + + vlsu_vrf_w_util = d.vlsu_wvalid == 0 ? 0 : 100 * real'(d.vlsu_wtrans) / real'(d.vlsu_wvalid); + vlsu_vrf_w_avg_cyc = d.vlsu_wtrans == 0 ? 0 : real'(d.vlsu_wvalid) / real'(d.vlsu_wtrans); + vlsu_mem_util = d.vlsu_mem_valid == 0 ? 0 : 100 * real'(d.vlsu_mem_trans) / real'(d.vlsu_mem_valid); + vlsu_mem_avg_cyc = d.vlsu_mem_trans == 0 ? 0 : real'(d.vlsu_mem_valid) / real'(d.vlsu_mem_trans); + // ROB in use for x cycle, total usage Y: Utilization = Y/(X*depth) + vlsu_rob_usage_avg = d.vlsu_rob_use_cyc == 0 ? 0 : + 100 * real'(d.vlsu_rob_usage) / real'(d.vlsu_rob_use_cyc) / real'(NumSpatzOutstandingLoads); + // Not session-cut: cumulative peak over the whole run so far. + vlsu_rob_peak_util = 100 * real'(`CC_PATH(gy, gx, t, c).i_spatz.i_vlsu.rob_peak_q) / real'(NumSpatzOutstandingLoads); + vlsu_avg_insn_cyc = d.vlsu_insn_cnt == 0 ? 0 : real'(d.vlsu_insn_valid_cyc) / real'(d.vlsu_insn_cnt); + + vfu_vrf_w_util = d.vfu_wvalid == 0 ? 0 : 100 * real'(d.vfu_wtrans) / real'(d.vfu_wvalid); + vfu_vrf_w_avg_cyc = d.vfu_wtrans == 0 ? 0 : real'(d.vfu_wvalid) / real'(d.vfu_wtrans); + for (int unsigned p = 0; p < 3; p++) begin + vfu_vrf_r_util[p] = d.vfu_rvalid[p] == 0 ? 0 : 100 * real'(d.vfu_rtrans[p]) / real'(d.vfu_rvalid[p]); + vfu_vrf_r_avg_cyc[p] = d.vfu_rtrans[p] == 0 ? 0 : real'(d.vfu_rvalid[p]) / real'(d.vfu_rtrans[p]); + end + vfu_avg_insn_cyc = d.vfu_insn_cnt == 0 ? 0 : real'(d.vfu_insn_valid_cyc) / real'(d.vfu_insn_cnt); + + snitch_load_lat_avg = snitch_load_lat_cnt_q == 0 ? 0 : + real'(snitch_load_lat_sum_q) / real'(snitch_load_lat_cnt_q); + inst_serve_avg = inst_accepted_q == 0 ? 0 : + real'(inst_valid_cyc_q) / real'(inst_accepted_q); + + $fwrite(core_f, "*********************************************************************\n"); + $fwrite(core_f, "*** Session %0d (%s): cycles [%0d, %0d) ***\n", + idx, tag, session_start_cycle_q, cycle_q); + $fwrite(core_f, "*********************************************************************\n"); + $fwrite(core_f, "*** Snitch Scalar Core ***\n"); + $fwrite(core_f, " Inst Fetch Accepted Count: %32d\n", inst_accepted_q ); + $fwrite(core_f, " Inst Fetch AVG Serving Cycles: %32.2f\n", inst_serve_avg ); + $fwrite(core_f, " Scalar LSU Load Req-to-Commit Count: %29d\n", snitch_load_lat_cnt_q ); + $fwrite(core_f, " Scalar LSU Load AVG Req-to-Commit Cyc: %27.2f\n", snitch_load_lat_avg); + $fwrite(core_f, " Stall Cycles Total: %32d\n", stall_total_q ); + $fwrite(core_f, " Stall Cycles (No Valid Instr): %32d\n", stall_ifetch_q ); + $fwrite(core_f, " Stall Cycles (ITLB Miss): %32d\n", stall_itlb_q ); + $fwrite(core_f, " Stall Cycles (RAW Hazard): %32d\n", stall_hazard_q ); + $fwrite(core_f, " Stall Cycles (LSU Port Busy): %32d\n", stall_lsu_q ); + $fwrite(core_f, " Stall Cycles (Accelerator Port): %32d\n", stall_acc_q ); + $fwrite(core_f, " Stall Cycles (Fence Drain): %32d\n", stall_fence_q ); + $fwrite(core_f, "\n" ); + $fwrite(core_f, " VLSU Load Req-to-Commit Count: %32d\n", d.vlsu_load_lat_cnt ); + $fwrite(core_f, " VLSU Load AVG Req-to-Commit Cyc: %32.2f\n", load_lat_avg ); + $fwrite(core_f, "\n" ); + $fwrite(core_f, "*** Spatz Controller Utilization ***\n"); + $fwrite(core_f, " VLSU:\n" ); + $fwrite(core_f, " VLSU Active (not accurate): %32d\n", d.ctrl_vlsu_active ); + $fwrite(core_f, " VLSU Num Instructions: %32d\n", d.ctrl_vlsu_insn ); + $fwrite(core_f, " VLSU Stall Cycles: %32d\n", d.ctrl_vlsu_stall ); + $fwrite(core_f, " VLSU WR Stalls: %32.2f\n", ctrl_vlsu_wstall ); + $fwrite(core_f, " VFU:\n" ); + $fwrite(core_f, " VFU Active (not accurate): %32d\n", d.ctrl_vfu_active ); + $fwrite(core_f, " VFU Num Instructions: %32d\n", d.ctrl_vfu_insn ); + $fwrite(core_f, " VFU Stall Cycles: %32d\n", d.ctrl_vfu_stall ); + $fwrite(core_f, " VFU WR Stalls: %32.2f\n", ctrl_vfu_wstall ); + $fwrite(core_f, " VSLDU:\n" ); + $fwrite(core_f, " VSLDU Active (not accurate): %32d\n", d.ctrl_vsldu_active ); + $fwrite(core_f, " VSLDU Num Instructions: %32d\n", d.ctrl_vsldu_insn ); + $fwrite(core_f, " VSLDU Stall Cycles: %32d\n", d.ctrl_vsldu_stall ); + $fwrite(core_f, " VSLDU WR Stalls: %32.2f\n", ctrl_vsldu_wstall ); + $fwrite(core_f, "\n" ); + $fwrite(core_f, "*** Spatz VLSU Utilization ***\n"); + $fwrite(core_f, " Number of VRF Valid Cycles: %32d\n", d.vlsu_wvalid ); + $fwrite(core_f, " Number of VRF Transaction Counts: %32d\n", d.vlsu_wtrans ); + $fwrite(core_f, " VRF W Utilization: %32.2f\n", vlsu_vrf_w_util ); + $fwrite(core_f, " VRF W AVG Cycles: %32.2f\n", vlsu_vrf_w_avg_cyc ); + $fwrite(core_f, " Number of Mem Valid Cycles: %32d\n", d.vlsu_mem_valid ); + $fwrite(core_f, " Number of Mem Transaction Counts: %32d\n", d.vlsu_mem_trans ); + $fwrite(core_f, " Mem Utilization: %32.2f\n", vlsu_mem_util ); + $fwrite(core_f, " Mem AVG Req Accept Cycles: %32.2f\n", vlsu_mem_avg_cyc ); + $fwrite(core_f, " VLSU Stall Cycles (ROB Full): %32d\n", d.vlsu_rob_full_cyc ); + $fwrite(core_f, " ROB AVG Utilization: %32.2f\n", vlsu_rob_usage_avg ); + $fwrite(core_f, " ROB Peak Utilization (cumulative):%32.2f\n", vlsu_rob_peak_util ); + $fwrite(core_f, " Total Insn Count: %32d\n", d.vlsu_insn_cnt ); + $fwrite(core_f, " AVG Insn Cycles: %32.2f\n", vlsu_avg_insn_cyc ); + $fwrite(core_f, "\n" ); + $fwrite(core_f, "*** Spatz VFU Utilization ***\n"); + $fwrite(core_f, " VRF Write Port:\n" ); + $fwrite(core_f, " Number of VRF Valid Cycles: %32d\n", d.vfu_wvalid ); + $fwrite(core_f, " Number of VRF Transaction Counts: %32d\n", d.vfu_wtrans ); + $fwrite(core_f, " VRF W Utilization: %32.2f\n", vfu_vrf_w_util ); + $fwrite(core_f, " VRF W AVG Cycles: %32.2f\n", vfu_vrf_w_avg_cyc ); + for (int unsigned p = 0; p < 3; p++) begin + $fwrite(core_f, " VRF Read Port %0d:\n", p ); + $fwrite(core_f, " Number of VRF Valid Cycles: %32d\n", d.vfu_rvalid[p] ); + $fwrite(core_f, " Number of VRF Transaction Counts: %32d\n", d.vfu_rtrans[p] ); + $fwrite(core_f, " VRF R Utilization: %32.2f\n", vfu_vrf_r_util[p] ); + $fwrite(core_f, " VRF R AVG Cycles: %32.2f\n", vfu_vrf_r_avg_cyc[p] ); + end + $fwrite(core_f, " Total Insn Count: %32d\n", d.vfu_insn_cnt ); + $fwrite(core_f, " AVG Insn Cycles: %32.2f\n", vfu_avg_insn_cyc ); + $fwrite(core_f, "*********************************************************************\n"); + $fwrite(core_f, "\n" ); + endfunction + + always_ff @(posedge clk_i) begin + if (session_edge) dump_core_session(session_idx_q, session_tag(probe_q, 1'b0)); + end + + final begin + dump_core_session(session_idx_q, session_tag(cluster_probe_i, 1'b1)); + $fclose(core_f); + end + end + end + end + end + + `undef TILE_PATH + `undef CC_PATH + `undef CLUSTER_PATH +`endif + +endmodule diff --git a/hardware/tb/cachepool_noc_profiling.sv b/hardware/tb/cachepool_noc_profiling.sv new file mode 100644 index 0000000..024b1b7 --- /dev/null +++ b/hardware/tb/cachepool_noc_profiling.sv @@ -0,0 +1,663 @@ +// Copyright 2026 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +// Ported from TeraNoC's hardware/tb/tb_noc_profiling.svh to CachePool's +// group/tile/core hierarchy. Instantiated as a sibling of the DUT (same +// pattern as cachepool_monitor.sv) and reaches in purely via hierarchical +// references -- nothing here is threaded through synthesizable RTL. +// +// The S/P/Q log-line WIRE FORMAT is kept byte-for-byte identical to +// TeraNoC's, so hardware/scripts/perfetto_gen.py parses these logs +// unmodified -- only the SV-side data source changed. See that script's +// load_noc_router_logs/load_noc_tile_logs/load_pe_logs for the consumer. +// +// Architectural differences from MemPool/TeraNoC that shaped this port +// (see cachepool_pkg.sv for the underlying types): +// - CachePool has ONE req + one resp network per tile (width +// NumNoCPortsPerTile), not MemPool's wide/rdwr split -- so there is only +// one "port" dimension (`n`), not two. +// - No dedicated meta_id/MetaIdWidth field anywhere. The closest analog +// used throughout is `.user.req_id` (reqid_t, sized by +// NumSpatzOutstandingLoads). +// - Groups are addressed 2-D (x,y), row-major flat id g = gy*NumGroupsX+gx +// (see cachepool_cluster.sv's gen_group_y/gen_group_x). Router-level P +// lines carry x/y directly (the RTL's own noc_group_hdr_t already +// decodes them), so no re-derivation is needed there. Tile-level P/Q +// lines need a flat scalar to match TeraNoC's src_grp/dst_grp fields; +// decode_group() below re-derives it from the dst_tile_id/tile_id bit +// fields using the SAME layout formula as +// cachepool_group_noc_wrapper.sv's NocGroupOffset/NocGroupBitsX/Y +// (kept in sync manually -- there's no shared package constant for it). +// CAVEAT: perfetto_gen.py's `_gname` decodes g as +// (g // mesh_y, g % mesh_y), i.e. an X-major-fast convention. CachePool's +// g is Y-major/X-fast, so pass `--mesh-y NumGroupsX` (not NumGroupsY) to +// get the arithmetic right -- the printed tuple order then reads (y,x) +// rather than (x,y). Cosmetic only; no data is wrong. +// - The inter-group ("RG") mesh is entirely tied to '0 whenever +// NumRemoteGroupPortCore == 0 (the default single-group build, see +// config/config.mk num_rg_ports_per_core), so router and RG-tile logs +// are legitimately empty then -- this module mirrors the DUT's own +// `if (NumRemoteGroupPortCore > 0)` guard and only traces PE (core) +// ports unconditionally. Meaningful router/tile output needs a +// multi-group config, e.g. config/cachepool_4g.mk. +// - Intra-group ("LG") remote traffic (cachepool_group.sv's crossbar) has +// no MemPool equivalent and is NOT traced here (kept the port scope +// 1:1 with the original tb_noc_profiling.svh). +module cachepool_noc_profiling + import cachepool_pkg::*; +( + input logic clk_i, + input logic rst_ni, + // SPATZ_CLUSTER_PROBE bit (same signal cachepool_monitor.sv uses): gates all tracing to active kernel sessions and opens a new session_/ subfolder per rising edge. + input logic cluster_probe_i +); + +`ifndef TARGET_SYNTHESIS +`ifndef TARGET_VERILATOR + logic [63:0] cycle_q; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + cycle_q <= '0; + end else begin + cycle_q <= cycle_q + 64'd1; + end + end + +`ifdef NOC_PROFILING + // Hierarchical paths (no `dut.` alias exists in this TB -- see + // hardware/tb/tb_cachepool.sv, DUT instance is i_cluster_wrapper). + `define NOCPROF_GRP(gy, gx) \ + i_cluster_wrapper.i_cluster.gen_group_y[gy].gen_group_x[gx].i_group + `define NOCPROF_TILE(gy, gx, t) \ + `NOCPROF_GRP(gy, gx).i_group.gen_tiles[t].gen_tile.i_tile + + // Mirrors cachepool_group_noc_wrapper.sv's private dst_tile_id/tile_id bit + // layout (group_y | group_x | local_tile), row-major. Kept manually in + // sync since that layout isn't exported as a package constant. + localparam int unsigned NocGroupOffset = $clog2(NumTilesPerGroup); + localparam int unsigned NocGroupBitsX = (NumGroupsX > 1) ? $clog2(NumGroupsX) : 0; + localparam int unsigned NocGroupBitsY = (NumGroupsY > 1) ? $clog2(NumGroupsY) : 0; + // Same "at least 1" floor cachepool_group_noc_wrapper.sv uses for + // NumRemoteGroupPortTile: avoids a zero-size unpacked array dimension on + // the state-register arrays below when NumRemoteGroupPortCore==0 (the + // default single-group build) -- the generate-if guards ensure those + // elements are simply never touched in that case. + localparam int unsigned NumRGPortTile = (NumRemoteGroupPortCore == 0) ? 1 + : NumRemoteGroupPortCore * NrTCDMPortsPerCore; + // Part-select WIDTHS floored to 1 to dodge an illegal zero-width + // `+:`/range select when a dimension is degenerate (NumGroupsX==1 etc., + // i.e. NocGroupBits{X,Y}==0 or NocGroupOffset==0) -- unlike the DUT's own + // decode (cachepool_group_noc_wrapper.sv:311-322), which dodges this with + // a `generate if/else` that drops the untaken branch before elaboration, + // a function body can't do that: a runtime `?:` still requires BOTH + // branches to be well-typed at elaboration time, so the slice must always + // be legal; the surrounding ternary below then discards the resulting + // (don't-care) bit whenever the real width is 0. + localparam int unsigned NocGroupBitsXSafe = (NocGroupBitsX == 0) ? 1 : NocGroupBitsX; + localparam int unsigned NocGroupBitsYSafe = (NocGroupBitsY == 0) ? 1 : NocGroupBitsY; + localparam int unsigned NocGroupOffsetSafe = (NocGroupOffset == 0) ? 1 : NocGroupOffset; + // Start-index clamp for the degenerate NumGroups==1 case (NocGroupOffset + // == TileIDWidth, i.e. dst_tile_id has no group bits at all -- only + // possible/relevant if NumRemoteGroupPortCore>0 with a single-group build, + // which the RTL doesn't meaningfully support anyway): keeps the slice + // in-bounds so it stays a legal constant part-select; decode_group() + // still returns 0 in this case via the NocGroupBits{X,Y}==0 check above. + localparam int unsigned NocGroupXStart = (NocGroupOffset + NocGroupBitsXSafe <= TileIDWidth) + ? NocGroupOffset : (TileIDWidth - NocGroupBitsXSafe); + localparam int unsigned NocGroupYStart = (NocGroupOffset + NocGroupBitsX + NocGroupBitsYSafe <= TileIDWidth) + ? (NocGroupOffset + NocGroupBitsX) : (TileIDWidth - NocGroupBitsYSafe); + + function automatic int unsigned decode_group(logic [TileIDWidth-1:0] gtile_id); + automatic int unsigned gx = (NocGroupBitsX == 0) ? 0 : + int'(gtile_id[NocGroupXStart +: NocGroupBitsXSafe]); + automatic int unsigned gy = (NocGroupBitsY == 0) ? 0 : + int'(gtile_id[NocGroupYStart +: NocGroupBitsYSafe]); + return gy * NumGroupsX + gx; + endfunction + + function automatic int unsigned decode_local_tile(logic [TileIDWidth-1:0] gtile_id); + return (NocGroupOffset == 0) ? 0 : int'(gtile_id[NocGroupOffsetSafe-1:0]); + endfunction + + string app, log_path; + integer retval; + // MERGED file handles, same layout as TeraNoC: routers -> ONE req + ONE + // resp file per group (line tagged with flat noc_port); tiles/PE -> ONE + // file per tile (line tagged with port/core idx). + int f_rreq [NumGroups]; + int f_rresp [NumGroups]; + int f_tile [NumGroups][NumTilesPerGroup]; + int f_pe [NumGroups][NumTilesPerGroup]; + // L2 (DRAM-refill) mesh: one router pair per group, no tile/port fan-out. + int l2_f_req [NumGroups]; + int l2_f_resp [NumGroups]; + + // Session bookkeeping: session_idx_q numbers each cluster_probe_i rising edge (a kernel session); files_open_q is the trace blocks' write-enable, deliberately registered one cycle behind the actual fopen/fclose (via closing_q) so no always_ff race can hit a file descriptor before it's open or after it's closed. + logic probe_q; + int session_idx_q; + logic files_open_q; + logic closing_q; + + // Opens a fresh log_path/session_/ subfolder and (re)assigns every file handle to it. + task automatic open_session_files(input int idx); + string session_path; + session_path = $sformatf("%s/session_%0d", log_path, idx); + retval = $system({"mkdir -p ", session_path}); + for (int g = 0; g < NumGroups; g++) begin + f_rreq[g] = $fopen($sformatf("%s/router_g%0d_req.log", session_path, g), "w"); + f_rresp[g] = $fopen($sformatf("%s/router_g%0d_resp.log", session_path, g), "w"); + l2_f_req[g] = $fopen($sformatf("%s/l2_router_g%0d_req.log", session_path, g), "w"); + l2_f_resp[g] = $fopen($sformatf("%s/l2_router_g%0d_resp.log", session_path, g), "w"); + for (int t = 0; t < NumTilesPerGroup; t++) begin + f_tile[g][t] = $fopen($sformatf("%s/tile_g%0d_t%0d.log", session_path, g, t), "w"); + f_pe[g][t] = $fopen($sformatf("%s/pe_g%0d_t%0d.log", session_path, g, t), "w"); + end + end + endtask + + // Flushes each port's still-open RLE run (same trailing writes the old bare `final` block did) then closes every handle from open_session_files -- shared by a mid-run falling edge and by $finish leaving a session open. + task automatic close_session_files(); + for (int g = 0; g < NumGroups; g++) begin + for (int t = 0; t < NumTilesPerGroup; t++) begin + if (NumRemoteGroupPortCore > 0) begin + for (int n = 0; n < NumNoCPortsPerTile; n++) begin + automatic int noc_port = t*NumNoCPortsPerTile + n; + for (int d = 0; d < 4; d++) begin + $fwrite(f_rreq[g], "%0d S %0d 0 %0d %0d %0d\n", noc_port, d, rsq_start[g][t][n][d][0], cycle_q, rsq_st[g][t][n][d][0]); + $fwrite(f_rreq[g], "%0d S %0d 1 %0d %0d %0d\n", noc_port, d, rsq_start[g][t][n][d][1], cycle_q, rsq_st[g][t][n][d][1]); + end + $fwrite(f_rreq[g], "%0d S 4 0 %0d %0d %0d\n", noc_port, rsq_start[g][t][n][4][0], cycle_q, rsq_st[g][t][n][4][0]); + $fwrite(f_rreq[g], "%0d S 4 1 %0d %0d %0d\n", noc_port, rsq_start[g][t][n][4][1], cycle_q, rsq_st[g][t][n][4][1]); + for (int d = 0; d < 4; d++) begin + $fwrite(f_rresp[g], "%0d S %0d 0 %0d %0d %0d\n", noc_port, d, rsp_start[g][t][n][d][0], cycle_q, rsp_st[g][t][n][d][0]); + $fwrite(f_rresp[g], "%0d S %0d 1 %0d %0d %0d\n", noc_port, d, rsp_start[g][t][n][d][1], cycle_q, rsp_st[g][t][n][d][1]); + end + $fwrite(f_rresp[g], "%0d S 4 0 %0d %0d %0d\n", noc_port, rsp_start[g][t][n][4][0], cycle_q, rsp_st[g][t][n][4][0]); + $fwrite(f_rresp[g], "%0d S 4 1 %0d %0d %0d\n", noc_port, rsp_start[g][t][n][4][1], cycle_q, rsp_st[g][t][n][4][1]); + end + for (int p = 0; p < NumRGPortTile; p++) begin + $fwrite(f_tile[g][t], "S 0 1 %0d %0d %0d %0d\n", p, ts_mreq_s[g][t][p], cycle_q, ts_mreq_st[g][t][p]); + $fwrite(f_tile[g][t], "S 0 0 %0d %0d %0d %0d\n", p, ts_sreq_s[g][t][p], cycle_q, ts_sreq_st[g][t][p]); + $fwrite(f_tile[g][t], "S 1 0 %0d %0d %0d %0d\n", p, ts_mrsp_s[g][t][p], cycle_q, ts_mrsp_st[g][t][p]); + $fwrite(f_tile[g][t], "S 1 1 %0d %0d %0d %0d\n", p, ts_srsp_s[g][t][p], cycle_q, ts_srsp_st[g][t][p]); + end + end + $fclose(f_tile[g][t]); + for (int c = 0; c < NumCoresTile; c++) begin + $fwrite(f_pe[g][t], "%0d S 0 1 0 %0d %0d %0d\n", c, pe_req_s[g][t][c], cycle_q, pe_req_st[g][t][c]); + $fwrite(f_pe[g][t], "%0d S 1 0 0 %0d %0d %0d\n", c, pe_rsp_s[g][t][c], cycle_q, pe_rsp_st[g][t][c]); + end + $fclose(f_pe[g][t]); + end + $fclose(f_rreq[g]); + $fclose(f_rresp[g]); + for (int d = 0; d < 4; d++) begin + $fwrite(l2_f_req[g], "S %0d 0 %0d %0d %0d\n", d, l2q_start[g][d][0], cycle_q, l2q_st[g][d][0]); + $fwrite(l2_f_req[g], "S %0d 1 %0d %0d %0d\n", d, l2q_start[g][d][1], cycle_q, l2q_st[g][d][1]); + $fwrite(l2_f_resp[g], "S %0d 0 %0d %0d %0d\n", d, l2p_start[g][d][0], cycle_q, l2p_st[g][d][0]); + $fwrite(l2_f_resp[g], "S %0d 1 %0d %0d %0d\n", d, l2p_start[g][d][1], cycle_q, l2p_st[g][d][1]); + end + $fclose(l2_f_req[g]); + $fclose(l2_f_resp[g]); + end + endtask + + initial begin + void'($value$plusargs("APP=%s", app)); + log_path = "noc_profiling"; + retval = $system({"mkdir -p ", log_path}); + end + + // Opens session_/ on each cluster_probe_i rising edge and closes it on the matching falling edge; idle time between sessions is neither opened nor written (see the trace blocks' `!files_open_q` reset gating below). + // + // files_open_q only becomes visible to the (separately-clocked) trace + // blocks the cycle AFTER open_session_files() actually runs, and + // close_session_files() itself is deferred by one cycle (via closing_q) + // past the cycle files_open_q drops -- both one-cycle offsets exist solely + // so no trace block's always_ff can ever race this one's fopen/fclose on + // the same edge (assumes sessions are >1 cycle apart, always true for a + // real kernel region). + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + probe_q <= 1'b0; + session_idx_q <= '0; + files_open_q <= 1'b0; + closing_q <= 1'b0; + end else begin + probe_q <= cluster_probe_i; + if (cluster_probe_i && !probe_q) begin + open_session_files(session_idx_q); + session_idx_q <= session_idx_q + 1; + files_open_q <= 1'b1; + end else if (!cluster_probe_i && probe_q && files_open_q) begin + files_open_q <= 1'b0; + closing_q <= 1'b1; + end else if (closing_q) begin + close_session_files(); + closing_q <= 1'b0; + end + end + end + + // Module-scope state registers, flushed by close_session_files above. + // router: [group][tile][noc-port][portidx 0..4][io 0..1] + logic [1:0] rsq_st [NumGroups][NumTilesPerGroup][NumNoCPortsPerTile][5][2]; + logic [63:0] rsq_start [NumGroups][NumTilesPerGroup][NumNoCPortsPerTile][5][2]; + logic [1:0] rsp_st [NumGroups][NumTilesPerGroup][NumNoCPortsPerTile][5][2]; + logic [63:0] rsp_start [NumGroups][NumTilesPerGroup][NumNoCPortsPerTile][5][2]; + // tile: [group][tile][RG port] + logic [1:0] ts_mreq_st [NumGroups][NumTilesPerGroup][NumRGPortTile]; logic [63:0] ts_mreq_s [NumGroups][NumTilesPerGroup][NumRGPortTile]; + logic [1:0] ts_sreq_st [NumGroups][NumTilesPerGroup][NumRGPortTile]; logic [63:0] ts_sreq_s [NumGroups][NumTilesPerGroup][NumRGPortTile]; + logic [1:0] ts_mrsp_st [NumGroups][NumTilesPerGroup][NumRGPortTile]; logic [63:0] ts_mrsp_s [NumGroups][NumTilesPerGroup][NumRGPortTile]; + logic [1:0] ts_srsp_st [NumGroups][NumTilesPerGroup][NumRGPortTile]; logic [63:0] ts_srsp_s [NumGroups][NumTilesPerGroup][NumRGPortTile]; + // PE (Snitch core) reqrsp data port: req = core_req[c] (out), resp = core_rsp[c] (in). + logic [1:0] pe_req_st [NumGroups][NumTilesPerGroup][NumCoresTile]; logic [63:0] pe_req_s [NumGroups][NumTilesPerGroup][NumCoresTile]; + logic [1:0] pe_rsp_st [NumGroups][NumTilesPerGroup][NumCoresTile]; logic [63:0] pe_rsp_s [NumGroups][NumTilesPerGroup][NumCoresTile]; + // L2 router: [group][portidx 0..3][io 0..1] -- one router pair per group, + // no tile/port dimension (see cachepool_group_noc_wrapper.sv's single + // i_l2_req_router/i_l2_rsp_router per group). + logic [1:0] l2q_st [NumGroups][4][2]; logic [63:0] l2q_start [NumGroups][4][2]; + logic [1:0] l2p_st [NumGroups][4][2]; logic [63:0] l2p_start [NumGroups][4][2]; + + // ------------------------------------------------------------ + // Router port capture: mesh directions 0..3 (N/E/S/W, floo_pkg convention) + // + local (portidx 4) inject/eject -> per-group req/resp files. S lines + // RLE idle/stall/read/write; P lines log every accepted request/response + // flit. Guarded like the DUT's own `gen_noc`: under the default + // single-group build (NumRemoteGroupPortCore==0) req_mesh_*/rsp_mesh_* are + // tied to '0 by `gen_noc_disabled`, and packed_req/eject_req/inject_rsp/ + // eject_rsp don't exist at all -- so this whole block only elaborates + // when NumRemoteGroupPortCore > 0 (multi-group configs). + // ------------------------------------------------------------ + if (NumRemoteGroupPortCore > 0) begin : gen_router_trace + for (genvar gy = 0; gy < NumGroupsY; gy++) begin : gen_rstate_gy + for (genvar gx = 0; gx < NumGroupsX; gx++) begin : gen_rstate_gx + localparam int unsigned g = gy * NumGroupsX + gx; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni || !files_open_q) begin + rsq_st[g] <= '{default: '0}; rsq_start[g] <= '{default: '0}; + rsp_st[g] <= '{default: '0}; rsp_start[g] <= '{default: '0}; + end else begin + for (int t = 0; t < NumTilesPerGroup; t++) begin + for (int n = 0; n < NumNoCPortsPerTile; n++) begin + automatic int noc_port = t*NumNoCPortsPerTile + n; + for (int d = 0; d < 4; d++) begin + automatic logic [1:0] si = `NOCPROF_GRP(gy, gx).req_mesh_in_valid[t][n][d] + ? (`NOCPROF_GRP(gy, gx).req_mesh_in_ready[t][n][d] + ? (`NOCPROF_GRP(gy, gx).req_mesh_in[t][n][d].payload.write ? 2'd3 : 2'd2) : 2'd1) : 2'd0; + automatic logic [1:0] so = `NOCPROF_GRP(gy, gx).req_mesh_out_valid[t][n][d] + ? (`NOCPROF_GRP(gy, gx).req_mesh_out_ready[t][n][d] + ? (`NOCPROF_GRP(gy, gx).req_mesh_out[t][n][d].payload.write ? 2'd3 : 2'd2) : 2'd1) : 2'd0; + if (si != rsq_st[g][t][n][d][0]) begin + $fwrite(f_rreq[g], "%0d S %0d 0 %0d %0d %0d\n", noc_port, d, rsq_start[g][t][n][d][0], cycle_q, rsq_st[g][t][n][d][0]); + rsq_st[g][t][n][d][0] <= si; rsq_start[g][t][n][d][0] <= cycle_q; + end + if (so != rsq_st[g][t][n][d][1]) begin + $fwrite(f_rreq[g], "%0d S %0d 1 %0d %0d %0d\n", noc_port, d, rsq_start[g][t][n][d][1], cycle_q, rsq_st[g][t][n][d][1]); + rsq_st[g][t][n][d][1] <= so; rsq_start[g][t][n][d][1] <= cycle_q; + end + // P: portidx io cycle wen tgt_addr dst_x dst_y src_x src_y src_tile core req_id + if (si >= 2) + $fwrite(f_rreq[g], "%0d P %0d 0 %0d %0d %0h %0d %0d %0d %0d %0d %0d %0d\n", noc_port, d, cycle_q, + `NOCPROF_GRP(gy, gx).req_mesh_in[t][n][d].payload.write, + `NOCPROF_GRP(gy, gx).req_mesh_in[t][n][d].payload.addr, + `NOCPROF_GRP(gy, gx).req_mesh_in[t][n][d].hdr.dst_id.x, + `NOCPROF_GRP(gy, gx).req_mesh_in[t][n][d].hdr.dst_id.y, + `NOCPROF_GRP(gy, gx).req_mesh_in[t][n][d].hdr.src_id.x, + `NOCPROF_GRP(gy, gx).req_mesh_in[t][n][d].hdr.src_id.y, + `NOCPROF_GRP(gy, gx).req_mesh_in[t][n][d].hdr.src_tile_id, + `NOCPROF_GRP(gy, gx).req_mesh_in[t][n][d].payload.user.core_id, + `NOCPROF_GRP(gy, gx).req_mesh_in[t][n][d].payload.user.req_id); + if (so >= 2) + $fwrite(f_rreq[g], "%0d P %0d 1 %0d %0d %0h %0d %0d %0d %0d %0d %0d %0d\n", noc_port, d, cycle_q, + `NOCPROF_GRP(gy, gx).req_mesh_out[t][n][d].payload.write, + `NOCPROF_GRP(gy, gx).req_mesh_out[t][n][d].payload.addr, + `NOCPROF_GRP(gy, gx).req_mesh_out[t][n][d].hdr.dst_id.x, + `NOCPROF_GRP(gy, gx).req_mesh_out[t][n][d].hdr.dst_id.y, + `NOCPROF_GRP(gy, gx).req_mesh_out[t][n][d].hdr.src_id.x, + `NOCPROF_GRP(gy, gx).req_mesh_out[t][n][d].hdr.src_id.y, + `NOCPROF_GRP(gy, gx).req_mesh_out[t][n][d].hdr.src_tile_id, + `NOCPROF_GRP(gy, gx).req_mesh_out[t][n][d].payload.user.core_id, + `NOCPROF_GRP(gy, gx).req_mesh_out[t][n][d].payload.user.req_id); + end + begin : req_local + automatic logic [1:0] sli = `NOCPROF_GRP(gy, gx).gen_noc.packed_req_valid[noc_port] + ? (`NOCPROF_GRP(gy, gx).gen_noc.packed_req_ready[noc_port] + ? (`NOCPROF_GRP(gy, gx).gen_noc.packed_req[noc_port].payload.write ? 2'd3 : 2'd2) : 2'd1) : 2'd0; + automatic logic [1:0] slo = `NOCPROF_GRP(gy, gx).gen_noc.eject_req_valid[noc_port] + ? (`NOCPROF_GRP(gy, gx).gen_noc.eject_req_ready[noc_port] + ? (`NOCPROF_GRP(gy, gx).gen_noc.eject_req[noc_port].payload.write ? 2'd3 : 2'd2) : 2'd1) : 2'd0; + if (sli != rsq_st[g][t][n][4][0]) begin + $fwrite(f_rreq[g], "%0d S 4 0 %0d %0d %0d\n", noc_port, rsq_start[g][t][n][4][0], cycle_q, rsq_st[g][t][n][4][0]); + rsq_st[g][t][n][4][0] <= sli; rsq_start[g][t][n][4][0] <= cycle_q; + end + if (slo != rsq_st[g][t][n][4][1]) begin + $fwrite(f_rreq[g], "%0d S 4 1 %0d %0d %0d\n", noc_port, rsq_start[g][t][n][4][1], cycle_q, rsq_st[g][t][n][4][1]); + rsq_st[g][t][n][4][1] <= slo; rsq_start[g][t][n][4][1] <= cycle_q; + end + if (sli >= 2) + $fwrite(f_rreq[g], "%0d P 4 0 %0d %0d %0h %0d %0d %0d %0d %0d %0d %0d\n", noc_port, cycle_q, + `NOCPROF_GRP(gy, gx).gen_noc.packed_req[noc_port].payload.write, + `NOCPROF_GRP(gy, gx).gen_noc.packed_req[noc_port].payload.addr, + `NOCPROF_GRP(gy, gx).gen_noc.packed_req[noc_port].hdr.dst_id.x, + `NOCPROF_GRP(gy, gx).gen_noc.packed_req[noc_port].hdr.dst_id.y, + `NOCPROF_GRP(gy, gx).gen_noc.packed_req[noc_port].hdr.src_id.x, + `NOCPROF_GRP(gy, gx).gen_noc.packed_req[noc_port].hdr.src_id.y, + `NOCPROF_GRP(gy, gx).gen_noc.packed_req[noc_port].hdr.src_tile_id, + `NOCPROF_GRP(gy, gx).gen_noc.packed_req[noc_port].payload.user.core_id, + `NOCPROF_GRP(gy, gx).gen_noc.packed_req[noc_port].payload.user.req_id); + if (slo >= 2) + $fwrite(f_rreq[g], "%0d P 4 1 %0d %0d %0h %0d %0d %0d %0d %0d %0d %0d\n", noc_port, cycle_q, + `NOCPROF_GRP(gy, gx).gen_noc.eject_req[noc_port].payload.write, + `NOCPROF_GRP(gy, gx).gen_noc.eject_req[noc_port].payload.addr, + `NOCPROF_GRP(gy, gx).gen_noc.eject_req[noc_port].hdr.dst_id.x, + `NOCPROF_GRP(gy, gx).gen_noc.eject_req[noc_port].hdr.dst_id.y, + `NOCPROF_GRP(gy, gx).gen_noc.eject_req[noc_port].hdr.src_id.x, + `NOCPROF_GRP(gy, gx).gen_noc.eject_req[noc_port].hdr.src_id.y, + `NOCPROF_GRP(gy, gx).gen_noc.eject_req[noc_port].hdr.src_tile_id, + `NOCPROF_GRP(gy, gx).gen_noc.eject_req[noc_port].payload.user.core_id, + `NOCPROF_GRP(gy, gx).gen_noc.eject_req[noc_port].payload.user.req_id); + end + // ---- resp: no read/write split, no addr (resp flit carries none) ---- + for (int d = 0; d < 4; d++) begin + automatic logic [1:0] si = `NOCPROF_GRP(gy, gx).rsp_mesh_in_valid[t][n][d] + ? (`NOCPROF_GRP(gy, gx).rsp_mesh_in_ready[t][n][d] ? 2'd2 : 2'd1) : 2'd0; + automatic logic [1:0] so = `NOCPROF_GRP(gy, gx).rsp_mesh_out_valid[t][n][d] + ? (`NOCPROF_GRP(gy, gx).rsp_mesh_out_ready[t][n][d] ? 2'd2 : 2'd1) : 2'd0; + if (si != rsp_st[g][t][n][d][0]) begin + $fwrite(f_rresp[g], "%0d S %0d 0 %0d %0d %0d\n", noc_port, d, rsp_start[g][t][n][d][0], cycle_q, rsp_st[g][t][n][d][0]); + rsp_st[g][t][n][d][0] <= si; rsp_start[g][t][n][d][0] <= cycle_q; + end + if (so != rsp_st[g][t][n][d][1]) begin + $fwrite(f_rresp[g], "%0d S %0d 1 %0d %0d %0d\n", noc_port, d, rsp_start[g][t][n][d][1], cycle_q, rsp_st[g][t][n][d][1]); + rsp_st[g][t][n][d][1] <= so; rsp_start[g][t][n][d][1] <= cycle_q; + end + // P: portidx io cycle dst_x dst_y src_x src_y src_tile core req_id + if (si >= 2) + $fwrite(f_rresp[g], "%0d P %0d 0 %0d %0d %0d %0d %0d %0d %0d %0d\n", noc_port, d, cycle_q, + `NOCPROF_GRP(gy, gx).rsp_mesh_in[t][n][d].hdr.dst_id.x, + `NOCPROF_GRP(gy, gx).rsp_mesh_in[t][n][d].hdr.dst_id.y, + `NOCPROF_GRP(gy, gx).rsp_mesh_in[t][n][d].hdr.src_id.x, + `NOCPROF_GRP(gy, gx).rsp_mesh_in[t][n][d].hdr.src_id.y, + `NOCPROF_GRP(gy, gx).rsp_mesh_in[t][n][d].hdr.src_tile_id, + `NOCPROF_GRP(gy, gx).rsp_mesh_in[t][n][d].payload.user.core_id, + `NOCPROF_GRP(gy, gx).rsp_mesh_in[t][n][d].payload.user.req_id); + if (so >= 2) + $fwrite(f_rresp[g], "%0d P %0d 1 %0d %0d %0d %0d %0d %0d %0d %0d\n", noc_port, d, cycle_q, + `NOCPROF_GRP(gy, gx).rsp_mesh_out[t][n][d].hdr.dst_id.x, + `NOCPROF_GRP(gy, gx).rsp_mesh_out[t][n][d].hdr.dst_id.y, + `NOCPROF_GRP(gy, gx).rsp_mesh_out[t][n][d].hdr.src_id.x, + `NOCPROF_GRP(gy, gx).rsp_mesh_out[t][n][d].hdr.src_id.y, + `NOCPROF_GRP(gy, gx).rsp_mesh_out[t][n][d].hdr.src_tile_id, + `NOCPROF_GRP(gy, gx).rsp_mesh_out[t][n][d].payload.user.core_id, + `NOCPROF_GRP(gy, gx).rsp_mesh_out[t][n][d].payload.user.req_id); + end + begin : rsp_local + automatic logic [1:0] sli = `NOCPROF_GRP(gy, gx).gen_noc.inject_rsp_valid[noc_port] + ? (`NOCPROF_GRP(gy, gx).gen_noc.inject_rsp_ready[noc_port] ? 2'd2 : 2'd1) : 2'd0; + automatic logic [1:0] slo = `NOCPROF_GRP(gy, gx).gen_noc.eject_rsp_valid[noc_port] + ? (`NOCPROF_GRP(gy, gx).gen_noc.eject_rsp_ready[noc_port] ? 2'd2 : 2'd1) : 2'd0; + if (sli != rsp_st[g][t][n][4][0]) begin + $fwrite(f_rresp[g], "%0d S 4 0 %0d %0d %0d\n", noc_port, rsp_start[g][t][n][4][0], cycle_q, rsp_st[g][t][n][4][0]); + rsp_st[g][t][n][4][0] <= sli; rsp_start[g][t][n][4][0] <= cycle_q; + end + if (slo != rsp_st[g][t][n][4][1]) begin + $fwrite(f_rresp[g], "%0d S 4 1 %0d %0d %0d\n", noc_port, rsp_start[g][t][n][4][1], cycle_q, rsp_st[g][t][n][4][1]); + rsp_st[g][t][n][4][1] <= slo; rsp_start[g][t][n][4][1] <= cycle_q; + end + if (sli >= 2) + $fwrite(f_rresp[g], "%0d P 4 0 %0d %0d %0d %0d %0d %0d %0d %0d\n", noc_port, cycle_q, + `NOCPROF_GRP(gy, gx).gen_noc.inject_rsp[noc_port].hdr.dst_id.x, + `NOCPROF_GRP(gy, gx).gen_noc.inject_rsp[noc_port].hdr.dst_id.y, + `NOCPROF_GRP(gy, gx).gen_noc.inject_rsp[noc_port].hdr.src_id.x, + `NOCPROF_GRP(gy, gx).gen_noc.inject_rsp[noc_port].hdr.src_id.y, + `NOCPROF_GRP(gy, gx).gen_noc.inject_rsp[noc_port].hdr.src_tile_id, + `NOCPROF_GRP(gy, gx).gen_noc.inject_rsp[noc_port].payload.user.core_id, + `NOCPROF_GRP(gy, gx).gen_noc.inject_rsp[noc_port].payload.user.req_id); + if (slo >= 2) + $fwrite(f_rresp[g], "%0d P 4 1 %0d %0d %0d %0d %0d %0d %0d %0d\n", noc_port, cycle_q, + `NOCPROF_GRP(gy, gx).gen_noc.eject_rsp[noc_port].hdr.dst_id.x, + `NOCPROF_GRP(gy, gx).gen_noc.eject_rsp[noc_port].hdr.dst_id.y, + `NOCPROF_GRP(gy, gx).gen_noc.eject_rsp[noc_port].hdr.src_id.x, + `NOCPROF_GRP(gy, gx).gen_noc.eject_rsp[noc_port].hdr.src_id.y, + `NOCPROF_GRP(gy, gx).gen_noc.eject_rsp[noc_port].hdr.src_tile_id, + `NOCPROF_GRP(gy, gx).gen_noc.eject_rsp[noc_port].payload.user.core_id, + `NOCPROF_GRP(gy, gx).gen_noc.eject_rsp[noc_port].payload.user.req_id); + end + end + end + end + end + end + end + end : gen_router_trace + + // ------------------------------------------------------------ + // Tile port capture -> per-tile file. RG (inter-group) ports only (see + // header comment) -- req=remote_group_req_o(out,master)/remote_group_req_i + // (in,slave) split read/write by .q.write + P packet lines; resp= + // remote_group_rsp_i(in,master)/remote_group_rsp_o(out,slave) single + // handshake state (no address). + // ------------------------------------------------------------ + if (NumRemoteGroupPortCore > 0) begin : gen_tile_trace + for (genvar gy = 0; gy < NumGroupsY; gy++) begin : gen_tstate_gy + for (genvar gx = 0; gx < NumGroupsX; gx++) begin : gen_tstate_gx + localparam int unsigned g = gy * NumGroupsX + gx; + for (genvar t = 0; t < NumTilesPerGroup; t++) begin : gen_tstate_t + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni || !files_open_q) begin + ts_mreq_st[g][t] <= '{default: '0}; ts_mreq_s[g][t] <= '{default: '0}; + ts_sreq_st[g][t] <= '{default: '0}; ts_sreq_s[g][t] <= '{default: '0}; + ts_mrsp_st[g][t] <= '{default: '0}; ts_mrsp_s[g][t] <= '{default: '0}; + ts_srsp_st[g][t] <= '{default: '0}; ts_srsp_s[g][t] <= '{default: '0}; + end else begin + for (int p = 0; p < NumRGPortTile; p++) begin + automatic logic [1:0] smo = `NOCPROF_TILE(gy, gx, t).remote_group_req_o[p].q_valid + ? (`NOCPROF_TILE(gy, gx, t).remote_group_rsp_i[p].q_ready + ? (`NOCPROF_TILE(gy, gx, t).remote_group_req_o[p].q.write ? 2'd3 : 2'd2) : 2'd1) : 2'd0; + automatic logic [1:0] ssi = `NOCPROF_TILE(gy, gx, t).remote_group_req_i[p].q_valid + ? (`NOCPROF_TILE(gy, gx, t).remote_group_rsp_o[p].q_ready + ? (`NOCPROF_TILE(gy, gx, t).remote_group_req_i[p].q.write ? 2'd3 : 2'd2) : 2'd1) : 2'd0; + if (smo != ts_mreq_st[g][t][p]) begin + $fwrite(f_tile[g][t], "S 0 1 %0d %0d %0d %0d\n", p, ts_mreq_s[g][t][p], cycle_q, ts_mreq_st[g][t][p]); + ts_mreq_st[g][t][p] <= smo; ts_mreq_s[g][t][p] <= cycle_q; + end + if (ssi != ts_sreq_st[g][t][p]) begin + $fwrite(f_tile[g][t], "S 0 0 %0d %0d %0d %0d\n", p, ts_sreq_s[g][t][p], cycle_q, ts_sreq_st[g][t][p]); + ts_sreq_st[g][t][p] <= ssi; ts_sreq_s[g][t][p] <= cycle_q; + end + // P: io port cycle wen tgt_addr src_group dst_group req_tile req_core req_id + if (smo >= 2) // remote_group_req_o out (this tile issues a request) + $fwrite(f_tile[g][t], "P 1 %0d %0d %0d %0h %0d %0d %0d %0d %0d\n", p, cycle_q, + `NOCPROF_TILE(gy, gx, t).remote_group_req_o[p].q.write, + `NOCPROF_TILE(gy, gx, t).remote_group_req_o[p].q.addr, + g, // src group + decode_group(`NOCPROF_TILE(gy, gx, t).remote_group_req_o[p].q.user.dst_tile_id), // dst group + t, // requester tile + `NOCPROF_TILE(gy, gx, t).remote_group_req_o[p].q.user.core_id, // requester core + `NOCPROF_TILE(gy, gx, t).remote_group_req_o[p].q.user.req_id); // req_id + if (ssi >= 2) // remote_group_req_i in (a request arrives at this tile) + $fwrite(f_tile[g][t], "P 0 %0d %0d %0d %0h %0d %0d %0d %0d %0d\n", p, cycle_q, + `NOCPROF_TILE(gy, gx, t).remote_group_req_i[p].q.write, + `NOCPROF_TILE(gy, gx, t).remote_group_req_i[p].q.addr, + (`NOCPROF_TILE(gy, gx, t).remote_group_req_i[p].q.user.src_group_y * NumGroupsX + + `NOCPROF_TILE(gy, gx, t).remote_group_req_i[p].q.user.src_group_x), // src group + g, // dst group + decode_local_tile(`NOCPROF_TILE(gy, gx, t).remote_group_req_i[p].q.user.tile_id), // requester tile (local to src group) + `NOCPROF_TILE(gy, gx, t).remote_group_req_i[p].q.user.core_id, // requester core + `NOCPROF_TILE(gy, gx, t).remote_group_req_i[p].q.user.req_id); // req_id + end + for (int p = 0; p < NumRGPortTile; p++) begin + automatic logic [1:0] smi = `NOCPROF_TILE(gy, gx, t).remote_group_rsp_i[p].p_valid + ? (`NOCPROF_TILE(gy, gx, t).remote_group_req_o[p].p_ready ? 2'd2 : 2'd1) : 2'd0; + automatic logic [1:0] sso = `NOCPROF_TILE(gy, gx, t).remote_group_rsp_o[p].p_valid + ? (`NOCPROF_TILE(gy, gx, t).remote_group_req_i[p].p_ready ? 2'd2 : 2'd1) : 2'd0; + // Q lines (kept apart from P req lines): + // Q + // remote_group_rsp_i(in,io0): responder group unknown at this port -> srcvalid=0, dst=this group. + // remote_group_rsp_o(out,io1): src=this group, dst=decoded from the echoed requester tile_id. + if (smi != ts_mrsp_st[g][t][p]) begin + $fwrite(f_tile[g][t], "S 1 0 %0d %0d %0d %0d\n", p, ts_mrsp_s[g][t][p], cycle_q, ts_mrsp_st[g][t][p]); + ts_mrsp_st[g][t][p] <= smi; ts_mrsp_s[g][t][p] <= cycle_q; + end + if (smi >= 2) // remote_group_rsp_i in: response arrives for this tile's core + $fwrite(f_tile[g][t], "Q 0 %0d %0d 0 %0d %0d %0d %0d %0d\n", p, cycle_q, + g, g, t, + `NOCPROF_TILE(gy, gx, t).remote_group_rsp_i[p].p.user.core_id, + `NOCPROF_TILE(gy, gx, t).remote_group_rsp_i[p].p.user.req_id); + if (sso != ts_srsp_st[g][t][p]) begin + $fwrite(f_tile[g][t], "S 1 1 %0d %0d %0d %0d\n", p, ts_srsp_s[g][t][p], cycle_q, ts_srsp_st[g][t][p]); + ts_srsp_st[g][t][p] <= sso; ts_srsp_s[g][t][p] <= cycle_q; + end + if (sso >= 2) // remote_group_rsp_o out: this tile responds to a requester + $fwrite(f_tile[g][t], "Q 1 %0d %0d 1 %0d %0d %0d %0d %0d\n", p, cycle_q, + g, + decode_group(`NOCPROF_TILE(gy, gx, t).remote_group_rsp_o[p].p.user.tile_id), + decode_local_tile(`NOCPROF_TILE(gy, gx, t).remote_group_rsp_o[p].p.user.tile_id), + `NOCPROF_TILE(gy, gx, t).remote_group_rsp_o[p].p.user.core_id, + `NOCPROF_TILE(gy, gx, t).remote_group_rsp_o[p].p.user.req_id); + end + end + end + end + end + end + end : gen_tile_trace + + // ------------------------------------------------------------ + // PE (Snitch core) reqrsp data-port capture -> per-tile file (one line + // per core). Always traced, independent of NumRemoteGroupPortCore. Same + // format as a tile port; req = core_req[c] (out, write by .q.write, addr= + // .q.addr), resp = core_rsp[c] (in). No bw/util derived for PE ports. + // S ; P + // ------------------------------------------------------------ + for (genvar gy = 0; gy < NumGroupsY; gy++) begin : gen_pe_gy + for (genvar gx = 0; gx < NumGroupsX; gx++) begin : gen_pe_gx + localparam int unsigned g = gy * NumGroupsX + gx; + for (genvar t = 0; t < NumTilesPerGroup; t++) begin : gen_pe_t + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni || !files_open_q) begin + pe_req_st[g][t] <= '{default: '0}; pe_req_s[g][t] <= '{default: '0}; + pe_rsp_st[g][t] <= '{default: '0}; pe_rsp_s[g][t] <= '{default: '0}; + end else begin + for (int c = 0; c < NumCoresTile; c++) begin + automatic logic [1:0] rq = `NOCPROF_TILE(gy, gx, t).core_req[c].q_valid + ? (`NOCPROF_TILE(gy, gx, t).core_rsp[c].q_ready + ? (`NOCPROF_TILE(gy, gx, t).core_req[c].q.write ? 2'd3 : 2'd2) : 2'd1) : 2'd0; + automatic logic [1:0] rp = `NOCPROF_TILE(gy, gx, t).core_rsp[c].p_valid + ? (`NOCPROF_TILE(gy, gx, t).core_req[c].p_ready ? 2'd2 : 2'd1) : 2'd0; + if (rq != pe_req_st[g][t][c]) begin + $fwrite(f_pe[g][t], "%0d S 0 1 0 %0d %0d %0d\n", c, pe_req_s[g][t][c], cycle_q, pe_req_st[g][t][c]); + pe_req_st[g][t][c] <= rq; pe_req_s[g][t][c] <= cycle_q; + end + if (rp != pe_rsp_st[g][t][c]) begin + $fwrite(f_pe[g][t], "%0d S 1 0 0 %0d %0d %0d\n", c, pe_rsp_s[g][t][c], cycle_q, pe_rsp_st[g][t][c]); + pe_rsp_st[g][t][c] <= rp; pe_rsp_s[g][t][c] <= cycle_q; + end + if (rq >= 2) // req handshake -> one core load/store accepted (req_id = meta_id analog) + $fwrite(f_pe[g][t], "%0d P 1 0 %0d %0d %0h %0d\n", c, cycle_q, + `NOCPROF_TILE(gy, gx, t).core_req[c].q.write, + `NOCPROF_TILE(gy, gx, t).core_req[c].q.addr, + `NOCPROF_TILE(gy, gx, t).core_req[c].q.user.req_id); + if (rp >= 2) // resp handshake -> a response returns to this core + $fwrite(f_pe[g][t], "%0d P 0 0 %0d %0d\n", c, cycle_q, + `NOCPROF_TILE(gy, gx, t).core_rsp[c].p.user.req_id); + end + end + end + end + end + end + + // ------------------------------------------------------------ + // L2 (DRAM-refill) router capture: mesh directions 0..3 (N/E/S/W) -> one + // req + one resp file per group. No tile/port fan-out (unlike the L1 + // router section above) and no local/Eject-port trace here -- the "Eject" + // ports (each group's own manager chimney into this router) aren't graph + // edges; the West-boundary HBM channels ARE ordinary West-direction mesh + // links from the leftmost column's perspective, already covered below. + // L2 uses SOURCE ROUTING (l2_noc_hdr_t), not XY, so there's no + // hdr.dst_id.x/y to log -- only hdr.src_id (an opaque floo endpoint id) + // and, for req flits, the payload addr/write. Always traced (the L2 mesh + // exists regardless of NumRemoteGroupPortCore). + // S + // req P + // resp P + // ------------------------------------------------------------ + for (genvar gy = 0; gy < NumGroupsY; gy++) begin : gen_l2_gy + for (genvar gx = 0; gx < NumGroupsX; gx++) begin : gen_l2_gx + localparam int unsigned g = gy * NumGroupsX + gx; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni || !files_open_q) begin + l2q_st[g] <= '{default: '0}; l2q_start[g] <= '{default: '0}; + l2p_st[g] <= '{default: '0}; l2p_start[g] <= '{default: '0}; + end else begin + for (int d = 0; d < 4; d++) begin + begin : l2_req + automatic logic [1:0] si = `NOCPROF_GRP(gy, gx).l2_router_req_valid_in[d] + ? (`NOCPROF_GRP(gy, gx).l2_router_req_ready_out[d] + ? (`NOCPROF_GRP(gy, gx).l2_router_req_in[d].payload.write ? 2'd3 : 2'd2) : 2'd1) : 2'd0; + automatic logic [1:0] so = `NOCPROF_GRP(gy, gx).l2_router_req_valid_out[d] + ? (`NOCPROF_GRP(gy, gx).l2_router_req_ready_in[d] + ? (`NOCPROF_GRP(gy, gx).l2_router_req_out[d].payload.write ? 2'd3 : 2'd2) : 2'd1) : 2'd0; + if (si != l2q_st[g][d][0]) begin + $fwrite(l2_f_req[g], "S %0d 0 %0d %0d %0d\n", d, l2q_start[g][d][0], cycle_q, l2q_st[g][d][0]); + l2q_st[g][d][0] <= si; l2q_start[g][d][0] <= cycle_q; + end + if (so != l2q_st[g][d][1]) begin + $fwrite(l2_f_req[g], "S %0d 1 %0d %0d %0d\n", d, l2q_start[g][d][1], cycle_q, l2q_st[g][d][1]); + l2q_st[g][d][1] <= so; l2q_start[g][d][1] <= cycle_q; + end + if (si >= 2) + $fwrite(l2_f_req[g], "P %0d 0 %0d %0d %0h %0d\n", d, cycle_q, + `NOCPROF_GRP(gy, gx).l2_router_req_in[d].payload.write, + `NOCPROF_GRP(gy, gx).l2_router_req_in[d].payload.addr, + `NOCPROF_GRP(gy, gx).l2_router_req_in[d].hdr.src_id); + if (so >= 2) + $fwrite(l2_f_req[g], "P %0d 1 %0d %0d %0h %0d\n", d, cycle_q, + `NOCPROF_GRP(gy, gx).l2_router_req_out[d].payload.write, + `NOCPROF_GRP(gy, gx).l2_router_req_out[d].payload.addr, + `NOCPROF_GRP(gy, gx).l2_router_req_out[d].hdr.src_id); + end + + begin : l2_rsp + automatic logic [1:0] pi = `NOCPROF_GRP(gy, gx).l2_router_rsp_valid_in[d] + ? (`NOCPROF_GRP(gy, gx).l2_router_rsp_ready_out[d] ? 2'd2 : 2'd1) : 2'd0; + automatic logic [1:0] po = `NOCPROF_GRP(gy, gx).l2_router_rsp_valid_out[d] + ? (`NOCPROF_GRP(gy, gx).l2_router_rsp_ready_in[d] ? 2'd2 : 2'd1) : 2'd0; + if (pi != l2p_st[g][d][0]) begin + $fwrite(l2_f_resp[g], "S %0d 0 %0d %0d %0d\n", d, l2p_start[g][d][0], cycle_q, l2p_st[g][d][0]); + l2p_st[g][d][0] <= pi; l2p_start[g][d][0] <= cycle_q; + end + if (po != l2p_st[g][d][1]) begin + $fwrite(l2_f_resp[g], "S %0d 1 %0d %0d %0d\n", d, l2p_start[g][d][1], cycle_q, l2p_st[g][d][1]); + l2p_st[g][d][1] <= po; l2p_start[g][d][1] <= cycle_q; + end + if (pi >= 2) + $fwrite(l2_f_resp[g], "P %0d 0 %0d %0d\n", d, cycle_q, + `NOCPROF_GRP(gy, gx).l2_router_rsp_in[d].hdr.src_id); + if (po >= 2) + $fwrite(l2_f_resp[g], "P %0d 1 %0d %0d\n", d, cycle_q, + `NOCPROF_GRP(gy, gx).l2_router_rsp_out[d].hdr.src_id); + end + end + end + end + end + end + + // End-of-sim flush: also covers a pending deferred close (closing_q) so a session that fell mid-close at $finish still gets its files flushed/closed. + final begin + if (files_open_q || closing_q) close_session_files(); + end + + `undef NOCPROF_GRP + `undef NOCPROF_TILE + +`endif // NOC_PROFILING + +`endif // TARGET_VERILATOR +`endif // TARGET_SYNTHESIS + +endmodule diff --git a/hardware/tb/tb_cachepool.sv b/hardware/tb/tb_cachepool.sv index 7da3e78..e6239fc 100644 --- a/hardware/tb/tb_cachepool.sv +++ b/hardware/tb/tb_cachepool.sv @@ -66,18 +66,19 @@ module tb_cachepool; localparam NumAXISlaves = 2; localparam NumRules = NumAXISlaves-1; - // Spatz wide port to SoC (currently dram) - spatz_axi_out_req_t [NumL2Channel-1:0] axi_from_cluster_req; - spatz_axi_out_resp_t [NumL2Channel-1:0] axi_from_cluster_resp; - // From SoC to Spatz - spatz_axi_in_req_t axi_to_cluster_req; - spatz_axi_in_resp_t axi_to_cluster_resp; + // Spatz wide port to SoC (currently dram); IDs narrowed by wrapper-level axi_id_remap. + spatz_axi_wrapper_out_req_t [NumL2Channel-1:0] axi_from_cluster_req; + spatz_axi_wrapper_out_resp_t [NumL2Channel-1:0] axi_from_cluster_resp; + // REQRSP peripheral in-port to cluster (32b narrow with refill_user_t). + peri_narrow_req_t peri_to_cluster_req; + peri_narrow_rsp_t peri_to_cluster_rsp; - axi_uart_req_t axi_uart_req; - axi_uart_resp_t axi_uart_rsp; + // UART; IDs compressed by wrapper-level axi_id_remap (SpatzAxiUartIdWidth → WrapperAxiNarrowIdOutWidth). + spatz_axi_wrapper_narrow_out_req_t axi_uart_req; + spatz_axi_wrapper_narrow_out_resp_t axi_uart_rsp; // DRAM Scrambled request - spatz_axi_out_req_t [NumL2Channel-1:0] axi_dram_req; + spatz_axi_wrapper_out_req_t [NumL2Channel-1:0] axi_dram_req; /********* @@ -99,10 +100,25 @@ module tb_cachepool; .axi_out_resp_i (axi_from_cluster_resp ), .axi_narrow_req_o (axi_uart_req ), .axi_narrow_resp_i (axi_uart_rsp ), - .axi_in_req_i (axi_to_cluster_req ), - .axi_in_resp_o (axi_to_cluster_resp ), + .peri_ext_req_i (peri_to_cluster_req ), + .peri_ext_rsp_o (peri_to_cluster_rsp ), .cluster_probe_o (cluster_probe ) ); + + // Monitor block to generate logs for performance debugging + cachepool_monitor i_cachepool_monitor ( + .clk_i (clk ), + .rst_ni (rst_n ), + .cluster_probe_i (cluster_probe ) + ); + + // Cycle-accurate per-router/tile/core NoC traffic logs for perfetto_gen.py + // (enabled at compile time with +define+NOC_PROFILING) + cachepool_noc_profiling i_cachepool_noc_profiling ( + .clk_i (clk ), + .rst_ni (rst_n ), + .cluster_probe_i (cluster_probe ) + ); /************** * VCD Dump * **************/ @@ -136,29 +152,13 @@ module tb_cachepool; * Simulation control * ************************/ - `REQRSP_TYPEDEF_ALL(reqrsp_cluster_in, axi_addr_t, logic [31:0], logic [3:0], tcdm_user_t) - reqrsp_cluster_in_req_t to_cluster_req; - reqrsp_cluster_in_rsp_t to_cluster_rsp; - - reqrsp_to_axi #( - .DataWidth (SpatzDataWidth ), - .AxiUserWidth(SpatzAxiUserWidth ), - .UserWidth ($bits(tcdm_user_t) ), - .axi_req_t (spatz_axi_in_req_t ), - .axi_rsp_t (spatz_axi_in_resp_t ), - .reqrsp_req_t(reqrsp_cluster_in_req_t), - .reqrsp_rsp_t(reqrsp_cluster_in_rsp_t) - ) i_reqrsp_to_axi ( - .clk_i (clk ), - .rst_ni (rst_n ), - .user_i ('0 ), - .axi_req_o (axi_to_cluster_req ), - .axi_rsp_i (axi_to_cluster_resp), - .reqrsp_req_i(to_cluster_req ), - .reqrsp_rsp_o(to_cluster_rsp ) - ); - + // REQRSP signals driven by the simulation sequence (fesvr). + peri_narrow_req_t to_cluster_req; + peri_narrow_rsp_t to_cluster_rsp; + // Direct passthrough to cluster wrapper (no AXI conversion needed). + assign peri_to_cluster_req = to_cluster_req; + assign to_cluster_rsp = peri_to_cluster_rsp; logic [31:0] entry_point; @@ -178,7 +178,7 @@ module tb_cachepool; entry_point = get_entry_point(); $display("Loading entry point: %0x", entry_point); - // Wait for a while + // Wait for a while, so that all cores are in wfi mode repeat (1000) @(posedge clk); @@ -211,6 +211,79 @@ module tb_cachepool; to_cluster_req = '0; + // Initialize L1D cache before waking up cores + // Step 1: Write init instruction (flush + invalidate) + to_cluster_req = '{ + q: '{ + addr : PeriStartAddr + CACHEPOOL_PERIPHERAL_CFG_L1D_INSN_OFFSET, + data : 32'h3, + write : 1'b1, + strb : '1, + amo : reqrsp_pkg::AMONone, + default: '0 + }, + q_valid: 1'b1, + p_ready: 1'b0 + }; + `wait_for(to_cluster_rsp.q_ready); + to_cluster_req = '0; + `wait_for(to_cluster_rsp.p_valid); + to_cluster_req = '{p_ready: 1'b1, q: '{amo: reqrsp_pkg::AMONone, default: '0}, default: '0}; + @(posedge clk); + to_cluster_req = '0; + + // Step 2: Commit the instruction + to_cluster_req = '{ + q: '{ + addr : PeriStartAddr + CACHEPOOL_PERIPHERAL_L1D_INSN_COMMIT_OFFSET, + data : 32'h1, + write : 1'b1, + strb : '1, + amo : reqrsp_pkg::AMONone, + default: '0 + }, + q_valid: 1'b1, + p_ready: 1'b0 + }; + `wait_for(to_cluster_rsp.q_ready); + to_cluster_req = '0; + `wait_for(to_cluster_rsp.p_valid); + to_cluster_req = '{p_ready: 1'b1, q: '{amo: reqrsp_pkg::AMONone, default: '0}, default: '0}; + @(posedge clk); + to_cluster_req = '0; + + // Step 3: Poll until flush complete + begin + automatic logic [31:0] flush_status; + do begin + // Wait for 100 cycles before polling + repeat (100) + @(posedge clk); + to_cluster_req = '{ + q: '{ + addr : PeriStartAddr + CACHEPOOL_PERIPHERAL_L1D_FLUSH_STATUS_OFFSET, + write : 1'b0, + strb : '0, + amo : reqrsp_pkg::AMONone, + default: '0 + }, + q_valid: 1'b1, + p_ready: 1'b0 + }; + `wait_for(to_cluster_rsp.q_ready); + to_cluster_req = '0; + `wait_for(to_cluster_rsp.p_valid); + flush_status = to_cluster_rsp.p.data; + to_cluster_req = '{p_ready: 1'b1, q: '{amo: reqrsp_pkg::AMONone, default: '0}, default: '0}; + @(posedge clk); + to_cluster_req = '0; + end while (flush_status[0]); + end + + // Wait for a while, so that all cores are in wfi mode + repeat (1000) + @(posedge clk); + // Wake up cores debug_req = '1; @(posedge clk); @@ -227,8 +300,8 @@ module tb_cachepool; **********/ axi_uart #( - .axi_req_t (axi_uart_req_t ), - .axi_resp_t(axi_uart_resp_t) + .axi_req_t (spatz_axi_wrapper_narrow_out_req_t ), + .axi_resp_t(spatz_axi_wrapper_narrow_out_resp_t) ) i_axi_uart ( .clk_i (clk ), .rst_ni (rst_n ), @@ -242,10 +315,6 @@ module tb_cachepool; * L2 * ********/ - localparam int unsigned ConstantBits = $clog2(L2BankBeWidth * Interleave); - localparam int unsigned ScrambleBits = (NumL2Channel == 1) ? 1 : $clog2(NumL2Channel); - localparam int unsigned ReminderBits = SpatzAxiAddrWidth - ScrambleBits - ConstantBits; - dram_sim_engine #( .ClkPeriod (ClockPeriod ) ) i_dram_engine ( @@ -256,6 +325,9 @@ module tb_cachepool; localparam int unsigned debug = 0; // DRAMSys Initialization + // + // Interleaving-based: uses getDramCTRLInfo to map each byte to the correct + // DRAM channel and local address, matching the scrambleAddr routing in HW. for (genvar mem = 0; mem < NumL2Channel; mem++) begin : gen_drams_init initial begin : l2_init byte buffer []; @@ -278,13 +350,14 @@ module tb_cachepool; buffer = new[nwords * L2BankBeWidth]; void'(read_section(address, buffer)); if (address >= DramBase) begin - for (int i = 0; i < nwords * L2BankBeWidth; i++) begin //per byte + for (int i = 0; i < nwords * L2BankBeWidth; i++) begin automatic dram_ctrl_interleave_t dram_ctrl_info; dram_ctrl_info = getDramCTRLInfo(address + i - DramBase); if (dram_ctrl_info.dram_ctrl_id == mem) begin - gen_dram[mem].i_axi_dram_sim.i_sim_dram.load_a_byte_to_dram(dram_ctrl_info.dram_ctrl_addr, buffer[i]); + gen_dram[mem].i_axi_dram_sim.i_sim_dram.load_a_byte_to_dram( + dram_ctrl_info.dram_ctrl_addr, buffer[i]); if (debug == 1) begin - $display("putting data at %x into mem%x", dram_ctrl_info.dram_ctrl_addr, dram_ctrl_info.dram_ctrl_id); + $display("putting data at %x into mem%x", dram_ctrl_info.dram_ctrl_addr, mem); end end end @@ -298,8 +371,6 @@ module tb_cachepool; axi_addr_t [NumL2Channel-1:0] temp_addr_aw, temp_addr_ar; dram_ctrl_interleave_t [NumL2Channel-1:0] temp_dram_info_aw, temp_dram_info_ar; - - // DRAMSys address scrambling for (genvar ch = 0; ch < NumClusterSlv; ch ++) begin : gen_dram_scrambler always_comb begin axi_dram_req[ch] = axi_from_cluster_req[ch]; @@ -314,19 +385,19 @@ module tb_cachepool; for (genvar mem = 0; mem < NumL2Channel; mem++) begin: gen_dram axi_dram_sim #( - .BASE ( DramBase ), - .DRAMType ( DramType ), - .AxiAddrWidth ( SpatzAxiAddrWidth ), - .AxiDataWidth ( SpatzAxiDataWidth ), - .AxiIdWidth ( SpatzAxiIdOutWidth ), - .AxiUserWidth ( SpatzAxiUserWidth ), - .axi_req_t ( spatz_axi_out_req_t ), - .axi_resp_t ( spatz_axi_out_resp_t ), - .axi_ar_t ( spatz_axi_out_ar_chan_t ), - .axi_r_t ( spatz_axi_out_r_chan_t ), - .axi_aw_t ( spatz_axi_out_aw_chan_t ), - .axi_w_t ( spatz_axi_out_w_chan_t ), - .axi_b_t ( spatz_axi_out_b_chan_t ) + .BASE ( DramBase ), + .DRAMType ( DramType ), + .AxiAddrWidth ( SpatzAxiAddrWidth ), + .AxiDataWidth ( SpatzAxiDataWidth ), + .AxiIdWidth ( WrapperAxiIdOutWidth ), + .AxiUserWidth ( SpatzAxiUserWidth ), + .axi_req_t ( spatz_axi_wrapper_out_req_t ), + .axi_resp_t ( spatz_axi_wrapper_out_resp_t ), + .axi_ar_t ( spatz_axi_wrapper_out_ar_chan_t ), + .axi_r_t ( spatz_axi_wrapper_out_r_chan_t ), + .axi_aw_t ( spatz_axi_wrapper_out_aw_chan_t ), + .axi_w_t ( spatz_axi_wrapper_out_w_chan_t ), + .axi_b_t ( spatz_axi_wrapper_out_b_chan_t ) ) i_axi_dram_sim ( .clk_i ( clk ), .rst_ni ( rst_n ), diff --git a/iis-env.sh b/iis-env.sh new file mode 100644 index 0000000..787ba0c --- /dev/null +++ b/iis-env.sh @@ -0,0 +1,19 @@ +#!/bin/bash +# Copyright 2025 ETH Zurich and University of Bologna. +# Solderpad Hardware License, Version 0.51, see LICENSE for details. +# SPDX-License-Identifier: SHL-0.51 +# +# Sources the environment needed for the CachePool build on the ETH IIS CI runners. +# Replaces `make quick-tool`'s symlink: GCC_INSTALL_DIR/LLVM_INSTALL_DIR/BENDER_INSTALL_DIR/ +# ISA_SIM_INSTALL_DIR are derived from INSTALL_DIR via `?=` in toolchain.mk, so exporting +# INSTALL_DIR here is sufficient - no PATH changes needed, the runner already provides +# bender/clang/cmake/python/pip/gcc on PATH. + +export INSTALL_DIR=/home/dishen/cachepool-32b/install + +# Python deps for hardware code generation (make generate) - venv instead of uv, +# to match the existing local dev flow. +python3 -m venv cachepool +source cachepool/bin/activate +python3 -m pip install --quiet --upgrade pip +python3 -m pip install --quiet -r requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..3e97837 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,12 @@ +# Python packages required for hardware code generation (make generate). +# dataclasses is a stdlib backport needed on Python 3.6; it is a no-op on 3.7+. +dataclasses +hjson +jsonref +jsonschema +mako +termcolor +numpy +# CPU-only torch for data generation scripts (gen_data.py in software/tests/) +--extra-index-url https://download.pytorch.org/whl/cpu +torch diff --git a/sim/scripts/vsim_cluster.tcl b/sim/scripts/vsim_cluster.tcl index c8d66f3..564d33b 100644 --- a/sim/scripts/vsim_cluster.tcl +++ b/sim/scripts/vsim_cluster.tcl @@ -5,15 +5,63 @@ # Create group for Cluster onerror {resume} -set cluster_path $1 +quietly set cluster_path $1 +quietly set hbm0_path ${cluster_path}/gen_l2_refill_mesh/gen_hbm_west[0] -add wave -noupdate -group Cluster -group xbar -group req_xbar ${cluster_path}/i_cluster_xbar/i_req_xbar/* -add wave -noupdate -group Cluster -group xbar -group rsp_xbar ${cluster_path}/i_cluster_xbar/i_rsp_xbar/* -add wave -noupdate -group Cluster -group xbar ${cluster_path}/i_cluster_xbar/* +# Bootrom +add wave -noupdate -group Cluster -group BootROM ${cluster_path}/i_bootrom/* -add wave -noupdate -group Cluster -group CSR ${cluster_path}/i_cachepool_cluster_peripheral/* +add wave -noupdate -group Cluster -group BootROM -group Converter ${cluster_path}/i_reqrsp_to_reg_bootrom/* + +# Peripheral +add wave -noupdate -group Cluster -group Periph -group PeriReg ${cluster_path}/i_cachepool_cluster_peripheral/* + +add wave -noupdate -group Cluster -group Periph -group PeriXBar ${cluster_path}/i_peri_xbar/* + +if {[llength [find instances -nodu ${hbm0_path}/gen_hbm0_demux/i_hbm0_dram_reqrsp_to_axi]] > 0} { + add wave -noupdate -group Cluster -group Periph -group reqrsp2axi ${hbm0_path}/gen_hbm0_demux/i_hbm0_dram_reqrsp_to_axi/* +} +if {[llength [find instances -nodu ${hbm0_path}/gen_hbm0_demux/i_hbm0_reqrsp_demux]] > 0} { + add wave -noupdate -group Cluster -group Periph -group demux ${hbm0_path}/gen_hbm0_demux/i_hbm0_reqrsp_demux/* +} +if {[llength [find instances -nodu ${hbm0_path}/gen_hbm0_demux/i_hbm0_peri_dw]] > 0} { + add wave -noupdate -group Cluster -group Periph -group dw_conv ${hbm0_path}/gen_hbm0_demux/i_hbm0_peri_dw/* +} + + +# Barrier +add wave -noupdate -group Barrier -group Global ${cluster_path}/i_cluster_barrier/* + + +# L2 NoC +# HBM chimneys sit at the West/East edges of the L2 refill mesh, up to +# 8 channels per side (16 total). Loop over all 16 and stop each side +# independently on its first missing index, since the exact channel +# count depends on the config. +quietly set west_done 0 +quietly set east_done 0 +for {set idx 0} {$idx < 16} {incr idx} { + if {$idx < 8} { + if {$west_done} { continue } + quietly set side "west" + quietly set gy $idx + } else { + if {$east_done} { continue } + quietly set side "east" + quietly set gy [expr {$idx - 8}] + } + quietly set hbm_chimney_path ${cluster_path}/gen_l2_refill_mesh/gen_hbm_${side}[${gy}]/i_hbm_chimney + if {[catch {add wave -noupdate -group Cluster -group L2NoC -group HBM_${side}${gy}_chimney ${hbm_chimney_path}/*}]} { + if {$idx < 8} { + quietly set west_done 1 + } else { + quietly set east_done 1 + } + } +} + +# Others add wave -noupdate -group Cluster -group Internal ${cluster_path}/* -add wave -noupdate -group Barrier -group Global ${cluster_path}/i_cachepool_cluster_barrier/* diff --git a/sim/scripts/vsim_core.tcl b/sim/scripts/vsim_core.tcl index 9510e33..30ee61a 100644 --- a/sim/scripts/vsim_core.tcl +++ b/sim/scripts/vsim_core.tcl @@ -4,179 +4,192 @@ # Create group for core $1 onerror {resume} - -set core_path ${3} - -add wave -noupdate -group tile[$1]_core[$2] -group scalar_xbar ${core_path}/i_cachepool_cc/i_scalar_xbar/* - -add wave -noupdate -group tile[$1]_core[$2] -group Params ${core_path}/i_cachepool_cc/BootAddr -add wave -noupdate -group tile[$1]_core[$2] ${core_path}/i_cachepool_cc/i_snitch/clk_i -add wave -noupdate -group tile[$1]_core[$2] ${core_path}/i_cachepool_cc/i_snitch/rst_i -add wave -noupdate -group tile[$1]_core[$2] -radix unsigned ${core_path}/i_cachepool_cc/i_snitch/hart_id_i - -add wave -noupdate -group tile[$1]_core[$2] -divider Instructions -add wave -noupdate -group tile[$1]_core[$2] ${core_path}/i_cachepool_cc/i_snitch/inst_addr_o -add wave -noupdate -group tile[$1]_core[$2] ${core_path}/i_cachepool_cc/i_snitch/inst_data_i -add wave -noupdate -group tile[$1]_core[$2] ${core_path}/i_cachepool_cc/i_snitch/inst_valid_o -add wave -noupdate -group tile[$1]_core[$2] ${core_path}/i_cachepool_cc/i_snitch/inst_ready_i - -add wave -noupdate -group tile[$1]_core[$2] -divider Load/Store -add wave -noupdate -group tile[$1]_core[$2] ${core_path}/i_cachepool_cc/i_snitch/data_req_o -add wave -noupdate -group tile[$1]_core[$2] ${core_path}/i_cachepool_cc/i_snitch/data_rsp_i - -add wave -noupdate -group tile[$1]_core[$2] -divider Accelerator -add wave -noupdate -group tile[$1]_core[$2] ${core_path}/i_cachepool_cc/i_snitch/acc_qreq_o -add wave -noupdate -group tile[$1]_core[$2] ${core_path}/i_cachepool_cc/i_snitch/acc_qrsp_i -add wave -noupdate -group tile[$1]_core[$2] ${core_path}/i_cachepool_cc/i_snitch/acc_qvalid_o -add wave -noupdate -group tile[$1]_core[$2] ${core_path}/i_cachepool_cc/i_snitch/acc_qready_i -add wave -noupdate -group tile[$1]_core[$2] ${core_path}/i_cachepool_cc/i_snitch/acc_prsp_i -add wave -noupdate -group tile[$1]_core[$2] ${core_path}/i_cachepool_cc/i_snitch/acc_pvalid_i -add wave -noupdate -group tile[$1]_core[$2] ${core_path}/i_cachepool_cc/i_snitch/acc_pready_o - -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/illegal_inst -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/stall -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/lsu_stall -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/acc_stall -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/zero_lsb -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/pc_d -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/pc_q -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/wfi_d -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/wfi_q -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/fcsr_d -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/fcsr_q -add wave -noupdate -group tile[$1]_core[$2] -group Snitch -divider LSU -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/ls_size -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/ls_amo -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/ld_result -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/lsu_qready -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/lsu_qvalid -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/lsu_pvalid -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/lsu_pready -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/lsu_rd -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/retire_load -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/retire_i -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/retire_acc -add wave -noupdate -group tile[$1]_core[$2] -group Snitch -divider ALU -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/opa -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/opb -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/iimm -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/uimm -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/jimm -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/bimm -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/simm -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/adder_result -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/alu_result -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/rd -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/rs1 -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/rs2 -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/gpr_raddr -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/gpr_rdata -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/gpr_waddr -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/gpr_wdata -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/gpr_we -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/consec_pc -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/sb_d -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/sb_q -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/is_load -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/is_store -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/is_signed -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/ls_misaligned -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/ld_addr_misaligned -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/st_addr_misaligned -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/valid_instr -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/exception -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/alu_op -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/opa_select -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/opb_select -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/write_rd -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/uses_rd -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/next_pc -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/rd_select -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/rd_bypass -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/is_branch -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/csr_rvalue -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/csr_en -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/acc_register_rd -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/operands_ready -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/dst_ready -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/opa_ready -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/opb_ready -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/shift_opa -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/shift_opa_reversed -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/shift_right_result -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/shift_left_result -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/shift_opa_ext -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/shift_right_result_ext -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/shift_left -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/shift_arithmetic -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/alu_opa -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/alu_opb -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/alu_writeback -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/acc_mem_cnt_d -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/acc_mem_cnt_q -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/acc_mem_str_cnt_d -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/acc_mem_str_cnt_q -add wave -noupdate -group tile[$1]_core[$2] -group Snitch ${core_path}/i_cachepool_cc/i_snitch/core_events_o - -add wave -noupdate -group tile[$1]_core[$2] -group Snitch -group Internal -group RF ${core_path}/i_cachepool_cc/i_snitch/i_snitch_regfile/* -add wave -noupdate -group tile[$1]_core[$2] -group Snitch -group Internal ${core_path}/i_cachepool_cc/i_snitch/* - -add wave -noupdate -group tile[$1]_core[$2] -group Spatz ${core_path}/i_cachepool_cc/i_spatz/issue_valid_i -add wave -noupdate -group tile[$1]_core[$2] -group Spatz ${core_path}/i_cachepool_cc/i_spatz/issue_ready_o -add wave -noupdate -group tile[$1]_core[$2] -group Spatz ${core_path}/i_cachepool_cc/i_spatz/issue_req_i -add wave -noupdate -group tile[$1]_core[$2] -group Spatz ${core_path}/i_cachepool_cc/i_spatz/issue_rsp_o -add wave -noupdate -group tile[$1]_core[$2] -group Spatz ${core_path}/i_cachepool_cc/i_spatz/rsp_valid_o -add wave -noupdate -group tile[$1]_core[$2] -group Spatz ${core_path}/i_cachepool_cc/i_spatz/rsp_ready_i -add wave -noupdate -group tile[$1]_core[$2] -group Spatz ${core_path}/i_cachepool_cc/i_spatz/rsp_o -add wave -noupdate -group tile[$1]_core[$2] -group Spatz ${core_path}/i_cachepool_cc/i_spatz/spatz_mem_req_o -add wave -noupdate -group tile[$1]_core[$2] -group Spatz ${core_path}/i_cachepool_cc/i_spatz/spatz_mem_req_valid_o -add wave -noupdate -group tile[$1]_core[$2] -group Spatz ${core_path}/i_cachepool_cc/i_spatz/spatz_mem_req_ready_i -add wave -noupdate -group tile[$1]_core[$2] -group Spatz ${core_path}/i_cachepool_cc/i_spatz/spatz_mem_rsp_i -add wave -noupdate -group tile[$1]_core[$2] -group Spatz ${core_path}/i_cachepool_cc/i_spatz/spatz_mem_rsp_valid_i - -add wave -noupdate -group tile[$1]_core[$2] -group Spatz -group "FPU Sequencer" ${core_path}/i_cachepool_cc/i_spatz/gen_fpu_sequencer/i_fpu_sequencer/* -add wave -noupdate -group tile[$1]_core[$2] -group Spatz -group "FPU Sequencer" -group FPR ${core_path}/i_cachepool_cc/i_spatz/gen_fpu_sequencer/i_fpu_sequencer/i_fpr/* -add wave -noupdate -group tile[$1]_core[$2] -group Spatz -group "FPU Sequencer" -group LSU ${core_path}/i_cachepool_cc/i_spatz/gen_fpu_sequencer/i_fpu_sequencer/i_fp_lsu/* - -add wave -noupdate -group tile[$1]_core[$2] -group Spatz -group Controller ${core_path}/i_cachepool_cc/i_spatz/i_controller/* - -add wave -noupdate -group tile[$1]_core[$2] -group Spatz -group VRF -divider RegisterWrite -add wave -noupdate -group tile[$1]_core[$2] -group Spatz -group VRF ${core_path}/i_cachepool_cc/i_spatz/i_vrf/waddr_i -add wave -noupdate -group tile[$1]_core[$2] -group Spatz -group VRF ${core_path}/i_cachepool_cc/i_spatz/i_vrf/wdata_i -add wave -noupdate -group tile[$1]_core[$2] -group Spatz -group VRF ${core_path}/i_cachepool_cc/i_spatz/i_vrf/we_i -add wave -noupdate -group tile[$1]_core[$2] -group Spatz -group VRF ${core_path}/i_cachepool_cc/i_spatz/i_vrf/wbe_i -add wave -noupdate -group tile[$1]_core[$2] -group Spatz -group VRF ${core_path}/i_cachepool_cc/i_spatz/i_vrf/wvalid_o -add wave -noupdate -group tile[$1]_core[$2] -group Spatz -group VRF -divider RegisterRead -add wave -noupdate -group tile[$1]_core[$2] -group Spatz -group VRF ${core_path}/i_cachepool_cc/i_spatz/i_vrf/raddr_i -add wave -noupdate -group tile[$1]_core[$2] -group Spatz -group VRF ${core_path}/i_cachepool_cc/i_spatz/i_vrf/rdata_o -add wave -noupdate -group tile[$1]_core[$2] -group Spatz -group VRF ${core_path}/i_cachepool_cc/i_spatz/i_vrf/re_i -add wave -noupdate -group tile[$1]_core[$2] -group Spatz -group VRF ${core_path}/i_cachepool_cc/i_spatz/i_vrf/rvalid_o -add wave -noupdate -group tile[$1]_core[$2] -group Spatz -group VRF -divider Internal -add wave -noupdate -group tile[$1]_core[$2] -group Spatz -group VRF ${core_path}/i_cachepool_cc/i_spatz/i_vrf/waddr -add wave -noupdate -group tile[$1]_core[$2] -group Spatz -group VRF ${core_path}/i_cachepool_cc/i_spatz/i_vrf/wdata -add wave -noupdate -group tile[$1]_core[$2] -group Spatz -group VRF ${core_path}/i_cachepool_cc/i_spatz/i_vrf/we -add wave -noupdate -group tile[$1]_core[$2] -group Spatz -group VRF ${core_path}/i_cachepool_cc/i_spatz/i_vrf/wbe -add wave -noupdate -group tile[$1]_core[$2] -group Spatz -group VRF ${core_path}/i_cachepool_cc/i_spatz/i_vrf/raddr -add wave -noupdate -group tile[$1]_core[$2] -group Spatz -group VRF ${core_path}/i_cachepool_cc/i_spatz/i_vrf/rdata - -add wave -noupdate -group tile[$1]_core[$2] -group Spatz -group VLSU ${core_path}/i_cachepool_cc/i_spatz/i_vlsu/* - -add wave -noupdate -group tile[$1]_core[$2] -group Spatz -group VSLDU ${core_path}/i_cachepool_cc/i_spatz/i_vsldu/* - -add wave -noupdate -group tile[$1]_core[$2] -group Spatz -group VFU ${core_path}/i_cachepool_cc/i_spatz/i_vfu/* - -add wave -noupdate -group tile[$1]_core[$2] -group Spatz -group FPU ${core_path}/i_cachepool_cc/i_spatz/i_vfu/gen_fpu/* - -add wave -noupdate -group tile[$1]_core[$2] -group Internal ${core_path}/i_cachepool_cc/* - -add wave -noupdate -group tile[$1]_core[$2] -group Spatz -group VLSU -group ROB0 ${core_path}/i_cachepool_cc/i_spatz/i_vlsu/gen_rob[0]/i_reorder_buffer/* -add wave -noupdate -group tile[$1]_core[$2] -group Spatz -group VLSU -group ROB1 ${core_path}/i_cachepool_cc/i_spatz/i_vlsu/gen_rob[1]/i_reorder_buffer/* - -add wave -noupdate -group tile[$1]_core[$2] -group Spatz -group RSP_FIFO ${core_path}/i_cachepool_cc/spatz_mem_rsp -add wave -noupdate -group tile[$1]_core[$2] -group Spatz -group RSP_FIFO ${core_path}/i_cachepool_cc/spatz_mem_fifo -add wave -noupdate -group tile[$1]_core[$2] -group Spatz -group RSP_FIFO ${core_path}/i_cachepool_cc/spatz_mem_rsp_valid -add wave -noupdate -group tile[$1]_core[$2] -group Spatz -group RSP_FIFO ${core_path}/i_cachepool_cc/spatz_mem_rsp_ready -add wave -noupdate -group tile[$1]_core[$2] -group Spatz -group RSP_FIFO ${core_path}/i_cachepool_cc/spatz_mem_rsp_empty -add wave -noupdate -group tile[$1]_core[$2] -group Spatz -group RSP_FIFO ${core_path}/i_cachepool_cc/spatz_mem_rsp_pop -add wave -noupdate -group tile[$1]_core[$2] -group Spatz -group RSP_FIFO ${core_path}/i_cachepool_cc/spatz_mem_rsp_push -add wave -noupdate -group tile[$1]_core[$2] -group Spatz -group RSP_FIFO ${core_path}/i_cachepool_cc/spatz_mem_fifo_bypass +quietly WaveActivateNextPane {} 0 + +quietly set core_path ${4} +quietly set name g_${1}_t_${2}_c_${3} + +# Build the parent group prefix list from optional args 5 (GroupWP) and 6 (tile) +quietly set parent_grp [list] +if {$argc > 4 && "${5}" != ""} { + quietly lappend parent_grp -group ${5} +} +if {$argc > 5 && "${6}" != ""} { + quietly lappend parent_grp -group ${6} +} + +# The {*} syntax safely expands the list. +# If $parent_grp is empty, it safely ignores it instead of passing "". +add wave -noupdate {*}$parent_grp -group ${name} -group scalar_xbar ${core_path}/i_cachepool_cc/i_scalar_xbar/* + +add wave -noupdate {*}$parent_grp -group ${name} -group Params ${core_path}/i_cachepool_cc/BootAddr +add wave -noupdate {*}$parent_grp -group ${name} ${core_path}/i_cachepool_cc/i_snitch/clk_i +add wave -noupdate {*}$parent_grp -group ${name} ${core_path}/i_cachepool_cc/i_snitch/rst_i +add wave -noupdate {*}$parent_grp -group ${name} -radix unsigned ${core_path}/i_cachepool_cc/i_snitch/hart_id_i + +add wave -noupdate {*}$parent_grp -group ${name} -divider Instructions +add wave -noupdate {*}$parent_grp -group ${name} ${core_path}/i_cachepool_cc/i_snitch/inst_addr_o +add wave -noupdate {*}$parent_grp -group ${name} ${core_path}/i_cachepool_cc/i_snitch/inst_data_i +add wave -noupdate {*}$parent_grp -group ${name} ${core_path}/i_cachepool_cc/i_snitch/inst_valid_o +add wave -noupdate {*}$parent_grp -group ${name} ${core_path}/i_cachepool_cc/i_snitch/inst_ready_i + +add wave -noupdate {*}$parent_grp -group ${name} -divider Load/Store +add wave -noupdate {*}$parent_grp -group ${name} ${core_path}/i_cachepool_cc/i_snitch/data_req_o +add wave -noupdate {*}$parent_grp -group ${name} ${core_path}/i_cachepool_cc/i_snitch/data_rsp_i + +add wave -noupdate {*}$parent_grp -group ${name} -divider Accelerator +add wave -noupdate {*}$parent_grp -group ${name} ${core_path}/i_cachepool_cc/i_snitch/acc_qreq_o +add wave -noupdate {*}$parent_grp -group ${name} ${core_path}/i_cachepool_cc/i_snitch/acc_qrsp_i +add wave -noupdate {*}$parent_grp -group ${name} ${core_path}/i_cachepool_cc/i_snitch/acc_qvalid_o +add wave -noupdate {*}$parent_grp -group ${name} ${core_path}/i_cachepool_cc/i_snitch/acc_qready_i +add wave -noupdate {*}$parent_grp -group ${name} ${core_path}/i_cachepool_cc/i_snitch/acc_prsp_i +add wave -noupdate {*}$parent_grp -group ${name} ${core_path}/i_cachepool_cc/i_snitch/acc_pvalid_i +add wave -noupdate {*}$parent_grp -group ${name} ${core_path}/i_cachepool_cc/i_snitch/acc_pready_o + +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/illegal_inst +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/stall +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/lsu_stall +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/acc_stall +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/zero_lsb +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/pc_d +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/pc_q +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/wfi_d +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/wfi_q +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/fcsr_d +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/fcsr_q +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch -divider LSU +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/ls_size +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/ls_amo +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/ld_result +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/lsu_qready +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/lsu_qvalid +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/lsu_pvalid +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/lsu_pready +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/lsu_rd +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/retire_load +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/retire_i +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/retire_acc +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch -divider ALU +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/opa +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/opb +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/iimm +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/uimm +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/jimm +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/bimm +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/simm +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/adder_result +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/alu_result +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/rd +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/rs1 +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/rs2 +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/gpr_raddr +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/gpr_rdata +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/gpr_waddr +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/gpr_wdata +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/gpr_we +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/consec_pc +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/sb_d +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/sb_q +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/is_load +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/is_store +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/is_signed +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/ls_misaligned +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/ld_addr_misaligned +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/st_addr_misaligned +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/valid_instr +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/exception +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/alu_op +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/opa_select +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/opb_select +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/write_rd +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/uses_rd +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/next_pc +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/rd_select +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/rd_bypass +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/is_branch +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/csr_rvalue +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/csr_en +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/acc_register_rd +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/operands_ready +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/dst_ready +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/opa_ready +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/opb_ready +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/shift_opa +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/shift_opa_reversed +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/shift_right_result +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/shift_left_result +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/shift_opa_ext +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/shift_right_result_ext +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/shift_left +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/shift_arithmetic +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/alu_opa +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/alu_opb +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/alu_writeback +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/acc_mem_cnt_d +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/acc_mem_cnt_q +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/acc_mem_str_cnt_d +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/acc_mem_str_cnt_q +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch ${core_path}/i_cachepool_cc/i_snitch/core_events_o + +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch -group Internal -group RF ${core_path}/i_cachepool_cc/i_snitch/i_snitch_regfile/* +add wave -noupdate {*}$parent_grp -group ${name} -group Snitch -group Internal ${core_path}/i_cachepool_cc/i_snitch/* + +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz ${core_path}/i_cachepool_cc/i_spatz/issue_valid_i +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz ${core_path}/i_cachepool_cc/i_spatz/issue_ready_o +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz ${core_path}/i_cachepool_cc/i_spatz/issue_req_i +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz ${core_path}/i_cachepool_cc/i_spatz/issue_rsp_o +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz ${core_path}/i_cachepool_cc/i_spatz/rsp_valid_o +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz ${core_path}/i_cachepool_cc/i_spatz/rsp_ready_i +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz ${core_path}/i_cachepool_cc/i_spatz/rsp_o +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz ${core_path}/i_cachepool_cc/i_spatz/spatz_mem_req_o +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz ${core_path}/i_cachepool_cc/i_spatz/spatz_mem_req_valid_o +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz ${core_path}/i_cachepool_cc/i_spatz/spatz_mem_req_ready_i +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz ${core_path}/i_cachepool_cc/i_spatz/spatz_mem_rsp_i +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz ${core_path}/i_cachepool_cc/i_spatz/spatz_mem_rsp_valid_i + +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz -group "FPU Sequencer" ${core_path}/i_cachepool_cc/i_spatz/gen_fpu_sequencer/i_fpu_sequencer/* +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz -group "FPU Sequencer" -group FPR ${core_path}/i_cachepool_cc/i_spatz/gen_fpu_sequencer/i_fpu_sequencer/i_fpr/* +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz -group "FPU Sequencer" -group LSU ${core_path}/i_cachepool_cc/i_spatz/gen_fpu_sequencer/i_fpu_sequencer/i_fp_lsu/* + +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz -group Controller ${core_path}/i_cachepool_cc/i_spatz/i_controller/* + +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz -group VRF -divider RegisterWrite +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz -group VRF ${core_path}/i_cachepool_cc/i_spatz/i_vrf/waddr_i +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz -group VRF ${core_path}/i_cachepool_cc/i_spatz/i_vrf/wdata_i +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz -group VRF ${core_path}/i_cachepool_cc/i_spatz/i_vrf/we_i +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz -group VRF ${core_path}/i_cachepool_cc/i_spatz/i_vrf/wbe_i +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz -group VRF ${core_path}/i_cachepool_cc/i_spatz/i_vrf/wvalid_o +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz -group VRF -divider RegisterRead +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz -group VRF ${core_path}/i_cachepool_cc/i_spatz/i_vrf/raddr_i +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz -group VRF ${core_path}/i_cachepool_cc/i_spatz/i_vrf/rdata_o +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz -group VRF ${core_path}/i_cachepool_cc/i_spatz/i_vrf/re_i +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz -group VRF ${core_path}/i_cachepool_cc/i_spatz/i_vrf/rvalid_o +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz -group VRF -divider Internal +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz -group VRF ${core_path}/i_cachepool_cc/i_spatz/i_vrf/waddr +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz -group VRF ${core_path}/i_cachepool_cc/i_spatz/i_vrf/wdata +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz -group VRF ${core_path}/i_cachepool_cc/i_spatz/i_vrf/we +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz -group VRF ${core_path}/i_cachepool_cc/i_spatz/i_vrf/wbe +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz -group VRF ${core_path}/i_cachepool_cc/i_spatz/i_vrf/raddr +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz -group VRF ${core_path}/i_cachepool_cc/i_spatz/i_vrf/rdata + +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz -group VLSU ${core_path}/i_cachepool_cc/i_spatz/i_vlsu/* + +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz -group VSLDU ${core_path}/i_cachepool_cc/i_spatz/i_vsldu/* + +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz -group VFU ${core_path}/i_cachepool_cc/i_spatz/i_vfu/* + +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz -group FPU ${core_path}/i_cachepool_cc/i_spatz/i_vfu/gen_fpu/* + +add wave -noupdate {*}$parent_grp -group ${name} -group Internal ${core_path}/i_cachepool_cc/* + +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz -group VLSU -group ROB0 ${core_path}/i_cachepool_cc/i_spatz/i_vlsu/gen_rob[0]/i_reorder_buffer/* +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz -group VLSU -group ROB1 ${core_path}/i_cachepool_cc/i_spatz/i_vlsu/gen_rob[1]/i_reorder_buffer/* + +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz -group RSP_FIFO ${core_path}/i_cachepool_cc/spatz_mem_rsp +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz -group RSP_FIFO ${core_path}/i_cachepool_cc/spatz_mem_fifo +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz -group RSP_FIFO ${core_path}/i_cachepool_cc/spatz_mem_rsp_valid +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz -group RSP_FIFO ${core_path}/i_cachepool_cc/spatz_mem_rsp_ready +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz -group RSP_FIFO ${core_path}/i_cachepool_cc/spatz_mem_rsp_empty +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz -group RSP_FIFO ${core_path}/i_cachepool_cc/spatz_mem_rsp_pop +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz -group RSP_FIFO ${core_path}/i_cachepool_cc/spatz_mem_rsp_push +add wave -noupdate {*}$parent_grp -group ${name} -group Spatz -group RSP_FIFO ${core_path}/i_cachepool_cc/spatz_mem_fifo_bypass diff --git a/sim/scripts/vsim_group.tcl b/sim/scripts/vsim_group.tcl index 8edb7e5..4f48a90 100644 --- a/sim/scripts/vsim_group.tcl +++ b/sim/scripts/vsim_group.tcl @@ -2,18 +2,24 @@ # Solderpad Hardware License, Version 0.51, see LICENSE for details. # SPDX-License-Identifier: SHL-0.51 -# Create group for Tile $1 onerror {resume} -set group_path $1 +quietly set group_path $1 +quietly set parent_grp $3 # Add waves for remote xbar for {set p 0} {$p < $2} {incr p} { onerror {resume} - set xbar_path ${group_path}/gen_remote_tile_xbar[$p]/i_tile_remote_xbar + quietly set xbar_path ${group_path}/gen_remote_tile_xbar[$p]/i_tile_remote_xbar - add wave -noupdate -group Group -group remote_xbar[$p] ${xbar_path}/* + add wave -noupdate -group "${parent_grp}" -group remote_xbar[$p] ${xbar_path}/* } -add wave -noupdate -group Group -group Internal ${group_path}/* +add wave -noupdate -group "${parent_grp}" -group refill_xbar -group req_xbar ${group_path}/i_refill_xbar/i_req_xbar/* +add wave -noupdate -group "${parent_grp}" -group refill_xbar -group rsp_xbar ${group_path}/i_refill_xbar/i_rsp_xbar/* +add wave -noupdate -group "${parent_grp}" -group l2_icache -group axi_conv ${group_path}/i_l2icache_axi2reqrsp/* +add wave -noupdate -group "${parent_grp}" -group l2_icache -group bottom_lvl ${group_path}/i_l2icache_interco/gen_bottom_level/* +add wave -noupdate -group "${parent_grp}" -group l2_icache ${group_path}/i_l2icache_interco/* + +add wave -noupdate -group "${parent_grp}" -group group_internal ${group_path}/* diff --git a/sim/scripts/vsim_tile.tcl b/sim/scripts/vsim_tile.tcl index 8763440..40c21eb 100644 --- a/sim/scripts/vsim_tile.tcl +++ b/sim/scripts/vsim_tile.tcl @@ -5,38 +5,42 @@ # Create group for Tile $1 onerror {resume} -set tile_path $2 +quietly set tile_path $3 +quietly set parent_grp $4 + +# --- Configuration Variables --- +# NrTCDMPortsPerCore: 4 Spatz ports + 1 Snitch port +quietly set NUM_XBARS 5 +quietly set SNITCH_IDX [expr {$NUM_XBARS - 1}] -# Add waves for tcdm_mapper and csrs -# add wave -noupdate -group tile[$1] -group Barrier ${tile_path}/i_tile/i_snitch_barrier/* -# add wave -noupdate -group tile[$1] -group axi2reqrsp ${tile_path}/i_axi2reqrsp/* # Add waves for xbars -add wave -noupdate -group tile[$1] -group narrow_xbar ${tile_path}/i_tile/i_axi_narrow_xbar/* -add wave -noupdate -group tile[$1] -group wide_xbar ${tile_path}/i_tile/i_axi_wide_xbar/* +# add wave -noupdate -group ${parent_grp} -group tile[$1] -group wide_xbar ${tile_path}/i_tile/i_axi_wide_xbar/* + +add wave -noupdate -group ${parent_grp} -group tile[$1] -group Barrier ${tile_path}/i_tile/i_cachepool_tile_barrier/* -add wave -noupdate -group Barrier -group tile[$1] ${tile_path}/i_tile/i_cachepool_tile_barrier/* +add wave -noupdate -group ${parent_grp} -group tile[$1] -group L1ICache ${tile_path}/i_tile/i_snitch_icache/* # Add waves for cache controller for {set c 0} {$c < 4} {incr c} { onerror {resume} - set cache_path ${tile_path}/i_tile/gen_l1_cache_ctrl[$c]/i_l1_controller + quietly set cache_path ${tile_path}/i_tile/gen_l1_cache_ctrl[$c]/i_l1_controller - add wave -noupdate -group tile[$1] -group cache[$c] -group amo ${tile_path}/i_tile/gen_cache_connect[$c]/gen_cache_amo_connect[4]/gen_amo/i_cache_amo/* + add wave -noupdate -group ${parent_grp} -group tile[$1] -group cache[$c] -group amo ${tile_path}/i_tile/gen_cache_connect[$c]/gen_cache_amo_connect[${SNITCH_IDX}]/gen_amo/i_cache_amo/* - add wave -noupdate -group tile[$1] -group cache[$c] -group coalescer ${cache_path}/i_par_coalescer_for_spatz/gen_extend_window/i_par_coalescer_extend_window/i_par_coalescer/* - add wave -noupdate -group tile[$1] -group cache[$c] -group core ${cache_path}/i_insitu_cache_tcdm_wrapper/i_insitu_cache_core/* - add wave -noupdate -group tile[$1] -group cache[$c] -group meta_ctrl0 ${cache_path}/i_insitu_cache_tcdm_wrapper/gen_cache_banks[0]/i_access_ctrl_for_meta/* - add wave -noupdate -group tile[$1] -group cache[$c] -group meta_ctrl1 ${cache_path}/i_insitu_cache_tcdm_wrapper/gen_cache_banks[1]/i_access_ctrl_for_meta/* - add wave -noupdate -group tile[$1] -group cache[$c] -group meta_ctrl2 ${cache_path}/i_insitu_cache_tcdm_wrapper/gen_cache_banks[2]/i_access_ctrl_for_meta/* - add wave -noupdate -group tile[$1] -group cache[$c] -group meta_ctrl3 ${cache_path}/i_insitu_cache_tcdm_wrapper/gen_cache_banks[3]/i_access_ctrl_for_meta/* + add wave -noupdate -group ${parent_grp} -group tile[$1] -group cache[$c] -group coalescer ${cache_path}/i_par_coalescer_for_spatz/gen_extend_window/i_par_coalescer_extend_window/i_par_coalescer/* + add wave -noupdate -group ${parent_grp} -group tile[$1] -group cache[$c] -group core ${cache_path}/i_insitu_cache_tcdm_wrapper/i_insitu_cache_core/* + add wave -noupdate -group ${parent_grp} -group tile[$1] -group cache[$c] -group meta_ctrl0 ${cache_path}/i_insitu_cache_tcdm_wrapper/gen_cache_banks[0]/i_access_ctrl_for_meta/* + add wave -noupdate -group ${parent_grp} -group tile[$1] -group cache[$c] -group meta_ctrl1 ${cache_path}/i_insitu_cache_tcdm_wrapper/gen_cache_banks[1]/i_access_ctrl_for_meta/* + add wave -noupdate -group ${parent_grp} -group tile[$1] -group cache[$c] -group meta_ctrl2 ${cache_path}/i_insitu_cache_tcdm_wrapper/gen_cache_banks[2]/i_access_ctrl_for_meta/* + add wave -noupdate -group ${parent_grp} -group tile[$1] -group cache[$c] -group meta_ctrl3 ${cache_path}/i_insitu_cache_tcdm_wrapper/gen_cache_banks[3]/i_access_ctrl_for_meta/* - add wave -noupdate -group tile[$1] -group cache[$c] -group Internal ${cache_path}/* + add wave -noupdate -group ${parent_grp} -group tile[$1] -group cache[$c] -group Internal ${cache_path}/* } -for {set c 0} {$c < 5} {incr c} { - add wave -noupdate -group tile[$1] -group cache_xbar -group xbar[$c] ${tile_path}/i_tile/gen_cache_xbar[$c]/i_cache_xbar/* +for {set c 0} {$c < $NUM_XBARS} {incr c} { + add wave -noupdate -group ${parent_grp} -group tile[$1] -group cache_xbar -group xbar[$c] ${tile_path}/i_tile/gen_cache_xbar[$c]/gen_remote_group_slice/i_cache_xbar/* } # Add waves for remaining signals -add wave -noupdate -group tile[$1] -group Internal ${tile_path}/i_tile/* +add wave -noupdate -group ${parent_grp} -group tile[$1] -group Internal ${tile_path}/i_tile/* diff --git a/sim/scripts/vsim_wave.tcl b/sim/scripts/vsim_wave.tcl index d5fa528..8a1db67 100644 --- a/sim/scripts/vsim_wave.tcl +++ b/sim/scripts/vsim_wave.tcl @@ -1,12 +1,16 @@ -# Copyright 2021 ETH Zurich and University of Bologna. +# Copyright 2026 ETH Zurich and University of Bologna. # Solderpad Hardware License, Version 0.51, see LICENSE for details. # SPDX-License-Identifier: SHL-0.51 onerror {resume} quietly WaveActivateNextPane {} 0 -set cluster_path /tb_cachepool/i_cluster_wrapper/i_cluster -set group_path ${cluster_path}/gen_group/i_group +# --- Configuration Variables --- +quietly set cluster_path /tb_cachepool/i_cluster_wrapper/i_cluster +quietly set NUM_GROUPS 4 ;# Total number of groups +quietly set NUM_GROUPS_X 2 ;# X dimension of group mesh (NUM_GROUPS_Y = NUM_GROUPS / NUM_GROUPS_X) +quietly set NUM_TILES 4 ;# Tiles per group +quietly set NUM_CORES 4 ;# Cores per tile # Add the cluster probe add wave /tb_cachepool/cluster_probe @@ -14,23 +18,57 @@ add wave /tb_cachepool/cluster_probe # Cluster do sim/scripts/vsim_cluster.tcl ${cluster_path} -# Group -# add wave -noupdate -group Group ${group_path}/* -do sim/scripts/vsim_group.tcl ${group_path} 5 +# Iterate through all groups using 2D coordinates +for {set g 0} {$g < $NUM_GROUPS} {incr g} { + quietly set gy [expr {$g % $NUM_GROUPS_X}] + quietly set gx [expr {$g / $NUM_GROUPS_X}] + quietly set group_wp_path ${cluster_path}/gen_group_y[${gy}]/gen_group_x[${gx}]/i_group + quietly set group_path ${group_wp_path}/i_group + quietly set gwp_name "GroupWP_Y${gy}_X${gx}" -# Tile and Core -for {set tile 0} {$tile < 4} {incr tile} { - set tile_path ${group_path}/gen_tiles[$tile] + # 1. Plot GroupWP signals for this group + add wave -noupdate -group "${gwp_name}" -group L2Mesh -group Chimney ${group_wp_path}/i_l2_mgr_chimney/* + add wave -noupdate -group "${gwp_name}" -group L2Mesh -group ReqRouter ${group_wp_path}/i_l2_req_router/* + add wave -noupdate -group "${gwp_name}" -group L2Mesh -group RspRouter ${group_wp_path}/i_l2_rsp_router/* - do sim/scripts/vsim_tile.tcl $tile ${tile_path} - # Add all cores in Tile 0 - for {set core 0} {$core < 4} {incr core} { - set core_path ${tile_path}/i_tile/gen_core[$core] - do sim/scripts/vsim_core.tcl $tile $core ${core_path} + quietly set tile 0 + while {[llength [find instances -nodu ${group_wp_path}/gen_noc/gen_mst_t[${tile}]/i_noc_mst_xbar]] > 0} { + add wave -noupdate -group "${gwp_name}" -group L1NoC -group MstXbar -group tile[${tile}] ${group_wp_path}/gen_noc/gen_mst_t[${tile}]/i_noc_mst_xbar/* + + quietly set ch 0 + while {[llength [find instances -nodu ${group_wp_path}/gen_noc/gen_req_router_t[${tile}]/gen_req_router_n[${ch}]/i_req_router]] > 0} { + add wave -noupdate -group "${gwp_name}" -group L1NoC -group ReqRouter -group tile[${tile}] -group ch[${ch}] ${group_wp_path}/gen_noc/gen_req_router_t[${tile}]/gen_req_router_n[${ch}]/i_req_router/* + add wave -noupdate -group "${gwp_name}" -group L1NoC -group RspRouter -group tile[${tile}] -group ch[${ch}] ${group_wp_path}/gen_noc/gen_rsp_router_t[${tile}]/gen_rsp_router_n[${ch}]/i_rsp_router/* + incr ch + } + incr tile } - for {set ch 0} {$ch < 4} {incr ch} { - add wave -noupdate -group DramSys$ch /tb_cachepool/gen_dram[$ch]/i_axi_dram_sim/* + add wave -noupdate -group "${gwp_name}" -group L1NoC -group SlvXbar ${group_wp_path}/gen_noc/i_noc_slv_xbar/* + + add wave -noupdate -group "${gwp_name}" -group WP_Internal ${group_wp_path}/* + + + # 2. Plot Group-level signals nested inside GroupWP (always, all groups) + do sim/scripts/vsim_group.tcl ${group_path} 5 "${gwp_name}" + + # 3. Plot all tiles and cores for the diagonal groups: (0,0) always, + # and (1,1) if the mesh has at least 2 columns and 2 rows + if {($gx == 0 && $gy == 0) || ($gx == 0 && $gy == 1 && $NUM_GROUPS_X >= 2)} { + for {set tile 0} {$tile < $NUM_TILES} {incr tile} { + quietly set tile_path ${group_path}/gen_tiles[${tile}]/gen_tile + do sim/scripts/vsim_tile.tcl $tile $g ${tile_path} "${gwp_name}" + + # 4. Plot all cores grouped under their tile + for {set core 0} {$core < $NUM_CORES} {incr core} { + quietly set core_path ${tile_path}/i_tile/gen_core[${core}] + do sim/scripts/vsim_core.tcl $g $tile $core ${core_path} "${gwp_name}" "tile[${tile}]" + } + } } } +# Add DRAM waves once at the end +for {set ch 0} {$ch < 4} {incr ch} { + add wave -noupdate -group "DramSys_$ch" /tb_cachepool/gen_dram[$ch]/i_axi_dram_sim/* +} diff --git a/sim/scripts/vsim_wave_single_tile.tcl b/sim/scripts/vsim_wave_single_tile.tcl deleted file mode 100644 index 28e54e5..0000000 --- a/sim/scripts/vsim_wave_single_tile.tcl +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright 2021 ETH Zurich and University of Bologna. -# Solderpad Hardware License, Version 0.51, see LICENSE for details. -# SPDX-License-Identifier: SHL-0.51 - -onerror {resume} -quietly WaveActivateNextPane {} 0 - -set cluster_path /tb_cachepool/i_cluster_wrapper/i_cluster -set group_path ${cluster_path} -set tile_path ${group_path}/gen_tile - - -# Add the cluster probe -add wave /tb_cachepool/cluster_probe - -do sim/scripts/vsim_cluster.tcl ${cluster_path} - -do sim/scripts/vsim_tile.tcl 0 ${tile_path} -# Add all cores in Tile 0 -for {set core 0} {$core < 4} {incr core} { - set core_path ${tile_path}/i_tile/gen_core[$core] - do sim/scripts/vsim_core.tcl 0 $core ${core_path} -} - -for {set ch 0} {$ch < 4} {incr ch} { - add wave -noupdate -group DramSys$ch /tb_cachepool/gen_dram[$ch]/i_axi_dram_sim/* -} - diff --git a/sim/sim.mk b/sim/sim.mk index 56cee77..d27b4e2 100644 --- a/sim/sim.mk +++ b/sim/sim.mk @@ -82,7 +82,7 @@ ${SIM_DIR}/${DPI_LIB}/cachepool_dpi.so: ${dpi_target} # ----------------- ${WORK_DIR}/${FESVR_VERSION}_unzip: mkdir -p $(dir $@) - wget -O $(dir $@)/${FESVR_VERSION} https://github.com/riscv/riscv-isa-sim/tarball/${FESVR_VERSION} + curl -fL -o $(dir $@)/${FESVR_VERSION} https://github.com/riscv/riscv-isa-sim/tarball/${FESVR_VERSION} tar xfm $(dir $@)${FESVR_VERSION} --strip-components=1 -C $(dir $@) touch $@ @@ -100,17 +100,24 @@ ${WORK_DIR}/compile.vsim.tcl: ${SNLIB_DIR}/rtl_lib.cc ${SNLIB_DIR}/common_lib.cc echo 'return 0' >> $@ # Wrapper script & GUI script +# The generated scripts derive ROOT_DIR from their own location at runtime so +# that they remain portable across different checkout paths (CI runners, moved +# repos). All absolute paths baked in by make are replaced by a single sed pass. define QUESTASIM ${VSIM} -c -do "source $<; quit" | tee $(dir $<)vsim.log @! grep -P "Errors: [1-9]*," $(dir $<)vsim.log - @mkdir -p $(SIMBIN_DIR) $(SIMBIN_DIR)/logs - @echo "#!/bin/bash" > $(SIMBIN_DIR)/cachepool_cluster.vsim + @mkdir -p $(SIMBIN_DIR) $(SIMBIN_DIR)/logs $(SIMBIN_DIR)/logs/core $(SIMBIN_DIR)/logs/noc $(SIMBIN_DIR)/logs/others + @echo '#!/bin/bash' > $(SIMBIN_DIR)/cachepool_cluster.vsim + @echo 'ROOT_DIR="$$(cd "$$(dirname "$$(readlink -f "$$0")")/../.." && pwd)"' >> $(SIMBIN_DIR)/cachepool_cluster.vsim @echo 'echo `realpath $$1` > ${SIMBIN_DIR}/logs/.rtlbinary' >> $(SIMBIN_DIR)/cachepool_cluster.vsim @echo '${VSIM} +permissive ${VSIM_FLAGS} -do "run -a" -work ${WORK_DIR} -c -ldflags "-Wl,-rpath,${GCC_LIB} -L${FESVR}/lib -lfesvr_vsim -lutil" $1 +permissive-off ++$$1 +PRELOAD=$$1' >> $(SIMBIN_DIR)/cachepool_cluster.vsim + @sed -i 's|$(CACHEPOOL_DIR)|$${ROOT_DIR}|g' $(SIMBIN_DIR)/cachepool_cluster.vsim @chmod +x $(SIMBIN_DIR)/cachepool_cluster.vsim - @echo "#!/bin/bash" > $(SIMBIN_DIR)/cachepool_cluster.vsim.gui + @echo '#!/bin/bash' > $(SIMBIN_DIR)/cachepool_cluster.vsim.gui + @echo 'ROOT_DIR="$$(cd "$$(dirname "$$(readlink -f "$$0")")/../.." && pwd)"' >> $(SIMBIN_DIR)/cachepool_cluster.vsim.gui @echo 'echo `realpath $$1` > ${SIMBIN_DIR}/logs/.rtlbinary' >> $(SIMBIN_DIR)/cachepool_cluster.vsim.gui @echo '${VSIM} +permissive ${VSIM_FLAGS} -do "log -r /*; source ${WAVE_FILE}; run -a" -work ${WORK_DIR} -ldflags "-Wl,-rpath,${GCC_LIB} -L${FESVR}/lib -lfesvr_vsim -lutil" $1 +permissive-off ++$$1 +PRELOAD=$$1' >> $(SIMBIN_DIR)/cachepool_cluster.vsim.gui + @sed -i 's|$(CACHEPOOL_DIR)|$${ROOT_DIR}|g' $(SIMBIN_DIR)/cachepool_cluster.vsim.gui @chmod +x $(SIMBIN_DIR)/cachepool_cluster.vsim.gui endef @@ -124,4 +131,4 @@ vsim: dpi ${SIMBIN_DIR}/cachepool_cluster.vsim clean.vsim: rm -rf ${WORK_DIR}/compile.vsim.tcl ${SIMBIN_DIR}/cachepool_cluster.vsim ${SIMBIN_DIR}/cachepool_cluster.vsim.gui ${SIM_DIR}/work-vsim \ ${SIM_DIR}/work-dpi ${WORK_DIR} vsim.wlf vish_stacktrace.vstf transcript modelsim.ini logs *.tdb *.vstf bin - + rm -rf $(SIM_DIR)/bin/* diff --git a/software/scripts/timeline/data_gemm.csv b/software/scripts/timeline/data_gemm.csv deleted file mode 100644 index b4937d1..0000000 --- a/software/scripts/timeline/data_gemm.csv +++ /dev/null @@ -1,30 +0,0 @@ -pc,inst,type,start cycle,end cycle -80000784,"slli t2, t0, 2",scalar,0,1 -80000788,"add t2, a7, t2",scalar,1,2 -8000078c,"vle32.v v16, (t1)",vector,2,32 -80000790,"vfmacc.vf v0, ft3, v20",vector,3,14 -80000794,"flw ft3, 0(t2)",fp,4,18 -80000798,"vfmacc.vf v4, ft2, v20",vector,8,21 -8000079c,"flw ft2, 128(t2)",fp,9,19 -800007a0,"vfmacc.vf v8, ft1, v20",vector,10,30 -800007a4,"flw ft1, 256(t2)",fp,14,35 -800007a8,"vfmacc.vf v12, ft0, v20",vector,15,37 -800007ac,"flw ft0, 384(t2)",fp,18,49 -800007b0,"addi t1, t1, 256",scalar,19,20 -800007b4,"bgeu t0, s9, 0x80000700",scalar,20,21 -800007b8,"addi t2, t1, -128",scalar,21,22 -800007bc,"vle32.v v20, (t2)",vector,22,48 -800007c0,"slli t2, t0, 2",scalar,23,25 -800007c4,"ori t2, t2, 4",scalar,25,26 -800007c8,"add t2, a7, t2",scalar,26,27 -800007cc,"beqz t0, 0x80000760",scalar,27,28 -800007d0,"vfmacc.vf v0, ft3, v16",vector,28,45 -800007d4,"flw ft3, 0(t2)",fp,29,57 -800007d8,"vfmacc.vf v4, ft2, v16",vector,30,53 -800007dc,"flw ft2, 128(t2)",fp,32,67 -800007e0,"vfmacc.vf v8, ft1, v16",vector,45,61 -800007e4,"flw ft1, 256(t2)",fp,46,77 -800007e8,"vfmacc.vf v12, ft0, v16",vector,50,69 -800007ec,"flw ft0, 384(t2)",fp,51,85 -800007f0,"addi t0, t0, 2",scalar,62,63 -800007f4,"bne t0, s9, 0x80000784",scalar,63,64 \ No newline at end of file diff --git a/software/scripts/timeline/timeline.py b/software/scripts/timeline/timeline.py deleted file mode 100644 index aa966ea..0000000 --- a/software/scripts/timeline/timeline.py +++ /dev/null @@ -1,179 +0,0 @@ -import pandas as pd -import matplotlib.pyplot as plt -import re -import os -import matplotlib as mpl - -# Use a TrueType font and set larger font sizes globally -mpl.rcParams['font.family'] = 'DejaVu Sans' # or any installed TTF font you prefer -mpl.rcParams['font.size'] = 12 -mpl.rcParams['axes.titlesize'] = 14 -mpl.rcParams['axes.labelsize'] = 12 -mpl.rcParams['xtick.labelsize'] = 10 -mpl.rcParams['ytick.labelsize'] = 10 -mpl.rcParams['legend.fontsize'] = 12 - -# Load CSV -csv_path = 'data_gemm.csv' -df = pd.read_csv(csv_path) - -# Sort by PC to ensure correct program order -df = df.sort_values(by='pc').reset_index(drop=True) - -# Convert to list of tuples -raw_data = list(df[['inst', 'type', 'start cycle', 'end cycle']].itertuples(index=False, name=None)) - -# Parse registers function -def parse_regs(instr): - instr = instr.strip() - if instr.startswith(('add', 'addi', 'sub', 'slli', 'ori')): - parts = instr.split() - rd_rs = parts[1].split(',') - rd = rd_rs[0].strip() - rs = [x.strip() for x in rd_rs[1:]] - return rd, rs - elif instr.startswith('flw'): - parts = instr.split() - rd = parts[1].strip().strip(',') - rs1 = re.findall(r'\((.*?)\)', instr) - return rd, rs1 - elif instr.startswith('vle32.v'): - parts = instr.split() - vd = parts[1].strip().strip(',') - rs1 = re.findall(r'\((.*?)\)', instr) - return vd, rs1 - elif instr.startswith('vfmacc.vf'): - parts = instr.split() - vd = parts[1].strip().strip(',') - rs = [x.strip().strip(',') for x in parts[2:]] - return vd, rs - elif instr.startswith(('beqz', 'bgeu', 'bne')): - parts = instr.split() - rs = [parts[1].strip().strip(',')] - return None, rs - else: - return None, [] - -# Build all_tasks preserving program order -all_tasks = [] -for i, (instr, core, start, end) in enumerate(raw_data): - dest, srcs = parse_regs(instr) - instr_type = instr.split()[0] - all_tasks.append({ - 'instr': instr, - 'core': core, - 'start': start, - 'end': end, - 'dest': dest, - 'srcs': srcs, - 'instr_type': instr_type, - 'program_order': i # preserve original order - }) - -# === Allocate rows within each type while preserving program order === -def allocate_rows(tasks): - rows = [] - output = [] - for task in tasks: - instr, core, start, end = task['instr'], task['core'], task['start'], task['end'] - placed = False - for idx, row in enumerate(rows): - if all(not (start < e and end > s) for _, s, e in row): - row.append((instr, start, end)) - task['row'] = idx - placed = True - break - if not placed: - rows.append([(instr, start, end)]) - task['row'] = len(rows) - 1 - output.append(task) - return output - -# Split by type -scalar_tasks = [t for t in all_tasks if t['core'] == 'scalar'] -fp_tasks = [t for t in all_tasks if t['core'] == 'fp'] -vector_tasks = [t for t in all_tasks if t['core'] == 'vector'] - -# Allocate rows within each type -scalar_tasks = allocate_rows(scalar_tasks) -fp_tasks = allocate_rows(fp_tasks) -vector_tasks = allocate_rows(vector_tasks) - -# Merge back to all_tasks with updated row indices -all_tasks_updated = scalar_tasks + fp_tasks + vector_tasks -# Re-sort to restore original program order for dependency analysis -all_tasks_updated.sort(key=lambda x: x['program_order']) - -# === Dependency analysis === -reg_write_map = {} -dependencies = [] -for idx, task in enumerate(all_tasks_updated): - dest, srcs, program_order, instr_type = task['dest'], task['srcs'], task['program_order'], task['instr_type'] - for src in srcs: - if src in reg_write_map: - producer_task = reg_write_map[src] - # Only add selected type of instructions - if instr_type in ['vfmacc.vf']: - # Only add dependency if producer is earlier in program order - if producer_task['program_order'] < program_order: - dependencies.append({ - 'producer': producer_task, - 'consumer': task - }) - if dest: - reg_write_map[dest] = task - -# === Plotting === -fig, ax = plt.subplots(figsize=(30, 12)) - -# Colors for each type -colors = {'scalar': 'skyblue', 'fp': 'lightgreen', 'vector': 'salmon'} - -# Plot each task -for task in all_tasks_updated: - instr = task['instr'] - core = task['core'] - start = task['start'] - end = task['end'] - row = task['row'] - instr_type = task['instr_type'] - program_order = task['program_order'] - y = f"{core.capitalize()} Row {row}" - ax.barh(y, end - start, left=start, color=colors.get(core, 'gray'), edgecolor='black') - ax.text((start + end) / 2, y, instr, ha='center', va='center', fontsize=12, rotation=45) - # ax.text((start + end) / 2, y, f"{instr_type} ({program_order})", ha='center', va='center', fontsize=12, rotation=45) -# Plot dependencies -for dep in dependencies: - p = dep['producer'] - c = dep['consumer'] - p_x = p['end'] - p_y = f"{p['core'].capitalize()} Row {p['row']}" - c_x = c['start'] - c_y = f"{c['core'].capitalize()} Row {c['row']}" - ax.annotate("", xy=(c_x, c_y), xytext=(p_x, p_y), - arrowprops=dict(arrowstyle="->", color='gray', lw=1)) - -ax.set_xlabel('Cycle') -ax.set_title('Execution Timeline with Correct Program Order Dependencies') -ax.grid(axis='x') - -# Set y-ticks -scalar_yticks = [f"Scalar Row {i}" for i in range(max([t['row'] for t in scalar_tasks])+1)] -fp_yticks = [f"Fp Row {i}" for i in range(max([t['row'] for t in fp_tasks])+1)] -vector_yticks = [f"Vector Row {i}" for i in range(max([t['row'] for t in vector_tasks])+1)] -yticks = scalar_yticks + fp_yticks + vector_yticks - -ax.set_yticks(yticks) -ax.set_yticklabels(yticks) - -plt.tight_layout() - -# Create outputs directory if it doesn't exist -output_dir = 'outputs' -os.makedirs(output_dir, exist_ok=True) - -# Save PNG and PDF -plt.savefig(os.path.join(output_dir, 'execution_timeline.png'), dpi=300) -plt.savefig(os.path.join(output_dir, 'execution_timeline.pdf')) -# Show the plot -plt.show() diff --git a/software/snRuntime/CMakeLists.txt b/software/snRuntime/CMakeLists.txt index ff75a29..5c6b3cb 100644 --- a/software/snRuntime/CMakeLists.txt +++ b/software/snRuntime/CMakeLists.txt @@ -64,7 +64,6 @@ set(sources src/team.c src/alloc.c src/interrupt.c - src/perf_cnt.c src/l1cache.c ) @@ -74,24 +73,6 @@ set(standalone_snitch_sources src/platforms/standalone/putchar.c ) -# Sources only compatible with the LLVM toolchain -if (CMAKE_C_COMPILER_ID STREQUAL "Clang") - set(sources - ${sources} - # OpenMP support - src/omp/omp.c - src/omp/kmp.c - src/omp/eu.c - src/dm.c - ) - # Check if static OpenMP runtime is requested - if(OMPSTATIC_NUMTHREADS GREATER 0) - message(STATUS "Using ${OMPSTATIC_NUMTHREADS} threads for optimized OpenMP runtime") - add_compile_definitions(OMPSTATIC_NUMTHREADS=${OMPSTATIC_NUMTHREADS}) - else() - message(STATUS "Generic OpenMP runtime") - endif() -endif() if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) # Build all runtimes @@ -125,13 +106,11 @@ add_snitch_test(tls tests/tls.c) add_snitch_test(simple tests/simple.c) add_snitch_test(varargs_1 tests/varargs_1.c) add_snitch_test(varargs_2 tests/varargs_2.c) -add_snitch_test(barrier tests/barrier.c) -add_snitch_test(fence_i tests/fence_i.c) -add_snitch_test(interrupt-local tests/interrupt-local.c) add_snitch_test(printf_simple tests/printf_simple.c) # RTL only tests if(SNITCH_RUNTIME STREQUAL "snRuntime-cluster") - add_snitch_test(dma_simple tests/dma_simple.c) add_snitch_test(atomics tests/atomics.c) + add_snitch_test(dram_alloc tests/dram_alloc.c) + add_snitch_test(fence tests/fence.c) endif() diff --git a/software/snRuntime/README.md b/software/snRuntime/README.md index d19e74e..b804918 100644 --- a/software/snRuntime/README.md +++ b/software/snRuntime/README.md @@ -1,48 +1,198 @@ -# Snitch Runtime Library +# snRuntime — CachePool Software Runtime + +This library is the bare-metal software runtime for the CachePool manycore system. It is derived from the upstream Snitch runtime and extended with CachePool-specific cache management and peripheral APIs. + +## Folder Structure + +``` +snRuntime/ +├── include/ # Public headers — include these in application code +│ ├── snrt.h # Master header: topology, barriers, DMA, allocation +│ ├── l1cache.h # CachePool L1 data cache management API +│ ├── cachepool_peripheral.h # Register offsets for the cluster peripheral +│ ├── perf_cnt.h # Performance counter API +│ ├── team.h # Team/cluster descriptor structs +│ ├── interface.h # Hardware interface definitions +│ ├── debug.h # Debug printf helpers +│ ├── dm.h # Data-mover (DMA) low-level interface +│ ├── eu.h # Execution unit (work dispatch) interface +│ ├── kmp.h # OpenMP KMP interface +│ └── omp.h # OpenMP runtime interface +├── src/ # Runtime implementation +│ ├── start.S # Entry point (hart 0 boots, others wait for IPI) +│ ├── team.c # Team/topology initialisation +│ ├── barrier.c # Hardware and software barrier implementations +│ ├── l1cache.c # CachePool L1 cache management (flush, partition, xbar) +│ ├── alloc.c # L1 TCDM bump allocator + DRAM linked-list allocator +│ ├── memcpy.c # Optimised memcpy +│ ├── perf_cnt.c # Performance counter helpers +│ ├── printf.c # Lightweight printf (wraps vendor/printf.c) +│ ├── dm.c / dma.c # DMA engine helpers +│ ├── interrupt.c # Interrupt initialisation +│ └── platforms/ # Platform-specific startup and putchar +├── tests/ # Self-contained runtime unit tests +├── vendor/ # Third-party sources (printf, riscv-opcodes) +└── link/ # Linker script template (common.ld.in) +``` + +## Key API + +### Topology (`snrt.h`) + +```c +uint32_t snrt_cluster_core_idx(); // Core index within the cluster (0-based) +uint32_t snrt_cluster_core_num(); // Total cores in the cluster +uint32_t snrt_cluster_tile_idx(); // Tile index within the cluster +uint32_t snrt_cluster_tile_num(); // Number of tiles in the cluster +int snrt_is_compute_core(); // Non-zero if this is a compute (non-DMA) core +``` + +### Synchronisation (`snrt.h`) + +```c +void snrt_cluster_hw_barrier(); // Hardware barrier: stalls until all cluster cores arrive +void snrt_cluster_sw_barrier(); // Software barrier (polling) +void snrt_global_barrier(); // Cluster-to-cluster barrier +``` + +### L1 Data Cache — CachePool-specific (`l1cache.h`) + +All **cluster-wide** functions must be called by **every core** in the cluster. They +internally issue a `fence`, a hardware barrier, execute the operation on core 0 only, +and then issue a final barrier before returning. The low-level single-core variants +(without the `_cluster_` prefix) are for use inside the runtime or in single-core +contexts only. + +#### Cluster-wide flush (recommended for application code) + +```c +void l1d_cluster_flush(); // Flush all banks in all tiles +void l1d_cluster_shared_flush(); // Flush shared banks only +void l1d_cluster_private_flush(uint32_t tile); // Flush private banks of selected tiles (one-hot mask) +``` + +#### Cache configuration (cluster-wide) + +```c +// Set the crossbar interleaving offset (in bits). +// Granularity is clamped to >= log2(cacheline_bytes). +// Example: l1d_xbar_config(6) for 512-bit cachelines (6 = log2(64)). +void l1d_xbar_config(uint32_t offset); + +// Set the number of private banks per tile (0=all-shared … 4=all-private). +void l1d_part(uint32_t size); +``` + +#### Address boundary and polling + +```c +// Set the private/shared address boundary (default 0xA000_0000). +// Addresses >= boundary are private; addresses < boundary are shared. +// Requires a flush before changing while valid data is cached. +void l1d_addr(uint32_t addr); + +// Poll the peripheral until the current flush instruction completes. +// Used by the low-level flush functions; not normally needed in application code. +void l1d_wait(); +``` + +#### Cache initialisation (called once at boot, single-core) + +```c +// Invalidate all cache banks (insn = 2'b11). Called from start_snitch.S. +void l1d_init(uint32_t size); +``` + +### Performance Counters (`perf_cnt.h`) *TODO: REMOVE* + +```c +void snrt_start_perf_counter(enum snrt_perf_cnt, enum snrt_perf_cnt_type, uint32_t hart_id); +void snrt_stop_perf_counter(enum snrt_perf_cnt); +void snrt_reset_perf_counter(enum snrt_perf_cnt); +uint32_t snrt_get_perf_counter(enum snrt_perf_cnt); +``` + +Counter types include cycles, TCDM accesses, TCDM congestion, FPU issues, retired +instructions, DMA bandwidth events, and ICache statistics. + +### Memory Allocation (`snrt.h`) + +Two allocators are provided for different memory regions. + +**L1 TCDM — bump allocator** (no free support): + +```c +void *snrt_l1alloc(size_t size); // Bump-allocate from cluster TCDM scratchpad +void snrt_l1alloc_reset(); // Reclaim all L1 allocations at once +``` + +**DRAM — linked-list allocator** (single-core, supports free + coalescing): + +```c +void *snrt_malloc(size_t size); // Allocate from DRAM; payload rounded up to 64 B +void snrt_free(void *ptr); // Free and coalesce with following free blocks +``` -This library implements a minimal runtime for Snitch systems, which is responsible for the following: +Both the block header and the payload are cacheline-aligned (64 bytes). A request for +any size — even 1 byte — allocates a minimum of 64 bytes of payload. The allocator +must be called by a **single core only**; it is not thread-safe by design since +allocation is expected to happen in single-core initialisation phases. -- Detecting the hardware configuration (cores, clusters, ISA extensions, TCDM) -- Passing a descriptor struct to the executable -- Synchronization across cores and clusters -- Team-based multithreading and work splitting +The heap begins at `_edram + l3off` (set in `snrt_alloc_init`) and grows upward. +Block headers (64 bytes each) are stored in DRAM immediately before their payloads +and are accessed through the L1 cache like any other data. -## General Runtime +### DMA (`snrt.h`) *TODO: REMOVE* -The general runtime (`libsnRuntime`) relies on a bootloader or operating system to load the executable. This usually requires virtual memory to map the segments to the correct addresses. The general runtime does not provide any startup code in this scenario, but is more like a regular library providing some useful API. +```c +snrt_dma_txid_t snrt_dma_start_1d(void *dst, const void *src, size_t size); +snrt_dma_txid_t snrt_dma_start_2d(void *dst, const void *src, size_t size, + size_t dst_stride, size_t src_stride, size_t repeat); +void snrt_dma_wait(snrt_dma_txid_t tid); +void snrt_dma_wait_all(); +``` -## Bare Runtime +## Typical Initialisation Pattern -The bare runtimes (`libsnRuntime-`) assumes that the executable it is being linked into will run in a bare-metal fashion with no convenient bootloader or virtual memory setup. For this scenario, the runtime provides the `_start` symbol and implements a basic crt0. +```c +#include +#include -## Usage +int main() { + const uint32_t cid = snrt_cluster_core_idx(); -The runtime library can be compiled as follows: + // Configure cache xbar and partition — must be called by ALL cores. + l1d_xbar_config(6); // interleave at cacheline granularity + l1d_part(0); // all-shared - mkdir build - cd build - cmake .. - make + // Single-core init: allocate buffers, set up data structures. + if (cid == 0) { + float *buf = (float *)snrt_malloc(N * sizeof(float)); + // ... populate buf, other setup ... + } + snrt_cluster_hw_barrier(); -The tests can be executed as follows: + // ... parallel computation ... - make test + // Flush before reading results back — must be called by ALL cores. + l1d_cluster_flush(); -Interesting CMake options that can be set via `-D