Compare commits

...

18 Commits

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

Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2024-01-15 16:08:30 +02:00
iabdalkader 654aed0452 extmod/modopenamp.c: Add OpenAMP extmod module.
This module implements OpenAMP's basic initialization and shared resources
support, and provides support for OpenAMP's RPMsg component, by providing
an `endpoint` type (a logical connection on top of RPMsg channel) which can
be used to communicate with the remote core.

Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2024-01-15 16:08:20 +02:00
iabdalkader 6a54606a9f ports/stm32: Add libmetal port.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2023-12-29 09:19:56 +01:00
iabdalkader 3019acb736 ports/stm32: Add MPU config for shared memory region.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2023-12-29 09:19:56 +01:00
iabdalkader 5d7f64ffaf py/stream: Add stream seek helper function.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2023-12-29 09:19:56 +01:00
iabdalkader 013d1e532f lib/open-amp: Add OpenAMP submodule.
OpenAMP framework provides a standard inter processor communications
infrastructure for RTOS and bare metal environments. There are 3 major
components in OpenAMP: libmetal, remoteproc and RPMsg. libmetal provides
abstraction of the low-level underlying hardware, remoteproc is used for
processor Life Cycle Management (LCM) like loading firmware, starting,
stopping a core etc.. and RPMsg is a bus infrastructure that enables
Inter Processor Communications (IPC) between different cores.

Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2023-12-29 09:19:56 +01:00
iabdalkader dd296195b7 lib/libmetal: Add libmetal submodule.
libmetal provides an abstraction of the underlying hardware,
to support other OpenAMP components. Note a generic system
for MicroPython is included in this fork, that uses common
mp_hal functions, and provides a way for MicroPython's ports
to override defaults and configure libmetal.

Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2023-12-29 09:19:56 +01:00
Damien George 9feb0689ee all: Bump version to 1.22.0.
Signed-off-by: Damien George <damien@micropython.org>
2023-12-27 15:35:31 +11:00
Daniël van de Giessen d014c82826 extmod/nimble: Do not set GAP device name after sync.
Instead, configure the default once at compile-time. This means the GAP
name will no longer be set to default after re-initializing Bluetooth.

Signed-off-by: Daniël van de Giessen <daniel@dvdgiessen.nl>
2023-12-22 16:07:02 +11:00
Damien George 0b2676db5c lib/micropython-lib: Update submodule to latest.
Signed-off-by: Damien George <damien@micropython.org>
2023-12-22 14:18:09 +11:00
Nicko van Someren cfc212b108 rp2/rp2_dma: Introduce a new rp2.DMA class for control over DMA xfers.
This commit implements fairly complete support for the DMA controller in
the rp2 series of microcontrollers.  It provides a class for accessing the
DMA channels through a high-level, Pythonic interface, and functions for
setting and manipulating the DMA channel configurations.

Creating an instance of the rp2.DMA class claims one of the processor's DMA
channels.  A sensible, per-channel default value for the ctrl register can
be fetched from the DMA.pack_ctrl() function, and the components of this
register can be set via keyword arguments to pack_ctrl().

The read, write, count and ctrl attributes of the DMA class provide
read/write access to the respective registers of the DMA controller.  The
config() method allows any or all of these values to be set simultaneously
and adds a trigger keyword argument to allow the setup to immediately be
triggered.  The read and write attributes (or keywords in config()) accept
either actual addresses or any object that supports the buffer interface.
The active() method provides read/write control of the channel's activity,
allowing the user to start and stop the channel and test if it is running.

Standard MicroPython interrupt handlers are supported through the irq()
method and the channel can be released either by deleting it and allowing
it to be garbage-collected or with the explicit close() method.

Direct, unfettered access to the DMA controllers registers is provided
through a proxy memoryview() object returned by the DMA.registers attribute
that maps directly onto the memory-mapped registers.  This is necessary for
more fine-grained control and is helpful for allowing chaining of DMA
channels.

As a simple example, using DMA to do a fast memory copy just needs:

    src = bytearray(32*1024)
    dest = bytearray(32*1024)
    dma = rp2.DMA()
    dma.config(read=src, write=dest, count=len(src) // 4,
        ctrl=dma.pack_ctrl(), trigger=True)

    # Wait for completion
    while dma.active():
        pass

This API aims to strike a balance between simplicity and comprehensiveness.

Signed-off-by: Nicko van Someren <nicko@nicko.org>
Signed-off-by: Damien George <damien@micropython.org>
2023-12-22 13:04:51 +11:00
Sebastian Romero e4d3ab3304 nrf/main: Add /flash and /flash/lib to sys.path.
This allows to follow good practice and have libraries live in the lib
folder which means they will be found by the runtime without adding this
path manually at runtime.

Signed-off-by: Sebastian Romero <s.romero@arduino.cc>
2023-12-22 11:15:19 +11:00
Peter Züger d69e69adb6 py/mkrules.mk: Fix dependency file generation for compiler wrappers.
When compiling with distcc, it does not understand the -MD flag on its own.
This fixes the interaction by explicitly adding the -MF option.

The error in distcc is described here under "Problems with gcc -MD":
https://www.distcc.org/faq.html

Signed-off-by: Peter Züger <zueger.peter@icloud.com>
2023-12-22 11:07:59 +11:00
Peter Züger ce42c9ee16 extmod/vfs_lfs: Fix lfs cache_size calculation.
The calculation of the lfs2 cache_size was incorrect, the maximum allowed
size is block_size.

The cache size must be: "a multiple of the read and program sizes, and a
factor of the block size".

Signed-off-by: Peter Züger <zueger.peter@icloud.com>
2023-12-22 11:03:23 +11:00
Maarten van der Schrieck 3bca93b2d0 ports: Fix sys.stdout.buffer.write() return value.
MicroPython code may rely on the return value of sys.stdout.buffer.write()
to reflect the number of bytes actually written. While in most scenarios a
write() operation is successful, there are cases where it fails, leading to
data loss. This problem arises because, currently, write() merely returns
the number of bytes it was supposed to write, without indication of
failure.

One scenario where write() might fail, is where USB is used and the
receiving end doesn't read quickly enough to empty the receive buffer. In
that case, write() on the MicroPython side can timeout, resulting in the
loss of data without any indication, a behavior observed notably in
communication between a Pi Pico as a client and a Linux host using the ACM
driver.

A complex issue arises with mp_hal_stdout_tx_strn() when it involves
multiple outputs, such as USB, dupterm and hardware UART. The challenge is
in handling cases where writing to one output is successful, but another
fails, either fully or partially. This patch implements the following
solution:

mp_hal_stdout_tx_strn() attempts to write len bytes to all of the possible
destinations for that data, and returns the minimum successful write
length.

The implementation of this is complicated by several factors:
- multiple outputs may be enabled or disabled at compiled time
- multiple outputs may be enabled or disabled at runtime
- mp_os_dupterm_tx_strn() is one such output, optionally containing
  multiple additional outputs
- each of these outputs may or may not be able to report success
- each of these outputs may or may not be able to report partial writes

As a result, there's no single strategy that fits all ports, necessitating
unique logic for each instance of mp_hal_stdout_tx_strn().

Note that addressing sys.stdout.write() is more complex due to its data
modification process ("cooked" output), and it remains unchanged in this
patch. Developers who are concerned about accurate return values from
write operations should use sys.stdout.buffer.write().

This patch might disrupt some existing code, but it's also expected to
resolve issues, considering that the peculiar return value behavior of
sys.stdout.buffer.write() is not well-documented and likely not widely
known. Therefore, it's improbable that much existing code relies on the
previous behavior.

Signed-off-by: Maarten van der Schrieck <maarten@thingsconnected.nl>
2023-12-22 10:32:46 +11:00
Maarten van der Schrieck 91ee8ac894 extmod/os_dupterm: Let mp_os_dupterm_tx_strn() return num bytes written.
In case of multiple outputs, the minimum successful write length is
returned.  In line with this, in case any output has a write error, zero is
returned.

In case of no outputs, -1 is returned.

The return value can be used to assess whether writes were attempted, and
if so, whether they succeeded.

Signed-off-by: Maarten van der Schrieck <maarten@thingsconnected.nl>
2023-12-22 10:26:52 +11:00
56 changed files with 1867 additions and 62 deletions

6
.gitmodules vendored
View File

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

View File

@ -30,6 +30,7 @@ SRC_EXTMOD_C += \
extmod/modmachine.c \
extmod/modnetwork.c \
extmod/modonewire.c \
extmod/modopenamp.c \
extmod/modos.c \
extmod/modplatform.c\
extmod/modrandom.c \
@ -42,6 +43,7 @@ SRC_EXTMOD_C += \
extmod/moductypes.c \
extmod/modwebrepl.c \
extmod/modwebsocket.c \
extmod/modrproc.c \
extmod/network_cyw43.c \
extmod/network_esp_hosted.c \
extmod/network_lwip.c \
@ -509,3 +511,69 @@ include $(TOP)/extmod/btstack/btstack.mk
endif
endif
################################################################################
# openamp
ifeq ($(MICROPY_PY_OPENAMP),1)
OPENAMP_DIR = lib/open-amp
LIBMETAL_DIR = lib/libmetal
GIT_SUBMODULES += $(LIBMETAL_DIR) $(OPENAMP_DIR)
INC += -I$(TOP)/$(OPENAMP_DIR)
CFLAGS += -DMICROPY_PY_OPENAMP=1
ifeq ($(MICROPY_PY_REMOTEPROC),1)
CFLAGS += -DMICROPY_PY_REMOTEPROC=1
endif
CFLAGS_THIRDPARTY += \
-I$(TOP)/$(LIBMETAL_DIR) \
-I$(TOP)/$(OPENAMP_DIR) \
-I$(TOP)/$(OPENAMP_DIR)/lib/include/ \
-DMETAL_INTERNAL \
-DVIRTIO_DRIVER_ONLY \
-DNO_ATOMIC_64_SUPPORT \
-DRPMSG_BUFFER_SIZE=512 \
# Libmetal's source files.
SRC_THIRDPARTY_C += $(addprefix $(LIBMETAL_DIR)/metal/,\
device.c \
dma.c \
init.c \
io.c \
irq.c \
log.c \
shmem.c \
softirq.c \
version.c \
device.c \
system/micropython/condition.c \
system/micropython/device.c \
system/micropython/io.c \
system/micropython/irq.c \
system/micropython/shmem.c \
system/micropython/time.c \
)
# OpenAMP's source files.
SRC_THIRDPARTY_C += $(addprefix $(OPENAMP_DIR)/lib/,\
rpmsg/rpmsg.c \
rpmsg/rpmsg_virtio.c \
virtio/virtio.c \
virtio/virtqueue.c \
virtio_mmio/virtio_mmio_drv.c \
)
# OpenAMP's remoteproc source files.
ifeq ($(MICROPY_PY_REMOTEPROC),1)
SRC_THIRDPARTY_C += $(addprefix $(OPENAMP_DIR)/lib/remoteproc/,\
elf_loader.c \
remoteproc.c \
remoteproc_virtio.c \
rsc_table_parser.c \
)
endif # MICROPY_PY_REMOTEPROC
endif # MICROPY_PY_OPENAMP

View File

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

392
extmod/modopenamp.c Normal file
View File

@ -0,0 +1,392 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2023 Arduino SA
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* OpenAMP's Python module.
*/
#if MICROPY_PY_OPENAMP
#include "py/obj.h"
#include "py/nlr.h"
#include "py/runtime.h"
#include "metal/alloc.h"
#include "metal/errno.h"
#include "metal/io.h"
#include "metal/sys.h"
#include "metal/device.h"
#include "metal/utilities.h"
#include "metal/metal_port.h"
#include "openamp/open_amp.h"
#include "openamp/remoteproc.h"
#include "openamp/remoteproc_loader.h"
#include "modopenamp.h"
#if !MICROPY_PY_OPENAMP_RESOURCE_TABLE
#include "openamp_config.h"
#endif
#if MICROPY_PY_OPENAMP_RESOURCE_TABLE
#define VIRTIO_DEV_ID 0xFF
#define VIRTIO_DEV_FEATURES (1 << VIRTIO_RPMSG_F_NS)
#define VRING0_ID 0 // VRING0 ID (master to remote) fixed to 0 for linux compatibility
#define VRING1_ID 1 // VRING1 ID (remote to master) fixed to 1 for linux compatibility
#define VRING_NOTIFY_ID VRING0_ID
#define VRING_COUNT 2
#define VRING_ALIGNMENT 32
// Note the number of buffers must be a power of 2
#define VRING_NUM_BUFFS 64
// The following config should be enough for about 128 descriptors.
// See lib/include/openamp/virtio_ring.h for the layout of vrings
// and vring_size() to calculate the vring size.
#define VRING_RX_ADDR (METAL_SHM_ADDR)
#define VRING_TX_ADDR (METAL_SHM_ADDR + 0x1000)
#define VRING_BUFF_ADDR (METAL_SHM_ADDR + 0x2000)
#define VRING_BUFF_SIZE (METAL_SHM_SIZE - 0x2000)
static const char openamp_trace_buf[128];
#define MICROPY_PY_OPENAMP_TRACE_BUF ((uint32_t)openamp_trace_buf)
#define MICROPY_PY_OPENAMP_TRACE_BUF_LEN sizeof(MICROPY_PY_OPENAMP_TRACE_BUF)
#endif // MICROPY_PY_OPENAMP_RESOURCE_TABLE
#define debug_printf(...) // mp_printf(&mp_plat_print, __VA_ARGS__)
#if MICROPY_PY_REMOTEPROC
extern mp_obj_type_t rproc_type;
#endif
static struct metal_device shm_device = {
.name = METAL_SHM_NAME,
// The number of IO regions is fixed and must match the number and
// layout of the remote processor's IO regions. The first region is
// used for the vring shared memory, and the second one is used for
// the shared resource table.
.num_regions = METAL_MAX_DEVICE_REGIONS,
.regions = { { 0 } },
.node = { NULL },
.irq_num = 0,
.irq_info = NULL
};
static metal_phys_addr_t shm_physmap[] = { 0 };
// ###################### Virtio device class ######################
typedef struct _virtio_dev_obj_t {
mp_obj_base_t base;
struct rpmsg_virtio_device rvdev;
struct rpmsg_virtio_shm_pool shm_pool;
mp_obj_t ns_callback;
} virtio_dev_obj_t;
STATIC mp_obj_t virtio_dev_deinit(mp_obj_t self_in) {
virtio_dev_obj_t *self = MP_OBJ_TO_PTR(self_in);
rpmsg_deinit_vdev(&self->rvdev);
metal_finish();
MP_STATE_PORT(virtio_device) = NULL;
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(virtio_dev_deinit_obj, virtio_dev_deinit);
STATIC const mp_rom_map_elem_t virtio_dev_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_virtio_dev) },
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&virtio_dev_deinit_obj) },
};
STATIC MP_DEFINE_CONST_DICT(virtio_dev_locals_dict, virtio_dev_locals_dict_table);
MP_DEFINE_CONST_OBJ_TYPE(
virtio_dev_type,
MP_QSTR_virtio_dev,
MP_TYPE_FLAG_NONE,
locals_dict, &virtio_dev_locals_dict
);
// ###################### RPMsg Endpoint class ######################
typedef struct _rpmsg_obj_t {
mp_obj_base_t base;
mp_obj_t name;
uint32_t src_addr;
uint32_t dst_addr;
mp_obj_t callback;
struct rpmsg_endpoint ep;
} rpmsg_obj_t;
const mp_obj_type_t rpmsg_type;
static int rpmsg_recv_callback(struct rpmsg_endpoint *ept, void *data, size_t len, uint32_t src, void *priv) {
debug_printf("rpmsg_recv_callback() message received src_addr: %lu msg len: %d\n", src, len);
rpmsg_obj_t *self = metal_container_of(ept, rpmsg_obj_t, ep);
mp_call_function_2(self->callback, mp_obj_new_int(src), mp_obj_new_bytearray_by_ref(len, data));
return 0;
}
STATIC mp_obj_t _rpmsg_send(uint n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_src_addr, ARG_dst_addr, ARG_timeout };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_src_addr, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = -1 } },
{ MP_QSTR_dst_addr, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = -1 } },
{ MP_QSTR_timeout, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0 } },
};
// Parse args.
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args - 2, pos_args + 2, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
rpmsg_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
if (is_rpmsg_ept_ready(&self->ep) == false) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Endpoint not ready"));
}
uint32_t src_addr = self->src_addr;
if (args[ARG_src_addr].u_int != -1) {
src_addr = self->src_addr;
}
uint32_t dst_addr = self->dst_addr;
if (args[ARG_dst_addr].u_int != -1) {
dst_addr = self->dst_addr;
}
mp_buffer_info_t rbuf;
mp_get_buffer_raise(pos_args[1], &rbuf, MP_BUFFER_READ);
debug_printf("rpmsg_send() msg len: %d\n", rbuf.len);
int bytes = 0;
uint32_t timeout = args[ARG_timeout].u_int;
for (mp_uint_t start = mp_hal_ticks_ms(); ; mp_hal_delay_ms(10)) {
bytes = rpmsg_send_offchannel_raw(&self->ep, src_addr, dst_addr, rbuf.buf, rbuf.len, false);
if (bytes > 0) {
break;
}
if (timeout > 0 && (mp_hal_ticks_ms() - start > timeout)) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("timeout waiting for a free buffer"));
}
MICROPY_EVENT_POLL_HOOK
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(rpmsg_send_obj, 2, _rpmsg_send);
STATIC mp_obj_t rpmsg_deinit(mp_obj_t self_in) {
rpmsg_obj_t *self = MP_OBJ_TO_PTR(self_in);
rpmsg_destroy_ept(&self->ep);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(rpmsg_deinit_obj, rpmsg_deinit);
STATIC mp_obj_t rpmsg_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
enum { ARG_name, ARG_src_addr, ARG_dst_addr, ARG_callback };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_name, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_src_addr, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = RPMSG_ADDR_ANY } },
{ MP_QSTR_dst_addr, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = RPMSG_ADDR_ANY } },
{ MP_QSTR_callback, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
};
// Parse args.
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
rpmsg_obj_t *self = m_new_obj_with_finaliser(rpmsg_obj_t);
self->base.type = &rpmsg_type;
self->name = args[ARG_name].u_obj;
self->src_addr = args[ARG_src_addr].u_int;
self->dst_addr = args[ARG_dst_addr].u_int;
self->callback = args[ARG_callback].u_obj;
if (rpmsg_create_ept(&self->ep, &MP_STATE_PORT(virtio_device)->rvdev.rdev, mp_obj_str_get_str(self->name),
self->src_addr, self->dst_addr, rpmsg_recv_callback, NULL) != 0) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to create RPMsg endpoint"));
}
// If the source address was set to any, the endpoint is assigned a new address in create_ept.
self->src_addr = self->ep.addr;
return MP_OBJ_FROM_PTR(self);
}
STATIC const mp_rom_map_elem_t rpmsg_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_rpmsg) },
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&rpmsg_deinit_obj) },
{ MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&rpmsg_send_obj) },
};
STATIC MP_DEFINE_CONST_DICT(rpmsg_locals_dict, rpmsg_locals_dict_table);
MP_DEFINE_CONST_OBJ_TYPE(
rpmsg_type,
MP_QSTR_rpmsg,
MP_TYPE_FLAG_NONE,
make_new, rpmsg_make_new,
locals_dict, &rpmsg_locals_dict
);
// ###################### openamp module ######################
void metal_rproc_notified(mp_sched_node_t *node) {
(void)node;
rproc_virtio_notified(MP_STATE_PORT(virtio_device)->rvdev.vdev, VRING_NOTIFY_ID);
}
static void rpmsg_ns_callback(struct rpmsg_device *rdev, const char *name, uint32_t dest) {
debug_printf("rpmsg_new_service_callback() new service request name: %s dest %lu\n", name, dest);
// The remote processor advertises its presence to the master by sending
// the Name Service (NS) announcement containing the name of the channel.
virtio_dev_obj_t *self = metal_container_of(rdev, virtio_dev_obj_t, rvdev);
mp_call_function_2(self->ns_callback, mp_obj_new_int(dest), mp_obj_new_str(name, strlen(name)));
}
#if MICROPY_PY_OPENAMP_RESOURCE_TABLE
// The shared resource table must be initialized manually by the host here,
// because it's not located in the data region, so the startup code doesn't
// know about it.
static void openamp_rsc_table_init(openamp_rsc_table_t **rsc_table_out) {
openamp_rsc_table_t *rsc_table = METAL_RSC_ADDR;
memset(rsc_table, 0, METAL_RSC_SIZE);
rsc_table->version = 1;
rsc_table->num = MP_ARRAY_SIZE(rsc_table->offset);
rsc_table->offset[0] = offsetof(openamp_rsc_table_t, vdev);
#if MICROPY_PY_OPENAMP_TRACE_BUF_ENABLE
rsc_table->offset[1] = offsetof(openamp_rsc_table_t, trace);
#endif
rsc_table->vdev = (struct fw_rsc_vdev) {
RSC_VDEV, VIRTIO_ID_RPMSG, 0, VIRTIO_DEV_FEATURES, 0, 0, 0, VRING_COUNT, {0, 0}
};
rsc_table->vring0 = (struct fw_rsc_vdev_vring) {
VRING_TX_ADDR, VRING_ALIGNMENT, VRING_NUM_BUFFS, VRING0_ID, 0
};
rsc_table->vring1 = (struct fw_rsc_vdev_vring) {
VRING_RX_ADDR, VRING_ALIGNMENT, VRING_NUM_BUFFS, VRING1_ID, 0
};
#if MICROPY_PY_OPENAMP_TRACE_BUF_ENABLE
rsc_table->trace = (struct fw_rsc_trace) {
RSC_TRACE, MICROPY_PY_OPENAMP_TRACE_BUF, MICROPY_PY_OPENAMP_TRACE_BUF_LEN, 0, "trace_buf"
};
#endif
#ifdef VIRTIO_USE_DCACHE
// Flush resource table.
metal_cache_flush((uint32_t *)rsc_table, sizeof(openamp_rsc_table_t));
#endif
*rsc_table_out = rsc_table;
}
#endif // MICROPY_PY_OPENAMP_RESOURCE_TABLE
STATIC mp_obj_t openamp_init(uint n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_ns_callback };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_ns_callback, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_NONE } },
};
// Parse args.
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
if (MP_STATE_PORT(virtio_device) != NULL) {
MP_STATE_PORT(virtio_device)->ns_callback = args[ARG_ns_callback].u_obj;
return mp_const_none;
}
struct metal_device *device;
struct metal_init_params metal_params = METAL_INIT_DEFAULTS;
// Initialize libmetal.
metal_init(&metal_params);
// Initialize the shared resource table.
openamp_rsc_table_t *rsc_table;
openamp_rsc_table_init(&rsc_table);
if (metal_register_generic_device(&shm_device) != 0) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to register metal device"));
}
if (metal_device_open("generic", METAL_SHM_NAME, &device) != 0) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to open metal device"));
}
// Initialize shared memory IO region.
metal_io_init(&device->regions[0], (void *)METAL_SHM_ADDR, (void *)shm_physmap, METAL_SHM_SIZE, -1U, 0, NULL);
struct metal_io_region *shm_io = metal_device_io_region(device, 0);
if (shm_io == NULL) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to initialize device io region"));
}
// Initialize resource table IO region.
metal_io_init(&device->regions[1], (void *)rsc_table, (void *)rsc_table, sizeof(*rsc_table), -1U, 0, NULL);
struct metal_io_region *rsc_io = metal_device_io_region(device, 1);
if (rsc_io == NULL) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to initialize device io region"));
}
// Create virtio device.
struct virtio_device *vdev = rproc_virtio_create_vdev(RPMSG_HOST, VIRTIO_DEV_ID,
&rsc_table->vdev, rsc_io, NULL, metal_rproc_notify, NULL);
if (vdev == NULL) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to create virtio device"));
}
// Initialize vrings.
struct fw_rsc_vdev_vring *vring_rsc = &rsc_table->vring0;
for (int i = 0; i < VRING_COUNT; i++, vring_rsc++) {
if (rproc_virtio_init_vring(vdev, vring_rsc->notifyid, vring_rsc->notifyid,
(void *)vring_rsc->da, shm_io, vring_rsc->num, vring_rsc->align) != 0) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to initialize vrings"));
}
}
virtio_dev_obj_t *self = m_new_obj_with_finaliser(virtio_dev_obj_t);
self->base.type = &virtio_dev_type;
self->ns_callback = args[ARG_ns_callback].u_obj;
// The remote processor detects that the virtio device is ready by polling
// the status field in the resource table.
rpmsg_virtio_init_shm_pool(&self->shm_pool, (void *)VRING_BUFF_ADDR, (size_t)VRING_BUFF_SIZE);
rpmsg_init_vdev(&self->rvdev, vdev, rpmsg_ns_callback, shm_io, &self->shm_pool);
MP_STATE_PORT(virtio_device) = self;
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(openamp_init_obj, 0, openamp_init);
STATIC const mp_rom_map_elem_t globals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_openamp) },
{ MP_ROM_QSTR(MP_QSTR_RPMSG_ADDR_ANY), MP_ROM_INT(RPMSG_ADDR_ANY) },
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&openamp_init_obj) },
{ MP_ROM_QSTR(MP_QSTR_RPMsg), MP_ROM_PTR(&rpmsg_type) },
#if MICROPY_PY_REMOTEPROC
{ MP_ROM_QSTR(MP_QSTR_RProc), MP_ROM_PTR(&rproc_type) },
#endif
};
STATIC MP_DEFINE_CONST_DICT(globals_dict, globals_dict_table);
const mp_obj_module_t openamp_module = {
.base = { &mp_type_module },
.globals = (mp_obj_t)&globals_dict,
};
MP_REGISTER_ROOT_POINTER(struct _virtio_dev_obj_t *virtio_device);
MP_REGISTER_MODULE(MP_QSTR_openamp, openamp_module);
#endif // MICROPY_PY_OPENAMP

71
extmod/modopenamp.h Normal file
View File

@ -0,0 +1,71 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2023 Arduino SA
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* OpenAMP's Python module.
*/
#ifndef MICROPY_INCLUDED_MODOPENAMP_H
#define MICROPY_INCLUDED_MODOPENAMP_H
// Use the default resource table layout and initialization code.
// Note ports and boards can override the default table and provide
// a custom one in openamp_rsc_table_t and openamp_rsc_table_init()
#ifndef MICROPY_PY_OPENAMP_RESOURCE_TABLE
#define MICROPY_PY_OPENAMP_RESOURCE_TABLE (1)
#endif
// This enables a trace buffer that can be used by remote processor for
// writing trace logs. This is enabled by default to increase the default
// resource table's compatibility with common OpenAMP examples.
#ifndef MICROPY_PY_OPENAMP_TRACE_BUF_ENABLE
#define MICROPY_PY_OPENAMP_TRACE_BUF_ENABLE (1)
#endif
// The resource table is used for sharing the configuration of the virtio
// device, vrings and other resources, between the host and remote cores.
// The layout and address the table structure must match the one used in
// the remote processor's firmware.
#if MICROPY_PY_OPENAMP_RESOURCE_TABLE
typedef struct openamp_rsc_table {
unsigned int version;
unsigned int num;
unsigned int reserved[2];
#if MICROPY_PY_OPENAMP_TRACE_BUF_ENABLE
unsigned int offset[2];
#else
unsigned int offset[1];
#endif
struct fw_rsc_vdev vdev;
struct fw_rsc_vdev_vring vring0;
struct fw_rsc_vdev_vring vring1;
#if MICROPY_PY_OPENAMP_TRACE_BUF_ENABLE
struct fw_rsc_trace trace;
#endif
} openamp_rsc_table_t;
#endif // MICROPY_PY_OPENAMP_RESOURCE_TABLE
// Ports should run this callback in scheduler context, when
// the remote processor notifies the host of pending messages.
void metal_rproc_notified(mp_sched_node_t *node);
#endif // MICROPY_INCLUDED_MODOPENAMP_H

218
extmod/modrproc.c Normal file
View File

@ -0,0 +1,218 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2023 Arduino SA
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* OpenAMP's remoteproc Python module.
*/
#if MICROPY_PY_REMOTEPROC
#include "py/obj.h"
#include "py/nlr.h"
#include "py/runtime.h"
#include "py/stream.h"
#include "extmod/vfs.h"
#include "metal/alloc.h"
#include "metal/errno.h"
#include "metal/io.h"
#include "openamp/remoteproc.h"
#include "openamp/remoteproc_loader.h"
#include "modrproc.h"
#define debug_printf(...) // mp_printf(&mp_plat_print, __VA_ARGS__)
typedef struct rproc_obj_t {
mp_obj_base_t base;
struct remoteproc rproc;
} rproc_obj_t;
const mp_obj_type_t rproc_type;
// Port-defined remoteproc operations.
extern struct remoteproc_ops mp_rproc_ops;
#if !MICROPY_PY_REMOTEPROC_FILE_STORE
// Port-defined image store operations.
extern struct image_store_ops mp_store_ops;
#else
// A generic file-based image store.
static int store_open(void *store, const char *path, const void **image_data) {
debug_printf("store_open(): %s\n", path);
mp_obj_t args[2] = {
mp_obj_new_str(path, strlen(path)),
MP_OBJ_NEW_QSTR(MP_QSTR_rb),
};
rproc_filestore_t *fstore = store;
fstore->file = mp_vfs_open(MP_ARRAY_SIZE(args), args, (mp_map_t *)&mp_const_empty_map);
int error = 0;
mp_uint_t bytes = mp_stream_read_exactly(fstore->file, fstore->buf, RPROC_FILE_STORE_BUF_SIZE, &error);
if (error != 0 || bytes != RPROC_FILE_STORE_BUF_SIZE) {
return -EINVAL;
}
*image_data = fstore->buf;
return bytes;
}
static void store_close(void *store) {
debug_printf("store_close()\n");
rproc_filestore_t *fstore = store;
mp_stream_close(fstore->file);
metal_free_memory(fstore->buf);
metal_free_memory(fstore);
}
static int store_load(void *store, size_t offset, size_t size,
const void **data, metal_phys_addr_t pa,
struct metal_io_region *io,
char is_blocking) {
int error = 0;
rproc_filestore_t *fstore = store;
if (mp_stream_seek(fstore->file, offset, MP_SEEK_SET, &error) == -1) {
return -EINVAL;
}
if (pa == METAL_BAD_PHYS) {
if (size > fstore->len) {
// Note tracked allocs don't support realloc.
fstore->len = size;
fstore->buf = metal_allocate_memory(size);
debug_printf("store_load() realloc to %lu\n", fstore->len);
}
*data = fstore->buf;
debug_printf("store_load(): pa 0x%lx offset %u size %u \n", (uint32_t)pa, offset, size);
} else {
void *va = metal_io_phys_to_virt(io, pa);
if (va == NULL) {
return -EINVAL;
}
*data = va;
debug_printf("store_load(): pa 0x%lx va 0x%p offset %u size %u \n", (uint32_t)pa, va, offset, size);
}
mp_uint_t bytes = mp_stream_read_exactly(fstore->file, (void *)*data, size, &error);
if (bytes != size || error != 0) {
return -EINVAL;
}
return bytes;
}
const struct image_store_ops mp_store_ops = {
.open = store_open,
.close = store_close,
.load = store_load,
.features = SUPPORT_SEEK,
};
#endif // MICROPY_PY_REMOTEPROC_FILE_STORE
STATIC mp_obj_t rproc_start(mp_obj_t self_in) {
rproc_obj_t *self = MP_OBJ_TO_PTR(self_in);
// Start the processor to run the application.
if (remoteproc_start(&self->rproc) != 0) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to start remote processor"));
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(rproc_start_obj, rproc_start);
STATIC mp_obj_t rproc_stop(mp_obj_t self_in) {
rproc_obj_t *self = MP_OBJ_TO_PTR(self_in);
// Stop the processor, but the processor is not powered down.
if (remoteproc_stop(&self->rproc) != 0) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to stop remote processor"));
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(rproc_stop_obj, rproc_stop);
STATIC mp_obj_t rproc_shutdown(mp_obj_t self_in) {
rproc_obj_t *self = MP_OBJ_TO_PTR(self_in);
// Shutdown the remoteproc and release its resources.
if (remoteproc_shutdown(&self->rproc) != 0) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to shut down the remote processor"));
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(rproc_shutdown_obj, rproc_shutdown);
mp_obj_t rproc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
enum { ARG_entry };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_entry, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_NONE } },
};
// Parse args.
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
rproc_obj_t *self = m_new_obj_with_finaliser(rproc_obj_t);
self->base.type = &rproc_type;
// Instantiate the remoteproc instance
// NOTE: ports should use rproc->priv to allocate the image store,
// which gets passed to remoteproc_load(), and all of the store ops.
remoteproc_init(&self->rproc, &mp_rproc_ops, NULL);
// Configure the remote before loading applications (optional).
remoteproc_config(&self->rproc, NULL);
if (mp_obj_is_int(args[ARG_entry].u_obj)) {
self->rproc.bootaddr = mp_obj_get_int(args[ARG_entry].u_obj);
} else {
// Load firmware.
const char *path = mp_obj_str_get_str(args[ARG_entry].u_obj);
if (remoteproc_load(&self->rproc, path, self->rproc.priv, &mp_store_ops, NULL) != 0) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to load firmware"));
}
}
return MP_OBJ_FROM_PTR(self);
}
STATIC const mp_rom_map_elem_t rproc_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_rproc) },
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&rproc_shutdown_obj) },
{ MP_ROM_QSTR(MP_QSTR_start), MP_ROM_PTR(&rproc_start_obj) },
{ MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&rproc_stop_obj) },
{ MP_ROM_QSTR(MP_QSTR_shutdown), MP_ROM_PTR(&rproc_shutdown_obj) },
};
STATIC MP_DEFINE_CONST_DICT(rproc_dict, rproc_dict_table);
MP_DEFINE_CONST_OBJ_TYPE(
rproc_type,
MP_QSTR_rproc,
MP_TYPE_FLAG_NONE,
make_new, rproc_make_new,
locals_dict, &rproc_dict
);
#endif // MICROPY_PY_REMOTEPROC

54
extmod/modrproc.h Normal file
View File

@ -0,0 +1,54 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2023 Arduino SA
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* OpenAMP's remoteproc Python module.
*/
#ifndef MICROPY_INCLUDED_MODRPROC_H
#define MICROPY_INCLUDED_MODRPROC_H
#include "openamp/remoteproc.h"
#include "openamp/remoteproc_loader.h"
#ifndef MICROPY_PY_REMOTEPROC_FILE_STORE
#define MICROPY_PY_REMOTEPROC_FILE_STORE (1)
#endif
#ifndef MICROPY_PY_REMOTEPROC_FILE_STORE_BUF_SIZE
// Note the initial file buffer size needs to be at least 512 to read
// enough of the elf headers on the call to store_open() call, and on
// later calls, it gets realloc'd if needed.
#define RPROC_FILE_STORE_BUF_SIZE (1024)
#else
#define RPROC_FILE_STORE_BUF_SIZE MICROPY_PY_REMOTEPROC_FILE_STORE_BUF_SIZE
#endif // RPROC_FILE_STORE_BUF_SIZE
// For ports that don't define a custom image store, this generic
// file-based image store uses VFS to load an elf file from storage.
typedef struct rproc_filestore {
size_t len;
uint8_t *buf;
mp_obj_t file;
} rproc_filestore_t;
#endif // MICROPY_INCLUDED_MODRPROC_H

View File

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

View File

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

View File

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

View File

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

1
lib/libmetal Submodule

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

@ -1 +1 @@
Subproject commit e025c843b60e93689f0f991d753010bb5bd6a722
Subproject commit 7cdf70881519c73667efbc4a61a04d9c1a49babb

1
lib/open-amp Submodule

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -29,6 +29,7 @@
// Entries for sys.path
Q(/flash)
Q(/flash/lib)
// For os.sep
Q(/)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

469
ports/rp2/rp2_dma.c Normal file
View File

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

View File

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

View File

@ -417,6 +417,13 @@ endif
endif # MICROPY_PY_BLUETOOTH
ifeq ($(MICROPY_PY_OPENAMP),1)
SRC_C += metal/metal_port.c
ifeq ($(MICROPY_PY_REMOTEPROC),1)
SRC_C += mprprocport.c
endif
endif
# SRC_O should be placed first to work around this LTO bug with binutils <2.35:
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83967
OBJ += $(addprefix $(BUILD)/, $(SRC_O))

View File

@ -23,6 +23,8 @@ MICROPY_PY_LWIP = 1
MICROPY_PY_NETWORK_CYW43 = 1
MICROPY_PY_SSL = 1
MICROPY_SSL_MBEDTLS = 1
MICROPY_PY_OPENAMP = 1
MICROPY_PY_REMOTEPROC = 1
FROZEN_MANIFEST = $(BOARD_DIR)/manifest.py
MBEDTLS_CONFIG_FILE = '"$(BOARD_DIR)/mbedtls_config_board.h"'

View File

@ -14,10 +14,10 @@ MEMORY
SRAM4 (xrw) : ORIGIN = 0x38000000, LENGTH = 64K /* SRAM4 D3 */
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K /* Total available flash */
FLASH_EXT (rx) : ORIGIN = 0x90000000, LENGTH = 16384K /* 16MBs external QSPI flash */
FLASH_FS (r) : ORIGIN = 0x08020000, LENGTH = 128K /* sector 1 -> Flash storage */
FLASH_TEXT (rx) : ORIGIN = 0x08040000, LENGTH = 1792K /* Sector 0 -> Arduino Bootloader
Sector 1 -> Reserved for CM4/FS
Sectors 2 -> 15 firmware */
FLASH_FS (r) : ORIGIN = 0x08020000, LENGTH = 128K /* 0x08000000 -> 0x08020000 Arduino Bootloader */
FLASH_TEXT (rx) : ORIGIN = 0x08040000, LENGTH = 1280K /* 0x08020000 -> 0x08040000 Flash filesystem storage */
/* 0x08040000 -> 0x08180000 CM7 firmware */
/* 0x08180000 -> 0x08200000 CM4 firmware */
}
/* produce a link error if there is not this amount of RAM for these sections */
@ -44,4 +44,7 @@ _micropy_hw_internal_flash_storage_ram_cache_end = ORIGIN(DTCM) + LENGTH(DTCM);
_micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS);
_micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS);
_openamp_shm_region_start = ORIGIN(SRAM4);
_openamp_shm_region_end = ORIGIN(SRAM4) + LENGTH(SRAM4);
INCLUDE common_blifs.ld

View File

@ -174,6 +174,8 @@ static inline void restore_irq_pri(uint32_t state) {
#define IRQ_PRI_SPI NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 8, 0)
#define IRQ_PRI_HSEM NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 10, 0)
// Interrupt priority for non-special timers.
#define IRQ_PRI_TIMX NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 13, 0)

View File

@ -0,0 +1,123 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2023 Arduino SA
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* libmetal stm32 port.
*/
#include "py/mperrno.h"
#include "py/mphal.h"
#include "mpu.h"
#include "metal/sys.h"
#include "metal/utilities.h"
#include "metal/device.h"
struct metal_state _metal;
static mp_sched_node_t rproc_notify_node;
int metal_sys_init(const struct metal_init_params *params) {
metal_unused(params);
// Clear HSEM pending IRQ.
HSEM_COMMON->ICR |= (uint32_t)__HAL_HSEM_SEMID_TO_MASK(METAL_HSEM_MASTER_ID);
HAL_NVIC_ClearPendingIRQ(HSEM1_IRQn);
// Enable and configure HSEM.
__HAL_RCC_HSEM_CLK_ENABLE();
NVIC_SetPriority(HSEM1_IRQn, IRQ_PRI_HSEM);
HAL_NVIC_EnableIRQ(HSEM1_IRQn);
HAL_HSEM_ActivateNotification(__HAL_HSEM_SEMID_TO_MASK(METAL_HSEM_MASTER_ID));
#ifndef VIRTIO_USE_DCACHE
// If cache management is not enabled, configure the MPU to disable caching
// for the entire shared memory region.
uint32_t irq_state = mpu_config_start();
mpu_config_region(MPU_REGION_NUMBER15, MTEAL_MPU_REGION_BASE, MPU_REGION_SHARED_NCACHE(MTEAL_MPU_REGION_SIZE));
mpu_config_end(irq_state);
#endif
metal_bus_register(&metal_generic_bus);
return 0;
}
void metal_sys_assert(bool cond) {
assert(cond);
}
void metal_sys_finish(void) {
HAL_NVIC_DisableIRQ(HSEM1_IRQn);
HAL_HSEM_DeactivateNotification(__HAL_HSEM_SEMID_TO_MASK(METAL_HSEM_MASTER_ID));
__HAL_RCC_HSEM_CLK_DISABLE();
metal_bus_unregister(&metal_generic_bus);
}
unsigned int sys_irq_save_disable(void) {
return disable_irq();
}
void sys_irq_restore_enable(unsigned int state) {
enable_irq(state);
}
void sys_irq_enable(unsigned int vector) {
metal_unused(vector);
}
void sys_irq_disable(unsigned int vector) {
metal_unused(vector);
}
void *metal_machine_io_mem_map(void *va, metal_phys_addr_t pa,
size_t size, unsigned int flags) {
metal_unused(pa);
metal_unused(size);
metal_unused(flags);
return va;
}
void metal_machine_cache_flush(void *addr, unsigned int len) {
SCB_CleanDCache_by_Addr(addr, len);
}
void metal_machine_cache_invalidate(void *addr, unsigned int len) {
SCB_InvalidateDCache_by_Addr(addr, len);
}
int __metal_sleep_usec(unsigned int usec) {
mp_hal_delay_us(usec);
return 0;
}
int metal_rproc_notify(void *priv, uint32_t id) {
HAL_HSEM_FastTake(METAL_HSEM_REMOTE_ID);
HAL_HSEM_Release(METAL_HSEM_REMOTE_ID, 0);
return 0;
}
void HSEM1_IRQHandler(void) {
HAL_HSEM_IRQHandler();
mp_sched_schedule_node(&rproc_notify_node, metal_rproc_notified);
HAL_HSEM_ActivateNotification(__HAL_HSEM_SEMID_TO_MASK(METAL_HSEM_MASTER_ID));
}

View File

@ -0,0 +1,71 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2023 Arduino SA
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* libmetal stm32 port.
*/
#ifndef MICROPY_INCLUDED_STM32_METAL_PORT_H
#define MICROPY_INCLUDED_STM32_METAL_PORT_H
#include <stdlib.h>
#include "py/mphal.h"
#include "py/runtime.h"
#define METAL_HAVE_STDATOMIC_H 0
#define METAL_HAVE_FUTEX_H 0
#define METAL_PROCESSOR_CPU_H "metal/processor/arm/cpu.h"
#define METAL_PROCESSOR_ATOMIC_H "metal/processor/arm/atomic.h"
#define METAL_MAX_DEVICE_REGIONS 2
#define METAL_HSEM_REMOTE_ID 0
#define METAL_HSEM_MASTER_ID 1
// Note set to 1 to enable log output.
#define METAL_LOG_HANDLER_ENABLE 0
#define metal_cpu_yield()
#define metal_generic_default_poll() MICROPY_EVENT_POLL_HOOK
// Shared memory config
#define METAL_SHM_NAME "OPENAMP_SHM"
// Note 1K must be reserved at the start of the openamp
// shared memory region, for the shared resource table.
#define METAL_RSC_ADDR ((void *)_openamp_shm_region_start)
#define METAL_RSC_SIZE (1024)
#define METAL_SHM_ADDR ((metal_phys_addr_t)(_openamp_shm_region_start + METAL_RSC_SIZE))
#define METAL_SHM_SIZE ((size_t)(_openamp_shm_region_end - _openamp_shm_region_start - METAL_RSC_SIZE))
#define MTEAL_MPU_REGION_BASE ((uint32_t)_openamp_shm_region_start)
#define MTEAL_MPU_REGION_SIZE (MPU_REGION_SIZE_64KB)
extern const char _openamp_shm_region_start[];
extern const char _openamp_shm_region_end[];
void metal_sys_assert(bool cond);
int metal_rproc_notify(void *priv, uint32_t id);
extern void metal_rproc_notified(mp_sched_node_t *node);
#endif // MICROPY_INCLUDED_STM32_METAL_PORT_H

View File

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

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

@ -0,0 +1,168 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2023 Arduino SA
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* modremoteproc stm32 port.
*/
#include <stdio.h>
#include <stdint.h>
#include "py/obj.h"
#include "py/runtime.h"
#include "metal/alloc.h"
#include "metal/errno.h"
#include "metal/io.h"
#include "metal/sys.h"
#include "metal/device.h"
#include "metal/utilities.h"
#include "extmod/modrproc.h"
#define debug_printf(...) // mp_printf(&mp_plat_print, __VA_ARGS__)
static struct remoteproc *rproc_init(struct remoteproc *rproc,
const struct remoteproc_ops *ops, void *arg) {
debug_printf("rproc_init()\n");
rproc = rproc;
rproc->ops = ops;
// Allocate filestore struct.
rproc_filestore_t *filestore = metal_allocate_memory(sizeof(rproc_filestore_t));
filestore->len = RPROC_FILE_STORE_BUF_SIZE;
filestore->buf = metal_allocate_memory(RPROC_FILE_STORE_BUF_SIZE);
// Store in rproc's private data.
rproc->priv = filestore;
metal_list_init(&rproc->mems);
// Set RPROC_OFFLINE to make rproc_config get called.
rproc->state = RPROC_READY;
return rproc;
}
static void *rproc_mmap(struct remoteproc *rproc, metal_phys_addr_t *pa,
metal_phys_addr_t *da, size_t size, unsigned int attribute,
struct metal_io_region **io) {
debug_printf("rproc_mmap(): pa 0x%p da 0x%p io 0x%p size %u\n", *pa, *da, *io, size);
struct remoteproc_mem *mem;
metal_phys_addr_t lpa = *pa;
metal_phys_addr_t lda = *da;
if (lda == METAL_BAD_PHYS) {
return NULL;
}
if (lpa == METAL_BAD_PHYS) {
lpa = lda;
}
// Currently this port doesn't support loading firmware to flash,
// only SD/SRAM images are supported. Check of load address is in
// the flash region, and if so return NULL.
if (lda >= FLASH_BASE && lda < FLASH_END) {
return NULL;
}
mem = metal_allocate_memory(sizeof(*mem));
if (!mem) {
return NULL;
}
*io = metal_allocate_memory(sizeof(struct metal_io_region));
if (!*io) {
metal_free_memory(mem);
return NULL;
}
remoteproc_init_mem(mem, NULL, lpa, lda, size, *io);
metal_io_init(*io, (void *)mem->da, &mem->pa, size,
sizeof(metal_phys_addr_t) << 3, attribute, NULL);
remoteproc_add_mem(rproc, mem);
*pa = lpa;
*da = lda;
return metal_io_phys_to_virt(*io, mem->pa);
}
static int rproc_start(struct remoteproc *rproc) {
debug_printf("rproc_start()\n");
if (RCC->GCR & RCC_GCR_BOOT_C2) {
// CM4 core is already booted.
debug_printf("rproc_start(): CM4 core is already booted.\n");
return -1;
}
// Flush M7 cache.
struct metal_list *node;
metal_list_for_each(&rproc->mems, node) {
struct remoteproc_mem *mem;
mem = metal_container_of(node, struct remoteproc_mem, node);
SCB_CleanDCache_by_Addr((uint32_t *)mem->pa, mem->size);
}
HAL_SYSCFG_CM4BootAddConfig(SYSCFG_BOOT_ADDR0, (uint32_t)rproc->bootaddr);
HAL_RCCEx_EnableBootCore(RCC_BOOT_C2);
return 0;
}
static int rproc_stop(struct remoteproc *rproc) {
debug_printf("rproc_stop()\n");
if (rproc->state == RPROC_RUNNING) {
// There's no straightforward way to reset or shut down
// the remote processor, so a full system reset is needed.
NVIC_SystemReset();
}
return 0;
}
static int rproc_config(struct remoteproc *rproc, void *data) {
debug_printf("rproc_config()\n");
(void)rproc;
return 0;
}
static void rproc_remove(struct remoteproc *rproc) {
debug_printf("rproc_remove()\n");
(void)rproc;
}
static int rproc_shutdown(struct remoteproc *rproc) {
debug_printf("rproc_shutdown()\n");
if (rproc->state == RPROC_RUNNING) {
// There's no straightforward way to reset or shut down
// the remote processor, so a full system reset is needed.
NVIC_SystemReset();
}
return 0;
}
const struct remoteproc_ops mp_rproc_ops = {
.init = rproc_init,
.mmap = rproc_mmap,
.start = rproc_start,
.stop = rproc_stop,
.config = rproc_config,
.remove = rproc_remove,
.shutdown = rproc_shutdown,
};

View File

@ -73,6 +73,18 @@
| MPU_REGION_ENABLE << MPU_RASR_ENABLE_Pos \
)
#define MPU_REGION_SHARED_NCACHE(size) ( \
MPU_INSTRUCTION_ACCESS_DISABLE << MPU_RASR_XN_Pos \
| MPU_REGION_FULL_ACCESS << MPU_RASR_AP_Pos \
| MPU_TEX_LEVEL1 << MPU_RASR_TEX_Pos \
| MPU_ACCESS_SHAREABLE << MPU_RASR_S_Pos \
| MPU_ACCESS_NOT_CACHEABLE << MPU_RASR_C_Pos \
| MPU_ACCESS_NOT_BUFFERABLE << MPU_RASR_B_Pos \
| 0x00 << MPU_RASR_SRD_Pos \
| (size) << MPU_RASR_SIZE_Pos \
| MPU_REGION_ENABLE << MPU_RASR_ENABLE_Pos \
)
static inline void mpu_init(void) {
MPU->CTRL = MPU_PRIVILEGED_DEFAULT | MPU_CTRL_ENABLE_Msk;
SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -82,6 +82,18 @@ mp_uint_t mp_stream_rw(mp_obj_t stream, void *buf_, mp_uint_t size, int *errcode
return done;
}
mp_uint_t mp_stream_seek(mp_obj_t stream, mp_off_t offset, int whence, int *errcode) {
struct mp_stream_seek_t seek_s;
seek_s.offset = offset;
seek_s.whence = whence;
const mp_stream_p_t *stream_p = mp_get_stream(stream);
mp_uint_t res = stream_p->ioctl(MP_OBJ_FROM_PTR(stream), MP_STREAM_SEEK, (mp_uint_t)(uintptr_t)&seek_s, errcode);
if (res == MP_STREAM_ERROR) {
return -1;
}
return seek_s.offset;
}
const mp_stream_p_t *mp_get_stream_raise(mp_obj_t self_in, int flags) {
const mp_obj_type_t *type = mp_obj_get_type(self_in);
if (MP_OBJ_TYPE_HAS_SLOT(type, protocol)) {

View File

@ -115,6 +115,7 @@ mp_obj_t mp_stream_write(mp_obj_t self_in, const void *buf, size_t len, byte fla
mp_uint_t mp_stream_rw(mp_obj_t stream, void *buf, mp_uint_t size, int *errcode, byte flags);
#define mp_stream_write_exactly(stream, buf, size, err) mp_stream_rw(stream, (byte *)buf, size, err, MP_STREAM_RW_WRITE)
#define mp_stream_read_exactly(stream, buf, size, err) mp_stream_rw(stream, buf, size, err, MP_STREAM_RW_READ)
mp_uint_t mp_stream_seek(mp_obj_t stream, mp_off_t offset, int whence, int *errcode);
void mp_stream_write_adaptor(void *self, const char *buf, size_t len);

View File

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