diff options
95 files changed, 1042 insertions, 428 deletions
diff --git a/docs/develop/natmod.rst b/docs/develop/natmod.rst index 18678eaefb..2ccd832885 100644 --- a/docs/develop/natmod.rst +++ b/docs/develop/natmod.rst @@ -81,7 +81,14 @@ Linker limitation: the native module is not linked against the symbol table of t full MicroPython firmware. Rather, it is linked against an explicit table of exported symbols found in ``mp_fun_table`` (in ``py/nativeglue.h``), that is fixed at firmware build time. It is thus not possible to simply call some arbitrary HAL/OS/RTOS/system -function, for example. +function, for example, unless that resides at a fixed address. In that case, the path +of a linkerscript containing a series of symbol names and their fixed address can be +passed to ``mpy_ld.py`` via the ``--externs`` command line argument. That way symbols +appearing in the linkerscript will take precedence over what is provided from object +files, but at the moment the object files' implementation will still reside in the +final MPY file. The linkerscript parser is limited in its capabilities, and is +currently used only for parsing the ESP8266 port ROM symbols list (see +``ports/esp8266/boards/eagle.rom.addr.v6.ld``). New symbols can be added to the end of the table and the firmware rebuilt. The symbols also need to be added to ``tools/mpy_ld.py``'s ``fun_table`` dict in the diff --git a/docs/esp32/quickref.rst b/docs/esp32/quickref.rst index 63180b470a..c394414a76 100644 --- a/docs/esp32/quickref.rst +++ b/docs/esp32/quickref.rst @@ -544,14 +544,27 @@ Legacy methods: Equivalent to ``ADC.block().init(bits=bits)``. +The only chip that can switch resolution to a lower one is the normal esp32. +The C2 & S3 are stuck at 12 bits, while the S2 is at 13 bits. + For compatibility, the ``ADC`` object also provides constants matching the -supported ADC resolutions: +supported ADC resolutions, per chip: +ESP32: - ``ADC.WIDTH_9BIT`` = 9 - ``ADC.WIDTH_10BIT`` = 10 - ``ADC.WIDTH_11BIT`` = 11 - ``ADC.WIDTH_12BIT`` = 12 +ESP32 C3 & S3: + - ``ADC.WIDTH_12BIT`` = 12 + +ESP32 S2: + - ``ADC.WIDTH_13BIT`` = 13 + +.. method:: ADC.deinit() + + Provided to deinit the adc driver. Software SPI bus ---------------- diff --git a/docs/library/framebuf.rst b/docs/library/framebuf.rst index f22a3613bd..e2a231207d 100644 --- a/docs/library/framebuf.rst +++ b/docs/library/framebuf.rst @@ -137,6 +137,18 @@ Other methods is compared to the value from *palette*, not to the value directly from *fbuf*.) + *fbuf* can be another FrameBuffer instance, or a tuple or list of the form:: + + (buffer, width, height, format) + + or:: + + (buffer, width, height, format, stride) + + This matches the signature of the FrameBuffer constructor, and the elements + of the tuple/list are the same as the arguments to the constructor except that + the *buffer* here can be read-only. + The *palette* argument enables blitting between FrameBuffers with differing formats. Typical usage is to render a monochrome or grayscale glyph/icon to a color display. The *palette* is a FrameBuffer instance whose format is diff --git a/docs/library/socket.rst b/docs/library/socket.rst index 944e7e631a..38e0aab704 100644 --- a/docs/library/socket.rst +++ b/docs/library/socket.rst @@ -227,22 +227,28 @@ Methods has the same "no short writes" policy for blocking sockets, and will return number of bytes sent on non-blocking sockets. -.. method:: socket.recv(bufsize) +.. method:: socket.recv(bufsize, [flags]) Receive data from the socket. The return value is a bytes object representing the data received. The maximum amount of data to be received at once is specified by bufsize. + Most ports support the optional *flags* argument. Available *flags* are defined as constants + in the socket module and have the same meaning as in CPython. ``MSG_PEEK`` and ``MSG_DONTWAIT`` + are supported on all ports which accept the *flags* argument. + .. method:: socket.sendto(bytes, address) Send data to the socket. The socket should not be connected to a remote socket, since the destination socket is specified by *address*. -.. method:: socket.recvfrom(bufsize) +.. method:: socket.recvfrom(bufsize, [flags]) Receive data from the socket. The return value is a pair *(bytes, address)* where *bytes* is a bytes object representing the data received and *address* is the address of the socket sending the data. + See the `recv` function for an explanation of the optional *flags* argument. + .. method:: socket.setsockopt(level, optname, value) Set the value of the given socket option. The needed symbolic constants are defined in the diff --git a/examples/natmod/btree/Makefile b/examples/natmod/btree/Makefile index ff130d61b3..6273ccc657 100644 --- a/examples/natmod/btree/Makefile +++ b/examples/natmod/btree/Makefile @@ -8,7 +8,7 @@ MOD = btree_$(ARCH) SRC = btree_c.c # Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc) -ARCH = x64 +ARCH ?= x64 BTREE_DIR = $(MPY_DIR)/lib/berkeley-db-1.xx BERKELEY_DB_CONFIG_FILE ?= \"extmod/berkeley-db/berkeley_db_config_port.h\" @@ -32,6 +32,10 @@ SRC += $(addprefix $(realpath $(BTREE_DIR))/,\ mpool/mpool.c \ ) +ifeq ($(ARCH),xtensa) +MPY_EXTERN_SYM_FILE=$(MPY_DIR)/ports/esp8266/boards/eagle.rom.addr.v6.ld +endif + include $(MPY_DIR)/py/dynruntime.mk # btree needs gnu99 defined diff --git a/examples/natmod/deflate/Makefile b/examples/natmod/deflate/Makefile index 504130d572..1f63de20d2 100644 --- a/examples/natmod/deflate/Makefile +++ b/examples/natmod/deflate/Makefile @@ -8,6 +8,12 @@ MOD = deflate_$(ARCH) SRC = deflate.c # Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc) -ARCH = x64 +ARCH ?= x64 + +ifeq ($(ARCH),xtensa) +# Link with libm.a and libgcc.a from the toolchain +LINK_RUNTIME = 1 +MPY_EXTERN_SYM_FILE=$(MPY_DIR)/ports/esp8266/boards/eagle.rom.addr.v6.ld +endif include $(MPY_DIR)/py/dynruntime.mk diff --git a/examples/natmod/framebuf/Makefile b/examples/natmod/framebuf/Makefile index 2e2b815975..cb821736e7 100644 --- a/examples/natmod/framebuf/Makefile +++ b/examples/natmod/framebuf/Makefile @@ -8,6 +8,10 @@ MOD = framebuf_$(ARCH) SRC = framebuf.c # Architecture to build for (x86, x64, armv7m, xtensa, xtensawin) -ARCH = x64 +ARCH ?= x64 + +ifeq ($(ARCH),xtensa) +MPY_EXTERN_SYM_FILE=$(MPY_DIR)/ports/esp8266/boards/eagle.rom.addr.v6.ld +endif include $(MPY_DIR)/py/dynruntime.mk diff --git a/examples/natmod/framebuf/framebuf.c b/examples/natmod/framebuf/framebuf.c index 1ba702e33d..5fd7c6be3a 100644 --- a/examples/natmod/framebuf/framebuf.c +++ b/examples/natmod/framebuf/framebuf.c @@ -4,6 +4,9 @@ #include "py/dynruntime.h" #if !defined(__linux__) +void *memcpy(void *dst, const void *src, size_t n) { + return mp_fun_table.memmove_(dst, src, n); +} void *memset(void *s, int c, size_t n) { return mp_fun_table.memset_(s, c, n); } diff --git a/examples/natmod/random/Makefile b/examples/natmod/random/Makefile index 8abdb66dc8..5d23eac1e9 100644 --- a/examples/natmod/random/Makefile +++ b/examples/natmod/random/Makefile @@ -8,6 +8,10 @@ MOD = random_$(ARCH) SRC = random.c # Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc) -ARCH = x64 +ARCH ?= x64 + +ifeq ($(ARCH),xtensa) +MPY_EXTERN_SYM_FILE=$(MPY_DIR)/ports/esp8266/boards/eagle.rom.addr.v6.ld +endif include $(MPY_DIR)/py/dynruntime.mk diff --git a/extmod/btstack/modbluetooth_btstack.c b/extmod/btstack/modbluetooth_btstack.c index b29970842c..7694a1874f 100644 --- a/extmod/btstack/modbluetooth_btstack.c +++ b/extmod/btstack/modbluetooth_btstack.c @@ -705,12 +705,12 @@ int mp_bluetooth_init(void) { return 0; } -void mp_bluetooth_deinit(void) { +int mp_bluetooth_deinit(void) { DEBUG_printf("mp_bluetooth_deinit\n"); // Nothing to do if not initialised. if (!MP_STATE_PORT(bluetooth_btstack_root_pointers)) { - return; + return 0; } mp_bluetooth_gap_advertise_stop(); @@ -737,6 +737,9 @@ void mp_bluetooth_deinit(void) { deinit_stack(); DEBUG_printf("mp_bluetooth_deinit: complete\n"); + + bool timeout = mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_TIMEOUT; + return timeout ? MP_ETIMEDOUT : 0; } bool mp_bluetooth_is_active(void) { diff --git a/extmod/modbluetooth.c b/extmod/modbluetooth.c index b95c42a4ee..ffa407809a 100644 --- a/extmod/modbluetooth.c +++ b/extmod/modbluetooth.c @@ -290,12 +290,13 @@ static mp_obj_t bluetooth_ble_make_new(const mp_obj_type_t *type, size_t n_args, static mp_obj_t bluetooth_ble_active(size_t n_args, const mp_obj_t *args) { if (n_args == 2) { // Boolean enable/disable argument supplied, set current state. + int err; if (mp_obj_is_true(args[1])) { - int err = mp_bluetooth_init(); - bluetooth_handle_errno(err); + err = mp_bluetooth_init(); } else { - mp_bluetooth_deinit(); + err = mp_bluetooth_deinit(); } + bluetooth_handle_errno(err); } // Return current state. return mp_obj_new_bool(mp_bluetooth_is_active()); diff --git a/extmod/modbluetooth.h b/extmod/modbluetooth.h index 6a087c8e25..24f063fa5d 100644 --- a/extmod/modbluetooth.h +++ b/extmod/modbluetooth.h @@ -295,7 +295,7 @@ extern const mp_obj_type_t mp_type_bluetooth_uuid; int mp_bluetooth_init(void); // Disables the Bluetooth stack. Is a no-op when not enabled. -void mp_bluetooth_deinit(void); +int mp_bluetooth_deinit(void); // Returns true when the Bluetooth stack is active. bool mp_bluetooth_is_active(void); diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index b718a66cc6..5c4b9abf0c 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -270,8 +270,7 @@ static void fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, u formats[fb->format].fill_rect(fb, x, y, xend - x, yend - y, col); } -static mp_obj_t framebuf_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args_in) { - mp_arg_check_num(n_args, n_kw, 4, 5, false); +static mp_obj_t framebuf_make_new_helper(size_t n_args, const mp_obj_t *args_in, unsigned int buf_flags, mp_obj_framebuf_t *o) { mp_int_t width = mp_obj_get_int(args_in[1]); mp_int_t height = mp_obj_get_int(args_in[2]); @@ -318,13 +317,15 @@ static mp_obj_t framebuf_make_new(const mp_obj_type_t *type, size_t n_args, size } mp_buffer_info_t bufinfo; - mp_get_buffer_raise(args_in[0], &bufinfo, MP_BUFFER_WRITE); + mp_get_buffer_raise(args_in[0], &bufinfo, buf_flags); if ((strides_required * stride + (height_required - strides_required) * width_required) * bpp / 8 > bufinfo.len) { mp_raise_ValueError(NULL); } - mp_obj_framebuf_t *o = mp_obj_malloc(mp_obj_framebuf_t, type); + if (o == NULL) { + o = mp_obj_malloc(mp_obj_framebuf_t, (const mp_obj_type_t *)&mp_type_framebuf); + } o->buf_obj = args_in[0]; o->buf = bufinfo.buf; o->width = width; @@ -335,6 +336,11 @@ static mp_obj_t framebuf_make_new(const mp_obj_type_t *type, size_t n_args, size return MP_OBJ_FROM_PTR(o); } +static mp_obj_t framebuf_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args_in) { + mp_arg_check_num(n_args, n_kw, 4, 5, false); + return framebuf_make_new_helper(n_args, args_in, MP_BUFFER_WRITE, NULL); +} + static void framebuf_args(const mp_obj_t *args_in, mp_int_t *args_out, int n) { for (int i = 0; i < n; ++i) { args_out[i] = mp_obj_get_int(args_in[i + 1]); @@ -707,13 +713,27 @@ static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_poly_obj, 5, 6, framebuf_pol #endif // MICROPY_PY_ARRAY +static void get_readonly_framebuffer(mp_obj_t arg, mp_obj_framebuf_t *rofb) { + mp_obj_t fb = mp_obj_cast_to_native_base(arg, MP_OBJ_FROM_PTR(&mp_type_framebuf)); + if (fb != MP_OBJ_NULL) { + *rofb = *(mp_obj_framebuf_t *)MP_OBJ_TO_PTR(fb); + } else { + // A tuple/list of the form: (buffer, width, height, format[, stride]). + size_t len; + mp_obj_t *items; + mp_obj_get_array(arg, &len, &items); + if (len < 4 || len > 5) { + mp_raise_ValueError(NULL); + } + framebuf_make_new_helper(len, items, MP_BUFFER_READ, rofb); + } +} + static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]); - mp_obj_t source_in = mp_obj_cast_to_native_base(args_in[1], MP_OBJ_FROM_PTR(&mp_type_framebuf)); - if (source_in == MP_OBJ_NULL) { - mp_raise_TypeError(NULL); - } - mp_obj_framebuf_t *source = MP_OBJ_TO_PTR(source_in); + + mp_obj_framebuf_t source; + get_readonly_framebuffer(args_in[1], &source); mp_int_t x = mp_obj_get_int(args_in[2]); mp_int_t y = mp_obj_get_int(args_in[3]); @@ -721,16 +741,17 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { if (n_args > 4) { key = mp_obj_get_int(args_in[4]); } - mp_obj_framebuf_t *palette = NULL; + mp_obj_framebuf_t palette; + palette.buf = NULL; if (n_args > 5 && args_in[5] != mp_const_none) { - palette = MP_OBJ_TO_PTR(mp_obj_cast_to_native_base(args_in[5], MP_OBJ_FROM_PTR(&mp_type_framebuf))); + get_readonly_framebuffer(args_in[5], &palette); } if ( (x >= self->width) || (y >= self->height) || - (-x >= source->width) || - (-y >= source->height) + (-x >= source.width) || + (-y >= source.height) ) { // Out of bounds, no-op. return mp_const_none; @@ -741,15 +762,15 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { int y0 = MAX(0, y); int x1 = MAX(0, -x); int y1 = MAX(0, -y); - int x0end = MIN(self->width, x + source->width); - int y0end = MIN(self->height, y + source->height); + int x0end = MIN(self->width, x + source.width); + int y0end = MIN(self->height, y + source.height); for (; y0 < y0end; ++y0) { int cx1 = x1; for (int cx0 = x0; cx0 < x0end; ++cx0) { - uint32_t col = getpixel(source, cx1, y1); - if (palette) { - col = getpixel(palette, col, 0); + uint32_t col = getpixel(&source, cx1, y1); + if (palette.buf) { + col = getpixel(&palette, col, 0); } if (col != (uint32_t)key) { setpixel(self, cx0, y0, col); diff --git a/extmod/modlwip.c b/extmod/modlwip.c index 961803f5e8..65a3412ecb 100644 --- a/extmod/modlwip.c +++ b/extmod/modlwip.c @@ -70,6 +70,10 @@ #define TCP_NODELAY TF_NODELAY +// Socket flags +#define MSG_PEEK 0x01 +#define MSG_DONTWAIT 0x02 + // For compatibilily with older lwIP versions. #ifndef ip_set_option #define ip_set_option(pcb, opt) ((pcb)->so_options |= (opt)) @@ -673,13 +677,13 @@ static mp_uint_t lwip_raw_udp_send(lwip_socket_obj_t *socket, const byte *buf, m } // Helper function for recv/recvfrom to handle raw/UDP packets -static mp_uint_t lwip_raw_udp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_t len, ip_addr_t *ip, mp_uint_t *port, int *_errno) { +static mp_uint_t lwip_raw_udp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_t len, mp_int_t flags, ip_addr_t *ip, mp_uint_t *port, int *_errno) { lwip_incoming_packet_t *slot = &socket->incoming.udp_raw.array[socket->incoming.udp_raw.iget]; if (slot->pbuf == NULL) { - if (socket->timeout == 0) { - // Non-blocking socket. + // Non-blocking socket or flag + if (socket->timeout == 0 || (flags & MSG_DONTWAIT)) { *_errno = MP_EAGAIN; return -1; } @@ -705,9 +709,11 @@ static mp_uint_t lwip_raw_udp_receive(lwip_socket_obj_t *socket, byte *buf, mp_u MICROPY_PY_LWIP_ENTER u16_t result = pbuf_copy_partial(p, buf, ((p->tot_len > len) ? len : p->tot_len), 0); - pbuf_free(p); - slot->pbuf = NULL; - socket->incoming.udp_raw.iget = (socket->incoming.udp_raw.iget + 1) % LWIP_INCOMING_PACKET_QUEUE_LEN; + if ((flags & MSG_PEEK) == 0) { + pbuf_free(p); + slot->pbuf = NULL; + socket->incoming.udp_raw.iget = (socket->incoming.udp_raw.iget + 1) % LWIP_INCOMING_PACKET_QUEUE_LEN; + } MICROPY_PY_LWIP_EXIT @@ -815,14 +821,14 @@ static mp_uint_t lwip_tcp_send(lwip_socket_obj_t *socket, const byte *buf, mp_ui } // Helper function for recv/recvfrom to handle TCP packets -static mp_uint_t lwip_tcp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_t len, int *_errno) { +static mp_uint_t lwip_tcp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_t len, mp_int_t flags, int *_errno) { // Check for any pending errors STREAM_ERROR_CHECK(socket); if (socket->incoming.tcp.pbuf == NULL) { - // Non-blocking socket - if (socket->timeout == 0) { + // Non-blocking socket or flag + if (socket->timeout == 0 || (flags & MSG_DONTWAIT)) { if (socket->state == STATE_PEER_CLOSED) { return 0; } @@ -867,19 +873,21 @@ static mp_uint_t lwip_tcp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_ memcpy(buf, (byte *)p->payload + socket->recv_offset, len); - remaining -= len; - if (remaining == 0) { - socket->incoming.tcp.pbuf = p->next; - // If we don't ref here, free() will free the entire chain, - // if we ref, it does what we need: frees 1st buf, and decrements - // next buf's refcount back to 1. - pbuf_ref(p->next); - pbuf_free(p); - socket->recv_offset = 0; - } else { - socket->recv_offset += len; + if ((flags & MSG_PEEK) == 0) { + remaining -= len; + if (remaining == 0) { + socket->incoming.tcp.pbuf = p->next; + // If we don't ref here, free() will free the entire chain, + // if we ref, it does what we need: frees 1st buf, and decrements + // next buf's refcount back to 1. + pbuf_ref(p->next); + pbuf_free(p); + socket->recv_offset = 0; + } else { + socket->recv_offset += len; + } + tcp_recved(socket->pcb.tcp, len); } - tcp_recved(socket->pcb.tcp, len); MICROPY_PY_LWIP_EXIT @@ -1271,40 +1279,58 @@ static mp_obj_t lwip_socket_send(mp_obj_t self_in, mp_obj_t buf_in) { } static MP_DEFINE_CONST_FUN_OBJ_2(lwip_socket_send_obj, lwip_socket_send); -static mp_obj_t lwip_socket_recv(mp_obj_t self_in, mp_obj_t len_in) { - lwip_socket_obj_t *socket = MP_OBJ_TO_PTR(self_in); +// Common implementation for recv & recvfrom +static mp_obj_t lwip_socket_recv_common(size_t n_args, const mp_obj_t *args, ip_addr_t *ip, mp_uint_t *port) { + lwip_socket_obj_t *socket = MP_OBJ_TO_PTR(args[0]); + mp_int_t len = mp_obj_get_int(args[1]); + mp_int_t flags = n_args > 2 ? mp_obj_get_int(args[2]) : 0; int _errno; + vstr_t vstr; + mp_uint_t ret = 0; lwip_socket_check_connected(socket); - mp_int_t len = mp_obj_get_int(len_in); - vstr_t vstr; vstr_init_len(&vstr, len); - mp_uint_t ret = 0; switch (socket->type) { - case MOD_NETWORK_SOCK_STREAM: { - ret = lwip_tcp_receive(socket, (byte *)vstr.buf, len, &_errno); + case MOD_NETWORK_SOCK_STREAM: + if (ip != NULL) { + *ip = socket->tcp_peer_addr; + *port = (mp_uint_t)socket->tcp_peer_port; + } + ret = lwip_tcp_receive(socket, (byte *)vstr.buf, len, flags, &_errno); break; - } case MOD_NETWORK_SOCK_DGRAM: #if MICROPY_PY_LWIP_SOCK_RAW case MOD_NETWORK_SOCK_RAW: #endif - ret = lwip_raw_udp_receive(socket, (byte *)vstr.buf, len, NULL, NULL, &_errno); + ret = lwip_raw_udp_receive(socket, (byte *)vstr.buf, len, flags, ip, port, &_errno); break; } if (ret == -1) { mp_raise_OSError(_errno); } - if (ret == 0) { return mp_const_empty_bytes; } vstr.len = ret; return mp_obj_new_bytes_from_vstr(&vstr); } -static MP_DEFINE_CONST_FUN_OBJ_2(lwip_socket_recv_obj, lwip_socket_recv); + +static mp_obj_t lwip_socket_recv(size_t n_args, const mp_obj_t *args) { + return lwip_socket_recv_common(n_args, args, NULL, NULL); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(lwip_socket_recv_obj, 2, 3, lwip_socket_recv); + +static mp_obj_t lwip_socket_recvfrom(size_t n_args, const mp_obj_t *args) { + ip_addr_t ip; + mp_uint_t port; + mp_obj_t tuple[2]; + tuple[0] = lwip_socket_recv_common(n_args, args, &ip, &port); + tuple[1] = lwip_format_inet_addr(&ip, port); + return mp_obj_new_tuple(2, tuple); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(lwip_socket_recvfrom_obj, 2, 3, lwip_socket_recvfrom); static mp_obj_t lwip_socket_sendto(mp_obj_t self_in, mp_obj_t data_in, mp_obj_t addr_in) { lwip_socket_obj_t *socket = MP_OBJ_TO_PTR(self_in); @@ -1339,50 +1365,6 @@ static mp_obj_t lwip_socket_sendto(mp_obj_t self_in, mp_obj_t data_in, mp_obj_t } static MP_DEFINE_CONST_FUN_OBJ_3(lwip_socket_sendto_obj, lwip_socket_sendto); -static mp_obj_t lwip_socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in) { - lwip_socket_obj_t *socket = MP_OBJ_TO_PTR(self_in); - int _errno; - - lwip_socket_check_connected(socket); - - mp_int_t len = mp_obj_get_int(len_in); - vstr_t vstr; - vstr_init_len(&vstr, len); - ip_addr_t ip; - mp_uint_t port; - - mp_uint_t ret = 0; - switch (socket->type) { - case MOD_NETWORK_SOCK_STREAM: { - ip = socket->tcp_peer_addr; - port = (mp_uint_t)socket->tcp_peer_port; - ret = lwip_tcp_receive(socket, (byte *)vstr.buf, len, &_errno); - break; - } - case MOD_NETWORK_SOCK_DGRAM: - #if MICROPY_PY_LWIP_SOCK_RAW - case MOD_NETWORK_SOCK_RAW: - #endif - ret = lwip_raw_udp_receive(socket, (byte *)vstr.buf, len, &ip, &port, &_errno); - break; - } - if (ret == -1) { - mp_raise_OSError(_errno); - } - - mp_obj_t tuple[2]; - if (ret == 0) { - tuple[0] = mp_const_empty_bytes; - } else { - vstr.len = ret; - tuple[0] = mp_obj_new_bytes_from_vstr(&vstr); - } - tuple[1] = lwip_format_inet_addr(&ip, port); - - return mp_obj_new_tuple(2, tuple); -} -static MP_DEFINE_CONST_FUN_OBJ_2(lwip_socket_recvfrom_obj, lwip_socket_recvfrom); - static mp_obj_t lwip_socket_sendall(mp_obj_t self_in, mp_obj_t buf_in) { lwip_socket_obj_t *socket = MP_OBJ_TO_PTR(self_in); lwip_socket_check_connected(socket); @@ -1542,12 +1524,12 @@ static mp_uint_t lwip_socket_read(mp_obj_t self_in, void *buf, mp_uint_t size, i switch (socket->type) { case MOD_NETWORK_SOCK_STREAM: - return lwip_tcp_receive(socket, buf, size, errcode); + return lwip_tcp_receive(socket, buf, size, 0, errcode); case MOD_NETWORK_SOCK_DGRAM: #if MICROPY_PY_LWIP_SOCK_RAW case MOD_NETWORK_SOCK_RAW: #endif - return lwip_raw_udp_receive(socket, buf, size, NULL, NULL, errcode); + return lwip_raw_udp_receive(socket, buf, size, 0, NULL, NULL, errcode); } // Unreachable return MP_STREAM_ERROR; @@ -1919,6 +1901,8 @@ static const mp_rom_map_elem_t mp_module_lwip_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_IPPROTO_TCP), MP_ROM_INT(IP_PROTO_TCP) }, { MP_ROM_QSTR(MP_QSTR_TCP_NODELAY), MP_ROM_INT(TCP_NODELAY) }, + { MP_ROM_QSTR(MP_QSTR_MSG_PEEK), MP_ROM_INT(MSG_PEEK) }, + { MP_ROM_QSTR(MP_QSTR_MSG_DONTWAIT), MP_ROM_INT(MSG_DONTWAIT) }, }; static MP_DEFINE_CONST_DICT(mp_module_lwip_globals, mp_module_lwip_globals_table); diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c index 6ca0c17267..5e7030e36f 100644 --- a/extmod/nimble/modbluetooth_nimble.c +++ b/extmod/nimble/modbluetooth_nimble.c @@ -60,6 +60,7 @@ static uint8_t nimble_address_mode = BLE_OWN_ADDR_RANDOM; #define NIMBLE_STARTUP_TIMEOUT 2000 +#define NIMBLE_SHUTDOWN_TIMEOUT 500 // Any BLE_HS_xxx code not in this table will default to MP_EIO. static int8_t ble_hs_err_to_errno_table[] = { @@ -554,7 +555,7 @@ static void ble_hs_shutdown_stop_cb(int status, void *arg) { static struct ble_hs_stop_listener ble_hs_shutdown_stop_listener; -void mp_bluetooth_nimble_port_shutdown(void) { +int mp_bluetooth_nimble_port_shutdown(void) { DEBUG_printf("mp_bluetooth_nimble_port_shutdown (nimble default)\n"); // By default, just call ble_hs_stop directly and wait for the stack to stop. @@ -562,9 +563,17 @@ void mp_bluetooth_nimble_port_shutdown(void) { ble_hs_stop(&ble_hs_shutdown_stop_listener, ble_hs_shutdown_stop_cb, NULL); + mp_uint_t timeout_start_ticks_ms = mp_hal_ticks_ms(); while (mp_bluetooth_nimble_ble_state != MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF) { - mp_event_wait_indefinite(); + mp_uint_t elapsed = mp_hal_ticks_ms() - timeout_start_ticks_ms; + if (elapsed > NIMBLE_SHUTDOWN_TIMEOUT) { + // Stack had not responded (via ble_hs_shutdown_stop_cb) + return MP_ETIMEDOUT; + } + + mp_event_wait_ms(NIMBLE_SHUTDOWN_TIMEOUT - elapsed); } + return 0; } #endif // !MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY @@ -659,10 +668,11 @@ int mp_bluetooth_init(void) { return 0; } -void mp_bluetooth_deinit(void) { +int mp_bluetooth_deinit(void) { DEBUG_printf("mp_bluetooth_deinit %d\n", mp_bluetooth_nimble_ble_state); + int ret = 0; if (mp_bluetooth_nimble_ble_state == MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF) { - return; + return 0; } // Must call ble_hs_stop() in a port-specific way to stop the background @@ -675,7 +685,7 @@ void mp_bluetooth_deinit(void) { DEBUG_printf("mp_bluetooth_deinit: starting port shutdown\n"); - mp_bluetooth_nimble_port_shutdown(); + ret = mp_bluetooth_nimble_port_shutdown(); assert(mp_bluetooth_nimble_ble_state == MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF); } else { mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF; @@ -692,6 +702,7 @@ void mp_bluetooth_deinit(void) { #endif DEBUG_printf("mp_bluetooth_deinit: shut down\n"); + return ret; } bool mp_bluetooth_is_active(void) { diff --git a/extmod/nimble/modbluetooth_nimble.h b/extmod/nimble/modbluetooth_nimble.h index d9bef64920..0dd20eb658 100644 --- a/extmod/nimble/modbluetooth_nimble.h +++ b/extmod/nimble/modbluetooth_nimble.h @@ -84,7 +84,7 @@ void mp_bluetooth_nimble_port_hci_deinit(void); void mp_bluetooth_nimble_port_start(void); // Tell the port to stop its background task. -void mp_bluetooth_nimble_port_shutdown(void); +int mp_bluetooth_nimble_port_shutdown(void); // --- Called by the HCI UART layer to let us know when packets have been sent. void mp_bluetooth_nimble_sent_hci_packet(void); diff --git a/lib/libhydrogen b/lib/libhydrogen -Subproject 5c5d513093075f7245ea522101b17c50aa579af +Subproject bbca575b62510bfdc6dd927a4bfa7df4a51cb84 diff --git a/mpy-cross/main.c b/mpy-cross/main.c index 7ab95149c7..16f749ae4d 100644 --- a/mpy-cross/main.c +++ b/mpy-cross/main.c @@ -130,7 +130,7 @@ static int usage(char **argv) { "Target specific options:\n" "-msmall-int-bits=number : set the maximum bits used to encode a small-int\n" "-march=<arch> : set architecture for native emitter;\n" - " x86, x64, armv6, armv6m, armv7m, armv7em, armv7emsp, armv7emdp, xtensa, xtensawin, rv32imc, debug\n" + " x86, x64, armv6, armv6m, armv7m, armv7em, armv7emsp, armv7emdp, xtensa, xtensawin, rv32imc, host, debug\n" "\n" "Implementation specific options:\n", argv[0] ); @@ -350,6 +350,15 @@ MP_NOINLINE int main_(int argc, char **argv) { } } + #if MICROPY_EMIT_NATIVE + if ((MP_STATE_VM(default_emit_opt) == MP_EMIT_OPT_NATIVE_PYTHON + || MP_STATE_VM(default_emit_opt) == MP_EMIT_OPT_VIPER) + && mp_dynamic_compiler.native_arch == MP_NATIVE_ARCH_NONE) { + mp_printf(&mp_stderr_print, "arch not specified\n"); + exit(1); + } + #endif + if (input_file == NULL) { mp_printf(&mp_stderr_print, "no input file\n"); exit(1); diff --git a/ports/esp32/adc.c b/ports/esp32/adc.c index 83af6dce0b..d0e5e81330 100644 --- a/ports/esp32/adc.c +++ b/ports/esp32/adc.c @@ -26,83 +26,73 @@ */ #include "py/mphal.h" +#include "py/mperrno.h" #include "adc.h" -#include "driver/adc.h" +#include "esp_adc/adc_oneshot.h" #include "esp_adc/adc_cali_scheme.h" -#define DEFAULT_VREF 1100 +static esp_err_t ensure_adc_calibration(machine_adc_block_obj_t *self, adc_atten_t atten); -void madcblock_bits_helper(machine_adc_block_obj_t *self, mp_int_t bits) { - if (bits < SOC_ADC_RTC_MIN_BITWIDTH && bits > SOC_ADC_RTC_MAX_BITWIDTH) { - // Invalid value for the current chip, raise exception in the switch below. - bits = -1; +void adc_is_init_guard(machine_adc_block_obj_t *self) { + if (!self->handle) { + mp_raise_OSError(MP_EPERM); } - switch (bits) { - case 9: - self->width = ADC_BITWIDTH_9; - break; - case 10: - self->width = ADC_BITWIDTH_10; - break; - case 11: - self->width = ADC_BITWIDTH_11; - break; - case 12: - self->width = ADC_BITWIDTH_12; - break; - case 13: - self->width = ADC_BITWIDTH_13; - break; - default: - mp_raise_ValueError(MP_ERROR_TEXT("invalid bits")); - } - self->bits = bits; +} - if (self->unit_id == ADC_UNIT_1) { - adc1_config_width(self->width); - } +esp_err_t apply_self_adc_channel_atten(const machine_adc_obj_t *self, uint8_t atten) { + adc_is_init_guard(self->block); + + adc_oneshot_chan_cfg_t config = { + .atten = atten, + .bitwidth = self->block->bitwidth, + }; + esp_err_t ret = adc_oneshot_config_channel(self->block->handle, self->channel_id, &config); + return ret; } mp_int_t madcblock_read_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id) { - int raw = 0; - if (self->unit_id == ADC_UNIT_1) { - raw = adc1_get_raw(channel_id); - } else { - #if (SOC_ADC_PERIPH_NUM >= 2) - check_esp_err(adc2_get_raw(channel_id, self->width, &raw)); - #endif - } - return raw; + adc_is_init_guard(self); + + int reading = 0; + adc_oneshot_read(self->handle, channel_id, &reading); + return reading; +} + +/* +During testing, it turned out that the function `adc_cali_raw_to_voltage` does not account for the lower resolution, +instead it expects the full resolution value as an argument, hence the scaling applied here +*/ +mp_int_t madcblock_read_uv_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id, adc_atten_t atten) { + int raw = madcblock_read_helper(self, channel_id); + int uv = 0; + + check_esp_err(ensure_adc_calibration(self, atten)); + check_esp_err(adc_cali_raw_to_voltage(self->calib[atten], (raw << (ADC_WIDTH_MAX - self->bitwidth)), &uv)); + return (mp_int_t)uv * 1000; } static esp_err_t ensure_adc_calibration(machine_adc_block_obj_t *self, adc_atten_t atten) { - if (self->handle[atten] != NULL) { + if (self->calib[atten] != NULL) { return ESP_OK; } + esp_err_t ret = ESP_OK; + #if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED adc_cali_curve_fitting_config_t cali_config = { .unit_id = self->unit_id, .atten = atten, - .bitwidth = self->width, + .bitwidth = self->bitwidth, }; - return adc_cali_create_scheme_curve_fitting(&cali_config, &self->handle[atten]); + ret = adc_cali_create_scheme_curve_fitting(&cali_config, &self->calib[atten]); #else adc_cali_line_fitting_config_t cali_config = { .unit_id = self->unit_id, .atten = atten, - .bitwidth = self->width, + .bitwidth = self->bitwidth, }; - return adc_cali_create_scheme_line_fitting(&cali_config, &self->handle[atten]); + ret = adc_cali_create_scheme_line_fitting(&cali_config, &self->calib[atten]); #endif -} -mp_int_t madcblock_read_uv_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id, adc_atten_t atten) { - int raw = madcblock_read_helper(self, channel_id); - int uv; - - check_esp_err(ensure_adc_calibration(self, atten)); - check_esp_err(adc_cali_raw_to_voltage(self->handle[atten], raw, &uv)); - - return (mp_int_t)uv * 1000; + return ret; } diff --git a/ports/esp32/adc.h b/ports/esp32/adc.h index 5688e0a29a..ebf7fcc21a 100644 --- a/ports/esp32/adc.h +++ b/ports/esp32/adc.h @@ -29,17 +29,45 @@ #define MICROPY_INCLUDED_ESP32_ADC_H #include "py/runtime.h" -#include "esp_adc_cal.h" +#include "esp_adc/adc_oneshot.h" #include "esp_adc/adc_cali_scheme.h" -#define ADC_ATTEN_MAX SOC_ADC_ATTEN_NUM +#define ADC_ATTEN_COUNT SOC_ADC_ATTEN_NUM +#define ADC_ATTEN_MIN ADC_ATTEN_DB_0 +#define ADC_ATTEN_MAX ADC_ATTEN_DB_11 + +/* +https://github.com/espressif/esp-idf/issues/13128 +https://github.com/espressif/esp-idf/blob/release/v5.2/components/soc/esp32s3/include/soc/soc_caps.h +https://docs.espressif.com/projects/esp-chip-errata/en/latest/esp32s2/03-errata-description/index.html +https://docs.espressif.com/projects/esp-idf/en/v4.2/esp32/api-reference/peripherals/adc.html + +Looks like only the original esp32 is capable of bitwidth adjustment, all others are stuck at 12 bits, +except the S2, which is locked at 13 bits, otherwise attenuation doesn't work. +*/ +#if CONFIG_IDF_TARGET_ESP32S2 + +#define ADC_WIDTH_MIN ADC_BITWIDTH_13 +#define ADC_WIDTH_MAX ADC_BITWIDTH_13 + +#elif CONFIG_IDF_TARGET_ESP32 + +#define ADC_WIDTH_MIN ADC_BITWIDTH_9 +#define ADC_WIDTH_MAX ADC_BITWIDTH_12 + +#else + +#define ADC_WIDTH_MIN ADC_BITWIDTH_12 +#define ADC_WIDTH_MAX ADC_BITWIDTH_12 + +#endif typedef struct _machine_adc_block_obj_t { mp_obj_base_t base; adc_unit_t unit_id; - mp_int_t bits; - adc_bits_width_t width; - adc_cali_handle_t handle[ADC_ATTEN_MAX]; + adc_oneshot_unit_handle_t handle; + adc_bitwidth_t bitwidth; + adc_cali_handle_t calib[ADC_ATTEN_COUNT]; } machine_adc_block_obj_t; typedef struct _machine_adc_obj_t { @@ -51,11 +79,18 @@ typedef struct _machine_adc_obj_t { extern machine_adc_block_obj_t madcblock_obj[]; -void madcblock_bits_helper(machine_adc_block_obj_t *self, mp_int_t bits); +esp_err_t apply_self_adc_channel_atten(const machine_adc_obj_t *self, uint8_t atten); + mp_int_t madcblock_read_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id); mp_int_t madcblock_read_uv_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id, adc_atten_t atten); const machine_adc_obj_t *madc_search_helper(machine_adc_block_obj_t *block, adc_channel_t channel_id, gpio_num_t gpio_id); void madc_init_helper(const machine_adc_obj_t *self, size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +mp_int_t mp_machine_adc_atten_get_helper(const machine_adc_obj_t *self); +void mp_machine_adc_atten_set_helper(const machine_adc_obj_t *self, mp_int_t atten); + +mp_int_t mp_machine_adc_width_get_helper(const machine_adc_obj_t *self); +void mp_machine_adc_block_width_set_helper(machine_adc_block_obj_t *self, mp_int_t width); + #endif // MICROPY_INCLUDED_ESP32_ADC_H diff --git a/ports/esp32/boards/ARDUINO_NANO_ESP32/board.json b/ports/esp32/boards/ARDUINO_NANO_ESP32/board.json index 9d0016017f..936a498b5e 100644 --- a/ports/esp32/boards/ARDUINO_NANO_ESP32/board.json +++ b/ports/esp32/boards/ARDUINO_NANO_ESP32/board.json @@ -17,8 +17,8 @@ "ABX00092_01.iso_1000x750.jpg" ], "mcu": "esp32s3", - "product": "Arduino Nano ESP32", + "product": "Nano ESP32", "thumbnail": "", - "url": "https://store.arduino.cc/products/arduino-nano-esp32", + "url": "https://docs.arduino.cc/hardware/nano-esp32/", "vendor": "Arduino" } diff --git a/ports/esp32/boards/LILYGO_TTGO_LORA32/board.json b/ports/esp32/boards/LILYGO_TTGO_LORA32/board.json index d68a9baadd..7521c01a6b 100644 --- a/ports/esp32/boards/LILYGO_TTGO_LORA32/board.json +++ b/ports/esp32/boards/LILYGO_TTGO_LORA32/board.json @@ -19,8 +19,8 @@ "lilygo-ttgo-lora-32-v1-6.jpg" ], "mcu": "esp32", - "product": "LILYGO TTGO LoRa32", + "product": "TTGO LoRa32", "thumbnail": "", - "url": "http://www.lilygo.cn/prod_view.aspx?TypeId=50060&Id=1270&FId=t3:50060:3", + "url": "https://www.lilygo.cc/products/lora3", "vendor": "LILYGO" } diff --git a/ports/esp32/boards/M5STACK_ATOM/board.json b/ports/esp32/boards/M5STACK_ATOM/board.json index 3a1e7ce359..c3908a4779 100644 --- a/ports/esp32/boards/M5STACK_ATOM/board.json +++ b/ports/esp32/boards/M5STACK_ATOM/board.json @@ -18,8 +18,8 @@ "m5stack_atom.jpg" ], "mcu": "esp32", - "product": "M5 Stack Atom", + "product": "Atom", "thumbnail": "", - "url": "https://m5stack.com/", - "vendor": "M5 Stack" + "url": "https://shop.m5stack.com/products/atom-matrix-esp32-development-kit", + "vendor": "M5Stack" } diff --git a/ports/esp32/boards/M5STACK_ATOMS3_LITE/board.json b/ports/esp32/boards/M5STACK_ATOMS3_LITE/board.json index fe0e97f9f9..26b1a4d54a 100644 --- a/ports/esp32/boards/M5STACK_ATOMS3_LITE/board.json +++ b/ports/esp32/boards/M5STACK_ATOMS3_LITE/board.json @@ -20,5 +20,5 @@ "product": "AtomS3 Lite", "thumbnail": "", "url": "https://shop.m5stack.com/products/atoms3-lite-esp32s3-dev-kit", - "vendor": "M5 Stack" + "vendor": "M5Stack" } diff --git a/ports/esp32/boards/M5STACK_NANOC6/board.json b/ports/esp32/boards/M5STACK_NANOC6/board.json index 087851ae5e..ddf3406c83 100644 --- a/ports/esp32/boards/M5STACK_NANOC6/board.json +++ b/ports/esp32/boards/M5STACK_NANOC6/board.json @@ -18,7 +18,7 @@ "m5stack_nanoc6.jpg" ], "mcu": "esp32c6", - "product": "M5Stack NanoC6", + "product": "NanoC6", "url": "https://shop.m5stack.com/products/m5stack-nanoc6-dev-kit", "vendor": "M5Stack" } diff --git a/ports/esp32/boards/OLIMEX_ESP32_EVB/board.json b/ports/esp32/boards/OLIMEX_ESP32_EVB/board.json index 3eb9a5e0a7..198f1d4b6d 100644 --- a/ports/esp32/boards/OLIMEX_ESP32_EVB/board.json +++ b/ports/esp32/boards/OLIMEX_ESP32_EVB/board.json @@ -18,8 +18,8 @@ "ESP32-EVB_Rev_K1.png" ], "mcu": "esp32", - "product": "Olimex ESP32 EVB", + "product": "ESP32 EVB", "thumbnail": "", - "url": "https://www.olimex.com/Products/IoT/ESP32/ESP32-EVB/open-source-hardware", - "vendor": "OLIMEX" + "url": "https://www.olimex.com/Products/IoT/ESP32/ESP32-EVB", + "vendor": "Olimex" } diff --git a/ports/esp32/boards/OLIMEX_ESP32_POE/board.json b/ports/esp32/boards/OLIMEX_ESP32_POE/board.json index cda26178d7..1b00d213c7 100644 --- a/ports/esp32/boards/OLIMEX_ESP32_POE/board.json +++ b/ports/esp32/boards/OLIMEX_ESP32_POE/board.json @@ -19,8 +19,8 @@ "ESP32-POE-ISO-1.jpg" ], "mcu": "esp32", - "product": "Olimex ESP32 POE", + "product": "ESP32 POE", "thumbnail": "", - "url": "https://www.olimex.com/", - "vendor": "OLIMEX" + "url": "https://www.olimex.com/Products/IoT/ESP32/ESP32-POE", + "vendor": "Olimex" } diff --git a/ports/esp32/boards/SIL_WESP32/board.json b/ports/esp32/boards/SIL_WESP32/board.json index 53a50f3286..7126fa7ea1 100644 --- a/ports/esp32/boards/SIL_WESP32/board.json +++ b/ports/esp32/boards/SIL_WESP32/board.json @@ -18,7 +18,7 @@ "wesp32-top.jpg" ], "mcu": "esp32", - "product": "SIL WESP32", + "product": "wESP32", "thumbnail": "", "url": "https://wesp32.com/", "vendor": "Silicognition" diff --git a/ports/esp32/boards/sdkconfig.base b/ports/esp32/boards/sdkconfig.base index 5595444e86..823b916d96 100644 --- a/ports/esp32/boards/sdkconfig.base +++ b/ports/esp32/boards/sdkconfig.base @@ -117,7 +117,6 @@ CONFIG_ADC_CAL_LUT_ENABLE=y CONFIG_UART_ISR_IN_IRAM=y # IDF 5 deprecated -CONFIG_ADC_SUPPRESS_DEPRECATE_WARN=y CONFIG_RMT_SUPPRESS_DEPRECATE_WARN=y CONFIG_ETH_USE_SPI_ETHERNET=y diff --git a/ports/esp32/machine_adc.c b/ports/esp32/machine_adc.c index be1725c370..02acaa22da 100644 --- a/ports/esp32/machine_adc.c +++ b/ports/esp32/machine_adc.c @@ -30,7 +30,7 @@ #include "py/mphal.h" #include "adc.h" -#include "driver/adc.h" +#include "esp_adc/adc_oneshot.h" #define ADCBLOCK1 (&madcblock_obj[0]) #define ADCBLOCK2 (&madcblock_obj[1]) @@ -126,20 +126,8 @@ static const machine_adc_obj_t madc_obj[] = { #endif }; -// These values are initialised to 0, which means the corresponding ADC channel is not initialised. -// The madc_atten_get/madc_atten_set functions store (atten+1) here so that the uninitialised state -// can be distinguished from the initialised state. static uint8_t madc_obj_atten[MP_ARRAY_SIZE(madc_obj)]; -static inline adc_atten_t madc_atten_get(const machine_adc_obj_t *self) { - uint8_t value = madc_obj_atten[self - &madc_obj[0]]; - return value == 0 ? ADC_ATTEN_MAX : value - 1; -} - -static inline void madc_atten_set(const machine_adc_obj_t *self, adc_atten_t atten) { - madc_obj_atten[self - &madc_obj[0]] = atten + 1; -} - const machine_adc_obj_t *madc_search_helper(machine_adc_block_obj_t *block, adc_channel_t channel_id, gpio_num_t gpio_id) { for (int i = 0; i < MP_ARRAY_SIZE(madc_obj); i++) { const machine_adc_obj_t *adc = &madc_obj[i]; @@ -152,22 +140,7 @@ const machine_adc_obj_t *madc_search_helper(machine_adc_block_obj_t *block, adc_ static void mp_machine_adc_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { const machine_adc_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_printf(print, "ADC(Pin(%u), atten=%u)", self->gpio_id, madc_atten_get(self)); -} - -static void madc_atten_helper(const machine_adc_obj_t *self, mp_int_t atten) { - esp_err_t err = ESP_FAIL; - if (self->block->unit_id == ADC_UNIT_1) { - err = adc1_config_channel_atten(self->channel_id, atten); - } else { - #if SOC_ADC_PERIPH_NUM >= 2 - err = adc2_config_channel_atten(self->channel_id, atten); - #endif - } - if (err != ESP_OK) { - mp_raise_ValueError(MP_ERROR_TEXT("invalid atten")); - } - madc_atten_set(self, atten); + mp_printf(print, "ADC(Pin(%u), atten=%u)", self->gpio_id, mp_machine_adc_atten_get_helper(self)); } void madc_init_helper(const machine_adc_obj_t *self, size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -182,18 +155,32 @@ void madc_init_helper(const machine_adc_obj_t *self, size_t n_pos_args, const mp mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_pos_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - mp_int_t atten = args[ARG_atten].u_int; - if (atten != -1) { - madc_atten_helper(self, atten); - } else if (madc_atten_get(self) == ADC_ATTEN_MAX) { - madc_atten_helper(self, ADC_ATTEN_DB_0); + + if (!self->block->handle) { + adc_oneshot_unit_init_cfg_t init_config = { + .unit_id = self->block->unit_id + }; + check_esp_err(adc_oneshot_new_unit(&init_config, &self->block->handle)); } + + mp_int_t atten = args[ARG_atten].u_int; + mp_machine_adc_atten_set_helper(self, atten != -1 ? atten : ADC_ATTEN_MAX); + mp_machine_adc_block_width_set_helper(self->block, ADC_WIDTH_MAX); + apply_self_adc_channel_atten(self, mp_machine_adc_atten_get_helper(self)); + } static void mp_machine_adc_init_helper(machine_adc_obj_t *self, size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { madc_init_helper(self, n_pos_args, pos_args, kw_args); } +static void mp_machine_adc_deinit(machine_adc_obj_t *self) { + if (self->block->handle) { + check_esp_err(adc_oneshot_del_unit(self->block->handle)); + self->block->handle = NULL; + } +} + static mp_obj_t mp_machine_adc_make_new(const mp_obj_type_t *type, size_t n_pos_args, size_t n_kw_args, const mp_obj_t *args) { mp_arg_check_num(n_pos_args, n_kw_args, 1, MP_OBJ_FUN_ARGS_MAX, true); gpio_num_t gpio_id = machine_pin_get_id(args[0]); @@ -202,10 +189,6 @@ static mp_obj_t mp_machine_adc_make_new(const mp_obj_type_t *type, size_t n_pos_ mp_raise_ValueError(MP_ERROR_TEXT("invalid pin")); } - if (self->block->width == -1) { - madcblock_bits_helper(self->block, self->block->bits); - } - mp_map_t kw_args; mp_map_init_fixed_table(&kw_args, n_kw_args, args + n_pos_args); madc_init_helper(self, n_pos_args - 1, args + 1, &kw_args); @@ -225,20 +208,46 @@ static mp_int_t mp_machine_adc_read(machine_adc_obj_t *self) { static mp_int_t mp_machine_adc_read_u16(machine_adc_obj_t *self) { mp_uint_t raw = madcblock_read_helper(self->block, self->channel_id); // Scale raw reading to 16 bit value using a Taylor expansion (for 8 <= bits <= 16) - mp_int_t bits = self->block->bits; + mp_int_t bits = mp_machine_adc_width_get_helper(self); mp_uint_t u16 = raw << (16 - bits) | raw >> (2 * bits - 16); return u16; } static mp_int_t mp_machine_adc_read_uv(machine_adc_obj_t *self) { - adc_atten_t atten = madc_atten_get(self); - return madcblock_read_uv_helper(self->block, self->channel_id, atten); + return madcblock_read_uv_helper(self->block, self->channel_id, mp_machine_adc_atten_get_helper(self)); +} + +mp_int_t mp_machine_adc_atten_get_helper(const machine_adc_obj_t *self) { + uint8_t value = madc_obj_atten[self - &madc_obj[0]]; + return value == 0 ? ADC_ATTEN_MAX : value - 1; +} + +void mp_machine_adc_atten_set_helper(const machine_adc_obj_t *self, mp_int_t atten) { + if (atten < ADC_ATTEN_MIN || atten > ADC_ATTEN_MAX) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid attenuation")); + } + + madc_obj_atten[self - &madc_obj[0]] = atten + 1; } static void mp_machine_adc_atten_set(machine_adc_obj_t *self, mp_int_t atten) { - madc_atten_helper(self, atten); + mp_machine_adc_atten_set_helper(self, atten); + apply_self_adc_channel_atten(self, mp_machine_adc_atten_get_helper(self)); +} + +mp_int_t mp_machine_adc_width_get_helper(const machine_adc_obj_t *self) { + return self->block->bitwidth; +} + +void mp_machine_adc_block_width_set_helper(machine_adc_block_obj_t *self, mp_int_t width) { + if (width < ADC_WIDTH_MIN || width > ADC_WIDTH_MAX) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid bit-width")); + } + + self->bitwidth = width; } static void mp_machine_adc_width_set(machine_adc_obj_t *self, mp_int_t width) { - madcblock_bits_helper(self->block, width); + mp_machine_adc_block_width_set_helper(self->block, width); + apply_self_adc_channel_atten(self, mp_machine_adc_atten_get_helper(self)); } diff --git a/ports/esp32/machine_adc_block.c b/ports/esp32/machine_adc_block.c index a373603b7e..6b06b432c2 100644 --- a/ports/esp32/machine_adc_block.c +++ b/ports/esp32/machine_adc_block.c @@ -29,25 +29,21 @@ #include "py/mphal.h" #include "adc.h" -#include "driver/adc.h" +#include "esp_adc/adc_oneshot.h" machine_adc_block_obj_t madcblock_obj[] = { - {{&machine_adc_block_type}, ADC_UNIT_1, SOC_ADC_RTC_MAX_BITWIDTH, -1, {0}}, + {{&machine_adc_block_type}, ADC_UNIT_1, NULL, ADC_WIDTH_MAX, {0}}, #if SOC_ADC_PERIPH_NUM > 1 - {{&machine_adc_block_type}, ADC_UNIT_2, SOC_ADC_RTC_MAX_BITWIDTH, -1, {0}}, + {{&machine_adc_block_type}, ADC_UNIT_2, NULL, ADC_WIDTH_MAX, {0}}, #endif }; static void mp_machine_adc_block_print(const mp_print_t *print, machine_adc_block_obj_t *self) { - mp_printf(print, "ADCBlock(%u, bits=%u)", self->unit_id, self->bits); + mp_printf(print, "ADCBlock(%u, bits=%u)", self->unit_id, self->bitwidth); } static void mp_machine_adc_block_bits_set(machine_adc_block_obj_t *self, mp_int_t bits) { - if (bits != -1) { - madcblock_bits_helper(self, bits); - } else if (self->width == -1) { - madcblock_bits_helper(self, self->bits); - } + mp_machine_adc_block_width_set_helper(self, bits); } static machine_adc_block_obj_t *mp_machine_adc_block_get(mp_int_t unit) { diff --git a/ports/esp32/machine_timer.c b/ports/esp32/machine_timer.c index 34d49c79d2..a104288f6e 100644 --- a/ports/esp32/machine_timer.c +++ b/ports/esp32/machine_timer.c @@ -50,12 +50,13 @@ const mp_obj_type_t machine_timer_type; static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +static mp_obj_t machine_timer_deinit(mp_obj_t self_in); void machine_timer_deinit_all(void) { // Disable, deallocate and remove all timers from list machine_timer_obj_t **t = &MP_STATE_PORT(machine_timer_obj_head); while (*t != NULL) { - machine_timer_disable(*t); + machine_timer_deinit(*t); machine_timer_obj_t *next = (*t)->next; m_del_obj(machine_timer_obj_t, *t); *t = next; @@ -96,6 +97,7 @@ machine_timer_obj_t *machine_timer_create(mp_uint_t timer) { self = mp_obj_malloc(machine_timer_obj_t, &machine_timer_type); self->group = group; self->index = index; + self->handle = NULL; // Add the timer to the linked-list of timers self->next = MP_STATE_PORT(machine_timer_obj_head); @@ -131,9 +133,8 @@ void machine_timer_disable(machine_timer_obj_t *self) { } if (self->handle) { - // Free the interrupt handler. - esp_intr_free(self->handle); - self->handle = NULL; + // Disable the interrupt + ESP_ERROR_CHECK(esp_intr_disable(self->handle)); } // We let the disabled timer stay in the list, as it might be @@ -150,12 +151,16 @@ static void machine_timer_isr(void *self_in) { if (self->repeat) { timer_ll_enable_alarm(self->hal_context.dev, self->index, true); } - mp_sched_schedule(self->callback, self); - mp_hal_wake_main_task_from_isr(); + self->handler(self); } } -void machine_timer_enable(machine_timer_obj_t *self, void (*timer_isr)) { +static void machine_timer_isr_handler(machine_timer_obj_t *self) { + mp_sched_schedule(self->callback, self); + mp_hal_wake_main_task_from_isr(); +} + +void machine_timer_enable(machine_timer_obj_t *self) { // Initialise the timer. timer_hal_init(&self->hal_context, self->group, self->index); timer_ll_enable_counter(self->hal_context.dev, self->index, false); @@ -167,10 +172,17 @@ void machine_timer_enable(machine_timer_obj_t *self, void (*timer_isr)) { // Allocate and enable the alarm interrupt. timer_ll_enable_intr(self->hal_context.dev, TIMER_LL_EVENT_ALARM(self->index), false); timer_ll_clear_intr_status(self->hal_context.dev, TIMER_LL_EVENT_ALARM(self->index)); - ESP_ERROR_CHECK( - esp_intr_alloc(timer_group_periph_signals.groups[self->group].timer_irq_id[self->index], - TIMER_FLAGS, timer_isr, self, &self->handle) - ); + if (self->handle) { + ESP_ERROR_CHECK(esp_intr_enable(self->handle)); + } else { + ESP_ERROR_CHECK(esp_intr_alloc( + timer_group_periph_signals.groups[self->group].timer_irq_id[self->index], + TIMER_FLAGS, + machine_timer_isr, + self, + &self->handle + )); + } timer_ll_enable_intr(self->hal_context.dev, TIMER_LL_EVENT_ALARM(self->index), true); // Enable the alarm to trigger at the given period. @@ -224,16 +236,22 @@ static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, mp_uint_t n } self->repeat = args[ARG_mode].u_int; + self->handler = machine_timer_isr_handler; self->callback = args[ARG_callback].u_obj; - self->handle = NULL; - machine_timer_enable(self, machine_timer_isr); + machine_timer_enable(self); return mp_const_none; } static mp_obj_t machine_timer_deinit(mp_obj_t self_in) { - machine_timer_disable(self_in); + machine_timer_obj_t *self = self_in; + + machine_timer_disable(self); + if (self->handle) { + ESP_ERROR_CHECK(esp_intr_free(self->handle)); + self->handle = NULL; + } return mp_const_none; } diff --git a/ports/esp32/machine_timer.h b/ports/esp32/machine_timer.h index 914bedd86b..10fe2f39c9 100644 --- a/ports/esp32/machine_timer.h +++ b/ports/esp32/machine_timer.h @@ -55,12 +55,13 @@ typedef struct _machine_timer_obj_t { mp_obj_t callback; intr_handle_t handle; + void (*handler)(struct _machine_timer_obj_t *timer); struct _machine_timer_obj_t *next; } machine_timer_obj_t; machine_timer_obj_t *machine_timer_create(mp_uint_t timer); -void machine_timer_enable(machine_timer_obj_t *self, void (*timer_isr)); +void machine_timer_enable(machine_timer_obj_t *self); void machine_timer_disable(machine_timer_obj_t *self); #endif // MICROPY_INCLUDED_ESP32_MACHINE_TIMER_H diff --git a/ports/esp32/machine_uart.c b/ports/esp32/machine_uart.c index e5857e894b..982d9a7e27 100644 --- a/ports/esp32/machine_uart.c +++ b/ports/esp32/machine_uart.c @@ -110,30 +110,19 @@ static const char *_parity_name[] = {"None", "1", "0"}; { MP_ROM_QSTR(MP_QSTR_IRQ_RXIDLE), MP_ROM_INT(UART_IRQ_RXIDLE) }, \ { MP_ROM_QSTR(MP_QSTR_IRQ_BREAK), MP_ROM_INT(UART_IRQ_BREAK) }, \ -static void uart_timer_callback(void *self_in) { - machine_timer_obj_t *self = self_in; - - uint32_t intr_status = timer_ll_get_intr_status(self->hal_context.dev); - - if (intr_status & TIMER_LL_EVENT_ALARM(self->index)) { - timer_ll_clear_intr_status(self->hal_context.dev, TIMER_LL_EVENT_ALARM(self->index)); - if (self->repeat) { - timer_ll_enable_alarm(self->hal_context.dev, self->index, true); - } - } - +static void uart_timer_callback(machine_timer_obj_t *timer) { // The UART object is referred here by the callback field. - machine_uart_obj_t *uart = (machine_uart_obj_t *)self->callback; - if (uart->rxidle_state == RXIDLE_ALERT) { + machine_uart_obj_t *self = (machine_uart_obj_t *)timer->callback; + if (self->rxidle_state == RXIDLE_ALERT) { // At the first call, just switch the state - uart->rxidle_state = RXIDLE_ARMED; - } else if (uart->rxidle_state == RXIDLE_ARMED) { + self->rxidle_state = RXIDLE_ARMED; + } else if (self->rxidle_state == RXIDLE_ARMED) { // At the second call, run the irq callback and stop the timer - uart->rxidle_state = RXIDLE_STANDBY; - uart->mp_irq_flags = UART_IRQ_RXIDLE; - mp_irq_handler(uart->mp_irq_obj); + self->rxidle_state = RXIDLE_STANDBY; + self->mp_irq_flags = UART_IRQ_RXIDLE; + mp_irq_handler(self->mp_irq_obj); mp_hal_wake_main_task_from_isr(); - machine_timer_disable(uart->rxidle_timer); + machine_timer_disable(self->rxidle_timer); } } @@ -150,9 +139,7 @@ static void uart_event_task(void *self_in) { if (self->mp_irq_trigger & UART_IRQ_RXIDLE) { if (self->rxidle_state != RXIDLE_INACTIVE) { if (self->rxidle_state == RXIDLE_STANDBY) { - self->rxidle_timer->repeat = true; - self->rxidle_timer->handle = NULL; - machine_timer_enable(self->rxidle_timer, uart_timer_callback); + machine_timer_enable(self->rxidle_timer); } } self->rxidle_state = RXIDLE_ALERT; @@ -553,11 +540,11 @@ static void uart_irq_configure_timer(machine_uart_obj_t *self, mp_uint_t trigger } self->rxidle_period = period; self->rxidle_timer->period = period; + self->rxidle_timer->handler = uart_timer_callback; // The Python callback is not used. So use this // data field to hold a reference to the UART object. self->rxidle_timer->callback = self; self->rxidle_timer->repeat = true; - self->rxidle_timer->handle = NULL; self->rxidle_state = RXIDLE_STANDBY; } } diff --git a/ports/esp32/modesp32.c b/ports/esp32/modesp32.c index 0296ddf10e..4572e7b68b 100644 --- a/ports/esp32/modesp32.c +++ b/ports/esp32/modesp32.c @@ -30,7 +30,6 @@ #include <time.h> #include <sys/time.h> #include "driver/gpio.h" -#include "driver/adc.h" #include "esp_heap_caps.h" #include "multi_heap.h" diff --git a/ports/esp32/modsocket.c b/ports/esp32/modsocket.c index 7ea5e855d3..2050d1d04d 100644 --- a/ports/esp32/modsocket.c +++ b/ports/esp32/modsocket.c @@ -611,7 +611,7 @@ static MP_DEFINE_CONST_FUN_OBJ_2(socket_setblocking_obj, socket_setblocking); // XXX this can end up waiting a very long time if the content is dribbled in one character // at a time, as the timeout resets each time a recvfrom succeeds ... this is probably not // good behaviour. -static mp_uint_t _socket_read_data(mp_obj_t self_in, void *buf, size_t size, +static mp_uint_t _socket_read_data(mp_obj_t self_in, void *buf, size_t size, mp_int_t flags, struct sockaddr *from, socklen_t *from_len, int *errcode) { socket_obj_t *sock = MP_OBJ_TO_PTR(self_in); @@ -645,7 +645,7 @@ static mp_uint_t _socket_read_data(mp_obj_t self_in, void *buf, size_t size, if (release_gil) { MP_THREAD_GIL_EXIT(); } - int r = lwip_recvfrom(sock->fd, buf, size, 0, from, from_len); + int r = lwip_recvfrom(sock->fd, buf, size, flags, from, from_len); if (release_gil) { MP_THREAD_GIL_ENTER(); } @@ -655,7 +655,7 @@ static mp_uint_t _socket_read_data(mp_obj_t self_in, void *buf, size_t size, if (r >= 0) { return r; } - if (errno != EWOULDBLOCK) { + if (errno != EWOULDBLOCK || (flags & MSG_DONTWAIT)) { *errcode = errno; return MP_STREAM_ERROR; } @@ -666,14 +666,17 @@ static mp_uint_t _socket_read_data(mp_obj_t self_in, void *buf, size_t size, return MP_STREAM_ERROR; } -mp_obj_t _socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in, +mp_obj_t _socket_recvfrom(size_t n_args, const mp_obj_t *args, struct sockaddr *from, socklen_t *from_len) { - size_t len = mp_obj_get_int(len_in); + mp_obj_t self_in = args[0]; + size_t len = mp_obj_get_int(args[1]); + int flags = n_args > 2 ? mp_obj_get_int(args[2]) : 0; + vstr_t vstr; vstr_init_len(&vstr, len); int errcode; - mp_uint_t ret = _socket_read_data(self_in, vstr.buf, len, from, from_len, &errcode); + mp_uint_t ret = _socket_read_data(self_in, vstr.buf, len, flags, from, from_len, &errcode); if (ret == MP_STREAM_ERROR) { mp_raise_OSError(errcode); } @@ -682,17 +685,17 @@ mp_obj_t _socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in, return mp_obj_new_bytes_from_vstr(&vstr); } -static mp_obj_t socket_recv(mp_obj_t self_in, mp_obj_t len_in) { - return _socket_recvfrom(self_in, len_in, NULL, NULL); +static mp_obj_t socket_recv(size_t n_args, const mp_obj_t *args) { + return _socket_recvfrom(n_args, args, NULL, NULL); } -static MP_DEFINE_CONST_FUN_OBJ_2(socket_recv_obj, socket_recv); +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_recv_obj, 2, 3, socket_recv); -static mp_obj_t socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in) { +static mp_obj_t socket_recvfrom(size_t n_args, const mp_obj_t *args) { struct sockaddr from; socklen_t fromlen = sizeof(from); mp_obj_t tuple[2]; - tuple[0] = _socket_recvfrom(self_in, len_in, &from, &fromlen); + tuple[0] = _socket_recvfrom(n_args, args, &from, &fromlen); uint8_t *ip = (uint8_t *)&((struct sockaddr_in *)&from)->sin_addr; mp_uint_t port = lwip_ntohs(((struct sockaddr_in *)&from)->sin_port); @@ -700,7 +703,7 @@ static mp_obj_t socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in) { return mp_obj_new_tuple(2, tuple); } -static MP_DEFINE_CONST_FUN_OBJ_2(socket_recvfrom_obj, socket_recvfrom); +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_recvfrom_obj, 2, 3, socket_recvfrom); int _socket_send(socket_obj_t *sock, const char *data, size_t datalen) { int sentlen = 0; @@ -789,7 +792,7 @@ static mp_obj_t socket_makefile(size_t n_args, const mp_obj_t *args) { static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_makefile_obj, 1, 3, socket_makefile); static mp_uint_t socket_stream_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) { - return _socket_read_data(self_in, buf, size, NULL, NULL, errcode); + return _socket_read_data(self_in, buf, size, 0, NULL, NULL, errcode); } static mp_uint_t socket_stream_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) { @@ -1010,6 +1013,8 @@ static const mp_rom_map_elem_t mp_module_socket_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_SO_BINDTODEVICE), MP_ROM_INT(SO_BINDTODEVICE) }, { MP_ROM_QSTR(MP_QSTR_IP_ADD_MEMBERSHIP), MP_ROM_INT(IP_ADD_MEMBERSHIP) }, { MP_ROM_QSTR(MP_QSTR_TCP_NODELAY), MP_ROM_INT(TCP_NODELAY) }, + { MP_ROM_QSTR(MP_QSTR_MSG_PEEK), MP_ROM_INT(MSG_PEEK) }, + { MP_ROM_QSTR(MP_QSTR_MSG_DONTWAIT), MP_ROM_INT(MSG_DONTWAIT) }, }; static MP_DEFINE_CONST_DICT(mp_module_socket_globals, mp_module_socket_globals_table); diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index 35c8ff1831..a6f103cdef 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -124,6 +124,7 @@ #define MICROPY_PY_MACHINE_ADC_INCLUDEFILE "ports/esp32/machine_adc.c" #define MICROPY_PY_MACHINE_ADC_ATTEN_WIDTH (1) #define MICROPY_PY_MACHINE_ADC_INIT (1) +#define MICROPY_PY_MACHINE_ADC_DEINIT (1) #define MICROPY_PY_MACHINE_ADC_READ (1) #define MICROPY_PY_MACHINE_ADC_READ_UV (1) #define MICROPY_PY_MACHINE_ADC_BLOCK (1) diff --git a/ports/esp32/mpnimbleport.c b/ports/esp32/mpnimbleport.c index ce4b77727a..77185883fd 100644 --- a/ports/esp32/mpnimbleport.c +++ b/ports/esp32/mpnimbleport.c @@ -58,7 +58,7 @@ void mp_bluetooth_nimble_port_start(void) { nimble_port_freertos_init(ble_host_task); } -void mp_bluetooth_nimble_port_shutdown(void) { +int mp_bluetooth_nimble_port_shutdown(void) { DEBUG_printf("mp_bluetooth_nimble_port_shutdown\n"); #if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS_WITH_INTERLOCK @@ -79,6 +79,8 @@ void mp_bluetooth_nimble_port_shutdown(void) { // Mark stack as shutdown. mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF; + + return 0; } #endif diff --git a/ports/esp8266/main.c b/ports/esp8266/main.c index 2dd7c1dece..da712fce9b 100644 --- a/ports/esp8266/main.c +++ b/ports/esp8266/main.c @@ -49,6 +49,64 @@ static char heap[38 * 1024]; +#if MICROPY_HW_HARD_FAULT_DEBUG + +static void format_hex(uint32_t hex, char *buffer) { + static const char table[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + int offset = 7; + uint32_t value = hex; + while (offset >= 0) { + buffer[offset--] = table[value & 0x0F]; + value >>= 4; + } +} + +static void print_reset_info(void) { + struct rst_info *rst_info = system_get_rst_info(); + if ((rst_info->reason == REASON_WDT_RST) || (rst_info->reason == REASON_EXCEPTION_RST) || (rst_info->reason == REASON_SOFT_WDT_RST)) { + char buffer[8]; + mp_hal_stdout_tx_str("\r\n\r\nThe system restarted due to an error.\r\n\r\nReason: "); + switch (rst_info->reason) { + case REASON_WDT_RST: + mp_hal_stdout_tx_str("WDT"); + break; + + case REASON_EXCEPTION_RST: + mp_hal_stdout_tx_str("EXCEPTION"); + break; + + case REASON_SOFT_WDT_RST: + mp_hal_stdout_tx_str("SOFT_WDT"); + break; + + default: + assert(!"Should not ever get here."); + break; + } + mp_hal_stdout_tx_str(" Cause: "); + format_hex(rst_info->exccause, buffer); + mp_hal_stdout_tx_strn(buffer, sizeof(buffer)); + mp_hal_stdout_tx_str(" EPC1: "); + format_hex(rst_info->epc1, buffer); + mp_hal_stdout_tx_strn(buffer, sizeof(buffer)); + mp_hal_stdout_tx_str(" EPC2: "); + format_hex(rst_info->epc2, buffer); + mp_hal_stdout_tx_strn(buffer, sizeof(buffer)); + mp_hal_stdout_tx_str(" EPC3: "); + format_hex(rst_info->epc3, buffer); + mp_hal_stdout_tx_strn(buffer, sizeof(buffer)); + mp_hal_stdout_tx_str(" Exception Vector address: "); + format_hex(rst_info->excvaddr, buffer); + mp_hal_stdout_tx_strn(buffer, sizeof(buffer)); + mp_hal_stdout_tx_str(" DEPC: "); + format_hex(rst_info->depc, buffer); + mp_hal_stdout_tx_strn(buffer, sizeof(buffer)); + mp_hal_stdout_tx_str("\r\n\r\n"); + } +} + +#endif + static void mp_reset(void) { mp_stack_set_top((void *)0x40000000); mp_stack_set_limit(8192); @@ -114,6 +172,10 @@ void init_done(void) { pyexec_event_repl_init(); #endif + #if MICROPY_HW_HARD_FAULT_DEBUG + print_reset_info(); + #endif + #if !MICROPY_REPL_EVENT_DRIVEN soft_reset: for (;;) { diff --git a/ports/esp8266/mpconfigport.h b/ports/esp8266/mpconfigport.h index 83d80a7c96..03f3bb643d 100644 --- a/ports/esp8266/mpconfigport.h +++ b/ports/esp8266/mpconfigport.h @@ -117,6 +117,9 @@ #define MICROPY_FATFS_LFN_CODE_PAGE 437 /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */ #define MICROPY_ESP8266_APA102 (1) +// Print error information at reboot time if the board crashed. +#define MICROPY_HW_HARD_FAULT_DEBUG (0) + // No blocking wait-for-event on ESP8266, only non-blocking pump of the "OS" event // loop // diff --git a/ports/mimxrt/boards/ADAFRUIT_METRO_M7/board.json b/ports/mimxrt/boards/ADAFRUIT_METRO_M7/board.json index 9f260b3a00..804b4fd734 100644 --- a/ports/mimxrt/boards/ADAFRUIT_METRO_M7/board.json +++ b/ports/mimxrt/boards/ADAFRUIT_METRO_M7/board.json @@ -15,7 +15,7 @@ "Metro_M7.jpg" ], "mcu": "mimxrt", - "product": "Adafruit Metro M7", + "product": "Metro M7", "thumbnail": "", "url": "https://www.adafruit.com/product/4950", "vendor": "Adafruit" diff --git a/ports/mimxrt/boards/OLIMEX_RT1010/board.json b/ports/mimxrt/boards/OLIMEX_RT1010/board.json index 882b931f8b..a6f0316bc8 100644 --- a/ports/mimxrt/boards/OLIMEX_RT1010/board.json +++ b/ports/mimxrt/boards/OLIMEX_RT1010/board.json @@ -12,8 +12,8 @@ "OLIMEX_RT1010Py.jpg" ], "mcu": "mimxrt", - "product": "Olimex_RT1010Py", + "product": "RT1010-Py", "thumbnail": "", - "url": "https://www.olimex.com/Products/ARM/NXP", - "vendor": "OLIMEX" + "url": "https://www.olimex.com/Products/MicroPython/RT1010-Py", + "vendor": "Olimex" } diff --git a/ports/nrf/boards/ACTINIUS_ICARUS/board.json b/ports/nrf/boards/ACTINIUS_ICARUS/board.json index c1d9f1c4ef..09ed3fcba3 100644 --- a/ports/nrf/boards/ACTINIUS_ICARUS/board.json +++ b/ports/nrf/boards/ACTINIUS_ICARUS/board.json @@ -8,7 +8,7 @@ "icarus-v1.4-front-shadow-p-800.jpg" ], "mcu": "nrf91", - "product": "actinius_icarus", + "product": "Icarus", "thumbnail": "", "url": "https://www.actinius.com/icarus", "vendor": "Actinius" diff --git a/ports/nrf/boards/ARDUINO_NANO_33_BLE_SENSE/board.json b/ports/nrf/boards/ARDUINO_NANO_33_BLE_SENSE/board.json index 9079fbd666..72b5135611 100644 --- a/ports/nrf/boards/ARDUINO_NANO_33_BLE_SENSE/board.json +++ b/ports/nrf/boards/ARDUINO_NANO_33_BLE_SENSE/board.json @@ -15,7 +15,7 @@ "ABX00031_01.iso_998x749.jpg" ], "mcu": "nrf52", - "product": "Arduino Nano 33 BLE Sense", + "product": "Nano 33 BLE Sense", "thumbnail": "", "url": "https://store.arduino.cc/products/arduino-nano-33-ble-sense", "vendor": "Arduino" diff --git a/ports/nrf/boards/ARDUINO_PRIMO/board.json b/ports/nrf/boards/ARDUINO_PRIMO/board.json index f7afed0ced..236acfd448 100644 --- a/ports/nrf/boards/ARDUINO_PRIMO/board.json +++ b/ports/nrf/boards/ARDUINO_PRIMO/board.json @@ -8,8 +8,8 @@ "arduino_primo.jpg" ], "mcu": "nrf52", - "product": "arduino_primo", + "product": "Primo", "thumbnail": "", - "url": "", + "url": "https://docs.arduino.cc/retired/boards/arduino-primo/", "vendor": "Arduino" } diff --git a/ports/nrf/boards/BLUEIO_TAG_EVIM/board.json b/ports/nrf/boards/BLUEIO_TAG_EVIM/board.json index 5b6e5747d5..e910ea8db2 100644 --- a/ports/nrf/boards/BLUEIO_TAG_EVIM/board.json +++ b/ports/nrf/boards/BLUEIO_TAG_EVIM/board.json @@ -8,7 +8,7 @@ "blyst-nano-mod-4_jpg_project-body.jpg" ], "mcu": "nrf52", - "product": "blueio_tag_evim", + "product": "BLUEIO Tag EVIM", "thumbnail": "", "url": "https://www.i-syst.com/index.php/products/blyst-nano", "vendor": "I-SYST" diff --git a/ports/nrf/boards/DVK_BL652/board.json b/ports/nrf/boards/DVK_BL652/board.json index 5c9cfad26e..d0942ca256 100644 --- a/ports/nrf/boards/DVK_BL652/board.json +++ b/ports/nrf/boards/DVK_BL652/board.json @@ -8,8 +8,8 @@ "BL652-SA_JPG-500.jpg" ], "mcu": "nrf52", - "product": "dvk_bl652", + "product": "DVK-BL652", "thumbnail": "", - "url": "https://www.lairdconnect.com/wireless-modules/bluetooth-modules/bluetooth-5-modules/bl652-series-bluetooth-v5-nfc-module", - "vendor": "Laird Connectivity" + "url": "https://www.ezurio.com/wireless-modules/bluetooth-modules/bluetooth-5-modules/bl652-series-bluetooth-v5-nfc-module", + "vendor": "Ezurio" } diff --git a/ports/nrf/boards/EVK_NINA_B1/board.json b/ports/nrf/boards/EVK_NINA_B1/board.json index 657b0fa06d..ef9c6ae5be 100644 --- a/ports/nrf/boards/EVK_NINA_B1/board.json +++ b/ports/nrf/boards/EVK_NINA_B1/board.json @@ -8,7 +8,7 @@ "EVK-NINA-B1_.jpg" ], "mcu": "nrf52", - "product": "evk_nina_b1", + "product": "EVK-NINA-B1", "thumbnail": "", "url": "https://www.u-blox.com/en/product/evk-nina-b1", "vendor": "u-blox" diff --git a/ports/nrf/boards/EVK_NINA_B3/board.json b/ports/nrf/boards/EVK_NINA_B3/board.json index 54e3dc3585..6311b17338 100644 --- a/ports/nrf/boards/EVK_NINA_B3/board.json +++ b/ports/nrf/boards/EVK_NINA_B3/board.json @@ -8,7 +8,7 @@ "EVK-NINA-B3-top.jpg" ], "mcu": "nrf52", - "product": "evk_nina_b3", + "product": "EVK-NINA-B3", "thumbnail": "", "url": "https://www.u-blox.com/en/product/evk-nina-b3", "vendor": "u-blox" diff --git a/ports/nrf/boards/IBK_BLYST_NANO/board.json b/ports/nrf/boards/IBK_BLYST_NANO/board.json index 562c33607b..ddbbef66f5 100644 --- a/ports/nrf/boards/IBK_BLYST_NANO/board.json +++ b/ports/nrf/boards/IBK_BLYST_NANO/board.json @@ -8,7 +8,7 @@ "blyst-nano-fingertip-close_jpg_content-body-gallery.jpg" ], "mcu": "nrf52", - "product": "ibk_blyst_nano", + "product": "IBK BLYST Nano", "thumbnail": "", "url": "https://www.i-syst.com/products/blyst-nano", "vendor": "I-SYST" diff --git a/ports/nrf/boards/IDK_BLYST_NANO/board.json b/ports/nrf/boards/IDK_BLYST_NANO/board.json index 199721698d..cb692f44cd 100644 --- a/ports/nrf/boards/IDK_BLYST_NANO/board.json +++ b/ports/nrf/boards/IDK_BLYST_NANO/board.json @@ -8,7 +8,7 @@ "blyst-nano-fingertip-close_jpg_content-body-gallery.jpg" ], "mcu": "nrf52", - "product": "idk_blyst_nano", + "product": "IDK BLYST Nano", "thumbnail": "", "url": "https://www.i-syst.com/products/blyst-nano", "vendor": "I-SYST" diff --git a/ports/nrf/boards/NRF52840_MDK_USB_DONGLE/board.json b/ports/nrf/boards/NRF52840_MDK_USB_DONGLE/board.json index 99d0caad23..102102b6c2 100644 --- a/ports/nrf/boards/NRF52840_MDK_USB_DONGLE/board.json +++ b/ports/nrf/boards/NRF52840_MDK_USB_DONGLE/board.json @@ -8,7 +8,7 @@ "dongle_pcba_case.jpg" ], "mcu": "nrf52", - "product": "nrf52840-mdk-usb-dongle", + "product": "nrf52840 MDK USB Dongle", "thumbnail": "", "url": "https://wiki.makerdiary.com/nrf52840-mdk-usb-dongle", "vendor": "Makerdiary" diff --git a/ports/nrf/boards/SEEED_XIAO_NRF52/board.json b/ports/nrf/boards/SEEED_XIAO_NRF52/board.json index d40355bb2e..b00044a56a 100644 --- a/ports/nrf/boards/SEEED_XIAO_NRF52/board.json +++ b/ports/nrf/boards/SEEED_XIAO_NRF52/board.json @@ -16,8 +16,8 @@ "XIAO_nrf52840_front.jpg" ], "mcu": "nrf52", - "product": "SEEED XIAO nRF52840 Sense", + "product": "XIAO nRF52840 Sense", "thumbnail": "", - "url": "https://www.seeedstudio.com", + "url": "https://www.seeedstudio.com/Seeed-XIAO-BLE-Sense-nRF52840-p-5253.html", "vendor": "Seeed Studio" } diff --git a/ports/nrf/boards/WT51822_S4AT/board.json b/ports/nrf/boards/WT51822_S4AT/board.json index 89d008c6f5..2ec42ac414 100644 --- a/ports/nrf/boards/WT51822_S4AT/board.json +++ b/ports/nrf/boards/WT51822_S4AT/board.json @@ -8,8 +8,8 @@ "WT51822-S4AT.jpg" ], "mcu": "nrf51", - "product": "wt51822_s4at", + "product": "WT51822-S4AT", "thumbnail": "", - "url": "http://www.wireless-tag.com/portfolio/wt51822-s4at-2/", + "url": "https://shop.wireless-tag.com/products/esp32-c3-mini-1-10pcs-espressif-esp32-c3-mini-1-4mb-flash-pcb-antenna-15-gpios-wifi-ble-5-module-esp32-c3-module-on-esp32-c3-chip", "vendor": "Wireless-Tag" } diff --git a/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/board.json b/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/board.json index 4c4d4e4326..70a33b3515 100644 --- a/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/board.json +++ b/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/board.json @@ -15,7 +15,7 @@ "ABX00074_01.iso_1000x750.jpg" ], "mcu": "RA6M5", - "product": "Arduino Portenta C33", + "product": "Portenta C33", "thumbnail": "", "url": "https://store.arduino.cc/pages/portenta-c33", "vendor": "Arduino" diff --git a/ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/board.json b/ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/board.json index 5639aaa2a4..5914445ed8 100644 --- a/ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/board.json +++ b/ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/board.json @@ -17,7 +17,7 @@ "ABX00052_01.iso_999x750.jpg" ], "mcu": "rp2040", - "product": "Arduino Nano RP2040 Connect", + "product": "Nano RP2040 Connect", "thumbnail": "", "url": "https://store-usa.arduino.cc/products/arduino-nano-rp2040-connect", "vendor": "Arduino" diff --git a/ports/rp2/boards/GARATRONIC_PYBSTICK26_RP2040/board.json b/ports/rp2/boards/GARATRONIC_PYBSTICK26_RP2040/board.json index df9dd5d0de..108d632678 100644 --- a/ports/rp2/boards/GARATRONIC_PYBSTICK26_RP2040/board.json +++ b/ports/rp2/boards/GARATRONIC_PYBSTICK26_RP2040/board.json @@ -13,7 +13,7 @@ "pybstick-rp2040-26-broches-micropython-c.jpg" ], "mcu": "rp2040", - "product": "PYBSTICK26 RP2040", + "product": "RP2040 PYBStick", "thumbnail": "", "url": "https://shop.mchobby.be/product.php?id_product=2331", "vendor": "McHobby" diff --git a/ports/rp2/boards/POLOLU_3PI_2040_ROBOT/board.json b/ports/rp2/boards/POLOLU_3PI_2040_ROBOT/board.json index 5358db627e..4b12cfda65 100644 --- a/ports/rp2/boards/POLOLU_3PI_2040_ROBOT/board.json +++ b/ports/rp2/boards/POLOLU_3PI_2040_ROBOT/board.json @@ -15,7 +15,7 @@ "pololu_3pi_2040_robot.jpg" ], "mcu": "rp2040", - "product": "Pololu 3pi+ 2040 Robot", + "product": "3pi+ 2040 Robot", "thumbnail": "", "url": "https://www.pololu.com/3pi", "vendor": "Pololu" diff --git a/ports/rp2/boards/POLOLU_ZUMO_2040_ROBOT/board.json b/ports/rp2/boards/POLOLU_ZUMO_2040_ROBOT/board.json index 7ef383a9c4..1ec15ad591 100644 --- a/ports/rp2/boards/POLOLU_ZUMO_2040_ROBOT/board.json +++ b/ports/rp2/boards/POLOLU_ZUMO_2040_ROBOT/board.json @@ -16,7 +16,7 @@ "pololu_zumo_2040_robot.jpg" ], "mcu": "rp2040", - "product": "Pololu Zumo 2040 Robot", + "product": "Zumo 2040 Robot", "thumbnail": "", "url": "https://www.pololu.com/zumo", "vendor": "Pololu" diff --git a/ports/rp2/boards/SPARKFUN_PROMICRO/board.json b/ports/rp2/boards/SPARKFUN_PROMICRO/board.json index 66cea79dde..5ee6bcb754 100644 --- a/ports/rp2/boards/SPARKFUN_PROMICRO/board.json +++ b/ports/rp2/boards/SPARKFUN_PROMICRO/board.json @@ -14,7 +14,7 @@ "18288-SparkFun_Pro_Micro_-_RP2040-01.jpg" ], "mcu": "rp2040", - "product": "Pro Micro RP2040", + "product": "Pro Micro - RP2040", "thumbnail": "", "url": "https://www.sparkfun.com/products/18288", "vendor": "SparkFun" diff --git a/ports/rp2/boards/SPARKFUN_THINGPLUS/board.json b/ports/rp2/boards/SPARKFUN_THINGPLUS/board.json index e756c9bff4..ebd68bc5d9 100644 --- a/ports/rp2/boards/SPARKFUN_THINGPLUS/board.json +++ b/ports/rp2/boards/SPARKFUN_THINGPLUS/board.json @@ -17,7 +17,7 @@ "17745-SparkFun_Thing_Plus_-_RP2040-01a.jpg" ], "mcu": "rp2040", - "product": "Thing Plus RP2040", + "product": "Thing Plus - RP2040", "thumbnail": "", "url": "https://www.sparkfun.com/products/17745", "vendor": "SparkFun" diff --git a/ports/rp2/boards/W5100S_EVB_PICO/board.json b/ports/rp2/boards/W5100S_EVB_PICO/board.json index daaf1cf8ac..3d052de420 100644 --- a/ports/rp2/boards/W5100S_EVB_PICO/board.json +++ b/ports/rp2/boards/W5100S_EVB_PICO/board.json @@ -13,8 +13,8 @@ "W5100S-EVB-Pico.jpg" ], "mcu": "rp2040", - "product": "Wiznet W5100S-EVB-Pico", + "product": "W5100S-EVB-Pico", "thumbnail": "", - "url": "https://www.wiznet.io/product-item/w5100s-evb-pico/", - "vendor": "Wiznet" + "url": "https://docs.wiznet.io/Product/iEthernet/W5100S/w5100s-evb-pico", + "vendor": "WIZnet" } diff --git a/ports/rp2/boards/W5500_EVB_PICO/board.json b/ports/rp2/boards/W5500_EVB_PICO/board.json index e7625c3436..d4298014c0 100644 --- a/ports/rp2/boards/W5500_EVB_PICO/board.json +++ b/ports/rp2/boards/W5500_EVB_PICO/board.json @@ -13,8 +13,8 @@ "W5500-EVB-Pico.jpg" ], "mcu": "rp2040", - "product": "Wiznet W5500-EVB-Pico", + "product": "W5500-EVB-Pico", "thumbnail": "", "url": "https://docs.wiznet.io/Product/iEthernet/W5500/w5500-evb-pico", - "vendor": "Wiznet" + "vendor": "WIZnet" } diff --git a/ports/rp2/boards/WEACTSTUDIO/board.json b/ports/rp2/boards/WEACTSTUDIO/board.json index 8881b40e73..58d89ddf2e 100644 --- a/ports/rp2/boards/WEACTSTUDIO/board.json +++ b/ports/rp2/boards/WEACTSTUDIO/board.json @@ -12,7 +12,7 @@ "weact_rp2040.jpg" ], "mcu": "rp2040", - "product": "WeAct Studio RP2040", + "product": "Studio RP2040", "url": "https://github.com/WeActTC/WeActStudio.RP2040CoreBoard", "variants": { "FLASH_2M": "2 MiB Flash", diff --git a/ports/samd/boards/SEEED_XIAO_SAMD21/board.json b/ports/samd/boards/SEEED_XIAO_SAMD21/board.json index 4d19f6b945..0d6b2fc30d 100644 --- a/ports/samd/boards/SEEED_XIAO_SAMD21/board.json +++ b/ports/samd/boards/SEEED_XIAO_SAMD21/board.json @@ -10,7 +10,7 @@ "seeeduino-xiao.jpg" ], "mcu": "samd21", - "product": "Seeeduino XIAO (SAMD21)", + "product": "XIAO SAMD21", "thumbnail": "", "url": "https://www.seeedstudio.com/Seeeduino-XIAO-Arduino-Microcontroller-SAMD21-Cortex-M0+-p-4426.html", "vendor": "Seeed Studio" diff --git a/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/board.json b/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/board.json index 51d124e751..6e89cebadd 100644 --- a/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/board.json +++ b/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/board.json @@ -13,7 +13,7 @@ "sparkfun_samd51_thing_plus.jpg" ], "mcu": "samd51", - "product": "SparkFun SAMD51 Thing Plus", + "product": "SAMD51 Thing Plus", "thumbnail": "", "url": "https://www.sparkfun.com/products/14713", "vendor": "SparkFun" diff --git a/ports/stm32/boards/ARDUINO_GIGA/board.json b/ports/stm32/boards/ARDUINO_GIGA/board.json index 53c636c774..a99e747017 100644 --- a/ports/stm32/boards/ARDUINO_GIGA/board.json +++ b/ports/stm32/boards/ARDUINO_GIGA/board.json @@ -15,7 +15,7 @@ "ABX00063_01.front_1000x750.jpg" ], "mcu": "stm32h7", - "product": "Arduino Giga", + "product": "Giga", "thumbnail": "", "url": "https://store.arduino.cc/products/giga-r1-wifi", "vendor": "Arduino" diff --git a/ports/stm32/boards/ARDUINO_NICLA_VISION/board.json b/ports/stm32/boards/ARDUINO_NICLA_VISION/board.json index a4c81d69d4..129d9f2fc7 100644 --- a/ports/stm32/boards/ARDUINO_NICLA_VISION/board.json +++ b/ports/stm32/boards/ARDUINO_NICLA_VISION/board.json @@ -15,7 +15,7 @@ "ABX00051_01.iso_1000x750.jpg" ], "mcu": "stm32h7", - "product": "Arduino Nicla Vision", + "product": "Nicla Vision", "thumbnail": "", "url": "https://store.arduino.cc/products/nicla-vision", "vendor": "Arduino" diff --git a/ports/stm32/boards/ARDUINO_OPTA/board.json b/ports/stm32/boards/ARDUINO_OPTA/board.json index 3955d7df31..dd6c39a191 100644 --- a/ports/stm32/boards/ARDUINO_OPTA/board.json +++ b/ports/stm32/boards/ARDUINO_OPTA/board.json @@ -14,7 +14,7 @@ "AFX00002_01.iso_1000x750.jpg" ], "mcu": "stm32h7", - "product": "Arduino Opta WiFi", + "product": "Opta WiFi", "thumbnail": "", "url": "https://store.arduino.cc/products/opta-wifi", "vendor": "Arduino" diff --git a/ports/stm32/boards/ARDUINO_PORTENTA_H7/board.json b/ports/stm32/boards/ARDUINO_PORTENTA_H7/board.json index f39d7d4c47..c97b9f165c 100644 --- a/ports/stm32/boards/ARDUINO_PORTENTA_H7/board.json +++ b/ports/stm32/boards/ARDUINO_PORTENTA_H7/board.json @@ -16,7 +16,7 @@ "ABX00042_01.iso_1000x750.jpg" ], "mcu": "stm32h7", - "product": "Arduino Portenta H7", + "product": "Portenta H7", "thumbnail": "", "url": "https://store.arduino.cc/products/portenta-h7", "vendor": "Arduino" diff --git a/ports/stm32/boards/HYDRABUS/board.json b/ports/stm32/boards/HYDRABUS/board.json index 20a2ac5eae..24df6ac99f 100644 --- a/ports/stm32/boards/HYDRABUS/board.json +++ b/ports/stm32/boards/HYDRABUS/board.json @@ -10,6 +10,6 @@ "mcu": "stm32f4", "product": "HydraBus v1.0", "thumbnail": "", - "url": "", + "url": "https://github.com/hydrabus/hydrabus", "vendor": "HydraBus" } diff --git a/ports/stm32/boards/LIMIFROG/board.json b/ports/stm32/boards/LIMIFROG/board.json index f516963a5b..36c80f8f72 100644 --- a/ports/stm32/boards/LIMIFROG/board.json +++ b/ports/stm32/boards/LIMIFROG/board.json @@ -10,6 +10,6 @@ "mcu": "stm32l4", "product": "LimiFrog", "thumbnail": "", - "url": "", + "url": "https://github.com/LimiFrog", "vendor": "LimiFrog" } diff --git a/ports/stm32/boards/OLIMEX_E407/board.json b/ports/stm32/boards/OLIMEX_E407/board.json index c14755c564..408df5a320 100644 --- a/ports/stm32/boards/OLIMEX_E407/board.json +++ b/ports/stm32/boards/OLIMEX_E407/board.json @@ -8,8 +8,8 @@ "olimex_e407.jpg" ], "mcu": "stm32f4", - "product": "E407", + "product": "STM32-E407", "thumbnail": "", - "url": "", - "vendor": "OLIMEX" + "url": "https://www.olimex.com/Products/ARM/ST/STM32-E407", + "vendor": "Olimex" } diff --git a/ports/stm32/boards/OLIMEX_H407/board.json b/ports/stm32/boards/OLIMEX_H407/board.json index 9ecc860ddc..27d2940513 100644 --- a/ports/stm32/boards/OLIMEX_H407/board.json +++ b/ports/stm32/boards/OLIMEX_H407/board.json @@ -8,8 +8,8 @@ "olimex_h407.jpg" ], "mcu": "stm32f4", - "product": "H407", + "product": "STM32-H407", "thumbnail": "", - "url": "", - "vendor": "OLIMEX" + "url": "https://www.olimex.com/Products/ARM/ST/STM32-H407", + "vendor": "Olimex" } diff --git a/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/board.json b/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/board.json index 01ed363cf3..5efd188618 100644 --- a/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/board.json +++ b/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/board.json @@ -8,8 +8,8 @@ "sparkfun_micromod_stm32.jpg" ], "mcu": "stm32f4", - "product": "Micromod STM32", + "product": "MicroMod STM32", "thumbnail": "", - "url": "", + "url": "https://www.sparkfun.com/products/17713", "vendor": "SparkFun" } diff --git a/ports/stm32/machine_adc.c b/ports/stm32/machine_adc.c index 8f07075914..c3211ea4f4 100644 --- a/ports/stm32/machine_adc.c +++ b/ports/stm32/machine_adc.c @@ -445,10 +445,13 @@ static void adc_config_channel(ADC_TypeDef *adc, uint32_t channel, uint32_t samp static uint32_t adc_read_channel(ADC_TypeDef *adc) { uint32_t value; - #if defined(STM32G4) - // For STM32G4 there is errata 2.7.7, "Wrong ADC result if conversion done late after - // calibration or previous conversion". According to the errata, this can be avoided - // by performing two consecutive ADC conversions and keeping the second result. + #if defined(STM32G4) || defined(STM32WB) + // For STM32G4 errata 2.7.7 / STM32WB errata 2.7.1: + // "Wrong ADC result if conversion done late after calibration or previous conversion" + // states an incorrect reading is returned if more than 1ms has elapsed since the last + // reading or calibration. According to the errata, this can be avoided by performing + // two consecutive ADC conversions and keeping the second result. + // Note: On STM32WB55 @ 64Mhz each ADC read takes ~ 3us. for (uint8_t i = 0; i < 2; i++) #endif { diff --git a/ports/unix/coverage.c b/ports/unix/coverage.c index 2b65b47fc5..29e1457cb7 100644 --- a/ports/unix/coverage.c +++ b/ports/unix/coverage.c @@ -184,6 +184,18 @@ static void pairheap_test(size_t nops, int *ops) { mp_printf(&mp_plat_print, "\n"); } +static mp_sched_node_t mp_coverage_sched_node; +static bool coverage_sched_function_continue; + +static void coverage_sched_function(mp_sched_node_t *node) { + (void)node; + mp_printf(&mp_plat_print, "scheduled function\n"); + if (coverage_sched_function_continue) { + // Re-scheduling node will cause it to run again next time scheduled functions are run + mp_sched_schedule_node(&mp_coverage_sched_node, coverage_sched_function); + } +} + // function to run extra tests for things that can't be checked by scripts static mp_obj_t extra_coverage(void) { // mp_printf (used by ports that don't have a native printf) @@ -621,6 +633,19 @@ static mp_obj_t extra_coverage(void) { mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val)); } mp_handle_pending(true); + + coverage_sched_function_continue = true; + mp_sched_schedule_node(&mp_coverage_sched_node, coverage_sched_function); + for (int i = 0; i < 3; ++i) { + mp_printf(&mp_plat_print, "loop\n"); + mp_handle_pending(true); + } + // Clear this flag to prevent the function scheduling itself again + coverage_sched_function_continue = false; + // Will only run the first time through this loop, then not scheduled again + for (int i = 0; i < 3; ++i) { + mp_handle_pending(true); + } } // ringbuf diff --git a/ports/unix/main.c b/ports/unix/main.c index 9f51573fbf..530e20a386 100644 --- a/ports/unix/main.c +++ b/ports/unix/main.c @@ -775,7 +775,7 @@ MP_NOINLINE int main_(int argc, char **argv) { #endif #if MICROPY_PY_BLUETOOTH - void mp_bluetooth_deinit(void); + int mp_bluetooth_deinit(void); mp_bluetooth_deinit(); #endif diff --git a/ports/unix/modsocket.c b/ports/unix/modsocket.c index 6d6059ae44..2aaa21183a 100644 --- a/ports/unix/modsocket.c +++ b/ports/unix/modsocket.c @@ -701,6 +701,7 @@ static const mp_rom_map_elem_t mp_module_socket_globals_table[] = { C(MSG_DONTROUTE), C(MSG_DONTWAIT), + C(MSG_PEEK), C(SOL_SOCKET), C(SO_BROADCAST), diff --git a/ports/unix/variants/coverage/mpconfigvariant.h b/ports/unix/variants/coverage/mpconfigvariant.h index 04b5b8ae1a..cfefeb4672 100644 --- a/ports/unix/variants/coverage/mpconfigvariant.h +++ b/ports/unix/variants/coverage/mpconfigvariant.h @@ -44,6 +44,7 @@ #undef MICROPY_VFS_ROM_IOCTL #define MICROPY_VFS_ROM_IOCTL (1) #define MICROPY_PY_CRYPTOLIB_CTR (1) +#define MICROPY_SCHEDULER_STATIC_NODES (1) // Enable os.uname for attrtuple coverage test #define MICROPY_PY_OS_UNAME (1) diff --git a/ports/zephyr/modbluetooth_zephyr.c b/ports/zephyr/modbluetooth_zephyr.c index cdbeb7fc35..c1b8ddfd8b 100644 --- a/ports/zephyr/modbluetooth_zephyr.c +++ b/ports/zephyr/modbluetooth_zephyr.c @@ -305,11 +305,11 @@ int mp_bluetooth_init(void) { return 0; } -void mp_bluetooth_deinit(void) { +int mp_bluetooth_deinit(void) { DEBUG_printf("mp_bluetooth_deinit %d\n", mp_bluetooth_zephyr_ble_state); if (mp_bluetooth_zephyr_ble_state == MP_BLUETOOTH_ZEPHYR_BLE_STATE_OFF || mp_bluetooth_zephyr_ble_state == MP_BLUETOOTH_ZEPHYR_BLE_STATE_SUSPENDED) { - return; + return 0; } mp_bluetooth_gap_advertise_stop(); @@ -332,6 +332,7 @@ void mp_bluetooth_deinit(void) { MP_STATE_PORT(bluetooth_zephyr_root_pointers) = NULL; mp_bt_zephyr_next_conn = NULL; + return 0; } bool mp_bluetooth_is_active(void) { diff --git a/py/dynruntime.mk b/py/dynruntime.mk index 1ef521bd9a..84c78d6225 100644 --- a/py/dynruntime.mk +++ b/py/dynruntime.mk @@ -172,6 +172,9 @@ endif endif MPY_LD_FLAGS += $(addprefix -l, $(LIBGCC_PATH) $(LIBM_PATH)) endif +ifneq ($(MPY_EXTERN_SYM_FILE),) +MPY_LD_FLAGS += --externs "$(realpath $(MPY_EXTERN_SYM_FILE))" +endif CFLAGS += $(CFLAGS_EXTRA) diff --git a/py/scheduler.c b/py/scheduler.c index 2170b9577e..d4cdb59efb 100644 --- a/py/scheduler.c +++ b/py/scheduler.c @@ -88,17 +88,21 @@ static inline void mp_sched_run_pending(void) { #if MICROPY_SCHEDULER_STATIC_NODES // Run all pending C callbacks. - while (MP_STATE_VM(sched_head) != NULL) { - mp_sched_node_t *node = MP_STATE_VM(sched_head); - MP_STATE_VM(sched_head) = node->next; - if (MP_STATE_VM(sched_head) == NULL) { - MP_STATE_VM(sched_tail) = NULL; - } - mp_sched_callback_t callback = node->callback; - node->callback = NULL; - MICROPY_END_ATOMIC_SECTION(atomic_state); - callback(node); - atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); + mp_sched_node_t *original_tail = MP_STATE_VM(sched_tail); + if (original_tail != NULL) { + mp_sched_node_t *node; + do { + node = MP_STATE_VM(sched_head); + MP_STATE_VM(sched_head) = node->next; + if (MP_STATE_VM(sched_head) == NULL) { + MP_STATE_VM(sched_tail) = NULL; + } + mp_sched_callback_t callback = node->callback; + node->callback = NULL; + MICROPY_END_ATOMIC_SECTION(atomic_state); + callback(node); + atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); + } while (node != original_tail); // Don't execute any callbacks scheduled during this run } #endif diff --git a/tests/extmod/framebuf_blit.py b/tests/extmod/framebuf_blit.py new file mode 100644 index 0000000000..b1d98b330a --- /dev/null +++ b/tests/extmod/framebuf_blit.py @@ -0,0 +1,68 @@ +# Test FrameBuffer.blit method. + +try: + import framebuf +except ImportError: + print("SKIP") + raise SystemExit + + +def printbuf(): + print("--8<--") + for y in range(h): + for x in range(w): + print("%02x" % buf[(x + y * w)], end="") + print() + print("-->8--") + + +w = 5 +h = 4 +buf = bytearray(w * h) +fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.GS8) + +fbuf2 = framebuf.FrameBuffer(bytearray(4), 2, 2, framebuf.GS8) +fbuf2.fill(0xFF) + +# Blit another FrameBuffer, at various locations. +for x, y in ((-1, -1), (0, 0), (1, 1), (4, 3)): + fbuf.fill(0) + fbuf.blit(fbuf2, x, y) + printbuf() + +# Blit a bytes object. +fbuf.fill(0) +image = (b"\x10\x11\x12\x13", 2, 2, framebuf.GS8) +fbuf.blit(image, 1, 1) +printbuf() + +# Blit a bytes object that has a stride. +fbuf.fill(0) +image = (b"\x20\x21\xff\x22\x23\xff", 2, 2, framebuf.GS8, 3) +fbuf.blit(image, 1, 1) +printbuf() + +# Blit a bytes object with a bytes palette. +fbuf.fill(0) +image = (b"\x00\x01\x01\x00", 2, 2, framebuf.GS8) +palette = (b"\xa1\xa2", 2, 1, framebuf.GS8) +fbuf.blit(image, 1, 1, -1, palette) +printbuf() + +# Not enough elements in the tuple. +try: + fbuf.blit((0, 0, 0), 0, 0) +except ValueError: + print("ValueError") + +# Too many elements in the tuple. +try: + fbuf.blit((0, 0, 0, 0, 0, 0), 0, 0) +except ValueError: + print("ValueError") + +# Bytes too small. +try: + fbuf.blit((b"", 1, 1, framebuf.GS8), 0, 0) +except ValueError: + print("ValueError") diff --git a/tests/extmod/framebuf_blit.py.exp b/tests/extmod/framebuf_blit.py.exp new file mode 100644 index 0000000000..e340f1990c --- /dev/null +++ b/tests/extmod/framebuf_blit.py.exp @@ -0,0 +1,45 @@ +--8<-- +ff00000000 +0000000000 +0000000000 +0000000000 +-->8-- +--8<-- +ffff000000 +ffff000000 +0000000000 +0000000000 +-->8-- +--8<-- +0000000000 +00ffff0000 +00ffff0000 +0000000000 +-->8-- +--8<-- +0000000000 +0000000000 +0000000000 +00000000ff +-->8-- +--8<-- +0000000000 +0010110000 +0012130000 +0000000000 +-->8-- +--8<-- +0000000000 +0020210000 +0022230000 +0000000000 +-->8-- +--8<-- +0000000000 +00a1a20000 +00a2a10000 +0000000000 +-->8-- +ValueError +ValueError +ValueError diff --git a/tests/misc/print_exception.py b/tests/misc/print_exception.py index 1d196d6ab1..92754733b5 100644 --- a/tests/misc/print_exception.py +++ b/tests/misc/print_exception.py @@ -1,3 +1,5 @@ +# Test sys.print_exception (MicroPython) / traceback.print_exception (CPython). + try: import io import sys diff --git a/tests/multi_net/tcp_recv_peek.py b/tests/multi_net/tcp_recv_peek.py new file mode 100644 index 0000000000..ff540dd3c3 --- /dev/null +++ b/tests/multi_net/tcp_recv_peek.py @@ -0,0 +1,46 @@ +# Test TCP recv with MSG_PEEK +# +# Note that bare metal LWIP only returns at most one packet's worth of TCP data +# in any recv() call - including when peeking - so can't be too clever with +# different recv() combinations +import socket +import random + + +# Server +def instance0(): + PORT = random.randrange(10000, 50000) + multitest.globals(IP=multitest.get_network_ip(), PORT=PORT) + s = socket.socket() + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + s.bind(socket.getaddrinfo("0.0.0.0", PORT)[0][-1]) + s.listen() + multitest.next() + s2, _ = s.accept() + print(s2.recv(8, socket.MSG_PEEK)) + print(s2.recv(8)) + s2.send(b"1234567890") + multitest.broadcast("0-sent") + multitest.wait("1-sent") + print(s2.recv(5, socket.MSG_PEEK)) + print(s2.recv(5, socket.MSG_PEEK)) + multitest.broadcast("0-recved") + multitest.wait("1-recved") # sync here necessary as MP sends RST if closing TCP early + s2.close() + s.close() + + +# Client +def instance1(): + multitest.next() + s = socket.socket() + s.connect(socket.getaddrinfo(IP, PORT)[0][-1]) + s.send(b"abcdefgh") + multitest.broadcast("1-sent") + multitest.wait("0-sent") + s.send(b"klmnopqr") + print(s.recv(5, socket.MSG_PEEK)) + print(s.recv(10)) + multitest.broadcast("1-recved") + multitest.wait("0-recved") + s.close() diff --git a/tests/multi_net/udp_recv_dontwait.py b/tests/multi_net/udp_recv_dontwait.py new file mode 100644 index 0000000000..640f3f060e --- /dev/null +++ b/tests/multi_net/udp_recv_dontwait.py @@ -0,0 +1,59 @@ +# Test UDP recv and recvfrom with MSG_DONTWAIT +import random +import socket + +try: + import errno, time +except ImportError: + print("SKIP") + raise SystemExit + + +# Server +def instance0(): + PORT = random.randrange(10000, 50000) + multitest.globals(IP=multitest.get_network_ip(), PORT=PORT) + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + s.bind(socket.getaddrinfo("0.0.0.0", PORT)[0][-1]) + multitest.next() + begin = time.ticks_ms() + + # do some recvs before instance1 starts, when we know no packet is waiting + try: + print(s.recvfrom(8, socket.MSG_DONTWAIT)) + except OSError as e: + print(e.errno == errno.EAGAIN) + try: + print(s.recv(8, socket.MSG_DONTWAIT)) + except OSError as e: + print(e.errno == errno.EAGAIN) + + # the above steps should not have taken any substantial time + elapsed = time.ticks_diff(time.ticks_ms(), begin) + print(True if elapsed < 50 else elapsed) + + # Now instance1 will send us a UDP packet + multitest.broadcast("0-ready") + multitest.wait("1-sent") + + for _ in range(10): # retry if necessary, to allow for network delay + time.sleep_ms(100) + try: + print(s.recv(8, socket.MSG_DONTWAIT)) + break + except OSError as er: + if er.errno != errno.EAGAIN: + raise er + s.close() + + +# Client +def instance1(): + multitest.next() + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.connect(socket.getaddrinfo(IP, PORT)[0][-1]) + multitest.wait("0-ready") + print(s.send(b"abcdefgh")) + multitest.broadcast("1-sent") + s.close() diff --git a/tests/multi_net/udp_recv_dontwait.py.exp b/tests/multi_net/udp_recv_dontwait.py.exp new file mode 100644 index 0000000000..f61fd4bbe2 --- /dev/null +++ b/tests/multi_net/udp_recv_dontwait.py.exp @@ -0,0 +1,7 @@ +--- instance0 --- +True +True +True +b'abcdefgh' +--- instance1 --- +8 diff --git a/tests/multi_net/udp_recv_peek.py b/tests/multi_net/udp_recv_peek.py new file mode 100644 index 0000000000..47897ce553 --- /dev/null +++ b/tests/multi_net/udp_recv_peek.py @@ -0,0 +1,36 @@ +# Test UDP recv and recvfrom with MSG_PEEK +import random +import socket +import time + + +# Server +def instance0(): + PORT = random.randrange(10000, 50000) + multitest.globals(IP=multitest.get_network_ip(), PORT=PORT) + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + s.bind(socket.getaddrinfo("0.0.0.0", PORT)[0][-1]) + multitest.next() + peek_bytes, peek_addr = s.recvfrom(8, socket.MSG_PEEK) + print(peek_bytes) + real_bytes, real_addr = s.recvfrom(8) + print(real_bytes) + print(peek_addr == real_addr) # source addr should be the same for each + res = s.sendto(b"1234567890", peek_addr) + print(res) + print(s.recv(5, socket.MSG_PEEK)) + print(s.recv(5, socket.MSG_PEEK)) + s.close() + + +# Client +def instance1(): + multitest.next() + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.connect(socket.getaddrinfo(IP, PORT)[0][-1]) + s.send(b"abcdefgh") + s.send(b"klmnopqr") + print(s.recv(5, socket.MSG_PEEK)) + print(s.recv(10)) + s.close() diff --git a/tests/ports/unix/extra_coverage.py.exp b/tests/ports/unix/extra_coverage.py.exp index 5ff947e883..19b32f1c9d 100644 --- a/tests/ports/unix/extra_coverage.py.exp +++ b/tests/ports/unix/extra_coverage.py.exp @@ -122,6 +122,13 @@ unlocked KeyboardInterrupt: KeyboardInterrupt: 10 +loop +scheduled function +loop +scheduled function +loop +scheduled function +scheduled function # ringbuf 99 0 98 1 diff --git a/tests/run-natmodtests.py b/tests/run-natmodtests.py index b858989daa..073e0b053e 100755 --- a/tests/run-natmodtests.py +++ b/tests/run-natmodtests.py @@ -73,6 +73,7 @@ class __FS: return __File() vfs.mount(__FS(), '/__remote') sys.path.insert(0, '/__remote') +{import_prelude} sys.modules['{}'] = __import__('__injected') """ @@ -133,6 +134,13 @@ def detect_architecture(target): def run_tests(target_truth, target, args, stats, resolved_arch): + global injected_import_hook_code + + prelude = "" + if args.begin: + prelude = args.begin.read() + injected_import_hook_code = injected_import_hook_code.replace("{import_prelude}", prelude) + for test_file in args.files: # Find supported test test_file_basename = os.path.basename(test_file) @@ -212,6 +220,13 @@ def main(): cmd_parser.add_argument( "-a", "--arch", choices=AVAILABLE_ARCHS, help="override native architecture of the target" ) + cmd_parser.add_argument( + "-b", + "--begin", + type=argparse.FileType("rt"), + default=None, + help="prologue python file to execute before module import", + ) cmd_parser.add_argument("files", nargs="*", help="input test files") args = cmd_parser.parse_args() diff --git a/tests/run-tests.py b/tests/run-tests.py index 9294c7e636..cb7a8b7705 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -105,14 +105,11 @@ PC_PLATFORMS = ("darwin", "linux", "win32") # These are tests that are difficult to detect that they should not be run on the given target. platform_tests_to_skip = { "esp8266": ( - "micropython/viper_args.py", # too large - "micropython/viper_binop_arith.py", # too large - "misc/rge_sm.py", # too large + "misc/rge_sm.py", # incorrect values due to object representation C ), "minimal": ( "basics/class_inplace_op.py", # all special methods not supported "basics/subclass_native_init.py", # native subclassing corner cases not support - "misc/rge_sm.py", # too large "micropython/opt_level.py", # don't assume line numbers are stored ), "nrf": ( @@ -272,22 +269,17 @@ def detect_test_platform(pyb, args): print() -def prepare_script_for_target(args, *, script_filename=None, script_text=None, force_plain=False): +def prepare_script_for_target(args, *, script_text=None, force_plain=False): if force_plain or (not args.via_mpy and args.emit == "bytecode"): - if script_filename is not None: - with open(script_filename, "rb") as f: - script_text = f.read() + # A plain test to run as-is, no processing needed. + pass elif args.via_mpy: tempname = tempfile.mktemp(dir="") mpy_filename = tempname + ".mpy" - if script_filename is None: - script_filename = tempname + ".py" - cleanup_script_filename = True - with open(script_filename, "wb") as f: - f.write(script_text) - else: - cleanup_script_filename = False + script_filename = tempname + ".py" + with open(script_filename, "wb") as f: + f.write(script_text) try: subprocess.check_output( @@ -303,8 +295,7 @@ def prepare_script_for_target(args, *, script_filename=None, script_text=None, f script_text = b"__buf=" + bytes(repr(f.read()), "ascii") + b"\n" rm_f(mpy_filename) - if cleanup_script_filename: - rm_f(script_filename) + rm_f(script_filename) script_text += bytes(injected_import_hook_code, "ascii") else: @@ -315,9 +306,21 @@ def prepare_script_for_target(args, *, script_filename=None, script_text=None, f def run_script_on_remote_target(pyb, args, test_file, is_special): - had_crash, script = prepare_script_for_target( - args, script_filename=test_file, force_plain=is_special - ) + with open(test_file, "rb") as f: + script = f.read() + + # If the test is not a special test, prepend it with a print to indicate that it started. + # If the print does not execute this means that the test did not even start, eg it was + # too large for the target. + prepend_start_test = not is_special + if prepend_start_test: + if script.startswith(b"#"): + script = b"print('START TEST')" + script + else: + script = b"print('START TEST')\n" + script + + had_crash, script = prepare_script_for_target(args, script_text=script, force_plain=is_special) + if had_crash: return True, script @@ -328,9 +331,19 @@ def run_script_on_remote_target(pyb, args, test_file, is_special): except pyboard.PyboardError as e: had_crash = True if not is_special and e.args[0] == "exception": - output_mupy = e.args[1] + e.args[2] + b"CRASH" + if prepend_start_test and e.args[1] == b"" and b"MemoryError" in e.args[2]: + output_mupy = b"SKIP-TOO-LARGE\n" + else: + output_mupy = e.args[1] + e.args[2] + b"CRASH" else: output_mupy = bytes(e.args[0], "ascii") + b"\nCRASH" + + if prepend_start_test: + if output_mupy.startswith(b"START TEST\r\n"): + output_mupy = output_mupy.removeprefix(b"START TEST\r\n") + else: + had_crash = True + return had_crash, output_mupy @@ -474,7 +487,7 @@ def run_micropython(pyb, args, test_file, test_file_abspath, is_special=False): output_mupy = output_mupy.replace(b"\r\n", b"\n") # don't try to convert the output if we should skip this test - if had_crash or output_mupy in (b"SKIP\n", b"CRASH"): + if had_crash or output_mupy in (b"SKIP\n", b"SKIP-TOO-LARGE\n", b"CRASH"): return output_mupy # skipped special tests will output "SKIP" surrounded by other interpreter debug output @@ -605,9 +618,7 @@ class PyboardNodeRunner: def run_tests(pyb, tests, args, result_dir, num_threads=1): test_count = ThreadSafeCounter() testcase_count = ThreadSafeCounter() - passed_tests = ThreadSafeCounter([]) - failed_tests = ThreadSafeCounter([]) - skipped_tests = ThreadSafeCounter([]) + test_results = ThreadSafeCounter([]) skip_tests = set() skip_native = False @@ -896,7 +907,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): if skip_it: print("skip ", test_file) - skipped_tests.append((test_name, test_file)) + test_results.append((test_name, test_file, "skip", "")) return # Run the test on the MicroPython target. @@ -911,7 +922,11 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): # start-up code (eg boot.py) when preparing to run the next test. pyb.read_until(1, b"raw REPL; CTRL-B to exit\r\n") print("skip ", test_file) - skipped_tests.append((test_name, test_file)) + test_results.append((test_name, test_file, "skip", "")) + return + elif output_mupy == b"SKIP-TOO-LARGE\n": + print("lrge ", test_file) + test_results.append((test_name, test_file, "skip", "too large")) return # Look at the output of the test to see if unittest was used. @@ -994,7 +1009,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): # Print test summary, update counters, and save .exp/.out files if needed. if test_passed: print("pass ", test_file, extra_info) - passed_tests.append((test_name, test_file)) + test_results.append((test_name, test_file, "pass", "")) rm_f(filename_expected) rm_f(filename_mupy) else: @@ -1006,7 +1021,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): rm_f(filename_expected) # in case left over from previous failed run with open(filename_mupy, "wb") as f: f.write(output_mupy) - failed_tests.append((test_name, test_file)) + test_results.append((test_name, test_file, "fail", "")) test_count.increment() @@ -1035,9 +1050,13 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): print(line) sys.exit(1) - passed_tests = sorted(passed_tests.value) - skipped_tests = sorted(skipped_tests.value) - failed_tests = sorted(failed_tests.value) + test_results = test_results.value + passed_tests = list(r for r in test_results if r[2] == "pass") + skipped_tests = list(r for r in test_results if r[2] == "skip" and r[3] != "too large") + skipped_tests_too_large = list( + r for r in test_results if r[2] == "skip" and r[3] == "too large" + ) + failed_tests = list(r for r in test_results if r[2] == "fail") print( "{} tests performed ({} individual testcases)".format( @@ -1053,6 +1072,13 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): ) ) + if len(skipped_tests_too_large) > 0: + print( + "{} tests skipped because they are too large: {}".format( + len(skipped_tests_too_large), " ".join(test[0] for test in skipped_tests_too_large) + ) + ) + if len(failed_tests) > 0: print( "{} tests failed: {}".format( @@ -1069,9 +1095,11 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): with open(os.path.join(result_dir, RESULTS_FILE), "w") as f: json.dump( { + # The arguments passed on the command-line. "args": vars(args), - "passed_tests": [test[1] for test in passed_tests], - "skipped_tests": [test[1] for test in skipped_tests], + # A list of all results of the form [(test, result, reason), ...]. + "results": list(test[1:] for test in test_results), + # A list of failed tests. This is deprecated, use the "results" above instead. "failed_tests": [test[1] for test in failed_tests], }, f, @@ -1248,7 +1276,7 @@ the last matching regex is used: results_file = os.path.join(args.result_dir, RESULTS_FILE) if os.path.exists(results_file): with open(results_file, "r") as f: - tests = json.load(f)["failed_tests"] + tests = list(test[0] for test in json.load(f)["results"] if test[1] == "fail") else: tests = [] elif len(args.files) == 0: diff --git a/tools/ci.sh b/tools/ci.sh index d12b4bcd31..ea67e2c104 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -93,23 +93,30 @@ function ci_code_size_build { function code_size_build_step { COMMIT=$1 OUTFILE=$2 - IGNORE_ERRORS=$3 echo "Building ${COMMIT}..." git checkout --detach $COMMIT git submodule update --init $SUBMODULES git show -s tools/metrics.py clean $PORTS_TO_CHECK - tools/metrics.py build $PORTS_TO_CHECK | tee $OUTFILE || $IGNORE_ERRORS + tools/metrics.py build $PORTS_TO_CHECK | tee $OUTFILE + return $? } + # Allow errors from tools/metrics.py to propagate out of the pipe above. + set -o pipefail + # build reference, save to size0 # ignore any errors with this build, in case master is failing - code_size_build_step $REFERENCE ~/size0 true + code_size_build_step $REFERENCE ~/size0 # build PR/branch, save to size1 - code_size_build_step $COMPARISON ~/size1 false + code_size_build_step $COMPARISON ~/size1 + STATUS=$? + set +o pipefail unset -f code_size_build_step + + return $STATUS } ######################################################################################## @@ -524,38 +531,25 @@ function ci_native_mpy_modules_build { else arch=$1 fi - for natmod in features1 features3 features4 heapq re + for natmod in deflate features1 features3 features4 framebuf heapq random re do - make -C examples/natmod/$natmod clean + make -C examples/natmod/$natmod ARCH=$arch clean make -C examples/natmod/$natmod ARCH=$arch done - # deflate, framebuf, and random currently cannot build on xtensa due to - # some symbols that have been removed from the compiler's runtime, in - # favour of being provided from ROM. - if [ $arch != "xtensa" ]; then - for natmod in deflate framebuf random - do - make -C examples/natmod/$natmod clean - make -C examples/natmod/$natmod ARCH=$arch - done - fi - # features2 requires soft-float on armv7m, rv32imc, and xtensa. On armv6m # the compiler generates absolute relocations in the object file # referencing soft-float functions, which is not supported at the moment. - make -C examples/natmod/features2 clean + make -C examples/natmod/features2 ARCH=$arch clean if [ $arch = "rv32imc" ] || [ $arch = "armv7m" ] || [ $arch = "xtensa" ]; then make -C examples/natmod/features2 ARCH=$arch MICROPY_FLOAT_IMPL=float elif [ $arch != "armv6m" ]; then make -C examples/natmod/features2 ARCH=$arch fi - # btree requires thread local storage support on rv32imc, whilst on xtensa - # it relies on symbols that are provided from ROM but not exposed to - # natmods at the moment. - if [ $arch != "rv32imc" ] && [ $arch != "xtensa" ]; then - make -C examples/natmod/btree clean + # btree requires thread local storage support on rv32imc. + if [ $arch != "rv32imc" ]; then + make -C examples/natmod/btree ARCH=$arch clean make -C examples/natmod/btree ARCH=$arch fi } diff --git a/tools/mpy_ld.py b/tools/mpy_ld.py index a47653f900..6518037f2e 100755 --- a/tools/mpy_ld.py +++ b/tools/mpy_ld.py @@ -402,6 +402,7 @@ class LinkEnv: self.known_syms = {} # dict of symbols that are defined self.unresolved_syms = [] # list of unresolved symbols self.mpy_relocs = [] # list of relocations needed in the output .mpy file + self.externs = {} # dict of externally-defined symbols def check_arch(self, arch_name): if arch_name != self.arch.name: @@ -491,10 +492,14 @@ def populate_got(env): sym = got_entry.sym if hasattr(sym, "resolved"): sym = sym.resolved - sec = sym.section - addr = sym["st_value"] - got_entry.sec_name = sec.name - got_entry.link_addr += sec.addr + addr + if sym.name in env.externs: + got_entry.sec_name = ".external.fixed_addr" + got_entry.link_addr = env.externs[sym.name] + else: + sec = sym.section + addr = sym["st_value"] + got_entry.sec_name = sec.name + got_entry.link_addr += sec.addr + addr # Get sorted GOT, sorted by external, text, rodata, bss so relocations can be combined got_list = sorted( @@ -520,6 +525,9 @@ def populate_got(env): dest = int(got_entry.name.split("+")[1], 16) // env.arch.word_size elif got_entry.sec_name == ".external.mp_fun_table": dest = got_entry.sym.mp_fun_table_offset + elif got_entry.sec_name == ".external.fixed_addr": + # Fixed-address symbols should not be relocated. + continue elif got_entry.sec_name.startswith(".text"): dest = ".text" elif got_entry.sec_name.startswith(".rodata"): @@ -1207,6 +1215,9 @@ def link_objects(env, native_qstr_vals_len): sym.section = env.obj_table_section elif sym.name in env.known_syms: sym.resolved = env.known_syms[sym.name] + elif sym.name in env.externs: + # Fixed-address symbols do not need pre-processing. + continue else: if sym.name in fun_table: sym.section = mp_fun_table_sec @@ -1214,6 +1225,15 @@ def link_objects(env, native_qstr_vals_len): else: undef_errors.append("{}: undefined symbol: {}".format(sym.filename, sym.name)) + for sym in env.externs: + if sym in env.known_syms: + log( + LOG_LEVEL_1, + "Symbol {} is a fixed-address symbol at {:08x} and is also provided from an object file".format( + sym, env.externs[sym] + ), + ) + if undef_errors: raise LinkError("\n".join(undef_errors)) @@ -1456,6 +1476,9 @@ def do_link(args): log(LOG_LEVEL_2, "qstr vals: " + ", ".join(native_qstr_vals)) env = LinkEnv(args.arch) try: + if args.externs: + env.externs = parse_linkerscript(args.externs) + # Load object files for fn in args.files: with open(fn, "rb") as f: @@ -1484,6 +1507,50 @@ def do_link(args): sys.exit(1) +def parse_linkerscript(source): + # This extracts fixed-address symbol lists from linkerscripts, only parsing + # a small subset of all possible directives. Right now the only + # linkerscript file this is really tested against is the ESP8266's builtin + # ROM functions list ($SDK/ld/eagle.rom.addr.v6.ld). + # + # The parser should be able to handle symbol entries inside ESP-IDF's ROM + # symbol lists for the ESP32 range of MCUs as well (see *.ld files in + # $SDK/components/esp_rom/<name>/). + + symbols = {} + + LINE_REGEX = re.compile( + r'^(?P<weak>PROVIDE\()?' # optional weak marker start + r'(?P<symbol>[a-zA-Z_]\w*)' # symbol name + r'=0x(?P<address>[\da-fA-F]{1,8})*' # symbol address + r'(?(weak)\));$', # optional weak marker end and line terminator + re.ASCII, + ) + + inside_comment = False + for line in (line.strip() for line in source.readlines()): + if line.startswith('/*') and not inside_comment: + if not line.endswith('*/'): + inside_comment = True + continue + if inside_comment: + if line.endswith('*/'): + inside_comment = False + continue + if line.startswith('//'): + continue + match = LINE_REGEX.match(''.join(line.split())) + if not match: + continue + tokens = match.groupdict() + symbol = tokens['symbol'] + address = int(tokens['address'], 16) + if symbol in symbols: + raise ValueError(f"Symbol {symbol} already defined") + symbols[symbol] = address + return symbols + + def main(): import argparse @@ -1500,6 +1567,13 @@ def main(): cmd_parser.add_argument( "--output", "-o", default=None, help="output .mpy file (default to input with .o->.mpy)" ) + cmd_parser.add_argument( + "--externs", + "-e", + type=argparse.FileType("rt"), + default=None, + help="linkerscript providing fixed-address symbols to augment symbol resolution", + ) cmd_parser.add_argument("files", nargs="+", help="input files") args = cmd_parser.parse_args() |