-
Notifications
You must be signed in to change notification settings - Fork 44
Add SoCDAML Part III: hands-on lab for adding a new int8 operator #194
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
200ea13
009df36
dce801e
7fdde88
56d4491
9a42f1e
3d31a06
754aade
3eedd4b
f3fd13d
baff9c8
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 |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| # SoCDAML Part III - Student skeletons for `iLeakyReLU` | ||
|
|
||
| These files are your starting points for the Part III lab. Each one | ||
| contains the surrounding boilerplate; the conceptually interesting | ||
| parts are marked with `TODO(student)` comments and short hints. | ||
|
|
||
| | File | What's in it | What to do | | ||
| |------|--------------|------------| | ||
| | `generate.py` | Complete ONNX + golden-value generator | Run it (Step 1) | | ||
| | `iLeakyReLU.h` | Complete kernel header | Copy to `TargetLibraries/PULPOpen/inc/kernel/` (Step 3) | | ||
| | `iLeakyReLU.c` | Multi-core chunking provided; inner loop TODO | Fill the TODO, copy to `TargetLibraries/PULPOpen/src/` (Step 3) | | ||
| | `iLeakyReLU_simd.c` | SIMD chunking + load/max/store provided; one TODO line | Fill in Step 6b after the scalar works | | ||
| | `iLeakyReLUParser.py` | `parseNode` and `parseNodeCtxt` are TODO | Fill in, paste class into `Deeploy/Targets/Generic/Parsers.py` (Step 2) | | ||
| | `iLeakyReLUTemplate.py` | Mako template body is TODO | Fill in, copy to `Deeploy/Targets/PULPOpen/Templates/` (Step 4) | | ||
| | `iLeakyReLUTileConstraint.py` | Inherits `UnaryTileConstraint`; performance constraint TODO | Fill in (Step 5 + Step 6a), copy to `Deeploy/Targets/PULPOpen/TileConstraints/` | | ||
|
|
||
| The `docs/tutorials/introduction.md` ("Adding a New Operator") | ||
| walks through the six steps in order and includes collapsed solutions to | ||
| peek at when you're stuck. | ||
|
|
||
| If you really need the answer key, look in | ||
| `Deeploy/Tutorials/PartIII_solution/iLeakyReLU/`, but try the lab | ||
| first; you'll learn far more. | ||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,62 @@ | ||||||||||
| #!/usr/bin/env python3 | ||||||||||
| # ---------------------------------------------------------------------- | ||||||||||
| # File: generate.py (SoCDAML Part III - Step 1, provided complete) | ||||||||||
| # | ||||||||||
| # Builds the single-node ONNX graph + golden tensors that DeeployTest's | ||||||||||
| # harness will use to validate your iLeakyReLU implementation. | ||||||||||
| # | ||||||||||
| # Run from this directory: | ||||||||||
| # python generate.py | ||||||||||
| # | ||||||||||
| # Outputs: | ||||||||||
| # network.onnx, inputs.npz, outputs.npz | ||||||||||
| # | ||||||||||
| # Quantization-friendly LeakyReLU formula used here: | ||||||||||
| # out[i] = x if x >= 0 | ||||||||||
| # (mul*x) >> shift otherwise | ||||||||||
| # ---------------------------------------------------------------------- | ||||||||||
| # SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna | ||||||||||
| # | ||||||||||
| # SPDX-License-Identifier: Apache-2.0 | ||||||||||
|
|
||||||||||
| import numpy as np | ||||||||||
| import onnx | ||||||||||
| from onnx import TensorProto, helper | ||||||||||
|
|
||||||||||
| SHAPE = (1, 16, 64, 64) | ||||||||||
| MUL = 1 | ||||||||||
| SHIFT = 3 | ||||||||||
| SEED = 0xC0FFEE | ||||||||||
|
|
||||||||||
|
|
||||||||||
| def golden(x, mul, shift): | ||||||||||
| pos = x.astype(np.int32) | ||||||||||
| neg = (mul * pos) >> shift | ||||||||||
| out = np.where(pos >= 0, pos, neg) | ||||||||||
| return np.clip(out, -128, 127).astype(np.int8) | ||||||||||
|
|
||||||||||
|
|
||||||||||
| def build_onnx(): | ||||||||||
| in_value = helper.make_tensor_value_info('data_in', TensorProto.INT8, SHAPE) | ||||||||||
| out_value = helper.make_tensor_value_info('data_out', TensorProto.INT8, SHAPE) | ||||||||||
| node = helper.make_node('iLeakyReLU', ['data_in'], ['data_out'], name = 'iLeakyReLU_0', mul = MUL, shift = SHIFT) | ||||||||||
| graph = helper.make_graph([node], 'iLeakyReLU_single_node', [in_value], [out_value]) | ||||||||||
| model = helper.make_model(graph, producer_name = 'SoCDAML-PartIII') | ||||||||||
| model.opset_import[0].version = 13 | ||||||||||
| model.ir_version = 7 | ||||||||||
| return model | ||||||||||
|
|
||||||||||
|
|
||||||||||
| def main(): | ||||||||||
| rng = np.random.default_rng(SEED) | ||||||||||
| x = rng.integers(low = -128, high = 127, size = SHAPE, dtype = np.int8) | ||||||||||
| y = golden(x, MUL, SHIFT) | ||||||||||
| onnx.save(build_onnx(), 'network.onnx') | ||||||||||
| np.savez('inputs.npz', data_in = x) | ||||||||||
| np.savez('outputs.npz', data_out = y) | ||||||||||
|
Comment on lines
+55
to
+56
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. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win Write Deeploy-compatible NPZ artifacts. The generator currently writes Suggested fix- np.savez('inputs.npz', data_in = x)
- np.savez('outputs.npz', data_out = y)
+ np.savez('inputs.npz', input = x.astype(np.int64))
+ np.savez('outputs.npz', output = y.astype(np.int64))📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||
| print(f"OK: network.onnx, inputs.npz, outputs.npz " | ||||||||||
| f"(shape={SHAPE}, mul={MUL}, shift={SHIFT})") | ||||||||||
|
|
||||||||||
|
|
||||||||||
| if __name__ == '__main__': | ||||||||||
| main() | ||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| /* ===================================================================== | ||
| * Title: iLeakyReLU.c (SoCDAML Part III - Step 3 skeleton) | ||
| * | ||
| * Plain-C int8 LeakyReLU. The per-core chunking boilerplate is provided. | ||
| * Fill in the inner loop body marked `TODO(student)`. | ||
| * | ||
| * Goal: out[i] = (in[i] >= 0) ? in[i] : ((mul * in[i]) >> shift) | ||
| * | ||
| * Hints: | ||
| * - Cast in[i] to int32_t before the multiply to avoid 8-bit overflow. | ||
| * - Cast the final result back to int8_t before storing. | ||
| * | ||
| * Drop into: TargetLibraries/PULPOpen/src/iLeakyReLU.c | ||
| * ===================================================================== */ | ||
| /* SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna | ||
| * | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| #include "DeeployPULPMath.h" | ||
| #include "pmsis.h" | ||
|
|
||
| void PULPiLeakyReLU_i8_i8(int8_t *pIn, int8_t *pOut, uint32_t size, int32_t mul, | ||
| int32_t shift) { | ||
| uint32_t cid = pi_core_id(); | ||
| uint32_t nC = NUM_CORES; | ||
| uint32_t per = (size + nC - 1) / nC; | ||
| uint32_t start = cid * per; | ||
| uint32_t end = (start + per > size) ? size : (start + per); | ||
|
|
||
| for (uint32_t i = start; i < end; i++) { | ||
| // TODO(student): compute pOut[i] from pIn[i], mul, shift. | ||
| // Replace the following line: | ||
| pOut[i] = 0; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| /* ===================================================================== | ||
| * Title: iLeakyReLU.h (SoCDAML Part III - Step 3, provided) | ||
| * | ||
| * Header for the iLeakyReLU PULP kernel. | ||
| * Drop into: TargetLibraries/PULPOpen/inc/kernel/iLeakyReLU.h | ||
| * and add `#include "kernel/iLeakyReLU.h"` to DeeployPULPMath.h. | ||
| * ===================================================================== */ | ||
| /* SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna | ||
| * | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| #ifndef __DEEPLOY_KERNEL_ILEAKYRELU_H_ | ||
| #define __DEEPLOY_KERNEL_ILEAKYRELU_H_ | ||
|
|
||
| #include "DeeployPULPMath.h" | ||
|
|
||
| void PULPiLeakyReLU_i8_i8(int8_t *pIn, int8_t *pOut, uint32_t size, int32_t mul, | ||
| int32_t shift); | ||
|
|
||
| #endif // __DEEPLOY_KERNEL_ILEAKYRELU_H_ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| # ---------------------------------------------------------------------- | ||
| # File: iLeakyReLUParser.py (SoCDAML Part III - Step 2 skeleton) | ||
| # | ||
| # Paste this class into: | ||
| # Deeploy/Targets/Generic/Parsers.py | ||
| # | ||
| # Imports already present in that file (math, numpy as np, | ||
| # onnx_graphsurgeon as gs, NodeParser, NetworkContext). | ||
| # ---------------------------------------------------------------------- | ||
| # SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna | ||
| # | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
|
|
||
| class iLeakyReLUParser(NodeParser): | ||
|
|
||
| def __init__(self): | ||
| super().__init__() | ||
|
|
||
| def parseNode(self, node: gs.Node) -> bool: | ||
| # TODO(student): return False if the node doesn't have exactly | ||
| # one input, exactly one output, and both 'mul' and 'shift' | ||
| # attributes. On success, store them into | ||
| # self.operatorRepresentation as ints and return True. | ||
| return False | ||
|
|
||
| def parseNodeCtxt(self, ctxt: NetworkContext, node: gs.Node, channels_first: bool = True): | ||
| # TODO(student): look up the input and output tensors from ctxt | ||
| # using node.inputs[0].name / node.outputs[0].name, and populate | ||
| # self.operatorRepresentation with: | ||
| # 'data_in' -> input tensor name | ||
| # 'data_out' -> output tensor name | ||
| # 'size' -> int(np.prod(input_shape)) | ||
| # Return (ctxt, True). | ||
| return ctxt, False |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| # ---------------------------------------------------------------------- | ||
| # File: iLeakyReLUTemplate.py (SoCDAML Part III - Step 4 skeleton) | ||
| # | ||
| # Drop this file into: | ||
| # Deeploy/Targets/PULPOpen/Templates/iLeakyReLUTemplate.py | ||
| # ---------------------------------------------------------------------- | ||
| # SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna | ||
| # | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| from Deeploy.DeeployTypes import NodeTemplate | ||
|
|
||
|
|
||
| class _iLeakyReLUTemplate(NodeTemplate): | ||
|
|
||
| def __init__(self, templateStr): | ||
| super().__init__(templateStr) | ||
|
|
||
|
|
||
| # TODO(student): fill in the Mako template body so it emits a single | ||
| # call to your C kernel: | ||
| # | ||
| # PULPiLeakyReLU_i8_i8(<data_in>, <data_out>, <size>, <mul>, <shift>); | ||
| # | ||
| # All five `${...}` substitutions correspond to keys you populated in | ||
| # the parser (or that Deeploy fills automatically for tensor names). | ||
| referenceTemplate = _iLeakyReLUTemplate(""" | ||
| // iLeakyReLU (Name: ${nodeName}, Op: ${nodeOp}) | ||
| // TODO(student): emit the kernel call here. | ||
| """) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| # ---------------------------------------------------------------------- | ||
| # File: iLeakyReLUTileConstraint.py (SoCDAML Part III - Step 5+6a skeleton) | ||
| # | ||
| # Drop this file into: | ||
| # Deeploy/Targets/PULPOpen/TileConstraints/iLeakyReLUTileConstraint.py | ||
| # | ||
| # UnaryTileConstraint already implements the geometry and serializer | ||
| # you need for an elementwise op. You only have to subclass it. In | ||
| # Step 6a you'll add a performance constraint on top. | ||
| # ---------------------------------------------------------------------- | ||
| # SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna | ||
| # | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| from typing import Dict | ||
|
|
||
| from Deeploy.DeeployTypes import NetworkContext | ||
| from Deeploy.Targets.Generic.TileConstraints.UnaryTileConstraint import UnaryTileConstraint | ||
| from Deeploy.TilingExtension.TilerModel import TilerModel | ||
|
|
||
|
|
||
| class iLeakyReLUTileConstraint(UnaryTileConstraint): | ||
|
|
||
| @staticmethod | ||
| def addGeometricalConstraint(tilerModel: TilerModel, parseDict: Dict, ctxt: NetworkContext) -> TilerModel: | ||
| tilerModel = UnaryTileConstraint.addGeometricalConstraint(tilerModel, parseDict, ctxt) | ||
|
|
||
| # TODO(student, Step 6a): add a performance constraint so the | ||
| # innermost tile dim is a multiple of 16. Helpful API: | ||
| # tilerModel.addTileSizeDivisibleConstraint(parseDict, name, | ||
| # tensorDimVar, modulo) | ||
| # See: Deeploy/Targets/PULPOpen/TileConstraints/GEMMTileConstraint.py | ||
| # for a usage example. | ||
|
|
||
| return tilerModel |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| /* ===================================================================== | ||
| * Title: iLeakyReLU_simd.c (SoCDAML Part III - Step 6b skeleton) | ||
| * | ||
| * SIMD version of iLeakyReLU using XPULP packed 4x8b operations. | ||
| * The per-core chunking is provided. Fill in the inner SIMD body. | ||
| * | ||
| * Key identity (worth deriving on paper before reading hints below): | ||
| * LeakyReLU(x) = (x >= 0) ? x : (x >> shift) | ||
| * = max(x, x >> shift) | ||
| * because arithmetic right shift makes a negative value LESS negative | ||
| * (or zero) and doesn't change the sign of a non-negative value. | ||
| * | ||
| * Strategy hint (one path, two intrinsic-level operations per 4 lanes): | ||
| * - load v4s lane: v4s x = vIn[i]; | ||
| * - per-lane signed shift: v4s s = x >> shift; (GCC vector ext) | ||
| * - signed packed max: __builtin_pulp_max4(x, s); | ||
| * | ||
| * For the lab we assume `mul == 1` (the generator picks mul=1, shift=3). | ||
| * | ||
| * Drop into: TargetLibraries/PULPOpen/src/iLeakyReLU.c (overwrite scalar) | ||
| * ===================================================================== */ | ||
| /* SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna | ||
| * | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| #include "DeeployPULPMath.h" | ||
| #include "pmsis.h" | ||
|
|
||
| void PULPiLeakyReLU_i8_i8(int8_t *pIn, int8_t *pOut, uint32_t size, int32_t mul, | ||
| int32_t shift) { | ||
| (void)mul; // SIMD path assumes mul == 1 | ||
|
|
||
| uint32_t cid = pi_core_id(); | ||
| uint32_t nC = NUM_CORES; | ||
| uint32_t per = (size + nC - 1) / nC; | ||
| per &= ~0x3u; | ||
| uint32_t start = cid * per; | ||
| uint32_t end = (start + per > size) ? size : (start + per); | ||
|
Comment on lines
+36
to
+39
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. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Do not round per-core chunks down without covering the remainder.
🤖 Prompt for AI Agents |
||
|
|
||
| v4s *vIn = (v4s *)(pIn + start); | ||
| v4s *vOut = (v4s *)(pOut + start); | ||
| uint32_t nVec = (end - start) >> 2; | ||
|
|
||
| for (uint32_t i = 0; i < nVec; i++) { | ||
| v4s x = vIn[i]; | ||
| // TODO(student): one line to compute `s` from `x` and `shift`, | ||
| // one line to blend `x` and `s` with the packed | ||
| // signed max intrinsic and store it. | ||
| vOut[i] = x; // <- placeholder, replace | ||
| } | ||
| } | ||
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.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Align the SIMD description with the skeleton.
Line 12 says load/max/store are provided, but
iLeakyReLU_simd.cleaves the packed shift, max, and final store for the student. Update this row so the lab instructions match the actual file.🤖 Prompt for AI Agents