diff --git a/aidge_export_cpp_hls/kernels/activation.hpp b/aidge_export_cpp_hls/kernels/activation.hpp index d4a30540812571d460060f5dd2f13bdee18ea6ad..0c62a7a26c1e780043e09ce494a5812d5c5490b8 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 b6e0e638af47b5448d58f3e6963865c2c9f4dc00..478b6a58aed45e2bce0ed1683ad113f9c7a8bffb 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 6b4c48192c82d84269ef5417b9ec876b64c152bd..856010d97501f9d5c5a8cc77365e7fd335b757c0 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 d50afdfad4017c839882ef620cebb46930d3a419..3f2b930c733fe8c9bfe05e303357ef61ad59db2f 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 54d04f7040336cffbc1bf67fa51bd84be2151913..fe1c3c00f5c4cc78b56502de3e7d91208e22ff35 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 9c568cc2324e103eaf5119c496ecaea5d0b7ad13..793716bb0a3dccb4fc46c419dbb0a6919440d8cc 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 0000000000000000000000000000000000000000..0728388bec9ae3f7241392b2b9c88c2c61cd1ed7 --- /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 0000000000000000000000000000000000000000..d706d5a26f103316adfca0cd16f1146284e8177c --- /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 0000000000000000000000000000000000000000..4478ef7d664b3c59bd3499b0377d698da6cce834 --- /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)