Compare commits
10 Commits
5676bf2c28
...
4c0f97ff80
Author | SHA1 | Date |
---|---|---|
Ibrahim Abdelkader | 4c0f97ff80 | |
iabdalkader | d4c42014e9 | |
iabdalkader | 36c4233be4 | |
iabdalkader | 8b6ca0278f | |
iabdalkader | 654aed0452 | |
iabdalkader | 6a54606a9f | |
iabdalkader | 3019acb736 | |
iabdalkader | 5d7f64ffaf | |
iabdalkader | 013d1e532f | |
iabdalkader | dd296195b7 |
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 405c227686af1e5f53c0beb1ac5cdd9349b2e297
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 1904dee18da85400e72b8f55c5c99e872a486573
|
|
@ -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))
|
||||
|
|
|
@ -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"'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
|
@ -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
|
|
@ -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,
|
||||
};
|
|
@ -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;
|
||||
|
|
12
py/stream.c
12
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)) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue