Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
95a694a
[SRC] Change cluster base id to a paramemter instead of a logic input…
DiyouS Aug 17, 2026
e5aa7e5
[Util] Add missing yaml package into venv, and fix a hardcoded path i…
DiyouS Aug 17, 2026
357a6db
[SRC] Rename paraemter NumCore to NumCC
DiyouS Aug 18, 2026
92a0e48
[SRC] WIP: add dual-snitch basic hardware (not yet enabled)
DiyouS Aug 18, 2026
c6b7f5d
[SRC] Add the dual-scalar core hardware
DiyouS Aug 19, 2026
7986874
[CFG] Add dual scalar core configuration for testing
DiyouS Aug 19, 2026
32c2949
[SW] Add needed runtime functions to support dual-scalar core configu…
DiyouS Aug 19, 2026
cd3a5e6
[SW] Add missing makefile for configuration
DiyouS Aug 19, 2026
e09e462
[Makefile] Update venv environment to force python3.12. Update makefi…
DiyouS Aug 19, 2026
5728dd2
[Makefile] Update makefile targets
DiyouS Aug 20, 2026
7ae10e7
[SW] Add new test for dual scalar core. Fix bug accordingly
DiyouS Aug 20, 2026
e2f40db
[SW] Reconstruct software kernel folder to place them in better locat…
DiyouS Aug 24, 2026
d755220
[SRC] Update FSM implementation to have a cleaner result informing an…
DiyouS Aug 25, 2026
ce40785
[SRC] Fix a bug in Spatz related to dual-scalar core muxing. Add synt…
DiyouS Aug 25, 2026
5b24159
[SRC] Fix a bug in loading instruction handling for dual-scalar core
DiyouS Aug 26, 2026
2c8b4f5
[SW] Adapt the kernels for dual scalar core configuration.
DiyouS Aug 27, 2026
45b99f9
[CI] Add dual-scalar core configuration into CI and limit the thread …
DiyouS Aug 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 59 additions & 2 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ variables:
python3: 'python3'
# Config to build and test
CI_CONFIG: 'cachepool_fpu_4g'
SW_PREFIX: 'test-cachepool-'
CI_CONFIG_DUAL: 'cachepool_dual_fpu_4g'
SW_PREFIX: 'test-'

default:
tags: [shared]
Expand Down Expand Up @@ -60,6 +61,33 @@ build:
- hardware/deps/dram_rtl_sim/dramsys_lib/DRAMSys/build/lib/
- hardware/deps/dram_rtl_sim/dramsys_lib/DRAMSys/configs/

# ---------------------------------------------------------------------------
# Build stage: same as `build`, for the dual-scalar-per-CC config. Only the
# kernels adapted for dual-scalar (see `test-dual`) are exercised against it.
# ---------------------------------------------------------------------------
build-dual:
stage: build
script:
- echo "Using CC=$CC"
- echo "Using CXX=$CXX"
- test -x "$CC"
- test -x "$CXX"
- source iis-env.sh
- make init
- make dram-build
- make clean generate update-floonoc bootrom vsim sw config=$CI_CONFIG_DUAL DEBUG=0 noc_profiling=0
artifacts:
when: always
expire_in: 2h
paths:
- sim/work/
- sim/bin/cachepool_cluster.vsim
- sim/bin/cachepool_cluster.vsim.gui
- sim/work-dpi/
- software/build/CachePoolTests/
- 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
Expand Down Expand Up @@ -90,9 +118,38 @@ test:
- python3 util/auto-benchmark/check-ci.py test_${KERNEL}.log
artifacts:
when: always
expire_in: 1 week
expire_in: 3 days
paths:
# Full simulation log
- test_*.log
# Performance-monitor trace files written by the simulator
- sim/bin/logs/

# ---------------------------------------------------------------------------
# Test stage: dual-scalar-per-CC config. Limited to the kernels adapted and
# confirmed working under this config (host0-only Spatz access via
# snrt_cluster_vpu_*/snrt_cluster_is_primary); other tests are not yet
# verified for dual-scalar and are left to the default-config matrix above.
# ---------------------------------------------------------------------------
test-dual:
stage: test
needs: [build-dual]
parallel:
matrix:
- KERNEL:
- fdotp-32b_M32768
- gemv_M512_N128_K32
- fmatmul-32b_M64_N64_K64
- fft-32b_M1024_N16
script:
- 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_dual_${KERNEL}.log
- python3 util/auto-benchmark/check-ci.py test_dual_${KERNEL}.log
artifacts:
when: always
expire_in: 3 days
paths:
- test_dual_*.log
- sim/bin/logs/
2 changes: 1 addition & 1 deletion Bender.lock
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ packages:
dependencies:
- tech_cells_generic
spatz:
revision: f6ea19fb26c8bf992ae51a3e4acd3f2a36c6659f
revision: f4741b0a5bcc49103bd8a3db8d51fdf76d240df7
version: null
source:
Git: https://github.com/pulp-platform/spatz.git
Expand Down
3 changes: 3 additions & 0 deletions Bender.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ sources:
- hardware/generated/floo_cachepool_noc_pkg.sv
- hardware/src/cachepool_pkg.sv
- hardware/src/cachepool_cc.sv
- hardware/src/cachepool_spatz_lock.sv
- hardware/src/acc_mux.sv
- hardware/src/cachepool_cc_dual.sv
# Barrier
- hardware/src/cachepool_tile_barrier.sv
- hardware/src/cachepool_cluster_barrier.sv
Expand Down
70 changes: 47 additions & 23 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -190,12 +190,12 @@ $(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
$(PYTHON) -m 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
floogen pkg -c $(FLOO_CFG) -o $(FLOO_GEN_OUTDIR) --no-format

clean-floonoc:
rm -f $(FLOO_NOC)
Expand All @@ -213,7 +213,7 @@ dram-build: $(DRAMSYS_PATH)/README.md dram-config
if [ ! -d "build" ]; then \
mkdir build && cd build; \
$(CMAKE) -DCMAKE_CXX_FLAGS=-fPIC -DCMAKE_C_FLAGS=-fPIC -D DRAMSYS_WITH_DRAMPOWER=ON .. ; \
make -j; \
make -j4; \
fi

$(DRAMSYS_PATH)/README.md: dram-init
Expand Down Expand Up @@ -242,7 +242,7 @@ dram-clean:
fi

dram-init:
make -C ${DRAMSYS_DIR} -j8 dramsys CXX=$(CXX) CC=$(CC)
make -C ${DRAMSYS_DIR} -j4 dramsys CXX=$(CXX) CC=$(CC)

############
# Modelsim #
Expand Down Expand Up @@ -298,6 +298,10 @@ 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 += -DNUM_SCALAR_PER_CC=$(num_scalar_per_core)
ifeq ($(num_scalar_per_core),2)
VLOG_DEFS += -DCACHEPOOL_DUAL_CC
endif
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)
Expand Down Expand Up @@ -351,24 +355,28 @@ include sim/sim.mk

TESTS_DIR := $(SOFTWARE_DIR)/tests

# Test-group roots (relative to SOFTWARE_DIR) scanned for data-generated tests.
TEST_ROOTS := cache sync kernels/fp kernels/int rlc tests

# Auto-discover every test that has a script/gen_data.py.
# Convention: script/data_<params>.json → data/data_<params>.h
# Adding a new test or a new data variant needs no Makefile changes:
# - new test: drop gen_data.py + data_<params>.json into its script/ dir
# - new variant: add data_<params>.json to an existing test's script/ dir
DATA_TESTS := $(patsubst $(TESTS_DIR)/%/script/gen_data.py,%, \
$(wildcard $(TESTS_DIR)/*/script/gen_data.py))
DATA_TESTS := $(patsubst $(SOFTWARE_DIR)/%/script/gen_data.py,%, \
$(foreach root,$(TEST_ROOTS), \
$(wildcard $(SOFTWARE_DIR)/$(root)/*/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))
$(1)_DATA := $$(patsubst $(SOFTWARE_DIR)/$(1)/script/data_%.json, \
$(SOFTWARE_DIR)/$(1)/data/data_%.h, \
$$(wildcard $(SOFTWARE_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 $$<
$(SOFTWARE_DIR)/$(1)/data/data_%.h: \
$(SOFTWARE_DIR)/$(1)/script/data_%.json \
$(SOFTWARE_DIR)/$(1)/script/gen_data.py
$$(PYTHON) $(SOFTWARE_DIR)/$(1)/script/gen_data.py -c $$<
endef

$(foreach test,$(DATA_TESTS),$(eval $(call gen_data_rules,$(test))))
Expand All @@ -384,23 +392,32 @@ $(BANDWIDTH_DATA): $(TESTS_DIR)/bandwidth/script/data.json \
.PHONY: gen-data
gen-data: $(ALL_GEN_DATA)

# Clean targets, used to delete generated files
.PHONY: clean.data
clean.data:
rm -f $(ALL_GEN_DATA)

.PHONY: clean.sw
clean.sw:
clean.sw: clean.data
rm -rf ${SOFTWARE_DIR}/build

.PHONY: clean
clean: clean.sw clean.vsim clean.data
.PHONY: clean.generate
clean.generate:
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 \
$(CACHEPOOL_DIR)/wlf* \
$(CACHEPOOL_DIR)/noc_profiling/* \
$(SNRT_BOOTINFO_H)

.PHONY: clean.hw
clean.hw: clean.vsim clean.generate

.PHONY: clean
clean: clean.hw clean.sw

# Common CMake flags shared by sw and vsim targets.
# vsim appends -DSNITCH_SIMULATOR to point tests at the compiled binary.
SW_CMAKE_FLAGS = \
Expand All @@ -411,7 +428,8 @@ SW_CMAKE_FLAGS = \
-DLLVM_PATH=${LLVM_INSTALL_DIR} \
-DGCC_PATH=${GCC_INSTALL_DIR} \
-DPYTHON=${PYTHON} \
-DBUILD_TESTS=ON
-DBUILD_TESTS=ON \
-DNUM_SCALAR_PER_CORE=$(num_scalar_per_core)

.PHONY: sw
sw: generate bootrom gen-data
Expand All @@ -428,8 +446,11 @@ vsim: generate bootrom dpi ${SIMBIN_DIR}/cachepool_cluster.vsim
############

# Just a shortcut to build everything
.PHONY: hw
hw: generate bootrom vsim

.PHONY: all
all: generate bootrom vsim sw
all: hw sw

########
# Lint #
Expand Down Expand Up @@ -463,9 +484,7 @@ avg-log:
# 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.
# hardware/tb/cachepool_noc_profiling.sv's per-cycle NoC logs.
NPM ?= npm
VIS4MESH_DIR ?= ${CACHEPOOL_DIR}/util/vis4mesh
VIS4MESH_REPO ?= https://github.com/DiyouS/vis4mesh.git
Expand Down Expand Up @@ -531,13 +550,18 @@ help:
@echo "SW Build:"
@echo ""
@echo "*sw*: build software (generate + bootrom + cmake); overwrites previous build"
@echo "*clean.sw*: remove the software build directory"
@echo "*clean.sw*: remove the software build directory (also runs clean.data)"
@echo ""
@echo "Simulation:"
@echo ""
@echo "*vsim*: build hardware for QuestaSim simulation (use 'sw' to build software separately)"
@echo "*hw*: shortcut for generate + bootrom + vsim"
@echo "*all*: shortcut for hw + sw"
@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 "*clean.data*: remove generated data files (gen-data outputs)"
@echo "*clean.generate*: remove generated HJSON/bootrom/wlf/noc_profiling files [from 'generate'/'bootrom']"
@echo "*clean.hw*: clean.vsim + clean.generate"
@echo "*clean*: clean.hw + clean.sw (remove SW build, vsim build, and all generated HW files)"
@echo ""
@echo "Lint:"
@echo ""
Expand Down
22 changes: 19 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ 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`.

`make hw` is a shortcut for `generate bootrom vsim`; `make all` is a shortcut for `hw sw` (build everything).

#### Run the Simulation

The wrapper script launches the simulation (GUI or CLI) and expects a software ELF path as argument:
Expand Down Expand Up @@ -142,7 +144,7 @@ A lightweight benchmarking automation flow is provided under `util/auto-benchmar

CONFIGS="cachepool_fpu_4g cachepool_fpu_16g"
KERNELS="fdotp-32b_M32768 ffft-64b_M16384 fmatmul-64b_M2048"
PREFIX="test-cachepool-"
PREFIX="test-"
ROOT_PATH=../..

2. Run all builds and simulations:
Expand Down Expand Up @@ -204,9 +206,13 @@ Configuration names encode the number of groups and whether the FPU is enabled:
| `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 |
| `cachepool_dual_4g` | 4 | 2×2 | No (IPU only) | 4 | 4 | 64 CCs / 128 harts |
| `cachepool_dual_fpu_4g` | 4 | 2×2 | Yes | 4 | 4 | 64 CCs / 128 harts |

`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.

`cachepool_dual_4g`/`cachepool_dual_fpu_4g` set `num_scalar_per_core=2`: each Core Complex holds 2 Snitch scalar harts sharing 1 Spatz unit via a hardware ownership lock (`cachepool_spatz_lock.sv`), so "Cores" (Core Complex slots) and hart count diverge — 64 CCs, 128 harts total. `cachepool_dual_fpu_4g` is otherwise identical to `cachepool_fpu_4g` (same FPU/IPU counts), just with the shared-Spatz CC flavor — used to confirm the dual-scalar lock/mux design also works with the FPU enabled, not just IPU-only. The lock never blocks a core's pipeline: every acquire/release attempt (`software/snRuntime/include/spatz_lock.h`) completes immediately with an outcome (granted / denied / granted-but-still-draining), so a hart contending for a lock it doesn't get can always retry or do something else instead of hanging. See `note.md` for the full state-machine design.

The Spatz cluster consumes **`config/cachepool.hjson`**, which is **generated** from:
- `config/cachepool.hjson.tmpl` (skeleton with comments)
- `config/config.mk` (source of truth)
Expand Down Expand Up @@ -341,7 +347,7 @@ For a spatial, time-scrubbable view of NoC traffic (as opposed to `cachepool_mon
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=<multi-group config> noc_profiling=1
./sim/bin/cachepool_cluster.vsim software/build/CachePoolTests/test-cachepool-<test>
./sim/bin/cachepool_cluster.vsim software/build/CachePoolTests/test-<test>
```
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.

Expand Down Expand Up @@ -370,6 +376,16 @@ For a spatial, time-scrubbable view of NoC traffic (as opposed to `cachepool_mon

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.

### Dual-scalar flavor (`cachepool_cc_dual.sv`)

Set `num_scalar_per_core=2` (e.g. `cachepool_dual_4g`, see [Configurations](#configurations)) to build a Core Complex with **2 Snitch scalar harts sharing 1 Spatz unit**. Motivating workload: tasks where not every hart needs the vector unit at the same time, and control hands Spatz off between the pair at coarse task boundaries. The two harts get sequential hart/core IDs (e.g. cid 0/1 within a pair); `snrt_cluster_is_primary()` (`snrt.h`) tells a hart whether it's the pair's default owner (even `cid`) or its partner (odd `cid`).

Ownership is arbitrated by `cachepool_spatz_lock.sv`, a small hardware FSM (`Free`/`Locked`/`AcqWait`/`RelWait`) intercepting two dedicated peripheral addresses. It never blocks a core's pipeline: every acquire/release attempt is a plain **load** that always completes immediately, returning an outcome (`FAIL`/`SUCCESS`/`SUCCESS-WAIT`) instead of stalling the hart — so a hart that doesn't get the lock can retry, back off, or do other work instead of hanging. See `software/snRuntime/README.md` for the software API and `note.md` for the full state-machine design.

**Free vs. Locked performance**: while unlocked (`Free` state), `acc_mux.sv` shares Spatz between both harts via real round-robin arbitration, but every loadstore op must fully drain (real memory completion, not just issue-accept) before the next grant is offered — this holds even for a single hart with no contention from its partner, since the mux has no way to know a sequence of ops belongs to an uncontended hart until it tries to arbitrate again. Loadstore-heavy code therefore runs serialized on real memory latency in `Free` mode. Acquiring the lock (`Locked` state) removes this: the owner's issue path bypasses round-robin arbitration entirely and pipelines back-to-back LSU ops at full throughput. A kernel that does sustained vector/FP loadstore work should hold the lock around that section even if its partner hart never contends for Spatz at all.

Two per-role partial-barrier helpers, `snrt_cluster_host0_barrier()`/`snrt_cluster_host1_barrier()` (`snrt.h`), let a kernel synchronize only the pair's default owners (or only their partners) without hand-writing a participant mask; both degrade to an ordinary full barrier on a single-scalar-per-CC build, so kernels using them don't need a config-specific `#if`.

## Stack

Each core complex has a local **stack SPM**. Its depth is configured via parameters in `config/config.mk` (forwarded to RTL). If the stack exceeds the local SPM, it spills into the cache space (indexed with core ID bits).
Expand Down Expand Up @@ -408,7 +424,7 @@ make lint config=cachepool_fpu_4g
- 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.
- `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.
- Use `make clean` when switching configs to prevent stale build artifacts; `clean.sw`/`clean.hw` (or their finer-grained parts `clean.data`/`clean.generate`/`clean.vsim`) clean only one side if you don't need a full rebuild.
- 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_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.
Expand Down
Loading
Loading