diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c8a024c15..2e903eeb5e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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'") @@ -46,6 +46,8 @@ elseif(platform STREQUAL SoftHier) message(STATUS "Building for platform 'SoftHier'") elseif(platform STREQUAL Chimera) message(STATUS "Building for platform 'Chimera'") +elseif(platform STREQUAL Spatz) + message(STATUS "Building for platform 'Spatz'") else() message(FATAL_ERROR "Invalid platform '${platform}' specified!") endif() @@ -300,4 +302,34 @@ if(platform STREQUAL Chimera) 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() diff --git a/Deeploy/Targets/Generic/Bindings.py b/Deeploy/Targets/Generic/Bindings.py index 308b179aef..4b0ecfc258 100644 --- a/Deeploy/Targets/Generic/Bindings.py +++ b/Deeploy/Targets/Generic/Bindings.py @@ -19,12 +19,12 @@ GatherTemplate, GemmTemplate, IntegerDivTemplate, ITAMaxTemplate, ITAPartialMaxTemplate, MatMulTemplate, \ MaxPoolTemplate, MulTemplate, PadTemplate, QuantTemplate, ReduceMeanTemplate, ReduceSumTemplate, \ RequantShiftTemplate, ReshapeTemplate, RQIntegerDivTemplate, RQSiGELUTemplate, SliceTemplate, TransposeTemplate, \ - iGELUTemplate, iLayernormTemplate, iRMSNormTemplate, iSoftmaxTemplate + 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()]) @@ -327,3 +327,14 @@ ConvTransposeTemplate.referenceTemplate, BasicTransformer) for type in FloatDataTypes ] + +BasicTopKBindings = [ + NodeBinding( + TopKChecker( + [PointerClass(float32_t), PointerClass(int8_t)], # inputs + [PointerClass(float32_t), PointerClass(int8_t)] # outputs + ), + TopKTemplate.referenceTemplate, + BasicTransformer, + ) +] diff --git a/Deeploy/Targets/Generic/Layers.py b/Deeploy/Targets/Generic/Layers.py index cc733937cc..51b7b45dd4 100644 --- a/Deeploy/Targets/Generic/Layers.py +++ b/Deeploy/Targets/Generic/Layers.py @@ -709,3 +709,15 @@ def computeOps(self): numPx = opRep['dim_im_out_x'] return numPx * opsPerPx + + +class TopKLayer(ONNXLayer): + + def __init__(self, maps: List[NodeMapper]): + super().__init__(maps) + +# def computeOps(self): +# ??? +# +# def computeShapes(self): +# ??? \ No newline at end of file diff --git a/Deeploy/Targets/Generic/Parsers.py b/Deeploy/Targets/Generic/Parsers.py index ad787d9e4b..206f901fc9 100644 --- a/Deeploy/Targets/Generic/Parsers.py +++ b/Deeploy/Targets/Generic/Parsers.py @@ -982,7 +982,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 @@ -1002,10 +1002,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 Exception: + 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 @@ -2886,3 +2893,30 @@ def parseNodeCtxt(self, self.operatorRepresentation['size'] = int(np.prod(data_in.shape)) return ctxt, True + +# 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 + self.operatorRepresentation['indices_out'] = indices_out.name + + print("parsenodectxt returning true") + + return ctxt, True diff --git a/Deeploy/Targets/Generic/Platform.py b/Deeploy/Targets/Generic/Platform.py index e05e897270..2e4601bdd4 100644 --- a/Deeploy/Targets/Generic/Platform.py +++ b/Deeploy/Targets/Generic/Platform.py @@ -14,19 +14,19 @@ BasicPad1DBindings, BasicPad2DBindings, BasicPowBindings, BasicQuantBindings, BasicReduceMeanBindings, \ BasicReduceSumBindings, BasicReluBinding, BasicReshapeBindings, BasicRQIntegerDivBinding, BasicRQSBindings, \ BasicRQSGELUBinding, BasicSliceBindings, BasicSoftmaxBindings, BasicSqrtBindings, BasicTransposeBindings, \ - DummyBinding + DummyBinding, BasicTopKBindings from Deeploy.Targets.Generic.Layers import AddLayer, BatchNormalizationLayer, ConcatLayer, ConvLayer, \ ConvTransposeLayer, DebugPrintLayer, DequantLayer, DivLayer, GatherLayer, GELULayer, GEMMLayer, ITAMaxLayer, \ LayerNormLayer, MatMulLayer, MaxPoolLayer, MulLayer, PadLayer, PowLayer, QuantLayer, ReduceMeanLayer, \ ReduceSumLayer, ReluLayer, RequantShiftLayer, ReshapeLayer, RQIntegerDivLayer, RQSiGELULayer, SliceLayer, \ - SoftmaxLayer, SqrtLayer, TransposeLayer + SoftmaxLayer, SqrtLayer, TransposeLayer, TopKLayer from Deeploy.Targets.Generic.Parsers import AddParser, BatchNormParser, ConcatParser, ConvTranspose1DParser, \ DebugParser, DequantParser, DivParser, DummyParser, FlattenParser, GatherParser, GELUParser, GenericConv1DParser, \ GenericConv2DParser, GenericDWConv1DParser, GenericDWConv2DParser, GenericGEMMParser, GenericMaxPool2DParser, \ IntegerDivParser, ITAMaxParser, ITAPartialMaxParser, LayerNormParser, MatMulParser, MaxPool1DParser, MulParser, \ Pad1DParser, Pad2DParser, PowParser, QuantParser, ReduceMeanParser, ReduceSumParser, ReluParser, \ RequantShiftParser, ReshapeParser, RQIntegerDivParser, RQSiGELUParser, SliceParser, SoftmaxParser, SqrtParser, \ - TransposeParser, UnsqueezeParser, iLayerNormParser, iSoftmaxParser + TransposeParser, TopKParser, UnsqueezeParser, iLayerNormParser, iSoftmaxParser from Deeploy.Targets.Generic.Templates import AllocateTemplate, FreeTemplate from Deeploy.Targets.Generic.TopologyOptimizationPasses.Passes import DequantPatternPass, ExtractPaddingFromConvPass, \ ExtractPaddingFromPoolPass, MatMulAddMergePass, MergeConstAddAndRequantPass, QuantPatternPass, \ @@ -67,6 +67,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) @@ -113,6 +114,7 @@ 'RQIntegerDiv': RQIntegerDivLayer([RQIntegerDivMapper]), 'Squeeze': ReshapeLayer([UnsqueezeMapper]), 'Transpose': TransposeLayer([TransposeMapper]), + 'TopK': TopKLayer([TopKMapper]), 'Unsqueeze': ReshapeLayer([UnsqueezeMapper]), 'Slice': SliceLayer([SliceMapper]), 'Quant': QuantLayer([QuantMapper]), diff --git a/Deeploy/Targets/Generic/Templates/GatherTemplate.py b/Deeploy/Targets/Generic/Templates/GatherTemplate.py index dd5e534fa4..4efce4d050 100644 --- a/Deeploy/Targets/Generic/Templates/GatherTemplate.py +++ b/Deeploy/Targets/Generic/Templates/GatherTemplate.py @@ -10,8 +10,18 @@ width = int(data_in_type.referencedType.typeWidth/8) %> BEGIN_SINGLE_CORE +% if num_indices == 1: 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 """) diff --git a/Deeploy/Targets/Generic/Templates/TopKTemplate.py b/Deeploy/Targets/Generic/Templates/TopKTemplate.py new file mode 100644 index 0000000000..3f9b6474fa --- /dev/null +++ b/Deeploy/Targets/Generic/Templates/TopKTemplate.py @@ -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 +""") \ No newline at end of file diff --git a/Deeploy/Targets/Generic/TypeCheckers.py b/Deeploy/Targets/Generic/TypeCheckers.py index c2c8d436f8..5d363206f8 100644 --- a/Deeploy/Targets/Generic/TypeCheckers.py +++ b/Deeploy/Targets/Generic/TypeCheckers.py @@ -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] \ No newline at end of file diff --git a/Deeploy/Targets/Spatz/Bindings.py b/Deeploy/Targets/Spatz/Bindings.py new file mode 100644 index 0000000000..0430bb80ce --- /dev/null +++ b/Deeploy/Targets/Spatz/Bindings.py @@ -0,0 +1,29 @@ +from Deeploy.DeeployTypes import CodeTransformation, NodeBinding +from Deeploy.CommonExtensions.CodeTransformationPasses.MemoryAllocation import ArgumentStructGeneration, \ + MemoryManagementGeneration +from Deeploy.FutureExtension.CodeTransformationPasses.FutureCodeTransformation import FutureGeneration +from Deeploy.AbstractDataTypes import PointerClass +from Deeploy.CommonExtensions.DataTypes import FloatDataTypes, IntegerDataTypes, SignedIntegerDataTypes, float32_t, int8_t, int32_t, uint8_t +from Deeploy.Targets.Generic.TypeCheckers import GatherChecker +from Deeploy.Targets.Spatz.Templates import GatherTemplate + +BasicTransformer = CodeTransformation([ArgumentStructGeneration(), MemoryManagementGeneration(), FutureGeneration()]) + +SpatzGatherBindings = [ + NodeBinding( + GatherChecker( + [PointerClass(type), PointerClass(int32_t)], + [PointerClass(type)] + ), + GatherTemplate.referenceTemplate, + BasicTransformer + ) for type in SignedIntegerDataTypes] + [ + NodeBinding( + GatherChecker( + [PointerClass(float32_t), PointerClass(type)], + [PointerClass(float32_t)] + ), + GatherTemplate.referenceTemplate, + BasicTransformer + ) for type in IntegerDataTypes +] \ No newline at end of file diff --git a/Deeploy/Targets/Spatz/Deployer.py b/Deeploy/Targets/Spatz/Deployer.py new file mode 100644 index 0000000000..2442059606 --- /dev/null +++ b/Deeploy/Targets/Spatz/Deployer.py @@ -0,0 +1,48 @@ +# SPDX-FileCopyrightText: 2023 ETH Zurich and University of Bologna +# +# SPDX-License-Identifier: Apache-2.0 + +from typing import Callable, Dict, Type + +import onnx_graphsurgeon as gs + +from Deeploy.AbstractDataTypes import Pointer +from Deeploy.CommonExtensions.NetworkDeployers.SignPropDeployer import SignPropDeployer +from Deeploy.CommonExtensions.OptimizationPasses.TopologyOptimizationPasses.DebugPasses import DebugPrintMergePass +from Deeploy.CommonExtensions.OptimizationPasses.TopologyOptimizationPasses.LoweringOptimizationPasses import \ + NCHWtoNHWCPass, TransposeMatmulInputsPass +from Deeploy.DeeployTypes import DeploymentPlatform, TopologyOptimizer +from Deeploy.Targets.Generic.TopologyOptimizationPasses.Passes import TransposeConstOptPass, TransposeMergePass + + +class SpatzDeployer(SignPropDeployer): + + def __init__(self, + graph: gs.Graph, + deploymentPlatform: DeploymentPlatform, + inputTypes: Dict[str, Type[Pointer]], + loweringOptimizer: TopologyOptimizer, + scheduler: Callable = lambda x: x, + name: str = 'DeeployNetwork', + default_channels_first = False, + deeployStateDir: str = "DeeployStateDir", + inputOffsets: Dict[str, int] = {}): + + super().__init__(graph, + deploymentPlatform, + inputTypes, + loweringOptimizer, + scheduler, + name, + default_channels_first = default_channels_first, + deeployStateDir = deeployStateDir) + +# self.inputOffsets = inputOffsets +# +# self.loweringOptimizer.passes += [ +# TransposeMatmulInputsPass(), +# NCHWtoNHWCPass(self.default_channels_first), +# TransposeMergePass(), +# TransposeConstOptPass(), +# DebugPrintMergePass() +# ] diff --git a/Deeploy/Targets/Spatz/Platform.py b/Deeploy/Targets/Spatz/Platform.py new file mode 100644 index 0000000000..6cede5ca8a --- /dev/null +++ b/Deeploy/Targets/Spatz/Platform.py @@ -0,0 +1,79 @@ +from typing import List + +from Deeploy.DeeployTypes import VariableBuffer, TransientBuffer, ConstantBuffer, StructBuffer, \ + NodeMapper, NodeTemplate, TopologyOptimizer, DeploymentEngine, DeploymentPlatform + +from Deeploy.Targets.Generic.Templates import AllocateTemplate as GenericAllocateTemplate +from Deeploy.Targets.Spatz.Templates import AllocateTemplate as SpatzAllocateTemplate +from Deeploy.Targets.Spatz.Templates import FreeTemplate as SpatzFreeTemplate + +from Deeploy.Targets.Spatz.Bindings import SpatzGatherBindings +from Deeploy.Targets.Generic.Bindings import BasicAddBindings, BasicMatMulBindings, BasicSoftmaxBindings, BasicTopKBindings +from Deeploy.Targets.Generic.Layers import AddLayer, GEMMLayer, SoftmaxLayer, TopKLayer, GatherLayer +from Deeploy.Targets.Generic.Parsers import AddParser, MatMulParser, SoftmaxParser, TopKParser, GatherParser + +SpatzAddMapper = NodeMapper(AddParser(), BasicAddBindings) +MatMulMapper = NodeMapper(MatMulParser(), BasicMatMulBindings) +SoftmaxMapper = NodeMapper(SoftmaxParser(), BasicSoftmaxBindings) +TopKMapper = NodeMapper(TopKParser(), BasicTopKBindings) +GatherMapper = NodeMapper(GatherParser(), SpatzGatherBindings) + +SpatzMapping = { + 'Add': AddLayer([SpatzAddMapper]), + 'MatMul': GEMMLayer([MatMulMapper]), + 'Softmax': SoftmaxLayer([SoftmaxMapper]), + 'TopK': TopKLayer([TopKMapper]), + 'Gather': GatherLayer([GatherMapper]), + # sparse attention : ... +} + + +class SpatzaVariableBuffer(VariableBuffer): + initTemplate = GenericAllocateTemplate.referenceInitTemplate + allocTemplate = SpatzAllocateTemplate.referenceAllocateTemplate + deallocTemplate = SpatzFreeTemplate.spatzLocalTemplate + + +class SpatzTransientBuffer(TransientBuffer): + initTemplate = GenericAllocateTemplate.referenceInitTemplate + allocTemplate = SpatzAllocateTemplate.referenceAllocateTemplate + deallocTemplate = SpatzFreeTemplate.spatzLocalTemplate + + +class SpatzConstantBuffer(ConstantBuffer): + initTemplate = GenericAllocateTemplate.referenceGlobalInitTemplate + allocTemplate = GenericAllocateTemplate.referenceGlobalAllocateTemplate + deallocTemplate = NodeTemplate("") # const not deallocated + + +class SpatzStructBuffer(StructBuffer): + initTemplate = GenericAllocateTemplate.referenceStructInitTemplate + allocTemplate = GenericAllocateTemplate.referenceStructAllocateTemplate + deallocTemplate = NodeTemplate("") # struct not deallocated ? + + +SpatzOptimizer = TopologyOptimizer([ + # TODO add something ? +], name = "SpatzOptimizer") + +includeList = [ + "DeeploySpatzMath.h", +] + + +class SpatzEngine(DeploymentEngine): + def __init__(self, name: str, Mapping = SpatzMapping, initCode = "", includeList = includeList) -> None: + super().__init__(name, Mapping, initCode, includeList) + + +class SpatzPlatform(DeploymentPlatform): + + def __init__( self, + engines = [SpatzEngine("SpatzVectorProcessor")], + variableBuffer = SpatzaVariableBuffer, + transientBuffer = SpatzTransientBuffer, + constantBuffer = SpatzConstantBuffer, + structBuffer = SpatzStructBuffer, + includeList: List[str] = includeList + ): + super().__init__(engines, variableBuffer, constantBuffer, structBuffer, transientBuffer) diff --git a/Deeploy/Targets/Spatz/Templates/AllocateTemplate.py b/Deeploy/Targets/Spatz/Templates/AllocateTemplate.py new file mode 100644 index 0000000000..78d24b9f14 --- /dev/null +++ b/Deeploy/Targets/Spatz/Templates/AllocateTemplate.py @@ -0,0 +1,5 @@ +from Deeploy.DeeployTypes import NodeTemplate + +# allocate +referenceAllocateTemplate = NodeTemplate( + "${name} = (${type.typeName}) snrt_l1alloc(${type.referencedType.typeWidth//8} * ${size});\n") diff --git a/Deeploy/Targets/Spatz/Templates/FreeTemplate.py b/Deeploy/Targets/Spatz/Templates/FreeTemplate.py new file mode 100644 index 0000000000..f67cb3de38 --- /dev/null +++ b/Deeploy/Targets/Spatz/Templates/FreeTemplate.py @@ -0,0 +1,5 @@ +from Deeploy.DeeployTypes import NodeTemplate + +# snrt_l1alloc currently does not support free-ing of memory (spatz/sw/snRuntime/src/alloc.c) +spatzLocalTemplate = NodeTemplate("") +spatzGlobalTemplate = NodeTemplate("") \ No newline at end of file diff --git a/Deeploy/Targets/Spatz/Templates/GatherTemplate.py b/Deeploy/Targets/Spatz/Templates/GatherTemplate.py new file mode 100644 index 0000000000..d54a8f1374 --- /dev/null +++ b/Deeploy/Targets/Spatz/Templates/GatherTemplate.py @@ -0,0 +1,23 @@ +from Deeploy.DeeployTypes import NodeTemplate + +referenceTemplate = NodeTemplate(""" +// Gather (Name: ${nodeName}, Op: ${nodeOp}) +<% +width = int(data_in_type.referencedType.typeWidth/8) +%> +BEGIN_SINGLE_CORE +% if num_indices == 1: +for (uint32_t i=0; i<${batch}; ++i) { + snrt_dma_start_1d(${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) { + snrt_dma_start_1d(${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 +""") diff --git a/DeeployTest/CMakeLists.txt b/DeeployTest/CMakeLists.txt index b7f3535790..71f632cbd2 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 SPATZ) + add_subdirectory(Platforms/Spatz) elseif(platform STREQUAL GAP9) # Search for hex files generated by Python code generator diff --git a/DeeployTest/Platforms/Spatz/CMakeLists.txt b/DeeployTest/Platforms/Spatz/CMakeLists.txt new file mode 100644 index 0000000000..2eec84530b --- /dev/null +++ b/DeeployTest/Platforms/Spatz/CMakeLists.txt @@ -0,0 +1,13 @@ +set(ProjectId ${TESTNAME}) + +file(GLOB_RECURSE SOURCES + main.c +) + +add_deeploy_executable(${ProjectId} EXCLUDE_FROM_ALL ${SOURCES}) + +target_link_libraries(${ProjectId} PRIVATE network deeploylib) +target_compile_options(${ProjectId} INTERFACE network) + +add_spatz_gvsoc_emulation(${ProjectId} "spatz_v2") +add_spatz_vsim_simulation(${ProjectId}) \ No newline at end of file diff --git a/DeeployTest/Platforms/Spatz/main.c b/DeeployTest/Platforms/Spatz/main.c new file mode 100644 index 0000000000..2ab98ce54d --- /dev/null +++ b/DeeployTest/Platforms/Spatz/main.c @@ -0,0 +1,69 @@ + +#include +#include +#include "printf.h" + +#include "Network.h" +#include "testinputs.h" +#include "testoutputs.h" + +int main() { + const unsigned int cid = snrt_cluster_core_idx(); + + // do it only with one of the two spatz cores + if (cid==0){ + printf("Initializing network...\r\n"); + + InitNetwork(0, 1); + + for (uint32_t buf = 0; buf < DeeployNetwork_num_inputs; buf++) { + snrt_dma_start_1d(DeeployNetwork_inputs[buf], testInputVector[buf], DeeployNetwork_inputs_bytes[buf]); + } + + printf("Running network...\r\n"); + RunNetwork(0, 1); + + int32_t tot_err = 0; + uint32_t tot = 0; + OUTPUTTYPE diff; + OUTPUTTYPE expected, actual; + + for (uint32_t buf = 0; buf < DeeployNetwork_num_outputs; buf++) { + tot += DeeployNetwork_outputs_bytes[buf] / sizeof(OUTPUTTYPE); + for (uint32_t i = 0; + i < DeeployNetwork_outputs_bytes[buf] / sizeof(OUTPUTTYPE); i++) { + expected = ((OUTPUTTYPE *)testOutputVector[buf])[i]; + actual = ((OUTPUTTYPE *)DeeployNetwork_outputs[buf])[i]; + diff = expected - actual; + +#if ISOUTPUTFLOAT == 1 + // RUNWANG: Allow margin of error for float32_t + if ((diff < -1e-4) || (diff > 1e-4)) { + tot_err += 1; + printf("Expected: %10.6f ", (float)expected); + printf("Actual: %10.6f ", (float)actual); + printf("Diff: %10.6f at Index %12u in Output %u\r\n", (float)diff, i, + buf); + } +#else + // RUNWANG: No margin for integer comparison + if (diff != 0) { + tot_err += 1; + printf("Expected: %4d ", expected); + printf("Actual: %4d ", actual); + printf("Diff: %4d at Index %12u in Output %u\r\n", diff, i, buf); + } +#endif + } + } + + printf("Errors: %d out of %d \r\n", tot_err, tot); + + return tot_err; + } else { + // wait for core 0 to finish + snrt_cluster_hw_barrier(); + return 0; + } + +} \ No newline at end of file diff --git a/DeeployTest/Tests/Kernels/FP32/Attention/inputs.npz b/DeeployTest/Tests/Kernels/FP32/Attention/inputs.npz new file mode 100644 index 0000000000..1f27f7766e Binary files /dev/null and b/DeeployTest/Tests/Kernels/FP32/Attention/inputs.npz differ diff --git a/DeeployTest/Tests/Kernels/FP32/Attention/network.onnx b/DeeployTest/Tests/Kernels/FP32/Attention/network.onnx new file mode 100644 index 0000000000..e35b7eb103 Binary files /dev/null and b/DeeployTest/Tests/Kernels/FP32/Attention/network.onnx differ diff --git a/DeeployTest/Tests/Kernels/FP32/Attention/outputs.npz b/DeeployTest/Tests/Kernels/FP32/Attention/outputs.npz new file mode 100644 index 0000000000..b65882fc0a Binary files /dev/null and b/DeeployTest/Tests/Kernels/FP32/Attention/outputs.npz differ diff --git a/DeeployTest/Tests/Kernels/FP32/TopK/10/inputs.npz b/DeeployTest/Tests/Kernels/FP32/TopK/10/inputs.npz new file mode 100644 index 0000000000..a02c827160 Binary files /dev/null and b/DeeployTest/Tests/Kernels/FP32/TopK/10/inputs.npz differ diff --git a/DeeployTest/Tests/Kernels/FP32/TopK/10/network.onnx b/DeeployTest/Tests/Kernels/FP32/TopK/10/network.onnx new file mode 100644 index 0000000000..13fae39c48 Binary files /dev/null and b/DeeployTest/Tests/Kernels/FP32/TopK/10/network.onnx differ diff --git a/DeeployTest/Tests/Kernels/FP32/TopK/10/outputs.npz b/DeeployTest/Tests/Kernels/FP32/TopK/10/outputs.npz new file mode 100644 index 0000000000..b9bc6c4183 Binary files /dev/null and b/DeeployTest/Tests/Kernels/FP32/TopK/10/outputs.npz differ diff --git a/DeeployTest/Tests/Kernels/FP32/TopK/20/inputs.npz b/DeeployTest/Tests/Kernels/FP32/TopK/20/inputs.npz new file mode 100644 index 0000000000..0b66bfd41b Binary files /dev/null and b/DeeployTest/Tests/Kernels/FP32/TopK/20/inputs.npz differ diff --git a/DeeployTest/Tests/Kernels/FP32/TopK/20/network.onnx b/DeeployTest/Tests/Kernels/FP32/TopK/20/network.onnx new file mode 100644 index 0000000000..2252473f7a Binary files /dev/null and b/DeeployTest/Tests/Kernels/FP32/TopK/20/network.onnx differ diff --git a/DeeployTest/Tests/Kernels/FP32/TopK/20/outputs.npz b/DeeployTest/Tests/Kernels/FP32/TopK/20/outputs.npz new file mode 100644 index 0000000000..abccfba295 Binary files /dev/null and b/DeeployTest/Tests/Kernels/FP32/TopK/20/outputs.npz differ diff --git a/DeeployTest/deeployRunner_spatz.py b/DeeployTest/deeployRunner_spatz.py new file mode 100644 index 0000000000..d17e3f510f --- /dev/null +++ b/DeeployTest/deeployRunner_spatz.py @@ -0,0 +1,11 @@ +import sys + +from testUtils.deeployRunner import main + +if __name__ == "__main__": + sys.exit( + main( + default_platform = "Spatz", + default_simulator = "gvsoc", + ) + ) diff --git a/DeeployTest/testUtils/deeployRunner.py b/DeeployTest/testUtils/deeployRunner.py index a5a8d70ef3..0c98e254aa 100644 --- a/DeeployTest/testUtils/deeployRunner.py +++ b/DeeployTest/testUtils/deeployRunner.py @@ -348,6 +348,7 @@ def main(default_platform: Optional[str] = None, "snitch": "Snitch", "chimera": "Chimera", "softhier": "SoftHier", + "spatz": "Spatz", } if args.platform: @@ -388,6 +389,7 @@ def main(default_platform: Optional[str] = None, "Snitch": "gvsoc", "Chimera": "gvsoc", "SoftHier": "gvsoc", + "Spatz": "vsim", } simulator = simulator_map.get(platform, "host") log.info(f"No simulator specified, using default for {platform}: {simulator}") diff --git a/DeeployTest/testUtils/platformMapping.py b/DeeployTest/testUtils/platformMapping.py index 9d526906f9..69a83f1e8d 100644 --- a/DeeployTest/testUtils/platformMapping.py +++ b/DeeployTest/testUtils/platformMapping.py @@ -10,6 +10,8 @@ from Deeploy.DeeployTypes import DeploymentPlatform, NetworkDeployer, TopologyOptimizer from Deeploy.MemoryLevelExtension.MemoryLevels import MemoryHierarchy, MemoryLevel from Deeploy.MemoryLevelExtension.NetworkDeployers.MemoryLevelDeployer import MemoryPlatform, MemoryPlatformWrapper +from Deeploy.Targets.Spatz.Deployer import SpatzDeployer +from Deeploy.Targets.Spatz.Platform import SpatzOptimizer, SpatzPlatform from Deeploy.Targets.Chimera.Deployer import ChimeraDeployer from Deeploy.Targets.Chimera.Platform import ChimeraOptimizer, ChimeraPlatform from Deeploy.Targets.CortexM.Deployer import CMSISDeployer @@ -31,7 +33,7 @@ from Deeploy.Targets.SoftHier.Platform import SoftHierOptimizer, SoftHierPlatform _SIGNPROP_PLATFORMS = ["Apollo3", "Apollo4", "QEMU-ARM", "Generic", "MemPool", "SoftHier"] -_NONSIGNPROP_PLATFORMS = ["Siracusa", "Siracusa_w_neureka", "PULPOpen", "Snitch", "Chimera", "GAP9"] +_NONSIGNPROP_PLATFORMS = ["Siracusa", "Siracusa_w_neureka", "PULPOpen", "Snitch", "Chimera", "GAP9", "Spatz"] _PLATFORMS = _SIGNPROP_PLATFORMS + _NONSIGNPROP_PLATFORMS @@ -76,6 +78,9 @@ def mapPlatform(platformName: str) -> Tuple[DeploymentPlatform, bool]: elif platformName == "Chimera": Platform = ChimeraPlatform() + elif platformName == "Spatz": + Platform = SpatzPlatform() + else: raise RuntimeError(f"Deployment platform {platformName} is not implemented") @@ -272,6 +277,18 @@ def mapDeployer(platform: DeploymentPlatform, name = name, default_channels_first = default_channels_first, deeployStateDir = deeployStateDir) + + elif isinstance(platform, (SpatzPlatform)): + deployer = SpatzDeployer( + graph, + platform, + inputTypes, + SpatzOptimizer, + scheduler, + name = name, + default_channels_first = default_channels_first, + deeployStateDir = deeployStateDir + ) else: raise RuntimeError(f"Deployer for platform {platform} is not implemented") diff --git a/DeeployTest/test_platforms.py b/DeeployTest/test_platforms.py index 6d9f3cfcd7..6be4bef197 100644 --- a/DeeployTest/test_platforms.py +++ b/DeeployTest/test_platforms.py @@ -110,6 +110,14 @@ def param_id(param): "model_tests": SNITCH_MODEL_TESTS, "default_num_cores": SNITCH_DEFAULT_NUM_CORES, }, + "spatz": { + "platform": "Spatz", + "simulator": "vsim", + # TODO: Define KERNEL_TESTS and MODEL_TESTS for Spatz + "kernel_tests": [], + "model_tests": [], + # "default_num_cores": , + }, "gap9": { "platform": "GAP9", "simulator": "gvsoc", diff --git a/Makefile b/Makefile index d40a49da11..88385251fc 100644 --- a/Makefile +++ b/Makefile @@ -27,10 +27,12 @@ PICOLIBC_RV32IMF_INSTALL_DIR ?= ${LLVM_INSTALL_DIR}/picolibc/riscv/rv32imf CHIMERA_SDK_INSTALL_DIR ?= ${DEEPLOY_INSTALL_DIR}/chimera-sdk PULP_SDK_INSTALL_DIR ?= ${DEEPLOY_INSTALL_DIR}/pulp-sdk SNITCH_INSTALL_DIR ?= ${DEEPLOY_INSTALL_DIR}/snitch_cluster +SPATZ_INSTALL_DIR ?= ${DEEPLOY_INSTALL_DIR}/spatz QEMU_INSTALL_DIR ?= ${DEEPLOY_INSTALL_DIR}/qemu BANSHEE_INSTALL_DIR ?= ${DEEPLOY_INSTALL_DIR}/banshee MEMPOOL_INSTALL_DIR ?= ${DEEPLOY_INSTALL_DIR}/mempool GVSOC_INSTALL_DIR ?= ${DEEPLOY_INSTALL_DIR}/gvsoc +GVSOC_SPATZ_INSTALL_DIR ?= ${DEEPLOY_INSTALL_DIR}/gvsoc_spatz SOFTHIER_INSTALL_DIR ?= ${DEEPLOY_INSTALL_DIR}/softhier MINIMALLOC_INSTALL_DIR ?= ${DEEPLOY_INSTALL_DIR}/minimalloc XTL_INSTALL_DIR ?= ${DEEPLOY_INSTALL_DIR}/xtl @@ -44,8 +46,11 @@ PICOLIBC_COMMIT_HASH ?= 31ff1b3601b379e4cab63837f253f59729ce1fef PULP_SDK_COMMIT_HASH ?= 7f4f22516157a1b7c55bcbbc72ca81326180b3b4 MEMPOOL_COMMIT_HASH ?= affd45d94e05e375a6966af6a762deeb182a7bd6 SNITCH_COMMIT_HASH ?= e02cc9e3f24b92d4607455d5345caba3eb6273b2 +SPATZ_COMMIT_HASH ?= 9974c6aeabead537e232a0409742cb6fc534171e SOFTHIER_COMMIT_HASH ?= 0 # bowwang: to be updated -GVSOC_COMMIT_HASH ?= edfcd8398840ceb1e151711befa06678b05f06a0 +# GVSOC_COMMIT_HASH ?= edfcd8398840ceb1e151711befa06678b05f06a0 # old +GVSOC_COMMIT_HASH ?= 209c147cbd293d5c1590694e68c489122c777acc # new +# GVSOC_SPATZ_COMMIT_HASH ?= 209c147cbd293d5c1590694e68c489122c777acc MINIMALLOC_COMMMIT_HASH ?= e9eaf54094025e1c246f9ec231b905f8ef42a29d CHIMERA_SDK_COMMIT_HASH ?= b2392f6efcff75c03f4c65eaf3e12104442b22ea XTL_VERSION ?= 0.7.5 @@ -69,7 +74,7 @@ else $(error unsupported platform $(OS)) endif -all: toolchain emulators docs echo-bash +all: toolchain emulators # docs echo-bash echo-bash: @@ -79,6 +84,7 @@ echo-bash: @echo "export PULP_SDK_HOME=${PULP_SDK_INSTALL_DIR}" @echo "export CHIMERA_SDK_HOME=${CHIMERA_SDK_INSTALL_DIR}" @echo "export SNITCH_HOME=${SNITCH_INSTALL_DIR}" + @echo "export SPATZ_HOME=${SPATZ_INSTALL_DIR}" @echo "export GVSOC_INSTALL_DIR=${GVSOC_INSTALL_DIR}" @echo "export SOFTHIER_INSTALL_DIR=${SOFTHIER_INSTALL_DIR}" @echo "export LLVM_INSTALL_DIR=${LLVM_INSTALL_DIR}" @@ -124,6 +130,7 @@ ${LLVM_INSTALL_DIR}: ${TOOLCHAIN_DIR}/llvm-project llvm: ${LLVM_INSTALL_DIR} +# runtimes for different architectures ${LLVM_CLANG_RT_RISCV_RV32IM}: ${TOOLCHAIN_DIR}/llvm-project cd ${TOOLCHAIN_DIR}/llvm-project && mkdir -p build-compiler-rt-riscv-rv32im \ && cd build-compiler-rt-riscv-rv32im; \ @@ -429,16 +436,55 @@ ${SNITCH_INSTALL_DIR}: ${TOOLCHAIN_DIR}/snitch_cluster snitch_runtime: ${SNITCH_INSTALL_DIR} +${TOOLCHAIN_DIR}/spatz: + cd ${TOOLCHAIN_DIR} && \ + git clone https://github.com/pulp-platform/spatz.git && \ + cd ${TOOLCHAIN_DIR}/spatz && git checkout ${SPATZ_COMMIT_HASH} && \ + git submodule update --init --recursive + +${SPATZ_INSTALL_DIR}: ${TOOLCHAIN_DIR}/spatz + mkdir -p ${SPATZ_INSTALL_DIR} + cp -r ${TOOLCHAIN_DIR}/spatz/ ${SPATZ_INSTALL_DIR}/../ + cd ${SPATZ_INSTALL_DIR} + make all -j8 && \ + python3.6 -m venv .venv && \ + .venv/bin/pip install jsonref jsonschema jstyleson dataclasses hjson mako && \ + source .venv/bin/activate && \ + source util/iis-env.sh && \ + make init && \ + cd hw/system/spatz_cluster/ && \ + make sw + +spatz_runtime: ${SPATZ_INSTALL_DIR} + +# ${TOOLCHAIN_DIR}/gvsoc_spatz: +# cd ${TOOLCHAIN_DIR} && \ +# git clone https://github.com/gvsoc/gvsoc.git gvsoc_spatz && \ +# cd ${TOOLCHAIN_DIR}/gvsoc_spatz && git checkout ${GVSOC_SPATZ_COMMIT_HASH} && \ +# git submodule update --init --recursive && \ +# python3 -m venv venv && source venv/bin/activate &&\ +# pip3 install -r core/requirements.txt && pip3 install -r gapy/requirements.txt && pip3 install psutil && \ +# cd core && git apply ${TOOLCHAIN_DIR}/gvsoc.patch +# +# +# ${GVSOC_SPATZ_INSTALL_DIR}: ${TOOLCHAIN_DIR}/gvsoc_spatz +# cd ${TOOLCHAIN_DIR}/gvsoc_spatz && \ +# source venv/bin/activate &&\ +# CXX=g++-11.2.0 CC=gcc-11.2.0 CMAKE=cmake-3.18.1 make all TARGETS=spatz_v2 INSTALLDIR=${GVSOC_SPATZ_INSTALL_DIR} +# +# gvsoc_spatz: ${GVSOC_SPATZ_INSTALL_DIR} + ${TOOLCHAIN_DIR}/gvsoc: cd ${TOOLCHAIN_DIR} && \ git clone https://github.com/gvsoc/gvsoc.git && \ cd ${TOOLCHAIN_DIR}/gvsoc && git checkout ${GVSOC_COMMIT_HASH} && \ git submodule update --init --recursive && \ - pip install -r core/requirements.txt && pip install -r gapy/requirements.txt + pip3 install -r core/requirements.txt && pip3 install -r gapy/requirements.txt && pip3 install psutil &&\ + cd core && git apply ${TOOLCHAIN_DIR}/gvsoc.patch ${GVSOC_INSTALL_DIR}: ${TOOLCHAIN_DIR}/gvsoc cd ${TOOLCHAIN_DIR}/gvsoc && \ - XTENSOR_INSTALL_DIR=${XTENSOR_INSTALL_DIR}/include XTL_INSTALL_DIR=${XTL_INSTALL_DIR}/include XSIMD_INSTALL_DIR=${XSIMD_INSTALL_DIR}/include make all TARGETS="pulp.snitch.snitch_cluster_single siracusa chimera" build INSTALLDIR=${GVSOC_INSTALL_DIR} + XTENSOR_INSTALL_DIR=${XTENSOR_INSTALL_DIR}/include XTL_INSTALL_DIR=${XTL_INSTALL_DIR}/include XSIMD_INSTALL_DIR=${XSIMD_INSTALL_DIR}/include make all TARGETS="pulp.snitch.snitch_cluster_single siracusa chimera spatz_v2" build INSTALLDIR=${GVSOC_INSTALL_DIR} gvsoc: ${GVSOC_INSTALL_DIR} diff --git a/TargetLibraries/Spatz/CMakeLists.txt b/TargetLibraries/Spatz/CMakeLists.txt new file mode 100644 index 0000000000..7d192c2416 --- /dev/null +++ b/TargetLibraries/Spatz/CMakeLists.txt @@ -0,0 +1,13 @@ +file(GLOB_RECURSE SOURCES + "src/**" +) + +include(cmake/spatz-runtime-precompiled.cmake) + +add_deeploy_library(deeployspatz STATIC ${SOURCES}) +target_include_directories(deeployspatz + PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/inc +) +target_include_directories(deeployspatz SYSTEM PUBLIC ${SPATZ_RUNTIME_INCLUDE}) +target_link_libraries(deeployspatz INTERFACE spatz-runtime) diff --git a/TargetLibraries/Spatz/cmake/spatz-runtime-precompiled.cmake b/TargetLibraries/Spatz/cmake/spatz-runtime-precompiled.cmake new file mode 100644 index 0000000000..42e15e1b31 --- /dev/null +++ b/TargetLibraries/Spatz/cmake/spatz-runtime-precompiled.cmake @@ -0,0 +1,27 @@ + +set(SPATZ_RUNTIME_BASE_INCLUDE + ${SPATZ_HOME}/sw/snRuntime/include + ${SPATZ_HOME}/sw/snRuntime/vendor + ${SPATZ_HOME}/sw/toolchain/riscv-opcodes +) + +set(SPATZ_CLUSTER_LINK_INCLUDE + ${SPATZ_HOME}/hw/system/spatz_cluster/sw/build/snRuntime +) + +set(SPATZ_LINKER_SCRIPT ${SPATZ_HOME}/hw/system/spatz_cluster/sw/build/snRuntime/common.ld) +# set(SPATZ_LINKER_SCRIPT ${SNITCH_RUNTIME_HOME}/base.ld) +if(NOT EXISTS ${SPATZ_LINKER_SCRIPT}) + message(FATAL_ERROR "Spatz linker script not found: ${SPATZ_LINKER_SCRIPT}") +endif() + +set(SPATZ_CLUSTER_LINK_OPTIONS + -Wl,--gc-sections + -T ${SPATZ_LINKER_SCRIPT} +) + +set(SPATZ_RUNTIME_INCLUDE ${SPATZ_RUNTIME_BASE_INCLUDE}) + +add_library(spatz-runtime INTERFACE) +target_link_directories(spatz-runtime INTERFACE ${SPATZ_CLUSTER_LINK_INCLUDE}) +target_link_libraries(spatz-runtime INTERFACE ${SPATZ_CLUSTER_LINK_OPTIONS} libsnRuntime-cluster.a) diff --git a/TargetLibraries/Spatz/inc/DeeploySpatzMath.h b/TargetLibraries/Spatz/inc/DeeploySpatzMath.h new file mode 100644 index 0000000000..0e856fbf1c --- /dev/null +++ b/TargetLibraries/Spatz/inc/DeeploySpatzMath.h @@ -0,0 +1,20 @@ +/* + * SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DEEPLOY_SPATZ_MATH_HEADER_ +#define __DEEPLOY_SPATZ_MATH_HEADER_ + +#include +#include + +#include "DeeployBasicMath.h" +#include "snrt.h" + +#define BEGIN_SINGLE_CORE if (core_id == 0) { +#define END_SINGLE_CORE } +#define SINGLE_CORE if (core_id == 0) + +#endif // __DEEPLOY_SPATZ_MATH_HEADER_ diff --git a/TargetLibraries/Spatz/inc/Util.h b/TargetLibraries/Spatz/inc/Util.h new file mode 100644 index 0000000000..893d687fa1 --- /dev/null +++ b/TargetLibraries/Spatz/inc/Util.h @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna +// SPDX-License-Identifier: Apache-2.0 + +#ifndef SPATZ_UTIL_H +#define SPATZ_UTIL_H + +void spatz_util_dummy(void); + +#endif // SPATZ_UTIL_H diff --git a/TargetLibraries/Spatz/src/Util.c b/TargetLibraries/Spatz/src/Util.c new file mode 100644 index 0000000000..9c30c11f49 --- /dev/null +++ b/TargetLibraries/Spatz/src/Util.c @@ -0,0 +1,5 @@ +// SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna +// SPDX-License-Identifier: Apache-2.0 + +// Minimal stub for Spatz runtime linkage +void spatz_util_dummy(void) {} diff --git a/cmake/simulation.cmake b/cmake/simulation.cmake index 55525feedd..983dc0e4ee 100644 --- a/cmake/simulation.cmake +++ b/cmake/simulation.cmake @@ -102,3 +102,19 @@ macro(add_gvsoc_emulation name target) USES_TERMINAL ) endmacro() + +macro(add_spatz_gvsoc_emulation name target) + set(GVSOC_WORKDIR ${CMAKE_BINARY_DIR}/gvsoc_workdir) + make_directory(${GVSOC_WORKDIR}) + set(GVSOC_BINARY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name}") + add_custom_target(gvsoc_${name} + DEPENDS ${name} + WORKING_DIRECTORY ${GVSOC_WORKDIR} + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_BINARY_DIR}/*.bin ${GVSOC_WORKDIR}/ || true + COMMAND bash -c "${GVSOC_INSTALL_DIR}/bin/gvrun --target ${target} --param chip/soc/binary=${GVSOC_BINARY} run" + COMMENT "Simulating deeploytest ${name} with gvsoc for the target ${target}" + POST_BUILD + USES_TERMINAL + VERBATIM + ) +endmacro() diff --git a/cmake/spatz/spatz.cmake b/cmake/spatz/spatz.cmake new file mode 100644 index 0000000000..b715f625c9 --- /dev/null +++ b/cmake/spatz/spatz.cmake @@ -0,0 +1,30 @@ +add_compile_definitions( + DEEPLOY_SPATZ_PLATFORM +) + +set(DEEPLOY_ARCH SPATZ) + +set(num_threads ${NUM_CORES}) + +macro(add_spatz_vsim_simulation name) + add_custom_target(vsim_${name} + WORKING_DIRECTORY ${SPATZ_HOME}/hw/system/spatz_cluster + DEPENDS ${name} + COMMAND ${QUESTA} bin/spatz_cluster.vsim + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name} || true + COMMENT "Simulating deeploytest with vsim (Spatz cluster)" + POST_BUILD + USES_TERMINAL + VERBATIM + ) +endmacro() + +add_compile_options( + -ffast-math +) + +add_link_options( + -ffast-math + -Wl,--gc-sections +) + diff --git a/cmake/spatz/toolchain_llvm.cmake b/cmake/spatz/toolchain_llvm.cmake new file mode 100644 index 0000000000..3a149c04f0 --- /dev/null +++ b/cmake/spatz/toolchain_llvm.cmake @@ -0,0 +1,72 @@ + +set(CMAKE_SYSTEM_NAME Generic) + +# Crucial: Point CMake to the specialized Clang toolchain instead of system cc +set(SPATZ_TOOLCHAIN_DIR ${SPATZ_HOME}/sw/toolchain/llvm-project/build/bin) + +set(CMAKE_C_COMPILER ${SPATZ_TOOLCHAIN_DIR}/clang) +set(CMAKE_CXX_COMPILER ${SPATZ_TOOLCHAIN_DIR}/clang++) +set(CMAKE_ASM_COMPILER ${SPATZ_TOOLCHAIN_DIR}/clang) +set(CMAKE_OBJCOPY ${SPATZ_TOOLCHAIN_DIR}/llvm-objcopy) +set(CMAKE_OBJDUMP ${SPATZ_TOOLCHAIN_DIR}/llvm-objdump) +set(CMAKE_LINKER ${SPATZ_TOOLCHAIN_DIR}/ld.lld) +set(CMAKE_EXECUTABLE_SUFFIX ".elf") + +set(ISA rv32imafdvzfh_xdma) + +# Compile options based on user's manual compilation commands +add_compile_options( + -target riscv32-unknown-elf + # -MP + -mcpu=snitch + -mcmodel=small + + -ffast-math + -fno-builtin-printf + -fno-common + -falign-loops=16 + -ffunction-sections + -Wextra + + # LLVM specific flags from user command + -mllvm -misched-topdown + -menable-experimental-extensions + -mno-relax + + -march=${ISA} + -mabi=ilp32d + -isystem ${SPATZ_HOME}/sw/toolchain/riscv-gnu-toolchain/riscv-newlib/newlib/libc/include + + # Optimization and debug + -O3 + -g +) + +# Link options matching user command +add_link_options( + # -target riscv32-unknown-elf + -mcpu=snitch + -march=${ISA} + -mabi=ilp32d + -mcmodel=small + + -fuse-ld=lld + -nostartfiles + + -ffast-math + -fno-common + -fno-builtin-printf + + -static + -Wl,-z,norelro + -Wl,--gc-sections + -Wl,--no-relax + + --gcc-toolchain=/usr/pack/riscv-1.0-kgf/spatz-gcc-7.1.1 +) + +# User command linked: -lm -lgcc -lm -lgcc libsnRuntime-cluster.a +# libsnRuntime-cluster.a is handled by our target_link_libraries(deeployspatz INTERFACE spatz-runtime) +link_libraries( + -lm -lgcc -lm -lgcc +) diff --git a/toolchain/gvsoc.patch b/toolchain/gvsoc.patch new file mode 100644 index 0000000000..22e65922a9 --- /dev/null +++ b/toolchain/gvsoc.patch @@ -0,0 +1,12 @@ +diff --git a/engine/src/launcher.cpp b/engine/src/launcher.cpp +index f0b1b654..48c83592 100644 +--- a/engine/src/launcher.cpp ++++ b/engine/src/launcher.cpp +@@ -21,6 +21,7 @@ + + #include + #include ++#include + #include + + #include