From 8e8d530779cfd6778f641fedc17e5fe2fb9fcc61 Mon Sep 17 00:00:00 2001 From: Christian Walther Date: Sun, 21 Apr 2024 16:10:18 +0200 Subject: [PATCH 01/13] py/mkrules.mk: Fix 'make submodules' when building out-of-tree. When MicroPython is used as a submodule and built from the containing project, e.g. for the embed port, `make submodules` fails because it goes looking for the sub-sub-module paths in the outer repository instead of in the micropython repository. Fix this by invoking git inside the micropython submodule. Signed-off-by: Christian Walther --- py/mkrules.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/py/mkrules.mk b/py/mkrules.mk index 5050935873..0dc1cdfe15 100644 --- a/py/mkrules.mk +++ b/py/mkrules.mk @@ -251,8 +251,8 @@ endif submodules: $(ECHO) "Updating submodules: $(GIT_SUBMODULES)" ifneq ($(GIT_SUBMODULES),) - $(Q)git submodule sync $(addprefix $(TOP)/,$(GIT_SUBMODULES)) - $(Q)git submodule update --init $(addprefix $(TOP)/,$(GIT_SUBMODULES)) + $(Q)cd $(TOP) && git submodule sync $(GIT_SUBMODULES) + $(Q)cd $(TOP) && git submodule update --init $(GIT_SUBMODULES) endif .PHONY: submodules From 16db37e7c6b5bab9636ea671c5461bcc4418d3d8 Mon Sep 17 00:00:00 2001 From: Christian Walther Date: Mon, 1 May 2023 15:11:19 +0200 Subject: [PATCH 02/13] examples/embedding-full: Add a more full-featured embedding example. It compiles and runs in this state, but a lot of functionality is still missing, to be extended over the following commits. Signed-off-by: Christian Walther --- .github/workflows/examples.yml | 9 +++ examples/embedding-full/Makefile | 25 ++++++++ examples/embedding-full/README.md | 41 +++++++++++++ examples/embedding-full/main.c | 62 ++++++++++++++++++++ examples/embedding-full/micropython_embed.mk | 9 +++ examples/embedding-full/mpconfigport.h | 32 ++++++++++ examples/embedding-full/mphal.c | 26 ++++++++ examples/embedding/README.md | 9 +-- ports/embed/port/mphalport.h | 4 ++ 9 files changed, 213 insertions(+), 4 deletions(-) create mode 100644 examples/embedding-full/Makefile create mode 100644 examples/embedding-full/README.md create mode 100644 examples/embedding-full/main.c create mode 100644 examples/embedding-full/micropython_embed.mk create mode 100644 examples/embedding-full/mpconfigport.h create mode 100644 examples/embedding-full/mphal.c diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 6613f10662..fe7c6640a5 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -23,3 +23,12 @@ jobs: run: make -C examples/embedding -f micropython_embed.mk && make -C examples/embedding - name: Run run: ./examples/embedding/embed | grep "hello world" + + embedding-full: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Build + run: make -C examples/embedding-full -f micropython_embed.mk submodules && make -C examples/embedding-full -f micropython_embed.mk && make -C examples/embedding-full + - name: Run + run: ./examples/embedding-full/embed diff --git a/examples/embedding-full/Makefile b/examples/embedding-full/Makefile new file mode 100644 index 0000000000..c51fe5ed4d --- /dev/null +++ b/examples/embedding-full/Makefile @@ -0,0 +1,25 @@ +# This file is part of the MicroPython project, http://micropython.org/ +# The MIT License (MIT) +# Copyright (c) 2022-2023 Damien P. George +# +# This is a very simple makefile that demonstrates how to build the embed port. +# All it needs to do is build all *.c files in the micropython_embed directory. +# This makefile would be replaced with your custom build system. + +EMBED_DIR = micropython_embed +PROG = embed + +CFLAGS += -I. +CFLAGS += -I$(EMBED_DIR) +CFLAGS += -I$(EMBED_DIR)/port +CFLAGS += -Wall -Og -fno-common + +SRC += main.c mphal.c +SRC += $(wildcard $(EMBED_DIR)/*/*.c) $(wildcard $(EMBED_DIR)/*/*/*.c) +OBJ += $(SRC:.c=.o) + +$(PROG): $(OBJ) + $(CC) -o $@ $^ + +clean: + /bin/rm -f $(OBJ) $(PROG) diff --git a/examples/embedding-full/README.md b/examples/embedding-full/README.md new file mode 100644 index 0000000000..a185e16b2b --- /dev/null +++ b/examples/embedding-full/README.md @@ -0,0 +1,41 @@ +Example of embedding MicroPython in a standalone C application (full) +===================================================================== + +This directory contains a simple example of how to embed a full-featured +version of MicroPython in an existing C application. +See also _embedding_ for a more minimal version. + +A C application is represented here by the file `main.c`. It executes two +simple Python scripts which print things to the standard output. +Functions used by the MicroPython core that need to be provided by the +application are implemented in `mphal.c`. + +Building the example +-------------------- + +First build the embed port using: + + $ make -f micropython_embed.mk submodules + $ make -f micropython_embed.mk + +This will generate the `micropython_embed` directory which is a self-contained +copy of MicroPython suitable for embedding. The .c files in this directory need +to be compiled into your project, in whatever way your project can do that. The +example here uses make and a provided `Makefile`. + +To build the example project, based on `main.c`, use: + + $ make + +That will create an executable called `embed` which you can run: + + $ ./embed + +Out of tree build +----------------- + +This example is set up to work out of the box, being part of the MicroPython +tree. Your application will be outside of this tree, but the only thing you +need to do for that is to change `MICROPYTHON_TOP` (found in `micropython_embed.mk`) +to point to the location of the MicroPython repository. The MicroPython +repository may, for example, be a git submodule in your project. diff --git a/examples/embedding-full/main.c b/examples/embedding-full/main.c new file mode 100644 index 0000000000..f0cdb0c1c3 --- /dev/null +++ b/examples/embedding-full/main.c @@ -0,0 +1,62 @@ +/* This file is part of the MicroPython project, http://micropython.org/ + * The MIT License (MIT) + * Copyright (c) 2022-2023 Damien P. George + */ + +#include "port/micropython_embed.h" +#include "py/stackctrl.h" + +// This is example 1 script, which will be compiled and executed. +static const char *example_1 = + "print('hello world!', list(x + 1 for x in range(10)), end='eol\\n')"; + +// This is example 2 script, which will be compiled and executed. +static const char *example_2 = + "for i in range(10):\n" + " print('iter {:08}'.format(i))\n" + "\n" + "try:\n" + " 1//0\n" + "except Exception as er:\n" + " print('caught exception', repr(er))\n" + "\n" + "import gc\n" + "print('run GC collect')\n" + "gc.collect()\n" + "\n" + "print('help(\\'modules\\'):')\n" + "help('modules')\n" + "import sys\n" + "help(sys)\n" + "\n" + "print('finish')\n" + ; + +// This array is the MicroPython GC heap. +static char heap[8 * 1024]; + +int main() { +#if MICROPY_STACK_CHECK + // Set the stack limit, otherwise the default is zero and we will end up in + // nlr_jump_fail() immediately. + mp_stack_set_limit(10240); +#endif + // Initialise MicroPython. + // + // Note: &stack_top below should be good enough for many cases. + // However, depending on environment, there might be more appropriate + // ways to get the stack top value. + // eg. pthread_get_stackaddr_np, pthread_getattr_np, + // __builtin_frame_address/__builtin_stack_address, etc. + int stack_top; + mp_embed_init(&heap[0], sizeof(heap), &stack_top); + + // Run the example scripts (they will be compiled first). + mp_embed_exec_str(example_1); + mp_embed_exec_str(example_2); + + // Deinitialise MicroPython. + mp_embed_deinit(); + + return 0; +} diff --git a/examples/embedding-full/micropython_embed.mk b/examples/embedding-full/micropython_embed.mk new file mode 100644 index 0000000000..5db5415927 --- /dev/null +++ b/examples/embedding-full/micropython_embed.mk @@ -0,0 +1,9 @@ +# This file is part of the MicroPython project, http://micropython.org/ +# The MIT License (MIT) +# Copyright (c) 2022-2023 Damien P. George + +# Set the location of the top of the MicroPython repository. +MICROPYTHON_TOP = ../.. + +# Include the main makefile fragment to build the MicroPython component. +include $(MICROPYTHON_TOP)/ports/embed/embed.mk diff --git a/examples/embedding-full/mpconfigport.h b/examples/embedding-full/mpconfigport.h new file mode 100644 index 0000000000..ec96e5ad1f --- /dev/null +++ b/examples/embedding-full/mpconfigport.h @@ -0,0 +1,32 @@ +/* This file is part of the MicroPython project, http://micropython.org/ + * The MIT License (MIT) + * Copyright (c) 2022-2023 Damien P. George + */ + +// Include common MicroPython embed configuration. +#include + +// Use the same starting configuration as on most bare-metal targets. +#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES) + +// MicroPython configuration. +#define MICROPY_ENABLE_COMPILER (1) +#define MICROPY_ENABLE_GC (1) +#define MICROPY_PY_GC (1) + +#define MICROPY_PY_SYS_PLATFORM "embedded" + +// Requires shared/readline/readline.h, don't bother as we have no input. +#define MICROPY_PY_BUILTINS_INPUT (0) + +// Can be enabled once extmod/moductypes.c is included in the build. +#define MICROPY_PY_UCTYPES (0) + +// Can be enabled once either shared/runtime/sys_stdio_mphal.c or +// extmod/vfs_posix_file.c is included in the build. +#define MICROPY_PY_SYS_STDFILES (0) + +// Can be enabled if you provide an implementation of +// mp_hal_set_interrupt_char() in mphal.c or include +// shared/runtime/interrupt_char.c in the build. +#define MICROPY_KBD_EXCEPTION (0) diff --git a/examples/embedding-full/mphal.c b/examples/embedding-full/mphal.c new file mode 100644 index 0000000000..32e508b1e9 --- /dev/null +++ b/examples/embedding-full/mphal.c @@ -0,0 +1,26 @@ +/* This file is part of the MicroPython project, http://micropython.org/ + * The MIT License (MIT) + * Copyright (c) 2022-2023 Damien P. George + */ + +#include "py/builtin.h" +#include "py/compile.h" +#include "py/mperrno.h" + +#if !MICROPY_VFS + +mp_lexer_t *mp_lexer_new_from_file(qstr filename) { + mp_raise_OSError(MP_ENOENT); +} + +mp_import_stat_t mp_import_stat(const char *path) { + (void)path; + return MP_IMPORT_STAT_NO_EXIST; +} + +mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, mp_builtin_open); + +#endif diff --git a/examples/embedding/README.md b/examples/embedding/README.md index 3f19aff41f..4beeb93623 100644 --- a/examples/embedding/README.md +++ b/examples/embedding/README.md @@ -1,8 +1,9 @@ -Example of embedding MicroPython in a standalone C application -============================================================== +Example of embedding MicroPython in a standalone C application (minimal) +======================================================================== -This directory contains a simple example of how to embed MicroPython in an -existing C application. +This directory contains a simple example of how to embed a minimal version of +MicroPython in an existing C application. +See also _embedding-full_ for a more full-featured version. A C application is represented here by the file `main.c`. It executes two simple Python scripts which print things to the standard output. diff --git a/ports/embed/port/mphalport.h b/ports/embed/port/mphalport.h index 49928e154d..fd4a58aa8f 100644 --- a/ports/embed/port/mphalport.h +++ b/ports/embed/port/mphalport.h @@ -1,2 +1,6 @@ // Define so there's no dependency on extmod/virtpin.h #define mp_hal_pin_obj_t + +#if MICROPY_KBD_EXCEPTION +void mp_hal_set_interrupt_char(int c); +#endif From 5e1ea0b5e9cf322310a9f10e4e01cf9b02e3ae38 Mon Sep 17 00:00:00 2001 From: Christian Walther Date: Mon, 1 May 2023 18:18:27 +0200 Subject: [PATCH 03/13] ports/embed: Move text output to the application. The implementation of mp_hal_stdout_tx_strn_cooked() does not belong into the library, but into the embedding application. Some applications may not want Python output to go straight to their stdout, or may not even have printf() available. Signed-off-by: Christian Walther --- examples/embedding-full/mpconfigport.h | 3 +++ examples/embedding-full/mphal.c | 12 ++++++++++ ports/embed/port/mpconfigport_common.h | 10 ++++++++ ports/embed/port/mphalport.c | 33 -------------------------- 4 files changed, 25 insertions(+), 33 deletions(-) delete mode 100644 ports/embed/port/mphalport.c diff --git a/examples/embedding-full/mpconfigport.h b/examples/embedding-full/mpconfigport.h index ec96e5ad1f..38cd0f2baf 100644 --- a/examples/embedding-full/mpconfigport.h +++ b/examples/embedding-full/mpconfigport.h @@ -30,3 +30,6 @@ // mp_hal_set_interrupt_char() in mphal.c or include // shared/runtime/interrupt_char.c in the build. #define MICROPY_KBD_EXCEPTION (0) + +// We have our own implementation of mp_hal_stdout_tx_strn_cooked(). +#undef MP_PLAT_PRINT_STRN diff --git a/examples/embedding-full/mphal.c b/examples/embedding-full/mphal.c index 32e508b1e9..9af36b44a3 100644 --- a/examples/embedding-full/mphal.c +++ b/examples/embedding-full/mphal.c @@ -3,6 +3,7 @@ * Copyright (c) 2022-2023 Damien P. George */ +#include #include "py/builtin.h" #include "py/compile.h" #include "py/mperrno.h" @@ -24,3 +25,14 @@ mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, mp_builtin_open); #endif + +// Text-mode standard output +void mp_hal_stdout_tx_strn_cooked(const char *str, size_t len) { + // This is a simplistic implementation for demonstration purposes. A real + // one would probably want to prefix every line, not just at the start of a + // write operation. + static int start_of_line = 1; + if (start_of_line) printf("py: "); + printf("%.*s", (int)len, str); + start_of_line = (len > 0 && (str[len-1] == '\n' || str[len-1] == '\r')); +} diff --git a/ports/embed/port/mpconfigport_common.h b/ports/embed/port/mpconfigport_common.h index 8e19859ed2..0e8ee4847a 100644 --- a/ports/embed/port/mpconfigport_common.h +++ b/ports/embed/port/mpconfigport_common.h @@ -40,3 +40,13 @@ typedef long mp_off_t; #endif #define MICROPY_MPHALPORT_H "port/mphalport.h" + +// Default implementation for applications that want output to go to the system +// printf(). If you don't, or don't have printf() available, or use +// shared/libc/printf.c (MICROPY_USE_INTERNAL_PRINTF) which would be a circular +// definition, #undef this and implement mp_hal_stdout_tx_strn_cooked(). +#define MP_PLAT_PRINT_STRN(str, len) \ + do { \ + extern int printf(const char *fmt, ...); \ + printf("%.*s", (int)len, str); \ + } while (0) diff --git a/ports/embed/port/mphalport.c b/ports/embed/port/mphalport.c deleted file mode 100644 index 8e76a8e22e..0000000000 --- a/ports/embed/port/mphalport.c +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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); -} From 1be753fb7e5eccfb49ff6198d77b048d849d7baf Mon Sep 17 00:00:00 2001 From: Christian Walther Date: Mon, 1 May 2023 16:35:26 +0200 Subject: [PATCH 04/13] ports/embed: Enable use of extmod modules. To include extmod and its dependencies in the output, include the word "extmod" in make variable EMBED_EXTRA when building the port. Ideally this would be deduced automatically from the set of modules enabled in mpconfigport.h (in a more fine-grained way too), but I can't think of a simple way of achieving that. Change in mphalport.h: fixes compiler error "type name requires a specifier or qualifier" in machine_i2c.h, included from machine_i2c.c. Signed-off-by: Christian Walther --- examples/embedding-full/Makefile | 5 +++- examples/embedding-full/main.c | 6 +++++ examples/embedding-full/micropython_embed.mk | 3 +++ examples/embedding-full/mpconfigport.h | 3 +++ examples/embedding-full/mphal.c | 26 ++++++++++++++++++++ ports/embed/embed.mk | 14 +++++++++++ ports/embed/port/mphalport.h | 2 +- 7 files changed, 57 insertions(+), 2 deletions(-) diff --git a/examples/embedding-full/Makefile b/examples/embedding-full/Makefile index c51fe5ed4d..3e696857fd 100644 --- a/examples/embedding-full/Makefile +++ b/examples/embedding-full/Makefile @@ -15,7 +15,10 @@ CFLAGS += -I$(EMBED_DIR)/port CFLAGS += -Wall -Og -fno-common SRC += main.c mphal.c -SRC += $(wildcard $(EMBED_DIR)/*/*.c) $(wildcard $(EMBED_DIR)/*/*/*.c) +SRC += $(wildcard $(EMBED_DIR)/*/*.c) +# Filter out lib because the files in there cannot be compiled separately, they +# are #included by other .c files. +SRC += $(filter-out $(EMBED_DIR)/lib/%.c,$(wildcard $(EMBED_DIR)/*/*/*.c)) OBJ += $(SRC:.c=.o) $(PROG): $(OBJ) diff --git a/examples/embedding-full/main.c b/examples/embedding-full/main.c index f0cdb0c1c3..2c936b53fa 100644 --- a/examples/embedding-full/main.c +++ b/examples/embedding-full/main.c @@ -28,6 +28,12 @@ static const char *example_2 = "help('modules')\n" "import sys\n" "help(sys)\n" + "import os\n" + "help(os)\n" + "import random\n" + "help(random)\n" + "import time\n" + "help(time)\n" "\n" "print('finish')\n" ; diff --git a/examples/embedding-full/micropython_embed.mk b/examples/embedding-full/micropython_embed.mk index 5db5415927..31dd2e8ecd 100644 --- a/examples/embedding-full/micropython_embed.mk +++ b/examples/embedding-full/micropython_embed.mk @@ -5,5 +5,8 @@ # Set the location of the top of the MicroPython repository. MICROPYTHON_TOP = ../.. +# Include modules from extmod in the output. +EMBED_EXTRA = extmod + # Include the main makefile fragment to build the MicroPython component. include $(MICROPYTHON_TOP)/ports/embed/embed.mk diff --git a/examples/embedding-full/mpconfigport.h b/examples/embedding-full/mpconfigport.h index 38cd0f2baf..3389c4d036 100644 --- a/examples/embedding-full/mpconfigport.h +++ b/examples/embedding-full/mpconfigport.h @@ -19,6 +19,9 @@ // Requires shared/readline/readline.h, don't bother as we have no input. #define MICROPY_PY_BUILTINS_INPUT (0) +// Requires MICROPY_EVENT_POLL_HOOK, don't bother as we have no pollable objects. +#define MICROPY_PY_SELECT (0) + // Can be enabled once extmod/moductypes.c is included in the build. #define MICROPY_PY_UCTYPES (0) diff --git a/examples/embedding-full/mphal.c b/examples/embedding-full/mphal.c index 9af36b44a3..897a8f6985 100644 --- a/examples/embedding-full/mphal.c +++ b/examples/embedding-full/mphal.c @@ -26,6 +26,32 @@ MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, mp_builtin_open); #endif +#if MICROPY_PY_ASYNCIO + +mp_uint_t mp_hal_ticks_ms(void) { + return 0; +} + +#endif + +#if MICROPY_PY_TIME + +void mp_hal_delay_ms(mp_uint_t ms) { +} + +void mp_hal_delay_us(mp_uint_t us) { +} + +mp_uint_t mp_hal_ticks_us(void) { + return 0; +} + +mp_uint_t mp_hal_ticks_cpu(void) { + return 0; +} + +#endif + // Text-mode standard output void mp_hal_stdout_tx_strn_cooked(const char *str, size_t len) { // This is a simplistic implementation for demonstration purposes. A real diff --git a/ports/embed/embed.mk b/ports/embed/embed.mk index bea5e5d65b..b0ca6c82a4 100644 --- a/ports/embed/embed.mk +++ b/ports/embed/embed.mk @@ -12,6 +12,9 @@ include $(MICROPYTHON_TOP)/py/mkenv.mk # Include py core make definitions. include $(TOP)/py/py.mk +ifeq ($(filter extmod,$(EMBED_EXTRA)),extmod) +include $(TOP)/extmod/extmod.mk +endif # Set the location of the MicroPython embed port. MICROPYTHON_EMBED_PORT = $(MICROPYTHON_TOP)/ports/embed @@ -43,6 +46,9 @@ clean-micropython-embed-package: PACKAGE_DIR ?= micropython_embed PACKAGE_DIR_LIST = $(addprefix $(PACKAGE_DIR)/,py extmod shared/runtime genhdr port) +ifeq ($(filter extmod,$(EMBED_EXTRA)),extmod) +PACKAGE_DIR_LIST += $(addprefix $(PACKAGE_DIR)/,lib/uzlib lib/crypto-algorithms lib/re1.5) +endif .PHONY: micropython-embed-package micropython-embed-package: $(GENHDR_OUTPUT) @@ -52,7 +58,15 @@ micropython-embed-package: $(GENHDR_OUTPUT) $(ECHO) "- py" $(Q)$(CP) $(TOP)/py/*.[ch] $(PACKAGE_DIR)/py $(ECHO) "- extmod" +ifeq ($(filter extmod,$(EMBED_EXTRA)),extmod) + $(Q)$(CP) $(TOP)/extmod/*.[ch] $(PACKAGE_DIR)/extmod + $(ECHO) "- lib" + $(Q)$(CP) $(TOP)/lib/uzlib/*.[ch] $(PACKAGE_DIR)/lib/uzlib + $(Q)$(CP) $(TOP)/lib/crypto-algorithms/*.[ch] $(PACKAGE_DIR)/lib/crypto-algorithms + $(Q)$(CP) $(TOP)/lib/re1.5/*.[ch] $(PACKAGE_DIR)/lib/re1.5 +else $(Q)$(CP) $(TOP)/extmod/modplatform.h $(PACKAGE_DIR)/extmod +endif $(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 diff --git a/ports/embed/port/mphalport.h b/ports/embed/port/mphalport.h index fd4a58aa8f..43cf41de12 100644 --- a/ports/embed/port/mphalport.h +++ b/ports/embed/port/mphalport.h @@ -1,5 +1,5 @@ // Define so there's no dependency on extmod/virtpin.h -#define mp_hal_pin_obj_t +#define mp_hal_pin_obj_t mp_obj_t #if MICROPY_KBD_EXCEPTION void mp_hal_set_interrupt_char(int c); From d3656e97fef97a681b0196842800c1ae3230c82f Mon Sep 17 00:00:00 2001 From: Christian Walther Date: Wed, 3 May 2023 22:02:30 +0200 Subject: [PATCH 05/13] ports/embed: Enable freezing Python modules. Use FROZEN_MANIFEST as documented in docs/reference/manifest.rst. Signed-off-by: Christian Walther --- examples/embedding-full/README.md | 2 ++ examples/embedding-full/main.c | 3 +++ examples/embedding-full/manifest.py | 1 + examples/embedding-full/micropython_embed.mk | 3 +++ examples/embedding-full/modules/frozenhello.py | 2 ++ examples/embedding-full/mpconfigport.h | 10 ++++++++++ ports/embed/embed.mk | 14 +++++++++++++- 7 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 examples/embedding-full/manifest.py create mode 100644 examples/embedding-full/modules/frozenhello.py diff --git a/examples/embedding-full/README.md b/examples/embedding-full/README.md index a185e16b2b..1670526cc4 100644 --- a/examples/embedding-full/README.md +++ b/examples/embedding-full/README.md @@ -15,6 +15,8 @@ Building the example First build the embed port using: + $ make -C mpy-cross + $ cd examples/embedding-full $ make -f micropython_embed.mk submodules $ make -f micropython_embed.mk diff --git a/examples/embedding-full/main.c b/examples/embedding-full/main.c index 2c936b53fa..a24760afdb 100644 --- a/examples/embedding-full/main.c +++ b/examples/embedding-full/main.c @@ -34,6 +34,9 @@ static const char *example_2 = "help(random)\n" "import time\n" "help(time)\n" + "import frozenhello\n" + "help(frozenhello)\n" + "print('frozenhello.hello():', frozenhello.hello())\n" "\n" "print('finish')\n" ; diff --git a/examples/embedding-full/manifest.py b/examples/embedding-full/manifest.py new file mode 100644 index 0000000000..19c20cc7e7 --- /dev/null +++ b/examples/embedding-full/manifest.py @@ -0,0 +1 @@ +module("frozenhello.py", base_path="modules") diff --git a/examples/embedding-full/micropython_embed.mk b/examples/embedding-full/micropython_embed.mk index 31dd2e8ecd..ee96a5f486 100644 --- a/examples/embedding-full/micropython_embed.mk +++ b/examples/embedding-full/micropython_embed.mk @@ -8,5 +8,8 @@ MICROPYTHON_TOP = ../.. # Include modules from extmod in the output. EMBED_EXTRA = extmod +# Freeze Python modules. +FROZEN_MANIFEST ?= manifest.py + # Include the main makefile fragment to build the MicroPython component. include $(MICROPYTHON_TOP)/ports/embed/embed.mk diff --git a/examples/embedding-full/modules/frozenhello.py b/examples/embedding-full/modules/frozenhello.py new file mode 100644 index 0000000000..cad6804776 --- /dev/null +++ b/examples/embedding-full/modules/frozenhello.py @@ -0,0 +1,2 @@ +def hello(): + return "Hello from the cold!" diff --git a/examples/embedding-full/mpconfigport.h b/examples/embedding-full/mpconfigport.h index 3389c4d036..8175b54db0 100644 --- a/examples/embedding-full/mpconfigport.h +++ b/examples/embedding-full/mpconfigport.h @@ -36,3 +36,13 @@ // We have our own implementation of mp_hal_stdout_tx_strn_cooked(). #undef MP_PLAT_PRINT_STRN + +// Enable freezing of Python modules. These would be set automatically for the +// port build based on the presence of FROZEN_MANIFEST by py/mkrules.mk, but +// must be set explicitly for the application build. +#define MICROPY_MODULE_FROZEN_MPY 1 +#define MICROPY_MODULE_FROZEN_STR 1 +#define MICROPY_QSTR_EXTRA_POOL mp_qstr_frozen_const_pool +// must match MPY_TOOL_FLAGS or defaults for mpy-tool.py arguments +#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) +#define MPZ_DIG_SIZE (16) diff --git a/ports/embed/embed.mk b/ports/embed/embed.mk index b0ca6c82a4..d5e6fb69d7 100644 --- a/ports/embed/embed.mk +++ b/ports/embed/embed.mk @@ -34,6 +34,11 @@ GENHDR_OUTPUT = $(addprefix $(BUILD)/genhdr/, \ root_pointers.h \ ) +# Define the module freezing output. +ifneq ($(FROZEN_MANIFEST),) +FROZEN_OUTPUT = $(BUILD)/frozen_content.c +endif + # Define the top-level target, the generated output files. .PHONY: all all: micropython-embed-package @@ -49,9 +54,12 @@ PACKAGE_DIR_LIST = $(addprefix $(PACKAGE_DIR)/,py extmod shared/runtime genhdr p ifeq ($(filter extmod,$(EMBED_EXTRA)),extmod) PACKAGE_DIR_LIST += $(addprefix $(PACKAGE_DIR)/,lib/uzlib lib/crypto-algorithms lib/re1.5) endif +ifneq ($(FROZEN_MANIFEST),) +PACKAGE_DIR_LIST += $(addprefix $(PACKAGE_DIR)/,frozen) +endif .PHONY: micropython-embed-package -micropython-embed-package: $(GENHDR_OUTPUT) +micropython-embed-package: $(GENHDR_OUTPUT) $(FROZEN_OUTPUT) $(ECHO) "Generate micropython_embed output:" $(Q)$(RM) -rf $(PACKAGE_DIR_LIST) $(Q)$(MKDIR) -p $(PACKAGE_DIR_LIST) @@ -72,6 +80,10 @@ endif $(Q)$(CP) $(TOP)/shared/runtime/gchelper_generic.c $(PACKAGE_DIR)/shared/runtime $(ECHO) "- genhdr" $(Q)$(CP) $(GENHDR_OUTPUT) $(PACKAGE_DIR)/genhdr +ifneq ($(FROZEN_MANIFEST),) + $(ECHO) "- frozen" + $(Q)$(CP) $(FROZEN_OUTPUT) $(PACKAGE_DIR)/frozen +endif $(ECHO) "- port" $(Q)$(CP) $(MICROPYTHON_EMBED_PORT)/port/*.[ch] $(PACKAGE_DIR)/port From 605493d50877c41776faddd447dfddbd78f97647 Mon Sep 17 00:00:00 2001 From: Christian Walther Date: Wed, 3 May 2023 22:45:43 +0200 Subject: [PATCH 06/13] examples/embedding-full: Add example user C module. This works out of the box in the embed port. Signed-off-by: Christian Walther --- examples/embedding-full/Makefile | 2 +- examples/embedding-full/main.c | 3 +++ examples/embedding-full/micropython_embed.mk | 3 +++ .../modules/c_hello/micropython.mk | 2 ++ .../modules/c_hello/modc_hello.c | 23 +++++++++++++++++++ 5 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 examples/embedding-full/modules/c_hello/micropython.mk create mode 100644 examples/embedding-full/modules/c_hello/modc_hello.c diff --git a/examples/embedding-full/Makefile b/examples/embedding-full/Makefile index 3e696857fd..b0ce3daf14 100644 --- a/examples/embedding-full/Makefile +++ b/examples/embedding-full/Makefile @@ -14,7 +14,7 @@ CFLAGS += -I$(EMBED_DIR) CFLAGS += -I$(EMBED_DIR)/port CFLAGS += -Wall -Og -fno-common -SRC += main.c mphal.c +SRC += main.c mphal.c modules/c_hello/modc_hello.c SRC += $(wildcard $(EMBED_DIR)/*/*.c) # Filter out lib because the files in there cannot be compiled separately, they # are #included by other .c files. diff --git a/examples/embedding-full/main.c b/examples/embedding-full/main.c index a24760afdb..73d7ebf45f 100644 --- a/examples/embedding-full/main.c +++ b/examples/embedding-full/main.c @@ -37,6 +37,9 @@ static const char *example_2 = "import frozenhello\n" "help(frozenhello)\n" "print('frozenhello.hello():', frozenhello.hello())\n" + "import c_hello\n" + "help(c_hello)\n" + "print('c_hello.hello():', c_hello.hello())\n" "\n" "print('finish')\n" ; diff --git a/examples/embedding-full/micropython_embed.mk b/examples/embedding-full/micropython_embed.mk index ee96a5f486..d4e76b609b 100644 --- a/examples/embedding-full/micropython_embed.mk +++ b/examples/embedding-full/micropython_embed.mk @@ -11,5 +11,8 @@ EMBED_EXTRA = extmod # Freeze Python modules. FROZEN_MANIFEST ?= manifest.py +# Add C modules. +USER_C_MODULES = modules + # Include the main makefile fragment to build the MicroPython component. include $(MICROPYTHON_TOP)/ports/embed/embed.mk diff --git a/examples/embedding-full/modules/c_hello/micropython.mk b/examples/embedding-full/modules/c_hello/micropython.mk new file mode 100644 index 0000000000..7d610d1edd --- /dev/null +++ b/examples/embedding-full/modules/c_hello/micropython.mk @@ -0,0 +1,2 @@ +C_HELLO_MOD_DIR := $(USERMOD_DIR) +SRC_USERMOD_C += $(C_HELLO_MOD_DIR)/modc_hello.c diff --git a/examples/embedding-full/modules/c_hello/modc_hello.c b/examples/embedding-full/modules/c_hello/modc_hello.c new file mode 100644 index 0000000000..548c7a3349 --- /dev/null +++ b/examples/embedding-full/modules/c_hello/modc_hello.c @@ -0,0 +1,23 @@ +// Check examples/usercmodule for more info about how this works. + +#include "py/runtime.h" +#include "py/objstr.h" + +static mp_obj_t c_hello_hello() { + static const MP_DEFINE_STR_OBJ(hello_str, "Hello from C!"); + return MP_OBJ_FROM_PTR(&hello_str); +} +static MP_DEFINE_CONST_FUN_OBJ_0(c_hello_hello_obj, c_hello_hello); + +static const mp_rom_map_elem_t c_hello_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_c_hello) }, + { MP_ROM_QSTR(MP_QSTR_hello), MP_ROM_PTR(&c_hello_hello_obj) }, +}; +static MP_DEFINE_CONST_DICT(c_hello_module_globals, c_hello_module_globals_table); + +const mp_obj_module_t c_hello_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&c_hello_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_c_hello, c_hello_module); From 124b2dbfe017b5fa69f4629299ad1ec3a0ee4a87 Mon Sep 17 00:00:00 2001 From: Christian Walther Date: Wed, 3 May 2023 22:51:59 +0200 Subject: [PATCH 07/13] examples/embedding-full: Enable os.uname(). Signed-off-by: Christian Walther --- examples/embedding-full/main.c | 1 + examples/embedding-full/mpconfigport.h | 3 +++ 2 files changed, 4 insertions(+) diff --git a/examples/embedding-full/main.c b/examples/embedding-full/main.c index 73d7ebf45f..5fc31ec638 100644 --- a/examples/embedding-full/main.c +++ b/examples/embedding-full/main.c @@ -30,6 +30,7 @@ static const char *example_2 = "help(sys)\n" "import os\n" "help(os)\n" + "print('os.uname():', os.uname())\n" "import random\n" "help(random)\n" "import time\n" diff --git a/examples/embedding-full/mpconfigport.h b/examples/embedding-full/mpconfigport.h index 8175b54db0..103172687a 100644 --- a/examples/embedding-full/mpconfigport.h +++ b/examples/embedding-full/mpconfigport.h @@ -15,6 +15,9 @@ #define MICROPY_PY_GC (1) #define MICROPY_PY_SYS_PLATFORM "embedded" +#define MICROPY_PY_OS_UNAME (1) +#define MICROPY_HW_BOARD_NAME "embedded" +#define MICROPY_HW_MCU_NAME "C" // Requires shared/readline/readline.h, don't bother as we have no input. #define MICROPY_PY_BUILTINS_INPUT (0) From 5a1638986fa5f613f147963e956643a996e0fcc2 Mon Sep 17 00:00:00 2001 From: Christian Walther Date: Wed, 3 May 2023 22:56:59 +0200 Subject: [PATCH 08/13] examples/embedding-full: Enable floating point numbers and math module. Signed-off-by: Christian Walther --- examples/embedding-full/Makefile | 2 +- examples/embedding-full/main.c | 4 +++- examples/embedding-full/mpconfigport.h | 3 +++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/examples/embedding-full/Makefile b/examples/embedding-full/Makefile index b0ce3daf14..abdf8866e0 100644 --- a/examples/embedding-full/Makefile +++ b/examples/embedding-full/Makefile @@ -22,7 +22,7 @@ SRC += $(filter-out $(EMBED_DIR)/lib/%.c,$(wildcard $(EMBED_DIR)/*/*/*.c)) OBJ += $(SRC:.c=.o) $(PROG): $(OBJ) - $(CC) -o $@ $^ + $(CC) -o $@ $^ -lm clean: /bin/rm -f $(OBJ) $(PROG) diff --git a/examples/embedding-full/main.c b/examples/embedding-full/main.c index 5fc31ec638..457af0b24e 100644 --- a/examples/embedding-full/main.c +++ b/examples/embedding-full/main.c @@ -8,7 +8,7 @@ // This is example 1 script, which will be compiled and executed. static const char *example_1 = - "print('hello world!', list(x + 1 for x in range(10)), end='eol\\n')"; + "print('hello world!', list(x + 1.5 for x in range(10)), end='eol\\n')"; // This is example 2 script, which will be compiled and executed. static const char *example_2 = @@ -35,6 +35,8 @@ static const char *example_2 = "help(random)\n" "import time\n" "help(time)\n" + "import math\n" + "help(math)\n" "import frozenhello\n" "help(frozenhello)\n" "print('frozenhello.hello():', frozenhello.hello())\n" diff --git a/examples/embedding-full/mpconfigport.h b/examples/embedding-full/mpconfigport.h index 103172687a..2678c722b8 100644 --- a/examples/embedding-full/mpconfigport.h +++ b/examples/embedding-full/mpconfigport.h @@ -19,6 +19,9 @@ #define MICROPY_HW_BOARD_NAME "embedded" #define MICROPY_HW_MCU_NAME "C" +// Enable floating point numbers and the math module. +#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) + // Requires shared/readline/readline.h, don't bother as we have no input. #define MICROPY_PY_BUILTINS_INPUT (0) From 170ba153117daedc390ae8bf4761f64f857b0a71 Mon Sep 17 00:00:00 2001 From: Christian Walther Date: Fri, 5 May 2023 19:37:49 +0200 Subject: [PATCH 09/13] ports/embed: Implement additional time functions. Optionally adds gmtime, localtime, mktime, time, time_ns to the time module, implemented using mp_hal_time_ns(). This could also be used by other ports. I'm unsure where to put modtime_mphal.h, it could also be in extmod. The important thing is that for MICROPY_PY_TIME_INCLUDEFILE to work it must be at the same path in both the port build (original source tree) and the application build (micropython_embed distribution), therefore not in ports/embed/port. It is named .h, mismatching the corresponding ports/*/modtime.c, because it must not be compiled separately, which naming it .c would make harder for users of the embed port - they would need to explicitly exclude it, whereas this way they can continue to just compile all the .c files found in the micropython_embed distribution except those in lib. Signed-off-by: Christian Walther --- examples/embedding-full/main.c | 1 + examples/embedding-full/micropython_embed.mk | 6 +++ examples/embedding-full/mpconfigport.h | 6 +++ examples/embedding-full/mphal.c | 9 ++++ ports/embed/embed.mk | 13 +++++ shared/timeutils/modtime_mphal.h | 52 ++++++++++++++++++++ 6 files changed, 87 insertions(+) create mode 100644 shared/timeutils/modtime_mphal.h diff --git a/examples/embedding-full/main.c b/examples/embedding-full/main.c index 457af0b24e..c4c1e040ad 100644 --- a/examples/embedding-full/main.c +++ b/examples/embedding-full/main.c @@ -35,6 +35,7 @@ static const char *example_2 = "help(random)\n" "import time\n" "help(time)\n" + "print('time.gmtime(736622952) = 2023-05-05T17:29:12Z:', time.gmtime(736622952))\n" "import math\n" "help(math)\n" "import frozenhello\n" diff --git a/examples/embedding-full/micropython_embed.mk b/examples/embedding-full/micropython_embed.mk index d4e76b609b..786582030e 100644 --- a/examples/embedding-full/micropython_embed.mk +++ b/examples/embedding-full/micropython_embed.mk @@ -8,6 +8,12 @@ MICROPYTHON_TOP = ../.. # Include modules from extmod in the output. EMBED_EXTRA = extmod +# Include helper sources for the time module in the output. +EMBED_EXTRA += \ + shared/timeutils/timeutils.c \ + shared/timeutils/timeutils.h \ + shared/timeutils/modtime_mphal.h + # Freeze Python modules. FROZEN_MANIFEST ?= manifest.py diff --git a/examples/embedding-full/mpconfigport.h b/examples/embedding-full/mpconfigport.h index 2678c722b8..6120bd70a8 100644 --- a/examples/embedding-full/mpconfigport.h +++ b/examples/embedding-full/mpconfigport.h @@ -22,6 +22,12 @@ // Enable floating point numbers and the math module. #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) +// Enable more functions in the time module. Requires additions to EMBED_EXTRA, +// see micropython_embed.mk. +#define MICROPY_PY_TIME_GMTIME_LOCALTIME_MKTIME (1) +#define MICROPY_PY_TIME_TIME_TIME_NS (1) +#define MICROPY_PY_TIME_INCLUDEFILE "shared/timeutils/modtime_mphal.h" + // Requires shared/readline/readline.h, don't bother as we have no input. #define MICROPY_PY_BUILTINS_INPUT (0) diff --git a/examples/embedding-full/mphal.c b/examples/embedding-full/mphal.c index 897a8f6985..47b39e31ec 100644 --- a/examples/embedding-full/mphal.c +++ b/examples/embedding-full/mphal.c @@ -52,6 +52,15 @@ mp_uint_t mp_hal_ticks_cpu(void) { #endif +#if MICROPY_PY_TIME_TIME_TIME_NS + +uint64_t mp_hal_time_ns(void) { + // Nanoseconds since the Epoch. + return 0; +} + +#endif + // Text-mode standard output void mp_hal_stdout_tx_strn_cooked(const char *str, size_t len) { // This is a simplistic implementation for demonstration purposes. A real diff --git a/ports/embed/embed.mk b/ports/embed/embed.mk index d5e6fb69d7..bb39f3b529 100644 --- a/ports/embed/embed.mk +++ b/ports/embed/embed.mk @@ -16,6 +16,12 @@ ifeq ($(filter extmod,$(EMBED_EXTRA)),extmod) include $(TOP)/extmod/extmod.mk endif +# The parts of EMBED_EXTRA that name literal files and don't need special handling. +EMBED_EXTRA_FILES = $(filter-out extmod,$(EMBED_EXTRA)) + +# Extra files need to be searched for QSTRs. +SRC_QSTR += $(addprefix $(TOP)/,$(EMBED_EXTRA_FILES)) + # Set the location of the MicroPython embed port. MICROPYTHON_EMBED_PORT = $(MICROPYTHON_TOP)/ports/embed @@ -57,6 +63,9 @@ endif ifneq ($(FROZEN_MANIFEST),) PACKAGE_DIR_LIST += $(addprefix $(PACKAGE_DIR)/,frozen) endif +ifneq ($(EMBED_EXTRA_FILES),) +PACKAGE_DIR_LIST += $(addprefix $(PACKAGE_DIR)/,$(filter-out ./,$(sort $(dir $(EMBED_EXTRA_FILES))))) +endif .PHONY: micropython-embed-package micropython-embed-package: $(GENHDR_OUTPUT) $(FROZEN_OUTPUT) @@ -86,6 +95,10 @@ ifneq ($(FROZEN_MANIFEST),) endif $(ECHO) "- port" $(Q)$(CP) $(MICROPYTHON_EMBED_PORT)/port/*.[ch] $(PACKAGE_DIR)/port +ifneq ($(EMBED_EXTRA_FILES),) + $(ECHO) "- extra" + $(Q)$(foreach FILE,$(EMBED_EXTRA_FILES),$(CP) $(TOP)/$(FILE) $(dir $(PACKAGE_DIR)/$(FILE)) &&) true +endif # Include remaining core make rules. include $(TOP)/py/mkrules.mk diff --git a/shared/timeutils/modtime_mphal.h b/shared/timeutils/modtime_mphal.h new file mode 100644 index 0000000000..c1a2c07a0b --- /dev/null +++ b/shared/timeutils/modtime_mphal.h @@ -0,0 +1,52 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-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 "py/obj.h" +#include "py/mphal.h" +#include "shared/timeutils/timeutils.h" + +// Return the localtime as an 8-tuple. +static mp_obj_t mp_time_localtime_get(void) { + mp_int_t seconds = mp_hal_time_ns() / 1000000000; + timeutils_struct_time_t tm; + timeutils_seconds_since_epoch_to_struct_time(seconds, &tm); + mp_obj_t tuple[8] = { + tuple[0] = mp_obj_new_int(tm.tm_year), + tuple[1] = mp_obj_new_int(tm.tm_mon), + tuple[2] = mp_obj_new_int(tm.tm_mday), + tuple[3] = mp_obj_new_int(tm.tm_hour), + tuple[4] = mp_obj_new_int(tm.tm_min), + tuple[5] = mp_obj_new_int(tm.tm_sec), + tuple[6] = mp_obj_new_int(tm.tm_wday), + tuple[7] = mp_obj_new_int(tm.tm_yday), + }; + return mp_obj_new_tuple(8, tuple); +} + +// Returns the number of seconds, as an integer, since the Epoch. +static mp_obj_t mp_time_time_get(void) { + return mp_obj_new_int(mp_hal_time_ns() / 1000000000); +} From e03158d2b92e9e110e88a2e8e29418a2f6bfc4f0 Mon Sep 17 00:00:00 2001 From: Christian Walther Date: Fri, 5 May 2023 23:17:32 +0200 Subject: [PATCH 10/13] examples/embedding-full: Enable uctypes module. Signed-off-by: Christian Walther --- examples/embedding-full/main.c | 2 ++ examples/embedding-full/mpconfigport.h | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/examples/embedding-full/main.c b/examples/embedding-full/main.c index c4c1e040ad..0b1d2adf64 100644 --- a/examples/embedding-full/main.c +++ b/examples/embedding-full/main.c @@ -38,6 +38,8 @@ static const char *example_2 = "print('time.gmtime(736622952) = 2023-05-05T17:29:12Z:', time.gmtime(736622952))\n" "import math\n" "help(math)\n" + "import uctypes\n" + "help(uctypes)\n" "import frozenhello\n" "help(frozenhello)\n" "print('frozenhello.hello():', frozenhello.hello())\n" diff --git a/examples/embedding-full/mpconfigport.h b/examples/embedding-full/mpconfigport.h index 6120bd70a8..adbaa48e07 100644 --- a/examples/embedding-full/mpconfigport.h +++ b/examples/embedding-full/mpconfigport.h @@ -34,8 +34,9 @@ // Requires MICROPY_EVENT_POLL_HOOK, don't bother as we have no pollable objects. #define MICROPY_PY_SELECT (0) -// Can be enabled once extmod/moductypes.c is included in the build. -#define MICROPY_PY_UCTYPES (0) +// On by default and works in this configuration, but disable it to avoid a +// linker error if you don't include extmod/moductypes.c in the build. +//#define MICROPY_PY_UCTYPES (0) // Can be enabled once either shared/runtime/sys_stdio_mphal.c or // extmod/vfs_posix_file.c is included in the build. From 972938c7213e33bb70ea8a9650f256ae28e62c1e Mon Sep 17 00:00:00 2001 From: Christian Walther Date: Sat, 30 Mar 2024 23:26:51 +0100 Subject: [PATCH 11/13] examples/embedding-full: Enable mphal-backed sys.stdin/out/err. Signed-off-by: Christian Walther --- examples/embedding-full/main.c | 8 ++++- examples/embedding-full/micropython_embed.mk | 5 ++++ examples/embedding-full/mpconfigport.h | 2 +- examples/embedding-full/mphal.c | 31 ++++++++++++++++++++ 4 files changed, 44 insertions(+), 2 deletions(-) diff --git a/examples/embedding-full/main.c b/examples/embedding-full/main.c index 0b1d2adf64..f3002c9d6d 100644 --- a/examples/embedding-full/main.c +++ b/examples/embedding-full/main.c @@ -28,6 +28,12 @@ static const char *example_2 = "help('modules')\n" "import sys\n" "help(sys)\n" + "print('sys.stdin.read(3):', repr(sys.stdin.read(3)))\n" + "print('sys.stdin.buffer.read(3):', repr(sys.stdin.buffer.read(3)))\n" + "sys.stdout.write('hello stdout text\\n')\n" + "sys.stdout.buffer.write(b'hello stdout binary\\n')\n" + "sys.stdout.write('hello stderr text\\n')\n" + "sys.stdout.buffer.write(b'hello stderr binary\\n')\n" "import os\n" "help(os)\n" "print('os.uname():', os.uname())\n" @@ -51,7 +57,7 @@ static const char *example_2 = ; // This array is the MicroPython GC heap. -static char heap[8 * 1024]; +static char heap[10 * 1024]; int main() { #if MICROPY_STACK_CHECK diff --git a/examples/embedding-full/micropython_embed.mk b/examples/embedding-full/micropython_embed.mk index 786582030e..e21c88d1fe 100644 --- a/examples/embedding-full/micropython_embed.mk +++ b/examples/embedding-full/micropython_embed.mk @@ -14,6 +14,11 @@ EMBED_EXTRA += \ shared/timeutils/timeutils.h \ shared/timeutils/modtime_mphal.h +# Include source for mphal-backed stdio in the output. +# Disable when using POSIX-backed stdio (MICROPY_VFS_POSIX). +EMBED_EXTRA += \ + shared/runtime/sys_stdio_mphal.c + # Freeze Python modules. FROZEN_MANIFEST ?= manifest.py diff --git a/examples/embedding-full/mpconfigport.h b/examples/embedding-full/mpconfigport.h index adbaa48e07..bc326a35fd 100644 --- a/examples/embedding-full/mpconfigport.h +++ b/examples/embedding-full/mpconfigport.h @@ -40,7 +40,7 @@ // Can be enabled once either shared/runtime/sys_stdio_mphal.c or // extmod/vfs_posix_file.c is included in the build. -#define MICROPY_PY_SYS_STDFILES (0) +//#define MICROPY_PY_SYS_STDFILES (0) // Can be enabled if you provide an implementation of // mp_hal_set_interrupt_char() in mphal.c or include diff --git a/examples/embedding-full/mphal.c b/examples/embedding-full/mphal.c index 47b39e31ec..76654c6155 100644 --- a/examples/embedding-full/mphal.c +++ b/examples/embedding-full/mphal.c @@ -7,6 +7,9 @@ #include "py/builtin.h" #include "py/compile.h" #include "py/mperrno.h" +#if MICROPY_PY_SYS_STDFILES +#include "py/stream.h" +#endif #if !MICROPY_VFS @@ -71,3 +74,31 @@ void mp_hal_stdout_tx_strn_cooked(const char *str, size_t len) { printf("%.*s", (int)len, str); start_of_line = (len > 0 && (str[len-1] == '\n' || str[len-1] == '\r')); } + +#if MICROPY_PY_SYS_STDFILES + +// Binary-mode standard input +// Receive single character, blocking until one is available. +int mp_hal_stdin_rx_chr(void) { + return 'X'; +} + +// Binary-mode standard output +// Send the string of given length. +mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) { + printf("tx: %.*s", (int)len, str); + return len; +} + +uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { + uintptr_t ret = 0; + if ((poll_flags & MP_STREAM_POLL_RD) /* && can_read */) { + ret |= MP_STREAM_POLL_RD; + } + if ((poll_flags & MP_STREAM_POLL_WR) /* && can_write */) { + ret |= MP_STREAM_POLL_WR; + } + return ret; +} + +#endif From 3a8f8db38d10a12ba84fc4a95ee0eef5306cd2c8 Mon Sep 17 00:00:00 2001 From: Christian Walther Date: Sun, 25 Feb 2024 15:44:49 +0100 Subject: [PATCH 12/13] ports/embed: Enable use of littlefs. To make use of it, as demonstrated in the embedding-full example: - Include the word(s) "littlefs1" and/or "littlefs2" in EMBED_EXTRA in micropython_embed.mk. - Enable the desired MICROPY_*VFS* features in mpconfigport.h. - Add include path "$(EMBED_DIR)/lib/littlefs" to your application build system. For demonstration, a block device implementation containing a small read-only littlefs2 image is added to the embedding-full example. The block device could have been written in C and interface to some external, possibly writable storage medium, but was written in Python and frozen for simplicity. What makes this a bit awkward is the fact that the littlefs sources are located in lib/ and do need to be compiled into the application, unlike all previous .c files from lib/ that are #included by other C files and must not be compiled separately. Therefore, introduce a new directory 'libsrc' in the embed build output that contains these files and can be added to the application build system as usual, while 'lib' can remain excluded. The headers need to stay in 'lib' because vfs_lfs.c refers to them under that path. I am also not entirely happy about having to add an additional include path to the application build, but see no way around that as long as vfs_lfs.c refers to "lib/littlefs/lfs2.h" while lfs2.c refers to "lfs2.h". Signed-off-by: Christian Walther --- examples/embedding-full/Makefile | 1 + examples/embedding-full/main.c | 11 +++++++- examples/embedding-full/manifest.py | 1 + examples/embedding-full/micropython_embed.mk | 3 +++ examples/embedding-full/modules/fsimage.py | 27 ++++++++++++++++++++ examples/embedding-full/mpconfigport.h | 6 +++++ extmod/modvfs.c | 6 +++++ ports/embed/embed.mk | 16 +++++++++++- 8 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 examples/embedding-full/modules/fsimage.py diff --git a/examples/embedding-full/Makefile b/examples/embedding-full/Makefile index abdf8866e0..abae7b71b8 100644 --- a/examples/embedding-full/Makefile +++ b/examples/embedding-full/Makefile @@ -12,6 +12,7 @@ PROG = embed CFLAGS += -I. CFLAGS += -I$(EMBED_DIR) CFLAGS += -I$(EMBED_DIR)/port +CFLAGS += -I$(EMBED_DIR)/lib/littlefs CFLAGS += -Wall -Og -fno-common SRC += main.c mphal.c modules/c_hello/modc_hello.c diff --git a/examples/embedding-full/main.c b/examples/embedding-full/main.c index f3002c9d6d..f8318ee7ed 100644 --- a/examples/embedding-full/main.c +++ b/examples/embedding-full/main.c @@ -52,12 +52,21 @@ static const char *example_2 = "import c_hello\n" "help(c_hello)\n" "print('c_hello.hello():', c_hello.hello())\n" + "import fsimage, vfs\n" + "vfs.mount(fsimage.BlockDev(), '/lfs', readonly=True)\n" + "print('os.listdir(\\'/lfs\\'):', os.listdir('/lfs'))\n" + "with open('/lfs/lfshello.py', 'rb') as f:\n" + " print('lfshello.py:', f.read())\n" + "sys.path.append('/lfs')\n" + "import lfshello\n" + "help(lfshello)\n" + "print('lfshello.hello():', lfshello.hello())\n" "\n" "print('finish')\n" ; // This array is the MicroPython GC heap. -static char heap[10 * 1024]; +static char heap[12 * 1024]; int main() { #if MICROPY_STACK_CHECK diff --git a/examples/embedding-full/manifest.py b/examples/embedding-full/manifest.py index 19c20cc7e7..69ca7adc21 100644 --- a/examples/embedding-full/manifest.py +++ b/examples/embedding-full/manifest.py @@ -1 +1,2 @@ module("frozenhello.py", base_path="modules") +module("fsimage.py", base_path="modules") diff --git a/examples/embedding-full/micropython_embed.mk b/examples/embedding-full/micropython_embed.mk index e21c88d1fe..881e4b11a4 100644 --- a/examples/embedding-full/micropython_embed.mk +++ b/examples/embedding-full/micropython_embed.mk @@ -19,6 +19,9 @@ EMBED_EXTRA += \ EMBED_EXTRA += \ shared/runtime/sys_stdio_mphal.c +# Include library sources for littlefs 2 in the output. +EMBED_EXTRA += littlefs2 + # Freeze Python modules. FROZEN_MANIFEST ?= manifest.py diff --git a/examples/embedding-full/modules/fsimage.py b/examples/embedding-full/modules/fsimage.py new file mode 100644 index 0000000000..46fbbd4c57 --- /dev/null +++ b/examples/embedding-full/modules/fsimage.py @@ -0,0 +1,27 @@ +# a minimal read-only block device containing a tiny littlefs image + + +class BlockDev: + def readblocks(self, block_num, buf, offset=0): + addr = block_num * 128 + offset + for i in range(len(buf)): + buf[i] = ( + b"\x03\x00\x00\x00\xf0\x0f\xff\xf7littlefs/\xe0\x00\x10\x01\x00\x02\x00\x80\x00\x00\x00\x03\x00\x00\x00" + b"\xff\x00\x00\x00\xff\xff\xff\x7f\xfe\x03\x00\x00 \x00\x04\x13lfshello.py 0\x00\x03\x02" + b"\x00\x00\x006\x00\x00\x00\x100\x00\x00\x00\xda#\xed=!\xb7\x17`\x1f\xf8!\x99\xf8T\x19\xff\xff\xff\xff\xff" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x02\x00\x00\x00\xf0\x0f\xff\xf7littlefs/\xe0\x00\x10\x01\x00\x02\x00\x80\x00\x00\x00\x03\x00\x00\x00" + b"\xff\x00\x00\x00\xff\xff\xff\x7f\xfe\x03\x00\x00\x7f\xef\xfc\x10 \x00\x00\x00R\xaa\xf5\xe6\x0f\xe0\x00\x0cq)\x81\xa1" + b"\x90\x0f\xf8\x04@\x00\x00\x0blfshello.py \x00\x00\x0bp\x1f\xf8%\x12\xbc\x179\xff" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b'def hello():\n return "Hello f' + b'rom a littlefs file!"\n\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff' + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + )[addr + i] + + def ioctl(self, op, arg): + if op == 4: # block count + return 3 + if op == 5: # block size + return 128 diff --git a/examples/embedding-full/mpconfigport.h b/examples/embedding-full/mpconfigport.h index bc326a35fd..59bbc2dbfa 100644 --- a/examples/embedding-full/mpconfigport.h +++ b/examples/embedding-full/mpconfigport.h @@ -19,6 +19,12 @@ #define MICROPY_HW_BOARD_NAME "embedded" #define MICROPY_HW_MCU_NAME "C" +// Enable the VFS subsystem, littlefs, importing from VFS, and the vfs module. +#define MICROPY_VFS (1) +#define MICROPY_VFS_LFS2 (1) +#define MICROPY_READER_VFS (1) +#define MICROPY_PY_VFS (1) + // Enable floating point numbers and the math module. #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) diff --git a/extmod/modvfs.c b/extmod/modvfs.c index 32dc0e5d08..611c19bab7 100644 --- a/extmod/modvfs.c +++ b/extmod/modvfs.c @@ -29,9 +29,15 @@ #if MICROPY_PY_VFS #include "extmod/vfs.h" +#if MICROPY_VFS_FAT #include "extmod/vfs_fat.h" +#endif +#if MICROPY_VFS_LFS1 || MICROPY_VFS_LFS2 #include "extmod/vfs_lfs.h" +#endif +#if MICROPY_VFS_POSIX #include "extmod/vfs_posix.h" +#endif #if !MICROPY_VFS #error "MICROPY_PY_VFS requires MICROPY_VFS" diff --git a/ports/embed/embed.mk b/ports/embed/embed.mk index bb39f3b529..cd63bc6bdb 100644 --- a/ports/embed/embed.mk +++ b/ports/embed/embed.mk @@ -17,7 +17,7 @@ include $(TOP)/extmod/extmod.mk endif # The parts of EMBED_EXTRA that name literal files and don't need special handling. -EMBED_EXTRA_FILES = $(filter-out extmod,$(EMBED_EXTRA)) +EMBED_EXTRA_FILES = $(filter-out extmod littlefs1 littlefs2,$(EMBED_EXTRA)) # Extra files need to be searched for QSTRs. SRC_QSTR += $(addprefix $(TOP)/,$(EMBED_EXTRA_FILES)) @@ -60,6 +60,9 @@ PACKAGE_DIR_LIST = $(addprefix $(PACKAGE_DIR)/,py extmod shared/runtime genhdr p ifeq ($(filter extmod,$(EMBED_EXTRA)),extmod) PACKAGE_DIR_LIST += $(addprefix $(PACKAGE_DIR)/,lib/uzlib lib/crypto-algorithms lib/re1.5) endif +ifneq ($(filter littlefs1 littlefs2,$(EMBED_EXTRA)),) +PACKAGE_DIR_LIST += $(addprefix $(PACKAGE_DIR)/,lib/littlefs libsrc/littlefs) +endif ifneq ($(FROZEN_MANIFEST),) PACKAGE_DIR_LIST += $(addprefix $(PACKAGE_DIR)/,frozen) endif @@ -83,6 +86,17 @@ ifeq ($(filter extmod,$(EMBED_EXTRA)),extmod) $(Q)$(CP) $(TOP)/lib/re1.5/*.[ch] $(PACKAGE_DIR)/lib/re1.5 else $(Q)$(CP) $(TOP)/extmod/modplatform.h $(PACKAGE_DIR)/extmod +endif +ifneq ($(filter littlefs1 littlefs2,$(EMBED_EXTRA)),) + $(ECHO) "- libsrc" +ifeq ($(filter littlefs1,$(EMBED_EXTRA)),littlefs1) + $(Q)$(CP) $(TOP)/lib/littlefs/lfs1*.h $(PACKAGE_DIR)/lib/littlefs + $(Q)$(CP) $(TOP)/lib/littlefs/lfs1*.c $(PACKAGE_DIR)/libsrc/littlefs +endif +ifeq ($(filter littlefs2,$(EMBED_EXTRA)),littlefs2) + $(Q)$(CP) $(TOP)/lib/littlefs/lfs2*.h $(PACKAGE_DIR)/lib/littlefs + $(Q)$(CP) $(TOP)/lib/littlefs/lfs2*.c $(PACKAGE_DIR)/libsrc/littlefs +endif endif $(ECHO) "- shared" $(Q)$(CP) $(TOP)/shared/runtime/gchelper.h $(PACKAGE_DIR)/shared/runtime From ca93048ab9d6c36bd01698fce824b822dcdbf9f0 Mon Sep 17 00:00:00 2001 From: Christian Walther Date: Sat, 30 Mar 2024 23:05:20 +0100 Subject: [PATCH 13/13] ports/embed: Enable use of POSIX VFS. In the embedding-full example, that requires disabling mphal-backed stdio again as it conflicts with the implementation brought in by vfs_posix_file.c. Signed-off-by: Christian Walther --- examples/embedding-full/main.c | 20 +++++++++++++++++++- examples/embedding-full/micropython_embed.mk | 4 ++-- examples/embedding-full/mpconfigport.h | 4 +++- examples/embedding-full/mphal.c | 4 +++- ports/embed/port/mphalport.h | 20 ++++++++++++++++++++ 5 files changed, 47 insertions(+), 5 deletions(-) diff --git a/examples/embedding-full/main.c b/examples/embedding-full/main.c index f8318ee7ed..62b18e8e9f 100644 --- a/examples/embedding-full/main.c +++ b/examples/embedding-full/main.c @@ -28,8 +28,15 @@ static const char *example_2 = "help('modules')\n" "import sys\n" "help(sys)\n" + // Skip testing stdin when using the POSIX implementation, i.e. it is + // connected to actual stdin, because that makes the program wait for input, + // which can be inconvenient or confusing (giving the appearance of being + // stuck). There is no harm enabling it if you remember to provide some + // input. +#if !MICROPY_VFS_POSIX "print('sys.stdin.read(3):', repr(sys.stdin.read(3)))\n" "print('sys.stdin.buffer.read(3):', repr(sys.stdin.buffer.read(3)))\n" +#endif "sys.stdout.write('hello stdout text\\n')\n" "sys.stdout.buffer.write(b'hello stdout binary\\n')\n" "sys.stdout.write('hello stderr text\\n')\n" @@ -61,12 +68,23 @@ static const char *example_2 = "import lfshello\n" "help(lfshello)\n" "print('lfshello.hello():', lfshello.hello())\n" + "vfs.mount(vfs.VfsPosix('.'), '/posix')\n" + "print('os.listdir(\\'/posix\\'):', os.listdir('/posix'))\n" + "with open('/posix/posixhello.py', 'wb') as f:\n" + " f.write(b'def hello():\\n return \"Hello from a POSIX file!\"\\n')\n" + "with open('/posix/posixhello.py', 'rb') as f:\n" + " print('posixhello.py:', f.read())\n" + "sys.path.append('/posix')\n" + "import posixhello\n" + "help(posixhello)\n" + "print('posixhello.hello():', posixhello.hello())\n" + "os.unlink('/posix/posixhello.py')" "\n" "print('finish')\n" ; // This array is the MicroPython GC heap. -static char heap[12 * 1024]; +static char heap[16 * 1024]; int main() { #if MICROPY_STACK_CHECK diff --git a/examples/embedding-full/micropython_embed.mk b/examples/embedding-full/micropython_embed.mk index 881e4b11a4..1d30d2a45a 100644 --- a/examples/embedding-full/micropython_embed.mk +++ b/examples/embedding-full/micropython_embed.mk @@ -16,8 +16,8 @@ EMBED_EXTRA += \ # Include source for mphal-backed stdio in the output. # Disable when using POSIX-backed stdio (MICROPY_VFS_POSIX). -EMBED_EXTRA += \ - shared/runtime/sys_stdio_mphal.c +#EMBED_EXTRA += \ +# shared/runtime/sys_stdio_mphal.c # Include library sources for littlefs 2 in the output. EMBED_EXTRA += littlefs2 diff --git a/examples/embedding-full/mpconfigport.h b/examples/embedding-full/mpconfigport.h index 59bbc2dbfa..2bfb38f7eb 100644 --- a/examples/embedding-full/mpconfigport.h +++ b/examples/embedding-full/mpconfigport.h @@ -19,9 +19,11 @@ #define MICROPY_HW_BOARD_NAME "embedded" #define MICROPY_HW_MCU_NAME "C" -// Enable the VFS subsystem, littlefs, importing from VFS, and the vfs module. +// Enable the VFS subsystem, littlefs and POSIX VFS, importing from VFS, and +// the vfs module. #define MICROPY_VFS (1) #define MICROPY_VFS_LFS2 (1) +#define MICROPY_VFS_POSIX (1) #define MICROPY_READER_VFS (1) #define MICROPY_PY_VFS (1) diff --git a/examples/embedding-full/mphal.c b/examples/embedding-full/mphal.c index 76654c6155..c540179a05 100644 --- a/examples/embedding-full/mphal.c +++ b/examples/embedding-full/mphal.c @@ -65,6 +65,8 @@ uint64_t mp_hal_time_ns(void) { #endif // Text-mode standard output +// (When MICROPY_PY_SYS_STDFILES && MICROPY_VFS_POSIX, this is only used for +// mp_plat_print (mostly debug output), but not for print() and sys.stdout.) void mp_hal_stdout_tx_strn_cooked(const char *str, size_t len) { // This is a simplistic implementation for demonstration purposes. A real // one would probably want to prefix every line, not just at the start of a @@ -75,7 +77,7 @@ void mp_hal_stdout_tx_strn_cooked(const char *str, size_t len) { start_of_line = (len > 0 && (str[len-1] == '\n' || str[len-1] == '\r')); } -#if MICROPY_PY_SYS_STDFILES +#if MICROPY_PY_SYS_STDFILES && !MICROPY_VFS_POSIX // Binary-mode standard input // Receive single character, blocking until one is available. diff --git a/ports/embed/port/mphalport.h b/ports/embed/port/mphalport.h index 43cf41de12..d0e2e8b01c 100644 --- a/ports/embed/port/mphalport.h +++ b/ports/embed/port/mphalport.h @@ -4,3 +4,23 @@ #if MICROPY_KBD_EXCEPTION void mp_hal_set_interrupt_char(int c); #endif + +#if MICROPY_VFS_POSIX +// This macro is used to implement PEP 475 to retry specified syscalls on EINTR +#define MP_HAL_RETRY_SYSCALL(ret, syscall, raise) { \ + for (;;) { \ + MP_THREAD_GIL_EXIT(); \ + ret = syscall; \ + MP_THREAD_GIL_ENTER(); \ + if (ret == -1) { \ + int err = errno; \ + if (err == EINTR) { \ + mp_handle_pending(true); \ + continue; \ + } \ + raise; \ + } \ + break; \ + } \ +} +#endif