From a8a1ad13916aa74fca30ad23061a5552935bf097 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 6 Oct 2022 11:14:13 +1100 Subject: [PATCH] embed: Add new "embed" port which builds a simple C package. Signed-off-by: Damien George --- ports/embed/README.md | 18 +++++ ports/embed/embed.mk | 65 +++++++++++++++ ports/embed/port/embed_util.c | 106 +++++++++++++++++++++++++ ports/embed/port/micropython_embed.h | 41 ++++++++++ ports/embed/port/mpconfigport_common.h | 38 +++++++++ ports/embed/port/mphalport.c | 33 ++++++++ ports/embed/port/mphalport.h | 2 + 7 files changed, 303 insertions(+) create mode 100644 ports/embed/README.md create mode 100644 ports/embed/embed.mk create mode 100644 ports/embed/port/embed_util.c create mode 100644 ports/embed/port/micropython_embed.h create mode 100644 ports/embed/port/mpconfigport_common.h create mode 100644 ports/embed/port/mphalport.c create mode 100644 ports/embed/port/mphalport.h diff --git a/ports/embed/README.md b/ports/embed/README.md new file mode 100644 index 0000000000..556cfc749d --- /dev/null +++ b/ports/embed/README.md @@ -0,0 +1,18 @@ +MicroPython embed port +====================== + +This is a port of MicroPython that outputs a set of .c and .h files for embedding +into a wider project. This port essentially targets the C language, instead of a +particular hardware architecture or platform. + +To use this port in a project there are three main steps: + +1. Provide configuration for the project via an `mpconfigport.h` file. + +2. Build this embed port against that configuration, using the provided `embed.mk`. + The output is a set of self-contained source files for building MicroPython. + These files can be placed outside this repository. + +3. Build the project. This requires compiling all .c files from the above step. + +See `examples/embedding` for an example. diff --git a/ports/embed/embed.mk b/ports/embed/embed.mk new file mode 100644 index 0000000000..7fb344d704 --- /dev/null +++ b/ports/embed/embed.mk @@ -0,0 +1,65 @@ +# This file is part of the MicroPython project, http://micropython.org/ +# The MIT License (MIT) +# Copyright (c) 2022-2023 Damien P. George +# +# This file is intended to be included by a Makefile in a custom project. + +# Set the build output directory for the generated files. +BUILD = build-embed + +# Include the core environment definitions; this will set $(TOP). +include $(MICROPYTHON_TOP)/py/mkenv.mk + +# Include py core make definitions. +include $(TOP)/py/py.mk + +# Set the location of the MicroPython embed port. +MICROPYTHON_EMBED_PORT = $(MICROPYTHON_TOP)/ports/embed + +# Set default makefile-level MicroPython feature configurations. +MICROPY_ROM_TEXT_COMPRESSION ?= 0 + +# Set CFLAGS for the MicroPython build. +CFLAGS += -I. -I$(TOP) -I$(BUILD) -I$(MICROPYTHON_EMBED_PORT) +CFLAGS += -Wall -Werror -std=c99 + +# Define the required generated header files. +GENHDR_OUTPUT = $(addprefix $(BUILD)/genhdr/, \ + moduledefs.h \ + mpversion.h \ + qstrdefs.generated.h \ + root_pointers.h \ + ) + +# Define the top-level target, the generated output files. +.PHONY: all +all: micropython-embed-package + +clean: clean-micropython-embed-package + +.PHONY: clean-micropython-embed-package +clean-micropython-embed-package: + $(RM) -rf $(PACKAGE_DIR) + +PACKAGE_DIR ?= micropython_embed +PACKAGE_DIR_LIST = $(addprefix $(PACKAGE_DIR)/,py extmod shared/runtime genhdr port) + +.PHONY: micropython-embed-package +micropython-embed-package: $(GENHDR_OUTPUT) + $(ECHO) "Generate micropython_embed output:" + $(Q)$(RM) -rf $(PACKAGE_DIR_LIST) + $(Q)$(MKDIR) -p $(PACKAGE_DIR_LIST) + $(ECHO) "- py" + $(Q)$(CP) $(TOP)/py/*.[ch] $(PACKAGE_DIR)/py + $(ECHO) "- extmod" + $(Q)$(CP) $(TOP)/extmod/moduplatform.h $(PACKAGE_DIR)/extmod + $(ECHO) "- shared" + $(Q)$(CP) $(TOP)/shared/runtime/gchelper.h $(PACKAGE_DIR)/shared/runtime + $(Q)$(CP) $(TOP)/shared/runtime/gchelper_generic.c $(PACKAGE_DIR)/shared/runtime + $(ECHO) "- genhdr" + $(Q)$(CP) $(GENHDR_OUTPUT) $(PACKAGE_DIR)/genhdr + $(ECHO) "- port" + $(Q)$(CP) $(MICROPYTHON_EMBED_PORT)/port/*.[ch] $(PACKAGE_DIR)/port + +# Include remaining core make rules. +include $(TOP)/py/mkrules.mk diff --git a/ports/embed/port/embed_util.c b/ports/embed/port/embed_util.c new file mode 100644 index 0000000000..a3850f3c0f --- /dev/null +++ b/ports/embed/port/embed_util.c @@ -0,0 +1,106 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022-2023 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include "py/compile.h" +#include "py/gc.h" +#include "py/persistentcode.h" +#include "py/runtime.h" +#include "py/stackctrl.h" +#include "shared/runtime/gchelper.h" +#include "port/micropython_embed.h" + +// Initialise the runtime. +void mp_embed_init(void *gc_heap, size_t gc_heap_size) { + mp_stack_ctrl_init(); + gc_init(gc_heap, (uint8_t *)gc_heap + gc_heap_size); + mp_init(); +} + +#if MICROPY_ENABLE_COMPILER +// Compile and execute the given source script (Python text). +void mp_embed_exec_str(const char *src) { + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + // Compile, parse and execute the given string. + mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0); + qstr source_name = lex->source_name; + mp_parse_tree_t parse_tree = mp_parse(lex, MP_PARSE_FILE_INPUT); + mp_obj_t module_fun = mp_compile(&parse_tree, source_name, true); + mp_call_function_0(module_fun); + nlr_pop(); + } else { + // Uncaught exception: print it out. + mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val); + } +} +#endif + +#if MICROPY_PERSISTENT_CODE_LOAD +void mp_embed_exec_mpy(const uint8_t *mpy, size_t len) { + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + // Execute the given .mpy data. + mp_module_context_t *ctx = m_new_obj(mp_module_context_t); + ctx->module.globals = mp_globals_get(); + mp_compiled_module_t cm = mp_raw_code_load_mem(mpy, len, ctx); + mp_obj_t f = mp_make_function_from_raw_code(cm.rc, ctx, MP_OBJ_NULL); + mp_call_function_0(f); + nlr_pop(); + } else { + // Uncaught exception: print it out. + mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val); + } +} +#endif + +// Deinitialise the runtime. +void mp_embed_deinit(void) { + mp_deinit(); +} + +#if MICROPY_ENABLE_GC +// Run a garbage collection cycle. +void gc_collect(void) { + gc_collect_start(); + gc_helper_collect_regs_and_stack(); + gc_collect_end(); +} +#endif + +// Called if an exception is raised outside all C exception-catching handlers. +void nlr_jump_fail(void *val) { + for (;;) { + } +} + +#ifndef NDEBUG +// Used when debugging is enabled. +void __assert_func(const char *file, int line, const char *func, const char *expr) { + for (;;) { + } +} +#endif diff --git a/ports/embed/port/micropython_embed.h b/ports/embed/port/micropython_embed.h new file mode 100644 index 0000000000..bf55d9b2b4 --- /dev/null +++ b/ports/embed/port/micropython_embed.h @@ -0,0 +1,41 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022-2023 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MICROPY_INCLUDED_MICROPYTHON_EMBED_H +#define MICROPY_INCLUDED_MICROPYTHON_EMBED_H + +#include +#include + +void mp_embed_init(void *gc_heap, size_t gc_heap_size); +void mp_embed_deinit(void); + +// Only available if MICROPY_ENABLE_COMPILER is enabled. +void mp_embed_exec_str(const char *src); + +// Only available if MICROPY_PERSISTENT_CODE_LOAD is enabled. +void mp_embed_exec_mpy(const uint8_t *mpy, size_t len); + +#endif // MICROPY_INCLUDED_MICROPYTHON_EMBED_H diff --git a/ports/embed/port/mpconfigport_common.h b/ports/embed/port/mpconfigport_common.h new file mode 100644 index 0000000000..69216a7582 --- /dev/null +++ b/ports/embed/port/mpconfigport_common.h @@ -0,0 +1,38 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022-2023 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +// Type definitions for the specific machine + +typedef intptr_t mp_int_t; // must be pointer size +typedef uintptr_t mp_uint_t; // must be pointer size +typedef long mp_off_t; + +// Need to provide a declaration/definition of alloca() +#include + +#define MICROPY_MPHALPORT_H "port/mphalport.h" diff --git a/ports/embed/port/mphalport.c b/ports/embed/port/mphalport.c new file mode 100644 index 0000000000..8e76a8e22e --- /dev/null +++ b/ports/embed/port/mphalport.c @@ -0,0 +1,33 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022-2023 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include "py/mphal.h" + +// Send string of given length to stdout, converting \n to \r\n. +void mp_hal_stdout_tx_strn_cooked(const char *str, size_t len) { + printf("%.*s", (int)len, str); +} diff --git a/ports/embed/port/mphalport.h b/ports/embed/port/mphalport.h new file mode 100644 index 0000000000..49928e154d --- /dev/null +++ b/ports/embed/port/mphalport.h @@ -0,0 +1,2 @@ +// Define so there's no dependency on extmod/virtpin.h +#define mp_hal_pin_obj_t