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