From 88938969d6a20f5019816f83b2fb39592fb8f76d Mon Sep 17 00:00:00 2001 From: na255940 <nermine.ali@cea.fr> Date: Wed, 12 Jun 2024 20:15:32 +0200 Subject: [PATCH] adds generation and conversion functions from cpp export --- aidge_export_cpp_hls/kernels/activation.hpp | 6 ++- aidge_export_cpp_hls/kernels/pooling.hpp | 6 ++- .../static/include/network/rescaling.hpp | 9 ++-- aidge_export_cpp_hls/static/main.cpp | 2 + .../templates/memory/mem_info.jinja | 8 ++- .../templates/network/network_forward.jinja | 18 +++++++ aidge_export_cpp_hls/utils/__init__.py | 27 ++++++++++ aidge_export_cpp_hls/utils/converter.py | 35 +++++++++++++ aidge_export_cpp_hls/utils/generation.py | 51 +++++++++++++++++++ 9 files changed, 153 insertions(+), 9 deletions(-) create mode 100644 aidge_export_cpp_hls/utils/__init__.py create mode 100644 aidge_export_cpp_hls/utils/converter.py create mode 100644 aidge_export_cpp_hls/utils/generation.py diff --git a/aidge_export_cpp_hls/kernels/activation.hpp b/aidge_export_cpp_hls/kernels/activation.hpp index d4a3054..0c62a7a 100644 --- a/aidge_export_cpp_hls/kernels/activation.hpp +++ b/aidge_export_cpp_hls/kernels/activation.hpp @@ -9,7 +9,7 @@ template<typename Output_T, typename T, typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr> __attribute__((always_inline)) inline -Output_T saturate (T value, int32_t sat) +Output_T saturate (T value, int32_t /*sat*/) { return value; } @@ -49,6 +49,10 @@ Output_T activation_forward_value (Sum_T weightedSum, break; } + // Value fixed here for now, but it should be generated by + // the export module or determined by the type of Output_T + // For now it only works for int8_t and uint8_t + const uint32_t NB_BITS = 8; return saturate<Output_T>(rescaling(weightedSum, output), NB_BITS); } diff --git a/aidge_export_cpp_hls/kernels/pooling.hpp b/aidge_export_cpp_hls/kernels/pooling.hpp index b6e0e63..478b6a5 100644 --- a/aidge_export_cpp_hls/kernels/pooling.hpp +++ b/aidge_export_cpp_hls/kernels/pooling.hpp @@ -3,6 +3,8 @@ #include "network/typedefs.hpp" #include "network/utils.hpp" +#include <limits> +#include <stdexcept> template<int NB_CHANNELS, @@ -84,7 +86,7 @@ void pooling_forward( outputs[oOffset + output] = maxVal; } else if (POOLING_TYPE == Average) { - SUM_T sum = 0; + int32_t sum = 0; for (int sy = 0; sy < POOL_HEIGHT; ++sy) { if ((PADDING_Y != 0 @@ -114,7 +116,7 @@ void pooling_forward( outputs[oOffset + output] = (Output_T) (sum / (POOL_HEIGHT * POOL_WIDTH)); } else { - throw(std::runtime_error, "The export only supports Max and Average pooling."); + throw std::runtime_error("The export only supports Max and Average pooling."); } } } diff --git a/aidge_export_cpp_hls/static/include/network/rescaling.hpp b/aidge_export_cpp_hls/static/include/network/rescaling.hpp index 6b4c481..856010d 100644 --- a/aidge_export_cpp_hls/static/include/network/rescaling.hpp +++ b/aidge_export_cpp_hls/static/include/network/rescaling.hpp @@ -1,14 +1,15 @@ #ifndef __AIDGE_EXPORT_CPP_NETWORK_RESCALING__ #define __AIDGE_EXPORT_CPP_NETWORK_RESCALING__ -// For this demo -#define SUM_T float -#define NB_BITS -32 struct NoScaling { - SUM_T operator()(SUM_T weightedSum, unsigned int /*output*/) const { + + template<typename Sum_T> + Sum_T operator()(Sum_T weightedSum, unsigned int /*output*/) const + { return weightedSum; } + }; diff --git a/aidge_export_cpp_hls/static/main.cpp b/aidge_export_cpp_hls/static/main.cpp index d50afdf..3f2b930 100644 --- a/aidge_export_cpp_hls/static/main.cpp +++ b/aidge_export_cpp_hls/static/main.cpp @@ -5,6 +5,8 @@ int main() { + // Example for MNIST dataset + // Feel free to change this file for your own projects const unsigned int nb_classes = 10; float results[nb_classes]; diff --git a/aidge_export_cpp_hls/templates/memory/mem_info.jinja b/aidge_export_cpp_hls/templates/memory/mem_info.jinja index 54d04f7..fe1c3c0 100644 --- a/aidge_export_cpp_hls/templates/memory/mem_info.jinja +++ b/aidge_export_cpp_hls/templates/memory/mem_info.jinja @@ -4,9 +4,13 @@ #define MEMORY_SIZE {{ mem_size }} -{% for offset in offsets %} -#define {{ offset }} +{% for i in range(mem_info|length) -%} +{%- set layer_name = mem_info[i][0] %} +/* {{layer_name}} memory */ +{% for j in range(1, mem_info[i]|length) %} +#define {{ layer_name|upper }}_{{ mem_info_legends[j]|upper }} {{ mem_info[i][j] }} {%- endfor %} +{% endfor %} #endif /* MEM_INFO_H */ \ No newline at end of file diff --git a/aidge_export_cpp_hls/templates/network/network_forward.jinja b/aidge_export_cpp_hls/templates/network/network_forward.jinja index 9c568cc..793716b 100644 --- a/aidge_export_cpp_hls/templates/network/network_forward.jinja +++ b/aidge_export_cpp_hls/templates/network/network_forward.jinja @@ -23,3 +23,21 @@ void CCS_BLOCK(model_forward)(ac_sync &start, const {{input_t}}* {{inputs}}, {{o } private: + mem_in<{{name|upper}}_NB_CHANNELS, + {{name|upper}}_CHANNELS_HEIGHT, + {{name|upper}}_CHANNELS_WIDTH, + {{name|upper}}_OUTPUTS_HEIGHT, + {{name|upper}}_OUTPUTS_WIDTH, + {{name|upper}}_PADDING_Y, + {{name|upper}}_PADDING_X, + {{name|upper}}_STRIDE_Y, + {{name|upper}}_STRIDE_X, + {{name|upper}}_KERNEL_HEIGHT, + {{name|upper}}_KERNEL_WIDTH, + {{name|upper}}_ACTIVATION, + DATA_T, + {{child_name|upper}}_in_t> {# it's the name of the next layer not the current one #} + mem_in_inst; + + {# Need to add all transitions between layers below #} + diff --git a/aidge_export_cpp_hls/utils/__init__.py b/aidge_export_cpp_hls/utils/__init__.py new file mode 100644 index 0000000..0728388 --- /dev/null +++ b/aidge_export_cpp_hls/utils/__init__.py @@ -0,0 +1,27 @@ +from pathlib import Path +import os + +# Constants +FILE = Path(__file__).resolve() +ROOT = FILE.parents[1] + + +OPERATORS_REGISTRY = {} + +def operator_register(*args): + + key_list = [arg for arg in args] + + def decorator(operator): + class Wrapper(operator): + def __init__(self, *args, **kwargs): + return operator(*args, **kwargs) + + for key in key_list: + OPERATORS_REGISTRY[key] = operator + + return Wrapper + return decorator + +def supported_operators(): + return list(OPERATORS_REGISTRY.keys()) diff --git a/aidge_export_cpp_hls/utils/converter.py b/aidge_export_cpp_hls/utils/converter.py new file mode 100644 index 0000000..d706d5a --- /dev/null +++ b/aidge_export_cpp_hls/utils/converter.py @@ -0,0 +1,35 @@ +import numpy as np +import aidge_core + +def numpy_dtype2ctype(dtype): + if dtype == np.int8: + return "int8_t" + elif dtype == np.int16: + return "int16_t" + elif dtype == np.int32: + return "int32_t" + elif dtype == np.int64: + return "int64_t" + elif dtype == np.float32: + return "float" + elif dtype == np.float64: + return "double" + # Add more dtype mappings as needed + else: + raise ValueError(f"Unsupported {dtype} dtype") + + +def aidge_datatype2ctype(datatype): + if datatype == aidge_core.DataType.Int8: + return "int8_t" + elif datatype == aidge_core.DataType.Int32: + return "int32_t" + elif datatype == aidge_core.DataType.Int64: + return "int64_t" + elif datatype == aidge_core.DataType.Float32: + return "float" + elif datatype == aidge_core.DataType.Float64: + return "double" + # Add more dtype mappings as needed + else: + raise ValueError(f"Unsupported {datatype} aidge datatype") \ No newline at end of file diff --git a/aidge_export_cpp_hls/utils/generation.py b/aidge_export_cpp_hls/utils/generation.py new file mode 100644 index 0000000..4478ef7 --- /dev/null +++ b/aidge_export_cpp_hls/utils/generation.py @@ -0,0 +1,51 @@ +import re +import os +import shutil +from jinja2 import Environment, FileSystemLoader + + +def get_functions_from_c_file(file_path): + functions = [] + pattern = r'\w+\s+(\w+)\s*\(([^)]*)\)\s*{' + + with open(file_path, 'r') as file: + file_content = file.read() + + matches = re.findall(pattern, file_content) + for match in matches: + function_name = match[0] + arguments = match[1].split(',') + arguments = [arg.strip() for arg in arguments] + + return_type = get_return_type(file_content, function_name) + + function_string = f"{return_type} {function_name}({', '.join(arguments)});" + functions.append(function_string) + + return functions + + +def get_return_type(file_content, function_name): + pattern = rf'\w+\s+{function_name}\s*\([^)]*\)\s*{{' + return_type = re.search(pattern, file_content).group() + return_type = return_type.split()[0].strip() + return return_type + + +def get_functions_from_c_folder(folder_path): + functions = [] + + for _, _, files in os.walk(folder_path): + for file in files: + functions += get_functions_from_c_file(os.path.join(folder_path, file)) + + return functions + + +def copyfile(filename, dst_folder): + + # If directory doesn't exist, create it + if not os.path.exists(dst_folder): + os.makedirs(dst_folder) + + shutil.copy(filename, dst_folder) -- GitLab