diff --git a/dependencies/TinyMaix b/dependencies/TinyMaix index 7bcb087..186656a 160000 --- a/dependencies/TinyMaix +++ b/dependencies/TinyMaix @@ -1 +1 @@ -Subproject commit 7bcb087b6a8ff69fa3e2ac89e67df5b35aa86df9 +Subproject commit 186656ac3affe772d9ce57ea89f378e7819a56ea 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 0000000..eb6bcb8 --- /dev/null +++ b/src/emlearn_cnn_fp32/emlearn_cnn_fp32.c @@ -0,0 +1,17 @@ +/* + * emlearn_cnn_fp32 wrapper + * 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 + +// for external module we need static +#define TM_STATIC static + +/* 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/emlearn_cnn_fp32.py b/src/emlearn_cnn_fp32/emlearn_cnn_fp32.py new file mode 100644 index 0000000..48463e8 --- /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 emlearn_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 0000000..904c649 --- /dev/null +++ b/src/emlearn_cnn_fp32/micropython.mk @@ -0,0 +1,16 @@ +# emlearn_cnn_fp32 wrapper for Unix port +# This wrapper sets CONFIG_FP32 before including mod_cnn.c + +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 - 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 + +# Compile flags to suppress TinyMaix warnings +CFLAGS_USERMOD += -Wno-error=unused-variable -Wno-error=multichar -Wdouble-promotion 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 0000000..f8554b2 --- /dev/null +++ b/src/emlearn_cnn_int8/emlearn_cnn_int8.c @@ -0,0 +1,17 @@ +/* + * emlearn_cnn_int8 wrapper + * 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 + +// for external module we need static +#define TM_STATIC static + +/* 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/emlearn_cnn_int8.py b/src/emlearn_cnn_int8/emlearn_cnn_int8.py new file mode 100644 index 0000000..665a947 --- /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 0000000..f9ee250 --- /dev/null +++ b/src/emlearn_cnn_int8/micropython.mk @@ -0,0 +1,16 @@ +# 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 - 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 + +# 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 9f55e5d..6c90e27 100644 --- a/src/manifest_unix.py +++ b/src/manifest_unix.py @@ -4,6 +4,8 @@ # 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') module("emlearn_logreg.py", base_path='./emlearn_logreg') diff --git a/src/tinymaix_cnn/Makefile b/src/tinymaix_cnn/Makefile index 3129668..730661a 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 4ef6777..6937efb 100644 --- a/src/tinymaix_cnn/fp32/tm_port.h +++ b/src/tinymaix_cnn/fp32/tm_port.h @@ -35,20 +35,24 @@ 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 +#ifndef TM_WEAK #define TM_WEAK __attribute__((weak)) +#endif // Disable "static" (non-const) globals, since they are not supported by MicroPython mpy_ld.py -#define TM_STATIC +#ifndef TM_STATIC +#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 ec5cf44..453f5ee 100644 --- a/src/tinymaix_cnn/int8/tm_port.h +++ b/src/tinymaix_cnn/int8/tm_port.h @@ -35,20 +35,24 @@ 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 +#ifndef TM_WEAK #define TM_WEAK __attribute__((weak)) +#endif // Disable "static" (non-const) globals, since they are not supported by MicroPython mpy_ld.py -#define TM_STATIC +#ifndef TM_STATIC +#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 adee99f..f457000 100644 --- a/src/tinymaix_cnn/mod_cnn.c +++ b/src/tinymaix_cnn/mod_cnn.c @@ -5,11 +5,40 @@ #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 -void mod_cnn_free(void *ptr); +// 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 -// TinyMaix config -#include "./tm_port.h" +// Forward declaration for tm_port.h +void CNN_FREE(void *ptr); + +// 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 +#error "No config defined" +#endif +#endif #include @@ -37,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; @@ -81,18 +110,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 +145,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 +312,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 @@ -301,14 +330,14 @@ 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); 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,13 +349,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, }; -// FIXME: unhardcode config part of module name -MP_REGISTER_MODULE(MP_QSTR_emlearn_cnn_int8, mod_cnn_cmodule); +// Module name depends on CONFIG +#ifdef CONFIG_FP32 +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_native, CNN_CMODULE); +#endif #endif - -