From daaa429824f3087ec377b4364b693794d3aff371 Mon Sep 17 00:00:00 2001 From: mohasnik Date: Fri, 10 Jul 2026 16:01:40 +0200 Subject: [PATCH 1/2] initial cmake configs and platform definitions --- CMakeLists.txt | 24 +- Deeploy/Targets/xheep/Bindings.py | 32 + Deeploy/Targets/xheep/Layers.py | 794 ++++++++++++++++++ Deeploy/Targets/xheep/Parsers.py | 16 + Deeploy/Targets/xheep/Platform.py | 233 +++++ Deeploy/Targets/xheep/Templates/__init__.py | 5 + .../Targets/xheep/TileConstraints/__init__.py | 5 + .../TopologyOptimizationPasses/Passes.py | 15 + .../TopologyOptimizationPasses/__init__.py | 5 + Deeploy/Targets/xheep/TypeCheckers.py | 12 + Deeploy/Targets/xheep/__init__.py | 5 + TargetLibraries/xheep/CMakeLists.txt | 31 + cmake/xheep/toolchain_gcc.cmake | 57 ++ cmake/xheep/xheep.cmake | 105 +++ 14 files changed, 1338 insertions(+), 1 deletion(-) create mode 100644 Deeploy/Targets/xheep/Bindings.py create mode 100644 Deeploy/Targets/xheep/Layers.py create mode 100644 Deeploy/Targets/xheep/Parsers.py create mode 100644 Deeploy/Targets/xheep/Platform.py create mode 100644 Deeploy/Targets/xheep/Templates/__init__.py create mode 100644 Deeploy/Targets/xheep/TileConstraints/__init__.py create mode 100644 Deeploy/Targets/xheep/TopologyOptimizationPasses/Passes.py create mode 100644 Deeploy/Targets/xheep/TopologyOptimizationPasses/__init__.py create mode 100644 Deeploy/Targets/xheep/TypeCheckers.py create mode 100644 Deeploy/Targets/xheep/__init__.py create mode 100644 TargetLibraries/xheep/CMakeLists.txt create mode 100644 cmake/xheep/toolchain_gcc.cmake create mode 100644 cmake/xheep/xheep.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 20a85bccf8..1ea405e9ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,7 @@ 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(platform MemPool CACHE STRING "Platform (MemPool, SoftHier, QEMU, Siracusa, Siracusa_w_neureka, PULP-Open, GAP9, Generic, Snitch, Xheep)") set_property(CACHE platform PROPERTY STRINGS MemPool SoftHier QEMU Siracusa Siracusa_w_neureka PULP-Open GAP9 Generic Snitch) if(platform STREQUAL MemPool) @@ -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 Xheep) + message(STATUS "Building for platform 'X-HEEP'") else() message(FATAL_ERROR "Invalid platform '${platform}' specified!") endif() @@ -326,5 +328,25 @@ if(platform STREQUAL XDNA2) endif() +if(platform STREQUAL Xheep) + set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_LIST_DIR}/cmake/xheep/toolchain_gcc.cmake) + + include(${CMAKE_CURRENT_LIST_DIR}/cmake/xheep/xheep.cmake) + + project(deeploy LANGUAGES C ASM) + + message(STATUS "============================= X-HEEP Configuration ============================") + message(STATUS "[cMake ] GENERATED_SOURCE = " ${GENERATED_SOURCE}) + message(STATUS "[cMake ] TESTNAME = " ${TESTNAME}) + message(STATUS "==============================================================================") + message(STATUS "") + + add_subdirectory(TargetLibraries/Generic) + add_subdirectory(TargetLibraries/xheep) + add_subdirectory(DeeployTest) + + target_link_libraries(deeploylib INTERFACE deeploybasic deeployxheep) +endif() + print_simulation_config() diff --git a/Deeploy/Targets/xheep/Bindings.py b/Deeploy/Targets/xheep/Bindings.py new file mode 100644 index 0000000000..b76518e166 --- /dev/null +++ b/Deeploy/Targets/xheep/Bindings.py @@ -0,0 +1,32 @@ +# SPDX-FileCopyrightText: 2024 ETH Zurich and University of Bologna +# +# SPDX-License-Identifier: Apache-2.0 + +import itertools + +from Deeploy.AbstractDataTypes import PointerClass +from Deeploy.CommonExtensions.CodeTransformationPasses.MemoryAllocation import ArgumentStructGeneration, \ + MemoryManagementGeneration, MemoryPassthroughGeneration +from Deeploy.CommonExtensions.DataTypes import FloatDataTypes, IntegerDataTypes, SignedIntegerDataTypes, float32_t, \ + int8_t, int32_t, uint8_t +from Deeploy.DeeployTypes import CodeTransformation, NodeBinding +from Deeploy.FutureExtension.CodeTransformationPasses.FutureCodeTransformation import FutureGeneration +from Deeploy.Targets.Generic.Templates import AddTemplate, BatchNormalizationTemplate, ConcatTemplate, ConvTemplate, \ + ConvTransposeTemplate, DebugPrintTemplate, DequantTemplate, DummyTemplate, DWConvTemplate, FloatAddTemplate, \ + FloatAveragePoolTemplate, FloatCeilTemplate, FloatClipTemplate, FloatConvTemplate, FloatDivTemplate, \ + FloatDWConvTemplate, FloatExpTemplate, FloatFloorTemplate, FloatGELUTemplate, FloatGemmTemplate, \ + FloatGlobalAveragePoolTemplate, FloatGlobalMaxPoolTemplate, FloatGroupNormTemplate, FloatHardSigmoidTemplate, \ + FloatHardSwishTemplate, FloatInstanceNormTemplate, FloatLayernormTemplate, FloatMatMulTemplate, \ + FloatMaxPoolTemplate, FloatMulTemplate, FloatPadTemplate, FloatPowTemplate, FloatReduceMeanTemplate, \ + FloatReluTemplate, FloatSigmoidTemplate, FloatSoftmaxTemplate, FloatSqrtTemplate, FloatSubTemplate, \ + FloatSwishTemplate, GatherTemplate, GemmTemplate, IntegerDivTemplate, ITAMaxTemplate, ITAPartialMaxTemplate, \ + MatMulTemplate, MaxPoolTemplate, MulTemplate, PadTemplate, QuantTemplate, ReduceMeanTemplate, ReduceSumTemplate, \ + RequantShiftTemplate, ReshapeTemplate, RQIntegerDivTemplate, RQSiGELUTemplate, SliceTemplate, SubTemplate, \ + TransposeTemplate, iGELUTemplate, iLayernormTemplate, iRMSNormTemplate, iSoftmaxTemplate +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 + + diff --git a/Deeploy/Targets/xheep/Layers.py b/Deeploy/Targets/xheep/Layers.py new file mode 100644 index 0000000000..af4d00c081 --- /dev/null +++ b/Deeploy/Targets/xheep/Layers.py @@ -0,0 +1,794 @@ +# # SPDX-FileCopyrightText: 2021 ETH Zurich and University of Bologna +# # +# # SPDX-License-Identifier: Apache-2.0 + +# import copy +# from typing import List, Tuple + +# import numpy as np + +# from Deeploy.DeeployTypes import NodeMapper, ONNXLayer, OperatorRepresentation, Shape + + +# class SingleOperationPerElementLayer(ONNXLayer): + +# def computeOps(self): +# return self.mapper.parser.operatorRepresentation['size'] + + +# class ConcatLayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + + +# class iRMSNormLayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + + +# class SliceLayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + + +# class ReshapeLayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + + +# class GatherLayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + + +# class GELULayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + +# def computeOps(self): +# size = self.mapper.parser.operatorRepresentation['size'] +# # RW: Sigmoid approximation +# mul1 = size # Multiply by 1.702 +# neg = size # Negate the result +# exp = size # Compute exponential +# add = size # Add 1 +# div = size # Division for sigmoid +# mul2 = size # Final multiplication by x + +# return mul1 + neg + exp + add + div + mul2 + + +# class GELUGradLayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + +# def computeOps(self): +# size = self.mapper.parser.operatorRepresentation['size'] +# ops_per_element = 9 +# gelu_grad_ops = size * ops_per_element +# return gelu_grad_ops + + +# class iHardswishLayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + + +# class iNoNormLayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + +# def computeOps(self): +# return self.mapper.parser.operatorRepresentation['size'] * 4 # 2 mul, 1 add, 1 right shift + +# def computeShapes(self, inputShapes: Shape, outputShapes: Shape, operatorRepresentation: OperatorRepresentation, +# channels_first: bool) -> Tuple[Shape]: + +# # JUNGVI: Broadcast the weights and bias to have as many dimensions as the inputs +# inputShapes[1] = [1] * (len(inputShapes[0]) - len(inputShapes[1])) + list(inputShapes[1]) +# inputShapes[2] = inputShapes[1] +# return (inputShapes, outputShapes) + + +# class RQSiGELULayer(GELULayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + + +# class RQSiHardswishLayer(iHardswishLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + + +# class SoftmaxLayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + +# def computeOps(self): + +# size = self.mapper.parser.operatorRepresentation['size'] +# last_dim_length = self.mapper.parser.operatorRepresentation['lastDimLength'] +# batch_size = size // last_dim_length + +# max_ops = last_dim_length - 1 +# exp_ops = last_dim_length * 2 +# sum_ops = last_dim_length - 1 +# div_ops = last_dim_length +# ops_per_batch = max_ops + exp_ops + sum_ops + div_ops +# total_ops = ops_per_batch * batch_size + +# return total_ops + + +# class SoftmaxGradLayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + +# def computeOps(self): +# input_size = self.mapper.parser.operatorRepresentation['size'] + +# # SoftmaxGrad operation: dy * (y - (y * sum(dy * y))) +# mul_ops = input_size +# sum_ops = input_size +# broadcast_mul_ops = input_size +# sub_ops = input_size +# final_mul_ops = input_size + +# return mul_ops + sum_ops + broadcast_mul_ops + sub_ops + final_mul_ops + + +# class ITAMaxLayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + + +# class RequantShiftLayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + +# def computeShapes(self, inputShapes: List[Shape], outputShapes: Shape, operatorRepresentation, +# channels_first) -> Tuple[Shape, Shape]: + +# channel_dim = inputShapes[0][1] +# inputShapes[2] = [inputShapes[0][0], channel_dim] + list(inputShapes[2][1:]) +# inputShapes[1] = [inputShapes[0][0], channel_dim] + list(inputShapes[1][1:]) + +# return (inputShapes, outputShapes) + +# def computeOps(self): +# return self.mapper.parser.operatorRepresentation['size'] * 3 # One add, one mul, one div + + +# class AddLayer(SingleOperationPerElementLayer): + +# def computeShapes(self, inputShapes: Shape, outputShapes: Shape, operatorRepresentation, +# channels_first) -> Tuple[Shape, Shape]: + +# if len(inputShapes[0]) > len(inputShapes[1]): +# inputShapes[1] = inputShapes[0] +# else: +# inputShapes[0] = inputShapes[1] + +# outputShapes = [inputShapes[0]] +# return (inputShapes, outputShapes) + + +# SubLayer = AddLayer + + +# class MatMulLayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + +# def computeOps(self): +# return 2 * self.mapper.parser.operatorRepresentation['M'] * self.mapper.parser.operatorRepresentation[ +# 'N'] * self.mapper.parser.operatorRepresentation['O'] * self.mapper.parser.operatorRepresentation['batch'] + +# def computeShapes(self, inputShapes: Tuple[Shape, Shape], outputShapes: Shape, operatorRepresentation, +# channels_first) -> Tuple[Tuple[Shape, Shape], Shape]: + +# A_shape, B_shape = inputShapes +# if len(A_shape) < 2: +# A_shape = [1] * (2 - len(A_shape)) + A_shape + +# if len(B_shape) < 2: +# B_shape = B_shape + [1] * (2 - len(B_shape)) + +# if A_shape[-1] != B_shape[-2]: +# raise ValueError(f"MatMul requires A.shape[-1] == B.shape[-2], but got {A_shape} and {B_shape}") + +# if len(A_shape) > len(B_shape): +# B_shape = [1] * (len(A_shape) - len(B_shape)) + list(B_shape) + +# elif len(A_shape) < len(B_shape): +# A_shape = [1] * (len(B_shape) - len(A_shape)) + list(A_shape) + +# return [A_shape, B_shape], outputShapes + + +# class RQMatMulLayer(MatMulLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + +# def computeShapes(self, inputShapes: List[Shape], outputShapes: Shape, operatorRepresentation, +# channels_first) -> Tuple[Shape, Shape]: + +# channel_dim = inputShapes[0][1] +# inputShapes[3] = [inputShapes[0][0]] + list(inputShapes[3][1:]) +# inputShapes[2] = [inputShapes[0][0]] + list(inputShapes[2][1:]) + +# return (inputShapes, outputShapes) + +# def computeOps(self): +# matmul = super().computeOps() +# rqs = self.mapper.parser.operatorRepresentation['size'] * 3 +# return matmul + rqs + + +# class PowLayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + + +# class SqrtLayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + + +# class DivLayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + + +# class RQIntegerDivLayer(DivLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + + +# class GEMMLayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + +# def computeShapes(self, inputShapes: Shape, outputShapes: Shape, operatorRepresentation, +# channels_first) -> Tuple[Shape, Shape]: +# if operatorRepresentation['transA']: +# M = inputShapes[0][-1] +# else: +# M = inputShapes[0][-2] + +# if operatorRepresentation['transB']: +# N = inputShapes[1][-2] +# else: +# N = inputShapes[1][-1] + +# if len(inputShapes) == 3: +# inputShapes[2] = [M, N] + +# return (inputShapes, outputShapes) + +# def computeOps(self): +# matmul = 2 * self.mapper.parser.operatorRepresentation['M'] * self.mapper.parser.operatorRepresentation[ +# 'N'] * self.mapper.parser.operatorRepresentation['O'] * self.mapper.parser.operatorRepresentation['batch'] +# gemm = matmul + 3 * self.mapper.parser.operatorRepresentation['M'] * self.mapper.parser.operatorRepresentation[ +# 'O'] * self.mapper.parser.operatorRepresentation['batch'] + +# return gemm + + +# class RQGEMMLayer(GEMMLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + +# def computeShapes(self, inputShapes: List[Shape], outputShapes: Shape, operatorRepresentation, +# channels_first) -> Tuple[Shape, Shape]: +# if operatorRepresentation['transA']: +# M = inputShapes[0][-1] +# else: +# M = inputShapes[0][-2] + +# if operatorRepresentation['transB']: +# N = inputShapes[1][-2] +# else: +# N = inputShapes[1][-1] + +# if len(inputShapes) == 5: +# inputShapes[2] = [M, N] +# inputShapes[4] = [inputShapes[0][0]] + list(inputShapes[4][1:]) +# inputShapes[3] = [inputShapes[0][0]] + list(inputShapes[3][1:]) +# else: +# inputShapes[3] = [inputShapes[0][0]] + list(inputShapes[3][1:]) +# inputShapes[2] = [ +# inputShapes[0][0], +# ] + list(inputShapes[2][1:]) + +# return (inputShapes, outputShapes) + +# def computeOps(self): +# gemm = super().computeOps() +# rqs = self.mapper.parser.operatorRepresentation['size'] * 3 +# return gemm + rqs + + +# class MulLayer(SingleOperationPerElementLayer): + +# def computeShapes(self, inputShapes: Shape, outputShapes: Shape, operatorRepresentation, +# channels_first) -> Tuple[Shape, Shape]: + +# if inputShapes[1] == () or inputShapes[1] == []: +# inputShapes[1] = (1,) + +# if len(inputShapes[0]) > len(inputShapes[1]): +# inputShapes[1] = inputShapes[0] +# else: +# inputShapes[0] = inputShapes[1] +# return (inputShapes, outputShapes) + + +# class ConvLayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + +# def computeShapes(self, inputShapes: Shape, outputShapes: Shape, operatorRepresentation, +# channels_first) -> Tuple[Shape, Shape]: +# if len(inputShapes) == 3: +# inputShapes[2] = inputShapes[1][0] +# return (inputShapes, outputShapes) + +# def computeOps(self): +# if "group" in self.mapper.parser.operatorRepresentation: +# groups = self.mapper.parser.operatorRepresentation['group'] +# else: +# groups = 1 +# opsPerPx = int( +# np.prod(self.mapper.parser.operatorRepresentation['kernel_shape']) * +# self.mapper.parser.operatorRepresentation['ch_im_in'] * +# self.mapper.parser.operatorRepresentation['ch_im_out'] / groups) * 2 +# if 'dim_im_out_y' in self.mapper.parser.operatorRepresentation: +# numPx = self.mapper.parser.operatorRepresentation[ +# 'dim_im_out_x'] * self.mapper.parser.operatorRepresentation['dim_im_out_y'] +# else: +# numPx = self.mapper.parser.operatorRepresentation['dim_im_out_x'] +# return numPx * opsPerPx + + +# class RQSConvLayer(ConvLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + +# def computeOps(self): +# conv = super().computeOps() + +# if 'dim_im_out_y' in self.mapper.parser.operatorRepresentation: +# rqs = self.mapper.parser.operatorRepresentation['dim_im_out_x'] * self.mapper.parser.operatorRepresentation[ +# 'dim_im_out_y'] * 3 +# else: +# rqs = self.mapper.parser.operatorRepresentation['dim_im_out_x'] * 3 + +# return conv + rqs + + +# class PadLayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + + +# class MaxPoolLayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + +# def computeOps(self): +# kernel_shape = self.mapper.parser.operatorRepresentation['kernel_shape'] +# elements_per_window = int(np.prod(kernel_shape)) +# data_out_size = self.mapper.parser.operatorRepresentation['data_out_size'] +# comparisons_per_window = elements_per_window - 1 +# total_ops = data_out_size * comparisons_per_window +# return total_ops + + +# class ReduceMeanLayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + + +# class ReduceSumLayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + +# def computeShapes(self, inputShapes: Shape, outputShapes: Shape, operatorRepresentation, +# channels_first) -> Tuple[Shape, Shape]: +# outputShapes = copy.deepcopy(inputShapes) +# axis = operatorRepresentation['axes'][0] + +# if operatorRepresentation['keepdims']: +# outputShapes[0][axis] = 1 +# else: +# outputShapes[0] = outputShapes[0][:axis] + outputShapes[0][axis + 1:] +# return (inputShapes, outputShapes) + + +# class ReluLayer(SingleOperationPerElementLayer): +# pass + + +# class LayerNormLayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + +# def computeOps(self): +# compAverage = self.mapper.parser.operatorRepresentation['size'] +# compNormalize = self.mapper.parser.operatorRepresentation['size'] +# compSqr = self.mapper.parser.operatorRepresentation['size'] +# compSum = self.mapper.parser.operatorRepresentation['size'] +# compSqrt = self.mapper.parser.operatorRepresentation['size'] +# compDiv = self.mapper.parser.operatorRepresentation['size'] +# return compAverage + compNormalize + compSqr + compSum + compSqrt + compDiv + + +# class LayerNormGradLayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + + +# class TransposeLayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + + +# class SoftmaxCrossEntropyLossLayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + + +# class SoftmaxCrossEntropyLossGradLayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + + +# class SGDLayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + + +# class LinearAttentionLayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + +# def computeShapes(self, inputShapes: Shape, outputShapes: Shape, operatorRepresentation, +# channels_first) -> Tuple[Shape, Shape]: +# inputShapes[4] = inputShapes[3][0] +# inputShapes[6] = inputShapes[5][0] +# inputShapes[8] = inputShapes[7][0] +# inputShapes[10] = inputShapes[9][0] + +# return (inputShapes, outputShapes) + +# def computeOps(self): +# # seqLen = self.mapper.parser.operatorRepresentation['in_C'] +# # dim = self.mapper.parser.operatorRepresentation['dim'] +# # dim_head = self.mapper.parser.operatorRepresentation['dim_head'] +# # heads = self.mapper.parser.operatorRepresentation['heads'] +# # QOps = seqLen * dim * dim_head * heads * 2 +# # # WQ * Q (H ) +# # KOps = seqLen * dim * dim_head * heads * 2 +# # # WK * K +# # VOps = seqLen * dim * dim_head * heads * 2 +# # # WV * V +# # KVOps = seqLen * dim_head * dim_head * heads * 2 +# # # Q * KT +# # QKVOps = seqLen * dim_head * dim_head * heads * 2 +# # # N H S S * N H S D -> N H S D +# # OutOps = seqLen * dim_head * heads * dim * 2 +# # # WO * O +# # totOps = QOps + KOps + VOps + KVOps + QKVOps + OutOps +# # return totOps + +# return 0 + + +# class CLCALayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + +# def computeShapes(self, inputShapes: Shape, outputShapes: Shape, operatorRepresentation, +# channels_first) -> Tuple[Shape, Shape]: +# inputShapes[3] = inputShapes[2][0] +# inputShapes[5] = inputShapes[4][0] +# inputShapes[7] = inputShapes[6][0] +# # WQ Requant +# inputShapes[8] = [operatorRepresentation['dim_head'] * operatorRepresentation['heads'], 1] +# inputShapes[9] = [operatorRepresentation['dim_head'] * operatorRepresentation['heads'], 1] +# inputShapes[10] = [operatorRepresentation['dim_head'] * operatorRepresentation['heads'], 1] +# # WK Requant +# inputShapes[11] = [1, 1] +# inputShapes[12] = [1, 1] +# inputShapes[13] = [1, 1] +# # WV Requant +# inputShapes[14] = [operatorRepresentation['dim_head'] * operatorRepresentation['heads'], 1] +# inputShapes[15] = [operatorRepresentation['dim_head'] * operatorRepresentation['heads'], 1] +# inputShapes[16] = [operatorRepresentation['dim_head'] * operatorRepresentation['heads'], 1] +# # Kdiv Requanat +# inputShapes[17] = [1, 1] +# inputShapes[18] = [1, 1] +# inputShapes[19] = [1, 1] +# # Preattn Requant +# inputShapes[20] = [1, 1] +# inputShapes[21] = [1, 1] +# inputShapes[22] = [1, 1] +# # Postattn Requant +# inputShapes[23] = [1, 1] +# inputShapes[24] = [1, 1] +# inputShapes[25] = [1, 1] +# # WO Requant +# inputShapes[26] = [operatorRepresentation['out_dim'], 1] +# inputShapes[27] = [operatorRepresentation['out_dim'], 1] +# inputShapes[28] = [operatorRepresentation['out_dim'], 1] +# return (inputShapes, outputShapes) + +# def computeOps(self): + +# qLen = self.mapper.parser.operatorRepresentation['q_shape'][-1] +# kLen = self.mapper.parser.operatorRepresentation['kv_shape'][-1] +# inDim = self.mapper.parser.operatorRepresentation['q_shape'][-2] +# heads = self.mapper.parser.operatorRepresentation['heads'] +# dim_head = self.mapper.parser.operatorRepresentation['dim_head'] +# out_dim = self.mapper.parser.operatorRepresentation['out_dim'] + +# # q -> Q +# QOps = qLen * 1 * inDim * heads * dim_head * 2 +# # v -> V +# VOps = kLen * 1 * inDim * heads * dim_head * 2 +# # V -> K +# KOps = kLen * heads * dim_head * 2 +# # KOps = 0 + +# EOps = heads * kLen * heads * dim_head + +# MMKTV = heads * dim_head * kLen * dim_head * 2 +# MMQA = heads * qLen * dim_head * dim_head * 2 +# MMQE = heads * qLen * dim_head * 1 * 2 + +# # Divs, Adds(eps), muls(delta, eps) +# DivOps = heads * qLen * dim_head + heads * qLen + 2 * heads * qLen * dim_head + +# OOps = (heads * dim_head) * qLen * out_dim * 1 * 2 + +# return QOps + VOps + KOps + EOps + MMKTV + MMQA + MMQE + DivOps + OOps + + +# class MHSALayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + +# def computeShapes(self, inputShapes: Shape, outputShapes: Shape, operatorRepresentation, +# channels_first) -> Tuple[Shape, Shape]: +# outputShapes = [[inputShapes[0][0], operatorRepresentation['heads']] + inputShapes[0][1:]] + +# return (inputShapes, outputShapes) + +# def computeOps(self): +# seqLen = self.mapper.parser.operatorRepresentation['S'] +# dim = self.mapper.parser.operatorRepresentation['dim'] +# dim_head = self.mapper.parser.operatorRepresentation['dim_head'] +# heads = self.mapper.parser.operatorRepresentation['heads'] +# QOps = seqLen * dim * dim_head * heads * 2 +# # WQ * Q (H ) +# KOps = seqLen * dim * dim_head * heads * 2 +# # WK * K +# VOps = seqLen * dim * dim_head * heads * 2 +# # WV * V +# QKOps = seqLen * seqLen * dim_head * heads * 2 +# # Q * KT +# AVOps = seqLen * seqLen * dim_head * heads * 2 +# # N H S S * N H S D -> N H S D +# OutOps = seqLen * dim_head * heads * dim * 2 +# # WO * O +# totOps = QOps + KOps + VOps + QKOps + AVOps + OutOps +# return totOps + + +# class DebugPrintLayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + + +# class QuantLayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + + +# class DequantLayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + + +# class BatchNormalizationLayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + +# def computeOps(self): +# # 5 operations per element: sub, mul, add, sqrt, div +# B = self.mapper.parser.operatorRepresentation['batch_size'] +# C = self.mapper.parser.operatorRepresentation['channel_size'] +# W = self.mapper.parser.operatorRepresentation['window_size'] +# return B * C * W * 5 + + +# class ConvTransposeLayer(ONNXLayer): + +# def __init__(self, maps: List[NodeMapper]): +# super().__init__(maps) + +# def computeShapes(self, inputShapes: Shape, outputShapes: Shape, operatorRepresentation, +# channels_first) -> Tuple[Shape, Shape]: +# """ +# Infers output shapes for ConvTranspose using only static info. +# - inputShapes[0]: input tensor shape (e.g., [N, C_in, W] for 1D, [N, C_in, H, W] for 2D) +# - inputShapes[1]: weight tensor shape (e.g., [C_in, C_out // group, kW] for 1D) +# - outputShapes[0]: output tensor shape (to be updated) +# """ +# newInputShapes = list(inputShapes) +# newOutputShapes = list(outputShapes) +# group = operatorRepresentation.get('group', 1) +# weight_shape = inputShapes[1] + +# if newOutputShapes and len(newOutputShapes[0]) >= 2: +# # For 1D: weight_shape = [C_in, C_out // group, kW] +# # For 2D: weight_shape = [C_in, C_out // group, kH, kW] +# ch_out = weight_shape[1] * group +# if channels_first: +# newOutputShapes[0][1] = ch_out +# else: +# newOutputShapes[0][-1] = ch_out + +# return newInputShapes, newOutputShapes + +# def computeOps(self): +# opRep = self.mapper.parser.operatorRepresentation + +# groups = opRep.get('group', 1) +# kernel_shape = np.prod(opRep['kernel_shape']) # es. [3, 3] -> 9 +# ch_in = opRep['ch_im_in'] +# ch_out = opRep['ch_im_out'] + +# opsPerPx = int(kernel_shape * ch_in * ch_out / groups) * 2 + +# # ConvTranspose upscales spatial dims, quindi num pixel viene da output +# if 'dim_im_out_y' in opRep: +# numPx = opRep['dim_im_out_x'] * opRep['dim_im_out_y'] +# else: +# numPx = opRep['dim_im_out_x'] + +# return numPx * opsPerPx + + +# class CeilLayer(SingleOperationPerElementLayer): +# pass + + +# class FloorLayer(SingleOperationPerElementLayer): +# pass + + +# class ClipLayer(ONNXLayer): + +# def computeOps(self): +# # compare vs min and max +# return self.mapper.parser.operatorRepresentation['size'] * 2 + + +# class ExpLayer(SingleOperationPerElementLayer): +# pass + + +# class SigmoidLayer(ONNXLayer): + +# def computeOps(self): +# # sigmoid(x) = 1 / (1 + exp(-x)): neg, exp, add, div +# return self.mapper.parser.operatorRepresentation['size'] * 4 + + +# class SwishLayer(ONNXLayer): + +# def computeOps(self): +# # x * sigmoid(x): 4 ops for sigmoid + 1 mul +# return self.mapper.parser.operatorRepresentation['size'] * 5 + + +# class HardSigmoidLayer(ONNXLayer): + +# def computeOps(self): +# # max(0, min(1, alpha*x + beta)): mul, add, clip(min), clip(max) +# return self.mapper.parser.operatorRepresentation['size'] * 4 + + +# class HardSwishLayer(ONNXLayer): + +# def computeOps(self): +# # x * HardSigmoid(x): 4 ops for hard sigmoid + 1 mul +# return self.mapper.parser.operatorRepresentation['size'] * 5 + + +# class InstanceNormLayer(ONNXLayer): + +# def computeOps(self): +# # per element: mean-sum(1) + variance(sub+sq+add=3) + normalize(sub+div=2) + affine(mul+add=2) = 8 +# # per (batch, channel): mean(div=1) + variance(sqrt+div=2) = 3 +# opRep = self.mapper.parser.operatorRepresentation +# B, C, S = int(opRep['batch_size']), int(opRep['num_channels']), int(opRep['spatial']) +# return B * C * (S * 8 + 3) + + +# class GroupNormLayer(ONNXLayer): + +# def computeOps(self): +# # same structure as InstanceNorm: 8 ops/element + 3 ops per (batch, channel) +# opRep = self.mapper.parser.operatorRepresentation +# B, C, S = int(opRep['batch_size']), int(opRep['num_channels']), int(opRep['spatial']) +# return B * C * (S * 8 + 3) + + +# class AveragePoolLayer(ONNXLayer): + +# def computeOps(self): +# opRep = self.mapper.parser.operatorRepresentation +# kernel_elements = int(np.prod(opRep['kernel_shape'])) +# # (kernel_elements - 1) additions + 1 division per output element +# return opRep['data_out_size'] * kernel_elements + + +# class GlobalAveragePoolLayer(ONNXLayer): + +# def computeOps(self): +# opRep = self.mapper.parser.operatorRepresentation +# # (spatial_size - 1) additions + 1 division per output channel +# return int(opRep['batch_size'] * opRep['num_channels'] * opRep['spatial_size']) + + +# class GlobalMaxPoolLayer(ONNXLayer): + +# 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)) diff --git a/Deeploy/Targets/xheep/Parsers.py b/Deeploy/Targets/xheep/Parsers.py new file mode 100644 index 0000000000..861666bfff --- /dev/null +++ b/Deeploy/Targets/xheep/Parsers.py @@ -0,0 +1,16 @@ +# Copyright (C) 2026 EPFL. +# Solderpad Hardware License, Version 2.1, see LICENSE.md for details. +# SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +# +# File: Parsers.py +# Author: Mohammad Hossein Nikkhah +# Description: + +import math +from typing import Tuple + +import numpy as np +import onnx_graphsurgeon as gs + +from Deeploy.DeeployTypes import ConstantBuffer, NetworkContext, NodeParser, VariableBuffer + diff --git a/Deeploy/Targets/xheep/Platform.py b/Deeploy/Targets/xheep/Platform.py new file mode 100644 index 0000000000..37380e34ee --- /dev/null +++ b/Deeploy/Targets/xheep/Platform.py @@ -0,0 +1,233 @@ +# Copyright (C) 2026 EPFL. +# Solderpad Hardware License, Version 2.1, see LICENSE.md for details. +# SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +# +# File: Platform.py +# Author: Mohammad Hossein Nikkhah +# Description: + + +from Deeploy.CommonExtensions.OptimizationPasses.TopologyOptimizationPasses.LoweringOptimizationPasses import \ + RemoveEmptyConvBiasPass, RemoveOnlySingletonReduceMeanPass + + +from Deeploy.DeeployTypes import ConstantBuffer, DeploymentEngine, DeploymentPlatform, NodeMapper, NodeTemplate, \ + StructBuffer, TopologyOptimizer, TransientBuffer, VariableBuffer + + +from Deeploy.Targets.Generic.Bindings import BasicAddBindings, BasicAveragePool1DBindings, BasicAveragePool2DBindings, \ + BasicBatchNormBindings, BasicCeilBindings, BasicClipBindings, BasicConcatBindings, BasicConv1DBindings, \ + BasicConv2DBindings, BasicConvTransposeBindings, BasicDebugPrintBindings, BasicDequantBindings, BasicDivBindings, \ + BasicDWConv1DBinding, BasicDWConv2DBindings, BasicExpBindings, BasicFloorBindings, BasicGatherBindings, \ + BasicGELUBindings, BasicGEMMBindings, BasicGlobalAveragePoolBindings, BasicGlobalMaxPoolBindings, \ + BasicGroupNormBindings, BasicHardSigmoidBindings, BasicHardSwishBindings, BasicInstanceNormBindings, \ + BasicITAPartialSoftmaxBinding, BasicITASoftmaxBinding, BasicLayerNormBindings, BasicMatMulBindings, \ + BasicMaxPool1DBindings, BasicMaxPool2DBindings, BasicMulBindings, BasicPad1DBindings, BasicPad2DBindings, \ + BasicPowBindings, BasicQuantBindings, BasicReduceMeanBindings, BasicReduceSumBindings, BasicReluBinding, \ + BasicReshapeBindings, BasicRQIntegerDivBinding, BasicRQSBindings, BasicRQSGELUBinding, BasicSigmoidBindings, \ + BasicSliceBindings, BasicSoftmaxBindings, BasicSqrtBindings, BasicSubBindings, BasicSwishBindings, \ + BasicTransposeBindings, DummyBinding + + +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 +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, \ + GenericDWConv1DParser, GenericDWConv2DParser, GenericGEMMParser, GenericMaxPool2DParser, GlobalAveragePoolParser, \ + GlobalMaxPoolParser, GroupNormParser, HardSigmoidParser, HardSwishParser, InstanceNormParser, IntegerDivParser, \ + 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 + +from Deeploy.Targets.Generic.Templates import AllocateTemplate, FreeTemplate + +from Deeploy.Targets.Generic.Platform import GenericVariableBuffer, GenericConstantBuffer, GenericTransientBuffer, GenericStructBuffer + +from Deeploy.Targets.Generic.TopologyOptimizationPasses.Passes import DequantPatternPass, ExtractPaddingFromConvPass, \ + ExtractPaddingFromPoolPass, MatMulAddMergePass, MergeConstAddAndRequantPass, QuantPatternPass, \ + iGELURequantMergePass + +AddMapper = NodeMapper(AddParser(), BasicAddBindings) +SubMapper = NodeMapper(SubParser(), BasicSubBindings) +Conv1DMapper = NodeMapper(GenericConv1DParser(), BasicConv1DBindings) +Conv2DMapper = NodeMapper(GenericConv2DParser(), BasicConv2DBindings) +ConcatMapper = NodeMapper(ConcatParser(), BasicConcatBindings) +DebugMapper = NodeMapper(DebugParser(), BasicDebugPrintBindings) +DWConv1DMapper = NodeMapper(GenericDWConv1DParser(), [BasicDWConv1DBinding]) +DWConv2DMapper = NodeMapper(GenericDWConv2DParser(), BasicDWConv2DBindings) +FlattenMapper = NodeMapper(FlattenParser(), BasicReshapeBindings) +GatherMapper = NodeMapper(GatherParser(), BasicGatherBindings) +GELUMapper = NodeMapper(GELUParser(), BasicGELUBindings) +GEMMMapper = NodeMapper(GenericGEMMParser(), BasicGEMMBindings) +LayerNormMapper = NodeMapper(LayerNormParser(), BasicLayerNormBindings) +iLayerNormMapper = NodeMapper(iLayerNormParser(), BasicLayerNormBindings) +DivMapper = NodeMapper(DivParser(), BasicDivBindings) +IntegerDivMapper = NodeMapper(IntegerDivParser(), BasicDivBindings) +ITAMaxMapper = NodeMapper(ITAMaxParser(), [BasicITASoftmaxBinding]) +ITAPartialMaxMapper = NodeMapper(ITAPartialMaxParser(), [BasicITAPartialSoftmaxBinding]) +MatMulMapper = NodeMapper(MatMulParser(), BasicMatMulBindings) +MaxPool2DMapper = NodeMapper(GenericMaxPool2DParser(), BasicMaxPool2DBindings) +MaxPool1DMapper = NodeMapper(MaxPool1DParser(), BasicMaxPool1DBindings) +MulMapper = NodeMapper(MulParser(), BasicMulBindings) +PowMapper = NodeMapper(PowParser(), BasicPowBindings) +SqrtMapper = NodeMapper(SqrtParser(), BasicSqrtBindings) +Pad1DMapper = NodeMapper(Pad1DParser(), BasicPad1DBindings) +Pad2DMapper = NodeMapper(Pad2DParser(), BasicPad2DBindings) +ReduceMeanMapper = NodeMapper(ReduceMeanParser(), BasicReduceMeanBindings) +ReduceSumMapper = NodeMapper(ReduceSumParser(), BasicReduceSumBindings) +ReluMapper = NodeMapper(ReluParser(), [BasicReluBinding]) +RequantShiftMapper = NodeMapper(RequantShiftParser(), BasicRQSBindings) +ReshapeMapper = NodeMapper(ReshapeParser(), BasicReshapeBindings) +RQGELUMapper = NodeMapper(RQSiGELUParser(), [BasicRQSGELUBinding]) +RQIntegerDivMapper = NodeMapper(RQIntegerDivParser(), [BasicRQIntegerDivBinding]) +SoftmaxMapper = NodeMapper(SoftmaxParser(), BasicSoftmaxBindings) +iSoftmaxMapper = NodeMapper(iSoftmaxParser(), BasicSoftmaxBindings) +TransposeMapper = NodeMapper(TransposeParser(), BasicTransposeBindings) +UnsqueezeMapper = NodeMapper(UnsqueezeParser(), BasicReshapeBindings) +QuantMapper = NodeMapper(QuantParser(), BasicQuantBindings) +DequantMapper = NodeMapper(DequantParser(), BasicDequantBindings) +BatchNormalizationMapper = NodeMapper(BatchNormParser(), BasicBatchNormBindings) +ConvTransposeMapper = NodeMapper(ConvTranspose1DParser(), BasicConvTransposeBindings) +SliceMapper = NodeMapper(SliceParser(), BasicSliceBindings) +CeilMapper = NodeMapper(CeilParser(), BasicCeilBindings) +FloorMapper = NodeMapper(FloorParser(), BasicFloorBindings) +ClipMapper = NodeMapper(ClipParser(), BasicClipBindings) +ExpMapper = NodeMapper(ExpParser(), BasicExpBindings) +SigmoidMapper = NodeMapper(SigmoidParser(), BasicSigmoidBindings) +SwishMapper = NodeMapper(SwishParser(), BasicSwishBindings) +HardSigmoidMapper = NodeMapper(HardSigmoidParser(), BasicHardSigmoidBindings) +HardSwishMapper = NodeMapper(HardSwishParser(), BasicHardSwishBindings) +InstanceNormMapper = NodeMapper(InstanceNormParser(), BasicInstanceNormBindings) +GroupNormMapper = NodeMapper(GroupNormParser(), BasicGroupNormBindings) +AveragePool1DMapper = NodeMapper(AveragePool1DParser(), BasicAveragePool1DBindings) +AveragePool2DMapper = NodeMapper(AveragePool2DParser(), BasicAveragePool2DBindings) +GlobalAveragePoolMapper = NodeMapper(GlobalAveragePoolParser(), BasicGlobalAveragePoolBindings) +GlobalMaxPoolMapper = NodeMapper(GlobalMaxPoolParser(), BasicGlobalMaxPoolBindings) + +# Dummy nodes are intended for development purposes only! +# They should always generate compiler errors to not accidentally end up in production code +DummyMapper = NodeMapper(DummyParser(), [DummyBinding]) + +GenericMapping = { + 'Add': AddLayer([AddMapper]), + 'Sub': SubLayer([SubMapper]), + 'Conv': ConvLayer([Conv2DMapper, DWConv2DMapper, Conv1DMapper, DWConv1DMapper]), + 'Concat': ConcatLayer([ConcatMapper]), + 'DebugPrint': DebugPrintLayer([DebugMapper]), + 'Div': DivLayer([DivMapper]), + 'Flatten': ReshapeLayer([FlattenMapper]), + 'Gather': GatherLayer([GatherMapper]), + 'Gemm': GEMMLayer([GEMMMapper]), + 'iGELU': GELULayer([GELUMapper]), + 'Gelu': GELULayer([GELUMapper]), + 'LayerNormalization': LayerNormLayer([LayerNormMapper]), + 'iLayerNorm': LayerNormLayer([iLayerNormMapper]), + 'IntegerDiv': DivLayer([IntegerDivMapper]), + 'IntegerMean': ReduceMeanLayer([ReduceMeanMapper]), + 'Softmax': SoftmaxLayer([SoftmaxMapper]), + 'iSoftmax': SoftmaxLayer([iSoftmaxMapper]), + 'ITAMax': ITAMaxLayer([ITAMaxMapper]), + 'ITAPartialMax': ITAMaxLayer([ITAPartialMaxMapper]), + 'MatMul': GEMMLayer([MatMulMapper]), + 'MatMulInteger': MatMulLayer([MatMulMapper]), + 'MaxPool': MaxPoolLayer([MaxPool1DMapper, MaxPool2DMapper]), + 'Mul': MulLayer([MulMapper]), + 'Pow': PowLayer([PowMapper]), + 'Sqrt': SqrtLayer([SqrtMapper]), + 'Pad': PadLayer([Pad1DMapper, Pad2DMapper]), + 'ReduceMean': ReduceMeanLayer([ReduceMeanMapper]), + 'ReduceSum': ReduceSumLayer([ReduceSumMapper]), + 'Relu': ReluLayer([ReluMapper]), + 'RequantizediGELU': RQSiGELULayer([RQGELUMapper]), + 'RequantShift': RequantShiftLayer([RequantShiftMapper]), + 'Reshape': ReshapeLayer([ReshapeMapper]), + 'RQIntegerDiv': RQIntegerDivLayer([RQIntegerDivMapper]), + 'Squeeze': ReshapeLayer([UnsqueezeMapper]), + 'Transpose': TransposeLayer([TransposeMapper]), + 'Unsqueeze': ReshapeLayer([UnsqueezeMapper]), + 'Slice': SliceLayer([SliceMapper]), + 'Quant': QuantLayer([QuantMapper]), + 'Dequant': DequantLayer([DequantMapper]), + 'BatchNormalization': BatchNormalizationLayer([BatchNormalizationMapper]), + 'ConvTranspose': ConvTransposeLayer([ConvTransposeMapper]), + 'Ceil': CeilLayer([CeilMapper]), + 'Floor': FloorLayer([FloorMapper]), + 'Clip': ClipLayer([ClipMapper]), + 'Exp': ExpLayer([ExpMapper]), + 'Sigmoid': SigmoidLayer([SigmoidMapper]), + 'Swish': SwishLayer([SwishMapper]), + 'HardSigmoid': SigmoidLayer([HardSigmoidMapper]), + 'HardSwish': SwishLayer([HardSwishMapper]), + 'InstanceNormalization': InstanceNormLayer([InstanceNormMapper]), + 'GroupNormalization': GroupNormLayer([GroupNormMapper]), + 'AveragePool': AveragePoolLayer([AveragePool1DMapper, AveragePool2DMapper]), + 'GlobalAveragePool': GlobalAveragePoolLayer([GlobalAveragePoolMapper]), + 'GlobalMaxPool': GlobalMaxPoolLayer([GlobalMaxPoolMapper]), + # # For example, you can use the DummpyMapper, in case you want to test + # # deployment or optimizations with GlobalAveragePool nodes but did not yet + # # implement the corresponding kernel + # 'GlobalAveragePool': ConvLayer([DummyMapper]), +} + + +class XHeepVariableBuffer(GenericVariableBuffer): + def __init__(self, name = '', shape=..., aliases = None): + super().__init__(name, shape, aliases) + + +class XHeepTransientBuffer(GenericTransientBuffer): + def __init__(self, name = '', size=0): + super().__init__(name, size) + +class XHeepConstantBuffer(GenericConstantBuffer): + def __init__(self, name = '', shape=..., values=...): + super().__init__(name, shape, values) + + + +class XHeepStructBuffer(GenericStructBuffer): + def __init__(self, name, structDict): + super().__init__(name, structDict) + + +XHeepOptimizer = TopologyOptimizer( + [ + QuantPatternPass(), + DequantPatternPass(), + iGELURequantMergePass(), + MatMulAddMergePass(), + MergeConstAddAndRequantPass(), + ExtractPaddingFromConvPass(), + ExtractPaddingFromPoolPass(), + RemoveEmptyConvBiasPass(), + RemoveOnlySingletonReduceMeanPass(), + # DebugPrintPass(r'.*[Mm]at[Mm]ul.*', position = 'after'), + ], + name = "XHeepOptimizer") + + + +includeList = ["DeeployBasicMath.h"] + + +class XHeepEngine(DeploymentEngine): + + def __init__(self, name: str, Mapping = GenericMapping, initCode: str = "", includeList = includeList) -> None: + super().__init__(name, Mapping, initCode, includeList) + + +class XHeepPlatform(DeploymentPlatform): + + def __init__(self, + engines = [XHeepEngine("XHeep")], + variableBuffer = XHeepVariableBuffer, + constantBuffer = XHeepConstantBuffer, + structBuffer = XHeepStructBuffer, + transientBuffer = XHeepTransientBuffer): + super().__init__(engines, variableBuffer, constantBuffer, structBuffer, transientBuffer) diff --git a/Deeploy/Targets/xheep/Templates/__init__.py b/Deeploy/Targets/xheep/Templates/__init__.py new file mode 100644 index 0000000000..be436b64a3 --- /dev/null +++ b/Deeploy/Targets/xheep/Templates/__init__.py @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: 2024 ETH Zurich and University of Bologna +# +# SPDX-License-Identifier: Apache-2.0 + +from . import * diff --git a/Deeploy/Targets/xheep/TileConstraints/__init__.py b/Deeploy/Targets/xheep/TileConstraints/__init__.py new file mode 100644 index 0000000000..be436b64a3 --- /dev/null +++ b/Deeploy/Targets/xheep/TileConstraints/__init__.py @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: 2024 ETH Zurich and University of Bologna +# +# SPDX-License-Identifier: Apache-2.0 + +from . import * diff --git a/Deeploy/Targets/xheep/TopologyOptimizationPasses/Passes.py b/Deeploy/Targets/xheep/TopologyOptimizationPasses/Passes.py new file mode 100644 index 0000000000..7b24a5346b --- /dev/null +++ b/Deeploy/Targets/xheep/TopologyOptimizationPasses/Passes.py @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2023 ETH Zurich and University of Bologna +# +# SPDX-License-Identifier: Apache-2.0 + +import copy +from collections import OrderedDict +from functools import partial +from typing import List + +import numpy as np +import onnx_graphsurgeon as gs + +from Deeploy.CommonExtensions.OptimizationPasses.Matchers import BranchingMatcher, Match, NonBranchingMatcher +from Deeploy.CommonExtensions.OptimizationPasses.PassClasses import ReplaceSequentialPatternPass, contextagnostic + diff --git a/Deeploy/Targets/xheep/TopologyOptimizationPasses/__init__.py b/Deeploy/Targets/xheep/TopologyOptimizationPasses/__init__.py new file mode 100644 index 0000000000..be436b64a3 --- /dev/null +++ b/Deeploy/Targets/xheep/TopologyOptimizationPasses/__init__.py @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: 2024 ETH Zurich and University of Bologna +# +# SPDX-License-Identifier: Apache-2.0 + +from . import * diff --git a/Deeploy/Targets/xheep/TypeCheckers.py b/Deeploy/Targets/xheep/TypeCheckers.py new file mode 100644 index 0000000000..1b6e49948a --- /dev/null +++ b/Deeploy/Targets/xheep/TypeCheckers.py @@ -0,0 +1,12 @@ +# SPDX-FileCopyrightText: 2021 ETH Zurich and University of Bologna +# +# SPDX-License-Identifier: Apache-2.0 + +from typing import List, Optional, Sequence, Type + +import numpy as np + +from Deeploy.AbstractDataTypes import Pointer +from Deeploy.CommonExtensions.TypeCheckers.SignPropTypeChecker import SignPropTypeChecker +from Deeploy.DeeployTypes import ConstantBuffer, OperatorRepresentation, VariableBuffer + diff --git a/Deeploy/Targets/xheep/__init__.py b/Deeploy/Targets/xheep/__init__.py new file mode 100644 index 0000000000..be436b64a3 --- /dev/null +++ b/Deeploy/Targets/xheep/__init__.py @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: 2024 ETH Zurich and University of Bologna +# +# SPDX-License-Identifier: Apache-2.0 + +from . import * diff --git a/TargetLibraries/xheep/CMakeLists.txt b/TargetLibraries/xheep/CMakeLists.txt new file mode 100644 index 0000000000..9f88cdf04d --- /dev/null +++ b/TargetLibraries/xheep/CMakeLists.txt @@ -0,0 +1,31 @@ +# Copyright (C) 2026 EPFL. +# Solderpad Hardware License, Version 2.1, see LICENSE.md for details. +# SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +# +# File: CMakeLists.txt +# Author: Mohammad Hossein Nikkhah +# Description: + +if(NOT DEFINED XHEEP_CRT_SOURCES OR NOT DEFINED XHEEP_RUNTIME_SOURCES) + message(FATAL_ERROR "Include cmake/xheep/xheep.cmake before TargetLibraries/xheep.") +endif() + +set_source_files_properties(${XHEEP_CRT_SOURCES} PROPERTIES COMPILE_FLAGS -DLANGUAGE_ASSEMBLY) + +add_library(xheep-runtime OBJECT + ${XHEEP_CRT_SOURCES} + ${XHEEP_RUNTIME_SOURCES} +) + +target_include_directories(xheep-runtime SYSTEM PUBLIC + ${XHEEP_INCLUDE_DIRS} +) + +add_library(deeployxheep INTERFACE) + +target_include_directories(deeployxheep INTERFACE + ${XHEEP_INCLUDE_DIRS} +) + +target_link_libraries(deeployxheep INTERFACE xheep-runtime) +target_sources(deeployxheep INTERFACE $) diff --git a/cmake/xheep/toolchain_gcc.cmake b/cmake/xheep/toolchain_gcc.cmake new file mode 100644 index 0000000000..fd56b7b3e7 --- /dev/null +++ b/cmake/xheep/toolchain_gcc.cmake @@ -0,0 +1,57 @@ +# Copyright (C) 2026 EPFL. +# Solderpad Hardware License, Version 2.1, see LICENSE.md for details. +# SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +# +# File: toolchain_gcc.cmake +# Author: Mohammad Hossein Nikkhah +# Description: + +set(TOOLCHAIN_PREFIX ${TOOLCHAIN_INSTALL_DIR}/bin/riscv32-unknown-elf) + +set(CMAKE_SYSTEM_NAME Generic) + +set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc) +set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++) +set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER}) +set(CMAKE_OBJCOPY ${TOOLCHAIN_PREFIX}-objcopy) +set(CMAKE_OBJDUMP ${TOOLCHAIN_PREFIX}-objdump) +set(CMAKE_AR ${TOOLCHAIN_PREFIX}-ar) +set(SIZE ${TOOLCHAIN_PREFIX}-size) + +set(ISA rv32imc_zicsr CACHE STRING "X-HEEP RISC-V ISA") +set(CMAKE_SYSTEM_PROCESSOR ${ISA} CACHE STRING "X-HEEP RISC-V ISA") + + +set(CMAKE_EXECUTABLE_SUFFIX ".elf") + +add_compile_options( + -march=${ISA} + -ffunction-sections + -fdata-sections + # -fomit-frame-pointer + # -fno-jump-tables + # -fno-tree-loop-distribute-patterns + -O2 + -g + # -DNUM_CORES=${NUM_CORES} + -MMD + -MP +) + +add_link_options( + -MMD + -MP + -march=${ISA} + -nostartfiles + # -nostdlibs + -Wl,--print-memory-usage +) + +link_libraries( + -lc + -lm + -lgcc +) + +add_compile_definitions(__LINK_LD) +add_compile_definitions(__TOOLCHAIN_GCC__) diff --git a/cmake/xheep/xheep.cmake b/cmake/xheep/xheep.cmake new file mode 100644 index 0000000000..376d66dab7 --- /dev/null +++ b/cmake/xheep/xheep.cmake @@ -0,0 +1,105 @@ +# Copyright (C) 2026 EPFL. +# Solderpad Hardware License, Version 2.1, see LICENSE.md for details. +# SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +# +# File: xheep.cmake +# Author: Mohammad Hossein Nikkhah +# Description: + +if(NOT XHEEP_HOME AND DEFINED ENV{XHEEP_HOME}) + set(XHEEP_HOME "$ENV{XHEEP_HOME}" CACHE PATH "Path to X-HEEP checkout" FORCE) +else() + set(XHEEP_HOME "" CACHE PATH "Path to X-HEEP checkout") +endif() + +if(NOT XHEEP_HOME) + message(FATAL_ERROR "XHEEP_HOME is not set. Pass -DXHEEP_HOME= or export XHEEP_HOME.") +endif() + +set(XHEEP_SW_DIR "${XHEEP_HOME}/sw") +set(XHEEP_LINKER_DIR "${XHEEP_SW_DIR}/linker") +set(XHEEP_CRT_DIR "${XHEEP_SW_DIR}/device/lib/crt") +set(XHEEP_RUNTIME_DIR "${XHEEP_SW_DIR}/device/lib/runtime") +set(XHEEP_DEVICE_DIR "${XHEEP_SW_DIR}/device") +set(XHEEP_TARGET sim CACHE STRING "X-HEEP software target") +set(XHEEP_LINKER on_chip CACHE STRING "X-HEEP linker mode") +set(XHEEP_COMPILER_PREFIX "riscv32-unknown-" CACHE STRING "X-HEEP GCC compiler prefix") +set_property(CACHE XHEEP_LINKER PROPERTY STRINGS on_chip flash_load flash_exec) + +if(XHEEP_LINKER STREQUAL on_chip) + set(XHEEP_LINKER_FILE link.ld) + set(XHEEP_CRT_TYPE ON_CHIP) +elseif(XHEEP_LINKER STREQUAL flash_load) + set(XHEEP_LINKER_FILE link_flash_load.ld) + set(XHEEP_CRT_TYPE FLASH_LOAD) +elseif(XHEEP_LINKER STREQUAL flash_exec) + set(XHEEP_LINKER_FILE link_flash_exec.ld) + set(XHEEP_CRT_TYPE FLASH_EXEC) +else() + message(FATAL_ERROR "Unsupported XHEEP_LINKER '${XHEEP_LINKER}'. Use on_chip, flash_load, or flash_exec.") +endif() + +set(XHEEP_LINKER_SCRIPT "${XHEEP_LINKER_DIR}/${XHEEP_LINKER_FILE}") +set(XHEEP_CRT_SOURCES + "${XHEEP_CRT_DIR}/crt0.S" + "${XHEEP_CRT_DIR}/vectors.S" +) +set(XHEEP_RUNTIME_SOURCES + "${XHEEP_RUNTIME_DIR}/core_v_mini_mcu.c" + "${XHEEP_RUNTIME_DIR}/handler.c" + "${XHEEP_RUNTIME_DIR}/init.c" + "${XHEEP_RUNTIME_DIR}/syscalls.c" + "${XHEEP_DEVICE_DIR}/lib/base/memory.c" + "${XHEEP_DEVICE_DIR}/lib/base/mmio.c" + "${XHEEP_DEVICE_DIR}/lib/drivers/soc_ctrl/soc_ctrl.c" + "${XHEEP_DEVICE_DIR}/lib/drivers/uart/uart.c" +) + +set(XHEEP_REQUIRED_FILES + "${XHEEP_LINKER_SCRIPT}" + ${XHEEP_CRT_SOURCES} + ${XHEEP_RUNTIME_SOURCES} + "${XHEEP_RUNTIME_DIR}/core_v_mini_mcu.h" + "${XHEEP_RUNTIME_DIR}/core_v_mini_mcu_memory.h" + "${XHEEP_DEVICE_DIR}/target/${XHEEP_TARGET}/x-heep.h" +) + +foreach(XHEEP_REQUIRED_FILE IN LISTS XHEEP_REQUIRED_FILES) + if(NOT EXISTS "${XHEEP_REQUIRED_FILE}") + message(FATAL_ERROR + "Required X-HEEP file not found: ${XHEEP_REQUIRED_FILE}\n" + "Run `make mcu-gen` in ${XHEEP_HOME}, then reconfigure Deeploy.") + endif() +endforeach() + +file(GLOB_RECURSE XHEEP_DEVICE_HEADERS CONFIGURE_DEPENDS "${XHEEP_DEVICE_DIR}/*.h") +set(XHEEP_INCLUDE_DIRS + "${XHEEP_SW_DIR}" + "${XHEEP_DEVICE_DIR}" + "${XHEEP_DEVICE_DIR}/target/${XHEEP_TARGET}" +) +foreach(XHEEP_HEADER IN LISTS XHEEP_DEVICE_HEADERS) + get_filename_component(XHEEP_HEADER_DIR "${XHEEP_HEADER}" DIRECTORY) + list(APPEND XHEEP_INCLUDE_DIRS "${XHEEP_HEADER_DIR}") +endforeach() +list(REMOVE_DUPLICATES XHEEP_INCLUDE_DIRS) +include_directories(SYSTEM ${XHEEP_INCLUDE_DIRS}) + +set(DEEPLOY_ARCH XHEEP) + +add_compile_definitions( + DEEPLOY_XHEEP_PLATFORM + HOST_BUILD + ${XHEEP_CRT_TYPE} + INTERNAL_CRTO + portasmHANDLE_INTERRUPT=vSystemIrqHandler +) + +set(XHEEP_GCC_LIB_DIR "${TOOLCHAIN_INSTALL_DIR}/${XHEEP_COMPILER_PREFIX}elf/lib") +add_link_options( + -T "${XHEEP_LINKER_SCRIPT}" + -static + -Wl,--gc-sections + "-L${XHEEP_GCC_LIB_DIR}" + -specs=nano.specs +) From 938761c9a530073216b20284cb1badd2d1b2b41e Mon Sep 17 00:00:00 2001 From: mohasnik Date: Mon, 13 Jul 2026 12:10:39 +0200 Subject: [PATCH 2/2] first build is complete, let's test it on x-heep --- CMakeLists.txt | 2 +- DeeployTest/CMakeLists.txt | 2 ++ DeeployTest/deeployRunner_xheep.py | 12 ++++++++++++ DeeployTest/testUtils/deeployRunner.py | 1 + DeeployTest/testUtils/platformMapping.py | 12 +++++++++--- cmake/xheep/xheep.cmake | 3 +++ 6 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 DeeployTest/deeployRunner_xheep.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ea405e9ae..83dc3ab7c2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,7 +21,7 @@ if(TOOLCHAIN STREQUAL GCC) endif() set(platform MemPool CACHE STRING "Platform (MemPool, SoftHier, QEMU, Siracusa, Siracusa_w_neureka, PULP-Open, GAP9, Generic, Snitch, Xheep)") -set_property(CACHE platform PROPERTY STRINGS 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 Snitc Xheep) if(platform STREQUAL MemPool) message(STATUS "Building for platform 'MemPool'") diff --git a/DeeployTest/CMakeLists.txt b/DeeployTest/CMakeLists.txt index b7f3535790..2b7c8565ed 100644 --- a/DeeployTest/CMakeLists.txt +++ b/DeeployTest/CMakeLists.txt @@ -50,6 +50,8 @@ elseif(DEEPLOY_ARCH STREQUAL SNITCH) add_subdirectory(Platforms/Snitch) elseif(DEEPLOY_ARCH STREQUAL CHIMERA) add_subdirectory(Platforms/Chimera) +elseif(DEEPLOY_ARCH STREQUAL XHEEP) + add_subdirectory(Platforms/Generic) elseif(platform STREQUAL GAP9) # Search for hex files generated by Python code generator diff --git a/DeeployTest/deeployRunner_xheep.py b/DeeployTest/deeployRunner_xheep.py new file mode 100644 index 0000000000..24e4c233b5 --- /dev/null +++ b/DeeployTest/deeployRunner_xheep.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python +# SPDX-FileCopyrightText: 2025 ETH Zurich and University of Bologna +# +# SPDX-License-Identifier: Apache-2.0 + +import sys + +from testUtils.deeployRunner import main + +if __name__ == "__main__": + #TODO : make the co-simulation with X-HEEP repository + sys.exit(main(default_platform = "Xheep", default_simulator = "none", tiling_enabled = False)) diff --git a/DeeployTest/testUtils/deeployRunner.py b/DeeployTest/testUtils/deeployRunner.py index bad25ee7f5..0f3f43e3e1 100644 --- a/DeeployTest/testUtils/deeployRunner.py +++ b/DeeployTest/testUtils/deeployRunner.py @@ -367,6 +367,7 @@ def main(default_platform: Optional[str] = None, "chimera": "Chimera", "softhier": "SoftHier", "xdna2": "XDNA2", + "xheep": "Xheep" } if args.platform: diff --git a/DeeployTest/testUtils/platformMapping.py b/DeeployTest/testUtils/platformMapping.py index 9155ed77ae..4809e4249d 100644 --- a/DeeployTest/testUtils/platformMapping.py +++ b/DeeployTest/testUtils/platformMapping.py @@ -29,8 +29,9 @@ from Deeploy.Targets.Snitch.Platform import SnitchOptimizer, SnitchPlatform from Deeploy.Targets.SoftHier.Deployer import SoftHierDeployer from Deeploy.Targets.SoftHier.Platform import SoftHierOptimizer, SoftHierPlatform +from Deeploy.Targets.xheep.Platform import XHeepOptimizer, XHeepPlatform -_SIGNPROP_PLATFORMS = ["Apollo3", "Apollo4", "QEMU-ARM", "Generic", "MemPool", "SoftHier"] +_SIGNPROP_PLATFORMS = ["Apollo3", "Apollo4", "QEMU-ARM", "Generic", "MemPool", "SoftHier", "Xheep"] _NONSIGNPROP_PLATFORMS = ["Siracusa", "Siracusa_w_neureka", "PULPOpen", "Snitch", "Chimera", "GAP9", "XDNA2"] _PLATFORMS = _SIGNPROP_PLATFORMS + _NONSIGNPROP_PLATFORMS @@ -79,6 +80,9 @@ def mapPlatform(platformName: str) -> Tuple[DeploymentPlatform, bool]: elif platformName == "XDNA2": from Deeploy.Targets.XDNA2.Platform import XDNA2Platform Platform = XDNA2Platform() + + elif platformName == "Xheep": + Platform = XHeepPlatform() else: raise RuntimeError(f"Deployment platform {platformName} is not implemented") @@ -173,7 +177,7 @@ def mapDeployer(platform: DeploymentPlatform, deeployStateDir = deeployStateDir, inputOffsets = inputOffsets) - elif isinstance(platform, GenericPlatform): + elif isinstance(platform, (GenericPlatform, XHeepPlatform)): # WIESEP: CMSIS performs add-multiply-divide and we normally do multiply-add-divide # Because these deployer were fine-tuned with a add-multiply-divide aware deployer can emulate this # behavior with the EmulateCMSISRequantPass @@ -276,7 +280,9 @@ def mapDeployer(platform: DeploymentPlatform, name = name, default_channels_first = default_channels_first, deeployStateDir = deeployStateDir) - + + # TODO: add a branch for X-HEEP if needed + else: # Lazy-import XDNA2 to avoid requiring mlir-aie on non-XDNA2 platforms try: diff --git a/cmake/xheep/xheep.cmake b/cmake/xheep/xheep.cmake index 376d66dab7..25c7ec657d 100644 --- a/cmake/xheep/xheep.cmake +++ b/cmake/xheep/xheep.cmake @@ -53,6 +53,7 @@ set(XHEEP_RUNTIME_SOURCES "${XHEEP_DEVICE_DIR}/lib/base/mmio.c" "${XHEEP_DEVICE_DIR}/lib/drivers/soc_ctrl/soc_ctrl.c" "${XHEEP_DEVICE_DIR}/lib/drivers/uart/uart.c" + "${XHEEP_DEVICE_DIR}/lib/drivers/fast_intr_ctrl/fast_intr_ctrl.c" ) set(XHEEP_REQUIRED_FILES @@ -62,6 +63,7 @@ set(XHEEP_REQUIRED_FILES "${XHEEP_RUNTIME_DIR}/core_v_mini_mcu.h" "${XHEEP_RUNTIME_DIR}/core_v_mini_mcu_memory.h" "${XHEEP_DEVICE_DIR}/target/${XHEEP_TARGET}/x-heep.h" + "${XHEEP_DEVICE_DIR}/lib/drivers/fast_intr_ctrl/fast_intr_ctrl.c" ) foreach(XHEEP_REQUIRED_FILE IN LISTS XHEEP_REQUIRED_FILES) @@ -89,6 +91,7 @@ set(DEEPLOY_ARCH XHEEP) add_compile_definitions( DEEPLOY_XHEEP_PLATFORM + DEEPLOY_GENERIC_PLATFORM HOST_BUILD ${XHEEP_CRT_TYPE} INTERNAL_CRTO