From 5bed292c06ec712f501dda7e2cc202b7039c33b4 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Mon, 13 Nov 2023 17:17:57 +0100 Subject: [PATCH 01/16] lib/open-amp: Add OpenAMP submodule. OpenAMP framework provides a standard inter processor communications infrastructure for RTOS and bare metal environments. There are 3 major components in OpenAMP: libmetal, remoteproc and RPMsg. libmetal provides abstraction of the low-level underlying hardware, remoteproc is used for processor Life Cycle Management (LCM) like loading firmware, starting, stopping a core etc., and RPMsg is a bus infrastructure that enables Inter Processor Communications (IPC) between different cores. Signed-off-by: iabdalkader --- .gitmodules | 3 +++ lib/open-amp | 1 + 2 files changed, 4 insertions(+) create mode 160000 lib/open-amp diff --git a/.gitmodules b/.gitmodules index 75bffdadddeb8..39cf208412404 100644 --- a/.gitmodules +++ b/.gitmodules @@ -59,3 +59,6 @@ [submodule "lib/protobuf-c"] path = lib/protobuf-c url = https://github.com/protobuf-c/protobuf-c.git +[submodule "lib/open-amp"] + path = lib/open-amp + url = https://github.com/OpenAMP/open-amp.git diff --git a/lib/open-amp b/lib/open-amp new file mode 160000 index 0000000000000..1904dee18da85 --- /dev/null +++ b/lib/open-amp @@ -0,0 +1 @@ +Subproject commit 1904dee18da85400e72b8f55c5c99e872a486573 From f6213ffc5cb7ea1cb3c7ec10ccd9032035c12159 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Tue, 20 Feb 2024 12:28:36 +0100 Subject: [PATCH 02/16] lib/libmetal: Add libmetal submodule. libmetal provides an abstraction of the underlying hardware, to support other OpenAMP components. Signed-off-by: iabdalkader --- .gitmodules | 3 +++ lib/libmetal | 1 + 2 files changed, 4 insertions(+) create mode 160000 lib/libmetal diff --git a/.gitmodules b/.gitmodules index 39cf208412404..aa9bb3d6d94cd 100644 --- a/.gitmodules +++ b/.gitmodules @@ -62,3 +62,6 @@ [submodule "lib/open-amp"] path = lib/open-amp url = https://github.com/OpenAMP/open-amp.git +[submodule "lib/libmetal"] + path = lib/libmetal + url = https://github.com/OpenAMP/libmetal.git diff --git a/lib/libmetal b/lib/libmetal new file mode 160000 index 0000000000000..0cb7d293a7f25 --- /dev/null +++ b/lib/libmetal @@ -0,0 +1 @@ +Subproject commit 0cb7d293a7f25394a06847a28d0f0ace9862936e From 61ee59ad8917d64f4f4a0e7a261fd838c5724014 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Tue, 20 Feb 2024 12:41:04 +0100 Subject: [PATCH 03/16] extmod/libmetal: Add MicroPython platform for libmetal. Add a MicroPython platform for libmetal, based on the generic platform. The MicroPython platform uses common mp_hal_xxx functions and allows ports to customize default configurations for libmetal. Signed-off-by: Jim Mussared Signed-off-by: iabdalkader --- extmod/libmetal/libmetal.mk | 51 ++++++++++++ extmod/libmetal/metal/config.h | 81 +++++++++++++++++++ .../libmetal/metal/system/micropython/alloc.h | 1 + .../libmetal/metal/system/micropython/log.h | 1 + .../libmetal/metal/system/micropython/sleep.h | 1 + .../libmetal/metal/system/micropython/sys.h | 5 ++ 6 files changed, 140 insertions(+) create mode 100644 extmod/libmetal/libmetal.mk create mode 100644 extmod/libmetal/metal/config.h create mode 100644 extmod/libmetal/metal/system/micropython/alloc.h create mode 100644 extmod/libmetal/metal/system/micropython/log.h create mode 100644 extmod/libmetal/metal/system/micropython/sleep.h create mode 100644 extmod/libmetal/metal/system/micropython/sys.h diff --git a/extmod/libmetal/libmetal.mk b/extmod/libmetal/libmetal.mk new file mode 100644 index 0000000000000..852b04289833b --- /dev/null +++ b/extmod/libmetal/libmetal.mk @@ -0,0 +1,51 @@ +# Makefile directives for libmetal + +# libmetal is intended to run through a pre-processor (as part of its CMake-based build system). +# This replicates the basic functionality of the pre-processor, including adding a "micropython" +# platform that is almost identical to the built-in "generic" platform with a few small changes +# provided by the files in extmod/libmetal. +$(BUILD)/openamp: $(BUILD) + $(MKDIR) -p $@ + +$(BUILD)/openamp/metal: $(BUILD)/openamp + $(MKDIR) -p $@ + +$(BUILD)/openamp/metal/config.h: $(BUILD)/openamp/metal $(TOP)/$(LIBMETAL_DIR)/lib/config.h + @$(ECHO) "GEN $@" + @for file in $(TOP)/$(LIBMETAL_DIR)/lib/*.c $(TOP)/$(LIBMETAL_DIR)/lib/*.h; do $(SED) -e "s/@PROJECT_SYSTEM@/micropython/g" -e "s/@PROJECT_PROCESSOR@/arm/g" $$file > $(BUILD)/openamp/metal/$$(basename $$file); done + $(MKDIR) -p $(BUILD)/openamp/metal/processor/arm + @$(CP) $(TOP)/$(LIBMETAL_DIR)/lib/processor/arm/*.h $(BUILD)/openamp/metal/processor/arm + $(MKDIR) -p $(BUILD)/openamp/metal/compiler/gcc + @$(CP) $(TOP)/$(LIBMETAL_DIR)/lib/compiler/gcc/*.h $(BUILD)/openamp/metal/compiler/gcc + $(MKDIR) -p $(BUILD)/openamp/metal/system/micropython + @$(CP) $(TOP)/$(LIBMETAL_DIR)/lib/system/generic/*.c $(TOP)/$(LIBMETAL_DIR)/lib/system/generic/*.h $(BUILD)/openamp/metal/system/micropython + @$(CP) $(TOP)/extmod/libmetal/metal/system/micropython/* $(BUILD)/openamp/metal/system/micropython + @$(CP) $(TOP)/extmod/libmetal/metal/config.h $(BUILD)/openamp/metal/config.h + +# libmetal's source files. +SRC_LIBMETAL_C := $(addprefix $(BUILD)/openamp/metal/,\ + device.c \ + dma.c \ + init.c \ + io.c \ + irq.c \ + log.c \ + shmem.c \ + softirq.c \ + version.c \ + device.c \ + system/micropython/condition.c \ + system/micropython/device.c \ + system/micropython/io.c \ + system/micropython/irq.c \ + system/micropython/shmem.c \ + system/micropython/time.c \ + ) + +# These files are generated by the rule above (along with config.h), so we need to make the .c +# files depend on that step -- can't use the .o files as the target because the .c files don't +# exist yet. +$(SRC_LIBMETAL_C): $(BUILD)/openamp/metal/config.h + +# Qstr processing will include the generated libmetal headers, so add them as a qstr requirement. +QSTR_GLOBAL_REQUIREMENTS += $(BUILD)/openamp/metal/config.h diff --git a/extmod/libmetal/metal/config.h b/extmod/libmetal/metal/config.h new file mode 100644 index 0000000000000..459a2f4ca424d --- /dev/null +++ b/extmod/libmetal/metal/config.h @@ -0,0 +1,81 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 Arduino SA + * + * 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. + * + * MicroPython libmetal config. + */ +#ifndef MICROPY_INCLUDED_METAL_MICROPYTHON_H +#define MICROPY_INCLUDED_METAL_MICROPYTHON_H + +#include + +// Port-specific config. +#include "mpmetalport.h" + +// Library major version number. +#define METAL_VER_MAJOR 1 + +// Library minor version number. +#define METAL_VER_MINOR 5 + +// Library patch level. +#define METAL_VER_PATCH 0 + +// Library version string. +#define METAL_VER "1.5.0" + +#if METAL_HAVE_STDATOMIC_H +#define HAVE_STDATOMIC_H +#endif + +#if METAL_HAVE_FUTEX_H +#define HAVE_FUTEX_H +#endif + +#ifndef METAL_MAX_DEVICE_REGIONS +#define METAL_MAX_DEVICE_REGIONS 1 +#endif + +// generic/log.h +#if METAL_LOG_HANDLER_ENABLE +#include "py/mphal.h" +#undef metal_log +#define metal_log(level, ...) mp_printf(&mp_plat_print, __VA_ARGS__) +#endif + +static inline void *__metal_allocate_memory(unsigned int size) { + return m_tracked_calloc(1, size); +} + +static inline void __metal_free_memory(void *ptr) { + m_tracked_free(ptr); +} + +// The following functions must be provided by the port (in mpmetalport.h / mpmetalport.c). +int __metal_sleep_usec(unsigned int usec); +void sys_irq_enable(unsigned int vector); +void sys_irq_disable(unsigned int vector); +void sys_irq_restore_enable(unsigned int flags); +unsigned int sys_irq_save_disable(void); +#endif // MICROPY_INCLUDED_METAL_MICROPYTHON_H diff --git a/extmod/libmetal/metal/system/micropython/alloc.h b/extmod/libmetal/metal/system/micropython/alloc.h new file mode 100644 index 0000000000000..dd0fdb13f9252 --- /dev/null +++ b/extmod/libmetal/metal/system/micropython/alloc.h @@ -0,0 +1 @@ +#include diff --git a/extmod/libmetal/metal/system/micropython/log.h b/extmod/libmetal/metal/system/micropython/log.h new file mode 100644 index 0000000000000..dd0fdb13f9252 --- /dev/null +++ b/extmod/libmetal/metal/system/micropython/log.h @@ -0,0 +1 @@ +#include diff --git a/extmod/libmetal/metal/system/micropython/sleep.h b/extmod/libmetal/metal/system/micropython/sleep.h new file mode 100644 index 0000000000000..dd0fdb13f9252 --- /dev/null +++ b/extmod/libmetal/metal/system/micropython/sleep.h @@ -0,0 +1 @@ +#include diff --git a/extmod/libmetal/metal/system/micropython/sys.h b/extmod/libmetal/metal/system/micropython/sys.h new file mode 100644 index 0000000000000..0f404dcd61284 --- /dev/null +++ b/extmod/libmetal/metal/system/micropython/sys.h @@ -0,0 +1,5 @@ +#include + +struct metal_state { + struct metal_common_state common; +}; From 85028aadab6763a0d899e71f54643840d9f8546d Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Mon, 20 Nov 2023 14:24:04 +0100 Subject: [PATCH 04/16] py/stream: Add mp_stream_seek() helper function. Signed-off-by: iabdalkader --- py/stream.c | 12 ++++++++++++ py/stream.h | 1 + 2 files changed, 13 insertions(+) diff --git a/py/stream.c b/py/stream.c index aa905ca8cc904..e008fb999ea32 100644 --- a/py/stream.c +++ b/py/stream.c @@ -82,6 +82,18 @@ mp_uint_t mp_stream_rw(mp_obj_t stream, void *buf_, mp_uint_t size, int *errcode return done; } +mp_uint_t mp_stream_seek(mp_obj_t stream, mp_off_t offset, int whence, int *errcode) { + struct mp_stream_seek_t seek_s; + seek_s.offset = offset; + seek_s.whence = whence; + const mp_stream_p_t *stream_p = mp_get_stream(stream); + mp_uint_t res = stream_p->ioctl(MP_OBJ_FROM_PTR(stream), MP_STREAM_SEEK, (mp_uint_t)(uintptr_t)&seek_s, errcode); + if (res == MP_STREAM_ERROR) { + return -1; + } + return seek_s.offset; +} + const mp_stream_p_t *mp_get_stream_raise(mp_obj_t self_in, int flags) { const mp_obj_type_t *type = mp_obj_get_type(self_in); if (MP_OBJ_TYPE_HAS_SLOT(type, protocol)) { diff --git a/py/stream.h b/py/stream.h index e6e6f283df177..7c2e87a87c47a 100644 --- a/py/stream.h +++ b/py/stream.h @@ -115,6 +115,7 @@ mp_obj_t mp_stream_write(mp_obj_t self_in, const void *buf, size_t len, byte fla mp_uint_t mp_stream_rw(mp_obj_t stream, void *buf, mp_uint_t size, int *errcode, byte flags); #define mp_stream_write_exactly(stream, buf, size, err) mp_stream_rw(stream, (byte *)buf, size, err, MP_STREAM_RW_WRITE) #define mp_stream_read_exactly(stream, buf, size, err) mp_stream_rw(stream, buf, size, err, MP_STREAM_RW_READ) +mp_uint_t mp_stream_seek(mp_obj_t stream, mp_off_t offset, int whence, int *errcode); void mp_stream_write_adaptor(void *self, const char *buf, size_t len); From b72602250929234dbdbba084ca0dc4fc94b056ae Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 29 Feb 2024 15:21:56 +1100 Subject: [PATCH 05/16] py/stream: Factor stream implementations. So there's only one location that does the ioctl(MP_STREAM_SEEK) call. Signed-off-by: Damien George --- py/stream.c | 31 ++++++++++++------------------- py/stream.h | 2 +- 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/py/stream.c b/py/stream.c index e008fb999ea32..d7a8881e1a32d 100644 --- a/py/stream.c +++ b/py/stream.c @@ -82,14 +82,14 @@ mp_uint_t mp_stream_rw(mp_obj_t stream, void *buf_, mp_uint_t size, int *errcode return done; } -mp_uint_t mp_stream_seek(mp_obj_t stream, mp_off_t offset, int whence, int *errcode) { +mp_off_t mp_stream_seek(mp_obj_t stream, mp_off_t offset, int whence, int *errcode) { struct mp_stream_seek_t seek_s; seek_s.offset = offset; seek_s.whence = whence; const mp_stream_p_t *stream_p = mp_get_stream(stream); mp_uint_t res = stream_p->ioctl(MP_OBJ_FROM_PTR(stream), MP_STREAM_SEEK, (mp_uint_t)(uintptr_t)&seek_s, errcode); if (res == MP_STREAM_ERROR) { - return -1; + return (mp_off_t)-1; } return seek_s.offset; } @@ -457,28 +457,26 @@ static mp_obj_t mp_stream___exit__(size_t n_args, const mp_obj_t *args) { MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream___exit___obj, 4, 4, mp_stream___exit__); static mp_obj_t stream_seek(size_t n_args, const mp_obj_t *args) { - struct mp_stream_seek_t seek_s; // TODO: Could be uint64 - seek_s.offset = mp_obj_get_int(args[1]); - seek_s.whence = SEEK_SET; + mp_off_t offset = mp_obj_get_int(args[1]); + int whence = SEEK_SET; if (n_args == 3) { - seek_s.whence = mp_obj_get_int(args[2]); + whence = mp_obj_get_int(args[2]); } // In POSIX, it's error to seek before end of stream, we enforce it here. - if (seek_s.whence == SEEK_SET && seek_s.offset < 0) { + if (whence == SEEK_SET && offset < 0) { mp_raise_OSError(MP_EINVAL); } - const mp_stream_p_t *stream_p = mp_get_stream(args[0]); int error; - mp_uint_t res = stream_p->ioctl(args[0], MP_STREAM_SEEK, (mp_uint_t)(uintptr_t)&seek_s, &error); - if (res == MP_STREAM_ERROR) { + mp_off_t res = mp_stream_seek(args[0], offset, whence, &error); + if (res == (mp_off_t)-1) { mp_raise_OSError(error); } // TODO: Could be uint64 - return mp_obj_new_int_from_uint(seek_s.offset); + return mp_obj_new_int_from_uint(res); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_seek_obj, 2, 3, stream_seek); @@ -557,16 +555,11 @@ ssize_t mp_stream_posix_read(void *stream, void *buf, size_t len) { } off_t mp_stream_posix_lseek(void *stream, off_t offset, int whence) { - const mp_obj_base_t *o = stream; - const mp_stream_p_t *stream_p = MP_OBJ_TYPE_GET_SLOT(o->type, protocol); - struct mp_stream_seek_t seek_s; - seek_s.offset = offset; - seek_s.whence = whence; - mp_uint_t res = stream_p->ioctl(MP_OBJ_FROM_PTR(stream), MP_STREAM_SEEK, (mp_uint_t)(uintptr_t)&seek_s, &errno); - if (res == MP_STREAM_ERROR) { + mp_off_t res = mp_stream_seek(MP_OBJ_FROM_PTR(stream), offset, whence, &errno); + if (res == (mp_off_t)-1) { return -1; } - return seek_s.offset; + return res; } int mp_stream_posix_fsync(void *stream) { diff --git a/py/stream.h b/py/stream.h index 7c2e87a87c47a..7c4d38afa9b67 100644 --- a/py/stream.h +++ b/py/stream.h @@ -115,7 +115,7 @@ mp_obj_t mp_stream_write(mp_obj_t self_in, const void *buf, size_t len, byte fla mp_uint_t mp_stream_rw(mp_obj_t stream, void *buf, mp_uint_t size, int *errcode, byte flags); #define mp_stream_write_exactly(stream, buf, size, err) mp_stream_rw(stream, (byte *)buf, size, err, MP_STREAM_RW_WRITE) #define mp_stream_read_exactly(stream, buf, size, err) mp_stream_rw(stream, buf, size, err, MP_STREAM_RW_READ) -mp_uint_t mp_stream_seek(mp_obj_t stream, mp_off_t offset, int whence, int *errcode); +mp_off_t mp_stream_seek(mp_obj_t stream, mp_off_t offset, int whence, int *errcode); void mp_stream_write_adaptor(void *self, const char *buf, size_t len); From 162054be85a58466edea2297abba9aadc6c3d597 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Mon, 11 Dec 2023 13:19:07 +0100 Subject: [PATCH 06/16] stm32/mpu: Add MPU config for shared, uncached memory region. Signed-off-by: iabdalkader --- ports/stm32/mpu.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/ports/stm32/mpu.h b/ports/stm32/mpu.h index 64880a85db75f..5ef1466184aac 100644 --- a/ports/stm32/mpu.h +++ b/ports/stm32/mpu.h @@ -36,6 +36,7 @@ #define MPU_REGION_QSPI3 (MPU_REGION_NUMBER3) #define MPU_REGION_SDRAM1 (MPU_REGION_NUMBER4) #define MPU_REGION_SDRAM2 (MPU_REGION_NUMBER5) +#define MPU_REGION_OPENAMP (MPU_REGION_NUMBER15) // Only relevant on CPUs with D-Cache, must be higher priority than SDRAM #define MPU_REGION_DMA_UNCACHED_1 (MPU_REGION_NUMBER6) @@ -94,6 +95,18 @@ | MPU_REGION_ENABLE << MPU_RASR_ENABLE_Pos \ ) +#define MPU_CONFIG_SHARED_UNCACHED(size) ( \ + MPU_INSTRUCTION_ACCESS_DISABLE << MPU_RASR_XN_Pos \ + | MPU_REGION_FULL_ACCESS << MPU_RASR_AP_Pos \ + | MPU_TEX_LEVEL1 << MPU_RASR_TEX_Pos \ + | MPU_ACCESS_SHAREABLE << MPU_RASR_S_Pos \ + | MPU_ACCESS_NOT_CACHEABLE << MPU_RASR_C_Pos \ + | MPU_ACCESS_NOT_BUFFERABLE << MPU_RASR_B_Pos \ + | 0x00 << MPU_RASR_SRD_Pos \ + | (size) << MPU_RASR_SIZE_Pos \ + | MPU_REGION_ENABLE << MPU_RASR_ENABLE_Pos \ + ) + static inline void mpu_init(void) { MPU->CTRL = MPU_PRIVILEGED_DEFAULT | MPU_CTRL_ENABLE_Msk; SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk; From 14dae42fe7a4c8c147d528e998e10a6a80d28ae5 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Tue, 20 Feb 2024 14:07:13 +0100 Subject: [PATCH 07/16] extmod/modopenamp: Add new OpenAMP module. This module implements OpenAMP's basic initialization and shared resources support, and provides support for OpenAMP's RPMsg component, by providing an `endpoint` type (a logical connection on top of RPMsg channel) which can be used to communicate with the remote core. Signed-off-by: iabdalkader --- extmod/extmod.mk | 42 +++++ extmod/modopenamp.c | 395 ++++++++++++++++++++++++++++++++++++++++++++ extmod/modopenamp.h | 81 +++++++++ 3 files changed, 518 insertions(+) create mode 100644 extmod/modopenamp.c create mode 100644 extmod/modopenamp.h diff --git a/extmod/extmod.mk b/extmod/extmod.mk index 4f5f7d53ac4b8..54689ef65e9d4 100644 --- a/extmod/extmod.mk +++ b/extmod/extmod.mk @@ -31,6 +31,7 @@ SRC_EXTMOD_C += \ extmod/modmachine.c \ extmod/modnetwork.c \ extmod/modonewire.c \ + extmod/modopenamp.c \ extmod/modos.c \ extmod/modplatform.c\ extmod/modrandom.c \ @@ -515,3 +516,44 @@ include $(TOP)/extmod/btstack/btstack.mk endif endif + +################################################################################ +# openamp + +ifeq ($(MICROPY_PY_OPENAMP),1) +OPENAMP_DIR = lib/open-amp +LIBMETAL_DIR = lib/libmetal +GIT_SUBMODULES += $(LIBMETAL_DIR) $(OPENAMP_DIR) +include $(TOP)/extmod/libmetal/libmetal.mk + +INC += -I$(TOP)/$(OPENAMP_DIR) +CFLAGS += -DMICROPY_PY_OPENAMP=1 + +CFLAGS_THIRDPARTY += \ + -I$(BUILD)/openamp \ + -I$(TOP)/$(OPENAMP_DIR) \ + -I$(TOP)/$(OPENAMP_DIR)/lib/include/ \ + -DMETAL_INTERNAL \ + -DVIRTIO_DRIVER_ONLY \ + -DNO_ATOMIC_64_SUPPORT \ + -DRPMSG_BUFFER_SIZE=512 \ + +# OpenAMP's source files. +SRC_OPENAMP_C += $(addprefix $(OPENAMP_DIR)/lib/,\ + rpmsg/rpmsg.c \ + rpmsg/rpmsg_virtio.c \ + virtio/virtio.c \ + virtio/virtqueue.c \ + virtio_mmio/virtio_mmio_drv.c \ + ) + +# Disable compiler warnings in OpenAMP (variables used only for assert). +$(BUILD)/$(OPENAMP_DIR)/lib/rpmsg/rpmsg_virtio.o: CFLAGS += -Wno-unused-but-set-variable +$(BUILD)/$(OPENAMP_DIR)/lib/virtio_mmio/virtio_mmio_drv.o: CFLAGS += -Wno-unused-but-set-variable + +# We need to have generated libmetal before compiling OpenAMP. +$(addprefix $(BUILD)/, $(SRC_OPENAMP_C:.c=.o)): $(BUILD)/openamp/metal/config.h + +SRC_THIRDPARTY_C += $(SRC_LIBMETAL_C) $(SRC_OPENAMP_C) + +endif # MICROPY_PY_OPENAMP diff --git a/extmod/modopenamp.c b/extmod/modopenamp.c new file mode 100644 index 0000000000000..b0c834c2819bf --- /dev/null +++ b/extmod/modopenamp.c @@ -0,0 +1,395 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023-2024 Arduino SA + * + * 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. + * + * OpenAMP's Python module. + */ + +#if MICROPY_PY_OPENAMP + +#include "py/obj.h" +#include "py/nlr.h" +#include "py/runtime.h" + +#include "metal/sys.h" +#include "metal/alloc.h" +#include "metal/errno.h" +#include "metal/io.h" +#include "metal/device.h" +#include "metal/utilities.h" + +#include "openamp/open_amp.h" +#include "openamp/remoteproc.h" +#include "openamp/remoteproc_loader.h" +#include "modopenamp.h" + +#if !MICROPY_ENABLE_FINALISER +#error "MICROPY_PY_OPENAMP requires MICROPY_ENABLE_FINALISER" +#endif + +#if MICROPY_PY_OPENAMP_RSC_TABLE_ENABLE +#define VIRTIO_DEV_ID 0xFF +#define VIRTIO_DEV_FEATURES (1 << VIRTIO_RPMSG_F_NS) + +#define VRING0_ID 0 // VRING0 ID (host to remote) fixed to 0 for linux compatibility +#define VRING1_ID 1 // VRING1 ID (remote to host) fixed to 1 for linux compatibility +#define VRING_NOTIFY_ID VRING0_ID + +#define VRING_COUNT 2 +#define VRING_ALIGNMENT 32 +// Note the number of buffers must be a power of 2 +#define VRING_NUM_BUFFS 64 + +// The following config should be enough for about 128 descriptors. +// See lib/include/openamp/virtio_ring.h for the layout of vrings +// and vring_size() to calculate the vring size. +#define VRING_RX_ADDR (METAL_SHM_ADDR) +#define VRING_TX_ADDR (METAL_SHM_ADDR + 0x1000) +#define VRING_BUFF_ADDR (METAL_SHM_ADDR + 0x2000) +#define VRING_BUFF_SIZE (METAL_SHM_SIZE - 0x2000) + +static const char openamp_trace_buf[128]; +#define MICROPY_PY_OPENAMP_TRACE_BUF ((uint32_t)openamp_trace_buf) +#define MICROPY_PY_OPENAMP_TRACE_BUF_LEN sizeof(MICROPY_PY_OPENAMP_TRACE_BUF) + +#endif // MICROPY_PY_OPENAMP_RSC_TABLE_ENABLE + +#define debug_printf(...) // mp_printf(&mp_plat_print, __VA_ARGS__) + +static struct metal_device shm_device = { + .name = METAL_SHM_NAME, + // The number of IO regions is fixed and must match the number and + // layout of the remote processor's IO regions. The first region is + // used for the vring shared memory, and the second one is used for + // the shared resource table. + .num_regions = METAL_MAX_DEVICE_REGIONS, + .regions = { { 0 } }, + .node = { NULL }, + .irq_num = 0, + .irq_info = NULL +}; +static metal_phys_addr_t shm_physmap[] = { 0 }; + +// ###################### Virtio device class ###################### +typedef struct _virtio_dev_obj_t { + mp_obj_base_t base; + struct rpmsg_virtio_device rvdev; + struct rpmsg_virtio_shm_pool shm_pool; + mp_obj_t ns_callback; +} virtio_dev_obj_t; + +static mp_obj_t virtio_dev_deinit(mp_obj_t self_in) { + virtio_dev_obj_t *virtio_device = MP_OBJ_TO_PTR(self_in); + rpmsg_deinit_vdev(&virtio_device->rvdev); + metal_finish(); + MP_STATE_PORT(virtio_device) = NULL; + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(virtio_dev_deinit_obj, virtio_dev_deinit); + +static const mp_rom_map_elem_t virtio_dev_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_VirtIODev) }, + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&virtio_dev_deinit_obj) }, +}; +static MP_DEFINE_CONST_DICT(virtio_dev_locals_dict, virtio_dev_locals_dict_table); + +static MP_DEFINE_CONST_OBJ_TYPE( + virtio_dev_type, + MP_QSTR_VirtIODev, + MP_TYPE_FLAG_NONE, + locals_dict, &virtio_dev_locals_dict + ); + +// ###################### RPMsg Endpoint class ###################### +typedef struct _endpoint_obj_t { + mp_obj_base_t base; + mp_obj_t name; + mp_obj_t callback; + struct rpmsg_endpoint ep; +} endpoint_obj_t; + +static const mp_obj_type_t endpoint_type; + +static int endpoint_recv_callback(struct rpmsg_endpoint *ept, void *data, size_t len, uint32_t src, void *priv) { + debug_printf("endpoint_recv_callback() message received src: %lu msg len: %d\n", src, len); + endpoint_obj_t *self = metal_container_of(ept, endpoint_obj_t, ep); + if (self->callback != mp_const_none) { + mp_call_function_2(self->callback, mp_obj_new_int(src), mp_obj_new_bytearray_by_ref(len, data)); + } + return 0; +} + +static mp_obj_t endpoint_send(uint n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_src, ARG_dest, ARG_timeout }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_src, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = -1 } }, + { MP_QSTR_dest, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = -1 } }, + { MP_QSTR_timeout, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = -1 } }, + }; + + // Parse args. + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 2, pos_args + 2, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + endpoint_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + if (is_rpmsg_ept_ready(&self->ep) == false) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Endpoint not ready")); + } + + uint32_t src = self->ep.addr; + if (args[ARG_src].u_int != -1) { + src = args[ARG_src].u_int; + } + + uint32_t dest = self->ep.dest_addr; + if (args[ARG_dest].u_int != -1) { + dest = args[ARG_dest].u_int; + } + + mp_buffer_info_t rbuf; + mp_get_buffer_raise(pos_args[1], &rbuf, MP_BUFFER_READ); + debug_printf("endpoint_send() msg len: %d\n", rbuf.len); + + int bytes = 0; + mp_int_t timeout = args[ARG_timeout].u_int; + for (mp_uint_t start = mp_hal_ticks_ms(); ;) { + bytes = rpmsg_send_offchannel_raw(&self->ep, src, dest, rbuf.buf, rbuf.len, false); + if (bytes > 0 || timeout == 0) { + MICROPY_EVENT_POLL_HOOK + break; + } + if (timeout > 0 && (mp_hal_ticks_ms() - start > timeout)) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("timeout waiting for a free buffer")); + } + MICROPY_EVENT_POLL_HOOK + } + return mp_obj_new_int(bytes); +} +static MP_DEFINE_CONST_FUN_OBJ_KW(endpoint_send_obj, 2, endpoint_send); + +static mp_obj_t endpoint_is_ready(mp_obj_t self_in) { + endpoint_obj_t *self = MP_OBJ_TO_PTR(self_in); + return is_rpmsg_ept_ready(&self->ep) ? mp_const_true : mp_const_false; +} +static MP_DEFINE_CONST_FUN_OBJ_1(endpoint_is_ready_obj, endpoint_is_ready); + +static mp_obj_t endpoint_deinit(mp_obj_t self_in) { + endpoint_obj_t *self = MP_OBJ_TO_PTR(self_in); + rpmsg_destroy_ept(&self->ep); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(endpoint_deinit_obj, endpoint_deinit); + +static mp_obj_t endpoint_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_name, ARG_callback, ARG_src, ARG_dest }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_name, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_callback, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_NONE } }, + { MP_QSTR_src, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = RPMSG_ADDR_ANY } }, + { MP_QSTR_dest, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = RPMSG_ADDR_ANY } }, + }; + + // Parse args. + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + endpoint_obj_t *self = mp_obj_malloc_with_finaliser(endpoint_obj_t, &endpoint_type); + self->name = args[ARG_name].u_obj; + self->callback = args[ARG_callback].u_obj; + + if (MP_STATE_PORT(virtio_device) == NULL) { + openamp_init(); + } + + if (rpmsg_create_ept(&self->ep, &MP_STATE_PORT(virtio_device)->rvdev.rdev, mp_obj_str_get_str(self->name), + args[ARG_src].u_int, args[ARG_dest].u_int, endpoint_recv_callback, NULL) != 0) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to create RPMsg endpoint")); + } + + return MP_OBJ_FROM_PTR(self); +} + +static const mp_rom_map_elem_t endpoint_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_Endpoint) }, + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&endpoint_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&endpoint_send_obj) }, + { MP_ROM_QSTR(MP_QSTR_is_ready), MP_ROM_PTR(&endpoint_is_ready_obj) }, +}; +static MP_DEFINE_CONST_DICT(endpoint_locals_dict, endpoint_locals_dict_table); + +static MP_DEFINE_CONST_OBJ_TYPE( + endpoint_type, + MP_QSTR_Endpoint, + MP_TYPE_FLAG_NONE, + make_new, endpoint_make_new, + locals_dict, &endpoint_locals_dict + ); + +// ###################### openamp module ###################### +void openamp_remoteproc_notified(mp_sched_node_t *node) { + (void)node; + rproc_virtio_notified(MP_STATE_PORT(virtio_device)->rvdev.vdev, VRING_NOTIFY_ID); +} + +static void openamp_ns_callback(struct rpmsg_device *rdev, const char *name, uint32_t dest) { + debug_printf("rpmsg_new_service_callback() new service request name: %s dest %lu\n", name, dest); + // The remote processor advertises its presence to the host by sending + // the Name Service (NS) announcement containing the name of the channel. + virtio_dev_obj_t *virtio_device = metal_container_of(rdev, virtio_dev_obj_t, rvdev); + if (virtio_device->ns_callback != mp_const_none) { + mp_call_function_2(virtio_device->ns_callback, mp_obj_new_int(dest), mp_obj_new_str(name, strlen(name))); + } +} + +#if MICROPY_PY_OPENAMP_RSC_TABLE_ENABLE +// The shared resource table must be initialized manually by the host here, +// because it's not located in the data region, so the startup code doesn't +// know about it. +static void openamp_rsc_table_init(openamp_rsc_table_t **rsc_table_out) { + openamp_rsc_table_t *rsc_table = METAL_RSC_ADDR; + memset(rsc_table, 0, METAL_RSC_SIZE); + + rsc_table->version = 1; + rsc_table->num = MP_ARRAY_SIZE(rsc_table->offset); + rsc_table->offset[0] = offsetof(openamp_rsc_table_t, vdev); + #if MICROPY_PY_OPENAMP_TRACE_BUF_ENABLE + rsc_table->offset[1] = offsetof(openamp_rsc_table_t, trace); + #endif + rsc_table->vdev = (struct fw_rsc_vdev) { + RSC_VDEV, VIRTIO_ID_RPMSG, 0, VIRTIO_DEV_FEATURES, 0, 0, 0, VRING_COUNT, {0, 0} + }; + rsc_table->vring0 = (struct fw_rsc_vdev_vring) { + VRING_TX_ADDR, VRING_ALIGNMENT, VRING_NUM_BUFFS, VRING0_ID, 0 + }; + rsc_table->vring1 = (struct fw_rsc_vdev_vring) { + VRING_RX_ADDR, VRING_ALIGNMENT, VRING_NUM_BUFFS, VRING1_ID, 0 + }; + #if MICROPY_PY_OPENAMP_TRACE_BUF_ENABLE + rsc_table->trace = (struct fw_rsc_trace) { + RSC_TRACE, MICROPY_PY_OPENAMP_TRACE_BUF, MICROPY_PY_OPENAMP_TRACE_BUF_LEN, 0, "trace_buf" + }; + #endif + #ifdef VIRTIO_USE_DCACHE + // Flush resource table. + metal_cache_flush((uint32_t *)rsc_table, sizeof(openamp_rsc_table_t)); + #endif + *rsc_table_out = rsc_table; +} +#endif // MICROPY_PY_OPENAMP_RSC_TABLE_ENABLE + +static mp_obj_t openamp_new_service_callback(mp_obj_t ns_callback) { + if (MP_STATE_PORT(virtio_device) == NULL) { + openamp_init(); + } + if (ns_callback != mp_const_none && !mp_obj_is_callable(ns_callback)) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid callback")); + } + MP_STATE_PORT(virtio_device)->ns_callback = ns_callback; + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(openamp_new_service_callback_obj, openamp_new_service_callback); + +void openamp_init(void) { + if (MP_STATE_PORT(virtio_device) != NULL) { + // Already initialized. + return; + } + + struct metal_device *device; + struct metal_init_params metal_params = METAL_INIT_DEFAULTS; + + // Initialize libmetal. + metal_init(&metal_params); + + // Initialize the shared resource table. + openamp_rsc_table_t *rsc_table; + openamp_rsc_table_init(&rsc_table); + + if (metal_register_generic_device(&shm_device) != 0) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to register metal device")); + } + + if (metal_device_open("generic", METAL_SHM_NAME, &device) != 0) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to open metal device")); + } + + // Initialize shared memory IO region. + metal_io_init(&device->regions[0], (void *)METAL_SHM_ADDR, (void *)shm_physmap, METAL_SHM_SIZE, -1U, 0, NULL); + struct metal_io_region *shm_io = metal_device_io_region(device, 0); + if (shm_io == NULL) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to initialize device io region")); + } + + // Initialize resource table IO region. + metal_io_init(&device->regions[1], (void *)rsc_table, (void *)rsc_table, sizeof(*rsc_table), -1U, 0, NULL); + struct metal_io_region *rsc_io = metal_device_io_region(device, 1); + if (rsc_io == NULL) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to initialize device io region")); + } + + // Create virtio device. + struct virtio_device *vdev = rproc_virtio_create_vdev(RPMSG_HOST, VIRTIO_DEV_ID, + &rsc_table->vdev, rsc_io, NULL, metal_rproc_notify, NULL); + if (vdev == NULL) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to create virtio device")); + } + + // Initialize vrings. + struct fw_rsc_vdev_vring *vring_rsc = &rsc_table->vring0; + for (int i = 0; i < VRING_COUNT; i++, vring_rsc++) { + if (rproc_virtio_init_vring(vdev, vring_rsc->notifyid, vring_rsc->notifyid, + (void *)vring_rsc->da, shm_io, vring_rsc->num, vring_rsc->align) != 0) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to initialize vrings")); + } + } + + virtio_dev_obj_t *virtio_device = mp_obj_malloc_with_finaliser(virtio_dev_obj_t, &virtio_dev_type); + virtio_device->ns_callback = mp_const_none; + + // The remote processor detects that the virtio device is ready by polling + // the status field in the resource table. + rpmsg_virtio_init_shm_pool(&virtio_device->shm_pool, (void *)VRING_BUFF_ADDR, (size_t)VRING_BUFF_SIZE); + rpmsg_init_vdev(&virtio_device->rvdev, vdev, openamp_ns_callback, shm_io, &virtio_device->shm_pool); + + MP_STATE_PORT(virtio_device) = virtio_device; +} + +static const mp_rom_map_elem_t globals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_openamp) }, + { MP_ROM_QSTR(MP_QSTR_ENDPOINT_ADDR_ANY), MP_ROM_INT(RPMSG_ADDR_ANY) }, + { MP_ROM_QSTR(MP_QSTR_new_service_callback), MP_ROM_PTR(&openamp_new_service_callback_obj) }, + { MP_ROM_QSTR(MP_QSTR_Endpoint), MP_ROM_PTR(&endpoint_type) }, +}; +static MP_DEFINE_CONST_DICT(globals_dict, globals_dict_table); + +const mp_obj_module_t openamp_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_t)&globals_dict, +}; + +MP_REGISTER_ROOT_POINTER(struct _virtio_dev_obj_t *virtio_device); +MP_REGISTER_MODULE(MP_QSTR_openamp, openamp_module); + +#endif // MICROPY_PY_OPENAMP diff --git a/extmod/modopenamp.h b/extmod/modopenamp.h new file mode 100644 index 0000000000000..750f68929770f --- /dev/null +++ b/extmod/modopenamp.h @@ -0,0 +1,81 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023 Arduino SA + * + * 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. + * + * OpenAMP's Python module. + */ +#ifndef MICROPY_INCLUDED_MODOPENAMP_H +#define MICROPY_INCLUDED_MODOPENAMP_H + +// Include a port config file if one is defined. +#ifdef MICROPY_PY_OPENAMP_CONFIG_FILE +#include MICROPY_PY_OPENAMP_CONFIG_FILE +#endif + +// Use the default resource table layout and initialization code. +// Note ports and boards can override the default table and provide +// a custom one in openamp_rsc_table_t and openamp_rsc_table_init() +#ifndef MICROPY_PY_OPENAMP_RSC_TABLE_ENABLE +#define MICROPY_PY_OPENAMP_RSC_TABLE_ENABLE (1) +#endif + +// This enables a trace buffer that can be used by remote processor for +// writing trace logs. This is enabled by default to increase the default +// resource table's compatibility with common OpenAMP examples. +#ifndef MICROPY_PY_OPENAMP_TRACE_BUF_ENABLE +#define MICROPY_PY_OPENAMP_TRACE_BUF_ENABLE (1) +#endif + +// The resource table is used for sharing the configuration of the virtio +// device, vrings and other resources, between the host and remote cores. +// The layout and address the table structure must match the one used in +// the remote processor's firmware. +#if MICROPY_PY_OPENAMP_RSC_TABLE_ENABLE +typedef struct openamp_rsc_table { + unsigned int version; + unsigned int num; + unsigned int reserved[2]; + #if MICROPY_PY_OPENAMP_TRACE_BUF_ENABLE + unsigned int offset[2]; + #else + unsigned int offset[1]; + #endif + struct fw_rsc_vdev vdev; + struct fw_rsc_vdev_vring vring0; + struct fw_rsc_vdev_vring vring1; + #if MICROPY_PY_OPENAMP_TRACE_BUF_ENABLE + struct fw_rsc_trace trace; + #endif +} openamp_rsc_table_t; +#endif // MICROPY_PY_OPENAMP_RSC_TABLE_ENABLE + +// Performs low-level initialization of OpenAMP, such as initializing libmetal, +// the shared resource table, shared memory regions, vrings and virtio device. +void openamp_init(void); + +// Ports should run this callback in scheduler context, when +// the remote processor notifies the host of pending messages. +void openamp_remoteproc_notified(mp_sched_node_t *node); + +#endif // MICROPY_INCLUDED_MODOPENAMP_H From 81aba8253ae3be7090cc0fcb4702f38cdd0c50c8 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Tue, 20 Feb 2024 14:09:42 +0100 Subject: [PATCH 08/16] extmod/modopenamp_remoteproc: Add new OpenAMP RemoteProc class. RemoteProc provides an API to load firmware and control remote processors. Note: port-specific operations must be implemented to support this class. Signed-off-by: iabdalkader --- extmod/extmod.mk | 16 +++ extmod/modopenamp.c | 7 ++ extmod/modopenamp.h | 12 ++ extmod/modopenamp_remoteproc.c | 175 +++++++++++++++++++++++++++ extmod/modopenamp_remoteproc.h | 46 +++++++ extmod/modopenamp_remoteproc_store.c | 146 ++++++++++++++++++++++ 6 files changed, 402 insertions(+) create mode 100644 extmod/modopenamp_remoteproc.c create mode 100644 extmod/modopenamp_remoteproc.h create mode 100644 extmod/modopenamp_remoteproc_store.c diff --git a/extmod/extmod.mk b/extmod/extmod.mk index 54689ef65e9d4..bf686fbbb4d95 100644 --- a/extmod/extmod.mk +++ b/extmod/extmod.mk @@ -32,6 +32,8 @@ SRC_EXTMOD_C += \ extmod/modnetwork.c \ extmod/modonewire.c \ extmod/modopenamp.c \ + extmod/modopenamp_remoteproc.c \ + extmod/modopenamp_remoteproc_store.c \ extmod/modos.c \ extmod/modplatform.c\ extmod/modrandom.c \ @@ -529,6 +531,10 @@ include $(TOP)/extmod/libmetal/libmetal.mk INC += -I$(TOP)/$(OPENAMP_DIR) CFLAGS += -DMICROPY_PY_OPENAMP=1 +ifeq ($(MICROPY_PY_OPENAMP_REMOTEPROC),1) +CFLAGS += -DMICROPY_PY_OPENAMP_REMOTEPROC=1 +endif + CFLAGS_THIRDPARTY += \ -I$(BUILD)/openamp \ -I$(TOP)/$(OPENAMP_DIR) \ @@ -547,6 +553,16 @@ SRC_OPENAMP_C += $(addprefix $(OPENAMP_DIR)/lib/,\ virtio_mmio/virtio_mmio_drv.c \ ) +# OpenAMP's remoteproc source files. +ifeq ($(MICROPY_PY_OPENAMP_REMOTEPROC),1) +SRC_OPENAMP_C += $(addprefix $(OPENAMP_DIR)/lib/remoteproc/,\ + elf_loader.c \ + remoteproc.c \ + remoteproc_virtio.c \ + rsc_table_parser.c \ + ) +endif # MICROPY_PY_OPENAMP_REMOTEPROC + # Disable compiler warnings in OpenAMP (variables used only for assert). $(BUILD)/$(OPENAMP_DIR)/lib/rpmsg/rpmsg_virtio.o: CFLAGS += -Wno-unused-but-set-variable $(BUILD)/$(OPENAMP_DIR)/lib/virtio_mmio/virtio_mmio_drv.o: CFLAGS += -Wno-unused-but-set-variable diff --git a/extmod/modopenamp.c b/extmod/modopenamp.c index b0c834c2819bf..eb19c4b737ac1 100644 --- a/extmod/modopenamp.c +++ b/extmod/modopenamp.c @@ -77,6 +77,10 @@ static const char openamp_trace_buf[128]; #define debug_printf(...) // mp_printf(&mp_plat_print, __VA_ARGS__) +#if MICROPY_PY_OPENAMP_REMOTEPROC +extern mp_obj_type_t openamp_remoteproc_type; +#endif + static struct metal_device shm_device = { .name = METAL_SHM_NAME, // The number of IO regions is fixed and must match the number and @@ -381,6 +385,9 @@ static const mp_rom_map_elem_t globals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_ENDPOINT_ADDR_ANY), MP_ROM_INT(RPMSG_ADDR_ANY) }, { MP_ROM_QSTR(MP_QSTR_new_service_callback), MP_ROM_PTR(&openamp_new_service_callback_obj) }, { MP_ROM_QSTR(MP_QSTR_Endpoint), MP_ROM_PTR(&endpoint_type) }, + #if MICROPY_PY_OPENAMP_REMOTEPROC + { MP_ROM_QSTR(MP_QSTR_RemoteProc), MP_ROM_PTR(&openamp_remoteproc_type) }, + #endif }; static MP_DEFINE_CONST_DICT(globals_dict, globals_dict_table); diff --git a/extmod/modopenamp.h b/extmod/modopenamp.h index 750f68929770f..8f677788f90be 100644 --- a/extmod/modopenamp.h +++ b/extmod/modopenamp.h @@ -47,6 +47,18 @@ #define MICROPY_PY_OPENAMP_TRACE_BUF_ENABLE (1) #endif +// For ports that don't define a custom image store, this enables a generic +// VFS-based image store that supports loading elf files from storage. +#ifndef MICROPY_PY_OPENAMP_REMOTEPROC_STORE_ENABLE +#define MICROPY_PY_OPENAMP_REMOTEPROC_STORE_ENABLE (1) +#endif + +// Enable or disable support for loading elf files. This option saves +// around 7KBs when disabled. +#ifndef MICROPY_PY_OPENAMP_REMOTEPROC_ELFLD_ENABLE +#define MICROPY_PY_OPENAMP_REMOTEPROC_ELFLD_ENABLE (1) +#endif + // The resource table is used for sharing the configuration of the virtio // device, vrings and other resources, between the host and remote cores. // The layout and address the table structure must match the one used in diff --git a/extmod/modopenamp_remoteproc.c b/extmod/modopenamp_remoteproc.c new file mode 100644 index 0000000000000..6f43c71546bf2 --- /dev/null +++ b/extmod/modopenamp_remoteproc.c @@ -0,0 +1,175 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023-2024 Arduino SA + * + * 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. + * + * OpenAMP's remoteproc class. + */ + +#if MICROPY_PY_OPENAMP_REMOTEPROC + +#include "py/obj.h" +#include "py/nlr.h" +#include "py/runtime.h" +#include "py/stream.h" +#include "extmod/vfs.h" + +#include "metal/sys.h" +#include "metal/alloc.h" +#include "metal/errno.h" +#include "metal/io.h" + +#include "openamp/open_amp.h" +#include "openamp/remoteproc.h" +#include "openamp/remoteproc_loader.h" + +#include "modopenamp.h" +#include "modopenamp_remoteproc.h" + +#define DEBUG_printf(...) // mp_printf(&mp_plat_print, __VA_ARGS__) + +#if !MICROPY_PY_OPENAMP +#error "MICROPY_PY_OPENAMP_REMOTEPROC requires MICROPY_PY_OPENAMP" +#endif + +typedef struct openamp_remoteproc_obj { + mp_obj_base_t base; + struct remoteproc rproc; +} openamp_remoteproc_obj_t; + +const mp_obj_type_t openamp_remoteproc_type; + +// Port-defined image store operations. +extern struct image_store_ops openamp_remoteproc_store_ops; + +// Port-defined remote-proc operations. +const struct remoteproc_ops openamp_remoteproc_ops = { + .init = mp_openamp_remoteproc_init, + .mmap = mp_openamp_remoteproc_mmap, + .start = mp_openamp_remoteproc_start, + .stop = mp_openamp_remoteproc_stop, + .config = mp_openamp_remoteproc_config, + .remove = mp_openamp_remoteproc_remove, + .shutdown = mp_openamp_remoteproc_shutdown, +}; + +static mp_obj_t openamp_remoteproc_start(mp_obj_t self_in) { + openamp_remoteproc_obj_t *self = MP_OBJ_TO_PTR(self_in); + + // Start the processor to run the application. + int error = remoteproc_start(&self->rproc); + if (error != 0) { + self->rproc.state = RPROC_ERROR; + mp_raise_OSError(error); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(openamp_remoteproc_start_obj, openamp_remoteproc_start); + +static mp_obj_t openamp_remoteproc_stop(mp_obj_t self_in) { + openamp_remoteproc_obj_t *self = MP_OBJ_TO_PTR(self_in); + + // Stop the processor, but the processor is not powered down. + int error = remoteproc_stop(&self->rproc); + if (error != 0) { + mp_raise_OSError(error); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(openamp_remoteproc_stop_obj, openamp_remoteproc_stop); + +static mp_obj_t openamp_remoteproc_shutdown(mp_obj_t self_in) { + openamp_remoteproc_obj_t *self = MP_OBJ_TO_PTR(self_in); + + // Shutdown the remoteproc and release its resources. + int error = remoteproc_shutdown(&self->rproc); + if (error != 0) { + mp_raise_OSError(error); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(openamp_remoteproc_shutdown_obj, openamp_remoteproc_shutdown); + +mp_obj_t openamp_remoteproc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_entry }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_entry, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_NONE } }, + }; + + // Parse args. + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + openamp_remoteproc_obj_t *self = mp_obj_malloc_with_finaliser(openamp_remoteproc_obj_t, &openamp_remoteproc_type); + + // Make sure OpenAMP is initialized. + if (MP_STATE_PORT(virtio_device) == NULL) { + openamp_init(); + } + + // Create a remoteproc instance. + // NOTE: ports should use rproc->priv to allocate the image store, + // which gets passed to remoteproc_load(), and all of the store ops. + remoteproc_init(&self->rproc, &openamp_remoteproc_ops, NULL); + + // Configure the remote before loading applications (optional). + int error = remoteproc_config(&self->rproc, NULL); + if (error != 0) { + mp_raise_OSError(error); + } + + if (mp_obj_is_int(args[ARG_entry].u_obj)) { + self->rproc.bootaddr = mp_obj_get_int(args[ARG_entry].u_obj); + } else { + #if MICROPY_PY_OPENAMP_REMOTEPROC_ELFLD_ENABLE + // Load firmware. + const char *path = mp_obj_str_get_str(args[ARG_entry].u_obj); + int error = remoteproc_load(&self->rproc, path, self->rproc.priv, &openamp_remoteproc_store_ops, NULL); + if (error != 0) { + mp_raise_OSError(error); + } + #else + mp_raise_TypeError(MP_ERROR_TEXT("loading firmware is not supported.")); + #endif + } + return MP_OBJ_FROM_PTR(self); +} + +static const mp_rom_map_elem_t openamp_remoteproc_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_RemoteProc) }, + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&openamp_remoteproc_shutdown_obj) }, + { MP_ROM_QSTR(MP_QSTR_start), MP_ROM_PTR(&openamp_remoteproc_start_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&openamp_remoteproc_stop_obj) }, + { MP_ROM_QSTR(MP_QSTR_shutdown), MP_ROM_PTR(&openamp_remoteproc_shutdown_obj) }, +}; +static MP_DEFINE_CONST_DICT(openamp_remoteproc_dict, openamp_remoteproc_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + openamp_remoteproc_type, + MP_QSTR_RemoteProc, + MP_TYPE_FLAG_NONE, + make_new, openamp_remoteproc_make_new, + locals_dict, &openamp_remoteproc_dict + ); + +#endif // MICROPY_PY_OPENAMP_REMOTEPROC diff --git a/extmod/modopenamp_remoteproc.h b/extmod/modopenamp_remoteproc.h new file mode 100644 index 0000000000000..9bc2b07064b4b --- /dev/null +++ b/extmod/modopenamp_remoteproc.h @@ -0,0 +1,46 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023-2024 Arduino SA + * + * 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. + * + * OpenAMP's remoteproc class. + */ +#ifndef MICROPY_INCLUDED_MODOPENAMP_REMOTEPROC_H +#define MICROPY_INCLUDED_MODOPENAMP_REMOTEPROC_H + +#include "openamp/remoteproc.h" +#include "openamp/remoteproc_loader.h" + +void *mp_openamp_remoteproc_store_alloc(void); +struct remoteproc *mp_openamp_remoteproc_init(struct remoteproc *rproc, + const struct remoteproc_ops *ops, void *arg); +void *mp_openamp_remoteproc_mmap(struct remoteproc *rproc, metal_phys_addr_t *pa, + metal_phys_addr_t *da, size_t size, unsigned int attribute, + struct metal_io_region **io); +int mp_openamp_remoteproc_start(struct remoteproc *rproc); +int mp_openamp_remoteproc_stop(struct remoteproc *rproc); +int mp_openamp_remoteproc_config(struct remoteproc *rproc, void *data); +void mp_openamp_remoteproc_remove(struct remoteproc *rproc); +int mp_openamp_remoteproc_shutdown(struct remoteproc *rproc); + +#endif // MICROPY_INCLUDED_MODOPENAMP_REMOTEPROC_H diff --git a/extmod/modopenamp_remoteproc_store.c b/extmod/modopenamp_remoteproc_store.c new file mode 100644 index 0000000000000..857c133469d6e --- /dev/null +++ b/extmod/modopenamp_remoteproc_store.c @@ -0,0 +1,146 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023-2024 Arduino SA + * + * 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. + * + * OpenAMP's remoteproc store. + */ + +#if MICROPY_PY_OPENAMP_REMOTEPROC + +#include "py/obj.h" +#include "py/nlr.h" +#include "py/runtime.h" +#include "py/stream.h" +#include "extmod/vfs.h" + +#include "metal/sys.h" +#include "metal/alloc.h" +#include "metal/errno.h" +#include "metal/io.h" + +#include "openamp/remoteproc.h" +#include "openamp/remoteproc_loader.h" + +#include "modopenamp.h" +#include "modopenamp_remoteproc.h" + +#if MICROPY_PY_OPENAMP_REMOTEPROC_STORE_ENABLE + +#define DEBUG_printf(...) // mp_printf(&mp_plat_print, __VA_ARGS__) + +// Note the initial file buffer size needs to be at least 512 to read +// enough of the elf headers on the first call to store_open(), and on +// subsequent calls to store functions, it gets reallocated if needed. +#define RPROC_FILE_STORE_BUF_SIZE (1024) + +typedef struct openamp_remoteproc_filestore { + size_t len; + uint8_t *buf; + mp_obj_t file; +} openamp_remoteproc_filestore_t; + +void *mp_openamp_remoteproc_store_alloc(void) { + // Allocate an rproc filestore. + openamp_remoteproc_filestore_t *fstore; + fstore = metal_allocate_memory(sizeof(openamp_remoteproc_filestore_t)); + fstore->len = RPROC_FILE_STORE_BUF_SIZE; + fstore->buf = metal_allocate_memory(RPROC_FILE_STORE_BUF_SIZE); + return fstore; +} + +static int openamp_remoteproc_store_open(void *store, const char *path, const void **image_data) { + DEBUG_printf("store_open(): %s\n", path); + mp_obj_t args[2] = { + mp_obj_new_str(path, strlen(path)), + MP_OBJ_NEW_QSTR(MP_QSTR_rb), + }; + + openamp_remoteproc_filestore_t *fstore = store; + fstore->file = mp_vfs_open(MP_ARRAY_SIZE(args), args, (mp_map_t *)&mp_const_empty_map); + + int error = 0; + mp_uint_t bytes = mp_stream_read_exactly(fstore->file, fstore->buf, RPROC_FILE_STORE_BUF_SIZE, &error); + if (error != 0 || bytes != RPROC_FILE_STORE_BUF_SIZE) { + return -EINVAL; + } + *image_data = fstore->buf; + return bytes; +} + +static void openamp_remoteproc_store_close(void *store) { + DEBUG_printf("store_close()\n"); + openamp_remoteproc_filestore_t *fstore = store; + mp_stream_close(fstore->file); + metal_free_memory(fstore->buf); + metal_free_memory(fstore); +} + +static int openamp_remoteproc_store_load(void *store, size_t offset, size_t size, + const void **data, metal_phys_addr_t pa, + struct metal_io_region *io, + char is_blocking) { + + int error = 0; + openamp_remoteproc_filestore_t *fstore = store; + + if (mp_stream_seek(fstore->file, offset, MP_SEEK_SET, &error) == -1) { + return -EINVAL; + } + + if (pa == METAL_BAD_PHYS) { + if (size > fstore->len) { + // Note tracked allocs don't support realloc. + fstore->len = size; + fstore->buf = metal_allocate_memory(size); + DEBUG_printf("store_load() realloc to %lu\n", fstore->len); + } + *data = fstore->buf; + DEBUG_printf("store_load(): pa 0x%lx offset %u size %u \n", (uint32_t)pa, offset, size); + } else { + void *va = metal_io_phys_to_virt(io, pa); + if (va == NULL) { + return -EINVAL; + } + *data = va; + DEBUG_printf("store_load(): pa 0x%lx va 0x%p offset %u size %u \n", (uint32_t)pa, va, offset, size); + } + + mp_uint_t bytes = mp_stream_read_exactly(fstore->file, (void *)*data, size, &error); + if (bytes != size || error != 0) { + return -EINVAL; + } + + return bytes; +} + +const struct image_store_ops openamp_remoteproc_store_ops = { + .open = openamp_remoteproc_store_open, + .close = openamp_remoteproc_store_close, + .load = openamp_remoteproc_store_load, + .features = SUPPORT_SEEK, +}; + +#endif // MICROPY_PY_OPENAMP_REMOTEPROC_STORE_ENABLE + +#endif // MICROPY_PY_OPENAMP_REMOTEPROC From 13297d8c3a3cedbf5fd0d2817077270b284fe401 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Mon, 13 Nov 2023 17:24:41 +0100 Subject: [PATCH 09/16] stm32: Implement port backend for libmetal. Signed-off-by: iabdalkader --- ports/stm32/Makefile | 7 +++ ports/stm32/irq.h | 2 + ports/stm32/mpmetalport.c | 107 ++++++++++++++++++++++++++++++++++++++ ports/stm32/mpmetalport.h | 76 +++++++++++++++++++++++++++ 4 files changed, 192 insertions(+) create mode 100644 ports/stm32/mpmetalport.c create mode 100644 ports/stm32/mpmetalport.h diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index d1694426d3529..75609031966d5 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -422,6 +422,13 @@ endif endif # MICROPY_PY_BLUETOOTH +# Add stm32-specific implementation of libmetal (and optionally OpenAMP's rproc). +# Note: libmetal code is generated via a pre-processor so ensure that runs first. +ifeq ($(MICROPY_PY_OPENAMP),1) +SRC_C += mpmetalport.c +$(BUILD)/mpmetalport.o: $(BUILD)/openamp/metal/config.h +endif + # SRC_O should be placed first to work around this LTO bug with binutils <2.35: # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83967 OBJ += $(addprefix $(BUILD)/, $(SRC_O)) diff --git a/ports/stm32/irq.h b/ports/stm32/irq.h index e3a204ec96a32..58e6d0a804854 100644 --- a/ports/stm32/irq.h +++ b/ports/stm32/irq.h @@ -174,6 +174,8 @@ static inline void restore_irq_pri(uint32_t state) { #define IRQ_PRI_SPI NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 8, 0) +#define IRQ_PRI_HSEM NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 10, 0) + // Interrupt priority for non-special timers. #define IRQ_PRI_TIMX NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 13, 0) diff --git a/ports/stm32/mpmetalport.c b/ports/stm32/mpmetalport.c new file mode 100644 index 0000000000000..72f5537ce5746 --- /dev/null +++ b/ports/stm32/mpmetalport.c @@ -0,0 +1,107 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023 Arduino SA + * + * 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. + * + * libmetal stm32 port. + */ + +#include "py/mperrno.h" +#include "py/mphal.h" + +#include "mpu.h" + +#include "metal/sys.h" +#include "metal/utilities.h" +#include "metal/device.h" + +struct metal_state _metal; +static mp_sched_node_t rproc_notify_node; + +int metal_sys_init(const struct metal_init_params *params) { + metal_unused(params); + + // Clear HSEM pending IRQ. + HSEM_COMMON->ICR |= (uint32_t)__HAL_HSEM_SEMID_TO_MASK(METAL_HSEM_MASTER_ID); + HAL_NVIC_ClearPendingIRQ(HSEM1_IRQn); + + // Enable and configure HSEM. + __HAL_RCC_HSEM_CLK_ENABLE(); + NVIC_SetPriority(HSEM1_IRQn, IRQ_PRI_HSEM); + HAL_NVIC_EnableIRQ(HSEM1_IRQn); + HAL_HSEM_ActivateNotification(__HAL_HSEM_SEMID_TO_MASK(METAL_HSEM_MASTER_ID)); + + #ifndef VIRTIO_USE_DCACHE + // If cache management is not enabled, configure the MPU to disable caching + // for the entire shared memory region. + uint32_t irq_state = mpu_config_start(); + mpu_config_region(MPU_REGION_OPENAMP, METAL_MPU_REGION_BASE, MPU_CONFIG_SHARED_UNCACHED(METAL_MPU_REGION_SIZE)); + mpu_config_end(irq_state); + #endif + + metal_bus_register(&metal_generic_bus); + return 0; +} + +void metal_sys_finish(void) { + HAL_NVIC_DisableIRQ(HSEM1_IRQn); + HAL_HSEM_DeactivateNotification(__HAL_HSEM_SEMID_TO_MASK(METAL_HSEM_MASTER_ID)); + __HAL_RCC_HSEM_CLK_DISABLE(); + metal_bus_unregister(&metal_generic_bus); +} + +unsigned int sys_irq_save_disable(void) { + return disable_irq(); +} + +void sys_irq_restore_enable(unsigned int state) { + enable_irq(state); +} + +void *metal_machine_io_mem_map(void *va, metal_phys_addr_t pa, + size_t size, unsigned int flags) { + metal_unused(pa); + metal_unused(size); + metal_unused(flags); + return va; +} + +void metal_machine_cache_flush(void *addr, unsigned int len) { + SCB_CleanDCache_by_Addr(addr, len); +} + +void metal_machine_cache_invalidate(void *addr, unsigned int len) { + SCB_InvalidateDCache_by_Addr(addr, len); +} + +int metal_rproc_notify(void *priv, uint32_t id) { + HAL_HSEM_FastTake(METAL_HSEM_REMOTE_ID); + HAL_HSEM_Release(METAL_HSEM_REMOTE_ID, 0); + return 0; +} + +void HSEM1_IRQHandler(void) { + HAL_HSEM_IRQHandler(); + mp_sched_schedule_node(&rproc_notify_node, openamp_remoteproc_notified); + HAL_HSEM_ActivateNotification(__HAL_HSEM_SEMID_TO_MASK(METAL_HSEM_MASTER_ID)); +} diff --git a/ports/stm32/mpmetalport.h b/ports/stm32/mpmetalport.h new file mode 100644 index 0000000000000..53f329d15b093 --- /dev/null +++ b/ports/stm32/mpmetalport.h @@ -0,0 +1,76 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023 Arduino SA + * + * 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. + * + * libmetal stm32 port. + */ +#ifndef MICROPY_INCLUDED_STM32_MPMETALPORT_H +#define MICROPY_INCLUDED_STM32_MPMETALPORT_H + +#include +#include "py/mphal.h" +#include "py/runtime.h" + +#define METAL_HAVE_STDATOMIC_H 0 +#define METAL_HAVE_FUTEX_H 0 + +#define METAL_MAX_DEVICE_REGIONS 2 + +#define METAL_HSEM_REMOTE_ID 0 +#define METAL_HSEM_MASTER_ID 1 + +// Set to 1 to enable log output. +#define METAL_LOG_HANDLER_ENABLE 0 + +#define metal_cpu_yield() + +// Shared memory config +#define METAL_SHM_NAME "OPENAMP_SHM" +// Note 1K must be reserved at the start of the openamp +// shared memory region, for the shared resource table. +#define METAL_RSC_ADDR ((void *)_openamp_shm_region_start) +#define METAL_RSC_SIZE (1024) + +#define METAL_SHM_ADDR ((metal_phys_addr_t)(_openamp_shm_region_start + METAL_RSC_SIZE)) +#define METAL_SHM_SIZE ((size_t)(_openamp_shm_region_end - _openamp_shm_region_start - METAL_RSC_SIZE)) + +#define METAL_MPU_REGION_BASE ((uint32_t)_openamp_shm_region_start) +#define METAL_MPU_REGION_SIZE (MPU_REGION_SIZE_64KB) + +extern const char _openamp_shm_region_start[]; +extern const char _openamp_shm_region_end[]; + +int metal_rproc_notify(void *priv, uint32_t id); +extern void openamp_remoteproc_notified(mp_sched_node_t *node); + +static inline int __metal_sleep_usec(unsigned int usec) { + mp_hal_delay_us(usec); + return 0; +} + +static inline void metal_generic_default_poll(void) { + MICROPY_EVENT_POLL_HOOK +} + +#endif // MICROPY_INCLUDED_STM32_METAL_PORT_H From 09eb4caccb34b16647a650ddd396bcdd318613dc Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Mon, 13 Nov 2023 17:25:11 +0100 Subject: [PATCH 10/16] stm32: Implement port backend for OpenAMP's remoteproc. Signed-off-by: iabdalkader --- ports/stm32/Makefile | 4 + ports/stm32/mpremoteprocport.c | 153 +++++++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 ports/stm32/mpremoteprocport.c diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index 75609031966d5..50ac48e5a18eb 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -427,6 +427,10 @@ endif # MICROPY_PY_BLUETOOTH ifeq ($(MICROPY_PY_OPENAMP),1) SRC_C += mpmetalport.c $(BUILD)/mpmetalport.o: $(BUILD)/openamp/metal/config.h +ifeq ($(MICROPY_PY_OPENAMP_REMOTEPROC),1) +SRC_C += mpremoteprocport.c +$(BUILD)/mpremoteprocport.o: $(BUILD)/openamp/metal/config.h +endif endif # SRC_O should be placed first to work around this LTO bug with binutils <2.35: diff --git a/ports/stm32/mpremoteprocport.c b/ports/stm32/mpremoteprocport.c new file mode 100644 index 0000000000000..56dff43da4e73 --- /dev/null +++ b/ports/stm32/mpremoteprocport.c @@ -0,0 +1,153 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023-2024 Arduino SA + * + * 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. + * + * modremoteproc stm32 port. + */ + +#include +#include + +#include "py/obj.h" +#include "py/runtime.h" + +#include "metal/alloc.h" +#include "metal/errno.h" +#include "metal/io.h" +#include "metal/sys.h" +#include "metal/device.h" +#include "metal/utilities.h" +#include "extmod/modopenamp_remoteproc.h" + +#define DEBUG_printf(...) // mp_printf(&mp_plat_print, __VA_ARGS__) + +struct remoteproc *mp_openamp_remoteproc_init(struct remoteproc *rproc, + const struct remoteproc_ops *ops, void *arg) { + DEBUG_printf("rproc_init()\n"); + + rproc->ops = ops; + rproc->state = RPROC_OFFLINE; + // Allocate the image store and save it in private data. + rproc->priv = mp_openamp_remoteproc_store_alloc(); + return rproc; +} + +void *mp_openamp_remoteproc_mmap(struct remoteproc *rproc, metal_phys_addr_t *pa, + metal_phys_addr_t *da, size_t size, unsigned int attribute, + struct metal_io_region **io) { + DEBUG_printf("rproc_mmap(): pa 0x%p da 0x%p io 0x%p size %u\n", *pa, *da, *io, size); + + struct remoteproc_mem *mem; + metal_phys_addr_t lpa = *pa; + metal_phys_addr_t lda = *da; + + if (lda == METAL_BAD_PHYS) { + return NULL; + } + + if (lpa == METAL_BAD_PHYS) { + lpa = lda; + } + + // Currently this port doesn't support loading firmware to flash, + // only SD/SRAM images are supported. Check of load address is in + // the flash region, and if so return NULL. + if (lda >= FLASH_BASE && lda < FLASH_END) { + return NULL; + } + + mem = metal_allocate_memory(sizeof(*mem)); + if (!mem) { + return NULL; + } + + *io = metal_allocate_memory(sizeof(struct metal_io_region)); + if (!*io) { + metal_free_memory(mem); + return NULL; + } + + remoteproc_init_mem(mem, NULL, lpa, lda, size, *io); + + metal_io_init(*io, (void *)mem->da, &mem->pa, size, + sizeof(metal_phys_addr_t) << 3, attribute, NULL); + + remoteproc_add_mem(rproc, mem); + *pa = lpa; + *da = lda; + return metal_io_phys_to_virt(*io, mem->pa); +} + +int mp_openamp_remoteproc_start(struct remoteproc *rproc) { + DEBUG_printf("rproc_start()\n"); + if ((RCC->GCR & RCC_GCR_BOOT_C2) || (FLASH->OPTSR_CUR & FLASH_OPTSR_BCM4)) { + // The CM4 core has already been started manually, or auto-boot is enabled + // via the option bytes, in either case the core can't be restarted. + DEBUG_printf("rproc_start(): CM4 core is already booted.\n"); + return -1; + } + + // Flush M7 cache. + struct metal_list *node; + metal_list_for_each(&rproc->mems, node) { + struct remoteproc_mem *mem; + mem = metal_container_of(node, struct remoteproc_mem, node); + SCB_CleanDCache_by_Addr((uint32_t *)mem->pa, mem->size); + } + + HAL_SYSCFG_CM4BootAddConfig(SYSCFG_BOOT_ADDR0, (uint32_t)rproc->bootaddr); + HAL_RCCEx_EnableBootCore(RCC_BOOT_C2); + return 0; +} + +int mp_openamp_remoteproc_stop(struct remoteproc *rproc) { + DEBUG_printf("rproc_stop()\n"); + if (rproc->state == RPROC_RUNNING) { + // There's no straightforward way to reset or shut down + // the remote processor, so a full system reset is needed. + NVIC_SystemReset(); + } + return 0; +} + +int mp_openamp_remoteproc_config(struct remoteproc *rproc, void *data) { + DEBUG_printf("rproc_config()\n"); + (void)rproc; + return 0; +} + +void mp_openamp_remoteproc_remove(struct remoteproc *rproc) { + DEBUG_printf("rproc_remove()\n"); + (void)rproc; +} + +int mp_openamp_remoteproc_shutdown(struct remoteproc *rproc) { + DEBUG_printf("rproc_shutdown()\n"); + if (rproc->state == RPROC_RUNNING) { + // There's no straightforward way to reset or shut down + // the remote processor, so a full system reset is needed. + NVIC_SystemReset(); + } + return 0; +} From c859978da31bde0a43848490a4da339cdea22c2f Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Mon, 20 Nov 2023 10:03:22 +0100 Subject: [PATCH 11/16] stm32/boards/ARDUINO_GIGA: Enable OpenAMP. Signed-off-by: iabdalkader --- ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.mk | 2 ++ ports/stm32/boards/ARDUINO_GIGA/stm32h747.ld | 12 ++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.mk b/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.mk index b927388c5577a..46fe40c2a6a82 100644 --- a/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.mk +++ b/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.mk @@ -23,6 +23,8 @@ MICROPY_PY_LWIP = 1 MICROPY_PY_NETWORK_CYW43 = 1 MICROPY_PY_SSL = 1 MICROPY_SSL_MBEDTLS = 1 +MICROPY_PY_OPENAMP = 1 +MICROPY_PY_OPENAMP_REMOTEPROC = 1 FROZEN_MANIFEST = $(BOARD_DIR)/manifest.py MBEDTLS_CONFIG_FILE = '"$(BOARD_DIR)/mbedtls_config_board.h"' diff --git a/ports/stm32/boards/ARDUINO_GIGA/stm32h747.ld b/ports/stm32/boards/ARDUINO_GIGA/stm32h747.ld index d5bd9598f9820..793a76b970292 100644 --- a/ports/stm32/boards/ARDUINO_GIGA/stm32h747.ld +++ b/ports/stm32/boards/ARDUINO_GIGA/stm32h747.ld @@ -14,10 +14,10 @@ MEMORY SRAM4 (xrw) : ORIGIN = 0x38000000, LENGTH = 64K /* SRAM4 D3 */ FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K /* Total available flash */ FLASH_EXT (rx) : ORIGIN = 0x90000000, LENGTH = 16384K /* 16MBs external QSPI flash */ - FLASH_FS (r) : ORIGIN = 0x08020000, LENGTH = 128K /* sector 1 -> Flash storage */ - FLASH_TEXT (rx) : ORIGIN = 0x08040000, LENGTH = 1792K /* Sector 0 -> Arduino Bootloader - Sector 1 -> Reserved for CM4/FS - Sectors 2 -> 15 firmware */ + FLASH_BL (rx) : ORIGIN = 0x08000000, LENGTH = 128K /* Arduino bootloader */ + FLASH_FS (r) : ORIGIN = 0x08020000, LENGTH = 128K /* filesystem */ + FLASH_TEXT (rx) : ORIGIN = 0x08040000, LENGTH = 1280K /* CM7 firmware */ + FLASH_CM4 (rx) : ORIGIN = 0x08180000, LENGTH = 512K /* CM4 firmware */ } /* produce a link error if there is not this amount of RAM for these sections */ @@ -44,4 +44,8 @@ _micropy_hw_internal_flash_storage_ram_cache_end = ORIGIN(DTCM) + LENGTH(DTCM); _micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); _micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); +/* OpenAMP shared memory region */ +_openamp_shm_region_start = ORIGIN(SRAM4); +_openamp_shm_region_end = ORIGIN(SRAM4) + LENGTH(SRAM4); + INCLUDE common_blifs.ld From fc9734363919c1ecba1f9ae58f8c093e47e55d56 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Mon, 11 Mar 2024 09:31:11 +0100 Subject: [PATCH 12/16] stm32/boards/ARDUINO_NICLA_VISION: Enable OpenAMP. Signed-off-by: iabdalkader --- .../boards/ARDUINO_NICLA_VISION/mpconfigboard.mk | 2 ++ ports/stm32/boards/ARDUINO_NICLA_VISION/stm32h747.ld | 12 ++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.mk b/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.mk index 2c48c17f3805e..77d547bce05e9 100644 --- a/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.mk +++ b/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.mk @@ -23,6 +23,8 @@ MICROPY_PY_LWIP = 1 MICROPY_PY_NETWORK_CYW43 = 1 MICROPY_PY_SSL = 1 MICROPY_SSL_MBEDTLS = 1 +MICROPY_PY_OPENAMP = 1 +MICROPY_PY_OPENAMP_REMOTEPROC = 1 FROZEN_MANIFEST = $(BOARD_DIR)/manifest.py MBEDTLS_CONFIG_FILE = '"$(BOARD_DIR)/mbedtls_config_board.h"' diff --git a/ports/stm32/boards/ARDUINO_NICLA_VISION/stm32h747.ld b/ports/stm32/boards/ARDUINO_NICLA_VISION/stm32h747.ld index fcbfc94d46204..6d6ce279f299c 100644 --- a/ports/stm32/boards/ARDUINO_NICLA_VISION/stm32h747.ld +++ b/ports/stm32/boards/ARDUINO_NICLA_VISION/stm32h747.ld @@ -14,10 +14,10 @@ MEMORY SRAM4 (xrw) : ORIGIN = 0x38000000, LENGTH = 64K /* SRAM4 D3 */ FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K /* Total available flash */ FLASH_EXT (rx) : ORIGIN = 0x90000000, LENGTH = 16384K /* 16MBs external QSPI flash */ - FLASH_FS (r) : ORIGIN = 0x08020000, LENGTH = 128K /* sector 1 -> Flash storage */ - FLASH_TEXT (rx) : ORIGIN = 0x08040000, LENGTH = 1792K /* Sector 0 -> Arduino Bootloader - Sector 1 -> Reserved for CM4/FS - Sectors 2 -> 15 firmware */ + FLASH_BL (rx) : ORIGIN = 0x08000000, LENGTH = 128K /* Arduino bootloader */ + FLASH_FS (r) : ORIGIN = 0x08020000, LENGTH = 128K /* filesystem */ + FLASH_TEXT (rx) : ORIGIN = 0x08040000, LENGTH = 1280K /* CM7 firmware */ + FLASH_CM4 (rx) : ORIGIN = 0x08180000, LENGTH = 512K /* CM4 firmware */ } _cm4_ram_start = ORIGIN(SRAM4); @@ -46,4 +46,8 @@ _micropy_hw_internal_flash_storage_ram_cache_end = ORIGIN(DTCM) + LENGTH(DTCM); _micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); _micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); +/* OpenAMP shared memory region */ +_openamp_shm_region_start = ORIGIN(SRAM4); +_openamp_shm_region_end = ORIGIN(SRAM4) + LENGTH(SRAM4); + INCLUDE common_blifs.ld From 864e4596bf1c99112019b3d0d169f140ea9f2284 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Mon, 11 Mar 2024 09:32:10 +0100 Subject: [PATCH 13/16] stm32/boards/ARDUINO_PORTENTA_H7: Enable OpenAMP. Signed-off-by: iabdalkader --- .../boards/ARDUINO_PORTENTA_H7/mpconfigboard.mk | 2 ++ ports/stm32/boards/ARDUINO_PORTENTA_H7/stm32h747.ld | 12 ++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.mk b/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.mk index cf4d40e5fe9b9..c56c8f005f72b 100644 --- a/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.mk +++ b/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.mk @@ -23,6 +23,8 @@ MICROPY_PY_LWIP = 1 MICROPY_PY_NETWORK_CYW43 = 1 MICROPY_PY_SSL = 1 MICROPY_SSL_MBEDTLS = 1 +MICROPY_PY_OPENAMP = 1 +MICROPY_PY_OPENAMP_REMOTEPROC = 1 FROZEN_MANIFEST = $(BOARD_DIR)/manifest.py MBEDTLS_CONFIG_FILE = '"$(BOARD_DIR)/mbedtls_config_board.h"' diff --git a/ports/stm32/boards/ARDUINO_PORTENTA_H7/stm32h747.ld b/ports/stm32/boards/ARDUINO_PORTENTA_H7/stm32h747.ld index d5bd9598f9820..793a76b970292 100644 --- a/ports/stm32/boards/ARDUINO_PORTENTA_H7/stm32h747.ld +++ b/ports/stm32/boards/ARDUINO_PORTENTA_H7/stm32h747.ld @@ -14,10 +14,10 @@ MEMORY SRAM4 (xrw) : ORIGIN = 0x38000000, LENGTH = 64K /* SRAM4 D3 */ FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K /* Total available flash */ FLASH_EXT (rx) : ORIGIN = 0x90000000, LENGTH = 16384K /* 16MBs external QSPI flash */ - FLASH_FS (r) : ORIGIN = 0x08020000, LENGTH = 128K /* sector 1 -> Flash storage */ - FLASH_TEXT (rx) : ORIGIN = 0x08040000, LENGTH = 1792K /* Sector 0 -> Arduino Bootloader - Sector 1 -> Reserved for CM4/FS - Sectors 2 -> 15 firmware */ + FLASH_BL (rx) : ORIGIN = 0x08000000, LENGTH = 128K /* Arduino bootloader */ + FLASH_FS (r) : ORIGIN = 0x08020000, LENGTH = 128K /* filesystem */ + FLASH_TEXT (rx) : ORIGIN = 0x08040000, LENGTH = 1280K /* CM7 firmware */ + FLASH_CM4 (rx) : ORIGIN = 0x08180000, LENGTH = 512K /* CM4 firmware */ } /* produce a link error if there is not this amount of RAM for these sections */ @@ -44,4 +44,8 @@ _micropy_hw_internal_flash_storage_ram_cache_end = ORIGIN(DTCM) + LENGTH(DTCM); _micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); _micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); +/* OpenAMP shared memory region */ +_openamp_shm_region_start = ORIGIN(SRAM4); +_openamp_shm_region_end = ORIGIN(SRAM4) + LENGTH(SRAM4); + INCLUDE common_blifs.ld From e5ca06a06fbb36b82dc50c118735cc1c10506080 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 29 Feb 2024 12:31:24 +0100 Subject: [PATCH 14/16] docs/library/openamp: Document the new openamp module. Signed-off-by: iabdalkader --- docs/library/index.rst | 1 + docs/library/openamp.rst | 115 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 docs/library/openamp.rst diff --git a/docs/library/index.rst b/docs/library/index.rst index eb29b7680542e..4209a0781a60e 100644 --- a/docs/library/index.rst +++ b/docs/library/index.rst @@ -103,6 +103,7 @@ the following libraries. micropython.rst neopixel.rst network.rst + openamp.rst uctypes.rst vfs.rst diff --git a/docs/library/openamp.rst b/docs/library/openamp.rst new file mode 100644 index 0000000000000..de4d51ad06b5e --- /dev/null +++ b/docs/library/openamp.rst @@ -0,0 +1,115 @@ +:mod:`openamp` -- provides standard Asymmetric Multiprocessing (AMP) support +============================================================================ + +.. module:: openamp + :synopsis: provides standard Asymmetric Multiprocessing (AMP) support + +The ``openamp`` module provides a standard inter-processor communications infrastructure +for MicroPython. The module handles all of the details of OpenAMP, such as setting up +the shared resource table, initializing vrings, etc. It provides an API for using the +RPMsg bus infrastructure with the `Endpoint` class, and provides processor Life Cycle +Management (LCM) support, such as loading firmware and starting and stopping a remote +core, via the `RemoteProc` class. + +Example usage:: + + import openamp + + def ept_recv_callback(src, data): + print("Received message on endpoint", data) + + # Create a new RPMsg endpoint to communicate with the remote core. + ept = openamp.Endpoint("vuart-channel", callback=ept_recv_callback) + + # Create a RemoteProc object, load its firmware and start it. + rproc = openamp.RemoteProc("virtual_uart.elf") # Or entry point address (ex 0x081E0000) + rproc.start() + + while True: + if ept.is_ready(): + ept.send("data") + +Functions +--------- + +.. function:: new_service_callback(ns_callback) + + Set the new service callback. + + The *ns_callback* argument is a function that will be called when the remote processor + announces new services. At that point the host processor can choose to create the + announced endpoint, if this particular service is supported, or ignore it if it's + not. If this function is not set, the host processor should first register the + endpoint locally, and it will be automatically bound when the remote announces + the service. + +Endpoint class +-------------- + +.. class:: Endpoint(name, callback, src=ENDPOINT_ADDR_ANY, dest=ENDPOINT_ADDR_ANY) + + Construct a new RPMsg Endpoint. An endpoint is a bidirectional communication + channel between two cores. + + Arguments are: + + - *name* is the name of the endpoint. + - *callback* is a function that is called when the endpoint receives data with the + source address of the remote point, and the data as bytes passed by reference. + - *src* is the endpoint source address. If none is provided one will be assigned + to the endpoint by the library. + - *dest* is the endpoint destination address. If the endpoint is created from the + new_service_callback, this must be provided and it must match the remote endpoint's + source address. If the endpoint is registered locally, before the announcement, the + destination address will be assigned by the library when the endpoint is bound. + +.. method:: Endpoint.deinit() + + Destroy the endpoint and release all of its resources. + +.. method:: Endpoint.is_ready() + + Returns True if the endpoint is ready to send (i.e., has both a source and destination addresses) + +.. method:: Endpoint.send(src=-1, dest=-1, timeout=-1) + + Send a message to the remote processor over this endpoint. + + Arguments are: + + - *src* is the source endpoint address of the message. If none is provided, the + source address the endpoint is bound to is used. + - *dest* is the destination endpoint address of the message. If none is provided, + the destination address the endpoint is bound to is used. + - *timeout* specifies the time in milliseconds to wait for a free buffer. By default + the function is blocking. + +RemoteProc class +---------------- + +.. class:: RemoteProc(entry) + + The RemoteProc object provides processor Life Cycle Management (LCM) support, such as + loading firmware, starting and stopping a remote core. + + The *entry* argument can be a path to firmware image, in which case the firmware is + loaded from file to its target memory, or an entry point address, in which case the + firmware must be loaded already at the given address. + +.. method:: RemoteProc.start() + + Starts the remote processor. + +.. method:: RemoteProc.stop() + + Stops the remote processor. The exact behavior is platform-dependent. On the STM32H7 for + example it's not possible to stop and then restart the Cortex-M4 core, so a complete + system reset is performed on a call to this function. + +.. method:: RemoteProc.shutdown() + + Shutdown stops the remote processor and releases all of its resources. The exact behavior + is platform-dependent, however typically it disables power and clocks to the remote core. + This function is also used as the finaliser (i.e., called when ``RemoteProc`` object is + collected). Note that on the STM32H7, it's not possible to stop and then restart the + Cortex-M4 core, so a complete system reset is performed on a call to this function. From 486ca3a688542f9f535522c29342e2dafe805d5a Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 14 Mar 2024 08:41:52 +0100 Subject: [PATCH 15/16] tools/ci.sh: Add Arduino GIGA to stm32 CI build. Signed-off-by: iabdalkader --- .github/workflows/ports_stm32.yml | 1 + tools/ci.sh | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/.github/workflows/ports_stm32.yml b/.github/workflows/ports_stm32.yml index 84d30b27f6e66..f5e01dc1f620e 100644 --- a/.github/workflows/ports_stm32.yml +++ b/.github/workflows/ports_stm32.yml @@ -25,6 +25,7 @@ jobs: ci_func: # names are functions in ci.sh - stm32_pyb_build - stm32_nucleo_build + - stm32_misc_build runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v4 diff --git a/tools/ci.sh b/tools/ci.sh index 3cbc51cfad625..f94f23893b710 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -366,6 +366,12 @@ function ci_stm32_nucleo_build { diff $BUILD_WB55/firmware.unpack.dfu $BUILD_WB55/firmware.unpack_no_sk.dfu } +function ci_stm32_misc_build { + make ${MAKEOPTS} -C mpy-cross + make ${MAKEOPTS} -C ports/stm32 BOARD=ARDUINO_GIGA submodules + make ${MAKEOPTS} -C ports/stm32 BOARD=ARDUINO_GIGA +} + ######################################################################################## # ports/unix From df2ff0c3553f67c3e8fe29f41013f27a75b608af Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 15 Mar 2024 17:51:47 +1100 Subject: [PATCH 16/16] LICENSE: Add libmetal and open-amp to 3rd-party license list. Signed-off-by: Damien George --- LICENSE | 2 ++ 1 file changed, 2 insertions(+) diff --git a/LICENSE b/LICENSE index b85f31808a483..9142c46045846 100644 --- a/LICENSE +++ b/LICENSE @@ -48,12 +48,14 @@ used during the build process and is not part of the compiled source code. /cmsis (BSD-3-clause) /crypto-algorithms (NONE) /libhydrogen (ISC) + /libmetal (BSD-3-clause) /littlefs (BSD-3-clause) /lwip (BSD-3-clause) /mynewt-nimble (Apache-2.0) /nrfx (BSD-3-clause) /nxp_driver (BSD-3-Clause) /oofatfs (BSD-1-clause) + /open-amp (BSD-3-clause) /pico-sdk (BSD-3-clause) /re15 (BSD-3-clause) /stm32lib (BSD-3-clause)