Compare commits
18 Commits
ca98a6329c
...
d4c42014e9
Author | SHA1 | Date |
---|---|---|
iabdalkader | d4c42014e9 | |
iabdalkader | 36c4233be4 | |
iabdalkader | 8b6ca0278f | |
iabdalkader | 654aed0452 | |
iabdalkader | 6a54606a9f | |
iabdalkader | 3019acb736 | |
iabdalkader | 5d7f64ffaf | |
iabdalkader | 013d1e532f | |
iabdalkader | dd296195b7 | |
Damien George | 9feb0689ee | |
Daniël van de Giessen | d014c82826 | |
Damien George | 0b2676db5c | |
Nicko van Someren | cfc212b108 | |
Sebastian Romero | e4d3ab3304 | |
Peter Züger | d69e69adb6 | |
Peter Züger | ce42c9ee16 | |
Maarten van der Schrieck | 3bca93b2d0 | |
Maarten van der Schrieck | 91ee8ac894 |
|
@ -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
|
||||
|
|
|
@ -39,10 +39,12 @@ bool mp_os_dupterm_is_builtin_stream(mp_const_obj_t stream);
|
|||
void mp_os_dupterm_stream_detached_attached(mp_obj_t stream_detached, mp_obj_t stream_attached);
|
||||
uintptr_t mp_os_dupterm_poll(uintptr_t poll_flags);
|
||||
int mp_os_dupterm_rx_chr(void);
|
||||
void mp_os_dupterm_tx_strn(const char *str, size_t len);
|
||||
int mp_os_dupterm_tx_strn(const char *str, size_t len);
|
||||
void mp_os_deactivate(size_t dupterm_idx, const char *msg, mp_obj_t exc);
|
||||
#else
|
||||
#define mp_os_dupterm_tx_strn(s, l)
|
||||
static inline int mp_os_dupterm_tx_strn(const char *s, size_t l) {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // MICROPY_INCLUDED_EXTMOD_MISC_H
|
||||
|
|
|
@ -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
|
|
@ -53,10 +53,6 @@
|
|||
#include "nimble/host/src/ble_hs_hci_priv.h"
|
||||
#endif
|
||||
|
||||
#ifndef MICROPY_PY_BLUETOOTH_DEFAULT_GAP_NAME
|
||||
#define MICROPY_PY_BLUETOOTH_DEFAULT_GAP_NAME "MPY NIMBLE"
|
||||
#endif
|
||||
|
||||
#define DEBUG_printf(...) // printf("nimble: " __VA_ARGS__)
|
||||
|
||||
#define ERRNO_BLUETOOTH_NOT_ACTIVE MP_ENODEV
|
||||
|
@ -350,9 +346,6 @@ STATIC void sync_cb(void) {
|
|||
assert(rc == 0);
|
||||
}
|
||||
|
||||
DEBUG_printf("sync_cb: Setting device name\n");
|
||||
ble_svc_gap_device_name_set(MICROPY_PY_BLUETOOTH_DEFAULT_GAP_NAME);
|
||||
|
||||
mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_ACTIVE;
|
||||
}
|
||||
|
||||
|
|
|
@ -134,7 +134,11 @@ int nimble_sprintf(char *str, const char *fmt, ...);
|
|||
#define MYNEWT_VAL_BLE_SVC_GAP_APPEARANCE (0)
|
||||
#define MYNEWT_VAL_BLE_SVC_GAP_APPEARANCE_WRITE_PERM (-1)
|
||||
#define MYNEWT_VAL_BLE_SVC_GAP_CENTRAL_ADDRESS_RESOLUTION (-1)
|
||||
#define MYNEWT_VAL_BLE_SVC_GAP_DEVICE_NAME ("pybd")
|
||||
#ifdef MICROPY_PY_BLUETOOTH_DEFAULT_GAP_NAME
|
||||
#define MYNEWT_VAL_BLE_SVC_GAP_DEVICE_NAME (MICROPY_PY_BLUETOOTH_DEFAULT_GAP_NAME)
|
||||
#else
|
||||
#define MYNEWT_VAL_BLE_SVC_GAP_DEVICE_NAME ("MPY NIMBLE")
|
||||
#endif
|
||||
#define MYNEWT_VAL_BLE_SVC_GAP_DEVICE_NAME_MAX_LENGTH (31)
|
||||
#define MYNEWT_VAL_BLE_SVC_GAP_DEVICE_NAME_WRITE_PERM (-1)
|
||||
#define MYNEWT_VAL_BLE_SVC_GAP_PPCP_MAX_CONN_INTERVAL (0)
|
||||
|
|
|
@ -169,29 +169,43 @@ int mp_os_dupterm_rx_chr(void) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
void mp_os_dupterm_tx_strn(const char *str, size_t len) {
|
||||
int mp_os_dupterm_tx_strn(const char *str, size_t len) {
|
||||
// Returns the minimum successful write length, or -1 if no write is attempted.
|
||||
int ret = len;
|
||||
bool did_write = false;
|
||||
for (size_t idx = 0; idx < MICROPY_PY_OS_DUPTERM; ++idx) {
|
||||
if (MP_STATE_VM(dupterm_objs[idx]) == MP_OBJ_NULL) {
|
||||
continue;
|
||||
}
|
||||
did_write = true;
|
||||
|
||||
#if MICROPY_PY_OS_DUPTERM_BUILTIN_STREAM
|
||||
if (mp_os_dupterm_is_builtin_stream(MP_STATE_VM(dupterm_objs[idx]))) {
|
||||
int errcode = 0;
|
||||
const mp_stream_p_t *stream_p = mp_get_stream(MP_STATE_VM(dupterm_objs[idx]));
|
||||
stream_p->write(MP_STATE_VM(dupterm_objs[idx]), str, len, &errcode);
|
||||
mp_uint_t written = stream_p->write(MP_STATE_VM(dupterm_objs[idx]), str, len, &errcode);
|
||||
int write_res = MAX(0, written);
|
||||
ret = MIN(write_res, ret);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
nlr_buf_t nlr;
|
||||
if (nlr_push(&nlr) == 0) {
|
||||
mp_stream_write(MP_STATE_VM(dupterm_objs[idx]), str, len, MP_STREAM_RW_WRITE);
|
||||
mp_obj_t written = mp_stream_write(MP_STATE_VM(dupterm_objs[idx]), str, len, MP_STREAM_RW_WRITE);
|
||||
if (written == mp_const_none) {
|
||||
ret = 0;
|
||||
} else if (mp_obj_is_small_int(written)) {
|
||||
int written_int = MAX(0, MP_OBJ_SMALL_INT_VALUE(written));
|
||||
ret = MIN(written_int, ret);
|
||||
}
|
||||
nlr_pop();
|
||||
} else {
|
||||
mp_os_deactivate(idx, "dupterm: Exception in write() method, deactivating: ", MP_OBJ_FROM_PTR(nlr.ret_val));
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
return did_write ? ret : -1;
|
||||
}
|
||||
|
||||
STATIC mp_obj_t mp_os_dupterm(size_t n_args, const mp_obj_t *args) {
|
||||
|
|
|
@ -99,7 +99,7 @@ STATIC void MP_VFS_LFSx(init_config)(MP_OBJ_VFS_LFSx * self, mp_obj_t bdev, size
|
|||
config->lookahead_buffer = m_new(uint8_t, config->lookahead / 8);
|
||||
#else
|
||||
config->block_cycles = 100;
|
||||
config->cache_size = 4 * MAX(read_size, prog_size);
|
||||
config->cache_size = MIN(config->block_size, (4 * MAX(read_size, prog_size)));
|
||||
config->lookahead_size = lookahead;
|
||||
config->read_buffer = m_new(uint8_t, config->cache_size);
|
||||
config->prog_buffer = m_new(uint8_t, config->cache_size);
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 405c227686af1e5f53c0beb1ac5cdd9349b2e297
|
|
@ -1 +1 @@
|
|||
Subproject commit e025c843b60e93689f0f991d753010bb5bd6a722
|
||||
Subproject commit 7cdf70881519c73667efbc4a61a04d9c1a49babb
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 1904dee18da85400e72b8f55c5c99e872a486573
|
|
@ -140,10 +140,15 @@ void mp_hal_delay_ms(mp_uint_t delay) {
|
|||
}
|
||||
}
|
||||
|
||||
void mp_hal_stdout_tx_strn(const char *str, size_t len) {
|
||||
mp_os_dupterm_tx_strn(str, len);
|
||||
mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) {
|
||||
mp_uint_t ret = len;
|
||||
int dupterm_res = mp_os_dupterm_tx_strn(str, len);
|
||||
if (dupterm_res >= 0) {
|
||||
ret = dupterm_res;
|
||||
}
|
||||
// and also to telnet
|
||||
telnet_tx_strn(str, len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mp_hal_stdin_rx_chr(void) {
|
||||
|
|
|
@ -3,6 +3,8 @@ CONFIG_BT_ENABLED=y
|
|||
CONFIG_BT_NIMBLE_ENABLED=y
|
||||
CONFIG_BT_CONTROLLER_ENABLED=y
|
||||
|
||||
CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME="MPY ESP32"
|
||||
|
||||
CONFIG_BT_NIMBLE_MAX_CONNECTIONS=4
|
||||
|
||||
# Put NimBLE on core 1, and for synchronisation
|
||||
|
|
|
@ -125,24 +125,34 @@ int mp_hal_stdin_rx_chr(void) {
|
|||
}
|
||||
}
|
||||
|
||||
void mp_hal_stdout_tx_strn(const char *str, size_t len) {
|
||||
mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) {
|
||||
// Only release the GIL if many characters are being sent
|
||||
mp_uint_t ret = len;
|
||||
bool did_write = false;
|
||||
bool release_gil = len > 20;
|
||||
if (release_gil) {
|
||||
MP_THREAD_GIL_EXIT();
|
||||
}
|
||||
#if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
|
||||
usb_serial_jtag_tx_strn(str, len);
|
||||
did_write = true;
|
||||
#elif CONFIG_USB_OTG_SUPPORTED
|
||||
usb_tx_strn(str, len);
|
||||
did_write = true;
|
||||
#endif
|
||||
#if MICROPY_HW_ENABLE_UART_REPL
|
||||
uart_stdout_tx_strn(str, len);
|
||||
did_write = true;
|
||||
#endif
|
||||
if (release_gil) {
|
||||
MP_THREAD_GIL_ENTER();
|
||||
}
|
||||
mp_os_dupterm_tx_strn(str, len);
|
||||
int dupterm_res = mp_os_dupterm_tx_strn(str, len);
|
||||
if (dupterm_res >= 0) {
|
||||
did_write = true;
|
||||
ret = MIN((mp_uint_t)dupterm_res, ret);
|
||||
}
|
||||
return did_write ? ret : 0;
|
||||
}
|
||||
|
||||
uint32_t mp_hal_ticks_ms(void) {
|
||||
|
|
|
@ -94,8 +94,14 @@ void mp_hal_debug_str(const char *str) {
|
|||
}
|
||||
#endif
|
||||
|
||||
void mp_hal_stdout_tx_strn(const char *str, uint32_t len) {
|
||||
mp_os_dupterm_tx_strn(str, len);
|
||||
mp_uint_t mp_hal_stdout_tx_strn(const char *str, uint32_t len) {
|
||||
int dupterm_res = mp_os_dupterm_tx_strn(str, len);
|
||||
if (dupterm_res < 0) {
|
||||
// no outputs, nothing was written
|
||||
return 0;
|
||||
} else {
|
||||
return dupterm_res;
|
||||
}
|
||||
}
|
||||
|
||||
void mp_hal_debug_tx_strn_cooked(void *env, const char *str, uint32_t len) {
|
||||
|
|
|
@ -112,9 +112,12 @@ int mp_hal_stdin_rx_chr(void) {
|
|||
}
|
||||
}
|
||||
|
||||
void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
|
||||
mp_uint_t mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
|
||||
mp_uint_t ret = len;
|
||||
bool did_write = false;
|
||||
if (tud_cdc_connected()) {
|
||||
for (size_t i = 0; i < len;) {
|
||||
size_t i = 0;
|
||||
while (i < len) {
|
||||
uint32_t n = len - i;
|
||||
if (n > CFG_TUD_CDC_EP_BUFSIZE) {
|
||||
n = CFG_TUD_CDC_EP_BUFSIZE;
|
||||
|
@ -125,6 +128,7 @@ void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
|
|||
MICROPY_EVENT_POLL_HOOK
|
||||
}
|
||||
if (ticks_us64() >= timeout) {
|
||||
ret = i;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -132,10 +136,17 @@ void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
|
|||
tud_cdc_write_flush();
|
||||
i += n2;
|
||||
}
|
||||
did_write = true;
|
||||
ret = MIN(i, ret);
|
||||
}
|
||||
#if MICROPY_PY_OS_DUPTERM
|
||||
mp_os_dupterm_tx_strn(str, len);
|
||||
int dupterm_res = mp_os_dupterm_tx_strn(str, len);
|
||||
if (dupterm_res >= 0) {
|
||||
did_write = true;
|
||||
ret = MIN((mp_uint_t)dupterm_res, ret);
|
||||
}
|
||||
#endif
|
||||
return did_write ? ret : 0;
|
||||
}
|
||||
|
||||
uint64_t mp_hal_time_ns(void) {
|
||||
|
|
|
@ -29,10 +29,14 @@ int mp_hal_stdin_rx_chr(void) {
|
|||
}
|
||||
|
||||
// Send string of given length
|
||||
void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
|
||||
mp_uint_t mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
|
||||
mp_uint_t ret = len;
|
||||
#if MICROPY_MIN_USE_STDOUT
|
||||
int r = write(STDOUT_FILENO, str, len);
|
||||
(void)r;
|
||||
if (r >= 0) {
|
||||
// in case of an error in the syscall, report no bytes written
|
||||
ret = 0;
|
||||
}
|
||||
#elif MICROPY_MIN_USE_STM32_MCU
|
||||
while (len--) {
|
||||
// wait for TXE
|
||||
|
@ -41,4 +45,5 @@ void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
|
|||
USART1->DR = *str++;
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -110,10 +110,11 @@ int mp_hal_stdin_rx_chr(void) {
|
|||
return (int)byte;
|
||||
}
|
||||
|
||||
void mp_hal_stdout_tx_strn(const char *str, size_t len) {
|
||||
mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) {
|
||||
// Not connected: drop output
|
||||
if (!ble_uart_enabled()) return;
|
||||
if (!ble_uart_enabled()) return 0;
|
||||
|
||||
mp_uint_t ret = len;
|
||||
uint8_t *buf = (uint8_t *)str;
|
||||
size_t send_len;
|
||||
|
||||
|
@ -134,6 +135,7 @@ void mp_hal_stdout_tx_strn(const char *str, size_t len) {
|
|||
len -= send_len;
|
||||
buf += send_len;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ble_uart_tx_char(char c) {
|
||||
|
|
|
@ -231,12 +231,12 @@ int mp_hal_stdin_rx_chr(void) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
|
||||
|
||||
mp_uint_t mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
|
||||
for (const char *top = str + len; str < top; str++) {
|
||||
ringbuf_put((ringbuf_t*)&tx_ringbuf, *str);
|
||||
usb_cdc_loop();
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
void mp_hal_stdout_tx_strn_cooked(const char *str, mp_uint_t len) {
|
||||
|
|
|
@ -191,6 +191,9 @@ soft_reset:
|
|||
|
||||
if (ret != 0) {
|
||||
printf("MPY: can't mount flash\n");
|
||||
} else {
|
||||
mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_flash));
|
||||
mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_flash_slash_lib));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -196,10 +196,12 @@ int mp_hal_stdin_rx_chr(void) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
|
||||
mp_uint_t mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
|
||||
if (MP_STATE_VM(dupterm_objs[0]) != MP_OBJ_NULL) {
|
||||
uart_tx_strn(MP_STATE_VM(dupterm_objs[0]), str, len);
|
||||
return len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mp_hal_stdout_tx_strn_cooked(const char *str, mp_uint_t len) {
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
|
||||
// Entries for sys.path
|
||||
Q(/flash)
|
||||
Q(/flash/lib)
|
||||
|
||||
// For os.sep
|
||||
Q(/)
|
||||
|
|
|
@ -75,10 +75,12 @@ void mp_hal_stdout_tx_str(const char *str) {
|
|||
mp_hal_stdout_tx_strn(str, strlen(str));
|
||||
}
|
||||
|
||||
void mp_hal_stdout_tx_strn(const char *str, size_t len) {
|
||||
mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) {
|
||||
mp_uint_t ret = len;
|
||||
for (; len > 0; --len) {
|
||||
uart_tx_char(*str++);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void mp_hal_stdout_tx_strn_cooked(const char *str, size_t len) {
|
||||
|
|
|
@ -108,7 +108,7 @@ int mp_hal_stdin_rx_chr(void) {
|
|||
}
|
||||
|
||||
|
||||
void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
|
||||
mp_uint_t mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
|
||||
int i;
|
||||
for (i = 0; i < len; i++) {
|
||||
while (lpc_uart_tx_full()) {
|
||||
|
@ -116,4 +116,5 @@ void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
|
|||
}
|
||||
lpc_uart_reg_write(REG_RBR, str[i]);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
|
|
@ -118,7 +118,7 @@ int mp_hal_stdin_rx_chr(void) {
|
|||
return (char)(val & 0x000000ff);
|
||||
}
|
||||
|
||||
void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
|
||||
mp_uint_t mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
|
@ -129,4 +129,5 @@ void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
|
|||
}
|
||||
potato_uart_reg_write(POTATO_CONSOLE_TX, val);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
|
|
@ -154,16 +154,20 @@ int mp_hal_stdin_rx_chr(void) {
|
|||
}
|
||||
|
||||
// Send string of given length
|
||||
void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
|
||||
mp_uint_t mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
|
||||
mp_uint_t ret = len;
|
||||
bool did_write = false;
|
||||
#if MICROPY_HW_ENABLE_UART_REPL
|
||||
if (MP_STATE_PORT(pyb_stdio_uart) != NULL) {
|
||||
uart_tx_strn(MP_STATE_PORT(pyb_stdio_uart), str, len);
|
||||
did_write = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if MICROPY_HW_USB_CDC
|
||||
if (tud_cdc_connected()) {
|
||||
for (size_t i = 0; i < len;) {
|
||||
size_t i = 0;
|
||||
while (i < len) {
|
||||
uint32_t n = len - i;
|
||||
if (n > CFG_TUD_CDC_EP_BUFSIZE) {
|
||||
n = CFG_TUD_CDC_EP_BUFSIZE;
|
||||
|
@ -180,12 +184,20 @@ void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
|
|||
tud_cdc_write_flush();
|
||||
i += n2;
|
||||
}
|
||||
ret = MIN(i, ret);
|
||||
did_write = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_OS_DUPTERM
|
||||
mp_os_dupterm_tx_strn(str, len);
|
||||
int dupterm_res = mp_os_dupterm_tx_strn(str, len);
|
||||
if (dupterm_res >= 0) {
|
||||
did_write = true;
|
||||
ret = MIN((mp_uint_t)dupterm_res, ret);
|
||||
}
|
||||
#endif
|
||||
|
||||
return did_write ? ret : 0;
|
||||
}
|
||||
|
||||
void mp_hal_ticks_cpu_enable(void) {
|
||||
|
|
|
@ -132,6 +132,7 @@ set(MICROPY_SOURCE_PORT
|
|||
pendsv.c
|
||||
rp2_flash.c
|
||||
rp2_pio.c
|
||||
rp2_dma.c
|
||||
uart.c
|
||||
usbd.c
|
||||
msc_disk.c
|
||||
|
@ -156,6 +157,7 @@ set(MICROPY_SOURCE_QSTR
|
|||
${MICROPY_PORT_DIR}/modos.c
|
||||
${MICROPY_PORT_DIR}/rp2_flash.c
|
||||
${MICROPY_PORT_DIR}/rp2_pio.c
|
||||
${MICROPY_PORT_DIR}/rp2_dma.c
|
||||
${CMAKE_BINARY_DIR}/pins_${MICROPY_BOARD}.c
|
||||
)
|
||||
|
||||
|
|
|
@ -159,6 +159,7 @@ int main(int argc, char **argv) {
|
|||
readline_init0();
|
||||
machine_pin_init();
|
||||
rp2_pio_init();
|
||||
rp2_dma_init();
|
||||
machine_i2s_init0();
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH
|
||||
|
@ -207,6 +208,7 @@ int main(int argc, char **argv) {
|
|||
#if MICROPY_PY_NETWORK
|
||||
mod_network_deinit();
|
||||
#endif
|
||||
rp2_dma_deinit();
|
||||
rp2_pio_deinit();
|
||||
#if MICROPY_PY_BLUETOOTH
|
||||
mp_bluetooth_deinit();
|
||||
|
|
|
@ -88,6 +88,7 @@ STATIC const mp_rom_map_elem_t rp2_module_globals_table[] = {
|
|||
{ MP_ROM_QSTR(MP_QSTR_Flash), MP_ROM_PTR(&rp2_flash_type) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_PIO), MP_ROM_PTR(&rp2_pio_type) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_StateMachine), MP_ROM_PTR(&rp2_state_machine_type) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_DMA), MP_ROM_PTR(&rp2_dma_type) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_bootsel_button), MP_ROM_PTR(&rp2_bootsel_button_obj) },
|
||||
|
||||
#if MICROPY_PY_NETWORK_CYW43
|
||||
|
|
|
@ -31,8 +31,12 @@
|
|||
extern const mp_obj_type_t rp2_flash_type;
|
||||
extern const mp_obj_type_t rp2_pio_type;
|
||||
extern const mp_obj_type_t rp2_state_machine_type;
|
||||
extern const mp_obj_type_t rp2_dma_type;
|
||||
|
||||
void rp2_pio_init(void);
|
||||
void rp2_pio_deinit(void);
|
||||
|
||||
void rp2_dma_init(void);
|
||||
void rp2_dma_deinit(void);
|
||||
|
||||
#endif // MICROPY_INCLUDED_RP2_MODRP2_H
|
||||
|
|
|
@ -151,14 +151,18 @@ int mp_hal_stdin_rx_chr(void) {
|
|||
}
|
||||
|
||||
// Send string of given length
|
||||
void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
|
||||
mp_uint_t mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
|
||||
mp_uint_t ret = len;
|
||||
bool did_write = false;
|
||||
#if MICROPY_HW_ENABLE_UART_REPL
|
||||
mp_uart_write_strn(str, len);
|
||||
did_write = true;
|
||||
#endif
|
||||
|
||||
#if MICROPY_HW_USB_CDC
|
||||
if (tud_cdc_connected()) {
|
||||
for (size_t i = 0; i < len;) {
|
||||
size_t i = 0;
|
||||
while (i < len) {
|
||||
uint32_t n = len - i;
|
||||
if (n > CFG_TUD_CDC_EP_BUFSIZE) {
|
||||
n = CFG_TUD_CDC_EP_BUFSIZE;
|
||||
|
@ -173,18 +177,26 @@ void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
|
|||
mp_usbd_task();
|
||||
}
|
||||
if (timeout >= MICROPY_HW_USB_CDC_TX_TIMEOUT) {
|
||||
ret = i;
|
||||
break;
|
||||
}
|
||||
uint32_t n2 = tud_cdc_write(str + i, n);
|
||||
tud_cdc_write_flush();
|
||||
i += n2;
|
||||
}
|
||||
ret = MIN(i, ret);
|
||||
did_write = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_OS_DUPTERM
|
||||
mp_os_dupterm_tx_strn(str, len);
|
||||
int dupterm_res = mp_os_dupterm_tx_strn(str, len);
|
||||
if (dupterm_res >= 0) {
|
||||
did_write = true;
|
||||
ret = MIN((mp_uint_t)dupterm_res, ret);
|
||||
}
|
||||
#endif
|
||||
return did_write ? ret : 0;
|
||||
}
|
||||
|
||||
void mp_hal_delay_ms(mp_uint_t ms) {
|
||||
|
|
|
@ -0,0 +1,469 @@
|
|||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Nicko van Someren
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "py/runtime.h"
|
||||
#include "py/mperrno.h"
|
||||
#include "py/objarray.h"
|
||||
#include "shared/runtime/mpirq.h"
|
||||
#include "modrp2.h"
|
||||
|
||||
#include "hardware/irq.h"
|
||||
#include "hardware/dma.h"
|
||||
|
||||
#define CHANNEL_CLOSED 0xff
|
||||
|
||||
typedef struct _rp2_dma_ctrl_obj_t {
|
||||
mp_obj_base_t base;
|
||||
uint32_t value;
|
||||
} rp2_dma_config_obj_t;
|
||||
|
||||
typedef struct _rp2_dma_obj_t {
|
||||
mp_obj_base_t base;
|
||||
uint8_t channel;
|
||||
uint8_t irq_flag : 1;
|
||||
uint8_t irq_trigger : 1;
|
||||
} rp2_dma_obj_t;
|
||||
|
||||
typedef struct _rp2_dma_ctrl_field_t {
|
||||
qstr name;
|
||||
uint8_t shift : 5;
|
||||
uint8_t length : 3;
|
||||
uint8_t read_only : 1;
|
||||
} rp2_dma_ctrl_field_t;
|
||||
|
||||
STATIC rp2_dma_ctrl_field_t rp2_dma_ctrl_fields_table[] = {
|
||||
{ MP_QSTR_enable, 0, 1, 0 },
|
||||
{ MP_QSTR_high_pri, 1, 1, 0 },
|
||||
{ MP_QSTR_size, 2, 2, 0 },
|
||||
{ MP_QSTR_inc_read, 4, 1, 0 },
|
||||
{ MP_QSTR_inc_write, 5, 1, 0 },
|
||||
{ MP_QSTR_ring_size, 6, 4, 0 },
|
||||
{ MP_QSTR_ring_sel, 10, 1, 0 },
|
||||
{ MP_QSTR_chain_to, 11, 4, 0 },
|
||||
{ MP_QSTR_treq_sel, 15, 6, 0 },
|
||||
{ MP_QSTR_irq_quiet, 21, 1, 0 },
|
||||
{ MP_QSTR_bswap, 22, 1, 0 },
|
||||
{ MP_QSTR_sniff_en, 23, 1, 0 },
|
||||
{ MP_QSTR_busy, 24, 1, 1 },
|
||||
// bits 25 through 28 are reserved
|
||||
{ MP_QSTR_write_err, 29, 1, 0 },
|
||||
{ MP_QSTR_read_err, 30, 1, 0 },
|
||||
{ MP_QSTR_ahb_err, 31, 1, 1 },
|
||||
};
|
||||
|
||||
STATIC const uint32_t rp2_dma_ctrl_field_count = MP_ARRAY_SIZE(rp2_dma_ctrl_fields_table);
|
||||
|
||||
#define REG_TYPE_COUNT 0 // Accept just integers
|
||||
#define REG_TYPE_CONF 1 // Accept integers or ctrl values
|
||||
#define REG_TYPE_ADDR_READ 2 // Accept integers, buffers or objects that can be read from
|
||||
#define REG_TYPE_ADDR_WRITE 3 // Accept integers, buffers or objects that can be written to
|
||||
|
||||
STATIC uint32_t rp2_dma_register_value_from_obj(mp_obj_t o, int reg_type) {
|
||||
if (reg_type == REG_TYPE_ADDR_READ || reg_type == REG_TYPE_ADDR_WRITE) {
|
||||
mp_buffer_info_t buf_info;
|
||||
mp_uint_t flags = MP_BUFFER_READ;
|
||||
if (mp_get_buffer(o, &buf_info, flags)) {
|
||||
return (uint32_t)buf_info.buf;
|
||||
}
|
||||
}
|
||||
|
||||
return mp_obj_get_int_truncated(o);
|
||||
}
|
||||
|
||||
STATIC void rp2_dma_irq_handler(void) {
|
||||
// Main IRQ handler
|
||||
uint32_t irq_bits = dma_hw->ints0;
|
||||
dma_hw->ints0 = 0xffff;
|
||||
|
||||
for (int i = 0; i < NUM_DMA_CHANNELS; i++) {
|
||||
if (irq_bits & (1u << i)) {
|
||||
mp_irq_obj_t *handler = MP_STATE_PORT(rp2_dma_irq_obj[i]);
|
||||
if (handler) {
|
||||
rp2_dma_obj_t *self = (rp2_dma_obj_t *)handler->parent;
|
||||
self->irq_flag = 1;
|
||||
mp_irq_handler(handler);
|
||||
} else {
|
||||
// We got an interrupt with no handler. Disable the channel
|
||||
dma_channel_set_irq0_enabled(i, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
STATIC mp_uint_t rp2_dma_irq_trigger(mp_obj_t self_in, mp_uint_t new_trigger) {
|
||||
rp2_dma_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
irq_set_enabled(DMA_IRQ_0, false);
|
||||
self->irq_flag = 0;
|
||||
dma_channel_set_irq0_enabled(self->channel, (new_trigger != 0));
|
||||
irq_set_enabled(DMA_IRQ_0, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
STATIC mp_uint_t rp2_dma_irq_info(mp_obj_t self_in, mp_uint_t info_type) {
|
||||
rp2_dma_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
if (info_type == MP_IRQ_INFO_FLAGS) {
|
||||
return self->irq_flag;
|
||||
} else if (info_type == MP_IRQ_INFO_TRIGGERS) {
|
||||
return (dma_hw->ints0 & (1u << self->channel)) != 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
STATIC const mp_irq_methods_t rp2_dma_irq_methods = {
|
||||
.trigger = rp2_dma_irq_trigger,
|
||||
.info = rp2_dma_irq_info,
|
||||
};
|
||||
|
||||
STATIC mp_obj_t rp2_dma_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||
mp_arg_check_num(n_args, n_kw, 0, 0, false);
|
||||
|
||||
int dma_channel = dma_claim_unused_channel(false);
|
||||
if (dma_channel < 0) {
|
||||
mp_raise_OSError(MP_EBUSY);
|
||||
}
|
||||
|
||||
rp2_dma_obj_t *self = m_new_obj_with_finaliser(rp2_dma_obj_t);
|
||||
self->base.type = &rp2_dma_type;
|
||||
self->channel = dma_channel;
|
||||
|
||||
// Return the DMA object.
|
||||
return MP_OBJ_FROM_PTR(self);
|
||||
}
|
||||
|
||||
STATIC void rp2_dma_error_if_closed(rp2_dma_obj_t *self) {
|
||||
if (self->channel == CHANNEL_CLOSED) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("channel closed"));
|
||||
}
|
||||
}
|
||||
|
||||
STATIC void rp2_dma_attr(mp_obj_t self_in, qstr attr_in, mp_obj_t *dest) {
|
||||
rp2_dma_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
|
||||
if (dest[0] == MP_OBJ_NULL) {
|
||||
// Load attribute
|
||||
dma_channel_hw_t *reg_block = dma_channel_hw_addr(self->channel);
|
||||
if (attr_in == MP_QSTR_read) {
|
||||
rp2_dma_error_if_closed(self);
|
||||
dest[0] = mp_obj_new_int_from_uint((mp_uint_t)reg_block->read_addr);
|
||||
} else if (attr_in == MP_QSTR_write) {
|
||||
rp2_dma_error_if_closed(self);
|
||||
dest[0] = mp_obj_new_int_from_uint((mp_uint_t)reg_block->write_addr);
|
||||
} else if (attr_in == MP_QSTR_count) {
|
||||
rp2_dma_error_if_closed(self);
|
||||
dest[0] = mp_obj_new_int_from_uint((mp_uint_t)reg_block->transfer_count);
|
||||
} else if (attr_in == MP_QSTR_ctrl) {
|
||||
rp2_dma_error_if_closed(self);
|
||||
dest[0] = mp_obj_new_int_from_uint((mp_uint_t)reg_block->al1_ctrl);
|
||||
} else if (attr_in == MP_QSTR_channel) {
|
||||
dest[0] = mp_obj_new_int_from_uint(self->channel);
|
||||
} else if (attr_in == MP_QSTR_registers) {
|
||||
mp_obj_array_t *reg_view = m_new_obj(mp_obj_array_t);
|
||||
mp_obj_memoryview_init(reg_view, 'I' | MP_OBJ_ARRAY_TYPECODE_FLAG_RW, 0, 16, dma_channel_hw_addr(self->channel));
|
||||
dest[0] = reg_view;
|
||||
} else {
|
||||
// Continue attribute search in locals dict.
|
||||
dest[1] = MP_OBJ_SENTINEL;
|
||||
}
|
||||
} else {
|
||||
// Set or delete attribute
|
||||
if (dest[1] == MP_OBJ_NULL) {
|
||||
// We don't support deleting attributes.
|
||||
return;
|
||||
}
|
||||
|
||||
rp2_dma_error_if_closed(self);
|
||||
|
||||
if (attr_in == MP_QSTR_read) {
|
||||
uint32_t value = rp2_dma_register_value_from_obj(dest[1], REG_TYPE_ADDR_READ);
|
||||
dma_channel_set_read_addr(self->channel, (volatile void *)value, false);
|
||||
dest[0] = MP_OBJ_NULL; // indicate success
|
||||
} else if (attr_in == MP_QSTR_write) {
|
||||
uint32_t value = rp2_dma_register_value_from_obj(dest[1], REG_TYPE_ADDR_WRITE);
|
||||
dma_channel_set_write_addr(self->channel, (volatile void *)value, false);
|
||||
dest[0] = MP_OBJ_NULL; // indicate success
|
||||
} else if (attr_in == MP_QSTR_count) {
|
||||
uint32_t value = rp2_dma_register_value_from_obj(dest[1], REG_TYPE_COUNT);
|
||||
dma_channel_set_trans_count(self->channel, value, false);
|
||||
dest[0] = MP_OBJ_NULL; // indicate success
|
||||
} else if (attr_in == MP_QSTR_ctrl) {
|
||||
uint32_t value = rp2_dma_register_value_from_obj(dest[1], REG_TYPE_CONF);
|
||||
dma_channel_set_config(self->channel, (dma_channel_config *)&value, false);
|
||||
dest[0] = MP_OBJ_NULL; // indicate success
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
STATIC void rp2_dma_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
||||
rp2_dma_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
mp_printf(print, "DMA(%u)", self->channel);
|
||||
}
|
||||
|
||||
// DMA.config(*, read, write, count, ctrl, trigger)
|
||||
STATIC mp_obj_t rp2_dma_config(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
rp2_dma_obj_t *self = MP_OBJ_TO_PTR(*pos_args);
|
||||
|
||||
rp2_dma_error_if_closed(self);
|
||||
|
||||
enum {
|
||||
ARG_read,
|
||||
ARG_write,
|
||||
ARG_count,
|
||||
ARG_ctrl,
|
||||
ARG_trigger,
|
||||
};
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_read, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
|
||||
{ MP_QSTR_write, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
|
||||
{ MP_QSTR_count, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
|
||||
{ MP_QSTR_ctrl, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
|
||||
{ MP_QSTR_trigger, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
// Don't include self in arg parsing
|
||||
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
// We only do anything if there was at least one argument
|
||||
if (kw_args->used) {
|
||||
bool trigger = args[ARG_trigger].u_bool;
|
||||
mp_int_t value_count = trigger ? kw_args->used - 1 : kw_args->used;
|
||||
if (trigger && (value_count == 0)) {
|
||||
// Only a "true" trigger was passed; just start a transfer
|
||||
dma_channel_start(self->channel);
|
||||
} else {
|
||||
if (args[ARG_read].u_obj != MP_OBJ_NULL) {
|
||||
uint32_t value = rp2_dma_register_value_from_obj(args[ARG_read].u_obj, REG_TYPE_ADDR_READ);
|
||||
value_count--;
|
||||
dma_channel_set_read_addr(self->channel, (volatile void *)value, trigger && (value_count == 0));
|
||||
}
|
||||
if (args[ARG_write].u_obj != MP_OBJ_NULL) {
|
||||
uint32_t value = rp2_dma_register_value_from_obj(args[ARG_write].u_obj, REG_TYPE_ADDR_WRITE);
|
||||
value_count--;
|
||||
dma_channel_set_write_addr(self->channel, (volatile void *)value, trigger && (value_count == 0));
|
||||
}
|
||||
if (args[ARG_count].u_obj != MP_OBJ_NULL) {
|
||||
uint32_t value = rp2_dma_register_value_from_obj(args[ARG_count].u_obj, REG_TYPE_COUNT);
|
||||
value_count--;
|
||||
dma_channel_set_trans_count(self->channel, value, trigger && (value_count == 0));
|
||||
}
|
||||
if (args[ARG_ctrl].u_obj != MP_OBJ_NULL) {
|
||||
uint32_t value = rp2_dma_register_value_from_obj(args[ARG_ctrl].u_obj, REG_TYPE_CONF);
|
||||
value_count--;
|
||||
dma_channel_set_config(self->channel, (dma_channel_config *)&value, trigger && (value_count == 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(rp2_dma_config_obj, 1, rp2_dma_config);
|
||||
|
||||
// DMA.active([value])
|
||||
STATIC mp_obj_t rp2_dma_active(size_t n_args, const mp_obj_t *args) {
|
||||
rp2_dma_obj_t *self = MP_OBJ_TO_PTR(args[0]);
|
||||
rp2_dma_error_if_closed(self);
|
||||
|
||||
if (n_args > 1) {
|
||||
if (mp_obj_is_true(args[1])) {
|
||||
dma_channel_start(self->channel);
|
||||
} else {
|
||||
dma_channel_abort(self->channel);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t busy = dma_channel_is_busy(self->channel);
|
||||
return mp_obj_new_bool((mp_int_t)busy);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(rp2_dma_active_obj, 1, 2, rp2_dma_active);
|
||||
|
||||
// Default is quiet, unpaced, read and write incrementing, word transfers, enabled
|
||||
#define DEFAULT_DMA_CONFIG (1 << 21) | (0x3f << 15) | (1 << 5) | (1 << 4) | (2 << 2) | (1 << 0)
|
||||
|
||||
// DMA.pack_ctrl(...)
|
||||
STATIC mp_obj_t rp2_dma_pack_ctrl(size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
// Pack keyword settings into a control register value, using either the default for this
|
||||
// DMA channel or the provided defaults
|
||||
rp2_dma_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
|
||||
mp_uint_t value = DEFAULT_DMA_CONFIG | ((self->channel & 0xf) << 11);
|
||||
|
||||
if (n_pos_args > 1) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("pack_ctrl only takes keyword arguments"));
|
||||
}
|
||||
mp_uint_t remaining = kw_args->used;
|
||||
|
||||
mp_map_elem_t *default_entry = mp_map_lookup(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_default), MP_MAP_LOOKUP);
|
||||
if (default_entry) {
|
||||
remaining--;
|
||||
value = mp_obj_get_int_truncated(default_entry->value);
|
||||
}
|
||||
|
||||
for (mp_uint_t i = 0; i < rp2_dma_ctrl_field_count; i++) {
|
||||
mp_map_elem_t *field_entry = mp_map_lookup(
|
||||
kw_args,
|
||||
MP_OBJ_NEW_QSTR(rp2_dma_ctrl_fields_table[i].name),
|
||||
MP_MAP_LOOKUP
|
||||
);
|
||||
if (field_entry) {
|
||||
remaining--;
|
||||
// Silently ignore read-only fields, to allow the passing of a modified unpack_ctrl() results
|
||||
if (!rp2_dma_ctrl_fields_table[i].read_only) {
|
||||
mp_uint_t field_value = mp_obj_get_int_truncated(field_entry->value);
|
||||
mp_uint_t mask = ((1 << rp2_dma_ctrl_fields_table[i].length) - 1);
|
||||
mp_uint_t masked_value = field_value & mask;
|
||||
if (field_value != masked_value) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("bad field value"));
|
||||
}
|
||||
value &= ~(mask << rp2_dma_ctrl_fields_table[i].shift);
|
||||
value |= masked_value << rp2_dma_ctrl_fields_table[i].shift;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (remaining) {
|
||||
mp_raise_TypeError(NULL);
|
||||
}
|
||||
|
||||
return mp_obj_new_int_from_uint(value);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(rp2_dma_pack_ctrl_obj, 1, rp2_dma_pack_ctrl);
|
||||
|
||||
// DMA.unpack_ctrl(value)
|
||||
STATIC mp_obj_t rp2_dma_unpack_ctrl(mp_obj_t value_obj) {
|
||||
// Return a dict representing the unpacked fields of a control register value
|
||||
mp_obj_t result_dict[rp2_dma_ctrl_field_count * 2];
|
||||
|
||||
mp_uint_t value = mp_obj_get_int_truncated(value_obj);
|
||||
|
||||
for (mp_uint_t i = 0; i < rp2_dma_ctrl_field_count; i++) {
|
||||
result_dict[i * 2] = MP_OBJ_NEW_QSTR(rp2_dma_ctrl_fields_table[i].name);
|
||||
mp_uint_t field_value =
|
||||
(value >> rp2_dma_ctrl_fields_table[i].shift) & ((1 << rp2_dma_ctrl_fields_table[i].length) - 1);
|
||||
result_dict[i * 2 + 1] = MP_OBJ_NEW_SMALL_INT(field_value);
|
||||
}
|
||||
|
||||
return mp_obj_dict_make_new(&mp_type_dict, 0, rp2_dma_ctrl_field_count, result_dict);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(rp2_dma_unpack_ctrl_fun_obj, rp2_dma_unpack_ctrl);
|
||||
STATIC MP_DEFINE_CONST_STATICMETHOD_OBJ(rp2_dma_unpack_ctrl_obj, MP_ROM_PTR(&rp2_dma_unpack_ctrl_fun_obj));
|
||||
|
||||
STATIC mp_obj_t rp2_dma_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
enum { ARG_handler, ARG_hard };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_handler, MP_ARG_OBJ, {.u_rom_obj = mp_const_none} },
|
||||
{ MP_QSTR_hard, MP_ARG_BOOL, {.u_bool = false} },
|
||||
};
|
||||
|
||||
// Parse the arguments.
|
||||
rp2_dma_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
// Get the IRQ object.
|
||||
mp_irq_obj_t *irq = MP_STATE_PORT(rp2_dma_irq_obj[self->channel]);
|
||||
|
||||
// Allocate the IRQ object if it doesn't already exist.
|
||||
if (irq == NULL) {
|
||||
irq = mp_irq_new(&rp2_dma_irq_methods, MP_OBJ_FROM_PTR(self));
|
||||
MP_STATE_PORT(rp2_dma_irq_obj[self->channel]) = irq;
|
||||
}
|
||||
|
||||
if (n_args > 1 || kw_args->used != 0) {
|
||||
// Disable all IRQs while data is updated.
|
||||
irq_set_enabled(DMA_IRQ_0, false);
|
||||
|
||||
// Update IRQ data.
|
||||
irq->handler = args[ARG_handler].u_obj;
|
||||
irq->ishard = args[ARG_hard].u_bool;
|
||||
self->irq_flag = 0;
|
||||
|
||||
// Enable IRQ if a handler is given.
|
||||
bool enable = (args[ARG_handler].u_obj != mp_const_none);
|
||||
dma_channel_set_irq0_enabled(self->channel, enable);
|
||||
|
||||
irq_set_enabled(DMA_IRQ_0, true);
|
||||
}
|
||||
|
||||
return MP_OBJ_FROM_PTR(irq);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(rp2_dma_irq_obj, 1, rp2_dma_irq);
|
||||
|
||||
// DMA.close()
|
||||
STATIC mp_obj_t rp2_dma_close(mp_obj_t self_in) {
|
||||
rp2_dma_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
uint8_t channel = self->channel;
|
||||
|
||||
if (channel != CHANNEL_CLOSED) {
|
||||
// Clean up interrupt handler to ensure garbage collection
|
||||
mp_irq_obj_t *irq = MP_STATE_PORT(rp2_dma_irq_obj[channel]);
|
||||
MP_STATE_PORT(rp2_dma_irq_obj[channel]) = MP_OBJ_NULL;
|
||||
if (irq) {
|
||||
irq->parent = MP_OBJ_NULL;
|
||||
irq->handler = MP_OBJ_NULL;
|
||||
dma_channel_set_irq0_enabled(channel, false);
|
||||
}
|
||||
dma_channel_unclaim(channel);
|
||||
self->channel = CHANNEL_CLOSED;
|
||||
}
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(rp2_dma_close_obj, rp2_dma_close);
|
||||
|
||||
STATIC const mp_rom_map_elem_t rp2_dma_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&rp2_dma_config_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&rp2_dma_active_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_irq), MP_ROM_PTR(&rp2_dma_irq_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&rp2_dma_close_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&rp2_dma_close_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_pack_ctrl), MP_ROM_PTR(&rp2_dma_pack_ctrl_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_unpack_ctrl), MP_ROM_PTR(&rp2_dma_unpack_ctrl_obj) },
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(rp2_dma_locals_dict, rp2_dma_locals_dict_table);
|
||||
|
||||
MP_DEFINE_CONST_OBJ_TYPE(
|
||||
rp2_dma_type,
|
||||
MP_QSTR_DMA,
|
||||
MP_TYPE_FLAG_NONE,
|
||||
make_new, rp2_dma_make_new,
|
||||
print, rp2_dma_print,
|
||||
attr, rp2_dma_attr,
|
||||
locals_dict, &rp2_dma_locals_dict
|
||||
);
|
||||
|
||||
void rp2_dma_init(void) {
|
||||
// Set up interrupts.
|
||||
memset(MP_STATE_PORT(rp2_dma_irq_obj), 0, sizeof(MP_STATE_PORT(rp2_dma_irq_obj)));
|
||||
irq_set_exclusive_handler(DMA_IRQ_0, rp2_dma_irq_handler);
|
||||
}
|
||||
|
||||
void rp2_dma_deinit(void) {
|
||||
// Disable and clear interrupts.
|
||||
irq_set_mask_enabled(1u << DMA_IRQ_0, false);
|
||||
irq_remove_handler(DMA_IRQ_0, rp2_dma_irq_handler);
|
||||
}
|
||||
|
||||
MP_REGISTER_ROOT_POINTER(void *rp2_dma_irq_obj[NUM_DMA_CHANNELS]);
|
|
@ -200,9 +200,12 @@ int mp_hal_stdin_rx_chr(void) {
|
|||
}
|
||||
}
|
||||
|
||||
void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
|
||||
mp_uint_t mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
|
||||
mp_uint_t ret = len;
|
||||
bool did_write = false;
|
||||
if (tud_cdc_connected()) {
|
||||
for (size_t i = 0; i < len;) {
|
||||
size_t i = 0;
|
||||
while (i < len) {
|
||||
uint32_t n = len - i;
|
||||
if (n > CFG_TUD_CDC_EP_BUFSIZE) {
|
||||
n = CFG_TUD_CDC_EP_BUFSIZE;
|
||||
|
@ -213,14 +216,22 @@ void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
|
|||
MICROPY_EVENT_POLL_HOOK_WITH_USB;
|
||||
}
|
||||
if (timeout >= MICROPY_HW_USB_CDC_TX_TIMEOUT) {
|
||||
ret = i;
|
||||
break;
|
||||
}
|
||||
uint32_t n2 = tud_cdc_write(str + i, n);
|
||||
tud_cdc_write_flush();
|
||||
i += n2;
|
||||
}
|
||||
ret = MIN(i, ret);
|
||||
did_write = true;
|
||||
}
|
||||
#if MICROPY_PY_OS_DUPTERM
|
||||
mp_os_dupterm_tx_strn(str, len);
|
||||
int dupterm_res = mp_os_dupterm_tx_strn(str, len);
|
||||
if (dupterm_res >= 0) {
|
||||
did_write = true;
|
||||
ret = MIN((mp_uint_t)dupterm_res, ret);
|
||||
}
|
||||
#endif
|
||||
return did_write ? ret : 0;
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -57,14 +57,22 @@ MP_WEAK int mp_hal_stdin_rx_chr(void) {
|
|||
}
|
||||
}
|
||||
|
||||
MP_WEAK void mp_hal_stdout_tx_strn(const char *str, size_t len) {
|
||||
MP_WEAK mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) {
|
||||
mp_uint_t ret = len;
|
||||
bool did_write = false;
|
||||
if (MP_STATE_PORT(pyb_stdio_uart) != NULL) {
|
||||
uart_tx_strn(MP_STATE_PORT(pyb_stdio_uart), str, len);
|
||||
did_write = true;
|
||||
}
|
||||
#if 0 && defined(USE_HOST_MODE) && MICROPY_HW_HAS_LCD
|
||||
lcd_print_strn(str, len);
|
||||
#endif
|
||||
mp_os_dupterm_tx_strn(str, len);
|
||||
int dupterm_res = mp_os_dupterm_tx_strn(str, len);
|
||||
if (dupterm_res >= 0) {
|
||||
did_write = true;
|
||||
ret = MIN((mp_uint_t)dupterm_res, ret);
|
||||
}
|
||||
return did_write ? ret : 0;
|
||||
}
|
||||
|
||||
#if __CORTEX_M >= 0x03
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -184,10 +184,15 @@ main_term:;
|
|||
return c;
|
||||
}
|
||||
|
||||
void mp_hal_stdout_tx_strn(const char *str, size_t len) {
|
||||
mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) {
|
||||
ssize_t ret;
|
||||
MP_HAL_RETRY_SYSCALL(ret, write(STDOUT_FILENO, str, len), {});
|
||||
mp_os_dupterm_tx_strn(str, len);
|
||||
mp_uint_t written = ret < 0 ? 0 : ret;
|
||||
int dupterm_res = mp_os_dupterm_tx_strn(str, len);
|
||||
if (dupterm_res >= 0) {
|
||||
written = MIN((mp_uint_t)dupterm_res, written);
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
// cooked is same as uncooked because the terminal does some postprocessing
|
||||
|
|
|
@ -27,8 +27,9 @@
|
|||
#include "library.h"
|
||||
#include "mphalport.h"
|
||||
|
||||
void mp_hal_stdout_tx_strn(const char *str, size_t len) {
|
||||
mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) {
|
||||
mp_js_write(str, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
void mp_hal_delay_ms(mp_uint_t ms) {
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
#include "shared/runtime/interrupt_char.h"
|
||||
|
||||
#define mp_hal_stdin_rx_chr() (0)
|
||||
void mp_hal_stdout_tx_strn(const char *str, size_t len);
|
||||
mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len);
|
||||
|
||||
void mp_hal_delay_ms(mp_uint_t ms);
|
||||
void mp_hal_delay_us(mp_uint_t us);
|
||||
|
|
|
@ -221,10 +221,11 @@ int mp_hal_stdin_rx_chr(void) {
|
|||
}
|
||||
}
|
||||
|
||||
void mp_hal_stdout_tx_strn(const char *str, size_t len) {
|
||||
mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) {
|
||||
MP_THREAD_GIL_EXIT();
|
||||
write(STDOUT_FILENO, str, len);
|
||||
int ret = write(STDOUT_FILENO, str, len);
|
||||
MP_THREAD_GIL_ENTER();
|
||||
return ret < 0 ? 0 : ret; // return the number of bytes written, so in case of an error in the syscall, return 0
|
||||
}
|
||||
|
||||
void mp_hal_stdout_tx_strn_cooked(const char *str, size_t len) {
|
||||
|
|
|
@ -44,7 +44,8 @@ int mp_hal_stdin_rx_chr(void) {
|
|||
}
|
||||
|
||||
// Send string of given length
|
||||
void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
|
||||
mp_uint_t mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
|
||||
mp_uint_t ret = len;
|
||||
#ifdef CONFIG_CONSOLE_SUBSYS
|
||||
while (len--) {
|
||||
char c = *str++;
|
||||
|
@ -60,4 +61,5 @@ void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
|
|||
uart_poll_out(uart_console_dev, *str++);
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ $(BUILD)/%.o: %.s
|
|||
|
||||
define compile_c
|
||||
$(ECHO) "CC $<"
|
||||
$(Q)$(CC) $(CFLAGS) -c -MD -o $@ $< || (echo -e $(HELP_BUILD_ERROR); false)
|
||||
$(Q)$(CC) $(CFLAGS) -c -MD -MF $(@:.o=.d) -o $@ $< || (echo -e $(HELP_BUILD_ERROR); false)
|
||||
@# The following fixes the dependency file.
|
||||
@# See http://make.paulandlesley.org/autodep.html for details.
|
||||
@# Regex adjusted from the above to play better with Windows paths, etc.
|
||||
|
@ -75,7 +75,7 @@ endef
|
|||
|
||||
define compile_cxx
|
||||
$(ECHO) "CXX $<"
|
||||
$(Q)$(CXX) $(CXXFLAGS) -c -MD -o $@ $< || (echo -e $(HELP_BUILD_ERROR); false)
|
||||
$(Q)$(CXX) $(CXXFLAGS) -c -MD -MF $(@:.o=.d) -o $@ $< || (echo -e $(HELP_BUILD_ERROR); false)
|
||||
@# The following fixes the dependency file.
|
||||
@# See http://make.paulandlesley.org/autodep.html for details.
|
||||
@# Regex adjusted from the above to play better with Windows paths, etc.
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
#define MICROPY_VERSION_MAJOR 1
|
||||
#define MICROPY_VERSION_MINOR 22
|
||||
#define MICROPY_VERSION_MICRO 0
|
||||
#define MICROPY_VERSION_PRERELEASE 1
|
||||
#define MICROPY_VERSION_PRERELEASE 0
|
||||
|
||||
// Combined version as a 32-bit number for convenience to allow version
|
||||
// comparison. Doesn't include prerelease state.
|
||||
|
|
|
@ -56,7 +56,7 @@ void mp_hal_stdout_tx_str(const char *str);
|
|||
#endif
|
||||
|
||||
#ifndef mp_hal_stdout_tx_strn
|
||||
void mp_hal_stdout_tx_strn(const char *str, size_t len);
|
||||
mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len);
|
||||
#endif
|
||||
|
||||
#ifndef mp_hal_stdout_tx_strn_cooked
|
||||
|
|
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);
|
||||
|
||||
|
|
|
@ -142,8 +142,7 @@ STATIC mp_uint_t stdio_buffer_read(mp_obj_t self_in, void *buf, mp_uint_t size,
|
|||
}
|
||||
|
||||
STATIC mp_uint_t stdio_buffer_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) {
|
||||
mp_hal_stdout_tx_strn(buf, size);
|
||||
return size;
|
||||
return mp_hal_stdout_tx_strn(buf, size);
|
||||
}
|
||||
|
||||
STATIC const mp_stream_p_t stdio_buffer_obj_stream_p = {
|
||||
|
|
Loading…
Reference in New Issue