-
Notifications
You must be signed in to change notification settings - Fork 48
DRAFT: Upgrade the Snitch Cluster version and add Verilator Model #205
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: devel
Are you sure you want to change the base?
Changes from all commits
77ad65e
ef897b7
c053f8a
1f9ed47
714a7e0
56fee8d
a99a6da
20924be
79177f2
fbdc171
a34a1d4
a9618dc
1dbc036
ce83421
1cd2ade
88e8c20
764f4c4
e81147f
361f887
123f0a0
a7bdd18
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Replace the placeholder pull-request link. Line 25 uses 🤖 Prompt for AI Agents |
||
|
|
||
| ### Added | ||
| - tests for Regular and DW Conv2D with 3x3 kernel | ||
|
|
@@ -37,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` | ||
|
|
@@ -62,8 +64,24 @@ 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`. | ||
| - 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. | ||
| - 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. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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) | ||
|
Comment on lines
+98
to
+105
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win Reject output widths below eight or use a remainder kernel. Line 104 skips the constraint when Apply the divisibility constraint unconditionally, or dispatch these shapes to a kernel with a remainder path. 🤖 Prompt for AI Agents |
||
|
|
||
| return tilerModel | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Restore the published project image before merging.
If this override remains after merge, every non-tag, non-main ref will use
ghcr.io/gamzeisl/deeploy:snitch-upgradeinstead of the published project image. Replace it withghcr.io/pulp-platform/deeploy:develand remove the temporary override comments.🤖 Prompt for AI Agents