From 758792c965c503cbf79fce9c0bf5de1627eb3df3 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sun, 19 Apr 2026 20:14:33 +0200 Subject: [PATCH 1/4] cnn: Enable for Unix/external build (fp32 only for now) --- src/emlearn_cnn_fp32/emlearn_cnn_fp32.py | 6 ++++++ src/emlearn_cnn_fp32/micropython.mk | 18 ++++++++++++++++++ src/manifest_unix.py | 1 + src/tinymaix_cnn/mod_cnn.c | 10 +++++++--- 4 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 src/emlearn_cnn_fp32/emlearn_cnn_fp32.py create mode 100644 src/emlearn_cnn_fp32/micropython.mk diff --git a/src/emlearn_cnn_fp32/emlearn_cnn_fp32.py b/src/emlearn_cnn_fp32/emlearn_cnn_fp32.py new file mode 100644 index 00000000..40e0ad55 --- /dev/null +++ b/src/emlearn_cnn_fp32/emlearn_cnn_fp32.py @@ -0,0 +1,6 @@ +# emlearn_cnn_fp32 - wrapper for frozen unix build +# Imports the native C module compiled with fp32 configuration +try: + from tinymaix_cnn_fp32_native import * +except ImportError: + pass diff --git a/src/emlearn_cnn_fp32/micropython.mk b/src/emlearn_cnn_fp32/micropython.mk new file mode 100644 index 00000000..1de87275 --- /dev/null +++ b/src/emlearn_cnn_fp32/micropython.mk @@ -0,0 +1,18 @@ +# emlearn_cnn_fp32 wrapper for Unix port +# Compiles mod_cnn.c with fp32 configuration + +CNN_SRC := $(USERMOD_DIR)/../tinymaix_cnn + +# Add C source file from tinymaix_cnn +SRC_USERMOD_C += $(CNN_SRC)/mod_cnn.c + +# Include paths +CFLAGS_USERMOD += -I$(CNN_SRC)/fp32 +CFLAGS_USERMOD += -I$(CNN_SRC)/../../dependencies/TinyMaix/include +CFLAGS_USERMOD += -I$(CNN_SRC)/../../dependencies/TinyMaix/src + +# Compile flags +CFLAGS_USERMOD += -Wno-error=unused-variable -Wno-error=multichar -Wdouble-promotion + +# Define CONFIG_FP32 for the C code +CFLAGS_USERMOD += -DCONFIG_FP32 diff --git a/src/manifest_unix.py b/src/manifest_unix.py index 9f55e5d0..176fc2f1 100644 --- a/src/manifest_unix.py +++ b/src/manifest_unix.py @@ -4,6 +4,7 @@ # Ref https://docs.micropython.org/en/latest/reference/manifest.html module("emlearn_trees.py", base_path='./emlearn_trees') module("emlearn_kmeans.py", base_path='./emlearn_kmeans') +module("emlearn_cnn_fp32.py", base_path='./emlearn_cnn_fp32') module("emlearn_fft.py", base_path='./emlearn_fft') module("emlearn_linreg.py", base_path='./emlearn_linreg') module("emlearn_logreg.py", base_path='./emlearn_logreg') diff --git a/src/tinymaix_cnn/mod_cnn.c b/src/tinymaix_cnn/mod_cnn.c index adee99fd..3ceeccfd 100644 --- a/src/tinymaix_cnn/mod_cnn.c +++ b/src/tinymaix_cnn/mod_cnn.c @@ -301,8 +301,8 @@ mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *a // Define a class static const mp_rom_map_elem_t mod_cnn_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_run), MP_ROM_PTR(&mod_cnn_run_obj) }, - { MP_ROM_QSTR(MP_QSTR_output_dimensions), MP_ROM_PTR(&mod_cnn_del_obj) }, - { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mod_cnn_output_dimensions_obj) } + { MP_ROM_QSTR(MP_QSTR_output_dimensions), MP_ROM_PTR(&mod_cnn_output_dimensions_obj) }, + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mod_cnn_del_obj) } }; static MP_DEFINE_CONST_DICT(mod_cnn_locals_dict, mod_cnn_locals_dict_table); @@ -325,8 +325,12 @@ const mp_obj_module_t mod_cnn_cmodule = { .globals = (mp_obj_dict_t *)&mod_cnn_globals, }; -// FIXME: unhardcode config part of module name +// Module name depends on CONFIG +#ifdef CONFIG_FP32 +MP_REGISTER_MODULE(MP_QSTR_tinymaix_cnn_fp32_native, mod_cnn_cmodule); +#else MP_REGISTER_MODULE(MP_QSTR_emlearn_cnn_int8, mod_cnn_cmodule); #endif +#endif From 47911a76ead7f1d09f8e4c1554c544b6877d5d3f Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sun, 19 Apr 2026 20:27:25 +0200 Subject: [PATCH 2/4] cnn: Try to support both int8 and fp32 for external A bit tricky since one needs to avoid duplicate symbols, and the MicroPython build system is quite opinionated --- src/emlearn_cnn_fp32/emlearn_cnn_fp32.c | 6 +++ src/emlearn_cnn_fp32/emlearn_cnn_fp32.py | 2 +- src/emlearn_cnn_fp32/micropython.mk | 13 +++---- src/emlearn_cnn_int8/emlearn_cnn_int8.c | 6 +++ src/emlearn_cnn_int8/emlearn_cnn_int8.py | 6 +++ src/emlearn_cnn_int8/micropython.mk | 15 ++++++++ src/manifest_unix.py | 1 + src/tinymaix_cnn/fp32/tm_port.h | 9 ++++- src/tinymaix_cnn/int8/tm_port.h | 9 ++++- src/tinymaix_cnn/mod_cnn.c | 47 ++++++++++++++++-------- 10 files changed, 85 insertions(+), 29 deletions(-) create mode 100644 src/emlearn_cnn_fp32/emlearn_cnn_fp32.c create mode 100644 src/emlearn_cnn_int8/emlearn_cnn_int8.c create mode 100644 src/emlearn_cnn_int8/emlearn_cnn_int8.py create mode 100644 src/emlearn_cnn_int8/micropython.mk diff --git a/src/emlearn_cnn_fp32/emlearn_cnn_fp32.c b/src/emlearn_cnn_fp32/emlearn_cnn_fp32.c new file mode 100644 index 00000000..8974438e --- /dev/null +++ b/src/emlearn_cnn_fp32/emlearn_cnn_fp32.c @@ -0,0 +1,6 @@ +/* + * emlearn_cnn_fp32 wrapper + * This file sets CONFIG_FP32 and includes the main mod_cnn.c + */ +#define CONFIG_FP32 +#include "../tinymaix_cnn/mod_cnn.c" diff --git a/src/emlearn_cnn_fp32/emlearn_cnn_fp32.py b/src/emlearn_cnn_fp32/emlearn_cnn_fp32.py index 40e0ad55..48463e8f 100644 --- a/src/emlearn_cnn_fp32/emlearn_cnn_fp32.py +++ b/src/emlearn_cnn_fp32/emlearn_cnn_fp32.py @@ -1,6 +1,6 @@ # emlearn_cnn_fp32 - wrapper for frozen unix build # Imports the native C module compiled with fp32 configuration try: - from tinymaix_cnn_fp32_native import * + from emlearn_cnn_fp32_native import * except ImportError: pass diff --git a/src/emlearn_cnn_fp32/micropython.mk b/src/emlearn_cnn_fp32/micropython.mk index 1de87275..ee2b6b91 100644 --- a/src/emlearn_cnn_fp32/micropython.mk +++ b/src/emlearn_cnn_fp32/micropython.mk @@ -1,18 +1,15 @@ # emlearn_cnn_fp32 wrapper for Unix port -# Compiles mod_cnn.c with fp32 configuration +# This wrapper sets CONFIG_FP32 before including mod_cnn.c CNN_SRC := $(USERMOD_DIR)/../tinymaix_cnn -# Add C source file from tinymaix_cnn -SRC_USERMOD_C += $(CNN_SRC)/mod_cnn.c +# Add wrapper C file which defines CONFIG_FP32 +SRC_USERMOD_C += $(USERMOD_DIR)/emlearn_cnn_fp32.c -# Include paths +# Include paths - need to include tm_port.h location CFLAGS_USERMOD += -I$(CNN_SRC)/fp32 CFLAGS_USERMOD += -I$(CNN_SRC)/../../dependencies/TinyMaix/include CFLAGS_USERMOD += -I$(CNN_SRC)/../../dependencies/TinyMaix/src -# Compile flags +# Compile flags to suppress TinyMaix warnings CFLAGS_USERMOD += -Wno-error=unused-variable -Wno-error=multichar -Wdouble-promotion - -# Define CONFIG_FP32 for the C code -CFLAGS_USERMOD += -DCONFIG_FP32 diff --git a/src/emlearn_cnn_int8/emlearn_cnn_int8.c b/src/emlearn_cnn_int8/emlearn_cnn_int8.c new file mode 100644 index 00000000..244a0c4c --- /dev/null +++ b/src/emlearn_cnn_int8/emlearn_cnn_int8.c @@ -0,0 +1,6 @@ +/* + * emlearn_cnn_int8 wrapper + * This file sets CONFIG_INT8 and includes the main mod_cnn.c + */ +#define CONFIG_INT8 +#include "../tinymaix_cnn/mod_cnn.c" diff --git a/src/emlearn_cnn_int8/emlearn_cnn_int8.py b/src/emlearn_cnn_int8/emlearn_cnn_int8.py new file mode 100644 index 00000000..665a9478 --- /dev/null +++ b/src/emlearn_cnn_int8/emlearn_cnn_int8.py @@ -0,0 +1,6 @@ +# emlearn_cnn_int8 - wrapper for frozen unix build +# Imports the native C module compiled with int8 configuration +try: + from emlearn_cnn_int8_native import * +except ImportError: + pass diff --git a/src/emlearn_cnn_int8/micropython.mk b/src/emlearn_cnn_int8/micropython.mk new file mode 100644 index 00000000..9080d63f --- /dev/null +++ b/src/emlearn_cnn_int8/micropython.mk @@ -0,0 +1,15 @@ +# emlearn_cnn_int8 wrapper for Unix port +# This wrapper sets CONFIG_INT8 before including mod_cnn.c + +CNN_SRC := $(USERMOD_DIR)/../tinymaix_cnn + +# Add wrapper C file which defines CONFIG_INT8 +SRC_USERMOD_C += $(USERMOD_DIR)/emlearn_cnn_int8.c + +# Include paths - need to include tm_port.h location +CFLAGS_USERMOD += -I$(CNN_SRC)/int8 +CFLAGS_USERMOD += -I$(CNN_SRC)/../../dependencies/TinyMaix/include +CFLAGS_USERMOD += -I$(CNN_SRC)/../../dependencies/TinyMaix/src + +# Compile flags to suppress TinyMaix warnings +CFLAGS_USERMOD += -Wno-error=unused-variable -Wno-error=multichar -Wdouble-promotion diff --git a/src/manifest_unix.py b/src/manifest_unix.py index 176fc2f1..6c90e270 100644 --- a/src/manifest_unix.py +++ b/src/manifest_unix.py @@ -4,6 +4,7 @@ # Ref https://docs.micropython.org/en/latest/reference/manifest.html module("emlearn_trees.py", base_path='./emlearn_trees') module("emlearn_kmeans.py", base_path='./emlearn_kmeans') +module("emlearn_cnn_int8.py", base_path='./emlearn_cnn_int8') module("emlearn_cnn_fp32.py", base_path='./emlearn_cnn_fp32') module("emlearn_fft.py", base_path='./emlearn_fft') module("emlearn_linreg.py", base_path='./emlearn_linreg') diff --git a/src/tinymaix_cnn/fp32/tm_port.h b/src/tinymaix_cnn/fp32/tm_port.h index 4ef67777..40741d28 100644 --- a/src/tinymaix_cnn/fp32/tm_port.h +++ b/src/tinymaix_cnn/fp32/tm_port.h @@ -44,11 +44,16 @@ limitations under the License. #define TM_WEAK __attribute__((weak)) // Disable "static" (non-const) globals, since they are not supported by MicroPython mpy_ld.py -#define TM_STATIC +// But when building multiple variants, we need static to avoid duplicate definitions +#ifdef CONFIG_FP32 +#define TM_STATIC static +#else +#define TM_STATIC +#endif // Use MicroPython for dynamic allocation #define tm_malloc(x) m_malloc(x) -#define tm_free(x) mod_cnn_free(x) +#define tm_free(x) CNN_FREE(x) // FIXME: set theese to use MicroPython primitives diff --git a/src/tinymaix_cnn/int8/tm_port.h b/src/tinymaix_cnn/int8/tm_port.h index ec5cf445..ff025337 100644 --- a/src/tinymaix_cnn/int8/tm_port.h +++ b/src/tinymaix_cnn/int8/tm_port.h @@ -44,11 +44,16 @@ limitations under the License. #define TM_WEAK __attribute__((weak)) // Disable "static" (non-const) globals, since they are not supported by MicroPython mpy_ld.py -#define TM_STATIC +// But when building multiple variants, we need static to avoid duplicate definitions +#ifdef CONFIG_INT8 +#define TM_STATIC static +#else +#define TM_STATIC +#endif // Use MicroPython for dynamic allocation #define tm_malloc(x) m_malloc(x) -#define tm_free(x) mod_cnn_free(x) +#define tm_free(x) CNN_FREE(x) // FIXME: set theese to use MicroPython primitives diff --git a/src/tinymaix_cnn/mod_cnn.c b/src/tinymaix_cnn/mod_cnn.c index 3ceeccfd..f5b32e5a 100644 --- a/src/tinymaix_cnn/mod_cnn.c +++ b/src/tinymaix_cnn/mod_cnn.c @@ -5,8 +5,23 @@ #include "py/runtime.h" #endif +// Define unique symbol names based on CONFIG +#ifdef CONFIG_FP32 +#define CNN_TYPE mod_cnn_fp32_type +#define CNN_CMODULE mod_cnn_fp32_cmodule +#define CNN_FREE mod_cnn_fp32_free +#elif defined(CONFIG_INT8) +#define CNN_TYPE mod_cnn_int8_type +#define CNN_CMODULE mod_cnn_int8_cmodule +#define CNN_FREE mod_cnn_int8_free +#else +#define CNN_TYPE mod_cnn_int8_type +#define CNN_CMODULE mod_cnn_int8_cmodule +#define CNN_FREE mod_cnn_int8_free +#endif -void mod_cnn_free(void *ptr); +// Forward declaration for tm_port.h +void CNN_FREE(void *ptr); // TinyMaix config #include "./tm_port.h" @@ -81,18 +96,18 @@ typedef struct _mp_obj_mod_cnn_t { } mp_obj_mod_cnn_t; #if MICROPY_ENABLE_DYNRUNTIME -mp_obj_full_type_t mod_cnn_type; +mp_obj_full_type_t CNN_TYPE; #else -static const mp_obj_type_t mod_cnn_type; +static const mp_obj_type_t CNN_TYPE; #endif -void mod_cnn_free(void *ptr) +void CNN_FREE(void *ptr) { #if MICROPY_ENABLE_DYNRUNTIME return m_free(ptr); #else - return m_del(void *, ptr, 0); // XXX: not sure if safe + return m_del(void *, ptr, 0); #endif } @@ -116,7 +131,7 @@ static mp_obj_t mod_cnn_new(mp_obj_t model_data_obj) { const int model_data_length = bufinfo.len / sizeof(*model_data_buffer); // Construct object - mp_obj_mod_cnn_t *o = mp_obj_malloc(mp_obj_mod_cnn_t, (mp_obj_type_t *)&mod_cnn_type); + mp_obj_mod_cnn_t *o = mp_obj_malloc(mp_obj_mod_cnn_t, (mp_obj_type_t *)&CNN_TYPE); tm_mdl_t *model = &o->model; // Copy the model data @@ -283,15 +298,15 @@ mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *a mp_store_global(MP_QSTR_new, MP_OBJ_FROM_PTR(&mod_cnn_new_obj)); - mod_cnn_type.base.type = (void*)&mp_fun_table.type_type; - mod_cnn_type.flags = MP_TYPE_FLAG_ITER_IS_CUSTOM; - mod_cnn_type.name = MP_QSTR_tinymaixcnn; + CNN_TYPE.base.type = (void*)&mp_fun_table.type_type; + CNN_TYPE.flags = MP_TYPE_FLAG_ITER_IS_CUSTOM; + CNN_TYPE.name = MP_QSTR_tinymaixcnn; // methods mod_locals_dict_table[0] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_run), MP_OBJ_FROM_PTR(&mod_cnn_run_obj) }; mod_locals_dict_table[1] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR___del__), MP_OBJ_FROM_PTR(&mod_cnn_del_obj) }; mod_locals_dict_table[2] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_output_dimensions), MP_OBJ_FROM_PTR(&mod_cnn_output_dimensions_obj) }; - MP_OBJ_TYPE_SET_SLOT(&mod_cnn_type, locals_dict, (void*)&mod_locals_dict, 2); + MP_OBJ_TYPE_SET_SLOT(&CNN_TYPE, locals_dict, (void*)&mod_locals_dict, 2); // This must be last, it restores the globals dict MP_DYNRUNTIME_INIT_EXIT @@ -308,7 +323,7 @@ static MP_DEFINE_CONST_DICT(mod_cnn_locals_dict, mod_cnn_locals_dict_table); static MP_DEFINE_CONST_OBJ_TYPE( - mod_cnn_type, + CNN_TYPE, MP_QSTR_tinymaix_cnn, MP_TYPE_FLAG_NONE, locals_dict, &mod_cnn_locals_dict @@ -320,17 +335,17 @@ static const mp_rom_map_elem_t mod_cnn_globals_table[] = { }; static MP_DEFINE_CONST_DICT(mod_cnn_globals, mod_cnn_globals_table); -const mp_obj_module_t mod_cnn_cmodule = { +const mp_obj_module_t CNN_CMODULE = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t *)&mod_cnn_globals, }; // Module name depends on CONFIG #ifdef CONFIG_FP32 -MP_REGISTER_MODULE(MP_QSTR_tinymaix_cnn_fp32_native, mod_cnn_cmodule); +MP_REGISTER_MODULE(MP_QSTR_emlearn_cnn_fp32_native, CNN_CMODULE); +#elif defined(CONFIG_INT8) +MP_REGISTER_MODULE(MP_QSTR_emlearn_cnn_int8_native, CNN_CMODULE); #else -MP_REGISTER_MODULE(MP_QSTR_emlearn_cnn_int8, mod_cnn_cmodule); +MP_REGISTER_MODULE(MP_QSTR_emlearn_cnn_int8_native, CNN_CMODULE); #endif #endif - - From a919097e69378a1074ef63009f01e72535216256 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sun, 19 Apr 2026 21:42:35 +0200 Subject: [PATCH 3/4] cnn: Fixup int8 when built as external C module The TinyMaix symbols were defined as weak - so int8 config would call a function that had fp32 configuration. Would fail in tm_load --- dependencies/TinyMaix | 2 +- src/emlearn_cnn_fp32/emlearn_cnn_fp32.c | 10 +++++++++- src/emlearn_cnn_fp32/micropython.mk | 3 ++- src/emlearn_cnn_int8/emlearn_cnn_int8.c | 10 +++++++++- src/emlearn_cnn_int8/micropython.mk | 3 ++- src/tinymaix_cnn/fp32/tm_port.h | 2 +- src/tinymaix_cnn/int8/tm_port.h | 2 +- src/tinymaix_cnn/mod_cnn.c | 20 +++++++++++++++++--- 8 files changed, 42 insertions(+), 10 deletions(-) diff --git a/dependencies/TinyMaix b/dependencies/TinyMaix index 7bcb087b..9040e29f 160000 --- a/dependencies/TinyMaix +++ b/dependencies/TinyMaix @@ -1 +1 @@ -Subproject commit 7bcb087b6a8ff69fa3e2ac89e67df5b35aa86df9 +Subproject commit 9040e29fadce92826105b5ab4d4d7630bf199fe4 diff --git a/src/emlearn_cnn_fp32/emlearn_cnn_fp32.c b/src/emlearn_cnn_fp32/emlearn_cnn_fp32.c index 8974438e..d429a2ad 100644 --- a/src/emlearn_cnn_fp32/emlearn_cnn_fp32.c +++ b/src/emlearn_cnn_fp32/emlearn_cnn_fp32.c @@ -1,6 +1,14 @@ /* * emlearn_cnn_fp32 wrapper - * This file sets CONFIG_FP32 and includes the main mod_cnn.c + * This file includes fp32/tm_port.h directly before including mod_cnn.c + * We need to modify mod_cnn.c to not include tm_port.h when CONFIG is already defined */ + +/* Define CONFIG_FP32 first */ #define CONFIG_FP32 + +/* Include the fp32 tm_port.h directly */ +#include "../tinymaix_cnn/fp32/tm_port.h" + +/* Now include mod_cnn.c - it will see CONFIG_FP32 is defined and use fp32/tm_port.h */ #include "../tinymaix_cnn/mod_cnn.c" diff --git a/src/emlearn_cnn_fp32/micropython.mk b/src/emlearn_cnn_fp32/micropython.mk index ee2b6b91..904c6490 100644 --- a/src/emlearn_cnn_fp32/micropython.mk +++ b/src/emlearn_cnn_fp32/micropython.mk @@ -6,8 +6,9 @@ CNN_SRC := $(USERMOD_DIR)/../tinymaix_cnn # Add wrapper C file which defines CONFIG_FP32 SRC_USERMOD_C += $(USERMOD_DIR)/emlearn_cnn_fp32.c -# Include paths - need to include tm_port.h location +# Include paths - config directory first to ensure correct tm_port.h is found CFLAGS_USERMOD += -I$(CNN_SRC)/fp32 +CFLAGS_USERMOD += -I$(CNN_SRC)/int8 CFLAGS_USERMOD += -I$(CNN_SRC)/../../dependencies/TinyMaix/include CFLAGS_USERMOD += -I$(CNN_SRC)/../../dependencies/TinyMaix/src diff --git a/src/emlearn_cnn_int8/emlearn_cnn_int8.c b/src/emlearn_cnn_int8/emlearn_cnn_int8.c index 244a0c4c..af76abc4 100644 --- a/src/emlearn_cnn_int8/emlearn_cnn_int8.c +++ b/src/emlearn_cnn_int8/emlearn_cnn_int8.c @@ -1,6 +1,14 @@ /* * emlearn_cnn_int8 wrapper - * This file sets CONFIG_INT8 and includes the main mod_cnn.c + * This file includes int8/tm_port.h directly before including mod_cnn.c + * We need to modify mod_cnn.c to not include tm_port.h when CONFIG is already defined */ + +/* Define CONFIG_INT8 first */ #define CONFIG_INT8 + +/* Include the int8 tm_port.h directly */ +#include "../tinymaix_cnn/int8/tm_port.h" + +/* Now include mod_cnn.c - it will see CONFIG_INT8 is defined and use int8/tm_port.h */ #include "../tinymaix_cnn/mod_cnn.c" diff --git a/src/emlearn_cnn_int8/micropython.mk b/src/emlearn_cnn_int8/micropython.mk index 9080d63f..f9ee2507 100644 --- a/src/emlearn_cnn_int8/micropython.mk +++ b/src/emlearn_cnn_int8/micropython.mk @@ -6,8 +6,9 @@ CNN_SRC := $(USERMOD_DIR)/../tinymaix_cnn # Add wrapper C file which defines CONFIG_INT8 SRC_USERMOD_C += $(USERMOD_DIR)/emlearn_cnn_int8.c -# Include paths - need to include tm_port.h location +# Include paths - config directory first to ensure correct tm_port.h is found CFLAGS_USERMOD += -I$(CNN_SRC)/int8 +CFLAGS_USERMOD += -I$(CNN_SRC)/fp32 CFLAGS_USERMOD += -I$(CNN_SRC)/../../dependencies/TinyMaix/include CFLAGS_USERMOD += -I$(CNN_SRC)/../../dependencies/TinyMaix/src diff --git a/src/tinymaix_cnn/fp32/tm_port.h b/src/tinymaix_cnn/fp32/tm_port.h index 40741d28..438908b2 100644 --- a/src/tinymaix_cnn/fp32/tm_port.h +++ b/src/tinymaix_cnn/fp32/tm_port.h @@ -41,7 +41,7 @@ limitations under the License. #define TM_MAX_KCSIZE (3*3*256) //max kernel_size*channels //cost TM_MAX_KSIZE*sizeof(mtype_t) Byte #define TM_INLINE __attribute__((always_inline)) static inline -#define TM_WEAK __attribute__((weak)) +#define TM_WEAK static // Disable "static" (non-const) globals, since they are not supported by MicroPython mpy_ld.py // But when building multiple variants, we need static to avoid duplicate definitions diff --git a/src/tinymaix_cnn/int8/tm_port.h b/src/tinymaix_cnn/int8/tm_port.h index ff025337..20b87c85 100644 --- a/src/tinymaix_cnn/int8/tm_port.h +++ b/src/tinymaix_cnn/int8/tm_port.h @@ -41,7 +41,7 @@ limitations under the License. #define TM_MAX_KCSIZE (3*3*256) //max kernel_size*channels //cost TM_MAX_KSIZE*sizeof(mtype_t) Byte #define TM_INLINE __attribute__((always_inline)) static inline -#define TM_WEAK __attribute__((weak)) +#define TM_WEAK static // Disable "static" (non-const) globals, since they are not supported by MicroPython mpy_ld.py // But when building multiple variants, we need static to avoid duplicate definitions diff --git a/src/tinymaix_cnn/mod_cnn.c b/src/tinymaix_cnn/mod_cnn.c index f5b32e5a..1882279e 100644 --- a/src/tinymaix_cnn/mod_cnn.c +++ b/src/tinymaix_cnn/mod_cnn.c @@ -5,6 +5,11 @@ #include "py/runtime.h" #endif +// Check that only one CONFIG is defined +#if defined(CONFIG_FP32) && defined(CONFIG_INT8) +#error "Only one of CONFIG_FP32 or CONFIG_INT8 should be defined" +#endif + // Define unique symbol names based on CONFIG #ifdef CONFIG_FP32 #define CNN_TYPE mod_cnn_fp32_type @@ -23,8 +28,17 @@ // Forward declaration for tm_port.h void CNN_FREE(void *ptr); -// TinyMaix config -#include "./tm_port.h" +// TinyMaix config - include from the config directory +// Only include if not already included by wrapper +#ifndef __TM_PORT_H +#ifdef CONFIG_INT8 +#include "./int8/tm_port.h" +#elif defined(CONFIG_FP32) +#include "./fp32/tm_port.h" +#else +#include "./int8/tm_port.h" // default +#endif +#endif #include @@ -52,7 +66,7 @@ void *memset(void *s, int c, size_t n) { // get model output shapes //mdl: model handle; in: input mat; out: output mat -int TM_WEAK tm_get_outputs(tm_mdl_t* mdl, tm_mat_t* out, int out_length) +static int tm_get_outputs(tm_mdl_t* mdl, tm_mat_t* out, int out_length) { // NOTE: based on tm_run, but without actually executing int out_idx = 0; From 0097209d9336ddfbdafba62173d24f852b78d6a1 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sun, 19 Apr 2026 22:02:41 +0200 Subject: [PATCH 4/4] cnn: Fixup dynamic module build Needs different config from external C... --- dependencies/TinyMaix | 2 +- src/emlearn_cnn_fp32/emlearn_cnn_fp32.c | 3 +++ src/emlearn_cnn_int8/emlearn_cnn_int8.c | 3 +++ src/tinymaix_cnn/Makefile | 4 +++- src/tinymaix_cnn/fp32/tm_port.h | 13 ++++++------- src/tinymaix_cnn/int8/tm_port.h | 11 +++++------ src/tinymaix_cnn/mod_cnn.c | 2 +- 7 files changed, 22 insertions(+), 16 deletions(-) diff --git a/dependencies/TinyMaix b/dependencies/TinyMaix index 9040e29f..186656ac 160000 --- a/dependencies/TinyMaix +++ b/dependencies/TinyMaix @@ -1 +1 @@ -Subproject commit 9040e29fadce92826105b5ab4d4d7630bf199fe4 +Subproject commit 186656ac3affe772d9ce57ea89f378e7819a56ea diff --git a/src/emlearn_cnn_fp32/emlearn_cnn_fp32.c b/src/emlearn_cnn_fp32/emlearn_cnn_fp32.c index d429a2ad..eb6bcb84 100644 --- a/src/emlearn_cnn_fp32/emlearn_cnn_fp32.c +++ b/src/emlearn_cnn_fp32/emlearn_cnn_fp32.c @@ -7,6 +7,9 @@ /* Define CONFIG_FP32 first */ #define CONFIG_FP32 +// for external module we need static +#define TM_STATIC static + /* Include the fp32 tm_port.h directly */ #include "../tinymaix_cnn/fp32/tm_port.h" diff --git a/src/emlearn_cnn_int8/emlearn_cnn_int8.c b/src/emlearn_cnn_int8/emlearn_cnn_int8.c index af76abc4..f8554b26 100644 --- a/src/emlearn_cnn_int8/emlearn_cnn_int8.c +++ b/src/emlearn_cnn_int8/emlearn_cnn_int8.c @@ -7,6 +7,9 @@ /* Define CONFIG_INT8 first */ #define CONFIG_INT8 +// for external module we need static +#define TM_STATIC static + /* Include the int8 tm_port.h directly */ #include "../tinymaix_cnn/int8/tm_port.h" diff --git a/src/tinymaix_cnn/Makefile b/src/tinymaix_cnn/Makefile index 31296687..730661a8 100644 --- a/src/tinymaix_cnn/Makefile +++ b/src/tinymaix_cnn/Makefile @@ -35,6 +35,8 @@ $(DIST_FILE): $(MOD).mpy $(DIST_DIR) # Include to get the rules for compiling and linking the module include $(MPY_DIR)/py/dynruntime.mk -CFLAGS += -I$(CONFIG_DIR) -I$(TINYMAIX_DIR)/include -I$(TINYMAIX_DIR)/src -Wno-error=unused-variable -Wno-error=multichar -Wdouble-promotion +CONFIG_DEFINE = CONFIG_$(shell echo $(CONFIG) | tr '[:lower:]' '[:upper:]') + +CFLAGS += -D${CONFIG_DEFINE} -I$(CONFIG_DIR) -I$(TINYMAIX_DIR)/include -I$(TINYMAIX_DIR)/src -Wno-error=unused-variable -Wno-error=multichar -Wdouble-promotion dist: $(DIST_FILE) diff --git a/src/tinymaix_cnn/fp32/tm_port.h b/src/tinymaix_cnn/fp32/tm_port.h index 438908b2..6937efb3 100644 --- a/src/tinymaix_cnn/fp32/tm_port.h +++ b/src/tinymaix_cnn/fp32/tm_port.h @@ -35,21 +35,20 @@ limitations under the License. #define TM_MDL_TYPE TM_MDL_FP32 #define TM_FASTSCALE (0) //enable if your chip don't have FPU, may speed up 1/3, but decrease accuracy #define TM_LOCAL_MATH (1) //use local math func (like exp()) to avoid libm -#define TM_ENABLE_STAT (1) //enable mdl stat functions +#define TM_ENABLE_STAT (0) //enable mdl stat functions #define TM_MAX_CSIZE (1000) //max channel num //used if INT8 mdl //cost TM_MAX_CSIZE*4 Byte #define TM_MAX_KSIZE (5*5) //max kernel_size //cost TM_MAX_KSIZE*4 Byte #define TM_MAX_KCSIZE (3*3*256) //max kernel_size*channels //cost TM_MAX_KSIZE*sizeof(mtype_t) Byte #define TM_INLINE __attribute__((always_inline)) static inline -#define TM_WEAK static +#ifndef TM_WEAK +#define TM_WEAK __attribute__((weak)) +#endif // Disable "static" (non-const) globals, since they are not supported by MicroPython mpy_ld.py -// But when building multiple variants, we need static to avoid duplicate definitions -#ifdef CONFIG_FP32 -#define TM_STATIC static -#else +#ifndef TM_STATIC #define TM_STATIC -#endif +#endif // Use MicroPython for dynamic allocation #define tm_malloc(x) m_malloc(x) diff --git a/src/tinymaix_cnn/int8/tm_port.h b/src/tinymaix_cnn/int8/tm_port.h index 20b87c85..453f5eed 100644 --- a/src/tinymaix_cnn/int8/tm_port.h +++ b/src/tinymaix_cnn/int8/tm_port.h @@ -35,19 +35,18 @@ limitations under the License. #define TM_MDL_TYPE TM_MDL_INT8 #define TM_FASTSCALE (0) //enable if your chip don't have FPU, may speed up 1/3, but decrease accuracy #define TM_LOCAL_MATH (1) //use local math func (like exp()) to avoid libm -#define TM_ENABLE_STAT (1) //enable mdl stat functions +#define TM_ENABLE_STAT (0) //enable mdl stat functions #define TM_MAX_CSIZE (1000) //max channel num //used if INT8 mdl //cost TM_MAX_CSIZE*4 Byte #define TM_MAX_KSIZE (5*5) //max kernel_size //cost TM_MAX_KSIZE*4 Byte #define TM_MAX_KCSIZE (3*3*256) //max kernel_size*channels //cost TM_MAX_KSIZE*sizeof(mtype_t) Byte #define TM_INLINE __attribute__((always_inline)) static inline -#define TM_WEAK static +#ifndef TM_WEAK +#define TM_WEAK __attribute__((weak)) +#endif // Disable "static" (non-const) globals, since they are not supported by MicroPython mpy_ld.py -// But when building multiple variants, we need static to avoid duplicate definitions -#ifdef CONFIG_INT8 -#define TM_STATIC static -#else +#ifndef TM_STATIC #define TM_STATIC #endif diff --git a/src/tinymaix_cnn/mod_cnn.c b/src/tinymaix_cnn/mod_cnn.c index 1882279e..f4570009 100644 --- a/src/tinymaix_cnn/mod_cnn.c +++ b/src/tinymaix_cnn/mod_cnn.c @@ -36,7 +36,7 @@ void CNN_FREE(void *ptr); #elif defined(CONFIG_FP32) #include "./fp32/tm_port.h" #else -#include "./int8/tm_port.h" // default +#error "No config defined" #endif #endif