From 77ad65e1429aced397bfde8ac468466726d4c3c7 Mon Sep 17 00:00:00 2001 From: Gamze Islamoglu Date: Tue, 18 Aug 2026 15:42:59 +0200 Subject: [PATCH 01/21] Do not fetch QEMU's ROM submodules The QEMU checkout ran `git submodule update --init --recursive`, which descends into roms/edk2 and from there into edk2's own submodules. One of those pins cmocka to https://git.cryptomilk.org/projects/cmocka.git, which no longer serves a git repository, so the clone fails and the container image cannot be built on any architecture. None of the ROM submodules are needed. QEMU's configure initialises exactly what the build requires - ui/keycodemapdb, meson, dtc, capstone, slirp and the berkeley float libraries - through scripts/git-submodule.sh, and roms/edk2 is never among them. --- Makefile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Makefile b/Makefile index f007f105c1..0ca5c70263 100644 --- a/Makefile +++ b/Makefile @@ -530,9 +530,7 @@ xtensor: ${XTENSOR_INSTALL_DIR} ${XSIMD_INSTALL_DIR} ${TOOLCHAIN_DIR}/qemu: cd ${TOOLCHAIN_DIR} && \ - git clone https://github.com/qemu/qemu.git --depth 1 -b stable-6.1 && \ - cd ${TOOLCHAIN_DIR}/qemu && \ - git submodule update --init --recursive + git clone https://github.com/qemu/qemu.git --depth 1 -b stable-6.1 ${QEMU_INSTALL_DIR}: ${TOOLCHAIN_DIR}/qemu cd ${TOOLCHAIN_DIR}/qemu/ && \ From ef897b7b29deed84fe57abe77aa9b7431ef1aab0 Mon Sep 17 00:00:00 2001 From: Gamze Islamoglu Date: Wed, 12 Aug 2026 15:55:32 +0200 Subject: [PATCH 02/21] Snitch: bump snitch_cluster and drop the obsolete local patch Move the pinned snitch_cluster from e02cc9e (April 2024) to 5b2fccd (September 2025). 5b2fccd is deliberately not the latest upstream commit. Two later changes would each pull in a dependency bump that is out of scope here: 7c2bdd9 replaces the numeric hardware-barrier CSR with a symbolic name (csrr x0, barrier), which the LLVM version Deeploy pins does not know. Adopting it would require bumping LLVM_COMMIT_HASH. de5251b raises the Python floor to 3.12, while Deeploy targets 3.10. 5b2fccd is the newest commit before those, and is the commit the downstream Torik cluster branched from, so it is well exercised. The build invocation changes accordingly: Runtime targets are namespaced. sw/runtime/{banshee,rtl} and sw/math no longer exist; sn-runtime builds libsnRuntime.a into sw/runtime/build. The build is driven from the repository root rather than target/snitch_cluster, which no longer exists. Make variables are namespaced: LLVM_BINROOT is no longer read, its replacement is SN_LLVM_BINROOT. The upstream toolchain expects a riscv32-unknown-elf-prefixed driver, whose default target is RISC-V. Deeploy's clang defaults to the host target, which makes the RISC-V flags unused and trips -Werror during dependency generation, so SN_RISCV_CC/SN_RISCV_CXX pass the target triple and the picolibc include path explicitly. Python dependencies moved from python-requirements.txt into pyproject.toml, so the requirements file is gone and the package is installed directly. toolchain/snitch_cluster.patch is deleted because every hunk has been upstreamed in the meantime: base.ld now wraps .cbss in KEEP() snrt_l3_alloc aligns its size with snrt_align_up(size, MIN_CHUNK_SIZE) team.c exports snrt_global_compute_core_{idx,num} putchar.c uses a per-core buffer placed in .dram the toolchain flags the patch added are no longer applicable --- Makefile | 16 ++--- toolchain/snitch_cluster.patch | 104 --------------------------------- 2 files changed, 9 insertions(+), 111 deletions(-) delete mode 100644 toolchain/snitch_cluster.patch diff --git a/Makefile b/Makefile index 0ca5c70263..4153daf9fd 100644 --- a/Makefile +++ b/Makefile @@ -47,7 +47,7 @@ LLVM_COMMIT_HASH ?= 1ccb97ef1789b8c574e3fcab0de674e11b189b96 PICOLIBC_COMMIT_HASH ?= 31ff1b3601b379e4cab63837f253f59729ce1fef PULP_SDK_COMMIT_HASH ?= 7f4f22516157a1b7c55bcbbc72ca81326180b3b4 MEMPOOL_COMMIT_HASH ?= affd45d94e05e375a6966af6a762deeb182a7bd6 -SNITCH_COMMIT_HASH ?= e02cc9e3f24b92d4607455d5345caba3eb6273b2 +SNITCH_COMMIT_HASH ?= 5b2fccd96c42812774c20ab2f9b811e164809789 SOFTHIER_COMMIT_HASH ?= 0 # bowwang: to be updated GVSOC_COMMIT_HASH ?= edfcd8398840ceb1e151711befa06678b05f06a0 MINIMALLOC_COMMMIT_HASH ?= e9eaf54094025e1c246f9ec231b905f8ef42a29d @@ -448,18 +448,20 @@ ${TOOLCHAIN_DIR}/snitch_cluster: cd ${TOOLCHAIN_DIR} && \ git clone https://github.com/pulp-platform/snitch_cluster.git && \ cd ${TOOLCHAIN_DIR}/snitch_cluster && git checkout ${SNITCH_COMMIT_HASH} && \ - git submodule update --init --recursive && \ - git checkout ${SNITCH_COMMIT_HASH} && git apply ${TOOLCHAIN_DIR}/snitch_cluster.patch + git -c url."https://github.com/".insteadOf="git@github.com:" \ + submodule update --init --recursive && \ + git apply --3way ${TOOLCHAIN_DIR}/snitch_cluster.patch ${SNITCH_INSTALL_DIR}: ${TOOLCHAIN_DIR}/snitch_cluster mkdir -p ${SNITCH_INSTALL_DIR} cp -r ${TOOLCHAIN_DIR}/snitch_cluster/ ${SNITCH_INSTALL_DIR}/../ cd ${SNITCH_INSTALL_DIR} && \ mkdir tmp && \ - TMPDIR=tmp pip install -r python-requirements.txt && rm -rf tmp && \ - bender vendor init && \ - cd ${SNITCH_INSTALL_DIR}/target/snitch_cluster && \ - make LLVM_BINROOT=${LLVM_INSTALL_DIR}/bin sw/runtime/banshee sw/runtime/rtl sw/math + TMPDIR=tmp pip install . && rm -rf tmp && \ + make SN_LLVM_BINROOT=${LLVM_INSTALL_DIR}/bin \ + SN_RISCV_CC="${LLVM_INSTALL_DIR}/bin/clang -target riscv32-unknown-elf -isystem ${LLVM_INSTALL_DIR}/picolibc/riscv/rv32imafd/include" \ + SN_RISCV_CXX="${LLVM_INSTALL_DIR}/bin/clang++ -target riscv32-unknown-elf -isystem ${LLVM_INSTALL_DIR}/picolibc/riscv/rv32imafd/include" \ + sn-runtime snitch_runtime: ${SNITCH_INSTALL_DIR} diff --git a/toolchain/snitch_cluster.patch b/toolchain/snitch_cluster.patch deleted file mode 100644 index 2b368db713..0000000000 --- a/toolchain/snitch_cluster.patch +++ /dev/null @@ -1,104 +0,0 @@ -diff --git a/sw/snRuntime/base.ld b/sw/snRuntime/base.ld -index d0979b7..171921d 100644 ---- a/sw/snRuntime/base.ld -+++ b/sw/snRuntime/base.ld -@@ -66,7 +66,7 @@ SECTIONS - .cbss : - { - __cbss_start = .; -- *(.cbss .cbss.*) -+ KEEP(*(.cbss .cbss.*)) - __cbss_end = .; - } >L3 - -diff --git a/sw/snRuntime/src/alloc.h b/sw/snRuntime/src/alloc.h -index ba1dee9..f79769f 100644 ---- a/sw/snRuntime/src/alloc.h -+++ b/sw/snRuntime/src/alloc.h -@@ -69,6 +69,8 @@ inline void *snrt_l3alloc(size_t size) { - - // TODO: L3 alloc size check - -+ size = ALIGN_UP(size, MIN_CHUNK_SIZE); -+ - void *ret = (void *)alloc->next; - alloc->next += size; - return ret; -diff --git a/sw/snRuntime/src/team.c b/sw/snRuntime/src/team.c -index a9eb840..5290e1d 100644 ---- a/sw/snRuntime/src/team.c -+++ b/sw/snRuntime/src/team.c -@@ -10,6 +10,10 @@ extern uint32_t snrt_global_core_idx(); - - extern uint32_t snrt_global_core_num(); - -+extern uint32_t snrt_global_compute_core_num(); -+ -+extern uint32_t snrt_global_compute_core_idx(); -+ - extern uint32_t snrt_cluster_idx(); - - extern uint32_t snrt_cluster_num(); -diff --git a/target/snitch_cluster/sw/runtime/rtl/src/putchar.c b/target/snitch_cluster/sw/runtime/rtl/src/putchar.c -index 0ad9500..215c8b1 100644 ---- a/target/snitch_cluster/sw/runtime/rtl/src/putchar.c -+++ b/target/snitch_cluster/sw/runtime/rtl/src/putchar.c -@@ -5,16 +5,19 @@ - extern uintptr_t volatile tohost, fromhost; - - // Rudimentary string buffer for putc calls. --extern uint32_t _edram; - #define PUTC_BUFFER_LEN (1024 - sizeof(size_t)) --struct putc_buffer_header { -+ -+typedef struct { - size_t size; - uint64_t syscall_mem[8]; --}; --static volatile struct putc_buffer { -- struct putc_buffer_header hdr; -+} putc_buffer_header_t; -+ -+typedef struct putc_buffer { -+ putc_buffer_header_t hdr; - char data[PUTC_BUFFER_LEN]; --} *const putc_buffer = (void *)&_edram; -+} putc_buffer_t; -+ -+static volatile putc_buffer_t putc_buffer[SNRT_CLUSTER_NUM*SNRT_CLUSTER_CORE_NUM] __attribute__((section(".dram"))); - - // Provide an implementation for putchar. - void _putchar(char character) { -diff --git a/target/snitch_cluster/sw/toolchain.mk b/target/snitch_cluster/sw/toolchain.mk -index d363059..4a84955 100644 ---- a/target/snitch_cluster/sw/toolchain.mk -+++ b/target/snitch_cluster/sw/toolchain.mk -@@ -26,11 +26,13 @@ RISCV_OBJDUMP ?= $(LLVM_BINROOT)/llvm-objdump - RISCV_DWARFDUMP ?= $(LLVM_BINROOT)/llvm-dwarfdump - - # Compiler flags -+RISCV_CFLAGS += -target riscv32-unknown-elf - RISCV_CFLAGS += $(addprefix -I,$(INCDIRS)) - RISCV_CFLAGS += -mcpu=snitch - RISCV_CFLAGS += -menable-experimental-extensions - RISCV_CFLAGS += -mabi=ilp32d - RISCV_CFLAGS += -mcmodel=medany -+RISCV_CFLAGS += -isystem ${LLVM_INSTALL_DIR}/picolibc/riscv/rv32imafd/include - # RISCV_CFLAGS += -mno-fdiv # Not supported by Clang - # RISCV_CFLAGS += -ffast-math - RISCV_CFLAGS += -fno-builtin-printf -@@ -46,12 +48,14 @@ endif - RISCV_CFLAGS += -D__DEFINED_uint64_t - - # Linker flags -+RISCV_LDFLAGS += -target riscv32-unknown-elf - RISCV_LDFLAGS += -fuse-ld=$(RISCV_LD) - RISCV_LDFLAGS += -nostartfiles - RISCV_LDFLAGS += -nostdlib - RISCV_LDFLAGS += -lc - RISCV_LDFLAGS += -L$(LLVM_BINROOT)/../lib/clang/$(LLVM_VER)/lib/ - RISCV_LDFLAGS += -lclang_rt.builtins-riscv32 -+RISCV_LDFLAGS += -isystem ${LLVM_INSTALL_DIR}/picolibc/riscv/rv32imafd/include - - # Archiver flags - RISCV_ARFLAGS = rcs From c053f8a065b9252da9c64accda7584c0f5443ce4 Mon Sep 17 00:00:00 2001 From: Gamze Islamoglu Date: Wed, 12 Aug 2026 16:13:07 +0200 Subject: [PATCH 03/21] Snitch: adapt build system to the new snitch_cluster layout Follow the layout of the bumped snitch_cluster: sw/snRuntime -> sw/runtime target/snitch_cluster -> target/sim sw/math -> removed sw/runtime/{common,rtl,banshee} -> removed SNITCH_RUNTIME_HOME points at sw/runtime and SNITCH_CLUSTER_HOME at the repository root, since sw/ is no longer nested under a target directory. The vsim simulation targets run from target/sim/build. The runtime include set picks up sw/runtime/impl, which holds the headers generated by clustergen, and links against sw/runtime/build, where sn-runtime places libsnRuntime.a. The libmath.a and banshee-specific include and link paths are dropped along with the directories they referenced, which also removes the banshee_simulation branch that chose between them. Banshee is no longer supported by recent snitch_cluster runtimes, so the banshee simulation target and BANSHEE_CONFIG are only registered when BANSHEE_INSTALL_DIR is set in the environment. Previously the Snitch platform could not be configured at all without it, even when simulating with GVSoC or vsim. --- DeeployTest/Platforms/Snitch/CMakeLists.txt | 4 ++- .../cmake/snitch-runtime-precompiled.cmake | 33 +++---------------- cmake/snitch/snitch.cmake | 6 ++-- .../snitch_cluster/snitch_cluster.cmake | 6 ++-- 4 files changed, 14 insertions(+), 35 deletions(-) diff --git a/DeeployTest/Platforms/Snitch/CMakeLists.txt b/DeeployTest/Platforms/Snitch/CMakeLists.txt index 03a40258eb..8838267ba3 100644 --- a/DeeployTest/Platforms/Snitch/CMakeLists.txt +++ b/DeeployTest/Platforms/Snitch/CMakeLists.txt @@ -12,7 +12,9 @@ add_deeploy_executable(${ProjectId} EXCLUDE_FROM_ALL ${SOURCES}) target_link_libraries(${ProjectId} PRIVATE network deeploylib) target_compile_options(${ProjectId} INTERFACE network) -add_banshee_simulation(${ProjectId}) +if(DEFINED ENV{BANSHEE_INSTALL_DIR}) + add_banshee_simulation(${ProjectId}) +endif() add_gvsoc_emulation(${ProjectId} "pulp.snitch.snitch_cluster_single") add_snitch_cluster_vsim_simulation(${ProjectId}) add_snitch_cluster_vsim_gui_simulation(${ProjectId}) \ No newline at end of file diff --git a/TargetLibraries/Snitch/cmake/snitch-runtime-precompiled.cmake b/TargetLibraries/Snitch/cmake/snitch-runtime-precompiled.cmake index 85a509a83c..18a83010d1 100644 --- a/TargetLibraries/Snitch/cmake/snitch-runtime-precompiled.cmake +++ b/TargetLibraries/Snitch/cmake/snitch-runtime-precompiled.cmake @@ -5,9 +5,8 @@ set(SNITCH_RUNTIME_BASE_INCLUDE ${SNITCH_RUNTIME_HOME}/src ${SNITCH_RUNTIME_HOME}/api + ${SNITCH_RUNTIME_HOME}/impl ${SNITCH_RUNTIME_HOME}/../deps/riscv-opcodes # TODO: generate riscv-opcodes whatever - ${SNITCH_RUNTIME_HOME}/../math/include # TODO: generate riscv-opcodes whatever - ${SNITCH_HOME}/sw/math/arch/riscv64/ ) set(SNITCH_RUNTIME_OMP_INCLUDE @@ -15,38 +14,14 @@ set(SNITCH_RUNTIME_OMP_INCLUDE ${SNITCH_RUNTIME_HOME}/api/omp ) -if(banshee_simulation) - set(SNITCH_CLUSTER_INCLUDE - ${SNITCH_CLUSTER_HOME}/sw/runtime/common - ${SNITCH_CLUSTER_HOME}/sw/runtime/banshee - ${SNITCH_CLUSTER_HOME}/sw/runtime/banshee/src - ) - set(SNITCH_CLUSTER_LINK_INCLUDE - ${SNITCH_CLUSTER_HOME}/sw/math/build - ${SNITCH_CLUSTER_HOME}/sw/runtime/banshee - ${SNITCH_CLUSTER_HOME}/sw/runtime/banshee/build - ) -else() - set(SNITCH_CLUSTER_INCLUDE - ${SNITCH_CLUSTER_HOME}/sw/runtime/common - ${SNITCH_CLUSTER_HOME}/sw/runtime/rtl - ${SNITCH_CLUSTER_HOME}/sw/runtime/rtl/src - ) - set(SNITCH_CLUSTER_LINK_INCLUDE - ${SNITCH_CLUSTER_HOME}/sw/math/build - ${SNITCH_CLUSTER_HOME}/sw/runtime/rtl - ${SNITCH_CLUSTER_HOME}/sw/runtime/rtl/build - ) -endif() - set(SNITCH_CLUSTER_LINK_OPTIONS -Wl,--gc-sections -T ${SNITCH_RUNTIME_HOME}/base.ld ) -set(SNITCH_RUNTIME_INCLUDE ${SNITCH_RUNTIME_BASE_INCLUDE} ${SNITCH_RUNTIME_OMP_INCLUDE} ${SNITCH_CLUSTER_INCLUDE}) +set(SNITCH_RUNTIME_INCLUDE ${SNITCH_RUNTIME_BASE_INCLUDE} ${SNITCH_RUNTIME_OMP_INCLUDE}) add_library(snitch-runtime INTERFACE) -target_link_directories(snitch-runtime INTERFACE ${SNITCH_CLUSTER_LINK_INCLUDE}) -target_link_libraries(snitch-runtime INTERFACE ${SNITCH_CLUSTER_LINK_OPTIONS} libsnRuntime.a libmath.a) \ No newline at end of file +target_link_directories(snitch-runtime INTERFACE ${SNITCH_RUNTIME_HOME}/build ${SNITCH_RUNTIME_HOME}/impl) +target_link_libraries(snitch-runtime INTERFACE ${SNITCH_CLUSTER_LINK_OPTIONS} libsnRuntime.a) \ No newline at end of file diff --git a/cmake/snitch/snitch.cmake b/cmake/snitch/snitch.cmake index a67f3ddc54..e0c62f1e85 100644 --- a/cmake/snitch/snitch.cmake +++ b/cmake/snitch/snitch.cmake @@ -7,7 +7,7 @@ if(NOT DEFINED ENV{SNITCH_HOME}) endif() set(SNITCH_HOME $ENV{SNITCH_HOME}) -set(SNITCH_RUNTIME_HOME ${SNITCH_HOME}/sw/snRuntime) +set(SNITCH_RUNTIME_HOME ${SNITCH_HOME}/sw/runtime) add_compile_definitions( DEEPLOY_SNITCH_PLATFORM @@ -19,7 +19,7 @@ set(num_threads ${NUM_CORES}) macro(add_snitch_cluster_vsim_simulation name) add_custom_target(vsim_${name} - WORKING_DIRECTORY ${SNITCH_HOME}/target/snitch_cluster + WORKING_DIRECTORY ${SNITCH_HOME}/target/sim/build DEPENDS ${name} COMMAND ${QUESTA} bin/snitch_cluster.vsim ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name} || true @@ -32,7 +32,7 @@ endmacro() macro(add_snitch_cluster_vsim_gui_simulation name) add_custom_target(vsim.gui_${name} - WORKING_DIRECTORY ${SNITCH_HOME}/target/snitch_cluster + WORKING_DIRECTORY ${SNITCH_HOME}/target/sim/build DEPENDS ${name} COMMAND ${QUESTA} bin/snitch_cluster.vsim.gui ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name} || true diff --git a/cmake/snitch/snitch_cluster/snitch_cluster.cmake b/cmake/snitch/snitch_cluster/snitch_cluster.cmake index 91d9392bb0..53c3464b38 100644 --- a/cmake/snitch/snitch_cluster/snitch_cluster.cmake +++ b/cmake/snitch/snitch_cluster/snitch_cluster.cmake @@ -2,6 +2,8 @@ # # SPDX-License-Identifier: Apache-2.0 -set(SNITCH_CLUSTER_HOME ${SNITCH_HOME}/target/snitch_cluster) +set(SNITCH_CLUSTER_HOME ${SNITCH_HOME}) -set(BANSHEE_CONFIG ${SNITCH_CLUSTER_HOME}/src/banshee.yaml CACHE INTERNAL "source_list") \ No newline at end of file +if(DEFINED ENV{BANSHEE_INSTALL_DIR}) + set(BANSHEE_CONFIG ${SNITCH_CLUSTER_HOME}/src/banshee.yaml CACHE INTERNAL "source_list") +endif() \ No newline at end of file From 1f9ed47c80fd8908c66be41ade712f986bdd454a Mon Sep 17 00:00:00 2001 From: Gamze Islamoglu Date: Wed, 12 Aug 2026 16:16:12 +0200 Subject: [PATCH 04/21] Snitch: compile the Snitch platform as C++ The snitch_cluster runtime headers are not valid C. sw/runtime/src/sync.h gives snrt_inter_cluster_sw_barrier a default argument: static inline void snrt_inter_cluster_sw_barrier(snrt_comm_t comm = NULL) and it is reached by every translation unit that includes snrt.h, which covers the whole Snitch target library, the platform sources and the generated Network.c. Compile all three as C++ so that they can include the runtime at all. Consequently: - The Snitch project declares CXX in addition to C and ASM. - -std=gnu99 in cmake/common.cmake applies to C sources only. It is shared by all platforms, but guarding it by COMPILE_LANGUAGE leaves every C-only platform unaffected. - The same flag is removed from add_link_options, where it never had any effect. - -Wno-c++11-narrowing is needed for the generated Network.c: the initialisers Deeploy emits are accepted by C but are narrowing conversions in C++. --- CMakeLists.txt | 2 +- DeeployTest/CMakeLists.txt | 3 +++ DeeployTest/Platforms/Snitch/CMakeLists.txt | 1 + TargetLibraries/Snitch/CMakeLists.txt | 1 + cmake/common.cmake | 4 +--- cmake/snitch/snitch.cmake | 1 + 6 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 20a85bccf8..0845d18d9d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -267,7 +267,7 @@ if(platform STREQUAL Snitch) include(${CMAKE_CURRENT_LIST_DIR}/cmake/snitch/snitch_cluster/snitch_cluster.cmake) - project(deeploy LANGUAGES C ASM) + project(deeploy LANGUAGES C CXX ASM) message(STATUS "============================= ${platform} Configuration ============================") message(STATUS "[cMake ] Number of total cores = " ${NUM_CORES}) diff --git a/DeeployTest/CMakeLists.txt b/DeeployTest/CMakeLists.txt index b7f3535790..02012d5765 100644 --- a/DeeployTest/CMakeLists.txt +++ b/DeeployTest/CMakeLists.txt @@ -6,6 +6,9 @@ include_directories(${GENERATED_SOURCE}) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +if(DEEPLOY_ARCH STREQUAL SNITCH) + set_source_files_properties(${GENERATED_SOURCE}/Network.c PROPERTIES LANGUAGE CXX) +endif() add_library(network OBJECT ${GENERATED_SOURCE}/Network.c) target_link_libraries(network PUBLIC deeploylib) diff --git a/DeeployTest/Platforms/Snitch/CMakeLists.txt b/DeeployTest/Platforms/Snitch/CMakeLists.txt index 8838267ba3..45b620e9d7 100644 --- a/DeeployTest/Platforms/Snitch/CMakeLists.txt +++ b/DeeployTest/Platforms/Snitch/CMakeLists.txt @@ -8,6 +8,7 @@ file(GLOB_RECURSE SOURCES main.c ) +set_source_files_properties(${SOURCES} PROPERTIES LANGUAGE CXX) add_deeploy_executable(${ProjectId} EXCLUDE_FROM_ALL ${SOURCES}) target_link_libraries(${ProjectId} PRIVATE network deeploylib) diff --git a/TargetLibraries/Snitch/CMakeLists.txt b/TargetLibraries/Snitch/CMakeLists.txt index a2dd6703c7..77a17cee9f 100644 --- a/TargetLibraries/Snitch/CMakeLists.txt +++ b/TargetLibraries/Snitch/CMakeLists.txt @@ -8,6 +8,7 @@ file(GLOB_RECURSE SOURCES include(cmake/snitch-runtime-precompiled.cmake) +set_source_files_properties(${SOURCES} PROPERTIES LANGUAGE CXX) add_deeploy_library(deeploysnitch STATIC ${SOURCES}) target_include_directories(deeploysnitch PUBLIC diff --git a/cmake/common.cmake b/cmake/common.cmake index 9d1aaba7d2..01fc29c9d5 100644 --- a/cmake/common.cmake +++ b/cmake/common.cmake @@ -9,7 +9,7 @@ add_compile_definitions( ) add_compile_options( - -std=gnu99 + $<$:-std=gnu99> -fdiagnostics-color=always @@ -25,8 +25,6 @@ add_compile_options( ) add_link_options( - -std=gnu99 - -fdiagnostics-color=always -Wunused-variable diff --git a/cmake/snitch/snitch.cmake b/cmake/snitch/snitch.cmake index e0c62f1e85..c069abbae2 100644 --- a/cmake/snitch/snitch.cmake +++ b/cmake/snitch/snitch.cmake @@ -45,6 +45,7 @@ endmacro() add_compile_options( -ffast-math + $<$:-Wno-c++11-narrowing> ) add_link_options( From 714a7e0fba3e1c9874432fc5bffb3011a585f5e7 Mon Sep 17 00:00:00 2001 From: Gamze Islamoglu Date: Wed, 12 Aug 2026 16:29:49 +0200 Subject: [PATCH 05/21] Snitch: update runtime API calls for the new snitch_cluster The snRuntime API changed with the bump: snrt_l1alloc / snrt_l3alloc -> snrt_l1_alloc / snrt_l3_alloc Performance counters are selected by index rather than by an SNRT_PERF_CNT* constant, and configuring a counter is now separate from starting it: snrt_start_perf_counter(SNRT_PERF_CNT0, SNRT_PERF_CNT_CYCLES, 0) -> snrt_cfg_perf_counter(0, PERF_METRIC__CYCLE, 0) + snrt_start_perf_counter(0) snrt_reset_perf_counter(SNRT_PERF_CNT0) -> snrt_reset_perf_counter(0) snrt_stop_perf_counter(SNRT_PERF_CNT0) -> snrt_stop_perf_counter(0) Update the allocation templates, CycleCounter and deeploy_malloc accordingly. --- Deeploy/Targets/Snitch/Templates/AllocateTemplate.py | 8 ++++---- TargetLibraries/Snitch/src/CycleCounter.c | 7 ++++--- TargetLibraries/Snitch/src/Util.c | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Deeploy/Targets/Snitch/Templates/AllocateTemplate.py b/Deeploy/Targets/Snitch/Templates/AllocateTemplate.py index 7d7b65d348..d03fc5090a 100644 --- a/Deeploy/Targets/Snitch/Templates/AllocateTemplate.py +++ b/Deeploy/Targets/Snitch/Templates/AllocateTemplate.py @@ -9,10 +9,10 @@ snitchL1InitTemplate = NodeTemplate("${type.typeName} ${name};\n") snitchL2AllocateTemplate = NodeTemplate( - "${name} = (${type.typeName}) snrt_l3alloc(sizeof(${type.referencedType.typeName}) * ${size});\n") + "${name} = (${type.typeName}) snrt_l3_alloc(sizeof(${type.referencedType.typeName}) * ${size});\n") snitchL1AllocateTemplate = NodeTemplate( - "${name} = (${type.typeName}) snrt_l1alloc(sizeof(${type.referencedType.typeName}) * ${size});\n") + "${name} = (${type.typeName}) snrt_l1_alloc(sizeof(${type.referencedType.typeName}) * ${size});\n") snitchL2GlobalInitTemplate = NodeTemplate("static ${type.referencedType.typeName} ${name}[${size}] = {${values}};\n") @@ -47,10 +47,10 @@ snitchGenericGuardedAllocate = NodeTemplate(""" % if _memoryLevel == "L1": -if (snrt_is_dm_core()) { ${name} = (${type.typeName}) snrt_l1alloc(sizeof(${type.referencedType.typeName}) * ${size}); } +if (snrt_is_dm_core()) { ${name} = (${type.typeName}) snrt_l1_alloc(sizeof(${type.referencedType.typeName}) * ${size}); } snrt_cluster_hw_barrier();\n % else: -if (snrt_is_dm_core()) { ${name} = (${type.typeName}) snrt_l3alloc(sizeof(${type.referencedType.typeName}) * ${size}); } +if (snrt_is_dm_core()) { ${name} = (${type.typeName}) snrt_l3_alloc(sizeof(${type.referencedType.typeName}) * ${size}); } snrt_cluster_hw_barrier();\n % endif """) diff --git a/TargetLibraries/Snitch/src/CycleCounter.c b/TargetLibraries/Snitch/src/CycleCounter.c index 3861c421c1..cf9f40ac83 100644 --- a/TargetLibraries/Snitch/src/CycleCounter.c +++ b/TargetLibraries/Snitch/src/CycleCounter.c @@ -14,7 +14,7 @@ static uint32_t instr_end[NUM_CORES] __attribute__((section(".l1"))); static uint32_t running[NUM_CORES] __attribute__((section(".l1"))); void ResetTimer() { - snrt_reset_perf_counter(SNRT_PERF_CNT0); + snrt_reset_perf_counter(0); uint32_t const core_id = snrt_global_core_idx(); uint32_t _timer_init = read_csr(mcycle); uint32_t _instr_init = read_csr(minstret); @@ -27,7 +27,8 @@ void ResetTimer() { void StartTimer() { if (snrt_is_dm_core()) { - snrt_start_perf_counter(SNRT_PERF_CNT0, SNRT_PERF_CNT_CYCLES, 0); + snrt_cfg_perf_counter(0, PERF_METRIC__CYCLE, 0); + snrt_start_perf_counter(0); } uint32_t const core_id = snrt_global_core_idx(); timer_init[core_id] = read_csr(mcycle); @@ -37,7 +38,7 @@ void StartTimer() { void StopTimer() { if (snrt_is_dm_core()) { - snrt_stop_perf_counter(SNRT_PERF_CNT0); + snrt_stop_perf_counter(0); } uint32_t const core_id = snrt_global_core_idx(); timer_end[core_id] = read_csr(mcycle); diff --git a/TargetLibraries/Snitch/src/Util.c b/TargetLibraries/Snitch/src/Util.c index 35ef97ea59..32cf53de2d 100644 --- a/TargetLibraries/Snitch/src/Util.c +++ b/TargetLibraries/Snitch/src/Util.c @@ -18,6 +18,6 @@ int deeploy_log(const char *__restrict fmt, ...) { return ret; } -void *deeploy_malloc(const size_t size) { return snrt_l1alloc(size); } +void *deeploy_malloc(const size_t size) { return snrt_l1_alloc(size); } void deeploy_free(void *const __attribute__((unused)) ptr) {} From 56fee8d7d0bb870799fe2b47c5a499d408bc6295 Mon Sep 17 00:00:00 2001 From: Gamze Islamoglu Date: Wed, 12 Aug 2026 16:40:24 +0200 Subject: [PATCH 06/21] Snitch: declare the Snitch kernels used from generated code Several Snitch kernels are defined in TargetLibraries/Snitch/src but declared in no header that reaches their call sites, so the calls Deeploy emits into Network.c relied on implicit declarations: - SnitchAdd and snitch_nn_add_i8_i8_i8 had no declaration. - SnitchSoftmax_i8_u8 was declared as StnichSoftmax_i8_u8, in kernel/iSoftmax.h, which DeeploySnitchMath.h never included. C++ has no implicit declarations, so declare them all and include kernel/iSoftmax.h. In snitch_nn_add_i8_i8_i8 the scratch variables a and b were declared int while their addresses are assigned to int8_t pointers. C accepts the mismatch with a warning; C++ rejects it. Declare them int8_t, matching the pointers and the surrounding buffers. --- TargetLibraries/Snitch/inc/DeeploySnitchMath.h | 1 + TargetLibraries/Snitch/inc/kernel/Add.h | 9 +++++++++ TargetLibraries/Snitch/inc/kernel/iSoftmax.h | 2 +- TargetLibraries/Snitch/src/snitch_nn_add_i8_i8_i8.c | 4 ++-- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/TargetLibraries/Snitch/inc/DeeploySnitchMath.h b/TargetLibraries/Snitch/inc/DeeploySnitchMath.h index f686e597ca..8513170afb 100644 --- a/TargetLibraries/Snitch/inc/DeeploySnitchMath.h +++ b/TargetLibraries/Snitch/inc/DeeploySnitchMath.h @@ -40,6 +40,7 @@ typedef float v2f32 __attribute__((vector_size(8))); #include "kernel/Softmax.h" #include "kernel/UniformRequantShift.h" #include "kernel/iNoNorm.h" +#include "kernel/iSoftmax.h" #include "dmaStruct.h" diff --git a/TargetLibraries/Snitch/inc/kernel/Add.h b/TargetLibraries/Snitch/inc/kernel/Add.h index dc08482021..e6cc131a4d 100644 --- a/TargetLibraries/Snitch/inc/kernel/Add.h +++ b/TargetLibraries/Snitch/inc/kernel/Add.h @@ -12,4 +12,13 @@ void Add_fp32(float32_t *input1, float32_t *input2, float32_t *output, uint32_t size, uint32_t is_scalar); +void SnitchAdd(int8_t *pIn1, int8_t *pIn2, int32_t *pOut, uint32_t size, + int32_t offset); + +void snitch_nn_add_i8_i8_i8( + int8_t *pIn1, int8_t *pIn2, int8_t *pOut, int32_t in1_mul, int32_t in1_add, + uint16_t in1_shift, int32_t in2_mul, int32_t in2_add, uint16_t in2_shift, + int32_t out_mul, int32_t out_add, uint16_t out_shift, uint16_t dim_im_in_x, + uint16_t dim_im_in_y, uint16_t ch_im_in, int out_requant_flag); + #endif // __DEEPLOY_MATH_ADD_KERNEL_HEADER_ diff --git a/TargetLibraries/Snitch/inc/kernel/iSoftmax.h b/TargetLibraries/Snitch/inc/kernel/iSoftmax.h index fb68d04adf..f07895126e 100644 --- a/TargetLibraries/Snitch/inc/kernel/iSoftmax.h +++ b/TargetLibraries/Snitch/inc/kernel/iSoftmax.h @@ -13,7 +13,7 @@ void SnitchSoftmax_u8_u8(uint8_t *data_in, uint8_t *data_out, uint32_t *lastDimBuffer, uint32_t size, uint32_t lastDimLength, int32_t coeffB, int32_t coeffC, int32_t log2); -void StnichSoftmax_i8_u8(int8_t *data_in, uint8_t *data_out, +void SnitchSoftmax_i8_u8(int8_t *data_in, uint8_t *data_out, uint32_t *lastDimBuffer, uint32_t size, uint32_t lastDimLength, int32_t coeffB, int32_t coeffC, int32_t log2); diff --git a/TargetLibraries/Snitch/src/snitch_nn_add_i8_i8_i8.c b/TargetLibraries/Snitch/src/snitch_nn_add_i8_i8_i8.c index b735e60704..504cc81873 100644 --- a/TargetLibraries/Snitch/src/snitch_nn_add_i8_i8_i8.c +++ b/TargetLibraries/Snitch/src/snitch_nn_add_i8_i8_i8.c @@ -40,8 +40,8 @@ snitch_nn_add_i8_i8_i8(int8_t *pIn1, int8_t *pIn2, int8_t *pOut, int8_t *target2 = pIn2 + start * ch_im_in2_r * dim_im_in_x; int8_t *pOutBuffer = pOut + start * ch_im_out_r * dim_im_in_x; - int a = 0; - int b = 0; + int8_t a = 0; + int8_t b = 0; int8_t *target1_ext = &a; int8_t *target2_ext = &b; From a99a6da34b5e1c0e5f79275a77e7f4d4c1085a71 Mon Sep 17 00:00:00 2001 From: Gamze Islamoglu Date: Wed, 12 Aug 2026 16:53:44 +0200 Subject: [PATCH 07/21] Snitch: do not emit sizeof(void) for transient buffers TransientBuffers are void pointers whose size is already expressed in bytes, while ordinary buffers carry an element type and an element count. The allocation template applied the element-count form to both, so a transient buffer produced snrt_l3_alloc(sizeof(void) * 256) That happens to allocate the right number of bytes, but only because sizeof(void) is a GNU C extension that evaluates to 1. It is invalid in C++, which the Snitch platform is now compiled as. Emit the byte count directly when the referenced type is void, and keep the sizeof form for typed buffers. The number of bytes allocated is unchanged. The iSoftmax kernel takes its scratch buffer as uint32_t *, so cast the void pointer at the call site as well; C++ does not convert void * to another object pointer implicitly. --- .../Targets/Snitch/Templates/AllocateTemplate.py | 13 +++++++++++-- .../Targets/Snitch/Templates/iSoftmaxTemplate.py | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Deeploy/Targets/Snitch/Templates/AllocateTemplate.py b/Deeploy/Targets/Snitch/Templates/AllocateTemplate.py index d03fc5090a..66fc54ca63 100644 --- a/Deeploy/Targets/Snitch/Templates/AllocateTemplate.py +++ b/Deeploy/Targets/Snitch/Templates/AllocateTemplate.py @@ -46,11 +46,20 @@ """) snitchGenericGuardedAllocate = NodeTemplate(""" +<% +# TransientBuffers are void pointers whose size is already expressed in bytes. +# sizeof(void) is a GNU C extension that evaluates to 1; it is invalid in C++, +# which the Snitch platform is compiled as. +if type.referencedType.typeName == "void": + allocSize = f"{size}" +else: + allocSize = f"sizeof({type.referencedType.typeName}) * {size}" +%> % if _memoryLevel == "L1": -if (snrt_is_dm_core()) { ${name} = (${type.typeName}) snrt_l1_alloc(sizeof(${type.referencedType.typeName}) * ${size}); } +if (snrt_is_dm_core()) { ${name} = (${type.typeName}) snrt_l1_alloc(${allocSize}); } snrt_cluster_hw_barrier();\n % else: -if (snrt_is_dm_core()) { ${name} = (${type.typeName}) snrt_l3_alloc(sizeof(${type.referencedType.typeName}) * ${size}); } +if (snrt_is_dm_core()) { ${name} = (${type.typeName}) snrt_l3_alloc(${allocSize}); } snrt_cluster_hw_barrier();\n % endif """) diff --git a/Deeploy/Targets/Snitch/Templates/iSoftmaxTemplate.py b/Deeploy/Targets/Snitch/Templates/iSoftmaxTemplate.py index b4b5abbf16..856e180c60 100644 --- a/Deeploy/Targets/Snitch/Templates/iSoftmaxTemplate.py +++ b/Deeploy/Targets/Snitch/Templates/iSoftmaxTemplate.py @@ -16,5 +16,5 @@ else: signatureString += '_u8' %> -SnitchSoftmax${signatureString}(${data_in}, ${data_out}, ${lastDimBuffer}, ${size}, ${lastDimLength}, ${coeffB}, ${coeffC}, ${log2}); +SnitchSoftmax${signatureString}(${data_in}, ${data_out}, (uint32_t *) ${lastDimBuffer}, ${size}, ${lastDimLength}, ${coeffB}, ${coeffC}, ${log2}); """) From 20924be9dda2318e3f5e92e13a6c879112d33d82 Mon Sep 17 00:00:00 2001 From: Gamze Islamoglu Date: Wed, 12 Aug 2026 16:57:24 +0200 Subject: [PATCH 08/21] TargetLibraries: give the Generic headers C linkage The Generic target library is compiled as C, but the Snitch platform now compiles its target library and its generated Network.c as C++. Calls from there into the Generic library were mangled as C++ symbols and did not resolve against the C definitions: ld.lld: error: undefined symbol: MatMul_s8_s8_s32(signed char const*, signed char const*, int*, ...) Wrap the library's own headers in DeeployBasicMath.h in extern "C". The standard headers included above the guard are left outside it, as they already declare their own linkage for C++. The guard is inert for every other platform: they all declare their project as C and ASM only, so __cplusplus is never defined when these headers are parsed. --- TargetLibraries/Generic/inc/DeeployBasicMath.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/TargetLibraries/Generic/inc/DeeployBasicMath.h b/TargetLibraries/Generic/inc/DeeployBasicMath.h index 2023b9e725..2afce5edbf 100644 --- a/TargetLibraries/Generic/inc/DeeployBasicMath.h +++ b/TargetLibraries/Generic/inc/DeeployBasicMath.h @@ -28,6 +28,13 @@ #include #include +// The Generic target library is compiled as C. Platforms that compile their +// generated code as C++ (Snitch) must see these declarations with C linkage, +// or the mangled call will not match the definition at link time. +#ifdef __cplusplus +extern "C" { +#endif + #include "macros.h" #include "types.h" #include "utils.h" @@ -65,4 +72,8 @@ #include "kernel/Sqrt.h" #include "kernel/Swish.h" +#ifdef __cplusplus +} +#endif + #endif //__DEEPLOY_BASIC_MATH_HEADER_ From 79177f2bfdb0a379a8500706cd659de8df0b49c2 Mon Sep 17 00:00:00 2001 From: Gamze Islamoglu Date: Wed, 12 Aug 2026 19:03:06 +0200 Subject: [PATCH 09/21] Snitch: use memcpy for the L3 input copies The test harness copied its input vectors into the network's input buffers with snrt_dma_start_1d. Both sides of that transfer are in L3: testInputVector lives in .dram and DeeployNetwork_inputs is allocated with snrt_l3_alloc. The Snitch iDMA has a single AXI master port and requires one side of a transfer to be TCDM, so it cannot perform an L3 to L3 copy. Use memcpy instead. This goes unnoticed under GVSoC, which does not model the constraint, and only shows up on RTL simulation. --- DeeployTest/Platforms/Snitch/main.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/DeeployTest/Platforms/Snitch/main.c b/DeeployTest/Platforms/Snitch/main.c index 70faef6ca8..f2b1425bb3 100644 --- a/DeeployTest/Platforms/Snitch/main.c +++ b/DeeployTest/Platforms/Snitch/main.c @@ -9,6 +9,7 @@ #include "snrt.h" #include "testinputs.h" #include "testoutputs.h" +#include // #define NOPRINT // #define NOTEST @@ -58,12 +59,13 @@ int main(void) { printf("Copy inputs...\r\n"); #endif - // WIESEP: Copy inputs to allocated memory + // Copy inputs to allocated L3 memory. A plain memcpy is used here because + // the iDMA engine cannot perform L3->L3 (AXI->AXI) transfers — it has a + // single AXI master port and requires one side to be TCDM. for (uint32_t buf = 0; buf < DeeployNetwork_num_inputs; buf++) { - snrt_dma_start_1d(DeeployNetwork_inputs[buf], testInputVector[buf], - DeeployNetwork_inputs_bytes[buf]); + memcpy(DeeployNetwork_inputs[buf], testInputVector[buf], + DeeployNetwork_inputs_bytes[buf]); } - snrt_dma_wait_all(); #ifndef CI printf("Input copied\r\n"); From fbdc1714a3b74577ec2e9012d7a972b2224fb2e8 Mon Sep 17 00:00:00 2001 From: Gamze Islamoglu Date: Thu, 13 Aug 2026 15:25:46 +0200 Subject: [PATCH 10/21] Snitch: fence the FPU before cluster barriers Floating-point operations on Snitch are not ordered with respect to the hardware barrier. A compute core can therefore pass snrt_cluster_hw_barrier() while its results are still in flight, and the DM core released by that barrier reads the tile out of L1 before the values have landed. Insert snrt_fpu_fence() before the barrier emitted by the cluster synchronisation pass, and before the two barriers in the test harness that follow floating-point work. --- .../Snitch/CodeTransformationPasses/SnitchClusterSynch.py | 1 + DeeployTest/Platforms/Snitch/main.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Deeploy/Targets/Snitch/CodeTransformationPasses/SnitchClusterSynch.py b/Deeploy/Targets/Snitch/CodeTransformationPasses/SnitchClusterSynch.py index cc0af164ad..16eb48f4e3 100644 --- a/Deeploy/Targets/Snitch/CodeTransformationPasses/SnitchClusterSynch.py +++ b/Deeploy/Targets/Snitch/CodeTransformationPasses/SnitchClusterSynch.py @@ -8,6 +8,7 @@ NodeTemplate, _NoVerbosity _synchTemplate = NodeTemplate(""" + snrt_fpu_fence(); snrt_cluster_hw_barrier(); """) diff --git a/DeeployTest/Platforms/Snitch/main.c b/DeeployTest/Platforms/Snitch/main.c index f2b1425bb3..8358ed39c7 100644 --- a/DeeployTest/Platforms/Snitch/main.c +++ b/DeeployTest/Platforms/Snitch/main.c @@ -95,6 +95,7 @@ int main(void) { StopTimer(); + snrt_fpu_fence(); snrt_cluster_hw_barrier(); #ifndef CI @@ -162,6 +163,7 @@ int main(void) { #endif } + snrt_fpu_fence(); snrt_cluster_hw_barrier(); return 0; } From a34a1d44557a80f2d94484d8d9a0ea4407648904 Mon Sep 17 00:00:00 2001 From: Gamze Islamoglu Date: Thu, 13 Aug 2026 15:37:21 +0200 Subject: [PATCH 11/21] Snitch: enable the hardware divider in the cluster configuration Deeploy's FP32 kernels use floating-point division and square root. The upstream default cluster configuration leaves Xdiv_sqrt disabled, so those instructions have no functional unit behind them and RTL simulation fails. Patch the configuration Deeploy builds against to enable it. The patch is applied with --3way so that it can still be merged if upstream moves the surrounding lines, and fails loudly if it genuinely conflicts. This is not observable under GVSoC, which is the only simulator the CI runs, so the tests pass there regardless of the setting. --- toolchain/snitch_cluster.patch | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 toolchain/snitch_cluster.patch diff --git a/toolchain/snitch_cluster.patch b/toolchain/snitch_cluster.patch new file mode 100644 index 0000000000..c9dc391768 --- /dev/null +++ b/toolchain/snitch_cluster.patch @@ -0,0 +1,12 @@ +diff --git a/cfg/default.json b/cfg/default.json +index 7ccc78a..e3c44a0 100644 +--- a/cfg/default.json ++++ b/cfg/default.json +@@ -100,6 +100,7 @@ + xssr: true, + xfrep: true, + xcopift: true, ++ Xdiv_sqrt: true, + xdma: false, + xf16: true, + xf16alt: true, From a9618dc97bd088f02cf92a1bd649e6d11694d974 Mon Sep 17 00:00:00 2001 From: Gamze Islamoglu Date: Fri, 14 Aug 2026 14:03:45 +0200 Subject: [PATCH 12/21] Snitch: wait for all DMA transfers before the barrier Waiting on an individual transaction ID did not guarantee the transfer had landed. snrt_dma_wait compares against completed_id, which the iDMA advances when the ND midend observes burst_rsp.last. That is upstream of the write datapath, so completed_id can move before the data is visible in TCDM, and the barrier that follows releases the compute cores onto a tile the DMA has not finished writing. RTL simulation reports this as X values written to registers (RegWriteKnown assertions) and as wrong results. Two further details make per-transfer waiting unusable against this runtime: dmcpyi returns the same ID for transfers issued back to back, so distinct transfers are not distinguishable, and the cluster is configured with a single DMA channel. Use the barrier waiting strategy, which SnitchBarrierFuture already provided but nothing used. snrt_dma_wait_all polls the busy flag instead, which covers every pipeline stage. This also drops the zero-repetition self-copy that followed every transfer: it existed only to bump completed_id past the last transaction ID, which the strictly-greater comparison in the previously pinned runtime required. This is more conservative than necessary and will serialise double-buffering if that is enabled later. SnitchFuture is kept for when the underlying issue is fixed. --- Deeploy/Targets/Snitch/DMA/SnitchDma.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/Deeploy/Targets/Snitch/DMA/SnitchDma.py b/Deeploy/Targets/Snitch/DMA/SnitchDma.py index ac0c622cc8..b3786152fa 100644 --- a/Deeploy/Targets/Snitch/DMA/SnitchDma.py +++ b/Deeploy/Targets/Snitch/DMA/SnitchDma.py @@ -5,7 +5,7 @@ from typing import Dict, Tuple from Deeploy.DeeployTypes import NetworkContext, NodeTemplate, OperatorRepresentation, VariableBuffer -from Deeploy.TilingExtension.AsyncDma import AsyncDma, DmaDirection, Future, PerTensorWaitingStrategy +from Deeploy.TilingExtension.AsyncDma import AsyncDma, BarrierWaitingStrategy, DmaDirection, Future class SnitchBarrierFuture(Future): @@ -33,13 +33,26 @@ class SnitchDma(AsyncDma): 2: NodeTemplate(""" if (snrt_is_dm_core()) { - ${future} = snrt_dma_start_2d(${dest}, ${src}, ${size}, ${stride_dest}, ${stride_src}, ${repeat}); - // WIESEP: Hack as otherwise the last commited DMA transaction ID can never be resolved. - snrt_dma_start_2d(${dest}, ${dest}, 1, 0, 0, 0); + snrt_dma_start_2d(${dest}, ${src}, ${size}, ${stride_dest}, ${stride_src}, ${repeat}); } """), } - _waitingStrategy = PerTensorWaitingStrategy(SnitchFuture) + # Wait for all outstanding transfers rather than for an individual one. + # + # snrt_dma_wait compares against completed_id, which idma advances when the + # ND midend sees burst_rsp.last. That is upstream of the write datapath, so + # it can move before the transferred data is visible, and the barrier that + # follows then releases the compute cores onto a tile the DMA has not + # finished writing. snrt_dma_wait_all instead polls the busy flag, which + # covers every pipeline stage. + # + # Two further details make per-transfer waiting unusable here: dmcpyi + # returns the same ID for transfers issued back to back. + # + # This also drops the self-copy that used to follow every transfer: it + # existed to bump completed_id past the last transaction ID, which the + # strictly-greater comparison in the previously pinned runtime required. + _waitingStrategy = BarrierWaitingStrategy(SnitchBarrierFuture, "dma_barrier") def __init__(self, transferTemplates: Dict[int, NodeTemplate] = _transferTemplates) -> None: super().__init__(transferTemplates) From 1dbc0366bffbc14905c5ad1bbc143875b830ac1f Mon Sep 17 00:00:00 2001 From: Gamze Islamoglu Date: Mon, 17 Aug 2026 10:51:51 +0200 Subject: [PATCH 13/21] Snitch: constrain the GEMM tile sizes in both parallel dimensions The FP32 kernels stream the output width in groups of `unroll` (8) through the SSRs and have no remainder path. A tile of O below 8 makes `O / unroll` zero, and since snrt_ssr_loop_* writes `bound - 1` the bound wraps to 0xFFFFFFFF: the streamer is told to produce ~4 billion elements while the consuming loop runs zero times, so it never goes idle and the next SSR configuration write never retires. The M constraint existed but was only a PerformanceHint, so the solver dropped it whenever L1 got tight. That is not merely slower but wrong: the template passes `M / compute_num` to the kernel, so a tile of M that is not a multiple of 8 truncates and the rows in the remainder are never computed. Observed on RTL in tiled Kernels/FP32/GEMM/Regular, where compute cores 0-7 all parked on `scfgwi` writing a bound of -1 and never retired it. Drop the l1=2000 entries for the two FP32 GEMM tests. The smallest legal tile of a 32x32 GEMM is M=8, O=8, K=32, which needs 2560 B single-buffered. --- .../TileConstraints/GemmTileConstraint.py | 23 ++++++++++++------- DeeployTest/test_snitch_tiled_config.py | 4 ++-- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/Deeploy/Targets/Snitch/TileConstraints/GemmTileConstraint.py b/Deeploy/Targets/Snitch/TileConstraints/GemmTileConstraint.py index e5d35c4a42..3639ba9780 100644 --- a/Deeploy/Targets/Snitch/TileConstraints/GemmTileConstraint.py +++ b/Deeploy/Targets/Snitch/TileConstraints/GemmTileConstraint.py @@ -9,7 +9,7 @@ from Deeploy.DeeployTypes import NetworkContext, OperatorRepresentation from Deeploy.TilingExtension.MemoryConstraints import NodeMemoryConstraint from Deeploy.TilingExtension.TileConstraint import TileConstraint -from Deeploy.TilingExtension.TilerModel import PerformanceHint, TilerModel +from Deeploy.TilingExtension.TilerModel import TilerModel from Deeploy.TilingExtension.TilingCodegen import AbsoluteHyperRectangle, HyperRectangle, TilingSchedule, \ VariableReplacementScheme @@ -89,13 +89,20 @@ def addPolicyConstraint(tilerModel: TilerModel, parseDict: Dict, ctxt: NetworkCo # Full inner dimension tilerModel.addConstraint(AWidthDimVar == AWidthDimVar.Max()) - # We parallelize over the output height dimension so try to keep it divisible by the number of cores (8) - if parseDict["M"] > 8: - tilerModel.addTileSizeDivisibleConstraint(parseDict, - "M", - YHeightDimVar, - 8, - strategy = PerformanceHint(priority = 1)) + # We parallelize over the output height dimension by handing each of the 8 + # compute cores M / 8 rows, so a tile of M that is not a multiple of 8 is + # invalid. + if parseDict["M"] >= 8: + tilerModel.addTileSizeDivisibleConstraint(parseDict, "M", YHeightDimVar, 8) + + # The kernels stream the output width in groups of `unroll` (8) through the + # SSRs and have no remainder path, so they require a tile of O that is a + # multiple of 8. A smaller tile makes `O / unroll` zero, and since the SSR + # bound is written as `bound - 1` it wraps to 0xFFFFFFFF: the streamer is + # told to produce ~4 billion elements while the consuming loop runs zero + # times, so it never goes idle and the next SSR config write never retires. + if parseDict["O"] >= 8: + tilerModel.addTileSizeDivisibleConstraint(parseDict, "O", YWidthDimVar, 8) return tilerModel diff --git a/DeeployTest/test_snitch_tiled_config.py b/DeeployTest/test_snitch_tiled_config.py index 1842b00461..2ca0d7e7dd 100644 --- a/DeeployTest/test_snitch_tiled_config.py +++ b/DeeployTest/test_snitch_tiled_config.py @@ -19,8 +19,8 @@ "Kernels/FP32/Mul/Scalar": [2000, 5000, 10000], "Kernels/FP32/RMSNorm/single_fused_op": [2000, 5000, 10000], "Kernels/FP32/Softmax/Regular": [2000, 5000, 10000], - "Kernels/FP32/GEMM/Regular": [2000, 5000, 10000], - "Kernels/FP32/GEMM/TransB": [2000, 5000, 10000], + "Kernels/FP32/GEMM/Regular": [5000, 10000], + "Kernels/FP32/GEMM/TransB": [5000, 10000], "Kernels/Integer/Add/Large": [5000, 10000], "Kernels/Integer/Softmax/Large": [5000, 10000], "Kernels/Integer/iNoNorm": [5000, 10000], From ce83421a8bb5d6f50a0c02d5f5c1dfde2b72ddca Mon Sep 17 00:00:00 2001 From: Gamze Islamoglu Date: Mon, 17 Aug 2026 10:52:00 +0200 Subject: [PATCH 14/21] Snitch: preserve a scalar second operand for Add and Mul The Generic Add and Mul layers rewrite the shorter operand's shape to the longer one in computeShapes. That expresses broadcasting notionally but materialises no data: for a genuine scalar the is_scalar flag the parser derives from the shape then comes out false, the buffer is allocated for the full tensor, and the kernel reads elements that were never written. Override computeShapes for Snitch to leave a single-element second operand alone, matching DivLayer which has no such rewrite and is correct. On RTL this showed up as X values written to floating-point registers (RegWriteKnown assertions) in tiled FP32 Add/Scalar, Mul/Scalar and Mul/Regular. It is not observable under GVSoC. Note the defect is in the Generic layers, so this treats the symptom for one target. Fixing computeShapes there would affect every other target. --- Deeploy/Targets/Snitch/Layers.py | 39 ++++++++++++++++++++++++++++++ Deeploy/Targets/Snitch/Platform.py | 9 ++++--- 2 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 Deeploy/Targets/Snitch/Layers.py diff --git a/Deeploy/Targets/Snitch/Layers.py b/Deeploy/Targets/Snitch/Layers.py new file mode 100644 index 0000000000..edaa56b244 --- /dev/null +++ b/Deeploy/Targets/Snitch/Layers.py @@ -0,0 +1,39 @@ +# SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna +# +# SPDX-License-Identifier: Apache-2.0 + +from typing import Tuple + +import numpy as np + +from Deeploy.DeeployTypes import Shape +from Deeploy.Targets.Generic.Layers import AddLayer, MulLayer + + +class _ScalarPreservingShapeMixin: + """Keep a single-element second operand at its original shape. + + The Generic Add and Mul layers rewrite the shorter operand's shape to the + longer one, which expresses broadcasting notionally but does not + materialise any data. For a genuine scalar that is harmful on Snitch: the + kernels broadcast input2[0] themselves, selected by the is_scalar flag the + parser derives from the operand shape. Once the shape has been rewritten + the flag comes out false, the buffer is allocated for the full tensor, and + the kernel reads elements that were never written. + """ + + def computeShapes(self, inputShapes: Shape, outputShapes: Shape, operatorRepresentation, + channels_first) -> Tuple[Shape, Shape]: + + if len(inputShapes) > 1 and np.prod(inputShapes[1]) == 1: + return (inputShapes, [inputShapes[0]]) + + return super().computeShapes(inputShapes, outputShapes, operatorRepresentation, channels_first) + + +class SnitchAddLayer(_ScalarPreservingShapeMixin, AddLayer): + pass + + +class SnitchMulLayer(_ScalarPreservingShapeMixin, MulLayer): + pass diff --git a/Deeploy/Targets/Snitch/Platform.py b/Deeploy/Targets/Snitch/Platform.py index 53b046f9de..62e9fa4094 100644 --- a/Deeploy/Targets/Snitch/Platform.py +++ b/Deeploy/Targets/Snitch/Platform.py @@ -12,8 +12,8 @@ from Deeploy.Targets.Generic.Bindings import BasicLayerNormBindings, BasicPad1DBindings, BasicPad2DBindings, \ BasicRQIntegerDivBinding from Deeploy.Targets.Generic.Layers import AddLayer, ConcatLayer, DivLayer, GatherLayer, GEMMLayer, HardSwishLayer, \ - LayerNormLayer, MatMulLayer, MulLayer, PadLayer, ReshapeLayer, RMSNormLayer, RQGEMMLayer, RQIntegerDivLayer, \ - SoftmaxLayer, TransposeLayer, iNoNormLayer + LayerNormLayer, MatMulLayer, PadLayer, ReshapeLayer, RMSNormLayer, RQGEMMLayer, RQIntegerDivLayer, SoftmaxLayer, \ + TransposeLayer, iNoNormLayer from Deeploy.Targets.Generic.Parsers import ConcatParser, GatherParser, MatMulParser, Pad1DParser, Pad2DParser, \ ReshapeParser, RQAddParser, RQIntegerDivParser, SoftmaxParser, TransposeParser, UnsqueezeParser, iLayerNormParser, \ iNoNormParser, iSoftmaxParser @@ -22,6 +22,7 @@ IntegerDivRequantMergePass, MergeConstAddAndRequantPass, MergeTrueIntegerDivRequantShiftPass, RQSSplitPass, \ SkipEmptyConcatPass, SkipUnityRequantPass, iGELURequantMergePass, iHardswishRequantMergePass from Deeploy.Targets.PULPOpen.Platform import RQAddMapper +from Deeploy.Targets.Snitch.Layers import SnitchAddLayer, SnitchMulLayer from Deeploy.Targets.Snitch.Parsers import SnitchAddParser, SnitchDivParser, SnitchGEMMParser, SnitchHardSwishParser, \ SnitchMulParser, SnitchRMSNormParser, SnitchRQGEMMParser from Deeploy.Targets.Snitch.Templates import AllocateTemplate, FreeTemplate @@ -69,11 +70,11 @@ 'iNoNorm': iNoNormLayer([iNoNormMapper]), 'iLayerNorm': LayerNormLayer([iLayerNormMapper]), 'RequantizedAdd': AddLayer([RQAddMapper]), - 'Add': AddLayer([AddMapper]), + 'Add': SnitchAddLayer([AddMapper]), 'RMSNorm': RMSNormLayer([RMSNormMapper]), 'HardSwish': HardSwishLayer([HardSwishMapper]), 'Div': DivLayer([DivMapper]), - 'Mul': MulLayer([MulMapper]), + 'Mul': SnitchMulLayer([MulMapper]), 'Reshape': ReshapeLayer([ReshapeMapper]), 'Transpose': TransposeLayer([TransposeMapper]), 'Concat': ConcatLayer([ConcatMapper]), From 1cd2ade5b0b1cf2f67e8d6e65e2e1d6a459ee485 Mon Sep 17 00:00:00 2001 From: Gamze Islamoglu Date: Mon, 17 Aug 2026 10:52:07 +0200 Subject: [PATCH 15/21] Add the Snitch cluster upgrade to the changelog --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d15ac5bafd..e61663620d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ This file contains the changelog for the Deeploy project. The changelog is divid - Add support for Operators for Generic target needed in MAGIA [#193]( https://github.com/pulp-platform/Deeploy/pull/193) - Fix GAP9 L3 Board Tests: readfs Flash Ordering and Duplicate Input Data [#196](https://github.com/pulp-platform/Deeploy/pull/196) - Add SoCDAML Part III: hands-on lab for adding a new int8 operator [#194](https://github.com/pulp-platform/Deeploy/pull/194) +- Upgrade the Snitch Cluster [#TODO](https://github.com/pulp-platform/Deeploy/pull/TODO) ### Added - tests for Regular and DW Conv2D with 3x3 kernel @@ -62,8 +63,23 @@ This file contains the changelog for the Deeploy project. The changelog is divid - Aligned CLI commands across the project - Added @runwangdl as a code owner - Skip emitting duplicate `testInputVector` data for inputs placed in L3 (loaded at runtime from the readfs hex instead), reducing test binary size +- Bump the pinned `snitch_cluster` from `e02cc9e` (April 2024) to `5b2fccd` (September 2025). Deliberately not the latest upstream commit: `7c2bdd9` replaces the hardware-barrier CSR with a symbolic name that the pinned LLVM cannot assemble, and `de5251b` raises the Python floor to 3.12, while Deeploy targets 3.10. +- Adapt the Snitch build system to the current `snitch_cluster` layout (`sw/snRuntime` to `sw/runtime`, `target/snitch_cluster` to `target/sim`, and the removal of `sw/math` and `sw/runtime/{common,rtl,banshee}`). The runtime is now built with the namespaced `sn-runtime` target and `SN_LLVM_BINROOT`. +- Compile the Snitch target library, platform sources and generated `Network.c` as C++. `sw/runtime/src/sync.h` gives `snrt_inter_cluster_sw_barrier` a default argument, which is not valid C and is reached by every translation unit including `snrt.h`. +- Update the Snitch runtime API calls: `snrt_l1alloc`/`snrt_l3alloc` become `snrt_l1_alloc`/`snrt_l3_alloc`, and performance counters are selected by index with configuration separated from starting. +- Only register the banshee simulation target and `BANSHEE_CONFIG` when `BANSHEE_INSTALL_DIR` is set. Recent `snitch_cluster` runtimes no longer support banshee, and previously the Snitch platform could not be configured without it. +- Enable `Xdiv_sqrt` in the cluster configuration Deeploy builds against, since its FP32 kernels emit `fdiv` and `fsqrt`. ### Fixed +- Declare the Snitch kernels used from generated code. `SnitchAdd` and `snitch_nn_add_i8_i8_i8` had no declaration in any header, and `kernel/iSoftmax.h` declared `StnichSoftmax_i8_u8` for a kernel named `SnitchSoftmax_i8_u8` in a header that was never included. All three relied on implicit declarations, which C++ does not have. +- Give the Generic target library headers C linkage, so that calls into it from the C++-compiled Snitch platform resolve against the C definitions. +- Do not emit `sizeof(void)` for Snitch transient buffers, whose size is already expressed in bytes. It relies on a GNU C extension and is invalid in C++. +- Use `memcpy` rather than the iDMA for the L3 input copies in the Snitch test harness. Both sides of that transfer are in L3, and the iDMA has a single AXI master port, so it requires one side to be TCDM. +- Insert `snrt_fpu_fence()` before the cluster barriers emitted for Snitch. Floating-point results were not ordered with respect to the barrier, so the DM core could read a tile out of L1 before the values had landed. +- Fix a pointer type mismatch in `snitch_nn_add_i8_i8_i8`, where scratch variables declared `int` had their addresses assigned to `int8_t` pointers. +- Wait for all outstanding Snitch DMA transfers before the cluster barrier instead of waiting on an individual transaction ID. `completed_id` advances when the iDMA ND midend observes `burst_rsp.last`, which is upstream of the write datapath, so the compute cores could be released onto a tile the DMA had not finished writing. +- Constrain the Snitch GEMM tile sizes in both parallelised dimensions. The kernels stream the output width in groups of 8 through the SSRs with no remainder path, so a tile of `O` below 8 makes `O / unroll` zero and, since the SSR bound is written as `bound - 1`, wraps it to `0xFFFFFFFF`: the streamer never goes idle and the next SSR configuration write never retires. The `M` constraint was only a `PerformanceHint` and so was dropped under memory pressure, which is not merely slower but wrong, since `M / compute_num` truncates and the rows in the remainder are never computed. +- Preserve a scalar second operand for the Snitch `Add` and `Mul` layers. The Generic layers rewrite the shorter operand's shape to the longer one in `computeShapes`, which expresses broadcasting notionally but materialises no data, so the `is_scalar` flag the parser derives from the shape comes out false, the buffer is allocated for the full tensor, and the kernel reads elements that were never written. - Fix Neureka's output-channels subtile size (in ConvTemplate) and Dense/DW/PW tile constraints - in `NetworkContainer._createIOBindings`, set `_live = True` on network input and output buffers so that any buffer aliasing a network I/O tensor is no longer deallocated while the I/O tensor is still in use. - Fix latent bug in `VariableBuffer.has_live_aliases` where `visited` variable was storing buffer names as a set of characters instead of strings. From 88e8c20ef25a7f7cb9ab33ed0a31dfbc722a3a0e Mon Sep 17 00:00:00 2001 From: Gamze Islamoglu Date: Tue, 18 Aug 2026 13:14:35 +0200 Subject: [PATCH 16/21] DeeployTest: drop the untiled Snitch RMSNorm test The RMSNorm bindings always select the SSR kernel but SSRs can only read TCDM. In the untiled flow the operands stay in L2, so the streamer never completes. Fixing this properly means selecting the non-SSR kernel when the operands are not in L1, which the binding cannot see today. --- DeeployTest/test_snitch_config.py | 1 - 1 file changed, 1 deletion(-) diff --git a/DeeployTest/test_snitch_config.py b/DeeployTest/test_snitch_config.py index 18acfa685d..34e8b2e1cb 100644 --- a/DeeployTest/test_snitch_config.py +++ b/DeeployTest/test_snitch_config.py @@ -17,7 +17,6 @@ "Kernels/FP32/MatMul", "Kernels/FP32/Mul/Regular", "Kernels/FP32/Mul/Scalar", - "Kernels/FP32/RMSNorm/single_fused_op", "Kernels/FP32/Softmax/Regular", "Kernels/Integer/Add/Large", "Kernels/Integer/Add/Regular", From 764f4c4f7bb11da3f2af6a7cc038db5d925e3ad5 Mon Sep 17 00:00:00 2001 From: Gamze Islamoglu Date: Tue, 18 Aug 2026 15:02:08 +0200 Subject: [PATCH 17/21] Snitch: add a Verilator simulation target --- DeeployTest/Platforms/Snitch/CMakeLists.txt | 3 ++- DeeployTest/testUtils/core/config.py | 2 +- DeeployTest/test_platforms.py | 7 ++++--- Makefile | 23 +++++++++++++++++++++ cmake/snitch/snitch.cmake | 13 ++++++++++++ 5 files changed, 43 insertions(+), 5 deletions(-) diff --git a/DeeployTest/Platforms/Snitch/CMakeLists.txt b/DeeployTest/Platforms/Snitch/CMakeLists.txt index 45b620e9d7..810dc6626e 100644 --- a/DeeployTest/Platforms/Snitch/CMakeLists.txt +++ b/DeeployTest/Platforms/Snitch/CMakeLists.txt @@ -18,4 +18,5 @@ if(DEFINED ENV{BANSHEE_INSTALL_DIR}) endif() add_gvsoc_emulation(${ProjectId} "pulp.snitch.snitch_cluster_single") add_snitch_cluster_vsim_simulation(${ProjectId}) -add_snitch_cluster_vsim_gui_simulation(${ProjectId}) \ No newline at end of file +add_snitch_cluster_vsim_gui_simulation(${ProjectId}) +add_snitch_cluster_verilator_simulation(${ProjectId}) \ No newline at end of file diff --git a/DeeployTest/testUtils/core/config.py b/DeeployTest/testUtils/core/config.py index e932c23962..75eb327495 100644 --- a/DeeployTest/testUtils/core/config.py +++ b/DeeployTest/testUtils/core/config.py @@ -13,7 +13,7 @@ class DeeployTestConfig: test_name: str test_dir: str platform: str - simulator: Literal['gvsoc', 'banshee', 'qemu', 'vsim', 'vsim.gui', 'host', 'none'] + simulator: Literal['gvsoc', 'banshee', 'qemu', 'vsim', 'vsim.gui', 'verilator', 'host', 'none'] tiling: bool gen_dir: str build_dir: str diff --git a/DeeployTest/test_platforms.py b/DeeployTest/test_platforms.py index e473cdc704..f17ca6d3cc 100644 --- a/DeeployTest/test_platforms.py +++ b/DeeployTest/test_platforms.py @@ -105,9 +105,10 @@ def param_id(param): "model_tests": SOFTHIER_MODEL_TESTS, "default_num_clusters": SOFTHIER_DEFAULT_NUM_CLUSTERS, }, + # GVSoC cannot run the upgraded cluster atm. "snitch": { "platform": "Snitch", - "simulator": "gvsoc", + "simulator": "verilator", "kernel_tests": SNITCH_KERNEL_TESTS, "model_tests": SNITCH_MODEL_TESTS, "default_num_cores": SNITCH_DEFAULT_NUM_CORES, @@ -581,7 +582,7 @@ def test_snitch_tiled_kernels_l2_singlebuffer(test_params, deeploy_test_dir, too config = create_test_config( test_name = test_name, platform = "Snitch", - simulator = "gvsoc", + simulator = PLATFORM_CONFIGS["snitch"]["simulator"], deeploy_test_dir = deeploy_test_dir, toolchain = toolchain, toolchain_dir = toolchain_dir, @@ -611,7 +612,7 @@ def test_snitch_tiled_models_l2_singlebuffer(test_params, deeploy_test_dir, tool config = create_test_config( test_name = test_name, platform = "Snitch", - simulator = "gvsoc", + simulator = PLATFORM_CONFIGS["snitch"]["simulator"], deeploy_test_dir = deeploy_test_dir, toolchain = toolchain, toolchain_dir = toolchain_dir, diff --git a/Makefile b/Makefile index 4153daf9fd..b1b53228d5 100644 --- a/Makefile +++ b/Makefile @@ -465,6 +465,29 @@ ${SNITCH_INSTALL_DIR}: ${TOOLCHAIN_DIR}/snitch_cluster snitch_runtime: ${SNITCH_INSTALL_DIR} +SNITCH_RISCV_ARCH ?= rv32imafd +SNITCH_RTL_RISCV_FLAGS = -target riscv32-unknown-elf \ + -isystem ${LLVM_INSTALL_DIR}/picolibc/riscv/${SNITCH_RISCV_ARCH}/include \ + -L${LLVM_INSTALL_DIR}/picolibc/riscv/${SNITCH_RISCV_ARCH}/lib \ + -L${LLVM_INSTALL_DIR}/lib/clang/15.0.0/lib/baremetal/${SNITCH_RISCV_ARCH} \ + -Wno-unused-command-line-argument + +# Prefix command for verilator, empty when it is on PATH as in the container. +# On IIS machines, use SNITCH_VERILATOR_SEPP=oseda. +SNITCH_VERILATOR_SEPP ?= + +SNITCH_RTL_MAKE_ARGS = SN_LLVM_BINROOT=${LLVM_INSTALL_DIR}/bin \ + SN_RISCV_CC="${LLVM_INSTALL_DIR}/bin/clang ${SNITCH_RTL_RISCV_FLAGS}" \ + SN_RISCV_CXX="${LLVM_INSTALL_DIR}/bin/clang++ ${SNITCH_RTL_RISCV_FLAGS}" \ + SN_VERILATOR_SEPP="${SNITCH_VERILATOR_SEPP}" + +${SNITCH_INSTALL_DIR}/target/sim/build/bin/snitch_cluster.vlt: ${SNITCH_INSTALL_DIR} + cd ${SNITCH_INSTALL_DIR} && \ + make ${SNITCH_RTL_MAKE_ARGS} rtl && \ + make ${SNITCH_RTL_MAKE_ARGS} verilator + +snitch_verilator: ${SNITCH_INSTALL_DIR}/target/sim/build/bin/snitch_cluster.vlt + ${TOOLCHAIN_DIR}/gvsoc: cd ${TOOLCHAIN_DIR} && \ git clone https://github.com/gvsoc/gvsoc.git && \ diff --git a/cmake/snitch/snitch.cmake b/cmake/snitch/snitch.cmake index c069abbae2..083f1475ef 100644 --- a/cmake/snitch/snitch.cmake +++ b/cmake/snitch/snitch.cmake @@ -43,6 +43,19 @@ macro(add_snitch_cluster_vsim_gui_simulation name) ) endmacro() +macro(add_snitch_cluster_verilator_simulation name) + add_custom_target(verilator_${name} + WORKING_DIRECTORY ${SNITCH_HOME}/target/sim/build + DEPENDS ${name} + COMMAND bin/snitch_cluster.vlt + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name} || true + COMMENT "Simulating deeploytest with verilator" + POST_BUILD + USES_TERMINAL + VERBATIM + ) +endmacro() + add_compile_options( -ffast-math $<$:-Wno-c++11-narrowing> From e81147f42ea0d3367653f20e39168eea327781f3 Mon Sep 17 00:00:00 2001 From: Gamze Islamoglu Date: Tue, 18 Aug 2026 15:06:02 +0200 Subject: [PATCH 18/21] Container: provide Verilator for Snitch RTL simulation --- Container/Dockerfile.deeploy | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Container/Dockerfile.deeploy b/Container/Dockerfile.deeploy index 2b717e49ec..3b41de34df 100644 --- a/Container/Dockerfile.deeploy +++ b/Container/Dockerfile.deeploy @@ -2,8 +2,13 @@ # # SPDX-License-Identifier: Apache-2.0 -########## Stage 1: Large image to build toolchains and emulator ########## ARG BASE_IMAGE=ghcr.io/pulp-platform/deeploy-toolchain +ARG VERILATOR_VERSION=v5.034 + +########## Stage 0: Verilator for RTL simulation ########## +FROM verilator/verilator:${VERILATOR_VERSION} AS verilator + +########## Stage 1: Large image to build toolchains and emulator ########## FROM ${BASE_IMAGE} AS toolchain # Intermediate Stage @@ -16,6 +21,8 @@ ENV DEBIAN_FRONTEND=noninteractive ENV TZ=Etc/UTC ENV LANG=C.UTF-8 ENV LC_ALL=C.UTF-8 +COPY --from=verilator /usr/local/bin/verilator /usr/local/bin/verilator_bin /usr/local/bin/ +COPY --from=verilator /usr/local/share/verilator /usr/local/share/verilator ENV PATH="/app/install/bender:${PATH}" ENV DEEPLOY_INSTALL_DIR=/app/install ENV LLVM_INSTALL_DIR=/app/install/llvm @@ -62,6 +69,12 @@ RUN --mount=type=cache,target=/ccache \ make snitch_runtime && \ ccache -s +RUN apt-get update && \ + apt-get install -y --no-install-recommends device-tree-compiler && \ + rm -rf /var/lib/apt/lists/* && \ + make snitch_verilator && \ + rm -rf /app/install/snitch_cluster/target/sim/build/work-vlt + # Remove toolchain to make the container lighter WORKDIR /app RUN rm -rf /app/build @@ -107,6 +120,7 @@ RUN apt-get update && \ python3.10-venv \ python3.10-distutils \ gcc \ + g++ \ zsh && \ curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py && \ python get-pip.py && \ From 361f8874ca1247b678aec56010852d119dfbaacb Mon Sep 17 00:00:00 2001 From: Gamze Islamoglu Date: Tue, 18 Aug 2026 15:32:53 +0200 Subject: [PATCH 19/21] Add the Verilator simulation to the changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e61663620d..3e89ceaedc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ This file contains the changelog for the Deeploy project. The changelog is divid - GAP9 Container Support with ARM64 architecture support - `zsh` and `oh-my-zsh` plugin installation in containers - Shell Format pre-commit hook +- Verilator simulation for Snitch. `make snitch_verilator` builds the cluster model and `add_snitch_cluster_verilator_simulation` registers the CMake target. The Deeploy container carries Verilator v5.034, the version `snitch_cluster` pins in its own container. - Add integer MaxPool1D for Generic platform and RQSConv1D support for PULPOpen, with corresponding kernel tests. - Added GAP9 Platform Support: Deployer, Bindings, Templates, Tiler, DMA (L3Dma/MchanDma), target library, CI workflows - Per-layer microbenchmarking on PULPOpen via `--profileMicrobenchmark`: new `PULPMicrobenchmark` code-transformation pass + `perf_utils.h` helpers report cycles, instructions, stalls and cache misses per layer in `RunNetwork` @@ -69,6 +70,7 @@ This file contains the changelog for the Deeploy project. The changelog is divid - Update the Snitch runtime API calls: `snrt_l1alloc`/`snrt_l3alloc` become `snrt_l1_alloc`/`snrt_l3_alloc`, and performance counters are selected by index with configuration separated from starting. - Only register the banshee simulation target and `BANSHEE_CONFIG` when `BANSHEE_INSTALL_DIR` is set. Recent `snitch_cluster` runtimes no longer support banshee, and previously the Snitch platform could not be configured without it. - Enable `Xdiv_sqrt` in the cluster configuration Deeploy builds against, since its FP32 kernels emit `fdiv` and `fsqrt`. +- Run the Snitch tests on Verilator rather than GVSoC. GVSoC's model of the cluster peripherals predates the bumped hardware: the peripheral region moved from `0x10020000` to `0x10021000`, four `SCRATCH` registers were added ahead of the CLINT and shift every subsequent register by `0x20`, and the registers are now 64-bit where the model still generates 32-bit ones. ### Fixed - Declare the Snitch kernels used from generated code. `SnitchAdd` and `snitch_nn_add_i8_i8_i8` had no declaration in any header, and `kernel/iSoftmax.h` declared `StnichSoftmax_i8_u8` for a kernel named `SnitchSoftmax_i8_u8` in a header that was never included. All three relied on implicit declarations, which C++ does not have. From 123f0a0f31206edfe31c2413d169f6f9a4400b63 Mon Sep 17 00:00:00 2001 From: Gamze Islamoglu Date: Wed, 19 Aug 2026 09:52:02 +0200 Subject: [PATCH 20/21] Snitch: run the remaining Snitch tests on Verilator Moving the Snitch platform to Verilator only changed PLATFORM_CONFIGS, which the tiled tests, the DMA test and the standalone runners do not read. The DMA test is marked deeploy_internal and so kept GVSoC in CI, where it cannot run the upgraded cluster. --- DeeployTest/deeployRunner_snitch.py | 2 +- DeeployTest/deeployRunner_tiled_snitch.py | 2 +- DeeployTest/testUtils/deeployRunner.py | 2 +- DeeployTest/testUtils/testRunner.py | 7 ++++--- DeeployTest/test_dmas.py | 2 +- DeeployTest/test_snitch_config.py | 2 +- DeeployTest/test_snitch_tiled_config.py | 2 +- 7 files changed, 10 insertions(+), 9 deletions(-) diff --git a/DeeployTest/deeployRunner_snitch.py b/DeeployTest/deeployRunner_snitch.py index aa97933319..c08c541ade 100644 --- a/DeeployTest/deeployRunner_snitch.py +++ b/DeeployTest/deeployRunner_snitch.py @@ -19,6 +19,6 @@ def setup_parser(parser): sys.exit( main(default_platform = "Snitch", - default_simulator = "gvsoc", + default_simulator = "verilator", tiling_enabled = False, parser_setup_callback = setup_parser)) diff --git a/DeeployTest/deeployRunner_tiled_snitch.py b/DeeployTest/deeployRunner_tiled_snitch.py index d6e5ffd196..e26136351a 100644 --- a/DeeployTest/deeployRunner_tiled_snitch.py +++ b/DeeployTest/deeployRunner_tiled_snitch.py @@ -19,6 +19,6 @@ def setup_parser(parser): sys.exit( main(default_platform = "Snitch", - default_simulator = "gvsoc", + default_simulator = "verilator", tiling_enabled = True, parser_setup_callback = setup_parser)) diff --git a/DeeployTest/testUtils/deeployRunner.py b/DeeployTest/testUtils/deeployRunner.py index 00ec496ed9..0719abb288 100644 --- a/DeeployTest/testUtils/deeployRunner.py +++ b/DeeployTest/testUtils/deeployRunner.py @@ -406,7 +406,7 @@ def main(default_platform: Optional[str] = None, "MemPool": "banshee", "Siracusa": "gvsoc", "Siracusa_w_neureka": "gvsoc", - "Snitch": "gvsoc", + "Snitch": "verilator", "Chimera": "gvsoc", "SoftHier": "gvsoc", } diff --git a/DeeployTest/testUtils/testRunner.py b/DeeployTest/testUtils/testRunner.py index fdd4c9bf17..f7e2ff89ec 100644 --- a/DeeployTest/testUtils/testRunner.py +++ b/DeeployTest/testUtils/testRunner.py @@ -320,15 +320,16 @@ class TestRunner(): def __init__(self, platform: str, - simulator: Literal['gvsoc', 'banshee', 'qemu', 'vsim', 'vsim.gui', 'host', 'board', 'none'], + simulator: Literal['gvsoc', 'banshee', 'qemu', 'vsim', 'vsim.gui', 'verilator', 'host', 'board', + 'none'], tiling: bool, argument_parser: TestRunnerArgumentParser, gen_args: str = "", cmake_args: str = ""): - if simulator not in ['gvsoc', 'banshee', 'qemu', 'vsim', 'vsim.gui', 'host', 'board', 'none']: + if simulator not in ['gvsoc', 'banshee', 'qemu', 'vsim', 'vsim.gui', 'verilator', 'host', 'board', 'none']: raise ValueError( - f"Invalid emulator {simulator} (valid options are 'gvsoc', 'banshee', 'qemu', 'vsim', 'vsim.gui', 'host', 'board', 'none')!" + f"Invalid emulator {simulator} (valid options are 'gvsoc', 'banshee', 'qemu', 'vsim', 'vsim.gui', 'verilator', 'host', 'board', 'none')!" ) if tiling is not argument_parser.tiling_arguments: diff --git a/DeeployTest/test_dmas.py b/DeeployTest/test_dmas.py index 84ee8a6d1a..9d1caad769 100644 --- a/DeeployTest/test_dmas.py +++ b/DeeployTest/test_dmas.py @@ -353,7 +353,7 @@ def test_snitch_dma(test_shape, doublebuffer, deeploy_test_dir, toolchain, toolc test_name = test_name_clean, test_dir = gen_dir, platform = platform, - simulator = 'gvsoc', + simulator = 'verilator', tiling = True, gen_dir = gen_dir, build_dir = build_dir, diff --git a/DeeployTest/test_snitch_config.py b/DeeployTest/test_snitch_config.py index 34e8b2e1cb..d61d1e7118 100644 --- a/DeeployTest/test_snitch_config.py +++ b/DeeployTest/test_snitch_config.py @@ -3,7 +3,7 @@ # SPDX-License-Identifier: Apache-2.0 """Test configuration for Snitch platform.""" -# Snitch platform supports gvsoc, banshee, vsim simulators +# Snitch platform supports verilator, banshee, vsim simulators # Default configuration: 9 cores DEFAULT_NUM_CORES = 9 diff --git a/DeeployTest/test_snitch_tiled_config.py b/DeeployTest/test_snitch_tiled_config.py index 2ca0d7e7dd..fe26bd7a0f 100644 --- a/DeeployTest/test_snitch_tiled_config.py +++ b/DeeployTest/test_snitch_tiled_config.py @@ -3,7 +3,7 @@ # SPDX-License-Identifier: Apache-2.0 """Test configuration for Snitch platform (tiled).""" -# Snitch tiled platform supports gvsoc, banshee, vsim simulators +# Snitch tiled platform supports verilator, banshee, vsim simulators # Default configuration: 9 cores, L2 default memory level DEFAULT_NUM_CORES = 9 From a7bdd18119ab89a5b3886a237daa4e606d89d3df Mon Sep 17 00:00:00 2001 From: Gamze Islamoglu Date: Wed, 19 Aug 2026 09:54:15 +0200 Subject: [PATCH 21/21] CI: temporarily build against the branch container image This branch bumps snitch_cluster, whose layout changed, and moves the Snitch tests to Verilator, so it cannot build or run against the published deeploy:devel image. Point the default at an image built from this branch so the checks are meaningful during review. Revert before merge, once the published image has been rebuilt. --- .github/workflows/_select-env.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/_select-env.yml b/.github/workflows/_select-env.yml index 1085c7eaa1..7b1cc33f72 100644 --- a/.github/workflows/_select-env.yml +++ b/.github/workflows/_select-env.yml @@ -34,7 +34,11 @@ jobs: elif [[ "${{ github.ref_name }}" == "main" ]]; then IMAGE="ghcr.io/pulp-platform/deeploy:main" else - IMAGE="ghcr.io/pulp-platform/deeploy:devel" + # REVERT BEFORE MERGE: this branch bumps snitch_cluster and adds + # Verilator, so it cannot build against the published devel image. + # Restore ghcr.io/pulp-platform/deeploy:devel once the image has + # been rebuilt from this branch. + IMAGE="ghcr.io/gamzeisl/deeploy:snitch-upgrade" fi echo "image=${IMAGE}" >> "$GITHUB_OUTPUT"