Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
107 commits
Select commit Hold shift + click to select a range
ba01027
add placeholdeer code for spatz platform
Bumblebee00 Mar 2, 2026
9fc201d
code generation with generic c code
Bumblebee00 Mar 2, 2026
8dd7d32
modified spatz c code to use proper memory allocation and copying fun…
Bumblebee00 Mar 12, 2026
f04f3c0
tmp commit to send to badie103
Bumblebee00 Mar 16, 2026
9e26bb0
modified Makefile and cmakefiles to build and use spatz runtime. ugly…
Mar 23, 2026
9136b05
vsim simulator runnable by deeployRunner
Mar 23, 2026
f7456f9
Removed reverence to conda environment and added commands to create v…
Mar 23, 2026
6e565f4
typo
Mar 27, 2026
0bc60d4
double gvsoc temporaney configuration
Mar 27, 2026
c780baa
reunited gvsoc build for spatz and other platforms
Mar 27, 2026
8c6bfdd
forgot comment
Mar 27, 2026
cc1d7b6
forgot comment
Mar 27, 2026
49f2ce8
added topk generic binding (hardcoded k=10)
Mar 29, 2026
e1fa310
added matmul softmax and topk generic bindings to spatz
Mar 29, 2026
4788bac
switched default simulator to gvsoc bc is faster
Mar 29, 2026
204d522
added topk test network
Mar 29, 2026
bff3402
added sparse attention test network
Mar 29, 2026
5e12f08
added topk binding to generic platform
Mar 29, 2026
3167dda
improved generic gather node to support more than one index
Mar 29, 2026
68b5e7d
added gather binding for spatz
Mar 29, 2026
621cc87
now for any k inot the graph
Apr 1, 2026
4780de4
added big attention
Apr 13, 2026
24b4031
first draft of tiling (not working)
Apr 13, 2026
dbe43e9
minimalloc fix
Apr 13, 2026
ba347e1
fixed makefile adding missing things
Apr 13, 2026
9d0b187
added yaml for my conda environment
Apr 14, 2026
a1f7cb7
modified bindings to use snrt_dma_wait_all function
Apr 14, 2026
b872a2e
added different dimensions of FP32/MatMul
Apr 16, 2026
bb818db
fixed simulation staling issue
Apr 16, 2026
94d6357
fixed memory levels
Apr 16, 2026
b9bb792
added cycles indication
Apr 21, 2026
0c35b57
added fp32 matmul kernel that uses vector instructions
Apr 21, 2026
39d45a8
went back to not using snrt_l3alloc because its not working
Apr 21, 2026
2c0d624
[tiling] enable tiling extension on Spatz
Apr 23, 2026
cdfa5e0
[template] Add proper allocation template and fix DMA template
Apr 23, 2026
cea39d7
[sw] use memcpy instead of DMA for DRAM buffer init
Apr 23, 2026
df652e8
changed commit hash to include new version of spatz that has snrt_l3a…
Apr 23, 2026
27c6ccc
removed redundant memcpy
Apr 23, 2026
bddacf8
added gather only test
Apr 23, 2026
878a3fd
improved gather template
Apr 23, 2026
eb0c971
added nice dimensions of Matmul
Apr 27, 2026
0e2a78a
improved main
Apr 27, 2026
dec2ba2
spatz matmulfunction now splits work between cores
Apr 27, 2026
8bca32e
fixed name of input in graph
Apr 27, 2026
366af52
removed unnecessary print
Apr 27, 2026
9a7b3aa
gather tiling
Apr 27, 2026
585e49f
topk tiling
Apr 27, 2026
fe70c52
updated tiling to work with constant buffers
Apr 27, 2026
4871b1b
updated tiling to work with nodes with >1 output
Apr 27, 2026
ca2112c
fixed topk template
Apr 28, 2026
4ea8c14
added softmax tiled (not working)
Apr 30, 2026
7523a2b
added another softmax test
Apr 30, 2026
76d7cef
added softmax function with custom exp and inv functions
May 1, 2026
585321c
detect quiet nan float output
May 8, 2026
9e6c79c
use non vector for when one dim is one
May 8, 2026
80e43a4
fixed matmul kernel
May 13, 2026
6698acd
divide on p to have double performance
May 18, 2026
06e391e
added benchmarking code transformation pass
May 20, 2026
82eeecd
include necessary for benchmark code
May 20, 2026
eb75093
improved code transformation passes for allocation of all necessary b…
May 20, 2026
3dfaf23
split gather workload on two cores
May 20, 2026
d352d87
cleaner matmul template
May 22, 2026
c322c7a
initial version of dynamic dma code
May 22, 2026
6ed9ede
fixed sizes of gather template
May 22, 2026
e1aee4f
fixed sizes of gather template
May 22, 2026
61a4220
fixed gather tile contraint (removed contraint on tile fo data_in)
May 26, 2026
82593e7
removed unnecessary prints
May 26, 2026
9ae2f48
modified matmul variable to be bigger integers
May 26, 2026
43fafe8
removed very long memcopy
May 26, 2026
3f4467a
added hw barrier
May 26, 2026
2036eea
added new tests
May 26, 2026
1973309
added another main.c for spatz
May 26, 2026
17ce78a
improved hack to avoid memcpy
May 26, 2026
ffaaa34
new faster expf function, and softmax smartly devided between cores
May 27, 2026
d20847c
minor fixes
May 27, 2026
9dbea0a
added new template for topk that uses min heap
May 27, 2026
e303796
smal fix
May 27, 2026
18621e9
added topk function
May 28, 2026
3cb8165
added new test networks
Jun 2, 2026
bbd9306
work on matmul kernel
Jun 10, 2026
c6a9828
[fix] enforce 8-byte alignment for memory allocation
Jun 12, 2026
267ea2d
[fix] 8-byte aligned tiling constraints
Jun 12, 2026
0732873
cleaned topk template
Jun 12, 2026
07ae93f
added matmul columns reduction function
Jun 12, 2026
61d5f08
better condition for column matmul
Jun 12, 2026
e40dd50
how to do double buffering on spatz
Jun 17, 2026
c7c37ea
investigated double buffering
Jun 22, 2026
8a41c8d
[cleanup] removed deadcode, cleaned allocation template and bindings
Jun 12, 2026
16a97ef
[cleanup] revert debug print
Jun 12, 2026
f44e89a
[cleanup] only keep necessary changes in tiling extension
Jun 12, 2026
e36e505
[cleanup] fix lint issues (partially)
Jun 12, 2026
d149968
removed unnecessary tests
Jun 22, 2026
179a7c2
modified tests
Jun 22, 2026
bc2b8bd
removed unised comments
Jun 22, 2026
5ff85c6
necessary spatz makefile modification
Jun 22, 2026
68875c5
unecessary comment
Jun 22, 2026
520518b
missing slash in makefile
Jun 22, 2026
fd83c34
removed old gather templates
Jun 22, 2026
096571e
removed comments
Jun 22, 2026
2ee09ac
removed unnecessary matmuls
Jun 22, 2026
f8c6a6f
rmoved unnecessary softmax
Jun 22, 2026
d06e15a
renamed .yml
Jun 22, 2026
903eaeb
tmp commit
Jul 2, 2026
e917a03
fix merge error
Bumblebee00 Jul 2, 2026
ce4526f
fix merge error 2
Bumblebee00 Jul 2, 2026
ae6d3b9
updated readme and changelog
Bumblebee00 Jul 2, 2026
b76080b
added CI for Spatz
Bumblebee00 Jul 2, 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
42 changes: 42 additions & 0 deletions .github/workflows/_runner-spatz-tiled.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# SPDX-FileCopyrightText: 2024 ETH Zurich and University of Bologna
#
# SPDX-License-Identifier: Apache-2.0

---
name: _runner-spatz-tiled

"on":
workflow_call:
inputs:
runner:
required: true
type: string
docker-image:
required: true
type: string
pytest-marker:
required: true
type: string

jobs:
test-runner-spatz-tiled:
runs-on: ${{ inputs.runner }}
container:
image: ${{ inputs.docker-image }}
steps:
- name: Mark workspace as safe
run: git config --global --add safe.directory '*'
- name: Checkout Repo
uses: actions/checkout@v4
with:
submodules: recursive
- name: Build Deeploy
shell: bash
run: pip install -e .
- name: Run Test
run: |
cd DeeployTest
mkdir -p /app/.ccache
export CCACHE_DIR=/app/.ccache
pytest test_platforms.py -v -n 4 -m "spatz_tiled and ${{ inputs.pytest-marker }}"
shell: bash
38 changes: 38 additions & 0 deletions .github/workflows/ci-platform-spatz-tiled.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# SPDX-FileCopyrightText: 2025 ETH Zurich and University of Bologna
#
# SPDX-License-Identifier: Apache-2.0

---
name: CI • Spatz (Tiled)

"on":
push:
branches:
- "**"
tags:
- "v*.*.*"
pull_request:
workflow_dispatch:
inputs:
docker_image_deeploy:
description: "Deeploy Image to use"
required: false
default: "ghcr.io/pulp-platform/deeploy:devel"

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
select-env:
uses: ./.github/workflows/_select-env.yml
with:
docker_image_deeploy: ${{ inputs.docker_image_deeploy }}

spatz-kernels-tiled-singlebuffer-L2:
needs: select-env
uses: ./.github/workflows/_runner-spatz-tiled-sequential.yml
with:
runner: ${{ needs.select-env.outputs.runner }}
docker-image: ${{ needs.select-env.outputs.image }}
pytest-marker: "kernels"
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ This file contains the changelog for the Deeploy project. The changelog is divid


### List of Pull Requests
- Spatz integration [#168](https://github.com/pulp-platform/Deeploy/pull/168)
- XDNA2 Platform Support [#179](https://github.com/pulp-platform/Deeploy/pull/179)
- Add Microbenchmarking Infrastructure and CI Using GVSoC CSR [#162](https://github.com/pulp-platform/Deeploy/pull/162)
- Fix CI Cache Generation [#176](https://github.com/pulp-platform/Deeploy/pull/176)
Expand All @@ -21,6 +22,7 @@ This file contains the changelog for the Deeploy project. The changelog is divid
- Fix GAP9 L3 Board Tests: readfs Flash Ordering and Duplicate Input Data [#196](https://github.com/pulp-platform/Deeploy/pull/196)

### Added
- Spatz platform integration with runtime, DMA, tiling, and fp32 MatMul/Softmax/TopK/Gather support, plus updated test runners and Makefile flow
- XDNA2 (AIE2p) platform beta: first MLIR backend for Deeploy, targeting AMD/Xilinx NPU2 with a single BF16 Add kernel
- `MLIRNodeTemplate` and `MLIRCodeTransformation` base classes for MLIR-emitting backends
- Auto-tiling with L1 memory constraints for XDNA2
Expand Down
36 changes: 34 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ if(TOOLCHAIN STREQUAL GCC)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
endif()

set(platform MemPool CACHE STRING "Platform (MemPool, SoftHier, QEMU, Siracusa, Siracusa_w_neureka, PULP-Open, GAP9, Generic, Snitch)")
set_property(CACHE platform PROPERTY STRINGS MemPool SoftHier QEMU Siracusa Siracusa_w_neureka PULP-Open GAP9 Generic Snitch)
set(platform MemPool CACHE STRING "Platform (MemPool, SoftHier, QEMU, Siracusa, Siracusa_w_neureka, PULP-Open, GAP9, Generic, Snitch, Spatz)")
set_property(CACHE platform PROPERTY STRINGS MemPool SoftHier QEMU Siracusa Siracusa_w_neureka PULP-Open GAP9 Generic Snitch Spatz)

if(platform STREQUAL MemPool)
message(STATUS "Building for platform 'MemPool'")
Expand Down Expand Up @@ -57,6 +57,8 @@ elseif(platform STREQUAL Chimera)
message(STATUS "Building for platform 'Chimera'")
elseif(platform STREQUAL XDNA2)
message(STATUS "Building for platform 'XDNA2'")
elseif(platform STREQUAL Spatz)
message(STATUS "Building for platform 'Spatz'")
else()
message(FATAL_ERROR "Invalid platform '${platform}' specified!")
endif()
Expand Down Expand Up @@ -327,4 +329,34 @@ if(platform STREQUAL XDNA2)
endif()



if(platform STREQUAL Spatz)

if(NOT DEFINED ENV{SPATZ_HOME})
message(FATAL_ERROR "Environment variable SPATZ_HOME not set.")
endif()

set(SPATZ_HOME $ENV{SPATZ_HOME})

set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_LIST_DIR}/cmake/spatz/toolchain_llvm.cmake)

include(${CMAKE_CURRENT_LIST_DIR}/cmake/spatz/spatz.cmake)

project(deeploy LANGUAGES C ASM)

message(STATUS "============================= ${platform} Configuration ============================")
message(STATUS "[cMake ] ISA = " ${ISA})
message(STATUS "================================================================================")
message(STATUS "")

add_subdirectory(TargetLibraries/Generic)
add_subdirectory(TargetLibraries/Spatz)
target_include_directories(deeployspatz PUBLIC TargetLibraries/Generic/inc)

add_subdirectory(DeeployTest)
target_link_libraries(deeploylib INTERFACE deeploybasic deeployspatz)

endif()


print_simulation_config()
14 changes: 12 additions & 2 deletions Deeploy/Targets/Generic/Bindings.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@
FloatSwishTemplate, GatherTemplate, GemmTemplate, IntegerDivTemplate, ITAMaxTemplate, ITAPartialMaxTemplate, \
MatMulTemplate, MaxPoolTemplate, MulTemplate, PadTemplate, QuantTemplate, ReduceMeanTemplate, ReduceSumTemplate, \
RequantShiftTemplate, ReshapeTemplate, RQIntegerDivTemplate, RQSiGELUTemplate, SliceTemplate, SubTemplate, \
TransposeTemplate, iGELUTemplate, iLayernormTemplate, iRMSNormTemplate, iSoftmaxTemplate
TransposeTemplate, iGELUTemplate, iLayernormTemplate, iRMSNormTemplate, iSoftmaxTemplate, TopKTemplate
from Deeploy.Targets.Generic.TypeCheckers import AddChecker, BatchNormChecker, ConcatChecker, ConvChecker, \
DebugPrintChecker, DequantChecker, DivChecker, DummyChecker, GatherChecker, GELUChecker, GEMMChecker, \
LayerNormChecker, MatMulChecker, MaxPoolChecker, MulChecker, PadChecker, QuantChecker, ReduceMeanChecker, \
ReduceSumChecker, ReluChecker, RequantShiftChecker, ReshapeChecker, RQIntegerDivChecker, SliceChecker, \
SoftmaxChecker, TransposeChecker
SoftmaxChecker, TransposeChecker, TopKChecker

BasicTransformer = CodeTransformation([ArgumentStructGeneration(), MemoryManagementGeneration(), FutureGeneration()])

Expand Down Expand Up @@ -420,3 +420,13 @@
NodeBinding(DummyChecker([PointerClass(float32_t)], [PointerClass(float32_t)]),
FloatGlobalMaxPoolTemplate.referenceTemplate, BasicTransformer)
]
BasicTopKBindings = [
NodeBinding(
TopKChecker(
[PointerClass(float32_t), PointerClass(int8_t)], # inputs
[PointerClass(float32_t), PointerClass(int8_t)] # outputs
),
TopKTemplate.referenceTemplate,
BasicTransformer,
)
]
5 changes: 5 additions & 0 deletions Deeploy/Targets/Generic/Layers.py
Original file line number Diff line number Diff line change
Expand Up @@ -792,3 +792,8 @@ def computeOps(self):
opRep = self.mapper.parser.operatorRepresentation
# (spatial_size - 1) comparisons per output channel
return int(opRep['batch_size'] * opRep['num_channels'] * (opRep['spatial_size'] - 1))

class TopKLayer(ONNXLayer):

def __init__(self, maps: List[NodeMapper]):
super().__init__(maps)
42 changes: 37 additions & 5 deletions Deeploy/Targets/Generic/Parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1002,7 +1002,7 @@ def parseNode(self, node: gs.Node) -> (bool):
return False

indices_shape = node.inputs[1].shape
assert np.prod(indices_shape) == 1, f"Only indices of size 1 supported. Got indices of shape {indices_shape}"
self.operatorRepresentation['num_indices'] = int(np.prod(indices_shape))

self.operatorRepresentation['axis'] = node.attrs['axis'] if 'axis' in node.attrs else 0
return True
Expand All @@ -1022,10 +1022,17 @@ def parseNodeCtxt(self,

axis = self.operatorRepresentation['axis']
shape = ctxt.lookup(node.inputs[0].name).shape
self.operatorRepresentation['batch'] = np.prod(shape[:axis])
self.operatorRepresentation['batch_length'] = np.prod(shape[axis:])
self.operatorRepresentation['axis_length'] = np.prod(shape[axis + 1:])
self.operatorRepresentation['index'] = int(node.inputs[1].values.item())
self.operatorRepresentation['batch'] = int(np.prod(shape[:axis])) if axis > 0 else 1
self.operatorRepresentation['batch_length'] = int(np.prod(shape[axis:]))
self.operatorRepresentation['axis_length'] = int(np.prod(shape[axis + 1:])) if axis + 1 < len(shape) else 1

if self.operatorRepresentation['num_indices'] == 1:
try:
self.operatorRepresentation['index'] = int(node.inputs[1].values.item())
except AttributeError:
self.operatorRepresentation['index'] = f"{self.operatorRepresentation['indices']}[0]"
else:
self.operatorRepresentation['index'] = 0 # in this case is not used but is needed for mako template

return ctxt, True

Expand Down Expand Up @@ -3124,3 +3131,28 @@ class GlobalMaxPoolParser(GlobalPoolParser):

def parseNode(self, node: gs.Node) -> bool:
return super().parseNode(node) and node.op == 'GlobalMaxPool'

# TopKParser: selects the largest k elements from a vector
class TopKParser(NodeParser):
def __init__(self):
super().__init__()

def parseNode(self, node: gs.Node) -> bool:
return len(node.inputs)==2 and len(node.outputs)==2 and node.op=='TopK'

def parseNodeCtxt(self,
ctxt: NetworkContext,
node: gs.Node,
channels_first: bool = True) -> Tuple[NetworkContext, bool]:
data_in = ctxt.lookup(node.inputs[0].name)
k_in = ctxt.lookup(node.inputs[1].name)
values_out = ctxt.lookup(node.outputs[0].name)
indices_out = ctxt.lookup(node.outputs[1].name)

self.operatorRepresentation['data_in'] = data_in.name
self.operatorRepresentation['data_in_size'] = int(np.prod(data_in.shape))
self.operatorRepresentation['k_value'] = int(k_in.values[0])
self.operatorRepresentation['values_out'] = values_out.name
Comment on lines +3154 to +3155

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Validate k as a constant scalar and bound it to input size.

Line 2916 assumes k_in.values[0] exists and is valid. If k is non-constant, scalar-shaped differently, or k > data_in_size, the generated TopK loop indexes beyond the local pairs buffer.

Suggested fix
-        self.operatorRepresentation['k_value'] = int(k_in.values[0])
+        if not hasattr(k_in, "values"):
+            return ctxt, False
+        k_value = int(np.asarray(k_in.values).item())
+        if k_value <= 0 or k_value > self.operatorRepresentation['data_in_size']:
+            return ctxt, False
+        self.operatorRepresentation['k_value'] = k_value
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
self.operatorRepresentation['k_value'] = int(k_in.values[0])
self.operatorRepresentation['values_out'] = values_out.name
if not hasattr(k_in, "values"):
return ctxt, False
k_value = int(np.asarray(k_in.values).item())
if k_value <= 0 or k_value > self.operatorRepresentation['data_in_size']:
return ctxt, False
self.operatorRepresentation['k_value'] = k_value
self.operatorRepresentation['values_out'] = values_out.name
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Deeploy/Targets/Generic/Parsers.py` around lines 2916 - 2917, Before
assigning the k value at line 2916 in the TopK operator representation setup,
add validation to ensure that k_in is a constant scalar and that its value does
not exceed the size of the input data. Check that k_in is constant-valued,
verify it has scalar shape, extract the k value safely, and validate that the k
value is within bounds relative to data_in_size to prevent buffer overruns in
the TopK loop. If validation fails, raise an appropriate error describing which
constraint was violated.

self.operatorRepresentation['indices_out'] = indices_out.name

return ctxt, True
8 changes: 5 additions & 3 deletions Deeploy/Targets/Generic/Platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@
BasicPowBindings, BasicQuantBindings, BasicReduceMeanBindings, BasicReduceSumBindings, BasicReluBinding, \
BasicReshapeBindings, BasicRQIntegerDivBinding, BasicRQSBindings, BasicRQSGELUBinding, BasicSigmoidBindings, \
BasicSliceBindings, BasicSoftmaxBindings, BasicSqrtBindings, BasicSubBindings, BasicSwishBindings, \
BasicTransposeBindings, DummyBinding
BasicTransposeBindings, DummyBinding, BasicTopKBindings
from Deeploy.Targets.Generic.Layers import AddLayer, AveragePoolLayer, BatchNormalizationLayer, CeilLayer, ClipLayer, \
ConcatLayer, ConvLayer, ConvTransposeLayer, DebugPrintLayer, DequantLayer, DivLayer, ExpLayer, FloorLayer, \
GatherLayer, GELULayer, GEMMLayer, GlobalAveragePoolLayer, GlobalMaxPoolLayer, GroupNormLayer, InstanceNormLayer, \
ITAMaxLayer, LayerNormLayer, MatMulLayer, MaxPoolLayer, MulLayer, PadLayer, PowLayer, QuantLayer, ReduceMeanLayer, \
ReduceSumLayer, ReluLayer, RequantShiftLayer, ReshapeLayer, RQIntegerDivLayer, RQSiGELULayer, SigmoidLayer, \
SliceLayer, SoftmaxLayer, SqrtLayer, SubLayer, SwishLayer, TransposeLayer
SliceLayer, SoftmaxLayer, SqrtLayer, SubLayer, SwishLayer, TransposeLayer, TopKLayer
from Deeploy.Targets.Generic.Parsers import AddParser, AveragePool1DParser, AveragePool2DParser, BatchNormParser, \
CeilParser, ClipParser, ConcatParser, ConvTranspose1DParser, DebugParser, DequantParser, DivParser, DummyParser, \
ExpParser, FlattenParser, FloorParser, GatherParser, GELUParser, GenericConv1DParser, GenericConv2DParser, \
Expand All @@ -32,7 +32,7 @@
ITAMaxParser, ITAPartialMaxParser, LayerNormParser, MatMulParser, MaxPool1DParser, MulParser, Pad1DParser, \
Pad2DParser, PowParser, QuantParser, ReduceMeanParser, ReduceSumParser, ReluParser, RequantShiftParser, \
ReshapeParser, RQIntegerDivParser, RQSiGELUParser, SigmoidParser, SliceParser, SoftmaxParser, SqrtParser, \
SubParser, SwishParser, TransposeParser, UnsqueezeParser, iLayerNormParser, iSoftmaxParser
SubParser, SwishParser, TransposeParser, UnsqueezeParser, iLayerNormParser, iSoftmaxParser, TopKParser
from Deeploy.Targets.Generic.Templates import AllocateTemplate, FreeTemplate
from Deeploy.Targets.Generic.TopologyOptimizationPasses.Passes import DequantPatternPass, ExtractPaddingFromConvPass, \
ExtractPaddingFromPoolPass, MatMulAddMergePass, MergeConstAddAndRequantPass, QuantPatternPass, \
Expand Down Expand Up @@ -74,6 +74,7 @@
SoftmaxMapper = NodeMapper(SoftmaxParser(), BasicSoftmaxBindings)
iSoftmaxMapper = NodeMapper(iSoftmaxParser(), BasicSoftmaxBindings)
TransposeMapper = NodeMapper(TransposeParser(), BasicTransposeBindings)
TopKMapper = NodeMapper(TopKParser(), BasicTopKBindings)
UnsqueezeMapper = NodeMapper(UnsqueezeParser(), BasicReshapeBindings)
QuantMapper = NodeMapper(QuantParser(), BasicQuantBindings)
DequantMapper = NodeMapper(DequantParser(), BasicDequantBindings)
Expand Down Expand Up @@ -135,6 +136,7 @@
'RQIntegerDiv': RQIntegerDivLayer([RQIntegerDivMapper]),
'Squeeze': ReshapeLayer([UnsqueezeMapper]),
'Transpose': TransposeLayer([TransposeMapper]),
'TopK': TopKLayer([TopKMapper]),
'Unsqueeze': ReshapeLayer([UnsqueezeMapper]),
'Slice': SliceLayer([SliceMapper]),
'Quant': QuantLayer([QuantMapper]),
Expand Down
10 changes: 10 additions & 0 deletions Deeploy/Targets/Generic/Templates/GatherTemplate.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,18 @@
width = int(data_in_type.referencedType.typeWidth/8)
%>
BEGIN_SINGLE_CORE
% if num_indices == 1:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we have this case here? what is the meaning of num_indices?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is to not generate a loop with one iteration:

    for (uint32_t j=0; j<1; ++j) {

is cleaner c code

for (uint32_t i=0; i<${batch}; ++i) {
memcpy(${data_out} + i * ${axis_length}, ${data_in} + i * ${batch_length} + ${index} * ${axis_length}, ${axis_length} * ${width});
}
% else:
for (uint32_t i=0; i<${batch}; ++i) {
for (uint32_t j=0; j<${num_indices}; ++j) {
memcpy(${data_out} + i * (${num_indices} * ${axis_length}) + j * ${axis_length},
${data_in} + i * ${batch_length} + ${indices}[j] * ${axis_length},
${axis_length} * ${width});
}
}
% endif
END_SINGLE_CORE
""")
40 changes: 40 additions & 0 deletions Deeploy/Targets/Generic/Templates/TopKTemplate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from typing import Dict, List, Tuple

from Deeploy.DeeployTypes import NetworkContext, NodeTemplate, OperatorRepresentation


referenceTemplate = NodeTemplate("""
// TopK (Name: ${nodeName}, Op: ${nodeOp})
BEGIN_SINGLE_CORE
// Find the top ${k_value} values and their indices
// Assumes 1D input for simplicity
typedef struct {
${data_in_type.referencedType.typeName} value;
uint32_t index;
} topk_pair_t;

topk_pair_t pairs[${data_in_size}];
for (uint32_t i = 0; i < ${data_in_size}; ++i) {
pairs[i].value = ((${data_in_type.referencedType.typeName}*)${data_in})[i];
pairs[i].index = i;
}
// Simple selection sort for top-k
for (uint32_t i = 0; i < ${k_value}; ++i) {
uint32_t max_idx = i;
for (uint32_t j = i + 1; j < ${data_in_size}; ++j) {
if (pairs[j].value > pairs[max_idx].value) {
max_idx = j;
}
}
// Swap
if (max_idx != i) {
topk_pair_t tmp = pairs[i];
pairs[i] = pairs[max_idx];
pairs[max_idx] = tmp;
}
// Write output
((${values_out_type.referencedType.typeName}*)${values_out})[i] = pairs[i].value;
((${indices_out_type.referencedType.typeName}*)${indices_out})[i] = pairs[i].index;
}
END_SINGLE_CORE
""")
14 changes: 14 additions & 0 deletions Deeploy/Targets/Generic/TypeCheckers.py
Original file line number Diff line number Diff line change
Expand Up @@ -610,3 +610,17 @@ def _inferNumLevels(self, inputs: List[VariableBuffer],
def _inferSignedness(self, inputs: List[VariableBuffer],
operatorRepresentation: OperatorRepresentation) -> List[bool]:
return [True]

# TopKChecker: infers types for both values and indices outputs of TopK operation
class TopKChecker(SignPropTypeChecker):
def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]):
super().__init__(input_types, output_types)

def _inferNumLevels(self, inputs: List[VariableBuffer], operatorRepresentation: OperatorRepresentation) -> List[int]:
# Output 0: values (same as input), Output 1: indices (integer, usually not quantized)
# We assume indices output is not quantized (set to 0 or 1)
return [inputs[0].nLevels, 1]

def _inferSignedness(self, inputs: List[VariableBuffer], operatorRepresentation: OperatorRepresentation) -> List[bool]:
# Output 0: values (same signedness as input), Output 1: indices (unsigned)
return [inputs[0]._signed, False]
Loading