Compare commits

...

10 Commits

Author SHA1 Message Date
Ibrahim Abdelkader 4c0f97ff80
Merge d4c42014e9 into bd21820b4c 2024-01-15 14:08:41 +00:00
iabdalkader d4c42014e9 stm32/boards/ARDUINO_GIGA: Enable OpenAMP.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2024-01-15 16:08:30 +02:00
iabdalkader 36c4233be4 ports/stm32: Add OpenAMP's remoteproc port.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2024-01-15 16:08:30 +02:00
iabdalkader 8b6ca0278f extmod/modrproc.c: Add OpenAMP's remoteproc extmod module.
modrproc provides an API to load firmware and control remote processors.
Note port-specific ops must be implemented to support this module.

Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2024-01-15 16:08:30 +02:00
iabdalkader 654aed0452 extmod/modopenamp.c: Add OpenAMP extmod 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 <i.abdalkader@gmail.com>
2024-01-15 16:08:20 +02:00
iabdalkader 6a54606a9f ports/stm32: Add libmetal port.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2023-12-29 09:19:56 +01:00
iabdalkader 3019acb736 ports/stm32: Add MPU config for shared memory region.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2023-12-29 09:19:56 +01:00
iabdalkader 5d7f64ffaf py/stream: Add stream seek helper function.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2023-12-29 09:19:56 +01:00
iabdalkader 013d1e532f 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 <i.abdalkader@gmail.com>
2023-12-29 09:19:56 +01:00
iabdalkader dd296195b7 lib/libmetal: Add libmetal submodule.
libmetal provides an abstraction of the underlying hardware,
to support other OpenAMP components. Note a generic system
for MicroPython is included in this fork, that uses common
mp_hal functions, and provides a way for MicroPython's ports
to override defaults and configure libmetal.

Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2023-12-29 09:19:56 +01:00
18 changed files with 1216 additions and 4 deletions

6
.gitmodules vendored
View File

@ -59,3 +59,9 @@
[submodule "lib/protobuf-c"]
path = lib/protobuf-c
url = https://github.com/protobuf-c/protobuf-c.git
[submodule "lib/libmetal"]
path = lib/libmetal
url = https://github.com/iabdalkader/libmetal.git
[submodule "lib/open-amp"]
path = lib/open-amp
url = https://github.com/OpenAMP/open-amp.git

View File

@ -30,6 +30,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 \
@ -42,6 +43,7 @@ SRC_EXTMOD_C += \
extmod/moductypes.c \
extmod/modwebrepl.c \
extmod/modwebsocket.c \
extmod/modrproc.c \
extmod/network_cyw43.c \
extmod/network_esp_hosted.c \
extmod/network_lwip.c \
@ -509,3 +511,69 @@ 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)
INC += -I$(TOP)/$(OPENAMP_DIR)
CFLAGS += -DMICROPY_PY_OPENAMP=1
ifeq ($(MICROPY_PY_REMOTEPROC),1)
CFLAGS += -DMICROPY_PY_REMOTEPROC=1
endif
CFLAGS_THIRDPARTY += \
-I$(TOP)/$(LIBMETAL_DIR) \
-I$(TOP)/$(OPENAMP_DIR) \
-I$(TOP)/$(OPENAMP_DIR)/lib/include/ \
-DMETAL_INTERNAL \
-DVIRTIO_DRIVER_ONLY \
-DNO_ATOMIC_64_SUPPORT \
-DRPMSG_BUFFER_SIZE=512 \
# Libmetal's source files.
SRC_THIRDPARTY_C += $(addprefix $(LIBMETAL_DIR)/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 \
)
# OpenAMP's source files.
SRC_THIRDPARTY_C += $(addprefix $(OPENAMP_DIR)/lib/,\
rpmsg/rpmsg.c \
rpmsg/rpmsg_virtio.c \
virtio/virtio.c \
virtio/virtqueue.c \
virtio_mmio/virtio_mmio_drv.c \
)
# OpenAMP's remoteproc source files.
ifeq ($(MICROPY_PY_REMOTEPROC),1)
SRC_THIRDPARTY_C += $(addprefix $(OPENAMP_DIR)/lib/remoteproc/,\
elf_loader.c \
remoteproc.c \
remoteproc_virtio.c \
rsc_table_parser.c \
)
endif # MICROPY_PY_REMOTEPROC
endif # MICROPY_PY_OPENAMP

392
extmod/modopenamp.c Normal file
View File

@ -0,0 +1,392 @@
/*
* 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.
*/
#if MICROPY_PY_OPENAMP
#include "py/obj.h"
#include "py/nlr.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 "metal/metal_port.h"
#include "openamp/open_amp.h"
#include "openamp/remoteproc.h"
#include "openamp/remoteproc_loader.h"
#include "modopenamp.h"
#if !MICROPY_PY_OPENAMP_RESOURCE_TABLE
#include "openamp_config.h"
#endif
#if MICROPY_PY_OPENAMP_RESOURCE_TABLE
#define VIRTIO_DEV_ID 0xFF
#define VIRTIO_DEV_FEATURES (1 << VIRTIO_RPMSG_F_NS)
#define VRING0_ID 0 // VRING0 ID (master to remote) fixed to 0 for linux compatibility
#define VRING1_ID 1 // VRING1 ID (remote to master) 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_RESOURCE_TABLE
#define debug_printf(...) // mp_printf(&mp_plat_print, __VA_ARGS__)
#if MICROPY_PY_REMOTEPROC
extern mp_obj_type_t rproc_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
// 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 *self = MP_OBJ_TO_PTR(self_in);
rpmsg_deinit_vdev(&self->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_virtio_dev) },
{ 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);
MP_DEFINE_CONST_OBJ_TYPE(
virtio_dev_type,
MP_QSTR_virtio_dev,
MP_TYPE_FLAG_NONE,
locals_dict, &virtio_dev_locals_dict
);
// ###################### RPMsg Endpoint class ######################
typedef struct _rpmsg_obj_t {
mp_obj_base_t base;
mp_obj_t name;
uint32_t src_addr;
uint32_t dst_addr;
mp_obj_t callback;
struct rpmsg_endpoint ep;
} rpmsg_obj_t;
const mp_obj_type_t rpmsg_type;
static int rpmsg_recv_callback(struct rpmsg_endpoint *ept, void *data, size_t len, uint32_t src, void *priv) {
debug_printf("rpmsg_recv_callback() message received src_addr: %lu msg len: %d\n", src, len);
rpmsg_obj_t *self = metal_container_of(ept, rpmsg_obj_t, ep);
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 _rpmsg_send(uint n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_src_addr, ARG_dst_addr, ARG_timeout };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_src_addr, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = -1 } },
{ MP_QSTR_dst_addr, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = -1 } },
{ MP_QSTR_timeout, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0 } },
};
// 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);
rpmsg_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_addr = self->src_addr;
if (args[ARG_src_addr].u_int != -1) {
src_addr = self->src_addr;
}
uint32_t dst_addr = self->dst_addr;
if (args[ARG_dst_addr].u_int != -1) {
dst_addr = self->dst_addr;
}
mp_buffer_info_t rbuf;
mp_get_buffer_raise(pos_args[1], &rbuf, MP_BUFFER_READ);
debug_printf("rpmsg_send() msg len: %d\n", rbuf.len);
int bytes = 0;
uint32_t timeout = args[ARG_timeout].u_int;
for (mp_uint_t start = mp_hal_ticks_ms(); ; mp_hal_delay_ms(10)) {
bytes = rpmsg_send_offchannel_raw(&self->ep, src_addr, dst_addr, rbuf.buf, rbuf.len, false);
if (bytes > 0) {
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_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(rpmsg_send_obj, 2, _rpmsg_send);
STATIC mp_obj_t rpmsg_deinit(mp_obj_t self_in) {
rpmsg_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(rpmsg_deinit_obj, rpmsg_deinit);
STATIC mp_obj_t rpmsg_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_src_addr, ARG_dst_addr, ARG_callback };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_name, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_src_addr, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = RPMSG_ADDR_ANY } },
{ MP_QSTR_dst_addr, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = RPMSG_ADDR_ANY } },
{ MP_QSTR_callback, MP_ARG_OBJ, {.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);
rpmsg_obj_t *self = m_new_obj_with_finaliser(rpmsg_obj_t);
self->base.type = &rpmsg_type;
self->name = args[ARG_name].u_obj;
self->src_addr = args[ARG_src_addr].u_int;
self->dst_addr = args[ARG_dst_addr].u_int;
self->callback = args[ARG_callback].u_obj;
if (rpmsg_create_ept(&self->ep, &MP_STATE_PORT(virtio_device)->rvdev.rdev, mp_obj_str_get_str(self->name),
self->src_addr, self->dst_addr, rpmsg_recv_callback, NULL) != 0) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to create RPMsg endpoint"));
}
// If the source address was set to any, the endpoint is assigned a new address in create_ept.
self->src_addr = self->ep.addr;
return MP_OBJ_FROM_PTR(self);
}
STATIC const mp_rom_map_elem_t rpmsg_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_rpmsg) },
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&rpmsg_deinit_obj) },
{ MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&rpmsg_send_obj) },
};
STATIC MP_DEFINE_CONST_DICT(rpmsg_locals_dict, rpmsg_locals_dict_table);
MP_DEFINE_CONST_OBJ_TYPE(
rpmsg_type,
MP_QSTR_rpmsg,
MP_TYPE_FLAG_NONE,
make_new, rpmsg_make_new,
locals_dict, &rpmsg_locals_dict
);
// ###################### openamp module ######################
void metal_rproc_notified(mp_sched_node_t *node) {
(void)node;
rproc_virtio_notified(MP_STATE_PORT(virtio_device)->rvdev.vdev, VRING_NOTIFY_ID);
}
static void rpmsg_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 master by sending
// the Name Service (NS) announcement containing the name of the channel.
virtio_dev_obj_t *self = metal_container_of(rdev, virtio_dev_obj_t, rvdev);
mp_call_function_2(self->ns_callback, mp_obj_new_int(dest), mp_obj_new_str(name, strlen(name)));
}
#if MICROPY_PY_OPENAMP_RESOURCE_TABLE
// 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_RESOURCE_TABLE
STATIC mp_obj_t openamp_init(uint n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_ns_callback };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_ns_callback, 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(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
if (MP_STATE_PORT(virtio_device) != NULL) {
MP_STATE_PORT(virtio_device)->ns_callback = args[ARG_ns_callback].u_obj;
return mp_const_none;
}
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 *self = m_new_obj_with_finaliser(virtio_dev_obj_t);
self->base.type = &virtio_dev_type;
self->ns_callback = args[ARG_ns_callback].u_obj;
// The remote processor detects that the virtio device is ready by polling
// the status field in the resource table.
rpmsg_virtio_init_shm_pool(&self->shm_pool, (void *)VRING_BUFF_ADDR, (size_t)VRING_BUFF_SIZE);
rpmsg_init_vdev(&self->rvdev, vdev, rpmsg_ns_callback, shm_io, &self->shm_pool);
MP_STATE_PORT(virtio_device) = self;
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(openamp_init_obj, 0, openamp_init);
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_RPMSG_ADDR_ANY), MP_ROM_INT(RPMSG_ADDR_ANY) },
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&openamp_init_obj) },
{ MP_ROM_QSTR(MP_QSTR_RPMsg), MP_ROM_PTR(&rpmsg_type) },
#if MICROPY_PY_REMOTEPROC
{ MP_ROM_QSTR(MP_QSTR_RProc), MP_ROM_PTR(&rproc_type) },
#endif
};
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

71
extmod/modopenamp.h Normal file
View File

@ -0,0 +1,71 @@
/*
* 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
// 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_RESOURCE_TABLE
#define MICROPY_PY_OPENAMP_RESOURCE_TABLE (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_RESOURCE_TABLE
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_RESOURCE_TABLE
// Ports should run this callback in scheduler context, when
// the remote processor notifies the host of pending messages.
void metal_rproc_notified(mp_sched_node_t *node);
#endif // MICROPY_INCLUDED_MODOPENAMP_H

218
extmod/modrproc.c Normal file
View File

@ -0,0 +1,218 @@
/*
* 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 remoteproc Python module.
*/
#if MICROPY_PY_REMOTEPROC
#include "py/obj.h"
#include "py/nlr.h"
#include "py/runtime.h"
#include "py/stream.h"
#include "extmod/vfs.h"
#include "metal/alloc.h"
#include "metal/errno.h"
#include "metal/io.h"
#include "openamp/remoteproc.h"
#include "openamp/remoteproc_loader.h"
#include "modrproc.h"
#define debug_printf(...) // mp_printf(&mp_plat_print, __VA_ARGS__)
typedef struct rproc_obj_t {
mp_obj_base_t base;
struct remoteproc rproc;
} rproc_obj_t;
const mp_obj_type_t rproc_type;
// Port-defined remoteproc operations.
extern struct remoteproc_ops mp_rproc_ops;
#if !MICROPY_PY_REMOTEPROC_FILE_STORE
// Port-defined image store operations.
extern struct image_store_ops mp_store_ops;
#else
// A generic file-based image store.
static int 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),
};
rproc_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 store_close(void *store) {
debug_printf("store_close()\n");
rproc_filestore_t *fstore = store;
mp_stream_close(fstore->file);
metal_free_memory(fstore->buf);
metal_free_memory(fstore);
}
static int 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;
rproc_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 mp_store_ops = {
.open = store_open,
.close = store_close,
.load = store_load,
.features = SUPPORT_SEEK,
};
#endif // MICROPY_PY_REMOTEPROC_FILE_STORE
STATIC mp_obj_t rproc_start(mp_obj_t self_in) {
rproc_obj_t *self = MP_OBJ_TO_PTR(self_in);
// Start the processor to run the application.
if (remoteproc_start(&self->rproc) != 0) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to start remote processor"));
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(rproc_start_obj, rproc_start);
STATIC mp_obj_t rproc_stop(mp_obj_t self_in) {
rproc_obj_t *self = MP_OBJ_TO_PTR(self_in);
// Stop the processor, but the processor is not powered down.
if (remoteproc_stop(&self->rproc) != 0) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to stop remote processor"));
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(rproc_stop_obj, rproc_stop);
STATIC mp_obj_t rproc_shutdown(mp_obj_t self_in) {
rproc_obj_t *self = MP_OBJ_TO_PTR(self_in);
// Shutdown the remoteproc and release its resources.
if (remoteproc_shutdown(&self->rproc) != 0) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to shut down the remote processor"));
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(rproc_shutdown_obj, rproc_shutdown);
mp_obj_t rproc_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);
rproc_obj_t *self = m_new_obj_with_finaliser(rproc_obj_t);
self->base.type = &rproc_type;
// Instantiate the 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, &mp_rproc_ops, NULL);
// Configure the remote before loading applications (optional).
remoteproc_config(&self->rproc, NULL);
if (mp_obj_is_int(args[ARG_entry].u_obj)) {
self->rproc.bootaddr = mp_obj_get_int(args[ARG_entry].u_obj);
} else {
// Load firmware.
const char *path = mp_obj_str_get_str(args[ARG_entry].u_obj);
if (remoteproc_load(&self->rproc, path, self->rproc.priv, &mp_store_ops, NULL) != 0) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to load firmware"));
}
}
return MP_OBJ_FROM_PTR(self);
}
STATIC const mp_rom_map_elem_t rproc_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_rproc) },
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&rproc_shutdown_obj) },
{ MP_ROM_QSTR(MP_QSTR_start), MP_ROM_PTR(&rproc_start_obj) },
{ MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&rproc_stop_obj) },
{ MP_ROM_QSTR(MP_QSTR_shutdown), MP_ROM_PTR(&rproc_shutdown_obj) },
};
STATIC MP_DEFINE_CONST_DICT(rproc_dict, rproc_dict_table);
MP_DEFINE_CONST_OBJ_TYPE(
rproc_type,
MP_QSTR_rproc,
MP_TYPE_FLAG_NONE,
make_new, rproc_make_new,
locals_dict, &rproc_dict
);
#endif // MICROPY_PY_REMOTEPROC

54
extmod/modrproc.h Normal file
View File

@ -0,0 +1,54 @@
/*
* 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 remoteproc Python module.
*/
#ifndef MICROPY_INCLUDED_MODRPROC_H
#define MICROPY_INCLUDED_MODRPROC_H
#include "openamp/remoteproc.h"
#include "openamp/remoteproc_loader.h"
#ifndef MICROPY_PY_REMOTEPROC_FILE_STORE
#define MICROPY_PY_REMOTEPROC_FILE_STORE (1)
#endif
#ifndef MICROPY_PY_REMOTEPROC_FILE_STORE_BUF_SIZE
// Note the initial file buffer size needs to be at least 512 to read
// enough of the elf headers on the call to store_open() call, and on
// later calls, it gets realloc'd if needed.
#define RPROC_FILE_STORE_BUF_SIZE (1024)
#else
#define RPROC_FILE_STORE_BUF_SIZE MICROPY_PY_REMOTEPROC_FILE_STORE_BUF_SIZE
#endif // RPROC_FILE_STORE_BUF_SIZE
// For ports that don't define a custom image store, this generic
// file-based image store uses VFS to load an elf file from storage.
typedef struct rproc_filestore {
size_t len;
uint8_t *buf;
mp_obj_t file;
} rproc_filestore_t;
#endif // MICROPY_INCLUDED_MODRPROC_H

1
lib/libmetal Submodule

@ -0,0 +1 @@
Subproject commit 405c227686af1e5f53c0beb1ac5cdd9349b2e297

1
lib/open-amp Submodule

@ -0,0 +1 @@
Subproject commit 1904dee18da85400e72b8f55c5c99e872a486573

View File

@ -417,6 +417,13 @@ endif
endif # MICROPY_PY_BLUETOOTH
ifeq ($(MICROPY_PY_OPENAMP),1)
SRC_C += metal/metal_port.c
ifeq ($(MICROPY_PY_REMOTEPROC),1)
SRC_C += mprprocport.c
endif
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))

View File

@ -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_REMOTEPROC = 1
FROZEN_MANIFEST = $(BOARD_DIR)/manifest.py
MBEDTLS_CONFIG_FILE = '"$(BOARD_DIR)/mbedtls_config_board.h"'

View File

@ -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_FS (r) : ORIGIN = 0x08020000, LENGTH = 128K /* 0x08000000 -> 0x08020000 Arduino Bootloader */
FLASH_TEXT (rx) : ORIGIN = 0x08040000, LENGTH = 1280K /* 0x08020000 -> 0x08040000 Flash filesystem storage */
/* 0x08040000 -> 0x08180000 CM7 firmware */
/* 0x08180000 -> 0x08200000 CM4 firmware */
}
/* produce a link error if there is not this amount of RAM for these sections */
@ -44,4 +44,7 @@ _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_shm_region_start = ORIGIN(SRAM4);
_openamp_shm_region_end = ORIGIN(SRAM4) + LENGTH(SRAM4);
INCLUDE common_blifs.ld

View File

@ -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)

View File

@ -0,0 +1,123 @@
/*
* 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_NUMBER15, MTEAL_MPU_REGION_BASE, MPU_REGION_SHARED_NCACHE(MTEAL_MPU_REGION_SIZE));
mpu_config_end(irq_state);
#endif
metal_bus_register(&metal_generic_bus);
return 0;
}
void metal_sys_assert(bool cond) {
assert(cond);
}
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 sys_irq_enable(unsigned int vector) {
metal_unused(vector);
}
void sys_irq_disable(unsigned int vector) {
metal_unused(vector);
}
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_sleep_usec(unsigned int usec) {
mp_hal_delay_us(usec);
return 0;
}
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, metal_rproc_notified);
HAL_HSEM_ActivateNotification(__HAL_HSEM_SEMID_TO_MASK(METAL_HSEM_MASTER_ID));
}

View File

@ -0,0 +1,71 @@
/*
* 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_METAL_PORT_H
#define MICROPY_INCLUDED_STM32_METAL_PORT_H
#include <stdlib.h>
#include "py/mphal.h"
#include "py/runtime.h"
#define METAL_HAVE_STDATOMIC_H 0
#define METAL_HAVE_FUTEX_H 0
#define METAL_PROCESSOR_CPU_H "metal/processor/arm/cpu.h"
#define METAL_PROCESSOR_ATOMIC_H "metal/processor/arm/atomic.h"
#define METAL_MAX_DEVICE_REGIONS 2
#define METAL_HSEM_REMOTE_ID 0
#define METAL_HSEM_MASTER_ID 1
// Note set to 1 to enable log output.
#define METAL_LOG_HANDLER_ENABLE 0
#define metal_cpu_yield()
#define metal_generic_default_poll() MICROPY_EVENT_POLL_HOOK
// 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 MTEAL_MPU_REGION_BASE ((uint32_t)_openamp_shm_region_start)
#define MTEAL_MPU_REGION_SIZE (MPU_REGION_SIZE_64KB)
extern const char _openamp_shm_region_start[];
extern const char _openamp_shm_region_end[];
void metal_sys_assert(bool cond);
int metal_rproc_notify(void *priv, uint32_t id);
extern void metal_rproc_notified(mp_sched_node_t *node);
#endif // MICROPY_INCLUDED_STM32_METAL_PORT_H

168
ports/stm32/mprprocport.c Normal file
View File

@ -0,0 +1,168 @@
/*
* 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.
*
* modremoteproc stm32 port.
*/
#include <stdio.h>
#include <stdint.h>
#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/modrproc.h"
#define debug_printf(...) // mp_printf(&mp_plat_print, __VA_ARGS__)
static struct remoteproc *rproc_init(struct remoteproc *rproc,
const struct remoteproc_ops *ops, void *arg) {
debug_printf("rproc_init()\n");
rproc = rproc;
rproc->ops = ops;
// Allocate filestore struct.
rproc_filestore_t *filestore = metal_allocate_memory(sizeof(rproc_filestore_t));
filestore->len = RPROC_FILE_STORE_BUF_SIZE;
filestore->buf = metal_allocate_memory(RPROC_FILE_STORE_BUF_SIZE);
// Store in rproc's private data.
rproc->priv = filestore;
metal_list_init(&rproc->mems);
// Set RPROC_OFFLINE to make rproc_config get called.
rproc->state = RPROC_READY;
return rproc;
}
static void *rproc_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);
}
static int rproc_start(struct remoteproc *rproc) {
debug_printf("rproc_start()\n");
if (RCC->GCR & RCC_GCR_BOOT_C2) {
// CM4 core is already booted.
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;
}
static int rproc_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;
}
static int rproc_config(struct remoteproc *rproc, void *data) {
debug_printf("rproc_config()\n");
(void)rproc;
return 0;
}
static void rproc_remove(struct remoteproc *rproc) {
debug_printf("rproc_remove()\n");
(void)rproc;
}
static int rproc_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;
}
const struct remoteproc_ops mp_rproc_ops = {
.init = rproc_init,
.mmap = rproc_mmap,
.start = rproc_start,
.stop = rproc_stop,
.config = rproc_config,
.remove = rproc_remove,
.shutdown = rproc_shutdown,
};

View File

@ -73,6 +73,18 @@
| MPU_REGION_ENABLE << MPU_RASR_ENABLE_Pos \
)
#define MPU_REGION_SHARED_NCACHE(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;

View File

@ -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)) {

View File

@ -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);