From 5df1d8be6c1c55292824c698761159dc29b384d6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 17 Jan 2022 14:23:24 +1100 Subject: [PATCH 001/619] tests/run-multitests.py: Ignore lld_pdu_get_tx_flush_nb msgs from IDF. BLE still functions correctly even though these messages are sometimes printed by the IDF. Ignoring them allows the multi_bluetooth tests to pass on an esp32 board. Signed-off-by: Damien George --- tests/run-multitests.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/run-multitests.py b/tests/run-multitests.py index 34389e4292..20ef4a7aa6 100755 --- a/tests/run-multitests.py +++ b/tests/run-multitests.py @@ -76,12 +76,14 @@ multitest.flush() """ # The btstack implementation on Unix generates some spurious output that we -# can't control. +# can't control. Also other platforms may output certain warnings/errors that +# can be safely ignored. IGNORE_OUTPUT_MATCHES = ( "libusb: error ", # It tries to open devices that it doesn't have access to (libusb prints unconditionally). "hci_transport_h2_libusb.c", # Same issue. We enable LOG_ERROR in btstack. "USB Path: ", # Hardcoded in btstack's libusb transport. "hci_number_completed_packet", # Warning from btstack. + "lld_pdu_get_tx_flush_nb HCI packet count mismatch (", # From ESP-IDF, see https://github.com/espressif/esp-idf/issues/5105 ) From c54717a78f8640cbf4ae6c824569df253ffb689c Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 17 Jan 2022 17:15:31 +1100 Subject: [PATCH 002/619] tests/run-multitests.py: Set HOST_IP so tests work between PC and board. Signed-off-by: Damien George --- tests/run-multitests.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/tests/run-multitests.py b/tests/run-multitests.py index 20ef4a7aa6..f55d52b6e9 100755 --- a/tests/run-multitests.py +++ b/tests/run-multitests.py @@ -66,7 +66,7 @@ class multitest: import network ip = network.WLAN().ifconfig()[0] except: - ip = "127.0.0.1" + ip = HOST_IP return ip {} @@ -87,6 +87,20 @@ IGNORE_OUTPUT_MATCHES = ( ) +def get_host_ip(_ip_cache=[]): + if not _ip_cache: + try: + import socket + + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.connect(("8.8.8.8", 80)) + _ip_cache.append(s.getsockname()[0]) + s.close() + except: + _ip_cache.append("127.0.0.1") + return _ip_cache[0] + + class PyInstance: def __init__(self): pass @@ -274,6 +288,13 @@ def run_test_on_instances(test_file, num_instances, instances): injected_globals = "" output = [[] for _ in range(num_instances)] + # If the test calls get_network_ip() then inject HOST_IP so that devices can know + # the IP address of the host. Do this lazily to not require a TCP/IP connection + # on the host if it's not needed. + with open(test_file, "rb") as f: + if b"get_network_ip" in f.read(): + injected_globals += "HOST_IP = '" + get_host_ip() + "'\n" + if cmd_args.trace_output: print("TRACE {}:".format("|".join(str(i) for i in instances))) From 2c9dc5742abf1f3c787f166d2cbffb4b556ff77b Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 17 Jan 2022 17:20:06 +1100 Subject: [PATCH 003/619] tests/multi_net: Add testing key/cert to SSL server/client test. So that this tests works with mbedtls. Signed-off-by: Damien George --- tests/multi_net/ssl_data.py | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/tests/multi_net/ssl_data.py b/tests/multi_net/ssl_data.py index 81239b454c..cf2f96e0f3 100644 --- a/tests/multi_net/ssl_data.py +++ b/tests/multi_net/ssl_data.py @@ -1,10 +1,39 @@ # Simple test creating an SSL connection and transferring some data # This test won't run under CPython because it requires key/cert -import usocket as socket, ussl as ssl +import ubinascii as binascii, usocket as socket, ussl as ssl PORT = 8000 +# This self-signed key/cert pair is randomly generated and to be used for +# testing/demonstration only. You should always generate your own key/cert. +key = binascii.unhexlify( + b"3082013b020100024100cc20643fd3d9c21a0acba4f48f61aadd675f52175a9dcf07fbef" + b"610a6a6ba14abb891745cd18a1d4c056580d8ff1a639460f867013c8391cdc9f2e573b0f" + b"872d0203010001024100bb17a54aeb3dd7ae4edec05e775ca9632cf02d29c2a089b563b0" + b"d05cdf95aeca507de674553f28b4eadaca82d5549a86058f9996b07768686a5b02cb240d" + b"d9f1022100f4a63f5549e817547dca97b5c658038e8593cb78c5aba3c4642cc4cd031d86" + b"8f022100d598d870ffe4a34df8de57047a50b97b71f4d23e323f527837c9edae88c79483" + b"02210098560c89a70385c36eb07fd7083235c4c1184e525d838aedf7128958bedfdbb102" + b"2051c0dab7057a8176ca966f3feb81123d4974a733df0f958525f547dfd1c271f9022044" + b"6c2cafad455a671a8cf398e642e1be3b18a3d3aec2e67a9478f83c964c4f1f" +) +cert = binascii.unhexlify( + b"308201d53082017f020203e8300d06092a864886f70d01010505003075310b3009060355" + b"0406130258583114301206035504080c0b54686550726f76696e63653110300e06035504" + b"070c075468654369747931133011060355040a0c0a436f6d70616e7958595a3113301106" + b"0355040b0c0a436f6d70616e7958595a3114301206035504030c0b546865486f73744e61" + b"6d65301e170d3139313231383033333935355a170d3239313231353033333935355a3075" + b"310b30090603550406130258583114301206035504080c0b54686550726f76696e636531" + b"10300e06035504070c075468654369747931133011060355040a0c0a436f6d70616e7958" + b"595a31133011060355040b0c0a436f6d70616e7958595a3114301206035504030c0b5468" + b"65486f73744e616d65305c300d06092a864886f70d0101010500034b003048024100cc20" + b"643fd3d9c21a0acba4f48f61aadd675f52175a9dcf07fbef610a6a6ba14abb891745cd18" + b"a1d4c056580d8ff1a639460f867013c8391cdc9f2e573b0f872d0203010001300d06092a" + b"864886f70d0101050500034100b0513fe2829e9ecbe55b6dd14c0ede7502bde5d46153c8" + b"e960ae3ebc247371b525caeb41bbcf34686015a44c50d226e66aef0a97a63874ca5944ef" + b"979b57f0b3" +) # Server def instance0(): @@ -15,7 +44,7 @@ def instance0(): s.listen(1) multitest.next() s2, _ = s.accept() - s2 = ssl.wrap_socket(s2, server_side=True) + s2 = ssl.wrap_socket(s2, server_side=True, key=key, cert=cert) print(s2.read(16)) s2.write(b"server to client") s.close() From 5e506567a390db1b1d2df39ae192f39ed9f617c3 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 17 Jan 2022 17:21:48 +1100 Subject: [PATCH 004/619] stm32/mbedtls: Enable MBEDTLS_TLS_DEFAULT_ALLOW_SHA1_IN_KEY_EXCHANGE. This adds MBEDTLS_MD_SHA1 to the list of default hashes for TLS 1.2 handshake signatures. Although SHA-1 is weak, this option is turned on in the default mbedtls configuration file, and allows better compatibility with older servers. In particular it allows an stm32-mbedtls-based client to connect to an axtls-based client (eg default unix port and esp8266). Signed-off-by: Damien George --- ports/stm32/mbedtls/mbedtls_config.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/stm32/mbedtls/mbedtls_config.h b/ports/stm32/mbedtls/mbedtls_config.h index 56fbbf3aaf..904d08548f 100644 --- a/ports/stm32/mbedtls/mbedtls_config.h +++ b/ports/stm32/mbedtls/mbedtls_config.h @@ -82,6 +82,7 @@ #define MBEDTLS_SSL_CLI_C #define MBEDTLS_SSL_SRV_C #define MBEDTLS_SSL_TLS_C +#define MBEDTLS_TLS_DEFAULT_ALLOW_SHA1_IN_KEY_EXCHANGE #define MBEDTLS_X509_CRT_PARSE_C #define MBEDTLS_X509_USE_C From 037b2c72a1d5b54a5508a58ab2044628a7a39fa4 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Mon, 10 Jan 2022 09:03:27 -0600 Subject: [PATCH 005/619] py/objstr: Support '{:08}'.format("Jan") like Python 3.10. The new test has an .exp file, because it is not compatible with Python 3.9 and lower. See CPython version of the issue at https://bugs.python.org/issue27772 Signed-off-by: Jeff Epler --- py/objstr.c | 2 +- tests/basics/string_format_cp310.py | 9 +++++++++ tests/basics/string_format_cp310.py.exp | 4 ++++ tests/basics/string_format_error.py | 2 +- 4 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 tests/basics/string_format_cp310.py create mode 100644 tests/basics/string_format_cp310.py.exp diff --git a/py/objstr.c b/py/objstr.c index 7d7f0e1dfa..321bb058dc 100644 --- a/py/objstr.c +++ b/py/objstr.c @@ -1163,7 +1163,7 @@ STATIC vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *ar s++; } if (*s == '0') { - if (!align) { + if (!align && arg_looks_numeric(arg)) { align = '='; } if (!fill) { diff --git a/tests/basics/string_format_cp310.py b/tests/basics/string_format_cp310.py new file mode 100644 index 0000000000..77295330be --- /dev/null +++ b/tests/basics/string_format_cp310.py @@ -0,0 +1,9 @@ +# Python 3.10+ functionality test for {} format string + +def test(fmt, *args): + print('{:8s}'.format(fmt) + '>' + fmt.format(*args) + '<') + +test("{:0s}", "ab") +test("{:06s}", "ab") +test("{:<06s}", "ab") +test("{:>06s}", "ab") diff --git a/tests/basics/string_format_cp310.py.exp b/tests/basics/string_format_cp310.py.exp new file mode 100644 index 0000000000..1c26473d83 --- /dev/null +++ b/tests/basics/string_format_cp310.py.exp @@ -0,0 +1,4 @@ +{:0s} >ab< +{:06s} >ab0000< +{:<06s} >ab0000< +{:>06s} >0000ab< diff --git a/tests/basics/string_format_error.py b/tests/basics/string_format_error.py index 708348d59f..74dc6c52ed 100644 --- a/tests/basics/string_format_error.py +++ b/tests/basics/string_format_error.py @@ -1,7 +1,7 @@ # tests for errors in {} format string try: - '{0:0}'.format('zzz') + '{0:=}'.format('zzz') except (ValueError): print('ValueError') From 608d421752c55e466cb8c50d26ab2a172280dfd2 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 20 Jan 2022 16:08:17 +1100 Subject: [PATCH 006/619] stm32/mboot: Remove custom HAL_RCC_GetHCLKFreq and use HAL provided one. So that a board can access other HAL_RCC functions if it needs them (this was not possible previously by just adding hal_rcc.c to the src list for a board because it would clash with the custom HAL_RCC_GetHCLKFreq function). Signed-off-by: Damien George --- ports/stm32/mboot/Makefile | 1 + ports/stm32/mboot/main.c | 5 ----- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/ports/stm32/mboot/Makefile b/ports/stm32/mboot/Makefile index 553fc4ac66..7e1b497cef 100755 --- a/ports/stm32/mboot/Makefile +++ b/ports/stm32/mboot/Makefile @@ -161,6 +161,7 @@ SRC_HAL += $(addprefix $(HAL_DIR)/Src/stm32$(MCU_SERIES)xx_,\ hal_flash_ex.c \ hal_pcd.c \ hal_pcd_ex.c \ + hal_rcc.c \ ll_usb.c \ ) diff --git a/ports/stm32/mboot/main.c b/ports/stm32/mboot/main.c index 278c07fb4a..3c3e4a8642 100644 --- a/ports/stm32/mboot/main.c +++ b/ports/stm32/mboot/main.c @@ -326,11 +326,6 @@ void SystemClock_Config(void) { #endif -// Needed by HAL_PCD_IRQHandler -uint32_t HAL_RCC_GetHCLKFreq(void) { - return SystemCoreClock; -} - /******************************************************************************/ // GPIO From ce4f8b49ceed0340cdd05d4935e3a1147d0d3d39 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 20 Jan 2022 16:35:49 +1100 Subject: [PATCH 007/619] tools/mpremote: Use machine instead of umachine in commands. Because bare-metal boards will have machine but not always umachine. Signed-off-by: Damien George --- tools/mpremote/mpremote/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/mpremote/mpremote/main.py b/tools/mpremote/mpremote/main.py index 99e7bcd9bc..13615f6dd2 100644 --- a/tools/mpremote/mpremote/main.py +++ b/tools/mpremote/mpremote/main.py @@ -80,7 +80,7 @@ _BUILTIN_COMMAND_EXPANSIONS = { "command": [ "exec", "--no-follow", - "import utime, umachine; utime.sleep_ms(t_ms); umachine.reset()", + "import utime, machine; utime.sleep_ms(t_ms); machine.reset()", ], "help": "reset the device after delay", }, @@ -88,7 +88,7 @@ _BUILTIN_COMMAND_EXPANSIONS = { "command": [ "exec", "--no-follow", - "import utime, umachine; utime.sleep_ms(t_ms); umachine.bootloader()", + "import utime, machine; utime.sleep_ms(t_ms); machine.bootloader()", ], "help": "make the device enter its bootloader", }, From 7b0a42374e1ae6a4ab3f756090c6f3b66e82acb5 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 20 Jan 2022 16:43:55 +1100 Subject: [PATCH 008/619] rp2/machine_i2c: Provide more specific error codes from I2C transfer. Signed-off-by: Damien George --- ports/rp2/machine_i2c.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/ports/rp2/machine_i2c.c b/ports/rp2/machine_i2c.c index 3b895ba4d4..56f012a5e8 100644 --- a/ports/rp2/machine_i2c.c +++ b/ports/rp2/machine_i2c.c @@ -145,11 +145,20 @@ STATIC int machine_i2c_transfer_single(mp_obj_base_t *self_in, uint16_t addr, si ret = mp_machine_soft_i2c_transfer(&soft_i2c.base, addr, 1, &bufs, flags); gpio_set_function(self->scl, GPIO_FUNC_I2C); gpio_set_function(self->sda, GPIO_FUNC_I2C); + return ret; } else { ret = i2c_write_blocking(self->i2c_inst, addr, buf, len, nostop); } } - return (ret < 0) ? -MP_EIO : ret; + if (ret < 0) { + if (ret == PICO_ERROR_TIMEOUT) { + return -MP_ETIMEDOUT; + } else { + return -MP_EIO; + } + } else { + return ret; + } } STATIC const mp_machine_i2c_p_t machine_i2c_p = { From f44fb760551809d038d3fda49ffe07975e92091d Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Tue, 18 Jan 2022 22:06:02 +0200 Subject: [PATCH 009/619] rp2/mpconfigport.h: Use internal error numbers. --- ports/rp2/mpconfigport.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h index e6a85bb682..63d9818e31 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -70,6 +70,7 @@ #define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1) #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) +#define MICROPY_USE_INTERNAL_ERRNO (1) #define MICROPY_SCHEDULER_DEPTH (8) // Fine control over Python builtins, classes, modules, etc From 5db278f1dd061ea0d7d9263cc6b4a3b212b34caf Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sat, 15 Jan 2022 04:32:01 +0200 Subject: [PATCH 010/619] rp2/mphalport: Add optional dupterm support. --- ports/rp2/moduos.c | 1 + ports/rp2/mphalport.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/ports/rp2/moduos.c b/ports/rp2/moduos.c index 02f34eb960..a9dc30df89 100644 --- a/ports/rp2/moduos.c +++ b/ports/rp2/moduos.c @@ -26,6 +26,7 @@ #include "py/objstr.h" #include "py/runtime.h" +#include "extmod/misc.h" #include "extmod/vfs.h" #include "extmod/vfs_fat.h" #include "extmod/vfs_lfs.h" diff --git a/ports/rp2/mphalport.c b/ports/rp2/mphalport.c index 3385323db0..c4169fb66c 100644 --- a/ports/rp2/mphalport.c +++ b/ports/rp2/mphalport.c @@ -27,6 +27,7 @@ #include "py/runtime.h" #include "py/stream.h" #include "py/mphal.h" +#include "extmod/misc.h" #include "shared/timeutils/timeutils.h" #include "tusb.h" #include "uart.h" @@ -74,6 +75,9 @@ uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { ret |= MP_STREAM_POLL_RD; } #endif + #if MICROPY_PY_OS_DUPTERM + ret |= mp_uos_dupterm_poll(poll_flags); + #endif return ret; } @@ -95,6 +99,12 @@ int mp_hal_stdin_rx_chr(void) { } } #endif + #if MICROPY_PY_OS_DUPTERM + int dupterm_c = mp_uos_dupterm_rx_chr(); + if (dupterm_c >= 0) { + return dupterm_c; + } + #endif MICROPY_EVENT_POLL_HOOK } } @@ -123,6 +133,10 @@ void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { } } #endif + + #if MICROPY_PY_OS_DUPTERM + mp_uos_dupterm_tx_strn(str, len); + #endif } void mp_hal_delay_ms(mp_uint_t ms) { From 981664fd079a379dfd9749e9068781590813b401 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Fri, 14 Jan 2022 02:16:44 +0200 Subject: [PATCH 011/619] drivers/ninaw10: Add function to check socket state/data availability. --- drivers/ninaw10/nina_wifi_drv.c | 30 ++++++++++++++++++++++-------- drivers/ninaw10/nina_wifi_drv.h | 1 + 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/drivers/ninaw10/nina_wifi_drv.c b/drivers/ninaw10/nina_wifi_drv.c index b7c016f421..e7eac478be 100644 --- a/drivers/ninaw10/nina_wifi_drv.c +++ b/drivers/ninaw10/nina_wifi_drv.c @@ -132,7 +132,7 @@ typedef enum { NINA_CMD_SOCKET_OPEN = 0x3F, NINA_CMD_SOCKET_CLOSE = 0x2E, NINA_CMD_SOCKET_CONNECT = 0x2D, - NINA_CMD_SOCKET_ACCEPT = 0x2B, + NINA_CMD_SOCKET_AVAIL = 0x2B, NINA_CMD_SOCKET_BIND = 0x28, NINA_CMD_SOCKET_STATE = 0x2F, NINA_CMD_SOCKET_REMOTE_ADDR = 0x3A, @@ -752,21 +752,35 @@ int nina_socket_listen(int fd, uint32_t backlog) { return 0; // No listen ? } -int nina_socket_accept(int fd, uint8_t *ip, uint16_t *port, int *fd_out, uint32_t timeout) { +int nina_socket_avail(int fd, int type, uint16_t *data) { uint16_t size = 2; - uint16_t sock = NO_SOCKET_AVAIL; + + if (nina_send_command_read_vals(NINA_CMD_SOCKET_AVAIL, + 1, ARG_8BITS, NINA_ARGS(ARG_BYTE(fd)), + 1, ARG_8BITS, NINA_VALS({&size, data})) != 0) { + return -1; + } + + // For TCP sockets in listen state, return 0 if there's no accepted socket. + if (*data == NO_SOCKET_AVAIL && type == NINA_SOCKET_TYPE_TCP + && nina_server_socket_status(fd) == SOCKET_STATE_LISTEN) { + *data = 0; + } + + return 0; +} + +int nina_socket_accept(int fd, uint8_t *ip, uint16_t *port, int *fd_out, uint32_t timeout) { + uint16_t sock = 0; if (nina_server_socket_status(fd) != SOCKET_STATE_LISTEN) { return -1; } - for (mp_uint_t start = mp_hal_ticks_ms(); sock == 0 || sock == NO_SOCKET_AVAIL; mp_hal_delay_ms(10)) { - if (nina_send_command_read_vals(NINA_CMD_SOCKET_ACCEPT, - 1, ARG_8BITS, NINA_ARGS(ARG_BYTE(fd)), - 1, ARG_8BITS, NINA_VALS({&size, &sock})) != 0) { + for (mp_uint_t start = mp_hal_ticks_ms(); !sock; mp_hal_delay_ms(10)) { + if (nina_socket_avail(fd, NINA_SOCKET_TYPE_TCP, &sock) != 0) { return -1; } - if (timeout && (mp_hal_ticks_ms() - start) >= timeout) { return NINA_ERROR_TIMEOUT; } diff --git a/drivers/ninaw10/nina_wifi_drv.h b/drivers/ninaw10/nina_wifi_drv.h index d8c55f5e67..8b13bfc1f5 100644 --- a/drivers/ninaw10/nina_wifi_drv.h +++ b/drivers/ninaw10/nina_wifi_drv.h @@ -109,6 +109,7 @@ int nina_socket_socket(uint8_t type); int nina_socket_close(int fd); int nina_socket_bind(int fd, uint8_t *ip, uint16_t port, int type); int nina_socket_listen(int fd, uint32_t backlog); +int nina_socket_avail(int fd, int type, uint16_t *data); int nina_socket_accept(int fd, uint8_t *ip, uint16_t *port, int *fd_out, uint32_t timeout); int nina_socket_connect(int fd, uint8_t *ip, uint16_t port, uint32_t timeout); int nina_socket_send(int fd, const uint8_t *buf, uint32_t len, uint32_t timeout); From 9a61bc3aa78058bf222dec3f01ce3b90fa79108a Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Fri, 14 Jan 2022 02:17:11 +0200 Subject: [PATCH 012/619] extmod/network_ninaw10: Implement MP_STREAM_POLL in ioctl. There is currently no function to query if the socket is writable. --- extmod/network_ninaw10.c | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/extmod/network_ninaw10.c b/extmod/network_ninaw10.c index cba24ea94c..c8fd0ce317 100644 --- a/extmod/network_ninaw10.c +++ b/extmod/network_ninaw10.c @@ -545,8 +545,42 @@ STATIC int network_ninaw10_socket_settimeout(mod_network_socket_obj_t *socket, m } STATIC int network_ninaw10_socket_ioctl(mod_network_socket_obj_t *socket, mp_uint_t request, mp_uint_t arg, int *_errno) { - *_errno = MP_EIO; - return -1; + mp_uint_t ret = 0; + uint8_t type; + + switch (socket->type) { + case MOD_NETWORK_SOCK_STREAM: + type = NINA_SOCKET_TYPE_TCP; + break; + + case MOD_NETWORK_SOCK_DGRAM: + type = NINA_SOCKET_TYPE_UDP; + break; + + default: + *_errno = MP_EINVAL; + return MP_STREAM_ERROR; + } + + if (request == MP_STREAM_POLL) { + if (arg & MP_STREAM_POLL_RD) { + uint16_t avail = 0; + if (nina_socket_avail(socket->fileno, type, &avail) != 0) { + *_errno = MP_EIO; + ret = MP_STREAM_ERROR; + } else if (avail) { + // Readable or accepted socket ready. + ret |= MP_STREAM_POLL_RD; + } + } + if (arg & MP_STREAM_POLL_WR) { + ret |= MP_STREAM_POLL_WR; + } + } else { + *_errno = MP_EINVAL; + ret = MP_STREAM_ERROR; + } + return ret; } static const mp_rom_map_elem_t nina_locals_dict_table[] = { From e401ff8935fb7764adede3ee4e0d9a6cf974c6c3 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Fri, 14 Jan 2022 02:59:07 +0200 Subject: [PATCH 013/619] drivers/ninaw10: Fix timeout handling to match modusocket. --- drivers/ninaw10/nina_wifi_drv.c | 30 +++++++++++++++++++----------- drivers/ninaw10/nina_wifi_drv.h | 12 ++++++------ extmod/network_ninaw10.c | 16 ++++++++++------ 3 files changed, 35 insertions(+), 23 deletions(-) diff --git a/drivers/ninaw10/nina_wifi_drv.c b/drivers/ninaw10/nina_wifi_drv.c index e7eac478be..12707fe051 100644 --- a/drivers/ninaw10/nina_wifi_drv.c +++ b/drivers/ninaw10/nina_wifi_drv.c @@ -770,7 +770,7 @@ int nina_socket_avail(int fd, int type, uint16_t *data) { return 0; } -int nina_socket_accept(int fd, uint8_t *ip, uint16_t *port, int *fd_out, uint32_t timeout) { +int nina_socket_accept(int fd, uint8_t *ip, uint16_t *port, int *fd_out, int32_t timeout) { uint16_t sock = 0; if (nina_server_socket_status(fd) != SOCKET_STATE_LISTEN) { @@ -781,7 +781,7 @@ int nina_socket_accept(int fd, uint8_t *ip, uint16_t *port, int *fd_out, uint32_ if (nina_socket_avail(fd, NINA_SOCKET_TYPE_TCP, &sock) != 0) { return -1; } - if (timeout && (mp_hal_ticks_ms() - start) >= timeout) { + if (timeout == 0 || (timeout > 0 && (mp_hal_ticks_ms() - start) >= timeout)) { return NINA_ERROR_TIMEOUT; } } @@ -798,7 +798,7 @@ int nina_socket_accept(int fd, uint8_t *ip, uint16_t *port, int *fd_out, uint32_ return 0; } -int nina_socket_connect(int fd, uint8_t *ip, uint16_t port, uint32_t timeout) { +int nina_socket_connect(int fd, uint8_t *ip, uint16_t port, int32_t timeout) { if (nina_send_command_read_ack(NINA_CMD_SOCKET_CONNECT, 4, ARG_8BITS, NINA_ARGS( @@ -819,7 +819,7 @@ int nina_socket_connect(int fd, uint8_t *ip, uint16_t port, uint32_t timeout) { break; } - if (timeout && (mp_hal_ticks_ms() - start) >= timeout) { + if (timeout == 0 || (timeout > 0 && (mp_hal_ticks_ms() - start) >= timeout)) { return NINA_ERROR_TIMEOUT; } } @@ -827,7 +827,7 @@ int nina_socket_connect(int fd, uint8_t *ip, uint16_t port, uint32_t timeout) { return 0; } -int nina_socket_send(int fd, const uint8_t *buf, uint32_t len, uint32_t timeout) { +int nina_socket_send(int fd, const uint8_t *buf, uint32_t len, int32_t timeout) { uint16_t size = 2; uint16_t bytes = 0; @@ -853,7 +853,7 @@ int nina_socket_send(int fd, const uint8_t *buf, uint32_t len, uint32_t timeout) break; } - if (timeout && (mp_hal_ticks_ms() - start) >= timeout) { + if (timeout == 0 || (timeout > 0 && (mp_hal_ticks_ms() - start) >= timeout)) { return NINA_ERROR_TIMEOUT; } mp_hal_delay_ms(1); @@ -862,7 +862,7 @@ int nina_socket_send(int fd, const uint8_t *buf, uint32_t len, uint32_t timeout) return bytes; } -int nina_socket_recv(int fd, uint8_t *buf, uint32_t len, uint32_t timeout) { +int nina_socket_recv(int fd, uint8_t *buf, uint32_t len, int32_t timeout) { uint16_t bytes = 0; if (nina_socket_status(fd) != SOCKET_STATE_ESTABLISHED) { @@ -877,7 +877,11 @@ int nina_socket_recv(int fd, uint8_t *buf, uint32_t len, uint32_t timeout) { return -1; } - if (timeout && (mp_hal_ticks_ms() - start) >= timeout) { + if (bytes != 0) { + break; + } + + if (timeout == 0 || (timeout > 0 && (mp_hal_ticks_ms() - start) >= timeout)) { return NINA_ERROR_TIMEOUT; } } @@ -885,7 +889,7 @@ int nina_socket_recv(int fd, uint8_t *buf, uint32_t len, uint32_t timeout) { } // Check from the upper layer if the socket is bound, if not then auto-bind it first. -int nina_socket_sendto(int fd, const uint8_t *buf, uint32_t len, uint8_t *ip, uint16_t port, uint32_t timeout) { +int nina_socket_sendto(int fd, const uint8_t *buf, uint32_t len, uint8_t *ip, uint16_t port, int32_t timeout) { // TODO do we need to split the packet somewhere? if (nina_send_command_read_ack(NINA_CMD_SOCKET_CONNECT, 4, ARG_8BITS, @@ -912,7 +916,7 @@ int nina_socket_sendto(int fd, const uint8_t *buf, uint32_t len, uint8_t *ip, ui } // Check from the upper layer if the socket is bound, if not then auto-bind it first. -int nina_socket_recvfrom(int fd, uint8_t *buf, uint32_t len, uint8_t *ip, uint16_t *port, uint32_t timeout) { +int nina_socket_recvfrom(int fd, uint8_t *buf, uint32_t len, uint8_t *ip, uint16_t *port, int32_t timeout) { uint16_t bytes = 0; uint16_t port_len = 2; uint16_t ip_len = NINA_IPV4_ADDR_LEN; @@ -925,7 +929,11 @@ int nina_socket_recvfrom(int fd, uint8_t *buf, uint32_t len, uint8_t *ip, uint16 return -1; } - if (timeout && (mp_hal_ticks_ms() - start) >= timeout) { + if (bytes != 0) { + break; + } + + if (timeout == 0 || (timeout > 0 && (mp_hal_ticks_ms() - start) >= timeout)) { return NINA_ERROR_TIMEOUT; } } diff --git a/drivers/ninaw10/nina_wifi_drv.h b/drivers/ninaw10/nina_wifi_drv.h index 8b13bfc1f5..b8a6c4eb92 100644 --- a/drivers/ninaw10/nina_wifi_drv.h +++ b/drivers/ninaw10/nina_wifi_drv.h @@ -110,12 +110,12 @@ int nina_socket_close(int fd); int nina_socket_bind(int fd, uint8_t *ip, uint16_t port, int type); int nina_socket_listen(int fd, uint32_t backlog); int nina_socket_avail(int fd, int type, uint16_t *data); -int nina_socket_accept(int fd, uint8_t *ip, uint16_t *port, int *fd_out, uint32_t timeout); -int nina_socket_connect(int fd, uint8_t *ip, uint16_t port, uint32_t timeout); -int nina_socket_send(int fd, const uint8_t *buf, uint32_t len, uint32_t timeout); -int nina_socket_recv(int fd, uint8_t *buf, uint32_t len, uint32_t timeout); -int nina_socket_sendto(int fd, const uint8_t *buf, uint32_t len, uint8_t *ip, uint16_t port, uint32_t timeout); -int nina_socket_recvfrom(int fd, uint8_t *buf, uint32_t len, uint8_t *ip, uint16_t *port, uint32_t timeout); +int nina_socket_accept(int fd, uint8_t *ip, uint16_t *port, int *fd_out, int32_t timeout); +int nina_socket_connect(int fd, uint8_t *ip, uint16_t port, int32_t timeout); +int nina_socket_send(int fd, const uint8_t *buf, uint32_t len, int32_t timeout); +int nina_socket_recv(int fd, uint8_t *buf, uint32_t len, int32_t timeout); +int nina_socket_sendto(int fd, const uint8_t *buf, uint32_t len, uint8_t *ip, uint16_t port, int32_t timeout); +int nina_socket_recvfrom(int fd, uint8_t *buf, uint32_t len, uint8_t *ip, uint16_t *port, int32_t timeout); int nina_socket_setsockopt(int fd, uint32_t level, uint32_t opt, const void *optval, uint32_t optlen); #endif // MICROPY_INCLUDED_DRIVERS_NINAW10_NINA_WIFI_DRV_H diff --git a/extmod/network_ninaw10.c b/extmod/network_ninaw10.c index c8fd0ce317..26b4a811dc 100644 --- a/extmod/network_ninaw10.c +++ b/extmod/network_ninaw10.c @@ -406,7 +406,11 @@ STATIC int network_ninaw10_socket_accept(mod_network_socket_obj_t *socket, int fd = 0; // Call accept. int ret = nina_socket_accept(socket->fileno, ip, (uint16_t *)port, &fd, socket->timeout); - if (ret < 0) { + if (ret == NINA_ERROR_TIMEOUT) { + // The socket is Not closed on timeout when calling functions that accept a timeout. + *_errno = MP_ETIMEDOUT; + return -1; + } else if (ret < 0) { *_errno = ret; network_ninaw10_socket_close(socket); return -1; @@ -420,7 +424,11 @@ STATIC int network_ninaw10_socket_accept(mod_network_socket_obj_t *socket, STATIC int network_ninaw10_socket_connect(mod_network_socket_obj_t *socket, byte *ip, mp_uint_t port, int *_errno) { int ret = nina_socket_connect(socket->fileno, ip, port, socket->timeout); - if (ret < 0) { + if (ret == NINA_ERROR_TIMEOUT) { + // The socket is Not closed on timeout when calling functions that accept a timeout. + *_errno = MP_ETIMEDOUT; + return -1; + } else if (ret < 0) { *_errno = ret; network_ninaw10_socket_close(socket); return -1; @@ -536,10 +544,6 @@ STATIC int network_ninaw10_socket_setsockopt(mod_network_socket_obj_t *socket, m } STATIC int network_ninaw10_socket_settimeout(mod_network_socket_obj_t *socket, mp_uint_t timeout_ms, int *_errno) { - if (timeout_ms == UINT32_MAX) { - // no timeout is given, set the socket to blocking mode. - timeout_ms = 0; - } socket->timeout = timeout_ms; return 0; } From b23178a9c0606d933d231bb8acf4206b153957ce Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Fri, 14 Jan 2022 19:42:00 +0200 Subject: [PATCH 014/619] extmod/modusocket: Make setsockopt return if NIC is not connected. --- extmod/modusocket.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/extmod/modusocket.c b/extmod/modusocket.c index a42b3213f5..09a334d67e 100644 --- a/extmod/modusocket.c +++ b/extmod/modusocket.c @@ -303,6 +303,11 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_recvfrom_obj, socket_recvfrom); STATIC mp_obj_t socket_setsockopt(size_t n_args, const mp_obj_t *args) { mod_network_socket_obj_t *self = MP_OBJ_TO_PTR(args[0]); + if (self->nic == MP_OBJ_NULL) { + // not connected + return mp_const_none; + } + mp_int_t level = mp_obj_get_int(args[1]); mp_int_t opt = mp_obj_get_int(args[2]); From 155eb1361ec18caebc0ef33c115d91209309982a Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sat, 15 Jan 2022 21:32:53 +0200 Subject: [PATCH 015/619] extmod/modusocket: Add makefile() method and common socket options. --- extmod/modnetwork.h | 9 +++++++++ extmod/modusocket.c | 13 +++++++++++++ 2 files changed, 22 insertions(+) diff --git a/extmod/modnetwork.h b/extmod/modnetwork.h index bbf80bc474..b95760897a 100644 --- a/extmod/modnetwork.h +++ b/extmod/modnetwork.h @@ -38,6 +38,15 @@ #define MOD_NETWORK_STA_IF (0) #define MOD_NETWORK_AP_IF (1) +// Socket level option. +#define MOD_NETWORK_SOL_SOCKET (0x0FFF) + +// Common option flags per-socket. +#define MOD_NETWORK_SO_REUSEADDR (0x0004) +#define MOD_NETWORK_SO_KEEPALIVE (0x0008) +#define MOD_NETWORK_SO_SNDTIMEO (0x1005) +#define MOD_NETWORK_SO_RCVTIMEO (0x1006) + #if MICROPY_PY_LWIP struct netif; void mod_network_lwip_poll_wrapper(uint32_t ticks_ms); diff --git a/extmod/modusocket.c b/extmod/modusocket.c index 09a334d67e..373f2c125f 100644 --- a/extmod/modusocket.c +++ b/extmod/modusocket.c @@ -334,6 +334,12 @@ STATIC mp_obj_t socket_setsockopt(size_t n_args, const mp_obj_t *args) { } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_setsockopt_obj, 4, 4, socket_setsockopt); +STATIC mp_obj_t socket_makefile(size_t n_args, const mp_obj_t *args) { + (void)n_args; + return args[0]; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_makefile_obj, 1, 3, socket_makefile); + // method socket.settimeout(value) // timeout=0 means non-blocking // timeout=None means blocking @@ -390,6 +396,7 @@ STATIC const mp_rom_map_elem_t socket_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_sendto), MP_ROM_PTR(&socket_sendto_obj) }, { MP_ROM_QSTR(MP_QSTR_recvfrom), MP_ROM_PTR(&socket_recvfrom_obj) }, { MP_ROM_QSTR(MP_QSTR_setsockopt), MP_ROM_PTR(&socket_setsockopt_obj) }, + { MP_ROM_QSTR(MP_QSTR_makefile), MP_ROM_PTR(&socket_makefile_obj) }, { MP_ROM_QSTR(MP_QSTR_settimeout), MP_ROM_PTR(&socket_settimeout_obj) }, { MP_ROM_QSTR(MP_QSTR_setblocking), MP_ROM_PTR(&socket_setblocking_obj) }, @@ -528,6 +535,12 @@ STATIC const mp_rom_map_elem_t mp_module_usocket_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_SOCK_DGRAM), MP_ROM_INT(MOD_NETWORK_SOCK_DGRAM) }, { MP_ROM_QSTR(MP_QSTR_SOCK_RAW), MP_ROM_INT(MOD_NETWORK_SOCK_RAW) }, + { MP_ROM_QSTR(MP_QSTR_SOL_SOCKET), MP_ROM_INT(MOD_NETWORK_SOL_SOCKET) }, + { MP_ROM_QSTR(MP_QSTR_SO_REUSEADDR), MP_ROM_INT(MOD_NETWORK_SO_REUSEADDR) }, + { MP_ROM_QSTR(MP_QSTR_SO_KEEPALIVE), MP_ROM_INT(MOD_NETWORK_SO_KEEPALIVE) }, + { MP_ROM_QSTR(MP_QSTR_SO_SNDTIMEO), MP_ROM_INT(MOD_NETWORK_SO_SNDTIMEO) }, + { MP_ROM_QSTR(MP_QSTR_SO_RCVTIMEO), MP_ROM_INT(MOD_NETWORK_SO_RCVTIMEO) }, + /* { MP_ROM_QSTR(MP_QSTR_IPPROTO_IP), MP_ROM_INT(MOD_NETWORK_IPPROTO_IP) }, { MP_ROM_QSTR(MP_QSTR_IPPROTO_ICMP), MP_ROM_INT(MOD_NETWORK_IPPROTO_ICMP) }, From e6ddda29caa3292110c4d18625e791af47ca1e15 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Mon, 17 Jan 2022 15:15:57 +0200 Subject: [PATCH 016/619] tests/multi_net: Close accepted sockets when tests are done. gc_sweep_all() cleans up sockets via the finaliser, but tests should cleanly free resources they use. --- tests/multi_net/ssl_data.py | 1 + tests/multi_net/tcp_client_rst.py | 1 + tests/multi_net/tcp_data.py | 1 + 3 files changed, 3 insertions(+) diff --git a/tests/multi_net/ssl_data.py b/tests/multi_net/ssl_data.py index cf2f96e0f3..e5196c81ba 100644 --- a/tests/multi_net/ssl_data.py +++ b/tests/multi_net/ssl_data.py @@ -47,6 +47,7 @@ def instance0(): s2 = ssl.wrap_socket(s2, server_side=True, key=key, cert=cert) print(s2.read(16)) s2.write(b"server to client") + s2.close() s.close() diff --git a/tests/multi_net/tcp_client_rst.py b/tests/multi_net/tcp_client_rst.py index 14da8b1470..1fe994f36c 100644 --- a/tests/multi_net/tcp_client_rst.py +++ b/tests/multi_net/tcp_client_rst.py @@ -38,6 +38,7 @@ def instance0(): # TODO lwip raises here but apparently it shouldn't print(s2.recv(10)) print(convert_poll_list(poll.poll(1000))) + s2.close() s.close() diff --git a/tests/multi_net/tcp_data.py b/tests/multi_net/tcp_data.py index 61224efd2c..bba2d198c4 100644 --- a/tests/multi_net/tcp_data.py +++ b/tests/multi_net/tcp_data.py @@ -15,6 +15,7 @@ def instance0(): s2, _ = s.accept() print(s2.recv(16)) s2.send(b"server to client") + s2.close() s.close() From 6e8f4eaa5252c1a0e7b44238eb168c01abeb7a0e Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Tue, 18 Jan 2022 23:50:53 +0200 Subject: [PATCH 017/619] tests/multi_net/udp_data.py: Allow reusing port before bind. --- tests/multi_net/udp_data.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/multi_net/udp_data.py b/tests/multi_net/udp_data.py index fd3e00918f..0c0f165b74 100644 --- a/tests/multi_net/udp_data.py +++ b/tests/multi_net/udp_data.py @@ -12,6 +12,7 @@ def instance0(): multitest.next() for i in range(NUM_NEW_SOCKETS): 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.broadcast("server ready") for j in range(NUM_TRANSFERS): From a63875d5add278ce37a7cacb917488b4effac8a6 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Tue, 18 Jan 2022 23:56:27 +0200 Subject: [PATCH 018/619] extmod/modusocket: Create new sockets in blocking mode. To conform with CPython and other MicroPython ports. --- extmod/modusocket.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extmod/modusocket.c b/extmod/modusocket.c index 373f2c125f..10c818bf58 100644 --- a/extmod/modusocket.c +++ b/extmod/modusocket.c @@ -67,7 +67,7 @@ STATIC mp_obj_t socket_make_new(const mp_obj_type_t *type, size_t n_args, size_t } #if MICROPY_PY_USOCKET_EXTENDED_STATE - s->timeout = 0; + s->timeout = -1; s->state = NULL; #endif @@ -88,7 +88,7 @@ STATIC void socket_select_nic(mod_network_socket_obj_t *self, const byte *ip) { #if MICROPY_PY_USOCKET_EXTENDED_STATE // if a timeout was set before binding a NIC, call settimeout to reset it - if (self->timeout != 0 && self->nic_type->settimeout(self, self->timeout, &_errno) != 0) { + if (self->timeout != -1 && self->nic_type->settimeout(self, self->timeout, &_errno) != 0) { mp_raise_OSError(_errno); } #endif @@ -158,7 +158,7 @@ STATIC mp_obj_t socket_accept(mp_obj_t self_in) { socket2->bound = false; socket2->fileno = -1; #if MICROPY_PY_USOCKET_EXTENDED_STATE - socket2->timeout = 0; + socket2->timeout = -1; socket2->state = NULL; #endif From 1aac151d6894cf892c0ff7bb38e5589580d6c080 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 20 Jan 2022 17:12:36 +0200 Subject: [PATCH 019/619] drivers/ninaw10: Return standard error numbers. --- drivers/ninaw10/nina_wifi_drv.c | 26 ++++++----- drivers/ninaw10/nina_wifi_drv.h | 5 -- extmod/network_ninaw10.c | 82 +++++++++++++++------------------ 3 files changed, 51 insertions(+), 62 deletions(-) diff --git a/drivers/ninaw10/nina_wifi_drv.c b/drivers/ninaw10/nina_wifi_drv.c index 12707fe051..cf09711690 100644 --- a/drivers/ninaw10/nina_wifi_drv.c +++ b/drivers/ninaw10/nina_wifi_drv.c @@ -28,6 +28,7 @@ */ #include "py/mphal.h" +#include "py/mperrno.h" #if MICROPY_PY_NETWORK_NINAW10 @@ -480,7 +481,7 @@ int nina_connected_sta(uint32_t *sta_ip) { } int nina_wait_for_sta(uint32_t *sta_ip, uint32_t timeout) { - return NINA_ERROR_TIMEOUT; + return -MP_ETIMEDOUT; } int nina_ifconfig(nina_ifconfig_t *ifconfig, bool set) { @@ -597,7 +598,7 @@ int nina_scan(nina_scan_callback_t scan_callback, void *arg, uint32_t timeout) { if (timeout && (mp_hal_ticks_ms() - start) >= timeout) { // Timeout, no networks. - return NINA_ERROR_TIMEOUT; + return -MP_ETIMEDOUT; } mp_hal_delay_ms(100); @@ -723,7 +724,7 @@ int nina_socket_close(int fd) { break; } if ((mp_hal_ticks_ms() - start) >= 5000) { - return NINA_ERROR_TIMEOUT; + return -MP_ETIMEDOUT; } } } @@ -777,12 +778,15 @@ int nina_socket_accept(int fd, uint8_t *ip, uint16_t *port, int *fd_out, int32_t return -1; } - for (mp_uint_t start = mp_hal_ticks_ms(); !sock; mp_hal_delay_ms(10)) { + for (mp_uint_t start = mp_hal_ticks_ms(); ; mp_hal_delay_ms(10)) { if (nina_socket_avail(fd, NINA_SOCKET_TYPE_TCP, &sock) != 0) { return -1; } + if (sock != 0) { + break; + } if (timeout == 0 || (timeout > 0 && (mp_hal_ticks_ms() - start) >= timeout)) { - return NINA_ERROR_TIMEOUT; + return -MP_ETIMEDOUT; } } @@ -820,7 +824,7 @@ int nina_socket_connect(int fd, uint8_t *ip, uint16_t port, int32_t timeout) { } if (timeout == 0 || (timeout > 0 && (mp_hal_ticks_ms() - start) >= timeout)) { - return NINA_ERROR_TIMEOUT; + return -MP_ETIMEDOUT; } } @@ -832,7 +836,7 @@ int nina_socket_send(int fd, const uint8_t *buf, uint32_t len, int32_t timeout) uint16_t bytes = 0; if (nina_socket_status(fd) != SOCKET_STATE_ESTABLISHED) { - return -1; + return -MP_ENOTCONN; } if (nina_send_command_read_vals(NINA_CMD_TCP_SEND, @@ -854,7 +858,7 @@ int nina_socket_send(int fd, const uint8_t *buf, uint32_t len, int32_t timeout) } if (timeout == 0 || (timeout > 0 && (mp_hal_ticks_ms() - start) >= timeout)) { - return NINA_ERROR_TIMEOUT; + return -MP_ETIMEDOUT; } mp_hal_delay_ms(1); } @@ -866,7 +870,7 @@ int nina_socket_recv(int fd, uint8_t *buf, uint32_t len, int32_t timeout) { uint16_t bytes = 0; if (nina_socket_status(fd) != SOCKET_STATE_ESTABLISHED) { - return -1; + return -MP_ENOTCONN; } for (mp_uint_t start = mp_hal_ticks_ms(); bytes == 0; mp_hal_delay_ms(1)) { @@ -882,7 +886,7 @@ int nina_socket_recv(int fd, uint8_t *buf, uint32_t len, int32_t timeout) { } if (timeout == 0 || (timeout > 0 && (mp_hal_ticks_ms() - start) >= timeout)) { - return NINA_ERROR_TIMEOUT; + return -MP_ETIMEDOUT; } } return bytes; @@ -934,7 +938,7 @@ int nina_socket_recvfrom(int fd, uint8_t *buf, uint32_t len, uint8_t *ip, uint16 } if (timeout == 0 || (timeout > 0 && (mp_hal_ticks_ms() - start) >= timeout)) { - return NINA_ERROR_TIMEOUT; + return -MP_ETIMEDOUT; } } if (nina_send_command_read_vals(NINA_CMD_SOCKET_REMOTE_ADDR, diff --git a/drivers/ninaw10/nina_wifi_drv.h b/drivers/ninaw10/nina_wifi_drv.h index b8a6c4eb92..b990476b67 100644 --- a/drivers/ninaw10/nina_wifi_drv.h +++ b/drivers/ninaw10/nina_wifi_drv.h @@ -61,11 +61,6 @@ typedef enum { NINA_SOCKET_TYPE_TLS_BEARSSL } nina_socket_type_t; -typedef enum { - NINA_ERROR_IO = -1, - NINA_ERROR_TIMEOUT = -2, -} nina_error_t; - typedef struct { uint8_t ip_addr[NINA_IPV4_ADDR_LEN]; uint8_t subnet_addr[NINA_IPV4_ADDR_LEN]; diff --git a/extmod/network_ninaw10.c b/extmod/network_ninaw10.c index 26b4a811dc..e5d322a953 100644 --- a/extmod/network_ninaw10.c +++ b/extmod/network_ninaw10.c @@ -406,13 +406,12 @@ STATIC int network_ninaw10_socket_accept(mod_network_socket_obj_t *socket, int fd = 0; // Call accept. int ret = nina_socket_accept(socket->fileno, ip, (uint16_t *)port, &fd, socket->timeout); - if (ret == NINA_ERROR_TIMEOUT) { - // The socket is Not closed on timeout when calling functions that accept a timeout. - *_errno = MP_ETIMEDOUT; - return -1; - } else if (ret < 0) { - *_errno = ret; - network_ninaw10_socket_close(socket); + if (ret < 0) { + *_errno = -ret; + // Close socket if not a timeout error. + if (*_errno != MP_ETIMEDOUT) { + network_ninaw10_socket_close(socket); + } return -1; } @@ -424,13 +423,12 @@ STATIC int network_ninaw10_socket_accept(mod_network_socket_obj_t *socket, STATIC int network_ninaw10_socket_connect(mod_network_socket_obj_t *socket, byte *ip, mp_uint_t port, int *_errno) { int ret = nina_socket_connect(socket->fileno, ip, port, socket->timeout); - if (ret == NINA_ERROR_TIMEOUT) { - // The socket is Not closed on timeout when calling functions that accept a timeout. - *_errno = MP_ETIMEDOUT; - return -1; - } else if (ret < 0) { - *_errno = ret; - network_ninaw10_socket_close(socket); + if (ret < 0) { + *_errno = -ret; + // Close socket if not a timeout error. + if (*_errno != MP_ETIMEDOUT) { + network_ninaw10_socket_close(socket); + } return -1; } return 0; @@ -438,14 +436,12 @@ STATIC int network_ninaw10_socket_connect(mod_network_socket_obj_t *socket, byte STATIC mp_uint_t network_ninaw10_socket_send(mod_network_socket_obj_t *socket, const byte *buf, mp_uint_t len, int *_errno) { int ret = nina_socket_send(socket->fileno, buf, len, socket->timeout); - if (ret == NINA_ERROR_TIMEOUT) { - // The socket is Not closed on timeout when calling functions that accept a timeout. - *_errno = MP_ETIMEDOUT; - return -1; - } else if (ret < 0) { - // Close the socket on any other errors. - *_errno = ret; - network_ninaw10_socket_close(socket); + if (ret < 0) { + *_errno = -ret; + // Close socket if not a timeout error. + if (*_errno != MP_ETIMEDOUT) { + network_ninaw10_socket_close(socket); + } return -1; } return ret; @@ -460,15 +456,12 @@ STATIC mp_uint_t network_ninaw10_socket_recv(mod_network_socket_obj_t *socket, b } else { ret = nina_socket_recv(socket->fileno, buf, len, socket->timeout); } - - if (ret == NINA_ERROR_TIMEOUT) { - // The socket is Not closed on timeout when calling functions that accept a timeout. - *_errno = MP_ETIMEDOUT; - return -1; - } else if (ret < 0) { - // Close the socket on any other errors. - *_errno = ret; - network_ninaw10_socket_close(socket); + if (ret < 0) { + *_errno = -ret; + // Close socket if not a timeout error. + if (*_errno != MP_ETIMEDOUT) { + network_ninaw10_socket_close(socket); + } return -1; } return ret; @@ -493,13 +486,12 @@ STATIC mp_uint_t network_ninaw10_socket_sendto(mod_network_socket_obj_t *socket, } int ret = nina_socket_sendto(socket->fileno, buf, len, ip, port, socket->timeout); - if (ret == NINA_ERROR_TIMEOUT) { - // The socket is Not closed on timeout when calling functions that accept a timeout. - *_errno = MP_ETIMEDOUT; - return -1; - } else if (ret < 0) { - *_errno = ret; - network_ninaw10_socket_close(socket); + if (ret < 0) { + *_errno = -ret; + // Close socket if not a timeout error. + if (*_errno != MP_ETIMEDOUT) { + network_ninaw10_socket_close(socket); + } return -1; } return ret; @@ -519,14 +511,12 @@ STATIC mp_uint_t network_ninaw10_socket_recvfrom(mod_network_socket_obj_t *socke } ret = nina_socket_recvfrom(socket->fileno, buf, len, ip, (uint16_t *)port, socket->timeout); } - if (ret == NINA_ERROR_TIMEOUT) { - // The socket is Not closed on timeout when calling functions that accept a timeout. - *_errno = MP_ETIMEDOUT; - return -1; - } else if (ret < 0) { - // Close the socket on any other errors. - *_errno = ret; - network_ninaw10_socket_close(socket); + if (ret < 0) { + *_errno = -ret; + // Close socket if not a timeout error. + if (*_errno != MP_ETIMEDOUT) { + network_ninaw10_socket_close(socket); + } return -1; } return ret; From 9438fb7321f1f0fb9faae666808e86d7f47b8619 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 21 Jan 2022 12:44:49 +1100 Subject: [PATCH 020/619] extmod/modusocket: Support additional args to getaddrinfo. Signed-off-by: Damien George --- extmod/modusocket.c | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/extmod/modusocket.c b/extmod/modusocket.c index 10c818bf58..b6bab72e7a 100644 --- a/extmod/modusocket.c +++ b/extmod/modusocket.c @@ -472,18 +472,41 @@ STATIC const mp_obj_type_t socket_type = { // usocket module // function usocket.getaddrinfo(host, port) -STATIC mp_obj_t mod_usocket_getaddrinfo(mp_obj_t host_in, mp_obj_t port_in) { +STATIC mp_obj_t mod_usocket_getaddrinfo(size_t n_args, const mp_obj_t *args) { size_t hlen; - const char *host = mp_obj_str_get_data(host_in, &hlen); - mp_int_t port = mp_obj_get_int(port_in); + const char *host = mp_obj_str_get_data(args[0], &hlen); + mp_int_t port = mp_obj_get_int(args[1]); uint8_t out_ip[MOD_NETWORK_IPADDR_BUF_SIZE]; bool have_ip = false; + // if constraints were passed then check they are compatible with the supported params + if (n_args > 2) { + mp_int_t family = mp_obj_get_int(args[2]); + mp_int_t type = 0; + mp_int_t proto = 0; + mp_int_t flags = 0; + if (n_args > 3) { + type = mp_obj_get_int(args[3]); + if (n_args > 4) { + proto = mp_obj_get_int(args[4]); + if (n_args > 5) { + flags = mp_obj_get_int(args[5]); + } + } + } + if (!((family == 0 || family == MOD_NETWORK_AF_INET) + && (type == 0 || type == MOD_NETWORK_SOCK_STREAM) + && proto == 0 + && flags == 0)) { + mp_warning(MP_WARN_CAT(RuntimeWarning), "unsupported getaddrinfo constraints"); + } + } + if (hlen > 0) { // check if host is already in IP form nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { - netutils_parse_ipv4_addr(host_in, out_ip, NETUTILS_BIG); + netutils_parse_ipv4_addr(args[0], out_ip, NETUTILS_BIG); have_ip = true; nlr_pop(); } else { @@ -519,7 +542,7 @@ STATIC mp_obj_t mod_usocket_getaddrinfo(mp_obj_t host_in, mp_obj_t port_in) { tuple->items[4] = netutils_format_inet_addr(out_ip, port, NETUTILS_BIG); return mp_obj_new_list(1, (mp_obj_t *)&tuple); } -STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_usocket_getaddrinfo_obj, mod_usocket_getaddrinfo); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_usocket_getaddrinfo_obj, 2, 6, mod_usocket_getaddrinfo); STATIC const mp_rom_map_elem_t mp_module_usocket_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_usocket) }, From bef26d4e3fee7f00758d6e3ae7aa187b80d7878c Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 20 Jan 2022 20:45:31 +0200 Subject: [PATCH 021/619] rp2/machine_uart: Add machine.UART init/deinit methods. Without these methods a lot of existing "portable" scripts are broken. This change improves portability by making rp2 machine.UART more compliant with the documented machine UART interface. --- ports/rp2/machine_uart.c | 66 +++++++++++++++++++++++++++++----------- 1 file changed, 49 insertions(+), 17 deletions(-) diff --git a/ports/rp2/machine_uart.c b/ports/rp2/machine_uart.c index 887954276e..283c12ed54 100644 --- a/ports/rp2/machine_uart.c +++ b/ports/rp2/machine_uart.c @@ -162,11 +162,10 @@ STATIC void machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_pri self->timeout, self->timeout_char, _invert_name[self->invert]); } -STATIC mp_obj_t machine_uart_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - enum { ARG_id, ARG_baudrate, ARG_bits, ARG_parity, ARG_stop, ARG_tx, ARG_rx, ARG_cts, ARG_rts, +STATIC void machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_baudrate, ARG_bits, ARG_parity, ARG_stop, ARG_tx, ARG_rx, ARG_cts, ARG_rts, ARG_timeout, ARG_timeout_char, ARG_invert, ARG_flow, ARG_txbuf, ARG_rxbuf}; static const mp_arg_t allowed_args[] = { - { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_bits, MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_parity, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_INT(-1)} }, @@ -185,16 +184,7 @@ STATIC mp_obj_t machine_uart_make_new(const mp_obj_type_t *type, size_t n_args, // Parse args. mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - // Get UART bus. - int uart_id = mp_obj_get_int(args[ARG_id].u_obj); - if (uart_id < 0 || uart_id >= MP_ARRAY_SIZE(machine_uart_obj)) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("UART(%d) doesn't exist"), uart_id); - } - - // Get static peripheral object. - machine_uart_obj_t *self = (machine_uart_obj_t *)&machine_uart_obj[uart_id]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); // Set baudrate if configured. if (args[ARG_baudrate].u_int > 0) { @@ -305,7 +295,7 @@ STATIC mp_obj_t machine_uart_make_new(const mp_obj_type_t *type, size_t n_args, } // Initialise the UART peripheral if any arguments given, or it was not initialised previously. - if (n_args > 1 || n_kw > 0 || self->baudrate == 0) { + if (n_args > 0 || kw_args->used > 0 || self->baudrate == 0) { if (self->baudrate == 0) { self->baudrate = DEFAULT_UART_BAUDRATE; } @@ -339,10 +329,10 @@ STATIC mp_obj_t machine_uart_make_new(const mp_obj_type_t *type, size_t n_args, // Allocate the RX/TX buffers. ringbuf_alloc(&(self->read_buffer), rxbuf_len + 1); - MP_STATE_PORT(rp2_uart_rx_buffer[uart_id]) = self->read_buffer.buf; + MP_STATE_PORT(rp2_uart_rx_buffer[self->uart_id]) = self->read_buffer.buf; ringbuf_alloc(&(self->write_buffer), txbuf_len + 1); - MP_STATE_PORT(rp2_uart_tx_buffer[uart_id]) = self->write_buffer.buf; + MP_STATE_PORT(rp2_uart_tx_buffer[self->uart_id]) = self->write_buffer.buf; // Set the irq handler. if (self->uart_id == 0) { @@ -356,10 +346,50 @@ STATIC mp_obj_t machine_uart_make_new(const mp_obj_type_t *type, size_t n_args, // Enable the uart irq; this macro sets the rx irq level to 4. uart_set_irq_enables(self->uart, true, true); } +} + +STATIC mp_obj_t machine_uart_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); + + // Get UART bus. + int uart_id = mp_obj_get_int(args[0]); + if (uart_id < 0 || uart_id >= MP_ARRAY_SIZE(machine_uart_obj)) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("UART(%d) doesn't exist"), uart_id); + } + + // Get static peripheral object. + machine_uart_obj_t *self = (machine_uart_obj_t *)&machine_uart_obj[uart_id]; + + // Initialise the UART peripheral. + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + machine_uart_init_helper(self, n_args - 1, args + 1, &kw_args); return MP_OBJ_FROM_PTR(self); } +STATIC mp_obj_t machine_uart_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + // Initialise the UART peripheral. + machine_uart_init_helper(args[0], n_args - 1, args + 1, kw_args); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(machine_uart_init_obj, 1, machine_uart_init); + +STATIC mp_obj_t machine_uart_deinit(mp_obj_t self_in) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + uart_deinit(self->uart); + if (self->uart_id == 0) { + irq_set_enabled(UART0_IRQ, false); + } else { + irq_set_enabled(UART1_IRQ, false); + } + self->baudrate = 0; + MP_STATE_PORT(rp2_uart_rx_buffer[self->uart_id]) = NULL; + MP_STATE_PORT(rp2_uart_tx_buffer[self->uart_id]) = NULL; + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_uart_deinit_obj, machine_uart_deinit); + STATIC mp_obj_t machine_uart_any(mp_obj_t self_in) { machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); // get all bytes from the fifo first @@ -380,8 +410,10 @@ STATIC mp_obj_t machine_uart_sendbreak(mp_obj_t self_in) { STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_uart_sendbreak_obj, machine_uart_sendbreak); STATIC const mp_rom_map_elem_t machine_uart_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_any), MP_ROM_PTR(&machine_uart_any_obj) }, + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_uart_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_uart_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_any), MP_ROM_PTR(&machine_uart_any_obj) }, { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, From a707fe50b085ec83722106609f6fd219faf9f030 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 21 Jan 2022 14:52:03 +1100 Subject: [PATCH 022/619] rp2/machine_i2c: Use soft I2C only for len=0, and increase timeout. The RP2040 I2C hardware can do writes of length 1 and 2, just not of length 0. So only use software I2C for writes of length 0, to improve performance. Also increase the software I2C timeout for zero-length writes to accommodate the behaviour of a wider range of I2C devices. Fixes issue #8167. Signed-off-by: Damien George --- ports/rp2/machine_i2c.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ports/rp2/machine_i2c.c b/ports/rp2/machine_i2c.c index 56f012a5e8..f785ad7ded 100644 --- a/ports/rp2/machine_i2c.c +++ b/ports/rp2/machine_i2c.c @@ -127,12 +127,12 @@ STATIC int machine_i2c_transfer_single(mp_obj_base_t *self_in, uint16_t addr, si if (flags & MP_MACHINE_I2C_FLAG_READ) { ret = i2c_read_blocking(self->i2c_inst, addr, buf, len, nostop); } else { - if (len <= 2) { - // Workaround issue with hardware I2C not accepting short writes. + if (len == 0) { + // Workaround issue with hardware I2C not accepting zero-length writes. mp_machine_soft_i2c_obj_t soft_i2c = { .base = { &mp_machine_soft_i2c_type }, .us_delay = 500000 / self->freq + 1, - .us_timeout = 255, + .us_timeout = 50000, .scl = self->scl, .sda = self->sda, }; From 7d71ae25edc4b405dd03222f044bc7cf3b77f956 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 21 Jan 2022 14:59:13 +1100 Subject: [PATCH 023/619] extmod/machine_i2c: Increase default SoftI2C timeout to 50ms. Some devices, eg BNO055, can stretch SCL for a long time, so make the default large to accommodate them. 50ms matches the current default for stm32 hardware I2C . Signed-off-by: Damien George --- docs/library/machine.I2C.rst | 2 +- extmod/machine_i2c.c | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/library/machine.I2C.rst b/docs/library/machine.I2C.rst index f46d64ef51..82a88390c3 100644 --- a/docs/library/machine.I2C.rst +++ b/docs/library/machine.I2C.rst @@ -57,7 +57,7 @@ Constructors of *scl* and *sda* that cannot be changed. .. _machine.SoftI2C: -.. class:: SoftI2C(scl, sda, *, freq=400000, timeout=255) +.. class:: SoftI2C(scl, sda, *, freq=400000, timeout=50000) Construct a new software I2C object. The parameters are: diff --git a/extmod/machine_i2c.c b/extmod/machine_i2c.c index b2e39c534d..2b1275e5bb 100644 --- a/extmod/machine_i2c.c +++ b/extmod/machine_i2c.c @@ -33,6 +33,8 @@ #include "py/runtime.h" #include "extmod/machine_i2c.h" +#define SOFT_I2C_DEFAULT_TIMEOUT_US (50000) // 50ms + #if MICROPY_PY_MACHINE_SOFTI2C typedef mp_machine_soft_i2c_obj_t machine_i2c_obj_t; @@ -651,7 +653,7 @@ STATIC void mp_machine_soft_i2c_init(mp_obj_base_t *self_in, size_t n_args, cons { MP_QSTR_scl, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_sda, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 400000} }, - { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 255} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = SOFT_I2C_DEFAULT_TIMEOUT_US} }, }; mp_machine_soft_i2c_obj_t *self = (mp_machine_soft_i2c_obj_t *)self_in; From 4d2f487ee1ac93098b90c3108b071f6e855c9e7f Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 21 Jan 2022 22:29:11 +1100 Subject: [PATCH 024/619] docs/library: Specify additional ADC methods and new ADCBlock class. The new ADC methods are: init(), read_uv() and block(). The new ADCBlock class has methods: init() and connect(). See related discussions in #3943, #4213. Signed-off-by: Damien George --- docs/library/machine.ADC.rst | 39 ++++++++++++++++++--- docs/library/machine.ADCBlock.rst | 58 +++++++++++++++++++++++++++++++ docs/library/machine.rst | 1 + 3 files changed, 94 insertions(+), 4 deletions(-) create mode 100644 docs/library/machine.ADCBlock.rst diff --git a/docs/library/machine.ADC.rst b/docs/library/machine.ADC.rst index 1404b454ae..eb538a4424 100644 --- a/docs/library/machine.ADC.rst +++ b/docs/library/machine.ADC.rst @@ -8,28 +8,59 @@ The ADC class provides an interface to analog-to-digital convertors, and represents a single endpoint that can sample a continuous voltage and convert it to a discretised value. +For extra control over ADC sampling see :ref:`machine.ADCBlock `. + Example usage:: - import machine + from machine import ADC - adc = machine.ADC(pin) # create an ADC object acting on a pin - val = adc.read_u16() # read a raw analog value in the range 0-65535 + adc = ADC(pin) # create an ADC object acting on a pin + val = adc.read_u16() # read a raw analog value in the range 0-65535 + val = adc.read_uv() # read an analog value in microvolts Constructors ------------ -.. class:: ADC(id) +.. class:: ADC(id, *, sample_ns, atten) Access the ADC associated with a source identified by *id*. This *id* may be an integer (usually specifying a channel number), a :ref:`Pin ` object, or other value supported by the underlying machine. + If additional keyword-arguments are given then they will configure + various aspects of the ADC. If not given, these settings will take + previous or default values. The settings are: + + - *sample_ns* is the sampling time in nanoseconds. + + - *atten* specifies the input attenuation. + Methods ------- +.. method:: ADC.init(*, sample_ns, atten) + + Apply the given settings to the ADC. Only those arguments that are + specified will be changed. See the ADC constructor above for what the + arguments are. + +.. method:: ADC.block() + + Return the :ref:`ADCBlock ` instance associated with + this ADC object. + + This method only exists if the port supports the + :ref:`ADCBlock ` class. + .. method:: ADC.read_u16() Take an analog reading and return an integer in the range 0-65535. The return value represents the raw reading taken by the ADC, scaled such that the minimum value is 0 and the maximum value is 65535. + +.. method:: ADC.read_uv() + + Take an analog reading and return an integer value with units of + microvolts. It is up to the particular port whether or not this value + is calibrated, and how calibration is done. diff --git a/docs/library/machine.ADCBlock.rst b/docs/library/machine.ADCBlock.rst new file mode 100644 index 0000000000..56a468dd62 --- /dev/null +++ b/docs/library/machine.ADCBlock.rst @@ -0,0 +1,58 @@ +.. currentmodule:: machine +.. _machine.ADCBlock: + +class ADCBlock -- control ADC peripherals +========================================= + +The ADCBlock class provides access to an ADC peripheral which has a +number of channels that can be used to sample analog values. It allows +finer control over configuration of :ref:`machine.ADC ` +objects, which do the actual sampling. + +This class is not always available. + +Example usage:: + + from machine import ADCBlock + + block = ADCBlock(id, bits=12) # create an ADCBlock with 12-bit resolution + adc = block.connect(4, pin) # connect channel 4 to the given pin + val = adc.read_uv() # read an analog value + +Constructors +------------ + +.. class:: ADCBlock(id, *, bits) + + Access the ADC peripheral identified by *id*, which may be an integer + or string. + + The *bits* argument, if given, sets the resolution in bits of the + conversion process. If not specified then the previous or default + resolution is used. + +Methods +------- + +.. method:: ADCBlock.init(*, bits) + + Configure the ADC peripheral. *bits* will set the resolution of the + conversion process. + +.. method:: ADCBlock.connect(channel) + ADCBlock.connect(source) + ADCBlock.connect(channel, source) + + Connect up a channel on the ADC peripheral so it is ready for sampling, + and return an :ref:`ADC ` object that represents that connection. + + The *channel* argument must be an integer, and *source* must be an object + (for example a :ref:`Pin `) which can be connected up for sampling. + + If only *channel* is given then it is configured for sampling. + + If only *source* is given then that object is connected to a default + channel ready for sampling. + + If both *channel* and *source* are given then they are connected together + and made ready for sampling. diff --git a/docs/library/machine.rst b/docs/library/machine.rst index 5f45168ed6..d66423d0d4 100644 --- a/docs/library/machine.rst +++ b/docs/library/machine.rst @@ -199,6 +199,7 @@ Classes machine.Pin.rst machine.Signal.rst machine.ADC.rst + machine.ADCBlock.rst machine.PWM.rst machine.UART.rst machine.SPI.rst From 3300d6d3370dcd64d7212d6d977edcba42432234 Mon Sep 17 00:00:00 2001 From: Jonathan Hogg Date: Tue, 21 Dec 2021 13:25:55 +0000 Subject: [PATCH 025/619] docs/esp32: Document expanded ADC API in quickref. Document read_u16(), read_uv() and ADCBlock(). Mark old read(), atten() and width() methods as legacy. --- docs/esp32/quickref.rst | 105 +++++++++++++++++++++++++++------------- 1 file changed, 71 insertions(+), 34 deletions(-) diff --git a/docs/esp32/quickref.rst b/docs/esp32/quickref.rst index 337f87b66f..94b5d966ed 100644 --- a/docs/esp32/quickref.rst +++ b/docs/esp32/quickref.rst @@ -264,54 +264,91 @@ See more examples in the :ref:`esp32_pwm` tutorial. ADC (analog to digital conversion) ---------------------------------- -On the ESP32 ADC functionality is available on Pins 32-39. Note that, when -using the default configuration, input voltages on the ADC pin must be between -0.0v and 1.0v (anything above 1.0v will just read as 4095). Attenuation must -be applied in order to increase this usable voltage range. +On the ESP32 ADC functionality is available on pins 32-39 (ADC block 1) and pins +0, 2, 4, 12-15 and 25-27 (ADC block 2). Use the :ref:`machine.ADC ` class:: from machine import ADC - adc = ADC(Pin(32)) # create ADC object on ADC pin - adc.read() # read value, 0-4095 across voltage range 0.0v - 1.0v + adc = ADC(Pin(32)) # create ADC object for pin 32 + adc.read_u16() # read raw value, 0-65535 - adc.atten(ADC.ATTN_11DB) # set 11dB input attenuation (voltage range roughly 0.0v - 3.6v) - adc.width(ADC.WIDTH_9BIT) # set 9 bit return values (returned range 0-511) - adc.read() # read value using the newly configured attenuation and width +Note that the ESP32 uses an internal ADC reference voltage of 1.0v. To read +voltages above this value, input attenuation can be applied with the optional +``atten`` keyword argument to the constructor. Valid values are: -ESP32 specific ADC class method reference: + - ``ADC.ATTN_0DB``: No attenuation, this is the default + - ``ADC.ATTN_2_5DB``: 2.5dB attenuation, gives a maximum input voltage of + approximately 1.33v + - ``ADC.ATTN_6DB``: 6dB attenuation, gives a maximum input voltage of + approximately 2.00v + - ``ADC.ATTN_11DB``: 11dB attenuation, gives a maximum input voltage of + approximately 3.55v + +E.g.:: + + adc = ADC(Pin(25), atten=ADC.ATTEN_11DB) # 0.0v - 3.55v range + +.. Warning:: + Note that, although 11dB attenuation allows for a voltage range up to 3.55v, + the absolute maximum voltage rating for input pins is 3.6v, and so going + near this boundary risks damage to the IC! + +ESP32-specific ADC class method reference: + +.. method:: ADC.init(*, atten) + + Re-initialize the ADC pin with a different input attenuation. + +.. method:: ADC.read_uv() + + This method uses internal per-package calibration values - set during + manufacture - to return the ADC input voltage in microvolts, taking into + account any input attenuation applied. Note that the calibration curves do + not guarantee that an input tied to ground will read as 0, and the returned + values have only millivolt resolution. + +.. method:: ADC.block() + + Return the matching ``ADCBlock`` object. + +.. class:: ADCBlock(id, *, bits) + + Return the ADC block object with the given ``id`` (1 or 2) and initialize + it to the specified resolution (9 to 12-bits) or the default 12-bits. + +.. method:: ADCBlock.init(*, bits) + + Re-initialize the ADC block with a specific resolution. + +.. method:: ADCBlock.connect(channel_or_pin) + + Return the ``ADC`` object for the specified ADC channel number or Pin object. + +Legacy API methods: + +.. method:: ADC.read() + + This method returns the raw ADC value ranged according to the resolution of + the ADC block, 0-4095 for the default 12-bit resolution. .. method:: ADC.atten(attenuation) - This method allows for the setting of the amount of attenuation on the - input of the ADC. This allows for a wider possible input voltage range, - at the cost of accuracy (the same number of bits now represents a wider - range). The possible attenuation options are: - - - ``ADC.ATTN_0DB``: 0dB attenuation, gives a maximum input voltage - of 1.00v - this is the default configuration - - ``ADC.ATTN_2_5DB``: 2.5dB attenuation, gives a maximum input voltage - of approximately 1.34v - - ``ADC.ATTN_6DB``: 6dB attenuation, gives a maximum input voltage - of approximately 2.00v - - ``ADC.ATTN_11DB``: 11dB attenuation, gives a maximum input voltage - of approximately 3.6v - -.. Warning:: - Despite 11dB attenuation allowing for up to a 3.6v range, note that the - absolute maximum voltage rating for the input pins is 3.6v, and so going - near this boundary may be damaging to the IC! + Equivalent to ``ADC.init(atten=attenuation)``. .. method:: ADC.width(width) - This method allows for the setting of the number of bits to be utilised - and returned during ADC reads. Possible width options are: + Equivalent to ``ADC.block().init(bits=width)``. + +For compatibility, the ``ADC`` object also provides constants matching the +supported ADC resolutions: + + - ``ADC.WIDTH_9BIT`` = 9 + - ``ADC.WIDTH_10BIT`` = 10 + - ``ADC.WIDTH_11BIT`` = 11 + - ``ADC.WIDTH_12BIT`` = 12 - - ``ADC.WIDTH_9BIT``: 9 bit data - - ``ADC.WIDTH_10BIT``: 10 bit data - - ``ADC.WIDTH_11BIT``: 11 bit data - - ``ADC.WIDTH_12BIT``: 12 bit data - this is the default configuration Software SPI bus ---------------- From 63438a31bb6d0c8012e338433df95700f1cbca97 Mon Sep 17 00:00:00 2001 From: Jonathan Hogg Date: Tue, 21 Dec 2021 15:15:24 +0000 Subject: [PATCH 026/619] esp32/machine_adcblock: Add new machine.ADCBlock class and update ADC. Rework the ADC implementation to follow the improved ADC/ADCBlock API. This adds support for calibrated voltage readings and the ADC2 block. The ADC API is backwards compatible with what it was before this change. Resolves #6219. --- ports/esp32/boards/sdkconfig.base | 5 + ports/esp32/machine_adc.c | 295 +++++++++++++++++------------- ports/esp32/machine_adc.h | 16 ++ ports/esp32/machine_adcblock.c | 203 ++++++++++++++++++++ ports/esp32/machine_adcblock.h | 20 ++ ports/esp32/main/CMakeLists.txt | 2 + ports/esp32/modmachine.c | 1 + ports/esp32/modmachine.h | 1 + 8 files changed, 414 insertions(+), 129 deletions(-) create mode 100644 ports/esp32/machine_adc.h create mode 100644 ports/esp32/machine_adcblock.c create mode 100644 ports/esp32/machine_adcblock.h diff --git a/ports/esp32/boards/sdkconfig.base b/ports/esp32/boards/sdkconfig.base index 71229e8beb..4b29013434 100644 --- a/ports/esp32/boards/sdkconfig.base +++ b/ports/esp32/boards/sdkconfig.base @@ -58,3 +58,8 @@ CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" # To reduce iRAM usage CONFIG_ESP32_WIFI_IRAM_OPT=n CONFIG_ESP32_WIFI_RX_IRAM_OPT=n + +# ADC calibration +CONFIG_ADC_CAL_EFUSE_TP_ENABLE=y +CONFIG_ADC_CAL_EFUSE_VREF_ENABLE=y +CONFIG_ADC_CAL_LUT_ENABLE=y diff --git a/ports/esp32/machine_adc.c b/ports/esp32/machine_adc.c index 6731978e28..f6bc7b9639 100644 --- a/ports/esp32/machine_adc.c +++ b/ports/esp32/machine_adc.c @@ -4,6 +4,7 @@ * The MIT License (MIT) * * Copyright (c) 2017 Nick Moore + * Copyright (c) 2021 Jonathan Hogg * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,7 +25,6 @@ * THE SOFTWARE. */ - #include #include "esp_log.h" @@ -35,176 +35,213 @@ #include "py/runtime.h" #include "py/mphal.h" #include "modmachine.h" +#include "machine_adc.h" -typedef struct _madc_obj_t { - mp_obj_base_t base; - gpio_num_t gpio_id; - adc1_channel_t adc1_id; -} madc_obj_t; +#define ADCBLOCK1 (&madcblock_obj[0]) +#define ADCBLOCK2 (&madcblock_obj[1]) STATIC const madc_obj_t madc_obj[] = { #if CONFIG_IDF_TARGET_ESP32 - {{&machine_adc_type}, GPIO_NUM_36, ADC1_CHANNEL_0}, - {{&machine_adc_type}, GPIO_NUM_37, ADC1_CHANNEL_1}, - {{&machine_adc_type}, GPIO_NUM_38, ADC1_CHANNEL_2}, - {{&machine_adc_type}, GPIO_NUM_39, ADC1_CHANNEL_3}, - {{&machine_adc_type}, GPIO_NUM_32, ADC1_CHANNEL_4}, - {{&machine_adc_type}, GPIO_NUM_33, ADC1_CHANNEL_5}, - {{&machine_adc_type}, GPIO_NUM_34, ADC1_CHANNEL_6}, - {{&machine_adc_type}, GPIO_NUM_35, ADC1_CHANNEL_7}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_0, GPIO_NUM_36}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_1, GPIO_NUM_37}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_2, GPIO_NUM_38}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_3, GPIO_NUM_39}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_4, GPIO_NUM_32}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_5, GPIO_NUM_33}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_6, GPIO_NUM_34}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_7, GPIO_NUM_35}, + {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_0, GPIO_NUM_0}, + {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_1, GPIO_NUM_2}, + {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_2, GPIO_NUM_4}, + {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_3, GPIO_NUM_12}, + {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_4, GPIO_NUM_13}, + {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_5, GPIO_NUM_14}, + {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_6, GPIO_NUM_15}, + {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_7, GPIO_NUM_25}, + {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_8, GPIO_NUM_26}, + {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_9, GPIO_NUM_27}, #elif CONFIG_IDF_TARGET_ESP32C3 - {{&machine_adc_type}, GPIO_NUM_0, ADC1_CHANNEL_0}, - {{&machine_adc_type}, GPIO_NUM_1, ADC1_CHANNEL_1}, - {{&machine_adc_type}, GPIO_NUM_2, ADC1_CHANNEL_2}, - {{&machine_adc_type}, GPIO_NUM_3, ADC1_CHANNEL_3}, - {{&machine_adc_type}, GPIO_NUM_4, ADC1_CHANNEL_4}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_0, GPIO_NUM_0}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_1, GPIO_NUM_1}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_2, GPIO_NUM_2}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_3, GPIO_NUM_3}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_4, GPIO_NUM_4}, + {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_0, GPIO_NUM_5}, #elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 - {{&machine_adc_type}, GPIO_NUM_1, ADC1_CHANNEL_0}, - {{&machine_adc_type}, GPIO_NUM_2, ADC1_CHANNEL_1}, - {{&machine_adc_type}, GPIO_NUM_3, ADC1_CHANNEL_2}, - {{&machine_adc_type}, GPIO_NUM_4, ADC1_CHANNEL_3}, - {{&machine_adc_type}, GPIO_NUM_5, ADC1_CHANNEL_4}, - {{&machine_adc_type}, GPIO_NUM_6, ADC1_CHANNEL_5}, - {{&machine_adc_type}, GPIO_NUM_7, ADC1_CHANNEL_6}, - {{&machine_adc_type}, GPIO_NUM_8, ADC1_CHANNEL_7}, - {{&machine_adc_type}, GPIO_NUM_9, ADC1_CHANNEL_8}, - {{&machine_adc_type}, GPIO_NUM_10, ADC1_CHANNEL_9}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_0, GPIO_NUM_1}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_1, GPIO_NUM_2}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_2, GPIO_NUM_3}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_3, GPIO_NUM_4}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_4, GPIO_NUM_5}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_5, GPIO_NUM_6}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_6, GPIO_NUM_7}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_7, GPIO_NUM_8}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_8, GPIO_NUM_9}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_9, GPIO_NUM_10}, + {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_0, GPIO_NUM_11}, + {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_1, GPIO_NUM_12}, + {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_2, GPIO_NUM_13}, + {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_3, GPIO_NUM_14}, + {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_4, GPIO_NUM_15}, + {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_5, GPIO_NUM_16}, + {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_6, GPIO_NUM_17}, + {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_7, GPIO_NUM_18}, + {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_8, GPIO_NUM_19}, + {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_9, GPIO_NUM_20}, #endif }; -STATIC uint8_t adc_bit_width; +STATIC adc_atten_t madc_obj_atten[MP_ARRAY_SIZE(madc_obj)]; -STATIC mp_obj_t madc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, - const mp_obj_t *args) { - - static int initialized = 0; - if (!initialized) { - #if CONFIG_IDF_TARGET_ESP32S2 - adc1_config_width(ADC_WIDTH_BIT_13); - #else - adc1_config_width(ADC_WIDTH_BIT_12); - #endif - adc_bit_width = 12; - initialized = 1; - } - - mp_arg_check_num(n_args, n_kw, 1, 1, true); - gpio_num_t pin_id = machine_pin_get_id(args[0]); - const madc_obj_t *self = NULL; +const madc_obj_t *madc_search_helper(madcblock_obj_t *block, adc_channel_t channel_id, gpio_num_t gpio_id) { for (int i = 0; i < MP_ARRAY_SIZE(madc_obj); i++) { - if (pin_id == madc_obj[i].gpio_id) { - self = &madc_obj[i]; - break; + const madc_obj_t *adc = &madc_obj[i]; + if ((block == NULL || block == adc->block) && (channel_id == -1 || channel_id == adc->channel_id) && (gpio_id == -1 || gpio_id == adc->gpio_id)) { + return adc; } } - if (!self) { - mp_raise_ValueError(MP_ERROR_TEXT("invalid Pin for ADC")); - } - esp_err_t err = adc1_config_channel_atten(self->adc1_id, ADC_ATTEN_0db); - if (err == ESP_OK) { - return MP_OBJ_FROM_PTR(self); - } - mp_raise_ValueError(MP_ERROR_TEXT("parameter error")); + return NULL; } STATIC void madc_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { - madc_obj_t *self = self_in; - mp_printf(print, "ADC(Pin(%u))", self->gpio_id); + const madc_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "ADC(Pin(%u), atten=%u)", self->gpio_id, madc_obj_atten[self - madc_obj]); } -// read_u16() +STATIC void madc_atten_helper(const madc_obj_t *self, mp_int_t atten) { + esp_err_t err; + if (self->block->unit_id == ADC_UNIT_1) { + err = adc1_config_channel_atten(self->channel_id, atten); + } else { + err = adc2_config_channel_atten(self->channel_id, atten); + } + if (err != ESP_OK) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid atten")); + } + madc_obj_atten[self - madc_obj] = atten; +} + +void madc_init_helper(const madc_obj_t *self, size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { + ARG_atten, + }; + + static const mp_arg_t allowed_args[] = { + { MP_QSTR_atten, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + }; + + 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_obj_atten[self - madc_obj] == -1) { + madc_atten_helper(self, ADC_ATTEN_DB_0); + } +} + +STATIC mp_obj_t madc_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]); + const madc_obj_t *self = madc_search_helper(NULL, -1, gpio_id); + if (!self) { + 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); + + return MP_OBJ_FROM_PTR(self); +} + +STATIC mp_obj_t madc_init(size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + const madc_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + madc_init_helper(self, n_pos_args - 1, pos_args + 1, kw_args); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(madc_init_obj, 1, madc_init); + +STATIC mp_obj_t madc_block(mp_obj_t self_in) { + const madc_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_FROM_PTR(self->block); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(madc_block_obj, madc_block); + +STATIC mp_obj_t madc_read(mp_obj_t self_in) { + const madc_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_int_t raw = madcblock_read_helper(self->block, self->channel_id); + return MP_OBJ_NEW_SMALL_INT(raw); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(madc_read_obj, madc_read); + STATIC mp_obj_t madc_read_u16(mp_obj_t self_in) { - madc_obj_t *self = MP_OBJ_TO_PTR(self_in); - uint32_t raw = adc1_get_raw(self->adc1_id); + const madc_obj_t *self = MP_OBJ_TO_PTR(self_in); + 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) - uint32_t u16 = raw << (16 - adc_bit_width) | raw >> (2 * adc_bit_width - 16); + mp_int_t bits = self->block->bits; + mp_uint_t u16 = raw << (16 - bits) | raw >> (2 * bits - 16); return MP_OBJ_NEW_SMALL_INT(u16); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(madc_read_u16_obj, madc_read_u16); -// Legacy method -STATIC mp_obj_t madc_read(mp_obj_t self_in) { - madc_obj_t *self = self_in; - int val = adc1_get_raw(self->adc1_id); - if (val == -1) { - mp_raise_ValueError(MP_ERROR_TEXT("parameter error")); - } - return MP_OBJ_NEW_SMALL_INT(val); +STATIC mp_obj_t madc_read_uv(mp_obj_t self_in) { + const madc_obj_t *self = MP_OBJ_TO_PTR(self_in); + adc_atten_t atten = madc_obj_atten[self - madc_obj]; + return MP_OBJ_NEW_SMALL_INT(madcblock_read_uv_helper(self->block, self->channel_id, atten)); } -MP_DEFINE_CONST_FUN_OBJ_1(madc_read_obj, madc_read); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(madc_read_uv_obj, madc_read_uv); STATIC mp_obj_t madc_atten(mp_obj_t self_in, mp_obj_t atten_in) { - madc_obj_t *self = self_in; - adc_atten_t atten = mp_obj_get_int(atten_in); - esp_err_t err = adc1_config_channel_atten(self->adc1_id, atten); - if (err == ESP_OK) { - return mp_const_none; - } - mp_raise_ValueError(MP_ERROR_TEXT("parameter error")); + const madc_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_int_t atten = mp_obj_get_int(atten_in); + madc_atten_helper(self, atten); + return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_2(madc_atten_obj, madc_atten); -STATIC mp_obj_t madc_width(mp_obj_t cls_in, mp_obj_t width_in) { - adc_bits_width_t width = mp_obj_get_int(width_in); - esp_err_t err = adc1_config_width(width); - if (err != ESP_OK) { - mp_raise_ValueError(MP_ERROR_TEXT("parameter error")); - } - switch (width) { - #if CONFIG_IDF_TARGET_ESP32 - case ADC_WIDTH_9Bit: - adc_bit_width = 9; - break; - case ADC_WIDTH_10Bit: - adc_bit_width = 10; - break; - case ADC_WIDTH_11Bit: - adc_bit_width = 11; - break; - case ADC_WIDTH_12Bit: - adc_bit_width = 12; - break; - #elif CONFIG_IDF_TARGET_ESP32S2 - case ADC_WIDTH_BIT_13: - adc_bit_width = 13; - break; - #elif CONFIG_IDF_TARGET_ESP32S3 - case ADC_WIDTH_BIT_12: - adc_bit_width = 12; - break; - #endif - default: - break; - } +STATIC mp_obj_t madc_width(mp_obj_t self_in, mp_obj_t bits_in) { + const madc_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_int_t bits = mp_obj_get_int(bits_in); + madcblock_bits_helper(self->block, bits); return mp_const_none; } -MP_DEFINE_CONST_FUN_OBJ_2(madc_width_fun_obj, madc_width); -MP_DEFINE_CONST_CLASSMETHOD_OBJ(madc_width_obj, MP_ROM_PTR(&madc_width_fun_obj)); +MP_DEFINE_CONST_FUN_OBJ_2(madc_width_obj, madc_width); STATIC const mp_rom_map_elem_t madc_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_read_u16), MP_ROM_PTR(&madc_read_u16_obj) }, - + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&madc_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_block), MP_ROM_PTR(&madc_block_obj) }, { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&madc_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_read_u16), MP_ROM_PTR(&madc_read_u16_obj) }, + { MP_ROM_QSTR(MP_QSTR_read_uv), MP_ROM_PTR(&madc_read_uv_obj) }, + + // Legacy API methods: { MP_ROM_QSTR(MP_QSTR_atten), MP_ROM_PTR(&madc_atten_obj) }, { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&madc_width_obj) }, - { MP_ROM_QSTR(MP_QSTR_ATTN_0DB), MP_ROM_INT(ADC_ATTEN_0db) }, - { MP_ROM_QSTR(MP_QSTR_ATTN_2_5DB), MP_ROM_INT(ADC_ATTEN_2_5db) }, - { MP_ROM_QSTR(MP_QSTR_ATTN_6DB), MP_ROM_INT(ADC_ATTEN_6db) }, - { MP_ROM_QSTR(MP_QSTR_ATTN_11DB), MP_ROM_INT(ADC_ATTEN_11db) }, + { MP_ROM_QSTR(MP_QSTR_ATTN_0DB), MP_ROM_INT(ADC_ATTEN_DB_0) }, + { MP_ROM_QSTR(MP_QSTR_ATTN_2_5DB), MP_ROM_INT(ADC_ATTEN_DB_2_5) }, + { MP_ROM_QSTR(MP_QSTR_ATTN_6DB), MP_ROM_INT(ADC_ATTEN_DB_6) }, + { MP_ROM_QSTR(MP_QSTR_ATTN_11DB), MP_ROM_INT(ADC_ATTEN_DB_11) }, #if CONFIG_IDF_TARGET_ESP32 - { MP_ROM_QSTR(MP_QSTR_WIDTH_9BIT), MP_ROM_INT(ADC_WIDTH_9Bit) }, - { MP_ROM_QSTR(MP_QSTR_WIDTH_10BIT), MP_ROM_INT(ADC_WIDTH_10Bit) }, - { MP_ROM_QSTR(MP_QSTR_WIDTH_11BIT), MP_ROM_INT(ADC_WIDTH_11Bit) }, - { MP_ROM_QSTR(MP_QSTR_WIDTH_12BIT), MP_ROM_INT(ADC_WIDTH_12Bit) }, - #elif CONFIG_IDF_TARGET_ESP32S2 - { MP_ROM_QSTR(MP_QSTR_WIDTH_13BIT), MP_ROM_INT(ADC_WIDTH_BIT_13) }, - #elif CONFIG_IDF_TARGET_ESP32S3 - { MP_ROM_QSTR(MP_QSTR_WIDTH_12BIT), MP_ROM_INT(ADC_WIDTH_BIT_12) }, + { MP_ROM_QSTR(MP_QSTR_WIDTH_9BIT), MP_ROM_INT(9) }, + { MP_ROM_QSTR(MP_QSTR_WIDTH_10BIT), MP_ROM_INT(10) }, + { MP_ROM_QSTR(MP_QSTR_WIDTH_11BIT), MP_ROM_INT(11) }, + #endif + #if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 + { MP_ROM_QSTR(MP_QSTR_WIDTH_12BIT), MP_ROM_INT(12) }, + #endif + #if CONFIG_IDF_TARGET_ESP32S2 + { MP_ROM_QSTR(MP_QSTR_WIDTH_13BIT), MP_ROM_INT(13) }, #endif -}; +}; STATIC MP_DEFINE_CONST_DICT(madc_locals_dict, madc_locals_dict_table); const mp_obj_type_t machine_adc_type = { diff --git a/ports/esp32/machine_adc.h b/ports/esp32/machine_adc.h new file mode 100644 index 0000000000..0f229a2c5c --- /dev/null +++ b/ports/esp32/machine_adc.h @@ -0,0 +1,16 @@ +#ifndef MICROPY_INCLUDED_MACHINE_ADC_H +#define MICROPY_INCLUDED_MACHINE_ADC_H + +#include "machine_adcblock.h" + +typedef struct _madc_obj_t { + mp_obj_base_t base; + madcblock_obj_t *block; + adc_channel_t channel_id; + gpio_num_t gpio_id; +} madc_obj_t; + +extern const madc_obj_t *madc_search_helper(madcblock_obj_t *block, adc_channel_t channel_id, gpio_num_t gpio_id); +extern void madc_init_helper(const madc_obj_t *self, size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args); + +#endif // MICROPY_INCLUDED_MACHINE_ADC_H diff --git a/ports/esp32/machine_adcblock.c b/ports/esp32/machine_adcblock.c new file mode 100644 index 0000000000..77e4fcecc7 --- /dev/null +++ b/ports/esp32/machine_adcblock.c @@ -0,0 +1,203 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Jonathan Hogg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +#include "esp_log.h" + +#include "driver/gpio.h" +#include "driver/adc.h" + +#include "py/runtime.h" +#include "py/mphal.h" +#include "modmachine.h" +#include "machine_adc.h" +#include "machine_adcblock.h" + +#define DEFAULT_VREF 1100 + +madcblock_obj_t madcblock_obj[] = { + #if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C3 + {{&machine_adcblock_type}, ADC_UNIT_1, 12, -1, {0}}, + {{&machine_adcblock_type}, ADC_UNIT_2, 12, -1, {0}}, + #elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 + {{&machine_adcblock_type}, ADC_UNIT_1, 13, -1, {0}}, + {{&machine_adcblock_type}, ADC_UNIT_2, 13, -1, {0}}, + #endif +}; + +STATIC void madcblock_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + madcblock_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "ADCBlock(%u, bits=%u)", self->unit_id, self->bits); +} + +void madcblock_bits_helper(madcblock_obj_t *self, mp_int_t bits) { + switch (bits) { + #if CONFIG_IDF_TARGET_ESP32 + case 9: + self->width = ADC_WIDTH_BIT_9; + break; + case 10: + self->width = ADC_WIDTH_BIT_10; + break; + case 11: + self->width = ADC_WIDTH_BIT_11; + break; + #endif + #if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 + case 12: + self->width = ADC_WIDTH_BIT_12; + break; + #endif + #if CONFIG_IDF_TARGET_ESP32S2 + case 13: + self->width = ADC_WIDTH_BIT_13; + break; + #endif + default: + mp_raise_ValueError(MP_ERROR_TEXT("invalid bits")); + } + self->bits = bits; + + if (self->unit_id == ADC_UNIT_1) { + adc1_config_width(self->width); + } + for (adc_atten_t atten = ADC_ATTEN_DB_0; atten < ADC_ATTEN_MAX; atten++) { + if (self->characteristics[atten] != NULL) { + esp_adc_cal_characterize(self->unit_id, atten, self->width, DEFAULT_VREF, self->characteristics[atten]); + } + } +} + +STATIC void madcblock_init_helper(madcblock_obj_t *self, size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { + ARG_bits, + }; + + static const mp_arg_t allowed_args[] = { + { MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + }; + + 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 bits = args[ARG_bits].u_int; + if (bits != -1) { + madcblock_bits_helper(self, bits); + } else if (self->width == -1) { + madcblock_bits_helper(self, self->bits); + } +} + +STATIC mp_obj_t madcblock_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); + adc_unit_t unit = mp_obj_get_int(args[0]); + madcblock_obj_t *self = NULL; + for (int i = 0; i < MP_ARRAY_SIZE(madcblock_obj); i++) { + if (unit == madcblock_obj[i].unit_id) { + self = &madcblock_obj[i]; + break; + } + } + if (!self) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid block id")); + } + + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw_args, args + n_pos_args); + madcblock_init_helper(self, n_pos_args - 1, args + 1, &kw_args); + + return MP_OBJ_FROM_PTR(self); +} + +STATIC mp_obj_t madcblock_init(size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + madcblock_obj_t *self = pos_args[0]; + madcblock_init_helper(self, n_pos_args - 1, pos_args + 1, kw_args); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(madcblock_init_obj, 1, madcblock_init); + +STATIC mp_obj_t madcblock_connect(size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + madcblock_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + adc_channel_t channel_id = -1; + gpio_num_t gpio_id = -1; + if (n_pos_args == 2) { + if (mp_obj_is_int(pos_args[1])) { + channel_id = mp_obj_get_int(pos_args[1]); + } else { + gpio_id = machine_pin_get_id(pos_args[1]); + } + } else if (n_pos_args == 3) { + channel_id = mp_obj_get_int(pos_args[1]); + gpio_id = machine_pin_get_id(pos_args[2]); + } else { + mp_raise_TypeError(MP_ERROR_TEXT("too many positional args")); + } + + const madc_obj_t *adc = madc_search_helper(self, channel_id, gpio_id); + if (adc != NULL) { + madc_init_helper(adc, 0, pos_args + n_pos_args, kw_args); + return MP_OBJ_FROM_PTR(adc); + } + mp_raise_ValueError(MP_ERROR_TEXT("no matching ADC")); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(madcblock_connect_obj, 2, madcblock_connect); + +mp_int_t madcblock_read_helper(madcblock_obj_t *self, adc_channel_t channel_id) { + int raw; + if (self->unit_id == ADC_UNIT_1) { + raw = adc1_get_raw(channel_id); + } else { + check_esp_err(adc2_get_raw(channel_id, self->width, &raw)); + } + return raw; +} + +mp_int_t madcblock_read_uv_helper(madcblock_obj_t *self, adc_channel_t channel_id, adc_atten_t atten) { + int raw = madcblock_read_helper(self, channel_id); + esp_adc_cal_characteristics_t *adc_chars = self->characteristics[atten]; + if (adc_chars == NULL) { + adc_chars = malloc(sizeof(esp_adc_cal_characteristics_t)); + esp_adc_cal_characterize(self->unit_id, atten, self->width, DEFAULT_VREF, adc_chars); + self->characteristics[atten] = adc_chars; + } + mp_int_t uv = esp_adc_cal_raw_to_voltage(raw, adc_chars) * 1000; + return uv; +} + +STATIC const mp_rom_map_elem_t madcblock_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&madcblock_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&madcblock_connect_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(madcblock_locals_dict, madcblock_locals_dict_table); + +const mp_obj_type_t machine_adcblock_type = { + { &mp_type_type }, + .name = MP_QSTR_ADCBlock, + .print = madcblock_print, + .make_new = madcblock_make_new, + .locals_dict = (mp_obj_t)&madcblock_locals_dict, +}; diff --git a/ports/esp32/machine_adcblock.h b/ports/esp32/machine_adcblock.h new file mode 100644 index 0000000000..0500726d71 --- /dev/null +++ b/ports/esp32/machine_adcblock.h @@ -0,0 +1,20 @@ +#ifndef MICROPY_INCLUDED_MACHINE_ADCBLOCK_H +#define MICROPY_INCLUDED_MACHINE_ADCBLOCK_H + +#include "esp_adc_cal.h" + +typedef struct _madcblock_obj_t { + mp_obj_base_t base; + adc_unit_t unit_id; + mp_int_t bits; + adc_bits_width_t width; + esp_adc_cal_characteristics_t *characteristics[ADC_ATTEN_MAX]; +} madcblock_obj_t; + +extern madcblock_obj_t madcblock_obj[]; + +extern void madcblock_bits_helper(madcblock_obj_t *self, mp_int_t bits); +extern mp_int_t madcblock_read_helper(madcblock_obj_t *self, adc_channel_t channel_id); +extern mp_int_t madcblock_read_uv_helper(madcblock_obj_t *self, adc_channel_t channel_id, adc_atten_t atten); + +#endif // MICROPY_INCLUDED_MACHINE_ADCBLOCK_H diff --git a/ports/esp32/main/CMakeLists.txt b/ports/esp32/main/CMakeLists.txt index 68a4815d7e..c86ac4dadf 100644 --- a/ports/esp32/main/CMakeLists.txt +++ b/ports/esp32/main/CMakeLists.txt @@ -59,6 +59,7 @@ set(MICROPY_SOURCE_PORT ${PROJECT_DIR}/machine_pin.c ${PROJECT_DIR}/machine_touchpad.c ${PROJECT_DIR}/machine_adc.c + ${PROJECT_DIR}/machine_adcblock.c ${PROJECT_DIR}/machine_dac.c ${PROJECT_DIR}/machine_i2c.c ${PROJECT_DIR}/machine_i2s.c @@ -98,6 +99,7 @@ set(IDF_COMPONENTS bootloader_support bt driver + esp_adc_cal esp_common esp_eth esp_event diff --git a/ports/esp32/modmachine.c b/ports/esp32/modmachine.c index cca2015729..dfe2435ec2 100644 --- a/ports/esp32/modmachine.c +++ b/ports/esp32/modmachine.c @@ -297,6 +297,7 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_TouchPad), MP_ROM_PTR(&machine_touchpad_type) }, #endif { MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&machine_adc_type) }, + { MP_ROM_QSTR(MP_QSTR_ADCBlock), MP_ROM_PTR(&machine_adcblock_type) }, #if MICROPY_PY_MACHINE_DAC { MP_ROM_QSTR(MP_QSTR_DAC), MP_ROM_PTR(&machine_dac_type) }, #endif diff --git a/ports/esp32/modmachine.h b/ports/esp32/modmachine.h index c773f8dbc9..4d2ab9020d 100644 --- a/ports/esp32/modmachine.h +++ b/ports/esp32/modmachine.h @@ -14,6 +14,7 @@ extern const mp_obj_type_t machine_wdt_type; extern const mp_obj_type_t machine_pin_type; extern const mp_obj_type_t machine_touchpad_type; extern const mp_obj_type_t machine_adc_type; +extern const mp_obj_type_t machine_adcblock_type; extern const mp_obj_type_t machine_dac_type; extern const mp_obj_type_t machine_hw_i2c_type; extern const mp_obj_type_t machine_hw_spi_type; From 357078504d0d2c3a3f06ec4bd69e0ab3bcf9e016 Mon Sep 17 00:00:00 2001 From: jason Date: Wed, 12 May 2021 21:10:06 -0700 Subject: [PATCH 027/619] esp32: Pin MicroPython to core 1 again. This follows up on #5489, where we changed the esp32 core pinning to core 0 in order to work around an issue with IDF < 4.2.0. Now that IDF > 4.2.0 is available, we allow pinning back to core 1, which eliminates some problematic callback latency with WiFi enabled. NimBLE is also pinned to core 1 - the same core as MicroPython - when using IDF >=4.2. --- ports/esp32/CMakeLists.txt | 13 +++++++++++-- ports/esp32/boards/sdkconfig.ble | 8 -------- ports/esp32/boards/sdkconfig.nimble_core0 | 6 ++++++ ports/esp32/boards/sdkconfig.nimble_core1 | 6 ++++++ ports/esp32/mphalport.h | 11 ++++++++--- 5 files changed, 31 insertions(+), 13 deletions(-) create mode 100644 ports/esp32/boards/sdkconfig.nimble_core0 create mode 100644 ports/esp32/boards/sdkconfig.nimble_core1 diff --git a/ports/esp32/CMakeLists.txt b/ports/esp32/CMakeLists.txt index 29409adc74..8b2f09a724 100644 --- a/ports/esp32/CMakeLists.txt +++ b/ports/esp32/CMakeLists.txt @@ -18,12 +18,22 @@ if(NOT EXISTS ${MICROPY_BOARD_DIR}/mpconfigboard.cmake) message(FATAL_ERROR "Invalid MICROPY_BOARD specified: ${MICROPY_BOARD}") endif() +# Include main IDF cmake file. +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + # Define the output sdkconfig so it goes in the build directory. set(SDKCONFIG ${CMAKE_BINARY_DIR}/sdkconfig) # Include board config; this is expected to set SDKCONFIG_DEFAULTS (among other options). include(${MICROPY_BOARD_DIR}/mpconfigboard.cmake) +# Add sdkconfig fragments that depend on the IDF version. +if(IDF_VERSION_MAJOR EQUAL 4 AND IDF_VERSION_MINOR LESS 2) + set(SDKCONFIG_DEFAULTS ${SDKCONFIG_DEFAULTS} boards/sdkconfig.nimble_core0) +else() + set(SDKCONFIG_DEFAULTS ${SDKCONFIG_DEFAULTS} boards/sdkconfig.nimble_core1) +endif() + # Concatenate all sdkconfig files into a combined one for the IDF to use. file(WRITE ${CMAKE_BINARY_DIR}/sdkconfig.combined.in "") foreach(SDKCONFIG_DEFAULT ${SDKCONFIG_DEFAULTS}) @@ -33,6 +43,5 @@ endforeach() configure_file(${CMAKE_BINARY_DIR}/sdkconfig.combined.in ${CMAKE_BINARY_DIR}/sdkconfig.combined COPYONLY) set(SDKCONFIG_DEFAULTS ${CMAKE_BINARY_DIR}/sdkconfig.combined) -# Include main IDF cmake file and define the project. -include($ENV{IDF_PATH}/tools/cmake/project.cmake) +# Define the project. project(micropython) diff --git a/ports/esp32/boards/sdkconfig.ble b/ports/esp32/boards/sdkconfig.ble index 5565fd81a8..08d5e481f4 100644 --- a/ports/esp32/boards/sdkconfig.ble +++ b/ports/esp32/boards/sdkconfig.ble @@ -7,11 +7,3 @@ CONFIG_BTDM_CTRL_MODE_BTDM= CONFIG_BT_NIMBLE_ENABLED=y CONFIG_BT_NIMBLE_MAX_CONNECTIONS=4 - -# Pin to the same core as MP. -# Until we move to IDF 4.2+, we need NimBLE on core 0, and for synchronisation -# with the ringbuffer and scheduler MP needs to be on the same core. -# See https://github.com/micropython/micropython/issues/5489 -CONFIG_BT_NIMBLE_PINNED_TO_CORE_0=y -CONFIG_BT_NIMBLE_PINNED_TO_CORE_1=n -CONFIG_BT_NIMBLE_PINNED_TO_CORE=0 diff --git a/ports/esp32/boards/sdkconfig.nimble_core0 b/ports/esp32/boards/sdkconfig.nimble_core0 new file mode 100644 index 0000000000..cacaff1197 --- /dev/null +++ b/ports/esp32/boards/sdkconfig.nimble_core0 @@ -0,0 +1,6 @@ +# For IDF <4.2, we need NimBLE on core 0, and for synchronisation +# with the ringbuffer and scheduler MP needs to be on the same core. +# See https://github.com/micropython/micropython/issues/5489 +CONFIG_BT_NIMBLE_PINNED_TO_CORE_0=y +CONFIG_BT_NIMBLE_PINNED_TO_CORE_1=n +CONFIG_BT_NIMBLE_PINNED_TO_CORE=0 diff --git a/ports/esp32/boards/sdkconfig.nimble_core1 b/ports/esp32/boards/sdkconfig.nimble_core1 new file mode 100644 index 0000000000..33653cc4b1 --- /dev/null +++ b/ports/esp32/boards/sdkconfig.nimble_core1 @@ -0,0 +1,6 @@ +# For IDF >=4.2, we are able to put NimBLE on core 1, and for synchronisation +# with the ringbuffer and scheduler MP needs to be on the same core. +# MP on core 1 prevents interference with WiFi for time sensitive operations. +CONFIG_BT_NIMBLE_PINNED_TO_CORE_0=n +CONFIG_BT_NIMBLE_PINNED_TO_CORE_1=y +CONFIG_BT_NIMBLE_PINNED_TO_CORE=1 diff --git a/ports/esp32/mphalport.h b/ports/esp32/mphalport.h index 01c14ad700..c838bd2284 100644 --- a/ports/esp32/mphalport.h +++ b/ports/esp32/mphalport.h @@ -38,10 +38,15 @@ #define MICROPY_PLATFORM_VERSION "IDF" IDF_VER // The core that the MicroPython task(s) are pinned to. -// Until we move to IDF 4.2+, we need NimBLE on core 0, and for synchronisation -// with the ringbuffer and scheduler MP needs to be on the same core. -// See https://github.com/micropython/micropython/issues/5489 +// Now that we have IDF 4.2.0+, we are once again able to pin to core 1 +// and avoid the Wifi/BLE timing problems on the same core. +// Best effort here to remain backwards compatible in rare version edge cases... +// See https://github.com/micropython/micropython/issues/5489 for history +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0) +#define MP_TASK_COREID (1) +#else #define MP_TASK_COREID (0) +#endif extern TaskHandle_t mp_main_task_handle; From 648656dbbd841aae129628487a71037236aac739 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 22 Jan 2022 00:02:27 +1100 Subject: [PATCH 028/619] esp32/esp32_rmt: Call rmt_driver_install directly if running on core 1. Signed-off-by: Damien George --- ports/esp32/esp32_rmt.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/ports/esp32/esp32_rmt.c b/ports/esp32/esp32_rmt.c index 639e0467a8..ca751d42a8 100644 --- a/ports/esp32/esp32_rmt.c +++ b/ports/esp32/esp32_rmt.c @@ -60,16 +60,18 @@ typedef struct _esp32_rmt_obj_t { bool loop_en; } esp32_rmt_obj_t; +// Current channel used for machine.bitstream, in the machine_bitstream_high_low_rmt +// implementation. A value of -1 means do not use RMT. +int8_t esp32_rmt_bitstream_channel_id = RMT_CHANNEL_MAX - 1; + +#if MP_TASK_COREID == 0 + typedef struct _rmt_install_state_t { SemaphoreHandle_t handle; uint8_t channel_id; esp_err_t ret; } rmt_install_state_t; -// Current channel used for machine.bitstream, in the machine_bitstream_high_low_rmt -// implementation. A value of -1 means do not use RMT. -int8_t esp32_rmt_bitstream_channel_id = RMT_CHANNEL_MAX - 1; - STATIC void rmt_install_task(void *pvParameter) { rmt_install_state_t *state = pvParameter; state->ret = rmt_driver_install(state->channel_id, 0, 0); @@ -92,6 +94,16 @@ esp_err_t rmt_driver_install_core1(uint8_t channel_id) { return state.ret; } +#else + +// MicroPython runs on core 1, so we can call the RMT installer directly and its +// interrupt handler will also run on core 1. +esp_err_t rmt_driver_install_core1(uint8_t channel_id) { + return rmt_driver_install(channel_id, 0, 0); +} + +#endif + STATIC mp_obj_t esp32_rmt_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { static const mp_arg_t allowed_args[] = { { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = -1} }, From 23b1a4e0b6c6f538d6622359aba0740dfe14c44d Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 22 Jan 2022 00:46:12 +1100 Subject: [PATCH 029/619] esp32/main: Allocate at most 1/2 of available IDF heap for MP heap. So that there is some memory left for the OS, eg for ssl buffers. See issue #7214. Signed-off-by: Damien George --- ports/esp32/main.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/ports/esp32/main.c b/ports/esp32/main.c index c9b033d626..d21dc9f753 100644 --- a/ports/esp32/main.c +++ b/ports/esp32/main.c @@ -97,11 +97,12 @@ void mp_task(void *pvParameter) { #endif machine_init(); - // TODO: CONFIG_SPIRAM_SUPPORT is for 3.3 compatibility, remove after move to 4.0. - #if CONFIG_ESP32_SPIRAM_SUPPORT || CONFIG_SPIRAM_SUPPORT - // Try to use the entire external SPIRAM directly for the heap size_t mp_task_heap_size; - void *mp_task_heap = (void *)SOC_EXTRAM_DATA_LOW; + void *mp_task_heap = NULL; + + #if CONFIG_ESP32_SPIRAM_SUPPORT + // Try to use the entire external SPIRAM directly for the heap + mp_task_heap = (void *)SOC_EXTRAM_DATA_LOW; switch (esp_spiram_get_chip_size()) { case ESP_SPIRAM_SIZE_16MBITS: mp_task_heap_size = 2 * 1024 * 1024; @@ -112,27 +113,27 @@ void mp_task(void *pvParameter) { break; default: // No SPIRAM, fallback to normal allocation - mp_task_heap_size = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT); - mp_task_heap = malloc(mp_task_heap_size); + mp_task_heap = NULL; break; } #elif CONFIG_ESP32S2_SPIRAM_SUPPORT || CONFIG_ESP32S3_SPIRAM_SUPPORT // Try to use the entire external SPIRAM directly for the heap - size_t mp_task_heap_size; size_t esp_spiram_size = esp_spiram_get_size(); - void *mp_task_heap = (void *)SOC_EXTRAM_DATA_HIGH - esp_spiram_size; if (esp_spiram_size > 0) { + mp_task_heap = (void *)SOC_EXTRAM_DATA_HIGH - esp_spiram_size; mp_task_heap_size = esp_spiram_size; - } else { - // No SPIRAM, fallback to normal allocation - mp_task_heap_size = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT); + } + #endif + + if (mp_task_heap == NULL) { + // Allocate the uPy heap using malloc and get the largest available region, + // limiting to 1/2 total available memory to leave memory for the OS. + mp_task_heap_size = MIN( + heap_caps_get_largest_free_block(MALLOC_CAP_8BIT), + heap_caps_get_total_size(MALLOC_CAP_8BIT) / 2 + ); mp_task_heap = malloc(mp_task_heap_size); } - #else - // Allocate the uPy heap using malloc and get the largest available region - size_t mp_task_heap_size = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT); - void *mp_task_heap = malloc(mp_task_heap_size); - #endif soft_reset: // initialise the stack pointer for the main thread From 6a10d3ed99cf63a2505536862734db726008ccfd Mon Sep 17 00:00:00 2001 From: Sean Coates Date: Fri, 14 Jan 2022 17:28:29 -0500 Subject: [PATCH 030/619] esp32/README.md: Fix URL for esp-idf installation. The current URL points to a pinned version of the document (v4.0.2) and is currently not found (404). --- ports/esp32/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/esp32/README.md b/ports/esp32/README.md index 42cd588171..4524696899 100644 --- a/ports/esp32/README.md +++ b/ports/esp32/README.md @@ -32,7 +32,7 @@ Currently MicroPython supports v4.0.2, v4.1.1 and v4.2, although other IDF v4 versions may also work. To install the ESP-IDF the full instructions can be found at the -[Espressif Getting Started guide](https://docs.espressif.com/projects/esp-idf/en/v4.0.2/get-started/index.html#installation-step-by-step). +[Espressif Getting Started guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/index.html#installation-step-by-step). If you are on a Windows machine then the [Windows Subsystem for Linux](https://msdn.microsoft.com/en-au/commandline/wsl/install_guide) is the From ac39960aa13d1c4d4c93f002463b1b462be015e0 Mon Sep 17 00:00:00 2001 From: marcidy Date: Mon, 17 Jan 2022 20:41:23 -0800 Subject: [PATCH 031/619] esp32/modnetwork: Fix test for WIFI_AUTH_MAX for IDF v4.3.0. Signed-off-by: marcidy --- ports/esp32/modnetwork.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/esp32/modnetwork.c b/ports/esp32/modnetwork.c index a16c67eeb9..206fecbcf9 100644 --- a/ports/esp32/modnetwork.c +++ b/ports/esp32/modnetwork.c @@ -212,7 +212,7 @@ STATIC mp_obj_t esp_phy_mode(size_t n_args, const mp_obj_t *args) { } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_phy_mode_obj, 0, 1, esp_phy_mode); -#if ESP_IDF_VERSION > ESP_IDF_VERSION_VAL(4, 3, 0) +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0) #define TEST_WIFI_AUTH_MAX 9 #else #define TEST_WIFI_AUTH_MAX 8 From bb9d688454336591803858b769ce3ea23ab2df91 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 22 Jan 2022 10:45:17 +1100 Subject: [PATCH 032/619] esp32/main: Use heap_caps_get_info on IDF <4.1 to compute total heap. heap_caps_get_total_size() is only available in IDF 4.1 and above. Signed-off-by: Damien George --- ports/esp32/main.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/ports/esp32/main.c b/ports/esp32/main.c index d21dc9f753..e25e6fdd1c 100644 --- a/ports/esp32/main.c +++ b/ports/esp32/main.c @@ -128,10 +128,14 @@ void mp_task(void *pvParameter) { if (mp_task_heap == NULL) { // Allocate the uPy heap using malloc and get the largest available region, // limiting to 1/2 total available memory to leave memory for the OS. - mp_task_heap_size = MIN( - heap_caps_get_largest_free_block(MALLOC_CAP_8BIT), - heap_caps_get_total_size(MALLOC_CAP_8BIT) / 2 - ); + #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 1, 0) + size_t heap_total = heap_caps_get_total_size(MALLOC_CAP_8BIT); + #else + multi_heap_info_t info; + heap_caps_get_info(&info, MALLOC_CAP_8BIT); + size_t heap_total = info.total_free_bytes + info.total_allocated_bytes; + #endif + mp_task_heap_size = MIN(heap_caps_get_largest_free_block(MALLOC_CAP_8BIT), heap_total / 2); mp_task_heap = malloc(mp_task_heap_size); } From ea5744fd8decb1161a2bd212813a275e2705fd89 Mon Sep 17 00:00:00 2001 From: ubi de feo Date: Thu, 20 Jan 2022 07:07:06 +0100 Subject: [PATCH 033/619] esp32/boards: Provide custom deploy_c3.md for ESP32-C3 boards. This fixes the flash address for installation on ESP32-C3. --- ports/esp32/boards/GENERIC_C3/board.json | 2 +- ports/esp32/boards/GENERIC_C3_USB/board.json | 2 +- ports/esp32/boards/deploy_c3.md | 14 ++++++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 ports/esp32/boards/deploy_c3.md diff --git a/ports/esp32/boards/GENERIC_C3/board.json b/ports/esp32/boards/GENERIC_C3/board.json index 481e66bccd..6cc326bf14 100644 --- a/ports/esp32/boards/GENERIC_C3/board.json +++ b/ports/esp32/boards/GENERIC_C3/board.json @@ -1,6 +1,6 @@ { "deploy": [ - "../deploy.md" + "../deploy_c3.md" ], "docs": "", "features": [ diff --git a/ports/esp32/boards/GENERIC_C3_USB/board.json b/ports/esp32/boards/GENERIC_C3_USB/board.json index 94d86d4428..23f9a8eb20 100644 --- a/ports/esp32/boards/GENERIC_C3_USB/board.json +++ b/ports/esp32/boards/GENERIC_C3_USB/board.json @@ -1,6 +1,6 @@ { "deploy": [ - "../deploy.md" + "../deploy_c3.md" ], "docs": "", "features": [ diff --git a/ports/esp32/boards/deploy_c3.md b/ports/esp32/boards/deploy_c3.md new file mode 100644 index 0000000000..016ba7cabb --- /dev/null +++ b/ports/esp32/boards/deploy_c3.md @@ -0,0 +1,14 @@ +Program your board using the esptool.py program, found [here](https://github.com/espressif/esptool). + +If you are putting MicroPython on your board for the first time then you should +first erase the entire flash using: + +```bash +esptool.py --chip esp32c3 --port /dev/ttyUSB0 erase_flash +``` + +From then on program the firmware starting at address 0x0: + +```bash +esptool.py --chip esp32c3 --port /dev/ttyUSB0 --baud 460800 write_flash -z 0x0 esp32c3-20220117-v1.18.bin +``` From 5f8eef4521866a072e27914d597c48e44a2c16e4 Mon Sep 17 00:00:00 2001 From: Jos Verlinde Date: Wed, 19 Jan 2022 16:33:41 +0100 Subject: [PATCH 034/619] windows/README.md: Fix broken mingw link. --- ports/windows/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/windows/README.md b/ports/windows/README.md index 713ea82b87..1fa8008910 100644 --- a/ports/windows/README.md +++ b/ports/windows/README.md @@ -3,7 +3,7 @@ It is based on Unix port, and expected to remain so. The port requires additional testing, debugging, and patches. Please consider to contribute. -All gcc-based builds use the gcc compiler from [Mingw-w64](mingw-w64.org), +All gcc-based builds use the gcc compiler from [Mingw-w64](https://www.mingw-w64.org/), which is the advancement of the original mingw project. The latter is getting obsolete and is not actively supported by MicroPython. From a9448c0a86d6d1f04e190d5c093ca93d9470b99a Mon Sep 17 00:00:00 2001 From: stijn Date: Tue, 11 Jan 2022 17:25:06 +0100 Subject: [PATCH 035/619] all: Fix MICROPY_OBJ_REPR_D compilation with msvc. --- mpy-cross/mpconfigport.h | 2 +- ports/windows/mpconfigport.h | 2 +- py/objfun.c | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/mpy-cross/mpconfigport.h b/mpy-cross/mpconfigport.h index 8c716e958d..4c10afe9bb 100644 --- a/mpy-cross/mpconfigport.h +++ b/mpy-cross/mpconfigport.h @@ -139,7 +139,7 @@ typedef long mp_off_t; #define MP_NOINLINE __declspec(noinline) #define MP_LIKELY(x) (x) #define MP_UNLIKELY(x) (x) -#define MICROPY_PORT_CONSTANTS { "dummy", 0 } +#define MICROPY_PORT_CONSTANTS { MP_ROM_QSTR(MP_QSTR_dummy), MP_ROM_PTR(NULL) } #ifdef _WIN64 #define MP_SSIZE_MAX _I64_MAX #else diff --git a/ports/windows/mpconfigport.h b/ports/windows/mpconfigport.h index 96405bc5d2..1aae5a7500 100644 --- a/ports/windows/mpconfigport.h +++ b/ports/windows/mpconfigport.h @@ -244,7 +244,7 @@ extern const struct _mp_obj_module_t mp_module_time; #define MP_NOINLINE __declspec(noinline) #define MP_LIKELY(x) (x) #define MP_UNLIKELY(x) (x) -#define MICROPY_PORT_CONSTANTS { "dummy", 0 } // can't have zero-sized array +#define MICROPY_PORT_CONSTANTS { MP_ROM_QSTR(MP_QSTR_dummy), MP_ROM_PTR(NULL) } // can't have zero-sized array #ifdef _WIN64 #define MP_SSIZE_MAX _I64_MAX #else diff --git a/py/objfun.c b/py/objfun.c index d86a4d235a..85f531c88b 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -410,7 +410,7 @@ mp_obj_t mp_obj_new_fun_bc(mp_obj_t def_args_in, mp_obj_t def_kw_args, const byt STATIC mp_obj_t fun_native_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { MP_STACK_CHECK(); - mp_obj_fun_bc_t *self = self_in; + mp_obj_fun_bc_t *self = MP_OBJ_TO_PTR(self_in); mp_call_fun_t fun = MICROPY_MAKE_POINTER_CALLABLE((void *)self->bytecode); return fun(self_in, n_args, n_kw, args); } @@ -424,9 +424,9 @@ STATIC const mp_obj_type_t mp_type_fun_native = { }; mp_obj_t mp_obj_new_fun_native(mp_obj_t def_args_in, mp_obj_t def_kw_args, const void *fun_data, const mp_uint_t *const_table) { - mp_obj_fun_bc_t *o = mp_obj_new_fun_bc(def_args_in, def_kw_args, (const byte *)fun_data, const_table); + mp_obj_fun_bc_t *o = MP_OBJ_TO_PTR(mp_obj_new_fun_bc(def_args_in, def_kw_args, (const byte *)fun_data, const_table)); o->base.type = &mp_type_fun_native; - return o; + return MP_OBJ_FROM_PTR(o); } #endif // MICROPY_EMIT_NATIVE @@ -494,7 +494,7 @@ STATIC mp_uint_t convert_obj_for_inline_asm(mp_obj_t obj) { } STATIC mp_obj_t fun_asm_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { - mp_obj_fun_asm_t *self = self_in; + mp_obj_fun_asm_t *self = MP_OBJ_TO_PTR(self_in); mp_arg_check_num(n_args, n_kw, self->n_args, self->n_args, false); @@ -537,7 +537,7 @@ mp_obj_t mp_obj_new_fun_asm(size_t n_args, const void *fun_data, mp_uint_t type_ o->n_args = n_args; o->fun_data = fun_data; o->type_sig = type_sig; - return o; + return MP_OBJ_FROM_PTR(o); } #endif // MICROPY_EMIT_INLINE_ASM From e0b8d6982713c78169ec882770e746f2c5f4bc30 Mon Sep 17 00:00:00 2001 From: stijn Date: Wed, 12 Jan 2022 14:35:16 +0100 Subject: [PATCH 036/619] github/workflows: Show context for qemu-arm test failures. Make it easier to see what went wrong in CI builds. --- .github/workflows/ports_qemu-arm.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ports_qemu-arm.yml b/.github/workflows/ports_qemu-arm.yml index 8d144ca3cb..0489a93d5a 100644 --- a/.github/workflows/ports_qemu-arm.yml +++ b/.github/workflows/ports_qemu-arm.yml @@ -24,4 +24,4 @@ jobs: run: source tools/ci.sh && ci_qemu_arm_build - name: Print failures if: failure() - run: grep --text "FAIL" ports/qemu-arm/build/console.out + run: grep --before-context=100 --text "FAIL" ports/qemu-arm/build/console.out From dd6967202a734fce569127c07da7ed1e07ce8bc4 Mon Sep 17 00:00:00 2001 From: stijn Date: Wed, 20 Nov 2019 13:38:33 +0100 Subject: [PATCH 037/619] py/modmath: Add math.tau, math.nan and math.inf constants. Configurable by the new MICROPY_PY_MATH_CONSTANTS option. --- .../unix/variants/coverage/mpconfigvariant.h | 1 + ports/unix/variants/dev/mpconfigvariant.h | 1 + ports/windows/variants/dev/mpconfigvariant.h | 1 + py/modmath.c | 5 ++++ py/mpconfig.h | 5 ++++ py/obj.h | 30 +++++++++++++++++++ py/objfloat.c | 8 +++++ tests/float/math_constants.py | 11 +++++++ tests/float/math_constants_extra.py | 17 +++++++++++ 9 files changed, 79 insertions(+) create mode 100644 tests/float/math_constants.py create mode 100644 tests/float/math_constants_extra.py diff --git a/ports/unix/variants/coverage/mpconfigvariant.h b/ports/unix/variants/coverage/mpconfigvariant.h index f033dddb10..5ce40edbb9 100644 --- a/ports/unix/variants/coverage/mpconfigvariant.h +++ b/ports/unix/variants/coverage/mpconfigvariant.h @@ -48,6 +48,7 @@ #define MICROPY_PY_BUILTINS_HELP (1) #define MICROPY_PY_BUILTINS_HELP_MODULES (1) #define MICROPY_PY_SYS_GETSIZEOF (1) +#define MICROPY_PY_MATH_CONSTANTS (1) #define MICROPY_PY_MATH_FACTORIAL (1) #define MICROPY_PY_URANDOM_EXTRA_FUNCS (1) #define MICROPY_PY_IO_BUFFEREDWRITER (1) diff --git a/ports/unix/variants/dev/mpconfigvariant.h b/ports/unix/variants/dev/mpconfigvariant.h index 54ad993236..35c24c5f00 100644 --- a/ports/unix/variants/dev/mpconfigvariant.h +++ b/ports/unix/variants/dev/mpconfigvariant.h @@ -33,6 +33,7 @@ #define MICROPY_PY_BUILTINS_HELP (1) #define MICROPY_PY_BUILTINS_HELP_MODULES (1) +#define MICROPY_PY_MATH_CONSTANTS (1) #define MICROPY_PY_SYS_SETTRACE (1) #define MICROPY_PY_UOS_VFS (1) #define MICROPY_PY_URANDOM_EXTRA_FUNCS (1) diff --git a/ports/windows/variants/dev/mpconfigvariant.h b/ports/windows/variants/dev/mpconfigvariant.h index 2da0977140..1f205066f0 100644 --- a/ports/windows/variants/dev/mpconfigvariant.h +++ b/ports/windows/variants/dev/mpconfigvariant.h @@ -30,6 +30,7 @@ #define MICROPY_PY_BUILTINS_HELP (1) #define MICROPY_PY_BUILTINS_HELP_MODULES (1) +#define MICROPY_PY_MATH_CONSTANTS (1) #define MICROPY_PY_SYS_SETTRACE (1) #define MICROPY_PERSISTENT_CODE_SAVE (1) #define MICROPY_COMP_CONST (0) diff --git a/py/modmath.c b/py/modmath.c index ac9e0bbc44..8fc821c9d5 100644 --- a/py/modmath.c +++ b/py/modmath.c @@ -371,6 +371,11 @@ STATIC const mp_rom_map_elem_t mp_module_math_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_math) }, { MP_ROM_QSTR(MP_QSTR_e), mp_const_float_e }, { MP_ROM_QSTR(MP_QSTR_pi), mp_const_float_pi }, + #if MICROPY_PY_MATH_CONSTANTS + { MP_ROM_QSTR(MP_QSTR_tau), mp_const_float_tau }, + { MP_ROM_QSTR(MP_QSTR_inf), mp_const_float_inf }, + { MP_ROM_QSTR(MP_QSTR_nan), mp_const_float_nan }, + #endif { MP_ROM_QSTR(MP_QSTR_sqrt), MP_ROM_PTR(&mp_math_sqrt_obj) }, { MP_ROM_QSTR(MP_QSTR_pow), MP_ROM_PTR(&mp_math_pow_obj) }, { MP_ROM_QSTR(MP_QSTR_exp), MP_ROM_PTR(&mp_math_exp_obj) }, diff --git a/py/mpconfig.h b/py/mpconfig.h index c1a0bfc046..47779a67de 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -1221,6 +1221,11 @@ typedef double mp_float_t; #define MICROPY_PY_MATH (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) #endif +// Whether to provide all math module constants (Python 3.5+), or just pi and e. +#ifndef MICROPY_PY_MATH_CONSTANTS +#define MICROPY_PY_MATH_CONSTANTS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + // Whether to provide special math functions: math.{erf,erfc,gamma,lgamma} #ifndef MICROPY_PY_MATH_SPECIAL_FUNCTIONS #define MICROPY_PY_MATH_SPECIAL_FUNCTIONS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) diff --git a/py/obj.h b/py/obj.h index 7730059e6a..b52ee0e2f4 100644 --- a/py/obj.h +++ b/py/obj.h @@ -104,8 +104,18 @@ static inline bool mp_obj_is_immediate_obj(mp_const_obj_t o) { #if MICROPY_PY_BUILTINS_FLOAT #define mp_const_float_e MP_ROM_PTR(&mp_const_float_e_obj) #define mp_const_float_pi MP_ROM_PTR(&mp_const_float_pi_obj) +#if MICROPY_PY_MATH_CONSTANTS +#define mp_const_float_tau MP_ROM_PTR(&mp_const_float_tau_obj) +#define mp_const_float_inf MP_ROM_PTR(&mp_const_float_inf_obj) +#define mp_const_float_nan MP_ROM_PTR(&mp_const_float_nan_obj) +#endif extern const struct _mp_obj_float_t mp_const_float_e_obj; extern const struct _mp_obj_float_t mp_const_float_pi_obj; +#if MICROPY_PY_MATH_CONSTANTS +extern const struct _mp_obj_float_t mp_const_float_tau_obj; +extern const struct _mp_obj_float_t mp_const_float_inf_obj; +extern const struct _mp_obj_float_t mp_const_float_nan_obj; +#endif #define mp_obj_is_float(o) mp_obj_is_type((o), &mp_type_float) mp_float_t mp_obj_float_get(mp_obj_t self_in); @@ -139,8 +149,18 @@ static inline bool mp_obj_is_immediate_obj(mp_const_obj_t o) { #if MICROPY_PY_BUILTINS_FLOAT #define mp_const_float_e MP_ROM_PTR(&mp_const_float_e_obj) #define mp_const_float_pi MP_ROM_PTR(&mp_const_float_pi_obj) +#if MICROPY_PY_MATH_CONSTANTS +#define mp_const_float_tau MP_ROM_PTR(&mp_const_float_tau_obj) +#define mp_const_float_inf MP_ROM_PTR(&mp_const_float_inf_obj) +#define mp_const_float_nan MP_ROM_PTR(&mp_const_float_nan_obj) +#endif extern const struct _mp_obj_float_t mp_const_float_e_obj; extern const struct _mp_obj_float_t mp_const_float_pi_obj; +#if MICROPY_PY_MATH_CONSTANTS +extern const struct _mp_obj_float_t mp_const_float_tau_obj; +extern const struct _mp_obj_float_t mp_const_float_inf_obj; +extern const struct _mp_obj_float_t mp_const_float_nan_obj; +#endif #define mp_obj_is_float(o) mp_obj_is_type((o), &mp_type_float) mp_float_t mp_obj_float_get(mp_obj_t self_in); @@ -162,6 +182,11 @@ static inline bool mp_obj_is_small_int(mp_const_obj_t o) { #if MICROPY_PY_BUILTINS_FLOAT #define mp_const_float_e MP_ROM_PTR((mp_obj_t)(((0x402df854 & ~3) | 2) + 0x80800000)) #define mp_const_float_pi MP_ROM_PTR((mp_obj_t)(((0x40490fdb & ~3) | 2) + 0x80800000)) +#if MICROPY_PY_MATH_CONSTANTS +#define mp_const_float_tau MP_ROM_PTR((mp_obj_t)(((0x40c90fdb & ~3) | 2) + 0x80800000)) +#define mp_const_float_inf MP_ROM_PTR((mp_obj_t)(((0x7f800000 & ~3) | 2) + 0x80800000)) +#define mp_const_float_nan MP_ROM_PTR((mp_obj_t)(((0xffc00000 & ~3) | 2) + 0x80800000)) +#endif static inline bool mp_obj_is_float(mp_const_obj_t o) { return (((mp_uint_t)(o)) & 3) == 2 && (((mp_uint_t)(o)) & 0xff800007) != 0x00000006; @@ -226,6 +251,11 @@ static inline bool mp_obj_is_immediate_obj(mp_const_obj_t o) { #define mp_const_float_e {((mp_obj_t)((uint64_t)0x4005bf0a8b145769 + 0x8004000000000000))} #define mp_const_float_pi {((mp_obj_t)((uint64_t)0x400921fb54442d18 + 0x8004000000000000))} +#if MICROPY_PY_MATH_CONSTANTS +#define mp_const_float_tau {((mp_obj_t)((uint64_t)0x401921fb54442d18 + 0x8004000000000000))} +#define mp_const_float_inf {((mp_obj_t)((uint64_t)0x7ff0000000000000 + 0x8004000000000000))} +#define mp_const_float_nan {((mp_obj_t)((uint64_t)0xfff8000000000000 + 0x8004000000000000))} +#endif static inline bool mp_obj_is_float(mp_const_obj_t o) { return ((uint64_t)(o) & 0xfffc000000000000) != 0; diff --git a/py/objfloat.c b/py/objfloat.c index 5194dba51e..8ff2fa16c4 100644 --- a/py/objfloat.c +++ b/py/objfloat.c @@ -54,6 +54,14 @@ typedef struct _mp_obj_float_t { const mp_obj_float_t mp_const_float_e_obj = {{&mp_type_float}, (mp_float_t)M_E}; const mp_obj_float_t mp_const_float_pi_obj = {{&mp_type_float}, (mp_float_t)M_PI}; +#if MICROPY_PY_MATH_CONSTANTS +#ifndef NAN +#error NAN macro is not defined +#endif +const mp_obj_float_t mp_const_float_tau_obj = {{&mp_type_float}, (mp_float_t)(2.0 * M_PI)}; +const mp_obj_float_t mp_const_float_inf_obj = {{&mp_type_float}, (mp_float_t)INFINITY}; +const mp_obj_float_t mp_const_float_nan_obj = {{&mp_type_float}, (mp_float_t)NAN}; +#endif #endif diff --git a/tests/float/math_constants.py b/tests/float/math_constants.py new file mode 100644 index 0000000000..2e4c321052 --- /dev/null +++ b/tests/float/math_constants.py @@ -0,0 +1,11 @@ +# Tests various constants of the math module. +try: + import math + from math import exp, cos +except ImportError: + print("SKIP") + raise SystemExit + +print(math.e == exp(1.0)) + +print(cos(math.pi)) diff --git a/tests/float/math_constants_extra.py b/tests/float/math_constants_extra.py new file mode 100644 index 0000000000..dea49aef5a --- /dev/null +++ b/tests/float/math_constants_extra.py @@ -0,0 +1,17 @@ +# Tests constants of the math module available only with MICROPY_PY_MATH_CONSTANTS. +try: + import math + from math import isnan + + math.tau +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit + +print(math.tau == 2.0 * math.pi) + +print(math.inf == float("inf")) +print(-math.inf == -float("inf")) + +print(isnan(math.nan)) +print(isnan(-math.nan)) From f3229590a9ce8286627f5763884328cdc970fa1d Mon Sep 17 00:00:00 2001 From: stijn Date: Wed, 12 Jan 2022 14:38:45 +0100 Subject: [PATCH 038/619] tools/ci: Test math constants with MICROPY_OBJ_REPR_D. --- tools/ci.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/ci.sh b/tools/ci.sh index d0c5948a8c..0506a00314 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -467,7 +467,7 @@ function ci_unix_coverage_32bit_run_native_mpy_tests { function ci_unix_nanbox_build { # Use Python 2 to check that it can run the build scripts - ci_unix_build_helper PYTHON=python2 VARIANT=nanbox + ci_unix_build_helper PYTHON=python2 VARIANT=nanbox CFLAGS_EXTRA="-DMICROPY_PY_MATH_CONSTANTS=1" ci_unix_build_ffi_lib_helper gcc -m32 } From aafd8859e9f0a2b5cc49e41a50530fecec79a205 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 23 Jan 2022 09:33:19 +1100 Subject: [PATCH 039/619] docs/differences: Update differences now that math.tau/inf/nan exist. Signed-off-by: Damien George --- docs/differences/python_35.rst | 2 +- docs/differences/python_36.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/differences/python_35.rst b/docs/differences/python_35.rst index 84c38c9cc1..2a4c119a45 100644 --- a/docs/differences/python_35.rst +++ b/docs/differences/python_35.rst @@ -97,7 +97,7 @@ Changes to built-in modules: +-----------------------------------------------------------------------------------------------------------+---------------+ | `math `_ | +-----------------------------------------------------------------------------------------------------------+---------------+ - | Two new constants have been added to the math module: *inf* and *nan*. | | + | Two new constants have been added to the math module: *inf* and *nan*. | Completed | +-----------------------------------------------------------------------------------------------------------+---------------+ | A new function *isclose()* provides a way to test for approximate equality. | | +-----------------------------------------------------------------------------------------------------------+---------------+ diff --git a/docs/differences/python_36.rst b/docs/differences/python_36.rst index 4ea8742243..dec8c5943f 100644 --- a/docs/differences/python_36.rst +++ b/docs/differences/python_36.rst @@ -121,7 +121,7 @@ Changes to built-in modules: +--------------------------------------------------------------------------------------------------------------+----------------+ | `math `_ | +--------------------------------------------------------------------------------------------------------------+----------------+ - | The new math.tau (τ) constant has been added | | + | The new math.tau (τ) constant has been added | Completed | +--------------------------------------------------------------------------------------------------------------+----------------+ | `os `_ | +--------------------------------------------------------------------------------------------------------------+----------------+ From 2e3a2785cdd5c63788cac7ec8b5d62105ecfe4aa Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Sat, 15 Jan 2022 15:19:59 +0100 Subject: [PATCH 040/619] extmod/modubinascii: Add newline keyword to b2a_base64 function. This allows encoding things (eg a Basic-Auth header for a request) without slicing the \n from the string, which allocates additional memory. Co-authored-by: David Lechner --- docs/library/binascii.rst | 4 ++-- extmod/modubinascii.c | 22 ++++++++++++++++------ tests/extmod/ubinascii_b2a_base64.py | 3 +++ 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/docs/library/binascii.rst b/docs/library/binascii.rst index fc621a8abe..6c02f019aa 100644 --- a/docs/library/binascii.rst +++ b/docs/library/binascii.rst @@ -31,8 +31,8 @@ Functions Conforms to `RFC 2045 s.6.8 `_. Returns a bytes object. -.. function:: b2a_base64(data) +.. function:: b2a_base64(data, *, newline=True) Encode binary data in base64 format, as in `RFC 3548 `_. Returns the encoded data - followed by a newline character, as a bytes object. + followed by a newline character if newline is true, as a bytes object. diff --git a/extmod/modubinascii.c b/extmod/modubinascii.c index fab7717c4e..b34f232c62 100644 --- a/extmod/modubinascii.c +++ b/extmod/modubinascii.c @@ -165,12 +165,20 @@ STATIC mp_obj_t mod_binascii_a2b_base64(mp_obj_t data) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_binascii_a2b_base64_obj, mod_binascii_a2b_base64); -STATIC mp_obj_t mod_binascii_b2a_base64(mp_obj_t data) { +STATIC mp_obj_t mod_binascii_b2a_base64(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_newline }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_newline, MP_ARG_BOOL, {.u_bool = true} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + uint8_t newline = args[ARG_newline].u_bool; mp_buffer_info_t bufinfo; - mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_READ); + mp_get_buffer_raise(pos_args[0], &bufinfo, MP_BUFFER_READ); vstr_t vstr; - vstr_init_len(&vstr, ((bufinfo.len != 0) ? (((bufinfo.len - 1) / 3) + 1) * 4 : 0) + 1); + vstr_init_len(&vstr, ((bufinfo.len != 0) ? (((bufinfo.len - 1) / 3) + 1) * 4 : 0) + newline); // First pass, we convert input buffer to numeric base 64 values byte *in = bufinfo.buf, *out = (byte *)vstr.buf; @@ -196,7 +204,7 @@ STATIC mp_obj_t mod_binascii_b2a_base64(mp_obj_t data) { // Second pass, we convert number base 64 values to actual base64 ascii encoding out = (byte *)vstr.buf; - for (mp_uint_t j = vstr.len - 1; j--;) { + for (mp_uint_t j = vstr.len - newline; j--;) { if (*out < 26) { *out += 'A'; } else if (*out < 52) { @@ -212,10 +220,12 @@ STATIC mp_obj_t mod_binascii_b2a_base64(mp_obj_t data) { } out++; } - *out = '\n'; + if (newline) { + *out = '\n'; + } return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } -STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_binascii_b2a_base64_obj, mod_binascii_b2a_base64); +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_binascii_b2a_base64_obj, 1, mod_binascii_b2a_base64); #if MICROPY_PY_UBINASCII_CRC32 #include "lib/uzlib/tinf.h" diff --git a/tests/extmod/ubinascii_b2a_base64.py b/tests/extmod/ubinascii_b2a_base64.py index 9c100f972b..3f92488969 100644 --- a/tests/extmod/ubinascii_b2a_base64.py +++ b/tests/extmod/ubinascii_b2a_base64.py @@ -20,3 +20,6 @@ print(binascii.b2a_base64(b"\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f")) print(binascii.b2a_base64(b"\x7f\x80\xff")) print(binascii.b2a_base64(b"1234ABCDabcd")) print(binascii.b2a_base64(b"\x00\x00>")) # convert into '+' + +print(binascii.b2a_base64(b"foobar", newline=True)) +print(binascii.b2a_base64(b"foobar", newline=False)) From c153bfd31137a4eebb8abc4ee41d3cf4a3a7f234 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 23 Jan 2022 10:23:30 +1100 Subject: [PATCH 041/619] docs/differences: Update python_36 now that b2a_base64 accepts newline. Signed-off-by: Damien George --- docs/differences/python_36.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/differences/python_36.rst b/docs/differences/python_36.rst index dec8c5943f..edf6bef023 100644 --- a/docs/differences/python_36.rst +++ b/docs/differences/python_36.rst @@ -78,7 +78,7 @@ Changes to built-in modules: +--------------------------------------------------------------------------------------------------------------+----------------+ | `binascii `_ | | +--------------------------------------------------------------------------------------------------------------+----------------+ - | The b2a_base64() function now accepts an optional newline keyword argument to control whether the newline | | + | The b2a_base64() function now accepts an optional newline keyword argument to control whether the newline | Completed | | character is appended to the return value | | +--------------------------------------------------------------------------------------------------------------+----------------+ | `cmath `_ | | From 30b6ce86be384ac04566d8f7bbf67d5906d6f307 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Mon, 13 Sep 2021 10:43:42 +1000 Subject: [PATCH 042/619] windows: Add micropython.schedule support. --- ports/windows/mpconfigport.h | 12 ++++++++++++ ports/windows/windows_mphal.c | 12 +++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/ports/windows/mpconfigport.h b/ports/windows/mpconfigport.h index 1aae5a7500..88746744ae 100644 --- a/ports/windows/mpconfigport.h +++ b/ports/windows/mpconfigport.h @@ -69,6 +69,9 @@ #define MICROPY_MODULE_WEAK_LINKS (1) #define MICROPY_MODULE_OVERRIDE_MAIN_IMPORT (1) #define MICROPY_CAN_OVERRIDE_BUILTINS (1) +#ifndef MICROPY_ENABLE_SCHEDULER +#define MICROPY_ENABLE_SCHEDULER (1) +#endif #define MICROPY_VFS_POSIX_FILE (1) #define MICROPY_PY_FUNCTION_ATTRS (1) #define MICROPY_PY_DESCRIPTORS (1) @@ -216,6 +219,15 @@ extern const struct _mp_obj_module_t mp_module_time; #define MICROPY_MPHALPORT_H "windows_mphal.h" +#if MICROPY_ENABLE_SCHEDULER +#define MICROPY_EVENT_POLL_HOOK \ + do { \ + extern void mp_handle_pending(bool); \ + mp_handle_pending(true); \ + mp_hal_delay_us(500); \ + } while (0); +#endif + // We need to provide a declaration/definition of alloca() #include diff --git a/ports/windows/windows_mphal.c b/ports/windows/windows_mphal.c index 7a3d881a34..5398e2da51 100644 --- a/ports/windows/windows_mphal.c +++ b/ports/windows/windows_mphal.c @@ -262,8 +262,14 @@ uint64_t mp_hal_time_ns(void) { return (uint64_t)tv.tv_sec * 1000000000ULL + (uint64_t)tv.tv_usec * 1000ULL; } -// TODO: POSIX et al. define usleep() as guaranteedly capable only of 1s sleep: -// "The useconds argument shall be less than one million." void mp_hal_delay_ms(mp_uint_t ms) { - usleep((ms) * 1000); + #ifdef MICROPY_EVENT_POLL_HOOK + mp_uint_t start = mp_hal_ticks_ms(); + while (mp_hal_ticks_ms() - start < ms) { + // MICROPY_EVENT_POLL_HOOK does mp_hal_delay_us(500) (i.e. usleep(500)). + MICROPY_EVENT_POLL_HOOK + } + #else + SleepEx(ms, TRUE); + #endif } From 79a3158de6dbd0b8f628fbf8cd5ffdeeea37bb6e Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 27 Jan 2022 16:36:08 +1100 Subject: [PATCH 043/619] stm32/pin: Change remaining uses of "af" to "alt". The keyword "af" has been deprecated for some time and "alt" should be used instead (but "af" still works). Signed-off-by: Damien George --- docs/library/pyb.Pin.rst | 10 +++++----- ports/stm32/pin.c | 10 +++++----- ports/stm32/timer.c | 6 +++--- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/library/pyb.Pin.rst b/docs/library/pyb.Pin.rst index 97dfbffbcf..23ede48ed3 100644 --- a/docs/library/pyb.Pin.rst +++ b/docs/library/pyb.Pin.rst @@ -237,17 +237,17 @@ pin X3. For the pyboard, x3_af would contain: [Pin.AF1_TIM2, Pin.AF2_TIM5, Pin.AF3_TIM9, Pin.AF7_USART2] -Normally, each peripheral would configure the af automatically, but sometimes -the same function is available on multiple pins, and having more control -is desired. +Normally, each peripheral would configure the alternate function automatically, +but sometimes the same function is available on multiple pins, and having more +control is desired. To configure X3 to expose TIM2_CH3, you could use:: - pin = pyb.Pin(pyb.Pin.board.X3, mode=pyb.Pin.AF_PP, af=pyb.Pin.AF1_TIM2) + pin = pyb.Pin(pyb.Pin.board.X3, mode=pyb.Pin.AF_PP, alt=pyb.Pin.AF1_TIM2) or:: - pin = pyb.Pin(pyb.Pin.board.X3, mode=pyb.Pin.AF_PP, af=1) + pin = pyb.Pin(pyb.Pin.board.X3, mode=pyb.Pin.AF_PP, alt=1) Methods ------- diff --git a/ports/stm32/pin.c b/ports/stm32/pin.c index ad153311e3..b490a09b7f 100644 --- a/ports/stm32/pin.c +++ b/ports/stm32/pin.c @@ -230,9 +230,9 @@ STATIC void pin_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t mp_uint_t af_idx = pin_get_af(self); const pin_af_obj_t *af_obj = pin_find_af_by_index(self, af_idx); if (af_obj == NULL) { - mp_printf(print, ", af=%d)", af_idx); + mp_printf(print, ", alt=%d)", af_idx); } else { - mp_printf(print, ", af=Pin.%q)", af_obj->name); + mp_printf(print, ", alt=Pin.%q)", af_obj->name); } } else { mp_print_str(print, ")"); @@ -328,7 +328,7 @@ STATIC mp_obj_t pin_debug(size_t n_args, const mp_obj_t *args) { STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pin_debug_fun_obj, 1, 2, pin_debug); STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(pin_debug_obj, MP_ROM_PTR(&pin_debug_fun_obj)); -// init(mode, pull=None, af=-1, *, value, alt) +// init(mode, pull=None, alt=-1, *, value, alt) STATIC mp_obj_t pin_obj_init_helper(const pin_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { { MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT }, @@ -625,9 +625,9 @@ const mp_obj_type_t pin_type = { /// is desired. /// /// To configure X3 to expose TIM2_CH3, you could use: -/// pin = pyb.Pin(pyb.Pin.board.X3, mode=pyb.Pin.AF_PP, af=pyb.Pin.AF1_TIM2) +/// pin = pyb.Pin(pyb.Pin.board.X3, mode=pyb.Pin.AF_PP, alt=pyb.Pin.AF1_TIM2) /// or: -/// pin = pyb.Pin(pyb.Pin.board.X3, mode=pyb.Pin.AF_PP, af=1) +/// pin = pyb.Pin(pyb.Pin.board.X3, mode=pyb.Pin.AF_PP, alt=1) /// \method __str__() /// Return a string describing the alternate function. diff --git a/ports/stm32/timer.c b/ports/stm32/timer.c index c23e7e02d4..5a968ec56d 100644 --- a/ports/stm32/timer.c +++ b/ports/stm32/timer.c @@ -1110,14 +1110,14 @@ STATIC mp_obj_t pyb_timer_channel(size_t n_args, const mp_obj_t *pos_args, mp_ma const pin_obj_t *pin = MP_OBJ_TO_PTR(pin_obj); const pin_af_obj_t *af = pin_find_af(pin, AF_FN_TIM, self->tim_id); if (af == NULL) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("Pin(%q) doesn't have an af for Timer(%d)"), pin->name, self->tim_id); + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("Pin(%q) doesn't have an alt for Timer(%d)"), pin->name, self->tim_id); } - // pin.init(mode=AF_PP, af=idx) + // pin.init(mode=AF_PP, alt=idx) const mp_obj_t args2[6] = { MP_OBJ_FROM_PTR(&pin_init_obj), pin_obj, MP_OBJ_NEW_QSTR(MP_QSTR_mode), MP_OBJ_NEW_SMALL_INT(GPIO_MODE_AF_PP), - MP_OBJ_NEW_QSTR(MP_QSTR_af), MP_OBJ_NEW_SMALL_INT(af->idx) + MP_OBJ_NEW_QSTR(MP_QSTR_alt), MP_OBJ_NEW_SMALL_INT(af->idx) }; mp_call_method_n_kw(0, 2, args2); } From e1a84a0c6f7ddc0335b4a78a2ea3bcc5f2176965 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 27 Jan 2022 16:38:14 +1100 Subject: [PATCH 044/619] esp32/partitions: Increase size of app-part from 0x180000 to 0x1F0000. To fill out all of the available flash up to the start of the filesystem partition. Signed-off-by: Damien George --- ports/esp32/partitions-16MiB.csv | 2 +- ports/esp32/partitions.csv | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/esp32/partitions-16MiB.csv b/ports/esp32/partitions-16MiB.csv index 20d06bad47..ee3e8c7269 100644 --- a/ports/esp32/partitions-16MiB.csv +++ b/ports/esp32/partitions-16MiB.csv @@ -3,5 +3,5 @@ # Name, Type, SubType, Offset, Size, Flags nvs, data, nvs, 0x9000, 0x6000, phy_init, data, phy, 0xf000, 0x1000, -factory, app, factory, 0x10000, 0x180000, +factory, app, factory, 0x10000, 0x1F0000, vfs, data, fat, 0x200000, 0xD59F80, diff --git a/ports/esp32/partitions.csv b/ports/esp32/partitions.csv index d304dccc53..53f0f16744 100644 --- a/ports/esp32/partitions.csv +++ b/ports/esp32/partitions.csv @@ -3,5 +3,5 @@ # Name, Type, SubType, Offset, Size, Flags nvs, data, nvs, 0x9000, 0x6000, phy_init, data, phy, 0xf000, 0x1000, -factory, app, factory, 0x10000, 0x180000, +factory, app, factory, 0x10000, 0x1F0000, vfs, data, fat, 0x200000, 0x200000, From 2dd3d88409937908da6ee05d2d391453df9a3c8a Mon Sep 17 00:00:00 2001 From: robert-hh Date: Thu, 16 Dec 2021 22:10:50 +0100 Subject: [PATCH 045/619] mimxrt: Support gaps in the SPI an I2C device numbers. That allows also to use e.g. SPI1 and SPI2 instead SPI0 and SPI1. --- ports/mimxrt/machine_i2c.c | 2 +- ports/mimxrt/machine_spi.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/mimxrt/machine_i2c.c b/ports/mimxrt/machine_i2c.c index 618d0a2581..58dd824490 100644 --- a/ports/mimxrt/machine_i2c.c +++ b/ports/mimxrt/machine_i2c.c @@ -109,7 +109,7 @@ mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n // Get I2C bus. int i2c_id = mp_obj_get_int(args[ARG_id].u_obj); - if (i2c_id < 0 || i2c_id >= MICROPY_HW_I2C_NUM) { + if (i2c_id < 0 || i2c_id >= MICROPY_HW_I2C_NUM || i2c_index_table[i2c_id] == 0) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("I2C(%d) doesn't exist"), i2c_id); } diff --git a/ports/mimxrt/machine_spi.c b/ports/mimxrt/machine_spi.c index 41843b06d9..c7ee226bfc 100644 --- a/ports/mimxrt/machine_spi.c +++ b/ports/mimxrt/machine_spi.c @@ -137,7 +137,7 @@ mp_obj_t machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n // Get the SPI bus id. int spi_id = mp_obj_get_int(args[ARG_id].u_obj); - if (spi_id < 0 || spi_id >= MP_ARRAY_SIZE(spi_index_table)) { + if (spi_id < 0 || spi_id >= MP_ARRAY_SIZE(spi_index_table) || spi_index_table[spi_id] == 0) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("SPI(%d) doesn't exist"), spi_id); } From 84b76e4fbff86abb1abd340dd25d38c73d619132 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Fri, 17 Dec 2021 16:29:26 +0100 Subject: [PATCH 046/619] mimxrt: Allow for board-specific flash driver files. If in a board's mkconfigboard.mk the following symbol is set: MICROPY_HW_BOARD_FLASH_FILES = 1 then the files: ($BOARD)_flexspi_flash_config.h and qspi_nor_flash_config.c and/or qspi_hyper_flash_config.c are expected in the board directory. Otherwise the common files from the hal directory are used. --- ports/mimxrt/Makefile | 22 ++++++++++++++++++++-- ports/mimxrt/hal/flexspi_hyper_flash.h | 2 +- ports/mimxrt/hal/flexspi_nor_flash.h | 2 +- ports/mimxrt/hal/qspi_hyper_flash_config.c | 2 +- ports/mimxrt/hal/qspi_nor_flash_config.c | 2 +- 5 files changed, 24 insertions(+), 6 deletions(-) diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index 32b1145230..2881c7c767 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -95,6 +95,8 @@ ifeq ($(MICROPY_HW_FLASH_TYPE), qspi_nor) CFLAGS += -DBOARD_FLASH_OPS_HEADER_H=\"hal/flexspi_nor_flash.h\" else ifeq ($(MICROPY_HW_FLASH_TYPE), hyperflash) CFLAGS += -DBOARD_FLASH_OPS_HEADER_H=\"hal/flexspi_hyper_flash.h\" +else +$(error Error: Unknown board flash type $(MICROPY_HW_FLASH_TYPE)) endif ifeq ($(MICROPY_PY_MACHINE_SDCARD),1) @@ -272,6 +274,22 @@ SRC_C += \ $(SRC_HAL_IMX_C) \ $(SRC_ETH_C) \ +ifeq ($(MICROPY_HW_BOARD_FLASH_FILES), 1) +CFLAGS += -DBOARD_FLASH_CONFIG_HEADER_H=\"$(BOARD)_flexspi_flash_config.h\" + +ifeq ($(MICROPY_HW_FLASH_TYPE), qspi_nor) +SRC_C += \ + hal/flexspi_nor_flash.c \ + $(BOARD_DIR)/qspi_nor_flash_config.c +else ifeq ($(MICROPY_HW_FLASH_TYPE), hyperflash) +SRC_C += \ + hal/flexspi_hyper_flash.c \ + $(BOARD_DIR)/qspi_hyper_flash_config.c +endif + +else + +CFLAGS += -DBOARD_FLASH_CONFIG_HEADER_H=\"hal/flexspi_flash_config.h\" ifeq ($(MICROPY_HW_FLASH_TYPE), qspi_nor) SRC_C += \ @@ -281,8 +299,8 @@ else ifeq ($(MICROPY_HW_FLASH_TYPE), hyperflash) SRC_C += \ hal/flexspi_hyper_flash.c \ hal/qspi_hyper_flash_config.c -else -$(error Error: Unknown board flash type $(MICROPY_HW_FLASH_TYPE)) +endif + endif ifeq ($(MICROPY_FLOAT_IMPL),double) diff --git a/ports/mimxrt/hal/flexspi_hyper_flash.h b/ports/mimxrt/hal/flexspi_hyper_flash.h index f340aec10a..dbd028fd6f 100644 --- a/ports/mimxrt/hal/flexspi_hyper_flash.h +++ b/ports/mimxrt/hal/flexspi_hyper_flash.h @@ -28,7 +28,7 @@ #include "fsl_flexspi.h" #include "mpconfigboard.h" -#include "flexspi_flash_config.h" +#include BOARD_FLASH_CONFIG_HEADER_H // Defined in boards flash_config.c extern flexspi_nor_config_t qspiflash_config; diff --git a/ports/mimxrt/hal/flexspi_nor_flash.h b/ports/mimxrt/hal/flexspi_nor_flash.h index c2c30876c7..f8c31488a9 100644 --- a/ports/mimxrt/hal/flexspi_nor_flash.h +++ b/ports/mimxrt/hal/flexspi_nor_flash.h @@ -28,7 +28,7 @@ #include "fsl_flexspi.h" #include "mpconfigboard.h" -#include "flexspi_flash_config.h" +#include BOARD_FLASH_CONFIG_HEADER_H // Defined in boards flash_config.c extern flexspi_nor_config_t qspiflash_config; diff --git a/ports/mimxrt/hal/qspi_hyper_flash_config.c b/ports/mimxrt/hal/qspi_hyper_flash_config.c index 17a952b689..f5ffbe8413 100644 --- a/ports/mimxrt/hal/qspi_hyper_flash_config.c +++ b/ports/mimxrt/hal/qspi_hyper_flash_config.c @@ -5,7 +5,7 @@ * SPDX-License-Identifier: BSD-3-Clause */ -#include "flexspi_flash_config.h" +#include BOARD_FLASH_CONFIG_HEADER_H /* Component ID definition, used by tools. */ #ifndef FSL_COMPONENT_ID diff --git a/ports/mimxrt/hal/qspi_nor_flash_config.c b/ports/mimxrt/hal/qspi_nor_flash_config.c index f42730ad54..469a584b87 100644 --- a/ports/mimxrt/hal/qspi_nor_flash_config.c +++ b/ports/mimxrt/hal/qspi_nor_flash_config.c @@ -7,7 +7,7 @@ // Based on tinyusb/hw/bsp/teensy_40/evkmimxrt1010_flexspi_nor_config.c -#include "flexspi_flash_config.h" +#include BOARD_FLASH_CONFIG_HEADER_H /* Component ID definition, used by tools. */ #ifndef FSL_COMPONENT_ID From b8a0358c34bf4786b416a18f1d25ac2ab23d1704 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Mon, 27 Dec 2021 13:46:56 +0100 Subject: [PATCH 047/619] mimxrt: Compensate for a bug in the fsl_lpspi.c file. This library file has a bug, in that TransferBlocking returns before the transfer has finished. That is a problem if a write follows immediately a read. --- ports/mimxrt/machine_spi.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ports/mimxrt/machine_spi.c b/ports/mimxrt/machine_spi.c index c7ee226bfc..db2fab4ee6 100644 --- a/ports/mimxrt/machine_spi.c +++ b/ports/mimxrt/machine_spi.c @@ -230,7 +230,7 @@ void LPSPI_EDMAMasterCallback(LPSPI_Type *base, lpspi_master_edma_handle_t *hand STATIC void machine_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest) { machine_spi_obj_t *self = (machine_spi_obj_t *)self_in; // Use DMA for large transfers if channels are available - const size_t dma_min_size_threshold = 16; // That's the FIFO size + const size_t dma_min_size_threshold = FSL_FEATURE_LPSPI_FIFO_SIZEn(0); // The Macro argument is ignored int chan_tx = -1; int chan_rx = -1; @@ -302,6 +302,10 @@ STATIC void machine_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8 } if (!use_dma) { + // Wait until a previous Transfer is finished + while (LPSPI_GetTxFifoCount(self->spi_inst) > 0) { + MICROPY_EVENT_POLL_HOOK + } // Reconfigure the TCR, required after switch between DMA vs. non-DMA LPSPI_Enable(self->spi_inst, false); // Disable first before new settings are applied self->spi_inst->TCR = LPSPI_TCR_CPOL(self->master_config->cpol) | LPSPI_TCR_CPHA(self->master_config->cpha) | From 5bda84aed59be8ce75308abcb9562b6d015c69ef Mon Sep 17 00:00:00 2001 From: robert-hh Date: Wed, 29 Dec 2021 22:03:33 +0100 Subject: [PATCH 048/619] mimxrt: Remove two files from the Seeed Arch Mix directory. These were leftovers from a previous refactoring in the Seeed Arch Mix directory. --- .../SEEED_ARCH_MIX_flexspi_nor_config.h | 258 ------------------ .../SEEED_ARCH_MIX/qspi_nor_flash_config.c | 136 --------- 2 files changed, 394 deletions(-) delete mode 100644 ports/mimxrt/boards/SEEED_ARCH_MIX/SEEED_ARCH_MIX_flexspi_nor_config.h delete mode 100644 ports/mimxrt/boards/SEEED_ARCH_MIX/qspi_nor_flash_config.c diff --git a/ports/mimxrt/boards/SEEED_ARCH_MIX/SEEED_ARCH_MIX_flexspi_nor_config.h b/ports/mimxrt/boards/SEEED_ARCH_MIX/SEEED_ARCH_MIX_flexspi_nor_config.h deleted file mode 100644 index 33698a34d2..0000000000 --- a/ports/mimxrt/boards/SEEED_ARCH_MIX/SEEED_ARCH_MIX_flexspi_nor_config.h +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright 2019 NXP. - * All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -// Based on tinyusb/hw/bsp/teensy_40/evkmimxrt1020_flexspi_nor_config.h - -#ifndef __SEEED_ARCH_MIX_FLEXSPI_NOR_CONFIG__ -#define __SEEED_ARCH_MIX_FLEXSPI_NOR_CONFIG__ - -#include -#include -#include "fsl_common.h" - -/*! @name Driver version */ -/*@{*/ -/*! @brief XIP_BOARD driver version 2.0.0. */ -#define FSL_XIP_BOARD_DRIVER_VERSION (MAKE_VERSION(2, 0, 0)) -/*@}*/ - -/* FLEXSPI memory config block related defintions */ -#define FLEXSPI_CFG_BLK_TAG (0x42464346UL) // ascii "FCFB" Big Endian -#define FLEXSPI_CFG_BLK_VERSION (0x56010400UL) // V1.4.0 -#define FLEXSPI_CFG_BLK_SIZE (512) - -/* FLEXSPI Feature related definitions */ -#define FLEXSPI_FEATURE_HAS_PARALLEL_MODE 1 - -/* Lookup table related defintions */ -#define CMD_INDEX_READ 0 -#define CMD_INDEX_READSTATUS 1 -#define CMD_INDEX_WRITEENABLE 2 -#define CMD_INDEX_WRITE 4 - -#define CMD_LUT_SEQ_IDX_READ 0 -#define CMD_LUT_SEQ_IDX_READSTATUS 1 -#define CMD_LUT_SEQ_IDX_WRITEENABLE 3 -#define CMD_LUT_SEQ_IDX_WRITE 9 - -#define CMD_SDR 0x01 -#define CMD_DDR 0x21 -#define RADDR_SDR 0x02 -#define RADDR_DDR 0x22 -#define CADDR_SDR 0x03 -#define CADDR_DDR 0x23 -#define MODE1_SDR 0x04 -#define MODE1_DDR 0x24 -#define MODE2_SDR 0x05 -#define MODE2_DDR 0x25 -#define MODE4_SDR 0x06 -#define MODE4_DDR 0x26 -#define MODE8_SDR 0x07 -#define MODE8_DDR 0x27 -#define WRITE_SDR 0x08 -#define WRITE_DDR 0x28 -#define READ_SDR 0x09 -#define READ_DDR 0x29 -#define LEARN_SDR 0x0A -#define LEARN_DDR 0x2A -#define DATSZ_SDR 0x0B -#define DATSZ_DDR 0x2B -#define DUMMY_SDR 0x0C -#define DUMMY_DDR 0x2C -#define DUMMY_RWDS_SDR 0x0D -#define DUMMY_RWDS_DDR 0x2D -#define JMP_ON_CS 0x1F -#define STOP 0 - -#define FLEXSPI_1PAD 0 -#define FLEXSPI_2PAD 1 -#define FLEXSPI_4PAD 2 -#define FLEXSPI_8PAD 3 - -#define FLEXSPI_LUT_SEQ(cmd0, pad0, op0, cmd1, pad1, op1) \ - (FLEXSPI_LUT_OPERAND0(op0) | FLEXSPI_LUT_NUM_PADS0(pad0) | FLEXSPI_LUT_OPCODE0(cmd0) | FLEXSPI_LUT_OPERAND1(op1) | \ - FLEXSPI_LUT_NUM_PADS1(pad1) | FLEXSPI_LUT_OPCODE1(cmd1)) - -// !@brief Definitions for FlexSPI Serial Clock Frequency -typedef enum _FlexSpiSerialClockFreq -{ - kFlexSpiSerialClk_30MHz = 1, - kFlexSpiSerialClk_50MHz = 2, - kFlexSpiSerialClk_60MHz = 3, - kFlexSpiSerialClk_75MHz = 4, - kFlexSpiSerialClk_80MHz = 5, - kFlexSpiSerialClk_100MHz = 6, - kFlexSpiSerialClk_133MHz = 7, - kFlexSpiSerialClk_166MHz = 8, - kFlexSpiSerialClk_200MHz = 9, -} flexspi_serial_clk_freq_t; - -// !@brief FlexSPI clock configuration type -enum -{ - kFlexSpiClk_SDR, // !< Clock configure for SDR mode - kFlexSpiClk_DDR, // !< Clock configurat for DDR mode -}; - -// !@brief FlexSPI Read Sample Clock Source definition -typedef enum _FlashReadSampleClkSource -{ - kFlexSPIReadSampleClk_LoopbackInternally = 0, - kFlexSPIReadSampleClk_LoopbackFromDqsPad = 1, - kFlexSPIReadSampleClk_LoopbackFromSckPad = 2, - kFlexSPIReadSampleClk_ExternalInputFromDqsPad = 3, -} flexspi_read_sample_clk_t; - -// !@brief Misc feature bit definitions -enum -{ - kFlexSpiMiscOffset_DiffClkEnable = 0, // !< Bit for Differential clock enable - kFlexSpiMiscOffset_Ck2Enable = 1, // !< Bit for CK2 enable - kFlexSpiMiscOffset_ParallelEnable = 2, // !< Bit for Parallel mode enable - kFlexSpiMiscOffset_WordAddressableEnable = 3, // !< Bit for Word Addressable enable - kFlexSpiMiscOffset_SafeConfigFreqEnable = 4, // !< Bit for Safe Configuration Frequency enable - kFlexSpiMiscOffset_PadSettingOverrideEnable = 5, // !< Bit for Pad setting override enable - kFlexSpiMiscOffset_DdrModeEnable = 6, // !< Bit for DDR clock confiuration indication. -}; - -// !@brief Flash Type Definition -enum -{ - kFlexSpiDeviceType_SerialNOR = 1, // !< Flash devices are Serial NOR - kFlexSpiDeviceType_SerialNAND = 2, // !< Flash devices are Serial NAND - kFlexSpiDeviceType_SerialRAM = 3, // !< Flash devices are Serial RAM/HyperFLASH - kFlexSpiDeviceType_MCP_NOR_NAND = 0x12, // !< Flash device is MCP device, A1 is Serial NOR, A2 is Serial NAND - kFlexSpiDeviceType_MCP_NOR_RAM = 0x13, // !< Flash deivce is MCP device, A1 is Serial NOR, A2 is Serial RAMs -}; - -// !@brief Flash Pad Definitions -enum -{ - kSerialFlash_1Pad = 1, - kSerialFlash_2Pads = 2, - kSerialFlash_4Pads = 4, - kSerialFlash_8Pads = 8, -}; - -// !@brief FlexSPI LUT Sequence structure -typedef struct _lut_sequence -{ - uint8_t seqNum; // !< Sequence Number, valid number: 1-16 - uint8_t seqId; // !< Sequence Index, valid number: 0-15 - uint16_t reserved; -} flexspi_lut_seq_t; - -// !@brief Flash Configuration Command Type -enum -{ - kDeviceConfigCmdType_Generic, // !< Generic command, for example: configure dummy cycles, drive strength, etc - kDeviceConfigCmdType_QuadEnable, // !< Quad Enable command - kDeviceConfigCmdType_Spi2Xpi, // !< Switch from SPI to DPI/QPI/OPI mode - kDeviceConfigCmdType_Xpi2Spi, // !< Switch from DPI/QPI/OPI to SPI mode - kDeviceConfigCmdType_Spi2NoCmd, // !< Switch to 0-4-4/0-8-8 mode - kDeviceConfigCmdType_Reset, // !< Reset device command -}; - -// !@brief FlexSPI Memory Configuration Block -typedef struct _FlexSPIConfig -{ - uint32_t tag; // !< [0x000-0x003] Tag, fixed value 0x42464346UL - uint32_t version; // !< [0x004-0x007] Version,[31:24] -'V', [23:16] - Major, [15:8] - Minor, [7:0] - bugfix - uint32_t reserved0; // !< [0x008-0x00b] Reserved for future use - uint8_t readSampleClkSrc; // !< [0x00c-0x00c] Read Sample Clock Source, valid value: 0/1/3 - uint8_t csHoldTime; // !< [0x00d-0x00d] CS hold time, default value: 3 - uint8_t csSetupTime; // !< [0x00e-0x00e] CS setup time, default value: 3 - uint8_t columnAddressWidth; // !< [0x00f-0x00f] Column Address with, for HyperBus protocol, it is fixed to 3, For - // ! Serial NAND, need to refer to datasheet - uint8_t deviceModeCfgEnable; // !< [0x010-0x010] Device Mode Configure enable flag, 1 - Enable, 0 - Disable - uint8_t deviceModeType; // !< [0x011-0x011] Specify the configuration command type:Quad Enable, DPI/QPI/OPI switch, - // ! Generic configuration, etc. - uint16_t waitTimeCfgCommands; // !< [0x012-0x013] Wait time for all configuration commands, unit: 100us, Used for - // ! DPI/QPI/OPI switch or reset command - flexspi_lut_seq_t deviceModeSeq; // !< [0x014-0x017] Device mode sequence info, [7:0] - LUT sequence id, [15:8] - LUt - // ! sequence number, [31:16] Reserved - uint32_t deviceModeArg; // !< [0x018-0x01b] Argument/Parameter for device configuration - uint8_t configCmdEnable; // !< [0x01c-0x01c] Configure command Enable Flag, 1 - Enable, 0 - Disable - uint8_t configModeType[3]; // !< [0x01d-0x01f] Configure Mode Type, similar as deviceModeTpe - flexspi_lut_seq_t - configCmdSeqs[3]; // !< [0x020-0x02b] Sequence info for Device Configuration command, similar as deviceModeSeq - uint32_t reserved1; // !< [0x02c-0x02f] Reserved for future use - uint32_t configCmdArgs[3]; // !< [0x030-0x03b] Arguments/Parameters for device Configuration commands - uint32_t reserved2; // !< [0x03c-0x03f] Reserved for future use - uint32_t controllerMiscOption; // !< [0x040-0x043] Controller Misc Options, see Misc feature bit definitions for more - // ! details - uint8_t deviceType; // !< [0x044-0x044] Device Type: See Flash Type Definition for more details - uint8_t sflashPadType; // !< [0x045-0x045] Serial Flash Pad Type: 1 - Single, 2 - Dual, 4 - Quad, 8 - Octal - uint8_t serialClkFreq; // !< [0x046-0x046] Serial Flash Frequencey, device specific definitions, See System Boot - // ! Chapter for more details - uint8_t lutCustomSeqEnable; // !< [0x047-0x047] LUT customization Enable, it is required if the program/erase cannot - // ! be done using 1 LUT sequence, currently, only applicable to HyperFLASH - uint32_t reserved3[2]; // !< [0x048-0x04f] Reserved for future use - uint32_t sflashA1Size; // !< [0x050-0x053] Size of Flash connected to A1 - uint32_t sflashA2Size; // !< [0x054-0x057] Size of Flash connected to A2 - uint32_t sflashB1Size; // !< [0x058-0x05b] Size of Flash connected to B1 - uint32_t sflashB2Size; // !< [0x05c-0x05f] Size of Flash connected to B2 - uint32_t csPadSettingOverride; // !< [0x060-0x063] CS pad setting override value - uint32_t sclkPadSettingOverride; // !< [0x064-0x067] SCK pad setting override value - uint32_t dataPadSettingOverride; // !< [0x068-0x06b] data pad setting override value - uint32_t dqsPadSettingOverride; // !< [0x06c-0x06f] DQS pad setting override value - uint32_t timeoutInMs; // !< [0x070-0x073] Timeout threshold for read status command - uint32_t commandInterval; // !< [0x074-0x077] CS deselect interval between two commands - uint16_t dataValidTime[2]; // !< [0x078-0x07b] CLK edge to data valid time for PORT A and PORT B, in terms of 0.1ns - uint16_t busyOffset; // !< [0x07c-0x07d] Busy offset, valid value: 0-31 - uint16_t busyBitPolarity; // !< [0x07e-0x07f] Busy flag polarity, 0 - busy flag is 1 when flash device is busy, 1 - - // ! busy flag is 0 when flash device is busy - uint32_t lookupTable[64]; // !< [0x080-0x17f] Lookup table holds Flash command sequences - flexspi_lut_seq_t lutCustomSeq[12]; // !< [0x180-0x1af] Customizable LUT Sequences - uint32_t reserved4[4]; // !< [0x1b0-0x1bf] Reserved for future use -} flexspi_mem_config_t; - -/* */ -#define NOR_CMD_LUT_SEQ_IDX_READ_NORMAL 0 -#define NOR_CMD_LUT_SEQ_IDX_READSTATUSREG 1 -#define NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD 2 -#define NOR_CMD_LUT_SEQ_IDX_WRITEENABLE 3 -#define NOR_CMD_LUT_SEQ_IDX_READSTATUS_XPI 4 -#define NOR_CMD_LUT_SEQ_IDX_ERASESECTOR 5 -#define NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG 6 -#define NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD 7 -#define NOR_CMD_LUT_SEQ_IDX_READID 8 -#define NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM 9 -#define NOR_CMD_LUT_SEQ_IDX_ENTERQPI 10 -#define NOR_CMD_LUT_SEQ_IDX_CHIPERASE 11 -#define NOR_CMD_LUT_SEQ_IDX_EXITQPI 12 - -/* - * Serial NOR configuration block - */ -typedef struct _flexspi_nor_config -{ - flexspi_mem_config_t memConfig; // !< Common memory configuration info via FlexSPI - uint32_t pageSize; // !< Page size of Serial NOR - uint32_t sectorSize; // !< Sector size of Serial NOR - uint8_t ipcmdSerialClkFreq; // !< Clock frequency for IP command - uint8_t isUniformBlockSize; // !< Sector/Block size is the same - uint8_t reserved0[2]; // !< Reserved for future use - uint8_t serialNorType; // !< Serial NOR Flash type: 0/1/2/3 - uint8_t needExitNoCmdMode; // !< Need to exit NoCmd mode before other IP command - uint8_t halfClkForNonReadCmd; // !< Half the Serial Clock for non-read command: true/false - uint8_t needRestoreNoCmdMode; // !< Need to Restore NoCmd mode after IP commmand execution - uint32_t blockSize; // !< Block size - uint32_t reserve2[11]; // !< Reserved for future use -} flexspi_nor_config_t; - -#define FLASH_BUSY_STATUS_POL 0 -#define FLASH_BUSY_STATUS_OFFSET 0 - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __cplusplus -} -#endif -#endif /* __SEEED_ARCH_MIX_FLEXSPI_NOR_CONFIG__ */ diff --git a/ports/mimxrt/boards/SEEED_ARCH_MIX/qspi_nor_flash_config.c b/ports/mimxrt/boards/SEEED_ARCH_MIX/qspi_nor_flash_config.c deleted file mode 100644 index 7b2584d3de..0000000000 --- a/ports/mimxrt/boards/SEEED_ARCH_MIX/qspi_nor_flash_config.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2019 NXP. - * All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -// Based on tinyusb/hw/bsp/teensy_40/evkmimxrt1010_flexspi_nor_config.c - -#include BOARD_FLASH_CONFIG_HEADER_H - -/* Component ID definition, used by tools. */ -#ifndef FSL_COMPONENT_ID -#define FSL_COMPONENT_ID "platform.drivers.xip_board" -#endif - -/******************************************************************************* - * Code - ******************************************************************************/ -#if defined(XIP_BOOT_HEADER_ENABLE) && (XIP_BOOT_HEADER_ENABLE == 1) -#if defined(__ARMCC_VERSION) || defined(__GNUC__) -__attribute__((section(".boot_hdr.conf"))) -#elif defined(__ICCARM__) -#pragma location = ".boot_hdr.conf" -#endif - -const flexspi_nor_config_t qspiflash_config = { - .memConfig = - { - .tag = FLEXSPI_CFG_BLK_TAG, - .version = FLEXSPI_CFG_BLK_VERSION, - .readSampleClkSrc = kFlexSPIReadSampleClk_LoopbackFromDqsPad, - .csHoldTime = 3u, - .csSetupTime = 3u, - .busyOffset = FLASH_BUSY_STATUS_OFFSET, // Status bit 0 indicates busy. - .busyBitPolarity = FLASH_BUSY_STATUS_POL, // Busy when the bit is 1. - .deviceModeCfgEnable = 1u, - .deviceModeType = kDeviceConfigCmdType_QuadEnable, - .deviceModeSeq = { - .seqId = 4u, - .seqNum = 1u, - }, - .deviceModeArg = 0x40, - // Enable DDR mode, Wordaddassable, Safe configuration, Differential clock - .deviceType = kFlexSpiDeviceType_SerialNOR, - .sflashPadType = kSerialFlash_4Pads, - .serialClkFreq = kFlexSpiSerialClk_100MHz, - .sflashA1Size = BOARD_FLASH_SIZE, - .lookupTable = - { - // 0 Read LUTs 0 -> 0 - FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0xEB, RADDR_SDR, FLEXSPI_4PAD, 0x18), - FLEXSPI_LUT_SEQ(DUMMY_SDR, FLEXSPI_4PAD, 0x06, READ_SDR, FLEXSPI_4PAD, 0x04), - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - - // 1 Read status register -> 1 - FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x05, READ_SDR, FLEXSPI_1PAD, 0x01), - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - - // 2 Fast read quad mode - SDR - FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x6B, RADDR_SDR, FLEXSPI_1PAD, 0x18), - FLEXSPI_LUT_SEQ(DUMMY_SDR, FLEXSPI_4PAD, 0x08, READ_SDR, FLEXSPI_4PAD, 0x04), - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - - // 3 Write Enable -> 3 - FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x06, STOP, FLEXSPI_1PAD, 0), - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - - // 4 Read extend parameters - FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x81, READ_SDR, FLEXSPI_1PAD, 0x04), - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - - // 5 Erase Sector -> 5 - FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x20, RADDR_SDR, FLEXSPI_1PAD, 24), - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - - // 6 Write Status Reg - FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x01, WRITE_SDR, FLEXSPI_1PAD, 0x04), - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - - // 7 Page Program - quad mode (-> 9) - FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x32, RADDR_SDR, FLEXSPI_1PAD, 0x18), - FLEXSPI_LUT_SEQ(WRITE_SDR, FLEXSPI_4PAD, 0x04, STOP, FLEXSPI_1PAD, 0), - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - - // 8 Read ID - FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x90, DUMMY_SDR, FLEXSPI_1PAD, 24), - FLEXSPI_LUT_SEQ(READ_SDR, FLEXSPI_1PAD, 0x00, 0, 0, 0), - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - - // 9 Page Program - single mode -> 9 - FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x02, RADDR_SDR, FLEXSPI_1PAD, 24), - FLEXSPI_LUT_SEQ(WRITE_SDR, FLEXSPI_1PAD, 0, 0, 0, 0), - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - - // 10 Enter QPI mode - FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x35, STOP, FLEXSPI_1PAD, 0), - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - - // 11 Erase Chip - FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x60, STOP, FLEXSPI_1PAD, 0), - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - - // 12 Exit QPI mode - FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_4PAD, 0xF5, STOP, FLEXSPI_1PAD, 0), - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler - }, - }, - .pageSize = 256u, - .sectorSize = 4u * 1024u, - .blockSize = 256u * 1024u, - .isUniformBlockSize = false, - // .ipcmdSerialClkFreq = kFlexSpiSerialClk_30MHz, -}; -#endif /* XIP_BOOT_HEADER_ENABLE */ From 1dc366f90194789d9be1bd44dbca2376c75421dc Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sun, 2 Jan 2022 13:21:06 +0100 Subject: [PATCH 049/619] mimxrt: Replace Pin-config constants by a function call. The Pin config setting by IOMUXC_SetPinConfig() is supplied by a bit pattern. That pattern is specific for a MCU family. In preparation for supporting the MIMXRT117x family, the constant bit pattern is replaced by a function call, such that the bit pattern is created at a single place. The code for this functions was taken from machine_pin.c. Note: A working port for the MIMXRT1176 exists already. --- ports/mimxrt/eth.c | 6 ++- ports/mimxrt/machine_i2c.c | 7 ++-- ports/mimxrt/machine_pin.c | 34 +--------------- ports/mimxrt/machine_pwm.c | 4 +- ports/mimxrt/machine_spi.c | 8 ++-- ports/mimxrt/machine_uart.c | 6 ++- ports/mimxrt/pin.c | 80 +++++++++++++++++++++++++++++++++++++ ports/mimxrt/pin.h | 2 + ports/mimxrt/sdcard.c | 26 +++++------- 9 files changed, 111 insertions(+), 62 deletions(-) diff --git a/ports/mimxrt/eth.c b/ports/mimxrt/eth.c index 1a8210cb11..5a5138443c 100644 --- a/ports/mimxrt/eth.c +++ b/ports/mimxrt/eth.c @@ -194,7 +194,8 @@ void eth_init(eth_t *self, int mac_idx, const phy_operations_t *phy_ops, int phy const machine_pin_af_obj_t *af_obj = pin_find_af(reset_pin, PIN_AF_MODE_ALT5); IOMUXC_SetPinMux(reset_pin->muxRegister, af_obj->af_mode, 0, 0, reset_pin->configRegister, 0U); - IOMUXC_SetPinConfig(reset_pin->muxRegister, af_obj->af_mode, 0, 0, reset_pin->configRegister, 0xB0A9U); + IOMUXC_SetPinConfig(reset_pin->muxRegister, af_obj->af_mode, 0, 0, reset_pin->configRegister, + pin_generate_config(PIN_PULL_DISABLED, PIN_MODE_OUT, PIN_DRIVE_POWER_5, reset_pin->configRegister)); GPIO_PinInit(reset_pin->gpio, reset_pin->pin, &gpio_config); #endif @@ -204,7 +205,8 @@ void eth_init(eth_t *self, int mac_idx, const phy_operations_t *phy_ops, int phy af_obj = pin_find_af(int_pin, PIN_AF_MODE_ALT5); IOMUXC_SetPinMux(int_pin->muxRegister, af_obj->af_mode, 0, 0, int_pin->configRegister, 0U); - IOMUXC_SetPinConfig(int_pin->muxRegister, af_obj->af_mode, 0, 0, int_pin->configRegister, 0xB0A9U); + IOMUXC_SetPinConfig(int_pin->muxRegister, af_obj->af_mode, 0, 0, int_pin->configRegister, + pin_generate_config(PIN_PULL_UP_47K, PIN_MODE_IN, PIN_DRIVE_POWER_5, int_pin->configRegister)); GPIO_PinInit(int_pin->gpio, int_pin->pin, &gpio_config); #endif diff --git a/ports/mimxrt/machine_i2c.c b/ports/mimxrt/machine_i2c.c index 58dd824490..c64f46ff1b 100644 --- a/ports/mimxrt/machine_i2c.c +++ b/ports/mimxrt/machine_i2c.c @@ -72,15 +72,14 @@ static const iomux_table_t iomux_table[] = { IOMUX_TABLE_I2C }; bool lpi2c_set_iomux(int8_t hw_i2c, uint8_t drive) { int index = (hw_i2c - 1) * 2; + uint16_t pad_config = pin_generate_config(PIN_PULL_UP_100K, PIN_MODE_OPEN_DRAIN, drive, SCL.configRegister); if (SCL.muxRegister != 0) { IOMUXC_SetPinMux(SCL.muxRegister, SCL.muxMode, SCL.inputRegister, SCL.inputDaisy, SCL.configRegister, 1U); - IOMUXC_SetPinConfig(SCL.muxRegister, SCL.muxMode, SCL.inputRegister, SCL.inputDaisy, SCL.configRegister, - 0xF880u | drive << IOMUXC_SW_PAD_CTL_PAD_DSE_SHIFT); + IOMUXC_SetPinConfig(SCL.muxRegister, SCL.muxMode, SCL.inputRegister, SCL.inputDaisy, SCL.configRegister, pad_config); IOMUXC_SetPinMux(SDA.muxRegister, SDA.muxMode, SDA.inputRegister, SDA.inputDaisy, SDA.configRegister, 1U); - IOMUXC_SetPinConfig(SDA.muxRegister, SDA.muxMode, SDA.inputRegister, SDA.inputDaisy, SDA.configRegister, - 0xF880u | drive << IOMUXC_SW_PAD_CTL_PAD_DSE_SHIFT); + IOMUXC_SetPinConfig(SDA.muxRegister, SDA.muxMode, SDA.inputRegister, SDA.inputDaisy, SDA.configRegister, pad_config); return true; } else { return false; diff --git a/ports/mimxrt/machine_pin.c b/ports/mimxrt/machine_pin.c index 12975806d6..1f4afab97f 100644 --- a/ports/mimxrt/machine_pin.c +++ b/ports/mimxrt/machine_pin.c @@ -213,6 +213,7 @@ STATIC mp_obj_t machine_pin_obj_init_helper(const machine_pin_obj_t *self, size_ const machine_pin_af_obj_t *af_obj; uint32_t pad_config = 0UL; uint8_t pull = PIN_PULL_DISABLED; + uint32_t drive = (uint32_t)args[PIN_INIT_ARG_DRIVE].u_int; // Generate pin configuration if ((args[PIN_INIT_ARG_VALUE].u_obj != MP_OBJ_NULL) && (mp_obj_is_true(args[PIN_INIT_ARG_VALUE].u_obj))) { @@ -233,38 +234,7 @@ STATIC mp_obj_t machine_pin_obj_init_helper(const machine_pin_obj_t *self, size_ pull = (uint8_t)mp_obj_get_int(args[PIN_INIT_ARG_PULL].u_obj); } - pad_config |= IOMUXC_SW_PAD_CTL_PAD_SRE(0U); // Slow Slew Rate - pad_config |= IOMUXC_SW_PAD_CTL_PAD_SPEED(0b01); // medium(100MHz) - - if (mode == PIN_MODE_OPEN_DRAIN) { - pad_config |= IOMUXC_SW_PAD_CTL_PAD_ODE(0b1); // Open Drain Enabled - } else { - pad_config |= IOMUXC_SW_PAD_CTL_PAD_ODE(0b0); // Open Drain Disabled - } - - if (pull == PIN_PULL_DISABLED) { - pad_config |= IOMUXC_SW_PAD_CTL_PAD_PKE(0); // Pull/Keeper Disabled - } else if (pull == PIN_PULL_HOLD) { - pad_config |= IOMUXC_SW_PAD_CTL_PAD_PKE(1) | // Pull/Keeper Enabled - IOMUXC_SW_PAD_CTL_PAD_PUE(0); // Keeper selected - } else { - pad_config |= IOMUXC_SW_PAD_CTL_PAD_PKE(1) | // Pull/Keeper Enabled - IOMUXC_SW_PAD_CTL_PAD_PUE(1) | // Pull selected - IOMUXC_SW_PAD_CTL_PAD_PUS(pull); - } - - if (mode == PIN_MODE_IN) { - pad_config |= IOMUXC_SW_PAD_CTL_PAD_DSE(0b000) | // output driver disabled - IOMUXC_SW_PAD_CTL_PAD_HYS(1U); // Hysteresis enabled - } else { - uint drive = args[PIN_INIT_ARG_DRIVE].u_int; - if (!IS_GPIO_DRIVE(drive)) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid drive strength: %d"), drive); - } - - pad_config |= IOMUXC_SW_PAD_CTL_PAD_DSE(drive) | - IOMUXC_SW_PAD_CTL_PAD_HYS(0U); // Hysteresis disabled - } + pad_config = pin_generate_config(pull, mode, drive, self->configRegister); // Configure PAD as GPIO IOMUXC_SetPinMux(self->muxRegister, af_obj->af_mode, 0, 0, self->configRegister, 1U); // Software Input On Field: Input Path is determined by functionality diff --git a/ports/mimxrt/machine_pwm.c b/ports/mimxrt/machine_pwm.c index a13af0d80f..c21c7eb94b 100644 --- a/ports/mimxrt/machine_pwm.c +++ b/ports/mimxrt/machine_pwm.c @@ -518,7 +518,7 @@ STATIC mp_obj_t mp_machine_pwm_make_new(const mp_obj_type_t *type, size_t n_args IOMUXC_SetPinMux(pin1->muxRegister, af_obj1->af_mode, af_obj1->input_register, af_obj1->input_daisy, pin1->configRegister, 0U); IOMUXC_SetPinConfig(pin1->muxRegister, af_obj1->af_mode, af_obj1->input_register, af_obj1->input_daisy, - pin1->configRegister, 0x10B0U); + pin1->configRegister, pin_generate_config(PIN_PULL_DISABLED, PIN_MODE_OUT, PIN_DRIVE_POWER_5, pin1->configRegister)); // Settings for the second pin, if given. if (pin2 != NULL && pin2 != pin1) { @@ -529,7 +529,7 @@ STATIC mp_obj_t mp_machine_pwm_make_new(const mp_obj_type_t *type, size_t n_args IOMUXC_SetPinMux(pin2->muxRegister, af_obj2->af_mode, af_obj2->input_register, af_obj2->input_daisy, pin2->configRegister, 0U); IOMUXC_SetPinConfig(pin2->muxRegister, af_obj2->af_mode, af_obj2->input_register, af_obj2->input_daisy, - pin2->configRegister, 0x10B0U); + pin2->configRegister, pin_generate_config(PIN_PULL_DISABLED, PIN_MODE_OUT, PIN_DRIVE_POWER_5, pin2->configRegister)); } else { self->complementary = 0; } diff --git a/ports/mimxrt/machine_spi.c b/ports/mimxrt/machine_spi.c index db2fab4ee6..06c0278fb3 100644 --- a/ports/mimxrt/machine_spi.c +++ b/ports/mimxrt/machine_spi.c @@ -87,19 +87,19 @@ bool lpspi_set_iomux(int8_t spi, uint8_t drive) { if (SCK.muxRegister != 0) { IOMUXC_SetPinMux(SCK.muxRegister, SCK.muxMode, SCK.inputRegister, SCK.inputDaisy, SCK.configRegister, 0U); IOMUXC_SetPinConfig(SCK.muxRegister, SCK.muxMode, SCK.inputRegister, SCK.inputDaisy, SCK.configRegister, - 0x1080u | drive << IOMUXC_SW_PAD_CTL_PAD_DSE_SHIFT); + pin_generate_config(PIN_PULL_UP_100K, PIN_MODE_OUT, drive, SCK.configRegister)); IOMUXC_SetPinMux(CS0.muxRegister, CS0.muxMode, CS0.inputRegister, CS0.inputDaisy, CS0.configRegister, 0U); IOMUXC_SetPinConfig(CS0.muxRegister, CS0.muxMode, CS0.inputRegister, CS0.inputDaisy, CS0.configRegister, - 0x1080u | drive << IOMUXC_SW_PAD_CTL_PAD_DSE_SHIFT); + pin_generate_config(PIN_PULL_UP_100K, PIN_MODE_OUT, drive, CS0.configRegister)); IOMUXC_SetPinMux(SDO.muxRegister, SDO.muxMode, SDO.inputRegister, SDO.inputDaisy, SDO.configRegister, 0U); IOMUXC_SetPinConfig(SDO.muxRegister, SDO.muxMode, SDO.inputRegister, SDO.inputDaisy, SDO.configRegister, - 0x1080u | drive << IOMUXC_SW_PAD_CTL_PAD_DSE_SHIFT); + pin_generate_config(PIN_PULL_UP_100K, PIN_MODE_OUT, drive, SDO.configRegister)); IOMUXC_SetPinMux(SDI.muxRegister, SDI.muxMode, SDI.inputRegister, SDI.inputDaisy, SDI.configRegister, 0U); IOMUXC_SetPinConfig(SDI.muxRegister, SDI.muxMode, SDI.inputRegister, SDI.inputDaisy, SDI.configRegister, - 0x1080u | drive << IOMUXC_SW_PAD_CTL_PAD_DSE_SHIFT); + pin_generate_config(PIN_PULL_UP_100K, PIN_MODE_IN, drive, SDI.configRegister)); return true; } else { diff --git a/ports/mimxrt/machine_uart.c b/ports/mimxrt/machine_uart.c index 9aa8a0c742..6807a994e1 100644 --- a/ports/mimxrt/machine_uart.c +++ b/ports/mimxrt/machine_uart.c @@ -84,10 +84,12 @@ bool lpuart_set_iomux(int8_t uart) { if (TX.muxRegister != 0) { IOMUXC_SetPinMux(TX.muxRegister, TX.muxMode, TX.inputRegister, TX.inputDaisy, TX.configRegister, 0U); - IOMUXC_SetPinConfig(TX.muxRegister, TX.muxMode, TX.inputRegister, TX.inputDaisy, TX.configRegister, 0x10B0u); + IOMUXC_SetPinConfig(TX.muxRegister, TX.muxMode, TX.inputRegister, TX.inputDaisy, TX.configRegister, + pin_generate_config(PIN_PULL_UP_100K, PIN_MODE_OUT, PIN_DRIVE_POWER_6, TX.configRegister)); IOMUXC_SetPinMux(RX.muxRegister, RX.muxMode, RX.inputRegister, RX.inputDaisy, RX.configRegister, 0U); - IOMUXC_SetPinConfig(RX.muxRegister, RX.muxMode, RX.inputRegister, RX.inputDaisy, RX.configRegister, 0x10B0u); + IOMUXC_SetPinConfig(RX.muxRegister, RX.muxMode, RX.inputRegister, RX.inputDaisy, RX.configRegister, + pin_generate_config(PIN_PULL_UP_100K, PIN_MODE_IN, PIN_DRIVE_POWER_6, RX.configRegister)); return true; } else { return false; diff --git a/ports/mimxrt/pin.c b/ports/mimxrt/pin.c index 964fb537b8..e4136bd1c3 100644 --- a/ports/mimxrt/pin.c +++ b/ports/mimxrt/pin.c @@ -62,6 +62,86 @@ uint32_t pin_get_af(const machine_pin_obj_t *pin) { return (uint32_t)(mux_register & IOMUXC_SW_MUX_CTL_PAD_MUX_MODE_MASK) >> IOMUXC_SW_MUX_CTL_PAD_MUX_MODE_SHIFT; } +uint32_t pin_generate_config(const uint32_t pull, const uint32_t mode, const uint32_t drive, uint32_t config_register) { + uint32_t pad_config = 0x0UL; + + #if defined MIMXRT117x_SERIES + + // Set Pull-up + if ((config_register >= 0x400E8350 && config_register <= 0x400E83dc) || // GPIO_AD_xx + (config_register >= 0x40C08040 && config_register <= 0x40C0807C)) { // GPIO_LPSR_xx + pad_config |= IOMUXC_SW_PAD_CTL_PAD_SRE(0U); // Set slew rate; there is a discrepancy between doc and header file + if (pull != PIN_PULL_DISABLED) { + pad_config |= IOMUXC_SW_PAD_CTL_PAD_PUE(1) | // Pull Enabled + IOMUXC_SW_PAD_CTL_PAD_PUS(pull != PIN_PULL_DOWN_100K); // Up or DOWn + } + } else { // GPIO_SD_Bx_xx + if (pull == PIN_PULL_DISABLED) { + pad_config |= IOMUXC_SW_PAD_CTL_PAD_PULL(0b11); + } else if (pull == PIN_PULL_DOWN_100K) { + pad_config |= IOMUXC_SW_PAD_CTL_PAD_PULL(0b10); + } else { + pad_config |= IOMUXC_SW_PAD_CTL_PAD_PULL(0b01); + } + } + // Set open Drain; different for LPSR GPIO! + if (config_register >= 0x40C08040 && config_register <= 0x40C0807C) { // GPIO_LPSR_xx + if (mode == PIN_MODE_OPEN_DRAIN) { + pad_config |= 1 << 5; // Open Drain Enabled, no Macro provided + } + } else { + if (mode == PIN_MODE_OPEN_DRAIN) { + pad_config |= IOMUXC_SW_PAD_CTL_PAD_ODE(0b1); // Open Drain Enabled + } + } + // Set drive strength + if (mode != PIN_MODE_IN) { + if (!IS_GPIO_DRIVE(drive)) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid drive strength: %d"), drive); + } + pad_config |= IOMUXC_SW_PAD_CTL_PAD_DSE(drive >= PIN_DRIVE_POWER_4); + } + + #else + + pad_config |= IOMUXC_SW_PAD_CTL_PAD_SRE(0U); // Slow Slew Rate + pad_config |= IOMUXC_SW_PAD_CTL_PAD_SPEED(0b01); // medium(100MHz) + + if (mode == PIN_MODE_OPEN_DRAIN) { + pad_config |= IOMUXC_SW_PAD_CTL_PAD_ODE(0b1); // Open Drain Enabled + } else { + pad_config |= IOMUXC_SW_PAD_CTL_PAD_ODE(0b0); // Open Drain Disabled + } + + if (pull == PIN_PULL_DISABLED) { + pad_config |= IOMUXC_SW_PAD_CTL_PAD_PKE(0); // Pull/Keeper Disabled + } else if (pull == PIN_PULL_HOLD) { + pad_config |= IOMUXC_SW_PAD_CTL_PAD_PKE(1) | // Pull/Keeper Enabled + IOMUXC_SW_PAD_CTL_PAD_PUE(0); // Keeper selected + } else { + pad_config |= IOMUXC_SW_PAD_CTL_PAD_PKE(1) | // Pull/Keeper Enabled + IOMUXC_SW_PAD_CTL_PAD_PUE(1) | // Pull selected + IOMUXC_SW_PAD_CTL_PAD_PUS(pull); + } + + if (mode == PIN_MODE_IN) { + pad_config |= IOMUXC_SW_PAD_CTL_PAD_DSE(0b000) | // output driver disabled + IOMUXC_SW_PAD_CTL_PAD_HYS(1U); // Hysteresis enabled + } else { + + if (!IS_GPIO_DRIVE(drive)) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid drive strength: %d"), drive); + } + + pad_config |= IOMUXC_SW_PAD_CTL_PAD_DSE(drive) | + IOMUXC_SW_PAD_CTL_PAD_HYS(0U); // Hysteresis disabled + } + + #endif + + return pad_config; +} + const machine_pin_obj_t *pin_find(mp_obj_t user_obj) { const machine_pin_obj_t *pin_obj; diff --git a/ports/mimxrt/pin.h b/ports/mimxrt/pin.h index 22e7a7e0f5..d1597fab9b 100644 --- a/ports/mimxrt/pin.h +++ b/ports/mimxrt/pin.h @@ -55,6 +55,7 @@ enum { PIN_MODE_OUT, PIN_MODE_OPEN_DRAIN, PIN_MODE_ALT, + PIN_MODE_SKIP, }; enum { @@ -159,5 +160,6 @@ const machine_pin_af_obj_t *pin_find_af(const machine_pin_obj_t *pin, uint8_t fn const machine_pin_af_obj_t *pin_find_af_by_index(const machine_pin_obj_t *pin, mp_uint_t af_idx); const machine_pin_af_obj_t *pin_find_af_by_name(const machine_pin_obj_t *pin, const char *name); void machine_pin_set_mode(const machine_pin_obj_t *pin, uint8_t mode); +uint32_t pin_generate_config(const uint32_t pull, const uint32_t mode, const uint32_t drive, uint32_t config_register); #endif // MICROPY_INCLUDED_MIMXRT_PIN_H diff --git a/ports/mimxrt/sdcard.c b/ports/mimxrt/sdcard.c index fd3c910835..5c523068ad 100644 --- a/ports/mimxrt/sdcard.c +++ b/ports/mimxrt/sdcard.c @@ -725,24 +725,18 @@ static inline void sdcard_init_pin(const machine_pin_obj_t *pin, uint8_t af_idx, void sdcard_init_pins(mimxrt_sdcard_obj_t *card) { // speed and strength optimized for clock frequency < 100MHz - uint32_t speed = 1U; - uint32_t strength = 7U; const mimxrt_sdcard_obj_pins_t *pins = card->pins; - uint32_t default_config = IOMUXC_SW_PAD_CTL_PAD_SPEED(speed) | - IOMUXC_SW_PAD_CTL_PAD_SRE_MASK | - IOMUXC_SW_PAD_CTL_PAD_PKE_MASK | - IOMUXC_SW_PAD_CTL_PAD_PUE_MASK | - IOMUXC_SW_PAD_CTL_PAD_HYS_MASK | - IOMUXC_SW_PAD_CTL_PAD_PUS(1) | - IOMUXC_SW_PAD_CTL_PAD_DSE(strength); - uint32_t no_cd_config = IOMUXC_SW_PAD_CTL_PAD_SPEED(speed) | - IOMUXC_SW_PAD_CTL_PAD_SRE_MASK | - IOMUXC_SW_PAD_CTL_PAD_PKE_MASK | - IOMUXC_SW_PAD_CTL_PAD_PUE_MASK | - IOMUXC_SW_PAD_CTL_PAD_HYS_MASK | - IOMUXC_SW_PAD_CTL_PAD_PUS(0) | - IOMUXC_SW_PAD_CTL_PAD_DSE(strength); + uint32_t default_config = pin_generate_config( + PIN_PULL_UP_47K, PIN_MODE_SKIP, PIN_DRIVE_POWER_6, card->pins->clk.pin->configRegister); + #if USDHC_DATA3_PULL_DOWN_ON_BOARD + // Pull down on the board -> must not enable internal PD. + uint32_t no_cd_config = pin_generate_config( + PIN_PULL_DISABLED, PIN_MODE_SKIP, PIN_DRIVE_POWER_6, card->pins->data3.pin->configRegister); + #else + uint32_t no_cd_config = pin_generate_config( + PIN_PULL_DOWN_100K, PIN_MODE_SKIP, PIN_DRIVE_POWER_6, card->pins->data3.pin->configRegister); + #endif // USDHC_DATA3_PULL_DOWN_ON_BOARD sdcard_init_pin(card->pins->clk.pin, card->pins->clk.af_idx, default_config); // USDHC1_CLK sdcard_init_pin(card->pins->cmd.pin, card->pins->cmd.af_idx, default_config); // USDHC1_CMD From 30380962cff1a42dabbbdf172b951f21b5bffe14 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Mon, 10 Jan 2022 18:14:40 +0100 Subject: [PATCH 050/619] mimxrt: Allow to select cs0 or cs1 for SPI. Using the keyword argument cs=nnn in the constructor. The cs1 pin has to be defined in mpconfigboard.h. Note: Only a few boards have the CS1 pin exposed to the connectors. --- .../boards/MIMXRT1010_EVK/mpconfigboard.h | 3 +- .../boards/MIMXRT1020_EVK/mpconfigboard.h | 5 ++- .../boards/MIMXRT1050_EVK/mpconfigboard.h | 3 +- .../boards/MIMXRT1060_EVK/mpconfigboard.h | 3 +- .../boards/MIMXRT1064_EVK/mpconfigboard.h | 3 +- .../boards/SEEED_ARCH_MIX/mpconfigboard.h | 6 +++- ports/mimxrt/boards/TEENSY40/mpconfigboard.h | 6 +++- ports/mimxrt/boards/TEENSY41/mpconfigboard.h | 6 +++- ports/mimxrt/machine_spi.c | 32 +++++++++++++------ 9 files changed, 50 insertions(+), 17 deletions(-) diff --git a/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.h index 9a74a1c9c8..726a63904d 100644 --- a/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.h @@ -27,7 +27,8 @@ #define IOMUX_TABLE_SPI \ { IOMUXC_GPIO_AD_06_LPSPI1_SCK }, { IOMUXC_GPIO_AD_05_LPSPI1_PCS0 }, \ - { IOMUXC_GPIO_AD_04_LPSPI1_SDO }, { IOMUXC_GPIO_AD_03_LPSPI1_SDI }, + { IOMUXC_GPIO_AD_04_LPSPI1_SDO }, { IOMUXC_GPIO_AD_03_LPSPI1_SDI }, \ + { IOMUXC_GPIO_AD_02_LPSPI1_PCS1 } #define DMA_REQ_SRC_RX { 0, kDmaRequestMuxLPSPI1Rx, kDmaRequestMuxLPSPI2Rx } #define DMA_REQ_SRC_TX { 0, kDmaRequestMuxLPSPI1Tx, kDmaRequestMuxLPSPI2Tx } diff --git a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h index 5f5de5a3ed..2e7ee34e8d 100644 --- a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h @@ -37,10 +37,13 @@ #define IOMUX_TABLE_SPI \ { IOMUXC_GPIO_AD_B0_10_LPSPI1_SCK }, { IOMUXC_GPIO_AD_B0_11_LPSPI1_PCS0 }, \ { IOMUXC_GPIO_AD_B0_12_LPSPI1_SDO }, { IOMUXC_GPIO_AD_B0_13_LPSPI1_SDI }, \ + { 0 }, \ { 0 }, { 0 }, \ { 0 }, { 0 }, \ + { 0 }, \ { IOMUXC_GPIO_AD_B1_12_LPSPI3_SCK }, { IOMUXC_GPIO_AD_B1_13_LPSPI3_PCS0 }, \ - { IOMUXC_GPIO_AD_B1_14_LPSPI3_SDO }, { IOMUXC_GPIO_AD_B1_15_LPSPI3_SDI }, + { IOMUXC_GPIO_AD_B1_14_LPSPI3_SDO }, { IOMUXC_GPIO_AD_B1_15_LPSPI3_SDI }, \ + { IOMUXC_GPIO_AD_B1_09_LPSPI3_PCS1 } \ #define DMA_REQ_SRC_RX { 0, kDmaRequestMuxLPSPI1Rx, kDmaRequestMuxLPSPI2Rx, \ kDmaRequestMuxLPSPI3Rx, kDmaRequestMuxLPSPI4Rx } diff --git a/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h index aa9ab8d95c..c2783a3e59 100644 --- a/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h @@ -32,7 +32,8 @@ #define IOMUX_TABLE_SPI \ { IOMUXC_GPIO_SD_B0_00_LPSPI1_SCK }, { IOMUXC_GPIO_SD_B0_01_LPSPI1_PCS0 }, \ - { IOMUXC_GPIO_SD_B0_02_LPSPI1_SDO }, { IOMUXC_GPIO_SD_B0_03_LPSPI1_SDI }, + { IOMUXC_GPIO_SD_B0_02_LPSPI1_SDO }, { IOMUXC_GPIO_SD_B0_03_LPSPI1_SDI }, \ + { 0 }, #define DMA_REQ_SRC_RX { 0, kDmaRequestMuxLPSPI1Rx, kDmaRequestMuxLPSPI2Rx, \ kDmaRequestMuxLPSPI3Rx, kDmaRequestMuxLPSPI4Rx } diff --git a/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h index b108122ffc..475d07ddb1 100644 --- a/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h @@ -32,7 +32,8 @@ #define IOMUX_TABLE_SPI \ { IOMUXC_GPIO_SD_B0_00_LPSPI1_SCK }, { IOMUXC_GPIO_SD_B0_01_LPSPI1_PCS0 }, \ - { IOMUXC_GPIO_SD_B0_02_LPSPI1_SDO }, { IOMUXC_GPIO_SD_B0_03_LPSPI1_SDI }, + { IOMUXC_GPIO_SD_B0_02_LPSPI1_SDO }, { IOMUXC_GPIO_SD_B0_03_LPSPI1_SDI }, \ + { 0 }, #define DMA_REQ_SRC_RX { 0, kDmaRequestMuxLPSPI1Rx, kDmaRequestMuxLPSPI2Rx, \ kDmaRequestMuxLPSPI3Rx, kDmaRequestMuxLPSPI4Rx } diff --git a/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h index 3c724868f7..874991ac65 100644 --- a/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h @@ -32,7 +32,8 @@ #define IOMUX_TABLE_SPI \ { IOMUXC_GPIO_SD_B0_00_LPSPI1_SCK }, { IOMUXC_GPIO_SD_B0_01_LPSPI1_PCS0 }, \ - { IOMUXC_GPIO_SD_B0_02_LPSPI1_SDO }, { IOMUXC_GPIO_SD_B0_03_LPSPI1_SDI }, + { IOMUXC_GPIO_SD_B0_02_LPSPI1_SDO }, { IOMUXC_GPIO_SD_B0_03_LPSPI1_SDI }, \ + { 0 }, #define DMA_REQ_SRC_RX { 0, kDmaRequestMuxLPSPI1Rx, kDmaRequestMuxLPSPI2Rx, \ kDmaRequestMuxLPSPI3Rx, kDmaRequestMuxLPSPI4Rx } diff --git a/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.h b/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.h index 7c79063eef..53a3d4ed78 100644 --- a/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.h +++ b/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.h @@ -35,12 +35,16 @@ #define IOMUX_TABLE_SPI \ { 0 }, { 0 }, \ { 0 }, { 0 }, \ + { 0 }, \ { 0 }, { 0 }, \ { 0 }, { 0 }, \ + { 0 }, \ { IOMUXC_GPIO_AD_B1_15_LPSPI3_SCK }, { IOMUXC_GPIO_AD_B1_12_LPSPI3_PCS0 }, \ { IOMUXC_GPIO_AD_B1_14_LPSPI3_SDO }, { IOMUXC_GPIO_AD_B1_13_LPSPI3_SDI }, \ + { 0 }, \ { IOMUXC_GPIO_B1_07_LPSPI4_SCK }, { IOMUXC_GPIO_B1_04_LPSPI4_PCS0 }, \ - { IOMUXC_GPIO_B1_06_LPSPI4_SDO }, { IOMUXC_GPIO_B1_05_LPSPI4_SDI }, + { IOMUXC_GPIO_B1_06_LPSPI4_SDO }, { IOMUXC_GPIO_B1_05_LPSPI4_SDI }, \ + { IOMUXC_GPIO_B1_03_LPSPI4_PCS1 } #define DMA_REQ_SRC_RX { 0, kDmaRequestMuxLPSPI1Rx, kDmaRequestMuxLPSPI2Rx, \ kDmaRequestMuxLPSPI3Rx, kDmaRequestMuxLPSPI4Rx } diff --git a/ports/mimxrt/boards/TEENSY40/mpconfigboard.h b/ports/mimxrt/boards/TEENSY40/mpconfigboard.h index ae0a234629..08a774d886 100644 --- a/ports/mimxrt/boards/TEENSY40/mpconfigboard.h +++ b/ports/mimxrt/boards/TEENSY40/mpconfigboard.h @@ -27,12 +27,16 @@ #define IOMUX_TABLE_SPI \ { 0 }, { 0 }, \ { 0 }, { 0 }, \ + { 0 }, \ { 0 }, { 0 }, \ { 0 }, { 0 }, \ + { 0 }, \ { IOMUXC_GPIO_AD_B1_15_LPSPI3_SCK }, { IOMUXC_GPIO_AD_B0_03_LPSPI3_PCS0 }, \ { IOMUXC_GPIO_AD_B1_14_LPSPI3_SDO }, { IOMUXC_GPIO_AD_B0_02_LPSPI3_SDI }, \ + { 0 }, \ { IOMUXC_GPIO_B0_03_LPSPI4_SCK }, { IOMUXC_GPIO_B0_00_LPSPI4_PCS0 }, \ - { IOMUXC_GPIO_B0_02_LPSPI4_SDO }, { IOMUXC_GPIO_B0_01_LPSPI4_SDI }, + { IOMUXC_GPIO_B0_02_LPSPI4_SDO }, { IOMUXC_GPIO_B0_01_LPSPI4_SDI }, \ + { 0 }, #define DMA_REQ_SRC_RX { 0, kDmaRequestMuxLPSPI1Rx, kDmaRequestMuxLPSPI2Rx, \ kDmaRequestMuxLPSPI3Rx, kDmaRequestMuxLPSPI4Rx } diff --git a/ports/mimxrt/boards/TEENSY41/mpconfigboard.h b/ports/mimxrt/boards/TEENSY41/mpconfigboard.h index 4ca82d4b65..449f10babb 100644 --- a/ports/mimxrt/boards/TEENSY41/mpconfigboard.h +++ b/ports/mimxrt/boards/TEENSY41/mpconfigboard.h @@ -27,12 +27,16 @@ #define IOMUX_TABLE_SPI \ { IOMUXC_GPIO_SD_B0_00_LPSPI1_SCK }, { IOMUXC_GPIO_SD_B0_01_LPSPI1_PCS0 }, \ { IOMUXC_GPIO_SD_B0_02_LPSPI1_SDO }, { IOMUXC_GPIO_SD_B0_03_LPSPI1_SDI }, \ + { IOMUXC_GPIO_EMC_31_LPSPI1_PCS1 }, \ { 0 }, { 0 }, \ { 0 }, { 0 }, \ + { 0 }, \ { IOMUXC_GPIO_AD_B1_15_LPSPI3_SCK }, { IOMUXC_GPIO_AD_B1_12_LPSPI3_PCS0 }, \ { IOMUXC_GPIO_AD_B1_14_LPSPI3_SDO }, { IOMUXC_GPIO_AD_B1_13_LPSPI3_SDI }, \ + { 0 }, \ { IOMUXC_GPIO_B0_03_LPSPI4_SCK }, { IOMUXC_GPIO_B0_00_LPSPI4_PCS0 }, \ - { IOMUXC_GPIO_B0_02_LPSPI4_SDO }, { IOMUXC_GPIO_B0_01_LPSPI4_SDI }, + { IOMUXC_GPIO_B0_02_LPSPI4_SDO }, { IOMUXC_GPIO_B0_01_LPSPI4_SDI }, \ + { IOMUXC_GPIO_B1_03_LPSPI4_PCS1 } #define DMA_REQ_SRC_RX { 0, kDmaRequestMuxLPSPI1Rx, kDmaRequestMuxLPSPI2Rx, \ kDmaRequestMuxLPSPI3Rx, kDmaRequestMuxLPSPI4Rx } diff --git a/ports/mimxrt/machine_spi.c b/ports/mimxrt/machine_spi.c index 06c0278fb3..0332d9a9e8 100644 --- a/ports/mimxrt/machine_spi.c +++ b/ports/mimxrt/machine_spi.c @@ -53,6 +53,7 @@ #define CS0 (iomux_table[index + 1]) #define SDO (iomux_table[index + 2]) #define SDI (iomux_table[index + 3]) +#define CS1 (iomux_table[index + 4]) typedef struct _machine_spi_obj_t { mp_obj_base_t base; @@ -81,17 +82,25 @@ static const iomux_table_t iomux_table[] = { static uint16_t dma_req_src_rx[] = DMA_REQ_SRC_RX; static uint16_t dma_req_src_tx[] = DMA_REQ_SRC_TX; -bool lpspi_set_iomux(int8_t spi, uint8_t drive) { - int index = (spi - 1) * 4; +bool lpspi_set_iomux(int8_t spi, uint8_t drive, uint8_t cs) { + int index = (spi - 1) * 5; if (SCK.muxRegister != 0) { IOMUXC_SetPinMux(SCK.muxRegister, SCK.muxMode, SCK.inputRegister, SCK.inputDaisy, SCK.configRegister, 0U); IOMUXC_SetPinConfig(SCK.muxRegister, SCK.muxMode, SCK.inputRegister, SCK.inputDaisy, SCK.configRegister, pin_generate_config(PIN_PULL_UP_100K, PIN_MODE_OUT, drive, SCK.configRegister)); - IOMUXC_SetPinMux(CS0.muxRegister, CS0.muxMode, CS0.inputRegister, CS0.inputDaisy, CS0.configRegister, 0U); - IOMUXC_SetPinConfig(CS0.muxRegister, CS0.muxMode, CS0.inputRegister, CS0.inputDaisy, CS0.configRegister, - pin_generate_config(PIN_PULL_UP_100K, PIN_MODE_OUT, drive, CS0.configRegister)); + if (cs == 0 && CS0.muxRegister != 0) { + IOMUXC_SetPinMux(CS0.muxRegister, CS0.muxMode, CS0.inputRegister, CS0.inputDaisy, CS0.configRegister, 0U); + IOMUXC_SetPinConfig(CS0.muxRegister, CS0.muxMode, CS0.inputRegister, CS0.inputDaisy, CS0.configRegister, + 0x1080u | drive << IOMUXC_SW_PAD_CTL_PAD_DSE_SHIFT); + } else if (cs == 1 && CS1.muxRegister != 0) { + IOMUXC_SetPinMux(CS1.muxRegister, CS1.muxMode, CS1.inputRegister, CS1.inputDaisy, CS1.configRegister, 0U); + IOMUXC_SetPinConfig(CS1.muxRegister, CS1.muxMode, CS1.inputRegister, CS1.inputDaisy, CS1.configRegister, + 0x1080u | drive << IOMUXC_SW_PAD_CTL_PAD_DSE_SHIFT); + } else { + mp_raise_ValueError(MP_ERROR_TEXT("The chosen CS is not available")); + } IOMUXC_SetPinMux(SDO.muxRegister, SDO.muxMode, SDO.inputRegister, SDO.inputDaisy, SDO.configRegister, 0U); IOMUXC_SetPinConfig(SDO.muxRegister, SDO.muxMode, SDO.inputRegister, SDO.inputDaisy, SDO.configRegister, @@ -117,7 +126,7 @@ STATIC void machine_spi_print(const mp_print_t *print, mp_obj_t self_in, mp_prin } mp_obj_t machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - enum { ARG_id, ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit, ARG_gap_ns, ARG_drive }; + enum { ARG_id, ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit, ARG_gap_ns, ARG_drive, ARG_cs }; static const mp_arg_t allowed_args[] = { { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ }, { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = DEFAULT_SPI_BAUDRATE} }, @@ -127,6 +136,7 @@ mp_obj_t machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n { MP_QSTR_firstbit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_SPI_FIRSTBIT} }, { MP_QSTR_gap_ns, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_drive, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_SPI_DRIVE} }, + { MP_QSTR_cs, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, }; static bool clk_init = true; @@ -160,7 +170,6 @@ mp_obj_t machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n CLOCK_SetMux(kCLOCK_LpspiMux, 1); // Clock source is kCLOCK_Usb1PllPfd1Clk CLOCK_SetDiv(kCLOCK_LpspiDiv, CLOCK_DIVIDER); } - lpspi_set_iomux(spi_index_table[spi_id], drive); LPSPI_Reset(self->spi_inst); LPSPI_Enable(self->spi_inst, false); // Disable first before new settings are applies @@ -176,7 +185,12 @@ mp_obj_t machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n if (args[ARG_gap_ns].u_int != -1) { self->master_config->betweenTransferDelayInNanoSec = args[ARG_gap_ns].u_int; } + uint8_t cs = args[ARG_cs].u_int; + if (cs <= 1) { + self->master_config->whichPcs = cs; + } LPSPI_MasterInit(self->spi_inst, self->master_config, CLOCK_GetFreq(kCLOCK_Usb1PllPfd0Clk) / (CLOCK_DIVIDER + 1)); + lpspi_set_iomux(spi_index_table[spi_id], drive, cs); return MP_OBJ_FROM_PTR(self); } @@ -271,7 +285,7 @@ STATIC void machine_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8 masterXfer.txData = (uint8_t *)src; masterXfer.rxData = (uint8_t *)dest; masterXfer.dataSize = len; - masterXfer.configFlags = kLPSPI_MasterPcs0 | kLPSPI_MasterPcsContinuous | kLPSPI_MasterByteSwap; + masterXfer.configFlags = (self->master_config->whichPcs << LPSPI_MASTER_PCS_SHIFT) | kLPSPI_MasterPcsContinuous | kLPSPI_MasterByteSwap; // Reconfigure the TCR, required after switch between DMA vs. non-DMA LPSPI_Enable(self->spi_inst, false); // Disable first before new settings are applied @@ -317,7 +331,7 @@ STATIC void machine_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8 masterXfer.txData = (uint8_t *)src; masterXfer.rxData = (uint8_t *)dest; masterXfer.dataSize = len; - masterXfer.configFlags = kLPSPI_MasterPcs0 | kLPSPI_MasterPcsContinuous | kLPSPI_MasterByteSwap; + masterXfer.configFlags = (self->master_config->whichPcs << LPSPI_MASTER_PCS_SHIFT) | kLPSPI_MasterPcsContinuous | kLPSPI_MasterByteSwap; LPSPI_MasterTransferBlocking(self->spi_inst, &masterXfer); } From b73073d2460f14cbe7501e99b882520e0456ec94 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Wed, 12 Jan 2022 14:24:15 +0100 Subject: [PATCH 051/619] mimxrt: Add USB ID elements. - Manufacturer, set by MICROPY_HW_USB_STR_MANUF; default "MicroPython" - Board name, as set by MICROPY_HW_BOARD_NAME - Unique-ID, same as returned by machine.unique_id() - USB Vendor ID, as set by MICROPY_HW_USB_VID; default 0xf055 - USB Product ID, as set by MICROPY_HW_USB_PID; default 0x9802 --- ports/mimxrt/tusb_port.c | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/ports/mimxrt/tusb_port.c b/ports/mimxrt/tusb_port.c index 128861a49a..55855674c6 100644 --- a/ports/mimxrt/tusb_port.c +++ b/ports/mimxrt/tusb_port.c @@ -25,12 +25,20 @@ */ #include "tusb.h" +#include "mphalport.h" #ifndef MICROPY_HW_USB_VID #define MICROPY_HW_USB_VID (0xf055) +#endif + +#ifndef MICROPY_HW_USB_PID #define MICROPY_HW_USB_PID (0x9802) #endif +#ifndef MICROPY_HW_USB_STR_MANUF +#define MICROPY_HW_USB_STR_MANUF ("MicroPython") +#endif + #define USBD_DESC_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN) #define USBD_MAX_POWER_MA (250) @@ -77,9 +85,9 @@ static const uint8_t usbd_desc_cfg[USBD_DESC_LEN] = { }; static const char *const usbd_desc_str[] = { - [USBD_STR_MANUF] = "MicroPython", - [USBD_STR_PRODUCT] = "Board in FS mode", // Todo: fix string to indicate that product is running in High Speed mode - [USBD_STR_SERIAL] = "000000000000", // TODO + [USBD_STR_MANUF] = MICROPY_HW_USB_STR_MANUF, + [USBD_STR_PRODUCT] = MICROPY_HW_BOARD_NAME, + [USBD_STR_SERIAL] = "00000000000000000000", [USBD_STR_CDC] = "Board CDC", }; @@ -95,6 +103,9 @@ const uint8_t *tud_descriptor_configuration_cb(uint8_t index) { const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { #define DESC_STR_MAX (20) static uint16_t desc_str[DESC_STR_MAX]; + static const char hexchr[16] = "0123456789ABCDEF"; + + memset(desc_str, 0, sizeof(desc_str)); uint8_t len; if (index == 0) { @@ -104,9 +115,19 @@ const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { if (index >= sizeof(usbd_desc_str) / sizeof(usbd_desc_str[0])) { return NULL; } - const char *str = usbd_desc_str[index]; - for (len = 0; len < DESC_STR_MAX - 1 && str[len]; ++len) { - desc_str[1 + len] = str[len]; + if (index == USBD_STR_SERIAL) { + uint8_t uid[8]; + mp_hal_get_unique_id(uid); + // store it as a hex string + for (len = 0; len < 16; len += 2) { + desc_str[1 + len] = hexchr[uid[len / 2] >> 4]; + desc_str[1 + len + 1] = hexchr[uid[len / 2] & 0x0f]; + } + } else { + const char *str = usbd_desc_str[index]; + for (len = 0; len < DESC_STR_MAX - 1 && str[len]; ++len) { + desc_str[1 + len] = str[len]; + } } } From da9c3607a5803f207ac499049d43e42599d3dd80 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Fri, 14 Jan 2022 10:24:30 +0100 Subject: [PATCH 052/619] mimxrt: Add support for the Olimex RT1010 board. The board.json file is intentionally excluded, until the board will be sold. But including it into the mimxrt series make it easier to keep the build up-to-date. --- .../boards/OLIMEX_RT1010/clock_config.h | 104 ++++++++++++++++++ .../boards/OLIMEX_RT1010/mpconfigboard.h | 50 +++++++++ .../boards/OLIMEX_RT1010/mpconfigboard.mk | 13 +++ ports/mimxrt/boards/OLIMEX_RT1010/pins.csv | 36 ++++++ 4 files changed, 203 insertions(+) create mode 100644 ports/mimxrt/boards/OLIMEX_RT1010/clock_config.h create mode 100644 ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.h create mode 100644 ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.mk create mode 100644 ports/mimxrt/boards/OLIMEX_RT1010/pins.csv diff --git a/ports/mimxrt/boards/OLIMEX_RT1010/clock_config.h b/ports/mimxrt/boards/OLIMEX_RT1010/clock_config.h new file mode 100644 index 0000000000..76f3df422f --- /dev/null +++ b/ports/mimxrt/boards/OLIMEX_RT1010/clock_config.h @@ -0,0 +1,104 @@ +/* + * Copyright 2019 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _CLOCK_CONFIG_H_ +#define _CLOCK_CONFIG_H_ + +#include "fsl_common.h" + +/******************************************************************************* + * Definitions + ******************************************************************************/ +#define BOARD_XTAL0_CLK_HZ 24000000U /*!< Board xtal0 frequency in Hz */ + +#define BOARD_XTAL32K_CLK_HZ 32768U /*!< Board xtal32k frequency in Hz */ +/******************************************************************************* + ************************ BOARD_InitBootClocks function ************************ + ******************************************************************************/ + +#if defined(__cplusplus) +extern "C" { +#endif /* __cplusplus*/ + +/*! + * @brief This function executes default configuration of clocks. + * + */ +void BOARD_InitBootClocks(void); + +#if defined(__cplusplus) +} +#endif /* __cplusplus*/ + +/******************************************************************************* + ********************** Configuration BOARD_BootClockRUN *********************** + ******************************************************************************/ +/******************************************************************************* + * Definitions for BOARD_BootClockRUN configuration + ******************************************************************************/ +#define BOARD_BOOTCLOCKRUN_CORE_CLOCK 500000000U /*!< Core clock frequency: 500000000Hz */ + +/* Clock outputs (values are in Hz): */ +#define BOARD_BOOTCLOCKRUN_ADC_ALT_CLK 40000000UL +#define BOARD_BOOTCLOCKRUN_CKIL_SYNC_CLK_ROOT 32768UL +#define BOARD_BOOTCLOCKRUN_CLKO1_CLK 0UL +#define BOARD_BOOTCLOCKRUN_CLKO2_CLK 0UL +#define BOARD_BOOTCLOCKRUN_CLK_1M 1000000UL +#define BOARD_BOOTCLOCKRUN_CLK_24M 24000000UL +#define BOARD_BOOTCLOCKRUN_CORE_CLK_ROOT 500000000UL +#define BOARD_BOOTCLOCKRUN_ENET_500M_REF_CLK 500000000UL +#define BOARD_BOOTCLOCKRUN_FLEXIO1_CLK_ROOT 30000000UL +#define BOARD_BOOTCLOCKRUN_FLEXSPI_CLK_ROOT 132000000UL +#define BOARD_BOOTCLOCKRUN_GPT1_IPG_CLK_HIGHFREQ 62500000UL +#define BOARD_BOOTCLOCKRUN_GPT2_IPG_CLK_HIGHFREQ 62500000UL +#define BOARD_BOOTCLOCKRUN_IPG_CLK_ROOT 125000000UL +#define BOARD_BOOTCLOCKRUN_LPI2C_CLK_ROOT 60000000UL +#define BOARD_BOOTCLOCKRUN_LPSPI_CLK_ROOT 105600000UL +#define BOARD_BOOTCLOCKRUN_MQS_MCLK 63529411UL +#define BOARD_BOOTCLOCKRUN_PERCLK_CLK_ROOT 62500000UL +#define BOARD_BOOTCLOCKRUN_SAI1_CLK_ROOT 63529411UL +#define BOARD_BOOTCLOCKRUN_SAI1_MCLK1 63529411UL +#define BOARD_BOOTCLOCKRUN_SAI1_MCLK2 63529411UL +#define BOARD_BOOTCLOCKRUN_SAI1_MCLK3 30000000UL +#define BOARD_BOOTCLOCKRUN_SAI3_CLK_ROOT 63529411UL +#define BOARD_BOOTCLOCKRUN_SAI3_MCLK1 63529411UL +#define BOARD_BOOTCLOCKRUN_SAI3_MCLK2 0UL +#define BOARD_BOOTCLOCKRUN_SAI3_MCLK3 30000000UL +#define BOARD_BOOTCLOCKRUN_SPDIF0_CLK_ROOT 30000000UL +#define BOARD_BOOTCLOCKRUN_SPDIF0_EXTCLK_OUT 0UL +#define BOARD_BOOTCLOCKRUN_TRACE_CLK_ROOT 117333333UL +#define BOARD_BOOTCLOCKRUN_UART_CLK_ROOT 80000000UL +#define BOARD_BOOTCLOCKRUN_USBPHY_CLK 0UL + +/*! @brief Usb1 PLL set for BOARD_BootClockRUN configuration. + */ +extern const clock_usb_pll_config_t usb1PllConfig_BOARD_BootClockRUN; +/*! @brief Sys PLL for BOARD_BootClockRUN configuration. + */ +extern const clock_sys_pll_config_t sysPllConfig_BOARD_BootClockRUN; +/*! @brief Enet PLL set for BOARD_BootClockRUN configuration. + */ +extern const clock_enet_pll_config_t enetPllConfig_BOARD_BootClockRUN; + +/******************************************************************************* + * API for BOARD_BootClockRUN configuration + ******************************************************************************/ +#if defined(__cplusplus) +extern "C" { +#endif /* __cplusplus*/ + +/*! + * @brief This function executes configuration of clocks. + * + */ +void BOARD_BootClockRUN(void); + +#if defined(__cplusplus) +} +#endif /* __cplusplus*/ + +#endif /* _CLOCK_CONFIG_H_ */ diff --git a/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.h b/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.h new file mode 100644 index 0000000000..e004050aee --- /dev/null +++ b/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.h @@ -0,0 +1,50 @@ +#define MICROPY_HW_BOARD_NAME "RT1010-Py-DevKIT" +#define MICROPY_HW_MCU_NAME "MIMXRT1011DAE5A" +#define MICROPY_HW_USB_STR_MANUF "Olimex Ltd." +#define MICROPY_HW_USB_VID 0x15ba +#define MICROPY_HW_USB_PID 0x0046 +#define MICROPY_PY_UOS_DUPTERM_BUILTIN_STREAM (0) + +// Olimex RT1010-Py has 1 board LED +#define MICROPY_HW_LED1_PIN (pin_GPIO_11) +#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_high(pin)) +#define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_low(pin)) + +#define MICROPY_HW_NUM_PIN_IRQS (2 * 32) + +// Define mapping logical UART # to hardware UART # +// LPUART1 on RX/TX -> 1 +// LPUART4 on D5/D6 -> 2 + +#define MICROPY_HW_UART_NUM (sizeof(uart_index_table) / sizeof(uart_index_table)[0]) +#define MICROPY_HW_UART_INDEX { 0, 1, 4 } + +#define IOMUX_TABLE_UART \ + { IOMUXC_GPIO_10_LPUART1_TXD }, { IOMUXC_GPIO_09_LPUART1_RXD }, \ + { 0 }, { 0 }, \ + { 0 }, { 0 }, \ + { IOMUXC_GPIO_06_LPUART4_TXD }, { IOMUXC_GPIO_05_LPUART4_RXD }, + +#define MICROPY_HW_SPI_INDEX { 0, 1, 2 } + +#define IOMUX_TABLE_SPI \ + { IOMUXC_GPIO_AD_06_LPSPI1_SCK }, { IOMUXC_GPIO_AD_05_LPSPI1_PCS0 }, \ + { IOMUXC_GPIO_AD_04_LPSPI1_SDO }, { IOMUXC_GPIO_AD_03_LPSPI1_SDI }, \ + { IOMUXC_GPIO_AD_02_LPSPI1_PCS1 }, \ + { IOMUXC_GPIO_AD_12_LPSPI2_SCK }, { IOMUXC_GPIO_AD_11_LPSPI2_PCS0 }, \ + { IOMUXC_GPIO_AD_10_LPSPI2_SDO }, { IOMUXC_GPIO_AD_09_LPSPI2_SDI }, \ + { IOMUXC_GPIO_AD_01_LPSPI2_PCS1 } + +#define DMA_REQ_SRC_RX { 0, kDmaRequestMuxLPSPI1Rx, kDmaRequestMuxLPSPI2Rx } +#define DMA_REQ_SRC_TX { 0, kDmaRequestMuxLPSPI1Tx, kDmaRequestMuxLPSPI2Tx } + +// Define mapping hardware I2C # to logical I2C # +// SDA/SCL HW-I2C Logical I2C +// SDA1/SCL1 LPI2C1 -> 0 +// SDA2/SCL2 LPI2C2 -> 1 + +#define MICROPY_HW_I2C_INDEX { 0, 1, 2 } + +#define IOMUX_TABLE_I2C \ + { IOMUXC_GPIO_AD_14_LPI2C1_SCL }, { IOMUXC_GPIO_AD_13_LPI2C1_SDA }, \ + { IOMUXC_GPIO_AD_08_LPI2C2_SCL }, { IOMUXC_GPIO_AD_07_LPI2C2_SDA }, diff --git a/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.mk b/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.mk new file mode 100644 index 0000000000..e4f904903d --- /dev/null +++ b/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.mk @@ -0,0 +1,13 @@ +MCU_SERIES = MIMXRT1011 +MCU_VARIANT = MIMXRT1011DAE5A + +MICROPY_FLOAT_IMPL = single +MICROPY_PY_MACHINE_SDCARD ?= 0 +MICROPY_HW_FLASH_TYPE ?= qspi_nor +MICROPY_HW_FLASH_SIZE ?= 0x200000 # 2MB +MICROPY_HW_FLASH_RESERVED ?= 0x1000 # 4KB + +CFLAGS += -DMICROPY_HW_FLASH_DQS=kFlexSPIReadSampleClk_LoopbackInternally + +SRC_C += \ + hal/flexspi_nor_flash.c \ diff --git a/ports/mimxrt/boards/OLIMEX_RT1010/pins.csv b/ports/mimxrt/boards/OLIMEX_RT1010/pins.csv new file mode 100644 index 0000000000..d294f86b6d --- /dev/null +++ b/ports/mimxrt/boards/OLIMEX_RT1010/pins.csv @@ -0,0 +1,36 @@ +D0,GPIO_00 +D1,GPIO_01 +D2,GPIO_02 +D3,GPIO_03 +D4,GPIO_04 +D5,GPIO_05 +D6,GPIO_06 +D7,GPIO_07 +D8,GPIO_08 +D9,GPIO_SD_00 +D10,GPIO_SD_01 +D11,GPIO_SD_02 +D12,GPIO_SD_05 +D13,GPIO_SD_12 +D14,GPIO_SD_13 +A0,GPIO_AD_02 +A1,GPIO_AD_03 +A2,GPIO_AD_04 +A3,GPIO_AD_05 +A4,GPIO_AD_06 +LED,GPIO_11 +SDA1, GPIO_AD_13 +SCL1, GPIO_AD_14 +SDA2, GPIO_AD_07 +SCL2, GPIO_AD_08 +SDI, GPIO_AD_09 +SDO, GPIO_AD_10 +CS0, GPIO_AD_11 +SCK, GPIO_AD_12 +USB_OTG1_PWR, GPIO_AD_00 +BT0, GPIO_SD_04 +BT1, GPIO_SD_03 +RX, GPIO_09 +TX, GPIO_10 +RELAY1,GPIO_SD_12 +RELAY2,GPIO_SD_13 From c1841c2d455a99f4184487da658e88267785ebd5 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Tue, 25 Jan 2022 21:13:37 +0100 Subject: [PATCH 053/619] mimxrt: Simplify Makefile in selection of flash type. Simplify it a little bit by combining two sections regarding the flash type. Thanks to @alphaFred for suggesting it. --- ports/mimxrt/Makefile | 49 ++++++++++++++++++------------------------- 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index 2881c7c767..5b835d25b7 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -91,14 +91,6 @@ CFLAGS += -DXIP_EXTERNAL_FLASH=1 \ -DBOARD_FLASH_SIZE=$(MICROPY_HW_FLASH_SIZE) \ -DMICROPY_HW_FLASH_SIZE=$(MICROPY_HW_FLASH_SIZE) \ -ifeq ($(MICROPY_HW_FLASH_TYPE), qspi_nor) -CFLAGS += -DBOARD_FLASH_OPS_HEADER_H=\"hal/flexspi_nor_flash.h\" -else ifeq ($(MICROPY_HW_FLASH_TYPE), hyperflash) -CFLAGS += -DBOARD_FLASH_OPS_HEADER_H=\"hal/flexspi_hyper_flash.h\" -else -$(error Error: Unknown board flash type $(MICROPY_HW_FLASH_TYPE)) -endif - ifeq ($(MICROPY_PY_MACHINE_SDCARD),1) CFLAGS += -DMICROPY_PY_MACHINE_SDCARD=1 endif @@ -274,33 +266,32 @@ SRC_C += \ $(SRC_HAL_IMX_C) \ $(SRC_ETH_C) \ +ifeq ($(MICROPY_HW_FLASH_TYPE), qspi_nor) +CFLAGS += -DBOARD_FLASH_OPS_HEADER_H=\"hal/flexspi_nor_flash.h\" +SRC_C += hal/flexspi_nor_flash.c + ifeq ($(MICROPY_HW_BOARD_FLASH_FILES), 1) CFLAGS += -DBOARD_FLASH_CONFIG_HEADER_H=\"$(BOARD)_flexspi_flash_config.h\" - -ifeq ($(MICROPY_HW_FLASH_TYPE), qspi_nor) -SRC_C += \ - hal/flexspi_nor_flash.c \ - $(BOARD_DIR)/qspi_nor_flash_config.c -else ifeq ($(MICROPY_HW_FLASH_TYPE), hyperflash) -SRC_C += \ - hal/flexspi_hyper_flash.c \ - $(BOARD_DIR)/qspi_hyper_flash_config.c -endif - +SRC_C += $(BOARD_DIR)/qspi_nor_flash_config.c else - CFLAGS += -DBOARD_FLASH_CONFIG_HEADER_H=\"hal/flexspi_flash_config.h\" - -ifeq ($(MICROPY_HW_FLASH_TYPE), qspi_nor) -SRC_C += \ - hal/flexspi_nor_flash.c \ - hal/qspi_nor_flash_config.c -else ifeq ($(MICROPY_HW_FLASH_TYPE), hyperflash) -SRC_C += \ - hal/flexspi_hyper_flash.c \ - hal/qspi_hyper_flash_config.c +SRC_C += hal/qspi_nor_flash_config.c endif +# +else ifeq ($(MICROPY_HW_FLASH_TYPE), hyperflash) +CFLAGS += -DBOARD_FLASH_OPS_HEADER_H=\"hal/flexspi_hyper_flash.h\" +SRC_C += hal/flexspi_hyper_flash.c +ifeq ($(MICROPY_HW_BOARD_FLASH_FILES), 1) +CFLAGS += -DBOARD_FLASH_CONFIG_HEADER_H=\"$(BOARD)_flexspi_flash_config.h\" +SRC_C += $(BOARD_DIR)/qspi_hyper_flash_config.c +else +CFLAGS += -DBOARD_FLASH_CONFIG_HEADER_H=\"hal/flexspi_flash_config.h\" +SRC_C += hal/qspi_hyper_flash_config.c +endif +# +else +$(error Error: Unknown board flash type $(MICROPY_HW_FLASH_TYPE)) endif ifeq ($(MICROPY_FLOAT_IMPL),double) From a00e1e57355f850bce928a770851bc0dc01484c2 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sun, 23 Jan 2022 03:33:43 +0200 Subject: [PATCH 054/619] stm32/dac: Deinit all DACs on soft reset. DAC timed functions continue to run after a soft reset cycle, using collected memory in the case of write_timed. --- ports/stm32/dac.c | 7 +++++++ ports/stm32/dac.h | 2 ++ ports/stm32/main.c | 3 +++ 3 files changed, 12 insertions(+) diff --git a/ports/stm32/dac.c b/ports/stm32/dac.c index fe3aecdde8..d51312aebc 100644 --- a/ports/stm32/dac.c +++ b/ports/stm32/dac.c @@ -131,6 +131,13 @@ STATIC void dac_deinit(uint32_t dac_channel) { #endif } +void dac_deinit_all(void) { + dac_deinit(DAC_CHANNEL_1); + #if !defined(STM32L452xx) + dac_deinit(DAC_CHANNEL_2); + #endif +} + STATIC void dac_config_channel(uint32_t dac_channel, uint32_t trig, uint32_t outbuf) { DAC->CR &= ~(DAC_CR_EN1 << dac_channel); uint32_t cr_off = DAC_CR_DMAEN1 | DAC_CR_MAMP1 | DAC_CR_WAVE1 | DAC_CR_TSEL1 | DAC_CR_TEN1; diff --git a/ports/stm32/dac.h b/ports/stm32/dac.h index fca36e019e..d5686725a4 100644 --- a/ports/stm32/dac.h +++ b/ports/stm32/dac.h @@ -28,4 +28,6 @@ extern const mp_obj_type_t pyb_dac_type; +void dac_deinit_all(void); + #endif // MICROPY_INCLUDED_STM32_DAC_H diff --git a/ports/stm32/main.c b/ports/stm32/main.c index 431fa20dee..3291a80184 100644 --- a/ports/stm32/main.c +++ b/ports/stm32/main.c @@ -654,6 +654,9 @@ soft_reset_exit: #if MICROPY_HW_ENABLE_CAN can_deinit_all(); #endif + #if MICROPY_HW_ENABLE_DAC + dac_deinit_all(); + #endif machine_deinit(); #if MICROPY_PY_THREAD From 160e4d9a6d491cf3af0843feba5f987b2dd83c60 Mon Sep 17 00:00:00 2001 From: Herwin Grobben Date: Wed, 1 Dec 2021 16:37:04 +0100 Subject: [PATCH 055/619] stm32/fdcan: Fix FIFO1 usage and handling of error interrupts. The original code used a independent state with regards to the interrupt. During heavy bus error conditions the internal state could become out-of-sync with the interrupts. Further explanation: during the development of an application using CAN communication, a interrupt-run-away was found in some situations. It was found that the error interrupt triggered (Warning, Passive or Bus-Off, all triggered it) the run-away. The only recovery was a reset. Two problems were found: - the error interrupt is enabled but not cleared in the interrupt routine; - an internal variable 'State' that was used to track the message received state (empty, new, full, overflow) that was not directly related to interrupt that indicated the state. In this commit these issues are fixed by adding more values for the interrupt reason (warning, passive, bus off) and clearing the error interrupts, and making the internal state directly dependent on the interrupt state for received messages. Furthermore, introducing the FIFO1 in the CAN receive stage, another issue existed. Even if the messages are received into the FIFO1 (by selecting message filtering for FIFO0 and FIFO1), the interrupt firing was indicating FIFO0 Rx. The configuration of the interrupts for this is now also fixed. The CAN peripheral has 2 interrupt lines going into the NVIC controller. The assignment of the interrupt reasons to these 2 interrupt lines was missing. Now the reception of FIFO1 messages triggers the second interrupt line. Other interrupts (Rx FIFO0 and bus error) are assigned to the first interrupt line. Tested on a Nucleo-G474, and also checked the HAL function to work with the H7 family. --- ports/stm32/fdcan.c | 151 +++++++++++++++++++++++++++++------ ports/stm32/make-stmconst.py | 3 +- ports/stm32/pyb_can.c | 41 +++++++--- 3 files changed, 159 insertions(+), 36 deletions(-) diff --git a/ports/stm32/fdcan.c b/ports/stm32/fdcan.c index 2892572f40..778902735e 100644 --- a/ports/stm32/fdcan.c +++ b/ports/stm32/fdcan.c @@ -44,6 +44,13 @@ #define FDCAN_ELEMENT_MASK_FIDX (0x7f000000) // Filter Index #define FDCAN_ELEMENT_MASK_ANMF (0x80000000) // Accepted Non-matching Frame +#define FDCAN_RX_FIFO0_MASK (FDCAN_FLAG_RX_FIFO0_MESSAGE_LOST | FDCAN_FLAG_RX_FIFO0_FULL | FDCAN_FLAG_RX_FIFO0_NEW_MESSAGE) +#define FDCAN_RX_FIFO1_MASK (FDCAN_FLAG_RX_FIFO1_MESSAGE_LOST | FDCAN_FLAG_RX_FIFO1_FULL | FDCAN_FLAG_RX_FIFO1_NEW_MESSAGE) +#define FDCAN_ERROR_STATUS_MASK (FDCAN_FLAG_ERROR_PASSIVE | FDCAN_FLAG_ERROR_WARNING | FDCAN_FLAG_BUS_OFF) + +// also defined in _hal_fdcan.c, but not able to declare extern and reach the variable +static const uint8_t DLCtoBytes[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64}; + bool can_init(pyb_can_obj_t *can_obj, uint32_t mode, uint32_t prescaler, uint32_t sjw, uint32_t bs1, uint32_t bs2, bool auto_restart) { (void)auto_restart; @@ -60,6 +67,16 @@ bool can_init(pyb_can_obj_t *can_obj, uint32_t mode, uint32_t prescaler, uint32_ init->TransmitPause = DISABLE; init->ProtocolException = ENABLE; + #if defined(STM32G4) + init->ClockDivider = FDCAN_CLOCK_DIV1; + init->DataPrescaler = 1; + init->DataSyncJumpWidth = 1; + init->DataTimeSeg1 = 1; + init->DataTimeSeg2 = 1; + #endif + + #if defined(STM32H7) + // variable used to specify RAM address in HAL, only for H7, G4 uses defined offset address in HAL // The Message RAM is shared between CAN1 and CAN2. Setting the offset to half // the Message RAM for the second CAN and using half the resources for each CAN. if (can_obj->can_id == PYB_CAN_1) { @@ -67,6 +84,14 @@ bool can_init(pyb_can_obj_t *can_obj, uint32_t mode, uint32_t prescaler, uint32_ } else { init->MessageRAMOffset = 2560 / 2; } + #endif + + #if defined(STM32G4) + + init->StdFiltersNbr = 28; // /2 ? if FDCAN2 is used !!? + init->ExtFiltersNbr = 0; // Not used + + #elif defined(STM32H7) init->StdFiltersNbr = 64; // 128 / 2 init->ExtFiltersNbr = 0; // Not used @@ -83,6 +108,9 @@ bool can_init(pyb_can_obj_t *can_obj, uint32_t mode, uint32_t prescaler, uint32_ init->TxFifoQueueElmtsNbr = 16; // Tx fifo elements init->TxElmtSize = FDCAN_DATA_BYTES_8; + + #endif + init->TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION; FDCAN_GlobalTypeDef *CANx = NULL; @@ -148,21 +176,27 @@ bool can_init(pyb_can_obj_t *can_obj, uint32_t mode, uint32_t prescaler, uint32_ NVIC_SetPriority(FDCAN1_IT1_IRQn, IRQ_PRI_CAN); HAL_NVIC_EnableIRQ(FDCAN1_IT1_IRQn); break; + #if defined(MICROPY_HW_CAN2_TX) case PYB_CAN_2: NVIC_SetPriority(FDCAN2_IT0_IRQn, IRQ_PRI_CAN); HAL_NVIC_EnableIRQ(FDCAN2_IT0_IRQn); NVIC_SetPriority(FDCAN2_IT1_IRQn, IRQ_PRI_CAN); HAL_NVIC_EnableIRQ(FDCAN2_IT1_IRQn); break; + #endif default: return false; } + // FDCAN IT 0 + HAL_FDCAN_ConfigInterruptLines(&can_obj->can, FDCAN_IT_GROUP_RX_FIFO0 | FDCAN_IT_GROUP_BIT_LINE_ERROR | FDCAN_IT_GROUP_PROTOCOL_ERROR, FDCAN_INTERRUPT_LINE0); + // FDCAN IT 1 + HAL_FDCAN_ConfigInterruptLines(&can_obj->can, FDCAN_IT_GROUP_RX_FIFO1, FDCAN_INTERRUPT_LINE1); - __HAL_FDCAN_ENABLE_IT(&can_obj->can, FDCAN_IT_BUS_OFF | FDCAN_IT_ERROR_WARNING | FDCAN_IT_ERROR_PASSIVE); - __HAL_FDCAN_ENABLE_IT(&can_obj->can, FDCAN_IT_RX_FIFO0_NEW_MESSAGE | FDCAN_IT_RX_FIFO1_NEW_MESSAGE); - __HAL_FDCAN_ENABLE_IT(&can_obj->can, FDCAN_IT_RX_FIFO0_MESSAGE_LOST | FDCAN_IT_RX_FIFO1_MESSAGE_LOST); - __HAL_FDCAN_ENABLE_IT(&can_obj->can, FDCAN_IT_RX_FIFO0_FULL | FDCAN_IT_RX_FIFO1_FULL); - + uint32_t ActiveITs = FDCAN_IT_BUS_OFF | FDCAN_IT_ERROR_WARNING | FDCAN_IT_ERROR_PASSIVE; + ActiveITs |= FDCAN_IT_RX_FIFO0_NEW_MESSAGE | FDCAN_IT_RX_FIFO1_NEW_MESSAGE; + ActiveITs |= FDCAN_IT_RX_FIFO0_MESSAGE_LOST | FDCAN_IT_RX_FIFO1_MESSAGE_LOST; + ActiveITs |= FDCAN_IT_RX_FIFO0_FULL | FDCAN_IT_RX_FIFO1_FULL; + HAL_FDCAN_ActivateNotification(&can_obj->can, ActiveITs, 0); return true; } @@ -227,10 +261,19 @@ int can_receive(FDCAN_HandleTypeDef *can, int fifo, FDCAN_RxHeaderTypeDef *hdr, uint32_t index, *address; if (fifo == FDCAN_RX_FIFO0) { index = (*rxf & FDCAN_RXF0S_F0GI) >> FDCAN_RXF0S_F0GI_Pos; + #if defined(STM32G4) + address = (uint32_t *)(can->msgRam.RxFIFO0SA + (index * (18U * 4U))); // SRAMCAN_RF0_SIZE bytes, size not configurable + #else address = (uint32_t *)(can->msgRam.RxFIFO0SA + (index * can->Init.RxFifo0ElmtSize * 4)); + #endif } else { index = (*rxf & FDCAN_RXF1S_F1GI) >> FDCAN_RXF1S_F1GI_Pos; + #if defined(STM32G4) + // ToDo: test FIFO1, FIFO 0 is ok + address = (uint32_t *)(can->msgRam.RxFIFO1SA + (index * (18U * 4U))); // SRAMCAN_RF1_SIZE bytes, size not configurable + #else address = (uint32_t *)(can->msgRam.RxFIFO1SA + (index * can->Init.RxFifo1ElmtSize * 4)); + #endif } // Parse header of message @@ -251,7 +294,7 @@ int can_receive(FDCAN_HandleTypeDef *can, int fifo, FDCAN_RxHeaderTypeDef *hdr, // Copy data uint8_t *pdata = (uint8_t *)address; - for (uint32_t i = 0; i < 8; ++i) { // TODO use DLCtoBytes[hdr->DataLength] for length > 8 + for (uint32_t i = 0; i < DLCtoBytes[hdr->DataLength]; ++i) { *data++ = *pdata++; } @@ -269,41 +312,97 @@ STATIC void can_rx_irq_handler(uint can_id, uint fifo_id) { self = MP_STATE_PORT(pyb_can_obj_all)[can_id - 1]; + CAN_TypeDef *can = self->can.Instance; + + uint32_t RxFifo0ITs; + uint32_t RxFifo1ITs; + // uint32_t Errors; + uint32_t ErrorStatusITs; + uint32_t Psr; + + RxFifo0ITs = can->IR & FDCAN_RX_FIFO0_MASK; + RxFifo0ITs &= can->IE; + RxFifo1ITs = can->IR & FDCAN_RX_FIFO1_MASK; + RxFifo1ITs &= can->IE; + // Errors = (&self->can)->Instance->IR & FDCAN_ERROR_MASK; + // Errors &= (&self->can)->Instance->IE; + ErrorStatusITs = can->IR & FDCAN_ERROR_STATUS_MASK; + ErrorStatusITs &= can->IE; + Psr = can->PSR; + if (fifo_id == FDCAN_RX_FIFO0) { callback = self->rxcallback0; state = &self->rx_state0; + if (RxFifo0ITs & FDCAN_FLAG_RX_FIFO0_NEW_MESSAGE) { + __HAL_FDCAN_DISABLE_IT(&self->can, FDCAN_IT_RX_FIFO0_NEW_MESSAGE); + __HAL_FDCAN_CLEAR_FLAG(&self->can, FDCAN_FLAG_RX_FIFO0_NEW_MESSAGE); + irq_reason = MP_OBJ_NEW_SMALL_INT(0); + *state = RX_STATE_MESSAGE_PENDING; + + } + if (RxFifo0ITs & FDCAN_FLAG_RX_FIFO0_FULL) { + __HAL_FDCAN_DISABLE_IT(&self->can, FDCAN_IT_RX_FIFO0_FULL); + __HAL_FDCAN_CLEAR_FLAG(&self->can, FDCAN_FLAG_RX_FIFO0_FULL); + irq_reason = MP_OBJ_NEW_SMALL_INT(1); + *state = RX_STATE_FIFO_FULL; + + } + if (RxFifo0ITs & FDCAN_FLAG_RX_FIFO0_MESSAGE_LOST) { + __HAL_FDCAN_DISABLE_IT(&self->can, FDCAN_IT_RX_FIFO0_MESSAGE_LOST); + __HAL_FDCAN_CLEAR_FLAG(&self->can, FDCAN_FLAG_RX_FIFO0_MESSAGE_LOST); + irq_reason = MP_OBJ_NEW_SMALL_INT(2); + *state = RX_STATE_FIFO_OVERFLOW; + } + } else { callback = self->rxcallback1; state = &self->rx_state1; - } - - switch (*state) { - case RX_STATE_FIFO_EMPTY: - __HAL_FDCAN_DISABLE_IT(&self->can, (fifo_id == FDCAN_RX_FIFO0) ? - FDCAN_IT_RX_FIFO0_NEW_MESSAGE : FDCAN_IT_RX_FIFO1_NEW_MESSAGE); + if (RxFifo1ITs & FDCAN_FLAG_RX_FIFO1_NEW_MESSAGE) { + __HAL_FDCAN_DISABLE_IT(&self->can, FDCAN_IT_RX_FIFO1_NEW_MESSAGE); + __HAL_FDCAN_CLEAR_FLAG(&self->can, FDCAN_FLAG_RX_FIFO1_NEW_MESSAGE); irq_reason = MP_OBJ_NEW_SMALL_INT(0); *state = RX_STATE_MESSAGE_PENDING; - break; - case RX_STATE_MESSAGE_PENDING: - __HAL_FDCAN_DISABLE_IT(&self->can, (fifo_id == FDCAN_RX_FIFO0) ? FDCAN_IT_RX_FIFO0_FULL : FDCAN_IT_RX_FIFO1_FULL); - __HAL_FDCAN_CLEAR_FLAG(&self->can, (fifo_id == FDCAN_RX_FIFO0) ? FDCAN_FLAG_RX_FIFO0_FULL : FDCAN_FLAG_RX_FIFO1_FULL); + + } + if (RxFifo1ITs & FDCAN_FLAG_RX_FIFO1_FULL) { + __HAL_FDCAN_DISABLE_IT(&self->can, FDCAN_IT_RX_FIFO1_FULL); + __HAL_FDCAN_CLEAR_FLAG(&self->can, FDCAN_FLAG_RX_FIFO1_FULL); irq_reason = MP_OBJ_NEW_SMALL_INT(1); *state = RX_STATE_FIFO_FULL; - break; - case RX_STATE_FIFO_FULL: - __HAL_FDCAN_DISABLE_IT(&self->can, (fifo_id == FDCAN_RX_FIFO0) ? - FDCAN_IT_RX_FIFO0_MESSAGE_LOST : FDCAN_IT_RX_FIFO1_MESSAGE_LOST); - __HAL_FDCAN_CLEAR_FLAG(&self->can, (fifo_id == FDCAN_RX_FIFO0) ? - FDCAN_FLAG_RX_FIFO0_MESSAGE_LOST : FDCAN_FLAG_RX_FIFO1_MESSAGE_LOST); + + } + if (RxFifo1ITs & FDCAN_FLAG_RX_FIFO1_MESSAGE_LOST) { + __HAL_FDCAN_DISABLE_IT(&self->can, FDCAN_IT_RX_FIFO1_MESSAGE_LOST); + __HAL_FDCAN_CLEAR_FLAG(&self->can, FDCAN_FLAG_RX_FIFO1_MESSAGE_LOST); irq_reason = MP_OBJ_NEW_SMALL_INT(2); *state = RX_STATE_FIFO_OVERFLOW; - break; - case RX_STATE_FIFO_OVERFLOW: - // This should never happen - break; + } + } + + if (ErrorStatusITs & FDCAN_FLAG_ERROR_WARNING) { + __HAL_FDCAN_CLEAR_FLAG(&self->can, FDCAN_FLAG_ERROR_WARNING); + if (Psr & FDCAN_PSR_EW) { + irq_reason = MP_OBJ_NEW_SMALL_INT(3); + // mp_printf(MICROPY_ERROR_PRINTER, "clear warning %08x\n", (can->IR & FDCAN_ERROR_STATUS_MASK)); + } + } + if (ErrorStatusITs & FDCAN_FLAG_ERROR_PASSIVE) { + __HAL_FDCAN_CLEAR_FLAG(&self->can, FDCAN_FLAG_ERROR_PASSIVE); + if (Psr & FDCAN_PSR_EP) { + irq_reason = MP_OBJ_NEW_SMALL_INT(4); + // mp_printf(MICROPY_ERROR_PRINTER, "clear passive %08x\n", (can->IR & FDCAN_ERROR_STATUS_MASK)); + } + } + if (ErrorStatusITs & FDCAN_FLAG_BUS_OFF) { + __HAL_FDCAN_CLEAR_FLAG(&self->can, FDCAN_FLAG_BUS_OFF); + if (Psr & FDCAN_PSR_BO) { + irq_reason = MP_OBJ_NEW_SMALL_INT(5); + // mp_printf(MICROPY_ERROR_PRINTER, "bus off %08x\n", (can->IR & FDCAN_ERROR_STATUS_MASK)); + } } pyb_can_handle_callback(self, fifo_id, callback, irq_reason); + // mp_printf(MICROPY_ERROR_PRINTER, "Ints: %08x, %08x, %08x\n", RxFifo0ITs, RxFifo1ITs, ErrorStatusITs); } #if defined(MICROPY_HW_CAN1_TX) diff --git a/ports/stm32/make-stmconst.py b/ports/stm32/make-stmconst.py index 217a801a8f..554d662384 100644 --- a/ports/stm32/make-stmconst.py +++ b/ports/stm32/make-stmconst.py @@ -64,7 +64,7 @@ class Lexer: ( "#define typedef", re.compile( - r"#define +(?P[A-Z0-9_]+(ext)?) +\(\([A-Za-z0-9_]+_TypeDef \*\) (?P[A-Za-z0-9_]+)\)($| +/\*)" + r"#define +(?P[A-Z0-9_]+(ext)?) +\(\([A-Za-z0-9_]+_(Global)?TypeDef \*\) (?P[A-Za-z0-9_]+)\)($| +/\*)" ), ), ("typedef struct", re.compile(r"typedef struct$")), @@ -281,6 +281,7 @@ def main(): #'CAN_FIFOMailBox', #'CAN_FilterRegister', #'CAN', + "FDCAN", "CRC", "DAC", "DBGMCU", diff --git a/ports/stm32/pyb_can.c b/ports/stm32/pyb_can.c index 3e55069ab9..8007fd9e3c 100644 --- a/ports/stm32/pyb_can.c +++ b/ports/stm32/pyb_can.c @@ -47,8 +47,14 @@ #define CAN_FIFO1 FDCAN_RX_FIFO1 #define CAN_FILTER_FIFO0 (0) -// Default timings; 125Kbps assuming 48MHz clock +// Default timings; 125Kbps +#if defined(STM32G4) +// assuming 24MHz clock +#define CAN_DEFAULT_PRESCALER (16) +#else +// assuming 48MHz clock #define CAN_DEFAULT_PRESCALER (32) +#endif #define CAN_DEFAULT_SJW (1) #define CAN_DEFAULT_BS1 (8) #define CAN_DEFAULT_BS2 (3) @@ -60,8 +66,10 @@ #define CAN1_RX0_IRQn FDCAN1_IT0_IRQn #define CAN1_RX1_IRQn FDCAN1_IT1_IRQn +#if defined(CAN2) #define CAN2_RX0_IRQn FDCAN2_IT0_IRQn #define CAN2_RX1_IRQn FDCAN2_IT1_IRQn +#endif #define CAN_IT_FIFO0_FULL FDCAN_IT_RX_FIFO0_FULL #define CAN_IT_FIFO1_FULL FDCAN_IT_RX_FIFO1_FULL @@ -326,6 +334,9 @@ STATIC mp_obj_t pyb_can_restart(mp_obj_t self_in) { can->CCCR |= FDCAN_CCCR_INIT; while ((can->CCCR & FDCAN_CCCR_INIT) == 0) { } + can->CCCR |= FDCAN_CCCR_CCE; + while ((can->CCCR & FDCAN_CCCR_CCE) == 0) { + } can->CCCR &= ~FDCAN_CCCR_INIT; while ((can->CCCR & FDCAN_CCCR_INIT)) { } @@ -348,11 +359,12 @@ STATIC mp_obj_t pyb_can_state(mp_obj_t self_in) { if (self->is_enabled) { CAN_TypeDef *can = self->can.Instance; #if MICROPY_HW_ENABLE_FDCAN - if (can->PSR & FDCAN_PSR_BO) { + uint32_t psr = can->PSR; + if (psr & FDCAN_PSR_BO) { state = CAN_STATE_BUS_OFF; - } else if (can->PSR & FDCAN_PSR_EP) { + } else if (psr & FDCAN_PSR_EP) { state = CAN_STATE_ERROR_PASSIVE; - } else if (can->PSR & FDCAN_PSR_EW) { + } else if (psr & FDCAN_PSR_EW) { state = CAN_STATE_ERROR_WARNING; } else { state = CAN_STATE_ERROR_ACTIVE; @@ -375,10 +387,6 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_can_state_obj, pyb_can_state); // Get info about error states and TX/RX buffers STATIC mp_obj_t pyb_can_info(size_t n_args, const mp_obj_t *args) { - #if MICROPY_HW_ENABLE_FDCAN - // TODO implement for FDCAN - return mp_const_none; - #else pyb_can_obj_t *self = MP_OBJ_TO_PTR(args[0]); mp_obj_list_t *list; if (n_args == 1) { @@ -392,6 +400,20 @@ STATIC mp_obj_t pyb_can_info(size_t n_args, const mp_obj_t *args) { mp_raise_ValueError(NULL); } } + + #if MICROPY_HW_ENABLE_FDCAN + FDCAN_GlobalTypeDef *can = self->can.Instance; + uint32_t esr = can->ECR; + list->items[0] = MP_OBJ_NEW_SMALL_INT((esr & FDCAN_ECR_TEC_Msk) >> FDCAN_ECR_TEC_Pos); + list->items[1] = MP_OBJ_NEW_SMALL_INT((esr & FDCAN_ECR_REC_Msk) >> FDCAN_ECR_REC_Pos); + list->items[2] = MP_OBJ_NEW_SMALL_INT(self->num_error_warning); + list->items[3] = MP_OBJ_NEW_SMALL_INT(self->num_error_passive); + list->items[4] = MP_OBJ_NEW_SMALL_INT(self->num_bus_off); + uint32_t TXEFS = can->TXEFS; + list->items[5] = MP_OBJ_NEW_SMALL_INT(TXEFS & 0x7); + list->items[6] = MP_OBJ_NEW_SMALL_INT((can->RXF0S & FDCAN_RXF0S_F0FL_Msk) >> FDCAN_RXF0S_F0FL_Pos); + list->items[7] = MP_OBJ_NEW_SMALL_INT((can->RXF1S & FDCAN_RXF1S_F1FL_Msk) >> FDCAN_RXF1S_F1FL_Pos); + #else CAN_TypeDef *can = self->can.Instance; uint32_t esr = can->ESR; list->items[0] = MP_OBJ_NEW_SMALL_INT(esr >> CAN_ESR_TEC_Pos & 0xff); @@ -403,8 +425,9 @@ STATIC mp_obj_t pyb_can_info(size_t n_args, const mp_obj_t *args) { list->items[5] = MP_OBJ_NEW_SMALL_INT(n_tx_pending); list->items[6] = MP_OBJ_NEW_SMALL_INT(can->RF0R >> CAN_RF0R_FMP0_Pos & 3); list->items[7] = MP_OBJ_NEW_SMALL_INT(can->RF1R >> CAN_RF1R_FMP1_Pos & 3); - return MP_OBJ_FROM_PTR(list); #endif + + return MP_OBJ_FROM_PTR(list); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_can_info_obj, 1, 2, pyb_can_info); From 517e82eb6b7d93134637b5658132479f4fc260e3 Mon Sep 17 00:00:00 2001 From: Herwin Grobben Date: Wed, 1 Dec 2021 17:10:22 +0100 Subject: [PATCH 056/619] stm32/fdcan: Fix naming with regards to G4 series. --- ports/stm32/fdcan.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ports/stm32/fdcan.c b/ports/stm32/fdcan.c index 778902735e..8465b806ae 100644 --- a/ports/stm32/fdcan.c +++ b/ports/stm32/fdcan.c @@ -48,6 +48,14 @@ #define FDCAN_RX_FIFO1_MASK (FDCAN_FLAG_RX_FIFO1_MESSAGE_LOST | FDCAN_FLAG_RX_FIFO1_FULL | FDCAN_FLAG_RX_FIFO1_NEW_MESSAGE) #define FDCAN_ERROR_STATUS_MASK (FDCAN_FLAG_ERROR_PASSIVE | FDCAN_FLAG_ERROR_WARNING | FDCAN_FLAG_BUS_OFF) +#if defined(STM32H7) +// adaptations for H7 to G4 naming convention in HAL +#define FDCAN_IT_GROUP_RX_FIFO0 (FDCAN_ILS_RF0NL | FDCAN_ILS_RF0FL | FDCAN_ILS_RF0LL) +#define FDCAN_IT_GROUP_BIT_LINE_ERROR (FDCAN_ILS_EPE | FDCAN_ILS_ELOE) +#define FDCAN_IT_GROUP_PROTOCOL_ERROR (FDCAN_ILS_ARAE | FDCAN_ILS_PEDE | FDCAN_ILS_PEAE | FDCAN_ILS_WDIE | FDCAN_ILS_BOE | FDCAN_ILS_EWE) +#define FDCAN_IT_GROUP_RX_FIFO1 (FDCAN_ILS_RF1NL | FDCAN_ILS_RF1FL | FDCAN_ILS_RF1LL) +#endif + // also defined in _hal_fdcan.c, but not able to declare extern and reach the variable static const uint8_t DLCtoBytes[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64}; From 14becd80c9587e7afed56aebab4bf347971ff6e4 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 31 Jan 2022 16:38:32 +1100 Subject: [PATCH 057/619] stm32/usbd_conf: Set lpm_enable and battery_charging_enable on all MCUs. Signed-off-by: Damien George --- ports/stm32/usbd_conf.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/ports/stm32/usbd_conf.c b/ports/stm32/usbd_conf.c index 50e10ba178..1fb2777831 100644 --- a/ports/stm32/usbd_conf.c +++ b/ports/stm32/usbd_conf.c @@ -391,10 +391,8 @@ USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev, int high_speed, const pcd_fs_handle.Init.phy_itface = PCD_PHY_EMBEDDED; pcd_fs_handle.Init.Sof_enable = 0; pcd_fs_handle.Init.speed = PCD_SPEED_FULL; - #if defined(STM32L4) pcd_fs_handle.Init.lpm_enable = DISABLE; pcd_fs_handle.Init.battery_charging_enable = DISABLE; - #endif #if MICROPY_HW_USB_IS_MULTI_OTG pcd_fs_handle.Init.use_dedicated_ep1 = 0; pcd_fs_handle.Init.dma_enable = 0; From 4a4f269a1ae0e23c0d9815c7f8b1b7ff6ba22cc1 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 31 Jan 2022 16:39:26 +1100 Subject: [PATCH 058/619] stm32/powerctrlboot: Set HAL uwTickPrio variable when needed. Signed-off-by: Damien George --- ports/stm32/powerctrlboot.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ports/stm32/powerctrlboot.c b/ports/stm32/powerctrlboot.c index caa5632926..41b56296e0 100644 --- a/ports/stm32/powerctrlboot.c +++ b/ports/stm32/powerctrlboot.c @@ -33,6 +33,11 @@ void powerctrl_config_systick(void) { SysTick->CTRL |= SYSTICK_CLKSOURCE_HCLK; SysTick_Config(HAL_RCC_GetHCLKFreq() / 1000); NVIC_SetPriority(SysTick_IRQn, IRQ_PRI_SYSTICK); + + #if !BUILDING_MBOOT && (defined(STM32H7) || defined(STM32L4) || defined(STM32WB)) + // Set SysTick IRQ priority variable in case the HAL needs to use it + uwTickPrio = IRQ_PRI_SYSTICK; + #endif } #if defined(STM32F0) From c99ed8d6fa9d4429bf78bcd965ce60ee747e67c3 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 31 Jan 2022 16:40:15 +1100 Subject: [PATCH 059/619] stm32/powerctrl: Write bootloader-state as 64-bit word to work on H7. H7 MCUs have ECC and writes do not go through to SRAM until 64-bits have been written (on another location is written). So use 64-bit writes for the bootloader-state variable so it is committed before the system reset. As part of this change, the lower byte of the bootloader address in BL_STATE must now be the magic number 0x5a5 for the state to be valid (previously this was 0x000 which is not as robust). Signed-off-by: Damien George --- ports/stm32/powerctrl.c | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/ports/stm32/powerctrl.c b/ports/stm32/powerctrl.c index 406325581b..a3f8207e9a 100644 --- a/ports/stm32/powerctrl.c +++ b/ports/stm32/powerctrl.c @@ -75,9 +75,19 @@ #endif #if MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET -// Location in RAM of bootloader state (just after the top of the stack) -extern uint32_t _estack[]; -#define BL_STATE ((uint32_t *)&_estack) +// Location in RAM of bootloader state (just after the top of the stack). +// STM32H7 has ECC and writes to RAM must be 64-bit so they are fully committed +// to actual SRAM before a system reset occurs. +#define BL_STATE_PTR ((uint64_t *)&_estack) +#define BL_STATE_KEY (0x5a5) +#define BL_STATE_KEY_MASK (0xfff) +#define BL_STATE_KEY_SHIFT (32) +#define BL_STATE_INVALID (0) +#define BL_STATE_VALID(reg, addr) ((uint64_t)(reg) | ((uint64_t)((addr) | BL_STATE_KEY)) << BL_STATE_KEY_SHIFT) +#define BL_STATE_GET_REG(s) ((s) & 0xffffffff) +#define BL_STATE_GET_KEY(s) (((s) >> BL_STATE_KEY_SHIFT) & BL_STATE_KEY_MASK) +#define BL_STATE_GET_ADDR(s) (((s) >> BL_STATE_KEY_SHIFT) & ~BL_STATE_KEY_MASK) +extern uint64_t _estack[]; #endif static inline void powerctrl_disable_hsi_if_unused(void) { @@ -89,7 +99,7 @@ static inline void powerctrl_disable_hsi_if_unused(void) { NORETURN void powerctrl_mcu_reset(void) { #if MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET - BL_STATE[1] = 1; // invalidate bootloader address + *BL_STATE_PTR = BL_STATE_INVALID; #if __DCACHE_PRESENT == 1 SCB_CleanDCache(); #endif @@ -112,8 +122,7 @@ NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr) { // Enter the bootloader via a reset, so everything is reset (including WDT). // Upon reset powerctrl_check_enter_bootloader() will jump to the bootloader. - BL_STATE[0] = r0; - BL_STATE[1] = bl_addr; + *BL_STATE_PTR = BL_STATE_VALID(r0, bl_addr); #if __DCACHE_PRESENT == 1 SCB_CleanDCache(); #endif @@ -129,16 +138,15 @@ NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr) { void powerctrl_check_enter_bootloader(void) { #if MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET - uint32_t bl_addr = BL_STATE[1]; - BL_STATE[1] = 1; // invalidate bootloader address - if ((bl_addr & 0xfff) == 0 && (RCC->RCC_SR & RCC_SR_SFTRSTF)) { + uint64_t bl_state = *BL_STATE_PTR; + *BL_STATE_PTR = BL_STATE_INVALID; + if (BL_STATE_GET_KEY(bl_state) == BL_STATE_KEY && (RCC->RCC_SR & RCC_SR_SFTRSTF)) { // Reset by NVIC_SystemReset with bootloader data set -> branch to bootloader RCC->RCC_SR = RCC_SR_RMVF; #if defined(STM32F0) || defined(STM32F4) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) __HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH(); #endif - uint32_t r0 = BL_STATE[0]; - branch_to_bootloader(r0, bl_addr); + branch_to_bootloader(BL_STATE_GET_REG(bl_state), BL_STATE_GET_ADDR(bl_state)); } #endif } From c8c229b96c892ceb8ca1cf72e4a87096617ede64 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 31 Jan 2022 16:47:03 +1100 Subject: [PATCH 060/619] stm32/mboot: Use PLL3 for USB clock source on H7 MCUs. PLL3-Q is more reliable than PLL1-Q for the USB clock source when entering mboot from various reset states (eg power on vs MCU reset). (It was found that if the main application used PLL3-Q then sometimes the USB clock source would stay stuck on PLL3-Q and not switch to PLL1-Q after a reset.) Other related changes: - SystemCoreClockUpdate() should be called on H7 because the calculation can be involved in some cases. - __set_PRIMASK(0) should be called because on H7 the built-in ST DFU bootloader exits with IRQs disabled. Signed-off-by: Damien George --- ports/stm32/mboot/main.c | 42 +++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/ports/stm32/mboot/main.c b/ports/stm32/mboot/main.c index 3c3e4a8642..ae409168b9 100644 --- a/ports/stm32/mboot/main.c +++ b/ports/stm32/mboot/main.c @@ -265,27 +265,48 @@ void SystemClock_Config(void) { while (__HAL_RCC_GET_FLAG(RCC_FLAG_PLLRDY) != RESET) { } - // Configure PLL1 factors and source - RCC->PLLCKSELR = - MICROPY_HW_CLK_PLLM << RCC_PLLCKSELR_DIVM1_Pos - | 2 << RCC_PLLCKSELR_PLLSRC_Pos; // HSE selected as PLL source + // Disable PLL3 + __HAL_RCC_PLL3_DISABLE(); + while (__HAL_RCC_GET_FLAG(RCC_FLAG_PLL3RDY) != RESET) { + } + + // Select HSE as PLLx source + RCC->PLLCKSELR = 2 << RCC_PLLCKSELR_PLLSRC_Pos; + RCC->PLLCFGR = 0; + + // Configure PLL1 for use by SYSCLK + RCC->PLLCKSELR |= MICROPY_HW_CLK_PLLM << RCC_PLLCKSELR_DIVM1_Pos; + RCC->PLLCFGR |= RCC_PLLCFGR_DIVP1EN; + RCC->PLL1FRACR = 0; RCC->PLL1DIVR = (MICROPY_HW_CLK_PLLN - 1) << RCC_PLL1DIVR_N1_Pos | (MICROPY_HW_CLK_PLLP - 1) << RCC_PLL1DIVR_P1_Pos // only even P allowed | (MICROPY_HW_CLK_PLLQ - 1) << RCC_PLL1DIVR_Q1_Pos | (MICROPY_HW_CLK_PLLR - 1) << RCC_PLL1DIVR_R1_Pos; - // Enable PLL1 outputs for SYSCLK and USB - RCC->PLLCFGR = RCC_PLLCFGR_DIVP1EN | RCC_PLLCFGR_DIVQ1EN; + // Configure PLL3 for use by USB at Q=48MHz + RCC->PLLCKSELR |= MICROPY_HW_CLK_PLL3M << RCC_PLLCKSELR_DIVM3_Pos; + RCC->PLLCFGR |= RCC_PLLCFGR_DIVQ3EN; + RCC->PLL3FRACR = 0; + RCC->PLL3DIVR = + (MICROPY_HW_CLK_PLL3N - 1) << RCC_PLL3DIVR_N3_Pos + | (MICROPY_HW_CLK_PLL3P - 1) << RCC_PLL3DIVR_P3_Pos // only even P allowed + | (MICROPY_HW_CLK_PLL3Q - 1) << RCC_PLL3DIVR_Q3_Pos + | (MICROPY_HW_CLK_PLL3R - 1) << RCC_PLL3DIVR_R3_Pos; - // Select PLL1-Q for USB clock source - RCC->D2CCIP2R |= 1 << RCC_D2CCIP2R_USBSEL_Pos; + // Select PLL3-Q for USB clock source + MODIFY_REG(RCC->D2CCIP2R, RCC_D2CCIP2R_USBSEL, RCC_D2CCIP2R_USBSEL_1); // Enable PLL1 __HAL_RCC_PLL_ENABLE(); while(__HAL_RCC_GET_FLAG(RCC_FLAG_PLLRDY) == RESET) { } + // Enable PLL3 + __HAL_RCC_PLL3_ENABLE(); + while(__HAL_RCC_GET_FLAG(RCC_FLAG_PLL3RDY) == RESET) { + } + // Increase latency before changing SYSCLK if (MICROPY_HW_FLASH_LATENCY > (FLASH->ACR & FLASH_ACR_LATENCY)) { __HAL_FLASH_SET_LATENCY(MICROPY_HW_FLASH_LATENCY); @@ -320,7 +341,7 @@ void SystemClock_Config(void) { ; // Update clock value and reconfigure systick now that the frequency changed - SystemCoreClock = CORE_PLL_FREQ; + SystemCoreClockUpdate(); systick_init(); } @@ -1486,6 +1507,9 @@ enter_bootloader: // set the system clock to be HSE SystemClock_Config(); + // Ensure IRQs are enabled (needed coming out of ST bootloader on H7) + __set_PRIMASK(0); + #if USE_USB_POLLING // irqs with a priority value greater or equal to "pri" will be disabled // "pri" should be between 1 and 15 inclusive From 29867a24391ae3938ae43b354297139b7a229b73 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 31 Jan 2022 16:55:07 +1100 Subject: [PATCH 061/619] stm32/mboot: Include hal_rcc_ex.c in source file list. It's needed at least on F4 because this file overrides the weak function HAL_RCC_DeInit() from hal_rcc.c. Signed-off-by: Damien George --- ports/stm32/mboot/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/stm32/mboot/Makefile b/ports/stm32/mboot/Makefile index 7e1b497cef..20b0db9070 100755 --- a/ports/stm32/mboot/Makefile +++ b/ports/stm32/mboot/Makefile @@ -162,6 +162,7 @@ SRC_HAL += $(addprefix $(HAL_DIR)/Src/stm32$(MCU_SERIES)xx_,\ hal_pcd.c \ hal_pcd_ex.c \ hal_rcc.c \ + hal_rcc_ex.c \ ll_usb.c \ ) From ca1914fb474dd7a2a6e94ba835cea45b0bcd742b Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 31 Jan 2022 16:55:50 +1100 Subject: [PATCH 062/619] stm32/mboot: Support H7 MCUs in fwupdate.py. And optimise the speed of flash writing. Signed-off-by: Damien George --- ports/stm32/mboot/fwupdate.py | 137 +++++++++++++++++++++++----------- 1 file changed, 93 insertions(+), 44 deletions(-) diff --git a/ports/stm32/mboot/fwupdate.py b/ports/stm32/mboot/fwupdate.py index d7c8f46db4..8a366dae29 100644 --- a/ports/stm32/mboot/fwupdate.py +++ b/ports/stm32/mboot/fwupdate.py @@ -1,5 +1,5 @@ # Update Mboot or MicroPython from a .dfu.gz file on the board's filesystem -# MIT license; Copyright (c) 2019-2020 Damien P. George +# MIT license; Copyright (c) 2019-2022 Damien P. George from micropython import const import struct, time @@ -16,9 +16,6 @@ _ELEM_TYPE_MOUNT = const(2) _ELEM_TYPE_FSLOAD = const(3) _ELEM_TYPE_STATUS = const(4) -FLASH_KEY1 = 0x45670123 -FLASH_KEY2 = 0xCDEF89AB - def check_mem_contains(addr, buf): mem8 = stm.mem8 @@ -29,15 +26,6 @@ def check_mem_contains(addr, buf): return True -def check_mem_erased(addr, size): - mem16 = stm.mem16 - r = range(0, size, 2) - for off in r: - if mem16[addr + off] != 0xFFFF: - return False - return True - - def dfu_read(filename): f = open(filename, "rb") @@ -85,39 +73,99 @@ def dfu_read(filename): return elems -def flash_wait_not_busy(): - while stm.mem32[stm.FLASH + stm.FLASH_SR] & 1 << 16: - machine.idle() +class Flash: + _FLASH_KEY1 = 0x45670123 + _FLASH_KEY2 = 0xCDEF89AB + def __init__(self): + import os, uctypes -def flash_unlock(): - stm.mem32[stm.FLASH + stm.FLASH_KEYR] = FLASH_KEY1 - stm.mem32[stm.FLASH + stm.FLASH_KEYR] = FLASH_KEY2 + self.addressof = uctypes.addressof + # Detect MCU. + machine = os.uname().machine + if "STM32F4" in machine or "STM32F7" in machine: + dev_id = stm.mem32[0xE004_2000] & 0xFFF + elif "STM32H7" in machine: + dev_id = stm.mem32[0x5C00_1000] & 0xFFF + else: + dev_id = 0 -def flash_lock(): - stm.mem32[stm.FLASH + stm.FLASH_CR] = 1 << 31 # LOCK + # Configure flash parameters based on MCU. + if dev_id in (0x413, 0x419, 0x431, 0x451, 0x452): + # 0x413: STM32F405/407, STM32F415/417 + # 0x419: STM32F42x/43x + # 0x431: STM32F411 + # 0x451: STM32F76x/77x + # 0x452: STM32F72x/73x + self._keyr = stm.FLASH + stm.FLASH_KEYR + self._sr = stm.FLASH + stm.FLASH_SR + self._sr_busy = 1 << 16 + self._cr = stm.FLASH + stm.FLASH_CR + self._cr_lock = 1 << 31 + self._cr_init_erase = lambda s: 2 << 8 | s << 3 | 1 << 1 # PSIZE=32-bits, SNB, SER + self._cr_start_erase = 1 << 16 # STRT + self._cr_init_write = 2 << 8 | 1 << 0 # PSIZE=32-bits, PG + self._cr_flush = None + self._write_multiple = 4 + if dev_id == 0x451 and stm.mem32[0x1FFF_0008] & 1 << 13: # check nDBANK + # STM32F76x/77x in single-bank mode + self.sector0_size = 32 * 1024 + else: + self.sector0_size = 16 * 1024 + elif dev_id == 0x450: + # 0x450: STM32H742, STM32H743/753, STM32H750 + self._keyr = stm.FLASH + stm.FLASH_KEYR1 + self._sr = stm.FLASH + stm.FLASH_SR1 + self._sr_busy = 1 << 2 # QW1 + self._cr = stm.FLASH + stm.FLASH_CR1 + self._cr_lock = 1 << 0 # LOCK1 + self._cr_init_erase = lambda s: s << 8 | 3 << 4 | 1 << 2 # SNB1, PSIZE1=64-bits, SER1 + self._cr_start_erase = 1 << 7 # START1 + self._cr_init_write = 3 << 4 | 1 << 1 # PSIZE1=64-bits, PG1=1 + self._cr_flush = 1 << 6 # FW1=1 + self._write_multiple = 16 + self.sector0_size = 128 * 1024 + else: + raise Exception(f"unknown MCU {machine} DEV_ID=0x{dev_id:x}") + def wait_not_busy(self): + while stm.mem32[self._sr] & self._sr_busy: + machine.idle() -def flash_erase_sector(sector): - assert 0 <= sector <= 7 # for F722 - flash_wait_not_busy() - cr = 2 << 8 | sector << 3 | 1 << 1 # PSIZE = 32 bits # SNB # SER - stm.mem32[stm.FLASH + stm.FLASH_CR] = cr - stm.mem32[stm.FLASH + stm.FLASH_CR] = cr | 1 << 16 # STRT - flash_wait_not_busy() - stm.mem32[stm.FLASH + stm.FLASH_CR] = 0 + def unlock(self): + if stm.mem32[self._cr] & self._cr_lock: + stm.mem32[self._keyr] = self._FLASH_KEY1 + stm.mem32[self._keyr] = self._FLASH_KEY2 + def lock(self): + stm.mem32[self._cr] = self._cr_lock -def flash_write(addr, buf): - assert len(buf) % 4 == 0 - flash_wait_not_busy() - cr = 2 << 8 | 1 << 0 # PSIZE = 32 bits # PG - stm.mem32[stm.FLASH + stm.FLASH_CR] = cr - for off in range(0, len(buf), 4): - stm.mem32[addr + off] = struct.unpack_from("I", buf, off)[0] - flash_wait_not_busy() - stm.mem32[stm.FLASH + stm.FLASH_CR] = 0 + def erase_sector(self, sector): + assert 0 <= sector <= 7 + self.wait_not_busy() + stm.mem32[self._cr] = self._cr_init_erase(sector) + stm.mem32[self._cr] |= self._cr_start_erase + self.wait_not_busy() + stm.mem32[self._cr] = 0 + + # This method is optimised for speed, to reduce the time data is being written. + def write(self, addr, buf): + assert len(buf) % 4 == 0 + mem32 = stm.mem32 + buf_addr = self.addressof(buf) + r = range(0, len(buf), 4) + self.wait_not_busy() + mem32[self._cr] = self._cr_init_write + for off in r: + mem32[addr + off] = mem32[buf_addr + off] + if off % self._write_multiple == 0: + while mem32[self._sr] & self._sr_busy: + pass + if self._cr_flush is not None: + mem32[self._cr] |= self._cr_flush + self.wait_not_busy() + mem32[self._cr] = 0 def update_mboot(filename): @@ -144,13 +192,14 @@ def update_mboot(filename): print("Programming Mboot, do not turn off!") time.sleep_ms(50) + flash = Flash() irq = machine.disable_irq() - flash_unlock() - flash_erase_sector(0) - if len(mboot_fw) > 16 * 1024 and not check_mem_erased(mboot_addr + 16 * 1024, 16 * 1024): - flash_erase_sector(1) - flash_write(mboot_addr, mboot_fw) - flash_lock() + flash.unlock() + flash.erase_sector(0) + if len(mboot_fw) > flash.sector0_size: + flash.erase_sector(1) + flash.write(mboot_addr, mboot_fw) + flash.lock() machine.enable_irq(irq) print("New Mboot programmed.") From 60e05ae84ec08c06e3f9d9051339811641479a94 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 31 Jan 2022 17:26:47 +1100 Subject: [PATCH 063/619] stm32/mboot: Compute and check CRC32 of dfu file in fwupdate.py. Signed-off-by: Damien George --- ports/stm32/mboot/fwupdate.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/ports/stm32/mboot/fwupdate.py b/ports/stm32/mboot/fwupdate.py index 8a366dae29..3b8dc51e1c 100644 --- a/ports/stm32/mboot/fwupdate.py +++ b/ports/stm32/mboot/fwupdate.py @@ -27,6 +27,8 @@ def check_mem_contains(addr, buf): def dfu_read(filename): + from binascii import crc32 + f = open(filename, "rb") hdr = f.read(3) @@ -39,23 +41,28 @@ def dfu_read(filename): print("Invalid firmware", filename) return None + crc = 0 elems = [] hdr = f.read(11) + crc = crc32(hdr, crc) sig, ver, size, num_targ = struct.unpack("<5sBIB", hdr) file_offset = 11 for i in range(num_targ): hdr = f.read(274) + crc = crc32(hdr, crc) sig, alt, has_name, name, t_size, num_elem = struct.unpack("<6sBi255sII", hdr) file_offset += 274 file_offset_t = file_offset for j in range(num_elem): hdr = f.read(8) + crc = crc32(hdr, crc) addr, e_size = struct.unpack(" Date: Tue, 26 Jan 2021 14:49:56 +0100 Subject: [PATCH 064/619] stm32: Add support for G4 MCUs, and add NUCLEO_G474RE board defn. This commit adds support for the STM32G4 series of MCUs, and a board definition for NUCLEO_G474RE. This board has the REPL on LPUART1 which is connected to the on-board ST-link USB-UART. --- ports/stm32/Makefile | 5 + ports/stm32/adc.c | 50 +++- ports/stm32/adc.h | 2 + ports/stm32/boards/NUCLEO_G474RE/board.json | 15 ++ .../boards/NUCLEO_G474RE/mpconfigboard.h | 95 +++++++ .../boards/NUCLEO_G474RE/mpconfigboard.mk | 6 + ports/stm32/boards/NUCLEO_G474RE/pins.csv | 86 +++++++ .../boards/NUCLEO_G474RE/stm32g4xx_hal_conf.h | 19 ++ ports/stm32/boards/make-pins.py | 25 +- ports/stm32/boards/stm32g474.ld | 28 +++ ports/stm32/boards/stm32g474_af.csv | 109 ++++++++ ports/stm32/boards/stm32g4xx_hal_conf_base.h | 131 ++++++++++ ports/stm32/dac.c | 8 +- ports/stm32/dma.c | 237 +++++++++++++++++- ports/stm32/dma.h | 23 ++ ports/stm32/extint.c | 18 +- ports/stm32/extint.h | 2 +- ports/stm32/flash.c | 39 ++- ports/stm32/machine_adc.c | 27 +- ports/stm32/machine_uart.c | 2 +- ports/stm32/modmachine.c | 2 +- ports/stm32/mpconfigboard_common.h | 17 +- ports/stm32/mphalport.c | 2 +- ports/stm32/pin_defs_stm32.h | 4 +- ports/stm32/powerctrl.c | 13 +- ports/stm32/pyb_i2c.c | 24 +- ports/stm32/rtc.c | 21 +- ports/stm32/stm32_it.c | 20 +- ports/stm32/system_stm32.c | 62 ++++- ports/stm32/timer.c | 14 +- ports/stm32/uart.c | 27 +- ports/stm32/usbdev/core/inc/usbd_def.h | 8 +- 32 files changed, 1050 insertions(+), 91 deletions(-) create mode 100644 ports/stm32/boards/NUCLEO_G474RE/board.json create mode 100644 ports/stm32/boards/NUCLEO_G474RE/mpconfigboard.h create mode 100644 ports/stm32/boards/NUCLEO_G474RE/mpconfigboard.mk create mode 100644 ports/stm32/boards/NUCLEO_G474RE/pins.csv create mode 100644 ports/stm32/boards/NUCLEO_G474RE/stm32g4xx_hal_conf.h create mode 100644 ports/stm32/boards/stm32g474.ld create mode 100644 ports/stm32/boards/stm32g474_af.csv create mode 100644 ports/stm32/boards/stm32g4xx_hal_conf_base.h diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index 290503937d..c2581c100b 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -94,6 +94,7 @@ endif CFLAGS_MCU_f0 = $(CFLAGS_CORTEX_M) -mtune=cortex-m0 -mcpu=cortex-m0 CFLAGS_MCU_f4 = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4 CFLAGS_MCU_f7 = $(CFLAGS_CORTEX_M) -mtune=cortex-m7 -mcpu=cortex-m7 +CFLAGS_MCU_g4 = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4 CFLAGS_MCU_l0 = $(CFLAGS_CORTEX_M) -mtune=cortex-m0plus -mcpu=cortex-m0plus CFLAGS_MCU_l4 = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4 CFLAGS_MCU_h7 = $(CFLAGS_CORTEX_M) -mtune=cortex-m7 -mcpu=cortex-m7 @@ -429,6 +430,10 @@ $(BUILD)/$(HAL_DIR)/Src/stm32$(MCU_SERIES)xx_hal_mmc.o: CFLAGS += -Wno-error=par endif endif +ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),g4)) + HAL_SRC_C += $(addprefix $(HAL_DIR)/Src/stm32$(MCU_SERIES)xx_, hal_fdcan.c) +endif + ifeq ($(CMSIS_MCU),$(filter $(CMSIS_MCU),STM32H743xx STM32H750xx STM32H7A3xx STM32H7A3xxQ STM32H7B3xx STM32H7B3xxQ)) HAL_SRC_C += $(addprefix $(HAL_DIR)/Src/stm32$(MCU_SERIES)xx_, hal_fdcan.c) else diff --git a/ports/stm32/adc.c b/ports/stm32/adc.c index 3e61abce3c..2e52fb07aa 100644 --- a/ports/stm32/adc.c +++ b/ports/stm32/adc.c @@ -102,6 +102,16 @@ #define ADC_CAL2 ((uint16_t *)(ADC_CAL_ADDRESS + 4)) #define ADC_CAL_BITS (12) +#elif defined(STM32G4) + +#define ADC_FIRST_GPIO_CHANNEL (0) +#define ADC_LAST_GPIO_CHANNEL (18) +#define ADC_SCALE_V (((float)VREFINT_CAL_VREF) / 1000.0f) +#define ADC_CAL_ADDRESS VREFINT_CAL_ADDR +#define ADC_CAL1 TEMPSENSOR_CAL1_ADDR +#define ADC_CAL2 TEMPSENSOR_CAL2_ADDR +#define ADC_CAL_BITS (12) // UM2570, __HAL_ADC_CALC_TEMPERATURE: 'corresponds to a resolution of 12 bits' + #elif defined(STM32H7) #define ADC_SCALE_V (3.3f) @@ -141,6 +151,8 @@ defined(STM32F746xx) || defined(STM32F765xx) || \ defined(STM32F767xx) || defined(STM32F769xx) #define VBAT_DIV (4) +#elif defined(STM32G4) +#define VBAT_DIV (3) #elif defined(STM32H743xx) || defined(STM32H747xx) || \ defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || \ defined(STM32H7B3xx) || defined(STM32H7B3xxQ) || \ @@ -206,7 +218,7 @@ STATIC bool is_adcx_channel(int channel) { ADC_HandleTypeDef handle; handle.Instance = ADCx; return IS_ADC_CHANNEL(&handle, channel); - #elif defined(STM32WB) + #elif defined(STM32G4) || defined(STM32WB) ADC_HandleTypeDef handle; handle.Instance = ADCx; return __HAL_ADC_IS_CHANNEL_INTERNAL(channel) @@ -220,7 +232,7 @@ STATIC void adc_wait_for_eoc_or_timeout(ADC_HandleTypeDef *adcHandle, int32_t ti uint32_t tickstart = HAL_GetTick(); #if defined(STM32F4) || defined(STM32F7) while ((adcHandle->Instance->SR & ADC_FLAG_EOC) != ADC_FLAG_EOC) { - #elif defined(STM32F0) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB) + #elif defined(STM32F0) || defined(STM32G4) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB) while (READ_BIT(adcHandle->Instance->ISR, ADC_FLAG_EOC) != ADC_FLAG_EOC) { #else #error Unsupported processor @@ -237,6 +249,8 @@ STATIC void adcx_clock_enable(ADC_HandleTypeDef *adch) { #elif defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ) __HAL_RCC_ADC12_CLK_ENABLE(); __HAL_RCC_ADC_CONFIG(RCC_ADCCLKSOURCE_CLKP); + #elif defined(STM32G4) + __HAL_RCC_ADC12_CLK_ENABLE(); #elif defined(STM32H7) if (adch->Instance == ADC3) { __HAL_RCC_ADC3_CLK_ENABLE(); @@ -286,7 +300,7 @@ STATIC void adcx_init_periph(ADC_HandleTypeDef *adch, uint32_t resolution) { adch->Init.OversamplingMode = DISABLE; adch->Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE; adch->Init.ConversionDataManagement = ADC_CONVERSIONDATA_DR; - #elif defined(STM32L4) || defined(STM32WB) + #elif defined(STM32G4) || defined(STM32L4) || defined(STM32WB) adch->Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1; adch->Init.ScanConvMode = ADC_SCAN_DISABLE; adch->Init.LowPowerAutoWait = DISABLE; @@ -303,7 +317,7 @@ STATIC void adcx_init_periph(ADC_HandleTypeDef *adch, uint32_t resolution) { #if defined(STM32H7) HAL_ADCEx_Calibration_Start(adch, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED); #endif - #if defined(STM32L4) || defined(STM32WB) + #if defined(STM32G4) || defined(STM32L4) || defined(STM32WB) HAL_ADCEx_Calibration_Start(adch, ADC_SINGLE_ENDED); #endif } @@ -312,7 +326,7 @@ STATIC void adc_init_single(pyb_obj_adc_t *adc_obj) { adc_obj->handle.Instance = ADCx; adcx_init_periph(&adc_obj->handle, ADC_RESOLUTION_12B); - #if defined(STM32L4) && defined(ADC_DUALMODE_REGSIMULT_INJECSIMULT) + #if (defined(STM32G4) || defined(STM32L4)) && defined(ADC_DUALMODE_REGSIMULT_INJECSIMULT) ADC_MultiModeTypeDef multimode; multimode.Mode = ADC_MODE_INDEPENDENT; if (HAL_ADCEx_MultiModeConfigChannel(&adc_obj->handle, &multimode) != HAL_OK) { @@ -324,7 +338,7 @@ STATIC void adc_init_single(pyb_obj_adc_t *adc_obj) { STATIC void adc_config_channel(ADC_HandleTypeDef *adc_handle, uint32_t channel) { ADC_ChannelConfTypeDef sConfig; - #if defined(STM32H7) || defined(STM32WB) + #if defined(STM32G4) || defined(STM32H7) || defined(STM32WB) sConfig.Rank = ADC_REGULAR_RANK_1; if (__HAL_ADC_IS_CHANNEL_INTERNAL(channel) == 0) { channel = __HAL_ADC_DECIMAL_NB_TO_CHANNEL(channel); @@ -352,7 +366,7 @@ STATIC void adc_config_channel(ADC_HandleTypeDef *adc_handle, uint32_t channel) sConfig.OffsetNumber = ADC_OFFSET_NONE; sConfig.OffsetRightShift = DISABLE; sConfig.OffsetSignedSaturation = DISABLE; - #elif defined(STM32L4) || defined(STM32WB) + #elif defined(STM32G4) || defined(STM32L4) || defined(STM32WB) if (__HAL_ADC_IS_CHANNEL_INTERNAL(channel)) { sConfig.SamplingTime = ADC_SAMPLETIME_247CYCLES_5; } else { @@ -536,7 +550,7 @@ STATIC mp_obj_t adc_read_timed(mp_obj_t self_in, mp_obj_t buf_in, mp_obj_t freq_ // for subsequent samples we can just set the "start sample" bit #if defined(STM32F4) || defined(STM32F7) self->handle.Instance->CR2 |= (uint32_t)ADC_CR2_SWSTART; - #elif defined(STM32F0) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB) + #elif defined(STM32F0) || defined(STM32G4) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB) SET_BIT(self->handle.Instance->CR, ADC_CR_ADSTART); #else #error Unsupported processor @@ -646,7 +660,7 @@ STATIC mp_obj_t adc_read_timed_multi(mp_obj_t adc_array_in, mp_obj_t buf_array_i // ADC is started: set the "start sample" bit #if defined(STM32F4) || defined(STM32F7) adc->handle.Instance->CR2 |= (uint32_t)ADC_CR2_SWSTART; - #elif defined(STM32F0) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB) + #elif defined(STM32F0) || defined(STM32G4) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB) SET_BIT(adc->handle.Instance->CR, ADC_CR_ADSTART); #else #error Unsupported processor @@ -766,7 +780,16 @@ STATIC uint32_t adc_config_and_read_ref(ADC_HandleTypeDef *adcHandle, uint32_t c } int adc_read_core_temp(ADC_HandleTypeDef *adcHandle) { + #if defined(STM32G4) + int32_t raw_value = 0; + if (adcHandle->Instance == ADC1) { + raw_value = adc_config_and_read_ref(adcHandle, ADC_CHANNEL_TEMPSENSOR_ADC1); + } else { + return 0; + } + #else int32_t raw_value = adc_config_and_read_ref(adcHandle, ADC_CHANNEL_TEMPSENSOR); + #endif return ((raw_value - CORE_TEMP_V25) / CORE_TEMP_AVG_SLOPE) + 25; } @@ -775,7 +798,16 @@ int adc_read_core_temp(ADC_HandleTypeDef *adcHandle) { STATIC volatile float adc_refcor = 1.0f; float adc_read_core_temp_float(ADC_HandleTypeDef *adcHandle) { + #if defined(STM32G4) + int32_t raw_value = 0; + if (adcHandle->Instance == ADC1) { + raw_value = adc_config_and_read_ref(adcHandle, ADC_CHANNEL_TEMPSENSOR_ADC1); + } else { + return 0; + } + #else int32_t raw_value = adc_config_and_read_ref(adcHandle, ADC_CHANNEL_TEMPSENSOR); + #endif float core_temp_avg_slope = (*ADC_CAL2 - *ADC_CAL1) / 80.0f; return (((float)raw_value * adc_refcor - *ADC_CAL1) / core_temp_avg_slope) + 30.0f; } diff --git a/ports/stm32/adc.h b/ports/stm32/adc.h index 9101b9db83..db706ea494 100644 --- a/ports/stm32/adc.h +++ b/ports/stm32/adc.h @@ -48,6 +48,8 @@ static inline void adc_deselect_vbat(ADC_TypeDef *adc, uint32_t channel) { adc_common = ADC_COMMON_REGISTER(0); #elif defined(STM32F7) adc_common = ADC123_COMMON; + #elif defined(STM32G4) + adc_common = ADC12_COMMON; #elif defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ) adc_common = ADC12_COMMON; #elif defined(STM32H7) diff --git a/ports/stm32/boards/NUCLEO_G474RE/board.json b/ports/stm32/boards/NUCLEO_G474RE/board.json new file mode 100644 index 0000000000..3bbb749826 --- /dev/null +++ b/ports/stm32/boards/NUCLEO_G474RE/board.json @@ -0,0 +1,15 @@ +{ + "deploy": [ + "../deploy.md" + ], + "docs": "", + "features": [], + "images": [ + "nucleo_g474re.jpg" + ], + "mcu": "stm32g4", + "product": "Nucleo G474RE", + "thumbnail": "", + "url": "", + "vendor": "ST Microelectronics" +} diff --git a/ports/stm32/boards/NUCLEO_G474RE/mpconfigboard.h b/ports/stm32/boards/NUCLEO_G474RE/mpconfigboard.h new file mode 100644 index 0000000000..06b4e4c52a --- /dev/null +++ b/ports/stm32/boards/NUCLEO_G474RE/mpconfigboard.h @@ -0,0 +1,95 @@ +#define MICROPY_HW_BOARD_NAME "NUCLEO_G474RE" +#define MICROPY_HW_MCU_NAME "STM32G474" + +#define MICROPY_HW_ENABLE_RTC (1) +#define MICROPY_HW_ENABLE_RNG (1) +#define MICROPY_HW_ENABLE_ADC (1) +#define MICROPY_HW_ENABLE_DAC (1) // A4, A5 +#define MICROPY_HW_ENABLE_USB (0) // A12 (dp), A11 (dm) +#define MICROPY_HW_HAS_SWITCH (1) +#define MICROPY_HW_HAS_FLASH (0) // QSPI extflash not mounted + +#define MICROPY_PY_UASYNCIO (0) +#define MICROPY_PY_UZLIB (0) +#define MICROPY_PY_UBINASCII (0) +#define MICROPY_PY_UHASHLIB (0) +#define MICROPY_PY_UJSON (0) +#define MICROPY_PY_URE (0) +#define MICROPY_PY_FRAMEBUF (0) +#define MICROPY_PY_USOCKET (0) +#define MICROPY_PY_NETWORK (0) + +// The board has an 24MHz HSE, the following gives 170MHz CPU speed +#define MICROPY_HW_CLK_PLLM (6) +#define MICROPY_HW_CLK_PLLN (85) +#define MICROPY_HW_CLK_PLLP (2) +#define MICROPY_HW_CLK_PLLQ (8) +#define MICROPY_HW_CLK_PLLR (2) + +#define MICROPY_HW_CLK_USE_HSI48 (1) // for RNG + +// 4 wait states +#define MICROPY_HW_FLASH_LATENCY FLASH_LATENCY_8 + +// UART config +#define MICROPY_HW_LPUART1_TX (pin_A2) // A2 (to STLINK), B11, C1 +#define MICROPY_HW_LPUART1_RX (pin_A3) // A3 (to STLINK), B10, C0 +#define MICROPY_HW_UART1_TX (pin_C4) // A9, B6, C4, E0 +#define MICROPY_HW_UART1_RX (pin_C5) // A10, B7, C5, E1 +//#define MICROPY_HW_UART2_TX (pin_A2) // A14, B3, D5 +//#define MICROPY_HW_UART2_RX (pin_A3) // A15, B4, D6 +//#define MICROPY_HW_UART2_RTS (pin_A1) // D4 +//#define MICROPY_HW_UART2_CTS (pin_A0) // D3 +#define MICROPY_HW_UART3_TX (pin_B10) // B9, B10, C10, D8 +#define MICROPY_HW_UART3_RX (pin_B11) // B8 = boot0, B11, C11, D9, E15 +//#define MICROPY_HW_UART3_RTS (pin_B14) // D12, F6 +//#define MICROPY_HW_UART3_CTS (pin_B13) // A13, D11 +//#define MICROPY_HW_UART4_TX (pin_) // C10 +//#define MICROPY_HW_UART4_RX (pin_) // C11 + +#define MICROPY_HW_UART_REPL (PYB_LPUART_1) // default on Nucleo G474 +#define MICROPY_HW_UART_REPL_BAUD (115200) + +// I2C buses +#define MICROPY_HW_I2C1_SCL (pin_B8) // A13, A15, B8 (=boot0) +#define MICROPY_HW_I2C1_SDA (pin_B9) // A14, B7, B9 +//#define MICROPY_HW_I2C2_SCL (pin_) // A9, C4 +//#define MICROPY_HW_I2C2_SDA (pin_) // A8, F0 +//#define MICROPY_HW_I2C3_SCL (pin_) // A8, C8 +//#define MICROPY_HW_I2C3_SDA (pin_) // B5, C9, C11 + +// SPI +#define MICROPY_HW_SPI1_NSS (pin_A4) // A4, A15 (Nucleo64 specifies B6 as pin CS, must be done as GPIO, not as AF) +#define MICROPY_HW_SPI1_SCK (pin_A5) // A5 (LED), B3 (SWO) +#define MICROPY_HW_SPI1_MISO (pin_A6) // A6, B4 +#define MICROPY_HW_SPI1_MOSI (pin_A7) // A7, B5 +//#define MICROPY_HW_SPI2_NSS (pin_) // B12, D15, F0 +//#define MICROPY_HW_SPI2_SCK (pin_) // B13, F1, F9, F10 +//#define MICROPY_HW_SPI2_MISO (pin_) // A10, B14 +//#define MICROPY_HW_SPI2_MOSI (pin_) // A11, B15 +//#define MICROPY_HW_SPI3_NSS (pin_) // A4, A15 +//#define MICROPY_HW_SPI3_SCK (pin_) // B3, C10 +//#define MICROPY_HW_SPI3_MISO (pin_) // B4, C11 +//#define MICROPY_HW_SPI3_MOSI (pin_) // B5, C12 + +// USRSW is pulled low. Pressing the button makes the input go high. +#define MICROPY_HW_USRSW_PIN (pin_C13) +#define MICROPY_HW_USRSW_PULL (GPIO_NOPULL) +#define MICROPY_HW_USRSW_EXTI_MODE (GPIO_MODE_IT_RISING) +#define MICROPY_HW_USRSW_PRESSED (1) + +// LEDs +#define MICROPY_HW_LED1 (pin_A5) // green +#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_high(pin)) +#define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_low(pin)) + +// USB config pin A12 (dp), A11 (dm) not mounted on Nucleo +//#define MICROPY_HW_USB_FS (1) +//#define MICROPY_HW_USB_VBUS_DETECT_PIN (pin_A9) +//#define MICROPY_HW_USB_OTG_ID_PIN (pin_A10) + +// FDCAN bus +// User TODO: fit transceiver chip +#define MICROPY_HW_CAN1_NAME "FDCAN1" +#define MICROPY_HW_CAN1_TX (pin_A12) // A12, B9, D1 +#define MICROPY_HW_CAN1_RX (pin_A11) // A11, B8, D0 diff --git a/ports/stm32/boards/NUCLEO_G474RE/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_G474RE/mpconfigboard.mk new file mode 100644 index 0000000000..24a06e08ec --- /dev/null +++ b/ports/stm32/boards/NUCLEO_G474RE/mpconfigboard.mk @@ -0,0 +1,6 @@ +# MCU settings +MCU_SERIES = g4 +CMSIS_MCU = STM32G474xx +MICROPY_FLOAT_IMPL = single +AF_FILE = boards/stm32g474_af.csv +LD_FILES = boards/stm32g474.ld boards/common_basic.ld diff --git a/ports/stm32/boards/NUCLEO_G474RE/pins.csv b/ports/stm32/boards/NUCLEO_G474RE/pins.csv new file mode 100644 index 0000000000..473a5e26bc --- /dev/null +++ b/ports/stm32/boards/NUCLEO_G474RE/pins.csv @@ -0,0 +1,86 @@ +A0,PA0 +A1,PA1 +A2,PA4 +A3,BPB0 +A4,PC1 +A5,PC0 +D0,PC5 +D1,PC4 +D2,PA10 +D3,PB3 +D4,PB5 +D5,PB4 +D6,PB10 +D7,PA8 +D8,PA9 +D9,PC7 +D10,PB6 +D11,PA7 +D12,PA6 +D13,PA5 +D14,PB9 +D15,PB8 +SW,PC13 +I2C_SDA,PB9 +I2C_SCL,PB8 +LED1,PA5 +SPI_MOSI,PA7 +SPI_MISO,PA6 +SPI_SCK,PA5 +SPI_CS,PB6 +LPUART1_TX,PA2 +LPUART1_RX,PA3 +UART1_TX,PC4 +UART1_RX,PC5 +CN7_1,PC10 +CN7_2,PC11 +CN7_3,PC12 +CN7_4,PD2 +CN7_7,PB8 +CN7_13,PA13 +CN7_15,PA14 +CN7_17,PA15 +CN7_21,PB7 +CN7_23,PC13 +CN7_25,PC14 +CN7_27,PC15 +CN7_28,PA0 +CN7_29,PF0 +CN7_30,PA1 +CN7_31,PF1 +CN7_32,PA4 +CN7_34,PB0 +CN7_35,PC2 +CN7_36,PC1 +CN7_37,PC3 +CN7_38,PC0 +CN10_1,PC9 +CN10_2,PC8 +CN10_3,PB8 +CN10_4,PC6 +CN10_5,PB9 +CN10_6,PC5 +CN10_11,PA5 +CN10_12,PA12 +CN10_13,PA6 +CN10_14,PA11 +CN10_15,PA7 +CN10_16,PB12 +CN10_17,PB6 +CN10_18,PB11 +CN10_19,PC7 +CN10_21,PA9 +CN10_22,PB2 +CN10_23,PA8 +CN10_24,PB1 +CN10_25,PB10 +CN10_26,PB15 +CN10_27,PB4 +CN10_28,PB14 +CN10_29,PB5 +CN10_30,PB13 +CN10_31,PB3 +CN10_33,PA10 +CN10_34,PC4 +CN10_35,PC4 +CN10_37,PC5 diff --git a/ports/stm32/boards/NUCLEO_G474RE/stm32g4xx_hal_conf.h b/ports/stm32/boards/NUCLEO_G474RE/stm32g4xx_hal_conf.h new file mode 100644 index 0000000000..5e82fb4874 --- /dev/null +++ b/ports/stm32/boards/NUCLEO_G474RE/stm32g4xx_hal_conf.h @@ -0,0 +1,19 @@ +/* This file is part of the MicroPython project, http://micropython.org/ + * The MIT License (MIT) + * Copyright (c) 2019 Damien P. George + */ +#ifndef MICROPY_INCLUDED_STM32G4XX_HAL_CONF_H +#define MICROPY_INCLUDED_STM32G4XX_HAL_CONF_H + +#include "boards/stm32g4xx_hal_conf_base.h" + +// Oscillator values in Hz +#define HSE_VALUE (24000000) +#define LSE_VALUE (32768) +#define EXTERNAL_CLOCK_VALUE (24000) + +// Oscillator timeouts in ms +#define HSE_STARTUP_TIMEOUT (100) +#define LSE_STARTUP_TIMEOUT (5000) + +#endif // MICROPY_INCLUDED_STM32G4XX_HAL_CONF_H diff --git a/ports/stm32/boards/make-pins.py b/ports/stm32/boards/make-pins.py index 14c9e91097..4e895f8f60 100755 --- a/ports/stm32/boards/make-pins.py +++ b/ports/stm32/boards/make-pins.py @@ -219,23 +219,24 @@ class Pin(object): def parse_adc(self, adc_str): if adc_str[:3] != "ADC": return + adc, channel = None, None if adc_str.find("_INP") != -1: # STM32H7xx, entries have the form: ADCxx_IN[PN]yy/... - # for now just pick the entry with the most ADC periphs - adc, channel = None, None - for ss in adc_str.split("/"): - if ss.find("_INP") != -1: - a, c = ss.split("_") - if adc is None or len(a) > len(adc): - adc, channel = a, c - if adc is None: - return - channel = channel[3:] + sep = "_INP" else: # all other MCUs, entries have the form: ADCxx_INyy - adc, channel = adc_str.split("_") - channel = channel[2:] + sep = "_IN" + + # Pick the entry with the most ADC peripherals + for ss in adc_str.split("/"): + if ss.find(sep) != -1: + a, c = ss.split("_") + if adc is None or len(a) > len(adc): + adc, channel = a, c + if adc is None: + return + channel = channel[len(sep) - 1 :] for idx in range(3, len(adc)): adc_num = int(adc[idx]) # 1, 2, or 3 diff --git a/ports/stm32/boards/stm32g474.ld b/ports/stm32/boards/stm32g474.ld new file mode 100644 index 0000000000..4fe61a472f --- /dev/null +++ b/ports/stm32/boards/stm32g474.ld @@ -0,0 +1,28 @@ +/* Specify the memory areas */ +MEMORY +{ + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 352K + FLASH_FS (rx) : ORIGIN = 0x08058000, LENGTH = 160K /* starting @ 352K */ +} + +/* Generate a link error if heap and stack don't fit into RAM */ +_minimum_heap_size = 0x200; /* required amount of heap */ +_minimum_stack_size = 0x400; /* required amount of stack */ + +_ram_start = ORIGIN(RAM); +_ram_end = ORIGIN(RAM) + LENGTH(RAM); + +_micropy_hw_internal_flash_storage_ram_cache_end = ORIGIN(RAM) + LENGTH(RAM); +_micropy_hw_internal_flash_storage_ram_cache_start = _micropy_hw_internal_flash_storage_ram_cache_end - 2K; /* fs cache = 2K RAM */ + +/* Define the stack. The stack is full descending so begins just above last byte + of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */ +_estack = _micropy_hw_internal_flash_storage_ram_cache_start - _estack_reserve; +_sstack = _estack - 8K; /* tunable */ + +_heap_start = _ebss; /* heap starts just after statically allocated memory */ +_heap_end = _sstack; + +_micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); +_micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); diff --git a/ports/stm32/boards/stm32g474_af.csv b/ports/stm32/boards/stm32g474_af.csv new file mode 100644 index 0000000000..0108ee9013 --- /dev/null +++ b/ports/stm32/boards/stm32g474_af.csv @@ -0,0 +1,109 @@ +Port,,AF0,AF1,AF2,AF3,AF4,AF5,AF6,AF7,AF8,AF9,AF10,AF11,AF12,AF13,AF14,AF15, +,,I2C4/SYS_AF,LPTIM1/TIM2/5/15/16/17,I2C1/3/TIM1/2/3/4/5/8/20/15/COMP1,QUADSPI1/I2C3/4/SAI1/USB/HRTIM1/TIM8/20/15/COMP3,I2C1/2/3/4/TIM1/8/16/17,QUADSPI1/SPI1/2/3/4/I2S2/3/I2C4/UART4/5/TIM8/Infrared,QUADSPI1/SPI2/3/I2S2/3/TIM1/5/8/20/Infrared,USART1/2/3/CAN/COMP7/5/6,I2C3/4/UART4/5/LPUART1/COMP1/2/7/4/5/6/3,CAN/TIM1/8/15/CAN1/2,QUADSPI1/TIM2/3/4/8/17,LPTIM1/TIM1/8/CAN1/3,FMC/LPUART1/SAI1/HRTIM1/TIM1,SAI1SAI1/HRTIM1/OPAMP2,UART4/5/SAI1/TIM2/15/UCPD1,SYS,ADC,COMP,DAC,OPAMP +PortA,PA0,,TIM2_CH1,TIM5_CH1,,,,,USART2_CTS,COMP1_OUT,TIM8_BKIN,TIM8_ETR,,,,TIM2_ETR,EVENTOUT,ADC12_IN1,COMP1_INM/COMP3_INP,, +PortA,PA1,RTC_REFIN,TIM2_CH2,TIM5_CH2,,,,,USART2_RTS_DE,,TIM15_CH1N,,,,,,EVENTOUT,ADC12_IN2,COMP1_INP,,OPAMP1_VINP/OPAMP3_VINP/OPAMP6_VINM +PortA,PA2,,TIM2_CH3,TIM5_CH3,,,,,USART2_TX,COMP2_OUT,TIM15_CH1,QUADSPI1_BK1_NCS,,LPUART1_TX,,UCPD1_FRSTX,EVENTOUT,ADC1_IN3,COMP2_INM,,OPAMP1_VOUT +PortA,PA3,,TIM2_CH4,TIM5_CH4,SAI1_CK1,,,,USART2_RX,,TIM15_CH2,QUADSPI1_CLK,,LPUART1_RX,SAI1_MCLK_A,,EVENTOUT,ADC1_IN4,COMP2_INP,,OPAMP1_VINM/OPAMP1_VINP/OPAMP5_VINM +PortA,PA4,,,TIM3_CH2,,,SPI1_NSS,SPI3_NSS/I2S3_WS,USART2_CK,,,,,,SAI1_FS_B,,EVENTOUT,ADC2_IN17,COMP1_INM,DAC1_OUT1, +PortA,PA5,,TIM2_CH1,TIM2_ETR,,,SPI1_SCK,,,,,,,,,UCPD1_FRSTX,EVENTOUT,ADC2_IN13,COMP2_INM,DAC1_OUT2,OPAMP2_VINM +PortA,PA6,,TIM16_CH1,TIM3_CH1,,TIM8_BKIN,SPI1_MISO,TIM1_BKIN,,COMP1_OUT,,QUADSPI1_BK1_IO3,,LPUART1_CTS,,,EVENTOUT,ADC2_IN3,,DAC2_OUT1,OPAMP2_VOUT +PortA,PA7,,TIM17_CH1,TIM3_CH2,,TIM8_CH1N,SPI1_MOSI,TIM1_CH1N,,COMP2_OUT,,QUADSPI1_BK1_IO2,,,,UCPD1_FRSTX,EVENTOUT,ADC2_IN4,COMP2_INP,,OPAMP1_VINP/OPAMP2_VINP +PortA,PA8,MCO,,I2C3_SCL,,I2C2_SDA,I2S2_MCK,TIM1_CH1,USART1_CK,COMP7_OUT,,TIM4_ETR,CAN3_RX,SAI1_CK2,HRTIM1_CHA1,SAI1_SCK_A,EVENTOUT,ADC5_IN1,,,OPAMP5_VOUT +PortA,PA9,,,I2C3_SMBA,,I2C2_SCL,I2S3_MCK,TIM1_CH2,USART1_TX,COMP5_OUT,TIM15_BKIN,TIM2_CH3,,,HRTIM1_CHA2,SAI1_FS_A,EVENTOUT,ADC5_IN2,,, +PortA,PA10,,TIM17_BKIN,,USB_CRS_SYNC,I2C2_SMBA,SPI2_MISO,TIM1_CH3,USART1_RX,COMP6_OUT,,TIM2_CH4,TIM8_BKIN,SAI1_D1,HRTIM1_CHB1,SAI1_SD_A,EVENTOUT,,,, +PortA,PA11,,,,,,SPI2_MOSI/I2S2_SD,TIM1_CH1N,USART1_CTS,COMP1_OUT,CAN1_RX,TIM4_CH1,TIM1_CH4,TIM1_BKIN2,HRTIM1_CHB2,,EVENTOUT,,,, +PortA,PA12,,TIM16_CH1,,,,I2SCKIN,TIM1_CH2N,USART1_RTS_DE,COMP2_OUT,CAN1_TX,TIM4_CH2,TIM1_ETR,,HRTIM1_FLT1,,EVENTOUT,,,, +PortA,PA13,SWDIOJTMS,TIM16_CH1N,,I2C4_SCL,I2C1_SCL,IR_OUT,,USART3_CTS,,,TIM4_CH3,,,SAI1_SD_B,,EVENTOUT,,,, +PortA,PA14,SWCLKJTCK,LPTIM1_OUT,,I2C4_SMBA,I2C1_SDA,TIM8_CH2,TIM1_BKIN,USART2_TX,,,,,,SAI1_FS_B,,EVENTOUT,,,, +PortA,PA15,JTDI,TIM2_CH1,TIM8_CH1,,I2C1_SCL,SPI1_NSS,SPI3_NSS/I2S3_WS,USART2_RX,UART4_RTS_DE,TIM1_BKIN,,CAN3_TX,,HRTIM1_FLT2,TIM2_ETR,EVENTOUT,,,, +PortB,PB0,,,TIM3_CH3,,TIM8_CH2N,,TIM1_CH2N,,,,QUADSPI1_BK1_IO1,,,HRTIM1_FLT5,UCPD1_FRSTX,EVENTOUT,ADC3_IN12/ADC1_IN15,COMP4_INP,,OPAMP2_VINP/OPAMP3_VINP +PortB,PB1,,,TIM3_CH4,,TIM8_CH3N,,TIM1_CH3N,,COMP4_OUT,,QUADSPI1_BK1_IO0,,LPUART1_RTS_DE,HRTIM1_SCOUT,,EVENTOUT,ADC3_IN1/ADC1_IN12,COMP1_INP,,OPAMP3_VOUT/OPAMP6_VINM +PortB,PB2,RTC_OUT2,LPTIM1_OUT,TIM5_CH1,TIM20_CH1,I2C3_SMBA,,,,,,QUADSPI1_BK2_IO1,,,HRTIM1_SCIN,,EVENTOUT,ADC2_IN12,COMP4_INM,,OPAMP3_VINM +PortB,PB3,JTDOTRACESWO,TIM2_CH2,TIM4_ETR,USB_CRS_SYNC,TIM8_CH1N,SPI1_SCK,SPI3_SCK/I2S3_CK,USART2_TX,,,TIM3_ETR,CAN3_RX,HRTIM1_SCOUT,HRTIM1_EEV9,SAI1_SCK_B,EVENTOUT,,,, +PortB,PB4,JTRST,TIM16_CH1,TIM3_CH1,,TIM8_CH2N,SPI1_MISO,SPI3_MISO,USART2_RX,UART5_RTS_DE,,TIM17_BKIN,CAN3_TX,,HRTIM1_EEV7,SAI1_MCLK_B,EVENTOUT,,,, +PortB,PB5,,TIM16_BKIN,TIM3_CH2,TIM8_CH3N,I2C1_SMBA,SPI1_MOSI,SPI3_MOSI/I2S3_SD,USART2_CK,I2C3_SDA,CAN2_RX,TIM17_CH1,LPTIM1_IN1,SAI1_SD_B,HRTIM1_EEV6,UART5_CTS,EVENTOUT,,,, +PortB,PB6,,TIM16_CH1N,TIM4_CH1,,,TIM8_CH1,TIM8_ETR,USART1_TX,COMP4_OUT,CAN2_TX,TIM8_BKIN2,LPTIM1_ETR,HRTIM1_SCIN,HRTIM1_EEV4,SAI1_FS_B,EVENTOUT,,,, +PortB,PB7,,TIM17_CH1N,TIM4_CH2,I2C4_SDA,I2C1_SDA,TIM8_BKIN,,USART1_RX,COMP3_OUT,,TIM3_CH4,LPTIM1_IN2,FMC_NL,HRTIM1_EEV3,UART4_CTS,EVENTOUT,,,, +PortB,PB8,,TIM16_CH1,TIM4_CH3,SAI1_CK1,I2C1_SCL,,,USART3_RX,COMP1_OUT,CAN1_RX,TIM8_CH2,,TIM1_BKIN,HRTIM1_EEV8,SAI1_MCLK_A,EVENTOUT,,,, +PortB,PB9,,TIM17_CH1,TIM4_CH4,SAI1_D2,I2C1_SDA,,IR_OUT,USART3_TX,COMP2_OUT,CAN1_TX,TIM8_CH3,,TIM1_CH3N,HRTIM1_EEV5,SAI1_FS_A,EVENTOUT,,,, +PortB,PB10,,TIM2_CH3,,,,,,USART3_TX,LPUART1_RX,,QUADSPI1_CLK,,TIM1_BKIN,HRTIM1_FLT3,SAI1_SCK_A,EVENTOUT,,COMP5_INM,,OPAMP3_VINM/OPAMP4_VINM +PortB,PB11,,TIM2_CH4,,,,,,USART3_RX,LPUART1_TX,,QUADSPI1_BK1_NCS,,,HRTIM1_FLT4,,EVENTOUT,ADC12_IN14,COMP6_INP,,OPAMP4_VINP/OPAMP6_VOUT +PortB,PB12,,,TIM5_ETR,,I2C2_SMBA,SPI2_NSS/I2S2_WS,TIM1_BKIN,USART3_CK,LPUART1_RTS_DE,CAN2_RX,,,,HRTIM1_CHC1,,EVENTOUT,ADC4_IN3/ADC1_IN11,COMP7_INM,,OPAMP4_VOUT/OPAMP6_VINP +PortB,PB13,,,,,,SPI2_SCK/I2S2_CK,TIM1_CH1N,USART3_CTS,LPUART1_CTS,CAN2_TX,,,,HRTIM1_CHC2,,EVENTOUT,ADC3_IN5,COMP5_INP,,OPAMP3_VINP/OPAMP4_VINP/OPAMP6_VINP +PortB,PB14,,TIM15_CH1,,,,SPI2_MISO,TIM1_CH2N,USART3_RTS_DE,COMP4_OUT,,,,,HRTIM1_CHD1,,EVENTOUT,ADC4_IN4/ADC1_IN5,COMP7_INP,,OPAMP2_VINP/OPAMP5_VINP +PortB,PB15,RTC_REFIN,TIM15_CH2,TIM15_CH1N,COMP3_OUT,TIM1_CH3N,SPI2_MOSI/I2S2_SD,,,,,,,,HRTIM1_CHD2,,EVENTOUT,ADC4_IN5/ADC2_IN15,COMP6_INM,,OPAMP5_VINM +PortC,PC0,,LPTIM1_IN1,TIM1_CH1,,,,,,LPUART1_RX,,,,,,,EVENTOUT,ADC12_IN6,COMP3_INM, +PortC,PC1,,LPTIM1_OUT,TIM1_CH2,,,,,,LPUART1_TX,,QUADSPI1_BK2_IO0,,,SAI1_SD_A,,EVENTOUT,ADC12_IN7,COMP3_INP,, +PortC,PC2,,LPTIM1_IN2,TIM1_CH3,COMP3_OUT,,,TIM20_CH2,,,,QUADSPI1_BK2_IO1,,,,,EVENTOUT,ADC12_IN8,, +PortC,PC3,,LPTIM1_ETR,TIM1_CH4,SAI1_D1,,,TIM1_BKIN2,,,,QUADSPI1_BK2_IO2,,,SAI1_SD_A,,EVENTOUT,ADC12_IN9,,,OPAMP5_VINP +PortC,PC4,,,TIM1_ETR,,I2C2_SCL,,,USART1_TX,,,QUADSPI1_BK2_IO3,,,,,EVENTOUT,ADC2_IN5,,, +PortC,PC5,,,TIM15_BKIN,SAI1_D3,,,TIM1_CH4N,USART1_RX,,,,,,HRTIM1_EEV10,,EVENTOUT,ADC2_IN11,,,OPAMP1_VINM/OPAMP2_VINM +PortC,PC6,,,TIM3_CH1,HRTIM1_EEV10,TIM8_CH1,,I2S2_MCK,COMP6_OUT,I2C4_SCL,,,,,HRTIM1_CHF1,,EVENTOUT,,,, +PortC,PC7,,,TIM3_CH2,HRTIM1_FLT5,TIM8_CH2,,I2S3_MCK,COMP5_OUT,I2C4_SDA,,,,,HRTIM1_CHF2,,EVENTOUT,,,, +PortC,PC8,,,TIM3_CH3,HRTIM1_CHE1,TIM8_CH3,,TIM20_CH3,COMP7_OUT,I2C3_SCL,,,,,,,EVENTOUT,,,, +PortC,PC9,,,TIM3_CH4,HRTIM1_CHE2,TIM8_CH4,I2SCKIN,TIM8_BKIN2,,I2C3_SDA,,,,,,,EVENTOUT,,,, +PortC,PC10,,,,,TIM8_CH1N,UART4_TX,SPI3_SCK/I2S3_CK,USART3_TX,,,,,,HRTIM1_FLT6,,EVENTOUT,,,, +PortC,PC11,,,,HRTIM1_EEV2,TIM8_CH2N,UART4_RX,SPI3_MISO,USART3_RX,I2C3_SDA,,,,,,,EVENTOUT,,,, +PortC,PC12,,TIM5_CH2,,HRTIM1_EEV1,TIM8_CH3N,UART5_TX,SPI3_MOSI/I2S3_SD,USART3_CK,,,,,,,UCPD1_FRSTX,EVENTOUT,,,, +PortC,PC13,,,TIM1_BKIN,,TIM1_CH1N,,TIM8_CH4N,,,,,,,,,EVENTOUT,,,, +PortC,PC14,,,,,,,,,,,,,,,,EVENTOUT,,,, +PortC,PC15,,,,,,,,,,,,,,,,EVENTOUT,,,, +PortD,PD0,,,,,,,TIM8_CH4N,,,CAN1_RX,,,FMC_D2,,,EVENTOUT,,,, +PortD,PD1,,,,,TIM8_CH4,,TIM8_BKIN2,,,CAN1_TX,,,FMC_D3,,,EVENTOUT,,,, +PortD,PD2,,,TIM3_ETR,,TIM8_BKIN,UART5_RX,,,,,,,,,,EVENTOUT,,,, +PortD,PD3,,,TIM2_CH1/TIM2_ETR,,,,,USART2_CTS,,,QUADSPI1_BK2_NCS,,FMC_CLK,,,EVENTOUT,,,, +PortD,PD4,,,TIM2_CH2,,,,,USART2_RTS_DE,,,QUADSPI1_BK2_IO0,,FMC_NOE,,,EVENTOUT,,,, +PortD,PD5,,,,,,,,USART2_TX,,,QUADSPI1_BK2_IO1,,FMC_NWE,,,EVENTOUT,,,, +PortD,PD6,,,TIM2_CH4,SAI1_D1,,,,USART2_RX,,,QUADSPI1_BK2_IO2,,FMC_NWAIT,SAI1_SD_A,,EVENTOUT,,,, +PortD,PD7,,,TIM2_CH3,,,,,USART2_CK,,,QUADSPI1_BK2_IO3,,FMC_NCE/FMC_NE1,,,EVENTOUT,,,, +PortD,PD8,,,,,,,,USART3_TX,,,,,FMC_D13,,,EVENTOUT,ADC4_IN12/ADC5_IN12,,,OPAMP4_VINM +PortD,PD9,,,,,,,,USART3_RX,,,,,FMC_D14,,,EVENTOUT,ADC4_IN13/ADC5_IN13,,,OPAMP6_VINP +PortD,PD10,,,,,,,,USART3_CK,,,,,FMC_D15,,,EVENTOUT,ADC345_IN7,COMP6_INM,, +PortD,PD11,,TIM5_ETR,,,I2C4_SMBA,,,USART3_CTS,,,,,FMC_A16,,,EVENTOUT,ADC345_IN8,COMP6_INP,,OPAMP4_VINP +PortD,PD12,,,TIM4_CH1,,,,,USART3_RTS_DE,,,,,FMC_A17,,,EVENTOUT,ADC345_IN9,COMP5_INP,,OPAMP5_VINP +PortD,PD13,,,TIM4_CH2,,,,,,,,,,FMC_A18,,,EVENTOUT,ADC345_IN10,COMP5_INM,, +PortD,PD14,,,TIM4_CH3,,,,,,,,,,FMC_D0,,,EVENTOUT,ADC345_IN11,COMP7_INP,,OPAMP2_VINP +PortD,PD15,,,TIM4_CH4,,,,SPI2_NSS,,,,,,FMC_D1,,,EVENTOUT,,COMP7_INM,, +PortE,PE0,,,TIM4_ETR,TIM20_CH4N,TIM16_CH1,,TIM20_ETR,USART1_TX,,CAN1_RXFD,,,FMC_NBL0,,,EVENTOUT,,,, +PortE,PE1,,,,,TIM17_CH1,,TIM20_CH4,USART1_RX,,,,,FMC_NBL1,,,EVENTOUT,,,, +PortE,PE2,TRACECK,,TIM3_CH1,SAI1_CK1,,SPI4_SCK,TIM20_CH1,,,,,,FMC_A23,SAI1_MCLK_A,,EVENTOUT,,,, +PortE,PE3,TRACED0,,TIM3_CH2,,,SPI4_NSS,TIM20_CH2,,,,,,FMC_A19,SAI1_SD_B,,EVENTOUT,,,, +PortE,PE4,TRACED1,,TIM3_CH3,SAI1_D2,,SPI4_NSS,TIM20_CH1N,,,,,,FMC_A20,SAI1_FS_A,,EVENTOUT,,,, +PortE,PE5,TRACED2,,TIM3_CH4,SAI1_CK2,,SPI4_MISO,TIM20_CH2N,,,,,,FMC_A21,SAI1_SCK_A,,EVENTOUT,,,, +PortE,PE6,TRACED3,,,SAI1_D1,,SPI4_MOSI,TIM20_CH3N,,,,,,FMC_A22,SAI1_SD_A,,EVENTOUT,,,, +PortE,PE7,,,TIM1_ETR,,,,,,,,,,FMC_D4,SAI1_SD_B,,EVENTOUT,ADC3_IN4,COMP4_INP,, +PortE,PE8,,TIM5_CH3,TIM1_CH1N,,,,,,,,,,FMC_D5,SAI1_SCK_B,,EVENTOUT,ADC345_IN6,COMP4_INM,, +PortE,PE9,,TIM5_CH4,TIM1_CH1,,,,,,,,,,FMC_D6,SAI1_FS_B,,EVENTOUT,ADC3_IN2,,, +PortE,PE10,,,TIM1_CH2N,,,,,,,,QUADSPI1_CLK,,FMC_D7,SAI1_MCLK_B,,EVENTOUT,ADC345_IN14,,, +PortE,PE11,,,TIM1_CH2,,,SPI4_NSS,,,,,QUADSPI1_BK1_NCS,,FMC_D8,,,EVENTOUT,ADC345_IN15,,, +PortE,PE12,,,TIM1_CH3N,,,SPI4_SCK,,,,,QUADSPI1_BK1_IO0,,FMC_D9,,,EVENTOUT,ADC345_IN16,,, +PortE,PE13,,,TIM1_CH3,,,SPI4_MISO,,,,,QUADSPI1_BK1_IO1,,FMC_D10,,,EVENTOUT,ADC3_IN3,,, +PortE,PE14,,,TIM1_CH4,,,SPI4_MOSI,TIM1_BKIN2,,,,QUADSPI1_BK1_IO2,,FMC_D11,,,EVENTOUT,ADC4_IN1,,, +PortE,PE15,,,TIM1_BKIN,,,,TIM1_CH4N,USART3_RX,,,QUADSPI1_BK1_IO3,,FMC_D12,,,EVENTOUT,ADC4_IN2,,, +PortF,PF0,,,,,I2C2_SDA,SPI2_NSS/I2S2_WS,TIM1_CH3N,,,,,,,,,EVENTOUT,ADC1_IN10,,, +PortF,PF1,,,,,,SPI2_SCK/I2S2_CK,,,,,,,,,,EVENTOUT,ADC2_IN10,COMP3_INM,, +PortF,PF2,,,TIM20_CH3,,I2C2_SMBA,,,,,,,,FMC_A2,,,EVENTOUT,,,, +PortF,PF3,,,TIM20_CH4,,I2C3_SCL,,,,,,,,FMC_A3,,,EVENTOUT,,,, +PortF,PF4,,,COMP1_OUT,TIM20_CH1N,I2C3_SDA,,,,,,,,FMC_A4,,,EVENTOUT,,,, +PortF,PF5,,,TIM20_CH2N,,,,,,,,,,FMC_A5,,,EVENTOUT,,,, +PortF,PF6,,TIM5_ETR,TIM4_CH4,SAI1_SD_B,I2C2_SCL,,TIM5_CH1,USART3_RTS,,,QUADSPI1_BK1_IO3,,,,,EVENTOUT,,,, +PortF,PF7,,,TIM20_BKIN,,,,TIM5_CH2,,,,QUADSPI1_BK1_IO2,,FMC_A1,SAI1_MCLK_B,,EVENTOUT,,,, +PortF,PF8,,,TIM20_BKIN2,,,,TIM5_CH3,,,,QUADSPI1_BK1_IO0,,FMC_A24,SAI1_SCK_B,,EVENTOUT,,,, +PortF,PF9,,,TIM20_BKIN,TIM15_CH1,,SPI2_SCK,TIM5_CH4,,,,QUADSPI1_BK1_IO1,,FMC_A25,SAI1_FS_B,,EVENTOUT,,,, +PortF,PF10,,,TIM20_BKIN2,TIM15_CH2,,SPI2_SCK,,,,,QUADSPI1_CLK,,FMC_A0,SAI1_D3,,EVENTOUT,,,, +PortF,PF11,,,TIM20_ETR,,,,,,,,,,FMC_NE4,,,EVENTOUT,,,, +PortF,PF12,,,TIM20_CH1,,,,,,,,,,FMC_A6,,,EVENTOUT,,,, +PortF,PF13,,,TIM20_CH2,,I2C4_SMBA,,,,,,,,FMC_A7,,,EVENTOUT,,,, +PortF,PF14,,,TIM20_CH3,,I2C4_SCL,,,,,,,,FMC_A8,,,EVENTOUT,,,, +PortF,PF15,,,TIM20_CH4,,I2C4_SDA,,,,,,,,FMC_A9,,,EVENTOUT,,,, +PortG,PG0,,,TIM20_CH1N,,,,,,,,,,FMC_A10,,,EVENTOUT,,,, +PortG,PG1,,,TIM20_CH2N,,,,,,,,,,FMC_A11,,,EVENTOUT,,,, +PortG,PG2,,,TIM20_CH3N,,,SPI1_SCK,,,,,,,FMC_A12,,,EVENTOUT,,,, +PortG,PG3,,,TIM20_BKIN,,I2C4_SCL,SPI1_MISO,TIM20_CH4N,,,,,,FMC_A13,,,EVENTOUT,,,, +PortG,PG4,,,TIM20_BKIN2,,I2C4_SDA,SPI1_MOSI,,,,,,,FMC_A14,,,EVENTOUT,,,, +PortG,PG5,,,TIM20_ETR,,,SPI1_NSS,,,LPUART1_CTS,,,,FMC_A15,,,EVENTOUT,,,, +PortG,PG6,,,TIM20_BKIN,,I2C3_SMBA,,,,LPUART1_RTS_DE,,,,FMC_INT,,,EVENTOUT,,,, +PortG,PG7,,,,SAI1_CK1,I2C3_SCL,,,,LPUART1_TX,,,,FMC_INT,SAI1_MCLK_A,,EVENTOUT,,,, +PortG,PG8,,,,,I2C3_SDA,,,,LPUART1_RX,,,,FMC_NE3,,,EVENTOUT,,,, +PortG,PG9,,,,,,,SPI3_SCK,USART1_TX,,,,,FMC_NCE/FMC_NE2,,TIM15_CH1N,EVENTOUT,,,, +PortG,PG10,MCO,,,,,,,,,,,,,,,EVENTOUT diff --git a/ports/stm32/boards/stm32g4xx_hal_conf_base.h b/ports/stm32/boards/stm32g4xx_hal_conf_base.h new file mode 100644 index 0000000000..034d396539 --- /dev/null +++ b/ports/stm32/boards/stm32g4xx_hal_conf_base.h @@ -0,0 +1,131 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MICROPY_INCLUDED_STM32G4XX_HAL_CONF_BASE_H +#define MICROPY_INCLUDED_STM32G4XX_HAL_CONF_BASE_H + +// Include various HAL modules for convenience + +#include "stm32g4xx_hal_rcc.h" +#include "stm32g4xx_hal_gpio.h" +#include "stm32g4xx_hal_dma.h" +#include "stm32g4xx_hal_cortex.h" +#include "stm32g4xx_hal_adc.h" +#include "stm32g4xx_hal_comp.h" +#include "stm32g4xx_hal_cordic.h" +#include "stm32g4xx_hal_crc.h" +#include "stm32g4xx_hal_cryp.h" +#include "stm32g4xx_hal_dac.h" +#include "stm32g4xx_hal_exti.h" +#include "stm32g4xx_hal_fdcan.h" +#include "stm32g4xx_hal_flash.h" +#include "stm32g4xx_hal_fmac.h" +#include "stm32g4xx_hal_hrtim.h" +#include "stm32g4xx_hal_irda.h" +#include "stm32g4xx_hal_iwdg.h" +#include "stm32g4xx_hal_i2c.h" +#include "stm32g4xx_hal_i2s.h" +#include "stm32g4xx_hal_lptim.h" +#include "stm32g4xx_hal_nand.h" +#include "stm32g4xx_hal_nor.h" +#include "stm32g4xx_hal_opamp.h" +#include "stm32g4xx_hal_pcd.h" +#include "stm32g4xx_hal_pwr.h" +#include "stm32g4xx_hal_qspi.h" +#include "stm32g4xx_hal_rng.h" +#include "stm32g4xx_hal_rtc.h" +#include "stm32g4xx_hal_sai.h" +#include "stm32g4xx_hal_smartcard.h" +#include "stm32g4xx_hal_smbus.h" +#include "stm32g4xx_hal_spi.h" +#include "stm32g4xx_hal_sram.h" +#include "stm32g4xx_hal_tim.h" +#include "stm32g4xx_hal_uart.h" +#include "stm32g4xx_hal_usart.h" +#include "stm32g4xx_hal_wwdg.h" + +#include "stm32g4xx_ll_adc.h" +#include "stm32g4xx_ll_rtc.h" +#include "stm32g4xx_ll_usart.h" +#include "stm32g4xx_ll_lpuart.h" + +#define HAL_MODULE_ENABLED +#define HAL_ADC_MODULE_ENABLED +#define HAL_COMP_MODULE_ENABLED +#define HAL_CORDIC_MODULE_ENABLED +#define HAL_CORTEX_MODULE_ENABLED +#define HAL_CRC_MODULE_ENABLED +#define HAL_CRYP_MODULE_ENABLED +#define HAL_DAC_MODULE_ENABLED +#define HAL_DMA_MODULE_ENABLED +#define HAL_EXTI_MODULE_ENABLED +#define HAL_FDCAN_MODULE_ENABLED +#define HAL_FLASH_MODULE_ENABLED +#define HAL_FMAC_MODULE_ENABLED +#define HAL_GPIO_MODULE_ENABLED +#define HAL_HRTIM_MODULE_ENABLED +#define HAL_IRDA_MODULE_ENABLED +#define HAL_IWDG_MODULE_ENABLED +#define HAL_I2C_MODULE_ENABLED +#define HAL_I2S_MODULE_ENABLED +#define HAL_LPTIM_MODULE_ENABLED +#define HAL_NAND_MODULE_ENABLED +#define HAL_NOR_MODULE_ENABLED +#define HAL_OPAMP_MODULE_ENABLED +#define HAL_PCD_MODULE_ENABLED +#define HAL_PWR_MODULE_ENABLED +#define HAL_QSPI_MODULE_ENABLED +#define HAL_RCC_MODULE_ENABLED +#define HAL_RNG_MODULE_ENABLED +#define HAL_RTC_MODULE_ENABLED +#define HAL_SAI_MODULE_ENABLED +#define HAL_SMARTCARD_MODULE_ENABLED +#define HAL_SMBUS_MODULE_ENABLED +#define HAL_SPI_MODULE_ENABLED +#define HAL_SRAM_MODULE_ENABLED +#define HAL_TIM_MODULE_ENABLED +#define HAL_UART_MODULE_ENABLED +#define HAL_USART_MODULE_ENABLED +#define HAL_WWDG_MODULE_ENABLED + +// Oscillator values in Hz +#define HSI_VALUE (16000000) +#define HSI48_VALUE (48000000) +#define LSI_VALUE (32000) + +// SysTick priority +#define TICK_INT_PRIORITY (0x0F) + +// Miscellaneous HAL settings +#define USE_RTOS 0 +#define PREFETCH_ENABLE 0 +#define INSTRUCTION_CACHE_ENABLE 1 +#define DATA_CACHE_ENABLE 1 +#define USE_SPI_CRC 1 + +// HAL parameter assertions are disabled +#define assert_param(expr) ((void)0) + +#endif // MICROPY_INCLUDED_STM32G4XX_HAL_CONF_BASE_H diff --git a/ports/stm32/dac.c b/ports/stm32/dac.c index d51312aebc..b7fb0cbd70 100644 --- a/ports/stm32/dac.c +++ b/ports/stm32/dac.c @@ -101,7 +101,7 @@ STATIC uint32_t TIMx_Config(mp_obj_t timer) { } else if (tim->Instance == TIM4) { return DAC_TRIGGER_T4_TRGO; #endif - #if defined(TIM5) + #if defined(TIM5) && defined(DAC_TRIGGER_T5_TRGO) // G474 doesn't have this } else if (tim->Instance == TIM5) { return DAC_TRIGGER_T5_TRGO; #endif @@ -124,7 +124,7 @@ STATIC uint32_t TIMx_Config(mp_obj_t timer) { STATIC void dac_deinit(uint32_t dac_channel) { DAC->CR &= ~(DAC_CR_EN1 << dac_channel); - #if defined(STM32H7) || defined(STM32L4) + #if defined(STM32G4) || defined(STM32H7) || defined(STM32L4) DAC->MCR = (DAC->MCR & ~(DAC_MCR_MODE1_Msk << dac_channel)) | (DAC_OUTPUTBUFFER_DISABLE << dac_channel); #else DAC->CR |= DAC_CR_BOFF1 << dac_channel; @@ -142,7 +142,7 @@ STATIC void dac_config_channel(uint32_t dac_channel, uint32_t trig, uint32_t out DAC->CR &= ~(DAC_CR_EN1 << dac_channel); uint32_t cr_off = DAC_CR_DMAEN1 | DAC_CR_MAMP1 | DAC_CR_WAVE1 | DAC_CR_TSEL1 | DAC_CR_TEN1; uint32_t cr_on = trig; - #if defined(STM32H7) || defined(STM32L4) + #if defined(STM32G4) || defined(STM32H7) || defined(STM32L4) DAC->MCR = (DAC->MCR & ~(DAC_MCR_MODE1_Msk << dac_channel)) | (outbuf << dac_channel); #else cr_off |= DAC_CR_BOFF1; @@ -259,7 +259,7 @@ STATIC mp_obj_t pyb_dac_init_helper(pyb_dac_obj_t *self, size_t n_args, const mp __DAC_CLK_ENABLE(); #elif defined(STM32H7) __HAL_RCC_DAC12_CLK_ENABLE(); - #elif defined(STM32F0) || defined(STM32L4) + #elif defined(STM32F0) || defined(STM32G4) || defined(STM32L4) __HAL_RCC_DAC1_CLK_ENABLE(); #else #error Unsupported Processor diff --git a/ports/stm32/dma.c b/ports/stm32/dma.c index b376ee23b5..dc706e68aa 100644 --- a/ports/stm32/dma.c +++ b/ports/stm32/dma.c @@ -74,7 +74,7 @@ typedef union { struct _dma_descr_t { #if defined(STM32F4) || defined(STM32F7) || defined(STM32H7) DMA_Stream_TypeDef *instance; - #elif defined(STM32F0) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) + #elif defined(STM32F0) || defined(STM32G4) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) DMA_Channel_TypeDef *instance; #else #error "Unsupported Processor" @@ -89,7 +89,7 @@ struct _dma_descr_t { static const DMA_InitTypeDef dma_init_struct_spi_i2c = { #if defined(STM32F4) || defined(STM32F7) .Channel = 0, - #elif defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) + #elif defined(STM32G4) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) .Request = 0, #endif .Direction = 0, @@ -137,7 +137,7 @@ static const DMA_InitTypeDef dma_init_struct_i2s = { static const DMA_InitTypeDef dma_init_struct_sdio = { #if defined(STM32F4) || defined(STM32F7) .Channel = 0, - #elif defined(STM32L0) || defined(STM32L4) || defined(STM32WB) + #elif defined(STM32G4) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) .Request = 0, #endif .Direction = 0, @@ -147,7 +147,7 @@ static const DMA_InitTypeDef dma_init_struct_sdio = { .MemDataAlignment = DMA_MDATAALIGN_WORD, #if defined(STM32F4) || defined(STM32F7) .Mode = DMA_PFCTRL, - #elif defined(STM32L0) || defined(STM32L4) || defined(STM32WB) + #elif defined(STM32G4) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) .Mode = DMA_NORMAL, #endif .Priority = DMA_PRIORITY_VERY_HIGH, @@ -165,7 +165,7 @@ static const DMA_InitTypeDef dma_init_struct_sdio = { static const DMA_InitTypeDef dma_init_struct_dac = { #if defined(STM32F4) || defined(STM32F7) .Channel = 0, - #elif defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) + #elif defined(STM32G4) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) .Request = 0, #endif .Direction = 0, @@ -501,6 +501,86 @@ static const uint8_t dma_irqn[NSTREAM] = { DMA2_Channel7_IRQn, }; +#elif defined(STM32G4) + +#define NCONTROLLERS (2) + +#if defined(STM32G431xx) || defined(STM32G441xx) +#define NSTREAMS_PER_CONTROLLER (6) // Cat 2 devices = 6, Cat 3 = 8 +#else +#define NSTREAMS_PER_CONTROLLER (8) // Cat 2 devices = 6, Cat 3 = 8 +#endif +#define NSTREAM (NCONTROLLERS * NSTREAMS_PER_CONTROLLER) + +#define DMA_SUB_INSTANCE_AS_UINT8(dma_request) (dma_request) +#if defined(STM32G431xx) || defined(STM32G441xx) +#define DMA1_ENABLE_MASK (0x004f) // Bits in dma_enable_mask corresponding to DMA1 +#define DMA2_ENABLE_MASK (0x0fc0) // Bits in dma_enable_mask corresponding to DMA2 +#else +#define DMA1_ENABLE_MASK (0x00Ff) // Bits in dma_enable_mask corresponding to DMA1 +#define DMA2_ENABLE_MASK (0xff00) // Bits in dma_enable_mask corresponding to DMA2 +#endif + +// These descriptors are ordered by DMAx_Channel number, and within a channel by request +// number. The duplicate streams are ok as long as they aren't used at the same time. + +// DMA1 streams +const dma_descr_t dma_SPI_1_RX = { DMA1_Channel1, DMA_REQUEST_SPI1_RX, dma_id_0, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_SPI_1_TX = { DMA1_Channel2, DMA_REQUEST_SPI1_TX, dma_id_1, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_SPI_2_RX = { DMA1_Channel1, DMA_REQUEST_SPI2_RX, dma_id_0, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_SPI_2_TX = { DMA1_Channel2, DMA_REQUEST_SPI2_TX, dma_id_1, &dma_init_struct_spi_i2c }; + +const dma_descr_t dma_I2C_1_RX = { DMA1_Channel3, DMA_REQUEST_I2C1_RX, dma_id_2, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_I2C_1_TX = { DMA1_Channel4, DMA_REQUEST_I2C1_TX, dma_id_3, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_I2C_2_RX = { DMA1_Channel3, DMA_REQUEST_I2C2_RX, dma_id_2, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_I2C_2_TX = { DMA1_Channel4, DMA_REQUEST_I2C2_TX, dma_id_3, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_I2C_3_RX = { DMA1_Channel3, DMA_REQUEST_I2C3_RX, dma_id_2, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_I2C_3_TX = { DMA1_Channel4, DMA_REQUEST_I2C3_TX, dma_id_3, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_UART_3_RX = { DMA1_Channel3, DMA_REQUEST_USART3_RX, dma_id_2, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_UART_3_TX = { DMA1_Channel4, DMA_REQUEST_USART3_TX, dma_id_3, &dma_init_struct_spi_i2c }; + +#if MICROPY_HW_ENABLE_DAC +const dma_descr_t dma_DAC_1_TX = { DMA1_Channel5, DMA_REQUEST_DAC1_CHANNEL1, dma_id_4, &dma_init_struct_dac }; +const dma_descr_t dma_DAC_2_TX = { DMA1_Channel6, DMA_REQUEST_DAC1_CHANNEL2, dma_id_5, &dma_init_struct_dac }; +#endif +#if !defined(STM32G431xx) && !defined(STM32G441xx) +// channel 7 & 8 +#endif + +// DMA2 streams +const dma_descr_t dma_UART_1_RX = { DMA2_Channel1, DMA_REQUEST_USART1_RX, dma_id_6, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_UART_1_TX = { DMA2_Channel2, DMA_REQUEST_USART1_TX, dma_id_7, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_LPUART_1_RX = { DMA2_Channel3, DMA_REQUEST_LPUART1_RX, dma_id_8, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_LPUART_1_TX = { DMA2_Channel4, DMA_REQUEST_LPUART1_TX, dma_id_9, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_ADC_1 = { DMA2_Channel5, DMA_REQUEST_ADC1, dma_id_10, NULL }; +const dma_descr_t dma_MEM_2_MEM = { DMA2_Channel6, DMA_REQUEST_MEM2MEM, dma_id_11, NULL }; +#if !defined(STM32G431xx) && !defined(STM32G441xx) +// channel 7 & 8 +#endif + +static const uint8_t dma_irqn[NSTREAM] = { + DMA1_Channel1_IRQn, + DMA1_Channel2_IRQn, + DMA1_Channel3_IRQn, + DMA1_Channel4_IRQn, + DMA1_Channel5_IRQn, + DMA1_Channel6_IRQn, + #if !defined(STM32G431xx) && !defined(STM32G441xx) + DMA1_Channel7_IRQn, + DMA1_Channel8_IRQn, + #endif + DMA2_Channel1_IRQn, + DMA2_Channel2_IRQn, + DMA2_Channel3_IRQn, + DMA2_Channel4_IRQn, + DMA2_Channel5_IRQn, + DMA2_Channel6_IRQn, + #if !defined(STM32G431xx) && !defined(STM32G441xx) + DMA2_Channel7_IRQn, + DMA2_Channel8_IRQn, + #endif +}; + #elif defined(STM32H7) #define NCONTROLLERS (2) @@ -742,6 +822,125 @@ void DMA2_Stream7_IRQHandler(void) { IRQ_EXIT(DMA2_Stream7_IRQn); } +#elif defined(STM32G4) + +void DMA1_Channel1_IRQHandler(void) { + IRQ_ENTER(DMA1_Channel1_IRQn); + if (dma_handle[dma_id_0] != NULL) { + HAL_DMA_IRQHandler(dma_handle[dma_id_0]); + } + IRQ_EXIT(DMA1_Channel1_IRQn); +} +void DMA1_Channel2_IRQHandler(void) { + IRQ_ENTER(DMA1_Channel2_IRQn); + if (dma_handle[dma_id_1] != NULL) { + HAL_DMA_IRQHandler(dma_handle[dma_id_1]); + } + IRQ_EXIT(DMA1_Channel2_IRQn); +} +void DMA1_Channel3_IRQHandler(void) { + IRQ_ENTER(DMA1_Channel3_IRQn); + if (dma_handle[dma_id_2] != NULL) { + HAL_DMA_IRQHandler(dma_handle[dma_id_2]); + } + IRQ_EXIT(DMA1_Channel3_IRQn); +} +void DMA1_Channel4_IRQHandler(void) { + IRQ_ENTER(DMA1_Channel4_IRQn); + if (dma_handle[dma_id_3] != NULL) { + HAL_DMA_IRQHandler(dma_handle[dma_id_3]); + } + IRQ_EXIT(DMA1_Channel4_IRQn); +} +void DMA1_Channel5_IRQHandler(void) { + IRQ_ENTER(DMA1_Channel5_IRQn); + if (dma_handle[dma_id_4] != NULL) { + HAL_DMA_IRQHandler(dma_handle[dma_id_4]); + } + IRQ_EXIT(DMA1_Channel5_IRQn); +} +void DMA1_Channel6_IRQHandler(void) { + IRQ_ENTER(DMA1_Channel6_IRQn); + if (dma_handle[dma_id_5] != NULL) { + HAL_DMA_IRQHandler(dma_handle[dma_id_5]); + } + IRQ_EXIT(DMA1_Channel6_IRQn); +} +#if !(defined(STM32G431xx) || defined(STM32G441xx)) +void DMA1_Channel7_IRQHandler(void) { + IRQ_ENTER(DMA1_Channel7_IRQn); + if (dma_handle[dma_id_12] != NULL) { + HAL_DMA_IRQHandler(dma_handle[dma_id_12]); + } + IRQ_EXIT(DMA1_Channel7_IRQn); +} +void DMA1_Channel8_IRQHandler(void) { + IRQ_ENTER(DMA1_Channel8_IRQn); + if (dma_handle[dma_id_13] != NULL) { + HAL_DMA_IRQHandler(dma_handle[dma_id_13]); + } + IRQ_EXIT(DMA1_Channel8_IRQn); +} +#endif +void DMA2_Channel1_IRQHandler(void) { + IRQ_ENTER(DMA2_Channel1_IRQn); + if (dma_handle[dma_id_6] != NULL) { + HAL_DMA_IRQHandler(dma_handle[dma_id_6]); + } + IRQ_EXIT(DMA2_Channel1_IRQn); +} +void DMA2_Channel2_IRQHandler(void) { + IRQ_ENTER(DMA2_Channel2_IRQn); + if (dma_handle[dma_id_7] != NULL) { + HAL_DMA_IRQHandler(dma_handle[dma_id_7]); + } + IRQ_EXIT(DMA2_Channel2_IRQn); +} +void DMA2_Channel3_IRQHandler(void) { + IRQ_ENTER(DMA2_Channel3_IRQn); + if (dma_handle[dma_id_8] != NULL) { + HAL_DMA_IRQHandler(dma_handle[dma_id_8]); + } + IRQ_EXIT(DMA2_Channel3_IRQn); +} +void DMA2_Channel4_IRQHandler(void) { + IRQ_ENTER(DMA2_Channel4_IRQn); + if (dma_handle[dma_id_9] != NULL) { + HAL_DMA_IRQHandler(dma_handle[dma_id_9]); + } + IRQ_EXIT(DMA2_Channel4_IRQn); +} +void DMA2_Channel5_IRQHandler(void) { + IRQ_ENTER(DMA2_Channel5_IRQn); + if (dma_handle[dma_id_10] != NULL) { + HAL_DMA_IRQHandler(dma_handle[dma_id_10]); + } + IRQ_EXIT(DMA2_Channel5_IRQn); +} +void DMA2_Channel6_IRQHandler(void) { + IRQ_ENTER(DMA2_Channel6_IRQn); + if (dma_handle[dma_id_11] != NULL) { + HAL_DMA_IRQHandler(dma_handle[dma_id_11]); + } + IRQ_EXIT(DMA2_Channel6_IRQn); +} +#if !(defined(STM32G431xx) || defined(STM32G441xx)) +void DMA2_Channel7_IRQHandler(void) { + IRQ_ENTER(DMA2_Channel7_IRQn); + if (dma_handle[dma_id_14] != NULL) { + HAL_DMA_IRQHandler(dma_handle[dma_id_14]); + } + IRQ_EXIT(DMA2_Channel7_IRQn); +} +void DMA2_Channel8_IRQHandler(void) { + IRQ_ENTER(DMA2_Channel8_IRQn); + if (dma_handle[dma_id_15] != NULL) { + HAL_DMA_IRQHandler(dma_handle[dma_id_15]); + } + IRQ_EXIT(DMA2_Channel8_IRQn); +} +#endif + #elif defined(STM32L0) void DMA1_Channel1_IRQHandler(void) { @@ -909,6 +1108,9 @@ static void dma_enable_clock(dma_id_t dma_id) { if (dma_id < NSTREAMS_PER_CONTROLLER) { if (((old_enable_mask & DMA1_ENABLE_MASK) == 0) && !DMA1_IS_CLK_ENABLED()) { __HAL_RCC_DMA1_CLK_ENABLE(); + #if defined(STM32G4) + __HAL_RCC_DMAMUX1_CLK_ENABLE(); + #endif // We just turned on the clock. This means that anything stored // in dma_last_channel (for DMA1) needs to be invalidated. @@ -923,6 +1125,9 @@ static void dma_enable_clock(dma_id_t dma_id) { if (((old_enable_mask & DMA2_ENABLE_MASK) == 0) && !DMA2_IS_CLK_ENABLED()) { __HAL_RCC_DMA2_CLK_ENABLE(); + #if defined(STM32G4) + __HAL_RCC_DMAMUX1_CLK_ENABLE(); + #endif // We just turned on the clock. This means that anything stored // in dma_last_channel (for DMA2) needs to be invalidated. @@ -947,7 +1152,7 @@ void dma_init_handle(DMA_HandleTypeDef *dma, const dma_descr_t *dma_descr, uint3 dma->Instance = dma_descr->instance; dma->Init = *dma_descr->init; dma->Init.Direction = dir; - #if defined(STM32L0) || defined(STM32L4) || defined(STM32H7) || defined(STM32WB) + #if defined(STM32G4) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) dma->Init.Request = dma_descr->sub_instance; #else #if !defined(STM32F0) @@ -974,7 +1179,7 @@ void dma_init(DMA_HandleTypeDef *dma, const dma_descr_t *dma_descr, uint32_t dir dma_enable_clock(dma_id); - #if defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) + #if defined(STM32G4) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) // Always reset and configure the H7 and L0/L4 DMA peripheral // (dma->State is set to HAL_DMA_STATE_RESET by memset above) // TODO: understand how L0/L4 DMA works so this is not needed @@ -1070,10 +1275,23 @@ static void dma_idle_handler(uint32_t tick) { dma_idle.counter[controller] = 0; if (controller == 0) { __HAL_RCC_DMA1_CLK_DISABLE(); + #if defined(STM32G4) + #if defined(DMA2) + if (__HAL_RCC_DMA2_IS_CLK_DISABLED()) + #endif + { + __HAL_RCC_DMAMUX1_CLK_DISABLE(); + } + #endif } #if defined(DMA2) else { __HAL_RCC_DMA2_CLK_DISABLE(); + #if defined(STM32G4) + if (__HAL_RCC_DMA1_IS_CLK_DISABLED()) { + __HAL_RCC_DMAMUX1_CLK_DISABLE(); + } + #endif } #endif } else { @@ -1085,7 +1303,7 @@ static void dma_idle_handler(uint32_t tick) { } } -#if defined(STM32F0) || defined(STM32L0) || defined(STM32L4) +#if defined(STM32F0) || defined(STM32G4) || defined(STM32L0) || defined(STM32L4) void dma_nohal_init(const dma_descr_t *descr, uint32_t config) { DMA_Channel_TypeDef *dma = descr->instance; @@ -1108,6 +1326,9 @@ void dma_nohal_init(const dma_descr_t *descr, uint32_t config) { } else { __HAL_DMA2_REMAP(descr->sub_instance); } + #elif defined(STM32G4) + uint32_t *dmamux_ctrl = (void *)(DMAMUX1_Channel0_BASE + 0x04 * descr->id); + *dmamux_ctrl = (*dmamux_ctrl & ~(0x7f)) | descr->sub_instance; #else DMA_Request_TypeDef *dma_ctrl = (void *)(((uint32_t)dma & ~0xff) + (DMA1_CSELR_BASE - DMA1_BASE)); // DMA1_CSELR or DMA2_CSELR uint32_t channel_number = (((uint32_t)dma & 0xff) - 0x08) / 20; // 0 through 6 diff --git a/ports/stm32/dma.h b/ports/stm32/dma.h index 00200bae46..29d5c3d2eb 100644 --- a/ports/stm32/dma.h +++ b/ports/stm32/dma.h @@ -62,6 +62,29 @@ extern const dma_descr_t dma_I2S_1_TX; extern const dma_descr_t dma_I2S_2_RX; extern const dma_descr_t dma_I2S_2_TX; +#elif defined(STM32G4) + +extern const dma_descr_t dma_SPI_1_RX; +extern const dma_descr_t dma_SPI_1_TX; +extern const dma_descr_t dma_SPI_2_RX; +extern const dma_descr_t dma_SPI_2_TX; +extern const dma_descr_t dma_I2C_1_RX; +extern const dma_descr_t dma_I2C_1_TX; +extern const dma_descr_t dma_I2C_2_RX; +extern const dma_descr_t dma_I2C_2_TX; +extern const dma_descr_t dma_I2C_3_RX; +extern const dma_descr_t dma_I2C_3_TX; +extern const dma_descr_t dma_UART_3_RX; +extern const dma_descr_t dma_UART_3_TX; +extern const dma_descr_t dma_DAC_1_TX; +extern const dma_descr_t dma_DAC_2_TX; +extern const dma_descr_t dma_UART_1_RX; +extern const dma_descr_t dma_UART_1_TX; +extern const dma_descr_t dma_LPUART_1_RX; +extern const dma_descr_t dma_LPUART_1_TX; +extern const dma_descr_t dma_ADC_1; +extern const dma_descr_t dma_MEM_2_MEM; + #elif defined(STM32L0) extern const dma_descr_t dma_SPI_1_RX; diff --git a/ports/stm32/extint.c b/ports/stm32/extint.c index 699bc60040..bb26b00dde 100644 --- a/ports/stm32/extint.c +++ b/ports/stm32/extint.c @@ -91,7 +91,7 @@ #define EXTI_SWIER_BB(line) (*(__IO uint32_t *)(PERIPH_BB_BASE + ((EXTI_OFFSET + offsetof(EXTI_TypeDef, SWIER)) * 32) + ((line) * 4))) #endif -#if defined(STM32L4) || defined(STM32WB) +#if defined(STM32G4) || defined(STM32L4) || defined(STM32WB) // The L4 MCU supports 40 Events/IRQs lines of the type configurable and direct. // Here we only support configurable line types. Details, see page 330 of RM0351, Rev 1. // The USB_FS_WAKUP event is a direct type and there is no support for it. @@ -138,6 +138,10 @@ STATIC mp_obj_t pyb_extint_callback_arg[EXTI_NUM_VECTORS]; #define OTG_FS_WKUP_IRQn 42 // Some MCUs don't have FS IRQ, but we want a value to put in our table #endif +#if defined(STM32G4) +#define TAMP_STAMP_IRQn RTC_TAMP_LSECSS_IRQn +#endif + STATIC const uint8_t nvic_irq_channel[EXTI_NUM_VECTORS] = { #if defined(STM32F0) || defined(STM32L0) @@ -178,7 +182,7 @@ STATIC const uint8_t nvic_irq_channel[EXTI_NUM_VECTORS] = { TAMP_STAMP_LSECSS_IRQn, RTC_WKUP_IRQn, #else - #if defined(STM32L4) + #if defined(STM32G4) || defined(STM32L4) PVD_PVM_IRQn, #else PVD_IRQn, @@ -381,7 +385,7 @@ void extint_enable(uint line) { if (pyb_extint_mode[line] == EXTI_Mode_Interrupt) { #if defined(STM32H7) EXTI_D1->IMR1 |= (1 << line); - #elif defined(STM32WB) + #elif defined(STM32G4) || defined(STM32WB) EXTI->IMR1 |= (1 << line); #else EXTI->IMR |= (1 << line); @@ -389,7 +393,7 @@ void extint_enable(uint line) { } else { #if defined(STM32H7) EXTI_D1->EMR1 |= (1 << line); - #elif defined(STM32WB) + #elif defined(STM32G4) || defined(STM32WB) EXTI->EMR1 |= (1 << line); #else EXTI->EMR |= (1 << line); @@ -415,7 +419,7 @@ void extint_disable(uint line) { #if defined(STM32H7) EXTI_D1->IMR1 &= ~(1 << line); EXTI_D1->EMR1 &= ~(1 << line); - #elif defined(STM32WB) + #elif defined(STM32G4) || defined(STM32WB) EXTI->IMR1 &= ~(1 << line); EXTI->EMR1 &= ~(1 << line); #else @@ -437,7 +441,7 @@ void extint_swint(uint line) { return; } // we need 0 to 1 transition to trigger the interrupt - #if defined(STM32L4) || defined(STM32H7) || defined(STM32WB) + #if defined(STM32G4) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB) EXTI->SWIER1 &= ~(1 << line); EXTI->SWIER1 |= (1 << line); #else @@ -515,7 +519,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(extint_obj_swint_obj, extint_obj_swint); /// \classmethod regs() /// Dump the values of the EXTI registers. STATIC mp_obj_t extint_regs(void) { - #if defined(STM32L4) || defined(STM32WB) + #if defined(STM32G4) || defined(STM32L4) || defined(STM32WB) printf("EXTI_IMR1 %08x\n", (unsigned int)EXTI->IMR1); printf("EXTI_IMR2 %08x\n", (unsigned int)EXTI->IMR2); printf("EXTI_EMR1 %08x\n", (unsigned int)EXTI->EMR1); diff --git a/ports/stm32/extint.h b/ports/stm32/extint.h index 907af53dc2..0cdf5024a0 100644 --- a/ports/stm32/extint.h +++ b/ports/stm32/extint.h @@ -43,7 +43,7 @@ #endif #define EXTI_ETH_WAKEUP (19) #define EXTI_USB_OTG_HS_WAKEUP (20) -#if defined(STM32F0) || defined(STM32L4) +#if defined(STM32F0) || defined(STM32G4) || defined(STM32L4) #define EXTI_RTC_TIMESTAMP (19) #define EXTI_RTC_WAKEUP (20) #elif defined(STM32H7) || defined(STM32WB) diff --git a/ports/stm32/flash.c b/ports/stm32/flash.c index b926679f9f..eeeab52049 100644 --- a/ports/stm32/flash.c +++ b/ports/stm32/flash.c @@ -97,7 +97,7 @@ static const flash_layout_t flash_layout[] = { }; #endif -#elif defined(STM32L0) || defined(STM32L4) || defined(STM32WB) +#elif defined(STM32G4) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) static const flash_layout_t flash_layout[] = { { (uint32_t)FLASH_BASE, (uint32_t)FLASH_PAGE_SIZE, 512 }, @@ -126,12 +126,20 @@ static uint32_t get_bank(uint32_t addr) { if (addr < (FLASH_BASE + FLASH_BANK_SIZE)) { return FLASH_BANK_1; } else { + #if defined(FLASH_OPTR_DBANK) return FLASH_BANK_2; + #else + return 0; + #endif } } else { // bank swap if (addr < (FLASH_BASE + FLASH_BANK_SIZE)) { + #if defined(FLASH_OPTR_DBANK) return FLASH_BANK_2; + #else + return 0; + #endif } else { return FLASH_BANK_1; } @@ -157,6 +165,25 @@ static uint32_t get_page(uint32_t addr) { return (addr - FLASH_BASE) / FLASH_PAGE_SIZE; } +#elif defined(STM32G4) + +static uint32_t get_page(uint32_t addr) { + return (addr - FLASH_BASE) / FLASH_PAGE_SIZE; +} + +static uint32_t get_bank(uint32_t addr) { + // no bank swap + if (addr < (FLASH_BASE + FLASH_BANK_SIZE)) { + return FLASH_BANK_1; + } else { + #if defined(FLASH_OPTR_DBANK) + return FLASH_BANK_2; + #else + return 0; + #endif + } +} + #endif bool flash_is_valid_addr(uint32_t addr) { @@ -225,6 +252,12 @@ int flash_erase(uint32_t flash_dest, uint32_t num_word32) { EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES; EraseInitStruct.PageAddress = flash_dest; EraseInitStruct.NbPages = (4 * num_word32 + FLASH_PAGE_SIZE - 4) / FLASH_PAGE_SIZE; + #elif defined(STM32G4) + __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS); + EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES; + EraseInitStruct.Page = get_page(flash_dest); + EraseInitStruct.Banks = get_bank(flash_dest); + EraseInitStruct.NbPages = (4 * num_word32 + FLASH_PAGE_SIZE - 4) / FLASH_PAGE_SIZE; #elif defined(STM32L0) __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR); EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES; @@ -258,7 +291,7 @@ int flash_erase(uint32_t flash_dest, uint32_t num_word32) { #else EraseInitStruct.VoltageRange = 0; // unused parameter on STM32H7A3/B3 #endif - #if defined(STM32H7) + #if defined(STM32G4) || defined(STM32H7) EraseInitStruct.Banks = get_bank(flash_dest); #endif EraseInitStruct.Sector = flash_get_sector_info(flash_dest, NULL, NULL); @@ -337,7 +370,7 @@ int flash_write(uint32_t flash_dest, const uint32_t *src, uint32_t num_word32) { HAL_StatusTypeDef status = HAL_OK; - #if defined(STM32L4) || defined(STM32WB) + #if defined(STM32G4) || defined(STM32L4) || defined(STM32WB) // program the flash uint64 by uint64 for (int i = 0; i < num_word32 / 2; i++) { diff --git a/ports/stm32/machine_adc.c b/ports/stm32/machine_adc.c index 8480bff53e..21b35cdcdb 100644 --- a/ports/stm32/machine_adc.c +++ b/ports/stm32/machine_adc.c @@ -28,7 +28,7 @@ #include "py/mphal.h" #include "adc.h" -#if defined(STM32F0) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) +#if defined(STM32F0) || defined(STM32G4) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) #define ADC_V2 (1) #else #define ADC_V2 (0) @@ -45,6 +45,8 @@ #if defined(STM32F0) || defined(STM32L0) #define ADC_STAB_DELAY_US (1) #define ADC_TEMPSENSOR_DELAY_US (10) +#elif defined(STM32G4) +#define ADC_STAB_DELAY_US (1) // TODO: Check if this is enough #elif defined(STM32L4) #define ADC_STAB_DELAY_US (10) #elif defined(STM32WB) @@ -57,6 +59,9 @@ #elif defined(STM32F4) || defined(STM32F7) #define ADC_SAMPLETIME_DEFAULT ADC_SAMPLETIME_15CYCLES #define ADC_SAMPLETIME_DEFAULT_INT ADC_SAMPLETIME_480CYCLES +#elif defined(STM32G4) +#define ADC_SAMPLETIME_DEFAULT ADC_SAMPLETIME_12CYCLES_5 +#define ADC_SAMPLETIME_DEFAULT_INT ADC_SAMPLETIME_247CYCLES_5 #elif defined(STM32H7) #define ADC_SAMPLETIME_DEFAULT ADC_SAMPLETIME_8CYCLES_5 #define ADC_SAMPLETIME_DEFAULT_INT ADC_SAMPLETIME_387CYCLES_5 @@ -105,7 +110,7 @@ void adc_config(ADC_TypeDef *adc, uint32_t bits) { __HAL_RCC_ADC_CLK_ENABLE(); #else if (adc == ADC1) { - #if defined(STM32H7) + #if defined(STM32G4) || defined(STM32H7) __HAL_RCC_ADC12_CLK_ENABLE(); #else __HAL_RCC_ADC1_CLK_ENABLE(); @@ -113,7 +118,7 @@ void adc_config(ADC_TypeDef *adc, uint32_t bits) { } #if defined(ADC2) if (adc == ADC2) { - #if defined(STM32H7) + #if defined(STM32G4) || defined(STM32H7) __HAL_RCC_ADC12_CLK_ENABLE(); #else __HAL_RCC_ADC2_CLK_ENABLE(); @@ -122,7 +127,11 @@ void adc_config(ADC_TypeDef *adc, uint32_t bits) { #endif #if defined(ADC3) if (adc == ADC3) { + #if defined(ADC345_COMMON) + __HAL_RCC_ADC345_CLK_ENABLE(); + #else __HAL_RCC_ADC3_CLK_ENABLE(); + #endif } #endif #endif @@ -165,7 +174,7 @@ void adc_config(ADC_TypeDef *adc, uint32_t bits) { // ADC isn't enabled so calibrate it now #if defined(STM32F0) || defined(STM32L0) LL_ADC_StartCalibration(adc); - #elif defined(STM32L4) || defined(STM32WB) + #elif defined(STM32G4) || defined(STM32L4) || defined(STM32WB) LL_ADC_StartCalibration(adc, LL_ADC_SINGLE_ENDED); #else LL_ADC_StartCalibration(adc, LL_ADC_CALIB_OFFSET_LINEARITY, LL_ADC_SINGLE_ENDED); @@ -230,7 +239,7 @@ STATIC int adc_get_bits(ADC_TypeDef *adc) { uint32_t res = (adc->CFGR1 & ADC_CFGR1_RES) >> ADC_CFGR1_RES_Pos; #elif defined(STM32F4) || defined(STM32F7) uint32_t res = (adc->CR1 & ADC_CR1_RES) >> ADC_CR1_RES_Pos; - #elif defined(STM32H7) || defined(STM32L4) || defined(STM32WB) + #elif defined(STM32G4) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB) uint32_t res = (adc->CFGR & ADC_CFGR_RES) >> ADC_CFGR_RES_Pos; #endif return adc_cr_to_bits_table[res]; @@ -403,7 +412,11 @@ STATIC mp_obj_t machine_adc_make_new(const mp_obj_type_t *type, size_t n_args, s adc = ADC1; channel = mp_obj_get_int(source); if (channel == ADC_CHANNEL_VREFINT + #if defined(STM32G4) + || channel == ADC_CHANNEL_TEMPSENSOR_ADC1 + #else || channel == ADC_CHANNEL_TEMPSENSOR + #endif #if defined(ADC_CHANNEL_VBAT) || channel == ADC_CHANNEL_VBAT #endif @@ -455,7 +468,11 @@ STATIC const mp_rom_map_elem_t machine_adc_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_VREF), MP_ROM_INT(ADC_CHANNEL_VREF) }, { MP_ROM_QSTR(MP_QSTR_CORE_VREF), MP_ROM_INT(ADC_CHANNEL_VREFINT) }, + #if defined(STM32G4) + { MP_ROM_QSTR(MP_QSTR_CORE_TEMP), MP_ROM_INT(ADC_CHANNEL_TEMPSENSOR_ADC1) }, + #else { MP_ROM_QSTR(MP_QSTR_CORE_TEMP), MP_ROM_INT(ADC_CHANNEL_TEMPSENSOR) }, + #endif #if defined(ADC_CHANNEL_VBAT) { MP_ROM_QSTR(MP_QSTR_CORE_VBAT), MP_ROM_INT(ADC_CHANNEL_VBAT) }, #endif diff --git a/ports/stm32/machine_uart.c b/ports/stm32/machine_uart.c index 354a1e26ac..6a12fad324 100644 --- a/ports/stm32/machine_uart.c +++ b/ports/stm32/machine_uart.c @@ -459,7 +459,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_uart_readchar_obj, pyb_uart_readchar); // uart.sendbreak() STATIC mp_obj_t pyb_uart_sendbreak(mp_obj_t self_in) { pyb_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); - #if defined(STM32F0) || defined(STM32F7) || defined(STM32L0) || defined(STM32L4) || defined(STM32H7) || defined(STM32WB) + #if defined(STM32F0) || defined(STM32F7) || defined(STM32G4) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) self->uartx->RQR = USART_RQR_SBKRQ; // write-only register #else self->uartx->CR1 |= USART_CR1_SBK; diff --git a/ports/stm32/modmachine.c b/ports/stm32/modmachine.c index 92b80b3652..9b215c75cd 100644 --- a/ports/stm32/modmachine.c +++ b/ports/stm32/modmachine.c @@ -63,7 +63,7 @@ #define RCC_CSR_BORRSTF RCC_CSR_PORRSTF #endif -#if defined(STM32L4) || defined(STM32WB) +#if defined(STM32G4) || defined(STM32L4) || defined(STM32WB) // L4 does not have a POR, so use BOR instead #define RCC_CSR_PORRSTF RCC_CSR_BORRSTF #endif diff --git a/ports/stm32/mpconfigboard_common.h b/ports/stm32/mpconfigboard_common.h index f8342f51b9..17a4ad26e4 100644 --- a/ports/stm32/mpconfigboard_common.h +++ b/ports/stm32/mpconfigboard_common.h @@ -319,6 +319,16 @@ #define MICROPY_HW_MAX_UART (8) #define MICROPY_HW_MAX_LPUART (0) +// Configuration for STM32G4 series +#elif defined(STM32G4) + +#define MP_HAL_UNIQUE_ID_ADDRESS (UID_BASE) +#define PYB_EXTI_NUM_VECTORS (42) // up to 42 event/interrupt requests: 28 configurable lines, 14 direct lines +#define MICROPY_HW_MAX_I2C (3) +#define MICROPY_HW_MAX_TIMER (20) // TIM1-8, 20 +#define MICROPY_HW_MAX_UART (5) // UART1-5 + LPUART1 +#define MICROPY_HW_MAX_LPUART (1) + // Configuration for STM32H7A3/B3 series #elif defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || \ defined(STM32H7B3xx) || defined(STM32H7B3xxQ) @@ -410,7 +420,12 @@ #else // Use HSE as a clock source (bypass or oscillator) #define MICROPY_HW_CLK_VALUE (HSE_VALUE) +#if defined(STM32G4) +// enable HSI48 to run RNG on this clock +#define MICROPY_HW_RCC_OSCILLATOR_TYPE (RCC_OSCILLATORTYPE_HSE | RCC_OSCILLATORTYPE_HSI48) +#else #define MICROPY_HW_RCC_OSCILLATOR_TYPE (RCC_OSCILLATORTYPE_HSE) +#endif #define MICROPY_HW_RCC_PLL_SRC (RCC_PLLSOURCE_HSE) #define MICROPY_HW_RCC_CR_HSxON (RCC_CR_HSEON) #define MICROPY_HW_RCC_HSI_STATE (RCC_HSI_OFF) @@ -480,7 +495,7 @@ // Enable CAN if there are any peripherals defined #if defined(MICROPY_HW_CAN1_TX) || defined(MICROPY_HW_CAN2_TX) || defined(MICROPY_HW_CAN3_TX) #define MICROPY_HW_ENABLE_CAN (1) -#if defined(STM32H7) +#if defined(STM32G4) || defined(STM32H7) #define MICROPY_HW_ENABLE_FDCAN (1) // define for MCUs with FDCAN #endif #else diff --git a/ports/stm32/mphalport.c b/ports/stm32/mphalport.c index 81e84ef933..2b98a620ef 100644 --- a/ports/stm32/mphalport.c +++ b/ports/stm32/mphalport.c @@ -99,7 +99,7 @@ void mp_hal_gpio_clock_enable(GPIO_TypeDef *gpio) { #elif defined(STM32L0) #define AHBxENR IOPENR #define AHBxENR_GPIOAEN_Pos RCC_IOPENR_IOPAEN_Pos - #elif defined(STM32L4) || defined(STM32WB) + #elif defined(STM32G4) || defined(STM32L4) || defined(STM32WB) #define AHBxENR AHB2ENR #define AHBxENR_GPIOAEN_Pos RCC_AHB2ENR_GPIOAEN_Pos #endif diff --git a/ports/stm32/pin_defs_stm32.h b/ports/stm32/pin_defs_stm32.h index 20faab4e03..1f950c1179 100644 --- a/ports/stm32/pin_defs_stm32.h +++ b/ports/stm32/pin_defs_stm32.h @@ -121,8 +121,8 @@ enum { #define I2S2 SPI2 #define I2S3 SPI3 -#if defined(STM32H7) -// Make H7 FDCAN more like CAN +#if defined(STM32G4) || defined(STM32H7) +// Make G4/H7 FDCAN more like CAN #define CAN1 FDCAN1 #define CAN2 FDCAN2 #define GPIO_AF9_CAN1 GPIO_AF9_FDCAN1 diff --git a/ports/stm32/powerctrl.c b/ports/stm32/powerctrl.c index a3f8207e9a..ad3a3ea4c2 100644 --- a/ports/stm32/powerctrl.c +++ b/ports/stm32/powerctrl.c @@ -143,7 +143,7 @@ void powerctrl_check_enter_bootloader(void) { if (BL_STATE_GET_KEY(bl_state) == BL_STATE_KEY && (RCC->RCC_SR & RCC_SR_SFTRSTF)) { // Reset by NVIC_SystemReset with bootloader data set -> branch to bootloader RCC->RCC_SR = RCC_SR_RMVF; - #if defined(STM32F0) || defined(STM32F4) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) + #if defined(STM32F0) || defined(STM32F4) || defined(STM32G4) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) __HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH(); #endif branch_to_bootloader(BL_STATE_GET_REG(bl_state), BL_STATE_GET_ADDR(bl_state)); @@ -378,7 +378,7 @@ STATIC uint32_t calc_apb2_div(uint32_t wanted_div) { #endif } -#if defined(STM32F4) || defined(STM32F7) || defined(STM32H7) +#if defined(STM32F4) || defined(STM32F7) || defined(STM32G4) || defined(STM32H7) int powerctrl_set_sysclk(uint32_t sysclk, uint32_t ahb, uint32_t apb1, uint32_t apb2) { // Return straightaway if the clocks are already at the desired frequency @@ -686,7 +686,7 @@ void powerctrl_enter_stop_mode(void) { __HAL_RCC_WAKEUPSTOP_CLK_CONFIG(RCC_STOP_WAKEUPCLOCK_MSI); #endif - #if !defined(STM32F0) && !defined(STM32L0) && !defined(STM32L4) && !defined(STM32WB) + #if !defined(STM32F0) && !defined(STM32G4) && !defined(STM32L0) && !defined(STM32L4) && !defined(STM32WB) // takes longer to wake but reduces stop current HAL_PWREx_EnableFlashPowerDown(); #endif @@ -861,6 +861,9 @@ void powerctrl_enter_standby_mode(void) { #if defined(STM32F0) || defined(STM32L0) #define CR_BITS (RTC_CR_ALRAIE | RTC_CR_WUTIE | RTC_CR_TSIE) #define ISR_BITS (RTC_ISR_ALRAF | RTC_ISR_WUTF | RTC_ISR_TSF) + #elif defined(STM32G4) + #define CR_BITS (RTC_CR_ALRAIE | RTC_CR_ALRBIE | RTC_CR_WUTIE | RTC_CR_TSIE) + #define ISR_BITS (RTC_MISR_ALRAMF | RTC_MISR_ALRBMF | RTC_MISR_WUTMF | RTC_MISR_TSMF) #elif defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ) #define CR_BITS (RTC_CR_ALRAIE | RTC_CR_ALRBIE | RTC_CR_WUTIE | RTC_CR_TSIE) #define SR_BITS (RTC_SR_ALRAF | RTC_SR_ALRBF | RTC_SR_WUTF | RTC_SR_TSF) @@ -882,6 +885,8 @@ void powerctrl_enter_standby_mode(void) { // clear RTC wake-up flags #if defined(SR_BITS) RTC->SR &= ~SR_BITS; + #elif defined(STM32G4) + RTC->MISR &= ~ISR_BITS; #else RTC->ISR &= ~ISR_BITS; #endif @@ -898,7 +903,7 @@ void powerctrl_enter_standby_mode(void) { #elif defined(STM32H7) EXTI_D1->PR1 = 0x3fffff; PWR->WKUPCR |= PWR_WAKEUP_FLAG1 | PWR_WAKEUP_FLAG2 | PWR_WAKEUP_FLAG3 | PWR_WAKEUP_FLAG4 | PWR_WAKEUP_FLAG5 | PWR_WAKEUP_FLAG6; - #elif defined(STM32L4) || defined(STM32WB) + #elif defined(STM32G4) || defined(STM32L4) || defined(STM32WB) // clear all wake-up flags PWR->SCR |= PWR_SCR_CWUF5 | PWR_SCR_CWUF4 | PWR_SCR_CWUF3 | PWR_SCR_CWUF2 | PWR_SCR_CWUF1; // TODO diff --git a/ports/stm32/pyb_i2c.c b/ports/stm32/pyb_i2c.c index dbcf4bcb81..70a8e9f091 100644 --- a/ports/stm32/pyb_i2c.c +++ b/ports/stm32/pyb_i2c.c @@ -130,7 +130,7 @@ const pyb_i2c_obj_t pyb_i2c_obj[] = { #endif }; -#if defined(STM32F7) || defined(STM32L4) || defined(STM32H7) +#if defined(STM32F7) || defined(STM32G4) || defined(STM32L4) || defined(STM32H7) // The STM32F0, F3, F7, H7 and L4 use a TIMINGR register rather than ClockSpeed and // DutyCycle. @@ -163,6 +163,28 @@ const pyb_i2c_obj_t pyb_i2c_obj[] = { #define MICROPY_HW_I2C_BAUDRATE_DEFAULT (PYB_I2C_SPEED_FULL) #define MICROPY_HW_I2C_BAUDRATE_MAX (PYB_I2C_SPEED_FAST) +#elif defined(STM32G4) +// timing input depends on PLL +// for now: 170MHz sysclock, PCLK 10.625 MHz +// using PCLOCK +// generated using CubeMX +#if defined(STM32G431xx) || defined(STM32G441xx) +#define MICROPY_HW_I2C_BAUDRATE_TIMING { \ + {PYB_I2C_SPEED_STANDARD, 0x30A0A7FB}, \ + {PYB_I2C_SPEED_STANDARD, 0x30A0A7FB}, \ + {PYB_I2C_SPEED_STANDARD, 0x30A0A7FB}, \ +} +#else +#define MICROPY_HW_I2C_BAUDRATE_TIMING { \ + {PYB_I2C_SPEED_STANDARD, 0x30A0A7FB}, \ + {PYB_I2C_SPEED_STANDARD, 0x30A0A7FB}, \ + {PYB_I2C_SPEED_STANDARD, 0x30A0A7FB}, \ + {PYB_I2C_SPEED_STANDARD, 0x30A0A7FB}, \ +} +#endif +#define MICROPY_HW_I2C_BAUDRATE_DEFAULT (PYB_I2C_SPEED_STANDARD) +#define MICROPY_HW_I2C_BAUDRATE_MAX (PYB_I2C_SPEED_STANDARD) + #elif defined(STM32H7) // I2C TIMINGs obtained from the STHAL examples. diff --git a/ports/stm32/rtc.c b/ports/stm32/rtc.c index a5553b8a0a..2d6459e239 100644 --- a/ports/stm32/rtc.c +++ b/ports/stm32/rtc.c @@ -220,7 +220,7 @@ void rtc_init_finalise() { // fresh reset; configure RTC Calendar RTC_CalendarConfig(); - #if defined(STM32L4) || defined(STM32WB) + #if defined(STM32G4) || defined(STM32L4) || defined(STM32WB) if (__HAL_RCC_GET_FLAG(RCC_FLAG_BORRST) != RESET) { #else if (__HAL_RCC_GET_FLAG(RCC_FLAG_PORRST) != RESET) { @@ -261,7 +261,7 @@ STATIC HAL_StatusTypeDef PYB_RCC_OscConfig(RCC_OscInitTypeDef *RCC_OscInitStruct HAL_PWR_EnableBkUpAccess(); uint32_t tickstart = HAL_GetTick(); - #if defined(STM32F7) || defined(STM32L4) || defined(STM32H7) || defined(STM32WB) + #if defined(STM32F7) || defined(STM32G4) || defined(STM32L4) || defined(STM32H7) || defined(STM32WB) // __HAL_RCC_PWR_CLK_ENABLE(); // Enable write access to Backup domain // PWR->CR1 |= PWR_CR1_DBP; @@ -339,7 +339,7 @@ STATIC HAL_StatusTypeDef PYB_RTC_Init(RTC_HandleTypeDef *hrtc) { hrtc->Instance->PRER |= (uint32_t)(hrtc->Init.AsynchPrediv << 16); // Exit Initialization mode - #if defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ) + #if defined(STM32G4) || defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ) hrtc->Instance->ICSR &= (uint32_t) ~RTC_ICSR_INIT; #else hrtc->Instance->ISR &= (uint32_t) ~RTC_ISR_INIT; @@ -353,6 +353,9 @@ STATIC HAL_StatusTypeDef PYB_RTC_Init(RTC_HandleTypeDef *hrtc) { #elif defined(STM32F7) hrtc->Instance->OR &= (uint32_t) ~RTC_OR_ALARMTYPE; hrtc->Instance->OR |= (uint32_t)(hrtc->Init.OutPutType); + #elif defined(STM32G4) + hrtc->Instance->CR &= (uint32_t) ~RTC_CR_TAMPALRM_TYPE_Msk; + hrtc->Instance->CR |= (uint32_t)(hrtc->Init.OutPutType); #else hrtc->Instance->TAFCR &= (uint32_t) ~RTC_TAFCR_ALARMOUTTYPE; hrtc->Instance->TAFCR |= (uint32_t)(hrtc->Init.OutPutType); @@ -699,7 +702,7 @@ mp_obj_t pyb_rtc_wakeup(size_t n_args, const mp_obj_t *args) { RTC->CR &= ~RTC_CR_WUTE; // wait until WUTWF is set - #if defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ) + #if defined(STM32G4) || defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ) while (!(RTC->ICSR & RTC_ICSR_WUTWF)) { } #else @@ -720,7 +723,7 @@ mp_obj_t pyb_rtc_wakeup(size_t n_args, const mp_obj_t *args) { RTC->WPR = 0xff; // enable external interrupts on line EXTI_RTC_WAKEUP - #if defined(STM32L4) || defined(STM32WB) + #if defined(STM32G4) || defined(STM32L4) || defined(STM32WB) EXTI->IMR1 |= 1 << EXTI_RTC_WAKEUP; EXTI->RTSR1 |= 1 << EXTI_RTC_WAKEUP; #elif defined(STM32H7) @@ -732,12 +735,14 @@ mp_obj_t pyb_rtc_wakeup(size_t n_args, const mp_obj_t *args) { #endif // clear interrupt flags - #if defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ) + #if defined(STM32G4) + RTC->ICSR &= ~RTC_ICSR_WUTWF; + #elif defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ) RTC->SR &= ~RTC_SR_WUTF; #else RTC->ISR &= ~RTC_ISR_WUTF; #endif - #if defined(STM32L4) || defined(STM32WB) + #if defined(STM32G4) || defined(STM32L4) || defined(STM32WB) EXTI->PR1 = 1 << EXTI_RTC_WAKEUP; #elif defined(STM32H7) EXTI_D1->PR1 = 1 << EXTI_RTC_WAKEUP; @@ -757,7 +762,7 @@ mp_obj_t pyb_rtc_wakeup(size_t n_args, const mp_obj_t *args) { RTC->WPR = 0xff; // disable external interrupts on line EXTI_RTC_WAKEUP - #if defined(STM32L4) || defined(STM32WB) + #if defined(STM32G4) || defined(STM32L4) || defined(STM32WB) EXTI->IMR1 &= ~(1 << EXTI_RTC_WAKEUP); #elif defined(STM32H7) EXTI_D1->IMR1 |= 1 << EXTI_RTC_WAKEUP; diff --git a/ports/stm32/stm32_it.c b/ports/stm32/stm32_it.c index fe9b606225..84274e3c62 100644 --- a/ports/stm32/stm32_it.c +++ b/ports/stm32/stm32_it.c @@ -529,7 +529,9 @@ void TAMP_STAMP_IRQHandler(void) { void RTC_WKUP_IRQHandler(void) { IRQ_ENTER(RTC_WKUP_IRQn); - #if defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ) + #if defined(STM32G4) + RTC->MISR &= ~RTC_MISR_WUTMF; // clear wakeup interrupt flag + #elif defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ) RTC->SR &= ~RTC_SR_WUTF; // clear wakeup interrupt flag #else RTC->ISR &= ~RTC_ISR_WUTF; // clear wakeup interrupt flag @@ -593,7 +595,7 @@ void TIM1_BRK_TIM9_IRQHandler(void) { IRQ_EXIT(TIM1_BRK_TIM9_IRQn); } -#if defined(STM32L4) +#if defined(STM32G4) || defined(STM32L4) void TIM1_BRK_TIM15_IRQHandler(void) { IRQ_ENTER(TIM1_BRK_TIM15_IRQn); timer_irq_handler(15); @@ -608,7 +610,7 @@ void TIM1_UP_TIM10_IRQHandler(void) { IRQ_EXIT(TIM1_UP_TIM10_IRQn); } -#if defined(STM32L4) || defined(STM32WB) +#if defined(STM32G4) || defined(STM32L4) || defined(STM32WB) void TIM1_UP_TIM16_IRQHandler(void) { IRQ_ENTER(TIM1_UP_TIM16_IRQn); timer_irq_handler(1); @@ -631,7 +633,7 @@ void TIM1_TRG_COM_TIM11_IRQHandler(void) { IRQ_EXIT(TIM1_TRG_COM_TIM11_IRQn); } -#if defined(STM32L4) || defined(STM32WB) +#if defined(STM32G4) || defined(STM32L4) || defined(STM32WB) void TIM1_TRG_COM_TIM17_IRQHandler(void) { IRQ_ENTER(TIM1_TRG_COM_TIM17_IRQn); timer_irq_handler(17); @@ -679,12 +681,20 @@ void TIM6_DAC_IRQHandler(void) { #endif #if defined(TIM7) // STM32F401 doesn't have TIM7 +#if defined(STM32G4) +void TIM7_DAC_IRQHandler(void) { + IRQ_ENTER(TIM7_DAC_IRQn); + timer_irq_handler(7); + IRQ_EXIT(TIM7_DAC_IRQn); +} +#else void TIM7_IRQHandler(void) { IRQ_ENTER(TIM7_IRQn); timer_irq_handler(7); IRQ_EXIT(TIM7_IRQn); } #endif +#endif #if defined(TIM8) // STM32F401 doesn't have TIM8 void TIM8_BRK_TIM12_IRQHandler(void) { @@ -700,7 +710,7 @@ void TIM8_UP_TIM13_IRQHandler(void) { IRQ_EXIT(TIM8_UP_TIM13_IRQn); } -#if defined(STM32L4) +#if defined(STM32G4) || defined(STM32L4) void TIM8_UP_IRQHandler(void) { IRQ_ENTER(TIM8_UP_IRQn); timer_irq_handler(8); diff --git a/ports/stm32/system_stm32.c b/ports/stm32/system_stm32.c index 8142fd0a5c..582d7a3698 100644 --- a/ports/stm32/system_stm32.c +++ b/ports/stm32/system_stm32.c @@ -78,7 +78,7 @@ #include "py/mphal.h" #include "powerctrl.h" -#if defined(STM32F4) || defined(STM32F7) || defined(STM32H7) || defined(STM32L4) +#if defined(STM32F4) || defined(STM32F7) || defined(STM32G4) || defined(STM32H7) || defined(STM32L4) void __fatal_error(const char *msg); @@ -171,10 +171,10 @@ void SystemClock_Config(void) { RCC->DCKCFGR2 = 0; #endif - RCC_ClkInitTypeDef RCC_ClkInitStruct; - RCC_OscInitTypeDef RCC_OscInitStruct; - #if defined(STM32H7) - RCC_PeriphCLKInitTypeDef PeriphClkInitStruct; + RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; + RCC_OscInitTypeDef RCC_OscInitStruct = {0}; + #if defined(STM32G4) || defined(STM32H7) + RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0}; #endif #if defined(STM32F4) || defined(STM32F7) || defined(STM32H7) @@ -192,6 +192,10 @@ void SystemClock_Config(void) { clocked below the maximum system frequency, to update the voltage scaling value regarding system frequency refer to product datasheet. */ __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); + + #elif defined(STM32G4) + // Configure the main internal regulator output voltage + HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1_BOOST); #elif defined(STM32L4) // Configure LSE Drive Capability __HAL_RCC_LSEDRIVE_CONFIG(RCC_LSEDRIVE_LOW); @@ -204,7 +208,7 @@ void SystemClock_Config(void) { #endif /* Enable HSE Oscillator and activate PLL with HSE as source */ - #if defined(STM32F4) || defined(STM32F7) || defined(STM32H7) + #if defined(STM32F4) || defined(STM32F7) || defined(STM32G4) || defined(STM32H7) RCC_OscInitStruct.OscillatorType = MICROPY_HW_RCC_OSCILLATOR_TYPE; RCC_OscInitStruct.HSEState = MICROPY_HW_RCC_HSE_STATE; RCC_OscInitStruct.HSIState = MICROPY_HW_RCC_HSI_STATE; @@ -240,6 +244,27 @@ void SystemClock_Config(void) { #endif #endif + + #if defined(STM32G4) + RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; + RCC_OscInitStruct.HSI48State = RCC_HSI48_ON; + #if MICROPY_HW_CLK_USE_HSI && MICROPY_HW_CLK_USE_HSI48 + RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI | RCC_OSCILLATORTYPE_HSI48; + RCC_OscInitStruct.HSIState = RCC_HSI_ON; + RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI; + #else + RCC_OscInitStruct.OscillatorType = MICROPY_HW_RCC_OSCILLATOR_TYPE; + RCC_OscInitStruct.HSEState = MICROPY_HW_RCC_HSE_STATE; + RCC_OscInitStruct.HSIState = MICROPY_HW_RCC_HSI_STATE; + RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; + #endif + RCC_OscInitStruct.PLL.PLLM = MICROPY_HW_CLK_PLLM; + RCC_OscInitStruct.PLL.PLLN = MICROPY_HW_CLK_PLLN; + RCC_OscInitStruct.PLL.PLLP = MICROPY_HW_CLK_PLLP; + RCC_OscInitStruct.PLL.PLLQ = MICROPY_HW_CLK_PLLQ; + RCC_OscInitStruct.PLL.PLLR = MICROPY_HW_CLK_PLLR; + #endif + RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; /* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2 clocks dividers */ @@ -295,7 +320,7 @@ void SystemClock_Config(void) { RCC_OscInitStruct.PLL.PLLN = MICROPY_HW_CLK_PLLN; RCC_OscInitStruct.PLL.PLLP = MICROPY_HW_CLK_PLLP; RCC_OscInitStruct.PLL.PLLQ = MICROPY_HW_CLK_PLLQ; - #if defined(STM32L4) || defined(STM32H7) + #if defined(STM32G4) || defined(STM32H7) || defined(STM32L4) RCC_OscInitStruct.PLL.PLLR = MICROPY_HW_CLK_PLLR; #endif @@ -309,6 +334,11 @@ void SystemClock_Config(void) { RCC_ClkInitStruct.AHBCLKDivider = MICROPY_HW_CLK_AHB_DIV; RCC_ClkInitStruct.APB1CLKDivider = MICROPY_HW_CLK_APB1_DIV; RCC_ClkInitStruct.APB2CLKDivider = MICROPY_HW_CLK_APB2_DIV; + #elif defined(STM32G4) + RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; + RCC_ClkInitStruct.AHBCLKDivider = MICROPY_HW_CLK_AHB_DIV; + RCC_ClkInitStruct.APB1CLKDivider = MICROPY_HW_CLK_APB1_DIV; + RCC_ClkInitStruct.APB2CLKDivider = MICROPY_HW_CLK_APB2_DIV; #elif defined(STM32L4) RCC_ClkInitStruct.AHBCLKDivider = MICROPY_HW_CLK_AHB_DIV; RCC_ClkInitStruct.APB1CLKDivider = MICROPY_HW_CLK_APB1_DIV; @@ -351,12 +381,30 @@ void SystemClock_Config(void) { } #endif + #if defined(STM32G4) + if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_8) != HAL_OK) { + __fatal_error("HAL_RCC_ClockConfig"); + } + PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC | RCC_PERIPHCLK_LPUART1 + | RCC_PERIPHCLK_RNG | RCC_PERIPHCLK_ADC12 + | RCC_PERIPHCLK_FDCAN | RCC_PERIPHCLK_USB; + PeriphClkInitStruct.UsbClockSelection = RCC_USBCLKSOURCE_HSI48; + PeriphClkInitStruct.Lpuart1ClockSelection = RCC_LPUART1CLKSOURCE_PCLK1; + PeriphClkInitStruct.FdcanClockSelection = RCC_FDCANCLKSOURCE_HSE; + PeriphClkInitStruct.RngClockSelection = RCC_RNGCLKSOURCE_HSI48; + PeriphClkInitStruct.Adc12ClockSelection = RCC_ADC12CLKSOURCE_SYSCLK; + PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE; + if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) { + __fatal_error("HAL_RCCEx_PeriphCLKConfig"); + } + #else uint32_t vco_out = RCC_OscInitStruct.PLL.PLLN * (MICROPY_HW_CLK_VALUE / 1000000) / RCC_OscInitStruct.PLL.PLLM; uint32_t sysclk_mhz = vco_out / RCC_OscInitStruct.PLL.PLLP; bool need_pll48 = vco_out % 48 != 0; if (powerctrl_rcc_clock_config_pll(&RCC_ClkInitStruct, sysclk_mhz, need_pll48) != 0) { __fatal_error("HAL_RCC_ClockConfig"); } + #endif #if defined(STM32H7) /* Activate CSI clock mandatory for I/O Compensation Cell*/ diff --git a/ports/stm32/timer.c b/ports/stm32/timer.c index 5a968ec56d..57c8dc6c7b 100644 --- a/ports/stm32/timer.c +++ b/ports/stm32/timer.c @@ -482,7 +482,7 @@ STATIC void config_deadtime(pyb_timer_obj_t *self, mp_int_t ticks, mp_int_t brk) deadTimeConfig.DeadTime = compute_dtg_from_ticks(ticks); deadTimeConfig.BreakState = brk == BRK_OFF ? TIM_BREAK_DISABLE : TIM_BREAK_ENABLE; deadTimeConfig.BreakPolarity = brk == BRK_LOW ? TIM_BREAKPOLARITY_LOW : TIM_BREAKPOLARITY_HIGH; - #if defined(STM32F7) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB) + #if defined(STM32F7) || defined(STM32G4) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB) deadTimeConfig.BreakFilter = 0; deadTimeConfig.Break2State = TIM_BREAK_DISABLE; deadTimeConfig.Break2Polarity = TIM_BREAKPOLARITY_LOW; @@ -810,7 +810,7 @@ STATIC const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = { TIM_ENTRY(1, TIM1_UP_TIM10_IRQn), #elif defined(STM32H7) TIM_ENTRY(1, TIM1_UP_IRQn), - #elif defined(STM32L4) || defined(STM32WB) + #elif defined(STM32G4) || defined(STM32L4) || defined(STM32WB) TIM_ENTRY(1, TIM1_UP_TIM16_IRQn), #endif #endif @@ -832,12 +832,16 @@ STATIC const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = { #endif #endif #if defined(TIM7) + #if defined(STM32G4) + TIM_ENTRY(7, TIM7_DAC_IRQn), + #else TIM_ENTRY(7, TIM7_IRQn), #endif + #endif #if defined(TIM8) #if defined(STM32F4) || defined(STM32F7) || defined(STM32H7) TIM_ENTRY(8, TIM8_UP_TIM13_IRQn), - #elif defined(STM32L4) + #elif defined(STM32G4) || defined(STM32L4) TIM_ENTRY(8, TIM8_UP_IRQn), #endif #endif @@ -882,6 +886,9 @@ STATIC const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = { TIM_ENTRY(17, TIM1_TRG_COM_TIM17_IRQn), #endif #endif + #if defined(TIM20) + TIM_ENTRY(20, TIM20_UP_IRQn), + #endif }; #undef TIM_ENTRY @@ -1401,6 +1408,7 @@ STATIC mp_obj_t pyb_timer_callback(mp_obj_t self_in, mp_obj_t callback) { // start timer, so that it interrupts on overflow, but clear any // pending interrupts which may have been set by initializing it. __HAL_TIM_CLEAR_FLAG(&self->tim, TIM_IT_UPDATE); + HAL_TIM_Base_Stop(&self->tim); // internal timer state must be released before starting again HAL_TIM_Base_Start_IT(&self->tim); // This will re-enable the IRQ HAL_NVIC_EnableIRQ(self->irqn); } else { diff --git a/ports/stm32/uart.c b/ports/stm32/uart.c index 2c08257f32..61e72999e2 100644 --- a/ports/stm32/uart.c +++ b/ports/stm32/uart.c @@ -73,6 +73,15 @@ #define USART_CR3_IE_ALL (USART_CR3_IE_BASE) #endif +#elif defined(STM32G4) +#define USART_CR1_IE_ALL (USART_CR1_IE_BASE | USART_CR1_EOBIE | USART_CR1_RTOIE | USART_CR1_CMIE) +#define USART_CR2_IE_ALL (USART_CR2_IE_BASE) +#if defined(USART_CR3_TCBGTIE) +#define USART_CR3_IE_ALL (USART_CR3_IE_BASE | USART_CR3_TCBGTIE | USART_CR3_WUFIE) +#else +#define USART_CR3_IE_ALL (USART_CR3_IE_BASE | USART_CR3_WUFIE) +#endif + #elif defined(STM32H7) #define USART_CR1_IE_ALL (USART_CR1_IE_BASE | USART_CR1_RXFFIE | USART_CR1_TXFEIE | USART_CR1_EOBIE | USART_CR1_RTOIE | USART_CR1_CMIE) #define USART_CR2_IE_ALL (USART_CR2_IE_BASE) @@ -91,6 +100,7 @@ #else #define USART_CR3_IE_ALL (USART_CR3_IE_BASE | USART_CR3_WUFIE) #endif + #endif extern void NORETURN __fatal_error(const char *msg); @@ -501,6 +511,11 @@ bool uart_init(pyb_uart_obj_t *uart_obj, huart.Init.Mode = UART_MODE_TX_RX; huart.Init.HwFlowCtl = flow; huart.Init.OverSampling = UART_OVERSAMPLING_16; + + #if defined(STM32G4) // H7 and WB also have fifo.. + huart.FifoMode = UART_FIFOMODE_ENABLE; + #endif + #if !defined(STM32F4) huart.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE; #endif @@ -798,14 +813,14 @@ uint32_t uart_get_baudrate(pyb_uart_obj_t *self) { #if defined(LPUART1) if (self->uart_id == PYB_LPUART_1) { return LL_LPUART_GetBaudRate(self->uartx, uart_get_source_freq(self) - #if defined(STM32H7) || defined(STM32WB) + #if defined(STM32G4) || defined(STM32H7) || defined(STM32WB) , self->uartx->PRESC #endif ); } #endif return LL_USART_GetBaudRate(self->uartx, uart_get_source_freq(self), - #if defined(STM32H7) || defined(STM32WB) + #if defined(STM32G4) || defined(STM32H7) || defined(STM32WB) self->uartx->PRESC, #endif LL_USART_OVERSAMPLING_16); @@ -815,7 +830,7 @@ void uart_set_baudrate(pyb_uart_obj_t *self, uint32_t baudrate) { #if defined(LPUART1) if (self->uart_id == PYB_LPUART_1) { LL_LPUART_SetBaudRate(self->uartx, uart_get_source_freq(self), - #if defined(STM32H7) || defined(STM32WB) + #if defined(STM32G4) || defined(STM32H7) || defined(STM32WB) LL_LPUART_PRESCALER_DIV1, #endif baudrate); @@ -823,7 +838,7 @@ void uart_set_baudrate(pyb_uart_obj_t *self, uint32_t baudrate) { } #endif LL_USART_SetBaudRate(self->uartx, uart_get_source_freq(self), - #if defined(STM32H7) || defined(STM32WB) + #if defined(STM32G4) || defined(STM32H7) || defined(STM32WB) LL_USART_PRESCALER_DIV1, #endif LL_USART_OVERSAMPLING_16, baudrate); @@ -874,7 +889,7 @@ int uart_rx_char(pyb_uart_obj_t *self) { return data; } else { // no buffering - #if defined(STM32F0) || defined(STM32F7) || defined(STM32L0) || defined(STM32L4) || defined(STM32H7) || defined(STM32WB) + #if defined(STM32F0) || defined(STM32F7) || defined(STM32G4) || defined(STM32L0) || defined(STM32L4) || defined(STM32H7) || defined(STM32WB) int data = self->uartx->RDR & self->char_mask; self->uartx->ICR = USART_ICR_ORECF; // clear ORE if it was set return data; @@ -1024,7 +1039,7 @@ void uart_irq_handler(mp_uint_t uart_id) { uint16_t next_head = (self->read_buf_head + 1) % self->read_buf_len; if (next_head != self->read_buf_tail) { // only read data if room in buf - #if defined(STM32F0) || defined(STM32F7) || defined(STM32L0) || defined(STM32L4) || defined(STM32H7) || defined(STM32WB) + #if defined(STM32F0) || defined(STM32F7) || defined(STM32G4) || defined(STM32L0) || defined(STM32L4) || defined(STM32H7) || defined(STM32WB) int data = self->uartx->RDR; // clears UART_FLAG_RXNE #else self->mp_irq_flags = self->uartx->SR; // resample to get any new flags since next read of DR will clear SR diff --git a/ports/stm32/usbdev/core/inc/usbd_def.h b/ports/stm32/usbdev/core/inc/usbd_def.h index e0d1c37625..44c8dd8d93 100644 --- a/ports/stm32/usbdev/core/inc/usbd_def.h +++ b/ports/stm32/usbdev/core/inc/usbd_def.h @@ -267,8 +267,12 @@ typedef struct _USBD_HandleTypeDef with the DMA during the transaction process should be 4-bytes aligned */ #if defined (__GNUC__) /* GNU Compiler */ - #define __ALIGN_END __attribute__ ((aligned (4))) - #define __ALIGN_BEGIN + #ifndef __ALIGN_END + #define __ALIGN_END __attribute__ ((aligned (4))) + #endif /* __ALIGN_END */ + #ifndef __ALIGN_BEGIN + #define __ALIGN_BEGIN + #endif /* __ALIGN_BEGIN */ #else #define __ALIGN_END #if defined (__CC_ARM) /* ARM Compiler */ From a5003ba40719a3da70586bb79856ec635db1076d Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 1 Feb 2022 15:45:54 +1100 Subject: [PATCH 065/619] gitmodules: Update branch for stm32lib submodule. Signed-off-by: Damien George --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index a92757df2c..d0b73e2ecf 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,7 +13,7 @@ [submodule "lib/stm32lib"] path = lib/stm32lib url = https://github.com/micropython/stm32lib - branch = work-F4-1.13.1+F7-1.5.0+L4-1.3.0 + branch = work-F0-1.9.0+F4-1.16.0+F7-1.7.0+G4-1.3.0+H7-1.6.0+L0-1.11.2+L4-1.17.0+WB-1.10.0+WL-1.1.0 [submodule "lib/nrfx"] path = lib/nrfx url = https://github.com/NordicSemiconductor/nrfx.git From a5e64c209f2d76cb0cc903df875d87e8ca009507 Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Tue, 25 Jan 2022 17:07:24 +0200 Subject: [PATCH 066/619] esp32/machine_pwm: Fix PWM not allowing frequencies < 611 Hz. Fixes issue #8189. --- ports/esp32/machine_pwm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) mode change 100644 => 100755 ports/esp32/machine_pwm.c diff --git a/ports/esp32/machine_pwm.c b/ports/esp32/machine_pwm.c old mode 100644 new mode 100755 index 43d44249dc..9d5d9595e1 --- a/ports/esp32/machine_pwm.c +++ b/ports/esp32/machine_pwm.c @@ -85,10 +85,10 @@ STATIC ledc_timer_config_t timers[PWM_TIMER_MAX]; // duty_u16() and duty_ns() use 16-bit resolution or less // Possible highest resolution in device -#if (LEDC_TIMER_BIT_MAX - 1) < LEDC_TIMER_16_BIT -#define HIGHEST_PWM_RES (LEDC_TIMER_BIT_MAX - 1) -#else +#if CONFIG_IDF_TARGET_ESP32 #define HIGHEST_PWM_RES (LEDC_TIMER_16_BIT) // 20 bit for ESP32, but 16 bit is used +#else +#define HIGHEST_PWM_RES (LEDC_TIMER_BIT_MAX - 1) // 14 bit is used #endif // Duty resolution of user interface in `duty_u16()` and `duty_u16` parameter in constructor/initializer #define UI_RES_16_BIT (16) @@ -300,7 +300,7 @@ STATIC uint32_t get_duty_u16(machine_pwm_obj_t *self) { } STATIC uint32_t get_duty_u10(machine_pwm_obj_t *self) { - return get_duty_u16(self) >> (HIGHEST_PWM_RES - PWRES); + return get_duty_u16(self) >> (UI_RES_16_BIT - LEDC_TIMER_10_BIT); } STATIC uint32_t get_duty_ns(machine_pwm_obj_t *self) { From 15e65b77ebfaeddc9a678d94cf1a2ced625a6d45 Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Tue, 25 Jan 2022 22:57:22 +0200 Subject: [PATCH 067/619] esp32/machine_pwm: Clean up macro names and their use. - Remove UI_RES_SHIFT macro. - Rename PWFREQ to PWM_FREQ. - Rename PWRES to PWM_RES_10_BIT. - Use UI_RES_16_BIT flag instead of HIGHEST_PWM_RES. --- ports/esp32/machine_pwm.c | 41 ++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/ports/esp32/machine_pwm.c b/ports/esp32/machine_pwm.c index 9d5d9595e1..9308fcd3cb 100755 --- a/ports/esp32/machine_pwm.c +++ b/ports/esp32/machine_pwm.c @@ -71,15 +71,14 @@ STATIC ledc_timer_config_t timers[PWM_TIMER_MAX]; #define TIMER_IDX_TO_MODE(timer_idx) (timer_idx / LEDC_TIMER_MAX) #define TIMER_IDX_TO_TIMER(timer_idx) (timer_idx % LEDC_TIMER_MAX) -// Params for PW operation +// Params for PWM operation // 5khz is default frequency -#define PWFREQ (5000) - -// 10-bit resolution (compatible with esp8266 PWM) -#define PWRES (LEDC_TIMER_10_BIT) +#define PWM_FREQ (5000) +// default 10-bit resolution (compatible with esp8266 PWM) +#define PWM_RES_10_BIT (LEDC_TIMER_10_BIT) // Maximum duty value on 10-bit resolution -#define MAX_DUTY_U10 ((1 << PWRES) - 1) +#define MAX_DUTY_U10 ((1 << PWM_RES_10_BIT) - 1) // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/ledc.html#supported-range-of-frequency-and-duty-resolutions // duty() uses 10-bit resolution or less // duty_u16() and duty_ns() use 16-bit resolution or less @@ -94,8 +93,6 @@ STATIC ledc_timer_config_t timers[PWM_TIMER_MAX]; #define UI_RES_16_BIT (16) // Maximum duty value on highest user interface resolution #define UI_MAX_DUTY ((1 << UI_RES_16_BIT) - 1) -// How much to shift from the HIGHEST_PWM_RES duty resolution to the user interface duty resolution UI_RES_16_BIT -#define UI_RES_SHIFT (UI_RES_16_BIT - HIGHEST_PWM_RES) // 0 for ESP32, 2 for S2, S3, C3 // If the PWM frequency is less than EMPIRIC_FREQ, then LEDC_REF_CLK_HZ(1 MHz) source is used, else LEDC_APB_CLK_HZ(80 MHz) source is used #define EMPIRIC_FREQ (10) // Hz @@ -111,7 +108,7 @@ typedef struct _machine_pwm_obj_t { int mode; int channel; int timer; - int duty_x; // PWRES if duty(), HIGHEST_PWM_RES if duty_u16(), -HIGHEST_PWM_RES if duty_ns() + int duty_x; // PWM_RES_10_BIT if duty(), UI_RES_16_BIT if duty_u16(), -UI_RES_16_BIT if duty_ns() int duty_u10; // stored values from previous duty setters int duty_u16; // - / - int duty_ns; // - / - @@ -264,11 +261,11 @@ STATIC void set_freq(machine_pwm_obj_t *self, unsigned int freq, ledc_timer_conf // Save the same duty cycle when frequency is changed if (save_duty_resolution != timer->duty_resolution) { - if (self->duty_x == HIGHEST_PWM_RES) { + if (self->duty_x == UI_RES_16_BIT) { set_duty_u16(self, self->duty_u16); - } else if (self->duty_x == PWRES) { + } else if (self->duty_x == PWM_RES_10_BIT) { set_duty_u10(self, self->duty_u10); - } else if (self->duty_x == -HIGHEST_PWM_RES) { + } else if (self->duty_x == -UI_RES_16_BIT) { set_duty_ns(self, self->duty_ns); } } @@ -296,7 +293,7 @@ STATIC int duty_to_ns(machine_pwm_obj_t *self, int duty) { #define get_duty_raw(self) ledc_get_duty(self->mode, self->channel) STATIC uint32_t get_duty_u16(machine_pwm_obj_t *self) { - return ledc_get_duty(self->mode, self->channel) << (HIGHEST_PWM_RES + UI_RES_SHIFT - timers[TIMER_IDX(self->mode, self->timer)].duty_resolution); + return ledc_get_duty(self->mode, self->channel) << (UI_RES_16_BIT - timers[TIMER_IDX(self->mode, self->timer)].duty_resolution); } STATIC uint32_t get_duty_u10(machine_pwm_obj_t *self) { @@ -312,7 +309,7 @@ STATIC void set_duty_u16(machine_pwm_obj_t *self, int duty) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty_u16 must be from 0 to %d"), UI_MAX_DUTY); } ledc_timer_config_t timer = timers[TIMER_IDX(self->mode, self->timer)]; - int channel_duty = duty >> (HIGHEST_PWM_RES + UI_RES_SHIFT - timer.duty_resolution); + int channel_duty = duty >> (UI_RES_16_BIT - timer.duty_resolution); int max_duty = (1 << timer.duty_resolution) - 1; if (channel_duty < 0) { channel_duty = 0; @@ -336,7 +333,7 @@ STATIC void set_duty_u16(machine_pwm_obj_t *self, int duty) { } */ - self->duty_x = HIGHEST_PWM_RES; + self->duty_x = UI_RES_16_BIT; self->duty_u16 = duty; } @@ -344,8 +341,8 @@ STATIC void set_duty_u10(machine_pwm_obj_t *self, int duty) { if ((duty < 0) || (duty > MAX_DUTY_U10)) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty must be from 0 to %u"), MAX_DUTY_U10); } - set_duty_u16(self, duty << (HIGHEST_PWM_RES + UI_RES_SHIFT - PWRES)); - self->duty_x = PWRES; + set_duty_u16(self, duty << (UI_RES_16_BIT - LEDC_TIMER_10_BIT)); + self->duty_x = PWM_RES_10_BIT; self->duty_u10 = duty; } @@ -354,7 +351,7 @@ STATIC void set_duty_ns(machine_pwm_obj_t *self, int ns) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty_ns must be from 0 to %d ns"), duty_to_ns(self, UI_MAX_DUTY)); } set_duty_u16(self, ns_to_duty(self, ns)); - self->duty_x = -HIGHEST_PWM_RES; + self->duty_x = -UI_RES_16_BIT; self->duty_ns = ns; } @@ -425,9 +422,9 @@ STATIC void mp_machine_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_p if (self->active) { mp_printf(print, ", freq=%u", ledc_get_freq(self->mode, self->timer)); - if (self->duty_x == PWRES) { + if (self->duty_x == PWM_RES_10_BIT) { mp_printf(print, ", duty=%d", get_duty_u10(self)); - } else if (self->duty_x == -HIGHEST_PWM_RES) { + } else if (self->duty_x == -UI_RES_16_BIT) { mp_printf(print, ", duty_ns=%d", get_duty_ns(self)); } else { mp_printf(print, ", duty_u16=%d", get_duty_u16(self)); @@ -479,7 +476,7 @@ STATIC void mp_machine_pwm_init_helper(machine_pwm_obj_t *self, freq = timers[chans[channel_idx].timer_idx].freq_hz; } if (freq <= 0) { - freq = PWFREQ; + freq = PWM_FREQ; } } if ((freq <= 0) || (freq > 40000000)) { @@ -536,7 +533,7 @@ STATIC void mp_machine_pwm_init_helper(machine_pwm_obj_t *self, } else if (duty != -1) { set_duty_u10(self, duty); } else if (self->duty_x == 0) { - set_duty_u10(self, (1 << PWRES) / 2); // 50% + set_duty_u10(self, (1 << PWM_RES_10_BIT) / 2); // 50% } } From 1f04a9a1fcd4172f7c721e05a4515c9c8bb0e8c9 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 1 Feb 2022 16:58:56 +1100 Subject: [PATCH 068/619] esp32/esp32_rmt: Select correct last RMT channel on S2, S3, C3 variants. For example the ESP32-C3 has 2 TX channels and 2 RX channels in total, and in this case channel 1 must be the default for bitstream. Signed-off-by: Damien George --- ports/esp32/esp32_rmt.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/ports/esp32/esp32_rmt.c b/ports/esp32/esp32_rmt.c index ca751d42a8..ac897e3336 100644 --- a/ports/esp32/esp32_rmt.c +++ b/ports/esp32/esp32_rmt.c @@ -47,6 +47,13 @@ // This current MicroPython implementation lacks some major features, notably receive pulses // and carrier output. +// Last available RMT channel that can transmit. +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 4, 0) +#define RMT_LAST_TX_CHANNEL (RMT_CHANNEL_MAX - 1) +#else +#define RMT_LAST_TX_CHANNEL (SOC_RMT_TX_CANDIDATES_PER_GROUP - 1) +#endif + // Forward declaration extern const mp_obj_type_t esp32_rmt_type; @@ -62,7 +69,7 @@ typedef struct _esp32_rmt_obj_t { // Current channel used for machine.bitstream, in the machine_bitstream_high_low_rmt // implementation. A value of -1 means do not use RMT. -int8_t esp32_rmt_bitstream_channel_id = RMT_CHANNEL_MAX - 1; +int8_t esp32_rmt_bitstream_channel_id = RMT_LAST_TX_CHANNEL; #if MP_TASK_COREID == 0 @@ -336,7 +343,7 @@ STATIC mp_obj_t esp32_rmt_bitstream_channel(size_t n_args, const mp_obj_t *args) esp32_rmt_bitstream_channel_id = -1; } else { mp_int_t channel_id = mp_obj_get_int(args[0]); - if (channel_id < 0 || channel_id >= RMT_CHANNEL_MAX) { + if (channel_id < 0 || channel_id > RMT_LAST_TX_CHANNEL) { mp_raise_ValueError(MP_ERROR_TEXT("invalid channel")); } esp32_rmt_bitstream_channel_id = channel_id; From eae2e3516ca4067ca8cfeb51ac8a6505cb6324b7 Mon Sep 17 00:00:00 2001 From: Michael O'Cleirigh Date: Fri, 28 Jan 2022 00:35:53 -0500 Subject: [PATCH 069/619] esp32/main: Automatically size SPIRAM heap when allocated using malloc. This change allows the same heap allocation rules to be used when using malloc regardless if the board has SPRAM or normal RAM. Integrating with the esp32-camera for example requires that ESP32 SPRAM be allocatable using the esp-idf capabilities aware allocation functions. In the case of esp32-camera it's for the framebuffer. Detect when CONFIG_SPIRAM_USE_MALLOC is in use and use the standard automatic configuration of leaving 1/2 of the SPRAM available to other FreeRTOS tasks. --- ports/esp32/main.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ports/esp32/main.c b/ports/esp32/main.c index e25e6fdd1c..6c5edbb356 100644 --- a/ports/esp32/main.c +++ b/ports/esp32/main.c @@ -100,7 +100,10 @@ void mp_task(void *pvParameter) { size_t mp_task_heap_size; void *mp_task_heap = NULL; - #if CONFIG_ESP32_SPIRAM_SUPPORT + #if CONFIG_SPIRAM_USE_MALLOC + // SPIRAM is issued using MALLOC, fallback to normal allocation rules + mp_task_heap = NULL; + #elif CONFIG_ESP32_SPIRAM_SUPPORT // Try to use the entire external SPIRAM directly for the heap mp_task_heap = (void *)SOC_EXTRAM_DATA_LOW; switch (esp_spiram_get_chip_size()) { From b18d4392b4a4c5e6ed50dbcdb4a88828703289cb Mon Sep 17 00:00:00 2001 From: Emil Kondayan Date: Thu, 27 Jan 2022 16:05:59 +0200 Subject: [PATCH 070/619] esp32/adc: Fix wrong mapping between ADC2 channel and GPIO number. According to the IO_MUX table in the ESP32 datasheet, the ADC2 channels are mapped to different GPIO numbers. --- ports/esp32/machine_adc.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ports/esp32/machine_adc.c b/ports/esp32/machine_adc.c index f6bc7b9639..515d629a7e 100644 --- a/ports/esp32/machine_adc.c +++ b/ports/esp32/machine_adc.c @@ -50,16 +50,16 @@ STATIC const madc_obj_t madc_obj[] = { {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_5, GPIO_NUM_33}, {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_6, GPIO_NUM_34}, {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_7, GPIO_NUM_35}, - {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_0, GPIO_NUM_0}, - {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_1, GPIO_NUM_2}, - {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_2, GPIO_NUM_4}, - {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_3, GPIO_NUM_12}, + {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_0, GPIO_NUM_4}, + {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_1, GPIO_NUM_0}, + {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_2, GPIO_NUM_2}, + {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_3, GPIO_NUM_15}, {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_4, GPIO_NUM_13}, - {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_5, GPIO_NUM_14}, - {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_6, GPIO_NUM_15}, - {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_7, GPIO_NUM_25}, - {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_8, GPIO_NUM_26}, - {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_9, GPIO_NUM_27}, + {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_5, GPIO_NUM_12}, + {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_6, GPIO_NUM_14}, + {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_7, GPIO_NUM_27}, + {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_8, GPIO_NUM_25}, + {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_9, GPIO_NUM_26}, #elif CONFIG_IDF_TARGET_ESP32C3 {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_0, GPIO_NUM_0}, {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_1, GPIO_NUM_1}, From 872bab6b3c967deaeff5f0f3d0d066c3fde03802 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 2 Feb 2022 16:20:41 +1100 Subject: [PATCH 071/619] esp32: Remove unneeded modesp.h. Made redundant by 71f4faac2732d932dcc03bdbc9f80434e5757edb Signed-off-by: Damien George --- ports/esp32/modesp.c | 1 - ports/esp32/modesp.h | 1 - 2 files changed, 2 deletions(-) delete mode 100644 ports/esp32/modesp.h diff --git a/ports/esp32/modesp.c b/ports/esp32/modesp.c index 8c94e0cf41..b5beaf5053 100644 --- a/ports/esp32/modesp.c +++ b/ports/esp32/modesp.c @@ -36,7 +36,6 @@ #include "py/mperrno.h" #include "py/mphal.h" #include "drivers/dht/dht.h" -#include "modesp.h" STATIC mp_obj_t esp_osdebug(size_t n_args, const mp_obj_t *args) { esp_log_level_t level = LOG_LOCAL_LEVEL; diff --git a/ports/esp32/modesp.h b/ports/esp32/modesp.h deleted file mode 100644 index a822c02ecc..0000000000 --- a/ports/esp32/modesp.h +++ /dev/null @@ -1 +0,0 @@ -void esp_neopixel_write(uint8_t pin, uint8_t *pixels, uint32_t numBytes, uint8_t timing); From 326b2c79dfaf472d67e5e3fee5868e78ccc6be73 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 2 Feb 2022 16:27:10 +1100 Subject: [PATCH 072/619] docs: Remove reference to obsolete neopixel_write function. It has been replaced by machine.bitstream. Signed-off-by: Damien George --- docs/esp32/quickref.rst | 8 ++------ docs/esp8266/quickref.rst | 6 +----- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/docs/esp32/quickref.rst b/docs/esp32/quickref.rst index 94b5d966ed..de107ee25c 100644 --- a/docs/esp32/quickref.rst +++ b/docs/esp32/quickref.rst @@ -599,18 +599,14 @@ The APA106 driver extends NeoPixel, but internally uses a different colour order ap = APA106(pin, 8) r, g, b = ap[0] -For low-level driving of a NeoPixel:: - - import esp - esp.neopixel_write(pin, grb_buf, is800khz) - .. Warning:: By default ``NeoPixel`` is configured to control the more popular *800kHz* units. It is possible to use alternative timing to control other (typically 400kHz) devices by passing ``timing=0`` when constructing the ``NeoPixel`` object. -The low-level driver uses an RMT channel by default. To configure this see +For low-level driving of a NeoPixel see `machine.bitstream`. +This low-level driver uses an RMT channel by default. To configure this see `RMT.bitstream_channel`. APA102 (DotStar) uses a different driver as it has an additional clock pin. diff --git a/docs/esp8266/quickref.rst b/docs/esp8266/quickref.rst index 9e64723b27..4e00a92260 100644 --- a/docs/esp8266/quickref.rst +++ b/docs/esp8266/quickref.rst @@ -374,17 +374,13 @@ Use the ``neopixel`` module:: np.write() # write data to all pixels r, g, b = np[0] # get first pixel colour -For low-level driving of a NeoPixel:: - - import esp - esp.neopixel_write(pin, grb_buf, is800khz) - .. Warning:: By default ``NeoPixel`` is configured to control the more popular *800kHz* units. It is possible to use alternative timing to control other (typically 400kHz) devices by passing ``timing=0`` when constructing the ``NeoPixel`` object. +For low-level driving of a NeoPixel see `machine.bitstream`. APA102 driver ------------- From ab2923dfa1174dc177f0a90cb00a7e4ff87958d2 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 2 Feb 2022 16:11:25 +1100 Subject: [PATCH 073/619] all: Update Python formatting to latest Black version 22.1.0. Signed-off-by: Damien George --- examples/rp2/pio_pwm.py | 2 +- tests/cpydiff/modules_random_randint.py | 2 +- tests/float/complex1.py | 14 ++++----- tests/float/float1.py | 2 +- tests/float/float2int_doubleprec_intbig.py | 36 +++++++++++----------- tests/float/float2int_fp30_intbig.py | 36 +++++++++++----------- tests/float/float2int_intbig.py | 36 +++++++++++----------- tests/float/inf_nan_arith.py | 2 +- tests/float/int_big_float.py | 2 +- tests/float/int_divzero.py | 2 +- tests/float/int_power.py | 2 +- tests/micropython/import_mpy_native_x64.py | 2 +- tests/misc/rge_sm.py | 12 ++++---- tests/perf_bench/bm_chaos.py | 2 +- tests/perf_bench/misc_raytrace.py | 8 ++--- tests/run-perfbench.py | 6 ++-- 16 files changed, 83 insertions(+), 83 deletions(-) diff --git a/examples/rp2/pio_pwm.py b/examples/rp2/pio_pwm.py index 8e87448ca8..176a238091 100644 --- a/examples/rp2/pio_pwm.py +++ b/examples/rp2/pio_pwm.py @@ -41,5 +41,5 @@ pwm = PIOPWM(0, 25, max_count=(1 << 16) - 1, count_freq=10_000_000) while True: for i in range(256): - pwm.set(i ** 2) + pwm.set(i**2) sleep(0.01) diff --git a/tests/cpydiff/modules_random_randint.py b/tests/cpydiff/modules_random_randint.py index b05908a157..90607400cd 100644 --- a/tests/cpydiff/modules_random_randint.py +++ b/tests/cpydiff/modules_random_randint.py @@ -8,5 +8,5 @@ workaround: If you need integers larger than native wordsize use the random modu import random -x = random.randint(2 ** 128 - 1, 2 ** 128) +x = random.randint(2**128 - 1, 2**128) print("x={}".format(x)) diff --git a/tests/float/complex1.py b/tests/float/complex1.py index a510ffc830..139bb0c509 100644 --- a/tests/float/complex1.py +++ b/tests/float/complex1.py @@ -27,15 +27,15 @@ print(1j * 2j) print(1j / 2) print((1j / 2j).real) print(1j / (1 + 2j)) -ans = 0j ** 0 +ans = 0j**0 print("%.5g %.5g" % (ans.real, ans.imag)) -ans = 0j ** 1 +ans = 0j**1 print("%.5g %.5g" % (ans.real, ans.imag)) -ans = 0j ** 0j +ans = 0j**0j print("%.5g %.5g" % (ans.real, ans.imag)) -ans = 1j ** 2.5 +ans = 1j**2.5 print("%.5g %.5g" % (ans.real, ans.imag)) -ans = 1j ** 2.5j +ans = 1j**2.5j print("%.5g %.5g" % (ans.real, ans.imag)) # comparison @@ -116,10 +116,10 @@ except ZeroDivisionError: # zero division via power try: - 0j ** -1 + 0j**-1 except ZeroDivisionError: print("ZeroDivisionError") try: - 0j ** 1j + 0j**1j except ZeroDivisionError: print("ZeroDivisionError") diff --git a/tests/float/float1.py b/tests/float/float1.py index efde5146be..37f8b3face 100644 --- a/tests/float/float1.py +++ b/tests/float/float1.py @@ -88,7 +88,7 @@ except ZeroDivisionError: print("ZeroDivisionError") try: - 0.0 ** -1 + 0.0**-1 except ZeroDivisionError: print("ZeroDivisionError") diff --git a/tests/float/float2int_doubleprec_intbig.py b/tests/float/float2int_doubleprec_intbig.py index 418eddb607..adc76d4aeb 100644 --- a/tests/float/float2int_doubleprec_intbig.py +++ b/tests/float/float2int_doubleprec_intbig.py @@ -35,8 +35,8 @@ if ll_type != 0: print(int(1418774543.0)) print("%d" % 1418774543.0) if ll_type == 3: - print(int(2.0 ** 100)) - print("%d" % 2.0 ** 100) + print(int(2.0**100)) + print("%d" % 2.0**100) else: print(int(1073741823.0)) print("%d" % 1073741823.0) @@ -44,7 +44,7 @@ else: testpass = True p2_rng = ((30, 63, 1024), (62, 63, 1024))[is_64bit][ll_type] for i in range(0, p2_rng): - bitcnt = len(bin(int(2.0 ** i))) - 3 + bitcnt = len(bin(int(2.0**i))) - 3 if i != bitcnt: print("fail: 2**%u was %u bits long" % (i, bitcnt)) testpass = False @@ -53,7 +53,7 @@ print("power of 2 test: %s" % (testpass and "passed" or "failed")) testpass = True p10_rng = ((9, 18, 23), (18, 18, 23))[is_64bit][ll_type] for i in range(0, p10_rng): - digcnt = len(str(int(10.0 ** i))) - 1 + digcnt = len(str(int(10.0**i))) - 1 if i != digcnt: print("fail: 10**%u was %u digits long" % (i, digcnt)) testpass = False @@ -72,28 +72,28 @@ def fp2int_test(num, name, should_fail): if ll_type != 2: if ll_type == 0: if is_64bit: - neg_bad_fp = -1.00000005 * 2.0 ** 62.0 - pos_bad_fp = 2.0 ** 62.0 - neg_good_fp = -(2.0 ** 62.0) - pos_good_fp = 0.99999993 * 2.0 ** 62.0 + neg_bad_fp = -1.00000005 * 2.0**62.0 + pos_bad_fp = 2.0**62.0 + neg_good_fp = -(2.0**62.0) + pos_good_fp = 0.99999993 * 2.0**62.0 else: - neg_bad_fp = -1.00000005 * 2.0 ** 30.0 - pos_bad_fp = 2.0 ** 30.0 - neg_good_fp = -(2.0 ** 30.0) - pos_good_fp = 0.9999999499 * 2.0 ** 30.0 + neg_bad_fp = -1.00000005 * 2.0**30.0 + pos_bad_fp = 2.0**30.0 + neg_good_fp = -(2.0**30.0) + pos_good_fp = 0.9999999499 * 2.0**30.0 else: - neg_bad_fp = -0.51 * 2.0 ** 64.0 - pos_bad_fp = 2.0 ** 63.0 - neg_good_fp = -(2.0 ** 63.0) - pos_good_fp = 1.9999998 * 2.0 ** 62.0 + neg_bad_fp = -0.51 * 2.0**64.0 + pos_bad_fp = 2.0**63.0 + neg_good_fp = -(2.0**63.0) + pos_good_fp = 1.9999998 * 2.0**62.0 fp2int_test(neg_bad_fp, "neg bad", True) fp2int_test(pos_bad_fp, "pos bad", True) fp2int_test(neg_good_fp, "neg good", False) fp2int_test(pos_good_fp, "pos good", False) else: - fp2int_test(-1.9999999999999981 * 2.0 ** 1023.0, "large neg", False) - fp2int_test(1.9999999999999981 * 2.0 ** 1023.0, "large pos", False) + fp2int_test(-1.9999999999999981 * 2.0**1023.0, "large neg", False) + fp2int_test(1.9999999999999981 * 2.0**1023.0, "large pos", False) fp2int_test(float("inf"), "inf test", True) fp2int_test(float("-inf"), "inf test", True) diff --git a/tests/float/float2int_fp30_intbig.py b/tests/float/float2int_fp30_intbig.py index fcbe2e309f..4e3c1fa217 100644 --- a/tests/float/float2int_fp30_intbig.py +++ b/tests/float/float2int_fp30_intbig.py @@ -34,13 +34,13 @@ if ll_type is None: print(int(14187744.0)) print("%d" % 14187744.0) if ll_type == 2: - print(int(2.0 ** 100)) - print("%d" % 2.0 ** 100) + print(int(2.0**100)) + print("%d" % 2.0**100) testpass = True p2_rng = ((30, 63, 127), (62, 63, 127))[is_64bit][ll_type] for i in range(0, p2_rng): - bitcnt = len(bin(int(2.0 ** i))) - 3 + bitcnt = len(bin(int(2.0**i))) - 3 if i != bitcnt: print("fail: 2.**%u was %u bits long" % (i, bitcnt)) testpass = False @@ -50,7 +50,7 @@ print("power of 2 test: %s" % (testpass and "passed" or "failed")) testpass = True p10_rng = 9 for i in range(0, p10_rng): - digcnt = len(str(int(10.0 ** i))) - 1 + digcnt = len(str(int(10.0**i))) - 1 if i != digcnt: print("fail: 10.**%u was %u digits long" % (i, digcnt)) testpass = False @@ -69,28 +69,28 @@ def fp2int_test(num, name, should_fail): if ll_type != 2: if ll_type == 0: if is_64bit: - neg_bad_fp = -1.00000005 * 2.0 ** 62.0 - pos_bad_fp = 2.0 ** 62.0 - neg_good_fp = -(2.0 ** 62.0) - pos_good_fp = 0.99999993 * 2.0 ** 62.0 + neg_bad_fp = -1.00000005 * 2.0**62.0 + pos_bad_fp = 2.0**62.0 + neg_good_fp = -(2.0**62.0) + pos_good_fp = 0.99999993 * 2.0**62.0 else: - neg_bad_fp = -1.00000005 * 2.0 ** 30.0 - pos_bad_fp = 2.0 ** 30.0 - neg_good_fp = -(2.0 ** 30.0) - pos_good_fp = 0.9999999499 * 2.0 ** 30.0 + neg_bad_fp = -1.00000005 * 2.0**30.0 + pos_bad_fp = 2.0**30.0 + neg_good_fp = -(2.0**30.0) + pos_good_fp = 0.9999999499 * 2.0**30.0 else: - neg_bad_fp = -0.51 * 2.0 ** 64.0 - pos_bad_fp = 2.0 ** 63.0 - neg_good_fp = -(2.0 ** 63.0) - pos_good_fp = 1.9999998 * 2.0 ** 62.0 + neg_bad_fp = -0.51 * 2.0**64.0 + pos_bad_fp = 2.0**63.0 + neg_good_fp = -(2.0**63.0) + pos_good_fp = 1.9999998 * 2.0**62.0 fp2int_test(neg_bad_fp, "neg bad", True) fp2int_test(pos_bad_fp, "pos bad", True) fp2int_test(neg_good_fp, "neg good", False) fp2int_test(pos_good_fp, "pos good", False) else: - fp2int_test(-1.999999879 * 2.0 ** 126.0, "large neg", False) - fp2int_test(1.999999879 * 2.0 ** 126.0, "large pos", False) + fp2int_test(-1.999999879 * 2.0**126.0, "large neg", False) + fp2int_test(1.999999879 * 2.0**126.0, "large pos", False) fp2int_test(float("inf"), "inf test", True) fp2int_test(float("-inf"), "inf test", True) diff --git a/tests/float/float2int_intbig.py b/tests/float/float2int_intbig.py index 865aeea7b9..739f98f804 100644 --- a/tests/float/float2int_intbig.py +++ b/tests/float/float2int_intbig.py @@ -37,13 +37,13 @@ print(int(14187745.)) print("%d" % 14187745.) # fmt: on if ll_type == 2: - print(int(2.0 ** 100)) - print("%d" % 2.0 ** 100) + print(int(2.0**100)) + print("%d" % 2.0**100) testpass = True p2_rng = ((30, 63, 127), (62, 63, 127))[is_64bit][ll_type] for i in range(0, p2_rng): - bitcnt = len(bin(int(2.0 ** i))) - 3 + bitcnt = len(bin(int(2.0**i))) - 3 if i != bitcnt: print("fail: 2.**%u was %u bits long" % (i, bitcnt)) testpass = False @@ -53,7 +53,7 @@ print("power of 2 test: %s" % (testpass and "passed" or "failed")) testpass = True p10_rng = 9 if (ll_type == 0 and ~is_64bit) else 11 for i in range(0, p10_rng): - digcnt = len(str(int(10.0 ** i))) - 1 + digcnt = len(str(int(10.0**i))) - 1 if i != digcnt: print("fail: 10.**%u was %u digits long" % (i, digcnt)) testpass = False @@ -72,28 +72,28 @@ def fp2int_test(num, name, should_fail): if ll_type != 2: if ll_type == 0: if is_64bit: - neg_bad_fp = -1.00000005 * 2.0 ** 62.0 - pos_bad_fp = 2.0 ** 62.0 - neg_good_fp = -(2.0 ** 62.0) - pos_good_fp = 0.99999993 * 2.0 ** 62.0 + neg_bad_fp = -1.00000005 * 2.0**62.0 + pos_bad_fp = 2.0**62.0 + neg_good_fp = -(2.0**62.0) + pos_good_fp = 0.99999993 * 2.0**62.0 else: - neg_bad_fp = -1.00000005 * 2.0 ** 30.0 - pos_bad_fp = 2.0 ** 30.0 - neg_good_fp = -(2.0 ** 30.0) - pos_good_fp = 0.9999999499 * 2.0 ** 30.0 + neg_bad_fp = -1.00000005 * 2.0**30.0 + pos_bad_fp = 2.0**30.0 + neg_good_fp = -(2.0**30.0) + pos_good_fp = 0.9999999499 * 2.0**30.0 else: - neg_bad_fp = -0.51 * 2.0 ** 64.0 - pos_bad_fp = 2.0 ** 63.0 - neg_good_fp = -(2.0 ** 63.0) - pos_good_fp = 1.9999998 * 2.0 ** 62.0 + neg_bad_fp = -0.51 * 2.0**64.0 + pos_bad_fp = 2.0**63.0 + neg_good_fp = -(2.0**63.0) + pos_good_fp = 1.9999998 * 2.0**62.0 fp2int_test(neg_bad_fp, "neg bad", True) fp2int_test(pos_bad_fp, "pos bad", True) fp2int_test(neg_good_fp, "neg good", False) fp2int_test(pos_good_fp, "pos good", False) else: - fp2int_test(-1.999999879 * 2.0 ** 127.0, "large neg", False) - fp2int_test(1.999999879 * 2.0 ** 127.0, "large pos", False) + fp2int_test(-1.999999879 * 2.0**127.0, "large neg", False) + fp2int_test(1.999999879 * 2.0**127.0, "large pos", False) fp2int_test(float("inf"), "inf test", True) fp2int_test(float("nan"), "NaN test", True) diff --git a/tests/float/inf_nan_arith.py b/tests/float/inf_nan_arith.py index c27e38bc52..d1a6b18872 100644 --- a/tests/float/inf_nan_arith.py +++ b/tests/float/inf_nan_arith.py @@ -14,7 +14,7 @@ for x in values: except ZeroDivisionError: print(" / ZeroDivisionError") try: - print(" ** pow", x ** y, pow(x, y)) + print(" ** pow", x**y, pow(x, y)) except ZeroDivisionError: print(" ** pow ZeroDivisionError") print(" == != < <= > >=", x == y, x != y, x < y, x <= y, x > y, x >= y) diff --git a/tests/float/int_big_float.py b/tests/float/int_big_float.py index 0bd1662186..dc13e8e0dd 100644 --- a/tests/float/int_big_float.py +++ b/tests/float/int_big_float.py @@ -19,7 +19,7 @@ print("%.5g" % (i / 1.2)) print("%.5g" % (i * 1.2j).imag) # negative power should produce float -print("%.5g" % (i ** -1)) +print("%.5g" % (i**-1)) print("%.5g" % ((2 + i - i) ** -3)) try: diff --git a/tests/float/int_divzero.py b/tests/float/int_divzero.py index b311a1dbcf..ef3531bee8 100644 --- a/tests/float/int_divzero.py +++ b/tests/float/int_divzero.py @@ -4,6 +4,6 @@ except ZeroDivisionError: print("ZeroDivisionError") try: - 0 ** -1 + 0**-1 except ZeroDivisionError: print("ZeroDivisionError") diff --git a/tests/float/int_power.py b/tests/float/int_power.py index ba79247a56..bcda0f98ed 100644 --- a/tests/float/int_power.py +++ b/tests/float/int_power.py @@ -1,7 +1,7 @@ # negative power should produce float x = 2 -print(x ** -2) +print(x**-2) x = 3 x **= -2 diff --git a/tests/micropython/import_mpy_native_x64.py b/tests/micropython/import_mpy_native_x64.py index cad59e9916..ff0142a9cf 100644 --- a/tests/micropython/import_mpy_native_x64.py +++ b/tests/micropython/import_mpy_native_x64.py @@ -9,7 +9,7 @@ except (ImportError, AttributeError): print("SKIP") raise SystemExit -if not (usys.platform == "linux" and usys.maxsize > 2 ** 32): +if not (usys.platform == "linux" and usys.maxsize > 2**32): print("SKIP") raise SystemExit diff --git a/tests/misc/rge_sm.py b/tests/misc/rge_sm.py index f3bb4189f7..00b0a7a021 100644 --- a/tests/misc/rge_sm.py +++ b/tests/misc/rge_sm.py @@ -52,12 +52,12 @@ class RungeKutta(object): # couplings are: g1, g2, g3 of U(1), SU(2), SU(3); yt (top Yukawa), lambda (Higgs quartic) # see arxiv.org/abs/0812.4950, eqs 10-15 sysSM = ( - lambda *a: 41.0 / 96.0 / math.pi ** 2 * a[1] ** 3, # g1 - lambda *a: -19.0 / 96.0 / math.pi ** 2 * a[2] ** 3, # g2 - lambda *a: -42.0 / 96.0 / math.pi ** 2 * a[3] ** 3, # g3 + lambda *a: 41.0 / 96.0 / math.pi**2 * a[1] ** 3, # g1 + lambda *a: -19.0 / 96.0 / math.pi**2 * a[2] ** 3, # g2 + lambda *a: -42.0 / 96.0 / math.pi**2 * a[3] ** 3, # g3 lambda *a: 1.0 / 16.0 - / math.pi ** 2 + / math.pi**2 * ( 9.0 / 2.0 * a[4] ** 3 - 8.0 * a[3] ** 2 * a[4] @@ -66,7 +66,7 @@ sysSM = ( ), # yt lambda *a: 1.0 / 16.0 - / math.pi ** 2 + / math.pi**2 * ( 24.0 * a[5] ** 2 + 12.0 * a[4] ** 2 * a[5] @@ -137,5 +137,5 @@ def singleTraj(system, trajStart, h=0.02, tend=1.0): # initial conditions at M_Z singleTraj( - sysSM, [0.354, 0.654, 1.278, 0.983, 0.131], h=0.5, tend=math.log(10 ** 17) + sysSM, [0.354, 0.654, 1.278, 0.983, 0.131], h=0.5, tend=math.log(10**17) ) # true values diff --git a/tests/perf_bench/bm_chaos.py b/tests/perf_bench/bm_chaos.py index 55d282561f..d0f1337db7 100644 --- a/tests/perf_bench/bm_chaos.py +++ b/tests/perf_bench/bm_chaos.py @@ -15,7 +15,7 @@ class GVector(object): self.z = z def Mag(self): - return math.sqrt(self.x ** 2 + self.y ** 2 + self.z ** 2) + return math.sqrt(self.x**2 + self.y**2 + self.z**2) def dist(self, other): return math.sqrt( diff --git a/tests/perf_bench/misc_raytrace.py b/tests/perf_bench/misc_raytrace.py index b51acaccac..a729af99c2 100644 --- a/tests/perf_bench/misc_raytrace.py +++ b/tests/perf_bench/misc_raytrace.py @@ -22,7 +22,7 @@ class Vec: return Vec(self.x * rhs, self.y * rhs, self.z * rhs) def length(self): - return (self.x ** 2 + self.y ** 2 + self.z ** 2) ** 0.5 + return (self.x**2 + self.y**2 + self.z**2) ** 0.5 def normalise(self): l = self.length() @@ -87,12 +87,12 @@ class Sphere: def __init__(self, surface, centre, radius): self.surface = surface self.centre = centre - self.radsq = radius ** 2 + self.radsq = radius**2 def intersect(self, ray): v = self.centre - ray.p b = v.dot(ray.d) - det = b ** 2 - v.dot(v) + self.radsq + det = b**2 - v.dot(v) + self.radsq if det > 0: det **= 0.5 t1 = b - det @@ -180,7 +180,7 @@ def trace_ray(scene, ray, depth): if ndotl > 0: col += light_col * surf.diffuse * ndotl if ldotv > 0: - col += light_col * surf.specular * ldotv ** surf.spec_idx + col += light_col * surf.specular * ldotv**surf.spec_idx # Reflections if depth > 0 and surf.reflect > 0: diff --git a/tests/run-perfbench.py b/tests/run-perfbench.py index bcdbe69abb..5f299281fd 100755 --- a/tests/run-perfbench.py +++ b/tests/run-perfbench.py @@ -33,8 +33,8 @@ def compute_stats(lst): avg += x var += x * x avg /= len(lst) - var = max(0, var / len(lst) - avg ** 2) - return avg, var ** 0.5 + var = max(0, var / len(lst) - avg**2) + return avg, var**0.5 def run_script_on_target(target, script): @@ -201,7 +201,7 @@ def compute_diff(file1, file2, diff_score): sd1 *= av1 / 100 # convert from percent sd to absolute sd sd2 *= av2 / 100 # convert from percent sd to absolute sd av_diff = av2 - av1 - sd_diff = (sd1 ** 2 + sd2 ** 2) ** 0.5 + sd_diff = (sd1**2 + sd2**2) ** 0.5 percent = 100 * av_diff / av1 percent_sd = 100 * sd_diff / av1 print( From feeeb5ea3afe801b381eb5d4b310e83290634c46 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 2 Feb 2022 16:14:09 +1100 Subject: [PATCH 074/619] top: Update .git-blame-ignore-revs for latest Black formatting commits. Signed-off-by: Damien George --- .git-blame-ignore-revs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index c5039f58b5..fbd35fd2c2 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -1,3 +1,9 @@ +# all: Update Python formatting to latest Black version 22.1.0. +ab2923dfa1174dc177f0a90cb00a7e4ff87958d2 + +# all: Update Python formatting to latest Black version 21.12b0. +3770fab33449a5dadf8eb06edfae0767e75320a6 + # tools/gen-cpydiff.py: Fix formatting of doc strings for new Black. 0f78c36c5aa458a954eed39a46942209107a553e From 59b60995088ee4a9c0da400633f2096bd365971d Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 2 Feb 2022 20:14:05 +1100 Subject: [PATCH 075/619] tools/uf2conv.py: Update to latest version. Signed-off-by: Damien George --- tools/uf2conv.py | 129 ++++++++++++++++++++------- tools/uf2families.json | 192 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 291 insertions(+), 30 deletions(-) mode change 100755 => 100644 tools/uf2conv.py create mode 100644 tools/uf2families.json diff --git a/tools/uf2conv.py b/tools/uf2conv.py old mode 100755 new mode 100644 index d67a55224c..d3b1d9a265 --- a/tools/uf2conv.py +++ b/tools/uf2conv.py @@ -33,21 +33,13 @@ import re import os import os.path import argparse +import json UF2_MAGIC_START0 = 0x0A324655 # "UF2\n" UF2_MAGIC_START1 = 0x9E5D5157 # Randomly selected UF2_MAGIC_END = 0x0AB16F30 # Ditto -families = { - "SAMD21": 0x68ED2B88, - "SAMD51": 0x55114460, - "NRF52": 0x1B57745F, - "STM32F1": 0x5EE21072, - "STM32F4": 0x57755A57, - "ATMEGA32": 0x16573617, -} - INFO_FILE = "/INFO_UF2.TXT" appstartaddr = 0x2000 @@ -71,9 +63,14 @@ def is_hex(buf): def convert_from_uf2(buf): global appstartaddr + global familyid numblocks = len(buf) // 512 curraddr = None - outp = b"" + currfamilyid = None + families_found = {} + prev_flag = None + all_flags_same = True + outp = [] for blockno in range(numblocks): ptr = blockno * 512 block = buf[ptr : ptr + 512] @@ -88,9 +85,13 @@ def convert_from_uf2(buf): if datalen > 476: assert False, "Invalid UF2 data size at " + ptr newaddr = hd[3] - if curraddr == None: - appstartaddr = newaddr + if (hd[2] & 0x2000) and (currfamilyid == None): + currfamilyid = hd[7] + if curraddr == None or ((hd[2] & 0x2000) and hd[7] != currfamilyid): + currfamilyid = hd[7] curraddr = newaddr + if familyid == 0x0 or familyid == hd[7]: + appstartaddr = newaddr padding = newaddr - curraddr if padding < 0: assert False, "Block out of order at " + ptr @@ -101,19 +102,53 @@ def convert_from_uf2(buf): while padding > 0: padding -= 4 outp += b"\x00\x00\x00\x00" - outp += block[32 : 32 + datalen] + if familyid == 0x0 or ((hd[2] & 0x2000) and familyid == hd[7]): + outp.append(block[32 : 32 + datalen]) curraddr = newaddr + datalen - return outp + if hd[2] & 0x2000: + if hd[7] in families_found.keys(): + if families_found[hd[7]] > newaddr: + families_found[hd[7]] = newaddr + else: + families_found[hd[7]] = newaddr + if prev_flag == None: + prev_flag = hd[2] + if prev_flag != hd[2]: + all_flags_same = False + if blockno == (numblocks - 1): + print("--- UF2 File Header Info ---") + families = load_families() + for family_hex in families_found.keys(): + family_short_name = "" + for name, value in families.items(): + if value == family_hex: + family_short_name = name + print( + "Family ID is {:s}, hex value is 0x{:08x}".format( + family_short_name, family_hex + ) + ) + print("Target Address is 0x{:08x}".format(families_found[family_hex])) + if all_flags_same: + print("All block flag values consistent, 0x{:04x}".format(hd[2])) + else: + print("Flags were not all the same") + print("----------------------------") + if len(families_found) > 1 and familyid == 0x0: + outp = [] + appstartaddr = 0x0 + return b"".join(outp) def convert_to_carray(file_content): - outp = "const unsigned char bindata[] __attribute__((aligned(16))) = {" + outp = "const unsigned long bindata_len = %d;\n" % len(file_content) + outp += "const unsigned char bindata[] __attribute__((aligned(16))) = {" for i in range(len(file_content)): if i % 16 == 0: outp += "\n" - outp += "0x%02x, " % ord(file_content[i]) + outp += "0x%02x, " % file_content[i] outp += "\n};\n" - return outp + return bytes(outp, "utf-8") def convert_to_uf2(file_content): @@ -122,7 +157,7 @@ def convert_to_uf2(file_content): while len(datapadding) < 512 - 256 - 32 - 4: datapadding += b"\x00\x00\x00\x00" numblocks = (len(file_content) + 255) // 256 - outp = b"" + outp = [] for blockno in range(numblocks): ptr = 256 * blockno chunk = file_content[ptr : ptr + 256] @@ -144,8 +179,8 @@ def convert_to_uf2(file_content): chunk += b"\x00" block = hd + chunk + datapadding + struct.pack(b"= 3 and words[1] == "2" and words[2] == "FAT": drives.append(words[0]) @@ -270,7 +308,23 @@ def list_drives(): def write_file(name, buf): with open(name, "wb") as f: f.write(buf) - print("Wrote %d bytes to %s." % (len(buf), name)) + print("Wrote %d bytes to %s" % (len(buf), name)) + + +def load_families(): + # The expectation is that the `uf2families.json` file is in the same + # directory as this script. Make a path that works using `__file__` + # which contains the full path to this script. + filename = "uf2families.json" + pathname = os.path.join(os.path.dirname(os.path.abspath(__file__)), filename) + with open(pathname) as f: + raw_families = json.load(f) + + families = {} + for family in raw_families: + families[family["short_name"]] = int(family["id"], 0) + + return families def main(): @@ -303,6 +357,7 @@ def main(): parser.add_argument("-d", "--device", dest="device_path", help="select a device path to flash") parser.add_argument("-l", "--list", action="store_true", help="list connected devices") parser.add_argument("-c", "--convert", action="store_true", help="do not flash, just convert") + parser.add_argument("-D", "--deploy", action="store_true", help="just flash, do not convert") parser.add_argument( "-f", "--family", @@ -314,9 +369,17 @@ def main(): parser.add_argument( "-C", "--carray", action="store_true", help="convert binary file to a C array, not UF2" ) + parser.add_argument( + "-i", + "--info", + action="store_true", + help="display header information from UF2, do not convert", + ) args = parser.parse_args() appstartaddr = int(args.base, 0) + families = load_families() + if args.family.upper() in families: familyid = families[args.family.upper()] else: @@ -334,9 +397,14 @@ def main(): inpbuf = f.read() from_uf2 = is_uf2(inpbuf) ext = "uf2" - if from_uf2: + if args.deploy: + outbuf = inpbuf + elif from_uf2 and not args.info: outbuf = convert_from_uf2(inpbuf) ext = "bin" + elif from_uf2 and args.info: + outbuf = "" + convert_from_uf2(inpbuf) elif is_hex(inpbuf): outbuf = convert_from_hex_to_uf2(inpbuf.decode("utf-8")) elif args.carray: @@ -344,11 +412,12 @@ def main(): ext = "h" else: outbuf = convert_to_uf2(inpbuf) - print( - "Converting to %s, output size: %d, start address: 0x%x" - % (ext, len(outbuf), appstartaddr) - ) - if args.convert: + if not args.deploy and not args.info: + print( + "Converted to %s, output size: %d, start address: 0x%x" + % (ext, len(outbuf), appstartaddr) + ) + if args.convert or ext != "uf2": drives = [] if args.output == None: args.output = "flash." + ext diff --git a/tools/uf2families.json b/tools/uf2families.json new file mode 100644 index 0000000000..fafae82a60 --- /dev/null +++ b/tools/uf2families.json @@ -0,0 +1,192 @@ +[ + { + "id": "0x16573617", + "short_name": "ATMEGA32", + "description": "Microchip (Atmel) ATmega32" + }, + { + "id": "0x1851780a", + "short_name": "SAML21", + "description": "Microchip (Atmel) SAML21" + }, + { + "id": "0x1b57745f", + "short_name": "NRF52", + "description": "Nordic NRF52" + }, + { + "id": "0x1c5f21b0", + "short_name": "ESP32", + "description": "ESP32" + }, + { + "id": "0x1e1f432d", + "short_name": "STM32L1", + "description": "ST STM32L1xx" + }, + { + "id": "0x202e3a91", + "short_name": "STM32L0", + "description": "ST STM32L0xx" + }, + { + "id": "0x21460ff0", + "short_name": "STM32WL", + "description": "ST STM32WLxx" + }, + { + "id": "0x2abc77ec", + "short_name": "LPC55", + "description": "NXP LPC55xx" + }, + { + "id": "0x300f5633", + "short_name": "STM32G0", + "description": "ST STM32G0xx" + }, + { + "id": "0x31d228c6", + "short_name": "GD32F350", + "description": "GD32F350" + }, + { + "id": "0x04240bdf", + "short_name": "STM32L5", + "description": "ST STM32L5xx" + }, + { + "id": "0x4c71240a", + "short_name": "STM32G4", + "description": "ST STM32G4xx" + }, + { + "id": "0x4fb2d5bd", + "short_name": "MIMXRT10XX", + "description": "NXP i.MX RT10XX" + }, + { + "id": "0x53b80f00", + "short_name": "STM32F7", + "description": "ST STM32F7xx" + }, + { + "id": "0x55114460", + "short_name": "SAMD51", + "description": "Microchip (Atmel) SAMD51" + }, + { + "id": "0x57755a57", + "short_name": "STM32F4", + "description": "ST STM32F401" + }, + { + "id": "0x5a18069b", + "short_name": "FX2", + "description": "Cypress FX2" + }, + { + "id": "0x5d1a0a2e", + "short_name": "STM32F2", + "description": "ST STM32F2xx" + }, + { + "id": "0x5ee21072", + "short_name": "STM32F1", + "description": "ST STM32F103" + }, + { + "id": "0x621e937a", + "short_name": "NRF52833", + "description": "Nordic NRF52833" + }, + { + "id": "0x647824b6", + "short_name": "STM32F0", + "description": "ST STM32F0xx" + }, + { + "id": "0x68ed2b88", + "short_name": "SAMD21", + "description": "Microchip (Atmel) SAMD21" + }, + { + "id": "0x6b846188", + "short_name": "STM32F3", + "description": "ST STM32F3xx" + }, + { + "id": "0x6d0922fa", + "short_name": "STM32F407", + "description": "ST STM32F407" + }, + { + "id": "0x6db66082", + "short_name": "STM32H7", + "description": "ST STM32H7xx" + }, + { + "id": "0x70d16653", + "short_name": "STM32WB", + "description": "ST STM32WBxx" + }, + { + "id": "0x7eab61ed", + "short_name": "ESP8266", + "description": "ESP8266" + }, + { + "id": "0x7f83e793", + "short_name": "KL32L2", + "description": "NXP KL32L2x" + }, + { + "id": "0x8fb060fe", + "short_name": "STM32F407VG", + "description": "ST STM32F407VG" + }, + { + "id": "0xada52840", + "short_name": "NRF52840", + "description": "Nordic NRF52840" + }, + { + "id": "0xbfdd4eee", + "short_name": "ESP32S2", + "description": "ESP32-S2" + }, + { + "id": "0xc47e5767", + "short_name": "ESP32S3", + "description": "ESP32-S3" + }, + { + "id": "0xd42ba06c", + "short_name": "ESP32C3", + "description": "ESP32-C3" + }, + { + "id": "0x2b88d29c", + "short_name": "ESP32C2", + "description": "ESP32-C2" + }, + { + "id": "0x332726f6", + "short_name": "ESP32H2", + "description": "ESP32-H2" + }, + { + "id": "0xe48bff56", + "short_name": "RP2040", + "description": "Raspberry Pi RP2040" + }, + { + "id": "0x00ff6919", + "short_name": "STM32L4", + "description": "ST STM32L4xx" + }, + { + "id": "0x9af03e33", + "short_name": "GD32VF103", + "description": "GigaDevice GD32VF103" + } +] From 71b3ce3aceb25139d3f41af9441ade9d446498bb Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 2 Feb 2022 20:14:37 +1100 Subject: [PATCH 076/619] esp32: Create .uf2 binaries for S2 and S3 chips. The name of the filesystem partition is updated to support "ffat", as used by TinyUF2. Signed-off-by: Damien George --- ports/esp32/Makefile | 5 +++-- ports/esp32/makeimg.py | 31 +++++++++++++++++++++++++++++-- ports/esp32/modules/flashbdev.py | 3 +++ 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/ports/esp32/Makefile b/ports/esp32/Makefile index 403b698a32..c8ca9262c8 100644 --- a/ports/esp32/Makefile +++ b/ports/esp32/Makefile @@ -36,8 +36,9 @@ all: $(BUILD)/sdkconfig \ $(BUILD)/bootloader/bootloader.bin \ $(BUILD)/partition_table/partition-table.bin \ - $(BUILD)/micropython.bin \ - $(BUILD)/firmware.bin + $(BUILD)/micropython.bin \ + $(BUILD)/firmware.bin \ + $(BUILD)/micropython.uf2 $(BUILD)/bootloader/bootloader.bin $(BUILD)/partition_table/partition-table.bin $(BUILD)/micropython.bin: FORCE diff --git a/ports/esp32/makeimg.py b/ports/esp32/makeimg.py index 0646ce372c..5964e91d28 100644 --- a/ports/esp32/makeimg.py +++ b/ports/esp32/makeimg.py @@ -10,15 +10,29 @@ OFFSET_BOOTLOADER_DEFAULT = 0x1000 OFFSET_PARTITIONS_DEFAULT = 0x8000 -def load_sdkconfig_hex_value(filename, value, default): +def load_sdkconfig_value(filename, value, default): value = "CONFIG_" + value + "=" with open(filename, "r") as f: for line in f: if line.startswith(value): - return int(line.split("=", 1)[1], 16) + return line.split("=", 1)[1] return default +def load_sdkconfig_hex_value(filename, value, default): + value = load_sdkconfig_value(filename, value, None) + if value is None: + return default + return int(value, 16) + + +def load_sdkconfig_str_value(filename, value, default): + value = load_sdkconfig_value(filename, value, None) + if value is None: + return default + return value.strip().strip('"') + + def load_partition_table(filename): with open(filename, "rb") as f: return gen_esp32part.PartitionTable.from_binary(f.read()) @@ -30,8 +44,10 @@ arg_bootloader_bin = sys.argv[2] arg_partitions_bin = sys.argv[3] arg_application_bin = sys.argv[4] arg_output_bin = sys.argv[5] +arg_output_uf2 = sys.argv[6] # Load required sdkconfig values. +idf_target = load_sdkconfig_str_value(arg_sdkconfig, "IDF_TARGET", "").upper() offset_bootloader = load_sdkconfig_hex_value( arg_sdkconfig, "BOOTLOADER_OFFSET_IN_FLASH", OFFSET_BOOTLOADER_DEFAULT ) @@ -85,3 +101,14 @@ with open(file_out, "wb") as fout: ) sys.exit(1) print("%-22s% 8d" % ("total", cur_offset)) + +# Generate .uf2 file if the SoC has native USB. +if idf_target in ("ESP32S2", "ESP32S3"): + sys.path.append(os.path.join(os.path.dirname(__file__), "../../tools")) + import uf2conv + + families = uf2conv.load_families() + uf2conv.appstartaddr = 0 + uf2conv.familyid = families[idf_target] + with open(arg_application_bin, "rb") as fin, open(arg_output_uf2, "wb") as fout: + fout.write(uf2conv.convert_to_uf2(fin.read())) diff --git a/ports/esp32/modules/flashbdev.py b/ports/esp32/modules/flashbdev.py index bf4fec9f38..c3f75c7a7e 100644 --- a/ports/esp32/modules/flashbdev.py +++ b/ports/esp32/modules/flashbdev.py @@ -1,4 +1,7 @@ from esp32 import Partition +# MicroPython's partition table uses "vfs", TinyUF2 uses "ffat". bdev = Partition.find(Partition.TYPE_DATA, label="vfs") +if not bdev: + bdev = Partition.find(Partition.TYPE_DATA, label="ffat") bdev = bdev[0] if bdev else None From 102cc12dbbe10c3caf3e1e141be4ab4366dbd02f Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 2 Feb 2022 20:15:51 +1100 Subject: [PATCH 077/619] tools/autobuild: Provide .uf2 images for esp32 builds when available. Signed-off-by: Damien George --- tools/autobuild/build-boards.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/autobuild/build-boards.sh b/tools/autobuild/build-boards.sh index 4b5259b667..c6493f8fc1 100755 --- a/tools/autobuild/build-boards.sh +++ b/tools/autobuild/build-boards.sh @@ -30,8 +30,8 @@ function build_board { dest=$dest_dir/$descr$fw_tag.$ext if [ -r $build_dir/firmware.$ext ]; then mv $build_dir/firmware.$ext $dest - else - # esp32 has micropython.elf and micropython.map + elif [ -r $build_dir/micropython.$ext ]; then + # esp32 has micropython.elf, etc mv $build_dir/micropython.$ext $dest fi done @@ -93,7 +93,7 @@ function build_esp32_boards { else if [ $mcu != esp32 ]; then # build esp32-s2/s3/c3 based boards with IDF v4.4+ - build_board $board_json $fw_tag $dest_dir bin elf map + build_board $board_json $fw_tag $dest_dir bin elf map uf2 fi fi done From fbd47fc46cce80abfc59e680600666473a1868da Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 2 Feb 2022 16:04:00 +1100 Subject: [PATCH 078/619] ports: Consolidate inclusion of umachine module in built-ins. The inclusion of `umachine` in the list of built-in modules is now done centrally in py/objmodule.c. Enabling MICROPY_PY_MACHINE will include this module. As part of this, all ports now have `umachine` as the core module name (previously some had only `machine` as the name). Signed-off-by: Damien George --- ports/cc3200/mods/modmachine.c | 2 +- ports/cc3200/mpconfigport.h | 4 +--- ports/esp32/mpconfigport.h | 2 -- ports/esp8266/mpconfigport.h | 2 -- ports/mimxrt/mpconfigport.h | 2 -- ports/nrf/modules/machine/modmachine.c | 2 +- ports/nrf/mpconfigport.h | 5 +---- ports/qemu-arm/mpconfigport.h | 1 - ports/rp2/mpconfigport.h | 2 -- ports/samd/mpconfigport.h | 2 -- ports/stm32/modmachine.c | 2 +- ports/stm32/mpconfigport.h | 9 +++------ ports/unix/mpconfigport.h | 2 -- ports/windows/mpconfigport.h | 1 - ports/zephyr/mpconfigport.h | 2 -- py/objmodule.c | 3 +++ 16 files changed, 11 insertions(+), 32 deletions(-) diff --git a/ports/cc3200/mods/modmachine.c b/ports/cc3200/mods/modmachine.c index 89800810c7..ee76d6e516 100644 --- a/ports/cc3200/mods/modmachine.c +++ b/ports/cc3200/mods/modmachine.c @@ -209,7 +209,7 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { STATIC MP_DEFINE_CONST_DICT(machine_module_globals, machine_module_globals_table); -const mp_obj_module_t machine_module = { +const mp_obj_module_t mp_module_machine = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&machine_module_globals, }; diff --git a/ports/cc3200/mpconfigport.h b/ports/cc3200/mpconfigport.h index 345b64fe40..28192e9b3e 100644 --- a/ports/cc3200/mpconfigport.h +++ b/ports/cc3200/mpconfigport.h @@ -145,7 +145,6 @@ { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mp_builtin_open_obj) }, \ // extra built in modules to add to the list of known ones -extern const struct _mp_obj_module_t machine_module; extern const struct _mp_obj_module_t wipy_module; extern const struct _mp_obj_module_t mp_module_ure; extern const struct _mp_obj_module_t mp_module_ujson; @@ -158,7 +157,6 @@ extern const struct _mp_obj_module_t mp_module_ubinascii; extern const struct _mp_obj_module_t mp_module_ussl; #define MICROPY_PORT_BUILTIN_MODULES \ - { MP_ROM_QSTR(MP_QSTR_umachine), MP_ROM_PTR(&machine_module) }, \ { MP_ROM_QSTR(MP_QSTR_wipy), MP_ROM_PTR(&wipy_module) }, \ { MP_ROM_QSTR(MP_QSTR_uos), MP_ROM_PTR(&mp_module_uos) }, \ { MP_ROM_QSTR(MP_QSTR_utime), MP_ROM_PTR(&mp_module_utime) }, \ @@ -170,7 +168,7 @@ extern const struct _mp_obj_module_t mp_module_ussl; // extra constants #define MICROPY_PORT_CONSTANTS \ - { MP_ROM_QSTR(MP_QSTR_umachine), MP_ROM_PTR(&machine_module) }, \ + { MP_ROM_QSTR(MP_QSTR_umachine), MP_ROM_PTR(&mp_module_machine) }, \ // vm state and root pointers for the gc #define MP_STATE_PORT MP_STATE_VM diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index dc6bc2e53b..c457a5743b 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -224,7 +224,6 @@ extern const struct _mp_obj_module_t esp32_module; extern const struct _mp_obj_module_t utime_module; extern const struct _mp_obj_module_t uos_module; extern const struct _mp_obj_module_t mp_module_usocket; -extern const struct _mp_obj_module_t mp_module_machine; extern const struct _mp_obj_module_t mp_module_network; extern const struct _mp_obj_module_t mp_module_onewire; @@ -234,7 +233,6 @@ extern const struct _mp_obj_module_t mp_module_onewire; { MP_OBJ_NEW_QSTR(MP_QSTR_utime), (mp_obj_t)&utime_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_uos), (mp_obj_t)&uos_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_usocket), (mp_obj_t)&mp_module_usocket }, \ - { MP_OBJ_NEW_QSTR(MP_QSTR_machine), (mp_obj_t)&mp_module_machine }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_network), (mp_obj_t)&mp_module_network }, \ { MP_OBJ_NEW_QSTR(MP_QSTR__onewire), (mp_obj_t)&mp_module_onewire }, \ diff --git a/ports/esp8266/mpconfigport.h b/ports/esp8266/mpconfigport.h index 7d6d0a34c3..49c53a4671 100644 --- a/ports/esp8266/mpconfigport.h +++ b/ports/esp8266/mpconfigport.h @@ -174,7 +174,6 @@ extern const struct _mp_obj_module_t network_module; extern const struct _mp_obj_module_t utime_module; extern const struct _mp_obj_module_t uos_module; extern const struct _mp_obj_module_t mp_module_lwip; -extern const struct _mp_obj_module_t mp_module_machine; extern const struct _mp_obj_module_t mp_module_onewire; #define MICROPY_PORT_BUILTIN_MODULES \ @@ -183,7 +182,6 @@ extern const struct _mp_obj_module_t mp_module_onewire; { MP_ROM_QSTR(MP_QSTR_network), MP_ROM_PTR(&network_module) }, \ { MP_ROM_QSTR(MP_QSTR_utime), MP_ROM_PTR(&utime_module) }, \ { MP_ROM_QSTR(MP_QSTR_uos), MP_ROM_PTR(&uos_module) }, \ - { MP_ROM_QSTR(MP_QSTR_machine), MP_ROM_PTR(&mp_module_machine) }, \ { MP_ROM_QSTR(MP_QSTR__onewire), MP_ROM_PTR(&mp_module_onewire) }, \ #define MP_STATE_PORT MP_STATE_VM diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index e650579e69..4462a18a61 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -217,7 +217,6 @@ static inline void restore_irq_pri(uint32_t basepri) { #define MICROPY_PORT_BUILTINS \ { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mp_builtin_open_obj) }, -extern const struct _mp_obj_module_t mp_module_machine; extern const struct _mp_obj_module_t mp_module_mimxrt; extern const struct _mp_obj_module_t mp_module_onewire; extern const struct _mp_obj_module_t mp_module_uos; @@ -256,7 +255,6 @@ extern const struct _mp_obj_type_t network_lan_type; #endif #define MICROPY_PORT_BUILTIN_MODULES \ - { MP_ROM_QSTR(MP_QSTR_machine), MP_ROM_PTR(&mp_module_machine) }, \ { MP_ROM_QSTR(MP_QSTR_mimxrt), (mp_obj_t)&mp_module_mimxrt }, \ { MP_ROM_QSTR(MP_QSTR_uos), MP_ROM_PTR(&mp_module_uos) }, \ { MP_ROM_QSTR(MP_QSTR_utime), MP_ROM_PTR(&mp_module_utime) }, \ diff --git a/ports/nrf/modules/machine/modmachine.c b/ports/nrf/modules/machine/modmachine.c index fb3267e0db..3d4cda8e45 100644 --- a/ports/nrf/modules/machine/modmachine.c +++ b/ports/nrf/modules/machine/modmachine.c @@ -244,7 +244,7 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { STATIC MP_DEFINE_CONST_DICT(machine_module_globals, machine_module_globals_table); -const mp_obj_module_t machine_module = { +const mp_obj_module_t mp_module_machine = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&machine_module_globals, }; diff --git a/ports/nrf/mpconfigport.h b/ports/nrf/mpconfigport.h index 3ffb092c61..7a6e282e86 100644 --- a/ports/nrf/mpconfigport.h +++ b/ports/nrf/mpconfigport.h @@ -220,7 +220,6 @@ typedef long mp_off_t; // extra built in modules to add to the list of known ones extern const struct _mp_obj_module_t board_module; -extern const struct _mp_obj_module_t machine_module; extern const struct _mp_obj_module_t nrf_module; extern const struct _mp_obj_module_t mp_module_utime; extern const struct _mp_obj_module_t mp_module_uos; @@ -263,7 +262,6 @@ extern const struct _mp_obj_module_t ble_module; #define MICROPY_PORT_BUILTIN_MODULES \ { MP_ROM_QSTR(MP_QSTR_board), MP_ROM_PTR(&board_module) }, \ - { MP_ROM_QSTR(MP_QSTR_machine), MP_ROM_PTR(&machine_module) }, \ { MP_ROM_QSTR(MP_QSTR_utime), MP_ROM_PTR(&mp_module_utime) }, \ { MP_ROM_QSTR(MP_QSTR_time), MP_ROM_PTR(&mp_module_utime) }, \ { MP_ROM_QSTR(MP_QSTR_uos), MP_ROM_PTR(&mp_module_uos) }, \ @@ -278,7 +276,6 @@ extern const struct _mp_obj_module_t ble_module; extern const struct _mp_obj_module_t ble_module; #define MICROPY_PORT_BUILTIN_MODULES \ { MP_ROM_QSTR(MP_QSTR_board), MP_ROM_PTR(&board_module) }, \ - { MP_ROM_QSTR(MP_QSTR_machine), MP_ROM_PTR(&machine_module) }, \ { MP_ROM_QSTR(MP_QSTR_utime), MP_ROM_PTR(&mp_module_utime) }, \ { MP_ROM_QSTR(MP_QSTR_uos), MP_ROM_PTR(&mp_module_uos) }, \ MUSIC_MODULE \ @@ -296,7 +293,7 @@ extern const struct _mp_obj_module_t ble_module; // extra constants #define MICROPY_PORT_CONSTANTS \ { MP_ROM_QSTR(MP_QSTR_board), MP_ROM_PTR(&board_module) }, \ - { MP_ROM_QSTR(MP_QSTR_machine), MP_ROM_PTR(&machine_module) }, \ + { MP_ROM_QSTR(MP_QSTR_machine), MP_ROM_PTR(&mp_module_machine) }, \ BLE_MODULE \ #define MP_STATE_PORT MP_STATE_VM diff --git a/ports/qemu-arm/mpconfigport.h b/ports/qemu-arm/mpconfigport.h index 270995979c..6bfea74ea0 100644 --- a/ports/qemu-arm/mpconfigport.h +++ b/ports/qemu-arm/mpconfigport.h @@ -68,7 +68,6 @@ extern const struct _mp_obj_module_t mp_module_uos; #define MICROPY_PORT_BUILTIN_MODULES \ { MP_ROM_QSTR(MP_QSTR_uos), MP_ROM_PTR(&mp_module_uos) }, \ - { MP_ROM_QSTR(MP_QSTR_umachine), MP_ROM_PTR(&mp_module_machine) }, \ // We need to provide a declaration/definition of alloca() #include diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h index 63d9818e31..1ff9d5b511 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -121,7 +121,6 @@ #define MICROPY_PORT_BUILTINS \ { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mp_builtin_open_obj) }, -extern const struct _mp_obj_module_t mp_module_machine; extern const struct _mp_obj_module_t mp_module_network; extern const struct _mp_obj_module_t mp_module_onewire; extern const struct _mp_obj_module_t mp_module_rp2; @@ -171,7 +170,6 @@ extern const struct _mod_network_nic_type_t mod_network_nic_type_nina; #endif #define MICROPY_PORT_BUILTIN_MODULES \ - { MP_OBJ_NEW_QSTR(MP_QSTR_machine), (mp_obj_t)&mp_module_machine }, \ { MP_OBJ_NEW_QSTR(MP_QSTR__onewire), (mp_obj_t)&mp_module_onewire }, \ { MP_OBJ_NEW_QSTR(MP_QSTR__rp2), (mp_obj_t)&mp_module_rp2 }, \ { MP_ROM_QSTR(MP_QSTR_uos), MP_ROM_PTR(&mp_module_uos) }, \ diff --git a/ports/samd/mpconfigport.h b/ports/samd/mpconfigport.h index 645f16b3f7..54018f111e 100644 --- a/ports/samd/mpconfigport.h +++ b/ports/samd/mpconfigport.h @@ -108,12 +108,10 @@ #define MICROPY_PORT_BUILTINS \ { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mp_builtin_open_obj) }, -extern const struct _mp_obj_module_t mp_module_machine; extern const struct _mp_obj_module_t mp_module_samd; extern const struct _mp_obj_module_t mp_module_utime; #define MICROPY_PORT_BUILTIN_MODULES \ - { MP_ROM_QSTR(MP_QSTR_machine), MP_ROM_PTR(&mp_module_machine) }, \ { MP_ROM_QSTR(MP_QSTR_samd), MP_ROM_PTR(&mp_module_samd) }, \ { MP_ROM_QSTR(MP_QSTR_utime), MP_ROM_PTR(&mp_module_utime) }, \ diff --git a/ports/stm32/modmachine.c b/ports/stm32/modmachine.c index 9b215c75cd..f9026bc370 100644 --- a/ports/stm32/modmachine.c +++ b/ports/stm32/modmachine.c @@ -464,7 +464,7 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { STATIC MP_DEFINE_CONST_DICT(machine_module_globals, machine_module_globals_table); -const mp_obj_module_t machine_module = { +const mp_obj_module_t mp_module_machine = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t *)&machine_module_globals, }; diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h index 6bca5dfd92..3cca8f1925 100644 --- a/ports/stm32/mpconfigport.h +++ b/ports/stm32/mpconfigport.h @@ -169,7 +169,6 @@ { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mp_builtin_open_obj) }, // extra built in modules to add to the list of known ones -extern const struct _mp_obj_module_t machine_module; extern const struct _mp_obj_module_t pyb_module; extern const struct _mp_obj_module_t stm_module; extern const struct _mp_obj_module_t mp_module_ubinascii; @@ -197,10 +196,10 @@ extern const struct _mp_obj_module_t mp_module_onewire; #endif #if MICROPY_PY_MACHINE -#define MACHINE_BUILTIN_MODULE { MP_ROM_QSTR(MP_QSTR_umachine), MP_ROM_PTR(&machine_module) }, -#define MACHINE_BUILTIN_MODULE_CONSTANTS { MP_ROM_QSTR(MP_QSTR_machine), MP_ROM_PTR(&machine_module) }, +#define MACHINE_BUILTIN_MODULE_CONSTANTS \ + { MP_ROM_QSTR(MP_QSTR_umachine), MP_ROM_PTR(&mp_module_machine) }, \ + { MP_ROM_QSTR(MP_QSTR_machine), MP_ROM_PTR(&mp_module_machine) }, #else -#define MACHINE_BUILTIN_MODULE #define MACHINE_BUILTIN_MODULE_CONSTANTS #endif @@ -272,7 +271,6 @@ extern const struct _mod_network_nic_type_t mod_network_nic_type_cc3k; #endif #define MICROPY_PORT_BUILTIN_MODULES \ - MACHINE_BUILTIN_MODULE \ PYB_BUILTIN_MODULE \ STM_BUILTIN_MODULE \ UOS_BUILTIN_MODULE \ @@ -283,7 +281,6 @@ extern const struct _mod_network_nic_type_t mod_network_nic_type_cc3k; // extra constants #define MICROPY_PORT_CONSTANTS \ - MACHINE_BUILTIN_MODULE \ MACHINE_BUILTIN_MODULE_CONSTANTS \ PYB_BUILTIN_MODULE \ STM_BUILTIN_MODULE \ diff --git a/ports/unix/mpconfigport.h b/ports/unix/mpconfigport.h index 658cb680f1..d54d0b3ef3 100644 --- a/ports/unix/mpconfigport.h +++ b/ports/unix/mpconfigport.h @@ -204,7 +204,6 @@ extern const struct _mp_print_t mp_stderr_print; #define mp_type_fileio mp_type_vfs_posix_fileio #define mp_type_textio mp_type_vfs_posix_textio -extern const struct _mp_obj_module_t mp_module_machine; extern const struct _mp_obj_module_t mp_module_os; extern const struct _mp_obj_module_t mp_module_uos_vfs; extern const struct _mp_obj_module_t mp_module_uselect; @@ -255,7 +254,6 @@ extern const struct _mp_obj_module_t mp_module_jni; MICROPY_PY_JNI_DEF \ MICROPY_PY_UTIME_DEF \ MICROPY_PY_SOCKET_DEF \ - { MP_ROM_QSTR(MP_QSTR_umachine), MP_ROM_PTR(&mp_module_machine) }, \ MICROPY_PY_UOS_DEF \ MICROPY_PY_USELECT_DEF \ MICROPY_PY_TERMIOS_DEF \ diff --git a/ports/windows/mpconfigport.h b/ports/windows/mpconfigport.h index 88746744ae..d589dac7a7 100644 --- a/ports/windows/mpconfigport.h +++ b/ports/windows/mpconfigport.h @@ -207,7 +207,6 @@ extern const struct _mp_obj_module_t mp_module_os; extern const struct _mp_obj_module_t mp_module_time; #define MICROPY_PORT_BUILTIN_MODULES \ { MP_ROM_QSTR(MP_QSTR_utime), MP_ROM_PTR(&mp_module_time) }, \ - { MP_ROM_QSTR(MP_QSTR_umachine), MP_ROM_PTR(&mp_module_machine) }, \ { MP_ROM_QSTR(MP_QSTR_uos), MP_ROM_PTR(&mp_module_os) }, \ #if MICROPY_USE_READLINE == 1 diff --git a/ports/zephyr/mpconfigport.h b/ports/zephyr/mpconfigport.h index 501c39a1bb..b8e25faefd 100644 --- a/ports/zephyr/mpconfigport.h +++ b/ports/zephyr/mpconfigport.h @@ -138,7 +138,6 @@ typedef long mp_off_t; void *machine_pin_irq_list; /* Linked list of pin irq objects */ \ struct _mp_bluetooth_zephyr_root_pointers_t *bluetooth_zephyr_root_pointers; -extern const struct _mp_obj_module_t mp_module_machine; extern const struct _mp_obj_module_t mp_module_time; extern const struct _mp_obj_module_t mp_module_uos; extern const struct _mp_obj_module_t mp_module_usocket; @@ -176,7 +175,6 @@ extern const struct _mp_obj_module_t mp_module_zsensor; #endif #define MICROPY_PORT_BUILTIN_MODULES \ - { MP_ROM_QSTR(MP_QSTR_machine), MP_ROM_PTR(&mp_module_machine) }, \ MICROPY_PY_UOS_DEF \ MICROPY_PY_USOCKET_DEF \ MICROPY_PY_UTIME_DEF \ diff --git a/py/objmodule.c b/py/objmodule.c index 6b06740e4d..9be4bad92c 100644 --- a/py/objmodule.c +++ b/py/objmodule.c @@ -219,6 +219,9 @@ STATIC const mp_rom_map_elem_t mp_builtin_module_table[] = { #if MICROPY_PY_LWIP { MP_ROM_QSTR(MP_QSTR_lwip), MP_ROM_PTR(&mp_module_lwip) }, #endif + #if MICROPY_PY_MACHINE + { MP_ROM_QSTR(MP_QSTR_umachine), MP_ROM_PTR(&mp_module_machine) }, + #endif #if MICROPY_PY_UWEBSOCKET { MP_ROM_QSTR(MP_QSTR_uwebsocket), MP_ROM_PTR(&mp_module_uwebsocket) }, #endif From b8d55d4c52145e3ef2abf692a01a3ffce3ee78d0 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 3 Feb 2022 13:20:22 +1100 Subject: [PATCH 079/619] stm32/mboot: Allow HSI to be used as the main clock source. Signed-off-by: Damien George --- ports/stm32/mboot/main.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ports/stm32/mboot/main.c b/ports/stm32/mboot/main.c index ae409168b9..d62224b1a6 100644 --- a/ports/stm32/mboot/main.c +++ b/ports/stm32/mboot/main.c @@ -80,7 +80,7 @@ #undef MICROPY_HW_CLK_PLLP #undef MICROPY_HW_CLK_PLLQ #undef MICROPY_HW_CLK_PLLR -#define MICROPY_HW_CLK_PLLM (HSE_VALUE / 1000000) +#define MICROPY_HW_CLK_PLLM (MICROPY_HW_CLK_VALUE / 1000000) #define MICROPY_HW_CLK_PLLN (192) #define MICROPY_HW_CLK_PLLP (MICROPY_HW_CLK_PLLN / (CORE_PLL_FREQ / 1000000)) #define MICROPY_HW_CLK_PLLQ (4) @@ -184,10 +184,12 @@ void SystemClock_Config(void) { // Reduce power consumption __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); + #if !MICROPY_HW_CLK_USE_HSI // Turn HSE on __HAL_RCC_HSE_CONFIG(RCC_HSE_ON); while (__HAL_RCC_GET_FLAG(RCC_FLAG_HSERDY) == RESET) { } + #endif // Disable PLL __HAL_RCC_PLL_DISABLE(); @@ -196,7 +198,11 @@ void SystemClock_Config(void) { // Configure PLL factors and source RCC->PLLCFGR = + #if MICROPY_HW_CLK_USE_HSI + 0 << RCC_PLLCFGR_PLLSRC_Pos // HSI selected as PLL source + #else 1 << RCC_PLLCFGR_PLLSRC_Pos // HSE selected as PLL source + #endif | MICROPY_HW_CLK_PLLM << RCC_PLLCFGR_PLLM_Pos | MICROPY_HW_CLK_PLLN << RCC_PLLCFGR_PLLN_Pos | ((MICROPY_HW_CLK_PLLP >> 1) - 1) << RCC_PLLCFGR_PLLP_Pos From 4f918f4b26ec2c949f6832b972cdab80506b7038 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 3 Feb 2022 13:20:51 +1100 Subject: [PATCH 080/619] stm32/mboot: Add MBOOT_BOARD_ENTRY_INIT for a board to add entry code. Also change the signature of stm32_main to uint32_t, which is what it should be. Signed-off-by: Damien George --- ports/stm32/mboot/main.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ports/stm32/mboot/main.c b/ports/stm32/mboot/main.c index d62224b1a6..abde92de1b 100644 --- a/ports/stm32/mboot/main.c +++ b/ports/stm32/mboot/main.c @@ -1439,7 +1439,7 @@ static void leave_bootloader(void) { extern PCD_HandleTypeDef pcd_fs_handle; extern PCD_HandleTypeDef pcd_hs_handle; -void stm32_main(int initial_r0) { +void stm32_main(uint32_t initial_r0) { #if defined(STM32H7) // Configure write-once power options, and wait for voltage levels to be ready PWR->CR3 = PWR_CR3_LDOEN; @@ -1524,6 +1524,10 @@ enter_bootloader: __ASM volatile ("msr basepri_max, %0" : : "r" (pri) : "memory"); #endif + #if defined(MBOOT_BOARD_ENTRY_INIT) + MBOOT_BOARD_ENTRY_INIT(initial_r0); + #endif + #if defined(MBOOT_SPIFLASH_ADDR) MBOOT_SPIFLASH_SPIFLASH->config = MBOOT_SPIFLASH_CONFIG; mp_spiflash_init(MBOOT_SPIFLASH_SPIFLASH); From 26faf74d52e3e953c9ddbcd800a2f9e117477ce7 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 3 Feb 2022 13:21:57 +1100 Subject: [PATCH 081/619] stm32/mboot: Add support for F469/479 MCUs in fwupdate.py. And don't assert on the sector number in sector_erase, so it can support erasing arbitrary sectors. Signed-off-by: Damien George --- ports/stm32/mboot/fwupdate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/stm32/mboot/fwupdate.py b/ports/stm32/mboot/fwupdate.py index 3b8dc51e1c..0e7ea01418 100644 --- a/ports/stm32/mboot/fwupdate.py +++ b/ports/stm32/mboot/fwupdate.py @@ -105,10 +105,11 @@ class Flash: dev_id = 0 # Configure flash parameters based on MCU. - if dev_id in (0x413, 0x419, 0x431, 0x451, 0x452): + if dev_id in (0x413, 0x419, 0x431, 0x434, 0x451, 0x452): # 0x413: STM32F405/407, STM32F415/417 # 0x419: STM32F42x/43x # 0x431: STM32F411 + # 0x434: STM32F469/479 # 0x451: STM32F76x/77x # 0x452: STM32F72x/73x self._keyr = stm.FLASH + stm.FLASH_KEYR @@ -155,7 +156,6 @@ class Flash: stm.mem32[self._cr] = self._cr_lock def erase_sector(self, sector): - assert 0 <= sector <= 7 self.wait_not_busy() stm.mem32[self._cr] = self._cr_init_erase(sector) stm.mem32[self._cr] |= self._cr_start_erase From e4f59a00201b31903b11a6d30dddedc1457ed2ff Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 4 Feb 2022 08:30:39 +1100 Subject: [PATCH 082/619] stm32/rtc: Use LL_RTC functions to simplify some MCU-specific code. This also fixes a possible race condition when exiting initialisation mode: reading then writing to ISR (via ISR &= ~RTC_ISR_INIT) will clear any flags that were set by the hardware between the read and the write. The correct way to clear just the INIT bit is to just do a single write via ISR = ~RTC_ISR_INIT, which will not clear any other flags (they must be written to 0 to clear), and that is exactly what LL_RTC_DisableInitMode does. Signed-off-by: Damien George --- ports/stm32/rtc.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/ports/stm32/rtc.c b/ports/stm32/rtc.c index 2d6459e239..1164e71e08 100644 --- a/ports/stm32/rtc.c +++ b/ports/stm32/rtc.c @@ -339,11 +339,7 @@ STATIC HAL_StatusTypeDef PYB_RTC_Init(RTC_HandleTypeDef *hrtc) { hrtc->Instance->PRER |= (uint32_t)(hrtc->Init.AsynchPrediv << 16); // Exit Initialization mode - #if defined(STM32G4) || defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ) - hrtc->Instance->ICSR &= (uint32_t) ~RTC_ICSR_INIT; - #else - hrtc->Instance->ISR &= (uint32_t) ~RTC_ISR_INIT; - #endif + LL_RTC_DisableInitMode(hrtc->Instance); #if defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ) // do nothing @@ -702,13 +698,8 @@ mp_obj_t pyb_rtc_wakeup(size_t n_args, const mp_obj_t *args) { RTC->CR &= ~RTC_CR_WUTE; // wait until WUTWF is set - #if defined(STM32G4) || defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ) - while (!(RTC->ICSR & RTC_ICSR_WUTWF)) { + while (!LL_RTC_IsActiveFlag_WUTW(RTC)) { } - #else - while (!(RTC->ISR & RTC_ISR_WUTWF)) { - } - #endif if (enable) { // program WUT From 9127e6370819b32552caedd0184f206e1b9c9285 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 22 Feb 2021 11:54:31 +1100 Subject: [PATCH 083/619] stm32/Makefile: Make stm32lib configurable. The default stm32lib remains lib/stm32lib, but it can now be easily overriden at build time by specifying STM32LIB_DIR, or STM32LIB_CMSIS_DIR and STM32LIB_HAL_DIR. Signed-off-by: Damien George --- ports/stm32/Makefile | 68 +++++-------------- .../stm32/boards/NUCLEO_WB55/mpconfigboard.mk | 2 +- .../boards/USBDONGLE_WB55/mpconfigboard.mk | 2 +- ports/stm32/mboot/Makefile | 27 ++------ ports/stm32/stm32.mk | 68 +++++++++++++++++++ 5 files changed, 93 insertions(+), 74 deletions(-) create mode 100644 ports/stm32/stm32.mk diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index c2581c100b..4c90c6f1ef 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -36,12 +36,7 @@ include $(TOP)/py/py.mk GIT_SUBMODULES += lib/libhydrogen lib/lwip lib/mbedtls lib/stm32lib -MCU_SERIES_UPPER = $(shell echo $(MCU_SERIES) | tr '[:lower:]' '[:upper:]') -CMSIS_MCU_LOWER = $(shell echo $(CMSIS_MCU) | tr '[:upper:]' '[:lower:]') - LD_DIR=boards -CMSIS_DIR=$(TOP)/lib/stm32lib/CMSIS/STM32$(MCU_SERIES_UPPER)xx/Include -HAL_DIR=lib/stm32lib/STM32$(MCU_SERIES_UPPER)xx_HAL_Driver USBDEV_DIR=usbdev #USBHOST_DIR=usbhost DFU=$(TOP)/tools/dfu.py @@ -55,8 +50,8 @@ BOOTLOADER_DFU_USB_PID ?= 0xDF11 STFLASH ?= st-flash OPENOCD ?= openocd OPENOCD_CONFIG ?= boards/openocd_stm32f4.cfg -STARTUP_FILE ?= lib/stm32lib/CMSIS/STM32$(MCU_SERIES_UPPER)xx/Source/Templates/gcc/startup_$(CMSIS_MCU_LOWER).o -SYSTEM_FILE ?= lib/stm32lib/CMSIS/STM32$(MCU_SERIES_UPPER)xx/Source/Templates/system_stm32$(MCU_SERIES)xx.o + +include stm32.mk # Select the cross compile prefix CROSS_COMPILE ?= arm-none-eabi- @@ -65,41 +60,12 @@ INC += -I. INC += -I$(TOP) INC += -I$(BUILD) INC += -I$(TOP)/lib/cmsis/inc -INC += -I$(CMSIS_DIR)/ -INC += -I$(TOP)/$(HAL_DIR)/Inc +INC += -I$(STM32LIB_CMSIS_ABS)/Include +INC += -I$(STM32LIB_HAL_ABS)/Inc INC += -I$(USBDEV_DIR)/core/inc -I$(USBDEV_DIR)/class/inc #INC += -I$(USBHOST_DIR) INC += -Ilwip_inc -# Basic Cortex-M flags -CFLAGS_CORTEX_M = -mthumb - -# Select hardware floating-point support -SUPPORTS_HARDWARE_FP_SINGLE = 0 -SUPPORTS_HARDWARE_FP_DOUBLE = 0 -ifeq ($(CMSIS_MCU),$(filter $(CMSIS_MCU),STM32F765xx STM32F767xx STM32F769xx STM32H743xx STM32H750xx STM32H7A3xx STM32H7A3xxQ STM32H7B3xx STM32H7B3xxQ)) -CFLAGS_CORTEX_M += -mfpu=fpv5-d16 -mfloat-abi=hard -SUPPORTS_HARDWARE_FP_SINGLE = 1 -SUPPORTS_HARDWARE_FP_DOUBLE = 1 -else -ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f0 l0)) -CFLAGS_CORTEX_M += -msoft-float -else -CFLAGS_CORTEX_M += -mfpu=fpv4-sp-d16 -mfloat-abi=hard -SUPPORTS_HARDWARE_FP_SINGLE = 1 -endif -endif - -# Options for particular MCU series -CFLAGS_MCU_f0 = $(CFLAGS_CORTEX_M) -mtune=cortex-m0 -mcpu=cortex-m0 -CFLAGS_MCU_f4 = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4 -CFLAGS_MCU_f7 = $(CFLAGS_CORTEX_M) -mtune=cortex-m7 -mcpu=cortex-m7 -CFLAGS_MCU_g4 = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4 -CFLAGS_MCU_l0 = $(CFLAGS_CORTEX_M) -mtune=cortex-m0plus -mcpu=cortex-m0plus -CFLAGS_MCU_l4 = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4 -CFLAGS_MCU_h7 = $(CFLAGS_CORTEX_M) -mtune=cortex-m7 -mcpu=cortex-m7 -CFLAGS_MCU_wb = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4 - CFLAGS += $(INC) -Wall -Wpointer-arith -Werror -Wdouble-promotion -Wfloat-conversion -std=gnu99 -nostdlib $(CFLAGS_MOD) $(CFLAGS_EXTRA) CFLAGS += -D$(CMSIS_MCU) -DUSE_FULL_LL_DRIVER CFLAGS += $(CFLAGS_MCU_$(MCU_SERIES)) @@ -377,7 +343,7 @@ SRC_O += \ endif endif -HAL_SRC_C += $(addprefix $(HAL_DIR)/Src/stm32$(MCU_SERIES)xx_,\ +HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\ hal.c \ hal_adc.c \ hal_adc_ex.c \ @@ -403,13 +369,13 @@ HAL_SRC_C += $(addprefix $(HAL_DIR)/Src/stm32$(MCU_SERIES)xx_,\ ) ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f4 f7 h7 l0 l4 wb)) -HAL_SRC_C += $(addprefix $(HAL_DIR)/Src/stm32$(MCU_SERIES)xx_,\ +HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\ ll_usb.c \ ) endif ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f4 f7 h7 l4)) -HAL_SRC_C += $(addprefix $(HAL_DIR)/Src/stm32$(MCU_SERIES)xx_,\ +HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\ hal_sd.c \ ll_sdmmc.c \ ll_fmc.c \ @@ -417,7 +383,7 @@ HAL_SRC_C += $(addprefix $(HAL_DIR)/Src/stm32$(MCU_SERIES)xx_,\ endif ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f4 f7 h7)) -HAL_SRC_C += $(addprefix $(HAL_DIR)/Src/stm32$(MCU_SERIES)xx_,\ +HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\ hal_mmc.c \ hal_sdram.c \ hal_dma_ex.c \ @@ -426,35 +392,35 @@ HAL_SRC_C += $(addprefix $(HAL_DIR)/Src/stm32$(MCU_SERIES)xx_,\ ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f4)) # HAL F4-1.16.0 has a bug with missing parentheses in HAL_MMC_Erase. # This function is unused so let the error go by as a warning. -$(BUILD)/$(HAL_DIR)/Src/stm32$(MCU_SERIES)xx_hal_mmc.o: CFLAGS += -Wno-error=parentheses +$(BUILD)/$(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_hal_mmc.o: CFLAGS += -Wno-error=parentheses endif endif ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),g4)) - HAL_SRC_C += $(addprefix $(HAL_DIR)/Src/stm32$(MCU_SERIES)xx_, hal_fdcan.c) + HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_, hal_fdcan.c) endif ifeq ($(CMSIS_MCU),$(filter $(CMSIS_MCU),STM32H743xx STM32H750xx STM32H7A3xx STM32H7A3xxQ STM32H7B3xx STM32H7B3xxQ)) - HAL_SRC_C += $(addprefix $(HAL_DIR)/Src/stm32$(MCU_SERIES)xx_, hal_fdcan.c) + HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_, hal_fdcan.c) else ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f0 f4 f7 h7)) - HAL_SRC_C += $(addprefix $(HAL_DIR)/Src/stm32$(MCU_SERIES)xx_, hal_can.c) + HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_, hal_can.c) else ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),l4)) -HAL_SRC_C += $(addprefix $(HAL_DIR)/Src/Legacy/stm32$(MCU_SERIES)xx_, hal_can.c) -$(BUILD)/$(HAL_DIR)/Src/Legacy/stm32$(MCU_SERIES)xx_hal_can.o: CFLAGS += -Wno-error=cpp +HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/Legacy/stm32$(MCU_SERIES)xx_, hal_can.c) +$(BUILD)/$(STM32LIB_HAL_BASE)/Src/Legacy/stm32$(MCU_SERIES)xx_hal_can.o: CFLAGS += -Wno-error=cpp endif endif endif ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f0 f4 f7 l0)) -HAL_SRC_C += $(addprefix $(HAL_DIR)/Src/stm32$(MCU_SERIES)xx_,\ +HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\ hal_i2s.c \ ) endif ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f4)) -HAL_SRC_C += $(addprefix $(HAL_DIR)/Src/stm32$(MCU_SERIES)xx_,\ +HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\ hal_i2s_ex.c \ ) endif @@ -778,7 +744,7 @@ GEN_PLLFREQTABLE_HDR = $(HEADER_BUILD)/pllfreqtable.h GEN_STMCONST_HDR = $(HEADER_BUILD)/modstm_const.h GEN_STMCONST_QSTR = $(BUILD)/modstm_qstr.h GEN_STMCONST_MPZ = $(HEADER_BUILD)/modstm_mpz.h -CMSIS_MCU_HDR = $(CMSIS_DIR)/$(CMSIS_MCU_LOWER).h +CMSIS_MCU_HDR = $(STM32LIB_CMSIS_ABS)/Include/$(CMSIS_MCU_LOWER).h modmachine.c: $(GEN_PLLFREQTABLE_HDR) $(GEN_PLLFREQTABLE_HDR): $(PLLVALUES) | $(HEADER_BUILD) diff --git a/ports/stm32/boards/NUCLEO_WB55/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_WB55/mpconfigboard.mk index dcec788ed4..349ce46d79 100644 --- a/ports/stm32/boards/NUCLEO_WB55/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_WB55/mpconfigboard.mk @@ -1,7 +1,7 @@ MCU_SERIES = wb CMSIS_MCU = STM32WB55xx AF_FILE = boards/stm32wb55_af.csv -STARTUP_FILE = lib/stm32lib/CMSIS/STM32WBxx/Source/Templates/gcc/startup_stm32wb55xx_cm4.o +STARTUP_FILE = $(STM32LIB_CMSIS_BASE)/Source/Templates/gcc/startup_stm32wb55xx_cm4.o ifeq ($(USE_MBOOT),1) # When using Mboot all the text goes together after the bootloader diff --git a/ports/stm32/boards/USBDONGLE_WB55/mpconfigboard.mk b/ports/stm32/boards/USBDONGLE_WB55/mpconfigboard.mk index dcec788ed4..349ce46d79 100644 --- a/ports/stm32/boards/USBDONGLE_WB55/mpconfigboard.mk +++ b/ports/stm32/boards/USBDONGLE_WB55/mpconfigboard.mk @@ -1,7 +1,7 @@ MCU_SERIES = wb CMSIS_MCU = STM32WB55xx AF_FILE = boards/stm32wb55_af.csv -STARTUP_FILE = lib/stm32lib/CMSIS/STM32WBxx/Source/Templates/gcc/startup_stm32wb55xx_cm4.o +STARTUP_FILE = $(STM32LIB_CMSIS_BASE)/Source/Templates/gcc/startup_stm32wb55xx_cm4.o ifeq ($(USE_MBOOT),1) # When using Mboot all the text goes together after the bootloader diff --git a/ports/stm32/mboot/Makefile b/ports/stm32/mboot/Makefile index 20b0db9070..ee75fb2afe 100755 --- a/ports/stm32/mboot/Makefile +++ b/ports/stm32/mboot/Makefile @@ -32,11 +32,6 @@ include $(BOARD_DIR)/mpconfigboard.mk # A board can set MBOOT_TEXT0_ADDR to a custom location where mboot should reside. MBOOT_TEXT0_ADDR ?= 0x08000000 -MCU_SERIES_UPPER = $(shell echo $(MCU_SERIES) | tr '[:lower:]' '[:upper:]') -CMSIS_MCU_LOWER = $(shell echo $(CMSIS_MCU) | tr '[:upper:]' '[:lower:]') - -CMSIS_DIR=$(TOP)/lib/stm32lib/CMSIS/STM32$(MCU_SERIES_UPPER)xx/Include -HAL_DIR=lib/stm32lib/STM32$(MCU_SERIES_UPPER)xx_HAL_Driver USBDEV_DIR=usbdev DFU=$(TOP)/tools/dfu.py PYDFU ?= $(TOP)/tools/pydfu.py @@ -45,8 +40,8 @@ BOOTLOADER_DFU_USB_PID ?= 0xDF11 STFLASH ?= st-flash OPENOCD ?= openocd OPENOCD_CONFIG ?= boards/openocd_stm32f4.cfg -STARTUP_FILE ?= lib/stm32lib/CMSIS/STM32$(MCU_SERIES_UPPER)xx/Source/Templates/gcc/startup_$(CMSIS_MCU_LOWER).o -SYSTEM_FILE ?= lib/stm32lib/CMSIS/STM32$(MCU_SERIES_UPPER)xx/Source/Templates/system_stm32$(MCU_SERIES)xx.o + +include ../stm32.mk CROSS_COMPILE ?= arm-none-eabi- @@ -55,20 +50,10 @@ INC += -I.. INC += -I$(TOP) INC += -I$(BUILD) INC += -I$(TOP)/lib/cmsis/inc -INC += -I$(CMSIS_DIR)/ -INC += -I$(TOP)/$(HAL_DIR)/Inc +INC += -I$(STM32LIB_CMSIS_ABS)/Include +INC += -I$(STM32LIB_HAL_ABS)/Inc INC += -I../$(USBDEV_DIR)/core/inc -I../$(USBDEV_DIR)/class/inc -# Basic Cortex-M flags -CFLAGS_CORTEX_M = -mthumb - -# Options for particular MCU series -CFLAGS_MCU_f4 = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4 -CFLAGS_MCU_f7 = $(CFLAGS_CORTEX_M) -mtune=cortex-m7 -mcpu=cortex-m7 -CFLAGS_MCU_h7 = $(CFLAGS_CORTEX_M) -mtune=cortex-m7 -mcpu=cortex-m7 -CFLAGS_MCU_l4 = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4 -CFLAGS_MCU_wb = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4 - # Standard C functions like memset need to be compiled with special flags so # the compiler does not optimise these functions in terms of themselves. CFLAGS_BUILTIN ?= -ffreestanding -fno-builtin -fno-lto @@ -154,8 +139,8 @@ CFLAGS += -DMBOOT_PACK_CHUNKSIZE=$(MBOOT_PACK_CHUNKSIZE) CFLAGS += -DMBOOT_PACK_KEYS_FILE=\"$(MBOOT_PACK_KEYS_FILE)\" endif -$(BUILD)/$(HAL_DIR)/Src/stm32$(MCU_SERIES)xx_ll_usb.o: CFLAGS += -Wno-attributes -SRC_HAL += $(addprefix $(HAL_DIR)/Src/stm32$(MCU_SERIES)xx_,\ +$(BUILD)/$(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_ll_usb.o: CFLAGS += -Wno-attributes +SRC_HAL += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\ hal_cortex.c \ hal_flash.c \ hal_flash_ex.c \ diff --git a/ports/stm32/stm32.mk b/ports/stm32/stm32.mk new file mode 100644 index 0000000000..f17351cdca --- /dev/null +++ b/ports/stm32/stm32.mk @@ -0,0 +1,68 @@ +# +# Makefile fragment for STM32 MCUs. +# + +# Create variables for the MCU name. +MCU_SERIES_UPPER = $(shell echo $(MCU_SERIES) | tr '[:lower:]' '[:upper:]') +CMSIS_MCU_LOWER = $(shell echo $(CMSIS_MCU) | tr '[:upper:]' '[:lower:]') + +# Configure location of stm32lib. Options are: +# 1. default: use provided lib/stm32lib +# 2. set STM32LIB_DIR to point to an stm32lib directory structure +# 3. set STM32LIB_CMSIS_DIR to point to xxx/CMSIS/Device/ST/STM32xx +# and STM32LIB_HAL_DIR to point to xxx/STM32xx_HAL_Driver +ifneq ($(STM32LIB_CMSIS_DIR),) +# Option 3 +STM32LIB_CMSIS_BASE = $(abspath $(STM32LIB_CMSIS_DIR)) +STM32LIB_CMSIS_ABS = $(STM32LIB_CMSIS_BASE) +STM32LIB_HAL_BASE = $(abspath $(STM32LIB_HAL_DIR)) +STM32LIB_HAL_ABS = $(STM32LIB_HAL_BASE) +else +ifneq ($(STM32LIB_DIR),) +# Option 2 +STM32LIB_VPATH = $(abspath $(STM32LIB_DIR)) +STM32LIB_FROM_HERE = $(STM32LIB_VPATH) +else +# Option 1 +STM32LIB_VPATH = lib/stm32lib +STM32LIB_FROM_HERE = $(TOP)/$(STM32LIB_VPATH) +endif +STM32LIB_CMSIS_BASE = $(STM32LIB_VPATH)/CMSIS/STM32$(MCU_SERIES_UPPER)xx +STM32LIB_CMSIS_ABS = $(STM32LIB_FROM_HERE)/CMSIS/STM32$(MCU_SERIES_UPPER)xx +STM32LIB_HAL_BASE = $(STM32LIB_VPATH)/STM32$(MCU_SERIES_UPPER)xx_HAL_Driver +STM32LIB_HAL_ABS = $(STM32LIB_FROM_HERE)/STM32$(MCU_SERIES_UPPER)xx_HAL_Driver +endif + +STARTUP_FILE ?= $(STM32LIB_CMSIS_BASE)/Source/Templates/gcc/startup_$(CMSIS_MCU_LOWER).o +SYSTEM_FILE ?= $(STM32LIB_CMSIS_BASE)/Source/Templates/system_stm32$(MCU_SERIES)xx.o + +# Basic Cortex-M flags. +CFLAGS_CORTEX_M = -mthumb + +ifneq ($(BUILDING_MBOOT),1) +# Select hardware floating-point support. +SUPPORTS_HARDWARE_FP_SINGLE = 0 +SUPPORTS_HARDWARE_FP_DOUBLE = 0 +ifeq ($(CMSIS_MCU),$(filter $(CMSIS_MCU),STM32F765xx STM32F767xx STM32F769xx STM32H743xx STM32H750xx STM32H7A3xx STM32H7A3xxQ STM32H7B3xx STM32H7B3xxQ)) +CFLAGS_CORTEX_M += -mfpu=fpv5-d16 -mfloat-abi=hard +SUPPORTS_HARDWARE_FP_SINGLE = 1 +SUPPORTS_HARDWARE_FP_DOUBLE = 1 +else +ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f0 l0)) +CFLAGS_CORTEX_M += -msoft-float +else +CFLAGS_CORTEX_M += -mfpu=fpv4-sp-d16 -mfloat-abi=hard +SUPPORTS_HARDWARE_FP_SINGLE = 1 +endif +endif +endif + +# Options for particular MCU series. +CFLAGS_MCU_f0 = $(CFLAGS_CORTEX_M) -mtune=cortex-m0 -mcpu=cortex-m0 +CFLAGS_MCU_f4 = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4 +CFLAGS_MCU_f7 = $(CFLAGS_CORTEX_M) -mtune=cortex-m7 -mcpu=cortex-m7 +CFLAGS_MCU_g4 = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4 +CFLAGS_MCU_l0 = $(CFLAGS_CORTEX_M) -mtune=cortex-m0plus -mcpu=cortex-m0plus +CFLAGS_MCU_l4 = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4 +CFLAGS_MCU_h7 = $(CFLAGS_CORTEX_M) -mtune=cortex-m7 -mcpu=cortex-m7 +CFLAGS_MCU_wb = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4 From e0a0719416d6a936ade8c30314b7e9c90bfbbf23 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 22 Feb 2021 11:55:12 +1100 Subject: [PATCH 084/619] stm32: Add initial support for STM32WL MCUs. Signed-off-by: Damien George --- ports/stm32/Makefile | 6 +- ports/stm32/adc.h | 2 + ports/stm32/boards/stm32wl55_af.csv | 45 +++++++++++ ports/stm32/boards/stm32wl55xc.ld | 34 +++++++++ ports/stm32/boards/stm32wlxx_hal_conf_base.h | 78 ++++++++++++++++++++ ports/stm32/dma.c | 12 +-- ports/stm32/dma.h | 2 +- ports/stm32/extint.c | 22 ++++-- ports/stm32/extint.h | 2 +- ports/stm32/flash.c | 8 +- ports/stm32/machine_adc.c | 24 ++++-- ports/stm32/machine_uart.c | 2 +- ports/stm32/modmachine.c | 2 +- ports/stm32/mpconfigboard_common.h | 10 +++ ports/stm32/mphalport.c | 2 +- ports/stm32/powerctrl.c | 25 +++++-- ports/stm32/powerctrlboot.c | 37 ++++++++++ ports/stm32/rtc.c | 21 ++++-- ports/stm32/stm32.mk | 3 +- ports/stm32/stm32_it.c | 2 +- ports/stm32/timer.c | 4 +- ports/stm32/uart.c | 33 +++++++-- ports/stm32/uart.h | 2 +- 23 files changed, 316 insertions(+), 62 deletions(-) create mode 100644 ports/stm32/boards/stm32wl55_af.csv create mode 100644 ports/stm32/boards/stm32wl55xc.ld create mode 100644 ports/stm32/boards/stm32wlxx_hal_conf_base.h diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index 4c90c6f1ef..dd86f1e3f3 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -353,8 +353,6 @@ HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\ hal_flash_ex.c \ hal_gpio.c \ hal_i2c.c \ - hal_pcd.c \ - hal_pcd_ex.c \ hal_pwr.c \ hal_pwr_ex.c \ hal_rcc.c \ @@ -368,8 +366,10 @@ HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\ ll_utils.c \ ) -ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f4 f7 h7 l0 l4 wb)) +ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f4 f7 g4 h7 l0 l4 wb)) HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\ + hal_pcd.c \ + hal_pcd_ex.c \ ll_usb.c \ ) endif diff --git a/ports/stm32/adc.h b/ports/stm32/adc.h index db706ea494..3b79dc01f5 100644 --- a/ports/stm32/adc.h +++ b/ports/stm32/adc.h @@ -56,6 +56,8 @@ static inline void adc_deselect_vbat(ADC_TypeDef *adc, uint32_t channel) { adc_common = adc == ADC3 ? ADC3_COMMON : ADC12_COMMON; #elif defined(STM32L4) adc_common = __LL_ADC_COMMON_INSTANCE(0); + #elif defined(STM32WL) + adc_common = ADC_COMMON; #endif adc_common->CCR &= ~LL_ADC_PATH_INTERNAL_VBAT; diff --git a/ports/stm32/boards/stm32wl55_af.csv b/ports/stm32/boards/stm32wl55_af.csv new file mode 100644 index 0000000000..219b8b08b7 --- /dev/null +++ b/ports/stm32/boards/stm32wl55_af.csv @@ -0,0 +1,45 @@ +Port,,AF0,AF1,AF2,AF3,AF4,AF5,AF6,AF7,AF8,AF9,AF10,AF11,AF12,AF13,AF14,AF15, +,,SYS_AF,TIM1/TIM2/LPTIM1,TIM1/TIM2,SPI2S2/TIM1/LPTIM3,I2C1/I2C2/I2C3,SPI1/SPI2S2,RF,USART1/USART2,LPUART1,,,,COMP1/COMP2/TIM1,DEBUG,TIM2/TIM16/TIM17/LPTIM2,EVENOUT,ADC +PortA,PA0,,TIM2_CH1,,,I2C3_SMBA,I2S_CKIN,,USART2_CTS,,,,,COMP1_OUT,DEBUG_PWR_REGLP1S,TIM2_ETR,EVENTOUT,ADC123_IN0 +PortA,PA1,,TIM2_CH2,,LPTIM3_OUT,I2C1_SMBA,SPI1_SCK,,USART2_RTS,LPUART1_RTS,,,,,DEBUG_PWR_REGLP2S,,EVENTOUT,ADC123_IN1 +PortA,PA2,LSCO,TIM2_CH3,,,,,,USART2_TX,LPUART1_TX,,,,COMP2_OUT,DEBUG_PWR_LDORDY,,EVENTOUT,ADC123_IN2 +PortA,PA3,,TIM2_CH4,,,,I2S2_MCK,,USART2_RX,LPUART1_RX,,,,,,,EVENTOUT,ADC123_IN3 +PortA,PA4,RTC_OUT2,LPTIM1_OUT,,,,SPI1_NSS,,USART2_CK,,,,,,DEBUG_SUBGHZSPI_NSSOUT,LPTIM2_OUT,EVENTOUT,ADC12_IN4 +PortA,PA5,,TIM2_CH1,TIM2_ETR,SPI2_MISO,,SPI1_SCK,,,,,,,,DEBUG_SUBGHZSPI_SCKOUT,LPTIM2_ETR,EVENTOUT,ADC12_IN5 +PortA,PA6,,TIM1_BKIN,,,I2C2_SMBA,SPI1_MISO,,,LPUART1_CTS,,,,TIM1_BKIN,DEBUG_SUBGHZSPI_MISOOUT,TIM16_CH1,EVENTOUT,ADC12_IN6 +PortA,PA7,,TIM1_CH1N,,,I2C3_SCL,SPI1_MOSI,,,,,,,COMP2_OUT,DEBUG_SUBGHZSPI_MOSIOUT,TIM17_CH1,EVENTOUT,ADC12_IN7 +PortA,PA8,MCO,TIM1_CH1,,,,SPI2_SCK/I2S2_CK,,USART1_CK,,,,,,,LPTIM2_OUT,EVENTOUT, +PortA,PA9,,TIM1_CH2,,SPI2_NSS/I2S2_WS,I2C1_SCL,SPI2_SCK/I2S2_CK,,USART1_TX,,,,,,,,EVENTOUT, +PortA,PA10,RTC_REFIN,TIM1_CH3,,,I2C1_SDA,SPI2_MOSI/I2S2_SD,,USART1_RX,,,,,,DEBUG_RF_HSE32RDY,TIM17_BKIN,EVENTOUT, +PortA,PA11,,TIM1_CH4,TIM1_BKIN2,LPTIM3_ETR,I2C2_SDA,SPI1_MISO,,USART1_CTS,,,,,TIM1_BKIN2,DEBUG_RF_NRESET,,EVENTOUT, +PortA,PA12,,TIM1_ETR,,LPTIM3_IN1,I2C2_SCL,SPI1_MOSI,RF_BUSY,USART1_RTS,,,,,,,,EVENTOUT, +PortA,PA13,JTMS/SWDIO,,,,I2C2_SMBA,,,,IR_OUT,,,,,,,EVENTOUT, +PortA,PA14,JTCK/SWCLK,LPTIM1_OUT,,,I2C1_SMBA,,,,,,,,,,,EVENTOUT, +PortA,PA15,JTDI,TIM2_CH1,TIM2_ETR,,I2C2_SDA,SPI1_NSS,,,,,,,,,,EVENTOUT, +PortB,PB0,,,,,,,,,,,,,COMP1_OUT,,,EVENTOUT,ADC12_IN8 +PortB,PB1,,,,,,,,,LPUART1_RTS_DE,,,,,,LPTIM2_IN1,EVENTOUT,ADC12_IN9 +PortB,PB2,,LPTIM1_OUT,,,I2C3_SMBA,SPI1_NSS,,,,,,,,DEBUG_RF_SMPSRDY,,EVENTOUT, +PortB,PB3,JTDO/TRACESWO,TIM2_CH2,,,,SPI1_SCK,RF_IRQ0,USART1_RTS,,,,,,DEBUG_RF_DTB1,,EVENTOUT, +PortB,PB4,NJTRST,,,,I2C3_SDA,SPI1_MISO,,USART1_CTS,,,,,,DEBUG_RF_LDORDY,TIM17_BKIN,EVENTOUT, +PortB,PB5,,LPTIM1_IN1,,,I2C1_SMBA,SPI1_MOSI,RF_IRQ1,USART1_CK,,,,,COMP2_OUT,,TIM16_BKIN,EVENTOUT, +PortB,PB6,,LPTIM1_ETR,,,I2C1_SCL,,,USART1_TX,,,,,,,TIM16_CH1N,EVENTOUT, +PortB,PB7,,LPTIM1_IN2,,TIM1_BKIN,I2C1_SDA,,,USART1_RX,,,,,,,TIM17_CH1N,EVENTOUT, +PortB,PB8,,TIM1_CH2N,,,I2C1_SCL,,RF_IRQ2,,,,,,,,TIM16_CH1,EVENTOUT, +PortB,PB9,,TIM1_CH3N,,,I2C1_SDA,SPI2_NSS/I2S2_WS,,,IR_OUT,,,,,,TIM17_CH1,EVENTOUT, +PortB,PB10,,TIM2_CH3,,,I2C3_SCL,SPI2_SCK/I2S2_CK,,,LPUART1_RX,,,,COMP1_OUT,,,EVENTOUT, +PortB,PB11,,TIM2_CH4,,,I2C3_SDA,,,,LPUART1_TX,,,,COMP2_OUT,,,EVENTOUT, +PortB,PB12,,TIM1_BKIN,,TIM1_BKIN,I2C3_SMBA,SPI2_NSS/I2S2_WS,,,LPUART1_RTS,,,,,,,EVENTOUT, +PortB,PB13,,TIM1_CH1N,,,I2C3_SCL,SPI2_SCK/I2S2_CK,,,LPUART1_CTS,,,,,,,EVENTOUT, +PortB,PB14,,TIM1_CH2N,,I2S2_MCK,I2C3_SDA,SPI2_MISO,,,,,,,,,,EVENTOUT, +PortB,PB15,,TIM1_CH3N,,,I2C2_SCL,SPI2_MOSI/I2S2_SD,,,,,,,,,,EVENTOUT, +PortC,PC0,,LPTIM1_IN1,,,I2C3_SCL,,,,LPUART1_RX,,,,,,LPTIM2_IN1,EVENTOUT,ADC123_IN10 +PortC,PC1,,LPTIM1_OUT,,SPI2_MOSI/I2S2_SD,I2C3_SDA,,,,LPUART1_TX,,,,,,,EVENTOUT,ADC123_IN11 +PortC,PC2,,LPTIM1_IN2,,,,SPI2_MISO,,,,,,,,,,EVENTOUT,ADC123_IN12 +PortC,PC3,,LPTIM1_ETR,,,,SPI2_MOSI/I2S2_SD,,,,,,,,,LPTIM2_ETR,EVENTOUT,ADC123_IN13 +PortC,PC4,,,,,,,,,,,,,,,,EVENTOUT,ADC12_IN14 +PortC,PC5,,,,,,,,,,,,,,,,EVENTOUT,ADC12_IN15 +PortC,PC6,,,,,,I2S2_MCK,,,,,,,,,,EVENTOUT, +PortC,PC13,,,,,,,,,,,,,,,,EVENTOUT, +PortC,PC14,,,,,,,,,,,,,,,,EVENTOUT, +PortC,PC15,,,,,,,,,,,,,,,,EVENTOUT, +PortH,PH3,,,,,,,,,,,,,,,,EVENTOUT, diff --git a/ports/stm32/boards/stm32wl55xc.ld b/ports/stm32/boards/stm32wl55xc.ld new file mode 100644 index 0000000000..109675bb5e --- /dev/null +++ b/ports/stm32/boards/stm32wl55xc.ld @@ -0,0 +1,34 @@ +/* + GNU linker script for STM32WL55xC +*/ + +/* Specify the memory areas */ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 256K /* sectors 0-127 */ + FLASH_APP (rx) : ORIGIN = 0x08000000, LENGTH = 232K /* sectors 0-115 */ + FLASH_FS (r) : ORIGIN = 0x0803a000, LENGTH = 24K /* sectors 116-127 */ + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 64K /* SRAM1+SRAM2 */ +} + +/* produce a link error if there is not this amount of RAM for these sections */ +_minimum_stack_size = 2K; +_minimum_heap_size = 16K; + +/* RAM extents for the garbage collector */ +_ram_start = ORIGIN(RAM); +_ram_end = ORIGIN(RAM) + LENGTH(RAM); + +_micropy_hw_internal_flash_storage_ram_cache_end = ORIGIN(RAM) + LENGTH(RAM); +_micropy_hw_internal_flash_storage_ram_cache_start = _micropy_hw_internal_flash_storage_ram_cache_end - 2K; /* fs cache = 2K */ + +/* Define the stack. The stack is full descending so begins at the bottom of FS cache. + Note that EABI requires the stack to be 8-byte aligned for a call. */ +_estack = _micropy_hw_internal_flash_storage_ram_cache_start - _estack_reserve; +_sstack = _estack - 6K; + +_heap_start = _ebss; /* heap starts just after statically allocated memory */ +_heap_end = _sstack; + +_micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); +_micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); diff --git a/ports/stm32/boards/stm32wlxx_hal_conf_base.h b/ports/stm32/boards/stm32wlxx_hal_conf_base.h new file mode 100644 index 0000000000..e98b0525bd --- /dev/null +++ b/ports/stm32/boards/stm32wlxx_hal_conf_base.h @@ -0,0 +1,78 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MICROPY_INCLUDED_STM32WLXX_HAL_CONF_BASE_H +#define MICROPY_INCLUDED_STM32WLXX_HAL_CONF_BASE_H + +// Include various HAL modules for convenience +#include "stm32wlxx_hal_dma.h" +#include "stm32wlxx_hal_adc.h" +#include "stm32wlxx_hal_cortex.h" +#include "stm32wlxx_hal_flash.h" +#include "stm32wlxx_hal_gpio.h" +#include "stm32wlxx_hal_i2c.h" +#include "stm32wlxx_hal_pwr.h" +#include "stm32wlxx_hal_rcc.h" +#include "stm32wlxx_hal_rtc.h" +#include "stm32wlxx_hal_spi.h" +#include "stm32wlxx_hal_tim.h" +#include "stm32wlxx_hal_uart.h" +#include "stm32wlxx_hal_usart.h" +#include "stm32wlxx_ll_adc.h" +#include "stm32wlxx_ll_lpuart.h" +#include "stm32wlxx_ll_rtc.h" +#include "stm32wlxx_ll_usart.h" + +// Enable various HAL modules +#define HAL_MODULE_ENABLED +#define HAL_CORTEX_MODULE_ENABLED +#define HAL_DMA_MODULE_ENABLED +#define HAL_FLASH_MODULE_ENABLED +#define HAL_GPIO_MODULE_ENABLED +#define HAL_PWR_MODULE_ENABLED +#define HAL_RCC_MODULE_ENABLED +#define HAL_RTC_MODULE_ENABLED +#define HAL_SPI_MODULE_ENABLED +#define HAL_TIM_MODULE_ENABLED +#define HAL_UART_MODULE_ENABLED +#define HAL_USART_MODULE_ENABLED + +// Oscillator values in Hz +#define MSI_VALUE (4000000) + +// SysTick has the highest priority +#define TICK_INT_PRIORITY (0x00) + +// Miscellaneous HAL settings +#define DATA_CACHE_ENABLE 1 +#define INSTRUCTION_CACHE_ENABLE 1 +#define PREFETCH_ENABLE 0 +#define USE_SPI_CRC 0 +#define USE_RTOS 0 + +// HAL parameter assertions are disabled +#define assert_param(expr) ((void)0) + +#endif // MICROPY_INCLUDED_STM32WLXX_HAL_CONF_BASE_H diff --git a/ports/stm32/dma.c b/ports/stm32/dma.c index dc706e68aa..75c34325d7 100644 --- a/ports/stm32/dma.c +++ b/ports/stm32/dma.c @@ -74,7 +74,7 @@ typedef union { struct _dma_descr_t { #if defined(STM32F4) || defined(STM32F7) || defined(STM32H7) DMA_Stream_TypeDef *instance; - #elif defined(STM32F0) || defined(STM32G4) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) + #elif defined(STM32F0) || defined(STM32G4) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL) DMA_Channel_TypeDef *instance; #else #error "Unsupported Processor" @@ -89,7 +89,7 @@ struct _dma_descr_t { static const DMA_InitTypeDef dma_init_struct_spi_i2c = { #if defined(STM32F4) || defined(STM32F7) .Channel = 0, - #elif defined(STM32G4) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) + #elif defined(STM32G4) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL) .Request = 0, #endif .Direction = 0, @@ -467,7 +467,7 @@ static const uint8_t dma_irqn[NSTREAM] = { DMA2_Channel7_IRQn, }; -#elif defined(STM32WB) +#elif defined(STM32WB) || defined(STM32WL) #define NCONTROLLERS (2) #define NSTREAMS_PER_CONTROLLER (7) @@ -1152,7 +1152,7 @@ void dma_init_handle(DMA_HandleTypeDef *dma, const dma_descr_t *dma_descr, uint3 dma->Instance = dma_descr->instance; dma->Init = *dma_descr->init; dma->Init.Direction = dir; - #if defined(STM32G4) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) + #if defined(STM32G4) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL) dma->Init.Request = dma_descr->sub_instance; #else #if !defined(STM32F0) @@ -1179,7 +1179,7 @@ void dma_init(DMA_HandleTypeDef *dma, const dma_descr_t *dma_descr, uint32_t dir dma_enable_clock(dma_id); - #if defined(STM32G4) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) + #if defined(STM32G4) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL) // Always reset and configure the H7 and L0/L4 DMA peripheral // (dma->State is set to HAL_DMA_STATE_RESET by memset above) // TODO: understand how L0/L4 DMA works so this is not needed @@ -1353,7 +1353,7 @@ void dma_nohal_start(const dma_descr_t *descr, uint32_t src_addr, uint32_t dst_a dma->CCR |= DMA_CCR_EN; } -#elif defined(STM32WB) +#elif defined(STM32WB) || defined(STM32WL) // These functions are currently not implemented or needed for this MCU. diff --git a/ports/stm32/dma.h b/ports/stm32/dma.h index 29d5c3d2eb..9b0b4c77a6 100644 --- a/ports/stm32/dma.h +++ b/ports/stm32/dma.h @@ -100,7 +100,7 @@ extern const dma_descr_t dma_I2C_2_RX; extern const dma_descr_t dma_I2C_1_TX; extern const dma_descr_t dma_I2C_1_RX; -#elif defined(STM32L4) || defined(STM32WB) +#elif defined(STM32L4) || defined(STM32WB) || defined(STM32WL) extern const dma_descr_t dma_ADC_1_RX; extern const dma_descr_t dma_ADC_2_RX; diff --git a/ports/stm32/extint.c b/ports/stm32/extint.c index bb26b00dde..55c9095f11 100644 --- a/ports/stm32/extint.c +++ b/ports/stm32/extint.c @@ -91,7 +91,7 @@ #define EXTI_SWIER_BB(line) (*(__IO uint32_t *)(PERIPH_BB_BASE + ((EXTI_OFFSET + offsetof(EXTI_TypeDef, SWIER)) * 32) + ((line) * 4))) #endif -#if defined(STM32G4) || defined(STM32L4) || defined(STM32WB) +#if defined(STM32G4) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL) // The L4 MCU supports 40 Events/IRQs lines of the type configurable and direct. // Here we only support configurable line types. Details, see page 330 of RM0351, Rev 1. // The USB_FS_WAKUP event is a direct type and there is no support for it. @@ -181,6 +181,12 @@ STATIC const uint8_t nvic_irq_channel[EXTI_NUM_VECTORS] = { RTC_Alarm_IRQn, TAMP_STAMP_LSECSS_IRQn, RTC_WKUP_IRQn, + #elif defined(STM32WL) + PVD_PVM_IRQn, + RTC_Alarm_IRQn, + TAMP_STAMP_LSECSS_SSRU_IRQn, // SSRU + TAMP_STAMP_LSECSS_SSRU_IRQn, // TAMP, RTC_STAMP, LSE_CSS + RTC_WKUP_IRQn, #else #if defined(STM32G4) || defined(STM32L4) PVD_PVM_IRQn, @@ -308,7 +314,7 @@ void extint_register_pin(const pin_obj_t *pin, uint32_t mode, bool hard_irq, mp_ pyb_extint_callback_arg[line] = MP_OBJ_FROM_PTR(pin); // Route the GPIO to EXTI - #if !defined(STM32WB) + #if !defined(STM32WB) && !defined(STM32WL) __HAL_RCC_SYSCFG_CLK_ENABLE(); #endif SYSCFG->EXTICR[line >> 2] = @@ -345,7 +351,7 @@ void extint_set(const pin_obj_t *pin, uint32_t mode) { pyb_extint_callback_arg[line] = MP_OBJ_FROM_PTR(pin); // Route the GPIO to EXTI - #if !defined(STM32WB) + #if !defined(STM32WB) && !defined(STM32WL) __HAL_RCC_SYSCFG_CLK_ENABLE(); #endif SYSCFG->EXTICR[line >> 2] = @@ -385,7 +391,7 @@ void extint_enable(uint line) { if (pyb_extint_mode[line] == EXTI_Mode_Interrupt) { #if defined(STM32H7) EXTI_D1->IMR1 |= (1 << line); - #elif defined(STM32G4) || defined(STM32WB) + #elif defined(STM32G4) || defined(STM32WB) || defined(STM32WL) EXTI->IMR1 |= (1 << line); #else EXTI->IMR |= (1 << line); @@ -393,7 +399,7 @@ void extint_enable(uint line) { } else { #if defined(STM32H7) EXTI_D1->EMR1 |= (1 << line); - #elif defined(STM32G4) || defined(STM32WB) + #elif defined(STM32G4) || defined(STM32WB) || defined(STM32WL) EXTI->EMR1 |= (1 << line); #else EXTI->EMR |= (1 << line); @@ -419,7 +425,7 @@ void extint_disable(uint line) { #if defined(STM32H7) EXTI_D1->IMR1 &= ~(1 << line); EXTI_D1->EMR1 &= ~(1 << line); - #elif defined(STM32G4) || defined(STM32WB) + #elif defined(STM32G4) || defined(STM32WB) || defined(STM32WL) EXTI->IMR1 &= ~(1 << line); EXTI->EMR1 &= ~(1 << line); #else @@ -441,7 +447,7 @@ void extint_swint(uint line) { return; } // we need 0 to 1 transition to trigger the interrupt - #if defined(STM32G4) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB) + #if defined(STM32G4) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL) EXTI->SWIER1 &= ~(1 << line); EXTI->SWIER1 |= (1 << line); #else @@ -519,7 +525,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(extint_obj_swint_obj, extint_obj_swint); /// \classmethod regs() /// Dump the values of the EXTI registers. STATIC mp_obj_t extint_regs(void) { - #if defined(STM32G4) || defined(STM32L4) || defined(STM32WB) + #if defined(STM32G4) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL) printf("EXTI_IMR1 %08x\n", (unsigned int)EXTI->IMR1); printf("EXTI_IMR2 %08x\n", (unsigned int)EXTI->IMR2); printf("EXTI_EMR1 %08x\n", (unsigned int)EXTI->EMR1); diff --git a/ports/stm32/extint.h b/ports/stm32/extint.h index 0cdf5024a0..247852fa19 100644 --- a/ports/stm32/extint.h +++ b/ports/stm32/extint.h @@ -43,7 +43,7 @@ #endif #define EXTI_ETH_WAKEUP (19) #define EXTI_USB_OTG_HS_WAKEUP (20) -#if defined(STM32F0) || defined(STM32G4) || defined(STM32L4) +#if defined(STM32F0) || defined(STM32G4) || defined(STM32L4) || defined(STM32WL) #define EXTI_RTC_TIMESTAMP (19) #define EXTI_RTC_WAKEUP (20) #elif defined(STM32H7) || defined(STM32WB) diff --git a/ports/stm32/flash.c b/ports/stm32/flash.c index eeeab52049..0c4d2cf5b9 100644 --- a/ports/stm32/flash.c +++ b/ports/stm32/flash.c @@ -97,7 +97,7 @@ static const flash_layout_t flash_layout[] = { }; #endif -#elif defined(STM32G4) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) +#elif defined(STM32G4) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL) static const flash_layout_t flash_layout[] = { { (uint32_t)FLASH_BASE, (uint32_t)FLASH_PAGE_SIZE, 512 }, @@ -159,7 +159,7 @@ static uint32_t get_page(uint32_t addr) { } #endif -#elif (defined(STM32L4) && !defined(SYSCFG_MEMRMP_FB_MODE)) || defined(STM32WB) +#elif (defined(STM32L4) && !defined(SYSCFG_MEMRMP_FB_MODE)) || defined(STM32WB) || defined(STM32WL) static uint32_t get_page(uint32_t addr) { return (addr - FLASH_BASE) / FLASH_PAGE_SIZE; @@ -263,7 +263,7 @@ int flash_erase(uint32_t flash_dest, uint32_t num_word32) { EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES; EraseInitStruct.PageAddress = flash_dest; EraseInitStruct.NbPages = (4 * num_word32 + FLASH_PAGE_SIZE - 4) / FLASH_PAGE_SIZE; - #elif (defined(STM32L4) && !defined(SYSCFG_MEMRMP_FB_MODE)) || defined(STM32WB) + #elif (defined(STM32L4) && !defined(SYSCFG_MEMRMP_FB_MODE)) || defined(STM32WB) || defined(STM32WL) __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS); EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES; EraseInitStruct.Page = get_page(flash_dest); @@ -370,7 +370,7 @@ int flash_write(uint32_t flash_dest, const uint32_t *src, uint32_t num_word32) { HAL_StatusTypeDef status = HAL_OK; - #if defined(STM32G4) || defined(STM32L4) || defined(STM32WB) + #if defined(STM32G4) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL) // program the flash uint64 by uint64 for (int i = 0; i < num_word32 / 2; i++) { diff --git a/ports/stm32/machine_adc.c b/ports/stm32/machine_adc.c index 21b35cdcdb..819fd3414f 100644 --- a/ports/stm32/machine_adc.c +++ b/ports/stm32/machine_adc.c @@ -28,7 +28,7 @@ #include "py/mphal.h" #include "adc.h" -#if defined(STM32F0) || defined(STM32G4) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) +#if defined(STM32F0) || defined(STM32G4) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL) #define ADC_V2 (1) #else #define ADC_V2 (0) @@ -42,7 +42,7 @@ #define ADCx_COMMON __LL_ADC_COMMON_INSTANCE(0) #endif -#if defined(STM32F0) || defined(STM32L0) +#if defined(STM32F0) || defined(STM32L0) || defined(STM32WL) #define ADC_STAB_DELAY_US (1) #define ADC_TEMPSENSOR_DELAY_US (10) #elif defined(STM32G4) @@ -65,7 +65,7 @@ #elif defined(STM32H7) #define ADC_SAMPLETIME_DEFAULT ADC_SAMPLETIME_8CYCLES_5 #define ADC_SAMPLETIME_DEFAULT_INT ADC_SAMPLETIME_387CYCLES_5 -#elif defined(STM32L0) +#elif defined(STM32L0) || defined(STM32WL) #define ADC_SAMPLETIME_DEFAULT ADC_SAMPLETIME_12CYCLES_5 #define ADC_SAMPLETIME_DEFAULT_INT ADC_SAMPLETIME_160CYCLES_5 #elif defined(STM32L4) || defined(STM32WB) @@ -105,7 +105,7 @@ STATIC const uint8_t adc_cr_to_bits_table[] = {12, 10, 8, 6}; void adc_config(ADC_TypeDef *adc, uint32_t bits) { // Configure ADC clock source and enable ADC clock - #if defined(STM32L4) || defined(STM32WB) + #if defined(STM32L4) || defined(STM32WB) || defined(STM32WL) __HAL_RCC_ADC_CONFIG(RCC_ADCCLKSOURCE_SYSCLK); __HAL_RCC_ADC_CLK_ENABLE(); #else @@ -150,6 +150,8 @@ void adc_config(ADC_TypeDef *adc, uint32_t bits) { ADC1_COMMON->CCR = 0; // ADCPR=PCLK/2 #elif defined(STM32WB) ADC1_COMMON->CCR = 0 << ADC_CCR_PRESC_Pos | 0 << ADC_CCR_CKMODE_Pos; // PRESC=1, MODE=ASYNC + #elif defined(STM32WL) + ADC_COMMON->CCR = 0 << ADC_CCR_PRESC_Pos; // PRESC=1 #endif #if defined(STM32H7) || defined(STM32L4) || defined(STM32WB) @@ -158,7 +160,7 @@ void adc_config(ADC_TypeDef *adc, uint32_t bits) { } #endif - #if defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) + #if defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL) if (!(adc->CR & ADC_CR_ADVREGEN)) { adc->CR = ADC_CR_ADVREGEN; // enable VREG #if defined(STM32H7) @@ -172,7 +174,7 @@ void adc_config(ADC_TypeDef *adc, uint32_t bits) { #if ADC_V2 if (!(adc->CR & ADC_CR_ADEN)) { // ADC isn't enabled so calibrate it now - #if defined(STM32F0) || defined(STM32L0) + #if defined(STM32F0) || defined(STM32L0) || defined(STM32WL) LL_ADC_StartCalibration(adc); #elif defined(STM32G4) || defined(STM32L4) || defined(STM32WB) LL_ADC_StartCalibration(adc, LL_ADC_SINGLE_ENDED); @@ -235,7 +237,7 @@ void adc_config(ADC_TypeDef *adc, uint32_t bits) { } STATIC int adc_get_bits(ADC_TypeDef *adc) { - #if defined(STM32F0) || defined(STM32L0) + #if defined(STM32F0) || defined(STM32L0) || defined(STM32WL) uint32_t res = (adc->CFGR1 & ADC_CFGR1_RES) >> ADC_CFGR1_RES_Pos; #elif defined(STM32F4) || defined(STM32F7) uint32_t res = (adc->CR1 & ADC_CR1_RES) >> ADC_CR1_RES_Pos; @@ -409,7 +411,11 @@ STATIC mp_obj_t machine_adc_make_new(const mp_obj_type_t *type, size_t n_args, s uint32_t sample_time = ADC_SAMPLETIME_DEFAULT; ADC_TypeDef *adc; if (mp_obj_is_int(source)) { + #if defined(STM32WL) + adc = ADC; + #else adc = ADC1; + #endif channel = mp_obj_get_int(source); if (channel == ADC_CHANNEL_VREFINT #if defined(STM32G4) @@ -426,7 +432,11 @@ STATIC mp_obj_t machine_adc_make_new(const mp_obj_type_t *type, size_t n_args, s } else { const pin_obj_t *pin = pin_find(source); if (pin->adc_num & PIN_ADC1) { + #if defined(STM32WL) + adc = ADC; + #else adc = ADC1; + #endif #if defined(ADC2) } else if (pin->adc_num & PIN_ADC2) { adc = ADC2; diff --git a/ports/stm32/machine_uart.c b/ports/stm32/machine_uart.c index 6a12fad324..681939094b 100644 --- a/ports/stm32/machine_uart.c +++ b/ports/stm32/machine_uart.c @@ -459,7 +459,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_uart_readchar_obj, pyb_uart_readchar); // uart.sendbreak() STATIC mp_obj_t pyb_uart_sendbreak(mp_obj_t self_in) { pyb_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); - #if defined(STM32F0) || defined(STM32F7) || defined(STM32G4) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) + #if defined(STM32F0) || defined(STM32F7) || defined(STM32G4) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL) self->uartx->RQR = USART_RQR_SBKRQ; // write-only register #else self->uartx->CR1 |= USART_CR1_SBK; diff --git a/ports/stm32/modmachine.c b/ports/stm32/modmachine.c index f9026bc370..57b8ef5e6b 100644 --- a/ports/stm32/modmachine.c +++ b/ports/stm32/modmachine.c @@ -63,7 +63,7 @@ #define RCC_CSR_BORRSTF RCC_CSR_PORRSTF #endif -#if defined(STM32G4) || defined(STM32L4) || defined(STM32WB) +#if defined(STM32G4) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL) // L4 does not have a POR, so use BOR instead #define RCC_CSR_PORRSTF RCC_CSR_BORRSTF #endif diff --git a/ports/stm32/mpconfigboard_common.h b/ports/stm32/mpconfigboard_common.h index 17a4ad26e4..7c8662318d 100644 --- a/ports/stm32/mpconfigboard_common.h +++ b/ports/stm32/mpconfigboard_common.h @@ -404,6 +404,16 @@ #define MICROPY_HW_RFCORE_BLE_LL_ONLY (1) // use LL only, we provide the rest of the BLE stack #endif +// Configuration for STM32WL series +#elif defined(STM32WL) + +#define MP_HAL_UNIQUE_ID_ADDRESS (UID_BASE) +#define PYB_EXTI_NUM_VECTORS (21) // up to RTC_WKUP +#define MICROPY_HW_MAX_I2C (3) +#define MICROPY_HW_MAX_TIMER (17) +#define MICROPY_HW_MAX_UART (2) +#define MICROPY_HW_MAX_LPUART (1) + #else #error Unsupported MCU series #endif diff --git a/ports/stm32/mphalport.c b/ports/stm32/mphalport.c index 2b98a620ef..2025388413 100644 --- a/ports/stm32/mphalport.c +++ b/ports/stm32/mphalport.c @@ -99,7 +99,7 @@ void mp_hal_gpio_clock_enable(GPIO_TypeDef *gpio) { #elif defined(STM32L0) #define AHBxENR IOPENR #define AHBxENR_GPIOAEN_Pos RCC_IOPENR_IOPAEN_Pos - #elif defined(STM32G4) || defined(STM32L4) || defined(STM32WB) + #elif defined(STM32G4) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL) #define AHBxENR AHB2ENR #define AHBxENR_GPIOAEN_Pos RCC_AHB2ENR_GPIOAEN_Pos #endif diff --git a/ports/stm32/powerctrl.c b/ports/stm32/powerctrl.c index ad3a3ea4c2..a2e3b5c71b 100644 --- a/ports/stm32/powerctrl.c +++ b/ports/stm32/powerctrl.c @@ -151,7 +151,7 @@ void powerctrl_check_enter_bootloader(void) { #endif } -#if !defined(STM32F0) && !defined(STM32L0) && !defined(STM32WB) +#if !defined(STM32F0) && !defined(STM32L0) && !defined(STM32WB) && !defined(STM32WL) typedef struct _sysclk_scaling_table_entry_t { uint16_t mhz; @@ -542,9 +542,15 @@ set_clk: return 0; } -#elif defined(STM32WB) +#elif defined(STM32WB) || defined(STM32WL) +#if defined(STM32WB) #include "stm32wbxx_ll_utils.h" +#define FLASH_LATENCY_MAX LL_FLASH_LATENCY_3 +#else +#include "stm32wlxx_ll_utils.h" +#define FLASH_LATENCY_MAX LL_FLASH_LATENCY_2 +#endif #define LPR_THRESHOLD (2000000) #define VOS2_THRESHOLD (16000000) @@ -605,8 +611,8 @@ int powerctrl_set_sysclk(uint32_t sysclk, uint32_t ahb, uint32_t apb1, uint32_t } else if (sysclk_mode == SYSCLK_MODE_MSI) { // Set flash latency to maximum to ensure the latency is large enough for // both the current SYSCLK and the SYSCLK that will be selected below. - LL_FLASH_SetLatency(LL_FLASH_LATENCY_3); - while (LL_FLASH_GetLatency() != LL_FLASH_LATENCY_3) { + LL_FLASH_SetLatency(FLASH_LATENCY_MAX); + while (LL_FLASH_GetLatency() != FLASH_LATENCY_MAX) { } // Before changing the MSIRANGE value, if MSI is on then it must also be ready. @@ -686,7 +692,7 @@ void powerctrl_enter_stop_mode(void) { __HAL_RCC_WAKEUPSTOP_CLK_CONFIG(RCC_STOP_WAKEUPCLOCK_MSI); #endif - #if !defined(STM32F0) && !defined(STM32G4) && !defined(STM32L0) && !defined(STM32L4) && !defined(STM32WB) + #if !defined(STM32F0) && !defined(STM32G4) && !defined(STM32L0) && !defined(STM32L4) && !defined(STM32WB) && !defined(STM32WL) // takes longer to wake but reduces stop current HAL_PWREx_EnableFlashPowerDown(); #endif @@ -765,7 +771,7 @@ void powerctrl_enter_stop_mode(void) { #if defined(STM32H7) while (__HAL_RCC_GET_SYSCLK_SOURCE() != RCC_CFGR_SWS_PLL1) { } - #elif defined(STM32WB) + #elif defined(STM32WB) || defined(STM32WL) while (__HAL_RCC_GET_SYSCLK_SOURCE() != RCC_SYSCLKSOURCE_STATUS_PLLCLK) { } #else @@ -861,7 +867,7 @@ void powerctrl_enter_standby_mode(void) { #if defined(STM32F0) || defined(STM32L0) #define CR_BITS (RTC_CR_ALRAIE | RTC_CR_WUTIE | RTC_CR_TSIE) #define ISR_BITS (RTC_ISR_ALRAF | RTC_ISR_WUTF | RTC_ISR_TSF) - #elif defined(STM32G4) + #elif defined(STM32G4) || defined(STM32WL) #define CR_BITS (RTC_CR_ALRAIE | RTC_CR_ALRBIE | RTC_CR_WUTIE | RTC_CR_TSIE) #define ISR_BITS (RTC_MISR_ALRAMF | RTC_MISR_ALRBMF | RTC_MISR_WUTMF | RTC_MISR_TSMF) #elif defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ) @@ -885,7 +891,7 @@ void powerctrl_enter_standby_mode(void) { // clear RTC wake-up flags #if defined(SR_BITS) RTC->SR &= ~SR_BITS; - #elif defined(STM32G4) + #elif defined(STM32G4) || defined(STM32WL) RTC->MISR &= ~ISR_BITS; #else RTC->ISR &= ~ISR_BITS; @@ -907,6 +913,9 @@ void powerctrl_enter_standby_mode(void) { // clear all wake-up flags PWR->SCR |= PWR_SCR_CWUF5 | PWR_SCR_CWUF4 | PWR_SCR_CWUF3 | PWR_SCR_CWUF2 | PWR_SCR_CWUF1; // TODO + #elif defined(STM32WL) + // clear all wake-up flags + PWR->SCR |= PWR_SCR_CWUF3 | PWR_SCR_CWUF2 | PWR_SCR_CWUF1; #else // clear global wake-up flag PWR->CR |= PWR_CR_CWUF; diff --git a/ports/stm32/powerctrlboot.c b/ports/stm32/powerctrlboot.c index 41b56296e0..4ecd83e2c6 100644 --- a/ports/stm32/powerctrlboot.c +++ b/ports/stm32/powerctrlboot.c @@ -212,4 +212,41 @@ void SystemClock_Config(void) { powerctrl_config_systick(); } +#elif defined(STM32WL) + +#include "stm32wlxx_ll_utils.h" + +void SystemClock_Config(void) { + // Set flash latency + LL_FLASH_SetLatency(LL_FLASH_LATENCY_2); + while (LL_FLASH_GetLatency() != LL_FLASH_LATENCY_2) { + } + + LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1); + + // Enable MSI + LL_RCC_MSI_Enable(); + while (!LL_RCC_MSI_IsReady()) { + } + + // Configure MSI + LL_RCC_MSI_EnableRangeSelection(); + LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_11); + LL_RCC_MSI_SetCalibTrimming(0); + + // Select SYSCLK source + LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_MSI); + while (LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_MSI) { + } + + // Set bus dividers + LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1); + LL_RCC_SetAHB3Prescaler(LL_RCC_SYSCLK_DIV_1); + LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1); + LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1); + + SystemCoreClockUpdate(); + powerctrl_config_systick(); +} + #endif diff --git a/ports/stm32/rtc.c b/ports/stm32/rtc.c index 1164e71e08..e776f67bb9 100644 --- a/ports/stm32/rtc.c +++ b/ports/stm32/rtc.c @@ -94,6 +94,11 @@ STATIC bool rtc_need_init_finalise = false; #endif void rtc_init_start(bool force_init) { + #if defined(STM32WL) + // Enable the RTC APB bus clock, to communicate with the RTC. + __HAL_RCC_RTCAPB_CLK_ENABLE(); + #endif + RTCHandle.Instance = RTC; /* Configure RTC prescaler and RTC data registers */ @@ -220,7 +225,7 @@ void rtc_init_finalise() { // fresh reset; configure RTC Calendar RTC_CalendarConfig(); - #if defined(STM32G4) || defined(STM32L4) || defined(STM32WB) + #if defined(STM32G4) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL) if (__HAL_RCC_GET_FLAG(RCC_FLAG_BORRST) != RESET) { #else if (__HAL_RCC_GET_FLAG(RCC_FLAG_PORRST) != RESET) { @@ -252,7 +257,7 @@ STATIC HAL_StatusTypeDef PYB_RCC_OscConfig(RCC_OscInitTypeDef *RCC_OscInitStruct /*------------------------------ LSE Configuration -------------------------*/ if ((RCC_OscInitStruct->OscillatorType & RCC_OSCILLATORTYPE_LSE) == RCC_OSCILLATORTYPE_LSE) { - #if !defined(STM32H7) && !defined(STM32WB) + #if !defined(STM32H7) && !defined(STM32WB) && !defined(STM32WL) // Enable Power Clock __HAL_RCC_PWR_CLK_ENABLE(); #endif @@ -261,7 +266,7 @@ STATIC HAL_StatusTypeDef PYB_RCC_OscConfig(RCC_OscInitTypeDef *RCC_OscInitStruct HAL_PWR_EnableBkUpAccess(); uint32_t tickstart = HAL_GetTick(); - #if defined(STM32F7) || defined(STM32G4) || defined(STM32L4) || defined(STM32H7) || defined(STM32WB) + #if defined(STM32F7) || defined(STM32G4) || defined(STM32L4) || defined(STM32H7) || defined(STM32WB) || defined(STM32WL) // __HAL_RCC_PWR_CLK_ENABLE(); // Enable write access to Backup domain // PWR->CR1 |= PWR_CR1_DBP; @@ -349,7 +354,7 @@ STATIC HAL_StatusTypeDef PYB_RTC_Init(RTC_HandleTypeDef *hrtc) { #elif defined(STM32F7) hrtc->Instance->OR &= (uint32_t) ~RTC_OR_ALARMTYPE; hrtc->Instance->OR |= (uint32_t)(hrtc->Init.OutPutType); - #elif defined(STM32G4) + #elif defined(STM32G4) || defined(STM32WL) hrtc->Instance->CR &= (uint32_t) ~RTC_CR_TAMPALRM_TYPE_Msk; hrtc->Instance->CR |= (uint32_t)(hrtc->Init.OutPutType); #else @@ -714,7 +719,7 @@ mp_obj_t pyb_rtc_wakeup(size_t n_args, const mp_obj_t *args) { RTC->WPR = 0xff; // enable external interrupts on line EXTI_RTC_WAKEUP - #if defined(STM32G4) || defined(STM32L4) || defined(STM32WB) + #if defined(STM32G4) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL) EXTI->IMR1 |= 1 << EXTI_RTC_WAKEUP; EXTI->RTSR1 |= 1 << EXTI_RTC_WAKEUP; #elif defined(STM32H7) @@ -726,14 +731,14 @@ mp_obj_t pyb_rtc_wakeup(size_t n_args, const mp_obj_t *args) { #endif // clear interrupt flags - #if defined(STM32G4) + #if defined(STM32G4) || defined(STM32WL) RTC->ICSR &= ~RTC_ICSR_WUTWF; #elif defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ) RTC->SR &= ~RTC_SR_WUTF; #else RTC->ISR &= ~RTC_ISR_WUTF; #endif - #if defined(STM32G4) || defined(STM32L4) || defined(STM32WB) + #if defined(STM32G4) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL) EXTI->PR1 = 1 << EXTI_RTC_WAKEUP; #elif defined(STM32H7) EXTI_D1->PR1 = 1 << EXTI_RTC_WAKEUP; @@ -753,7 +758,7 @@ mp_obj_t pyb_rtc_wakeup(size_t n_args, const mp_obj_t *args) { RTC->WPR = 0xff; // disable external interrupts on line EXTI_RTC_WAKEUP - #if defined(STM32G4) || defined(STM32L4) || defined(STM32WB) + #if defined(STM32G4) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL) EXTI->IMR1 &= ~(1 << EXTI_RTC_WAKEUP); #elif defined(STM32H7) EXTI_D1->IMR1 |= 1 << EXTI_RTC_WAKEUP; diff --git a/ports/stm32/stm32.mk b/ports/stm32/stm32.mk index f17351cdca..4de101ce7a 100644 --- a/ports/stm32/stm32.mk +++ b/ports/stm32/stm32.mk @@ -48,7 +48,7 @@ CFLAGS_CORTEX_M += -mfpu=fpv5-d16 -mfloat-abi=hard SUPPORTS_HARDWARE_FP_SINGLE = 1 SUPPORTS_HARDWARE_FP_DOUBLE = 1 else -ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f0 l0)) +ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f0 l0 wl)) CFLAGS_CORTEX_M += -msoft-float else CFLAGS_CORTEX_M += -mfpu=fpv4-sp-d16 -mfloat-abi=hard @@ -66,3 +66,4 @@ CFLAGS_MCU_l0 = $(CFLAGS_CORTEX_M) -mtune=cortex-m0plus -mcpu=cortex-m0plus CFLAGS_MCU_l4 = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4 CFLAGS_MCU_h7 = $(CFLAGS_CORTEX_M) -mtune=cortex-m7 -mcpu=cortex-m7 CFLAGS_MCU_wb = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4 +CFLAGS_MCU_wl = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4 diff --git a/ports/stm32/stm32_it.c b/ports/stm32/stm32_it.c index 84274e3c62..39229a143b 100644 --- a/ports/stm32/stm32_it.c +++ b/ports/stm32/stm32_it.c @@ -529,7 +529,7 @@ void TAMP_STAMP_IRQHandler(void) { void RTC_WKUP_IRQHandler(void) { IRQ_ENTER(RTC_WKUP_IRQn); - #if defined(STM32G4) + #if defined(STM32G4) || defined(STM32WL) RTC->MISR &= ~RTC_MISR_WUTMF; // clear wakeup interrupt flag #elif defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ) RTC->SR &= ~RTC_SR_WUTF; // clear wakeup interrupt flag diff --git a/ports/stm32/timer.c b/ports/stm32/timer.c index 57c8dc6c7b..12f0515d4e 100644 --- a/ports/stm32/timer.c +++ b/ports/stm32/timer.c @@ -873,14 +873,14 @@ STATIC const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = { #endif #endif #if defined(TIM16) - #if defined(STM32F0) || defined(STM32H7) + #if defined(STM32F0) || defined(STM32H7) || defined(STM32WL) TIM_ENTRY(16, TIM16_IRQn), #else TIM_ENTRY(16, TIM1_UP_TIM16_IRQn), #endif #endif #if defined(TIM17) - #if defined(STM32F0) || defined(STM32H7) + #if defined(STM32F0) || defined(STM32H7) || defined(STM32WL) TIM_ENTRY(17, TIM17_IRQn), #else TIM_ENTRY(17, TIM1_TRG_COM_TIM17_IRQn), diff --git a/ports/stm32/uart.c b/ports/stm32/uart.c index 61e72999e2..13e1667d2c 100644 --- a/ports/stm32/uart.c +++ b/ports/stm32/uart.c @@ -41,16 +41,25 @@ #if defined(STM32F4) #define UART_RXNE_IS_SET(uart) ((uart)->SR & USART_SR_RXNE) #else -#if defined(STM32H7) +#if defined(STM32H7) || defined(STM32WL) #define USART_ISR_RXNE USART_ISR_RXNE_RXFNE #endif #define UART_RXNE_IS_SET(uart) ((uart)->ISR & USART_ISR_RXNE) #endif +#if defined(STM32WL) +#define UART_RXNE_IT_EN(uart) do { (uart)->CR1 |= USART_CR1_RXNEIE_RXFNEIE; } while (0) +#define UART_RXNE_IT_DIS(uart) do { (uart)->CR1 &= ~USART_CR1_RXNEIE_RXFNEIE; } while (0) +#else #define UART_RXNE_IT_EN(uart) do { (uart)->CR1 |= USART_CR1_RXNEIE; } while (0) #define UART_RXNE_IT_DIS(uart) do { (uart)->CR1 &= ~USART_CR1_RXNEIE; } while (0) +#endif +#if defined(STM32WL) +#define USART_CR1_IE_BASE (USART_CR1_PEIE | USART_CR1_TXEIE_TXFNFIE | USART_CR1_TCIE | USART_CR1_RXNEIE_RXFNEIE | USART_CR1_IDLEIE) +#else #define USART_CR1_IE_BASE (USART_CR1_PEIE | USART_CR1_TXEIE | USART_CR1_TCIE | USART_CR1_RXNEIE | USART_CR1_IDLEIE) +#endif #define USART_CR2_IE_BASE (USART_CR2_LBDIE) #define USART_CR3_IE_BASE (USART_CR3_CTSIE | USART_CR3_EIE) @@ -92,7 +101,7 @@ #define USART_CR2_IE_ALL (USART_CR2_IE_BASE) #define USART_CR3_IE_ALL (USART_CR3_IE_BASE | USART_CR3_WUFIE) -#elif defined(STM32L4) || defined(STM32WB) +#elif defined(STM32L4) || defined(STM32WB) || defined(STM32WL) #define USART_CR1_IE_ALL (USART_CR1_IE_BASE | USART_CR1_EOBIE | USART_CR1_RTOIE | USART_CR1_CMIE) #define USART_CR2_IE_ALL (USART_CR2_IE_BASE) #if defined(USART_CR3_TCBGTIE) @@ -113,9 +122,17 @@ typedef struct _pyb_uart_irq_map_t { STATIC const pyb_uart_irq_map_t mp_uart_irq_map[] = { { USART_CR1_IDLEIE, UART_FLAG_IDLE}, // RX idle { USART_CR1_PEIE, UART_FLAG_PE}, // parity error + #if defined(STM32WL) + { USART_CR1_TXEIE_TXFNFIE, UART_FLAG_TXE}, // TX register empty + #else { USART_CR1_TXEIE, UART_FLAG_TXE}, // TX register empty + #endif { USART_CR1_TCIE, UART_FLAG_TC}, // TX complete + #if defined(STM32WL) + { USART_CR1_RXNEIE_RXFNEIE, UART_FLAG_RXNE}, // RX register not empty + #else { USART_CR1_RXNEIE, UART_FLAG_RXNE}, // RX register not empty + #endif #if 0 // For now only IRQs selected by CR1 are supported #if defined(STM32F4) @@ -813,14 +830,14 @@ uint32_t uart_get_baudrate(pyb_uart_obj_t *self) { #if defined(LPUART1) if (self->uart_id == PYB_LPUART_1) { return LL_LPUART_GetBaudRate(self->uartx, uart_get_source_freq(self) - #if defined(STM32G4) || defined(STM32H7) || defined(STM32WB) + #if defined(STM32G4) || defined(STM32H7) || defined(STM32WB) || defined(STM32WL) , self->uartx->PRESC #endif ); } #endif return LL_USART_GetBaudRate(self->uartx, uart_get_source_freq(self), - #if defined(STM32G4) || defined(STM32H7) || defined(STM32WB) + #if defined(STM32G4) || defined(STM32H7) || defined(STM32WB) || defined(STM32WL) self->uartx->PRESC, #endif LL_USART_OVERSAMPLING_16); @@ -830,7 +847,7 @@ void uart_set_baudrate(pyb_uart_obj_t *self, uint32_t baudrate) { #if defined(LPUART1) if (self->uart_id == PYB_LPUART_1) { LL_LPUART_SetBaudRate(self->uartx, uart_get_source_freq(self), - #if defined(STM32G4) || defined(STM32H7) || defined(STM32WB) + #if defined(STM32G4) || defined(STM32H7) || defined(STM32WB) || defined(STM32WL) LL_LPUART_PRESCALER_DIV1, #endif baudrate); @@ -838,7 +855,7 @@ void uart_set_baudrate(pyb_uart_obj_t *self, uint32_t baudrate) { } #endif LL_USART_SetBaudRate(self->uartx, uart_get_source_freq(self), - #if defined(STM32G4) || defined(STM32H7) || defined(STM32WB) + #if defined(STM32G4) || defined(STM32H7) || defined(STM32WB) || defined(STM32WL) LL_USART_PRESCALER_DIV1, #endif LL_USART_OVERSAMPLING_16, baudrate); @@ -889,7 +906,7 @@ int uart_rx_char(pyb_uart_obj_t *self) { return data; } else { // no buffering - #if defined(STM32F0) || defined(STM32F7) || defined(STM32G4) || defined(STM32L0) || defined(STM32L4) || defined(STM32H7) || defined(STM32WB) + #if defined(STM32F0) || defined(STM32F7) || defined(STM32G4) || defined(STM32L0) || defined(STM32L4) || defined(STM32H7) || defined(STM32WB) || defined(STM32WL) int data = self->uartx->RDR & self->char_mask; self->uartx->ICR = USART_ICR_ORECF; // clear ORE if it was set return data; @@ -1039,7 +1056,7 @@ void uart_irq_handler(mp_uint_t uart_id) { uint16_t next_head = (self->read_buf_head + 1) % self->read_buf_len; if (next_head != self->read_buf_tail) { // only read data if room in buf - #if defined(STM32F0) || defined(STM32F7) || defined(STM32G4) || defined(STM32L0) || defined(STM32L4) || defined(STM32H7) || defined(STM32WB) + #if defined(STM32F0) || defined(STM32F7) || defined(STM32G4) || defined(STM32L0) || defined(STM32L4) || defined(STM32H7) || defined(STM32WB) || defined(STM32WL) int data = self->uartx->RDR; // clears UART_FLAG_RXNE #else self->mp_irq_flags = self->uartx->SR; // resample to get any new flags since next read of DR will clear SR diff --git a/ports/stm32/uart.h b/ports/stm32/uart.h index 0a268ad465..5fee841f8f 100644 --- a/ports/stm32/uart.h +++ b/ports/stm32/uart.h @@ -102,7 +102,7 @@ void uart_tx_strn(pyb_uart_obj_t *uart_obj, const char *str, uint len); static inline bool uart_tx_avail(pyb_uart_obj_t *self) { #if defined(STM32F4) return self->uartx->SR & USART_SR_TXE; - #elif defined(STM32H7) + #elif defined(STM32H7) || defined(STM32WL) return self->uartx->ISR & USART_ISR_TXE_TXFNF; #else return self->uartx->ISR & USART_ISR_TXE; From e306f2285ba497f4d12e4068149cb245036a99b9 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 22 Feb 2021 11:55:34 +1100 Subject: [PATCH 085/619] stm32/boards/NUCLEO_WL55: Add new board definition. Signed-off-by: Damien George --- ports/stm32/boards/NUCLEO_WL55/board.json | 15 +++++ .../stm32/boards/NUCLEO_WL55/mpconfigboard.h | 63 +++++++++++++++++++ .../stm32/boards/NUCLEO_WL55/mpconfigboard.mk | 13 ++++ ports/stm32/boards/NUCLEO_WL55/pins.csv | 46 ++++++++++++++ .../boards/NUCLEO_WL55/stm32wlxx_hal_conf.h | 18 ++++++ 5 files changed, 155 insertions(+) create mode 100644 ports/stm32/boards/NUCLEO_WL55/board.json create mode 100644 ports/stm32/boards/NUCLEO_WL55/mpconfigboard.h create mode 100644 ports/stm32/boards/NUCLEO_WL55/mpconfigboard.mk create mode 100644 ports/stm32/boards/NUCLEO_WL55/pins.csv create mode 100644 ports/stm32/boards/NUCLEO_WL55/stm32wlxx_hal_conf.h diff --git a/ports/stm32/boards/NUCLEO_WL55/board.json b/ports/stm32/boards/NUCLEO_WL55/board.json new file mode 100644 index 0000000000..d756a4cdbb --- /dev/null +++ b/ports/stm32/boards/NUCLEO_WL55/board.json @@ -0,0 +1,15 @@ +{ + "deploy": [ + "../deploy.md" + ], + "docs": "", + "features": [], + "images": [ + "nucleo_wl55.jpg" + ], + "mcu": "stm32wl", + "product": "Nucleo WL55", + "thumbnail": "", + "url": "", + "vendor": "ST Microelectronics" +} diff --git a/ports/stm32/boards/NUCLEO_WL55/mpconfigboard.h b/ports/stm32/boards/NUCLEO_WL55/mpconfigboard.h new file mode 100644 index 0000000000..baf1f023be --- /dev/null +++ b/ports/stm32/boards/NUCLEO_WL55/mpconfigboard.h @@ -0,0 +1,63 @@ +/* This file is part of the MicroPython project, https://micropython.org/ + * MIT License; Copyright (c) 2021 Damien P. George + */ + +#define MICROPY_HW_BOARD_NAME "NUCLEO-WL55" +#define MICROPY_HW_MCU_NAME "STM32WL55JCI7" + +#define MICROPY_EMIT_THUMB (0) +#define MICROPY_EMIT_INLINE_THUMB (0) +#define MICROPY_PY_BUILTINS_COMPLEX (0) +#define MICROPY_PY_GENERATOR_PEND_THROW (0) +#define MICROPY_PY_MATH (0) +#define MICROPY_PY_FRAMEBUF (0) +#define MICROPY_PY_USOCKET (0) +#define MICROPY_PY_NETWORK (0) +#define MICROPY_PY_ONEWIRE (0) +#define MICROPY_PY_STM (0) +#define MICROPY_PY_PYB_LEGACY (0) +#define MICROPY_PY_UHEAPQ (0) +#define MICROPY_PY_UTIMEQ (0) + +#define MICROPY_HW_HAS_FLASH (1) +#define MICROPY_HW_ENABLE_RTC (1) +#define MICROPY_HW_ENABLE_RNG (1) +#define MICROPY_HW_ENABLE_ADC (0) // use machine.ADC instead +#define MICROPY_HW_HAS_SWITCH (1) + +// There is an external 32kHz oscillator +#define MICROPY_HW_RTC_USE_LSE (1) +#define MICROPY_HW_RTC_USE_US (1) + +// UART buses +#define MICROPY_HW_UART1_TX (pin_B6) // Arduino D1, pin 7 on CN9 +#define MICROPY_HW_UART1_RX (pin_B7) // Arduino D0, pin 8 on CN9 +#define MICROPY_HW_LPUART1_TX (pin_A2) // ST-link +#define MICROPY_HW_LPUART1_RX (pin_A3) // ST-link +#define MICROPY_HW_UART_REPL PYB_LPUART_1 +#define MICROPY_HW_UART_REPL_BAUD 115200 + +// I2C buses +#define MICROPY_HW_I2C1_SCL (pin_A12) // Arduino D15, pin 10 on CN5 +#define MICROPY_HW_I2C1_SDA (pin_A11) // Arduino D14, pin 9 on CN5 +#define MICROPY_HW_I2C3_SCL (pin_B13) // Arduino A5, pin 6 on CN8 +#define MICROPY_HW_I2C3_SDA (pin_B14) // Arduino A4, pin 5 on CN8 + +// SPI buses +#define MICROPY_HW_SPI1_NSS (pin_A4) // Arduino D10 pin 3 on CN5 +#define MICROPY_HW_SPI1_SCK (pin_A5) // Arduino D13, pin 6 on CN5 +#define MICROPY_HW_SPI1_MISO (pin_A6) // Arduino D12, pin 5 on CN5 +#define MICROPY_HW_SPI1_MOSI (pin_A7) // Arduino D11, pin 4 on CN5 + +// User switch; pressing the button makes the input go low +#define MICROPY_HW_USRSW_PIN (pin_A0) +#define MICROPY_HW_USRSW_PULL (GPIO_PULLUP) +#define MICROPY_HW_USRSW_EXTI_MODE (GPIO_MODE_IT_FALLING) +#define MICROPY_HW_USRSW_PRESSED (0) + +// LEDs +#define MICROPY_HW_LED1 (pin_B15) // blue +#define MICROPY_HW_LED2 (pin_B9) // green +#define MICROPY_HW_LED3 (pin_B11) // red +#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_high(pin)) +#define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_low(pin)) diff --git a/ports/stm32/boards/NUCLEO_WL55/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_WL55/mpconfigboard.mk new file mode 100644 index 0000000000..210f3058c1 --- /dev/null +++ b/ports/stm32/boards/NUCLEO_WL55/mpconfigboard.mk @@ -0,0 +1,13 @@ +MCU_SERIES = wl +CMSIS_MCU = STM32WL55xx +AF_FILE = boards/stm32wl55_af.csv +STARTUP_FILE = $(STM32LIB_CMSIS_BASE)/Source/Templates/gcc/startup_stm32wl55xx_cm4.o +LD_FILES = boards/stm32wl55xc.ld boards/common_basic.ld +TEXT0_ADDR = 0x08000000 + +# MicroPython settings +MICROPY_VFS_FAT = 0 +MICROPY_VFS_LFS2 = 1 + +# Don't include default frozen modules because MCU is tight on flash space +FROZEN_MANIFEST ?= diff --git a/ports/stm32/boards/NUCLEO_WL55/pins.csv b/ports/stm32/boards/NUCLEO_WL55/pins.csv new file mode 100644 index 0000000000..afcf34df11 --- /dev/null +++ b/ports/stm32/boards/NUCLEO_WL55/pins.csv @@ -0,0 +1,46 @@ +,PA0 +,PA1 +,PA2 +,PA3 +,PA4 +,PA5 +,PA6 +,PA7 +,PA8 +,PA9 +,PA10 +,PA11 +,PA12 +,PA13 +,PA14 +,PA15 +,PB0 +,PB1 +,PB2 +,PB3 +,PB4 +,PB5 +,PB6 +,PB7 +,PB8 +,PB9 +,PB10 +,PB11 +,PB12 +,PB13 +,PB14 +,PB15 +,PC0 +,PC1 +,PC2 +,PC3 +,PC4 +,PC5 +,PC6 +SW,PA0 +SW1,PA0 +SW2,PA1 +SW3,PC6 +LED_GREEN,PB9 +LED_RED,PB11 +LED_BLUE,PB15 diff --git a/ports/stm32/boards/NUCLEO_WL55/stm32wlxx_hal_conf.h b/ports/stm32/boards/NUCLEO_WL55/stm32wlxx_hal_conf.h new file mode 100644 index 0000000000..2c28cc3d2a --- /dev/null +++ b/ports/stm32/boards/NUCLEO_WL55/stm32wlxx_hal_conf.h @@ -0,0 +1,18 @@ +/* This file is part of the MicroPython project, https://micropython.org/ + * The MIT License (MIT) + * Copyright (c) 2021 Damien P. George + */ +#ifndef MICROPY_INCLUDED_STM32WLXX_HAL_CONF_H +#define MICROPY_INCLUDED_STM32WLXX_HAL_CONF_H + +// Oscillator values in Hz +#define HSE_VALUE (32000000) +#define LSE_VALUE (32768) + +// Oscillator timeouts in ms +#define HSE_STARTUP_TIMEOUT (100) +#define LSE_STARTUP_TIMEOUT (5000) + +#include "boards/stm32wlxx_hal_conf_base.h" + +#endif // MICROPY_INCLUDED_STM32WLXX_HAL_CONF_H From ae5f647a2d294cf940367918fbb250f1a9c0c8c0 Mon Sep 17 00:00:00 2001 From: "Peter D. Gray" Date: Thu, 4 Jan 2018 12:10:15 -0500 Subject: [PATCH 086/619] stm32/system_stm32: Make SystemClock_Config() a weak symbol. This allows boards to override as needed. --- ports/stm32/system_stm32.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/stm32/system_stm32.c b/ports/stm32/system_stm32.c index 582d7a3698..2ee745a49b 100644 --- a/ports/stm32/system_stm32.c +++ b/ports/stm32/system_stm32.c @@ -163,7 +163,7 @@ void __fatal_error(const char *msg); * * Timers run from APBx if APBx_PRESC=1, else 2x APBx */ -void SystemClock_Config(void) { +MP_WEAK void SystemClock_Config(void) { #if defined(STM32F7) // The DFU bootloader changes the clocksource register from its default power // on reset value, so we set it back here, so the clocksources are the same From e5df4a96fa85ad4cf10d82b3cd5f8ca8b279050b Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Mon, 31 Jan 2022 19:12:48 +0200 Subject: [PATCH 087/619] stm32/adc: Fix L4 ADC channel numbers. Use HAL macro to map decimal numbers to channel numbers. This is needed since updating L4 HAL v1.17.0 in a0f5b3148a5c276aa1abf7b77b0964eec80cda16. Fixes issue #8233. --- ports/stm32/adc.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/ports/stm32/adc.c b/ports/stm32/adc.c index 2e52fb07aa..497bf5339f 100644 --- a/ports/stm32/adc.c +++ b/ports/stm32/adc.c @@ -214,11 +214,7 @@ STATIC bool is_adcx_channel(int channel) { #elif defined(STM32H7) return __HAL_ADC_IS_CHANNEL_INTERNAL(channel) || IS_ADC_CHANNEL(__HAL_ADC_DECIMAL_NB_TO_CHANNEL(channel)); - #elif defined(STM32L4) - ADC_HandleTypeDef handle; - handle.Instance = ADCx; - return IS_ADC_CHANNEL(&handle, channel); - #elif defined(STM32G4) || defined(STM32WB) + #elif defined(STM32G4) || defined(STM32L4) || defined(STM32WB) ADC_HandleTypeDef handle; handle.Instance = ADCx; return __HAL_ADC_IS_CHANNEL_INTERNAL(channel) @@ -338,7 +334,7 @@ STATIC void adc_init_single(pyb_obj_adc_t *adc_obj) { STATIC void adc_config_channel(ADC_HandleTypeDef *adc_handle, uint32_t channel) { ADC_ChannelConfTypeDef sConfig; - #if defined(STM32G4) || defined(STM32H7) || defined(STM32WB) + #if defined(STM32G4) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB) sConfig.Rank = ADC_REGULAR_RANK_1; if (__HAL_ADC_IS_CHANNEL_INTERNAL(channel) == 0) { channel = __HAL_ADC_DECIMAL_NB_TO_CHANNEL(channel); From 31f2440388be597ba39679e165676e4ef1b0a98c Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Tue, 1 Feb 2022 15:34:57 +0200 Subject: [PATCH 088/619] stm32/adc: Remove obsolete FIRST/LAST GPIO channel macros. --- ports/stm32/adc.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/ports/stm32/adc.c b/ports/stm32/adc.c index 497bf5339f..1d4beafa45 100644 --- a/ports/stm32/adc.c +++ b/ports/stm32/adc.c @@ -104,8 +104,6 @@ #elif defined(STM32G4) -#define ADC_FIRST_GPIO_CHANNEL (0) -#define ADC_LAST_GPIO_CHANNEL (18) #define ADC_SCALE_V (((float)VREFINT_CAL_VREF) / 1000.0f) #define ADC_CAL_ADDRESS VREFINT_CAL_ADDR #define ADC_CAL1 TEMPSENSOR_CAL1_ADDR From 5679fe6aee7811447ab9dfc9b4539526b23b1d84 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 3 Feb 2022 00:12:08 +1100 Subject: [PATCH 089/619] rp2/modutime: Fix time.localtime day-of-week value. The correct day-of-week is stored in the RTC (0=Monday, 6=Sunday) so there is no need to adjust it for the return value of time.localtime(). Fixes issue #7889. Signed-off-by: Damien George --- ports/rp2/modutime.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/rp2/modutime.c b/ports/rp2/modutime.c index 8041ae65b6..a586073101 100644 --- a/ports/rp2/modutime.c +++ b/ports/rp2/modutime.c @@ -45,7 +45,7 @@ STATIC mp_obj_t time_localtime(size_t n_args, const mp_obj_t *args) { mp_obj_new_int(t.hour), mp_obj_new_int(t.min), mp_obj_new_int(t.sec), - mp_obj_new_int((t.dotw + 6) % 7), // convert 0=Sunday to 6=Sunday + mp_obj_new_int(t.dotw), mp_obj_new_int(timeutils_year_day(t.year, t.month, t.day)), }; return mp_obj_new_tuple(8, tuple); From 5943a2ec79e51df91e0bb5d57ead68c57569f5cd Mon Sep 17 00:00:00 2001 From: Cem Eliguzel Date: Thu, 6 Jan 2022 13:53:43 +0300 Subject: [PATCH 090/619] docs/develop/porting.rst: Fix build and import problems in the example. --- docs/develop/porting.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/develop/porting.rst b/docs/develop/porting.rst index ae0cfd8b06..d08fd74e43 100644 --- a/docs/develop/porting.rst +++ b/docs/develop/porting.rst @@ -146,6 +146,9 @@ The following is an example of an ``mpconfigport.h`` file: #define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_TERSE) #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) + // Enable u-modules to be imported with their standard name, like sys. + #define MICROPY_MODULE_WEAK_LINKS (1) + // Fine control over Python builtins, classes, modules, etc. #define MICROPY_PY_ASYNC_AWAIT (0) #define MICROPY_PY_BUILTINS_SET (0) @@ -296,7 +299,7 @@ like this: mphalport.c \ ... - SRC_QSTR += modport.c + SRC_QSTR += modmyport.c If all went correctly then, after rebuilding, you should be able to import the new module: From ddda959e57d704873c91be05dec95846b7949c17 Mon Sep 17 00:00:00 2001 From: Luiz Brandao Date: Mon, 24 Jan 2022 10:16:35 -0500 Subject: [PATCH 091/619] docs/reference/isr_rules.rst: Fix inconsistent variable name in example. Fixed to be conistent with the code example above it. --- docs/reference/isr_rules.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/reference/isr_rules.rst b/docs/reference/isr_rules.rst index 7f466ab42f..a611f492a4 100644 --- a/docs/reference/isr_rules.rst +++ b/docs/reference/isr_rules.rst @@ -339,8 +339,8 @@ A critical section can comprise a single line of code and a single variable. Con This example illustrates a subtle source of bugs. The line ``count += 1`` in the main loop carries a specific race condition hazard known as a read-modify-write. This is a classic cause of bugs in real time systems. In the main loop -MicroPython reads the value of ``t.counter``, adds 1 to it, and writes it back. On rare occasions the interrupt occurs -after the read and before the write. The interrupt modifies ``t.counter`` but its change is overwritten by the main +MicroPython reads the value of ``count``, adds 1 to it, and writes it back. On rare occasions the interrupt occurs +after the read and before the write. The interrupt modifies ``count`` but its change is overwritten by the main loop when the ISR returns. In a real system this could lead to rare, unpredictable failures. As mentioned above, care should be taken if an instance of a Python built in type is modified in the main code and From a43764e6549957e1972ec375c330dd2a5c002d89 Mon Sep 17 00:00:00 2001 From: Jonathan Hogg Date: Thu, 3 Feb 2022 17:37:46 +0000 Subject: [PATCH 092/619] docs/esp32/quickref: Update ADC documentation. Update ADC documentation now that the new API is described in the main docs. --- docs/esp32/quickref.rst | 98 ++++++++++++++++++++++------------------- 1 file changed, 53 insertions(+), 45 deletions(-) diff --git a/docs/esp32/quickref.rst b/docs/esp32/quickref.rst index de107ee25c..da586819fd 100644 --- a/docs/esp32/quickref.rst +++ b/docs/esp32/quickref.rst @@ -264,82 +264,90 @@ See more examples in the :ref:`esp32_pwm` tutorial. ADC (analog to digital conversion) ---------------------------------- -On the ESP32 ADC functionality is available on pins 32-39 (ADC block 1) and pins -0, 2, 4, 12-15 and 25-27 (ADC block 2). +On the ESP32, ADC functionality is available on pins 32-39 (ADC block 1) and +pins 0, 2, 4, 12-15 and 25-27 (ADC block 2). Use the :ref:`machine.ADC ` class:: from machine import ADC - adc = ADC(Pin(32)) # create ADC object for pin 32 - adc.read_u16() # read raw value, 0-65535 + adc = ADC(pin) # create an ADC object acting on a pin + val = adc.read_u16() # read a raw analog value in the range 0-65535 + val = adc.read_uv() # read an analog value in microvolts -Note that the ESP32 uses an internal ADC reference voltage of 1.0v. To read -voltages above this value, input attenuation can be applied with the optional -``atten`` keyword argument to the constructor. Valid values are: +ADC block 2 is also used by WiFi and so attempting to read analog values from +block 2 pins when WiFi is active will raise an exception. - - ``ADC.ATTN_0DB``: No attenuation, this is the default - - ``ADC.ATTN_2_5DB``: 2.5dB attenuation, gives a maximum input voltage of - approximately 1.33v - - ``ADC.ATTN_6DB``: 6dB attenuation, gives a maximum input voltage of - approximately 2.00v - - ``ADC.ATTN_11DB``: 11dB attenuation, gives a maximum input voltage of - approximately 3.55v - -E.g.:: - - adc = ADC(Pin(25), atten=ADC.ATTEN_11DB) # 0.0v - 3.55v range - -.. Warning:: - Note that, although 11dB attenuation allows for a voltage range up to 3.55v, - the absolute maximum voltage rating for input pins is 3.6v, and so going - near this boundary risks damage to the IC! +The internal ADC reference voltage is typically 1.1V, but varies slightly from +package to package. The ADC is less linear close to the reference voltage +(particularly at higher attenuations) and has a minimum measurement voltage +around 100mV, voltages at or below this will read as 0. To read voltages +accurately, it is recommended to use the ``read_uv()`` method (see below). ESP32-specific ADC class method reference: -.. method:: ADC.init(*, atten) +.. class:: ADC(pin, *, atten) - Re-initialize the ADC pin with a different input attenuation. + Return the ADC object for the specified pin. ESP32 does not support + different timings for ADC sampling and so the ``sample_ns`` keyword argument + is not supported. + + To read voltages above the reference voltage, apply input attenuation with + the ``atten`` keyword argument. Valid values (and approximate linear + measurement ranges) are: + + - ``ADC.ATTN_0DB``: No attenuation (100mV - 950mV) + - ``ADC.ATTN_2_5DB``: 2.5dB attenuation (100mV - 1250mV) + - ``ADC.ATTN_6DB``: 6dB attenuation (150mV - 1750mV) + - ``ADC.ATTN_11DB``: 11dB attenuation (150mV - 2450mV) + +.. Warning:: + Note that the absolute maximum voltage rating for input pins is 3.6V. Going + near to this boundary risks damage to the IC! .. method:: ADC.read_uv() - This method uses internal per-package calibration values - set during - manufacture - to return the ADC input voltage in microvolts, taking into - account any input attenuation applied. Note that the calibration curves do - not guarantee that an input tied to ground will read as 0, and the returned - values have only millivolt resolution. + This method uses the known characteristics of the ADC and per-package eFuse + values - set during manufacture - to return a calibrated input voltage + (before attenuation) in microvolts. The returned value has only millivolt + resolution (i.e., will always be a multiple of 1000 microvolts). -.. method:: ADC.block() + The calibration is only valid across the linear range of the ADC. In + particular, an input tied to ground will read as a value above 0 microvolts. + Within the linear range, however, more accurate and consistent results will + be obtained than using `read_u16()` and scaling the result with a constant. - Return the matching ``ADCBlock`` object. +The ESP32 port also supports the :ref:`machine.ADC ` API: .. class:: ADCBlock(id, *, bits) Return the ADC block object with the given ``id`` (1 or 2) and initialize - it to the specified resolution (9 to 12-bits) or the default 12-bits. + it to the specified resolution (9 to 12-bits depending on the ESP32 series) + or the highest supported resolution if not specified. -.. method:: ADCBlock.init(*, bits) +.. method:: ADCBlock.connect(pin) + ADCBlock.connect(channel) + ADCBlock.connect(channel, pin) - Re-initialize the ADC block with a specific resolution. + Return the ``ADC`` object for the specified ADC pin or channel number. + Arbitrary connection of ADC channels to GPIO is not supported and so + specifying a pin that is not connected to this block, or specifying a + mismatched channel and pin, will raise an exception. -.. method:: ADCBlock.connect(channel_or_pin) - - Return the ``ADC`` object for the specified ADC channel number or Pin object. - -Legacy API methods: +Legacy methods: .. method:: ADC.read() This method returns the raw ADC value ranged according to the resolution of - the ADC block, 0-4095 for the default 12-bit resolution. + the block, e.g., 0-4095 for 12-bit resolution. -.. method:: ADC.atten(attenuation) +.. method:: ADC.atten(atten) - Equivalent to ``ADC.init(atten=attenuation)``. + Equivalent to ``ADC.init(atten=atten)``. -.. method:: ADC.width(width) +.. method:: ADC.width(bits) - Equivalent to ``ADC.block().init(bits=width)``. + Equivalent to ``ADC.block().init(bits=bits)``. For compatibility, the ``ADC`` object also provides constants matching the supported ADC resolutions: From 80b81acea5af425b15a92d09c3cc723dd24ebfb6 Mon Sep 17 00:00:00 2001 From: Peter Hinch Date: Sun, 30 Jan 2022 16:37:42 +0000 Subject: [PATCH 093/619] docs/reference/isr_rules.rst: Describe uasyncio-IRQ interface. --- docs/reference/isr_rules.rst | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/docs/reference/isr_rules.rst b/docs/reference/isr_rules.rst index a611f492a4..bdb838c590 100644 --- a/docs/reference/isr_rules.rst +++ b/docs/reference/isr_rules.rst @@ -219,6 +219,33 @@ Exceptions If an ISR raises an exception it will not propagate to the main loop. The interrupt will be disabled unless the exception is handled by the ISR code. +Interfacing to uasyncio +----------------------- + +When an ISR runs it can preempt the `uasyncio` scheduler. If the ISR performs a `uasyncio` +operation the scheduler's operation can be disrupted. This applies whether the interrupt is hard +or soft and also applies if the ISR has passed execution to another function via +`micropython.schedule`. In particular creating or cancelling tasks is invalid in an ISR context. +The safe way to interact with `uasyncio` is to implement a coroutine with synchronisation performed by +`uasyncio.ThreadSafeFlag`. The following fragment illustrates the creation of a task in response +to an interrupt: + +.. code:: python + + tsf = uasyncio.ThreadSafeFlag() + + def isr(_): # Interrupt handler + tsf.set() + + async def foo(): + while True: + await tsf.wait() + uasyncio.create_task(bar()) + +In this example there will be a variable amount of latency between the execution of the ISR and the execution +of ``foo()``. This is inherent to cooperative scheduling. The maximum latency is application +and platform dependent but may typically be measured in tens of ms. + General issues -------------- From a7530cbc03ba3ca4a70f7d3bd6b2e27656c25173 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 4 Feb 2022 14:28:21 +1100 Subject: [PATCH 094/619] stm32/boards/NUCLEO_L432KC: Disable MICROPY_OPT_COMPUTED_GOTO. To save space, after recent fixes to L4 ADC made it overflow flash. Signed-off-by: Damien George --- ports/stm32/boards/NUCLEO_L432KC/mpconfigboard.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/stm32/boards/NUCLEO_L432KC/mpconfigboard.h b/ports/stm32/boards/NUCLEO_L432KC/mpconfigboard.h index ce9de3c4dd..5e05053cdd 100644 --- a/ports/stm32/boards/NUCLEO_L432KC/mpconfigboard.h +++ b/ports/stm32/boards/NUCLEO_L432KC/mpconfigboard.h @@ -3,6 +3,7 @@ #define MICROPY_EMIT_THUMB (0) #define MICROPY_EMIT_INLINE_THUMB (0) +#define MICROPY_OPT_COMPUTED_GOTO (0) #define MICROPY_PY_BUILTINS_COMPLEX (0) #define MICROPY_PY_GENERATOR_PEND_THROW (0) #define MICROPY_PY_USOCKET (0) From 1f84440538a017e463aaad9686831ce9527122b5 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Tue, 1 Feb 2022 10:52:55 +1100 Subject: [PATCH 095/619] tools/mpremote: Fix "fs cp -r" on Windows. A backslash in the directory name will end up being passed through to the device and becoming a backslash in a filename, rather than being interpreted as directories. This makes "cp -r" problematic on Windows. Changing to simply "/",join() fixes this. --- tools/mpremote/mpremote/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/mpremote/mpremote/main.py b/tools/mpremote/mpremote/main.py index 13615f6dd2..5c63625589 100644 --- a/tools/mpremote/mpremote/main.py +++ b/tools/mpremote/mpremote/main.py @@ -268,7 +268,7 @@ def do_filesystem(pyb, args): def _list_recursive(files, path): if os.path.isdir(path): for entry in os.listdir(path): - _list_recursive(files, os.path.join(path, entry)) + _list_recursive(files, "/".join((path, entry))) else: files.append(os.path.split(path)) @@ -289,7 +289,7 @@ def do_filesystem(pyb, args): if d not in known_dirs: pyb.exec_("try:\n uos.mkdir('%s')\nexcept OSError as e:\n print(e)" % d) known_dirs.add(d) - pyboard.filesystem_command(pyb, ["cp", os.path.join(dir, file), ":" + dir + "/"]) + pyboard.filesystem_command(pyb, ["cp", "/".join((dir, file)), ":" + dir + "/"]) else: pyboard.filesystem_command(pyb, args) args.clear() From d865ca53b53aca5c66028970f7321e0766a0dd58 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Fri, 28 Jan 2022 14:43:17 +1100 Subject: [PATCH 096/619] tools/mpremote: Make ConsolePosix work without .raw attribute. When running mpremote in the vscode terminal on OSX the sys.stdout.buffer does not have the raw attribute. It works fine without it. --- tools/mpremote/mpremote/console.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tools/mpremote/mpremote/console.py b/tools/mpremote/mpremote/console.py index 2652c7393b..a0ee55dce9 100644 --- a/tools/mpremote/mpremote/console.py +++ b/tools/mpremote/mpremote/console.py @@ -11,8 +11,13 @@ except ImportError: class ConsolePosix: def __init__(self): self.infd = sys.stdin.fileno() - self.infile = sys.stdin.buffer.raw - self.outfile = sys.stdout.buffer.raw + self.infile = sys.stdin.buffer + self.outfile = sys.stdout.buffer + if hasattr(self.infile, "raw"): + self.infile = self.infile.raw + if hasattr(self.outfile, "raw"): + self.outfile = self.outfile.raw + self.orig_attr = termios.tcgetattr(self.infd) def enter(self): From 5a86d8dc424b5923d5d1a7c1352378ef328847d6 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Tue, 1 Feb 2022 12:07:40 +1100 Subject: [PATCH 097/619] tools/mpremote: During soft reboot wait long enough for 115200 data. --- tools/mpremote/mpremote/pyboardextended.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/mpremote/mpremote/pyboardextended.py b/tools/mpremote/mpremote/pyboardextended.py index 41f360e5fd..1b6463e853 100644 --- a/tools/mpremote/mpremote/pyboardextended.py +++ b/tools/mpremote/mpremote/pyboardextended.py @@ -641,7 +641,7 @@ class PyboardExtended(Pyboard): while n > 0: buf = self.serial.read(n) out_callback(buf) - time.sleep(0.1) + time.sleep(0.2) n = self.serial.inWaiting() self.serial.write(b"\x01") self.exec_(fs_hook_code) From b1519845f5dfd46b82fd5b3d980812d30ce0140b Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Wed, 2 Feb 2022 21:20:05 +1100 Subject: [PATCH 098/619] tools/mpremote: Accept both --help and help to show usage. --- tools/mpremote/mpremote/main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/mpremote/mpremote/main.py b/tools/mpremote/mpremote/main.py index 5c63625589..492d2d26d4 100644 --- a/tools/mpremote/mpremote/main.py +++ b/tools/mpremote/mpremote/main.py @@ -96,6 +96,7 @@ _BUILTIN_COMMAND_EXPANSIONS = { "exec", "import machine; machine.RTC().datetime((2020, 1, 1, 0, 10, 0, 0, 0))", ], + "--help": "help", } for port_num in range(4): From 203ec8ca7fa5f59e20bca95c74a77e3b1e8ef394 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Tue, 1 Feb 2022 11:59:59 +1100 Subject: [PATCH 099/619] tools/mpremote: Correctly manage mounted flag during soft-reset. --- tools/mpremote/mpremote/pyboardextended.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/mpremote/mpremote/pyboardextended.py b/tools/mpremote/mpremote/pyboardextended.py index 1b6463e853..2ec1fbbee9 100644 --- a/tools/mpremote/mpremote/pyboardextended.py +++ b/tools/mpremote/mpremote/pyboardextended.py @@ -614,10 +614,10 @@ class PyboardExtended(Pyboard): def mount_local(self, path): fout = self.serial - self.mounted = True if self.eval('"RemoteFS" in globals()') == b"False": self.exec_(fs_hook_code) self.exec_("__mount()") + self.mounted = True self.cmd = PyboardCommand(self.serial, fout, path) self.serial = SerialIntercept(self.serial, self.cmd) @@ -626,6 +626,9 @@ class PyboardExtended(Pyboard): if not self.mounted: return + # Clear flag while board reboots, it will be re-set once mounted. + self.mounted = False + # Wait for a response to the soft-reset command. for i in range(10): if self.serial.inWaiting(): @@ -646,6 +649,7 @@ class PyboardExtended(Pyboard): self.serial.write(b"\x01") self.exec_(fs_hook_code) self.exec_("__mount()") + self.mounted = True self.exit_raw_repl() self.read_until(4, b">>> ") self.serial = SerialIntercept(self.serial, self.cmd) From fecfd5269632e86f86c6b31a9bc2ec22876f8934 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 5 Feb 2022 23:29:44 +1100 Subject: [PATCH 100/619] tools/mpremote: Fix special handling of ctrl-D when host FS is mounted. Changes are: - decision to remount local filesystem on remote device is made only if "MPY: soft reboot" is seen in the output after sending a ctrl-D - a nice message is printed to the user when the remount occurs - soft reset during raw REPL is now handled correctly Fixes issue #7731. Signed-off-by: Damien George --- tools/mpremote/mpremote/main.py | 4 +- tools/mpremote/mpremote/pyboardextended.py | 65 ++++++++++++++++------ 2 files changed, 49 insertions(+), 20 deletions(-) diff --git a/tools/mpremote/mpremote/main.py b/tools/mpremote/mpremote/main.py index 492d2d26d4..1073bec7fa 100644 --- a/tools/mpremote/mpremote/main.py +++ b/tools/mpremote/mpremote/main.py @@ -304,8 +304,8 @@ def do_repl_main_loop(pyb, console_in, console_out_write, *, code_to_inject, fil if c == b"\x1d": # ctrl-], quit break elif c == b"\x04": # ctrl-D - # do a soft reset and reload the filesystem hook - pyb.soft_reset_with_mount(console_out_write) + # special handling needed for ctrl-D if filesystem is mounted + pyb.write_ctrl_d(console_out_write) elif c == b"\x0a" and code_to_inject is not None: # ctrl-j, inject code pyb.serial.write(code_to_inject) elif c == b"\x0b" and file_to_inject is not None: # ctrl-k, inject script diff --git a/tools/mpremote/mpremote/pyboardextended.py b/tools/mpremote/mpremote/pyboardextended.py index 2ec1fbbee9..6817e7bbf4 100644 --- a/tools/mpremote/mpremote/pyboardextended.py +++ b/tools/mpremote/mpremote/pyboardextended.py @@ -621,37 +621,66 @@ class PyboardExtended(Pyboard): self.cmd = PyboardCommand(self.serial, fout, path) self.serial = SerialIntercept(self.serial, self.cmd) - def soft_reset_with_mount(self, out_callback): + def write_ctrl_d(self, out_callback): self.serial.write(b"\x04") if not self.mounted: return - # Clear flag while board reboots, it will be re-set once mounted. - self.mounted = False + # Read response from the device until it is quiet (with a timeout). + INITIAL_TIMEOUT = 0.5 + QUIET_TIMEOUT = 0.2 + FULL_TIMEOUT = 5 + t_start = t_last_activity = time.monotonic() + data_all = b"" + while True: + t = time.monotonic() + n = self.serial.inWaiting() + if n > 0: + data = self.serial.read(n) + out_callback(data) + data_all += data + t_last_activity = t + else: + if len(data_all) == 0: + if t - t_start > INITIAL_TIMEOUT: + return + else: + if t - t_start > FULL_TIMEOUT: + return + if t - t_last_activity > QUIET_TIMEOUT: + break - # Wait for a response to the soft-reset command. - for i in range(10): - if self.serial.inWaiting(): - break - time.sleep(0.05) + # Check if a soft reset occurred. + if data_all.find(b"MPY: soft reboot") == -1: + return + if data_all.endswith(b">>> "): + in_friendly_repl = True + elif data_all.endswith(b">"): + in_friendly_repl = False else: - # Device didn't respond so it wasn't in a state to do a soft reset. return - out_callback(self.serial.read(1)) + # Clear state while board remounts, it will be re-set once mounted. + self.mounted = False self.serial = self.serial.orig_serial - n = self.serial.inWaiting() - while n > 0: - buf = self.serial.read(n) - out_callback(buf) - time.sleep(0.2) - n = self.serial.inWaiting() + + # Provide a message about the remount. + out_callback(bytes(f"\r\nRemount local directory {self.cmd.root} at /remote\r\n", "utf8")) + + # Enter raw REPL and re-mount the remote filesystem. self.serial.write(b"\x01") self.exec_(fs_hook_code) self.exec_("__mount()") self.mounted = True - self.exit_raw_repl() - self.read_until(4, b">>> ") + + # Exit raw REPL if needed, and wait for the friendly REPL prompt. + if in_friendly_repl: + self.exit_raw_repl() + prompt = b">>> " + else: + prompt = b">" + self.read_until(len(prompt), prompt) + out_callback(prompt) self.serial = SerialIntercept(self.serial, self.cmd) def umount_local(self): From 69c9a76786a584c716e26e6874cc778468d5d8c2 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Thu, 25 Nov 2021 11:44:21 +1100 Subject: [PATCH 101/619] windows/mingw: Include extmod/shared/lib sources properly. --- ports/windows/Makefile | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/ports/windows/Makefile b/ports/windows/Makefile index 4aceeb981c..5a22be4642 100644 --- a/ports/windows/Makefile +++ b/ports/windows/Makefile @@ -61,7 +61,18 @@ SRC_C = \ $(SRC_MOD) \ $(wildcard $(VARIANT_DIR)/*.c) +SHARED_SRC_C += $(addprefix shared/,\ + $(SHARED_SRC_C_EXTRA) \ + ) + +SRC_CXX += \ + $(SRC_MOD_CXX) + OBJ = $(PY_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(SRC_CXX:.cpp=.o)) +OBJ += $(addprefix $(BUILD)/, $(SHARED_SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(EXTMOD_SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(LIB_SRC_C:.c=.o)) ifeq ($(MICROPY_USE_READLINE),1) CFLAGS_MOD += -DMICROPY_USE_READLINE=1 @@ -71,7 +82,7 @@ endif LIB += -lws2_32 # List of sources for qstr extraction -SRC_QSTR += $(SRC_C) +SRC_QSTR += $(SRC_C) $(SRC_CXX) $(SHARED_SRC_C) $(EXTMOD_SRC_C) # Append any auto-generated sources that are needed by sources listed in # SRC_QSTR SRC_QSTR_AUTO_DEPS += From c708262c12cdf9b465e7007cc33cca7044310fcb Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Thu, 25 Nov 2021 12:09:40 +1100 Subject: [PATCH 102/619] windows/uselect: Enable micropython select in dev variant. --- ports/windows/msvc/sources.props | 1 + ports/windows/variants/dev/mpconfigvariant.h | 1 + 2 files changed, 2 insertions(+) diff --git a/ports/windows/msvc/sources.props b/ports/windows/msvc/sources.props index 5bf0fc453d..228a96d81e 100644 --- a/ports/windows/msvc/sources.props +++ b/ports/windows/msvc/sources.props @@ -14,6 +14,7 @@ + diff --git a/ports/windows/variants/dev/mpconfigvariant.h b/ports/windows/variants/dev/mpconfigvariant.h index 1f205066f0..09ee689daf 100644 --- a/ports/windows/variants/dev/mpconfigvariant.h +++ b/ports/windows/variants/dev/mpconfigvariant.h @@ -36,3 +36,4 @@ #define MICROPY_COMP_CONST (0) #define MICROPY_PY_URANDOM_EXTRA_FUNCS (1) #define MICROPY_PY_BUILTINS_SLICE_INDICES (1) +#define MICROPY_PY_USELECT (1) From 6f7d6c567f8862e0893da7fd08bd828cc6d054cf Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Mon, 7 Feb 2022 14:38:53 +1100 Subject: [PATCH 103/619] windows/uasyncio: Add support for uasyncio to windows dev variant. --- ports/windows/msvc/sources.props | 1 + ports/windows/variants/dev/manifest.py | 1 + ports/windows/variants/dev/mpconfigvariant.h | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/ports/windows/msvc/sources.props b/ports/windows/msvc/sources.props index 228a96d81e..e5109eecc8 100644 --- a/ports/windows/msvc/sources.props +++ b/ports/windows/msvc/sources.props @@ -7,6 +7,7 @@ + diff --git a/ports/windows/variants/dev/manifest.py b/ports/windows/variants/dev/manifest.py index 08295fc678..88a6937b4f 100644 --- a/ports/windows/variants/dev/manifest.py +++ b/ports/windows/variants/dev/manifest.py @@ -1 +1,2 @@ include("$(PORT_DIR)/variants/manifest.py") +include("$(MPY_DIR)/extmod/uasyncio/manifest.py") diff --git a/ports/windows/variants/dev/mpconfigvariant.h b/ports/windows/variants/dev/mpconfigvariant.h index 09ee689daf..abf43a0da9 100644 --- a/ports/windows/variants/dev/mpconfigvariant.h +++ b/ports/windows/variants/dev/mpconfigvariant.h @@ -37,3 +37,7 @@ #define MICROPY_PY_URANDOM_EXTRA_FUNCS (1) #define MICROPY_PY_BUILTINS_SLICE_INDICES (1) #define MICROPY_PY_USELECT (1) + +#ifndef MICROPY_PY_UASYNCIO +#define MICROPY_PY_UASYNCIO (1) +#endif From 6fe3856c4f5baeb5ef7b46dbdfd99bf34f967169 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 8 Feb 2022 12:24:15 +1100 Subject: [PATCH 104/619] esp32/partitions-16MiB.csv: Increase 14MiB filesystem to maximum size. The original value was 14000000, it's now changed to 14 * 1024 * 1024. Fixes issue #8266. Signed-off-by: Damien George --- ports/esp32/partitions-16MiB.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/esp32/partitions-16MiB.csv b/ports/esp32/partitions-16MiB.csv index ee3e8c7269..ae926c7b94 100644 --- a/ports/esp32/partitions-16MiB.csv +++ b/ports/esp32/partitions-16MiB.csv @@ -4,4 +4,4 @@ nvs, data, nvs, 0x9000, 0x6000, phy_init, data, phy, 0xf000, 0x1000, factory, app, factory, 0x10000, 0x1F0000, -vfs, data, fat, 0x200000, 0xD59F80, +vfs, data, fat, 0x200000, 0xE00000, From ada836b8344cac24b08cbec969b4b4cdb08eec8a Mon Sep 17 00:00:00 2001 From: Seon Rozenblum Date: Tue, 8 Feb 2022 11:14:28 +1100 Subject: [PATCH 105/619] esp32/machine_adcblock: Fix ADC bit width for ESP32-S3. --- ports/esp32/machine_adcblock.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/esp32/machine_adcblock.c b/ports/esp32/machine_adcblock.c index 77e4fcecc7..06c215f8ae 100644 --- a/ports/esp32/machine_adcblock.c +++ b/ports/esp32/machine_adcblock.c @@ -40,10 +40,10 @@ #define DEFAULT_VREF 1100 madcblock_obj_t madcblock_obj[] = { - #if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C3 + #if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 {{&machine_adcblock_type}, ADC_UNIT_1, 12, -1, {0}}, {{&machine_adcblock_type}, ADC_UNIT_2, 12, -1, {0}}, - #elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 + #elif CONFIG_IDF_TARGET_ESP32S2 {{&machine_adcblock_type}, ADC_UNIT_1, 13, -1, {0}}, {{&machine_adcblock_type}, ADC_UNIT_2, 13, -1, {0}}, #endif From 468d1979baf022b9598137243007cc550ff0b306 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 8 Feb 2022 15:03:42 +1100 Subject: [PATCH 106/619] esp32/machine_adc: Fix configuration of default ADC atten value. Prior to this fix, if the ADC atten value was not explicitly given then adc1_config_channel_atten() would never be called. Fixes issue #8275. Signed-off-by: Damien George --- ports/esp32/machine_adc.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/ports/esp32/machine_adc.c b/ports/esp32/machine_adc.c index 515d629a7e..cb45aab339 100644 --- a/ports/esp32/machine_adc.c +++ b/ports/esp32/machine_adc.c @@ -91,7 +91,19 @@ STATIC const madc_obj_t madc_obj[] = { #endif }; -STATIC adc_atten_t madc_obj_atten[MP_ARRAY_SIZE(madc_obj)]; +// 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 madc_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 madc_obj_t *self, adc_atten_t atten) { + madc_obj_atten[self - &madc_obj[0]] = atten + 1; +} const madc_obj_t *madc_search_helper(madcblock_obj_t *block, adc_channel_t channel_id, gpio_num_t gpio_id) { for (int i = 0; i < MP_ARRAY_SIZE(madc_obj); i++) { @@ -105,7 +117,7 @@ const madc_obj_t *madc_search_helper(madcblock_obj_t *block, adc_channel_t chann STATIC void madc_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { const madc_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_printf(print, "ADC(Pin(%u), atten=%u)", self->gpio_id, madc_obj_atten[self - madc_obj]); + mp_printf(print, "ADC(Pin(%u), atten=%u)", self->gpio_id, madc_atten_get(self)); } STATIC void madc_atten_helper(const madc_obj_t *self, mp_int_t atten) { @@ -118,7 +130,7 @@ STATIC void madc_atten_helper(const madc_obj_t *self, mp_int_t atten) { if (err != ESP_OK) { mp_raise_ValueError(MP_ERROR_TEXT("invalid atten")); } - madc_obj_atten[self - madc_obj] = atten; + madc_atten_set(self, atten); } void madc_init_helper(const madc_obj_t *self, size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -136,7 +148,7 @@ void madc_init_helper(const madc_obj_t *self, size_t n_pos_args, const mp_obj_t mp_int_t atten = args[ARG_atten].u_int; if (atten != -1) { madc_atten_helper(self, atten); - } else if (madc_obj_atten[self - madc_obj] == -1) { + } else if (madc_atten_get(self) == ADC_ATTEN_MAX) { madc_atten_helper(self, ADC_ATTEN_DB_0); } } @@ -192,7 +204,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(madc_read_u16_obj, madc_read_u16); STATIC mp_obj_t madc_read_uv(mp_obj_t self_in) { const madc_obj_t *self = MP_OBJ_TO_PTR(self_in); - adc_atten_t atten = madc_obj_atten[self - madc_obj]; + adc_atten_t atten = madc_atten_get(self); return MP_OBJ_NEW_SMALL_INT(madcblock_read_uv_helper(self->block, self->channel_id, atten)); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(madc_read_uv_obj, madc_read_uv); From aca40127bf9c9a0fb791d8b82be7637fa57ca343 Mon Sep 17 00:00:00 2001 From: Seon Rozenblum Date: Sun, 30 Jan 2022 12:43:28 +1100 Subject: [PATCH 107/619] esp32/boards: Add three UM ESP32-S3 based boards. --- ports/esp32/boards/UM_FEATHERS3/board.json | 27 ++++++ ports/esp32/boards/UM_FEATHERS3/board.md | 2 + ports/esp32/boards/UM_FEATHERS3/deploy.md | 52 +++++++++++ ports/esp32/boards/UM_FEATHERS3/manifest.py | 2 + .../boards/UM_FEATHERS3/modules/feathers3.py | 90 +++++++++++++++++++ .../boards/UM_FEATHERS3/mpconfigboard.cmake | 12 +++ .../esp32/boards/UM_FEATHERS3/mpconfigboard.h | 11 +++ .../esp32/boards/UM_FEATHERS3/sdkconfig.board | 23 +++++ ports/esp32/boards/UM_PROS3/board.json | 27 ++++++ ports/esp32/boards/UM_PROS3/board.md | 2 + ports/esp32/boards/UM_PROS3/deploy.md | 52 +++++++++++ ports/esp32/boards/UM_PROS3/manifest.py | 2 + ports/esp32/boards/UM_PROS3/modules/pros3.py | 66 ++++++++++++++ .../esp32/boards/UM_PROS3/mpconfigboard.cmake | 12 +++ ports/esp32/boards/UM_PROS3/mpconfigboard.h | 11 +++ ports/esp32/boards/UM_PROS3/sdkconfig.board | 23 +++++ ports/esp32/boards/UM_TINYS3/board.json | 26 ++++++ ports/esp32/boards/UM_TINYS3/board.md | 2 + ports/esp32/boards/UM_TINYS3/deploy.md | 52 +++++++++++ ports/esp32/boards/UM_TINYS3/manifest.py | 2 + .../esp32/boards/UM_TINYS3/modules/tinys3.py | 66 ++++++++++++++ .../boards/UM_TINYS3/mpconfigboard.cmake | 12 +++ ports/esp32/boards/UM_TINYS3/mpconfigboard.h | 11 +++ ports/esp32/boards/UM_TINYS3/sdkconfig.board | 23 +++++ 24 files changed, 608 insertions(+) create mode 100644 ports/esp32/boards/UM_FEATHERS3/board.json create mode 100644 ports/esp32/boards/UM_FEATHERS3/board.md create mode 100644 ports/esp32/boards/UM_FEATHERS3/deploy.md create mode 100644 ports/esp32/boards/UM_FEATHERS3/manifest.py create mode 100644 ports/esp32/boards/UM_FEATHERS3/modules/feathers3.py create mode 100644 ports/esp32/boards/UM_FEATHERS3/mpconfigboard.cmake create mode 100644 ports/esp32/boards/UM_FEATHERS3/mpconfigboard.h create mode 100644 ports/esp32/boards/UM_FEATHERS3/sdkconfig.board create mode 100644 ports/esp32/boards/UM_PROS3/board.json create mode 100644 ports/esp32/boards/UM_PROS3/board.md create mode 100644 ports/esp32/boards/UM_PROS3/deploy.md create mode 100644 ports/esp32/boards/UM_PROS3/manifest.py create mode 100644 ports/esp32/boards/UM_PROS3/modules/pros3.py create mode 100644 ports/esp32/boards/UM_PROS3/mpconfigboard.cmake create mode 100644 ports/esp32/boards/UM_PROS3/mpconfigboard.h create mode 100644 ports/esp32/boards/UM_PROS3/sdkconfig.board create mode 100644 ports/esp32/boards/UM_TINYS3/board.json create mode 100644 ports/esp32/boards/UM_TINYS3/board.md create mode 100644 ports/esp32/boards/UM_TINYS3/deploy.md create mode 100644 ports/esp32/boards/UM_TINYS3/manifest.py create mode 100644 ports/esp32/boards/UM_TINYS3/modules/tinys3.py create mode 100644 ports/esp32/boards/UM_TINYS3/mpconfigboard.cmake create mode 100644 ports/esp32/boards/UM_TINYS3/mpconfigboard.h create mode 100644 ports/esp32/boards/UM_TINYS3/sdkconfig.board diff --git a/ports/esp32/boards/UM_FEATHERS3/board.json b/ports/esp32/boards/UM_FEATHERS3/board.json new file mode 100644 index 0000000000..68423cca5e --- /dev/null +++ b/ports/esp32/boards/UM_FEATHERS3/board.json @@ -0,0 +1,27 @@ +{ + "deploy": [ + "deploy.md" + ], + "docs": "", + "features": [ + "Battery Charging", + "RGB LED", + "SPIRAM", + "USB-C", + "WiFi", + "BLE", + "STEMMA QT/QWIIC", + "Feather" + ], + "features_non_filterable": [ + ], + "id": "feathers3", + "images": [ + "unexpectedmaker_feathers3.jpg" + ], + "mcu": "esp32s3", + "product": "FeatherS3", + "thumbnail": "", + "url": "https://feathers3.io", + "vendor": "Unexpected Maker" +} diff --git a/ports/esp32/boards/UM_FEATHERS3/board.md b/ports/esp32/boards/UM_FEATHERS3/board.md new file mode 100644 index 0000000000..ca9c36ad33 --- /dev/null +++ b/ports/esp32/boards/UM_FEATHERS3/board.md @@ -0,0 +1,2 @@ +The following files are daily firmware for the FeatherS3. This firmware is +compiled using ESP-IDF v4.4 or later. diff --git a/ports/esp32/boards/UM_FEATHERS3/deploy.md b/ports/esp32/boards/UM_FEATHERS3/deploy.md new file mode 100644 index 0000000000..3a6a21a522 --- /dev/null +++ b/ports/esp32/boards/UM_FEATHERS3/deploy.md @@ -0,0 +1,52 @@ +Program your board using the latest version of the esptool.py program, found [here](https://github.com/espressif/esptool). + +To flash or erase your FeatherS3, you have to first put it into download mode. +To do this, follow these steps: + +- Press and hold the [BOOT] button +- Press and release the [RESET] button +- Release the [BOOT] button + +Now the board is in download mode and the native USB will have enumerated as a serial device. + +If you are putting MicroPython on your board for the first time then you should +first erase the entire flash using: + +### Linux +```bash +esptool.py --chip esp32s3 --port /dev/ttyACM0 erase_flash +``` + +### Mac +Please do a `ls /dev/cu.usbm*` to determine the port your board has enumerated as. +```bash +esptool.py --chip esp32s3 --port /dev/cu.usbmodem01 erase_flash +``` + +### Windows +Change (X) to whatever COM port is being used by the board +```bash +esptool --chip esp32s3 --port COM(X) erase_flash +``` + +Now download the version of the firmware you would like to install from the options below, +then use the following command to program the firmware starting at address 0x0, +remembering to replace `feathers3-micropython-firmware-version.bin` with the name of +the firmware you just downloaded: + +### Linux +```bash +esptool.py --chip esp32s3 --port /dev/ttyACM0 write_flash -z 0x0 feathers3-micropython-firmware-version.bin +``` + +### Mac +Please do a `ls /dev/cu.usbm*` to determine the port your board has enumerated as. +```bash +esptool.py --chip esp32s3 --port /dev/cu.usbmodem01 write_flash -z 0x0 feathers3-micropython-firmware-version.bin +``` + +### Windows +Change (X) to whatever COM port is being used by the board +```bash +esptool --chip esp32s3 --port COM(X) write_flash -z 0x0 feathers3-micropython-firmware-version.bin +``` diff --git a/ports/esp32/boards/UM_FEATHERS3/manifest.py b/ports/esp32/boards/UM_FEATHERS3/manifest.py new file mode 100644 index 0000000000..7ae2ed15d9 --- /dev/null +++ b/ports/esp32/boards/UM_FEATHERS3/manifest.py @@ -0,0 +1,2 @@ +include("$(PORT_DIR)/boards/manifest.py") +freeze("modules") diff --git a/ports/esp32/boards/UM_FEATHERS3/modules/feathers3.py b/ports/esp32/boards/UM_FEATHERS3/modules/feathers3.py new file mode 100644 index 0000000000..801f9cbf46 --- /dev/null +++ b/ports/esp32/boards/UM_FEATHERS3/modules/feathers3.py @@ -0,0 +1,90 @@ +# FeatherS3 MicroPython Helper Library +# MIT license; Copyright (c) 2022 Seon Rozenblum - Unexpected Maker +# +# Project home: +# http://feathers3.io + +# Import required libraries +from micropython import const +from machine import Pin, ADC +import time + +# FeatherS3 Hardware Pin Assignments + +# Sense Pins +VBUS_SENSE = const(34) +VBAT_SENSE = const(2) + +# RGB LED, LDO2 & Other Pins +RGB_DATA = const(40) +LDO2 = const(39) +LED = const(13) +AMB_LIGHT = const(4) + +# SPI +SPI_MOSI = const(35) +SPI_MISO = const(37) +SPI_CLK = const(36) + +# I2C +I2C_SDA = const(8) +I2C_SCL = const(9) + +# Helper functions + +# LED & Ambient Light Sensor control +def led_set(state): + """Set the state of the BLUE LED on IO13""" + l = Pin(LED, Pin.OUT) + l.value(state) + + +def led_blink(): + """Toggle the BLUE LED on IO13""" + l = Pin(LED, Pin.OUT) + l.value(not l.value()) + + +# Create ADC and set attenuation and return the ambient light value from the onboard sensor +def get_amb_light(): + """Get Ambient Light Sensor reading""" + adc = ADC(Pin(AMB_LIGHT)) + adc.atten(ADC.ATTN_11DB) + return adc.read() + + +def set_ldo2_power(state): + """Enable or Disable power to the second LDO""" + Pin(LDO2, Pin.OUT).value(state) + + +def get_battery_voltage(): + """ + Returns the current battery voltage. If no battery is connected, returns 4.2V which is the charge voltage + This is an approximation only, but useful to detect if the charge state of the battery is getting low. + """ + adc = ADC(Pin(VBAT_SENSE)) # Assign the ADC pin to read + measuredvbat = adc.read() + measuredvbat /= 4095 # divide by 4095 as we are using the default ADC attenuation of 0dB + measuredvbat *= 4.2 # Multiply by 4.2V, our max charge voltage for a 1S LiPo + return round(measuredvbat, 2) + + +def get_vbus_present(): + """Detect if VBUS (5V) power source is present""" + return Pin(VBUS_SENSE, Pin.IN).value() == 1 + + +# NeoPixel rainbow colour wheel +def rgb_color_wheel(wheel_pos): + """Color wheel to allow for cycling through the rainbow of RGB colors.""" + wheel_pos = wheel_pos % 255 + + if wheel_pos < 85: + return 255 - wheel_pos * 3, 0, wheel_pos * 3 + elif wheel_pos < 170: + wheel_pos -= 85 + return 0, wheel_pos * 3, 255 - wheel_pos * 3 + else: + wheel_pos -= 170 + return wheel_pos * 3, 255 - wheel_pos * 3, 0 diff --git a/ports/esp32/boards/UM_FEATHERS3/mpconfigboard.cmake b/ports/esp32/boards/UM_FEATHERS3/mpconfigboard.cmake new file mode 100644 index 0000000000..6c7f34009e --- /dev/null +++ b/ports/esp32/boards/UM_FEATHERS3/mpconfigboard.cmake @@ -0,0 +1,12 @@ +set(IDF_TARGET esp32s3) + +set(SDKCONFIG_DEFAULTS + boards/sdkconfig.base + boards/sdkconfig.usb + boards/sdkconfig.ble + boards/sdkconfig.240mhz + boards/sdkconfig.spiram_sx + boards/UM_TINYS3/sdkconfig.board +) + +set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py) diff --git a/ports/esp32/boards/UM_FEATHERS3/mpconfigboard.h b/ports/esp32/boards/UM_FEATHERS3/mpconfigboard.h new file mode 100644 index 0000000000..738b32ca1e --- /dev/null +++ b/ports/esp32/boards/UM_FEATHERS3/mpconfigboard.h @@ -0,0 +1,11 @@ +#define MICROPY_HW_BOARD_NAME "FeatherS3" +#define MICROPY_HW_MCU_NAME "ESP32-S3" + +#define MICROPY_PY_MACHINE_DAC (0) + +#define MICROPY_HW_I2C0_SCL (9) +#define MICROPY_HW_I2C0_SDA (8) + +#define MICROPY_HW_SPI1_MOSI (35) +#define MICROPY_HW_SPI1_MISO (37) +#define MICROPY_HW_SPI1_SCK (36) diff --git a/ports/esp32/boards/UM_FEATHERS3/sdkconfig.board b/ports/esp32/boards/UM_FEATHERS3/sdkconfig.board new file mode 100644 index 0000000000..5e20045125 --- /dev/null +++ b/ports/esp32/boards/UM_FEATHERS3/sdkconfig.board @@ -0,0 +1,23 @@ +CONFIG_FLASHMODE_QIO=y +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_ESPTOOLPY_FLASHSIZE_DETECT=y +CONFIG_ESPTOOLPY_AFTER_NORESET=y + +CONFIG_SPIRAM_MEMTEST= + +CONFIG_ESPTOOLPY_FLASHSIZE_4MB= +CONFIG_ESPTOOLPY_FLASHSIZE_8MB= +CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-16MiB.csv" + +CONFIG_LWIP_LOCAL_HOSTNAME="UMFeatherS3" + +# CONFIG_TINYUSB_DESC_USE_ESPRESSIF_VID is not set +CONFIG_TINYUSB_DESC_CUSTOM_VID=0x303A +# CONFIG_TINYUSB_DESC_USE_DEFAULT_PID is not set +CONFIG_TINYUSB_DESC_CUSTOM_PID=0x80D7 +CONFIG_TINYUSB_DESC_BCD_DEVICE=0x0100 +CONFIG_TINYUSB_DESC_MANUFACTURER_STRING="Unexpected Maker" +CONFIG_TINYUSB_DESC_PRODUCT_STRING="FeatherS3" +CONFIG_TINYUSB_DESC_SERIAL_STRING="_fs3_" \ No newline at end of file diff --git a/ports/esp32/boards/UM_PROS3/board.json b/ports/esp32/boards/UM_PROS3/board.json new file mode 100644 index 0000000000..0122e4b991 --- /dev/null +++ b/ports/esp32/boards/UM_PROS3/board.json @@ -0,0 +1,27 @@ +{ + "deploy": [ + "deploy.md" + ], + "docs": "", + "features": [ + "Battery Charging", + "RGB LED", + "SPIRAM", + "USB-C", + "WiFi", + "BLE", + "STEMMA QT/QWIIC", + "Feather" + ], + "features_non_filterable": [ + ], + "id": "pros3", + "images": [ + "unexpectedmaker_pros3.jpg" + ], + "mcu": "esp32s3", + "product": "ProS3", + "thumbnail": "", + "url": "https://pros3.io", + "vendor": "Unexpected Maker" +} diff --git a/ports/esp32/boards/UM_PROS3/board.md b/ports/esp32/boards/UM_PROS3/board.md new file mode 100644 index 0000000000..4d1c435a07 --- /dev/null +++ b/ports/esp32/boards/UM_PROS3/board.md @@ -0,0 +1,2 @@ +The following files are daily firmware for the ProS3. This firmware is +compiled using ESP-IDF v4.4 or later. diff --git a/ports/esp32/boards/UM_PROS3/deploy.md b/ports/esp32/boards/UM_PROS3/deploy.md new file mode 100644 index 0000000000..d35d7a02fe --- /dev/null +++ b/ports/esp32/boards/UM_PROS3/deploy.md @@ -0,0 +1,52 @@ +Program your board using the latest version of the esptool.py program, found [here](https://github.com/espressif/esptool). + +To flash or erase your ProS3, you have to first put it into download mode. +To do this, follow these steps: + +- Press and hold the [BOOT] button +- Press and release the [RESET] button +- Release the [BOOT] button + +Now the board is in download mode and the native USB will have enumerated as a serial device. + +If you are putting MicroPython on your board for the first time then you should +first erase the entire flash using: + +### Linux +```bash +esptool.py --chip esp32s3 --port /dev/ttyACM0 erase_flash +``` + +### Mac +Please do a `ls /dev/cu.usbm*` to determine the port your board has enumerated as. +```bash +esptool.py --chip esp32s3 --port /dev/cu.usbmodem01 erase_flash +``` + +### Windows +Change (X) to whatever COM port is being used by the board +```bash +esptool --chip esp32s3 --port COM(X) erase_flash +``` + +Now download the version of the firmware you would like to install from the options below, +then use the following command to program the firmware starting at address 0x0, +remembering to replace `pros3-micropython-firmware-version.bin` with the name of +the firmware you just downloaded: + +### Linux +```bash +esptool.py --chip esp32s3 --port /dev/ttyACM0 write_flash -z 0x0 pros3-micropython-firmware-version.bin +``` + +### Mac +Please do a `ls /dev/cu.usbm*` to determine the port your board has enumerated as. +```bash +esptool.py --chip esp32s3 --port /dev/cu.usbmodem01 write_flash -z 0x0 pros3-micropython-firmware-version.bin +``` + +### Windows +Change (X) to whatever COM port is being used by the board +```bash +esptool --chip esp32s3 --port COM(X) write_flash -z 0x0 pros3-pros3-firmware-version.bin +``` diff --git a/ports/esp32/boards/UM_PROS3/manifest.py b/ports/esp32/boards/UM_PROS3/manifest.py new file mode 100644 index 0000000000..7ae2ed15d9 --- /dev/null +++ b/ports/esp32/boards/UM_PROS3/manifest.py @@ -0,0 +1,2 @@ +include("$(PORT_DIR)/boards/manifest.py") +freeze("modules") diff --git a/ports/esp32/boards/UM_PROS3/modules/pros3.py b/ports/esp32/boards/UM_PROS3/modules/pros3.py new file mode 100644 index 0000000000..2edf3e42ea --- /dev/null +++ b/ports/esp32/boards/UM_PROS3/modules/pros3.py @@ -0,0 +1,66 @@ +# ProS3 MicroPython Helper Library +# MIT license; Copyright (c) 2022 Seon Rozenblum - Unexpected Maker +# +# Project home: +# http://pros3.io + +# Import required libraries +from micropython import const +from machine import Pin, ADC +import time + +# ProS3 Hardware Pin Assignments + +# Sense Pins +VBUS_SENSE = const(33) +VBAT_SENSE = const(10) + +# RGB LED & LDO2 Pins +RGB_DATA = const(18) +LDO2 = const(17) + +# SPI +SPI_MOSI = const(35) +SPI_MISO = const(37) +SPI_CLK = const(36) + +# I2C +I2C_SDA = const(8) +I2C_SCL = const(9) + +# Helper functions +def set_ldo2_power(state): + """Enable or Disable power to the second LDO""" + Pin(LDO2, Pin.OUT).value(state) + + +def get_battery_voltage(): + """ + Returns the current battery voltage. If no battery is connected, returns 4.2V which is the charge voltage + This is an approximation only, but useful to detect if the charge state of the battery is getting low. + """ + adc = ADC(Pin(VBAT_SENSE)) # Assign the ADC pin to read + measuredvbat = adc.read() + measuredvbat /= 4095 # divide by 4095 as we are using the default ADC attenuation of 0dB + measuredvbat *= 4.2 # Multiply by 4.2V, our max charge voltage for a 1S LiPo + return round(measuredvbat, 2) + + +def get_vbus_present(): + """Detect if VBUS (5V) power source is present""" + return Pin(VBUS_SENSE, Pin.IN).value() == 1 + + +# NeoPixel rainbow colour wheel +def rgb_color_wheel(wheel_pos): + """Color wheel to allow for cycling through the rainbow of RGB colors.""" + wheel_pos = wheel_pos % 255 + + if wheel_pos < 85: + return 255 - wheel_pos * 3, 0, wheel_pos * 3 + elif wheel_pos < 170: + wheel_pos -= 85 + return 0, wheel_pos * 3, 255 - wheel_pos * 3 + else: + wheel_pos -= 170 + return wheel_pos * 3, 255 - wheel_pos * 3, 0 diff --git a/ports/esp32/boards/UM_PROS3/mpconfigboard.cmake b/ports/esp32/boards/UM_PROS3/mpconfigboard.cmake new file mode 100644 index 0000000000..41a96f26e3 --- /dev/null +++ b/ports/esp32/boards/UM_PROS3/mpconfigboard.cmake @@ -0,0 +1,12 @@ +set(IDF_TARGET esp32s3) + +set(SDKCONFIG_DEFAULTS + boards/sdkconfig.base + boards/sdkconfig.usb + boards/sdkconfig.ble + boards/sdkconfig.240mhz + boards/sdkconfig.spiram_sx + boards/UM_PROS3/sdkconfig.board +) + +set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py) diff --git a/ports/esp32/boards/UM_PROS3/mpconfigboard.h b/ports/esp32/boards/UM_PROS3/mpconfigboard.h new file mode 100644 index 0000000000..1522e2aee3 --- /dev/null +++ b/ports/esp32/boards/UM_PROS3/mpconfigboard.h @@ -0,0 +1,11 @@ +#define MICROPY_HW_BOARD_NAME "ProS3" +#define MICROPY_HW_MCU_NAME "ESP32-S3" + +#define MICROPY_PY_MACHINE_DAC (0) + +#define MICROPY_HW_I2C0_SCL (9) +#define MICROPY_HW_I2C0_SDA (8) + +#define MICROPY_HW_SPI1_MOSI (35) +#define MICROPY_HW_SPI1_MISO (37) +#define MICROPY_HW_SPI1_SCK (36) diff --git a/ports/esp32/boards/UM_PROS3/sdkconfig.board b/ports/esp32/boards/UM_PROS3/sdkconfig.board new file mode 100644 index 0000000000..06b3a00a1c --- /dev/null +++ b/ports/esp32/boards/UM_PROS3/sdkconfig.board @@ -0,0 +1,23 @@ +CONFIG_FLASHMODE_QIO=y +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_ESPTOOLPY_FLASHSIZE_DETECT=y +CONFIG_ESPTOOLPY_AFTER_NORESET=y + +CONFIG_SPIRAM_MEMTEST= + +CONFIG_ESPTOOLPY_FLASHSIZE_4MB= +CONFIG_ESPTOOLPY_FLASHSIZE_8MB= +CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-16MiB.csv" + +CONFIG_LWIP_LOCAL_HOSTNAME="UMProS3" + +# CONFIG_TINYUSB_DESC_USE_ESPRESSIF_VID is not set +CONFIG_TINYUSB_DESC_CUSTOM_VID=0x303A +# CONFIG_TINYUSB_DESC_USE_DEFAULT_PID is not set +CONFIG_TINYUSB_DESC_CUSTOM_PID=0x80D4 +CONFIG_TINYUSB_DESC_BCD_DEVICE=0x0100 +CONFIG_TINYUSB_DESC_MANUFACTURER_STRING="Unexpected Maker" +CONFIG_TINYUSB_DESC_PRODUCT_STRING="ProS3" +CONFIG_TINYUSB_DESC_SERIAL_STRING="_ps3_" diff --git a/ports/esp32/boards/UM_TINYS3/board.json b/ports/esp32/boards/UM_TINYS3/board.json new file mode 100644 index 0000000000..73482a6284 --- /dev/null +++ b/ports/esp32/boards/UM_TINYS3/board.json @@ -0,0 +1,26 @@ +{ + "deploy": [ + "deploy.md" + ], + "docs": "", + "features": [ + "Battery Charging", + "RGB LED", + "SPIRAM", + "USB-C", + "WiFi", + "BLE" + ], + "features_non_filterable": [ + "TinyPICO Compatible" + ], + "id": "tinys3", + "images": [ + "unexpectedmaker_tinys3.jpg" + ], + "mcu": "esp32s3", + "product": "TinyS3", + "thumbnail": "", + "url": "https://tinys3.io", + "vendor": "Unexpected Maker" +} diff --git a/ports/esp32/boards/UM_TINYS3/board.md b/ports/esp32/boards/UM_TINYS3/board.md new file mode 100644 index 0000000000..da06e191f2 --- /dev/null +++ b/ports/esp32/boards/UM_TINYS3/board.md @@ -0,0 +1,2 @@ +The following files are daily firmware for the TinyS3. This firmware is +compiled using ESP-IDF v4.4 or later. diff --git a/ports/esp32/boards/UM_TINYS3/deploy.md b/ports/esp32/boards/UM_TINYS3/deploy.md new file mode 100644 index 0000000000..d65014e012 --- /dev/null +++ b/ports/esp32/boards/UM_TINYS3/deploy.md @@ -0,0 +1,52 @@ +Program your board using the latest version of the esptool.py program, found [here](https://github.com/espressif/esptool). + +To flash or erase your TinyS3, you have to first put it into download mode. +To do this, follow these steps: + +- Press and hold the [BOOT] button +- Press and release the [RESET] button +- Release the [BOOT] button + +Now the board is in download mode and the native USB will have enumerated as a serial device. + +If you are putting MicroPython on your board for the first time then you should +first erase the entire flash using: + +### Linux +```bash +esptool.py --chip esp32s3 --port /dev/ttyACM0 erase_flash +``` + +### Mac +Please do a `ls /dev/cu.usbm*` to determine the port your board has enumerated as. +```bash +esptool.py --chip esp32s3 --port /dev/cu.usbmodem01 erase_flash +``` + +### Windows +Change (X) to whatever COM port is being used by the board +```bash +esptool --chip esp32s3 --port COM(X) erase_flash +``` + +Now download the version of the firmware you would like to install from the options below, +then use the following command to program the firmware starting at address 0x0, +remembering to replace `tinys3-micropython-firmware-version.bin` with the name of +the firmware you just downloaded: + +### Linux +```bash +esptool.py --chip esp32s3 --port /dev/ttyACM0 write_flash -z 0x0 tinys3-micropython-firmware-version.bin +``` + +### Mac +Please do a `ls /dev/cu.usbm*` to determine the port your board has enumerated as. +```bash +esptool.py --chip esp32s3 --port /dev/cu.usbmodem01 write_flash -z 0x0 tinys3-micropython-firmware-version.bin +``` + +### Windows +Change (X) to whatever COM port is being used by the board +```bash +esptool --chip esp32s3 --port COM(X) write_flash -z 0x0 tinys3-micropython-firmware-version.bin +``` diff --git a/ports/esp32/boards/UM_TINYS3/manifest.py b/ports/esp32/boards/UM_TINYS3/manifest.py new file mode 100644 index 0000000000..7ae2ed15d9 --- /dev/null +++ b/ports/esp32/boards/UM_TINYS3/manifest.py @@ -0,0 +1,2 @@ +include("$(PORT_DIR)/boards/manifest.py") +freeze("modules") diff --git a/ports/esp32/boards/UM_TINYS3/modules/tinys3.py b/ports/esp32/boards/UM_TINYS3/modules/tinys3.py new file mode 100644 index 0000000000..4efcfe4b52 --- /dev/null +++ b/ports/esp32/boards/UM_TINYS3/modules/tinys3.py @@ -0,0 +1,66 @@ +# TinyS3 Helper Library +# MIT license; Copyright (c) 2022 Seon Rozenblum - Unexpected Maker +# +# Project home: +# https://tinys3.io + +# Import required libraries +from micropython import const +from machine import Pin, ADC +import time + +# TinyS3 Hardware Pin Assignments + +# Sense Pins +VBUS_SENSE = const(33) +VBAT_SENSE = const(10) + +# RGB LED Pins +RGB_DATA = const(18) +RGB_PWR = const(17) + +# SPI +SPI_MOSI = const(35) +SPI_MISO = const(37) +SPI_CLK = const(36) + +# I2C +I2C_SDA = const(8) +I2C_SCL = const(9) + +# Helper functions +def set_pixel_power(state): + """Enable or Disable power to the onboard NeoPixel to either show colour, or to reduce power for deep sleep.""" + Pin(RGB_PWR, Pin.OUT).value(state) + + +def get_battery_voltage(): + """ + Returns the current battery voltage. If no battery is connected, returns 4.2V which is the charge voltage + This is an approximation only, but useful to detect if the charge state of the battery is getting low. + """ + adc = ADC(Pin(VBAT_SENSE)) # Assign the ADC pin to read + measuredvbat = adc.read() + measuredvbat /= 4095 # divide by 4095 as we are using the default ADC attenuation of 0dB + measuredvbat *= 4.2 # Multiply by 4.2V, our max charge voltage for a 1S LiPo + return round(measuredvbat, 2) + + +def get_vbus_present(): + """Detect if VBUS (5V) power source is present""" + return Pin(VBUS_SENSE, Pin.IN).value() == 1 + + +# NeoPixel rainbow colour wheel +def rgb_color_wheel(wheel_pos): + """Color wheel to allow for cycling through the rainbow of RGB colors.""" + wheel_pos = wheel_pos % 255 + + if wheel_pos < 85: + return 255 - wheel_pos * 3, 0, wheel_pos * 3 + elif wheel_pos < 170: + wheel_pos -= 85 + return 0, wheel_pos * 3, 255 - wheel_pos * 3 + else: + wheel_pos -= 170 + return wheel_pos * 3, 255 - wheel_pos * 3, 0 diff --git a/ports/esp32/boards/UM_TINYS3/mpconfigboard.cmake b/ports/esp32/boards/UM_TINYS3/mpconfigboard.cmake new file mode 100644 index 0000000000..6c7f34009e --- /dev/null +++ b/ports/esp32/boards/UM_TINYS3/mpconfigboard.cmake @@ -0,0 +1,12 @@ +set(IDF_TARGET esp32s3) + +set(SDKCONFIG_DEFAULTS + boards/sdkconfig.base + boards/sdkconfig.usb + boards/sdkconfig.ble + boards/sdkconfig.240mhz + boards/sdkconfig.spiram_sx + boards/UM_TINYS3/sdkconfig.board +) + +set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py) diff --git a/ports/esp32/boards/UM_TINYS3/mpconfigboard.h b/ports/esp32/boards/UM_TINYS3/mpconfigboard.h new file mode 100644 index 0000000000..b2638a9847 --- /dev/null +++ b/ports/esp32/boards/UM_TINYS3/mpconfigboard.h @@ -0,0 +1,11 @@ +#define MICROPY_HW_BOARD_NAME "TinyS3" +#define MICROPY_HW_MCU_NAME "ESP32-S3-FN8" + +#define MICROPY_PY_MACHINE_DAC (0) + +#define MICROPY_HW_I2C0_SCL (9) +#define MICROPY_HW_I2C0_SDA (8) + +#define MICROPY_HW_SPI1_MOSI (35) +#define MICROPY_HW_SPI1_MISO (37) +#define MICROPY_HW_SPI1_SCK (36) diff --git a/ports/esp32/boards/UM_TINYS3/sdkconfig.board b/ports/esp32/boards/UM_TINYS3/sdkconfig.board new file mode 100644 index 0000000000..2b9ddbebe7 --- /dev/null +++ b/ports/esp32/boards/UM_TINYS3/sdkconfig.board @@ -0,0 +1,23 @@ +CONFIG_FLASHMODE_QIO=y +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_ESPTOOLPY_FLASHSIZE_DETECT=y +CONFIG_ESPTOOLPY_AFTER_NORESET=y + +CONFIG_SPIRAM_MEMTEST= + +CONFIG_ESPTOOLPY_FLASHSIZE_4MB= +CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y +CONFIG_ESPTOOLPY_FLASHSIZE_16MB= +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv" + +CONFIG_LWIP_LOCAL_HOSTNAME="UMTinyS3" + +# CONFIG_TINYUSB_DESC_USE_ESPRESSIF_VID is not set +CONFIG_TINYUSB_DESC_CUSTOM_VID=0x303A +# CONFIG_TINYUSB_DESC_USE_DEFAULT_PID is not set +CONFIG_TINYUSB_DESC_CUSTOM_PID=0x80D1 +CONFIG_TINYUSB_DESC_BCD_DEVICE=0x0100 +CONFIG_TINYUSB_DESC_MANUFACTURER_STRING="Unexpected Maker" +CONFIG_TINYUSB_DESC_PRODUCT_STRING="TinyS3" +CONFIG_TINYUSB_DESC_SERIAL_STRING="_ts3_" From 30a022548fcd20bf956789185e1481edb0f0acfc Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sat, 22 Jan 2022 20:37:50 +0200 Subject: [PATCH 108/619] tests/multi_net/udp_data.py: Make UDP test more reliable. The current test depends on a specific number and order of packets to pass, which can't be reproduced every run due to the unreliable UDP protocol. This patch adds simple packets sequencing, retransmits with timeouts, and a packet loss threshold, to make the test more tolerant to UDP protocol packet drops and reordering. --- tests/multi_net/udp_data.py | 52 ++++++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/tests/multi_net/udp_data.py b/tests/multi_net/udp_data.py index 0c0f165b74..8934d7df74 100644 --- a/tests/multi_net/udp_data.py +++ b/tests/multi_net/udp_data.py @@ -3,34 +3,68 @@ import socket NUM_NEW_SOCKETS = 4 -NUM_TRANSFERS = 4 +NUM_TRANSFERS = 10 +TOTAL_PACKETS = NUM_NEW_SOCKETS * NUM_TRANSFERS +# If more than 75% of packets are lost, the test fails. +PACKET_LOSS_THRESH = 0.75 * TOTAL_PACKETS PORT = 8000 + +def print_stats(seq): + if (TOTAL_PACKETS - seq) > PACKET_LOSS_THRESH: + print( + "packet loss %.1f%% %d/%d" + % (((TOTAL_PACKETS - seq) / TOTAL_PACKETS * 100), seq, TOTAL_PACKETS) + ) + else: + print("pass") + + # Server def instance0(): + seq = 0 multitest.globals(IP=multitest.get_network_ip()) multitest.next() for i in range(NUM_NEW_SOCKETS): 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]) + s.bind(socket.getaddrinfo("0.0.0.0", PORT + i)[0][-1]) + s.settimeout(0.250) multitest.broadcast("server ready") for j in range(NUM_TRANSFERS): - data, addr = s.recvfrom(1000) - print(data) - s.sendto(b"server to client %d %d" % (i, j), addr) + try: + data, addr = s.recvfrom(1000) + except: + continue + if int(data) == seq: + if seq < (TOTAL_PACKETS - PACKET_LOSS_THRESH): + print(seq) + seq += 1 + s.sendto(b"%d" % (seq), addr) s.close() + print_stats(seq) + # Client def instance1(): + seq = 0 multitest.next() - ai = socket.getaddrinfo(IP, PORT)[0][-1] for i in range(NUM_NEW_SOCKETS): + ai = socket.getaddrinfo(IP, PORT + i)[0][-1] s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.settimeout(0.250) multitest.wait("server ready") for j in range(NUM_TRANSFERS): - s.sendto(b"client to server %d %d" % (i, j), ai) - data, addr = s.recvfrom(1000) - print(data) + s.sendto(b"%d" % (seq), ai) + try: + data, addr = s.recvfrom(1000) + except: + continue + if int(data) == seq + 1: + if seq < (TOTAL_PACKETS - PACKET_LOSS_THRESH): + print(seq) + seq += 1 s.close() + + print_stats(seq) From a41abd94dcbb5c36cd3a66ae000fa02634472a6c Mon Sep 17 00:00:00 2001 From: Jos Verlinde Date: Mon, 12 Jul 2021 17:47:49 +0200 Subject: [PATCH 109/619] docs/library/collections.rst: Use class for deque and OrderedDict. --- docs/library/collections.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/library/collections.rst b/docs/library/collections.rst index 21f06fded7..6cf2c096ff 100644 --- a/docs/library/collections.rst +++ b/docs/library/collections.rst @@ -12,7 +12,7 @@ hold/accumulate various objects. Classes ------- -.. function:: deque(iterable, maxlen[, flags]) +.. class:: deque(iterable, maxlen[, flags]) Deques (double-ended queues) are a list-like container that support O(1) appends and pops from either side of the deque. New deques are created @@ -57,7 +57,7 @@ Classes print(t1.name) assert t2.name == t2[1] -.. function:: OrderedDict(...) +.. class:: OrderedDict(...) ``dict`` type subclass which remembers and preserves the order of keys added. When ordered dict is iterated over, keys/items are returned in From cd0531c533c9e3bb6e96ec8d994ae272b58503c6 Mon Sep 17 00:00:00 2001 From: Jos Verlinde Date: Mon, 12 Jul 2021 18:04:56 +0200 Subject: [PATCH 110/619] docs: Use the correct * keyword-only notation. --- docs/library/machine.PWM.rst | 4 ++-- docs/library/pyb.Pin.rst | 2 +- docs/wipy/general.rst | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/library/machine.PWM.rst b/docs/library/machine.PWM.rst index 4c72255d81..793c074a36 100644 --- a/docs/library/machine.PWM.rst +++ b/docs/library/machine.PWM.rst @@ -23,7 +23,7 @@ Example usage:: Constructors ------------ -.. class:: PWM(dest, \*, freq, duty_u16, duty_ns) +.. class:: PWM(dest, *, freq, duty_u16, duty_ns) Construct and return a new PWM object using the following parameters: @@ -42,7 +42,7 @@ Constructors Methods ------- -.. method:: PWM.init(\*, freq, duty_u16, duty_ns) +.. method:: PWM.init(*, freq, duty_u16, duty_ns) Modify settings for the PWM object. See the above constructor for details about the parameters. diff --git a/docs/library/pyb.Pin.rst b/docs/library/pyb.Pin.rst index 23ede48ed3..33b994e390 100644 --- a/docs/library/pyb.Pin.rst +++ b/docs/library/pyb.Pin.rst @@ -98,7 +98,7 @@ Class methods Methods ------- -.. method:: Pin.init(mode, pull=Pin.PULL_NONE, \*, value=None, alt=-1) +.. method:: Pin.init(mode, pull=Pin.PULL_NONE, *, value=None, alt=-1) Initialise the pin: diff --git a/docs/wipy/general.rst b/docs/wipy/general.rst index 7f24435db3..a1c8df8ef4 100644 --- a/docs/wipy/general.rst +++ b/docs/wipy/general.rst @@ -345,7 +345,7 @@ Example:: Create a server instance, see ``init`` for parameters of initialization. -.. method:: server.init(\*, login=('micro', 'python'), timeout=300) +.. method:: server.init(*, login=('micro', 'python'), timeout=300) Init (and effectively start the server). Optionally a new ``user``, ``password`` and ``timeout`` (in seconds) can be passed. @@ -368,7 +368,7 @@ Adhoc VFS-like support WiPy doesn't implement full MicroPython VFS support, instead following functions are defined in ``os`` module: -.. function:: mount(block_device, mount_point, \*, readonly=False) +.. function:: mount(block_device, mount_point, *, readonly=False) Mounts a block device (like an ``SD`` object) in the specified mount point. Example:: From 2c30ac7aeb0694ea0b4721706639ca7781de34f5 Mon Sep 17 00:00:00 2001 From: Jos Verlinde Date: Tue, 27 Jul 2021 00:11:11 +0200 Subject: [PATCH 111/619] docs/library/socket.rst: Document socket as a class. Following CPython: https://bugs.python.org/issue45772 --- docs/library/socket.rst | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/library/socket.rst b/docs/library/socket.rst index 704b614ab1..1d1c23abd1 100644 --- a/docs/library/socket.rst +++ b/docs/library/socket.rst @@ -66,19 +66,6 @@ Tuple address format for ``socket`` module: Functions --------- -.. function:: socket(af=AF_INET, type=SOCK_STREAM, proto=IPPROTO_TCP, /) - - Create a new socket using the given address family, socket type and - protocol number. Note that specifying *proto* in most cases is not - required (and not recommended, as some MicroPython ports may omit - ``IPPROTO_*`` constants). Instead, *type* argument will select needed - protocol automatically:: - - # Create STREAM TCP socket - socket(AF_INET, SOCK_STREAM) - # Create DGRAM UDP socket - socket(AF_INET, SOCK_DGRAM) - .. function:: getaddrinfo(host, port, af=0, type=0, proto=0, flags=0, /) Translate the host/port argument into a sequence of 5-tuples that contain all the @@ -176,6 +163,19 @@ Constants specific to WiPy: class socket ============ +.. class:: socket(af=AF_INET, type=SOCK_STREAM, proto=IPPROTO_TCP, /) + + Create a new socket using the given address family, socket type and + protocol number. Note that specifying *proto* in most cases is not + required (and not recommended, as some MicroPython ports may omit + ``IPPROTO_*`` constants). Instead, *type* argument will select needed + protocol automatically:: + + # Create STREAM TCP socket + socket(AF_INET, SOCK_STREAM) + # Create DGRAM UDP socket + socket(AF_INET, SOCK_DGRAM) + Methods ------- From 50b172023f27e571ffdc250a7e90916b46b8feca Mon Sep 17 00:00:00 2001 From: Jos Verlinde Date: Tue, 21 Sep 2021 18:26:39 +0200 Subject: [PATCH 112/619] docs/library/pyb.Timer.rst: Add pyb.Timer class constants. --- docs/library/pyb.Timer.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/library/pyb.Timer.rst b/docs/library/pyb.Timer.rst index 34fe71155f..cc7a01f580 100644 --- a/docs/library/pyb.Timer.rst +++ b/docs/library/pyb.Timer.rst @@ -262,3 +262,12 @@ Methods for which the pulse is active. The value can be an integer or floating-point number for more accuracy. For example, a value of 25 gives a duty cycle of 25%. + +Constants +--------- + +.. data:: Timer.UP + Timer.DOWN + Timer.CENTER + + Configures the timer to count Up, Down, or from 0 to ARR and then back down to 0. From ba4f6f5fdf949924b44bf54291f6a860d6e36bff Mon Sep 17 00:00:00 2001 From: Jos Verlinde Date: Tue, 21 Sep 2021 18:37:01 +0200 Subject: [PATCH 113/619] docs/library/pyb.rst: Add pyb.hid_mouse and pyb.hid_keyboard constants. --- docs/library/pyb.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/library/pyb.rst b/docs/library/pyb.rst index 880d68c531..c8ef2c5315 100644 --- a/docs/library/pyb.rst +++ b/docs/library/pyb.rst @@ -298,6 +298,15 @@ Miscellaneous functions The *high_speed* parameter, when set to ``True``, enables USB HS mode if it is supported by the hardware. +Constants +--------- + +.. data:: pyb.hid_mouse + pyb.hid_keyboard + + A tuple of (subclass, protocol, max packet length, polling interval, report + descriptor) to set appropriate values for a USB mouse or keyboard. + Classes ------- From 8715ad9ccfc45c25883fb1a7587c9f81da8133ab Mon Sep 17 00:00:00 2001 From: Jos Verlinde Date: Tue, 21 Sep 2021 21:58:19 +0200 Subject: [PATCH 114/619] docs/library/pyb.DAC.rst: Add DAC class constants. --- docs/library/pyb.DAC.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/library/pyb.DAC.rst b/docs/library/pyb.DAC.rst index bf07119ada..0eb469bef4 100644 --- a/docs/library/pyb.DAC.rst +++ b/docs/library/pyb.DAC.rst @@ -122,3 +122,15 @@ Methods dac2 = DAC(2) dac1.write_timed(buf1, pyb.Timer(6, freq=100), mode=DAC.CIRCULAR) dac2.write_timed(buf2, pyb.Timer(7, freq=200), mode=DAC.CIRCULAR) + +Constants +--------- + +.. data:: DAC.NORMAL + + NORMAL mode does a single transmission of the waveform in the data buffer, + +.. data:: DAC.CIRCULAR + + CIRCULAR mode does a transmission of the waveform in the data buffer, and wraps around + to the start of the data buffer every time it reaches the end of the table. From 7621b176365c8b2b6187861dbb1f20ffe4c68335 Mon Sep 17 00:00:00 2001 From: Jos Verlinde Date: Wed, 22 Sep 2021 15:47:27 +0200 Subject: [PATCH 115/619] docs/library/machine.SPI.rst: Add class constant SoftSPI.MSB and .LSB. --- docs/library/machine.SPI.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/library/machine.SPI.rst b/docs/library/machine.SPI.rst index 1116f0e8a3..7b0e8cf406 100644 --- a/docs/library/machine.SPI.rst +++ b/docs/library/machine.SPI.rst @@ -143,9 +143,11 @@ Constants for initialising the SPI bus to controller; this is only used for the WiPy .. data:: SPI.MSB + SoftSPI.MSB set the first bit to be the most significant bit .. data:: SPI.LSB + SoftSPI.LSB set the first bit to be the least significant bit From f99b6799167a6cce97a7a683825f6ea27a892e6e Mon Sep 17 00:00:00 2001 From: Jos Verlinde Date: Sun, 26 Sep 2021 23:14:56 +0200 Subject: [PATCH 116/619] docs/library/machine.WDT.rst: Use correct case for WDT.feed. --- docs/library/machine.WDT.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/library/machine.WDT.rst b/docs/library/machine.WDT.rst index 3c799583e6..8c81e10ea5 100644 --- a/docs/library/machine.WDT.rst +++ b/docs/library/machine.WDT.rst @@ -31,7 +31,7 @@ Constructors Methods ------- -.. method:: wdt.feed() +.. method:: WDT.feed() Feed the WDT to prevent it from resetting the system. The application should place this call in a sensible place ensuring that the WDT is From 58cd2a8b0a16ba65b7390e425d37a825141735d3 Mon Sep 17 00:00:00 2001 From: Jos Verlinde Date: Mon, 7 Feb 2022 18:34:57 +0100 Subject: [PATCH 117/619] docs/library/esp.rst: Document the osdebug function. --- docs/library/esp.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/library/esp.rst b/docs/library/esp.rst index 5fb370065f..8920c8241e 100644 --- a/docs/library/esp.rst +++ b/docs/library/esp.rst @@ -62,6 +62,21 @@ Functions .. function:: flash_erase(sector_no) +.. function:: osdebug(level) + + Turn esp os debugging messages on or off. + + The *level* parameter sets the threshold for the log messages for all esp components. + The log levels are defined as constants: + + * ``LOG_NONE`` -- No log output + * ``LOG_ERROR`` -- Critical errors, software module can not recover on its own + * ``LOG_WARN`` -- Error conditions from which recovery measures have been taken + * ``LOG_INFO`` -- Information messages which describe normal flow of events + * ``LOG_DEBUG`` -- Extra information which is not necessary for normal use (values, pointers, sizes, etc) + * ``LOG_VERBOSE`` -- Bigger chunks of debugging information, or frequent messages + which can potentially flood the output + .. function:: set_native_code_location(start, length) **Note**: ESP8266 only From 7f67524031a04d972ba59a50e5e320adadbcb893 Mon Sep 17 00:00:00 2001 From: Jos Verlinde Date: Mon, 7 Feb 2022 19:11:47 +0100 Subject: [PATCH 118/619] docs/library/pyb.SPI.rst: Document default for prescaler argument. To prevent "non-default argument follows default argument" errors. --- docs/library/pyb.SPI.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/library/pyb.SPI.rst b/docs/library/pyb.SPI.rst index 1bdb73a5dd..14b90a2240 100644 --- a/docs/library/pyb.SPI.rst +++ b/docs/library/pyb.SPI.rst @@ -51,7 +51,7 @@ Methods Turn off the SPI bus. -.. method:: SPI.init(mode, baudrate=328125, *, prescaler, polarity=1, phase=0, bits=8, firstbit=SPI.MSB, ti=False, crc=None) +.. method:: SPI.init(mode, baudrate=328125, *, prescaler=-1, polarity=1, phase=0, bits=8, firstbit=SPI.MSB, ti=False, crc=None) Initialise the SPI bus with the given parameters: From 6653856b87a76adc5584bb72b32924719effa970 Mon Sep 17 00:00:00 2001 From: Jos Verlinde Date: Mon, 7 Feb 2022 22:13:26 +0100 Subject: [PATCH 119/619] docs/library/machine.Pin.rst: Document defaults for Pin.init. --- docs/library/machine.Pin.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/library/machine.Pin.rst b/docs/library/machine.Pin.rst index 32fa05b49c..f80de11784 100644 --- a/docs/library/machine.Pin.rst +++ b/docs/library/machine.Pin.rst @@ -42,7 +42,7 @@ Usage Model:: Constructors ------------ -.. class:: Pin(id, mode=-1, pull=-1, *, value, drive, alt) +.. class:: Pin(id, mode=-1, pull=-1, *, value=None, drive=0, alt=-1) Access the pin peripheral (GPIO pin) associated with the given ``id``. If additional arguments are given in the constructor then they are used to initialise @@ -108,7 +108,7 @@ Constructors Methods ------- -.. method:: Pin.init(mode=-1, pull=-1, *, value, drive, alt) +.. method:: Pin.init(mode=-1, pull=-1, *, value=None, drive=0, alt=-1) Re-initialise the pin using the given parameters. Only those arguments that are specified will be set. The rest of the pin peripheral state will remain From 8f6924c9fb061742039554a3b5840f6e92aff7f2 Mon Sep 17 00:00:00 2001 From: Lars Kellogg-Stedman Date: Thu, 3 Feb 2022 23:03:23 -0500 Subject: [PATCH 120/619] docs/library/uasyncio.rst: Fix description of ThreadSafeFlag.wait. When a task waits on a ThreadSafeFlag (and the wait method returns), the flag is immediately reset. This was not clear in the documentation, which appeared to copy the description of the wait method from the Event class. Signed-off-by: Lars Kellogg-Stedman --- docs/library/uasyncio.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/library/uasyncio.rst b/docs/library/uasyncio.rst index 1fc8b53db0..a842cc64d4 100644 --- a/docs/library/uasyncio.rst +++ b/docs/library/uasyncio.rst @@ -159,7 +159,7 @@ class ThreadSafeFlag .. method:: ThreadSafeFlag.wait() Wait for the flag to be set. If the flag is already set then it returns - immediately. + immediately. The flag is automatically reset upon return from ``wait``. A flag may only be waited on by a single task at a time. From d8a7bf83ccf28d0e8acf9790a1fc38aa5d13a2e5 Mon Sep 17 00:00:00 2001 From: YoungJoon Chun Date: Thu, 3 Feb 2022 22:41:45 +0900 Subject: [PATCH 121/619] rp2/machine_uart: Fix UART RTS behaviour so RTS is deasserted. The UART hardware flow control was not working correctly, the receive FIFO was always fetched and RTS was never deasserted. This is not a problem when hardware flow control is not used: normally, if the receive FIFO is full, the UART receiver won't receive data into the FIFO anymore, but the current implementation fetches from the FIFO and discards it instead. The problem is that data is discarded even when RTS is enabled. This commit fixes the issue by only taking from the FIFO if there is room in the ring buffer to put the character. Signed-off-by: YoungJoon Chun --- ports/rp2/machine_uart.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ports/rp2/machine_uart.c b/ports/rp2/machine_uart.c index 283c12ed54..0642f704f7 100644 --- a/ports/rp2/machine_uart.c +++ b/ports/rp2/machine_uart.c @@ -109,10 +109,10 @@ STATIC const char *_invert_name[] = {"None", "INV_TX", "INV_RX", "INV_TX|INV_RX" /******************************************************************************/ // IRQ and buffer handling -// take all bytes from the fifo and store them, if possible, in the buffer +// take all bytes from the fifo and store them in the buffer STATIC void uart_drain_rx_fifo(machine_uart_obj_t *self) { - while (uart_is_readable(self->uart)) { - // try to write the data, ignore the fail + while (uart_is_readable(self->uart) && ringbuf_free(&self->read_buffer) > 0) { + // get a byte from uart and put into the buffer ringbuf_put(&(self->read_buffer), uart_get_hw(self->uart)->dr); } } From b33fdbe5357ae224d668b827958fbbd04b507540 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 10 Feb 2022 14:52:51 +1100 Subject: [PATCH 122/619] tests/run-perfbench.py: Allow a test to SKIP, and to have a .exp file. Signed-off-by: Damien George --- tests/run-perfbench.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/run-perfbench.py b/tests/run-perfbench.py index 5f299281fd..fccb7a7684 100755 --- a/tests/run-perfbench.py +++ b/tests/run-perfbench.py @@ -74,6 +74,8 @@ def run_feature_test(target, test): def run_benchmark_on_target(target, script): output, err = run_script_on_target(target, script) if err is None: + if output == "SKIP": + return -1, -1, "SKIP" time, norm, result = output.split(None, 2) try: return int(time), int(norm), result @@ -133,7 +135,14 @@ def run_benchmarks(target, param_n, param_m, n_average, test_list): # Check result against truth if needed if error is None and result_out != "None": - _, _, result_exp = run_benchmark_on_target(PYTHON_TRUTH, test_script) + test_file_expected = test_file + ".exp" + if os.path.isfile(test_file_expected): + # Expected result is given by a file, so read that in + with open(test_file_expected) as f: + result_exp = f.read().strip() + else: + # Run CPython to work out the expected result + _, _, result_exp = run_benchmark_on_target(PYTHON_TRUTH, test_script) if result_out != result_exp: error = "FAIL truth" From 75da124cf856d020efb17efbcba97543c5e7435c Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 10 Feb 2022 14:53:29 +1100 Subject: [PATCH 123/619] tests/perf_bench: Add perf tests for qstr interning and importing .mpy. Signed-off-by: Damien George --- tests/perf_bench/core_import_mpy_multi.py | 85 +++++++++++ tests/perf_bench/core_import_mpy_multi.py.exp | 1 + tests/perf_bench/core_import_mpy_single.py | 135 ++++++++++++++++++ .../perf_bench/core_import_mpy_single.py.exp | 1 + tests/perf_bench/core_qstr.py | 21 +++ 5 files changed, 243 insertions(+) create mode 100644 tests/perf_bench/core_import_mpy_multi.py create mode 100644 tests/perf_bench/core_import_mpy_multi.py.exp create mode 100644 tests/perf_bench/core_import_mpy_single.py create mode 100644 tests/perf_bench/core_import_mpy_single.py.exp create mode 100644 tests/perf_bench/core_qstr.py diff --git a/tests/perf_bench/core_import_mpy_multi.py b/tests/perf_bench/core_import_mpy_multi.py new file mode 100644 index 0000000000..0da466206b --- /dev/null +++ b/tests/perf_bench/core_import_mpy_multi.py @@ -0,0 +1,85 @@ +# Test performance of importing an .mpy file many times. + +import usys, uio, uos + +if not (hasattr(uio, "IOBase") and hasattr(uos, "mount")): + print("SKIP") + raise SystemExit + +# This is the test.py file that is compiled to test.mpy below. +""" +class A: + def __init__(self, arg): + self.arg = arg + def write(self): + pass + def read(self): + pass +def f(): + print, str, bytes, dict + Exception, ValueError, TypeError + x = "this will be a string object" + x = b"this will be a bytes object" + x = ("const tuple", None, False, True, 1, 2, 3) +result = 123 +""" +file_data = b'M\x05\x02\x1f \x81\x0c\x10\x12\x00\x07\x0etest.py\x8b\x07e`\x00T2\x00\x10\x02A4\x02\x16\x012\x01\x16\x02f"\x80{\x16\x0cresultQc\x00\x02\x81\x1c\x00\x10\x05\x07,EE\x00\x11\x00\x17\x16\x00\x16\x10\x03\x16\x00\x1a2\x00\x16\x00\x112\x01\x16\x00\xa42\x02\x16\x00}Qc\x00\x03<\x1a\x0c\x00\x11\x03@\x00\xb1\xb0\x18\x06argQc\x00\x00\x00\x89\x01,\t\x0e\x00\xa4\x03` \x00Qc\x00\x00\x00\x89,\t\x0e\x00}\x01``\x00Qc\x00\x00\x00\x89\x81l8\x16\t\x03\x80\x08/,##\x00\x12\x00{\x12\x00\x97\x12\x00B\x12\x00K*\x04Y\x12\x00$\x12\x007\x12\x006*\x03Y#\x00\xc0#\x01\xc0#\x02QPR\x81\x82\x83*\x07\xc0Qc\x03\x00s\x1cthis will be a string objectb\x1bthis will be a bytes objects\x0bconst tuple' + + +class File(uio.IOBase): + def __init__(self): + self.off = 0 + + def ioctl(self, request, arg): + return 0 + + def readinto(self, buf): + buf[:] = memoryview(file_data)[self.off : self.off + len(buf)] + self.off += len(buf) + return len(buf) + + +class FS: + def mount(self, readonly, mkfs): + pass + + def chdir(self, path): + pass + + def stat(self, path): + if path == "__injected.mpy": + return tuple(0 for _ in range(10)) + else: + raise OSError(-2) # ENOENT + + def open(self, path, mode): + return File() + + +def mount(): + uos.mount(FS(), "/__remote") + uos.chdir("/__remote") + + +def test(r): + global result + for _ in r: + usys.modules.clear() + module = __import__("__injected") + result = module.result + + +########################################################################### +# Benchmark interface + +bm_params = { + (100, 10): (50,), + (1000, 10): (500,), + (5000, 10): (5000,), +} + + +def bm_setup(params): + (nloop,) = params + mount() + return lambda: test(range(nloop)), lambda: (nloop, result) diff --git a/tests/perf_bench/core_import_mpy_multi.py.exp b/tests/perf_bench/core_import_mpy_multi.py.exp new file mode 100644 index 0000000000..190a18037c --- /dev/null +++ b/tests/perf_bench/core_import_mpy_multi.py.exp @@ -0,0 +1 @@ +123 diff --git a/tests/perf_bench/core_import_mpy_single.py b/tests/perf_bench/core_import_mpy_single.py new file mode 100644 index 0000000000..5ca3584959 --- /dev/null +++ b/tests/perf_bench/core_import_mpy_single.py @@ -0,0 +1,135 @@ +# Test performance of importing an .mpy file just once. +# The first import of a module will intern strings that don't already exist, and +# this test should be representative of what happens in a real application. + +import uio, uos + +if not (hasattr(uio, "IOBase") and hasattr(uos, "mount")): + print("SKIP") + raise SystemExit + +# This is the test.py file that is compiled to test.mpy below. +# Many known and unknown names/strings are included to test the linking process. +""" +class A0: + def a0(self): pass + def a1(self): pass + def a2(self): pass + def a3(self): pass +class A1: + def a0(self): pass + def a1(self): pass + def a2(self): pass + def a3(self): pass +def f0(): + __call__, __class__, __delitem__, __enter__, __exit__, __getattr__, __getitem__, + __hash__, __init__, __int__, __iter__, __len__, __main__, __module__, __name__, + __new__, __next__, __qualname__, __repr__, __setitem__, __str__, + ArithmeticError, AssertionError, AttributeError, BaseException, EOFError, Ellipsis, + Exception, GeneratorExit, ImportError, IndentationError, IndexError, KeyError, + KeyboardInterrupt, LookupError, MemoryError, NameError, NoneType, + NotImplementedError, OSError, OverflowError, RuntimeError, StopIteration, + SyntaxError, SystemExit, TypeError, ValueError, ZeroDivisionError, + abs, all, any, append, args, bool, builtins, bytearray, bytecode, bytes, callable, + chr, classmethod, clear, close, const, copy, count, dict, dir, divmod, end, + endswith, eval, exec, extend, find, format, from_bytes, get, getattr, globals, + hasattr, hash, id, index, insert, int, isalpha, isdigit, isinstance, islower, + isspace, issubclass, isupper, items, iter, join, key, keys, len, list, little, + locals, lower, lstrip, main, map, micropython, next, object, open, ord, pop, + popitem, pow, print, range, read, readinto, readline, remove, replace, repr, + reverse, rfind, rindex, round, rsplit, rstrip, self, send, sep, set, setattr, + setdefault, sort, sorted, split, start, startswith, staticmethod, step, stop, str, + strip, sum, super, throw, to_bytes, tuple, type, update, upper, value, values, + write, zip, + name0, name1, name2, name3, name4, name5, name6, name7, name8, name9, + quite_a_long_name0, quite_a_long_name1, quite_a_long_name2, quite_a_long_name3, + quite_a_long_name4, quite_a_long_name5, quite_a_long_name6, quite_a_long_name7, + quite_a_long_name8, quite_a_long_name9, quite_a_long_name10, quite_a_long_name11, +def f1(): + x = "this will be a string object 0" + x = "this will be a string object 1" + x = "this will be a string object 2" + x = "this will be a string object 3" + x = "this will be a string object 4" + x = "this will be a string object 5" + x = "this will be a string object 6" + x = "this will be a string object 7" + x = "this will be a string object 8" + x = "this will be a string object 9" + x = b"this will be a bytes object 0" + x = b"this will be a bytes object 1" + x = b"this will be a bytes object 2" + x = b"this will be a bytes object 3" + x = b"this will be a bytes object 4" + x = b"this will be a bytes object 5" + x = b"this will be a bytes object 6" + x = b"this will be a bytes object 7" + x = b"this will be a bytes object 8" + x = b"this will be a bytes object 9" + x = ("const tuple 0", None, False, True, 1, 2, 3) + x = ("const tuple 1", None, False, True, 1, 2, 3) + x = ("const tuple 2", None, False, True, 1, 2, 3) + x = ("const tuple 3", None, False, True, 1, 2, 3) + x = ("const tuple 4", None, False, True, 1, 2, 3) + x = ("const tuple 5", None, False, True, 1, 2, 3) + x = ("const tuple 6", None, False, True, 1, 2, 3) + x = ("const tuple 7", None, False, True, 1, 2, 3) + x = ("const tuple 8", None, False, True, 1, 2, 3) + x = ("const tuple 9", None, False, True, 1, 2, 3) +result = 123 +""" +file_data = b'M\x05\x02\x1f \x81\\\x10\x1a\x00\x07\x0etest.pyk@k@\x85\x18\x85\x1f\x00T2\x00\x10\x04A04\x02\x16\x01T2\x01\x10\x04A14\x02\x16\x012\x02\x16\x04f02\x03\x16\x04f1"\x80{\x16\x0cresultQc\x00\x04\x814\x00\x12\t\x0b,%%%\x00\x11\x00\x17\x16\x00\x16\x10\x03\x16\x00\x1a2\x00\x16\x04a02\x01\x16\x04a12\x02\x16\x04a22\x03\x16\x04a3Qc\x00\x04(\t\x0c\x07\x0b \x00Qc\x00\x00\x00\x89(\t\x0c\t\x03@\x00Qc\x00\x00\x00\x89(\t\x0c\t\x03`\x00Qc\x00\x00\x00\x89,\t\x0e\t\x03` \x00Qc\x00\x00\x00\x89\x818\x00\x14\x13\x03l`%%%\x00\x11\x00\x17\x16\x00\x16\x10\x03\x16\x00\x1a2\x00\x16\x0b2\x01\x16\x0b2\x02\x16\x0b2\x03\x16\x0bQc\x00\x04,\t\x0e\x07\x0b``\x00Qc\x00\x00\x00\x89,\t\x0e\t\x03\x80\x07\x00Qc\x00\x00\x00\x89,\t\x0e\t\x03\x80\x08\x00Qc\x00\x00\x00\x89,\t\x0e\t\x03\x80\t\x00Qc\x00\x00\x00\x89\x94\x1cPP\x13\x03\x80\x0b8;555222\x1f%\x1f%\x1f"\x1f"\x1f%\x1f%\x1f"\x1f%\x1f"\x1f%)\x1f"//\x00\x12\x00\t\x12\x00\n\x12\x00\x0b\x12\x00\x0c\x12\x00\r\x12\x00\x0e\x12\x00\x0f*\x07Y\x12\x00\x10\x12\x00\x11\x12\x00\x12\x12\x00\x13\x12\x00\x14\x12\x00\x15\x12\x00\x16\x12\x00\x17*\x08Y\x12\x00\x18\x12\x00\x19\x12\x00\x1a\x12\x00\x1b\x12\x00\x1c\x12\x00\x1d*\x06Y\x12\x00\x1e\x12\x00\x1f\x12\x00 \x12\x00!\x12\x00"\x12\x00#*\x06Y\x12\x00$\x12\x00%\x12\x00&\x12\x00\'\x12\x00(\x12\x00)*\x06Y\x12\x00*\x12\x00+\x12\x00,\x12\x00-\x12\x00.*\x05Y\x12\x00/\x12\x000\x12\x001\x12\x002\x12\x003*\x05Y\x12\x004\x12\x005\x12\x006\x12\x007\x12\x008*\x05Y\x12\x009\x12\x00:\x12\x00;\x12\x00<\x12\x00=\x12\x00>\x12\x00?\x12\x00@\x12\x00A\x12\x00B\x12\x00C*\x0bY\x12\x00D\x12\x00E\x12\x00F\x12\x00G\x12\x00H\x12\x00I\x12\x00J\x12\x00K\x12\x00L\x12\x00M\x12\x00N*\x0bY\x12\x00O\x12\x00P\x12\x00Q\x12\x00R\x12\x00S\x12\x00T\x12\x00U\x12\x00V\x12\x00W\x12\x00X*\nY\x12\x00Y\x12\x00Z\x12\x00[\x12\x00\\\x12\x00]\x12\x00^\x12\x00_\x12\x00`\x12\x00a\x12\x00b*\nY\x12\x00c\x12\x00d\x12\x00e\x12\x00f\x12\x00g\x12\x00h\x12\x00i\x12\x00j\x12\x00k\x12\x00l\x12\x00m*\x0bY\x12\x00n\x12\x00o\x12\x00p\x12\x00q\x12\x00r\x12\x00s\x12\x00t\x12\x00u\x12\x00v\x12\x00w\x12\x00x*\x0bY\x12\x00y\x12\x00z\x12\x00{\x12\x00|\x12\x00}\x12\x00~\x12\x00\x7f\x12\x00\x80\x12\x00\x81\x12\x00\x82*\nY\x12\x00\x83\x12\x00\x84\x12\x00\x85\x12\x00\x86\x12\x00\x87\x12\x00\x88\x12\x00\x89\x12\x00\x8a\x12\x00\x8b\x12\x00\x8c\x12\x00\x8d*\x0bY\x12\x00\x8e\x12\x00\x8f\x12\x00\x90\x12\x00\x91\x12\x00\x92\x12\x00\x93\x12\x00\x94\x12\x00\x95\x12\x00\x96\x12\x00\x97*\nY\x12\x00\x98\x12\x00\x99\x12\x00\x9a\x12\x00\x9b\x12\x00\x9c\x12\x00\x9d\x12\x00\x9e\x12\x00\x9f\x12\x00\xa0\x12\x00\xa2\x12\x00\xa3*\x0bY\x12\x00\xa4\x12\x00\xa5*\x02Y\x12\nname0\x12\nname1\x12\nname2\x12\nname3\x12\nname4\x12\nname5\x12\nname6\x12\nname7\x12\nname8\x12\nname9*\nY\x12$quite_a_long_name0\x12$quite_a_long_name1\x12$quite_a_long_name2\x12$quite_a_long_name3*\x04Y\x12$quite_a_long_name4\x12$quite_a_long_name5\x12$quite_a_long_name6\x12$quite_a_long_name7*\x04Y\x12$quite_a_long_name8\x12$quite_a_long_name9\x12&quite_a_long_name10\x12&quite_a_long_name11*\x04YQc\x00\x00\x86H8H?/\x80#####################+++++++++\x00#\x00\xc0#\x01\xc0#\x02\xc0#\x03\xc0#\x04\xc0#\x05\xc0#\x06\xc0#\x07\xc0#\x08\xc0#\t\xc0#\n\xc0#\x0b\xc0#\x0c\xc0#\r\xc0#\x0e\xc0#\x0f\xc0#\x10\xc0#\x11\xc0#\x12\xc0#\x13\xc0#\x14QPR\x81\x82\x83*\x07\xc0#\x15QPR\x81\x82\x83*\x07\xc0#\x16QPR\x81\x82\x83*\x07\xc0#\x17QPR\x81\x82\x83*\x07\xc0#\x18QPR\x81\x82\x83*\x07\xc0#\x19QPR\x81\x82\x83*\x07\xc0#\x1aQPR\x81\x82\x83*\x07\xc0#\x1bQPR\x81\x82\x83*\x07\xc0#\x1cQPR\x81\x82\x83*\x07\xc0#\x1dQPR\x81\x82\x83*\x07\xc0Qc\x1e\x00s\x1ethis will be a string object 0s\x1ethis will be a string object 1s\x1ethis will be a string object 2s\x1ethis will be a string object 3s\x1ethis will be a string object 4s\x1ethis will be a string object 5s\x1ethis will be a string object 6s\x1ethis will be a string object 7s\x1ethis will be a string object 8s\x1ethis will be a string object 9b\x1dthis will be a bytes object 0b\x1dthis will be a bytes object 1b\x1dthis will be a bytes object 2b\x1dthis will be a bytes object 3b\x1dthis will be a bytes object 4b\x1dthis will be a bytes object 5b\x1dthis will be a bytes object 6b\x1dthis will be a bytes object 7b\x1dthis will be a bytes object 8b\x1dthis will be a bytes object 9s\rconst tuple 0s\rconst tuple 1s\rconst tuple 2s\rconst tuple 3s\rconst tuple 4s\rconst tuple 5s\rconst tuple 6s\rconst tuple 7s\rconst tuple 8s\rconst tuple 9' + + +class File(uio.IOBase): + def __init__(self): + self.off = 0 + + def ioctl(self, request, arg): + return 0 + + def readinto(self, buf): + buf[:] = memoryview(file_data)[self.off : self.off + len(buf)] + self.off += len(buf) + return len(buf) + + +class FS: + def mount(self, readonly, mkfs): + pass + + def chdir(self, path): + pass + + def stat(self, path): + if path == "__injected.mpy": + return tuple(0 for _ in range(10)) + else: + raise OSError(-2) # ENOENT + + def open(self, path, mode): + return File() + + +def mount(): + uos.mount(FS(), "/__remote") + uos.chdir("/__remote") + + +def test(): + global result + module = __import__("__injected") + result = module.result + + +########################################################################### +# Benchmark interface + +bm_params = { + (1, 1): (), +} + + +def bm_setup(params): + mount() + return lambda: test(), lambda: (1, result) diff --git a/tests/perf_bench/core_import_mpy_single.py.exp b/tests/perf_bench/core_import_mpy_single.py.exp new file mode 100644 index 0000000000..190a18037c --- /dev/null +++ b/tests/perf_bench/core_import_mpy_single.py.exp @@ -0,0 +1 @@ +123 diff --git a/tests/perf_bench/core_qstr.py b/tests/perf_bench/core_qstr.py new file mode 100644 index 0000000000..b87e2c0658 --- /dev/null +++ b/tests/perf_bench/core_qstr.py @@ -0,0 +1,21 @@ +# This tests qstr_find_strn() speed when the string being searched for is not found. + + +def test(r): + for _ in r: + str("a string that shouldn't be interned") + + +########################################################################### +# Benchmark interface + +bm_params = { + (100, 10): (400,), + (1000, 10): (4000,), + (5000, 10): (40000,), +} + + +def bm_setup(params): + (nloop,) = params + return lambda: test(range(nloop)), lambda: (nloop // 100, None) From a434705700c18544edbbcabc714a5bf507847eb4 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 11 Feb 2022 13:42:00 +1100 Subject: [PATCH 124/619] tests/perf_bench: Add perf test for yield-from execution. Signed-off-by: Damien George --- tests/perf_bench/core_yield_from.py | 31 +++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 tests/perf_bench/core_yield_from.py diff --git a/tests/perf_bench/core_yield_from.py b/tests/perf_bench/core_yield_from.py new file mode 100644 index 0000000000..2f6930e2b8 --- /dev/null +++ b/tests/perf_bench/core_yield_from.py @@ -0,0 +1,31 @@ +# Test a deep set of "yield from" statements. + + +def recursive_yield_from(depth, iter_): + if depth <= 0: + for i in iter_: + yield i + else: + yield from recursive_yield_from(depth - 1, iter_) + + +def test(n): + global result + result = 0 + for i in recursive_yield_from(10, range(n)): + result += i + + +########################################################################### +# Benchmark interface + +bm_params = { + (100, 10): (2000,), + (1000, 10): (20000,), + (5000, 10): (100000,), +} + + +def bm_setup(params): + (nloop,) = params + return lambda: test(nloop), lambda: (nloop // 100, result) From e8bc4a3a5be12ad639b811852337c63e8f1d6277 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 11 Feb 2022 22:19:38 +1100 Subject: [PATCH 125/619] tests/run-perfbench.py: Use SKIP consistently, and increase print width. A script will print "SKIP" if it wants to be skipped, so the test runner must also use uppercase SKIP. Signed-off-by: Damien George --- tests/run-perfbench.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/run-perfbench.py b/tests/run-perfbench.py index fccb7a7684..c143ae32f9 100755 --- a/tests/run-perfbench.py +++ b/tests/run-perfbench.py @@ -100,7 +100,7 @@ def run_benchmarks(target, param_n, param_m, n_average, test_list): and test_file.find("viper_") != -1 ) if skip: - print("skip") + print("SKIP") continue # Create test script @@ -171,7 +171,7 @@ def parse_output(filename): m = int(m.split("=")[1]) data = [] for l in f: - if l.find(": ") != -1 and l.find(": skip") == -1 and l.find("CRASH: ") == -1: + if l.find(": ") != -1 and l.find(": SKIP") == -1 and l.find("CRASH: ") == -1: name, values = l.strip().split(": ") values = tuple(float(v) for v in values.split()) data.append((name,) + values) @@ -193,7 +193,7 @@ def compute_diff(file1, file2, diff_score): else: hdr = "N={} M={} vs N={} M={}".format(n1, m1, n2, m2) print( - "{:24} {:>10} -> {:>10} {:>10} {:>7}% (error%)".format( + "{:26} {:>10} -> {:>10} {:>10} {:>7}% (error%)".format( hdr, file1, file2, "diff", "diff" ) ) @@ -214,7 +214,7 @@ def compute_diff(file1, file2, diff_score): percent = 100 * av_diff / av1 percent_sd = 100 * sd_diff / av1 print( - "{:24} {:10.2f} -> {:10.2f} : {:+10.2f} = {:+7.3f}% (+/-{:.2f}%)".format( + "{:26} {:10.2f} -> {:10.2f} : {:+10.2f} = {:+7.3f}% (+/-{:.2f}%)".format( name, av1, av2, av_diff, percent, percent_sd ) ) From 18b1ba086c0e5547ca81030bf13b026961f80720 Mon Sep 17 00:00:00 2001 From: Artyom Skrobov Date: Mon, 3 May 2021 14:17:36 -0400 Subject: [PATCH 126/619] py/qstr: Separate hash and len from string data. This allows the compiler to merge strings: e.g. "update", "difference_update" and "symmetric_difference_update" will all point to the same memory. No functional change. The size reduction depends on the number of qstrs in the build. The change this commit brings is: bare-arm: -4 -0.007% minimal x86: +150 +0.092% [incl +48(data)] unix x64: -608 -0.118% unix nanbox: -572 -0.126% [incl +32(data)] stm32: -1392 -0.352% PYBV10 cc3200: -448 -0.244% esp8266: -1208 -0.173% GENERIC esp32: -1028 -0.068% GENERIC[incl -1020(data)] nrf: -440 -0.252% pca10040 rp2: -1072 -0.217% PICO samd: -368 -0.264% ADAFRUIT_ITSYBITSY_M4_EXPRESS Performance is also improved (on bare metal at least) for the core_import_mpy_multi.py, core_import_mpy_single.py and core_qstr.py performance benchmarks. Originally at adafruit#4583 Signed-off-by: Artyom Skrobov --- py/makeqstrdata.py | 29 +++++----- py/mpstate.h | 2 +- py/qstr.c | 132 +++++++++++++++++++++++---------------------- py/qstr.h | 22 +++++++- tools/mpy-tool.py | 29 +++++++--- 5 files changed, 121 insertions(+), 93 deletions(-) diff --git a/py/makeqstrdata.py b/py/makeqstrdata.py index 403c406888..e332ab94ed 100644 --- a/py/makeqstrdata.py +++ b/py/makeqstrdata.py @@ -317,26 +317,24 @@ def parse_input_headers(infiles): return qcfgs, qstrs +def escape_bytes(qstr, qbytes): + if all(32 <= ord(c) <= 126 and c != "\\" and c != '"' for c in qstr): + # qstr is all printable ASCII so render it as-is (for easier debugging) + return qstr + else: + # qstr contains non-printable codes so render entire thing as hex pairs + return "".join(("\\x%02x" % b) for b in qbytes) + + def make_bytes(cfg_bytes_len, cfg_bytes_hash, qstr): qbytes = bytes_cons(qstr, "utf8") qlen = len(qbytes) qhash = compute_hash(qbytes, cfg_bytes_hash) - if all(32 <= ord(c) <= 126 and c != "\\" and c != '"' for c in qstr): - # qstr is all printable ASCII so render it as-is (for easier debugging) - qdata = qstr - else: - # qstr contains non-printable codes so render entire thing as hex pairs - qdata = "".join(("\\x%02x" % b) for b in qbytes) if qlen >= (1 << (8 * cfg_bytes_len)): print("qstr is too long:", qstr) assert False - qlen_str = ("\\x%02x" * cfg_bytes_len) % tuple( - ((qlen >> (8 * i)) & 0xFF) for i in range(cfg_bytes_len) - ) - qhash_str = ("\\x%02x" * cfg_bytes_hash) % tuple( - ((qhash >> (8 * i)) & 0xFF) for i in range(cfg_bytes_hash) - ) - return '(const byte*)"%s%s" "%s"' % (qhash_str, qlen_str, qdata) + qdata = escape_bytes(qstr, qbytes) + return '%d, %d, "%s"' % (qhash, qlen, qdata) def print_qstr_data(qcfgs, qstrs): @@ -349,10 +347,7 @@ def print_qstr_data(qcfgs, qstrs): print("") # add NULL qstr with no hash or data - print( - 'QDEF(MP_QSTRnull, (const byte*)"%s%s" "")' - % ("\\x00" * cfg_bytes_hash, "\\x00" * cfg_bytes_len) - ) + print('QDEF(MP_QSTRnull, 0, 0, "")') # go through each qstr and print it out for order, ident, qstr in sorted(qstrs.values(), key=lambda x: x[0]): diff --git a/py/mpstate.h b/py/mpstate.h index 8ece151663..7f86399f57 100644 --- a/py/mpstate.h +++ b/py/mpstate.h @@ -202,7 +202,7 @@ typedef struct _mp_state_vm_t { // pointer and sizes to store interned string data // (qstr_last_chunk can be root pointer but is also stored in qstr pool) - byte *qstr_last_chunk; + char *qstr_last_chunk; size_t qstr_last_alloc; size_t qstr_last_used; diff --git a/py/qstr.c b/py/qstr.c index e5b13b700d..848a583304 100644 --- a/py/qstr.c +++ b/py/qstr.c @@ -35,7 +35,6 @@ // NOTE: we are using linear arrays to store and search for qstr's (unique strings, interned strings) // ultimately we will replace this with a static hash table of some kind -// also probably need to include the length in the string data, to allow null bytes in the string #if MICROPY_DEBUG_VERBOSE // print debugging info #define DEBUG_printf DEBUG_printf @@ -44,34 +43,9 @@ #endif // A qstr is an index into the qstr pool. -// The data for a qstr contains (hash, length, data): -// - hash (configurable number of bytes) -// - length (configurable number of bytes) -// - data ("length" number of bytes) -// - \0 terminated (so they can be printed using printf) +// The data for a qstr is \0 terminated (so they can be printed using printf) -#if MICROPY_QSTR_BYTES_IN_HASH == 1 - #define Q_HASH_MASK (0xff) - #define Q_GET_HASH(q) ((mp_uint_t)(q)[0]) - #define Q_SET_HASH(q, hash) do { (q)[0] = (hash); } while (0) -#elif MICROPY_QSTR_BYTES_IN_HASH == 2 - #define Q_HASH_MASK (0xffff) - #define Q_GET_HASH(q) ((mp_uint_t)(q)[0] | ((mp_uint_t)(q)[1] << 8)) - #define Q_SET_HASH(q, hash) do { (q)[0] = (hash); (q)[1] = (hash) >> 8; } while (0) -#else - #error unimplemented qstr hash decoding -#endif -#define Q_GET_ALLOC(q) (MICROPY_QSTR_BYTES_IN_HASH + MICROPY_QSTR_BYTES_IN_LEN + Q_GET_LENGTH(q) + 1) -#define Q_GET_DATA(q) ((q) + MICROPY_QSTR_BYTES_IN_HASH + MICROPY_QSTR_BYTES_IN_LEN) -#if MICROPY_QSTR_BYTES_IN_LEN == 1 - #define Q_GET_LENGTH(q) ((q)[MICROPY_QSTR_BYTES_IN_HASH]) - #define Q_SET_LENGTH(q, len) do { (q)[MICROPY_QSTR_BYTES_IN_HASH] = (len); } while (0) -#elif MICROPY_QSTR_BYTES_IN_LEN == 2 - #define Q_GET_LENGTH(q) ((q)[MICROPY_QSTR_BYTES_IN_HASH] | ((q)[MICROPY_QSTR_BYTES_IN_HASH + 1] << 8)) - #define Q_SET_LENGTH(q, len) do { (q)[MICROPY_QSTR_BYTES_IN_HASH] = (len); (q)[MICROPY_QSTR_BYTES_IN_HASH + 1] = (len) >> 8; } while (0) -#else - #error unimplemented qstr length decoding -#endif +#define Q_HASH_MASK ((1 << (8 * MICROPY_QSTR_BYTES_IN_HASH)) - 1) #if MICROPY_PY_THREAD && !MICROPY_PY_THREAD_GIL #define QSTR_ENTER() mp_thread_mutex_lock(&MP_STATE_VM(qstr_mutex), 1) @@ -100,14 +74,32 @@ mp_uint_t qstr_compute_hash(const byte *data, size_t len) { return hash; } +const qstr_hash_t mp_qstr_const_hashes[] = { + #ifndef NO_QSTR +#define QDEF(id, hash, len, str) hash, + #include "genhdr/qstrdefs.generated.h" +#undef QDEF + #endif +}; + +const qstr_len_t mp_qstr_const_lengths[] = { + #ifndef NO_QSTR +#define QDEF(id, hash, len, str) len, + #include "genhdr/qstrdefs.generated.h" +#undef QDEF + #endif +}; + const qstr_pool_t mp_qstr_const_pool = { NULL, // no previous pool 0, // no previous pool MICROPY_ALLOC_QSTR_ENTRIES_INIT, MP_QSTRnumber_of, // corresponds to number of strings in array just below + (qstr_hash_t *)mp_qstr_const_hashes, + (qstr_len_t *)mp_qstr_const_lengths, { #ifndef NO_QSTR -#define QDEF(id, str) str, +#define QDEF(id, hash, len, str) str, #include "genhdr/qstrdefs.generated.h" #undef QDEF #endif @@ -130,19 +122,21 @@ void qstr_init(void) { #endif } -STATIC const byte *find_qstr(qstr q) { +STATIC qstr_pool_t *find_qstr(qstr *q) { // search pool for this qstr // total_prev_len==0 in the final pool, so the loop will always terminate qstr_pool_t *pool = MP_STATE_VM(last_pool); - while (q < pool->total_prev_len) { + while (*q < pool->total_prev_len) { pool = pool->prev; } - return pool->qstrs[q - pool->total_prev_len]; + *q -= pool->total_prev_len; + assert(*q < pool->len); + return pool; } // qstr_mutex must be taken while in this function -STATIC qstr qstr_add(const byte *q_ptr) { - DEBUG_printf("QSTR: add hash=%d len=%d data=%.*s\n", Q_GET_HASH(q_ptr), Q_GET_LENGTH(q_ptr), Q_GET_LENGTH(q_ptr), Q_GET_DATA(q_ptr)); +STATIC qstr qstr_add(mp_uint_t hash, mp_uint_t len, const char *q_ptr) { + DEBUG_printf("QSTR: add hash=%d len=%d data=%.*s\n", hash, len, len, q_ptr); // make sure we have room in the pool for a new qstr if (MP_STATE_VM(last_pool)->len >= MP_STATE_VM(last_pool)->alloc) { @@ -151,7 +145,9 @@ STATIC qstr qstr_add(const byte *q_ptr) { // Put a lower bound on the allocation size in case the extra qstr pool has few entries new_alloc = MAX(MICROPY_ALLOC_QSTR_ENTRIES_INIT, new_alloc); #endif - qstr_pool_t *pool = m_new_obj_var_maybe(qstr_pool_t, const char *, new_alloc); + mp_uint_t pool_size = sizeof(qstr_pool_t) + + (sizeof(const char *) + sizeof(qstr_hash_t) + sizeof(qstr_len_t)) * new_alloc; + qstr_pool_t *pool = (qstr_pool_t *)m_malloc_maybe(pool_size); if (pool == NULL) { // Keep qstr_last_chunk consistent with qstr_pool_t: qstr_last_chunk is not scanned // at garbage collection since it's reachable from a qstr_pool_t. And the caller of @@ -162,6 +158,8 @@ STATIC qstr qstr_add(const byte *q_ptr) { QSTR_EXIT(); m_malloc_fail(new_alloc); } + pool->hashes = (qstr_hash_t *)(pool->qstrs + new_alloc); + pool->lengths = (qstr_len_t *)(pool->hashes + new_alloc); pool->prev = MP_STATE_VM(last_pool); pool->total_prev_len = MP_STATE_VM(last_pool)->total_prev_len + MP_STATE_VM(last_pool)->len; pool->alloc = new_alloc; @@ -171,10 +169,14 @@ STATIC qstr qstr_add(const byte *q_ptr) { } // add the new qstr - MP_STATE_VM(last_pool)->qstrs[MP_STATE_VM(last_pool)->len++] = q_ptr; + mp_uint_t at = MP_STATE_VM(last_pool)->len; + MP_STATE_VM(last_pool)->hashes[at] = hash; + MP_STATE_VM(last_pool)->lengths[at] = len; + MP_STATE_VM(last_pool)->qstrs[at] = q_ptr; + MP_STATE_VM(last_pool)->len++; // return id for the newly-added qstr - return MP_STATE_VM(last_pool)->total_prev_len + MP_STATE_VM(last_pool)->len - 1; + return MP_STATE_VM(last_pool)->total_prev_len + at; } qstr qstr_find_strn(const char *str, size_t str_len) { @@ -183,9 +185,10 @@ qstr qstr_find_strn(const char *str, size_t str_len) { // search pools for the data for (qstr_pool_t *pool = MP_STATE_VM(last_pool); pool != NULL; pool = pool->prev) { - for (const byte **q = pool->qstrs, **q_top = pool->qstrs + pool->len; q < q_top; q++) { - if (Q_GET_HASH(*q) == str_hash && Q_GET_LENGTH(*q) == str_len && memcmp(Q_GET_DATA(*q), str, str_len) == 0) { - return pool->total_prev_len + (q - pool->qstrs); + for (mp_uint_t at = 0, top = pool->len; at < top; at++) { + if (pool->hashes[at] == str_hash && pool->lengths[at] == str_len + && memcmp(pool->qstrs[at], str, str_len) == 0) { + return pool->total_prev_len + at; } } } @@ -211,14 +214,14 @@ qstr qstr_from_strn(const char *str, size_t len) { } // compute number of bytes needed to intern this string - size_t n_bytes = MICROPY_QSTR_BYTES_IN_HASH + MICROPY_QSTR_BYTES_IN_LEN + len + 1; + size_t n_bytes = len + 1; if (MP_STATE_VM(qstr_last_chunk) != NULL && MP_STATE_VM(qstr_last_used) + n_bytes > MP_STATE_VM(qstr_last_alloc)) { // not enough room at end of previously interned string so try to grow - byte *new_p = m_renew_maybe(byte, MP_STATE_VM(qstr_last_chunk), MP_STATE_VM(qstr_last_alloc), MP_STATE_VM(qstr_last_alloc) + n_bytes, false); + char *new_p = m_renew_maybe(char, MP_STATE_VM(qstr_last_chunk), MP_STATE_VM(qstr_last_alloc), MP_STATE_VM(qstr_last_alloc) + n_bytes, false); if (new_p == NULL) { // could not grow existing memory; shrink it to fit previous - (void)m_renew_maybe(byte, MP_STATE_VM(qstr_last_chunk), MP_STATE_VM(qstr_last_alloc), MP_STATE_VM(qstr_last_used), false); + (void)m_renew_maybe(char, MP_STATE_VM(qstr_last_chunk), MP_STATE_VM(qstr_last_alloc), MP_STATE_VM(qstr_last_used), false); MP_STATE_VM(qstr_last_chunk) = NULL; } else { // could grow existing memory @@ -232,10 +235,10 @@ qstr qstr_from_strn(const char *str, size_t len) { if (al < MICROPY_ALLOC_QSTR_CHUNK_INIT) { al = MICROPY_ALLOC_QSTR_CHUNK_INIT; } - MP_STATE_VM(qstr_last_chunk) = m_new_maybe(byte, al); + MP_STATE_VM(qstr_last_chunk) = m_new_maybe(char, al); if (MP_STATE_VM(qstr_last_chunk) == NULL) { // failed to allocate a large chunk so try with exact size - MP_STATE_VM(qstr_last_chunk) = m_new_maybe(byte, n_bytes); + MP_STATE_VM(qstr_last_chunk) = m_new_maybe(char, n_bytes); if (MP_STATE_VM(qstr_last_chunk) == NULL) { QSTR_EXIT(); m_malloc_fail(n_bytes); @@ -247,40 +250,38 @@ qstr qstr_from_strn(const char *str, size_t len) { } // allocate memory from the chunk for this new interned string's data - byte *q_ptr = MP_STATE_VM(qstr_last_chunk) + MP_STATE_VM(qstr_last_used); + char *q_ptr = MP_STATE_VM(qstr_last_chunk) + MP_STATE_VM(qstr_last_used); MP_STATE_VM(qstr_last_used) += n_bytes; // store the interned strings' data mp_uint_t hash = qstr_compute_hash((const byte *)str, len); - Q_SET_HASH(q_ptr, hash); - Q_SET_LENGTH(q_ptr, len); - memcpy(q_ptr + MICROPY_QSTR_BYTES_IN_HASH + MICROPY_QSTR_BYTES_IN_LEN, str, len); - q_ptr[MICROPY_QSTR_BYTES_IN_HASH + MICROPY_QSTR_BYTES_IN_LEN + len] = '\0'; - q = qstr_add(q_ptr); + memcpy(q_ptr, str, len); + q_ptr[len] = '\0'; + q = qstr_add(hash, len, q_ptr); } QSTR_EXIT(); return q; } mp_uint_t qstr_hash(qstr q) { - const byte *qd = find_qstr(q); - return Q_GET_HASH(qd); + qstr_pool_t *pool = find_qstr(&q); + return pool->hashes[q]; } size_t qstr_len(qstr q) { - const byte *qd = find_qstr(q); - return Q_GET_LENGTH(qd); + qstr_pool_t *pool = find_qstr(&q); + return pool->lengths[q]; } const char *qstr_str(qstr q) { - const byte *qd = find_qstr(q); - return (const char *)Q_GET_DATA(qd); + qstr_pool_t *pool = find_qstr(&q); + return pool->qstrs[q]; } const byte *qstr_data(qstr q, size_t *len) { - const byte *qd = find_qstr(q); - *len = Q_GET_LENGTH(qd); - return Q_GET_DATA(qd); + qstr_pool_t *pool = find_qstr(&q); + *len = pool->lengths[q]; + return (byte *)pool->qstrs[q]; } void qstr_pool_info(size_t *n_pool, size_t *n_qstr, size_t *n_str_data_bytes, size_t *n_total_bytes) { @@ -292,13 +293,14 @@ void qstr_pool_info(size_t *n_pool, size_t *n_qstr, size_t *n_str_data_bytes, si for (qstr_pool_t *pool = MP_STATE_VM(last_pool); pool != NULL && pool != &CONST_POOL; pool = pool->prev) { *n_pool += 1; *n_qstr += pool->len; - for (const byte **q = pool->qstrs, **q_top = pool->qstrs + pool->len; q < q_top; q++) { - *n_str_data_bytes += Q_GET_ALLOC(*q); + for (qstr_len_t *l = pool->lengths, *l_top = pool->lengths + pool->len; l < l_top; l++) { + *n_str_data_bytes += *l + 1; } #if MICROPY_ENABLE_GC *n_total_bytes += gc_nbytes(pool); // this counts actual bytes used in heap #else - *n_total_bytes += sizeof(qstr_pool_t) + sizeof(qstr) * pool->alloc; + *n_total_bytes += sizeof(qstr_pool_t) + + (sizeof(const char *) + sizeof(qstr_hash_t) + sizeof(qstr_len_t)) * pool->alloc; #endif } *n_total_bytes += *n_str_data_bytes; @@ -309,8 +311,8 @@ void qstr_pool_info(size_t *n_pool, size_t *n_qstr, size_t *n_str_data_bytes, si void qstr_dump_data(void) { QSTR_ENTER(); for (qstr_pool_t *pool = MP_STATE_VM(last_pool); pool != NULL && pool != &CONST_POOL; pool = pool->prev) { - for (const byte **q = pool->qstrs, **q_top = pool->qstrs + pool->len; q < q_top; q++) { - mp_printf(&mp_plat_print, "Q(%s)\n", Q_GET_DATA(*q)); + for (const char **q = pool->qstrs, **q_top = pool->qstrs + pool->len; q < q_top; q++) { + mp_printf(&mp_plat_print, "Q(%s)\n", *q); } } QSTR_EXIT(); diff --git a/py/qstr.h b/py/qstr.h index 0b6fb12b08..19672afc0d 100644 --- a/py/qstr.h +++ b/py/qstr.h @@ -38,7 +38,7 @@ // first entry in enum will be MP_QSTRnull=0, which indicates invalid/no qstr enum { #ifndef NO_QSTR -#define QDEF(id, str) id, +#define QDEF(id, hash, len, str) id, #include "genhdr/qstrdefs.generated.h" #undef QDEF #endif @@ -47,12 +47,30 @@ enum { typedef size_t qstr; +#if MICROPY_QSTR_BYTES_IN_HASH == 1 +typedef uint8_t qstr_hash_t; +#elif MICROPY_QSTR_BYTES_IN_HASH == 2 +typedef uint16_t qstr_hash_t; +#else +#error unimplemented qstr hash decoding +#endif + +#if MICROPY_QSTR_BYTES_IN_LEN == 1 +typedef uint8_t qstr_len_t; +#elif MICROPY_QSTR_BYTES_IN_LEN == 2 +typedef uint16_t qstr_len_t; +#else +#error unimplemented qstr length decoding +#endif + typedef struct _qstr_pool_t { struct _qstr_pool_t *prev; size_t total_prev_len; size_t alloc; size_t len; - const byte *qstrs[]; + qstr_hash_t *hashes; + qstr_len_t *lengths; + const char *qstrs[]; } qstr_pool_t; #define QSTR_TOTAL() (MP_STATE_VM(last_pool)->total_prev_len + MP_STATE_VM(last_pool)->len) diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index aa0272111c..337f580b6e 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -814,7 +814,7 @@ def freeze_mpy(base_qstrs, raw_codes): # don't add duplicates if q is None or q.qstr_esc in base_qstrs or q.qstr_esc in new: continue - new[q.qstr_esc] = (len(new), q.qstr_esc, q.str) + new[q.qstr_esc] = (len(new), q.qstr_esc, q.str, bytes_cons(q.str, "utf8")) new = sorted(new.values(), key=lambda x: x[0]) print('#include "py/mpconfig.h"') @@ -864,6 +864,22 @@ def freeze_mpy(base_qstrs, raw_codes): # As in qstr.c, set so that the first dynamically allocated pool is twice this size; must be <= the len qstr_pool_alloc = min(len(new), 10) + print() + print("const qstr_hash_t mp_qstr_frozen_const_hashes[] = {") + qstr_size = {"metadata": 0, "data": 0} + for _, _, _, qbytes in new: + qhash = qstrutil.compute_hash(qbytes, config.MICROPY_QSTR_BYTES_IN_HASH) + print(" %d," % qhash) + print("};") + print() + print("const qstr_len_t mp_qstr_frozen_const_lengths[] = {") + for _, _, _, qbytes in new: + print(" %d," % len(qbytes)) + qstr_size["metadata"] += ( + config.MICROPY_QSTR_BYTES_IN_LEN + config.MICROPY_QSTR_BYTES_IN_HASH + ) + qstr_size["data"] += len(qbytes) + print("};") print() print("extern const qstr_pool_t mp_qstr_const_pool;") print("const qstr_pool_t mp_qstr_frozen_const_pool = {") @@ -871,14 +887,11 @@ def freeze_mpy(base_qstrs, raw_codes): print(" MP_QSTRnumber_of, // previous pool size") print(" %u, // allocated entries" % qstr_pool_alloc) print(" %u, // used entries" % len(new)) + print(" (qstr_hash_t *)mp_qstr_frozen_const_hashes,") + print(" (qstr_len_t *)mp_qstr_frozen_const_lengths,") print(" {") - for _, _, qstr in new: - print( - " %s," - % qstrutil.make_bytes( - config.MICROPY_QSTR_BYTES_IN_LEN, config.MICROPY_QSTR_BYTES_IN_HASH, qstr - ) - ) + for _, _, qstr, qbytes in new: + print(' "%s",' % qstrutil.escape_bytes(qstr, qbytes)) print(" },") print("};") From f46a7140f55a8f6d80f9c2d5f8db7af3de116794 Mon Sep 17 00:00:00 2001 From: Artyom Skrobov Date: Tue, 4 May 2021 03:35:45 -0400 Subject: [PATCH 127/619] py/qstr: Use `const` consistently to avoid a cast. Originally at adafruit#4707 Signed-off-by: Artyom Skrobov --- py/qstr.c | 20 ++++++++++---------- py/qstr.h | 2 +- tools/mpy-tool.py | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/py/qstr.c b/py/qstr.c index 848a583304..f9ca106837 100644 --- a/py/qstr.c +++ b/py/qstr.c @@ -122,10 +122,10 @@ void qstr_init(void) { #endif } -STATIC qstr_pool_t *find_qstr(qstr *q) { +STATIC const qstr_pool_t *find_qstr(qstr *q) { // search pool for this qstr // total_prev_len==0 in the final pool, so the loop will always terminate - qstr_pool_t *pool = MP_STATE_VM(last_pool); + const qstr_pool_t *pool = MP_STATE_VM(last_pool); while (*q < pool->total_prev_len) { pool = pool->prev; } @@ -184,7 +184,7 @@ qstr qstr_find_strn(const char *str, size_t str_len) { mp_uint_t str_hash = qstr_compute_hash((const byte *)str, str_len); // search pools for the data - for (qstr_pool_t *pool = MP_STATE_VM(last_pool); pool != NULL; pool = pool->prev) { + for (const qstr_pool_t *pool = MP_STATE_VM(last_pool); pool != NULL; pool = pool->prev) { for (mp_uint_t at = 0, top = pool->len; at < top; at++) { if (pool->hashes[at] == str_hash && pool->lengths[at] == str_len && memcmp(pool->qstrs[at], str, str_len) == 0) { @@ -264,22 +264,22 @@ qstr qstr_from_strn(const char *str, size_t len) { } mp_uint_t qstr_hash(qstr q) { - qstr_pool_t *pool = find_qstr(&q); + const qstr_pool_t *pool = find_qstr(&q); return pool->hashes[q]; } size_t qstr_len(qstr q) { - qstr_pool_t *pool = find_qstr(&q); + const qstr_pool_t *pool = find_qstr(&q); return pool->lengths[q]; } const char *qstr_str(qstr q) { - qstr_pool_t *pool = find_qstr(&q); + const qstr_pool_t *pool = find_qstr(&q); return pool->qstrs[q]; } const byte *qstr_data(qstr q, size_t *len) { - qstr_pool_t *pool = find_qstr(&q); + const qstr_pool_t *pool = find_qstr(&q); *len = pool->lengths[q]; return (byte *)pool->qstrs[q]; } @@ -290,7 +290,7 @@ void qstr_pool_info(size_t *n_pool, size_t *n_qstr, size_t *n_str_data_bytes, si *n_qstr = 0; *n_str_data_bytes = 0; *n_total_bytes = 0; - for (qstr_pool_t *pool = MP_STATE_VM(last_pool); pool != NULL && pool != &CONST_POOL; pool = pool->prev) { + for (const qstr_pool_t *pool = MP_STATE_VM(last_pool); pool != NULL && pool != &CONST_POOL; pool = pool->prev) { *n_pool += 1; *n_qstr += pool->len; for (qstr_len_t *l = pool->lengths, *l_top = pool->lengths + pool->len; l < l_top; l++) { @@ -310,8 +310,8 @@ void qstr_pool_info(size_t *n_pool, size_t *n_qstr, size_t *n_str_data_bytes, si #if MICROPY_PY_MICROPYTHON_MEM_INFO void qstr_dump_data(void) { QSTR_ENTER(); - for (qstr_pool_t *pool = MP_STATE_VM(last_pool); pool != NULL && pool != &CONST_POOL; pool = pool->prev) { - for (const char **q = pool->qstrs, **q_top = pool->qstrs + pool->len; q < q_top; q++) { + for (const qstr_pool_t *pool = MP_STATE_VM(last_pool); pool != NULL && pool != &CONST_POOL; pool = pool->prev) { + for (const char *const *q = pool->qstrs, *const *q_top = pool->qstrs + pool->len; q < q_top; q++) { mp_printf(&mp_plat_print, "Q(%s)\n", *q); } } diff --git a/py/qstr.h b/py/qstr.h index 19672afc0d..ded105760c 100644 --- a/py/qstr.h +++ b/py/qstr.h @@ -64,7 +64,7 @@ typedef uint16_t qstr_len_t; #endif typedef struct _qstr_pool_t { - struct _qstr_pool_t *prev; + const struct _qstr_pool_t *prev; size_t total_prev_len; size_t alloc; size_t len; diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index 337f580b6e..58a4a75c91 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -883,7 +883,7 @@ def freeze_mpy(base_qstrs, raw_codes): print() print("extern const qstr_pool_t mp_qstr_const_pool;") print("const qstr_pool_t mp_qstr_frozen_const_pool = {") - print(" (qstr_pool_t*)&mp_qstr_const_pool, // previous pool") + print(" &mp_qstr_const_pool, // previous pool") print(" MP_QSTRnumber_of, // previous pool size") print(" %u, // allocated entries" % qstr_pool_alloc) print(" %u, // used entries" % len(new)) From 2ea21abae0358a46c0144f014f2e5891afe03dfd Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 12 Feb 2022 01:03:39 +1100 Subject: [PATCH 128/619] tests/extmod/vfs_fat_finaliser.py: Make finalisation more robust. Signed-off-by: Damien George --- tests/extmod/vfs_fat_finaliser.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/tests/extmod/vfs_fat_finaliser.py b/tests/extmod/vfs_fat_finaliser.py index b67afc2d94..b38e640c73 100644 --- a/tests/extmod/vfs_fat_finaliser.py +++ b/tests/extmod/vfs_fat_finaliser.py @@ -56,6 +56,12 @@ micropython.heap_unlock() # Here we test that the finaliser is actually called during a garbage collection. import gc +# Preallocate global variables, and list of filenames for the test (which may +# in turn allocate new qstrs and/or a new qstr pool). +f = None +n = None +names = ["x%d" % i for i in range(4)] + # Do a large number of single-block allocations to move the GC head forwards, # ensuring that the files are allocated from never-before-used blocks and # therefore couldn't possibly have any references to them left behind on @@ -63,14 +69,13 @@ import gc for i in range(1024): [] -N = 4 -for i in range(N): - n = "x%d" % i +# Run the test: create files without closing them, run GC, then read back files. +for n in names: f = vfs.open(n, "w") f.write(n) f = None # release f without closing - [0, 1, 2, 3] # use up Python stack so f is really gone + sorted([0, 1, 2, 3], key=lambda x: x) # use up Python and C stack so f is really gone gc.collect() # should finalise all N files by closing them -for i in range(N): - with vfs.open("x%d" % i, "r") as f: +for n in names: + with vfs.open(n, "r") as f: print(f.read()) From 18acd0318f927930dd7f9efd77f08d8e05a43ce8 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 17 Feb 2022 11:17:21 +1100 Subject: [PATCH 129/619] py/gc: Update debug code to compile with changes to qstr pool types. Following on from 18b1ba086c0e5547ca81030bf13b026961f80720 and f46a7140f55a8f6d80f9c2d5f8db7af3de116794. Signed-off-by: Damien George --- py/gc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/py/gc.c b/py/gc.c index a01d81abdc..0c1f3961df 100644 --- a/py/gc.c +++ b/py/gc.c @@ -920,13 +920,13 @@ void gc_dump_alloc_table(void) { // This code prints "Q" for qstr-pool data, and "q" for qstr-str // data. It can be useful to see how qstrs are being allocated, // but is disabled by default because it is very slow. - for (qstr_pool_t *pool = MP_STATE_VM(last_pool); c == 'h' && pool != NULL; pool = pool->prev) { - if ((qstr_pool_t *)ptr == pool) { + for (const qstr_pool_t *pool = MP_STATE_VM(last_pool); c == 'h' && pool != NULL; pool = pool->prev) { + if ((const qstr_pool_t *)ptr == pool) { c = 'Q'; break; } - for (const byte **q = pool->qstrs, **q_top = pool->qstrs + pool->len; q < q_top; q++) { - if ((const byte *)ptr == *q) { + for (const char *const *q = pool->qstrs, *const *q_top = pool->qstrs + pool->len; q < q_top; q++) { + if ((const char *)ptr == *q) { c = 'q'; break; } From 767058f3282bf4836e7b9f137bada3c0d8b717af Mon Sep 17 00:00:00 2001 From: YoungJoon Chun Date: Wed, 9 Feb 2022 20:58:56 +0900 Subject: [PATCH 130/619] rp2/Makefile: Add FROZEN_MANIFEST Makefile option, to override default. --- ports/rp2/Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ports/rp2/Makefile b/ports/rp2/Makefile index 2af13bfbbf..2d1e6d6f5c 100644 --- a/ports/rp2/Makefile +++ b/ports/rp2/Makefile @@ -14,6 +14,10 @@ ifdef USER_C_MODULES CMAKE_ARGS += -DUSER_C_MODULES=${USER_C_MODULES} endif +ifneq ($(FROZEN_MANIFEST),) +CMAKE_ARGS += -DMICROPY_FROZEN_MANIFEST=${FROZEN_MANIFEST} +endif + all: [ -d $(BUILD) ] || cmake -S . -B $(BUILD) -DPICO_BUILD_DOCS=0 ${CMAKE_ARGS} $(MAKE) $(MAKESILENT) -C $(BUILD) From 0dfd0447fa6f24bb8ed62b35dbaf91695a255f43 Mon Sep 17 00:00:00 2001 From: Bradley Wogsland Date: Wed, 9 Feb 2022 10:52:02 +0100 Subject: [PATCH 131/619] README: Update link for ARM embedded toolchain to developer.arm.com. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d197924f0e..920f10a503 100644 --- a/README.md +++ b/README.md @@ -156,7 +156,7 @@ The STM32 version The "stm32" port requires an ARM compiler, arm-none-eabi-gcc, and associated bin-utils. For those using Arch Linux, you need arm-none-eabi-binutils, arm-none-eabi-gcc and arm-none-eabi-newlib packages. Otherwise, try here: -https://launchpad.net/gcc-arm-embedded +https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm To build: From 28cb573b89f3d1edfff786f8ee757a93579c41bd Mon Sep 17 00:00:00 2001 From: David Lechner Date: Wed, 9 Feb 2022 10:26:35 -0600 Subject: [PATCH 132/619] windows/appveyor: Fix printing of test failures. In the `after_test` section, the current directory is `ports/windows` when tests are run, so running `run-tests.py` without changing the directory or specifying a path causes a file not found error. This commit fixes the problem by changing the directory before calling `run-tests.py`. Signed-off-by: David Lechner --- ports/windows/.appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/windows/.appveyor.yml b/ports/windows/.appveyor.yml index 739484f095..d7192236df 100644 --- a/ports/windows/.appveyor.yml +++ b/ports/windows/.appveyor.yml @@ -80,6 +80,7 @@ after_test: } C:\msys64\usr\bin\bash.exe -l -c "make V=1 test_full VARIANT=$($env:PyVariant)" if ($LASTEXITCODE -ne 0) { + cd (Join-Path $env:APPVEYOR_BUILD_FOLDER 'tests') & $env:MICROPY_CPYTHON3 run-tests.py --print-failures throw "Test failure" } From 5d6408f8f914f46f3c067fc59f5f31001b988fbc Mon Sep 17 00:00:00 2001 From: David Lechner Date: Wed, 9 Feb 2022 11:02:12 -0600 Subject: [PATCH 133/619] tools/verifygitlog.py: Ignore line length in body if it's a URL. This changes the git commit message line length check to ignore lines that contain URLs, since these cannot be wrapped without breaking tools that detect URLs and create a link. Signed-off-by: David Lechner --- tools/verifygitlog.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/verifygitlog.py b/tools/verifygitlog.py index cc4b80f4a9..ce36791256 100755 --- a/tools/verifygitlog.py +++ b/tools/verifygitlog.py @@ -69,7 +69,8 @@ def verify(sha): # Message body lines. for line in raw_body[2:]: - if len(line) >= 76: + # Long lines with URLs are exempt from the line length rule. + if len(line) >= 76 and "://" not in line: error("Message lines should be 75 or less characters: " + line) if not raw_body[-1].startswith("Signed-off-by: ") or "@" not in raw_body[-1]: From 465b74e78daf50f5bd92625ba57bf2f5597cb712 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 10 Feb 2022 22:30:01 +0200 Subject: [PATCH 134/619] drivers/ninaw10: Add NIC-level ioctl function. This commit adds support in the driver for irregular commands. It currently supports setting GPIO pin mode, and GPIO pin read/write value. --- drivers/ninaw10/nina_wifi_drv.c | 27 +++++++++++++++++++++++++++ drivers/ninaw10/nina_wifi_drv.h | 1 + extmod/network_ninaw10.c | 10 ++++++++++ 3 files changed, 38 insertions(+) diff --git a/drivers/ninaw10/nina_wifi_drv.c b/drivers/ninaw10/nina_wifi_drv.c index cf09711690..1ad77218b6 100644 --- a/drivers/ninaw10/nina_wifi_drv.c +++ b/drivers/ninaw10/nina_wifi_drv.c @@ -701,6 +701,33 @@ int nina_gethostbyname(const char *name, uint8_t *out_ip) { return 0; } +int nina_ioctl(uint32_t cmd, size_t len, uint8_t *buf, uint32_t iface) { + switch (cmd) { + case NINA_CMD_SET_PIN_MODE: + if (len != 2 || nina_send_command_read_ack(NINA_CMD_SET_PIN_MODE, + 2, ARG_8BITS, NINA_ARGS(ARG_BYTE(buf[0]), ARG_BYTE(buf[1]))) != SPI_ACK) { + return -1; + } + break; + case NINA_CMD_SET_DIGITAL_WRITE: + if (len != 2 || nina_send_command_read_ack(NINA_CMD_SET_DIGITAL_WRITE, + 2, ARG_8BITS, NINA_ARGS(ARG_BYTE(buf[0]), ARG_BYTE(buf[1]))) != SPI_ACK) { + return -1; + } + break; + case NINA_CMD_GET_DIGITAL_READ: + if (len != 1 || nina_send_command_read_vals(NINA_CMD_GET_DIGITAL_READ, + 1, ARG_8BITS, NINA_ARGS(ARG_BYTE(buf[0])), + 1, ARG_8BITS, NINA_VALS({(uint16_t *)&len, buf})) != 0) { + return -1; + } + break; + default: + return 0; + } + return 0; +} + int nina_socket_socket(uint8_t type) { uint16_t size = 1; uint8_t sock = 0; diff --git a/drivers/ninaw10/nina_wifi_drv.h b/drivers/ninaw10/nina_wifi_drv.h index b990476b67..4fe701eba1 100644 --- a/drivers/ninaw10/nina_wifi_drv.h +++ b/drivers/ninaw10/nina_wifi_drv.h @@ -100,6 +100,7 @@ int nina_get_rssi(void); int nina_fw_version(uint8_t *fw_ver); int nina_set_hostname(const char *name); int nina_gethostbyname(const char *name, uint8_t *out_ip); +int nina_ioctl(uint32_t cmd, size_t len, uint8_t *buf, uint32_t iface); int nina_socket_socket(uint8_t type); int nina_socket_close(int fd); int nina_socket_bind(int fd, uint8_t *ip, uint16_t port, int type); diff --git a/extmod/network_ninaw10.c b/extmod/network_ninaw10.c index e5d322a953..0340763073 100644 --- a/extmod/network_ninaw10.c +++ b/extmod/network_ninaw10.c @@ -317,6 +317,15 @@ STATIC mp_obj_t network_ninaw10_status(size_t n_args, const mp_obj_t *args) { } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_ninaw10_status_obj, 1, 2, network_ninaw10_status); +STATIC mp_obj_t network_ninaw10_ioctl(mp_obj_t self_in, mp_obj_t cmd_in, mp_obj_t buf_in) { + nina_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_buffer_info_t buf; + mp_get_buffer_raise(buf_in, &buf, MP_BUFFER_READ | MP_BUFFER_WRITE); + nina_ioctl(mp_obj_get_int(cmd_in), buf.len, buf.buf, self->itf); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(network_ninaw10_ioctl_obj, network_ninaw10_ioctl); + STATIC int network_ninaw10_gethostbyname(mp_obj_t nic, const char *name, mp_uint_t len, uint8_t *out_ip) { return nina_gethostbyname(name, out_ip); } @@ -586,6 +595,7 @@ static const mp_rom_map_elem_t nina_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&network_ninaw10_ifconfig_obj) }, { MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&network_ninaw10_config_obj) }, { MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&network_ninaw10_status_obj) }, + { MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&network_ninaw10_ioctl_obj) }, // Network is not secured. { MP_ROM_QSTR(MP_QSTR_OPEN), MP_ROM_INT(NINA_SEC_OPEN) }, From 9c05f3aa1d201ba943c7852bee975f8b7d48e235 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Tue, 15 Feb 2022 18:02:09 +0100 Subject: [PATCH 135/619] drivers/sdcard: Allow setting the final SPI baudrate. This baudrate is supplied in the constructor. The default is 1320000 as before. Example: sd = sdcard.SDCard(spi, cs, baudrate=20_000_000) --- drivers/sdcard/sdcard.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/sdcard/sdcard.py b/drivers/sdcard/sdcard.py index 0ba3076a3d..2c3e99d3c0 100644 --- a/drivers/sdcard/sdcard.py +++ b/drivers/sdcard/sdcard.py @@ -39,7 +39,7 @@ _TOKEN_DATA = const(0xFE) class SDCard: - def __init__(self, spi, cs): + def __init__(self, spi, cs, baudrate=1320000): self.spi = spi self.cs = cs @@ -51,7 +51,7 @@ class SDCard: self.dummybuf_memoryview = memoryview(self.dummybuf) # initialise the card - self.init_card() + self.init_card(baudrate) def init_spi(self, baudrate): try: @@ -63,7 +63,8 @@ class SDCard: # on pyboard self.spi.init(master, baudrate=baudrate, phase=0, polarity=0) - def init_card(self): + def init_card(self, baudrate): + # init CS pin self.cs.init(self.cs.OUT, value=1) @@ -111,7 +112,7 @@ class SDCard: raise OSError("can't set 512 block size") # set to high data rate now that it's initialised - self.init_spi(1320000) + self.init_spi(baudrate) def init_card_v1(self): for i in range(_CMD_TIMEOUT): From 8bb50c6301f5b3df143fff7a425f284e88a0b64d Mon Sep 17 00:00:00 2001 From: stijn Date: Tue, 15 Feb 2022 10:16:46 +0100 Subject: [PATCH 136/619] unix/Makefile: Remove explicit addition of -std=c++ flag. This was added merely for building the C++ user module example, so it's a better fit to add it in the corresponding micropython.mk. --- examples/usercmodule/cppexample/micropython.mk | 2 +- ports/unix/Makefile | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/examples/usercmodule/cppexample/micropython.mk b/examples/usercmodule/cppexample/micropython.mk index e10d965a00..0071d4fcc7 100644 --- a/examples/usercmodule/cppexample/micropython.mk +++ b/examples/usercmodule/cppexample/micropython.mk @@ -6,7 +6,7 @@ SRC_USERMOD_CXX += $(CPPEXAMPLE_MOD_DIR)/example.cpp # Add our module directory to the include path. CFLAGS_USERMOD += -I$(CPPEXAMPLE_MOD_DIR) -CXXFLAGS_USERMOD += -I$(CPPEXAMPLE_MOD_DIR) +CXXFLAGS_USERMOD += -I$(CPPEXAMPLE_MOD_DIR) -std=c++11 # We use C++ features so have to link against the standard library. LDFLAGS_USERMOD += -lstdc++ diff --git a/ports/unix/Makefile b/ports/unix/Makefile index cd8bb379c4..7732211e2d 100644 --- a/ports/unix/Makefile +++ b/ports/unix/Makefile @@ -270,12 +270,6 @@ CFLAGS += -DMPZ_DIG_SIZE=16 # force 16 bits to work on both 32 and 64 bit archs CFLAGS += -DMICROPY_MODULE_FROZEN_STR endif -HASCPP17 = $(shell expr `$(CC) -dumpversion | cut -f1 -d.` \>= 7) -ifeq ($(HASCPP17), 1) - CXXFLAGS += -std=c++17 -else - CXXFLAGS += -std=c++11 -endif CXXFLAGS += $(filter-out -Wmissing-prototypes -Wold-style-definition -std=gnu99,$(CFLAGS) $(CXXFLAGS_MOD)) ifeq ($(MICROPY_FORCE_32BIT),1) From c14f47faa329ea3655aae3068956c138f7e9c178 Mon Sep 17 00:00:00 2001 From: stijn Date: Thu, 17 Feb 2022 12:44:42 +0100 Subject: [PATCH 137/619] windows/Makefile: Specify CXXFLAGS in the Makefile. Enables building user modules which use C++ code, like the unix port. --- ports/windows/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ports/windows/Makefile b/ports/windows/Makefile index 5a22be4642..91744b7a5e 100644 --- a/ports/windows/Makefile +++ b/ports/windows/Makefile @@ -91,6 +91,8 @@ ifneq ($(FROZEN_MANIFEST),) CFLAGS += -DMICROPY_QSTR_EXTRA_POOL=mp_qstr_frozen_const_pool -DMICROPY_MODULE_FROZEN_MPY=1 -DMPZ_DIG_SIZE=16 endif +CXXFLAGS += $(filter-out -std=gnu99,$(CFLAGS) $(CXXFLAGS_MOD)) + include $(TOP)/py/mkrules.mk .PHONY: test test_full From 130f7db1fc2a881647d727bc76a0ef40bd179e5c Mon Sep 17 00:00:00 2001 From: Christophe Priouzeau Date: Thu, 11 Feb 2021 12:00:26 +0100 Subject: [PATCH 138/619] stm32/boards/NUCLEO_WB55: Add Arduino pin alias definitions. Fixes issue #8295. Signed-off-by: Christophe Priouzeau --- ports/stm32/boards/NUCLEO_WB55/pins.csv | 52 +++++++++++-------------- 1 file changed, 22 insertions(+), 30 deletions(-) diff --git a/ports/stm32/boards/NUCLEO_WB55/pins.csv b/ports/stm32/boards/NUCLEO_WB55/pins.csv index d7e0babf36..cc03a3f1f1 100644 --- a/ports/stm32/boards/NUCLEO_WB55/pins.csv +++ b/ports/stm32/boards/NUCLEO_WB55/pins.csv @@ -1,49 +1,41 @@ -,PA0 -,PA1 -,PA2 -,PA3 -,PA4 -,PA5 -,PA6 -,PA7 -,PA8 -,PA9 -,PA10 -,PA11 -,PA12 ,PA13 ,PA14 -,PA15 -,PB0 -,PB1 ,PB2 ,PB3 ,PB4 -,PB5 ,PB6 ,PB7 -,PB8 -,PB9 ,PB10 ,PB11 ,PB12 ,PB13 ,PB14 ,PB15 -,PC0 -,PC1 -,PC2 -,PC3 -,PC4 ,PC5 -,PC6 -,PC10 ,PC11 -,PC12 -,PC13 -,PD0 -,PD1 ,PE4 +A0,PC0 +A1,PC1 +A2,PA1 +A3,PA0 +A4,PC3 +A5,PC2 +D0,PA3 +D1,PA2 +D2,PC6 +D3,PA10 +D4,PC10 +D5,PA15 +D6,PA8 +D7,PC13 +D8,PC12 +D9,PA9 +D10,PA4 +D11,PA7 +D12,PA6 +D13,PA5 +D14,PB9 +D15,PB8 SW,PC4 SW1,PC4 SW2,PD0 From 2b62f1210392f405f6b8d3510c24c25c26a457a1 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 16 Feb 2022 22:18:39 +1100 Subject: [PATCH 139/619] stm32/mboot: Allow a board to fully configure system clocks. If a board wants to customise the clocks it can define the following: MBOOT_CLK_PLLM MBOOT_CLK_PLLN MBOOT_CLK_PLLP MBOOT_CLK_PLLQ MBOOT_CLK_PLLR (only needed on STM32H7) MBOOT_FLASH_LATENCY MBOOT_CLK_AHB_DIV MBOOT_CLK_APB1_DIV MBOOT_CLK_APB2_DIV MBOOT_CLK_APB3_DIV (only needed on STM32H7) MBOOT_CLK_APB4_DIV (only needed on STM32H7) Signed-off-by: Damien George --- ports/stm32/mboot/main.c | 106 +++++++++++++++++++-------------------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/ports/stm32/mboot/main.c b/ports/stm32/mboot/main.c index abde92de1b..aec31cbc20 100644 --- a/ports/stm32/mboot/main.c +++ b/ports/stm32/mboot/main.c @@ -60,31 +60,38 @@ // Most values are defined in irq.h. #define IRQ_PRI_I2C (NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 1, 0)) -// Configure PLL to give the desired CPU freq -#undef MICROPY_HW_FLASH_LATENCY -#if defined(STM32F4) || defined(STM32F7) - #if MBOOT_ENABLE_PACKING - // With encryption/signing/compression, a faster CPU makes processing much faster. +#if defined(MBOOT_CLK_PLLM) + // The board specified the PLL values, flash latency and bus dividers + #define CORE_PLL_FREQ (1000000 * MBOOT_CLK_PLLN / MBOOT_CLK_PLLP) +#else + // The board did not specify the clock values, so configure defaults + #if defined(STM32F4) || defined(STM32F7) + #if MBOOT_ENABLE_PACKING + // With encryption/signing/compression, a faster CPU makes processing much faster. + #define CORE_PLL_FREQ (96000000) + #define MBOOT_FLASH_LATENCY FLASH_LATENCY_3 + #else + #define CORE_PLL_FREQ (48000000) + #define MBOOT_FLASH_LATENCY FLASH_LATENCY_1 + #endif + #define MBOOT_CLK_AHB_DIV (RCC_SYSCLK_DIV1) + #define MBOOT_CLK_APB1_DIV (RCC_HCLK_DIV4) + #define MBOOT_CLK_APB2_DIV (RCC_HCLK_DIV2) + #elif defined(STM32H7) #define CORE_PLL_FREQ (96000000) - #define MICROPY_HW_FLASH_LATENCY FLASH_LATENCY_3 - #else - #define CORE_PLL_FREQ (48000000) - #define MICROPY_HW_FLASH_LATENCY FLASH_LATENCY_1 + #define MBOOT_FLASH_LATENCY FLASH_LATENCY_2 + #define MBOOT_CLK_AHB_DIV (RCC_HCLK_DIV2) + #define MBOOT_CLK_APB1_DIV (RCC_APB1_DIV2) + #define MBOOT_CLK_APB2_DIV (RCC_APB2_DIV2) + #define MBOOT_CLK_APB3_DIV (RCC_APB3_DIV2) + #define MBOOT_CLK_APB4_DIV (RCC_APB4_DIV2) #endif -#elif defined(STM32H7) - #define CORE_PLL_FREQ (96000000) - #define MICROPY_HW_FLASH_LATENCY FLASH_LATENCY_2 + #define MBOOT_CLK_PLLM (MICROPY_HW_CLK_VALUE / 1000000) + #define MBOOT_CLK_PLLN (192) + #define MBOOT_CLK_PLLP (MBOOT_CLK_PLLN / (CORE_PLL_FREQ / 1000000)) + #define MBOOT_CLK_PLLQ (4) + #define MBOOT_CLK_PLLR (2) #endif -#undef MICROPY_HW_CLK_PLLM -#undef MICROPY_HW_CLK_PLLN -#undef MICROPY_HW_CLK_PLLP -#undef MICROPY_HW_CLK_PLLQ -#undef MICROPY_HW_CLK_PLLR -#define MICROPY_HW_CLK_PLLM (MICROPY_HW_CLK_VALUE / 1000000) -#define MICROPY_HW_CLK_PLLN (192) -#define MICROPY_HW_CLK_PLLP (MICROPY_HW_CLK_PLLN / (CORE_PLL_FREQ / 1000000)) -#define MICROPY_HW_CLK_PLLQ (4) -#define MICROPY_HW_CLK_PLLR (2) // Work out which USB device to use for the USB DFU interface #if !defined(MICROPY_HW_USB_MAIN_DEV) @@ -203,10 +210,10 @@ void SystemClock_Config(void) { #else 1 << RCC_PLLCFGR_PLLSRC_Pos // HSE selected as PLL source #endif - | MICROPY_HW_CLK_PLLM << RCC_PLLCFGR_PLLM_Pos - | MICROPY_HW_CLK_PLLN << RCC_PLLCFGR_PLLN_Pos - | ((MICROPY_HW_CLK_PLLP >> 1) - 1) << RCC_PLLCFGR_PLLP_Pos - | MICROPY_HW_CLK_PLLQ << RCC_PLLCFGR_PLLQ_Pos + | MBOOT_CLK_PLLM << RCC_PLLCFGR_PLLM_Pos + | MBOOT_CLK_PLLN << RCC_PLLCFGR_PLLN_Pos + | ((MBOOT_CLK_PLLP >> 1) - 1) << RCC_PLLCFGR_PLLP_Pos + | MBOOT_CLK_PLLQ << RCC_PLLCFGR_PLLQ_Pos #ifdef RCC_PLLCFGR_PLLR | 2 << RCC_PLLCFGR_PLLR_Pos // default PLLR value of 2 #endif @@ -218,12 +225,12 @@ void SystemClock_Config(void) { } // Increase latency before changing clock - if (MICROPY_HW_FLASH_LATENCY > (FLASH->ACR & FLASH_ACR_LATENCY)) { - __HAL_FLASH_SET_LATENCY(MICROPY_HW_FLASH_LATENCY); + if (MBOOT_FLASH_LATENCY > (FLASH->ACR & FLASH_ACR_LATENCY)) { + __HAL_FLASH_SET_LATENCY(MBOOT_FLASH_LATENCY); } // Configure AHB divider - MODIFY_REG(RCC->CFGR, RCC_CFGR_HPRE, RCC_SYSCLK_DIV1); + MODIFY_REG(RCC->CFGR, RCC_CFGR_HPRE, MBOOT_CLK_AHB_DIV); // Configure SYSCLK source from PLL __HAL_RCC_SYSCLK_CONFIG(RCC_SYSCLKSOURCE_PLLCLK); @@ -231,13 +238,13 @@ void SystemClock_Config(void) { } // Decrease latency after changing clock - if (MICROPY_HW_FLASH_LATENCY < (FLASH->ACR & FLASH_ACR_LATENCY)) { - __HAL_FLASH_SET_LATENCY(MICROPY_HW_FLASH_LATENCY); + if (MBOOT_FLASH_LATENCY < (FLASH->ACR & FLASH_ACR_LATENCY)) { + __HAL_FLASH_SET_LATENCY(MBOOT_FLASH_LATENCY); } // Set APB clock dividers - MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE1, RCC_HCLK_DIV4); - MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE2, RCC_HCLK_DIV2 << 3); + MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE1, MBOOT_CLK_APB1_DIV); + MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE2, MBOOT_CLK_APB2_DIV << 3); // Update clock value and reconfigure systick now that the frequency changed SystemCoreClock = CORE_PLL_FREQ; @@ -281,14 +288,14 @@ void SystemClock_Config(void) { RCC->PLLCFGR = 0; // Configure PLL1 for use by SYSCLK - RCC->PLLCKSELR |= MICROPY_HW_CLK_PLLM << RCC_PLLCKSELR_DIVM1_Pos; + RCC->PLLCKSELR |= MBOOT_CLK_PLLM << RCC_PLLCKSELR_DIVM1_Pos; RCC->PLLCFGR |= RCC_PLLCFGR_DIVP1EN; RCC->PLL1FRACR = 0; RCC->PLL1DIVR = - (MICROPY_HW_CLK_PLLN - 1) << RCC_PLL1DIVR_N1_Pos - | (MICROPY_HW_CLK_PLLP - 1) << RCC_PLL1DIVR_P1_Pos // only even P allowed - | (MICROPY_HW_CLK_PLLQ - 1) << RCC_PLL1DIVR_Q1_Pos - | (MICROPY_HW_CLK_PLLR - 1) << RCC_PLL1DIVR_R1_Pos; + (MBOOT_CLK_PLLN - 1) << RCC_PLL1DIVR_N1_Pos + | (MBOOT_CLK_PLLP - 1) << RCC_PLL1DIVR_P1_Pos // only even P allowed + | (MBOOT_CLK_PLLQ - 1) << RCC_PLL1DIVR_Q1_Pos + | (MBOOT_CLK_PLLR - 1) << RCC_PLL1DIVR_R1_Pos; // Configure PLL3 for use by USB at Q=48MHz RCC->PLLCKSELR |= MICROPY_HW_CLK_PLL3M << RCC_PLLCKSELR_DIVM3_Pos; @@ -314,14 +321,14 @@ void SystemClock_Config(void) { } // Increase latency before changing SYSCLK - if (MICROPY_HW_FLASH_LATENCY > (FLASH->ACR & FLASH_ACR_LATENCY)) { - __HAL_FLASH_SET_LATENCY(MICROPY_HW_FLASH_LATENCY); + if (MBOOT_FLASH_LATENCY > (FLASH->ACR & FLASH_ACR_LATENCY)) { + __HAL_FLASH_SET_LATENCY(MBOOT_FLASH_LATENCY); } // Configure AHB divider RCC->D1CFGR = 0 << RCC_D1CFGR_D1CPRE_Pos // SYSCLK prescaler of 1 - | 8 << RCC_D1CFGR_HPRE_Pos // AHB prescaler of 2 + | MBOOT_CLK_AHB_DIV ; // Configure SYSCLK source from PLL @@ -330,21 +337,14 @@ void SystemClock_Config(void) { } // Decrease latency after changing clock - if (MICROPY_HW_FLASH_LATENCY < (FLASH->ACR & FLASH_ACR_LATENCY)) { - __HAL_FLASH_SET_LATENCY(MICROPY_HW_FLASH_LATENCY); + if (MBOOT_FLASH_LATENCY < (FLASH->ACR & FLASH_ACR_LATENCY)) { + __HAL_FLASH_SET_LATENCY(MBOOT_FLASH_LATENCY); } // Set APB clock dividers - RCC->D1CFGR |= - 4 << RCC_D1CFGR_D1PPRE_Pos // APB3 prescaler of 2 - ; - RCC->D2CFGR = - 4 << RCC_D2CFGR_D2PPRE2_Pos // APB2 prescaler of 2 - | 4 << RCC_D2CFGR_D2PPRE1_Pos // APB1 prescaler of 2 - ; - RCC->D3CFGR = - 4 << RCC_D3CFGR_D3PPRE_Pos // APB4 prescaler of 2 - ; + RCC->D1CFGR |= MBOOT_CLK_APB3_DIV; + RCC->D2CFGR = MBOOT_CLK_APB2_DIV | MBOOT_CLK_APB1_DIV; + RCC->D3CFGR = MBOOT_CLK_APB4_DIV; // Update clock value and reconfigure systick now that the frequency changed SystemCoreClockUpdate(); From 5995fb526158ae224dddacef212c1d553d6a6306 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 16 Feb 2022 22:22:04 +1100 Subject: [PATCH 140/619] stm32/mboot: Allow a board more control over entry initialisation. If MBOOT_BOARD_ENTRY_INIT is defined by a board then that function must now make sure system clocks are configured, eg by calling mboot_entry_init(). Signed-off-by: Damien George --- ports/stm32/mboot/main.c | 13 +------------ ports/stm32/mboot/mboot.h | 22 ++++++++++++++++++++-- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/ports/stm32/mboot/main.c b/ports/stm32/mboot/main.c index aec31cbc20..8d6e28f189 100644 --- a/ports/stm32/mboot/main.c +++ b/ports/stm32/mboot/main.c @@ -1507,14 +1507,7 @@ void stm32_main(uint32_t initial_r0) { enter_bootloader: - // Init subsystems (mboot_get_reset_mode() may call these, calling them again is ok) - led_init(); - - // set the system clock to be HSE - SystemClock_Config(); - - // Ensure IRQs are enabled (needed coming out of ST bootloader on H7) - __set_PRIMASK(0); + MBOOT_BOARD_ENTRY_INIT(&initial_r0); #if USE_USB_POLLING // irqs with a priority value greater or equal to "pri" will be disabled @@ -1524,10 +1517,6 @@ enter_bootloader: __ASM volatile ("msr basepri_max, %0" : : "r" (pri) : "memory"); #endif - #if defined(MBOOT_BOARD_ENTRY_INIT) - MBOOT_BOARD_ENTRY_INIT(initial_r0); - #endif - #if defined(MBOOT_SPIFLASH_ADDR) MBOOT_SPIFLASH_SPIFLASH->config = MBOOT_SPIFLASH_CONFIG; mp_spiflash_init(MBOOT_SPIFLASH_SPIFLASH); diff --git a/ports/stm32/mboot/mboot.h b/ports/stm32/mboot/mboot.h index 3a27ce08ef..254c842653 100644 --- a/ports/stm32/mboot/mboot.h +++ b/ports/stm32/mboot/mboot.h @@ -26,8 +26,7 @@ #ifndef MICROPY_INCLUDED_STM32_MBOOT_MBOOT_H #define MICROPY_INCLUDED_STM32_MBOOT_MBOOT_H -#include -#include +#include "py/mphal.h" // Use this to tag global static data in RAM that doesn't need to be zeroed on startup #define SECTION_NOZERO_BSS __attribute__((section(".nozero_bss"))) @@ -39,6 +38,10 @@ #define NORETURN __attribute__((noreturn)) #define MP_ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#ifndef MBOOT_BOARD_ENTRY_INIT +#define MBOOT_BOARD_ENTRY_INIT mboot_entry_init +#endif + enum { MBOOT_ERRNO_FLASH_ERASE_DISALLOWED = 200, MBOOT_ERRNO_FLASH_ERASE_FAILED, @@ -86,6 +89,8 @@ enum { extern uint8_t _estack[ELEM_DATA_SIZE]; void systick_init(void); +void led_init(void); +void SystemClock_Config(void); uint32_t get_le32(const uint8_t *b); void led_state_all(unsigned int mask); @@ -101,4 +106,17 @@ int do_write(uint32_t addr, const uint8_t *src8, size_t len); const uint8_t *elem_search(const uint8_t *elem, uint8_t elem_id); int fsload_process(void); +static inline void mboot_entry_init(uint32_t *initial_r0) { + // Init subsystems (mboot_get_reset_mode() may call these, calling them again is ok) + led_init(); + + // set the system clock to be HSE + SystemClock_Config(); + + #if defined(STM32H7) + // Ensure IRQs are enabled (needed coming out of ST bootloader on H7) + __set_PRIMASK(0); + #endif +} + #endif // MICROPY_INCLUDED_STM32_MBOOT_MBOOT_H From ff9c7085077a9aa4e715b3952d24ada7b096d744 Mon Sep 17 00:00:00 2001 From: stijn Date: Thu, 17 Feb 2022 10:51:17 +0100 Subject: [PATCH 141/619] tests/run-tests.py: Skip repl tests when running windows underneath. Some versions of Python (for instance: the mingw-w64 version which can be installed on MSYS2) do include a pty module and claim to be posix-like (os.name == 'posix'), yet the select.select call used in run-tests.py hangs forever. To be on the safe side just exclude anything which might be running on windows. --- tests/run-tests.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/run-tests.py b/tests/run-tests.py index 6f3c09d1da..f9a16283bc 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -109,6 +109,10 @@ def run_micropython(pyb, args, test_file, is_special=False): return b"SKIP\n" import select + # Even though these might have the pty module, it's unlikely to function. + if sys.platform in ["win32", "msys", "cygwin"]: + return b"SKIP\n" + def get(required=False): rv = b"" while True: From c8cd5a9960e7cda0da064dcddd748808aa95ffea Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 18 Feb 2022 15:50:01 +1100 Subject: [PATCH 142/619] esp32/mpconfigport.h: Use the "extra" feature level. This commit is a no-op change to simplify existing config. Signed-off-by: Damien George --- ports/esp32/mpconfigport.h | 96 ++++---------------------------------- 1 file changed, 8 insertions(+), 88 deletions(-) diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index c457a5743b..2ef90dea52 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -10,6 +10,10 @@ #include "freertos/FreeRTOS.h" #include "driver/i2s.h" +#ifndef MICROPY_CONFIG_ROM_LEVEL +#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES) +#endif + // object representation and NLR handling #define MICROPY_OBJ_REPR (MICROPY_OBJ_REPR_A) #define MICROPY_NLR_SETJMP (1) @@ -27,107 +31,36 @@ #endif // compiler configuration -#define MICROPY_COMP_MODULE_CONST (1) -#define MICROPY_COMP_TRIPLE_TUPLE_ASSIGN (1) +#define MICROPY_COMP_RETURN_IF_EXPR (0) // optimisations #define MICROPY_OPT_COMPUTED_GOTO (1) -#define MICROPY_OPT_LOAD_ATTR_FAST_PATH (1) -#define MICROPY_OPT_MAP_LOOKUP_CACHE (1) -#define MICROPY_OPT_MPZ_BITWISE (1) +#define MICROPY_OPT_MATH_FACTORIAL (0) // Python internal features #define MICROPY_READER_VFS (1) #define MICROPY_ENABLE_GC (1) -#define MICROPY_ENABLE_FINALISER (1) -#define MICROPY_STACK_CHECK (1) #define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1) -#define MICROPY_KBD_EXCEPTION (1) -#define MICROPY_HELPER_REPL (1) -#define MICROPY_REPL_EMACS_KEYS (1) -#define MICROPY_REPL_AUTO_INDENT (1) #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) -#define MICROPY_ENABLE_SOURCE_LINE (1) #define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_NORMAL) #define MICROPY_WARNINGS (1) #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) -#define MICROPY_CPYTHON_COMPAT (1) -#define MICROPY_STREAMS_NON_BLOCK (1) #define MICROPY_STREAMS_POSIX_API (1) -#define MICROPY_MODULE_BUILTIN_INIT (1) -#define MICROPY_MODULE_WEAK_LINKS (1) #define MICROPY_MODULE_FROZEN_STR (0) #define MICROPY_MODULE_FROZEN_MPY (1) #define MICROPY_QSTR_EXTRA_POOL mp_qstr_frozen_const_pool -#define MICROPY_CAN_OVERRIDE_BUILTINS (1) #define MICROPY_USE_INTERNAL_ERRNO (0) // errno.h from xtensa-esp32-elf/sys-include/sys #define MICROPY_USE_INTERNAL_PRINTF (0) // ESP32 SDK requires its own printf -#define MICROPY_ENABLE_SCHEDULER (1) #define MICROPY_SCHEDULER_DEPTH (8) #define MICROPY_VFS (1) // control over Python builtins -#define MICROPY_PY_FUNCTION_ATTRS (1) -#define MICROPY_PY_DESCRIPTORS (1) -#define MICROPY_PY_DELATTR_SETATTR (1) -#define MICROPY_PY_FSTRINGS (1) #define MICROPY_PY_STR_BYTES_CMP_WARN (1) -#define MICROPY_PY_BUILTINS_STR_UNICODE (1) -#define MICROPY_PY_BUILTINS_STR_CENTER (1) -#define MICROPY_PY_BUILTINS_STR_PARTITION (1) -#define MICROPY_PY_BUILTINS_STR_SPLITLINES (1) -#define MICROPY_PY_BUILTINS_BYTEARRAY (1) -#define MICROPY_PY_BUILTINS_MEMORYVIEW (1) -#define MICROPY_PY_BUILTINS_SET (1) -#define MICROPY_PY_BUILTINS_SLICE (1) -#define MICROPY_PY_BUILTINS_SLICE_ATTRS (1) -#define MICROPY_PY_BUILTINS_SLICE_INDICES (1) -#define MICROPY_PY_BUILTINS_FROZENSET (1) -#define MICROPY_PY_BUILTINS_PROPERTY (1) -#define MICROPY_PY_BUILTINS_RANGE_ATTRS (1) -#define MICROPY_PY_BUILTINS_ROUND_INT (1) -#define MICROPY_PY_ALL_SPECIAL_METHODS (1) #define MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS (1) -#define MICROPY_PY_REVERSE_SPECIAL_METHODS (1) -#define MICROPY_PY_BUILTINS_COMPILE (1) -#define MICROPY_PY_BUILTINS_ENUMERATE (1) -#define MICROPY_PY_BUILTINS_EXECFILE (1) -#define MICROPY_PY_BUILTINS_FILTER (1) -#define MICROPY_PY_BUILTINS_REVERSED (1) -#define MICROPY_PY_BUILTINS_NOTIMPLEMENTED (1) -#define MICROPY_PY_BUILTINS_INPUT (1) -#define MICROPY_PY_BUILTINS_MIN_MAX (1) -#define MICROPY_PY_BUILTINS_POW3 (1) -#define MICROPY_PY_BUILTINS_HELP (1) #define MICROPY_PY_BUILTINS_HELP_TEXT esp32_help_text -#define MICROPY_PY_BUILTINS_HELP_MODULES (1) -#define MICROPY_PY___FILE__ (1) -#define MICROPY_PY_MICROPYTHON_MEM_INFO (1) -#define MICROPY_PY_ARRAY (1) -#define MICROPY_PY_ARRAY_SLICE_ASSIGN (1) -#define MICROPY_PY_ATTRTUPLE (1) -#define MICROPY_PY_COLLECTIONS (1) -#define MICROPY_PY_COLLECTIONS_DEQUE (1) -#define MICROPY_PY_COLLECTIONS_ORDEREDDICT (1) -#define MICROPY_PY_MATH (1) -#define MICROPY_PY_MATH_SPECIAL_FUNCTIONS (1) -#define MICROPY_PY_MATH_ISCLOSE (1) -#define MICROPY_PY_CMATH (1) -#define MICROPY_PY_GC (1) -#define MICROPY_PY_IO (1) -#define MICROPY_PY_IO_IOBASE (1) -#define MICROPY_PY_IO_FILEIO (1) -#define MICROPY_PY_IO_BYTESIO (1) +#define MICROPY_PY_MATH_CONSTANTS (0) +#define MICROPY_PY_MATH_FACTORIAL (0) #define MICROPY_PY_IO_BUFFEREDWRITER (1) -#define MICROPY_PY_STRUCT (1) -#define MICROPY_PY_SYS (1) -#define MICROPY_PY_SYS_MAXSIZE (1) -#define MICROPY_PY_SYS_MODULES (1) -#define MICROPY_PY_SYS_EXIT (1) -#define MICROPY_PY_SYS_STDFILES (1) -#define MICROPY_PY_SYS_STDIO_BUFFER (1) -#define MICROPY_PY_UERRNO (1) -#define MICROPY_PY_USELECT (1) #define MICROPY_PY_UTIME_MP_HAL (1) #define MICROPY_PY_THREAD (1) #define MICROPY_PY_THREAD_GIL (1) @@ -140,22 +73,10 @@ #define MICROPY_BLUETOOTH_NIMBLE (1) #define MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY (1) #endif -#define MICROPY_PY_UASYNCIO (1) -#define MICROPY_PY_UCTYPES (1) -#define MICROPY_PY_UZLIB (1) -#define MICROPY_PY_UJSON (1) -#define MICROPY_PY_URE (1) -#define MICROPY_PY_URE_SUB (1) -#define MICROPY_PY_UHEAPQ (1) #define MICROPY_PY_UTIMEQ (1) -#define MICROPY_PY_UHASHLIB (1) #define MICROPY_PY_UHASHLIB_SHA1 (1) #define MICROPY_PY_UHASHLIB_SHA256 (1) #define MICROPY_PY_UCRYPTOLIB (1) -#define MICROPY_PY_UBINASCII (1) -#define MICROPY_PY_UBINASCII_CRC32 (1) -#define MICROPY_PY_URANDOM (1) -#define MICROPY_PY_URANDOM_EXTRA_FUNCS (1) #define MICROPY_PY_URANDOM_SEED_INIT_FUNC (esp_random()) #define MICROPY_PY_OS_DUPTERM (1) #define MICROPY_PY_MACHINE (1) @@ -192,7 +113,6 @@ #define MICROPY_PY_USSL_FINALISER (1) #define MICROPY_PY_UWEBSOCKET (1) #define MICROPY_PY_WEBREPL (1) -#define MICROPY_PY_FRAMEBUF (1) #define MICROPY_PY_BTREE (1) #define MICROPY_PY_ONEWIRE (1) #define MICROPY_PY_UPLATFORM (1) From 5935fa229c94e53c4b23d0a491cb16d9adb9dada Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 18 Feb 2022 16:01:18 +1100 Subject: [PATCH 143/619] esp32/mpconfigport.h: Enable remaining features to get to "extra" level. Signed-off-by: Damien George --- ports/esp32/mpconfigport.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index 2ef90dea52..1ea19ada01 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -30,12 +30,8 @@ #define MICROPY_EMIT_XTENSAWIN (1) #endif -// compiler configuration -#define MICROPY_COMP_RETURN_IF_EXPR (0) - // optimisations #define MICROPY_OPT_COMPUTED_GOTO (1) -#define MICROPY_OPT_MATH_FACTORIAL (0) // Python internal features #define MICROPY_READER_VFS (1) @@ -58,8 +54,6 @@ #define MICROPY_PY_STR_BYTES_CMP_WARN (1) #define MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS (1) #define MICROPY_PY_BUILTINS_HELP_TEXT esp32_help_text -#define MICROPY_PY_MATH_CONSTANTS (0) -#define MICROPY_PY_MATH_FACTORIAL (0) #define MICROPY_PY_IO_BUFFEREDWRITER (1) #define MICROPY_PY_UTIME_MP_HAL (1) #define MICROPY_PY_THREAD (1) From 106a83de222a7c9ceb3f70ca214677c996916489 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 7 Feb 2022 21:05:48 +1100 Subject: [PATCH 144/619] esp32/esp32_partition: Add support for specifying block_size. To support filesystems that use a block size different from the native erase-page size. Signed-off-by: Damien George --- docs/library/esp32.rst | 8 +++- ports/esp32/esp32_partition.c | 80 ++++++++++++++++++++++++++++------- 2 files changed, 70 insertions(+), 18 deletions(-) diff --git a/docs/library/esp32.rst b/docs/library/esp32.rst index ff1c99a553..82e69e4964 100644 --- a/docs/library/esp32.rst +++ b/docs/library/esp32.rst @@ -68,17 +68,21 @@ Flash partitions This class gives access to the partitions in the device's flash memory and includes methods to enable over-the-air (OTA) updates. -.. class:: Partition(id) +.. class:: Partition(id, block_size=4096) Create an object representing a partition. *id* can be a string which is the label of the partition to retrieve, or one of the constants: ``BOOT`` or ``RUNNING``. + *block_size* specifies the byte size of an individual block. -.. classmethod:: Partition.find(type=TYPE_APP, subtype=0xff, label=None) +.. classmethod:: Partition.find(type=TYPE_APP, subtype=0xff, label=None, block_size=4096) Find a partition specified by *type*, *subtype* and *label*. Returns a (possibly empty) list of Partition objects. Note: ``subtype=0xff`` matches any subtype and ``label=None`` matches any label. + *block_size* specifies the byte size of an individual block used by the returned + objects. + .. method:: Partition.info() Returns a 6-tuple ``(type, subtype, addr, size, label, encrypted)``. diff --git a/ports/esp32/esp32_partition.c b/ports/esp32/esp32_partition.c index dc2bdd7120..b779cb18c6 100644 --- a/ports/esp32/esp32_partition.c +++ b/ports/esp32/esp32_partition.c @@ -34,9 +34,12 @@ #include "esp_ota_ops.h" // esp_partition_read and esp_partition_write can operate on arbitrary bytes -// but esp_partition_erase_range operates on 4k blocks. But to make a partition -// implement the standard block protocol all operations are done on 4k blocks. -#define BLOCK_SIZE_BYTES (4096) +// but esp_partition_erase_range operates on 4k blocks. The default block size +// for a Partition object is therefore 4k, to make writes efficient, and also +// make it work well with filesystems like littlefs. The Partition object also +// supports smaller block sizes, in which case a cache is used and writes may +// be less efficient. +#define NATIVE_BLOCK_SIZE_BYTES (4096) enum { ESP32_PARTITION_BOOT, @@ -46,15 +49,23 @@ enum { typedef struct _esp32_partition_obj_t { mp_obj_base_t base; const esp_partition_t *part; + uint8_t *cache; + uint16_t block_size; } esp32_partition_obj_t; -STATIC esp32_partition_obj_t *esp32_partition_new(const esp_partition_t *part) { +STATIC esp32_partition_obj_t *esp32_partition_new(const esp_partition_t *part, uint16_t block_size) { if (part == NULL) { mp_raise_OSError(MP_ENOENT); } esp32_partition_obj_t *self = m_new_obj(esp32_partition_obj_t); self->base.type = &esp32_partition_type; self->part = part; + self->block_size = block_size; + if (self->block_size < NATIVE_BLOCK_SIZE_BYTES) { + self->cache = m_new(uint8_t, NATIVE_BLOCK_SIZE_BYTES); + } else { + self->cache = NULL; + } return self; } @@ -69,7 +80,7 @@ STATIC void esp32_partition_print(const mp_print_t *print, mp_obj_t self_in, mp_ STATIC mp_obj_t esp32_partition_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { // Check args - mp_arg_check_num(n_args, n_kw, 1, 1, false); + mp_arg_check_num(n_args, n_kw, 1, 2, false); // Get requested partition const esp_partition_t *part; @@ -94,17 +105,24 @@ STATIC mp_obj_t esp32_partition_make_new(const mp_obj_type_t *type, size_t n_arg } } + // Get block size if given + uint16_t block_size = NATIVE_BLOCK_SIZE_BYTES; + if (n_args == 2) { + block_size = mp_obj_get_int(all_args[1]); + } + // Return new object - return MP_OBJ_FROM_PTR(esp32_partition_new(part)); + return MP_OBJ_FROM_PTR(esp32_partition_new(part, block_size)); } STATIC mp_obj_t esp32_partition_find(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { // Parse args - enum { ARG_type, ARG_subtype, ARG_label }; + enum { ARG_type, ARG_subtype, ARG_label, ARG_block_size }; static const mp_arg_t allowed_args[] = { { MP_QSTR_type, MP_ARG_INT, {.u_int = ESP_PARTITION_TYPE_APP} }, { MP_QSTR_subtype, MP_ARG_INT, {.u_int = ESP_PARTITION_SUBTYPE_ANY} }, { MP_QSTR_label, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_block_size, MP_ARG_INT, {.u_int = NATIVE_BLOCK_SIZE_BYTES} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); @@ -115,11 +133,14 @@ STATIC mp_obj_t esp32_partition_find(size_t n_args, const mp_obj_t *pos_args, mp label = mp_obj_str_get_str(args[ARG_label].u_obj); } + // Get block size + uint16_t block_size = args[ARG_block_size].u_int; + // Build list of matching partitions mp_obj_t list = mp_obj_new_list(0, NULL); esp_partition_iterator_t iter = esp_partition_find(args[ARG_type].u_int, args[ARG_subtype].u_int, label); while (iter != NULL) { - mp_obj_list_append(list, MP_OBJ_FROM_PTR(esp32_partition_new(esp_partition_get(iter)))); + mp_obj_list_append(list, MP_OBJ_FROM_PTR(esp32_partition_new(esp_partition_get(iter), block_size))); iter = esp_partition_next(iter); } esp_partition_iterator_release(iter); @@ -145,7 +166,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp32_partition_info_obj, esp32_partition_info) STATIC mp_obj_t esp32_partition_readblocks(size_t n_args, const mp_obj_t *args) { esp32_partition_obj_t *self = MP_OBJ_TO_PTR(args[0]); - uint32_t offset = mp_obj_get_int(args[1]) * BLOCK_SIZE_BYTES; + uint32_t offset = mp_obj_get_int(args[1]) * self->block_size; mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_WRITE); if (n_args == 4) { @@ -158,12 +179,36 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp32_partition_readblocks_obj, 3, 4, STATIC mp_obj_t esp32_partition_writeblocks(size_t n_args, const mp_obj_t *args) { esp32_partition_obj_t *self = MP_OBJ_TO_PTR(args[0]); - uint32_t offset = mp_obj_get_int(args[1]) * BLOCK_SIZE_BYTES; + uint32_t offset = mp_obj_get_int(args[1]) * self->block_size; mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_READ); if (n_args == 3) { - check_esp_err(esp_partition_erase_range(self->part, offset, bufinfo.len)); + // A simple write, which requires erasing first. + if (self->block_size >= NATIVE_BLOCK_SIZE_BYTES) { + // Block size is at least native erase-page size, so do an efficient erase. + check_esp_err(esp_partition_erase_range(self->part, offset, bufinfo.len)); + } else { + // Block size is less than native erase-page size, so do erase in sections. + uint32_t addr = (offset / NATIVE_BLOCK_SIZE_BYTES) * NATIVE_BLOCK_SIZE_BYTES; + uint32_t o = offset % NATIVE_BLOCK_SIZE_BYTES; + uint32_t top_addr = offset + bufinfo.len; + while (addr < top_addr) { + if (o > 0 || top_addr < addr + NATIVE_BLOCK_SIZE_BYTES) { + check_esp_err(esp_partition_read(self->part, addr, self->cache, NATIVE_BLOCK_SIZE_BYTES)); + } + check_esp_err(esp_partition_erase_range(self->part, addr, NATIVE_BLOCK_SIZE_BYTES)); + if (o > 0) { + check_esp_err(esp_partition_write(self->part, addr, self->cache, o)); + } + if (top_addr < addr + NATIVE_BLOCK_SIZE_BYTES) { + check_esp_err(esp_partition_write(self->part, top_addr, self->cache, addr + NATIVE_BLOCK_SIZE_BYTES - top_addr)); + } + o = 0; + addr += NATIVE_BLOCK_SIZE_BYTES; + } + } } else { + // An extended write, erasing must have been done explicitly before this write. offset += mp_obj_get_int(args[3]); } check_esp_err(esp_partition_write(self->part, offset, bufinfo.buf, bufinfo.len)); @@ -182,12 +227,15 @@ STATIC mp_obj_t esp32_partition_ioctl(mp_obj_t self_in, mp_obj_t cmd_in, mp_obj_ case MP_BLOCKDEV_IOCTL_SYNC: return MP_OBJ_NEW_SMALL_INT(0); case MP_BLOCKDEV_IOCTL_BLOCK_COUNT: - return MP_OBJ_NEW_SMALL_INT(self->part->size / BLOCK_SIZE_BYTES); + return MP_OBJ_NEW_SMALL_INT(self->part->size / self->block_size); case MP_BLOCKDEV_IOCTL_BLOCK_SIZE: - return MP_OBJ_NEW_SMALL_INT(BLOCK_SIZE_BYTES); + return MP_OBJ_NEW_SMALL_INT(self->block_size); case MP_BLOCKDEV_IOCTL_BLOCK_ERASE: { - uint32_t offset = mp_obj_get_int(arg_in) * BLOCK_SIZE_BYTES; - check_esp_err(esp_partition_erase_range(self->part, offset, BLOCK_SIZE_BYTES)); + if (self->block_size != NATIVE_BLOCK_SIZE_BYTES) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + uint32_t offset = mp_obj_get_int(arg_in) * NATIVE_BLOCK_SIZE_BYTES; + check_esp_err(esp_partition_erase_range(self->part, offset, NATIVE_BLOCK_SIZE_BYTES)); return MP_OBJ_NEW_SMALL_INT(0); } default: @@ -205,7 +253,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp32_partition_set_boot_obj, esp32_partition_s STATIC mp_obj_t esp32_partition_get_next_update(mp_obj_t self_in) { esp32_partition_obj_t *self = MP_OBJ_TO_PTR(self_in); - return MP_OBJ_FROM_PTR(esp32_partition_new(esp_ota_get_next_update_partition(self->part))); + return MP_OBJ_FROM_PTR(esp32_partition_new(esp_ota_get_next_update_partition(self->part), NATIVE_BLOCK_SIZE_BYTES)); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp32_partition_get_next_update_obj, esp32_partition_get_next_update); From f30b32e084ec49cdb2bf6011963dd1fa98945706 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 7 Feb 2022 21:06:05 +1100 Subject: [PATCH 145/619] esp32/modules: Create ffat partition object with block_size=512. Because these are formatted with a 512 sector size. Signed-off-by: Damien George --- ports/esp32/modules/flashbdev.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/esp32/modules/flashbdev.py b/ports/esp32/modules/flashbdev.py index c3f75c7a7e..1ee6ff779b 100644 --- a/ports/esp32/modules/flashbdev.py +++ b/ports/esp32/modules/flashbdev.py @@ -3,5 +3,5 @@ from esp32 import Partition # MicroPython's partition table uses "vfs", TinyUF2 uses "ffat". bdev = Partition.find(Partition.TYPE_DATA, label="vfs") if not bdev: - bdev = Partition.find(Partition.TYPE_DATA, label="ffat") + bdev = Partition.find(Partition.TYPE_DATA, label="ffat", block_size=512) bdev = bdev[0] if bdev else None From 49934fcf8b0b2bd906a96e1ff57b724d8dd6d93e Mon Sep 17 00:00:00 2001 From: stijn Date: Fri, 18 Feb 2022 19:36:40 +0100 Subject: [PATCH 146/619] extmod/moduplatform: Move platform PP definitions into a header. These are more generally useful than just for the module so make them globally available, prefixed consistently with MICROPY_PLATFORM_. --- extmod/moduplatform.c | 82 +++------------------------------ extmod/moduplatform.h | 105 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 75 deletions(-) create mode 100644 extmod/moduplatform.h diff --git a/extmod/moduplatform.c b/extmod/moduplatform.c index 820feb312a..fa59c9227d 100644 --- a/extmod/moduplatform.c +++ b/extmod/moduplatform.c @@ -29,87 +29,19 @@ #include "py/objtuple.h" #include "py/objstr.h" #include "py/mphal.h" +#include "extmod/moduplatform.h" #include "genhdr/mpversion.h" #if MICROPY_PY_UPLATFORM // platform - Access to underlying platform's identifying data -// TODO: Add more architectures, compilers and libraries. -// See: https://sourceforge.net/p/predef/wiki/Home/ - -#if defined(__ARM_ARCH) -#define PLATFORM_ARCH "arm" -#elif defined(__x86_64__) || defined(_WIN64) -#define PLATFORM_ARCH "x86_64" -#elif defined(__i386__) || defined(_M_IX86) -#define PLATFORM_ARCH "x86" -#elif defined(__xtensa__) || defined(_M_IX86) -#define PLATFORM_ARCH "xtensa" -#else -#define PLATFORM_ARCH "" -#endif - -#if defined(__GNUC__) -#define PLATFORM_COMPILER \ - "GCC " \ - MP_STRINGIFY(__GNUC__) "." \ - MP_STRINGIFY(__GNUC_MINOR__) "." \ - MP_STRINGIFY(__GNUC_PATCHLEVEL__) -#elif defined(__ARMCC_VERSION) -#define PLATFORM_COMPILER \ - "ARMCC " \ - MP_STRINGIFY((__ARMCC_VERSION / 1000000)) "." \ - MP_STRINGIFY((__ARMCC_VERSION / 10000 % 100)) "." \ - MP_STRINGIFY((__ARMCC_VERSION % 10000)) -#elif defined(_MSC_VER) -#if defined(_WIN64) -#define COMPILER_BITS "64 bit" -#elif defined(_M_IX86) -#define COMPILER_BITS "32 bit" -#else -#define COMPILER_BITS "" -#endif -#define PLATFORM_COMPILER \ - "MSC v." MP_STRINGIFY(_MSC_VER) " " COMPILER_BITS -#else -#define PLATFORM_COMPILER "" -#endif - -#if defined(__GLIBC__) -#define PLATFORM_LIBC_LIB "glibc" -#define PLATFORM_LIBC_VER \ - MP_STRINGIFY(__GLIBC__) "." \ - MP_STRINGIFY(__GLIBC_MINOR__) -#elif defined(__NEWLIB__) -#define PLATFORM_LIBC_LIB "newlib" -#define PLATFORM_LIBC_VER _NEWLIB_VERSION -#else -#define PLATFORM_LIBC_LIB "" -#define PLATFORM_LIBC_VER "" -#endif - -#if defined(__linux) -#define PLATFORM_SYSTEM "Linux" -#elif defined(__unix__) -#define PLATFORM_SYSTEM "Unix" -#elif defined(__CYGWIN__) -#define PLATFORM_SYSTEM "Cygwin" -#elif defined(_WIN32) -#define PLATFORM_SYSTEM "Windows" -#else -#define PLATFORM_SYSTEM "MicroPython" -#endif - -#ifndef MICROPY_PLATFORM_VERSION -#define MICROPY_PLATFORM_VERSION "" -#endif - -STATIC const MP_DEFINE_STR_OBJ(info_platform_obj, PLATFORM_SYSTEM "-" MICROPY_VERSION_STRING "-" \ - PLATFORM_ARCH "-" MICROPY_PLATFORM_VERSION "-with-" PLATFORM_LIBC_LIB "" PLATFORM_LIBC_VER); -STATIC const MP_DEFINE_STR_OBJ(info_python_compiler_obj, PLATFORM_COMPILER); -STATIC const MP_DEFINE_STR_OBJ(info_libc_lib_obj, PLATFORM_LIBC_LIB); -STATIC const MP_DEFINE_STR_OBJ(info_libc_ver_obj, PLATFORM_LIBC_VER); +STATIC const MP_DEFINE_STR_OBJ(info_platform_obj, MICROPY_PLATFORM_SYSTEM "-" \ + MICROPY_VERSION_STRING "-" MICROPY_PLATFORM_ARCH "-" MICROPY_PLATFORM_VERSION "-with-" \ + MICROPY_PLATFORM_LIBC_LIB "" MICROPY_PLATFORM_LIBC_VER); +STATIC const MP_DEFINE_STR_OBJ(info_python_compiler_obj, MICROPY_PLATFORM_COMPILER); +STATIC const MP_DEFINE_STR_OBJ(info_libc_lib_obj, MICROPY_PLATFORM_LIBC_LIB); +STATIC const MP_DEFINE_STR_OBJ(info_libc_ver_obj, MICROPY_PLATFORM_LIBC_VER); STATIC const mp_rom_obj_tuple_t info_libc_tuple_obj = { {&mp_type_tuple}, 2, {MP_ROM_PTR(&info_libc_lib_obj), MP_ROM_PTR(&info_libc_ver_obj)} }; diff --git a/extmod/moduplatform.h b/extmod/moduplatform.h new file mode 100644 index 0000000000..2b9ad3ae49 --- /dev/null +++ b/extmod/moduplatform.h @@ -0,0 +1,105 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2021 Ibrahim Abdelkader + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MICROPY_INCLUDED_MODUPLATFORM_H +#define MICROPY_INCLUDED_MODUPLATFORM_H + +#include "py/misc.h" // For MP_STRINGIFY. +#include "py/mpconfig.h" + +// Preprocessor directives indentifying the platform. +// The (u)platform module itself is guarded by MICROPY_PY_UPLATFORM, see the +// .c file, but these are made available because they're generally usable. +// TODO: Add more architectures, compilers and libraries. +// See: https://sourceforge.net/p/predef/wiki/Home/ + +#if defined(__ARM_ARCH) +#define MICROPY_PLATFORM_ARCH "arm" +#elif defined(__x86_64__) || defined(_WIN64) +#define MICROPY_PLATFORM_ARCH "x86_64" +#elif defined(__i386__) || defined(_M_IX86) +#define MICROPY_PLATFORM_ARCH "x86" +#elif defined(__xtensa__) || defined(_M_IX86) +#define MICROPY_PLATFORM_ARCH "xtensa" +#else +#define MICROPY_PLATFORM_ARCH "" +#endif + +#if defined(__GNUC__) +#define MICROPY_PLATFORM_COMPILER \ + "GCC " \ + MP_STRINGIFY(__GNUC__) "." \ + MP_STRINGIFY(__GNUC_MINOR__) "." \ + MP_STRINGIFY(__GNUC_PATCHLEVEL__) +#elif defined(__ARMCC_VERSION) +#define MICROPY_PLATFORM_COMPILER \ + "ARMCC " \ + MP_STRINGIFY((__ARMCC_VERSION / 1000000)) "." \ + MP_STRINGIFY((__ARMCC_VERSION / 10000 % 100)) "." \ + MP_STRINGIFY((__ARMCC_VERSION % 10000)) +#elif defined(_MSC_VER) +#if defined(_WIN64) +#define MICROPY_PLATFORM_COMPILER_BITS "64 bit" +#elif defined(_M_IX86) +#define MICROPY_PLATFORM_COMPILER_BITS "32 bit" +#else +#define MICROPY_PLATFORM_COMPILER_BITS "" +#endif +#define MICROPY_PLATFORM_COMPILER \ + "MSC v." MP_STRINGIFY(_MSC_VER) " " MICROPY_PLATFORM_COMPILER_BITS +#else +#define MICROPY_PLATFORM_COMPILER "" +#endif + +#if defined(__GLIBC__) +#define MICROPY_PLATFORM_LIBC_LIB "glibc" +#define MICROPY_PLATFORM_LIBC_VER \ + MP_STRINGIFY(__GLIBC__) "." \ + MP_STRINGIFY(__GLIBC_MINOR__) +#elif defined(__NEWLIB__) +#define MICROPY_PLATFORM_LIBC_LIB "newlib" +#define MICROPY_PLATFORM_LIBC_VER _NEWLIB_VERSION +#else +#define MICROPY_PLATFORM_LIBC_LIB "" +#define MICROPY_PLATFORM_LIBC_VER "" +#endif + +#if defined(__linux) +#define MICROPY_PLATFORM_SYSTEM "Linux" +#elif defined(__unix__) +#define MICROPY_PLATFORM_SYSTEM "Unix" +#elif defined(__CYGWIN__) +#define MICROPY_PLATFORM_SYSTEM "Cygwin" +#elif defined(_WIN32) +#define MICROPY_PLATFORM_SYSTEM "Windows" +#else +#define MICROPY_PLATFORM_SYSTEM "MicroPython" +#endif + +#ifndef MICROPY_PLATFORM_VERSION +#define MICROPY_PLATFORM_VERSION "" +#endif + +#endif // MICROPY_INCLUDED_MODUPLATFORM_H From 5f50f4a130e60c4bc5f4fbf58ac849dcaf764e72 Mon Sep 17 00:00:00 2001 From: stijn Date: Fri, 18 Feb 2022 19:38:55 +0100 Subject: [PATCH 147/619] unix: Show compiler info in REPL banner. The unix port's main.c gets used by unix and windows ports, and with a variety of compilers, so it's convenient to see which version is actually being used immediately when starting micropython. This is similar to what CPython does. --- ports/unix/main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ports/unix/main.c b/ports/unix/main.c index b2790791af..2769f33191 100644 --- a/ports/unix/main.c +++ b/ports/unix/main.c @@ -47,6 +47,7 @@ #include "py/mphal.h" #include "py/mpthread.h" #include "extmod/misc.h" +#include "extmod/moduplatform.h" #include "extmod/vfs.h" #include "extmod/vfs_posix.h" #include "genhdr/mpversion.h" @@ -178,7 +179,8 @@ STATIC char *strjoin(const char *s1, int sep_char, const char *s2) { STATIC int do_repl(void) { mp_hal_stdout_tx_str("MicroPython " MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE "; " - MICROPY_PY_SYS_PLATFORM " version\nUse Ctrl-D to exit, Ctrl-E for paste mode\n"); + MICROPY_PY_SYS_PLATFORM " [" MICROPY_PLATFORM_COMPILER "] version\n" + "Use Ctrl-D to exit, Ctrl-E for paste mode\n"); #if MICROPY_USE_READLINE == 1 From b1afbe3336a3daca170f54c471f84d28622293e4 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 24 Feb 2022 15:23:32 +1100 Subject: [PATCH 148/619] tools/ci.sh: Update IDF v4.4 build to use v4.4 tag. Signed-off-by: Damien George --- tools/ci.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/ci.sh b/tools/ci.sh index 0506a00314..3e78ae4a05 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -107,8 +107,7 @@ function ci_esp32_idf402_setup { } function ci_esp32_idf44_setup { - # This commit is just before v5.0-dev - ci_esp32_setup_helper 142bb32c50fa9875b8b69fa539a2d59559460d72 + ci_esp32_setup_helper v4.4 } function ci_esp32_build { From 64bfaae7ab33e628f28ca3b53b10893fb047b48e Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 24 Feb 2022 15:23:50 +1100 Subject: [PATCH 149/619] esp32/README.md: Update list of supported IDF versions. Signed-off-by: Damien George --- ports/esp32/README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ports/esp32/README.md b/ports/esp32/README.md index 4524696899..c37213b303 100644 --- a/ports/esp32/README.md +++ b/ports/esp32/README.md @@ -28,7 +28,7 @@ manage the ESP32 microcontroller, as well as a way to manage the required build environment and toolchains needed to build the firmware. The ESP-IDF changes quickly and MicroPython only supports certain versions. -Currently MicroPython supports v4.0.2, v4.1.1 and v4.2, +Currently MicroPython supports v4.0.2, v4.1.1, v4.2.2, v4.3.2 and v4.4, although other IDF v4 versions may also work. To install the ESP-IDF the full instructions can be found at the @@ -50,7 +50,7 @@ To check out a copy of the IDF use git clone: $ git clone -b v4.0.2 --recursive https://github.com/espressif/esp-idf.git ``` -You can replace `v4.0.2` with `v4.1.1` or `v4.2` or any other supported version. +You can replace `v4.0.2` with `v4.2.2` or `v4.4` or any other supported version. (You don't need a full recursive clone; see the `ci_esp32_setup` function in `tools/ci.sh` in this repository for more detailed set-up commands.) @@ -77,8 +77,7 @@ The `install.sh` step only needs to be done once. You will need to source **Note:** If you are building MicroPython for the ESP32-S2, ESP32-C3 or ESP32-S3, please ensure you are using the following required IDF versions: -- ESP32-S3 currently requires latest `master`, but eventually `v4.4` or later when - it's available. +- ESP32-S3 currently requires `v4.4` or later. - ESP32-S2 and ESP32-C3 require `v4.3.1` or later. Building the firmware From f2040bfc7ee033e48acef9f289790f3b4e6b74e5 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 22 Oct 2021 22:22:47 +1100 Subject: [PATCH 150/619] py: Rework bytecode and .mpy file format to be mostly static data. Background: .mpy files are precompiled .py files, built using mpy-cross, that contain compiled bytecode functions (and can also contain machine code). The benefit of using an .mpy file over a .py file is that they are faster to import and take less memory when importing. They are also smaller on disk. But the real benefit of .mpy files comes when they are frozen into the firmware. This is done by loading the .mpy file during compilation of the firmware and turning it into a set of big C data structures (the job of mpy-tool.py), which are then compiled and downloaded into the ROM of a device. These C data structures can be executed in-place, ie directly from ROM. This makes importing even faster because there is very little to do, and also means such frozen modules take up much less RAM (because their bytecode stays in ROM). The downside of frozen code is that it requires recompiling and reflashing the entire firmware. This can be a big barrier to entry, slows down development time, and makes it harder to do OTA updates of frozen code (because the whole firmware must be updated). This commit attempts to solve this problem by providing a solution that sits between loading .mpy files into RAM and freezing them into the firmware. The .mpy file format has been reworked so that it consists of data and bytecode which is mostly static and ready to run in-place. If these new .mpy files are located in flash/ROM which is memory addressable, the .mpy file can be executed (mostly) in-place. With this approach there is still a small amount of unpacking and linking of the .mpy file that needs to be done when it's imported, but it's still much better than loading an .mpy from disk into RAM (although not as good as freezing .mpy files into the firmware). The main trick to make static .mpy files is to adjust the bytecode so any qstrs that it references now go through a lookup table to convert from local qstr number in the module to global qstr number in the firmware. That means the bytecode does not need linking/rewriting of qstrs when it's loaded. Instead only a small qstr table needs to be built (and put in RAM) at import time. This means the bytecode itself is static/constant and can be used directly if it's in addressable memory. Also the qstr string data in the .mpy file, and some constant object data, can be used directly. Note that the qstr table is global to the module (ie not per function). In more detail, in the VM what used to be (schematically): qst = DECODE_QSTR_VALUE; is now (schematically): idx = DECODE_QSTR_INDEX; qst = qstr_table[idx]; That allows the bytecode to be fixed at compile time and not need relinking/rewriting of the qstr values. Only qstr_table needs to be linked when the .mpy is loaded. Incidentally, this helps to reduce the size of bytecode because what used to be 2-byte qstr values in the bytecode are now (mostly) 1-byte indices. If the module uses the same qstr more than two times then the bytecode is smaller than before. The following changes are measured for this commit compared to the previous (the baseline): - average 7%-9% reduction in size of .mpy files - frozen code size is reduced by about 5%-7% - importing .py files uses about 5% less RAM in total - importing .mpy files uses about 4% less RAM in total - importing .py and .mpy files takes about the same time as before The qstr indirection in the bytecode has only a small impact on VM performance. For stm32 on PYBv1.0 the performance change of this commit is: diff of scores (higher is better) N=100 M=100 baseline -> this-commit diff diff% (error%) bm_chaos.py 371.07 -> 357.39 : -13.68 = -3.687% (+/-0.02%) bm_fannkuch.py 78.72 -> 77.49 : -1.23 = -1.563% (+/-0.01%) bm_fft.py 2591.73 -> 2539.28 : -52.45 = -2.024% (+/-0.00%) bm_float.py 6034.93 -> 5908.30 : -126.63 = -2.098% (+/-0.01%) bm_hexiom.py 48.96 -> 47.93 : -1.03 = -2.104% (+/-0.00%) bm_nqueens.py 4510.63 -> 4459.94 : -50.69 = -1.124% (+/-0.00%) bm_pidigits.py 650.28 -> 644.96 : -5.32 = -0.818% (+/-0.23%) core_import_mpy_multi.py 564.77 -> 581.49 : +16.72 = +2.960% (+/-0.01%) core_import_mpy_single.py 68.67 -> 67.16 : -1.51 = -2.199% (+/-0.01%) core_qstr.py 64.16 -> 64.12 : -0.04 = -0.062% (+/-0.00%) core_yield_from.py 362.58 -> 354.50 : -8.08 = -2.228% (+/-0.00%) misc_aes.py 429.69 -> 405.59 : -24.10 = -5.609% (+/-0.01%) misc_mandel.py 3485.13 -> 3416.51 : -68.62 = -1.969% (+/-0.00%) misc_pystone.py 2496.53 -> 2405.56 : -90.97 = -3.644% (+/-0.01%) misc_raytrace.py 381.47 -> 374.01 : -7.46 = -1.956% (+/-0.01%) viper_call0.py 576.73 -> 572.49 : -4.24 = -0.735% (+/-0.04%) viper_call1a.py 550.37 -> 546.21 : -4.16 = -0.756% (+/-0.09%) viper_call1b.py 438.23 -> 435.68 : -2.55 = -0.582% (+/-0.06%) viper_call1c.py 442.84 -> 440.04 : -2.80 = -0.632% (+/-0.08%) viper_call2a.py 536.31 -> 532.35 : -3.96 = -0.738% (+/-0.06%) viper_call2b.py 382.34 -> 377.07 : -5.27 = -1.378% (+/-0.03%) And for unix on x64: diff of scores (higher is better) N=2000 M=2000 baseline -> this-commit diff diff% (error%) bm_chaos.py 13594.20 -> 13073.84 : -520.36 = -3.828% (+/-5.44%) bm_fannkuch.py 60.63 -> 59.58 : -1.05 = -1.732% (+/-3.01%) bm_fft.py 112009.15 -> 111603.32 : -405.83 = -0.362% (+/-4.03%) bm_float.py 246202.55 -> 247923.81 : +1721.26 = +0.699% (+/-2.79%) bm_hexiom.py 615.65 -> 617.21 : +1.56 = +0.253% (+/-1.64%) bm_nqueens.py 215807.95 -> 215600.96 : -206.99 = -0.096% (+/-3.52%) bm_pidigits.py 8246.74 -> 8422.82 : +176.08 = +2.135% (+/-3.64%) misc_aes.py 16133.00 -> 16452.74 : +319.74 = +1.982% (+/-1.50%) misc_mandel.py 128146.69 -> 130796.43 : +2649.74 = +2.068% (+/-3.18%) misc_pystone.py 83811.49 -> 83124.85 : -686.64 = -0.819% (+/-1.03%) misc_raytrace.py 21688.02 -> 21385.10 : -302.92 = -1.397% (+/-3.20%) The code size change is (firmware with a lot of frozen code benefits the most): bare-arm: +396 +0.697% minimal x86: +1595 +0.979% [incl +32(data)] unix x64: +2408 +0.470% [incl +800(data)] unix nanbox: +1396 +0.309% [incl -96(data)] stm32: -1256 -0.318% PYBV10 cc3200: +288 +0.157% esp8266: -260 -0.037% GENERIC esp32: -216 -0.014% GENERIC[incl -1072(data)] nrf: +116 +0.067% pca10040 rp2: -664 -0.135% PICO samd: +844 +0.607% ADAFRUIT_ITSYBITSY_M4_EXPRESS As part of this change the .mpy file format version is bumped to version 6. And mpy-tool.py has been improved to provide a good visualisation of the contents of .mpy files. In summary: this commit changes the bytecode to use qstr indirection, and reworks the .mpy file format to be simpler and allow .mpy files to be executed in-place. Performance is not impacted too much. Eventually it will be possible to store such .mpy files in a linear, read-only, memory- mappable filesystem so they can be executed from flash/ROM. This will essentially be able to replace frozen code for most applications. Signed-off-by: Damien George --- mpy-cross/main.c | 5 +- ports/unix/coverage.c | 4 + py/asmbase.c | 3 +- py/asmbase.h | 2 +- py/bc.c | 69 +- py/bc.h | 84 +- py/builtinevex.c | 2 +- py/builtinimport.c | 28 +- py/compile.c | 171 ++- py/compile.h | 3 +- py/dynruntime.h | 10 +- py/emit.h | 50 +- py/emitbc.c | 156 +-- py/emitcommon.c | 11 + py/emitglue.c | 61 +- py/emitglue.h | 26 +- py/emitnative.c | 263 ++-- py/emitnx86.c | 2 +- py/frozenmod.c | 2 +- py/mpconfig.h | 8 + py/nativeglue.c | 6 +- py/nativeglue.h | 12 +- py/obj.h | 4 - py/objfun.c | 50 +- py/objfun.h | 10 +- py/objgenerator.c | 18 +- py/objmodule.c | 9 +- py/persistentcode.c | 558 +++----- py/persistentcode.h | 12 +- py/profile.c | 47 +- py/profile.h | 4 +- py/qstr.h | 1 + py/scope.c | 3 +- py/scope.h | 3 +- py/showbc.c | 73 +- py/vm.c | 73 +- shared/runtime/pyexec.c | 6 +- tests/cmdline/cmd_parsetree.py.exp | 74 +- tests/cmdline/cmd_showbc.py.exp | 762 ++++++----- tests/cmdline/cmd_verbose.py.exp | 18 +- tests/micropython/import_mpy_invalid.py | 1 - tests/micropython/import_mpy_invalid.py.exp | 1 - tests/micropython/import_mpy_native_gc.py | 8 +- tests/micropython/import_mpy_native_x64.py | 63 +- tests/perf_bench/core_import_mpy_multi.py | 2 +- tests/perf_bench/core_import_mpy_single.py | 2 +- tools/mpy-tool.py | 1326 +++++++++++++------ tools/mpy_ld.py | 35 +- 48 files changed, 2388 insertions(+), 1753 deletions(-) diff --git a/mpy-cross/main.c b/mpy-cross/main.c index 7218921b31..a2daf7b22f 100644 --- a/mpy-cross/main.c +++ b/mpy-cross/main.c @@ -72,7 +72,8 @@ STATIC int compile_and_save(const char *file, const char *output_file, const cha #endif mp_parse_tree_t parse_tree = mp_parse(lex, MP_PARSE_FILE_INPUT); - mp_raw_code_t *rc = mp_compile_to_raw_code(&parse_tree, source_name, false); + mp_module_context_t *ctx = m_new_obj(mp_module_context_t); + mp_compiled_module_t cm = mp_compile_to_raw_code(&parse_tree, source_name, false, ctx); vstr_t vstr; vstr_init(&vstr, 16); @@ -83,7 +84,7 @@ STATIC int compile_and_save(const char *file, const char *output_file, const cha } else { vstr_add_str(&vstr, output_file); } - mp_raw_code_save_file(rc, vstr_null_terminated_str(&vstr)); + mp_raw_code_save_file(&cm, vstr_null_terminated_str(&vstr)); vstr_clear(&vstr); nlr_pop(); diff --git a/ports/unix/coverage.c b/ports/unix/coverage.c index 6b00cdfef2..a855237200 100644 --- a/ports/unix/coverage.c +++ b/ports/unix/coverage.c @@ -2,6 +2,7 @@ #include #include "py/obj.h" +#include "py/objfun.h" #include "py/objstr.h" #include "py/runtime.h" #include "py/gc.h" @@ -449,7 +450,10 @@ STATIC mp_obj_t extra_coverage(void) { mp_printf(&mp_plat_print, "# VM\n"); // call mp_execute_bytecode with invalide bytecode (should raise NotImplementedError) + mp_module_context_t context; mp_obj_fun_bc_t fun_bc; + fun_bc.context = &context; + fun_bc.child_table = NULL; fun_bc.bytecode = (const byte *)"\x01"; // just needed for n_state mp_code_state_t *code_state = m_new_obj_var(mp_code_state_t, mp_obj_t, 1); code_state->fun_bc = &fun_bc; diff --git a/py/asmbase.c b/py/asmbase.c index 344e03e7a7..4a3fd089cb 100644 --- a/py/asmbase.c +++ b/py/asmbase.c @@ -61,7 +61,8 @@ void mp_asm_base_start_pass(mp_asm_base_t *as, int pass) { // all functions must go through this one to emit bytes // if as->pass < MP_ASM_PASS_EMIT, then this function just counts the number // of bytes needed and returns NULL, and callers should not store any data -uint8_t *mp_asm_base_get_cur_to_write_bytes(mp_asm_base_t *as, size_t num_bytes_to_write) { +uint8_t *mp_asm_base_get_cur_to_write_bytes(void *as_in, size_t num_bytes_to_write) { + mp_asm_base_t *as = as_in; uint8_t *c = NULL; if (as->pass == MP_ASM_PASS_EMIT) { assert(as->code_offset + num_bytes_to_write <= as->code_size); diff --git a/py/asmbase.h b/py/asmbase.h index 24c3af8679..960be7685f 100644 --- a/py/asmbase.h +++ b/py/asmbase.h @@ -45,7 +45,7 @@ typedef struct _mp_asm_base_t { void mp_asm_base_init(mp_asm_base_t *as, size_t max_num_labels); void mp_asm_base_deinit(mp_asm_base_t *as, bool free_code); void mp_asm_base_start_pass(mp_asm_base_t *as, int pass); -uint8_t *mp_asm_base_get_cur_to_write_bytes(mp_asm_base_t *as, size_t num_bytes_to_write); +uint8_t *mp_asm_base_get_cur_to_write_bytes(void *as, size_t num_bytes_to_write); void mp_asm_base_label_assign(mp_asm_base_t *as, size_t label); void mp_asm_base_align(mp_asm_base_t *as, unsigned int align); void mp_asm_base_data(mp_asm_base_t *as, unsigned int bytesize, uintptr_t val); diff --git a/py/bc.c b/py/bc.c index 69b6739e8f..b98df39e28 100644 --- a/py/bc.c +++ b/py/bc.c @@ -29,9 +29,9 @@ #include #include -#include "py/runtime.h" #include "py/bc0.h" #include "py/bc.h" +#include "py/objfun.h" #if MICROPY_DEBUG_VERBOSE // print debugging info #define DEBUG_PRINT (1) @@ -40,7 +40,23 @@ #define DEBUG_printf(...) (void)0 #endif -#if !MICROPY_PERSISTENT_CODE +void mp_encode_uint(void *env, mp_encode_uint_allocator_t allocator, mp_uint_t val) { + // We store each 7 bits in a separate byte, and that's how many bytes needed + byte buf[MP_ENCODE_UINT_MAX_BYTES]; + byte *p = buf + sizeof(buf); + // We encode in little-ending order, but store in big-endian, to help decoding + do { + *--p = val & 0x7f; + val >>= 7; + } while (val != 0); + byte *c = allocator(env, buf + sizeof(buf) - p); + if (c != NULL) { + while (p != buf + sizeof(buf) - 1) { + *c++ = *p++ | 0x80; + } + *c = *p; + } +} mp_uint_t mp_decode_uint(const byte **ptr) { mp_uint_t unum = 0; @@ -72,8 +88,6 @@ const byte *mp_decode_uint_skip(const byte *ptr) { return ptr; } -#endif - STATIC NORETURN void fun_pos_args_mismatch(mp_obj_fun_bc_t *f, size_t expected, size_t given) { #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE // generic message, used also for other argument issues @@ -107,8 +121,8 @@ STATIC void dump_args(const mp_obj_t *a, size_t sz) { // On entry code_state should be allocated somewhere (stack/heap) and // contain the following valid entries: // - code_state->fun_bc should contain a pointer to the function object -// - code_state->ip should contain the offset in bytes from the pointer -// code_state->fun_bc->bytecode to the entry n_state (0 for bytecode, non-zero for native) +// - code_state->ip should contain a pointer to the beginning of the prelude +// - code_state->n_state should be the number of objects in the local state void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw, const mp_obj_t *args) { // This function is pretty complicated. It's main aim is to be efficient in speed and RAM // usage for the common case of positional only args. @@ -116,9 +130,6 @@ void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw // get the function object that we want to set up (could be bytecode or native code) mp_obj_fun_bc_t *self = code_state->fun_bc; - // ip comes in as an offset into bytecode, so turn it into a true pointer - code_state->ip = self->bytecode + (size_t)code_state->ip; - #if MICROPY_STACKLESS code_state->prev = NULL; #endif @@ -134,6 +145,7 @@ void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw // Decode prelude size_t n_state_unused, n_exc_stack_unused, scope_flags, n_pos_args, n_kwonly_args, n_def_pos_args; MP_BC_PRELUDE_SIG_DECODE_INTO(code_state->ip, n_state_unused, n_exc_stack_unused, scope_flags, n_pos_args, n_kwonly_args, n_def_pos_args); + MP_BC_PRELUDE_SIZE_DECODE(code_state->ip); (void)n_state_unused; (void)n_exc_stack_unused; @@ -194,14 +206,20 @@ void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw *var_pos_kw_args = dict; } - // get pointer to arg_names array - const mp_obj_t *arg_names = (const mp_obj_t *)self->const_table; - for (size_t i = 0; i < n_kw; i++) { // the keys in kwargs are expected to be qstr objects mp_obj_t wanted_arg_name = kwargs[2 * i]; + + // get pointer to arg_names array + const uint8_t *arg_names = code_state->ip; + arg_names = mp_decode_uint_skip(arg_names); + for (size_t j = 0; j < n_pos_args + n_kwonly_args; j++) { - if (wanted_arg_name == arg_names[j]) { + qstr arg_qstr = mp_decode_uint(&arg_names); + #if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE + arg_qstr = self->context->constants.qstr_table[arg_qstr]; + #endif + if (wanted_arg_name == MP_OBJ_NEW_QSTR(arg_qstr)) { if (code_state->state[n_state - 1 - j] != MP_OBJ_NULL) { mp_raise_msg_varg(&mp_type_TypeError, MP_ERROR_TEXT("function got multiple values for argument '%q'"), MP_OBJ_QSTR_VALUE(wanted_arg_name)); @@ -248,17 +266,25 @@ void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw // Check that all mandatory keyword args are specified // Fill in default kw args if we have them + const uint8_t *arg_names = mp_decode_uint_skip(code_state->ip); + for (size_t i = 0; i < n_pos_args; i++) { + arg_names = mp_decode_uint_skip(arg_names); + } for (size_t i = 0; i < n_kwonly_args; i++) { + qstr arg_qstr = mp_decode_uint(&arg_names); + #if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE + arg_qstr = self->context->constants.qstr_table[arg_qstr]; + #endif if (code_state->state[n_state - 1 - n_pos_args - i] == MP_OBJ_NULL) { mp_map_elem_t *elem = NULL; if ((scope_flags & MP_SCOPE_FLAG_DEFKWARGS) != 0) { - elem = mp_map_lookup(&((mp_obj_dict_t *)MP_OBJ_TO_PTR(self->extra_args[n_def_pos_args]))->map, arg_names[n_pos_args + i], MP_MAP_LOOKUP); + elem = mp_map_lookup(&((mp_obj_dict_t *)MP_OBJ_TO_PTR(self->extra_args[n_def_pos_args]))->map, MP_OBJ_NEW_QSTR(arg_qstr), MP_MAP_LOOKUP); } if (elem != NULL) { code_state->state[n_state - 1 - n_pos_args - i] = elem->value; } else { mp_raise_msg_varg(&mp_type_TypeError, - MP_ERROR_TEXT("function missing required keyword argument '%q'"), MP_OBJ_QSTR_VALUE(arg_names[n_pos_args + i])); + MP_ERROR_TEXT("function missing required keyword argument '%q'"), arg_qstr); } } } @@ -273,12 +299,8 @@ void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw } } - // read the size part of the prelude - const byte *ip = code_state->ip; - MP_BC_PRELUDE_SIZE_DECODE(ip); - - // jump over code info (source file and line-number mapping) - ip += n_info; + // jump over code info (source file, argument names and line-number mapping) + const uint8_t *ip = code_state->ip + n_info; // bytecode prelude: initialise closed over variables for (; n_cell; --n_cell) { @@ -287,11 +309,6 @@ void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw mp_obj_new_cell(code_state->state[n_state - 1 - local_num]); } - #if !MICROPY_PERSISTENT_CODE - // so bytecode is aligned - ip = MP_ALIGN(ip, sizeof(mp_uint_t)); - #endif - // now that we skipped over the prelude, set the ip for the VM code_state->ip = ip; diff --git a/py/bc.h b/py/bc.h index ef5afeae16..7d761e30e6 100644 --- a/py/bc.h +++ b/py/bc.h @@ -28,7 +28,6 @@ #define MICROPY_INCLUDED_PY_BC_H #include "py/runtime.h" -#include "py/objfun.h" // bytecode layout: // @@ -50,7 +49,9 @@ // // source info section: // simple_name : var qstr -// source_file : var qstr +// argname0 : var qstr +// ... : var qstr +// argnameN : var qstr N = num_pos_args + num_kwonly_args - 1 // // // closure section: @@ -58,19 +59,16 @@ // ... : byte // local_numN : byte N = n_cells-1 // -// only needed if bytecode contains pointers -// // // // // constant table layout: // -// argname0 : obj (qstr) -// ... : obj (qstr) -// argnameN : obj (qstr) N = num_pos_args + num_kwonly_args // const0 : obj // constN : obj +#define MP_ENCODE_UINT_MAX_BYTES ((MP_BYTES_PER_OBJ_WORD * 8 + 6) / 7) + #define MP_BC_PRELUDE_SIG_ENCODE(S, E, scope, out_byte, out_env) \ do { \ /*// Get values to store in prelude */ \ @@ -182,9 +180,9 @@ typedef struct _mp_bytecode_prelude_t { uint n_pos_args; uint n_kwonly_args; uint n_def_pos_args; - qstr qstr_block_name; - qstr qstr_source_file; + qstr qstr_block_name_idx; const byte *line_info; + const byte *line_info_top; const byte *opcodes; } mp_bytecode_prelude_t; @@ -198,12 +196,46 @@ typedef struct _mp_exc_stack_t { mp_obj_base_t *prev_exc; } mp_exc_stack_t; +// Constants associated with a module, to interface bytecode with runtime. +typedef struct _mp_module_constants_t { + #if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE + qstr_short_t *qstr_table; + #else + qstr source_file; + #endif + mp_obj_t *obj_table; +} mp_module_constants_t; + +// State associated with a module. +typedef struct _mp_module_context_t { + mp_obj_module_t module; + mp_module_constants_t constants; +} mp_module_context_t; + +// Outer level struct defining a compiled module. +typedef struct _mp_compiled_module_t { + const mp_module_context_t *context; + const struct _mp_raw_code_t *rc; + #if MICROPY_PERSISTENT_CODE_SAVE + bool has_native; + size_t n_qstr; + size_t n_obj; + #endif +} mp_compiled_module_t; + +// Outer level struct defining a frozen module. +typedef struct _mp_frozen_module_t { + const mp_module_constants_t constants; + const struct _mp_raw_code_t *rc; +} mp_frozen_module_t; + +// State for an executing function. typedef struct _mp_code_state_t { // The fun_bc entry points to the underlying function object that is being executed. // It is needed to access the start of bytecode and the const_table. // It is also needed to prevent the GC from reclaiming the bytecode during execution, // because the ip pointer below will always point to the interior of the bytecode. - mp_obj_fun_bc_t *fun_bc; + struct _mp_obj_fun_bc_t *fun_bc; const byte *ip; mp_obj_t *sp; uint16_t n_state; @@ -222,6 +254,10 @@ typedef struct _mp_code_state_t { // mp_exc_stack_t exc_state[0]; } mp_code_state_t; +// Allocator may return NULL, in which case data is not stored (can be used to compute size). +typedef uint8_t *(*mp_encode_uint_allocator_t)(void *env, size_t nbytes); + +void mp_encode_uint(void *env, mp_encode_uint_allocator_t allocator, mp_uint_t val); mp_uint_t mp_decode_uint(const byte **ptr); mp_uint_t mp_decode_uint_value(const byte *ptr); const byte *mp_decode_uint_skip(const byte *ptr); @@ -229,10 +265,10 @@ const byte *mp_decode_uint_skip(const byte *ptr); mp_vm_return_kind_t mp_execute_bytecode(mp_code_state_t *code_state, volatile mp_obj_t inject_exc); mp_code_state_t *mp_obj_fun_bc_prepare_codestate(mp_obj_t func, size_t n_args, size_t n_kw, const mp_obj_t *args); void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw, const mp_obj_t *args); -void mp_bytecode_print(const mp_print_t *print, const void *descr, const byte *code, mp_uint_t len, const mp_uint_t *const_table); -void mp_bytecode_print2(const mp_print_t *print, const byte *code, size_t len, const mp_uint_t *const_table); +void mp_bytecode_print(const mp_print_t *print, const void *descr, const byte *code, mp_uint_t len, const mp_module_constants_t *cm); +void mp_bytecode_print2(const mp_print_t *print, const byte *code, size_t len, const mp_module_constants_t *cm); const byte *mp_bytecode_print_str(const mp_print_t *print, const byte *ip); -#define mp_bytecode_print_inst(print, code, const_table) mp_bytecode_print2(print, code, 1, const_table) +#define mp_bytecode_print_inst(print, code, x_table) mp_bytecode_print2(print, code, 1, x_table) // Helper macros to access pointer with least significant bits holding flags #define MP_TAGPTR_PTR(x) ((void *)((uintptr_t)(x) & ~((uintptr_t)3))) @@ -246,10 +282,26 @@ uint mp_opcode_format(const byte *ip, size_t *opcode_size, bool count_var_uint); #endif -static inline size_t mp_bytecode_get_source_line(const byte *line_info, size_t bc_offset) { +static inline void mp_module_context_alloc_tables(mp_module_context_t *context, size_t n_qstr, size_t n_obj) { + #if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE + size_t nq = (n_qstr * sizeof(qstr_short_t) + sizeof(mp_uint_t) - 1) / sizeof(mp_uint_t); + size_t no = n_obj; + mp_uint_t *mem = m_new(mp_uint_t, nq + no); + context->constants.qstr_table = (void *)(mem); + context->constants.obj_table = (void *)(mem + nq); + #else + if (n_obj == 0) { + context->constants.obj_table = NULL; + } else { + context->constants.obj_table = m_new(mp_obj_t, n_obj); + } + #endif +} + +static inline size_t mp_bytecode_get_source_line(const byte *line_info, const byte *line_info_top, size_t bc_offset) { size_t source_line = 1; - size_t c; - while ((c = *line_info)) { + while (line_info < line_info_top) { + size_t c = *line_info; size_t b, l; if ((c & 0x80) == 0) { // 0b0LLBBBBB encoding diff --git a/py/builtinevex.c b/py/builtinevex.c index 800a20223a..4e1c6a66d1 100644 --- a/py/builtinevex.c +++ b/py/builtinevex.c @@ -54,7 +54,7 @@ STATIC mp_obj_t code_execute(mp_obj_code_t *self, mp_obj_dict_t *globals, mp_obj // the correct one if (mp_obj_is_type(self->module_fun, &mp_type_fun_bc)) { mp_obj_fun_bc_t *fun_bc = MP_OBJ_TO_PTR(self->module_fun); - fun_bc->globals = globals; + ((mp_module_context_t *)fun_bc->context)->module.globals = globals; } // execute code diff --git a/py/builtinimport.c b/py/builtinimport.c index 3e336633d9..094959f97d 100644 --- a/py/builtinimport.c +++ b/py/builtinimport.c @@ -146,28 +146,28 @@ STATIC mp_import_stat_t stat_top_level_dir_or_file(qstr mod_name, vstr_t *dest) } #if MICROPY_MODULE_FROZEN_STR || MICROPY_ENABLE_COMPILER -STATIC void do_load_from_lexer(mp_obj_t module_obj, mp_lexer_t *lex) { +STATIC void do_load_from_lexer(mp_module_context_t *context, mp_lexer_t *lex) { #if MICROPY_PY___FILE__ qstr source_name = lex->source_name; - mp_store_attr(module_obj, MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name)); + mp_store_attr(MP_OBJ_FROM_PTR(&context->module), MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name)); #endif // parse, compile and execute the module in its context - mp_obj_dict_t *mod_globals = mp_obj_module_get_globals(module_obj); + mp_obj_dict_t *mod_globals = context->module.globals; mp_parse_compile_execute(lex, MP_PARSE_FILE_INPUT, mod_globals, mod_globals); } #endif #if (MICROPY_HAS_FILE_READER && MICROPY_PERSISTENT_CODE_LOAD) || MICROPY_MODULE_FROZEN_MPY -STATIC void do_execute_raw_code(mp_obj_t module_obj, mp_raw_code_t *raw_code, const char *source_name) { +STATIC void do_execute_raw_code(mp_module_context_t *context, const mp_raw_code_t *rc, const mp_module_context_t *mc, const char *source_name) { (void)source_name; #if MICROPY_PY___FILE__ - mp_store_attr(module_obj, MP_QSTR___file__, MP_OBJ_NEW_QSTR(qstr_from_str(source_name))); + mp_store_attr(MP_OBJ_FROM_PTR(&context->module), MP_QSTR___file__, MP_OBJ_NEW_QSTR(qstr_from_str(source_name))); #endif // execute the module in its context - mp_obj_dict_t *mod_globals = mp_obj_module_get_globals(module_obj); + mp_obj_dict_t *mod_globals = context->module.globals; // save context mp_obj_dict_t *volatile old_globals = mp_globals_get(); @@ -179,7 +179,7 @@ STATIC void do_execute_raw_code(mp_obj_t module_obj, mp_raw_code_t *raw_code, co nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { - mp_obj_t module_fun = mp_make_function_from_raw_code(raw_code, MP_OBJ_NULL, MP_OBJ_NULL); + mp_obj_t module_fun = mp_make_function_from_raw_code(rc, mc, NULL); mp_call_function_0(module_fun); // finish nlr block, restore context @@ -195,7 +195,7 @@ STATIC void do_execute_raw_code(mp_obj_t module_obj, mp_raw_code_t *raw_code, co } #endif -STATIC void do_load(mp_obj_t module_obj, vstr_t *file) { +STATIC void do_load(mp_module_context_t *module_obj, vstr_t *file) { #if MICROPY_MODULE_FROZEN || MICROPY_ENABLE_COMPILER || (MICROPY_PERSISTENT_CODE_LOAD && MICROPY_HAS_FILE_READER) const char *file_str = vstr_null_terminated_str(file); #endif @@ -222,7 +222,9 @@ STATIC void do_load(mp_obj_t module_obj, vstr_t *file) { // its data) in the list of frozen files, execute it. #if MICROPY_MODULE_FROZEN_MPY if (frozen_type == MP_FROZEN_MPY) { - do_execute_raw_code(module_obj, modref, file_str + frozen_path_prefix_len); + const mp_frozen_module_t *frozen = modref; + module_obj->constants = frozen->constants; + do_execute_raw_code(module_obj, frozen->rc, module_obj, file_str + frozen_path_prefix_len); return; } #endif @@ -234,8 +236,8 @@ STATIC void do_load(mp_obj_t module_obj, vstr_t *file) { // the correct format and, if so, load and execute the file. #if MICROPY_HAS_FILE_READER && MICROPY_PERSISTENT_CODE_LOAD if (file_str[file->len - 3] == 'm') { - mp_raw_code_t *raw_code = mp_raw_code_load_file(file_str); - do_execute_raw_code(module_obj, raw_code, file_str); + mp_compiled_module_t cm = mp_raw_code_load_file(file_str, module_obj); + do_execute_raw_code(module_obj, cm.rc, cm.context, file_str); return; } #endif @@ -434,7 +436,7 @@ STATIC mp_obj_t process_import_at_level(qstr full_mod_name, qstr level_mod_name, size_t orig_path_len = path->len; vstr_add_str(path, PATH_SEP_CHAR "__init__.py"); if (stat_file_py_or_mpy(path) == MP_IMPORT_STAT_FILE) { - do_load(module_obj, path); + do_load(MP_OBJ_TO_PTR(module_obj), path); } else { // No-op. Nothing to load. // mp_warning("%s is imported as namespace package", vstr_str(&path)); @@ -443,7 +445,7 @@ STATIC mp_obj_t process_import_at_level(qstr full_mod_name, qstr level_mod_name, path->len = orig_path_len; } else { // MP_IMPORT_STAT_FILE // File -- execute "path.(m)py". - do_load(module_obj, path); + do_load(MP_OBJ_TO_PTR(module_obj), path); // Note: This should be the last component in the import path. If // there are remaining components then it's an ImportError // because the current path(the module that was just loaded) is diff --git a/py/compile.c b/py/compile.c index 8ebcc2289f..92736e22e5 100644 --- a/py/compile.c +++ b/py/compile.c @@ -35,6 +35,7 @@ #include "py/compile.h" #include "py/runtime.h" #include "py/asmbase.h" +#include "py/nativeglue.h" #include "py/persistentcode.h" #if MICROPY_ENABLE_COMPILER @@ -88,7 +89,7 @@ typedef enum { #if MICROPY_EMIT_NATIVE && MICROPY_DYNAMIC_COMPILER #define NATIVE_EMITTER(f) emit_native_table[mp_dynamic_compiler.native_arch]->emit_##f -#define NATIVE_EMITTER_TABLE emit_native_table[mp_dynamic_compiler.native_arch] +#define NATIVE_EMITTER_TABLE (emit_native_table[mp_dynamic_compiler.native_arch]) STATIC const emit_method_table_t *emit_native_table[] = { NULL, @@ -121,7 +122,7 @@ STATIC const emit_method_table_t *emit_native_table[] = { #else #error "unknown native emitter" #endif -#define NATIVE_EMITTER_TABLE &NATIVE_EMITTER(method_table) +#define NATIVE_EMITTER_TABLE (&NATIVE_EMITTER(method_table)) #endif #if MICROPY_EMIT_INLINE_ASM && MICROPY_DYNAMIC_COMPILER @@ -162,8 +163,6 @@ STATIC const emit_inline_asm_method_table_t *emit_asm_table[] = { // elements in this struct are ordered to make it compact typedef struct _compiler_t { - qstr source_file; - uint8_t is_repl; uint8_t pass; // holds enum type pass_kind_t uint8_t have_star; @@ -185,7 +184,10 @@ typedef struct _compiler_t { scope_t *scope_head; scope_t *scope_cur; + mp_emit_common_t emit_common; + emit_t *emit; // current emitter + emit_t *emit_bc; #if NEED_METHOD_TABLE const emit_method_table_t *emit_method_table; // current emit method table #endif @@ -196,6 +198,72 @@ typedef struct _compiler_t { #endif } compiler_t; +/******************************************************************************/ +// mp_emit_common_t helper functions +// These are defined here so they can be inlined, to reduce code size. + +STATIC void mp_emit_common_init(mp_emit_common_t *emit, qstr source_file) { + #if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE + mp_map_init(&emit->qstr_map, 1); + + // add the source file as the first entry in the qstr table + mp_map_elem_t *elem = mp_map_lookup(&emit->qstr_map, MP_OBJ_NEW_QSTR(source_file), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); + elem->value = MP_OBJ_NEW_SMALL_INT(0); + #endif +} + +STATIC void mp_emit_common_start_pass(mp_emit_common_t *emit, pass_kind_t pass) { + emit->pass = pass; + if (pass == MP_PASS_STACK_SIZE) { + emit->ct_cur_obj_base = emit->ct_cur_obj; + } else if (pass > MP_PASS_STACK_SIZE) { + emit->ct_cur_obj = emit->ct_cur_obj_base; + } + if (pass == MP_PASS_EMIT) { + if (emit->ct_cur_child == 0) { + emit->children = NULL; + } else { + emit->children = m_new0(mp_raw_code_t *, emit->ct_cur_child); + } + } + emit->ct_cur_child = 0; +} + +STATIC void mp_emit_common_finalise(mp_emit_common_t *emit, bool has_native_code) { + emit->ct_cur_obj += has_native_code; // allocate an additional slot for &mp_fun_table + emit->const_table = m_new0(mp_uint_t, emit->ct_cur_obj); + emit->ct_cur_obj = has_native_code; // reserve slot 0 for &mp_fun_table + #if MICROPY_EMIT_NATIVE + if (has_native_code) { + // store mp_fun_table pointer at the start of the constant table + emit->const_table[0] = (mp_uint_t)(uintptr_t)&mp_fun_table; + } + #endif +} + +STATIC void mp_emit_common_populate_module_context(mp_emit_common_t *emit, qstr source_file, mp_module_context_t *context) { + #if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE + size_t qstr_map_used = emit->qstr_map.used; + mp_module_context_alloc_tables(context, qstr_map_used, emit->ct_cur_obj); + for (size_t i = 0; i < emit->qstr_map.alloc; ++i) { + if (mp_map_slot_is_filled(&emit->qstr_map, i)) { + size_t idx = MP_OBJ_SMALL_INT_VALUE(emit->qstr_map.table[i].value); + qstr qst = MP_OBJ_QSTR_VALUE(emit->qstr_map.table[i].key); + context->constants.qstr_table[idx] = qst; + } + } + #else + mp_module_context_alloc_tables(context, 0, emit->ct_cur_obj); + context->constants.source_file = source_file; + #endif + + if (emit->ct_cur_obj > 0) { + memcpy(context->constants.obj_table, emit->const_table, emit->ct_cur_obj * sizeof(mp_uint_t)); + } +} + +/******************************************************************************/ + STATIC void compile_error_set_line(compiler_t *comp, mp_parse_node_t pn) { // if the line of the error is unknown then try to update it from the pn if (comp->compile_error_line == 0 && MP_PARSE_NODE_IS_STRUCT(pn)) { @@ -246,7 +314,7 @@ STATIC void compile_decrease_except_level(compiler_t *comp) { } STATIC scope_t *scope_new_and_link(compiler_t *comp, scope_kind_t kind, mp_parse_node_t pn, uint emit_options) { - scope_t *scope = scope_new(kind, pn, comp->source_file, emit_options); + scope_t *scope = scope_new(kind, pn, emit_options); scope->parent = comp->scope_cur; scope->next = NULL; if (comp->scope_head == NULL) { @@ -290,7 +358,8 @@ STATIC void compile_generic_all_nodes(compiler_t *comp, mp_parse_node_struct_t * STATIC void compile_load_id(compiler_t *comp, qstr qst) { if (comp->pass == MP_PASS_SCOPE) { mp_emit_common_get_id_for_load(comp->scope_cur, qst); - } else { + } + { #if NEED_METHOD_TABLE mp_emit_common_id_op(comp->emit, &comp->emit_method_table->load_id, comp->scope_cur, qst); #else @@ -302,7 +371,8 @@ STATIC void compile_load_id(compiler_t *comp, qstr qst) { STATIC void compile_store_id(compiler_t *comp, qstr qst) { if (comp->pass == MP_PASS_SCOPE) { mp_emit_common_get_id_for_modification(comp->scope_cur, qst); - } else { + } + { #if NEED_METHOD_TABLE mp_emit_common_id_op(comp->emit, &comp->emit_method_table->store_id, comp->scope_cur, qst); #else @@ -314,7 +384,8 @@ STATIC void compile_store_id(compiler_t *comp, qstr qst) { STATIC void compile_delete_id(compiler_t *comp, qstr qst) { if (comp->pass == MP_PASS_SCOPE) { mp_emit_common_get_id_for_modification(comp->scope_cur, qst); - } else { + } + { #if NEED_METHOD_TABLE mp_emit_common_id_op(comp->emit, &comp->emit_method_table->delete_id, comp->scope_cur, qst); #else @@ -819,7 +890,7 @@ STATIC bool compile_built_in_decorator(compiler_t *comp, size_t name_len, mp_par compile_syntax_error(comp, name_nodes[1], MP_ERROR_TEXT("invalid micropython decorator")); } - #if MICROPY_DYNAMIC_COMPILER + #if MICROPY_EMIT_NATIVE && MICROPY_DYNAMIC_COMPILER if (*emit_options == MP_EMIT_OPT_NATIVE_PYTHON || *emit_options == MP_EMIT_OPT_VIPER) { if (emit_native_table[mp_dynamic_compiler.native_arch] == NULL) { compile_syntax_error(comp, name_nodes[1], MP_ERROR_TEXT("invalid arch")); @@ -2082,6 +2153,7 @@ STATIC void compile_lambdef(compiler_t *comp, mp_parse_node_struct_t *pns) { STATIC void compile_namedexpr_helper(compiler_t *comp, mp_parse_node_t pn_name, mp_parse_node_t pn_expr) { if (!MP_PARSE_NODE_IS_ID(pn_name)) { compile_syntax_error(comp, (mp_parse_node_t)pn_name, MP_ERROR_TEXT("can't assign to expression")); + return; // because pn_name is not a valid qstr (in compile_store_id below) } compile_node(comp, pn_expr); EMIT(dup_top); @@ -2814,6 +2886,7 @@ STATIC void compile_scope_func_lambda_param(compiler_t *comp, mp_parse_node_t pn // comes before a star, so counts as a positional parameter comp->scope_cur->num_pos_args += 1; } + mp_emit_common_use_qstr(&comp->emit_common, param_name); } else { assert(MP_PARSE_NODE_IS_STRUCT(pn)); pns = (mp_parse_node_struct_t *)pn; @@ -2827,6 +2900,7 @@ STATIC void compile_scope_func_lambda_param(compiler_t *comp, mp_parse_node_t pn // comes before a star, so counts as a positional parameter comp->scope_cur->num_pos_args += 1; } + mp_emit_common_use_qstr(&comp->emit_common, param_name); } else if (MP_PARSE_NODE_STRUCT_KIND(pns) == pn_star) { if (comp->have_star) { // more than one star @@ -2976,6 +3050,7 @@ STATIC void compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) { comp->pass = pass; comp->scope_cur = scope; comp->next_label = 0; + mp_emit_common_start_pass(&comp->emit_common, pass); EMIT_ARG(start_pass, pass, scope); reserve_labels_for_native(comp, 6); // used by native's start_pass @@ -2984,6 +3059,14 @@ STATIC void compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) { // they will be computed in this first pass scope->stack_size = 0; scope->exc_stack_size = 0; + + #if MICROPY_EMIT_NATIVE + if (scope->emit_options == MP_EMIT_OPT_NATIVE_PYTHON || scope->emit_options == MP_EMIT_OPT_VIPER) { + // allow native code to perfom basic tasks during the pass scope + // note: the first argument passed here is mp_emit_common_t, not the native emitter context + NATIVE_EMITTER_TABLE->start_pass((void *)&comp->emit_common, comp->pass, scope); + } + #endif } // compile @@ -3064,6 +3147,7 @@ STATIC void compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) { if (comp->pass == MP_PASS_SCOPE) { scope_find_or_add_id(comp->scope_cur, qstr_arg, ID_INFO_KIND_LOCAL); scope->num_pos_args = 1; + mp_emit_common_use_qstr(&comp->emit_common, MP_QSTR__star_); } // Set the source line number for the start of the comprehension @@ -3296,9 +3380,10 @@ STATIC void compile_scope_inline_asm(compiler_t *comp, scope_t *scope, pass_kind f, mp_asm_base_get_code_size((mp_asm_base_t *)comp->emit_inline_asm), NULL, #if MICROPY_PERSISTENT_CODE_SAVE - 0, 0, 0, 0, NULL, + 0, + 0, 0, NULL, #endif - comp->scope_cur->num_pos_args, 0, type_sig); + 0, comp->scope_cur->num_pos_args, type_sig); } } @@ -3310,7 +3395,7 @@ STATIC void compile_scope_inline_asm(compiler_t *comp, scope_t *scope, pass_kind } #endif -STATIC void scope_compute_things(scope_t *scope) { +STATIC void scope_compute_things(scope_t *scope, mp_emit_common_t *emit_common) { // in MicroPython we put the *x parameter after all other parameters (except **y) if (scope->scope_flags & MP_SCOPE_FLAG_VARARGS) { id_info_t *id_param = NULL; @@ -3399,6 +3484,7 @@ STATIC void scope_compute_things(scope_t *scope) { } scope->num_pos_args += num_free; // free vars are counted as params for passing them into the function scope->num_locals += num_free; + mp_emit_common_use_qstr(emit_common, MP_QSTR__star_); } } } @@ -3406,15 +3492,15 @@ STATIC void scope_compute_things(scope_t *scope) { #if !MICROPY_PERSISTENT_CODE_SAVE STATIC #endif -mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl) { +mp_compiled_module_t mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl, mp_module_context_t *context) { // put compiler state on the stack, it's relatively small compiler_t comp_state = {0}; compiler_t *comp = &comp_state; - comp->source_file = source_file; comp->is_repl = is_repl; comp->break_label = INVALID_LABEL; comp->continue_label = INVALID_LABEL; + mp_emit_common_init(&comp->emit_common, source_file); // create the module scope #if MICROPY_EMIT_NATIVE @@ -3425,10 +3511,11 @@ mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_f scope_t *module_scope = scope_new_and_link(comp, SCOPE_MODULE, parse_tree->root, emit_opt); // create standard emitter; it's used at least for MP_PASS_SCOPE - emit_t *emit_bc = emit_bc_new(); + emit_t *emit_bc = emit_bc_new(&comp->emit_common); - // compile pass 1 + // compile MP_PASS_SCOPE comp->emit = emit_bc; + comp->emit_bc = emit_bc; #if MICROPY_EMIT_NATIVE comp->emit_method_table = &emit_bc_method_table; #endif @@ -3458,14 +3545,24 @@ mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_f } // compute some things related to scope and identifiers + bool has_native_code = false; for (scope_t *s = comp->scope_head; s != NULL && comp->compile_error == MP_OBJ_NULL; s = s->next) { - scope_compute_things(s); + #if MICROPY_EMIT_NATIVE + if (s->emit_options == MP_EMIT_OPT_NATIVE_PYTHON || s->emit_options == MP_EMIT_OPT_VIPER) { + has_native_code = true; + } + #endif + + scope_compute_things(s, &comp->emit_common); } // set max number of labels now that it's calculated emit_bc_set_max_num_labels(emit_bc, max_num_labels); - // compile pass 2 and 3 + // finalise and allocate the constant table + mp_emit_common_finalise(&comp->emit_common, has_native_code); + + // compile MP_PASS_STACK_SIZE, MP_PASS_CODE_SIZE, MP_PASS_EMIT #if MICROPY_EMIT_NATIVE emit_t *emit_native = NULL; #endif @@ -3505,7 +3602,7 @@ mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_f case MP_EMIT_OPT_NATIVE_PYTHON: case MP_EMIT_OPT_VIPER: if (emit_native == NULL) { - emit_native = NATIVE_EMITTER(new)(&comp->compile_error, &comp->next_label, max_num_labels); + emit_native = NATIVE_EMITTER(new)(&comp->emit_common, &comp->compile_error, &comp->next_label, max_num_labels); } comp->emit_method_table = NATIVE_EMITTER_TABLE; comp->emit = emit_native; @@ -3540,10 +3637,33 @@ mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_f // number for the start of this scope compile_error_set_line(comp, comp->scope_cur->pn); // add a traceback to the exception using relevant source info - mp_obj_exception_add_traceback(comp->compile_error, comp->source_file, + mp_obj_exception_add_traceback(comp->compile_error, source_file, comp->compile_error_line, comp->scope_cur->simple_name); } + // construct the global qstr/const table for this module + mp_compiled_module_t cm; + cm.rc = module_scope->raw_code; + cm.context = context; + #if MICROPY_PERSISTENT_CODE_SAVE + cm.has_native = has_native_code; + cm.n_qstr = comp->emit_common.qstr_map.used; + cm.n_obj = comp->emit_common.ct_cur_obj; + #endif + if (comp->compile_error == MP_OBJ_NULL) { + mp_emit_common_populate_module_context(&comp->emit_common, source_file, context); + + #if MICROPY_DEBUG_PRINTERS + // now that the module context is valid, the raw codes can be printed + if (mp_verbose_flag >= 2) { + for (scope_t *s = comp->scope_head; s != NULL; s = s->next) { + mp_raw_code_t *rc = s->raw_code; + mp_bytecode_print(&mp_plat_print, rc, rc->fun_data, rc->fun_data_len, &cm.context->constants); + } + } + #endif + } + // free the emitters emit_bc_free(emit_bc); @@ -3562,7 +3682,6 @@ mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_f mp_parse_tree_clear(parse_tree); // free the scopes - mp_raw_code_t *outer_raw_code = module_scope->raw_code; for (scope_t *s = module_scope; s;) { scope_t *next = s->next; scope_free(s); @@ -3571,15 +3690,17 @@ mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_f if (comp->compile_error != MP_OBJ_NULL) { nlr_raise(comp->compile_error); - } else { - return outer_raw_code; } + + return cm; } mp_obj_t mp_compile(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl) { - mp_raw_code_t *rc = mp_compile_to_raw_code(parse_tree, source_file, is_repl); + mp_module_context_t *context = m_new_obj(mp_module_context_t); + context->module.globals = mp_globals_get(); + mp_compiled_module_t cm = mp_compile_to_raw_code(parse_tree, source_file, is_repl, context); // return function that executes the outer module - return mp_make_function_from_raw_code(rc, MP_OBJ_NULL, MP_OBJ_NULL); + return mp_make_function_from_raw_code(cm.rc, cm.context, NULL); } #endif // MICROPY_ENABLE_COMPILER diff --git a/py/compile.h b/py/compile.h index 1ad1f5e9cd..ae87bf2a04 100644 --- a/py/compile.h +++ b/py/compile.h @@ -32,11 +32,12 @@ // the compiler will raise an exception if an error occurred // the compiler will clear the parse tree before it returns +// mp_globals_get() will be used for the context mp_obj_t mp_compile(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl); #if MICROPY_PERSISTENT_CODE_SAVE // this has the same semantics as mp_compile -mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl); +mp_compiled_module_t mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl, mp_module_context_t *globals); #endif // this is implemented in runtime.c diff --git a/py/dynruntime.h b/py/dynruntime.h index fdb91ed37e..b9dca60d13 100644 --- a/py/dynruntime.h +++ b/py/dynruntime.h @@ -30,6 +30,7 @@ // MicroPython runtime API defined in py/obj.h and py/runtime.h. #include "py/nativeglue.h" +#include "py/objfun.h" #include "py/objstr.h" #include "py/objtype.h" @@ -177,8 +178,8 @@ static inline mp_obj_t mp_obj_len_dyn(mp_obj_t o) { #define mp_unary_op(op, obj) (mp_fun_table.unary_op((op), (obj))) #define mp_binary_op(op, lhs, rhs) (mp_fun_table.binary_op((op), (lhs), (rhs))) -#define mp_make_function_from_raw_code(rc, def_args, def_kw_args) \ - (mp_fun_table.make_function_from_raw_code((rc), (def_args), (def_kw_args))) +#define mp_make_function_from_raw_code(rc, context, def_args) \ + (mp_fun_table.make_function_from_raw_code((rc), (context), (def_args))) #define mp_call_function_n_kw(fun, n_args, n_kw, args) \ (mp_fun_table.call_function_n_kw((fun), (n_args) | ((n_kw) << 8), args)) @@ -187,11 +188,10 @@ static inline mp_obj_t mp_obj_len_dyn(mp_obj_t o) { (mp_fun_table.arg_check_num_sig((n_args), (n_kw), MP_OBJ_FUN_MAKE_SIG((n_args_min), (n_args_max), (takes_kw)))) #define MP_DYNRUNTIME_INIT_ENTRY \ - mp_obj_t old_globals = mp_fun_table.swap_globals(self->globals); \ + mp_obj_t old_globals = mp_fun_table.swap_globals(self->context->module.globals); \ mp_raw_code_t rc; \ rc.kind = MP_CODE_NATIVE_VIPER; \ rc.scope_flags = 0; \ - rc.const_table = (void *)self->const_table; \ (void)rc; #define MP_DYNRUNTIME_INIT_EXIT \ @@ -199,7 +199,7 @@ static inline mp_obj_t mp_obj_len_dyn(mp_obj_t o) { return mp_const_none; #define MP_DYNRUNTIME_MAKE_FUNCTION(f) \ - (mp_make_function_from_raw_code((rc.fun_data = (f), &rc), MP_OBJ_NULL, MP_OBJ_NULL)) + (mp_make_function_from_raw_code((rc.fun_data = (f), &rc), self->context, NULL)) #define mp_import_name(name, fromlist, level) \ (mp_fun_table.import_name((name), (fromlist), (level))) diff --git a/py/emit.h b/py/emit.h index 13bd3e9b2e..6f3593a0e8 100644 --- a/py/emit.h +++ b/py/emit.h @@ -92,6 +92,18 @@ typedef enum { typedef struct _emit_t emit_t; +typedef struct _mp_emit_common_t { + pass_kind_t pass; + uint16_t ct_cur_obj_base; + uint16_t ct_cur_obj; + uint16_t ct_cur_child; + mp_uint_t *const_table; + mp_raw_code_t **children; + #if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE + mp_map_t qstr_map; + #endif +} mp_emit_common_t; + typedef struct _mp_emit_method_table_id_ops_t { void (*local)(emit_t *emit, qstr qst, mp_uint_t local_num, int kind); void (*global)(emit_t *emit, qstr qst, int kind); @@ -99,7 +111,7 @@ typedef struct _mp_emit_method_table_id_ops_t { typedef struct _emit_method_table_t { #if MICROPY_DYNAMIC_COMPILER - emit_t *(*emit_new)(mp_obj_t * error_slot, uint *label_slot, mp_uint_t max_num_labels); + emit_t *(*emit_new)(mp_emit_common_t * emit_common, mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); void (*emit_free)(emit_t *emit); #endif @@ -161,6 +173,28 @@ typedef struct _emit_method_table_t { void (*end_except_handler)(emit_t *emit); } emit_method_table_t; +#if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE +qstr_short_t mp_emit_common_use_qstr(mp_emit_common_t *emit, qstr qst); +#else +static inline qstr_short_t mp_emit_common_use_qstr(mp_emit_common_t *emit, qstr qst) { + return qst; +} +#endif + +static inline size_t mp_emit_common_alloc_const_obj(mp_emit_common_t *emit, mp_obj_t obj) { + if (emit->pass == MP_PASS_EMIT) { + emit->const_table[emit->ct_cur_obj] = (mp_uint_t)obj; + } + return emit->ct_cur_obj++; +} + +static inline size_t mp_emit_common_alloc_const_child(mp_emit_common_t *emit, mp_raw_code_t *rc) { + if (emit->pass == MP_PASS_EMIT) { + emit->children[emit->ct_cur_child] = rc; + } + return emit->ct_cur_child++; +} + static inline void mp_emit_common_get_id_for_load(scope_t *scope, qstr qst) { scope_find_or_add_id(scope, qst, ID_INFO_KIND_GLOBAL_IMPLICIT); } @@ -180,13 +214,13 @@ extern const mp_emit_method_table_id_ops_t mp_emit_bc_method_table_load_id_ops; extern const mp_emit_method_table_id_ops_t mp_emit_bc_method_table_store_id_ops; extern const mp_emit_method_table_id_ops_t mp_emit_bc_method_table_delete_id_ops; -emit_t *emit_bc_new(void); -emit_t *emit_native_x64_new(mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); -emit_t *emit_native_x86_new(mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); -emit_t *emit_native_thumb_new(mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); -emit_t *emit_native_arm_new(mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); -emit_t *emit_native_xtensa_new(mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); -emit_t *emit_native_xtensawin_new(mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); +emit_t *emit_bc_new(mp_emit_common_t *emit_common); +emit_t *emit_native_x64_new(mp_emit_common_t *emit_common, mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); +emit_t *emit_native_x86_new(mp_emit_common_t *emit_common, mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); +emit_t *emit_native_thumb_new(mp_emit_common_t *emit_common, mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); +emit_t *emit_native_arm_new(mp_emit_common_t *emit_common, mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); +emit_t *emit_native_xtensa_new(mp_emit_common_t *emit_common, mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); +emit_t *emit_native_xtensawin_new(mp_emit_common_t *emit_common, mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels); void emit_bc_set_max_num_labels(emit_t *emit, mp_uint_t max_num_labels); diff --git a/py/emitbc.c b/py/emitbc.c index ca74046033..c04701ca78 100644 --- a/py/emitbc.c +++ b/py/emitbc.c @@ -36,8 +36,7 @@ #if MICROPY_ENABLE_COMPILER -#define BYTES_FOR_INT ((MP_BYTES_PER_OBJ_WORD * 8 + 6) / 7) -#define DUMMY_DATA_SIZE (BYTES_FOR_INT) +#define DUMMY_DATA_SIZE (MP_ENCODE_UINT_MAX_BYTES) struct _emit_t { // Accessed as mp_obj_t, so must be aligned as such, and we rely on the @@ -50,6 +49,7 @@ struct _emit_t { int stack_size; + mp_emit_common_t *emit_common; scope_t *scope; mp_uint_t last_source_line_offset; @@ -66,17 +66,11 @@ struct _emit_t { size_t n_info; size_t n_cell; - - #if MICROPY_PERSISTENT_CODE - uint16_t ct_cur_obj; - uint16_t ct_num_obj; - uint16_t ct_cur_raw_code; - #endif - mp_uint_t *const_table; }; -emit_t *emit_bc_new(void) { +emit_t *emit_bc_new(mp_emit_common_t *emit_common) { emit_t *emit = m_new0(emit_t, 1); + emit->emit_common = emit_common; return emit; } @@ -90,26 +84,9 @@ void emit_bc_free(emit_t *emit) { m_del_obj(emit_t, emit); } -typedef byte *(*emit_allocator_t)(emit_t *emit, int nbytes); - -STATIC void emit_write_uint(emit_t *emit, emit_allocator_t allocator, mp_uint_t val) { - // We store each 7 bits in a separate byte, and that's how many bytes needed - byte buf[BYTES_FOR_INT]; - byte *p = buf + sizeof(buf); - // We encode in little-ending order, but store in big-endian, to help decoding - do { - *--p = val & 0x7f; - val >>= 7; - } while (val != 0); - byte *c = allocator(emit, buf + sizeof(buf) - p); - while (p != buf + sizeof(buf) - 1) { - *c++ = *p++ | 0x80; - } - *c = *p; -} - // all functions must go through this one to emit code info -STATIC byte *emit_get_cur_to_write_code_info(emit_t *emit, int num_bytes_to_write) { +STATIC uint8_t *emit_get_cur_to_write_code_info(void *emit_in, size_t num_bytes_to_write) { + emit_t *emit = emit_in; if (emit->pass < MP_PASS_EMIT) { emit->code_info_offset += num_bytes_to_write; return emit->dummy_data; @@ -126,14 +103,7 @@ STATIC void emit_write_code_info_byte(emit_t *emit, byte val) { } STATIC void emit_write_code_info_qstr(emit_t *emit, qstr qst) { - #if MICROPY_PERSISTENT_CODE - assert((qst >> 16) == 0); - byte *c = emit_get_cur_to_write_code_info(emit, 2); - c[0] = qst; - c[1] = qst >> 8; - #else - emit_write_uint(emit, emit_get_cur_to_write_code_info, qst); - #endif + mp_encode_uint(emit, emit_get_cur_to_write_code_info, mp_emit_common_use_qstr(emit->emit_common, qst)); } #if MICROPY_ENABLE_SOURCE_LINE @@ -166,7 +136,8 @@ STATIC void emit_write_code_info_bytes_lines(emit_t *emit, mp_uint_t bytes_to_sk #endif // all functions must go through this one to emit byte code -STATIC byte *emit_get_cur_to_write_bytecode(emit_t *emit, int num_bytes_to_write) { +STATIC uint8_t *emit_get_cur_to_write_bytecode(void *emit_in, size_t num_bytes_to_write) { + emit_t *emit = emit_in; if (emit->pass < MP_PASS_EMIT) { emit->bytecode_offset += num_bytes_to_write; return emit->dummy_data; @@ -189,12 +160,12 @@ STATIC void emit_write_bytecode_byte(emit_t *emit, int stack_adj, byte b1) { c[0] = b1; } -// Similar to emit_write_bytecode_uint(), just some extra handling to encode sign +// Similar to mp_encode_uint(), just some extra handling to encode sign STATIC void emit_write_bytecode_byte_int(emit_t *emit, int stack_adj, byte b1, mp_int_t num) { emit_write_bytecode_byte(emit, stack_adj, b1); // We store each 7 bits in a separate byte, and that's how many bytes needed - byte buf[BYTES_FOR_INT]; + byte buf[MP_ENCODE_UINT_MAX_BYTES]; byte *p = buf + sizeof(buf); // We encode in little-ending order, but store in big-endian, to help decoding do { @@ -218,61 +189,25 @@ STATIC void emit_write_bytecode_byte_int(emit_t *emit, int stack_adj, byte b1, m STATIC void emit_write_bytecode_byte_uint(emit_t *emit, int stack_adj, byte b, mp_uint_t val) { emit_write_bytecode_byte(emit, stack_adj, b); - emit_write_uint(emit, emit_get_cur_to_write_bytecode, val); + mp_encode_uint(emit, emit_get_cur_to_write_bytecode, val); } -#if MICROPY_PERSISTENT_CODE -STATIC void emit_write_bytecode_byte_const(emit_t *emit, int stack_adj, byte b, mp_uint_t n, mp_uint_t c) { - if (emit->pass == MP_PASS_EMIT) { - emit->const_table[n] = c; - } +STATIC void emit_write_bytecode_byte_const(emit_t *emit, int stack_adj, byte b, mp_uint_t n) { emit_write_bytecode_byte_uint(emit, stack_adj, b, n); } -#endif STATIC void emit_write_bytecode_byte_qstr(emit_t *emit, int stack_adj, byte b, qstr qst) { - #if MICROPY_PERSISTENT_CODE - assert((qst >> 16) == 0); - mp_emit_bc_adjust_stack_size(emit, stack_adj); - byte *c = emit_get_cur_to_write_bytecode(emit, 3); - c[0] = b; - c[1] = qst; - c[2] = qst >> 8; - #else - emit_write_bytecode_byte_uint(emit, stack_adj, b, qst); - #endif + emit_write_bytecode_byte_uint(emit, stack_adj, b, mp_emit_common_use_qstr(emit->emit_common, qst)); } STATIC void emit_write_bytecode_byte_obj(emit_t *emit, int stack_adj, byte b, mp_obj_t obj) { - #if MICROPY_PERSISTENT_CODE emit_write_bytecode_byte_const(emit, stack_adj, b, - emit->scope->num_pos_args + emit->scope->num_kwonly_args - + emit->ct_cur_obj++, (mp_uint_t)obj); - #else - // aligns the pointer so it is friendly to GC - emit_write_bytecode_byte(emit, stack_adj, b); - emit->bytecode_offset = (size_t)MP_ALIGN(emit->bytecode_offset, sizeof(mp_obj_t)); - mp_obj_t *c = (mp_obj_t *)emit_get_cur_to_write_bytecode(emit, sizeof(mp_obj_t)); - // Verify thar c is already uint-aligned - assert(c == MP_ALIGN(c, sizeof(mp_obj_t))); - *c = obj; - #endif + mp_emit_common_alloc_const_obj(emit->emit_common, obj)); } -STATIC void emit_write_bytecode_byte_raw_code(emit_t *emit, int stack_adj, byte b, mp_raw_code_t *rc) { - #if MICROPY_PERSISTENT_CODE +STATIC void emit_write_bytecode_byte_child(emit_t *emit, int stack_adj, byte b, mp_raw_code_t *rc) { emit_write_bytecode_byte_const(emit, stack_adj, b, - emit->scope->num_pos_args + emit->scope->num_kwonly_args - + emit->ct_num_obj + emit->ct_cur_raw_code++, (mp_uint_t)(uintptr_t)rc); - #else - // aligns the pointer so it is friendly to GC - emit_write_bytecode_byte(emit, stack_adj, b); - emit->bytecode_offset = (size_t)MP_ALIGN(emit->bytecode_offset, sizeof(void *)); - void **c = (void **)emit_get_cur_to_write_bytecode(emit, sizeof(void *)); - // Verify thar c is already uint-aligned - assert(c == MP_ALIGN(c, sizeof(void *))); - *c = rc; - #endif + mp_emit_common_alloc_const_child(emit->emit_common, rc)); #if MICROPY_PY_SYS_SETTRACE rc->line_of_definition = emit->last_source_line; #endif @@ -343,27 +278,19 @@ void mp_emit_bc_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope) { } // Write number of cells and size of the source code info - if (pass >= MP_PASS_CODE_SIZE) { - MP_BC_PRELUDE_SIZE_ENCODE(emit->n_info, emit->n_cell, emit_write_code_info_byte, emit); + if (emit->pass >= MP_PASS_CODE_SIZE) { + size_t n_info = emit->n_info; + size_t n_cell = emit->n_cell; + MP_BC_PRELUDE_SIZE_ENCODE(n_info, n_cell, emit_write_code_info_byte, emit); } emit->n_info = emit->code_info_offset; - // Write the name and source file of this function. + // Write the name of this function. emit_write_code_info_qstr(emit, scope->simple_name); - emit_write_code_info_qstr(emit, scope->source_file); - - #if MICROPY_PERSISTENT_CODE - emit->ct_cur_obj = 0; - emit->ct_cur_raw_code = 0; - #endif - - if (pass == MP_PASS_EMIT) { - // Write argument names (needed to resolve positional args passed as - // keywords). We store them as full word-sized objects for efficient access - // in mp_setup_code_state this is the start of the prelude and is guaranteed - // to be aligned on a word boundary. + // Write argument names, needed to resolve positional args passed as keywords. + { // For a given argument position (indexed by i) we need to find the // corresponding id_info which is a parameter, as it has the correct // qstr name to use as the argument name. Note that it's not a simple @@ -383,7 +310,7 @@ void mp_emit_bc_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope) { break; } } - emit->const_table[i] = (mp_uint_t)MP_OBJ_NEW_QSTR(qst); + emit_write_code_info_qstr(emit, qst); } } } @@ -396,8 +323,6 @@ void mp_emit_bc_end_pass(emit_t *emit) { // check stack is back to zero size assert(emit->stack_size == 0); - emit_write_code_info_byte(emit, 0); // end of line number info - // Calculate size of source code info section emit->n_info = emit->code_info_offset - emit->n_info; @@ -412,39 +337,20 @@ void mp_emit_bc_end_pass(emit_t *emit) { } } - #if MICROPY_PERSISTENT_CODE - assert(emit->pass <= MP_PASS_STACK_SIZE || (emit->ct_num_obj == emit->ct_cur_obj)); - emit->ct_num_obj = emit->ct_cur_obj; - #endif - if (emit->pass == MP_PASS_CODE_SIZE) { - #if !MICROPY_PERSISTENT_CODE - // so bytecode is aligned - emit->code_info_offset = (size_t)MP_ALIGN(emit->code_info_offset, sizeof(mp_uint_t)); - #endif - // calculate size of total code-info + bytecode, in bytes emit->code_info_size = emit->code_info_offset; emit->bytecode_size = emit->bytecode_offset; emit->code_base = m_new0(byte, emit->code_info_size + emit->bytecode_size); - #if MICROPY_PERSISTENT_CODE - emit->const_table = m_new0(mp_uint_t, - emit->scope->num_pos_args + emit->scope->num_kwonly_args - + emit->ct_cur_obj + emit->ct_cur_raw_code); - #else - emit->const_table = m_new0(mp_uint_t, - emit->scope->num_pos_args + emit->scope->num_kwonly_args); - #endif - } else if (emit->pass == MP_PASS_EMIT) { mp_emit_glue_assign_bytecode(emit->scope->raw_code, emit->code_base, #if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS emit->code_info_size + emit->bytecode_size, #endif - emit->const_table, + emit->emit_common->children, #if MICROPY_PERSISTENT_CODE_SAVE - emit->ct_cur_obj, emit->ct_cur_raw_code, + emit->emit_common->ct_cur_child, #endif emit->scope->scope_flags); } @@ -783,21 +689,21 @@ void mp_emit_bc_unpack_ex(emit_t *emit, mp_uint_t n_left, mp_uint_t n_right) { void mp_emit_bc_make_function(emit_t *emit, scope_t *scope, mp_uint_t n_pos_defaults, mp_uint_t n_kw_defaults) { if (n_pos_defaults == 0 && n_kw_defaults == 0) { - emit_write_bytecode_byte_raw_code(emit, 1, MP_BC_MAKE_FUNCTION, scope->raw_code); + emit_write_bytecode_byte_child(emit, 1, MP_BC_MAKE_FUNCTION, scope->raw_code); } else { - emit_write_bytecode_byte_raw_code(emit, -1, MP_BC_MAKE_FUNCTION_DEFARGS, scope->raw_code); + emit_write_bytecode_byte_child(emit, -1, MP_BC_MAKE_FUNCTION_DEFARGS, scope->raw_code); } } void mp_emit_bc_make_closure(emit_t *emit, scope_t *scope, mp_uint_t n_closed_over, mp_uint_t n_pos_defaults, mp_uint_t n_kw_defaults) { if (n_pos_defaults == 0 && n_kw_defaults == 0) { int stack_adj = -n_closed_over + 1; - emit_write_bytecode_byte_raw_code(emit, stack_adj, MP_BC_MAKE_CLOSURE, scope->raw_code); + emit_write_bytecode_byte_child(emit, stack_adj, MP_BC_MAKE_CLOSURE, scope->raw_code); emit_write_bytecode_raw_byte(emit, n_closed_over); } else { assert(n_closed_over <= 255); int stack_adj = -2 - (mp_int_t)n_closed_over + 1; - emit_write_bytecode_byte_raw_code(emit, stack_adj, MP_BC_MAKE_CLOSURE_DEFARGS, scope->raw_code); + emit_write_bytecode_byte_child(emit, stack_adj, MP_BC_MAKE_CLOSURE_DEFARGS, scope->raw_code); emit_write_bytecode_raw_byte(emit, n_closed_over); } } diff --git a/py/emitcommon.c b/py/emitcommon.c index 791bf398ab..c1780d2db9 100644 --- a/py/emitcommon.c +++ b/py/emitcommon.c @@ -30,6 +30,17 @@ #if MICROPY_ENABLE_COMPILER +#if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE +qstr_short_t mp_emit_common_use_qstr(mp_emit_common_t *emit, qstr qst) { + mp_map_elem_t *elem = mp_map_lookup(&emit->qstr_map, MP_OBJ_NEW_QSTR(qst), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); + if (elem->value == MP_OBJ_NULL) { + assert(emit->pass == MP_PASS_SCOPE); + elem->value = MP_OBJ_NEW_SMALL_INT(emit->qstr_map.used - 1); + } + return MP_OBJ_SMALL_INT_VALUE(elem->value); +} +#endif + void mp_emit_common_get_id_for_modification(scope_t *scope, qstr qst) { // name adding/lookup id_info_t *id = scope_find_or_add_id(scope, qst, ID_INFO_KIND_GLOBAL_IMPLICIT); diff --git a/py/emitglue.c b/py/emitglue.c index 09b48682ff..cd902838af 100644 --- a/py/emitglue.c +++ b/py/emitglue.c @@ -34,6 +34,7 @@ #include "py/emitglue.h" #include "py/runtime0.h" #include "py/bc.h" +#include "py/objfun.h" #include "py/profile.h" #if MICROPY_DEBUG_VERBOSE // print debugging info @@ -63,20 +64,22 @@ void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, const byte *code, #if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS size_t len, #endif - const mp_uint_t *const_table, + mp_raw_code_t **children, #if MICROPY_PERSISTENT_CODE_SAVE - uint16_t n_obj, uint16_t n_raw_code, + size_t n_children, #endif mp_uint_t scope_flags) { rc->kind = MP_CODE_BYTECODE; rc->scope_flags = scope_flags; rc->fun_data = code; - rc->const_table = const_table; - #if MICROPY_PERSISTENT_CODE_SAVE + #if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS rc->fun_data_len = len; - rc->n_obj = n_obj; - rc->n_raw_code = n_raw_code; + #endif + rc->children = children; + + #if MICROPY_PERSISTENT_CODE_SAVE + rc->n_children = n_children; #endif #if MICROPY_PY_SYS_SETTRACE @@ -85,26 +88,22 @@ void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, const byte *code, #endif #ifdef DEBUG_PRINT - #if !MICROPY_DEBUG_PRINTERS + #if !(MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS) const size_t len = 0; #endif DEBUG_printf("assign byte code: code=%p len=" UINT_FMT " flags=%x\n", code, len, (uint)scope_flags); #endif - #if MICROPY_DEBUG_PRINTERS - if (mp_verbose_flag >= 2) { - mp_bytecode_print(&mp_plat_print, rc, code, len, const_table); - } - #endif } #if MICROPY_EMIT_MACHINE_CODE -void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void *fun_data, mp_uint_t fun_len, const mp_uint_t *const_table, +void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void *fun_data, mp_uint_t fun_len, + mp_raw_code_t **children, #if MICROPY_PERSISTENT_CODE_SAVE + size_t n_children, uint16_t prelude_offset, - uint16_t n_obj, uint16_t n_raw_code, uint16_t n_qstr, mp_qstr_link_entry_t *qstr_link, #endif - mp_uint_t n_pos_args, mp_uint_t scope_flags, mp_uint_t type_sig) { + mp_uint_t scope_flags, mp_uint_t n_pos_args, mp_uint_t type_sig) { assert(kind == MP_CODE_NATIVE_PY || kind == MP_CODE_NATIVE_VIPER || kind == MP_CODE_NATIVE_ASM); @@ -135,20 +134,24 @@ void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void rc->kind = kind; rc->scope_flags = scope_flags; - rc->n_pos_args = n_pos_args; rc->fun_data = fun_data; - rc->const_table = const_table; - rc->type_sig = type_sig; + + #if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS + rc->fun_data_len = fun_len; + #endif + rc->children = children; #if MICROPY_PERSISTENT_CODE_SAVE - rc->fun_data_len = fun_len; + rc->n_children = n_children; rc->prelude_offset = prelude_offset; - rc->n_obj = n_obj; - rc->n_raw_code = n_raw_code; rc->n_qstr = n_qstr; rc->qstr_link = qstr_link; #endif + // These two entries are only needed for MP_CODE_NATIVE_ASM. + rc->n_pos_args = n_pos_args; + rc->type_sig = type_sig; + #ifdef DEBUG_PRINT DEBUG_printf("assign native: kind=%d fun=%p len=" UINT_FMT " n_pos_args=" UINT_FMT " flags=%x\n", kind, fun_data, fun_len, n_pos_args, (uint)scope_flags); for (mp_uint_t i = 0; i < fun_len; i++) { @@ -170,15 +173,15 @@ void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void } #endif -mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, mp_obj_t def_args, mp_obj_t def_kw_args) { +mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, const mp_module_context_t *context, const mp_obj_t *def_args) { DEBUG_OP_printf("make_function_from_raw_code %p\n", rc); assert(rc != NULL); // def_args must be MP_OBJ_NULL or a tuple - assert(def_args == MP_OBJ_NULL || mp_obj_is_type(def_args, &mp_type_tuple)); + assert(def_args == NULL || def_args[0] == MP_OBJ_NULL || mp_obj_is_type(def_args[0], &mp_type_tuple)); // def_kw_args must be MP_OBJ_NULL or a dict - assert(def_kw_args == MP_OBJ_NULL || mp_obj_is_type(def_kw_args, &mp_type_dict)); + assert(def_args == NULL || def_args[1] == MP_OBJ_NULL || mp_obj_is_type(def_args[1], &mp_type_dict)); // make the function, depending on the raw code kind mp_obj_t fun; @@ -186,7 +189,7 @@ mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, mp_obj_t def_ar #if MICROPY_EMIT_NATIVE case MP_CODE_NATIVE_PY: case MP_CODE_NATIVE_VIPER: - fun = mp_obj_new_fun_native(def_args, def_kw_args, rc->fun_data, rc->const_table); + fun = mp_obj_new_fun_native(def_args, rc->fun_data, context, rc->children); // Check for a generator function, and if so change the type of the object if ((rc->scope_flags & MP_SCOPE_FLAG_GENERATOR) != 0) { ((mp_obj_base_t *)MP_OBJ_TO_PTR(fun))->type = &mp_type_native_gen_wrap; @@ -201,7 +204,7 @@ mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, mp_obj_t def_ar default: // rc->kind should always be set and BYTECODE is the only remaining case assert(rc->kind == MP_CODE_BYTECODE); - fun = mp_obj_new_fun_bc(def_args, def_kw_args, rc->fun_data, rc->const_table); + fun = mp_obj_new_fun_bc(def_args, rc->fun_data, context, rc->children); // check for generator functions and if so change the type of the object if ((rc->scope_flags & MP_SCOPE_FLAG_GENERATOR) != 0) { ((mp_obj_base_t *)MP_OBJ_TO_PTR(fun))->type = &mp_type_gen_wrap; @@ -218,16 +221,16 @@ mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, mp_obj_t def_ar return fun; } -mp_obj_t mp_make_closure_from_raw_code(const mp_raw_code_t *rc, mp_uint_t n_closed_over, const mp_obj_t *args) { +mp_obj_t mp_make_closure_from_raw_code(const mp_raw_code_t *rc, const mp_module_context_t *context, mp_uint_t n_closed_over, const mp_obj_t *args) { DEBUG_OP_printf("make_closure_from_raw_code %p " UINT_FMT " %p\n", rc, n_closed_over, args); // make function object mp_obj_t ffun; if (n_closed_over & 0x100) { // default positional and keyword args given - ffun = mp_make_function_from_raw_code(rc, args[0], args[1]); + ffun = mp_make_function_from_raw_code(rc, context, args); } else { // default positional and keyword args not given - ffun = mp_make_function_from_raw_code(rc, MP_OBJ_NULL, MP_OBJ_NULL); + ffun = mp_make_function_from_raw_code(rc, context, NULL); } // wrap function in closure object return mp_obj_new_closure(ffun, n_closed_over & 0xff, args + ((n_closed_over >> 7) & 2)); diff --git a/py/emitglue.h b/py/emitglue.h index a5411dc2e2..9a22a93218 100644 --- a/py/emitglue.h +++ b/py/emitglue.h @@ -54,16 +54,20 @@ typedef struct _mp_qstr_link_entry_t { uint16_t qst; } mp_qstr_link_entry_t; +// compiled bytecode: instance in RAM, referenced by outer scope, usually freed after first (and only) use +// mpy file: instance in RAM, created when .mpy file is loaded (same comments as above) +// frozen: instance in ROM typedef struct _mp_raw_code_t { mp_uint_t kind : 3; // of type mp_raw_code_kind_t mp_uint_t scope_flags : 7; mp_uint_t n_pos_args : 11; const void *fun_data; - const mp_uint_t *const_table; + #if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS + size_t fun_data_len; // so mp_raw_code_save and mp_bytecode_print work + #endif + struct _mp_raw_code_t **children; #if MICROPY_PERSISTENT_CODE_SAVE - size_t fun_data_len; - uint16_t n_obj; - uint16_t n_raw_code; + size_t n_children; #if MICROPY_PY_SYS_SETTRACE mp_bytecode_prelude_t prelude; // line_of_definition is a Python source line where the raw_code was @@ -89,22 +93,22 @@ void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, const byte *code, #if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS size_t len, #endif - const mp_uint_t *const_table, + mp_raw_code_t **children, #if MICROPY_PERSISTENT_CODE_SAVE - uint16_t n_obj, uint16_t n_raw_code, + size_t n_children, #endif mp_uint_t scope_flags); void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void *fun_data, mp_uint_t fun_len, - const mp_uint_t *const_table, + mp_raw_code_t **children, #if MICROPY_PERSISTENT_CODE_SAVE + size_t n_children, uint16_t prelude_offset, - uint16_t n_obj, uint16_t n_raw_code, uint16_t n_qstr, mp_qstr_link_entry_t *qstr_link, #endif - mp_uint_t n_pos_args, mp_uint_t scope_flags, mp_uint_t type_sig); + mp_uint_t scope_flags, mp_uint_t n_pos_args, mp_uint_t type_sig); -mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, mp_obj_t def_args, mp_obj_t def_kw_args); -mp_obj_t mp_make_closure_from_raw_code(const mp_raw_code_t *rc, mp_uint_t n_closed_over, const mp_obj_t *args); +mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, const mp_module_context_t *context, const mp_obj_t *def_args); +mp_obj_t mp_make_closure_from_raw_code(const mp_raw_code_t *rc, const mp_module_context_t *context, mp_uint_t n_closed_over, const mp_obj_t *args); #endif // MICROPY_INCLUDED_PY_EMITGLUE_H diff --git a/py/emitnative.c b/py/emitnative.c index 6504f37765..ca34e89f64 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -48,6 +48,7 @@ #include "py/emit.h" #include "py/nativeglue.h" +#include "py/objfun.h" #include "py/objstr.h" #if MICROPY_DEBUG_VERBOSE // print debugging info @@ -92,9 +93,13 @@ #define OFFSETOF_CODE_STATE_FUN_BC (offsetof(mp_code_state_t, fun_bc) / sizeof(uintptr_t)) #define OFFSETOF_CODE_STATE_IP (offsetof(mp_code_state_t, ip) / sizeof(uintptr_t)) #define OFFSETOF_CODE_STATE_SP (offsetof(mp_code_state_t, sp) / sizeof(uintptr_t)) -#define OFFSETOF_OBJ_FUN_BC_GLOBALS (offsetof(mp_obj_fun_bc_t, globals) / sizeof(uintptr_t)) +#define OFFSETOF_CODE_STATE_N_STATE (offsetof(mp_code_state_t, n_state) / sizeof(uintptr_t)) +#define OFFSETOF_OBJ_FUN_BC_CONTEXT (offsetof(mp_obj_fun_bc_t, context) / sizeof(uintptr_t)) +#define OFFSETOF_OBJ_FUN_BC_CHILD_TABLE (offsetof(mp_obj_fun_bc_t, child_table) / sizeof(uintptr_t)) #define OFFSETOF_OBJ_FUN_BC_BYTECODE (offsetof(mp_obj_fun_bc_t, bytecode) / sizeof(uintptr_t)) -#define OFFSETOF_OBJ_FUN_BC_CONST_TABLE (offsetof(mp_obj_fun_bc_t, const_table) / sizeof(uintptr_t)) +#define OFFSETOF_MODULE_CONTEXT_OBJ_TABLE (offsetof(mp_module_context_t, constants.obj_table) / sizeof(uintptr_t)) +#define OFFSETOF_MODULE_CONTEXT_GLOBALS (offsetof(mp_module_context_t, module.globals) / sizeof(uintptr_t)) +#define INDEX_OF_MP_FUN_TABLE_IN_CONST_TABLE (0) // If not already defined, set parent args to same as child call registers #ifndef REG_PARENT_RET @@ -205,6 +210,7 @@ typedef struct _exc_stack_entry_t { } exc_stack_entry_t; struct _emit_t { + mp_emit_common_t *emit_common; mp_obj_t *error_slot; uint *label_slot; uint exit_label; @@ -225,18 +231,17 @@ struct _emit_t { exc_stack_entry_t *exc_stack; int prelude_offset; + #if N_PRELUDE_AS_BYTES_OBJ + size_t prelude_const_table_offset; + #endif int start_offset; int n_state; uint16_t code_state_start; uint16_t stack_start; int stack_size; + uint16_t n_info; uint16_t n_cell; - uint16_t const_table_cur_obj; - uint16_t const_table_num_obj; - uint16_t const_table_cur_raw_code; - mp_uint_t *const_table; - #if MICROPY_PERSISTENT_CODE_SAVE uint16_t qstr_link_cur; mp_qstr_link_entry_t *qstr_link; @@ -255,8 +260,9 @@ STATIC void emit_native_global_exc_entry(emit_t *emit); STATIC void emit_native_global_exc_exit(emit_t *emit); STATIC void emit_native_load_const_obj(emit_t *emit, mp_obj_t obj); -emit_t *EXPORT_FUN(new)(mp_obj_t * error_slot, uint *label_slot, mp_uint_t max_num_labels) { +emit_t *EXPORT_FUN(new)(mp_emit_common_t * emit_common, mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels) { emit_t *emit = m_new0(emit_t, 1); + emit->emit_common = emit_common; emit->error_slot = error_slot; emit->label_slot = label_slot; emit->stack_info_alloc = 8; @@ -340,30 +346,22 @@ STATIC void emit_native_mov_reg_qstr_obj(emit_t *emit, int reg_dest, qstr qst) { emit_native_mov_state_reg((emit), (local_num), (reg_temp)); \ } while (false) -#define emit_native_mov_state_imm_fix_u16_via(emit, local_num, imm, reg_temp) \ - do { \ - ASM_MOV_REG_IMM_FIX_U16((emit)->as, (reg_temp), (imm)); \ - emit_native_mov_state_reg((emit), (local_num), (reg_temp)); \ - } while (false) - -#define emit_native_mov_state_imm_fix_word_via(emit, local_num, imm, reg_temp) \ - do { \ - ASM_MOV_REG_IMM_FIX_WORD((emit)->as, (reg_temp), (imm)); \ - emit_native_mov_state_reg((emit), (local_num), (reg_temp)); \ - } while (false) - STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope) { DEBUG_printf("start_pass(pass=%u, scope=%p)\n", pass, scope); + if (pass == MP_PASS_SCOPE) { + // Note: the first argument passed here is mp_emit_common_t, not the native emitter context + #if N_PRELUDE_AS_BYTES_OBJ + if (scope->emit_options == MP_EMIT_OPT_NATIVE_PYTHON) { + mp_emit_common_alloc_const_obj((mp_emit_common_t *)emit, mp_const_none); + } + #endif + return; + } + emit->pass = pass; emit->do_viper_types = scope->emit_options == MP_EMIT_OPT_VIPER; emit->stack_size = 0; - #if N_PRELUDE_AS_BYTES_OBJ - emit->const_table_cur_obj = emit->do_viper_types ? 0 : 1; // reserve first obj for prelude bytes obj - #else - emit->const_table_cur_obj = 0; - #endif - emit->const_table_cur_raw_code = 0; #if MICROPY_PERSISTENT_CODE_SAVE emit->qstr_link_cur = 0; #endif @@ -455,8 +453,9 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop #endif // Load REG_FUN_TABLE with a pointer to mp_fun_table, found in the const_table - ASM_LOAD_REG_REG_OFFSET(emit->as, REG_LOCAL_3, REG_PARENT_ARG_1, OFFSETOF_OBJ_FUN_BC_CONST_TABLE); - ASM_LOAD_REG_REG_OFFSET(emit->as, REG_FUN_TABLE, REG_LOCAL_3, 0); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_FUN_TABLE, REG_PARENT_ARG_1, OFFSETOF_OBJ_FUN_BC_CONTEXT); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_FUN_TABLE, REG_FUN_TABLE, OFFSETOF_MODULE_CONTEXT_OBJ_TABLE); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_FUN_TABLE, REG_FUN_TABLE, INDEX_OF_MP_FUN_TABLE_IN_CONST_TABLE); // Store function object (passed as first arg) to stack if needed if (NEED_FUN_OBJ(emit)) { @@ -514,7 +513,7 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop emit->stack_start = SIZEOF_CODE_STATE; #if N_PRELUDE_AS_BYTES_OBJ // Load index of prelude bytes object in const_table - mp_asm_base_data(&emit->as->base, ASM_WORD_SIZE, (uintptr_t)(emit->scope->num_pos_args + emit->scope->num_kwonly_args + 1)); + mp_asm_base_data(&emit->as->base, ASM_WORD_SIZE, (uintptr_t)emit->prelude_const_table_offset); #else mp_asm_base_data(&emit->as->base, ASM_WORD_SIZE, (uintptr_t)emit->prelude_offset); #endif @@ -536,8 +535,9 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop // Load REG_FUN_TABLE with a pointer to mp_fun_table, found in the const_table ASM_LOAD_REG_REG_OFFSET(emit->as, REG_TEMP0, REG_GENERATOR_STATE, LOCAL_IDX_FUN_OBJ(emit)); - ASM_LOAD_REG_REG_OFFSET(emit->as, REG_TEMP0, REG_TEMP0, OFFSETOF_OBJ_FUN_BC_CONST_TABLE); - ASM_LOAD_REG_REG_OFFSET(emit->as, REG_FUN_TABLE, REG_TEMP0, emit->scope->num_pos_args + emit->scope->num_kwonly_args); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_TEMP0, REG_TEMP0, OFFSETOF_OBJ_FUN_BC_CONTEXT); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_TEMP0, REG_TEMP0, OFFSETOF_MODULE_CONTEXT_OBJ_TABLE); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_FUN_TABLE, REG_TEMP0, INDEX_OF_MP_FUN_TABLE_IN_CONST_TABLE); } else { // The locals and stack start after the code_state structure emit->stack_start = emit->code_state_start + SIZEOF_CODE_STATE; @@ -555,22 +555,33 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop #endif // Load REG_FUN_TABLE with a pointer to mp_fun_table, found in the const_table - ASM_LOAD_REG_REG_OFFSET(emit->as, REG_LOCAL_3, REG_PARENT_ARG_1, OFFSETOF_OBJ_FUN_BC_CONST_TABLE); - ASM_LOAD_REG_REG_OFFSET(emit->as, REG_FUN_TABLE, REG_LOCAL_3, emit->scope->num_pos_args + emit->scope->num_kwonly_args); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_FUN_TABLE, REG_PARENT_ARG_1, OFFSETOF_OBJ_FUN_BC_CONTEXT); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_FUN_TABLE, REG_FUN_TABLE, OFFSETOF_MODULE_CONTEXT_OBJ_TABLE); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_FUN_TABLE, REG_FUN_TABLE, INDEX_OF_MP_FUN_TABLE_IN_CONST_TABLE); // Set code_state.fun_bc ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_FUN_OBJ(emit), REG_PARENT_ARG_1); - // Set code_state.ip (offset from start of this function to prelude info) + // Set code_state.ip, a pointer to the beginning of the prelude + // Need to use some locals for this, so assert that they are available for use + MP_STATIC_ASSERT(REG_LOCAL_3 != REG_PARENT_ARG_1); + MP_STATIC_ASSERT(REG_LOCAL_3 != REG_PARENT_ARG_2); + MP_STATIC_ASSERT(REG_LOCAL_3 != REG_PARENT_ARG_3); + MP_STATIC_ASSERT(REG_LOCAL_3 != REG_PARENT_ARG_4); int code_state_ip_local = emit->code_state_start + OFFSETOF_CODE_STATE_IP; #if N_PRELUDE_AS_BYTES_OBJ - // Prelude is a bytes object in const_table; store ip = prelude->data - fun_bc->bytecode - ASM_LOAD_REG_REG_OFFSET(emit->as, REG_LOCAL_3, REG_LOCAL_3, emit->scope->num_pos_args + emit->scope->num_kwonly_args + 1); + // Prelude is a bytes object in const_table[prelude_const_table_offset]. + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_LOCAL_3, REG_PARENT_ARG_1, OFFSETOF_OBJ_FUN_BC_CONTEXT); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_LOCAL_3, REG_LOCAL_3, OFFSETOF_MODULE_CONTEXT_OBJ_TABLE); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_LOCAL_3, REG_LOCAL_3, emit->prelude_const_table_offset); ASM_LOAD_REG_REG_OFFSET(emit->as, REG_LOCAL_3, REG_LOCAL_3, offsetof(mp_obj_str_t, data) / sizeof(uintptr_t)); - ASM_LOAD_REG_REG_OFFSET(emit->as, REG_PARENT_ARG_1, REG_PARENT_ARG_1, OFFSETOF_OBJ_FUN_BC_BYTECODE); - ASM_SUB_REG_REG(emit->as, REG_LOCAL_3, REG_PARENT_ARG_1); - emit_native_mov_state_reg(emit, code_state_ip_local, REG_LOCAL_3); #else + MP_STATIC_ASSERT(REG_LOCAL_2 != REG_PARENT_ARG_1); + MP_STATIC_ASSERT(REG_LOCAL_2 != REG_PARENT_ARG_2); + MP_STATIC_ASSERT(REG_LOCAL_2 != REG_PARENT_ARG_3); + MP_STATIC_ASSERT(REG_LOCAL_2 != REG_PARENT_ARG_4); + // Prelude is at the end of the machine code + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_LOCAL_3, REG_PARENT_ARG_1, OFFSETOF_OBJ_FUN_BC_BYTECODE); if (emit->pass == MP_PASS_CODE_SIZE) { // Commit to the encoding size based on the value of prelude_offset in this pass. // By using 32768 as the cut-off it is highly unlikely that prelude_offset will @@ -579,14 +590,16 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop } if (emit->prelude_offset_uses_u16_encoding) { assert(emit->prelude_offset <= 65535); - emit_native_mov_state_imm_fix_u16_via(emit, code_state_ip_local, emit->prelude_offset, REG_PARENT_ARG_1); + ASM_MOV_REG_IMM_FIX_U16((emit)->as, REG_LOCAL_2, emit->prelude_offset); } else { - emit_native_mov_state_imm_fix_word_via(emit, code_state_ip_local, emit->prelude_offset, REG_PARENT_ARG_1); + ASM_MOV_REG_IMM_FIX_WORD((emit)->as, REG_LOCAL_2, emit->prelude_offset); } + ASM_ADD_REG_REG(emit->as, REG_LOCAL_3, REG_LOCAL_2); #endif + emit_native_mov_state_reg(emit, code_state_ip_local, REG_LOCAL_3); // Set code_state.n_state (only works on little endian targets due to n_state being uint16_t) - emit_native_mov_state_imm_via(emit, emit->code_state_start + offsetof(mp_code_state_t, n_state) / sizeof(uintptr_t), emit->n_state, REG_ARG_1); + emit_native_mov_state_imm_via(emit, emit->code_state_start + OFFSETOF_CODE_STATE_N_STATE, emit->n_state, REG_ARG_1); // Put address of code_state into first arg ASM_MOV_REG_LOCAL_ADDR(emit->as, REG_ARG_1, emit->code_state_start); @@ -628,30 +641,17 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop emit->local_vtype[id->local_num] = VTYPE_PYOBJ; } } - - if (pass == MP_PASS_EMIT) { - // write argument names as qstr objects - // see comment in corresponding part of emitbc.c about the logic here - for (int i = 0; i < scope->num_pos_args + scope->num_kwonly_args; i++) { - qstr qst = MP_QSTR__star_; - for (int j = 0; j < scope->id_info_len; ++j) { - id_info_t *id = &scope->id_info[j]; - if ((id->flags & ID_FLAG_IS_PARAM) && id->local_num == i) { - qst = id->qst; - break; - } - } - emit->const_table[i] = (mp_uint_t)MP_OBJ_NEW_QSTR(qst); - } - } } - } static inline void emit_native_write_code_info_byte(emit_t *emit, byte val) { mp_asm_base_data(&emit->as->base, 1, val); } +static inline void emit_native_write_code_info_qstr(emit_t *emit, qstr qst) { + mp_encode_uint(&emit->as->base, mp_asm_base_get_cur_to_write_bytes, mp_emit_common_use_qstr(emit->emit_common, qst)); +} + STATIC void emit_native_end_pass(emit_t *emit) { emit_native_global_exc_exit(emit); @@ -662,21 +662,25 @@ STATIC void emit_native_end_pass(emit_t *emit) { size_t n_exc_stack = 0; // exc-stack not needed for native code MP_BC_PRELUDE_SIG_ENCODE(n_state, n_exc_stack, emit->scope, emit_native_write_code_info_byte, emit); - #if MICROPY_PERSISTENT_CODE - size_t n_info = 4; - #else - size_t n_info = 1; - #endif - MP_BC_PRELUDE_SIZE_ENCODE(n_info, emit->n_cell, emit_native_write_code_info_byte, emit); + size_t n_info = emit->n_info; + size_t n_cell = emit->n_cell; + MP_BC_PRELUDE_SIZE_ENCODE(n_info, n_cell, emit_native_write_code_info_byte, emit); - #if MICROPY_PERSISTENT_CODE - mp_asm_base_data(&emit->as->base, 1, emit->scope->simple_name); - mp_asm_base_data(&emit->as->base, 1, emit->scope->simple_name >> 8); - mp_asm_base_data(&emit->as->base, 1, emit->scope->source_file); - mp_asm_base_data(&emit->as->base, 1, emit->scope->source_file >> 8); - #else - mp_asm_base_data(&emit->as->base, 1, 1); - #endif + // bytecode prelude: source info (function and argument qstrs) + size_t info_start = mp_asm_base_get_code_pos(&emit->as->base); + emit_native_write_code_info_qstr(emit, emit->scope->simple_name); + for (int i = 0; i < emit->scope->num_pos_args + emit->scope->num_kwonly_args; i++) { + qstr qst = MP_QSTR__star_; + for (int j = 0; j < emit->scope->id_info_len; ++j) { + id_info_t *id = &emit->scope->id_info[j]; + if ((id->flags & ID_FLAG_IS_PARAM) && id->local_num == i) { + qst = id->qst; + break; + } + } + emit_native_write_code_info_qstr(emit, qst); + } + emit->n_info = mp_asm_base_get_code_pos(&emit->as->base) - info_start; // bytecode prelude: initialise closed over variables size_t cell_start = mp_asm_base_get_code_pos(&emit->as->base); @@ -690,13 +694,14 @@ STATIC void emit_native_end_pass(emit_t *emit) { emit->n_cell = mp_asm_base_get_code_pos(&emit->as->base) - cell_start; #if N_PRELUDE_AS_BYTES_OBJ - // Prelude bytes object is after qstr arg names and mp_fun_table - size_t table_off = emit->scope->num_pos_args + emit->scope->num_kwonly_args + 1; + // Create the prelude as a bytes object, and store it in the constant table + mp_obj_t prelude = mp_const_none; if (emit->pass == MP_PASS_EMIT) { void *buf = emit->as->base.code_base + emit->prelude_offset; size_t n = emit->as->base.code_offset - emit->prelude_offset; - emit->const_table[table_off] = (uintptr_t)mp_obj_new_bytes(buf, n); + prelude = mp_obj_new_bytes(buf, n); } + emit->prelude_const_table_offset = mp_emit_common_alloc_const_obj(emit->emit_common, prelude); #endif } @@ -706,31 +711,15 @@ STATIC void emit_native_end_pass(emit_t *emit) { assert(emit->stack_size == 0); assert(emit->exc_stack_size == 0); - // Deal with const table accounting - assert(emit->pass <= MP_PASS_STACK_SIZE || (emit->const_table_num_obj == emit->const_table_cur_obj)); - emit->const_table_num_obj = emit->const_table_cur_obj; + #if MICROPY_PERSISTENT_CODE_SAVE + // Allocate qstr_link table if needed if (emit->pass == MP_PASS_CODE_SIZE) { - size_t const_table_alloc = 1 + emit->const_table_num_obj + emit->const_table_cur_raw_code; - size_t nqstr = 0; - if (!emit->do_viper_types) { - // Add room for qstr names of arguments - nqstr = emit->scope->num_pos_args + emit->scope->num_kwonly_args; - const_table_alloc += nqstr; - } - emit->const_table = m_new(mp_uint_t, const_table_alloc); - #if !MICROPY_DYNAMIC_COMPILER - // Store mp_fun_table pointer just after qstrs - // (but in dynamic-compiler mode eliminate dependency on mp_fun_table) - emit->const_table[nqstr] = (mp_uint_t)(uintptr_t)&mp_fun_table; - #endif - - #if MICROPY_PERSISTENT_CODE_SAVE size_t qstr_link_alloc = emit->qstr_link_cur; if (qstr_link_alloc > 0) { emit->qstr_link = m_new(mp_qstr_link_entry_t, qstr_link_alloc); } - #endif } + #endif if (emit->pass == MP_PASS_EMIT) { void *f = mp_asm_base_get_code(&emit->as->base); @@ -738,13 +727,14 @@ STATIC void emit_native_end_pass(emit_t *emit) { mp_emit_glue_assign_native(emit->scope->raw_code, emit->do_viper_types ? MP_CODE_NATIVE_VIPER : MP_CODE_NATIVE_PY, - f, f_len, emit->const_table, + f, f_len, + emit->emit_common->children, #if MICROPY_PERSISTENT_CODE_SAVE + emit->emit_common->ct_cur_child, emit->prelude_offset, - emit->const_table_cur_obj, emit->const_table_cur_raw_code, emit->qstr_link_cur, emit->qstr_link, #endif - emit->scope->num_pos_args, emit->scope->scope_flags, 0); + emit->scope->scope_flags, 0, 0); } } @@ -1137,29 +1127,19 @@ STATIC exc_stack_entry_t *emit_native_pop_exc_stack(emit_t *emit) { return e; } -STATIC void emit_load_reg_with_ptr(emit_t *emit, int reg, mp_uint_t ptr, size_t table_off) { - if (!emit->do_viper_types) { - // Skip qstr names of arguments - table_off += emit->scope->num_pos_args + emit->scope->num_kwonly_args; - } - if (emit->pass == MP_PASS_EMIT) { - emit->const_table[table_off] = ptr; - } +STATIC void emit_load_reg_with_object(emit_t *emit, int reg, mp_obj_t obj) { + size_t table_off = mp_emit_common_alloc_const_obj(emit->emit_common, obj); emit_native_mov_reg_state(emit, REG_TEMP0, LOCAL_IDX_FUN_OBJ(emit)); - ASM_LOAD_REG_REG_OFFSET(emit->as, REG_TEMP0, REG_TEMP0, OFFSETOF_OBJ_FUN_BC_CONST_TABLE); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_TEMP0, REG_TEMP0, OFFSETOF_OBJ_FUN_BC_CONTEXT); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_TEMP0, REG_TEMP0, OFFSETOF_MODULE_CONTEXT_OBJ_TABLE); ASM_LOAD_REG_REG_OFFSET(emit->as, reg, REG_TEMP0, table_off); } -STATIC void emit_load_reg_with_object(emit_t *emit, int reg, mp_obj_t obj) { - // First entry is for mp_fun_table - size_t table_off = 1 + emit->const_table_cur_obj++; - emit_load_reg_with_ptr(emit, reg, (mp_uint_t)obj, table_off); -} - -STATIC void emit_load_reg_with_raw_code(emit_t *emit, int reg, mp_raw_code_t *rc) { - // First entry is for mp_fun_table, then constant objects - size_t table_off = 1 + emit->const_table_num_obj + emit->const_table_cur_raw_code++; - emit_load_reg_with_ptr(emit, reg, (mp_uint_t)rc, table_off); +STATIC void emit_load_reg_with_child(emit_t *emit, int reg, mp_raw_code_t *rc) { + size_t table_off = mp_emit_common_alloc_const_child(emit->emit_common, rc); + emit_native_mov_reg_state(emit, REG_TEMP0, LOCAL_IDX_FUN_OBJ(emit)); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_TEMP0, REG_TEMP0, OFFSETOF_OBJ_FUN_BC_CHILD_TABLE); + ASM_LOAD_REG_REG_OFFSET(emit->as, reg, REG_TEMP0, table_off); } STATIC void emit_native_label_assign(emit_t *emit, mp_uint_t l) { @@ -1203,7 +1183,8 @@ STATIC void emit_native_global_exc_entry(emit_t *emit) { if (!(emit->scope->scope_flags & MP_SCOPE_FLAG_GENERATOR)) { // Set new globals emit_native_mov_reg_state(emit, REG_ARG_1, LOCAL_IDX_FUN_OBJ(emit)); - ASM_LOAD_REG_REG_OFFSET(emit->as, REG_ARG_1, REG_ARG_1, OFFSETOF_OBJ_FUN_BC_GLOBALS); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_ARG_1, REG_ARG_1, OFFSETOF_OBJ_FUN_BC_CONTEXT); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_ARG_1, REG_ARG_1, OFFSETOF_MODULE_CONTEXT_GLOBALS); emit_call(emit, MP_F_NATIVE_SWAP_GLOBALS); // Save old globals (or NULL if globals didn't change) @@ -1254,7 +1235,8 @@ STATIC void emit_native_global_exc_entry(emit_t *emit) { #if N_NLR_SETJMP // Reload REG_FUN_TABLE, since it may be clobbered by longjmp emit_native_mov_reg_state(emit, REG_LOCAL_1, LOCAL_IDX_FUN_OBJ(emit)); - ASM_LOAD_REG_REG_OFFSET(emit->as, REG_LOCAL_1, REG_LOCAL_1, offsetof(mp_obj_fun_bc_t, const_table) / sizeof(uintptr_t)); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_LOCAL_1, REG_LOCAL_1, OFFSETOF_OBJ_FUN_BC_CONTEXT); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_LOCAL_1, REG_LOCAL_1, OFFSETOF_MODULE_CONTEXT_OBJ_TABLE); ASM_LOAD_REG_REG_OFFSET(emit->as, REG_FUN_TABLE, REG_LOCAL_1, emit->scope->num_pos_args + emit->scope->num_kwonly_args); #endif ASM_MOV_REG_LOCAL(emit->as, REG_LOCAL_1, LOCAL_IDX_EXC_HANDLER_PC(emit)); @@ -1385,11 +1367,7 @@ STATIC void emit_native_import(emit_t *emit, qstr qst, int kind) { STATIC void emit_native_load_const_tok(emit_t *emit, mp_token_kind_t tok) { DEBUG_printf("load_const_tok(tok=%u)\n", tok); if (tok == MP_TOKEN_ELLIPSIS) { - #if MICROPY_PERSISTENT_CODE_SAVE emit_native_load_const_obj(emit, MP_OBJ_FROM_PTR(&mp_const_ellipsis_obj)); - #else - emit_post_push_imm(emit, VTYPE_PYOBJ, (mp_uint_t)MP_OBJ_FROM_PTR(&mp_const_ellipsis_obj)); - #endif } else { emit_native_pre(emit); if (tok == MP_TOKEN_KW_NONE) { @@ -2682,33 +2660,46 @@ STATIC void emit_native_unpack_ex(emit_t *emit, mp_uint_t n_left, mp_uint_t n_ri STATIC void emit_native_make_function(emit_t *emit, scope_t *scope, mp_uint_t n_pos_defaults, mp_uint_t n_kw_defaults) { // call runtime, with type info for args, or don't support dict/default params, or only support Python objects for them emit_native_pre(emit); + emit_native_mov_reg_state(emit, REG_ARG_2, LOCAL_IDX_FUN_OBJ(emit)); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_ARG_2, REG_ARG_2, OFFSETOF_OBJ_FUN_BC_CONTEXT); if (n_pos_defaults == 0 && n_kw_defaults == 0) { need_reg_all(emit); - ASM_MOV_REG_IMM(emit->as, REG_ARG_2, (mp_uint_t)MP_OBJ_NULL); - ASM_MOV_REG_IMM(emit->as, REG_ARG_3, (mp_uint_t)MP_OBJ_NULL); + ASM_MOV_REG_IMM(emit->as, REG_ARG_3, 0); } else { - vtype_kind_t vtype_def_tuple, vtype_def_dict; - emit_pre_pop_reg_reg(emit, &vtype_def_dict, REG_ARG_3, &vtype_def_tuple, REG_ARG_2); - assert(vtype_def_tuple == VTYPE_PYOBJ); - assert(vtype_def_dict == VTYPE_PYOBJ); + emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, 2); need_reg_all(emit); } - emit_load_reg_with_raw_code(emit, REG_ARG_1, scope->raw_code); + emit_load_reg_with_child(emit, REG_ARG_1, scope->raw_code); ASM_CALL_IND(emit->as, MP_F_MAKE_FUNCTION_FROM_RAW_CODE); emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); } STATIC void emit_native_make_closure(emit_t *emit, scope_t *scope, mp_uint_t n_closed_over, mp_uint_t n_pos_defaults, mp_uint_t n_kw_defaults) { + // make function emit_native_pre(emit); + emit_native_mov_reg_state(emit, REG_ARG_2, LOCAL_IDX_FUN_OBJ(emit)); + ASM_LOAD_REG_REG_OFFSET(emit->as, REG_ARG_2, REG_ARG_2, OFFSETOF_OBJ_FUN_BC_CONTEXT); if (n_pos_defaults == 0 && n_kw_defaults == 0) { - emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, n_closed_over); - ASM_MOV_REG_IMM(emit->as, REG_ARG_2, n_closed_over); + need_reg_all(emit); + ASM_MOV_REG_IMM(emit->as, REG_ARG_3, 0); } else { - emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, n_closed_over + 2); - ASM_MOV_REG_IMM(emit->as, REG_ARG_2, 0x100 | n_closed_over); + emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, 2 + n_closed_over); + adjust_stack(emit, 2 + n_closed_over); + need_reg_all(emit); } - emit_load_reg_with_raw_code(emit, REG_ARG_1, scope->raw_code); - ASM_CALL_IND(emit->as, MP_F_MAKE_CLOSURE_FROM_RAW_CODE); + emit_load_reg_with_child(emit, REG_ARG_1, scope->raw_code); + ASM_CALL_IND(emit->as, MP_F_MAKE_FUNCTION_FROM_RAW_CODE); + + // make closure + #if REG_ARG_1 != REG_RET + ASM_MOV_REG_REG(emit->as, REG_ARG_1, REG_RET); + #endif + ASM_MOV_REG_IMM(emit->as, REG_ARG_2, n_closed_over); + emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, n_closed_over); + if (n_pos_defaults != 0 || n_kw_defaults != 0) { + adjust_stack(emit, -2); + } + ASM_CALL_IND(emit->as, MP_F_NEW_CLOSURE); emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); } diff --git a/py/emitnx86.c b/py/emitnx86.c index f0553f0682..d990ef5a9e 100644 --- a/py/emitnx86.c +++ b/py/emitnx86.c @@ -56,7 +56,7 @@ STATIC byte mp_f_n_args[MP_F_NUMBER_OF] = { [MP_F_UNPACK_EX] = 3, [MP_F_DELETE_NAME] = 1, [MP_F_DELETE_GLOBAL] = 1, - [MP_F_MAKE_CLOSURE_FROM_RAW_CODE] = 3, + [MP_F_NEW_CLOSURE] = 3, [MP_F_ARG_CHECK_NUM_SIG] = 3, [MP_F_SETUP_CODE_STATE] = 4, [MP_F_SMALL_INT_FLOOR_DIVIDE] = 2, diff --git a/py/frozenmod.c b/py/frozenmod.c index 6cb68d1ec0..61c2f20aa1 100644 --- a/py/frozenmod.c +++ b/py/frozenmod.c @@ -57,7 +57,7 @@ extern const char mp_frozen_str_content[]; #include "py/emitglue.h" -extern const mp_raw_code_t *const mp_frozen_mpy_content[]; +extern const mp_frozen_module_t *const mp_frozen_mpy_content[]; #endif // MICROPY_MODULE_FROZEN_MPY diff --git a/py/mpconfig.h b/py/mpconfig.h index 47779a67de..5edff69dfd 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -328,6 +328,14 @@ #define MICROPY_PERSISTENT_CODE (MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE || MICROPY_MODULE_FROZEN_MPY) #endif +// Whether bytecode uses a qstr_table to map internal qstr indices in the bytecode +// to global qstr values in the runtime (behaviour when feature is enabled), or +// just stores global qstr values directly in the bytecode. This must be enabled +// if MICROPY_PERSISTENT_CODE is enabled. +#ifndef MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE +#define MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE (MICROPY_PERSISTENT_CODE) +#endif + // Whether to emit x64 native code #ifndef MICROPY_EMIT_X64 #define MICROPY_EMIT_X64 (0) diff --git a/py/nativeglue.c b/py/nativeglue.c index 30e5b40061..aed6fecddf 100644 --- a/py/nativeglue.c +++ b/py/nativeglue.c @@ -300,7 +300,7 @@ const mp_fun_table_t mp_fun_table = { mp_unpack_ex, mp_delete_name, mp_delete_global, - mp_make_closure_from_raw_code, + mp_obj_new_closure, mp_arg_check_num_sig, mp_setup_code_state, mp_small_int_floor_divide, @@ -344,4 +344,8 @@ const mp_fun_table_t mp_fun_table = { &mp_stream_write_obj, }; +#elif MICROPY_EMIT_NATIVE && MICROPY_DYNAMIC_COMPILER + +const int mp_fun_table; + #endif // MICROPY_EMIT_NATIVE diff --git a/py/nativeglue.h b/py/nativeglue.h index 9d9a97b9e7..49ce665f2d 100644 --- a/py/nativeglue.h +++ b/py/nativeglue.h @@ -75,7 +75,7 @@ typedef enum { MP_F_UNPACK_EX, MP_F_DELETE_NAME, MP_F_DELETE_GLOBAL, - MP_F_MAKE_CLOSURE_FROM_RAW_CODE, + MP_F_NEW_CLOSURE, MP_F_ARG_CHECK_NUM_SIG, MP_F_SETUP_CODE_STATE, MP_F_SMALL_INT_FLOOR_DIVIDE, @@ -112,7 +112,7 @@ typedef struct _mp_fun_table_t { void (*set_store)(mp_obj_t self_in, mp_obj_t item); mp_obj_t (*list_append)(mp_obj_t self_in, mp_obj_t arg); mp_obj_t (*dict_store)(mp_obj_t self_in, mp_obj_t key, mp_obj_t value); - mp_obj_t (*make_function_from_raw_code)(const mp_raw_code_t *rc, mp_obj_t def_args, mp_obj_t def_kw_args); + mp_obj_t (*make_function_from_raw_code)(const mp_raw_code_t *rc, const mp_module_context_t *cm, const mp_obj_t *def_args); mp_obj_t (*call_function_n_kw)(mp_obj_t fun_in, size_t n_args_kw, const mp_obj_t *args); mp_obj_t (*call_method_n_kw)(size_t n_args, size_t n_kw, const mp_obj_t *args); mp_obj_t (*call_method_n_kw_var)(bool have_self, size_t n_args_n_kw, const mp_obj_t *args); @@ -129,7 +129,7 @@ typedef struct _mp_fun_table_t { void (*unpack_ex)(mp_obj_t seq, size_t num, mp_obj_t *items); void (*delete_name)(qstr qst); void (*delete_global)(qstr qst); - mp_obj_t (*make_closure_from_raw_code)(const mp_raw_code_t *rc, mp_uint_t n_closed_over, const mp_obj_t *args); + mp_obj_t (*new_closure)(mp_obj_t fun, size_t n_closed_over, const mp_obj_t *closed); void (*arg_check_num_sig)(size_t n_args, size_t n_kw, uint32_t sig); void (*setup_code_state)(mp_code_state_t *code_state, size_t n_args, size_t n_kw, const mp_obj_t *args); mp_int_t (*small_int_floor_divide)(mp_int_t num, mp_int_t denom); @@ -172,6 +172,12 @@ typedef struct _mp_fun_table_t { const mp_obj_fun_builtin_var_t *stream_write_obj; } mp_fun_table_t; +#if (MICROPY_EMIT_NATIVE && !MICROPY_DYNAMIC_COMPILER) || MICROPY_ENABLE_DYNRUNTIME extern const mp_fun_table_t mp_fun_table; +#elif MICROPY_EMIT_NATIVE && MICROPY_DYNAMIC_COMPILER +// In dynamic-compiler mode eliminate dependency on entries in mp_fun_table. +// This only needs to be an independent pointer, content doesn't matter. +extern const int mp_fun_table; +#endif #endif // MICROPY_INCLUDED_PY_NATIVEGLUE_H diff --git a/py/obj.h b/py/obj.h index b52ee0e2f4..08a35ee6f8 100644 --- a/py/obj.h +++ b/py/obj.h @@ -781,9 +781,6 @@ mp_obj_t mp_obj_new_exception_msg_varg(const mp_obj_type_t *exc_type, mp_rom_err #ifdef va_start mp_obj_t mp_obj_new_exception_msg_vlist(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, va_list arg); // same fmt restrictions as above #endif -mp_obj_t mp_obj_new_fun_bc(mp_obj_t def_args, mp_obj_t def_kw_args, const byte *code, const mp_uint_t *const_table); -mp_obj_t mp_obj_new_fun_native(mp_obj_t def_args_in, mp_obj_t def_kw_args, const void *fun_data, const mp_uint_t *const_table); -mp_obj_t mp_obj_new_fun_asm(size_t n_args, const void *fun_data, mp_uint_t type_sig); mp_obj_t mp_obj_new_gen_wrap(mp_obj_t fun); mp_obj_t mp_obj_new_closure(mp_obj_t fun, size_t n_closed, const mp_obj_t *closed); mp_obj_t mp_obj_new_tuple(size_t n, const mp_obj_t *items); @@ -992,7 +989,6 @@ typedef struct _mp_obj_fun_builtin_var_t { } mp_obj_fun_builtin_var_t; qstr mp_obj_fun_get_name(mp_const_obj_t fun); -qstr mp_obj_code_get_name(const byte *code_info); mp_obj_t mp_identity(mp_obj_t self); MP_DECLARE_CONST_FUN_OBJ_1(mp_identity_obj); diff --git a/py/objfun.c b/py/objfun.c index 85f531c88b..3542cc0e3f 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -143,13 +143,13 @@ const mp_obj_type_t mp_type_fun_builtin_var = { /******************************************************************************/ /* byte code functions */ -qstr mp_obj_code_get_name(const byte *code_info) { +STATIC qstr mp_obj_code_get_name(const mp_obj_fun_bc_t *fun, const byte *code_info) { MP_BC_PRELUDE_SIZE_DECODE(code_info); - #if MICROPY_PERSISTENT_CODE - return code_info[0] | (code_info[1] << 8); - #else - return mp_decode_uint_value(code_info); + mp_uint_t name = mp_decode_uint_value(code_info); + #if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE + name = fun->context->constants.qstr_table[name]; #endif + return name; } #if MICROPY_EMIT_NATIVE @@ -167,7 +167,7 @@ qstr mp_obj_fun_get_name(mp_const_obj_t fun_in) { const byte *bc = fun->bytecode; MP_BC_PRELUDE_SIG_DECODE(bc); - return mp_obj_code_get_name(bc); + return mp_obj_code_get_name(fun, bc); } #if MICROPY_CPYTHON_COMPAT @@ -209,7 +209,7 @@ STATIC void dump_args(const mp_obj_t *a, size_t sz) { #define INIT_CODESTATE(code_state, _fun_bc, _n_state, n_args, n_kw, args) \ code_state->fun_bc = _fun_bc; \ - code_state->ip = 0; \ + code_state->ip = _fun_bc->bytecode; \ code_state->n_state = _n_state; \ mp_setup_code_state(code_state, n_args, n_kw, args); \ code_state->old_globals = mp_globals_get(); @@ -240,7 +240,7 @@ mp_code_state_t *mp_obj_fun_bc_prepare_codestate(mp_obj_t self_in, size_t n_args INIT_CODESTATE(code_state, self, n_state, n_args, n_kw, args); // execute the byte code with the correct globals context - mp_globals_set(self->globals); + mp_globals_set(self->context->module.globals); return code_state; } @@ -285,7 +285,7 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const INIT_CODESTATE(code_state, self, n_state, n_args, n_kw, args); // execute the byte code with the correct globals context - mp_globals_set(self->globals); + mp_globals_set(self->context->module.globals); mp_vm_return_kind_t vm_return_kind = mp_execute_bytecode(code_state, MP_OBJ_NULL); mp_globals_set(code_state->old_globals); @@ -358,7 +358,7 @@ void mp_obj_fun_bc_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { } if (attr == MP_QSTR___globals__) { mp_obj_fun_bc_t *self = MP_OBJ_TO_PTR(self_in); - dest[0] = MP_OBJ_FROM_PTR(self->globals); + dest[0] = MP_OBJ_FROM_PTR(self->context->module.globals); } } #endif @@ -377,25 +377,29 @@ const mp_obj_type_t mp_type_fun_bc = { #endif }; -mp_obj_t mp_obj_new_fun_bc(mp_obj_t def_args_in, mp_obj_t def_kw_args, const byte *code, const mp_uint_t *const_table) { +mp_obj_t mp_obj_new_fun_bc(const mp_obj_t *def_args, const byte *code, const mp_module_context_t *context, struct _mp_raw_code_t *const *child_table) { size_t n_def_args = 0; size_t n_extra_args = 0; - mp_obj_tuple_t *def_args = MP_OBJ_TO_PTR(def_args_in); - if (def_args_in != MP_OBJ_NULL) { - assert(mp_obj_is_type(def_args_in, &mp_type_tuple)); - n_def_args = def_args->len; - n_extra_args = def_args->len; + mp_obj_tuple_t *def_pos_args = NULL; + mp_obj_t def_kw_args = MP_OBJ_NULL; + if (def_args != NULL && def_args[0] != MP_OBJ_NULL) { + assert(mp_obj_is_type(def_args[0], &mp_type_tuple)); + def_pos_args = MP_OBJ_TO_PTR(def_args[0]); + n_def_args = def_pos_args->len; + n_extra_args = def_pos_args->len; } - if (def_kw_args != MP_OBJ_NULL) { + if (def_args != NULL && def_args[1] != MP_OBJ_NULL) { + assert(mp_obj_is_type(def_args[1], &mp_type_dict)); + def_kw_args = def_args[1]; n_extra_args += 1; } mp_obj_fun_bc_t *o = m_new_obj_var(mp_obj_fun_bc_t, mp_obj_t, n_extra_args); o->base.type = &mp_type_fun_bc; - o->globals = mp_globals_get(); o->bytecode = code; - o->const_table = const_table; - if (def_args != NULL) { - memcpy(o->extra_args, def_args->items, n_def_args * sizeof(mp_obj_t)); + o->context = context; + o->child_table = child_table; + if (def_pos_args != NULL) { + memcpy(o->extra_args, def_pos_args->items, n_def_args * sizeof(mp_obj_t)); } if (def_kw_args != MP_OBJ_NULL) { o->extra_args[n_def_args] = def_kw_args; @@ -423,8 +427,8 @@ STATIC const mp_obj_type_t mp_type_fun_native = { .unary_op = mp_generic_unary_op, }; -mp_obj_t mp_obj_new_fun_native(mp_obj_t def_args_in, mp_obj_t def_kw_args, const void *fun_data, const mp_uint_t *const_table) { - mp_obj_fun_bc_t *o = MP_OBJ_TO_PTR(mp_obj_new_fun_bc(def_args_in, def_kw_args, (const byte *)fun_data, const_table)); +mp_obj_t mp_obj_new_fun_native(const mp_obj_t *def_args, const void *fun_data, const mp_module_context_t *mc, struct _mp_raw_code_t *const *child_table) { + mp_obj_fun_bc_t *o = MP_OBJ_TO_PTR(mp_obj_new_fun_bc(def_args, (const byte *)fun_data, mc, child_table)); o->base.type = &mp_type_fun_native; return MP_OBJ_FROM_PTR(o); } diff --git a/py/objfun.h b/py/objfun.h index 771bf31a95..9de15b8841 100644 --- a/py/objfun.h +++ b/py/objfun.h @@ -26,13 +26,14 @@ #ifndef MICROPY_INCLUDED_PY_OBJFUN_H #define MICROPY_INCLUDED_PY_OBJFUN_H +#include "py/bc.h" #include "py/obj.h" typedef struct _mp_obj_fun_bc_t { mp_obj_base_t base; - mp_obj_dict_t *globals; // the context within which this function was defined - const byte *bytecode; // bytecode for the function - const mp_uint_t *const_table; // constant table + const mp_module_context_t *context; // context within which this function was defined + struct _mp_raw_code_t *const *child_table; // table of children + const byte *bytecode; // bytecode for the function #if MICROPY_PY_SYS_SETTRACE const struct _mp_raw_code_t *rc; #endif @@ -42,6 +43,9 @@ typedef struct _mp_obj_fun_bc_t { mp_obj_t extra_args[]; } mp_obj_fun_bc_t; +mp_obj_t mp_obj_new_fun_bc(const mp_obj_t *def_args, const byte *code, const mp_module_context_t *cm, struct _mp_raw_code_t *const *raw_code_table); +mp_obj_t mp_obj_new_fun_native(const mp_obj_t *def_args, const void *fun_data, const mp_module_context_t *cm, struct _mp_raw_code_t *const *raw_code_table); +mp_obj_t mp_obj_new_fun_asm(size_t n_args, const void *fun_data, mp_uint_t type_sig); void mp_obj_fun_bc_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest); #endif // MICROPY_INCLUDED_PY_OBJFUN_H diff --git a/py/objgenerator.c b/py/objgenerator.c index 784310092e..cbe79e66a5 100644 --- a/py/objgenerator.c +++ b/py/objgenerator.c @@ -65,7 +65,7 @@ STATIC mp_obj_t gen_wrap_call(mp_obj_t self_in, size_t n_args, size_t n_kw, cons o->pend_exc = mp_const_none; o->code_state.fun_bc = self_fun; - o->code_state.ip = 0; + o->code_state.ip = self_fun->bytecode; o->code_state.n_state = n_state; mp_setup_code_state(&o->code_state, n_args, n_kw, args); return MP_OBJ_FROM_PTR(o); @@ -91,14 +91,18 @@ STATIC mp_obj_t native_gen_wrap_call(mp_obj_t self_in, size_t n_args, size_t n_k // The state for a native generating function is held in the same struct as a bytecode function mp_obj_fun_bc_t *self_fun = MP_OBJ_TO_PTR(self_in); - // Determine start of prelude, and extract n_state from it + // Determine start of prelude. uintptr_t prelude_offset = ((uintptr_t *)self_fun->bytecode)[0]; #if MICROPY_EMIT_NATIVE_PRELUDE_AS_BYTES_OBJ // Prelude is in bytes object in const_table, at index prelude_offset - mp_obj_str_t *prelude_bytes = MP_OBJ_TO_PTR(self_fun->const_table[prelude_offset]); - prelude_offset = (const byte *)prelude_bytes->data - self_fun->bytecode; + mp_obj_str_t *prelude_bytes = MP_OBJ_TO_PTR(self_fun->context->constants.obj_table[prelude_offset]); + const uint8_t *prelude_ptr = prelude_bytes->data; + #else + const uint8_t *prelude_ptr = self_fun->bytecode + prelude_offset; #endif - const uint8_t *ip = self_fun->bytecode + prelude_offset; + + // Extract n_state from the prelude. + const uint8_t *ip = prelude_ptr; size_t n_state, n_exc_stack_unused, scope_flags, n_pos_args, n_kwonly_args, n_def_args; MP_BC_PRELUDE_SIG_DECODE_INTO(ip, n_state, n_exc_stack_unused, scope_flags, n_pos_args, n_kwonly_args, n_def_args); size_t n_exc_stack = 0; @@ -111,7 +115,7 @@ STATIC mp_obj_t native_gen_wrap_call(mp_obj_t self_in, size_t n_args, size_t n_k // Parse the input arguments and set up the code state o->pend_exc = mp_const_none; o->code_state.fun_bc = self_fun; - o->code_state.ip = (const byte *)prelude_offset; + o->code_state.ip = prelude_ptr; o->code_state.n_state = n_state; mp_setup_code_state(&o->code_state, n_args, n_kw, args); @@ -184,7 +188,7 @@ mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_ // Set up the correct globals context for the generator and execute it self->code_state.old_globals = mp_globals_get(); - mp_globals_set(self->code_state.fun_bc->globals); + mp_globals_set(self->code_state.fun_bc->context->module.globals); mp_vm_return_kind_t ret_kind; diff --git a/py/objmodule.c b/py/objmodule.c index 9be4bad92c..4b04f7ca9c 100644 --- a/py/objmodule.c +++ b/py/objmodule.c @@ -112,6 +112,7 @@ const mp_obj_type_t mp_type_module = { .attr = module_attr, }; +#include "py/bc.h" mp_obj_t mp_obj_new_module(qstr module_name) { mp_map_t *mp_loaded_modules_map = &MP_STATE_VM(mp_loaded_modules_dict).map; mp_map_elem_t *el = mp_map_lookup(mp_loaded_modules_map, MP_OBJ_NEW_QSTR(module_name), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); @@ -122,12 +123,12 @@ mp_obj_t mp_obj_new_module(qstr module_name) { } // create new module object - mp_obj_module_t *o = m_new_obj(mp_obj_module_t); - o->base.type = &mp_type_module; - o->globals = MP_OBJ_TO_PTR(mp_obj_new_dict(MICROPY_MODULE_DICT_SIZE)); + mp_module_context_t *o = m_new_obj(mp_module_context_t); + o->module.base.type = &mp_type_module; + o->module.globals = MP_OBJ_TO_PTR(mp_obj_new_dict(MICROPY_MODULE_DICT_SIZE)); // store __name__ entry in the module - mp_obj_dict_store(MP_OBJ_FROM_PTR(o->globals), MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(module_name)); + mp_obj_dict_store(MP_OBJ_FROM_PTR(o->module.globals), MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(module_name)); // store the new module into the slot in the global dict holding all modules el->value = MP_OBJ_FROM_PTR(o); diff --git a/py/persistentcode.c b/py/persistentcode.c index ac523990c1..b473f18308 100644 --- a/py/persistentcode.c +++ b/py/persistentcode.c @@ -63,57 +63,6 @@ STATIC int mp_small_int_bits(void) { } #endif -#define QSTR_WINDOW_SIZE (32) - -typedef struct _qstr_window_t { - uint16_t idx; // indexes the head of the window - uint16_t window[QSTR_WINDOW_SIZE]; -} qstr_window_t; - -// Push a qstr to the head of the window, and the tail qstr is overwritten -STATIC void qstr_window_push(qstr_window_t *qw, qstr qst) { - qw->idx = (qw->idx + 1) % QSTR_WINDOW_SIZE; - qw->window[qw->idx] = qst; -} - -// Pull an existing qstr from within the window to the head of the window -STATIC qstr qstr_window_pull(qstr_window_t *qw, size_t idx) { - qstr qst = qw->window[idx]; - if (idx > qw->idx) { - memmove(&qw->window[idx], &qw->window[idx + 1], (QSTR_WINDOW_SIZE - idx - 1) * sizeof(uint16_t)); - qw->window[QSTR_WINDOW_SIZE - 1] = qw->window[0]; - idx = 0; - } - memmove(&qw->window[idx], &qw->window[idx + 1], (qw->idx - idx) * sizeof(uint16_t)); - qw->window[qw->idx] = qst; - return qst; -} - -#if MICROPY_PERSISTENT_CODE_LOAD - -// Access a qstr at the given index, relative to the head of the window (0=head) -STATIC qstr qstr_window_access(qstr_window_t *qw, size_t idx) { - return qstr_window_pull(qw, (qw->idx + QSTR_WINDOW_SIZE - idx) % QSTR_WINDOW_SIZE); -} - -#endif - -#if MICROPY_PERSISTENT_CODE_SAVE - -// Insert a qstr at the head of the window, either by pulling an existing one or pushing a new one -STATIC size_t qstr_window_insert(qstr_window_t *qw, qstr qst) { - for (size_t idx = 0; idx < QSTR_WINDOW_SIZE; ++idx) { - if (qw->window[idx] == qst) { - qstr_window_pull(qw, idx); - return (qw->idx + QSTR_WINDOW_SIZE - idx) % QSTR_WINDOW_SIZE; - } - } - qstr_window_push(qw, qst); - return QSTR_WINDOW_SIZE; -} - -#endif - typedef struct _bytecode_prelude_t { uint n_state; uint n_exc_stack; @@ -124,23 +73,6 @@ typedef struct _bytecode_prelude_t { uint code_info_size; } bytecode_prelude_t; -// ip will point to start of opcodes -// return value will point to simple_name, source_file qstrs -STATIC byte *extract_prelude(const byte **ip, bytecode_prelude_t *prelude) { - MP_BC_PRELUDE_SIG_DECODE(*ip); - prelude->n_state = n_state; - prelude->n_exc_stack = n_exc_stack; - prelude->scope_flags = scope_flags; - prelude->n_pos_args = n_pos_args; - prelude->n_kwonly_args = n_kwonly_args; - prelude->n_def_pos_args = n_def_pos_args; - MP_BC_PRELUDE_SIZE_DECODE(*ip); - byte *ip_info = (byte *)*ip; - *ip += n_info; - *ip += n_cell; - return ip_info; -} - #endif // MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE #if MICROPY_PERSISTENT_CODE_LOAD @@ -148,13 +80,14 @@ STATIC byte *extract_prelude(const byte **ip, bytecode_prelude_t *prelude) { #include "py/parsenum.h" STATIC int read_byte(mp_reader_t *reader); -STATIC size_t read_uint(mp_reader_t *reader, byte **out); +STATIC size_t read_uint(mp_reader_t *reader); #if MICROPY_EMIT_MACHINE_CODE typedef struct _reloc_info_t { mp_reader_t *reader; - mp_uint_t *const_table; + uint8_t *rodata; + uint8_t *bss; } reloc_info_t; #if MICROPY_EMIT_THUMB @@ -197,13 +130,13 @@ void mp_native_relocate(void *ri_in, uint8_t *text, uintptr_t reloc_text) { while ((op = read_byte(ri->reader)) != 0xff) { if (op & 1) { // Point to new location to make adjustments - size_t addr = read_uint(ri->reader, NULL); + size_t addr = read_uint(ri->reader); if ((addr & 1) == 0) { // Point to somewhere in text addr_to_adjust = &((uintptr_t *)text)[addr >> 1]; } else { // Point to somewhere in rodata - addr_to_adjust = &((uintptr_t *)ri->const_table[1])[addr >> 1]; + addr_to_adjust = &((uintptr_t *)ri->rodata)[addr >> 1]; } } op >>= 1; @@ -212,15 +145,18 @@ void mp_native_relocate(void *ri_in, uint8_t *text, uintptr_t reloc_text) { if (op <= 5) { if (op & 1) { // Read in number of adjustments to make - n = read_uint(ri->reader, NULL); + n = read_uint(ri->reader); } op >>= 1; if (op == 0) { // Destination is text dest = reloc_text; + } else if (op == 1) { + // Destination is rodata + dest = (uintptr_t)ri->rodata; } else { - // Destination is rodata (op=1) or bss (op=1 if no rodata, else op=2) - dest = ri->const_table[op]; + // Destination is bss + dest = (uintptr_t)ri->bss; } } else if (op == 6) { // Destination is mp_fun_table itself @@ -247,14 +183,10 @@ STATIC void read_bytes(mp_reader_t *reader, byte *buf, size_t len) { } } -STATIC size_t read_uint(mp_reader_t *reader, byte **out) { +STATIC size_t read_uint(mp_reader_t *reader) { size_t unum = 0; for (;;) { byte b = reader->readbyte(reader->data); - if (out != NULL) { - **out = b; - ++*out; - } unum = (unum << 7) | (b & 0x7f); if ((b & 0x80) == 0) { break; @@ -263,35 +195,41 @@ STATIC size_t read_uint(mp_reader_t *reader, byte **out) { return unum; } -STATIC qstr load_qstr(mp_reader_t *reader, qstr_window_t *qw) { - size_t len = read_uint(reader, NULL); - if (len == 0) { - // static qstr - return read_byte(reader); - } +STATIC qstr load_qstr(mp_reader_t *reader) { + size_t len = read_uint(reader); if (len & 1) { - // qstr in window - return qstr_window_access(qw, len >> 1); + // static qstr + return len >> 1; } len >>= 1; char *str = m_new(char, len); read_bytes(reader, (byte *)str, len); + read_byte(reader); // read and discard null terminator qstr qst = qstr_from_strn(str, len); m_del(char, str, len); - qstr_window_push(qw, qst); return qst; } STATIC mp_obj_t load_obj(mp_reader_t *reader) { byte obj_type = read_byte(reader); + #if MICROPY_EMIT_MACHINE_CODE + if (obj_type == 't') { + return MP_OBJ_FROM_PTR(&mp_fun_table); + } else + #endif if (obj_type == 'e') { return MP_OBJ_FROM_PTR(&mp_const_ellipsis_obj); } else { - size_t len = read_uint(reader, NULL); + size_t len = read_uint(reader); + if (len == 0 && obj_type == 'b') { + read_byte(reader); // skip null terminator + return mp_const_empty_bytes; + } vstr_t vstr; vstr_init_len(&vstr, len); read_bytes(reader, (byte *)vstr.buf, len); if (obj_type == 's' || obj_type == 'b') { + read_byte(reader); // skip null terminator return mp_obj_new_str_from_vstr(obj_type == 's' ? &mp_type_str : &mp_type_bytes, &vstr); } else if (obj_type == 'i') { return mp_parse_num_integer(vstr.buf, vstr.len, 10, NULL); @@ -302,58 +240,12 @@ STATIC mp_obj_t load_obj(mp_reader_t *reader) { } } -STATIC void load_prelude_qstrs(mp_reader_t *reader, qstr_window_t *qw, byte *ip) { - qstr simple_name = load_qstr(reader, qw); - ip[0] = simple_name; - ip[1] = simple_name >> 8; - qstr source_file = load_qstr(reader, qw); - ip[2] = source_file; - ip[3] = source_file >> 8; -} - -STATIC void load_prelude(mp_reader_t *reader, qstr_window_t *qw, byte **ip, bytecode_prelude_t *prelude) { - // Read in the prelude header - byte *ip_read = *ip; - read_uint(reader, &ip_read); // read in n_state/etc (is effectively a var-uint) - read_uint(reader, &ip_read); // read in n_info/n_cell (is effectively a var-uint) - - // Prelude header has been read into *ip, now decode and extract values from it - extract_prelude((const byte **)ip, prelude); - - // Load qstrs in prelude - load_prelude_qstrs(reader, qw, ip_read); - ip_read += 4; - - // Read remaining code info - read_bytes(reader, ip_read, *ip - ip_read); -} - -STATIC void load_bytecode(mp_reader_t *reader, qstr_window_t *qw, byte *ip, byte *ip_top) { - while (ip < ip_top) { - *ip = read_byte(reader); - size_t sz; - uint f = mp_opcode_format(ip, &sz, false); - ++ip; - --sz; - if (f == MP_BC_FORMAT_QSTR) { - qstr qst = load_qstr(reader, qw); - *ip++ = qst; - *ip++ = qst >> 8; - sz -= 2; - } else if (f == MP_BC_FORMAT_VAR_UINT) { - while ((*ip++ = read_byte(reader)) & 0x80) { - } - } - read_bytes(reader, ip, sz); - ip += sz; - } -} - -STATIC mp_raw_code_t *load_raw_code(mp_reader_t *reader, qstr_window_t *qw) { +STATIC mp_raw_code_t *load_raw_code(mp_reader_t *reader) { // Load function kind and data length - size_t kind_len = read_uint(reader, NULL); + size_t kind_len = read_uint(reader); int kind = (kind_len & 3) + MP_CODE_BYTECODE; - size_t fun_data_len = kind_len >> 2; + bool has_children = !!(kind_len & 4); + size_t fun_data_len = kind_len >> 3; #if !MICROPY_EMIT_MACHINE_CODE if (kind != MP_CODE_BYTECODE) { @@ -362,23 +254,18 @@ STATIC mp_raw_code_t *load_raw_code(mp_reader_t *reader, qstr_window_t *qw) { #endif uint8_t *fun_data = NULL; - bytecode_prelude_t prelude = {0}; #if MICROPY_EMIT_MACHINE_CODE size_t prelude_offset = 0; - mp_uint_t type_sig = 0; - size_t n_qstr_link = 0; + mp_uint_t native_scope_flags = 0; + mp_uint_t native_n_pos_args = 0; + mp_uint_t native_type_sig = 0; #endif if (kind == MP_CODE_BYTECODE) { // Allocate memory for the bytecode fun_data = m_new(uint8_t, fun_data_len); - - // Load prelude - byte *ip = fun_data; - load_prelude(reader, qw, &ip, &prelude); - // Load bytecode - load_bytecode(reader, qw, ip, fun_data + fun_data_len); + read_bytes(reader, fun_data, fun_data_len); #if MICROPY_EMIT_MACHINE_CODE } else { @@ -389,10 +276,10 @@ STATIC mp_raw_code_t *load_raw_code(mp_reader_t *reader, qstr_window_t *qw) { if (kind == MP_CODE_NATIVE_PY || kind == MP_CODE_NATIVE_VIPER) { // Parse qstr link table and link native code - n_qstr_link = read_uint(reader, NULL); + size_t n_qstr_link = read_uint(reader); for (size_t i = 0; i < n_qstr_link; ++i) { - size_t off = read_uint(reader, NULL); - qstr qst = load_qstr(reader, qw); + size_t off = read_uint(reader); + qstr qst = load_qstr(reader); uint8_t *dest = fun_data + (off >> 2); if ((off & 3) == 0) { // Generic 16-bit link @@ -409,113 +296,92 @@ STATIC mp_raw_code_t *load_raw_code(mp_reader_t *reader, qstr_window_t *qw) { } if (kind == MP_CODE_NATIVE_PY) { - // Extract prelude for later use - prelude_offset = read_uint(reader, NULL); + // Read prelude offset within fun_data, and extract scope flags. + prelude_offset = read_uint(reader); const byte *ip = fun_data + prelude_offset; - byte *ip_info = extract_prelude(&ip, &prelude); - // Load qstrs in prelude - load_prelude_qstrs(reader, qw, ip_info); + MP_BC_PRELUDE_SIG_DECODE(ip); + native_scope_flags = scope_flags; } else { - // Load basic scope info for viper and asm - prelude.scope_flags = read_uint(reader, NULL); - prelude.n_pos_args = 0; - prelude.n_kwonly_args = 0; + // Load basic scope info for viper and asm. + native_scope_flags = read_uint(reader); if (kind == MP_CODE_NATIVE_ASM) { - prelude.n_pos_args = read_uint(reader, NULL); - type_sig = read_uint(reader, NULL); + native_n_pos_args = read_uint(reader); + native_type_sig = read_uint(reader); } } #endif } - size_t n_obj = 0; - size_t n_raw_code = 0; - mp_uint_t *const_table = NULL; + size_t n_children = 0; + mp_raw_code_t **children = NULL; - if (kind != MP_CODE_NATIVE_ASM) { - // Load constant table for bytecode, native and viper - - // Number of entries in constant table - n_obj = read_uint(reader, NULL); - n_raw_code = read_uint(reader, NULL); - - // Allocate constant table - size_t n_alloc = prelude.n_pos_args + prelude.n_kwonly_args + n_obj + n_raw_code; - #if MICROPY_EMIT_MACHINE_CODE - if (kind != MP_CODE_BYTECODE) { - ++n_alloc; // additional entry for mp_fun_table - if (prelude.scope_flags & MP_SCOPE_FLAG_VIPERRODATA) { - ++n_alloc; // additional entry for rodata - } - if (prelude.scope_flags & MP_SCOPE_FLAG_VIPERBSS) { - ++n_alloc; // additional entry for BSS - } - } - #endif - - const_table = m_new(mp_uint_t, n_alloc); - mp_uint_t *ct = const_table; - - // Load function argument names (initial entries in const_table) - // (viper has n_pos_args=n_kwonly_args=0 so doesn't load any qstrs here) - for (size_t i = 0; i < prelude.n_pos_args + prelude.n_kwonly_args; ++i) { - *ct++ = (mp_uint_t)MP_OBJ_NEW_QSTR(load_qstr(reader, qw)); + #if MICROPY_EMIT_MACHINE_CODE + // Load optional BSS/rodata for viper. + uint8_t *rodata = NULL; + uint8_t *bss = NULL; + if (kind == MP_CODE_NATIVE_VIPER) { + size_t rodata_size = 0; + if (native_scope_flags & MP_SCOPE_FLAG_VIPERRODATA) { + rodata_size = read_uint(reader); } - #if MICROPY_EMIT_MACHINE_CODE - if (kind != MP_CODE_BYTECODE) { - // Populate mp_fun_table entry - *ct++ = (mp_uint_t)(uintptr_t)&mp_fun_table; + size_t bss_size = 0; + if (native_scope_flags & MP_SCOPE_FLAG_VIPERBSS) { + bss_size = read_uint(reader); + } - // Allocate and load rodata if needed - if (prelude.scope_flags & MP_SCOPE_FLAG_VIPERRODATA) { - size_t size = read_uint(reader, NULL); - uint8_t *rodata = m_new(uint8_t, size); - read_bytes(reader, rodata, size); - *ct++ = (uintptr_t)rodata; + if (rodata_size + bss_size != 0) { + bss_size = (uintptr_t)MP_ALIGN(bss_size, sizeof(uintptr_t)); + uint8_t *data = m_new0(uint8_t, bss_size + rodata_size); + bss = data; + rodata = bss + bss_size; + if (native_scope_flags & MP_SCOPE_FLAG_VIPERRODATA) { + read_bytes(reader, rodata, rodata_size); } - // Allocate BSS if needed - if (prelude.scope_flags & MP_SCOPE_FLAG_VIPERBSS) { - size_t size = read_uint(reader, NULL); - uint8_t *bss = m_new0(uint8_t, size); - *ct++ = (uintptr_t)bss; - } + // Viper code with BSS/rodata should not have any children. + // Reuse the children pointer to reference the BSS/rodata + // memory so that it is not reclaimed by the GC. + assert(!has_children); + children = (void *)data; } - #endif + } + #endif - // Load constant objects and raw code children - for (size_t i = 0; i < n_obj; ++i) { - *ct++ = (mp_uint_t)load_obj(reader); - } - for (size_t i = 0; i < n_raw_code; ++i) { - *ct++ = (mp_uint_t)(uintptr_t)load_raw_code(reader, qw); + // Load children if any. + if (has_children) { + n_children = read_uint(reader); + children = m_new(mp_raw_code_t *, n_children); + for (size_t i = 0; i < n_children; ++i) { + children[i] = load_raw_code(reader); } } // Create raw_code and return it mp_raw_code_t *rc = mp_emit_glue_new_raw_code(); if (kind == MP_CODE_BYTECODE) { + const byte *ip = fun_data; + MP_BC_PRELUDE_SIG_DECODE(ip); // Assign bytecode to raw code object mp_emit_glue_assign_bytecode(rc, fun_data, #if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS fun_data_len, #endif - const_table, + children, #if MICROPY_PERSISTENT_CODE_SAVE - n_obj, n_raw_code, + n_children, #endif - prelude.scope_flags); + scope_flags); #if MICROPY_EMIT_MACHINE_CODE } else { // Relocate and commit code to executable address space - reloc_info_t ri = {reader, const_table}; + reloc_info_t ri = {reader, rodata, bss}; #if defined(MP_PLAT_COMMIT_EXEC) - void *opt_ri = (prelude.scope_flags & MP_SCOPE_FLAG_VIPERRELOC) ? &ri : NULL; + void *opt_ri = (native_scope_flags & MP_SCOPE_FLAG_VIPERRELOC) ? &ri : NULL; fun_data = MP_PLAT_COMMIT_EXEC(fun_data, fun_data_len, opt_ri); #else - if (prelude.scope_flags & MP_SCOPE_FLAG_VIPERRELOC) { + if (native_scope_flags & MP_SCOPE_FLAG_VIPERRELOC) { #if MICROPY_PERSISTENT_CODE_TRACK_RELOC_CODE // If native code needs relocations then it's not guaranteed that a pointer to // the head of `buf` (containing the machine code) will be retained for the GC @@ -534,26 +400,27 @@ STATIC mp_raw_code_t *load_raw_code(mp_reader_t *reader, qstr_window_t *qw) { // Assign native code to raw code object mp_emit_glue_assign_native(rc, kind, - fun_data, fun_data_len, const_table, + fun_data, fun_data_len, + children, #if MICROPY_PERSISTENT_CODE_SAVE + n_children, prelude_offset, - n_obj, n_raw_code, - n_qstr_link, NULL, + 0, NULL, #endif - prelude.n_pos_args, prelude.scope_flags, type_sig); + native_scope_flags, native_n_pos_args, native_type_sig + ); #endif } return rc; } -mp_raw_code_t *mp_raw_code_load(mp_reader_t *reader) { +mp_compiled_module_t mp_raw_code_load(mp_reader_t *reader, mp_module_context_t *context) { byte header[4]; read_bytes(reader, header, sizeof(header)); if (header[0] != 'M' || header[1] != MPY_VERSION || MPY_FEATURE_DECODE_FLAGS(header[2]) != MPY_FEATURE_FLAGS - || header[3] > mp_small_int_bits() - || read_uint(reader, NULL) > QSTR_WINDOW_SIZE) { + || header[3] > mp_small_int_bits()) { mp_raise_ValueError(MP_ERROR_TEXT("incompatible .mpy file")); } if (MPY_FEATURE_DECODE_ARCH(header[2]) != MP_NATIVE_ARCH_NONE) { @@ -562,25 +429,49 @@ mp_raw_code_t *mp_raw_code_load(mp_reader_t *reader) { mp_raise_ValueError(MP_ERROR_TEXT("incompatible .mpy arch")); } } - qstr_window_t qw; - qw.idx = 0; - mp_raw_code_t *rc = load_raw_code(reader, &qw); + + size_t n_qstr = read_uint(reader); + size_t n_obj = read_uint(reader); + mp_module_context_alloc_tables(context, n_qstr, n_obj); + + // Load qstrs. + for (size_t i = 0; i < n_qstr; ++i) { + context->constants.qstr_table[i] = load_qstr(reader); + } + + // Load constant objects. + for (size_t i = 0; i < n_obj; ++i) { + context->constants.obj_table[i] = load_obj(reader); + } + + // Load top-level module. + mp_compiled_module_t cm2; + cm2.rc = load_raw_code(reader); + cm2.context = context; + + #if MICROPY_PERSISTENT_CODE_SAVE + cm2.has_native = MPY_FEATURE_DECODE_ARCH(header[2]) != MP_NATIVE_ARCH_NONE; + cm2.n_qstr = n_qstr; + cm2.n_obj = n_obj; + #endif + reader->close(reader->data); - return rc; + + return cm2; } -mp_raw_code_t *mp_raw_code_load_mem(const byte *buf, size_t len) { +mp_compiled_module_t mp_raw_code_load_mem(const byte *buf, size_t len, mp_module_context_t *context) { mp_reader_t reader; mp_reader_new_mem(&reader, buf, len, 0); - return mp_raw_code_load(&reader); + return mp_raw_code_load(&reader, context); } #if MICROPY_HAS_FILE_READER -mp_raw_code_t *mp_raw_code_load_file(const char *filename) { +mp_compiled_module_t mp_raw_code_load_file(const char *filename, mp_module_context_t *context) { mp_reader_t reader; mp_reader_new_file(&reader, filename); - return mp_raw_code_load(&reader); + return mp_raw_code_load(&reader, context); } #endif // MICROPY_HAS_FILE_READER @@ -607,26 +498,25 @@ STATIC void mp_print_uint(mp_print_t *print, size_t n) { print->print_strn(print->data, (char *)p, buf + sizeof(buf) - p); } -STATIC void save_qstr(mp_print_t *print, qstr_window_t *qw, qstr qst) { +STATIC void save_qstr(mp_print_t *print, qstr qst) { if (qst <= QSTR_LAST_STATIC) { // encode static qstr - byte buf[2] = {0, qst & 0xff}; - mp_print_bytes(print, buf, 2); - return; - } - size_t idx = qstr_window_insert(qw, qst); - if (idx < QSTR_WINDOW_SIZE) { - // qstr found in window, encode index to it - mp_print_uint(print, idx << 1 | 1); + mp_print_uint(print, qst << 1 | 1); return; } size_t len; const byte *str = qstr_data(qst, &len); mp_print_uint(print, len << 1); - mp_print_bytes(print, str, len); + mp_print_bytes(print, str, len + 1); // +1 to store null terminator } STATIC void save_obj(mp_print_t *print, mp_obj_t o) { + #if MICROPY_EMIT_MACHINE_CODE + if (o == MP_OBJ_FROM_PTR(&mp_fun_table)) { + byte obj_type = 't'; + mp_print_bytes(print, &obj_type, 1); + } else + #endif if (mp_obj_is_str_or_bytes(o)) { byte obj_type; if (mp_obj_is_str(o)) { @@ -638,7 +528,7 @@ STATIC void save_obj(mp_print_t *print, mp_obj_t o) { const char *str = mp_obj_str_get_data(o, &len); mp_print_bytes(print, &obj_type, 1); mp_print_uint(print, len); - mp_print_bytes(print, (const byte *)str, len); + mp_print_bytes(print, (const byte *)str, len + 1); // +1 to store null terminator } else if (MP_OBJ_TO_PTR(o) == &mp_const_ellipsis_obj) { byte obj_type = 'e'; mp_print_bytes(print, &obj_type, 1); @@ -667,136 +557,45 @@ STATIC void save_obj(mp_print_t *print, mp_obj_t o) { } } -STATIC void save_prelude_qstrs(mp_print_t *print, qstr_window_t *qw, const byte *ip) { - save_qstr(print, qw, ip[0] | (ip[1] << 8)); // simple_name - save_qstr(print, qw, ip[2] | (ip[3] << 8)); // source_file -} - -STATIC void save_bytecode(mp_print_t *print, qstr_window_t *qw, const byte *ip, const byte *ip_top) { - while (ip < ip_top) { - size_t sz; - uint f = mp_opcode_format(ip, &sz, true); - if (f == MP_BC_FORMAT_QSTR) { - mp_print_bytes(print, ip, 1); - qstr qst = ip[1] | (ip[2] << 8); - save_qstr(print, qw, qst); - ip += 3; - sz -= 3; - } - mp_print_bytes(print, ip, sz); - ip += sz; - } -} - -STATIC void save_raw_code(mp_print_t *print, mp_raw_code_t *rc, qstr_window_t *qstr_window) { +STATIC void save_raw_code(mp_print_t *print, const mp_raw_code_t *rc) { // Save function kind and data length - mp_print_uint(print, (rc->fun_data_len << 2) | (rc->kind - MP_CODE_BYTECODE)); + mp_print_uint(print, (rc->fun_data_len << 3) | ((rc->n_children != 0) << 2) | (rc->kind - MP_CODE_BYTECODE)); - bytecode_prelude_t prelude; + // Save function code. + mp_print_bytes(print, rc->fun_data, rc->fun_data_len); - if (rc->kind == MP_CODE_BYTECODE) { - // Extract prelude - const byte *ip = rc->fun_data; - const byte *ip_info = extract_prelude(&ip, &prelude); - - // Save prelude - mp_print_bytes(print, rc->fun_data, ip_info - (const byte *)rc->fun_data); - save_prelude_qstrs(print, qstr_window, ip_info); - ip_info += 4; - mp_print_bytes(print, ip_info, ip - ip_info); - - // Save bytecode - const byte *ip_top = (const byte *)rc->fun_data + rc->fun_data_len; - save_bytecode(print, qstr_window, ip, ip_top); #if MICROPY_EMIT_MACHINE_CODE - } else { - // Save native code - mp_print_bytes(print, rc->fun_data, rc->fun_data_len); - - if (rc->kind == MP_CODE_NATIVE_PY || rc->kind == MP_CODE_NATIVE_VIPER) { - // Save qstr link table for native code - mp_print_uint(print, rc->n_qstr); - for (size_t i = 0; i < rc->n_qstr; ++i) { - mp_print_uint(print, rc->qstr_link[i].off); - save_qstr(print, qstr_window, rc->qstr_link[i].qst); - } + if (rc->kind == MP_CODE_NATIVE_PY || rc->kind == MP_CODE_NATIVE_VIPER) { + // Save qstr link table for native code + mp_print_uint(print, rc->n_qstr); + for (size_t i = 0; i < rc->n_qstr; ++i) { + mp_print_uint(print, rc->qstr_link[i].off); + save_qstr(print, rc->qstr_link[i].qst); } + } - if (rc->kind == MP_CODE_NATIVE_PY) { - // Save prelude size - mp_print_uint(print, rc->prelude_offset); - - // Extract prelude and save qstrs in prelude - const byte *ip = (const byte *)rc->fun_data + rc->prelude_offset; - const byte *ip_info = extract_prelude(&ip, &prelude); - save_prelude_qstrs(print, qstr_window, ip_info); - } else { - // Save basic scope info for viper and asm - mp_print_uint(print, rc->scope_flags & MP_SCOPE_FLAG_ALL_SIG); - prelude.n_pos_args = 0; - prelude.n_kwonly_args = 0; - if (rc->kind == MP_CODE_NATIVE_ASM) { - mp_print_uint(print, rc->n_pos_args); - mp_print_uint(print, rc->type_sig); - } + if (rc->kind == MP_CODE_NATIVE_PY) { + // Save prelude size + mp_print_uint(print, rc->prelude_offset); + } else if (rc->kind == MP_CODE_NATIVE_VIPER || rc->kind == MP_CODE_NATIVE_ASM) { + // Save basic scope info for viper and asm + mp_print_uint(print, rc->scope_flags & MP_SCOPE_FLAG_ALL_SIG); + if (rc->kind == MP_CODE_NATIVE_ASM) { + mp_print_uint(print, rc->n_pos_args); + mp_print_uint(print, rc->type_sig); } + } #endif - } - if (rc->kind != MP_CODE_NATIVE_ASM) { - // Save constant table for bytecode, native and viper - - // Number of entries in constant table - mp_print_uint(print, rc->n_obj); - mp_print_uint(print, rc->n_raw_code); - - const mp_uint_t *const_table = rc->const_table; - - // Save function argument names (initial entries in const_table) - // (viper has n_pos_args=n_kwonly_args=0 so doesn't save any qstrs here) - for (size_t i = 0; i < prelude.n_pos_args + prelude.n_kwonly_args; ++i) { - mp_obj_t o = (mp_obj_t)*const_table++; - save_qstr(print, qstr_window, MP_OBJ_QSTR_VALUE(o)); - } - - if (rc->kind != MP_CODE_BYTECODE) { - // Skip saving mp_fun_table entry - ++const_table; - } - - // Save constant objects and raw code children - for (size_t i = 0; i < rc->n_obj; ++i) { - save_obj(print, (mp_obj_t)*const_table++); - } - for (size_t i = 0; i < rc->n_raw_code; ++i) { - save_raw_code(print, (mp_raw_code_t *)(uintptr_t)*const_table++, qstr_window); + if (rc->n_children) { + mp_print_uint(print, rc->n_children); + for (size_t i = 0; i < rc->n_children; ++i) { + save_raw_code(print, rc->children[i]); } } } -STATIC bool mp_raw_code_has_native(mp_raw_code_t *rc) { - if (rc->kind != MP_CODE_BYTECODE) { - return true; - } - - const byte *ip = rc->fun_data; - bytecode_prelude_t prelude; - extract_prelude(&ip, &prelude); - - const mp_uint_t *const_table = rc->const_table - + prelude.n_pos_args + prelude.n_kwonly_args - + rc->n_obj; - - for (size_t i = 0; i < rc->n_raw_code; ++i) { - if (mp_raw_code_has_native((mp_raw_code_t *)(uintptr_t)*const_table++)) { - return true; - } - } - - return false; -} - -void mp_raw_code_save(mp_raw_code_t *rc, mp_print_t *print) { +void mp_raw_code_save(mp_compiled_module_t *cm, mp_print_t *print) { // header contains: // byte 'M' // byte version @@ -813,16 +612,27 @@ void mp_raw_code_save(mp_raw_code_t *rc, mp_print_t *print) { mp_small_int_bits(), #endif }; - if (mp_raw_code_has_native(rc)) { + if (cm->has_native) { header[2] |= MPY_FEATURE_ENCODE_ARCH(MPY_FEATURE_ARCH_DYNAMIC); } mp_print_bytes(print, header, sizeof(header)); - mp_print_uint(print, QSTR_WINDOW_SIZE); - qstr_window_t qw; - qw.idx = 0; - memset(qw.window, 0, sizeof(qw.window)); - save_raw_code(print, rc, &qw); + // Number of entries in constant table. + mp_print_uint(print, cm->n_qstr); + mp_print_uint(print, cm->n_obj); + + // Save qstrs. + for (size_t i = 0; i < cm->n_qstr; ++i) { + save_qstr(print, cm->context->constants.qstr_table[i]); + } + + // Save constant objects. + for (size_t i = 0; i < cm->n_obj; ++i) { + save_obj(print, (mp_obj_t)cm->context->constants.obj_table[i]); + } + + // Save outer raw code, which will save all its child raw codes. + save_raw_code(print, cm->rc); } #if MICROPY_PERSISTENT_CODE_SAVE_FILE @@ -839,12 +649,12 @@ STATIC void fd_print_strn(void *env, const char *str, size_t len) { (void)ret; } -void mp_raw_code_save_file(mp_raw_code_t *rc, const char *filename) { +void mp_raw_code_save_file(mp_compiled_module_t *cm, const char *filename) { MP_THREAD_GIL_EXIT(); int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644); MP_THREAD_GIL_ENTER(); mp_print_t fd_print = {(void *)(intptr_t)fd, fd_print_strn}; - mp_raw_code_save(rc, &fd_print); + mp_raw_code_save(cm, &fd_print); MP_THREAD_GIL_EXIT(); close(fd); MP_THREAD_GIL_ENTER(); diff --git a/py/persistentcode.h b/py/persistentcode.h index 94fc3bf2d3..1991ba26ff 100644 --- a/py/persistentcode.h +++ b/py/persistentcode.h @@ -31,7 +31,7 @@ #include "py/emitglue.h" // The current version of .mpy files -#define MPY_VERSION 5 +#define MPY_VERSION 6 // Macros to encode/decode flags to/from the feature byte #define MPY_FEATURE_ENCODE_FLAGS(flags) (flags) @@ -102,12 +102,12 @@ enum { MP_NATIVE_ARCH_XTENSAWIN, }; -mp_raw_code_t *mp_raw_code_load(mp_reader_t *reader); -mp_raw_code_t *mp_raw_code_load_mem(const byte *buf, size_t len); -mp_raw_code_t *mp_raw_code_load_file(const char *filename); +mp_compiled_module_t mp_raw_code_load(mp_reader_t *reader, mp_module_context_t *ctx); +mp_compiled_module_t mp_raw_code_load_mem(const byte *buf, size_t len, mp_module_context_t *ctx); +mp_compiled_module_t mp_raw_code_load_file(const char *filename, mp_module_context_t *ctx); -void mp_raw_code_save(mp_raw_code_t *rc, mp_print_t *print); -void mp_raw_code_save_file(mp_raw_code_t *rc, const char *filename); +void mp_raw_code_save(mp_compiled_module_t *cm, mp_print_t *print); +void mp_raw_code_save_file(mp_compiled_module_t *cm, const char *filename); void mp_native_relocate(void *reloc, uint8_t *text, uintptr_t reloc_text); diff --git a/py/profile.c b/py/profile.c index d0ac99e074..4e23e9eac4 100644 --- a/py/profile.c +++ b/py/profile.c @@ -27,14 +27,16 @@ #include "py/profile.h" #include "py/bc0.h" #include "py/gc.h" +#include "py/objfun.h" #if MICROPY_PY_SYS_SETTRACE #define prof_trace_cb MP_STATE_THREAD(prof_trace_callback) +#define QSTR_MAP(context, idx) (context->constants.qstr_table[idx]) STATIC uint mp_prof_bytecode_lineno(const mp_raw_code_t *rc, size_t bc) { const mp_bytecode_prelude_t *prelude = &rc->prelude; - return mp_bytecode_get_source_line(prelude->line_info, bc); + return mp_bytecode_get_source_line(prelude->line_info, prelude->line_info_top, bc); } void mp_prof_extract_prelude(const byte *bytecode, mp_bytecode_prelude_t *prelude) { @@ -50,13 +52,14 @@ void mp_prof_extract_prelude(const byte *bytecode, mp_bytecode_prelude_t *prelud MP_BC_PRELUDE_SIZE_DECODE(ip); - prelude->line_info = ip + 4; + prelude->line_info_top = ip + n_info; prelude->opcodes = ip + n_info + n_cell; - qstr block_name = ip[0] | (ip[1] << 8); - qstr source_file = ip[2] | (ip[3] << 8); - prelude->qstr_block_name = block_name; - prelude->qstr_source_file = source_file; + prelude->qstr_block_name_idx = mp_decode_uint_value(ip); + for (size_t i = 0; i < 1 + n_pos_args + n_kwonly_args; ++i) { + ip = mp_decode_uint_skip(ip); + } + prelude->line_info = ip; } /******************************************************************************/ @@ -69,22 +72,19 @@ STATIC void code_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t k const mp_bytecode_prelude_t *prelude = &rc->prelude; mp_printf(print, "", - prelude->qstr_block_name, + QSTR_MAP(o->context, prelude->qstr_block_name_idx), o, - prelude->qstr_source_file, + QSTR_MAP(o->context, 0), rc->line_of_definition ); } -STATIC mp_obj_tuple_t *code_consts(const mp_raw_code_t *rc) { - const mp_bytecode_prelude_t *prelude = &rc->prelude; - int start = prelude->n_pos_args + prelude->n_kwonly_args + rc->n_obj; - int stop = prelude->n_pos_args + prelude->n_kwonly_args + rc->n_obj + rc->n_raw_code; - mp_obj_tuple_t *consts = MP_OBJ_TO_PTR(mp_obj_new_tuple(stop - start + 1, NULL)); +STATIC mp_obj_tuple_t *code_consts(const mp_module_context_t *context, const mp_raw_code_t *rc) { + mp_obj_tuple_t *consts = MP_OBJ_TO_PTR(mp_obj_new_tuple(rc->n_children + 1, NULL)); size_t const_no = 0; - for (int i = start; i < stop; ++i) { - mp_obj_t code = mp_obj_new_code((const mp_raw_code_t *)MP_OBJ_TO_PTR(rc->const_table[i])); + for (size_t i = 0; i < rc->n_children; ++i) { + mp_obj_t code = mp_obj_new_code(context, rc->children[i]); if (code == MP_OBJ_NULL) { m_malloc_fail(sizeof(mp_obj_code_t)); } @@ -149,16 +149,16 @@ STATIC void code_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { ); break; case MP_QSTR_co_consts: - dest[0] = MP_OBJ_FROM_PTR(code_consts(rc)); + dest[0] = MP_OBJ_FROM_PTR(code_consts(o->context, rc)); break; case MP_QSTR_co_filename: - dest[0] = MP_OBJ_NEW_QSTR(prelude->qstr_source_file); + dest[0] = MP_OBJ_NEW_QSTR(QSTR_MAP(o->context, 0)); break; case MP_QSTR_co_firstlineno: dest[0] = MP_OBJ_NEW_SMALL_INT(mp_prof_bytecode_lineno(rc, 0)); break; case MP_QSTR_co_name: - dest[0] = MP_OBJ_NEW_QSTR(prelude->qstr_block_name); + dest[0] = MP_OBJ_NEW_QSTR(QSTR_MAP(o->context, prelude->qstr_block_name_idx)); break; case MP_QSTR_co_names: dest[0] = MP_OBJ_FROM_PTR(o->dict_locals); @@ -180,12 +180,13 @@ const mp_obj_type_t mp_type_settrace_codeobj = { .attr = code_attr, }; -mp_obj_t mp_obj_new_code(const mp_raw_code_t *rc) { +mp_obj_t mp_obj_new_code(const mp_module_context_t *context, const mp_raw_code_t *rc) { mp_obj_code_t *o = m_new_obj_maybe(mp_obj_code_t); if (o == NULL) { return MP_OBJ_NULL; } o->base.type = &mp_type_settrace_codeobj; + o->context = context; o->rc = rc; o->dict_locals = mp_locals_get(); // this is a wrong! how to do this properly? o->lnotab = MP_OBJ_NULL; @@ -204,9 +205,9 @@ STATIC void frame_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t mp_printf(print, "", frame, - prelude->qstr_source_file, + QSTR_MAP(code->context, 0), frame->lineno, - prelude->qstr_block_name + QSTR_MAP(code->context, prelude->qstr_block_name_idx) ); } @@ -229,7 +230,7 @@ STATIC void frame_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { dest[0] = MP_OBJ_FROM_PTR(o->code); break; case MP_QSTR_f_globals: - dest[0] = MP_OBJ_FROM_PTR(o->code_state->fun_bc->globals); + dest[0] = MP_OBJ_FROM_PTR(o->code_state->fun_bc->context->module.globals); break; case MP_QSTR_f_lasti: dest[0] = MP_OBJ_NEW_SMALL_INT(o->lasti); @@ -258,7 +259,7 @@ mp_obj_t mp_obj_new_frame(const mp_code_state_t *code_state) { return MP_OBJ_NULL; } - mp_obj_code_t *code = o->code = MP_OBJ_TO_PTR(mp_obj_new_code(code_state->fun_bc->rc)); + mp_obj_code_t *code = o->code = MP_OBJ_TO_PTR(mp_obj_new_code(code_state->fun_bc->context, code_state->fun_bc->rc)); if (code == NULL) { return MP_OBJ_NULL; } diff --git a/py/profile.h b/py/profile.h index 64e207d04f..7f3f914034 100644 --- a/py/profile.h +++ b/py/profile.h @@ -34,7 +34,9 @@ #define mp_prof_is_executing MP_STATE_THREAD(prof_callback_is_executing) typedef struct _mp_obj_code_t { + // TODO this was 4 words mp_obj_base_t base; + const mp_module_context_t *context; const mp_raw_code_t *rc; mp_obj_dict_t *dict_locals; mp_obj_t lnotab; @@ -53,7 +55,7 @@ typedef struct _mp_obj_frame_t { void mp_prof_extract_prelude(const byte *bytecode, mp_bytecode_prelude_t *prelude); -mp_obj_t mp_obj_new_code(const mp_raw_code_t *rc); +mp_obj_t mp_obj_new_code(const mp_module_context_t *mc, const mp_raw_code_t *rc); mp_obj_t mp_obj_new_frame(const mp_code_state_t *code_state); // This is the implementation for the sys.settrace diff --git a/py/qstr.h b/py/qstr.h index ded105760c..fa634f90b0 100644 --- a/py/qstr.h +++ b/py/qstr.h @@ -46,6 +46,7 @@ enum { }; typedef size_t qstr; +typedef uint16_t qstr_short_t; #if MICROPY_QSTR_BYTES_IN_HASH == 1 typedef uint8_t qstr_hash_t; diff --git a/py/scope.c b/py/scope.c index 98e02fb53f..8fc0943289 100644 --- a/py/scope.c +++ b/py/scope.c @@ -40,7 +40,7 @@ STATIC const uint8_t scope_simple_name_table[] = { [SCOPE_GEN_EXPR] = MP_QSTR__lt_genexpr_gt_, }; -scope_t *scope_new(scope_kind_t kind, mp_parse_node_t pn, qstr source_file, mp_uint_t emit_options) { +scope_t *scope_new(scope_kind_t kind, mp_parse_node_t pn, mp_uint_t emit_options) { // Make sure those qstrs indeed fit in an uint8_t. MP_STATIC_ASSERT(MP_QSTR__lt_module_gt_ <= UINT8_MAX); MP_STATIC_ASSERT(MP_QSTR__lt_lambda_gt_ <= UINT8_MAX); @@ -52,7 +52,6 @@ scope_t *scope_new(scope_kind_t kind, mp_parse_node_t pn, qstr source_file, mp_u scope_t *scope = m_new0(scope_t, 1); scope->kind = kind; scope->pn = pn; - scope->source_file = source_file; if (kind == SCOPE_FUNCTION || kind == SCOPE_CLASS) { assert(MP_PARSE_NODE_IS_STRUCT(pn)); scope->simple_name = MP_PARSE_NODE_LEAF_ARG(((mp_parse_node_struct_t *)pn)->nodes[0]); diff --git a/py/scope.h b/py/scope.h index edf164c4ad..5006deadea 100644 --- a/py/scope.h +++ b/py/scope.h @@ -75,7 +75,6 @@ typedef struct _scope_t { struct _scope_t *next; mp_parse_node_t pn; mp_raw_code_t *raw_code; - uint16_t source_file; // a qstr uint16_t simple_name; // a qstr uint16_t scope_flags; // see runtime0.h uint16_t emit_options; // see emitglue.h @@ -90,7 +89,7 @@ typedef struct _scope_t { id_info_t *id_info; } scope_t; -scope_t *scope_new(scope_kind_t kind, mp_parse_node_t pn, qstr source_file, mp_uint_t emit_options); +scope_t *scope_new(scope_kind_t kind, mp_parse_node_t pn, mp_uint_t emit_options); void scope_free(scope_t *scope); id_info_t *scope_find_or_add_id(scope_t *scope, qstr qstr, id_info_kind_t kind); id_info_t *scope_find(scope_t *scope, qstr qstr); diff --git a/py/showbc.c b/py/showbc.c index da8077eccd..1300ac1fa3 100644 --- a/py/showbc.c +++ b/py/showbc.c @@ -41,43 +41,32 @@ #define DECODE_ULABEL do { unum = (ip[0] | (ip[1] << 8)); ip += 2; } while (0) #define DECODE_SLABEL do { unum = (ip[0] | (ip[1] << 8)) - 0x8000; ip += 2; } while (0) -#if MICROPY_PERSISTENT_CODE +#if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE #define DECODE_QSTR \ - qst = ip[0] | ip[1] << 8; \ - ip += 2; -#define DECODE_PTR \ DECODE_UINT; \ - unum = mp_showbc_const_table[unum] -#define DECODE_OBJ \ - DECODE_UINT; \ - unum = mp_showbc_const_table[unum] + qst = mp_showbc_constants->qstr_table[unum] #else -#define DECODE_QSTR { \ - qst = 0; \ - do { \ - qst = (qst << 7) + (*ip & 0x7f); \ - } while ((*ip++ & 0x80) != 0); \ -} -#define DECODE_PTR do { \ - ip = (byte *)MP_ALIGN(ip, sizeof(void *)); \ - unum = (uintptr_t)*(void **)ip; \ - ip += sizeof(void *); \ -} while (0) -#define DECODE_OBJ do { \ - ip = (byte *)MP_ALIGN(ip, sizeof(mp_obj_t)); \ - unum = (mp_uint_t)*(mp_obj_t *)ip; \ - ip += sizeof(mp_obj_t); \ -} while (0) +#define DECODE_QSTR \ + DECODE_UINT; \ + qst = unum; #endif -const byte *mp_showbc_code_start; -const mp_uint_t *mp_showbc_const_table; +#define DECODE_PTR \ + DECODE_UINT; -void mp_bytecode_print(const mp_print_t *print, const void *descr, const byte *ip, mp_uint_t len, const mp_uint_t *const_table) { +#define DECODE_OBJ \ + DECODE_UINT; \ + unum = (mp_uint_t)mp_showbc_constants->obj_table[unum] + +const byte * mp_showbc_code_start; +const mp_module_constants_t *mp_showbc_constants; + +#include "py/emitglue.h" +void mp_bytecode_print(const mp_print_t *print, const void *descr, const byte *ip, mp_uint_t len, const mp_module_constants_t *cm) { mp_showbc_code_start = ip; // Decode prelude @@ -85,16 +74,15 @@ void mp_bytecode_print(const mp_print_t *print, const void *descr, const byte *i MP_BC_PRELUDE_SIZE_DECODE(ip); const byte *code_info = ip; - #if MICROPY_PERSISTENT_CODE - qstr block_name = code_info[0] | (code_info[1] << 8); - qstr source_file = code_info[2] | (code_info[3] << 8); - code_info += 4; - #else qstr block_name = mp_decode_uint(&code_info); - qstr source_file = mp_decode_uint(&code_info); + #if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE + block_name = cm->qstr_table[block_name]; + qstr source_file = cm->qstr_table[0]; + #else + qstr source_file = cm->source_file; #endif - mp_printf(print, "File %s, code block '%s' (descriptor: %p, bytecode @%p " UINT_FMT " bytes)\n", - qstr_str(source_file), qstr_str(block_name), descr, mp_showbc_code_start, len); + mp_printf(print, "File %s, code block '%s' (descriptor: %p, bytecode @%p " UINT_FMT " bytes)\n",// rct=%p\n", + qstr_str(source_file), qstr_str(block_name), descr, mp_showbc_code_start, len);// , ((mp_raw_code_t*)descr)->children); // raw bytecode dump size_t prelude_size = ip - mp_showbc_code_start + n_info + n_cell; @@ -111,7 +99,11 @@ void mp_bytecode_print(const mp_print_t *print, const void *descr, const byte *i // bytecode prelude: arg names (as qstr objects) mp_printf(print, "arg names:"); for (mp_uint_t i = 0; i < n_pos_args + n_kwonly_args; i++) { - mp_printf(print, " %s", qstr_str(MP_OBJ_QSTR_VALUE(const_table[i]))); + qstr qst = mp_decode_uint(&code_info); + #if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE + qst = cm->qstr_table[qst]; + #endif + mp_printf(print, " %s", qstr_str(qst)); } mp_printf(print, "\n"); @@ -120,6 +112,7 @@ void mp_bytecode_print(const mp_print_t *print, const void *descr, const byte *i // skip over code_info ip += n_info; + const byte *line_info_top = ip; // bytecode prelude: initialise closed over variables for (size_t i = 0; i < n_cell; ++i) { @@ -132,7 +125,7 @@ void mp_bytecode_print(const mp_print_t *print, const void *descr, const byte *i mp_int_t bc = 0; mp_uint_t source_line = 1; mp_printf(print, " bc=" INT_FMT " line=" UINT_FMT "\n", bc, source_line); - for (const byte *ci = code_info; *ci;) { + for (const byte *ci = code_info; ci < line_info_top;) { if ((ci[0] & 0x80) == 0) { // 0b0LLBBBBB encoding bc += ci[0] & 0x1f; @@ -147,7 +140,7 @@ void mp_bytecode_print(const mp_print_t *print, const void *descr, const byte *i mp_printf(print, " bc=" INT_FMT " line=" UINT_FMT "\n", bc, source_line); } } - mp_bytecode_print2(print, ip, len - prelude_size, const_table); + mp_bytecode_print2(print, ip, len - prelude_size, cm); } const byte *mp_bytecode_print_str(const mp_print_t *print, const byte *ip) { @@ -535,9 +528,9 @@ const byte *mp_bytecode_print_str(const mp_print_t *print, const byte *ip) { return ip; } -void mp_bytecode_print2(const mp_print_t *print, const byte *ip, size_t len, const mp_uint_t *const_table) { +void mp_bytecode_print2(const mp_print_t *print, const byte *ip, size_t len, const mp_module_constants_t *cm) { mp_showbc_code_start = ip; - mp_showbc_const_table = const_table; + mp_showbc_constants = cm; while (ip < len + mp_showbc_code_start) { mp_printf(print, "%02u ", (uint)(ip - mp_showbc_code_start)); ip = mp_bytecode_print_str(print, ip); diff --git a/py/vm.c b/py/vm.c index 0289bcfae5..b762c92bf2 100644 --- a/py/vm.c +++ b/py/vm.c @@ -31,9 +31,9 @@ #include "py/emitglue.h" #include "py/objtype.h" +#include "py/objfun.h" #include "py/runtime.h" #include "py/bc0.h" -#include "py/bc.h" #include "py/profile.h" // *FORMAT-OFF* @@ -44,7 +44,7 @@ #else #define TRACE_PREFIX mp_printf(&mp_plat_print, "sp=%d ", (int)(sp - &code_state->state[0] + 1)) #endif -#define TRACE(ip) TRACE_PREFIX; mp_bytecode_print2(&mp_plat_print, ip, 1, code_state->fun_bc->const_table); +#define TRACE(ip) TRACE_PREFIX; mp_bytecode_print2(&mp_plat_print, ip, 1, &code_state->fun_bc->context->constants); #else #define TRACE(ip) #endif @@ -64,35 +64,28 @@ #define DECODE_ULABEL size_t ulab = (ip[0] | (ip[1] << 8)); ip += 2 #define DECODE_SLABEL size_t slab = (ip[0] | (ip[1] << 8)) - 0x8000; ip += 2 -#if MICROPY_PERSISTENT_CODE +#if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE #define DECODE_QSTR \ - qstr qst = ip[0] | ip[1] << 8; \ - ip += 2; -#define DECODE_PTR \ DECODE_UINT; \ - void *ptr = (void*)(uintptr_t)code_state->fun_bc->const_table[unum] -#define DECODE_OBJ \ - DECODE_UINT; \ - mp_obj_t obj = (mp_obj_t)code_state->fun_bc->const_table[unum] + qstr qst = qstr_table[unum] #else -#define DECODE_QSTR qstr qst = 0; \ - do { \ - qst = (qst << 7) + (*ip & 0x7f); \ - } while ((*ip++ & 0x80) != 0) -#define DECODE_PTR \ - ip = (byte*)MP_ALIGN(ip, sizeof(void*)); \ - void *ptr = *(void**)ip; \ - ip += sizeof(void*) -#define DECODE_OBJ \ - ip = (byte*)MP_ALIGN(ip, sizeof(mp_obj_t)); \ - mp_obj_t obj = *(mp_obj_t*)ip; \ - ip += sizeof(mp_obj_t) +#define DECODE_QSTR \ + DECODE_UINT; \ + qstr qst = unum; #endif +#define DECODE_PTR \ + DECODE_UINT; \ + void *ptr = (void *)(uintptr_t)code_state->fun_bc->child_table[unum] + +#define DECODE_OBJ \ + DECODE_UINT; \ + mp_obj_t obj = (mp_obj_t)code_state->fun_bc->context->constants.obj_table[unum] + #define PUSH(val) *++sp = (val) #define POP() (*sp--) #define TOP() (*sp) @@ -255,6 +248,9 @@ outer_dispatch_loop: // local variables that are not visible to the exception handler const byte *ip = code_state->ip; mp_obj_t *sp = code_state->sp; + #if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE + const qstr_short_t *qstr_table = code_state->fun_bc->context->constants.qstr_table; + #endif mp_obj_t obj_shared; MICROPY_VM_HOOK_INIT @@ -859,15 +855,15 @@ unwind_jump:; ENTRY(MP_BC_MAKE_FUNCTION): { DECODE_PTR; - PUSH(mp_make_function_from_raw_code(ptr, MP_OBJ_NULL, MP_OBJ_NULL)); + PUSH(mp_make_function_from_raw_code(ptr, code_state->fun_bc->context, NULL)); DISPATCH(); } ENTRY(MP_BC_MAKE_FUNCTION_DEFARGS): { DECODE_PTR; // Stack layout: def_tuple def_dict <- TOS - mp_obj_t def_dict = POP(); - SET_TOP(mp_make_function_from_raw_code(ptr, TOP(), def_dict)); + sp -= 1; + SET_TOP(mp_make_function_from_raw_code(ptr, code_state->fun_bc->context, sp)); DISPATCH(); } @@ -876,7 +872,7 @@ unwind_jump:; size_t n_closed_over = *ip++; // Stack layout: closed_overs <- TOS sp -= n_closed_over - 1; - SET_TOP(mp_make_closure_from_raw_code(ptr, n_closed_over, sp)); + SET_TOP(mp_make_closure_from_raw_code(ptr, code_state->fun_bc->context, n_closed_over, sp)); DISPATCH(); } @@ -885,7 +881,7 @@ unwind_jump:; size_t n_closed_over = *ip++; // Stack layout: def_tuple def_dict closed_overs <- TOS sp -= 2 + n_closed_over - 1; - SET_TOP(mp_make_closure_from_raw_code(ptr, 0x100 | n_closed_over, sp)); + SET_TOP(mp_make_closure_from_raw_code(ptr, code_state->fun_bc->context, 0x100 | n_closed_over, sp)); DISPATCH(); } @@ -1384,23 +1380,20 @@ unwind_loop: const byte *ip = code_state->fun_bc->bytecode; MP_BC_PRELUDE_SIG_DECODE(ip); MP_BC_PRELUDE_SIZE_DECODE(ip); + const byte *line_info_top = ip + n_info; const byte *bytecode_start = ip + n_info + n_cell; - #if !MICROPY_PERSISTENT_CODE - // so bytecode is aligned - bytecode_start = MP_ALIGN(bytecode_start, sizeof(mp_uint_t)); - #endif size_t bc = code_state->ip - bytecode_start; - #if MICROPY_PERSISTENT_CODE - qstr block_name = ip[0] | (ip[1] << 8); - qstr source_file = ip[2] | (ip[3] << 8); - ip += 4; - #else qstr block_name = mp_decode_uint_value(ip); - ip = mp_decode_uint_skip(ip); - qstr source_file = mp_decode_uint_value(ip); - ip = mp_decode_uint_skip(ip); + for (size_t i = 0; i < 1 + n_pos_args + n_kwonly_args; ++i) { + ip = mp_decode_uint_skip(ip); + } + #if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE + block_name = code_state->fun_bc->context->constants.qstr_table[block_name]; + qstr source_file = code_state->fun_bc->context->constants.qstr_table[0]; + #else + qstr source_file = code_state->fun_bc->context->constants.source_file; #endif - size_t source_line = mp_bytecode_get_source_line(ip, bc); + size_t source_line = mp_bytecode_get_source_line(ip, line_info_top, bc); mp_obj_exception_add_traceback(MP_OBJ_FROM_PTR(nlr.ret_val), source_file, source_line, block_name); } diff --git a/shared/runtime/pyexec.c b/shared/runtime/pyexec.c index 5f05c1da3e..ae6dd770b1 100644 --- a/shared/runtime/pyexec.c +++ b/shared/runtime/pyexec.c @@ -83,7 +83,11 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input #if MICROPY_MODULE_FROZEN_MPY if (exec_flags & EXEC_FLAG_SOURCE_IS_RAW_CODE) { // source is a raw_code object, create the function - module_fun = mp_make_function_from_raw_code(source, MP_OBJ_NULL, MP_OBJ_NULL); + const mp_frozen_module_t *frozen = source; + mp_module_context_t *ctx = m_new_obj(mp_module_context_t); + ctx->module.globals = mp_globals_get(); + ctx->constants = frozen->constants; + module_fun = mp_make_function_from_raw_code(frozen->rc, ctx, NULL); } else #endif { diff --git a/tests/cmdline/cmd_parsetree.py.exp b/tests/cmdline/cmd_parsetree.py.exp index bee4fc99d1..ac16f6d88d 100644 --- a/tests/cmdline/cmd_parsetree.py.exp +++ b/tests/cmdline/cmd_parsetree.py.exp @@ -39,50 +39,52 @@ [ 13] \(rule\|arglist\)(164) (n=1) id(b) ---------------- -File cmdline/cmd_parsetree.py, code block '' (descriptor: \.\+, bytecode @\.\+ bytes) -Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+): -######## -\.\+63 +File cmdline/cmd_parsetree.py, code block '' (descriptor: \.\+, bytecode @\.\+ 64 bytes) +Raw bytecode (code_info_size=13, bytecode_size=51): + 20 16 01 60 28 23 23 24 24 24 24 24 25 2a 00 5f + 4b 05 00 16 02 42 f8 7f 51 16 03 10 04 16 05 23 + 00 16 06 23 01 16 07 23 02 16 08 23 03 16 09 22 + 80 7b 16 0a 23 04 14 0b 11 05 36 01 16 0c 51 63 arg names: (N_STATE 5) (N_EXC_STACK 0) bc=0 line=1 bc=0 line=4 - bc=9 line=5 - bc=12 line=6 - bc=16 line=7 - bc=22 line=8 - bc=27 line=9 - bc=32 line=10 - bc=37 line=11 - bc=42 line=12 - bc=48 line=13 + bc=8 line=5 + bc=11 line=6 + bc=14 line=7 + bc=18 line=8 + bc=22 line=9 + bc=26 line=10 + bc=30 line=11 + bc=34 line=12 + bc=39 line=13 00 BUILD_TUPLE 0 02 GET_ITER_STACK -03 FOR_ITER 12 +03 FOR_ITER 11 06 STORE_NAME i -09 JUMP 3 -12 LOAD_CONST_NONE -13 STORE_NAME a -16 LOAD_CONST_STRING 'str' -19 STORE_NAME b -22 LOAD_CONST_OBJ \.\+ -24 STORE_NAME c -27 LOAD_CONST_OBJ \.\+ -29 STORE_NAME d -32 LOAD_CONST_OBJ \.\+ -34 STORE_NAME e -37 LOAD_CONST_OBJ \.\+ -39 STORE_NAME f -42 LOAD_CONST_SMALL_INT 123 -45 STORE_NAME g -48 LOAD_CONST_OBJ \.\+ -50 LOAD_METHOD format -53 LOAD_NAME b -56 CALL_METHOD n=1 nkw=0 -58 STORE_NAME h -61 LOAD_CONST_NONE -62 RETURN_VALUE +08 JUMP 3 +11 LOAD_CONST_NONE +12 STORE_NAME a +14 LOAD_CONST_STRING 'str' +16 STORE_NAME b +18 LOAD_CONST_OBJ \.\+='a very long str that will not be interned' +20 STORE_NAME c +22 LOAD_CONST_OBJ \.\+=b'bytes' +24 STORE_NAME d +26 LOAD_CONST_OBJ \.\+=b'a very long bytes that will not be interned' +28 STORE_NAME e +30 LOAD_CONST_OBJ \.\+=123456789012345678901234567890 +32 STORE_NAME f +34 LOAD_CONST_SMALL_INT 123 +37 STORE_NAME g +39 LOAD_CONST_OBJ \.\+="fstring: '{}'" +41 LOAD_METHOD format +43 LOAD_NAME b +45 CALL_METHOD n=1 nkw=0 +47 STORE_NAME h +49 LOAD_CONST_NONE +50 RETURN_VALUE mem: total=\\d\+, current=\\d\+, peak=\\d\+ stack: \\d\+ out of \\d\+ GC: total: \\d\+, used: \\d\+, free: \\d\+ diff --git a/tests/cmdline/cmd_showbc.py.exp b/tests/cmdline/cmd_showbc.py.exp index 22712b79ee..031820fcd9 100644 --- a/tests/cmdline/cmd_showbc.py.exp +++ b/tests/cmdline/cmd_showbc.py.exp @@ -1,51 +1,144 @@ -File cmdline/cmd_showbc.py, code block '' (descriptor: \.\+, bytecode @\.\+ bytes) -Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+): -######## -\.\+63 +File cmdline/cmd_showbc.py, code block '' (descriptor: \.\+, bytecode @\.\+ 63 bytes) +Raw bytecode (code_info_size=18, bytecode_size=45): + 10 20 01 60 20 84 7d 64 60 88 07 64 60 69 20 62 + 64 20 32 00 16 02 32 01 16 02 81 2a 01 53 33 02 + 16 02 32 03 16 02 54 32 04 10 03 34 02 16 03 19 + 03 32 05 16 02 80 10 04 2a 01 1b 05 69 51 63 arg names: (N_STATE 3) (N_EXC_STACK 0) bc=0 line=1 -######## - bc=\\d\+ line=160 + bc=0 line=4 + bc=0 line=5 + bc=4 line=130 + bc=8 line=133 + bc=8 line=136 + bc=16 line=143 + bc=20 line=146 + bc=20 line=149 + bc=29 line=152 + bc=29 line=153 + bc=31 line=156 + bc=35 line=159 + bc=35 line=160 00 MAKE_FUNCTION \.\+ -\\d\+ STORE_NAME f -\\d\+ MAKE_FUNCTION \.\+ -\\d\+ STORE_NAME f -\\d\+ LOAD_CONST_SMALL_INT 1 -\\d\+ BUILD_TUPLE 1 -\\d\+ LOAD_NULL -\\d\+ MAKE_FUNCTION_DEFARGS \.\+ -\\d\+ STORE_NAME f -\\d\+ MAKE_FUNCTION \.\+ -\\d\+ STORE_NAME f -\\d\+ LOAD_BUILD_CLASS -\\d\+ MAKE_FUNCTION \.\+ -\\d\+ LOAD_CONST_STRING 'Class' -\\d\+ CALL_FUNCTION n=2 nkw=0 -\\d\+ STORE_NAME Class -\\d\+ DELETE_NAME Class -\\d\+ MAKE_FUNCTION \.\+ -\\d\+ STORE_NAME f -\\d\+ LOAD_CONST_SMALL_INT 0 -\\d\+ LOAD_CONST_STRING '*' -\\d\+ BUILD_TUPLE 1 -\\d\+ IMPORT_NAME 'sys' -\\d\+ IMPORT_STAR -\\d\+ LOAD_CONST_NONE -\\d\+ RETURN_VALUE -File cmdline/cmd_showbc.py, code block 'f' (descriptor: \.\+, bytecode @\.\+ bytes) -Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+): +02 STORE_NAME f +04 MAKE_FUNCTION \.\+ +06 STORE_NAME f +08 LOAD_CONST_SMALL_INT 1 +09 BUILD_TUPLE 1 +11 LOAD_NULL +12 MAKE_FUNCTION_DEFARGS \.\+ +14 STORE_NAME f +16 MAKE_FUNCTION \.\+ +18 STORE_NAME f +20 LOAD_BUILD_CLASS +21 MAKE_FUNCTION \.\+ +23 LOAD_CONST_STRING 'Class' +25 CALL_FUNCTION n=2 nkw=0 +27 STORE_NAME Class +29 DELETE_NAME Class +31 MAKE_FUNCTION \.\+ +33 STORE_NAME f +35 LOAD_CONST_SMALL_INT 0 +36 LOAD_CONST_STRING '*' +38 BUILD_TUPLE 1 +40 IMPORT_NAME 'sys' +42 IMPORT_STAR +43 LOAD_CONST_NONE +44 RETURN_VALUE +File cmdline/cmd_showbc.py, code block 'f' (descriptor: \.\+, bytecode @\.\+ 48\[24\] bytes) +Raw bytecode (code_info_size=8\[46\], bytecode_size=398): + a8 12 9\[bf\] 03 02 60 60 26 22 24 64 22 26 25 25 24 + 26 23 63 22 22 25 23 23 31 6d 25 65 25 25 69 68 + 26 65 27 6a 62 20 23 62 2a 29 69 24 25 28 67 26 ######## -\.\+rg names: +\.\+81 63 +arg names: (N_STATE 22) (N_EXC_STACK 2) (INIT_CELL 14) (INIT_CELL 15) (INIT_CELL 16) bc=0 line=1 + bc=0 line=4 + bc=0 line=7 + bc=6 line=8 + bc=8 line=9 + bc=12 line=10 + bc=16 line=13 + bc=18 line=14 + bc=24 line=15 + bc=29 line=16 + bc=34 line=17 + bc=38 line=18 + bc=44 line=19 + bc=47 line=20 + bc=50 line=23 + bc=52 line=24 + bc=54 line=25 + bc=59 line=26 + bc=62 line=27 + bc=65 line=28 + bc=82 line=29 + bc=95 line=32 + bc=100 line=33 + bc=105 line=36 + bc=110 line=37 + bc=115 line=38 + bc=124 line=41 + bc=132 line=44 + bc=138 line=45 + bc=143 line=48 + bc=150 line=49 + bc=160 line=52 + bc=162 line=55 + bc=162 line=56 + bc=165 line=57 + bc=167 line=60 + bc=177 line=61 + bc=186 line=62 + bc=195 line=65 + bc=199 line=66 + bc=204 line=67 + bc=212 line=68 + bc=219 line=71 + bc=225 line=72 + bc=232 line=73 + bc=242 line=74 + bc=250 line=77 + bc=254 line=78 + bc=260 line=80 + bc=263 line=81 + bc=266 line=82 + bc=273 line=83 + bc=276 line=84 + bc=283 line=85 + bc=289 line=88 + bc=296 line=89 + bc=301 line=92 + bc=307 line=93 + bc=310 line=94 ######## - bc=\\d\+ line=127 + bc=321 line=96 + bc=329 line=98 + bc=332 line=99 + bc=335 line=100 + bc=338 line=101 +######## + bc=346 line=103 + bc=354 line=106 + bc=359 line=107 + bc=365 line=110 + bc=368 line=111 + bc=374 line=114 + bc=374 line=117 + bc=379 line=118 + bc=391 line=121 + bc=391 line=122 + bc=392 line=123 + bc=394 line=126 + bc=396 line=127 00 LOAD_CONST_NONE 01 LOAD_CONST_FALSE 02 BINARY_OP 27 __add__ @@ -80,247 +173,251 @@ Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+): 42 STORE_MAP 43 STORE_FAST 3 44 LOAD_CONST_STRING 'a' -47 STORE_FAST 4 -48 LOAD_CONST_OBJ \.\+ -\\d\+ STORE_FAST 5 -\\d\+ LOAD_CONST_SMALL_INT 1 -\\d\+ STORE_FAST 6 -\\d\+ LOAD_CONST_SMALL_INT 2 -\\d\+ STORE_FAST 7 -\\d\+ LOAD_FAST 0 -\\d\+ LOAD_DEREF 14 -58 BINARY_OP 27 __add__ -\\d\+ STORE_FAST 8 -\\d\+ LOAD_FAST 0 -\\d\+ UNARY_OP 1 __neg__ -\\d\+ STORE_FAST 9 -\\d\+ LOAD_FAST 0 -\\d\+ UNARY_OP 3 -\\d\+ STORE_FAST 10 -\\d\+ LOAD_FAST 0 -\\d\+ LOAD_DEREF 14 -\\d\+ DUP_TOP -\\d\+ ROT_THREE -\\d\+ BINARY_OP 2 __eq__ -\\d\+ JUMP_IF_FALSE_OR_POP \\d\+ -\\d\+ LOAD_FAST 1 -\\d\+ BINARY_OP 2 __eq__ -\\d\+ JUMP \\d\+ -\\d\+ ROT_TWO -\\d\+ POP_TOP -\\d\+ STORE_FAST 10 -\\d\+ LOAD_FAST 0 -\\d\+ LOAD_DEREF 14 -\\d\+ BINARY_OP 2 __eq__ -\\d\+ JUMP_IF_FALSE_OR_POP \\d\+ -\\d\+ LOAD_DEREF 14 -\\d\+ LOAD_FAST 1 -\\d\+ BINARY_OP 2 __eq__ -\\d\+ UNARY_OP 3 -\\d\+ STORE_FAST 10 -\\d\+ LOAD_DEREF 14 -\\d\+ LOAD_ATTR c -\\d\+ STORE_FAST 11 -\\d\+ LOAD_FAST 11 -\\d\+ LOAD_DEREF 14 -\\d\+ STORE_ATTR c -\\d\+ LOAD_DEREF 14 -\\d\+ LOAD_CONST_SMALL_INT 0 -\\d\+ LOAD_SUBSCR -\\d\+ STORE_FAST 12 -\\d\+ LOAD_FAST 12 -\\d\+ LOAD_DEREF 14 -\\d\+ LOAD_CONST_SMALL_INT 0 -\\d\+ STORE_SUBSCR -\\d\+ LOAD_DEREF 14 -\\d\+ LOAD_CONST_SMALL_INT 0 -\\d\+ DUP_TOP_TWO -\\d\+ LOAD_SUBSCR -\\d\+ LOAD_FAST 12 -\\d\+ BINARY_OP 14 __iadd__ -\\d\+ ROT_THREE -\\d\+ STORE_SUBSCR -\\d\+ LOAD_DEREF 14 -\\d\+ LOAD_CONST_NONE -\\d\+ LOAD_CONST_NONE -\\d\+ BUILD_SLICE 2 -\\d\+ LOAD_SUBSCR -\\d\+ STORE_FAST 0 -\\d\+ LOAD_FAST 1 -\\d\+ UNPACK_SEQUENCE 2 -\\d\+ STORE_FAST 0 -\\d\+ STORE_DEREF 14 -\\d\+ LOAD_FAST 0 -\\d\+ UNPACK_EX 1 -\\d\+ STORE_FAST 0 -\\d\+ STORE_FAST 0 -\\d\+ LOAD_DEREF 14 -\\d\+ LOAD_FAST 0 -\\d\+ ROT_TWO -\\d\+ STORE_FAST 0 -\\d\+ STORE_DEREF 14 -\\d\+ LOAD_FAST 1 -\\d\+ LOAD_DEREF 14 -\\d\+ LOAD_FAST 0 -\\d\+ ROT_THREE -\\d\+ ROT_TWO -\\d\+ STORE_FAST 0 -\\d\+ STORE_DEREF 14 -\\d\+ STORE_FAST 1 -\\d\+ DELETE_FAST 0 -\\d\+ LOAD_FAST 0 -\\d\+ STORE_GLOBAL gl -\\d\+ DELETE_GLOBAL gl -\\d\+ LOAD_FAST 14 -\\d\+ LOAD_FAST 15 -\\d\+ MAKE_CLOSURE \.\+ 2 -\\d\+ LOAD_FAST 2 -\\d\+ GET_ITER -\\d\+ CALL_FUNCTION n=1 nkw=0 -\\d\+ STORE_FAST 0 -\\d\+ LOAD_FAST 14 -\\d\+ LOAD_FAST 15 -\\d\+ MAKE_CLOSURE \.\+ 2 -\\d\+ LOAD_FAST 2 -\\d\+ CALL_FUNCTION n=1 nkw=0 -\\d\+ STORE_FAST 0 -\\d\+ LOAD_FAST 14 -\\d\+ LOAD_FAST 15 -\\d\+ MAKE_CLOSURE \.\+ 2 -\\d\+ LOAD_FAST 2 -\\d\+ CALL_FUNCTION n=1 nkw=0 -\\d\+ STORE_FAST 0 -\\d\+ LOAD_FAST 0 -\\d\+ CALL_FUNCTION n=0 nkw=0 -\\d\+ POP_TOP -\\d\+ LOAD_FAST 0 -\\d\+ LOAD_CONST_SMALL_INT 1 -\\d\+ CALL_FUNCTION n=1 nkw=0 -\\d\+ POP_TOP -\\d\+ LOAD_FAST 0 -\\d\+ LOAD_CONST_STRING 'b' -\\d\+ LOAD_CONST_SMALL_INT 1 -\\d\+ CALL_FUNCTION n=0 nkw=1 -\\d\+ POP_TOP -\\d\+ LOAD_FAST 0 -\\d\+ LOAD_DEREF 14 -\\d\+ LOAD_NULL -\\d\+ CALL_FUNCTION_VAR_KW n=0 nkw=0 -\\d\+ POP_TOP -\\d\+ LOAD_FAST 0 -\\d\+ LOAD_METHOD b -\\d\+ CALL_METHOD n=0 nkw=0 -\\d\+ POP_TOP -\\d\+ LOAD_FAST 0 -\\d\+ LOAD_METHOD b -\\d\+ LOAD_CONST_SMALL_INT 1 -\\d\+ CALL_METHOD n=1 nkw=0 -\\d\+ POP_TOP -\\d\+ LOAD_FAST 0 -\\d\+ LOAD_METHOD b -\\d\+ LOAD_CONST_STRING 'c' -\\d\+ LOAD_CONST_SMALL_INT 1 -\\d\+ CALL_METHOD n=0 nkw=1 -\\d\+ POP_TOP -\\d\+ LOAD_FAST 0 -\\d\+ LOAD_METHOD b -\\d\+ LOAD_FAST 1 -\\d\+ LOAD_NULL -\\d\+ CALL_METHOD_VAR_KW n=0 nkw=0 -\\d\+ POP_TOP -\\d\+ LOAD_FAST 0 -\\d\+ POP_JUMP_IF_FALSE \\d\+ -\\d\+ LOAD_DEREF 16 -\\d\+ POP_TOP -\\d\+ JUMP \\d\+ -\\d\+ LOAD_GLOBAL y -\\d\+ POP_TOP -\\d\+ JUMP \\d\+ -\\d\+ LOAD_DEREF 14 -\\d\+ POP_TOP -\\d\+ LOAD_FAST 0 -\\d\+ POP_JUMP_IF_TRUE \\d\+ -\\d\+ JUMP \\d\+ -\\d\+ LOAD_DEREF 14 -\\d\+ POP_TOP -\\d\+ LOAD_FAST 0 -\\d\+ POP_JUMP_IF_FALSE \\d\+ -\\d\+ LOAD_FAST 0 -\\d\+ JUMP_IF_TRUE_OR_POP \\d\+ -\\d\+ LOAD_FAST 0 -\\d\+ STORE_FAST 0 -\\d\+ LOAD_DEREF 14 -\\d\+ GET_ITER_STACK -\\d\+ FOR_ITER \\d\+ -\\d\+ STORE_FAST 0 -\\d\+ LOAD_FAST 1 -\\d\+ POP_TOP -\\d\+ JUMP \\d\+ -\\d\+ SETUP_FINALLY \\d\+ -\\d\+ SETUP_EXCEPT \\d\+ -\\d\+ JUMP \\d\+ -\\d\+ JUMP \\d\+ -\\d\+ LOAD_FAST 0 -\\d\+ POP_JUMP_IF_TRUE \\d\+ -\\d\+ POP_EXCEPT_JUMP \\d\+ -\\d\+ POP_TOP -\\d\+ LOAD_DEREF 14 -\\d\+ POP_TOP -\\d\+ POP_EXCEPT_JUMP \\d\+ -\\d\+ END_FINALLY -\\d\+ LOAD_CONST_NONE -\\d\+ LOAD_FAST 1 -\\d\+ POP_TOP -\\d\+ END_FINALLY -\\d\+ JUMP \\d\+ -\\d\+ SETUP_EXCEPT \\d\+ -\\d\+ UNWIND_JUMP \\d\+ 1 -\\d\+ POP_EXCEPT_JUMP \\d\+ -\\d\+ POP_TOP -\\d\+ POP_EXCEPT_JUMP \\d\+ -\\d\+ END_FINALLY -\\d\+ LOAD_FAST 0 -\\d\+ POP_JUMP_IF_TRUE \\d\+ -\\d\+ LOAD_FAST 0 -\\d\+ SETUP_WITH \\d\+ -\\d\+ POP_TOP -\\d\+ LOAD_DEREF 14 -\\d\+ POP_TOP -\\d\+ LOAD_CONST_NONE -\\d\+ WITH_CLEANUP -\\d\+ END_FINALLY -\\d\+ LOAD_CONST_SMALL_INT 1 -\\d\+ STORE_DEREF 16 -\\d\+ LOAD_FAST_N 16 -\\d\+ MAKE_CLOSURE \.\+ 1 -\\d\+ STORE_FAST 13 -\\d\+ LOAD_CONST_SMALL_INT 0 -\\d\+ LOAD_CONST_NONE -\\d\+ IMPORT_NAME 'a' -\\d\+ STORE_FAST 0 -\\d\+ LOAD_CONST_SMALL_INT 0 -\\d\+ LOAD_CONST_STRING 'b' -\\d\+ BUILD_TUPLE 1 -\\d\+ IMPORT_NAME 'a' -\\d\+ IMPORT_FROM 'b' -\\d\+ STORE_DEREF 14 -\\d\+ POP_TOP -\\d\+ RAISE_LAST -\\d\+ LOAD_CONST_SMALL_INT 1 -\\d\+ RAISE_OBJ -\\d\+ LOAD_CONST_NONE -\\d\+ RETURN_VALUE -\\d\+ LOAD_CONST_SMALL_INT 1 -\\d\+ RETURN_VALUE -File cmdline/cmd_showbc.py, code block 'f' (descriptor: \.\+, bytecode @\.\+ bytes) -Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+): -######## -\.\+rg names: +46 STORE_FAST 4 +47 LOAD_CONST_OBJ \.\+=b'a' +49 STORE_FAST 5 +50 LOAD_CONST_SMALL_INT 1 +51 STORE_FAST 6 +52 LOAD_CONST_SMALL_INT 2 +53 STORE_FAST 7 +54 LOAD_FAST 0 +55 LOAD_DEREF 14 +57 BINARY_OP 27 __add__ +58 STORE_FAST 8 +59 LOAD_FAST 0 +60 UNARY_OP 1 __neg__ +61 STORE_FAST 9 +62 LOAD_FAST 0 +63 UNARY_OP 3 +64 STORE_FAST 10 +65 LOAD_FAST 0 +66 LOAD_DEREF 14 +68 DUP_TOP +69 ROT_THREE +70 BINARY_OP 2 __eq__ +71 JUMP_IF_FALSE_OR_POP 79 +74 LOAD_FAST 1 +75 BINARY_OP 2 __eq__ +76 JUMP 81 +79 ROT_TWO +80 POP_TOP +81 STORE_FAST 10 +82 LOAD_FAST 0 +83 LOAD_DEREF 14 +85 BINARY_OP 2 __eq__ +86 JUMP_IF_FALSE_OR_POP 93 +89 LOAD_DEREF 14 +91 LOAD_FAST 1 +92 BINARY_OP 2 __eq__ +93 UNARY_OP 3 +94 STORE_FAST 10 +95 LOAD_DEREF 14 +97 LOAD_ATTR c +99 STORE_FAST 11 +100 LOAD_FAST 11 +101 LOAD_DEREF 14 +103 STORE_ATTR c +105 LOAD_DEREF 14 +107 LOAD_CONST_SMALL_INT 0 +108 LOAD_SUBSCR +109 STORE_FAST 12 +110 LOAD_FAST 12 +111 LOAD_DEREF 14 +113 LOAD_CONST_SMALL_INT 0 +114 STORE_SUBSCR +115 LOAD_DEREF 14 +117 LOAD_CONST_SMALL_INT 0 +118 DUP_TOP_TWO +119 LOAD_SUBSCR +120 LOAD_FAST 12 +121 BINARY_OP 14 __iadd__ +122 ROT_THREE +123 STORE_SUBSCR +124 LOAD_DEREF 14 +126 LOAD_CONST_NONE +127 LOAD_CONST_NONE +128 BUILD_SLICE 2 +130 LOAD_SUBSCR +131 STORE_FAST 0 +132 LOAD_FAST 1 +133 UNPACK_SEQUENCE 2 +135 STORE_FAST 0 +136 STORE_DEREF 14 +138 LOAD_FAST 0 +139 UNPACK_EX 1 +141 STORE_FAST 0 +142 STORE_FAST 0 +143 LOAD_DEREF 14 +145 LOAD_FAST 0 +146 ROT_TWO +147 STORE_FAST 0 +148 STORE_DEREF 14 +150 LOAD_FAST 1 +151 LOAD_DEREF 14 +153 LOAD_FAST 0 +154 ROT_THREE +155 ROT_TWO +156 STORE_FAST 0 +157 STORE_DEREF 14 +159 STORE_FAST 1 +160 DELETE_FAST 0 +162 LOAD_FAST 0 +163 STORE_GLOBAL gl +165 DELETE_GLOBAL gl +167 LOAD_FAST 14 +168 LOAD_FAST 15 +169 MAKE_CLOSURE \.\+ 2 +172 LOAD_FAST 2 +173 GET_ITER +174 CALL_FUNCTION n=1 nkw=0 +176 STORE_FAST 0 +177 LOAD_FAST 14 +178 LOAD_FAST 15 +179 MAKE_CLOSURE \.\+ 2 +182 LOAD_FAST 2 +183 CALL_FUNCTION n=1 nkw=0 +185 STORE_FAST 0 +186 LOAD_FAST 14 +187 LOAD_FAST 15 +188 MAKE_CLOSURE \.\+ 2 +191 LOAD_FAST 2 +192 CALL_FUNCTION n=1 nkw=0 +194 STORE_FAST 0 +195 LOAD_FAST 0 +196 CALL_FUNCTION n=0 nkw=0 +198 POP_TOP +199 LOAD_FAST 0 +200 LOAD_CONST_SMALL_INT 1 +201 CALL_FUNCTION n=1 nkw=0 +203 POP_TOP +204 LOAD_FAST 0 +205 LOAD_CONST_STRING 'b' +207 LOAD_CONST_SMALL_INT 1 +208 CALL_FUNCTION n=0 nkw=1 +211 POP_TOP +212 LOAD_FAST 0 +213 LOAD_DEREF 14 +215 LOAD_NULL +216 CALL_FUNCTION_VAR_KW n=0 nkw=0 +218 POP_TOP +219 LOAD_FAST 0 +220 LOAD_METHOD b +222 CALL_METHOD n=0 nkw=0 +224 POP_TOP +225 LOAD_FAST 0 +226 LOAD_METHOD b +228 LOAD_CONST_SMALL_INT 1 +229 CALL_METHOD n=1 nkw=0 +231 POP_TOP +232 LOAD_FAST 0 +233 LOAD_METHOD b +235 LOAD_CONST_STRING 'c' +237 LOAD_CONST_SMALL_INT 1 +238 CALL_METHOD n=0 nkw=1 +241 POP_TOP +242 LOAD_FAST 0 +243 LOAD_METHOD b +245 LOAD_FAST 1 +246 LOAD_NULL +247 CALL_METHOD_VAR_KW n=0 nkw=0 +249 POP_TOP +250 LOAD_FAST 0 +251 POP_JUMP_IF_FALSE 260 +254 LOAD_DEREF 16 +256 POP_TOP +257 JUMP 263 +260 LOAD_GLOBAL y +262 POP_TOP +263 JUMP 269 +266 LOAD_DEREF 14 +268 POP_TOP +269 LOAD_FAST 0 +270 POP_JUMP_IF_TRUE 266 +273 JUMP 279 +276 LOAD_DEREF 14 +278 POP_TOP +279 LOAD_FAST 0 +280 POP_JUMP_IF_FALSE 276 +283 LOAD_FAST 0 +284 JUMP_IF_TRUE_OR_POP 288 +287 LOAD_FAST 0 +288 STORE_FAST 0 +289 LOAD_DEREF 14 +291 GET_ITER_STACK +292 FOR_ITER 301 +295 STORE_FAST 0 +296 LOAD_FAST 1 +297 POP_TOP +298 JUMP 292 +301 SETUP_FINALLY 329 +304 SETUP_EXCEPT 320 +307 JUMP 313 +310 JUMP 317 +313 LOAD_FAST 0 +314 POP_JUMP_IF_TRUE 310 +317 POP_EXCEPT_JUMP 328 +320 POP_TOP +321 LOAD_DEREF 14 +323 POP_TOP +324 POP_EXCEPT_JUMP 328 +327 END_FINALLY +328 LOAD_CONST_NONE +329 LOAD_FAST 1 +330 POP_TOP +331 END_FINALLY +332 JUMP 350 +335 SETUP_EXCEPT 345 +338 UNWIND_JUMP 354 1 +342 POP_EXCEPT_JUMP 350 +345 POP_TOP +346 POP_EXCEPT_JUMP 350 +349 END_FINALLY +350 LOAD_FAST 0 +351 POP_JUMP_IF_TRUE 335 +354 LOAD_FAST 0 +355 SETUP_WITH 363 +358 POP_TOP +359 LOAD_DEREF 14 +361 POP_TOP +362 LOAD_CONST_NONE +363 WITH_CLEANUP +364 END_FINALLY +365 LOAD_CONST_SMALL_INT 1 +366 STORE_DEREF 16 +368 LOAD_FAST_N 16 +370 MAKE_CLOSURE \.\+ 1 +373 STORE_FAST 13 +374 LOAD_CONST_SMALL_INT 0 +375 LOAD_CONST_NONE +376 IMPORT_NAME 'a' +378 STORE_FAST 0 +379 LOAD_CONST_SMALL_INT 0 +380 LOAD_CONST_STRING 'b' +382 BUILD_TUPLE 1 +384 IMPORT_NAME 'a' +386 IMPORT_FROM 'b' +388 STORE_DEREF 14 +390 POP_TOP +391 RAISE_LAST +392 LOAD_CONST_SMALL_INT 1 +393 RAISE_OBJ +394 LOAD_CONST_NONE +395 RETURN_VALUE +396 LOAD_CONST_SMALL_INT 1 +397 RETURN_VALUE +File cmdline/cmd_showbc.py, code block 'f' (descriptor: \.\+, bytecode @\.\+ 59 bytes) +Raw bytecode (code_info_size=8, bytecode_size=51): + a8 10 0a 02 80 82 34 38 81 57 c0 57 c1 57 c2 57 + c3 57 c4 57 c5 57 c6 57 c7 57 c8 c9 82 57 ca 57 + cb 57 cc 57 cd 57 ce 57 cf 57 26 10 57 26 11 57 + 26 12 26 13 b9 24 13 f2 59 51 63 +arg names: (N_STATE 22) (N_EXC_STACK 0) bc=0 line=1 -######## - bc=\\d\+ line=133 + bc=0 line=131 + bc=20 line=132 + bc=44 line=133 00 LOAD_CONST_SMALL_INT 1 01 DUP_TOP 02 STORE_FAST 0 @@ -367,28 +464,29 @@ Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+): 48 POP_TOP 49 LOAD_CONST_NONE 50 RETURN_VALUE -File cmdline/cmd_showbc.py, code block 'f' (descriptor: \.\+, bytecode @\.\+ bytes) -Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+): -######## -\.\+63 +File cmdline/cmd_showbc.py, code block 'f' (descriptor: \.\+, bytecode @\.\+ 20 bytes) +Raw bytecode (code_info_size=9, bytecode_size=11): + a1 01 0b 02 06 80 88 40 00 82 2a 01 53 b0 21 00 + 01 c1 51 63 arg names: a (N_STATE 5) (N_EXC_STACK 0) (INIT_CELL 0) -######## - bc=\\d\+ line=139 + bc=0 line=1 + bc=0 line=137 + bc=0 line=139 00 LOAD_CONST_SMALL_INT 2 01 BUILD_TUPLE 1 03 LOAD_NULL 04 LOAD_FAST 0 05 MAKE_CLOSURE_DEFARGS \.\+ 1 -\\d\+ STORE_FAST 1 -\\d\+ LOAD_CONST_NONE -\\d\+ RETURN_VALUE -File cmdline/cmd_showbc.py, code block 'f' (descriptor: \.\+, bytecode @\.\+ bytes) -Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+): -######## -\.\+63 +08 STORE_FAST 1 +09 LOAD_CONST_NONE +10 RETURN_VALUE +File cmdline/cmd_showbc.py, code block 'f' (descriptor: \.\+, bytecode @\.\+ 21 bytes) +Raw bytecode (code_info_size=8, bytecode_size=13): + 88 40 0a 02 80 8f 23 23 51 67 59 81 67 59 81 5e + 51 68 59 51 63 arg names: (N_STATE 2) (N_EXC_STACK 0) @@ -409,49 +507,47 @@ arg names: 10 POP_TOP 11 LOAD_CONST_NONE 12 RETURN_VALUE -File cmdline/cmd_showbc.py, code block 'Class' (descriptor: \.\+, bytecode @\.\+ bytes) -Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+): -######## -\.\+63 +File cmdline/cmd_showbc.py, code block 'Class' (descriptor: \.\+, bytecode @\.\+ 1\[56\] bytes) +Raw bytecode (code_info_size=\[56\], bytecode_size=10): + 00 \.\+ 11 0c 16 0d 10 03 16 0e 51 63 arg names: (N_STATE 1) (N_EXC_STACK 0) bc=0 line=1 ######## - bc=12 line=150 + bc=8 line=150 00 LOAD_NAME __name__ -03 STORE_NAME __module__ -06 LOAD_CONST_STRING 'Class' -09 STORE_NAME __qualname__ -12 LOAD_CONST_NONE -13 RETURN_VALUE -File cmdline/cmd_showbc.py, code block 'f' (descriptor: \.\+, bytecode @\.\+ bytes) -Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+): -######## -\.\+63 +02 STORE_NAME __module__ +04 LOAD_CONST_STRING 'Class' +06 STORE_NAME __qualname__ +08 LOAD_CONST_NONE +09 RETURN_VALUE +File cmdline/cmd_showbc.py, code block 'f' (descriptor: \.\+, bytecode @\.\+ 18 bytes) +Raw bytecode (code_info_size=6, bytecode_size=12): + 19 08 02 0f 80 9c 12 10 12 11 b0 15 02 36 00 59 + 51 63 arg names: self (N_STATE 4) (N_EXC_STACK 0) bc=0 line=1 bc=0 line=157 00 LOAD_GLOBAL super -\\d\+ LOAD_GLOBAL __class__ -\\d\+ LOAD_FAST 0 -\\d\+ LOAD_SUPER_METHOD f -\\d\+ CALL_METHOD n=0 nkw=0 -\\d\+ POP_TOP -\\d\+ LOAD_CONST_NONE -\\d\+ RETURN_VALUE -File cmdline/cmd_showbc.py, code block '' (descriptor: \.\+, bytecode @\.\+ bytes) -Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+): -######## -\.\+63 +02 LOAD_GLOBAL __class__ +04 LOAD_FAST 0 +05 LOAD_SUPER_METHOD f +07 CALL_METHOD n=0 nkw=0 +09 POP_TOP +10 LOAD_CONST_NONE +11 RETURN_VALUE +File cmdline/cmd_showbc.py, code block '' (descriptor: \.\+, bytecode @\.\+ 31 bytes) +Raw bytecode (code_info_size=9, bytecode_size=22): + c3 40 0c 12 04 04 04 80 3b 53 b2 53 53 4b 0d 00 + c3 25 01 44 f7 7f 25 00 67 59 42 f0 7f 51 63 arg names: * * * (N_STATE 9) (N_EXC_STACK 0) bc=0 line=1 bc=0 line=60 -######## 00 LOAD_NULL 01 LOAD_FAST 2 02 LOAD_NULL @@ -466,16 +562,15 @@ arg names: * * * 17 JUMP 4 20 LOAD_CONST_NONE 21 RETURN_VALUE -File cmdline/cmd_showbc.py, code block '' (descriptor: \.\+, bytecode @\.\+ bytes) -Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+): -######## -\.\+63 +File cmdline/cmd_showbc.py, code block '' (descriptor: \.\+, bytecode @\.\+ 29 bytes) +Raw bytecode (code_info_size=8, bytecode_size=21): + 4b 0c 14 04 04 04 80 3c 2b 00 b2 5f 4b 0d 00 c3 + 25 01 44 f7 7f 25 00 2f 14 42 f0 7f 63 arg names: * * * (N_STATE 10) (N_EXC_STACK 0) bc=0 line=1 bc=0 line=61 -######## 00 BUILD_LIST 0 02 LOAD_FAST 2 03 GET_ITER_STACK @@ -487,15 +582,15 @@ arg names: * * * 15 STORE_COMP 20 17 JUMP 4 20 RETURN_VALUE -File cmdline/cmd_showbc.py, code block '' (descriptor: \.\+, bytecode @\.\+ bytes) -Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+): -######## -\.\+63 +File cmdline/cmd_showbc.py, code block '' (descriptor: \.\+, bytecode @\.\+ 31 bytes) +Raw bytecode (code_info_size=8, bytecode_size=23): + 53 0c 15 04 04 04 80 3d 2c 00 b2 5f 4b 0f 00 c3 + 25 01 44 f7 7f 25 00 25 00 2f 19 42 ee 7f 63 arg names: * * * (N_STATE 11) (N_EXC_STACK 0) bc=0 line=1 -######## + bc=0 line=62 00 BUILD_MAP 0 02 LOAD_FAST 2 03 GET_ITER_STACK @@ -508,16 +603,17 @@ arg names: * * * 17 STORE_COMP 25 19 JUMP 4 22 RETURN_VALUE -File cmdline/cmd_showbc.py, code block 'closure' (descriptor: \.\+, bytecode @\.\+ bytes) -Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+): -######## -\.\+63 +File cmdline/cmd_showbc.py, code block 'closure' (descriptor: \.\+, bytecode @\.\+ 20 bytes) +Raw bytecode (code_info_size=8, bytecode_size=12): + 19 0c 16 04 80 6f 25 23 25 00 81 f2 c1 81 27 00 + 29 00 51 63 arg names: * (N_STATE 4) (N_EXC_STACK 0) bc=0 line=1 -######## - bc=\\d\+ line=114 + bc=0 line=112 + bc=5 line=113 + bc=8 line=114 00 LOAD_DEREF 0 02 LOAD_CONST_SMALL_INT 1 03 BINARY_OP 27 __add__ @@ -527,16 +623,14 @@ arg names: * 08 DELETE_DEREF 0 10 LOAD_CONST_NONE 11 RETURN_VALUE -File cmdline/cmd_showbc.py, code block 'f' (descriptor: \.\+, bytecode @\.\+ bytes) -Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+): -######## -\.\+63 +File cmdline/cmd_showbc.py, code block 'f' (descriptor: \.\+, bytecode @\.\+ 13 bytes) +Raw bytecode (code_info_size=8, bytecode_size=5): + 9a 01 0a 02 04 09 80 8b b1 25 00 f2 63 arg names: * b (N_STATE 4) (N_EXC_STACK 0) bc=0 line=1 -######## - bc=\\d\+ line=140 + bc=0 line=140 00 LOAD_FAST 1 01 LOAD_DEREF 0 03 BINARY_OP 27 __add__ diff --git a/tests/cmdline/cmd_verbose.py.exp b/tests/cmdline/cmd_verbose.py.exp index 0edd050c22..ae833dbec8 100644 --- a/tests/cmdline/cmd_verbose.py.exp +++ b/tests/cmdline/cmd_verbose.py.exp @@ -1,19 +1,17 @@ -File cmdline/cmd_verbose.py, code block '' (descriptor: \.\+, bytecode \.\+ bytes) -Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+): - 08 \.\+ -######## -\.\+63 +File cmdline/cmd_verbose.py, code block '' (descriptor: \.\+, bytecode @\.\+ 12 bytes) +Raw bytecode (code_info_size=4, bytecode_size=8): + 08 04 01 40 11 02 81 34 01 59 51 63 arg names: (N_STATE 2) (N_EXC_STACK 0) bc=0 line=1 bc=0 line=3 00 LOAD_NAME print -03 LOAD_CONST_SMALL_INT 1 -04 CALL_FUNCTION n=1 nkw=0 -06 POP_TOP -07 LOAD_CONST_NONE -08 RETURN_VALUE +02 LOAD_CONST_SMALL_INT 1 +03 CALL_FUNCTION n=1 nkw=0 +05 POP_TOP +06 LOAD_CONST_NONE +07 RETURN_VALUE 1 mem: total=\\d\+, current=\\d\+, peak=\\d\+ stack: \\d\+ out of \\d\+ diff --git a/tests/micropython/import_mpy_invalid.py b/tests/micropython/import_mpy_invalid.py index 02fd4b1254..b02312a7a8 100644 --- a/tests/micropython/import_mpy_invalid.py +++ b/tests/micropython/import_mpy_invalid.py @@ -49,7 +49,6 @@ user_files = { "/mod0.mpy": b"", # empty file "/mod1.mpy": b"M", # too short header "/mod2.mpy": b"M\x00\x00\x00", # bad version - "/mod3.mpy": b"M\x00\x00\x00\x7f", # qstr window too large } # create and mount a user filesystem diff --git a/tests/micropython/import_mpy_invalid.py.exp b/tests/micropython/import_mpy_invalid.py.exp index ea748ff6c1..1727ea1cea 100644 --- a/tests/micropython/import_mpy_invalid.py.exp +++ b/tests/micropython/import_mpy_invalid.py.exp @@ -1,4 +1,3 @@ mod0 ValueError incompatible .mpy file mod1 ValueError incompatible .mpy file mod2 ValueError incompatible .mpy file -mod3 ValueError incompatible .mpy file diff --git a/tests/micropython/import_mpy_native_gc.py b/tests/micropython/import_mpy_native_gc.py index f982a4ee17..6f37b15ab7 100644 --- a/tests/micropython/import_mpy_native_gc.py +++ b/tests/micropython/import_mpy_native_gc.py @@ -49,14 +49,14 @@ class UserFS: # by the required value of sys.implementation.mpy. features0_file_contents = { # -march=x64 - 0xA05: b'M\x05\x0a\x1f \x84b\xe9/\x00\x00\x00SH\x8b\x1ds\x00\x00\x00\xbe\x02\x00\x00\x00\xffS\x18\xbf\x01\x00\x00\x00H\x85\xc0u\x0cH\x8bC \xbe\x02\x00\x00\x00[\xff\xe0H\x0f\xaf\xf8H\xff\xc8\xeb\xe6ATUSH\x8b\x1dA\x00\x00\x00H\x8b\x7f\x08L\x8bc(A\xff\xd4H\x8d5\x1f\x00\x00\x00H\x89\xc5H\x8b\x05-\x00\x00\x00\x0f\xb78\xffShH\x89\xefA\xff\xd4H\x8b\x03[]A\\\xc3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x90\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x84@\x12factorial\x10\x00\x00\r \x01"\x9f\x1c\x01\x1e\xff', + 0xA06: b'M\x06\n\x1f\x01\x004build/features0.native.mpy\x00\x8aB\xe9/\x00\x00\x00SH\x8b\x1d\x83\x00\x00\x00\xbe\x02\x00\x00\x00\xffS\x18\xbf\x01\x00\x00\x00H\x85\xc0u\x0cH\x8bC \xbe\x02\x00\x00\x00[\xff\xe0H\x0f\xaf\xf8H\xff\xc8\xeb\xe6ATUSH\x8b\x1dQ\x00\x00\x00H\x8bG\x08L\x8bc(H\x8bx\x08A\xff\xd4H\x8d5+\x00\x00\x00H\x89\xc5H\x8b\x059\x00\x00\x00\x0f\xb78\xffShH\x89\xefA\xff\xd4H\x8b\x03[]A\\\xc3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x85\x00\x12factorial\x00\x10\r$\x01&\x9f \x01"\xff', # -march=armv7m - 0x1605: b"M\x05\x16\x1f \x84\x12\x1a\xe0\x00\x00\x13\xb5\nK\nJ{D\x9cX\x02!\xe3h\x98G\x03F\x01 3\xb9\x02!#i\x01\x93\x02\xb0\xbd\xe8\x10@\x18GXC\x01;\xf4\xe7\x00\xbfj\x00\x00\x00\x00\x00\x00\x00\xf8\xb5\tN\tK~D\xf4X@hgi\xb8G\x05F\x07K\x07I\xf2XyD\x10\x88ck\x98G(F\xb8G h\xf8\xbd6\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x1c\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00\x01\x84\x00\x12factorial\x10\x00\x00\r<\x01>\x9f8\x01:\xff", + 0x1606: b"M\x06\x16\x1f\x01\x004build/features0.native.mpy\x00\x88B\x1a\xe0\x00\x00\x13\xb5\nK\nJ{D\x9cX\x02!\xe3h\x98G\x03F\x01 3\xb9\x02!#i\x01\x93\x02\xb0\xbd\xe8\x10@\x18GXC\x01;\xf4\xe7\x00\xbfn\x00\x00\x00\x00\x00\x00\x00\xf8\xb5\nN\nK~D\xf4XChgiXh\xb8G\x05F\x07K\x08I\xf2XyD\x10\x88ck\x98G(F\xb8G h\xf8\xbd\x00\xbf:\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x1e\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x84\x00\x00\x00\x00\x00\x00\x00\x01\x84\x10\x12factorial\x00\x10\r>\x01@\x9f:\x01<\xff", } # Populate other armv7m-derived archs based on armv7m. -for arch in (0x1A05, 0x1E05, 0x2205): - features0_file_contents[arch] = features0_file_contents[0x1605] +for arch in (0x1A06, 0x1E06, 0x2206): + features0_file_contents[arch] = features0_file_contents[0x1606] if sys.implementation.mpy not in features0_file_contents: print("SKIP") diff --git a/tests/micropython/import_mpy_native_x64.py b/tests/micropython/import_mpy_native_x64.py index ff0142a9cf..3a25df1866 100644 --- a/tests/micropython/import_mpy_native_x64.py +++ b/tests/micropython/import_mpy_native_x64.py @@ -52,49 +52,62 @@ class UserFS: # fmt: off user_files = { # bad architecture - '/mod0.mpy': b'M\x05\xfe\x00\x10', + '/mod0.mpy': b'M\x06\xfe\x00\x10', # test loading of viper and asm '/mod1.mpy': ( - b'M\x05\x0a\x1f\x20' # header + b'M\x06\x0a\x1f' # header - b'\x20' # n bytes, bytecode - b'\x00\x08\x02m\x02m' # prelude + b'\x02' # n_qstr + b'\x00' # n_obj + + b'\x0emod1.py\x00' # qstr0 = "mod1.py" + b'\x0aouter\x00' # qstr1 = "outer" + + b'\x2c' # 5 bytes, have children, bytecode + b'\x00\x02' # prelude + b'\x01' # simple name (qstr index) b'\x51' # LOAD_CONST_NONE b'\x63' # RETURN_VALUE - b'\x00\x02' # n_obj, n_raw_code + b'\x02' # 2 children - b'\x22' # n bytes, viper code - b'\x00\x00\x00\x00\x00\x00' # dummy machine code - b'\x00\x00' # qstr0 - b'\x01\x0c\x0aprint' # n_qstr, qstr0 - b'\x00\x00\x00' # scope_flags, n_obj, n_raw_code + b'\x42' # 8 bytes, no children, viper code + b'\x00\x00\x00\x00\x00\x00' # dummy machine code + b'\x00\x00' # slot for qstr0 + b'\x01\x0c\x0aprint\x00' # n_qstr=1, qstr0 + b'\x00' # scope_flags - b'\x23' # n bytes, asm code - b'\x00\x00\x00\x00\x00\x00\x00\x00' # dummy machine code - b'\x00\x00\x00' # scope_flags, n_pos_args, type_sig + b'\x43' # 8 bytes, no children, asm code + b'\x00\x00\x00\x00\x00\x00\x00\x00' # dummy machine code + b'\x00\x00\x00' # scope_flags, n_pos_args, type_sig ), # test loading viper with additional scope flags and relocation '/mod2.mpy': ( - b'M\x05\x0a\x1f\x20' # header + b'M\x06\x0a\x1f' # header - b'\x20' # n bytes, bytecode - b'\x00\x08\x02m\x02m' # prelude + b'\x02' # n_qstr + b'\x00' # n_obj + + b'\x0emod2.py\x00' # qstr0 = "mod2.py" + b'\x0aouter\x00' # qstr1 = "outer" + + b'\x2c' # 5 bytes, have children, bytecode + b'\x00\x02' # prelude + b'\x01' # simple name (qstr index) b'\x51' # LOAD_CONST_NONE b'\x63' # RETURN_VALUE - b'\x00\x01' # n_obj, n_raw_code + b'\x01' # 1 child - b'\x12' # n bytes(=4), viper code - b'\x00\x00\x00\x00' # dummy machine code - b'\x00' # n_qstr - b'\x70' # scope_flags: VIPERBSS | VIPERRODATA | VIPERRELOC - b'\x00\x00' # n_obj, n_raw_code - b'\x06rodata' # rodata, 6 bytes - b'\x04' # bss, 4 bytes - b'\x03\x01\x00' # dummy relocation of rodata + b'\x22' # 4 bytes, no children, viper code + b'\x00\x00\x00\x00' # dummy machine code + b'\x00' # n_qstr=0 + b'\x70' # scope_flags: VIPERBSS | VIPERRODATA | VIPERRELOC + b'\x06\x04' # rodata=6 bytes, bss=4 bytes + b'rodata' # rodata content + b'\x03\x01\x00' # dummy relocation of rodata ), } # fmt: on diff --git a/tests/perf_bench/core_import_mpy_multi.py b/tests/perf_bench/core_import_mpy_multi.py index 0da466206b..74caf20a38 100644 --- a/tests/perf_bench/core_import_mpy_multi.py +++ b/tests/perf_bench/core_import_mpy_multi.py @@ -23,7 +23,7 @@ def f(): x = ("const tuple", None, False, True, 1, 2, 3) result = 123 """ -file_data = b'M\x05\x02\x1f \x81\x0c\x10\x12\x00\x07\x0etest.py\x8b\x07e`\x00T2\x00\x10\x02A4\x02\x16\x012\x01\x16\x02f"\x80{\x16\x0cresultQc\x00\x02\x81\x1c\x00\x10\x05\x07,EE\x00\x11\x00\x17\x16\x00\x16\x10\x03\x16\x00\x1a2\x00\x16\x00\x112\x01\x16\x00\xa42\x02\x16\x00}Qc\x00\x03<\x1a\x0c\x00\x11\x03@\x00\xb1\xb0\x18\x06argQc\x00\x00\x00\x89\x01,\t\x0e\x00\xa4\x03` \x00Qc\x00\x00\x00\x89,\t\x0e\x00}\x01``\x00Qc\x00\x00\x00\x89\x81l8\x16\t\x03\x80\x08/,##\x00\x12\x00{\x12\x00\x97\x12\x00B\x12\x00K*\x04Y\x12\x00$\x12\x007\x12\x006*\x03Y#\x00\xc0#\x01\xc0#\x02QPR\x81\x82\x83*\x07\xc0Qc\x03\x00s\x1cthis will be a string objectb\x1bthis will be a bytes objects\x0bconst tuple' +file_data = b'M\x06\x02\x1f\x14\x03\x0etest.py\x00\x0f\x02A\x00\x02f\x00\x0cresult\x00/-5#\x82I\x81{\x81w\x82/\x81\x05\x81\x17Iom\x82\x13\x06arg\x00s\x1cthis will be a string object\x00b\x1bthis will be a bytes object\x00s\x0bconst tuple\x00\x81\\\x10\n\x01\x89\x07d`T2\x00\x10\x024\x02\x16\x022\x01\x16\x03"\x80{\x16\x04Qc\x02\x81d\x00\x08\x02(DD\x11\x05\x16\x06\x10\x02\x16\x072\x00\x16\x082\x01\x16\t2\x02\x16\nQc\x03`\x1a\x08\x08\x12\x13@\xb1\xb0\x18\x13Qc@\t\x08\t\x12` Qc@\t\x08\n\x12``Qc\x83\x008\x0e\x03\x80\x08+)##\x12\x0b\x12\x0c\x12\r\x12\x0e*\x04Y\x12\x0f\x12\x10\x12\x11*\x03Y#\x00\xc0#\x01\xc0#\x02QPR\x81\x82\x83*\x07\xc0Qc' class File(uio.IOBase): diff --git a/tests/perf_bench/core_import_mpy_single.py b/tests/perf_bench/core_import_mpy_single.py index 5ca3584959..1e8a4f531e 100644 --- a/tests/perf_bench/core_import_mpy_single.py +++ b/tests/perf_bench/core_import_mpy_single.py @@ -78,7 +78,7 @@ def f1(): x = ("const tuple 9", None, False, True, 1, 2, 3) result = 123 """ -file_data = b'M\x05\x02\x1f \x81\\\x10\x1a\x00\x07\x0etest.pyk@k@\x85\x18\x85\x1f\x00T2\x00\x10\x04A04\x02\x16\x01T2\x01\x10\x04A14\x02\x16\x012\x02\x16\x04f02\x03\x16\x04f1"\x80{\x16\x0cresultQc\x00\x04\x814\x00\x12\t\x0b,%%%\x00\x11\x00\x17\x16\x00\x16\x10\x03\x16\x00\x1a2\x00\x16\x04a02\x01\x16\x04a12\x02\x16\x04a22\x03\x16\x04a3Qc\x00\x04(\t\x0c\x07\x0b \x00Qc\x00\x00\x00\x89(\t\x0c\t\x03@\x00Qc\x00\x00\x00\x89(\t\x0c\t\x03`\x00Qc\x00\x00\x00\x89,\t\x0e\t\x03` \x00Qc\x00\x00\x00\x89\x818\x00\x14\x13\x03l`%%%\x00\x11\x00\x17\x16\x00\x16\x10\x03\x16\x00\x1a2\x00\x16\x0b2\x01\x16\x0b2\x02\x16\x0b2\x03\x16\x0bQc\x00\x04,\t\x0e\x07\x0b``\x00Qc\x00\x00\x00\x89,\t\x0e\t\x03\x80\x07\x00Qc\x00\x00\x00\x89,\t\x0e\t\x03\x80\x08\x00Qc\x00\x00\x00\x89,\t\x0e\t\x03\x80\t\x00Qc\x00\x00\x00\x89\x94\x1cPP\x13\x03\x80\x0b8;555222\x1f%\x1f%\x1f"\x1f"\x1f%\x1f%\x1f"\x1f%\x1f"\x1f%)\x1f"//\x00\x12\x00\t\x12\x00\n\x12\x00\x0b\x12\x00\x0c\x12\x00\r\x12\x00\x0e\x12\x00\x0f*\x07Y\x12\x00\x10\x12\x00\x11\x12\x00\x12\x12\x00\x13\x12\x00\x14\x12\x00\x15\x12\x00\x16\x12\x00\x17*\x08Y\x12\x00\x18\x12\x00\x19\x12\x00\x1a\x12\x00\x1b\x12\x00\x1c\x12\x00\x1d*\x06Y\x12\x00\x1e\x12\x00\x1f\x12\x00 \x12\x00!\x12\x00"\x12\x00#*\x06Y\x12\x00$\x12\x00%\x12\x00&\x12\x00\'\x12\x00(\x12\x00)*\x06Y\x12\x00*\x12\x00+\x12\x00,\x12\x00-\x12\x00.*\x05Y\x12\x00/\x12\x000\x12\x001\x12\x002\x12\x003*\x05Y\x12\x004\x12\x005\x12\x006\x12\x007\x12\x008*\x05Y\x12\x009\x12\x00:\x12\x00;\x12\x00<\x12\x00=\x12\x00>\x12\x00?\x12\x00@\x12\x00A\x12\x00B\x12\x00C*\x0bY\x12\x00D\x12\x00E\x12\x00F\x12\x00G\x12\x00H\x12\x00I\x12\x00J\x12\x00K\x12\x00L\x12\x00M\x12\x00N*\x0bY\x12\x00O\x12\x00P\x12\x00Q\x12\x00R\x12\x00S\x12\x00T\x12\x00U\x12\x00V\x12\x00W\x12\x00X*\nY\x12\x00Y\x12\x00Z\x12\x00[\x12\x00\\\x12\x00]\x12\x00^\x12\x00_\x12\x00`\x12\x00a\x12\x00b*\nY\x12\x00c\x12\x00d\x12\x00e\x12\x00f\x12\x00g\x12\x00h\x12\x00i\x12\x00j\x12\x00k\x12\x00l\x12\x00m*\x0bY\x12\x00n\x12\x00o\x12\x00p\x12\x00q\x12\x00r\x12\x00s\x12\x00t\x12\x00u\x12\x00v\x12\x00w\x12\x00x*\x0bY\x12\x00y\x12\x00z\x12\x00{\x12\x00|\x12\x00}\x12\x00~\x12\x00\x7f\x12\x00\x80\x12\x00\x81\x12\x00\x82*\nY\x12\x00\x83\x12\x00\x84\x12\x00\x85\x12\x00\x86\x12\x00\x87\x12\x00\x88\x12\x00\x89\x12\x00\x8a\x12\x00\x8b\x12\x00\x8c\x12\x00\x8d*\x0bY\x12\x00\x8e\x12\x00\x8f\x12\x00\x90\x12\x00\x91\x12\x00\x92\x12\x00\x93\x12\x00\x94\x12\x00\x95\x12\x00\x96\x12\x00\x97*\nY\x12\x00\x98\x12\x00\x99\x12\x00\x9a\x12\x00\x9b\x12\x00\x9c\x12\x00\x9d\x12\x00\x9e\x12\x00\x9f\x12\x00\xa0\x12\x00\xa2\x12\x00\xa3*\x0bY\x12\x00\xa4\x12\x00\xa5*\x02Y\x12\nname0\x12\nname1\x12\nname2\x12\nname3\x12\nname4\x12\nname5\x12\nname6\x12\nname7\x12\nname8\x12\nname9*\nY\x12$quite_a_long_name0\x12$quite_a_long_name1\x12$quite_a_long_name2\x12$quite_a_long_name3*\x04Y\x12$quite_a_long_name4\x12$quite_a_long_name5\x12$quite_a_long_name6\x12$quite_a_long_name7*\x04Y\x12$quite_a_long_name8\x12$quite_a_long_name9\x12&quite_a_long_name10\x12&quite_a_long_name11*\x04YQc\x00\x00\x86H8H?/\x80#####################+++++++++\x00#\x00\xc0#\x01\xc0#\x02\xc0#\x03\xc0#\x04\xc0#\x05\xc0#\x06\xc0#\x07\xc0#\x08\xc0#\t\xc0#\n\xc0#\x0b\xc0#\x0c\xc0#\r\xc0#\x0e\xc0#\x0f\xc0#\x10\xc0#\x11\xc0#\x12\xc0#\x13\xc0#\x14QPR\x81\x82\x83*\x07\xc0#\x15QPR\x81\x82\x83*\x07\xc0#\x16QPR\x81\x82\x83*\x07\xc0#\x17QPR\x81\x82\x83*\x07\xc0#\x18QPR\x81\x82\x83*\x07\xc0#\x19QPR\x81\x82\x83*\x07\xc0#\x1aQPR\x81\x82\x83*\x07\xc0#\x1bQPR\x81\x82\x83*\x07\xc0#\x1cQPR\x81\x82\x83*\x07\xc0#\x1dQPR\x81\x82\x83*\x07\xc0Qc\x1e\x00s\x1ethis will be a string object 0s\x1ethis will be a string object 1s\x1ethis will be a string object 2s\x1ethis will be a string object 3s\x1ethis will be a string object 4s\x1ethis will be a string object 5s\x1ethis will be a string object 6s\x1ethis will be a string object 7s\x1ethis will be a string object 8s\x1ethis will be a string object 9b\x1dthis will be a bytes object 0b\x1dthis will be a bytes object 1b\x1dthis will be a bytes object 2b\x1dthis will be a bytes object 3b\x1dthis will be a bytes object 4b\x1dthis will be a bytes object 5b\x1dthis will be a bytes object 6b\x1dthis will be a bytes object 7b\x1dthis will be a bytes object 8b\x1dthis will be a bytes object 9s\rconst tuple 0s\rconst tuple 1s\rconst tuple 2s\rconst tuple 3s\rconst tuple 4s\rconst tuple 5s\rconst tuple 6s\rconst tuple 7s\rconst tuple 8s\rconst tuple 9' +file_data = b"M\x06\x02\x1f\x81=\x1e\x0etest.py\x00\x0f\x04A0\x00\x04A1\x00\x04f0\x00\x04f1\x00\x0cresult\x00/-5\x04a0\x00\x04a1\x00\x04a2\x00\x04a3\x00\x13\x15\x17\x19\x1b\x1d\x1f!#%')+1379;=?ACEGIKMOQSUWY[]_acegikmoqsuwy{}\x7f\x81\x01\x81\x03\x81\x05\x81\x07\x81\t\x81\x0b\x81\r\x81\x0f\x81\x11\x81\x13\x81\x15\x81\x17\x81\x19\x81\x1b\x81\x1d\x81\x1f\x81!\x81#\x81%\x81'\x81)\x81+\x81-\x81/\x811\x813\x815\x817\x819\x81;\x81=\x81?\x81A\x81C\x81E\x81G\x81I\x81K\x81M\x81O\x81Q\x81S\x81U\x81W\x81Y\x81[\x81]\x81_\x81a\x81c\x81e\x81g\x81i\x81k\x81m\x81o\x81q\x81s\x81u\x81w\x81y\x81{\x81}\x81\x7f\x82\x01\x82\x03\x82\x05\x82\x07\x82\t\x82\x0b\x82\r\x82\x0f\x82\x11\x82\x13\x82\x15\x82\x17\x82\x19\x82\x1b\x82\x1d\x82\x1f\x82!\x82#\x82%\x82'\x82)\x82+\x82-\x82/\x821\x823\x825\x827\x829\x82;\x82=\x82?\x82A\x82E\x82G\x82I\x82K\nname0\x00\nname1\x00\nname2\x00\nname3\x00\nname4\x00\nname5\x00\nname6\x00\nname7\x00\nname8\x00\nname9\x00$quite_a_long_name0\x00$quite_a_long_name1\x00$quite_a_long_name2\x00$quite_a_long_name3\x00$quite_a_long_name4\x00$quite_a_long_name5\x00$quite_a_long_name6\x00$quite_a_long_name7\x00$quite_a_long_name8\x00$quite_a_long_name9\x00&quite_a_long_name10\x00&quite_a_long_name11\x00s\x1ethis will be a string object 0\x00s\x1ethis will be a string object 1\x00s\x1ethis will be a string object 2\x00s\x1ethis will be a string object 3\x00s\x1ethis will be a string object 4\x00s\x1ethis will be a string object 5\x00s\x1ethis will be a string object 6\x00s\x1ethis will be a string object 7\x00s\x1ethis will be a string object 8\x00s\x1ethis will be a string object 9\x00b\x1dthis will be a bytes object 0\x00b\x1dthis will be a bytes object 1\x00b\x1dthis will be a bytes object 2\x00b\x1dthis will be a bytes object 3\x00b\x1dthis will be a bytes object 4\x00b\x1dthis will be a bytes object 5\x00b\x1dthis will be a bytes object 6\x00b\x1dthis will be a bytes object 7\x00b\x1dthis will be a bytes object 8\x00b\x1dthis will be a bytes object 9\x00s\rconst tuple 0\x00s\rconst tuple 1\x00s\rconst tuple 2\x00s\rconst tuple 3\x00s\rconst tuple 4\x00s\rconst tuple 5\x00s\rconst tuple 6\x00s\rconst tuple 7\x00s\rconst tuple 8\x00s\rconst tuple 9\x00\x82d\x10\x12\x01i@i@\x84\x18\x84\x1fT2\x00\x10\x024\x02\x16\x02T2\x01\x10\x034\x02\x16\x032\x02\x16\x042\x03\x16\x05\"\x80{\x16\x06Qc\x04\x82\x0c\x00\n\x02($$$\x11\x07\x16\x08\x10\x02\x16\t2\x00\x16\n2\x01\x16\x0b2\x02\x16\x0c2\x03\x16\rQc\x04@\t\x08\n\x81\x0b Qc@\t\x08\x0b\x81\x0b@Qc@\t\x08\x0c\x81\x0b`QcH\t\n\r\x81\x0b` Qc\x82\x14\x00\x0c\x03h`$$$\x11\x07\x16\x08\x10\x03\x16\t2\x00\x16\n2\x01\x16\x0b2\x02\x16\x0c2\x03\x16\rQc\x04H\t\n\n\x81\x0b``QcH\t\n\x0b\x81\x0b\x80\x07QcH\t\n\x0c\x81\x0b\x80\x08QcH\t\n\r\x81\x0b\x80\tQc\xa08P:\x04\x80\x0b13///---997799<\x1f%\x1f\"\x1f%)\x1f\"//\x12\x0e\x12\x0f\x12\x10\x12\x11\x12\x12\x12\x13\x12\x14*\x07Y\x12\x15\x12\x16\x12\x17\x12\x18\x12\x19\x12\x1a\x12\x08\x12\x07*\x08Y\x12\x1b\x12\x1c\x12\t\x12\x1d\x12\x1e\x12\x1f*\x06Y\x12 \x12!\x12\"\x12#\x12$\x12%*\x06Y\x12&\x12'\x12(\x12)\x12*\x12+*\x06Y\x12,\x12-\x12.\x12/\x120*\x05Y\x121\x122\x123\x124\x125*\x05Y\x126\x127\x128\x129\x12:*\x05Y\x12;\x12<\x12=\x12>\x12?\x12@\x12A\x12B\x12C\x12D\x12E*\x0bY\x12F\x12G\x12H\x12I\x12J\x12K\x12L\x12M\x12N\x12O\x12P*\x0bY\x12Q\x12R\x12S\x12T\x12U\x12V\x12W\x12X\x12Y\x12Z*\nY\x12[\x12\\\x12]\x12^\x12_\x12`\x12a\x12b\x12c\x12d*\nY\x12e\x12f\x12g\x12h\x12i\x12j\x12k\x12l\x12m\x12n\x12o*\x0bY\x12p\x12q\x12r\x12s\x12t\x12u\x12v\x12w\x12x\x12y\x12z*\x0bY\x12{\x12|\x12}\x12~\x12\x7f\x12\x81\x00\x12\x81\x01\x12\x81\x02\x12\x81\x03\x12\x81\x04*\nY\x12\x81\x05\x12\x81\x06\x12\x81\x07\x12\x81\x08\x12\x81\t\x12\x81\n\x12\x81\x0b\x12\x81\x0c\x12\x81\r\x12\x81\x0e\x12\x81\x0f*\x0bY\x12\x81\x10\x12\x81\x11\x12\x81\x12\x12\x81\x13\x12\x81\x14\x12\x81\x15\x12\x81\x16\x12\x81\x17\x12\x81\x18\x12\x81\x19*\nY\x12\x81\x1a\x12\x81\x1b\x12\x81\x1c\x12\x81\x1d\x12\x81\x1e\x12\x81\x1f\x12\x81 \x12\x81!\x12\x81\"\x12\x81#\x12\x81$*\x0bY\x12\x81%\x12\x81&*\x02Y\x12\x81'\x12\x81(\x12\x81)\x12\x81*\x12\x81+\x12\x81,\x12\x81-\x12\x81.\x12\x81/\x12\x810*\nY\x12\x811\x12\x812\x12\x813\x12\x814*\x04Y\x12\x815\x12\x816\x12\x817\x12\x818*\x04Y\x12\x819\x12\x81:\x12\x81;\x12\x81<*\x04YQc\x8cp8@\x05\x80#####################+++++++++#\x00\xc0#\x01\xc0#\x02\xc0#\x03\xc0#\x04\xc0#\x05\xc0#\x06\xc0#\x07\xc0#\x08\xc0#\t\xc0#\n\xc0#\x0b\xc0#\x0c\xc0#\r\xc0#\x0e\xc0#\x0f\xc0#\x10\xc0#\x11\xc0#\x12\xc0#\x13\xc0#\x14QPR\x81\x82\x83*\x07\xc0#\x15QPR\x81\x82\x83*\x07\xc0#\x16QPR\x81\x82\x83*\x07\xc0#\x17QPR\x81\x82\x83*\x07\xc0#\x18QPR\x81\x82\x83*\x07\xc0#\x19QPR\x81\x82\x83*\x07\xc0#\x1aQPR\x81\x82\x83*\x07\xc0#\x1bQPR\x81\x82\x83*\x07\xc0#\x1cQPR\x81\x82\x83*\x07\xc0#\x1dQPR\x81\x82\x83*\x07\xc0Qc" class File(uio.IOBase): diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index 58a4a75c91..da6352dab7 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -29,27 +29,49 @@ from __future__ import print_function import platform if platform.python_version_tuple()[0] == "2": - str_cons = lambda val, enc=None: val + from binascii import hexlify as hexlify_py2 + + str_cons = lambda val, enc=None: str(val) bytes_cons = lambda val, enc=None: bytearray(val) is_str_type = lambda o: type(o) is str is_bytes_type = lambda o: type(o) is bytearray is_int_type = lambda o: type(o) is int or type(o) is long + + def hexlify_to_str(b): + x = hexlify_py2(b) + return ":".join(x[i : i + 2] for i in range(0, len(x), 2)) + else: + from binascii import hexlify + str_cons = str bytes_cons = bytes is_str_type = lambda o: type(o) is str is_bytes_type = lambda o: type(o) is bytes is_int_type = lambda o: type(o) is int + + def hexlify_to_str(b): + return str(hexlify(b, ":"), "ascii") + + # end compatibility code import sys import struct -from collections import namedtuple sys.path.append(sys.path[0] + "/../py") import makeqstrdata as qstrutil +class MPYReadError(Exception): + def __init__(self, filename, msg): + self.filename = filename + self.msg = msg + + def __str__(self): + return "%s: %s" % (self.filename, self.msg) + + class FreezeError(Exception): def __init__(self, rawcode, msg): self.rawcode = rawcode @@ -60,7 +82,7 @@ class FreezeError(Exception): class Config: - MPY_VERSION = 5 + MPY_VERSION = 6 MICROPY_LONGINT_IMPL_NONE = 0 MICROPY_LONGINT_IMPL_LONGLONG = 1 MICROPY_LONGINT_IMPL_MPZ = 2 @@ -82,20 +104,6 @@ for n in qstrutil.static_qstr_list: global_qstrs.append(QStrType(n)) -class QStrWindow: - def __init__(self, size): - self.window = [] - self.size = size - - def push(self, val): - self.window = [val] + self.window[: self.size - 1] - - def access(self, idx): - val = self.window[idx] - self.window = [val] + self.window[:idx] + self.window[idx + 1 :] - return val - - MP_CODE_BYTECODE = 2 MP_CODE_NATIVE_PY = 3 MP_CODE_NATIVE_VIPER = 4 @@ -113,6 +121,10 @@ MP_NATIVE_ARCH_ARMV7EMDP = 8 MP_NATIVE_ARCH_XTENSA = 9 MP_NATIVE_ARCH_XTENSAWIN = 10 +MP_SCOPE_FLAG_VIPERRELOC = 0x10 +MP_SCOPE_FLAG_VIPERRODATA = 0x20 +MP_SCOPE_FLAG_VIPERBSS = 0x40 + MP_BC_MASK_EXTRA_BYTE = 0x9E MP_BC_FORMAT_BYTE = 0 @@ -120,11 +132,180 @@ MP_BC_FORMAT_QSTR = 1 MP_BC_FORMAT_VAR_UINT = 2 MP_BC_FORMAT_OFFSET = 3 -# extra byte if caching enabled: -MP_BC_LOAD_NAME = 0x11 -MP_BC_LOAD_GLOBAL = 0x12 -MP_BC_LOAD_ATTR = 0x13 -MP_BC_STORE_ATTR = 0x18 +mp_unary_op_method_name = ( + "__pos__", + "__neg__", + "__invert__", + "", +) + +mp_binary_op_method_name = ( + "__lt__", + "__gt__", + "__eq__", + "__le__", + "__ge__", + "__ne__", + "", + "", + "", + "__ior__", + "__ixor__", + "__iand__", + "__ilshift__", + "__irshift__", + "__iadd__", + "__isub__", + "__imul__", + "__imatmul__", + "__ifloordiv__", + "__itruediv__", + "__imod__", + "__ipow__", + "__or__", + "__xor__", + "__and__", + "__lshift__", + "__rshift__", + "__add__", + "__sub__", + "__mul__", + "__matmul__", + "__floordiv__", + "__truediv__", + "__mod__", + "__pow__", +) + + +class Opcodes: + # fmt: off + # Load, Store, Delete, Import, Make, Build, Unpack, Call, Jump, Exception, For, sTack, Return, Yield, Op + MP_BC_BASE_RESERVED = (0x00) # ---------------- + MP_BC_BASE_QSTR_O = (0x10) # LLLLLLSSSDDII--- + MP_BC_BASE_VINT_E = (0x20) # MMLLLLSSDDBBBBBB + MP_BC_BASE_VINT_O = (0x30) # UUMMCCCC-------- + MP_BC_BASE_JUMP_E = (0x40) # J-JJJJJEEEEF---- + MP_BC_BASE_BYTE_O = (0x50) # LLLLSSDTTTTTEEFF + MP_BC_BASE_BYTE_E = (0x60) # --BREEEYYI------ + MP_BC_LOAD_CONST_SMALL_INT_MULTI = (0x70) # LLLLLLLLLLLLLLLL + # = (0x80) # LLLLLLLLLLLLLLLL + # = (0x90) # LLLLLLLLLLLLLLLL + # = (0xa0) # LLLLLLLLLLLLLLLL + MP_BC_LOAD_FAST_MULTI = (0xb0) # LLLLLLLLLLLLLLLL + MP_BC_STORE_FAST_MULTI = (0xc0) # SSSSSSSSSSSSSSSS + MP_BC_UNARY_OP_MULTI = (0xd0) # OOOOOOO + MP_BC_BINARY_OP_MULTI = (0xd7) # OOOOOOOOO + # = (0xe0) # OOOOOOOOOOOOOOOO + # = (0xf0) # OOOOOOOOOO------ + + MP_BC_LOAD_CONST_SMALL_INT_MULTI_NUM = 64 + MP_BC_LOAD_CONST_SMALL_INT_MULTI_EXCESS = 16 + MP_BC_LOAD_FAST_MULTI_NUM = 16 + MP_BC_STORE_FAST_MULTI_NUM = 16 + MP_BC_UNARY_OP_MULTI_NUM = 4 # MP_UNARY_OP_NUM_BYTECODE + MP_BC_BINARY_OP_MULTI_NUM = 35 # MP_BINARY_OP_NUM_BYTECODE + + MP_BC_LOAD_CONST_FALSE = (MP_BC_BASE_BYTE_O + 0x00) + MP_BC_LOAD_CONST_NONE = (MP_BC_BASE_BYTE_O + 0x01) + MP_BC_LOAD_CONST_TRUE = (MP_BC_BASE_BYTE_O + 0x02) + MP_BC_LOAD_CONST_SMALL_INT = (MP_BC_BASE_VINT_E + 0x02) # signed var-int + MP_BC_LOAD_CONST_STRING = (MP_BC_BASE_QSTR_O + 0x00) # qstr + MP_BC_LOAD_CONST_OBJ = (MP_BC_BASE_VINT_E + 0x03) # ptr + MP_BC_LOAD_NULL = (MP_BC_BASE_BYTE_O + 0x03) + + MP_BC_LOAD_FAST_N = (MP_BC_BASE_VINT_E + 0x04) # uint + MP_BC_LOAD_DEREF = (MP_BC_BASE_VINT_E + 0x05) # uint + MP_BC_LOAD_NAME = (MP_BC_BASE_QSTR_O + 0x01) # qstr + MP_BC_LOAD_GLOBAL = (MP_BC_BASE_QSTR_O + 0x02) # qstr + MP_BC_LOAD_ATTR = (MP_BC_BASE_QSTR_O + 0x03) # qstr + MP_BC_LOAD_METHOD = (MP_BC_BASE_QSTR_O + 0x04) # qstr + MP_BC_LOAD_SUPER_METHOD = (MP_BC_BASE_QSTR_O + 0x05) # qstr + MP_BC_LOAD_BUILD_CLASS = (MP_BC_BASE_BYTE_O + 0x04) + MP_BC_LOAD_SUBSCR = (MP_BC_BASE_BYTE_O + 0x05) + + MP_BC_STORE_FAST_N = (MP_BC_BASE_VINT_E + 0x06) # uint + MP_BC_STORE_DEREF = (MP_BC_BASE_VINT_E + 0x07) # uint + MP_BC_STORE_NAME = (MP_BC_BASE_QSTR_O + 0x06) # qstr + MP_BC_STORE_GLOBAL = (MP_BC_BASE_QSTR_O + 0x07) # qstr + MP_BC_STORE_ATTR = (MP_BC_BASE_QSTR_O + 0x08) # qstr + MP_BC_STORE_SUBSCR = (MP_BC_BASE_BYTE_O + 0x06) + + MP_BC_DELETE_FAST = (MP_BC_BASE_VINT_E + 0x08) # uint + MP_BC_DELETE_DEREF = (MP_BC_BASE_VINT_E + 0x09) # uint + MP_BC_DELETE_NAME = (MP_BC_BASE_QSTR_O + 0x09) # qstr + MP_BC_DELETE_GLOBAL = (MP_BC_BASE_QSTR_O + 0x0a) # qstr + + MP_BC_DUP_TOP = (MP_BC_BASE_BYTE_O + 0x07) + MP_BC_DUP_TOP_TWO = (MP_BC_BASE_BYTE_O + 0x08) + MP_BC_POP_TOP = (MP_BC_BASE_BYTE_O + 0x09) + MP_BC_ROT_TWO = (MP_BC_BASE_BYTE_O + 0x0a) + MP_BC_ROT_THREE = (MP_BC_BASE_BYTE_O + 0x0b) + + MP_BC_JUMP = (MP_BC_BASE_JUMP_E + 0x02) # rel byte code offset, 16-bit signed, in excess + MP_BC_POP_JUMP_IF_TRUE = (MP_BC_BASE_JUMP_E + 0x03) # rel byte code offset, 16-bit signed, in excess + MP_BC_POP_JUMP_IF_FALSE = (MP_BC_BASE_JUMP_E + 0x04) # rel byte code offset, 16-bit signed, in excess + MP_BC_JUMP_IF_TRUE_OR_POP = (MP_BC_BASE_JUMP_E + 0x05) # rel byte code offset, 16-bit signed, in excess + MP_BC_JUMP_IF_FALSE_OR_POP = (MP_BC_BASE_JUMP_E + 0x06) # rel byte code offset, 16-bit signed, in excess + MP_BC_UNWIND_JUMP = (MP_BC_BASE_JUMP_E + 0x00) # rel byte code offset, 16-bit signed, in excess; then a byte + MP_BC_SETUP_WITH = (MP_BC_BASE_JUMP_E + 0x07) # rel byte code offset, 16-bit unsigned + MP_BC_SETUP_EXCEPT = (MP_BC_BASE_JUMP_E + 0x08) # rel byte code offset, 16-bit unsigned + MP_BC_SETUP_FINALLY = (MP_BC_BASE_JUMP_E + 0x09) # rel byte code offset, 16-bit unsigned + MP_BC_POP_EXCEPT_JUMP = (MP_BC_BASE_JUMP_E + 0x0a) # rel byte code offset, 16-bit unsigned + MP_BC_FOR_ITER = (MP_BC_BASE_JUMP_E + 0x0b) # rel byte code offset, 16-bit unsigned + MP_BC_WITH_CLEANUP = (MP_BC_BASE_BYTE_O + 0x0c) + MP_BC_END_FINALLY = (MP_BC_BASE_BYTE_O + 0x0d) + MP_BC_GET_ITER = (MP_BC_BASE_BYTE_O + 0x0e) + MP_BC_GET_ITER_STACK = (MP_BC_BASE_BYTE_O + 0x0f) + + MP_BC_BUILD_TUPLE = (MP_BC_BASE_VINT_E + 0x0a) # uint + MP_BC_BUILD_LIST = (MP_BC_BASE_VINT_E + 0x0b) # uint + MP_BC_BUILD_MAP = (MP_BC_BASE_VINT_E + 0x0c) # uint + MP_BC_STORE_MAP = (MP_BC_BASE_BYTE_E + 0x02) + MP_BC_BUILD_SET = (MP_BC_BASE_VINT_E + 0x0d) # uint + MP_BC_BUILD_SLICE = (MP_BC_BASE_VINT_E + 0x0e) # uint + MP_BC_STORE_COMP = (MP_BC_BASE_VINT_E + 0x0f) # uint + MP_BC_UNPACK_SEQUENCE = (MP_BC_BASE_VINT_O + 0x00) # uint + MP_BC_UNPACK_EX = (MP_BC_BASE_VINT_O + 0x01) # uint + + MP_BC_RETURN_VALUE = (MP_BC_BASE_BYTE_E + 0x03) + MP_BC_RAISE_LAST = (MP_BC_BASE_BYTE_E + 0x04) + MP_BC_RAISE_OBJ = (MP_BC_BASE_BYTE_E + 0x05) + MP_BC_RAISE_FROM = (MP_BC_BASE_BYTE_E + 0x06) + MP_BC_YIELD_VALUE = (MP_BC_BASE_BYTE_E + 0x07) + MP_BC_YIELD_FROM = (MP_BC_BASE_BYTE_E + 0x08) + + MP_BC_MAKE_FUNCTION = (MP_BC_BASE_VINT_O + 0x02) # uint + MP_BC_MAKE_FUNCTION_DEFARGS = (MP_BC_BASE_VINT_O + 0x03) # uint + MP_BC_MAKE_CLOSURE = (MP_BC_BASE_VINT_E + 0x00) # uint; extra byte + MP_BC_MAKE_CLOSURE_DEFARGS = (MP_BC_BASE_VINT_E + 0x01) # uint; extra byte + MP_BC_CALL_FUNCTION = (MP_BC_BASE_VINT_O + 0x04) # uint + MP_BC_CALL_FUNCTION_VAR_KW = (MP_BC_BASE_VINT_O + 0x05) # uint + MP_BC_CALL_METHOD = (MP_BC_BASE_VINT_O + 0x06) # uint + MP_BC_CALL_METHOD_VAR_KW = (MP_BC_BASE_VINT_O + 0x07) # uint + + MP_BC_IMPORT_NAME = (MP_BC_BASE_QSTR_O + 0x0b) # qstr + MP_BC_IMPORT_FROM = (MP_BC_BASE_QSTR_O + 0x0c) # qstr + MP_BC_IMPORT_STAR = (MP_BC_BASE_BYTE_E + 0x09) + # fmt: on + + # Create a dict mapping opcode value to opcode name. + mapping = ["unknown" for _ in range(256)] + for op_name in list(locals()): + if op_name.startswith("MP_BC_"): + mapping[locals()[op_name]] = op_name[len("MP_BC_") :] + for i in range(MP_BC_LOAD_CONST_SMALL_INT_MULTI_NUM): + name = "LOAD_CONST_SMALL_INT %d" % (i - MP_BC_LOAD_CONST_SMALL_INT_MULTI_EXCESS) + mapping[MP_BC_LOAD_CONST_SMALL_INT_MULTI + i] = name + for i in range(MP_BC_LOAD_FAST_MULTI_NUM): + mapping[MP_BC_LOAD_FAST_MULTI + i] = "LOAD_FAST %d" % i + for i in range(MP_BC_STORE_FAST_MULTI_NUM): + mapping[MP_BC_STORE_FAST_MULTI + i] = "STORE_FAST %d" % i + for i in range(MP_BC_UNARY_OP_MULTI_NUM): + mapping[MP_BC_UNARY_OP_MULTI + i] = "UNARY_OP %d %s" % (i, mp_unary_op_method_name[i]) + for i in range(MP_BC_BINARY_OP_MULTI_NUM): + mapping[MP_BC_BINARY_OP_MULTI + i] = "BINARY_OP %d %s" % (i, mp_binary_op_method_name[i]) + # this function mirrors that in py/bc.c def mp_opcode_format(bytecode, ip, count_var_uint): @@ -147,6 +328,26 @@ def mp_opcode_format(bytecode, ip, count_var_uint): return f, ip - ip_start +def mp_opcode_decode(bytecode, ip): + opcode = bytecode[ip] + ip_start = ip + f = (0x000003A4 >> (2 * ((opcode) >> 4))) & 3 + extra_byte = (opcode & MP_BC_MASK_EXTRA_BYTE) == 0 + ip += 1 + arg = 0 + if f in (MP_BC_FORMAT_QSTR, MP_BC_FORMAT_VAR_UINT): + arg = bytecode[ip] & 0x7F + while bytecode[ip] & 0x80 != 0: + ip += 1 + arg = arg << 7 | bytecode[ip] & 0x7F + ip += 1 + elif f == MP_BC_FORMAT_OFFSET: + arg = bytecode[ip] | bytecode[ip + 1] << 8 + ip += 2 + ip += extra_byte + return f, ip - ip_start, arg + + def read_prelude_sig(read_byte): z = read_byte() # xSSSSEAA @@ -201,6 +402,7 @@ def extract_prelude(bytecode, ip): n_kwonly_args, n_def_pos_args, ) = read_prelude_sig(local_read_byte) + n_info, n_cell = read_prelude_size(local_read_byte) ip = ip_ref[0] @@ -208,74 +410,186 @@ def extract_prelude(bytecode, ip): ip = ip2 + n_info + n_cell # ip now points to first opcode # ip2 points to simple_name qstr - return ip, ip2, (n_state, n_exc_stack, scope_flags, n_pos_args, n_kwonly_args, n_def_pos_args) + + # Extract simple_name and argument qstrs (var uints). + args = [] + for arg_num in range(1 + n_pos_args + n_kwonly_args): + value = 0 + while True: + b = local_read_byte() + value = (value << 7) | (b & 0x7F) + if b & 0x80 == 0: + break + args.append(value) + + return ( + ip2, + ip, + ip_ref[0], + (n_state, n_exc_stack, scope_flags, n_pos_args, n_kwonly_args, n_def_pos_args), + args, + ) class MPFunTable: - pass + def __repr__(self): + return "mp_fun_table" -class RawCode(object): - # a set of all escaped names, to make sure they are unique - escaped_names = set() - - # convert code kind number to string - code_kind_str = { - MP_CODE_BYTECODE: "MP_CODE_BYTECODE", - MP_CODE_NATIVE_PY: "MP_CODE_NATIVE_PY", - MP_CODE_NATIVE_VIPER: "MP_CODE_NATIVE_VIPER", - MP_CODE_NATIVE_ASM: "MP_CODE_NATIVE_ASM", - } - - def __init__(self, code_kind, bytecode, prelude_offset, qstrs, objs, raw_codes): - # set core variables - self.code_kind = code_kind - self.bytecode = bytecode - self.prelude_offset = prelude_offset - self.qstrs = qstrs - self.objs = objs - self.raw_codes = raw_codes - - if self.prelude_offset is None: - # no prelude, assign a dummy simple_name - self.prelude_offset = 0 - self.simple_name = global_qstrs[1] - else: - # extract prelude - self.ip, self.ip2, self.prelude = extract_prelude(self.bytecode, self.prelude_offset) - self.simple_name = self._unpack_qstr(self.ip2) - self.source_file = self._unpack_qstr(self.ip2 + 2) - self.line_info_offset = self.ip2 + 4 +class CompiledModule: + def __init__( + self, + mpy_source_file, + mpy_segments, + header, + qstr_table, + obj_table, + raw_code, + raw_code_file_offset, + escaped_name, + ): + self.mpy_source_file = mpy_source_file + self.mpy_segments = mpy_segments + self.source_file = qstr_table[0] + self.header = header + self.qstr_table = qstr_table + self.obj_table = obj_table + self.raw_code_file_offset = raw_code_file_offset + self.raw_code = raw_code + self.escaped_name = escaped_name def _unpack_qstr(self, ip): qst = self.bytecode[ip] | self.bytecode[ip + 1] << 8 return global_qstrs[qst] - def dump(self): - # dump children first - for rc in self.raw_codes: - rc.freeze("") - # TODO + def hexdump(self): + with open(self.mpy_source_file, "rb") as f: + WIDTH = 16 + COL_OFF = "\033[0m" + COL_TABLE = ( + ("", ""), # META + ("\033[0;31m", "\033[0;91m"), # QSTR + ("\033[0;32m", "\033[0;92m"), # OBJ + ("\033[0;34m", "\033[0;94m"), # CODE + ) + cur_col = "" + cur_col_index = 0 + offset = 0 + segment_index = 0 + while True: + data = bytes_cons(f.read(WIDTH)) + if not data: + break - def freeze_children(self, parent_name): - self.escaped_name = parent_name + self.simple_name.qstr_esc + # Print out the hex dump of this line of data. + line_hex = cur_col + line_chr = cur_col + line_comment = "" + for i in range(len(data)): + # Determine the colour of the data, if any, and the line comment. + while segment_index < len(self.mpy_segments): + if offset + i == self.mpy_segments[segment_index].start: + cur_col = COL_TABLE[self.mpy_segments[segment_index].kind][ + cur_col_index + ] + cur_col_index = 1 - cur_col_index + line_hex += cur_col + line_chr += cur_col + line_comment += " %s%s%s" % ( + cur_col, + self.mpy_segments[segment_index].name, + COL_OFF, + ) + if offset + i == self.mpy_segments[segment_index].end: + cur_col = "" + line_hex += COL_OFF + line_chr += COL_OFF + segment_index += 1 + else: + break - # make sure the escaped name is unique - i = 2 - while self.escaped_name in RawCode.escaped_names: - self.escaped_name = parent_name + self.simple_name.qstr_esc + str(i) - i += 1 - RawCode.escaped_names.add(self.escaped_name) + # Add to the hex part of the line. + if i % 2 == 0: + line_hex += " " + line_hex += "%02x" % data[i] - # emit children first - for rc in self.raw_codes: - rc.freeze(self.escaped_name + "_") + # Add to the characters part of the line. + if 0x20 <= data[i] <= 0x7E: + line_chr += "%s" % chr(data[i]) + else: + line_chr += "." + + # Print out this line. + if cur_col: + line_hex += COL_OFF + line_chr += COL_OFF + pad = " " * ((WIDTH - len(data)) * 5 // 2) + print("%08x:%s%s %s %s" % (offset, line_hex, pad, line_chr, line_comment)) + offset += WIDTH + + def disassemble(self): + print("mpy_source_file:", self.mpy_source_file) + print("source_file:", self.source_file.str) + print("header:", hexlify_to_str(self.header)) + print("qstr_table[%u]:" % len(self.qstr_table)) + for q in self.qstr_table: + print(" %s" % q.str) + print("obj_table:", self.obj_table) + self.raw_code.disassemble() + + def freeze(self, compiled_module_index): + print() + print("/" * 80) + print("// frozen module %s" % self.escaped_name) + print("// - original source file: %s" % self.mpy_source_file) + print("// - frozen file name: %s" % self.source_file.str) + print("// - .mpy header: %s" % ":".join("%02x" % b for b in self.header)) + print() + + self.raw_code.freeze() + print() + + self.freeze_constants() + + print() + print("static const mp_frozen_module_t frozen_module_%s = {" % self.escaped_name) + print(" .constants = {") + if len(self.qstr_table): + print( + " .qstr_table = (qstr_short_t *)&const_qstr_table_data_%s," + % self.escaped_name + ) + else: + print(" .qstr_table = NULL,") + if len(self.obj_table): + print(" .obj_table = (mp_obj_t *)&const_obj_table_data_%s," % self.escaped_name) + else: + print(" .obj_table = NULL,") + print(" },") + print(" .rc = &raw_code_%s," % self.raw_code.escaped_name) + print("};") def freeze_constants(self): + global const_str_content, const_int_content, const_obj_content + + if len(self.qstr_table): + print( + "static const qstr_short_t const_qstr_table_data_%s[%u] = {" + % (self.escaped_name, len(self.qstr_table)) + ) + for q in self.qstr_table: + print(" %s," % q.qstr_id) + print("};") + + if not len(self.obj_table): + return + # generate constant objects - for i, obj in enumerate(self.objs): + print() + print("// constants") + for i, obj in enumerate(self.obj_table): obj_name = "const_obj_%s_%u" % (self.escaped_name, i) - if obj is MPFunTable: + if isinstance(obj, MPFunTable): pass elif obj is Ellipsis: print("#define %s mp_const_ellipsis_obj" % obj_name) @@ -286,7 +600,7 @@ class RawCode(object): else: obj_type = "mp_type_bytes" print( - 'STATIC const mp_obj_str_t %s = {{&%s}, %u, %u, (const byte*)"%s"};' + 'static const mp_obj_str_t %s = {{&%s}, %u, %u, (const byte*)"%s"};' % ( obj_name, obj_type, @@ -295,6 +609,8 @@ class RawCode(object): "".join(("\\x%02x" % b) for b in obj), ) ) + const_str_content += len(obj) + const_obj_content += 4 * 4 elif is_int_type(obj): if config.MICROPY_LONGINT_IMPL == config.MICROPY_LONGINT_IMPL_NONE: # TODO check if we can actually fit this long-int into a small-int @@ -316,76 +632,139 @@ class RawCode(object): ndigs = len(digs) digs = ",".join(("%#x" % d) for d in digs) print( - "STATIC const mp_obj_int_t %s = {{&mp_type_int}, " + "static const mp_obj_int_t %s = {{&mp_type_int}, " "{.neg=%u, .fixed_dig=1, .alloc=%u, .len=%u, .dig=(uint%u_t*)(const uint%u_t[]){%s}}};" % (obj_name, neg, ndigs, ndigs, bits_per_dig, bits_per_dig, digs) ) + const_int_content += (digs.count(",") + 1) * bits_per_dig // 8 + const_obj_content += 4 * 4 elif type(obj) is float: print( "#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_A || MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_B" ) print( - "STATIC const mp_obj_float_t %s = {{&mp_type_float}, (mp_float_t)%.16g};" + "static const mp_obj_float_t %s = {{&mp_type_float}, (mp_float_t)%.16g};" % (obj_name, obj) ) print("#endif") + const_obj_content += 3 * 4 elif type(obj) is complex: print( - "STATIC const mp_obj_complex_t %s = {{&mp_type_complex}, (mp_float_t)%.16g, (mp_float_t)%.16g};" + "static const mp_obj_complex_t %s = {{&mp_type_complex}, (mp_float_t)%.16g, (mp_float_t)%.16g};" % (obj_name, obj.real, obj.imag) ) else: raise FreezeError(self, "freezing of object %r is not implemented" % (obj,)) - # generate constant table, if it has any entries - const_table_len = len(self.qstrs) + len(self.objs) + len(self.raw_codes) - if const_table_len: - print( - "STATIC const mp_rom_obj_t const_table_data_%s[%u] = {" - % (self.escaped_name, const_table_len) - ) - for qst in self.qstrs: - print(" MP_ROM_QSTR(%s)," % global_qstrs[qst].qstr_id) - for i in range(len(self.objs)): - if self.objs[i] is MPFunTable: - print(" &mp_fun_table,") - elif type(self.objs[i]) is float: - print( - "#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_A || MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_B" - ) - print(" MP_ROM_PTR(&const_obj_%s_%u)," % (self.escaped_name, i)) - print("#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C") - n = struct.unpack("> 8,") - print(" ", self.source_file.qstr_id, "& 0xff,", self.source_file.qstr_id, ">> 8,") - print(" ", end="") - for i in range(self.ip2 + 4, self.ip): - print(" 0x%02x," % self.bytecode[i], end="") - print() - ip = self.ip - while ip < len(self.bytecode): - f, sz = mp_opcode_format(self.bytecode, ip, True) - if f == 1: - qst = self._unpack_qstr(ip + 1).qstr_id - extra = "" if sz == 3 else " 0x%02x," % self.bytecode[ip + 3] - print(" ", "0x%02x," % self.bytecode[ip], qst, "& 0xff,", qst, ">> 8,", extra) + def disassemble(self): + bc = self.fun_data + print("simple_name:", self.simple_name.str) + print(" raw bytecode:", len(bc), hexlify_to_str(bc)) + print(" prelude:", self.prelude) + print(" args:", [self.qstr_table[i].str for i in self.names[1:]]) + print(" line info:", hexlify_to_str(bc[self.offset_line_info : self.offset_opcodes])) + ip = self.offset_opcodes + while ip < len(bc): + fmt, sz, arg = mp_opcode_decode(bc, ip) + if bc[ip] == Opcodes.MP_BC_LOAD_CONST_OBJ: + arg = "%r" % self.obj_table[arg] + if fmt == MP_BC_FORMAT_QSTR: + arg = self.qstr_table[arg].str + elif fmt in (MP_BC_FORMAT_VAR_UINT, MP_BC_FORMAT_OFFSET): + pass else: - print(" ", "".join("0x%02x, " % self.bytecode[ip + i] for i in range(sz))) + arg = "" + print( + " %-11s %s %s" % (hexlify_to_str(bc[ip : ip + sz]), Opcodes.mapping[bc[ip]], arg) + ) ip += sz + self.disassemble_children() + + def freeze(self): + # generate bytecode data + bc = self.fun_data + print( + "// frozen bytecode for file %s, scope %s" + % (self.qstr_table[0].str, self.escaped_name) + ) + print("static const byte fun_data_%s[%u] = {" % (self.escaped_name, len(bc))) + + print(" ", end="") + for b in bc[: self.offset_names]: + print("0x%02x," % b, end="") + print(" // prelude") + + print(" ", end="") + for b in bc[self.offset_names : self.offset_line_info]: + print("0x%02x," % b, end="") + print(" // names: %s" % ", ".join(self.qstr_table[i].str for i in self.names)) + + print(" ", end="") + for b in bc[self.offset_line_info : self.offset_opcodes]: + print("0x%02x," % b, end="") + print(" // code info") + + ip = self.offset_opcodes + while ip < len(bc): + fmt, sz, arg = mp_opcode_decode(bc, ip) + opcode_name = Opcodes.mapping[bc[ip]] + if fmt == MP_BC_FORMAT_QSTR: + opcode_name += " " + self.qstr_table[arg].str + elif fmt in (MP_BC_FORMAT_VAR_UINT, MP_BC_FORMAT_OFFSET): + opcode_name += " %u" % arg + print( + " %s, // %s" % (",".join("0x%02x" % b for b in bc[ip : ip + sz]), opcode_name) + ) + ip += sz + print("};") - self.freeze_constants() - self.freeze_module() + self.freeze_children() + self.freeze_raw_code() + + global bc_content + bc_content += len(bc) class RawCodeNative(RawCode): def __init__( self, - code_kind, + cm_escaped_name, + qstr_table, + kind, fun_data, prelude_offset, - prelude, qstr_links, - qstrs, - objs, - raw_codes, + scope_flags, + n_pos_args, type_sig, ): super(RawCodeNative, self).__init__( - code_kind, fun_data, prelude_offset, qstrs, objs, raw_codes + cm_escaped_name, qstr_table, fun_data, prelude_offset, kind ) - self.prelude = prelude + + if kind in (MP_CODE_NATIVE_VIPER, MP_CODE_NATIVE_ASM): + self.scope_flags = scope_flags + self.n_pos_args = n_pos_args + self.qstr_links = qstr_links self.type_sig = type_sig if config.native_arch in ( @@ -502,11 +925,32 @@ class RawCodeNative(RawCode): # ARMVxxM -- two byte align. self.fun_data_attributes += " __attribute__ ((aligned (2)))" + def disassemble(self): + fun_data = self.fun_data + print("simple_name:", self.simple_name.str) + print( + " raw data:", + len(fun_data), + hexlify_to_str(fun_data[:32]), + "..." if len(fun_data) > 32 else "", + ) + if self.code_kind != MP_CODE_NATIVE_PY: + return + print(" prelude:", self.prelude) + print(" args:", [self.qstr_table[i].str for i in self.names[1:]]) + print(" line info:", fun_data[self.offset_line_info : self.offset_opcodes]) + ip = 0 + while ip < self.prelude_offset: + sz = 16 + print(" ", hexlify_to_str(fun_data[ip : min(ip + sz, self.prelude_offset)])) + ip += sz + self.disassemble_children() + def _asm_thumb_rewrite_mov(self, pc, val): - print(" (%u & 0xf0) | (%s >> 12)," % (self.bytecode[pc], val), end="") - print(" (%u & 0xfb) | (%s >> 9 & 0x04)," % (self.bytecode[pc + 1], val), end="") + print(" (%u & 0xf0) | (%s >> 12)," % (self.fun_data[pc], val), end="") + print(" (%u & 0xfb) | (%s >> 9 & 0x04)," % (self.fun_data[pc + 1], val), end="") print(" (%s & 0xff)," % (val,), end="") - print(" (%u & 0x07) | (%s >> 4 & 0x70)," % (self.bytecode[pc + 3], val)) + print(" (%u & 0x07) | (%s >> 4 & 0x70)," % (self.fun_data[pc + 3], val)) def _link_qstr(self, pc, kind, qst): if kind == 0: @@ -543,32 +987,22 @@ class RawCodeNative(RawCode): else: assert 0 - def freeze(self, parent_name): - if self.prelude[2] & ~0x0F: + def freeze(self): + if self.scope_flags & ~0x0F: raise FreezeError("unable to freeze code with relocations") - self.freeze_children(parent_name) - # generate native code data print() - if self.code_kind == MP_CODE_NATIVE_PY: - print( - "// frozen native code for file %s, scope %s%s" - % (self.source_file.str, parent_name, self.simple_name.str) - ) - elif self.code_kind == MP_CODE_NATIVE_VIPER: - print("// frozen viper code for scope %s" % (parent_name,)) - else: - print("// frozen assembler code for scope %s" % (parent_name,)) print( - "STATIC const byte fun_data_%s[%u] %s = {" - % (self.escaped_name, len(self.bytecode), self.fun_data_attributes) + "// frozen native code for file %s, scope %s" + % (self.qstr_table[0].str, self.escaped_name) + ) + print( + "static const byte fun_data_%s[%u] %s = {" + % (self.escaped_name, len(self.fun_data), self.fun_data_attributes) ) - if self.code_kind == MP_CODE_NATIVE_PY: - i_top = self.prelude_offset - else: - i_top = len(self.bytecode) + i_top = len(self.fun_data) i = 0 qi = 0 while i < i_top: @@ -585,229 +1019,253 @@ class RawCodeNative(RawCode): i16 = min(i16, self.qstr_links[qi][0]) print(" ", end="") for ii in range(i, i16): - print(" 0x%02x," % self.bytecode[ii], end="") + print(" 0x%02x," % self.fun_data[ii], end="") print() i = i16 - if self.code_kind == MP_CODE_NATIVE_PY: - print(" ", end="") - for i in range(self.prelude_offset, self.ip2): - print(" 0x%02x," % self.bytecode[i], end="") - print() - - print(" ", self.simple_name.qstr_id, "& 0xff,", self.simple_name.qstr_id, ">> 8,") - print(" ", self.source_file.qstr_id, "& 0xff,", self.source_file.qstr_id, ">> 8,") - - print(" ", end="") - for i in range(self.ip2 + 4, self.ip): - print(" 0x%02x," % self.bytecode[i], end="") - print() - print("};") - self.freeze_constants() - self.freeze_module(self.qstr_links, self.type_sig) + self.freeze_children() + self.freeze_raw_code(self.qstr_links, self.type_sig) -class BytecodeBuffer: - def __init__(self, size): - self.buf = bytearray(size) - self.idx = 0 +class MPYSegment: + META = 0 + QSTR = 1 + OBJ = 2 + CODE = 3 - def is_full(self): - return self.idx == len(self.buf) - - def append(self, b): - self.buf[self.idx] = b - self.idx += 1 + def __init__(self, kind, name, start, end): + self.kind = kind + self.name = name + self.start = start + self.end = end -def read_byte(f, out=None): - b = bytes_cons(f.read(1))[0] - if out is not None: - out.append(b) - return b +class MPYReader: + def __init__(self, filename, fileobj): + self.filename = filename + self.fileobj = fileobj + + def tell(self): + return self.fileobj.tell() + + def read_byte(self): + return bytes_cons(self.fileobj.read(1))[0] + + def read_bytes(self, n): + return bytes_cons(self.fileobj.read(n)) + + def read_uint(self): + i = 0 + while True: + b = self.read_byte() + i = (i << 7) | (b & 0x7F) + if b & 0x80 == 0: + break + return i -def read_uint(f, out=None): - i = 0 - while True: - b = read_byte(f, out) - i = (i << 7) | (b & 0x7F) - if b & 0x80 == 0: - break - return i - - -def read_qstr(f, qstr_win): - ln = read_uint(f) - if ln == 0: - # static qstr - return bytes_cons(f.read(1))[0] +def read_qstr(reader, segments): + start_pos = reader.tell() + ln = reader.read_uint() if ln & 1: - # qstr in table - return qstr_win.access(ln >> 1) + # static qstr + segments.append( + MPYSegment(MPYSegment.META, global_qstrs[ln >> 1].str, start_pos, start_pos) + ) + return ln >> 1 ln >>= 1 - data = str_cons(f.read(ln), "utf8") + start_pos = reader.tell() + data = str_cons(reader.read_bytes(ln), "utf8") + reader.read_byte() # read and discard null terminator + segments.append(MPYSegment(MPYSegment.QSTR, data, start_pos, reader.tell())) global_qstrs.append(QStrType(data)) - qstr_win.push(len(global_qstrs) - 1) return len(global_qstrs) - 1 -def read_obj(f): - obj_type = f.read(1) - if obj_type == b"e": +def read_obj(reader, segments): + obj_type = reader.read_bytes(1) + if obj_type == b"t": + return MPFunTable() + elif obj_type == b"e": return Ellipsis else: - buf = f.read(read_uint(f)) + ln = reader.read_uint() + start_pos = reader.tell() + buf = reader.read_bytes(ln) + if obj_type in (b"s", b"b"): + reader.read_byte() # read and discard null terminator if obj_type == b"s": - return str_cons(buf, "utf8") + obj = str_cons(buf, "utf8") elif obj_type == b"b": - return bytes_cons(buf) + obj = buf elif obj_type == b"i": - return int(str_cons(buf, "ascii"), 10) + obj = int(str_cons(buf, "ascii"), 10) elif obj_type == b"f": - return float(str_cons(buf, "ascii")) + obj = float(str_cons(buf, "ascii")) elif obj_type == b"c": - return complex(str_cons(buf, "ascii")) + obj = complex(str_cons(buf, "ascii")) else: - assert 0 + raise MPYReadError(reader.filename, "corrupt .mpy file") + segments.append(MPYSegment(MPYSegment.OBJ, obj, start_pos, reader.tell())) + return obj -def read_prelude(f, bytecode, qstr_win): - ( - n_state, - n_exc_stack, - scope_flags, - n_pos_args, - n_kwonly_args, - n_def_pos_args, - ) = read_prelude_sig(lambda: read_byte(f, bytecode)) - n_info, n_cell = read_prelude_size(lambda: read_byte(f, bytecode)) - read_qstr_and_pack(f, bytecode, qstr_win) # simple_name - read_qstr_and_pack(f, bytecode, qstr_win) # source_file - for _ in range(n_info - 4 + n_cell): - read_byte(f, bytecode) - return n_state, n_exc_stack, scope_flags, n_pos_args, n_kwonly_args, n_def_pos_args - - -def read_qstr_and_pack(f, bytecode, qstr_win): - qst = read_qstr(f, qstr_win) - bytecode.append(qst & 0xFF) - bytecode.append(qst >> 8) - - -def read_bytecode(file, bytecode, qstr_win): - while not bytecode.is_full(): - op = read_byte(file, bytecode) - f, sz = mp_opcode_format(bytecode.buf, bytecode.idx - 1, False) - sz -= 1 - if f == MP_BC_FORMAT_QSTR: - read_qstr_and_pack(file, bytecode, qstr_win) - sz -= 2 - elif f == MP_BC_FORMAT_VAR_UINT: - while read_byte(file, bytecode) & 0x80: - pass - for _ in range(sz): - read_byte(file, bytecode) - - -def read_raw_code(f, qstr_win): - kind_len = read_uint(f) +def read_raw_code(reader, cm_escaped_name, qstr_table, obj_table, segments): + # Read raw code header. + kind_len = reader.read_uint() kind = (kind_len & 3) + MP_CODE_BYTECODE - fun_data_len = kind_len >> 2 - fun_data = BytecodeBuffer(fun_data_len) + has_children = (kind_len >> 2) & 1 + fun_data_len = kind_len >> 3 + + # Read the body of the raw code. + file_offset = reader.tell() + fun_data = reader.read_bytes(fun_data_len) + segments_len = len(segments) if kind == MP_CODE_BYTECODE: - prelude = read_prelude(f, fun_data, qstr_win) - read_bytecode(f, fun_data, qstr_win) + # Create bytecode raw code. + rc = RawCodeBytecode(cm_escaped_name, qstr_table, obj_table, fun_data) else: - fun_data.buf[:] = f.read(fun_data_len) - + # Create native raw code. qstr_links = [] if kind in (MP_CODE_NATIVE_PY, MP_CODE_NATIVE_VIPER): - # load qstr link table - n_qstr_link = read_uint(f) + # Read qstr link table. + n_qstr_link = reader.read_uint() for _ in range(n_qstr_link): - off = read_uint(f) - qst = read_qstr(f, qstr_win) + off = reader.read_uint() + qst = read_qstr(reader, segments) qstr_links.append((off >> 2, off & 3, qst)) - type_sig = 0 + native_scope_flags = 0 + native_n_pos_args = 0 + native_type_sig = 0 if kind == MP_CODE_NATIVE_PY: - prelude_offset = read_uint(f) - _, name_idx, prelude = extract_prelude(fun_data.buf, prelude_offset) - fun_data.idx = name_idx # rewind to where qstrs are in prelude - read_qstr_and_pack(f, fun_data, qstr_win) # simple_name - read_qstr_and_pack(f, fun_data, qstr_win) # source_file + prelude_offset = reader.read_uint() else: - prelude_offset = None - scope_flags = read_uint(f) - n_pos_args = 0 - if kind == MP_CODE_NATIVE_ASM: - n_pos_args = read_uint(f) - type_sig = read_uint(f) - prelude = (None, None, scope_flags, n_pos_args, 0) + prelude_offset = 0 + native_scope_flags = reader.read_uint() + if kind == MP_CODE_NATIVE_VIPER: + # Read any additional sections for native viper. + if native_scope_flags & MP_SCOPE_FLAG_VIPERRODATA: + rodata_size = reader.read_uint() + if native_scope_flags & MP_SCOPE_FLAG_VIPERBSS: + bss_size = reader.read_uint() + if native_scope_flags & MP_SCOPE_FLAG_VIPERRODATA: + reader.read_bytes(rodata_size) + if native_scope_flags & MP_SCOPE_FLAG_VIPERRELOC: + while True: + op = reader.read_byte() + if op == 0xFF: + break + if op & 1: + addr = reader.read_uint() + op >>= 1 + if op <= 5 and op & 1: + n = reader.read_uint() + else: + assert kind == MP_CODE_NATIVE_ASM + native_n_pos_args = reader.read_uint() + native_type_sig = reader.read_uint() - qstrs = [] - objs = [] - raw_codes = [] - if kind != MP_CODE_NATIVE_ASM: - # load constant table - n_obj = read_uint(f) - n_raw_code = read_uint(f) - qstrs = [read_qstr(f, qstr_win) for _ in range(prelude[3] + prelude[4])] - if kind != MP_CODE_BYTECODE: - objs.append(MPFunTable) - objs.extend([read_obj(f) for _ in range(n_obj)]) - raw_codes = [read_raw_code(f, qstr_win) for _ in range(n_raw_code)] - - if kind == MP_CODE_BYTECODE: - return RawCodeBytecode(fun_data.buf, qstrs, objs, raw_codes) - else: - return RawCodeNative( + rc = RawCodeNative( + cm_escaped_name, + qstr_table, kind, - fun_data.buf, + fun_data, prelude_offset, - prelude, qstr_links, - qstrs, - objs, - raw_codes, - type_sig, + native_scope_flags, + native_n_pos_args, + native_type_sig, ) + # Add a segment for the raw code data. + segments.insert( + segments_len, + MPYSegment(MPYSegment.CODE, rc.simple_name.str, file_offset, file_offset + fun_data_len), + ) + + # Read children, if there are any. + rc.children = [] + if has_children: + n_children = reader.read_uint() + for _ in range(n_children): + rc.children.append( + read_raw_code(reader, cm_escaped_name, qstr_table, obj_table, segments) + ) + + return rc + def read_mpy(filename): - with open(filename, "rb") as f: - header = bytes_cons(f.read(4)) + with open(filename, "rb") as fileobj: + reader = MPYReader(filename, fileobj) + segments = [] + + # Read and verify the header. + header = reader.read_bytes(4) if header[0] != ord("M"): - raise Exception("not a valid .mpy file") + raise MPYReadError(filename, "not a valid .mpy file") if header[1] != config.MPY_VERSION: - raise Exception("incompatible .mpy version") + raise MPYReadError(filename, "incompatible .mpy version") feature_byte = header[2] - qw_size = read_uint(f) config.MICROPY_PY_BUILTINS_STR_UNICODE = (feature_byte & 2) != 0 mpy_native_arch = feature_byte >> 2 if mpy_native_arch != MP_NATIVE_ARCH_NONE: if config.native_arch == MP_NATIVE_ARCH_NONE: config.native_arch = mpy_native_arch elif config.native_arch != mpy_native_arch: - raise Exception("native architecture mismatch") + raise MPYReadError(filename, "native architecture mismatch") config.mp_small_int_bits = header[3] - qstr_win = QStrWindow(qw_size) - rc = read_raw_code(f, qstr_win) - rc.mpy_source_file = filename - rc.qstr_win_size = qw_size - return rc + + # Read number of qstrs, and number of objects. + n_qstr = reader.read_uint() + n_obj = reader.read_uint() + + # Read qstrs and construct qstr table. + qstr_table = [] + for i in range(n_qstr): + q = read_qstr(reader, segments) + qstr_table.append(global_qstrs[q]) + + # Read objects and construct object table. + obj_table = [] + for i in range(n_obj): + obj_table.append(read_obj(reader, segments)) + + # Compute the compiled-module escaped name. + cm_escaped_name = qstr_table[0].str.replace("/", "_")[:-3] + + # Read the outer raw code, which will in turn read all its children. + raw_code_file_offset = reader.tell() + raw_code = read_raw_code(reader, cm_escaped_name, qstr_table, obj_table, segments) + + # Create the outer-level compiled module representing the whole .mpy file. + return CompiledModule( + filename, + segments, + header, + qstr_table, + obj_table, + raw_code, + raw_code_file_offset, + cm_escaped_name, + ) -def dump_mpy(raw_codes): - for rc in raw_codes: - rc.dump() +def hexdump_mpy(compiled_modules): + for cm in compiled_modules: + cm.hexdump() -def freeze_mpy(base_qstrs, raw_codes): +def disassemble_mpy(compiled_modules): + for cm in compiled_modules: + cm.disassemble() + + +def freeze_mpy(base_qstrs, compiled_modules): # add to qstrs new = {} for q in global_qstrs: @@ -864,6 +1322,17 @@ def freeze_mpy(base_qstrs, raw_codes): # As in qstr.c, set so that the first dynamically allocated pool is twice this size; must be <= the len qstr_pool_alloc = min(len(new), 10) + global bc_content, const_str_content, const_int_content, const_obj_content, const_table_qstr_content, const_table_ptr_content, raw_code_count, raw_code_content + qstr_content = 0 + bc_content = 0 + const_str_content = 0 + const_int_content = 0 + const_obj_content = 0 + const_table_qstr_content = 0 + const_table_ptr_content = 0 + raw_code_count = 0 + raw_code_content = 0 + print() print("const qstr_hash_t mp_qstr_frozen_const_hashes[] = {") qstr_size = {"metadata": 0, "data": 0} @@ -892,32 +1361,49 @@ def freeze_mpy(base_qstrs, raw_codes): print(" {") for _, _, qstr, qbytes in new: print(' "%s",' % qstrutil.escape_bytes(qstr, qbytes)) + qstr_content += ( + config.MICROPY_QSTR_BYTES_IN_LEN + config.MICROPY_QSTR_BYTES_IN_HASH + len(qbytes) + 1 + ) print(" },") print("};") - for rc in raw_codes: - rc.freeze(rc.source_file.str.replace("/", "_")[:-3] + "_") + # Freeze all modules. + for idx, cm in enumerate(compiled_modules): + cm.freeze(idx) + # Print separator, separating individual modules from global data structures. + print() + print("/" * 80) + print("// collection of all frozen modules") + + # Define the string of frozen module names. print() print("const char mp_frozen_names[] = {") - print("#ifdef MP_FROZEN_STR_NAMES") + print(" #ifdef MP_FROZEN_STR_NAMES") # makemanifest.py might also include some frozen string content. - print("MP_FROZEN_STR_NAMES") - print("#endif") - for rc in raw_codes: - module_name = rc.source_file.str - print('"%s\\0"' % module_name) - print('"\\0"};') - - print("const mp_raw_code_t *const mp_frozen_mpy_content[] = {") - for rc in raw_codes: - print(" &raw_code_%s," % rc.escaped_name) + print(" MP_FROZEN_STR_NAMES") + print(" #endif") + mp_frozen_mpy_names_content = 1 + for cm in compiled_modules: + module_name = cm.source_file.str + print(' "%s\\0"' % module_name) + mp_frozen_mpy_names_content += len(cm.source_file.str) + 1 + print(' "\\0"') print("};") + # Define the array of pointers to frozen module content. + print() + print("const mp_frozen_module_t *const mp_frozen_mpy_content[] = {") + for cm in compiled_modules: + print(" &frozen_module_%s," % cm.escaped_name) + print("};") + mp_frozen_mpy_content_size = len(compiled_modules * 4) + # If a port defines MICROPY_FROZEN_LIST_ITEM then list all modules wrapped in that macro. + print() print("#ifdef MICROPY_FROZEN_LIST_ITEM") - for rc in raw_codes: - module_name = rc.source_file.str + for cm in compiled_modules: + module_name = cm.source_file.str if module_name.endswith("/__init__.py"): short_name = module_name[: -len("/__init__.py")] else: @@ -925,45 +1411,94 @@ def freeze_mpy(base_qstrs, raw_codes): print('MICROPY_FROZEN_LIST_ITEM("%s", "%s")' % (short_name, module_name)) print("#endif") + print() + print("/*") + print("byte sizes:") + print("qstr content: %d unique, %d bytes" % (len(new), qstr_content)) + print("bc content: %d" % bc_content) + print("const str content: %d" % const_str_content) + print("const int content: %d" % const_int_content) + print("const obj content: %d" % const_obj_content) + print( + "const table qstr content: %d entries, %d bytes" + % (const_table_qstr_content, const_table_qstr_content * 4) + ) + print( + "const table ptr content: %d entries, %d bytes" + % (const_table_ptr_content, const_table_ptr_content * 4) + ) + print("raw code content: %d * 4 = %d" % (raw_code_count, raw_code_content)) + print("mp_frozen_mpy_names_content: %d" % mp_frozen_mpy_names_content) + print("mp_frozen_mpy_content_size: %d" % mp_frozen_mpy_content_size) + print( + "total: %d" + % ( + qstr_content + + bc_content + + const_str_content + + const_int_content + + const_obj_content + + const_table_qstr_content * 4 + + const_table_ptr_content * 4 + + raw_code_content + + mp_frozen_mpy_names_content + + mp_frozen_mpy_content_size + ) + ) + print("*/") + def merge_mpy(raw_codes, output_file): - assert len(raw_codes) <= 31 # so var-uints all fit in 1 byte + assert len(raw_codes) <= 2 # so var-uints all fit in 1 byte merged_mpy = bytearray() if len(raw_codes) == 1: with open(raw_codes[0].mpy_source_file, "rb") as f: merged_mpy.extend(f.read()) else: - header = bytearray(5) + main_rc = None + for rc in raw_codes: + if len(rc.qstr_table) > 1 or len(rc.obj_table) > 0: + # Must use qstr_table and obj_table from this raw_code + if main_rc is not None: + raise Exception( + "can't merge files when more than one has a populated qstr or obj table" + ) + main_rc = rc + if main_rc is None: + main_rc = raw_codes[0] + + header = bytearray(4) header[0] = ord("M") header[1] = config.MPY_VERSION header[2] = config.native_arch << 2 | config.MICROPY_PY_BUILTINS_STR_UNICODE << 1 header[3] = config.mp_small_int_bits - header[4] = 32 # qstr_win_size merged_mpy.extend(header) + # Copy n_qstr, n_obj, qstr_table, obj_table from main_rc. + with open(main_rc.mpy_source_file, "rb") as f: + data = f.read(main_rc.raw_code_file_offset) + merged_mpy.extend(data[4:]) + bytecode = bytearray() - bytecode_len = 6 + len(raw_codes) * 5 + 2 - bytecode.append(bytecode_len << 2) # kind and length + bytecode_len = 3 + len(raw_codes) * 5 + 2 + bytecode.append(bytecode_len << 3 | 1 << 2) # kind, has_children and length bytecode.append(0b00000000) # signature prelude - bytecode.append(0b00001000) # size prelude - bytecode.extend(b"\x00\x01") # MP_QSTR_ - bytecode.extend(b"\x00\x01") # MP_QSTR_ + bytecode.append(0b00000010) # size prelude; n_info=1 + bytecode.extend(b"\x00") # simple_name: qstr index 0 (will use source filename) for idx in range(len(raw_codes)): bytecode.append(0x32) # MP_BC_MAKE_FUNCTION bytecode.append(idx) # index raw code bytecode.extend(b"\x34\x00\x59") # MP_BC_CALL_FUNCTION, 0 args, MP_BC_POP_TOP bytecode.extend(b"\x51\x63") # MP_BC_LOAD_NONE, MP_BC_RETURN_VALUE - bytecode.append(0) # n_obj - bytecode.append(len(raw_codes)) # n_raw_code - merged_mpy.extend(bytecode) + merged_mpy.append(len(raw_codes)) # n_children + for rc in raw_codes: with open(rc.mpy_source_file, "rb") as f: - f.read(4) # skip header - read_uint(f) # skip qstr_win_size + f.seek(rc.raw_code_file_offset) data = f.read() # read rest of mpy file merged_mpy.extend(data) @@ -978,7 +1513,12 @@ def main(): import argparse cmd_parser = argparse.ArgumentParser(description="A tool to work with MicroPython .mpy files.") - cmd_parser.add_argument("-d", "--dump", action="store_true", help="dump contents of files") + cmd_parser.add_argument( + "-x", "--hexdump", action="store_true", help="output an annotated hex dump of files" + ) + cmd_parser.add_argument( + "-d", "--disassemble", action="store_true", help="output disassembled contents of files" + ) cmd_parser.add_argument("-f", "--freeze", action="store_true", help="freeze files") cmd_parser.add_argument( "--merge", action="store_true", help="merge multiple .mpy files into one" @@ -1018,20 +1558,32 @@ def main(): else: config.MICROPY_QSTR_BYTES_IN_LEN = 1 config.MICROPY_QSTR_BYTES_IN_HASH = 1 - base_qstrs = {} + base_qstrs = list(qstrutil.static_qstr_list) - raw_codes = [read_mpy(file) for file in args.files] + # Load all .mpy files. + try: + compiled_modules = [read_mpy(file) for file in args.files] + except MPYReadError as er: + print(er, file=sys.stderr) + sys.exit(1) - if args.dump: - dump_mpy(raw_codes) - elif args.freeze: + if args.hexdump: + hexdump_mpy(compiled_modules) + + if args.disassemble: + if args.hexdump: + print() + disassemble_mpy(compiled_modules) + + if args.freeze: try: - freeze_mpy(base_qstrs, raw_codes) + freeze_mpy(base_qstrs, compiled_modules) except FreezeError as er: print(er, file=sys.stderr) sys.exit(1) - elif args.merge: - merged_mpy = merge_mpy(raw_codes, args.output) + + if args.merge: + merge_mpy(compiled_modules, args.output) if __name__ == "__main__": diff --git a/tools/mpy_ld.py b/tools/mpy_ld.py index 6bc1dbac05..0dac69e208 100755 --- a/tools/mpy_ld.py +++ b/tools/mpy_ld.py @@ -35,7 +35,7 @@ sys.path.append(os.path.dirname(__file__) + "/../py") import makeqstrdata as qstrutil # MicroPython constants -MPY_VERSION = 5 +MPY_VERSION = 6 MP_NATIVE_ARCH_X86 = 1 MP_NATIVE_ARCH_X64 = 2 MP_NATIVE_ARCH_ARMV7M = 5 @@ -860,11 +860,12 @@ class MPYOutput: def write_qstr(self, s): if s in qstrutil.static_qstr_list: - self.write_bytes(bytes([0, qstrutil.static_qstr_list.index(s) + 1])) + self.write_uint((qstrutil.static_qstr_list.index(s) + 1) << 1 | 1) else: s = bytes(s, "ascii") self.write_uint(len(s) << 1) self.write_bytes(s) + self.write_bytes(b"\x00") def write_reloc(self, base, offset, dest, n): need_offset = not (base == self.prev_base and offset == self.prev_offset + 1) @@ -905,14 +906,19 @@ def build_mpy(env, entry_offset, fmpy, native_qstr_vals, native_qstr_objs): out.open(fmpy) # MPY: header - out.write_bytes( - bytearray( - [ord("M"), MPY_VERSION, env.arch.mpy_feature, MP_SMALL_INT_BITS, QSTR_WINDOW_SIZE] - ) - ) + out.write_bytes(bytearray([ord("M"), MPY_VERSION, env.arch.mpy_feature, MP_SMALL_INT_BITS])) + + # MPY: n_qstr + out.write_uint(1) + + # MPY: n_obj + out.write_uint(0) + + # MPY: qstr table + out.write_qstr(fmpy) # filename # MPY: kind/len - out.write_uint(len(env.full_text) << 2 | (MP_CODE_NATIVE_VIPER - MP_CODE_BYTECODE)) + out.write_uint(len(env.full_text) << 3 | (MP_CODE_NATIVE_VIPER - MP_CODE_BYTECODE)) # MPY: machine code out.write_bytes(env.full_text) @@ -936,20 +942,15 @@ def build_mpy(env, entry_offset, fmpy, native_qstr_vals, native_qstr_objs): scope_flags |= MP_SCOPE_FLAG_VIPERBSS out.write_uint(scope_flags) - # MPY: n_obj - out.write_uint(0) - - # MPY: n_raw_code - out.write_uint(0) - - # MPY: rodata and/or bss + # MPY: bss and/or rodata if len(env.full_rodata): rodata_const_table_idx = 1 out.write_uint(len(env.full_rodata)) - out.write_bytes(env.full_rodata) if len(env.full_bss): - bss_const_table_idx = bool(env.full_rodata) + 1 + bss_const_table_idx = 2 out.write_uint(len(env.full_bss)) + if len(env.full_rodata): + out.write_bytes(env.full_rodata) # MPY: relocation information prev_kind = None From c0f2af4e862a9bb80e401894bad30378dbeac85e Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 18 Nov 2021 11:56:17 +1100 Subject: [PATCH 151/619] ports: Recompile bytecode tests now that .mpy format changed. Signed-off-by: Damien George --- ports/minimal/frozentest.mpy | Bin 196 -> 198 bytes ports/powerpc/frozentest.mpy | Bin 196 -> 198 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/ports/minimal/frozentest.mpy b/ports/minimal/frozentest.mpy index 8a89194a1048700453ca5b682c972e131a878610..cd75d65a8bad195029d131cc42f18437ffeb7523 100644 GIT binary patch delta 134 zcmX@Yc#KirmyJoDm02pSD8DK-uOzj&M6aNdfxoewtu&yLfhluhmMSB|#D0xPh9vH! zN}#-gp^>o(BSVXW03V||5NN3jG6^u7Fh&Y8DKkJQMhL|Orj&NQpCt-pH8}A!gp08V VG6}K(d5urQTdq65tB(v!1^}XBAlv`| delta 131 zcmX@cc!V*_mz7Cgp(#W_jDcM$tth`LHLoPKxJ0j@Qd2`iLtBGEkfBo(BSVXW03V||5NN3jG6^u7Fh&Y8DKkJQMhL|Orj&NQpCt-pH8}A!gp08V VG6}K(d5urQTdq65tB(v!1^}XBAlv`| delta 131 zcmX@cc!V*_mz7Cgp(#W_jDcM$tth`LHLoPKxJ0j@Qd2`iLtBGEkfB Date: Thu, 17 Feb 2022 12:01:49 +1100 Subject: [PATCH 152/619] github/workflows: Add new workflow to test .mpy file format and tools. Signed-off-by: Damien George --- .github/workflows/mpy_format.yml | 18 ++++++++++++++++++ tools/ci.sh | 17 +++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 .github/workflows/mpy_format.yml diff --git a/.github/workflows/mpy_format.yml b/.github/workflows/mpy_format.yml new file mode 100644 index 0000000000..ab90bb339a --- /dev/null +++ b/.github/workflows/mpy_format.yml @@ -0,0 +1,18 @@ +name: .mpy file format and tools + +on: + push: + pull_request: + paths: + - '.github/workflows/*.yml' + - 'tools/**' + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install packages + run: source tools/ci.sh && ci_mpy_format_setup + - name: Test mpy-tool.py + run: source tools/ci.sh && ci_mpy_format_test diff --git a/tools/ci.sh b/tools/ci.sh index 3e78ae4a05..075e5e423a 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -67,6 +67,23 @@ function ci_code_size_build { tools/metrics.py build bm | tee ~/size1 } +######################################################################################## +# .mpy file format + +function ci_mpy_format_setup { + sudo pip3 install pyelftools +} + +function ci_mpy_format_test { + # Test mpy-tool.py dump feature on bytecode + python2 ./tools/mpy-tool.py -xd ports/minimal/frozentest.mpy + python3 ./tools/mpy-tool.py -xd ports/minimal/frozentest.mpy + + # Test mpy-tool.py dump feature on native code + make -C examples/natmod/features1 + ./tools/mpy-tool.py -xd examples/natmod/features1/features1.mpy +} + ######################################################################################## # ports/cc3200 From 66fc0f45c17c8f3af68d688a432bb51318bc434e Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 17 Feb 2022 12:02:35 +1100 Subject: [PATCH 153/619] tools/ci.sh: Run urandom test scripts as part of native module tests. Signed-off-by: Damien George --- tools/ci.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/ci.sh b/tools/ci.sh index 075e5e423a..4ae21859db 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -454,7 +454,7 @@ function ci_unix_coverage_run_tests { function ci_unix_coverage_run_native_mpy_tests { MICROPYPATH=examples/natmod/features2 ./ports/unix/micropython-coverage -m features2 - (cd tests && ./run-natmodtests.py "$@" extmod/{btree*,framebuf*,uheapq*,ure*,uzlib*}.py) + (cd tests && ./run-natmodtests.py "$@" extmod/{btree*,framebuf*,uheapq*,urandom*,ure*,uzlib*}.py) } function ci_unix_32bit_setup { From 0a2895b0998bc7ab66b8339acf64fda0339d1e53 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 18 Feb 2022 16:30:24 +1100 Subject: [PATCH 154/619] tests/perf_bench: Skip bm_chaos test if random.randrange is unavailable. Signed-off-by: Damien George --- tests/perf_bench/bm_chaos.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/perf_bench/bm_chaos.py b/tests/perf_bench/bm_chaos.py index d0f1337db7..e0e9269ebd 100644 --- a/tests/perf_bench/bm_chaos.py +++ b/tests/perf_bench/bm_chaos.py @@ -218,6 +218,10 @@ class Chaosgame(object): ########################################################################### # Benchmark interface +if not hasattr(random, "randrange"): + print("SKIP") + raise SystemExit + bm_params = { (100, 50): (0.25, 100, 50, 50, 50, 1234), (1000, 1000): (0.25, 200, 400, 400, 1000, 1234), From 414b59d39c9f37c0da5ee00d270aa630c392c2fd Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 21 Feb 2022 10:36:04 +1100 Subject: [PATCH 155/619] qemu-arm: Add tests for freezing viper and asm_thumb code. Signed-off-by: Damien George --- ports/qemu-arm/test-frzmpy/frozen_asm.py | 29 ++++++++++++++++++++++ ports/qemu-arm/test-frzmpy/frozen_viper.py | 8 ++++++ tests/qemu-arm/asm_test.py | 7 ++++++ tests/qemu-arm/asm_test.py.exp | 5 ++++ tests/qemu-arm/viper_test.py | 3 +++ tests/qemu-arm/viper_test.py.exp | 1 + 6 files changed, 53 insertions(+) create mode 100644 ports/qemu-arm/test-frzmpy/frozen_asm.py create mode 100644 ports/qemu-arm/test-frzmpy/frozen_viper.py create mode 100644 tests/qemu-arm/asm_test.py create mode 100644 tests/qemu-arm/asm_test.py.exp create mode 100644 tests/qemu-arm/viper_test.py create mode 100644 tests/qemu-arm/viper_test.py.exp diff --git a/ports/qemu-arm/test-frzmpy/frozen_asm.py b/ports/qemu-arm/test-frzmpy/frozen_asm.py new file mode 100644 index 0000000000..2c7f5c92cd --- /dev/null +++ b/ports/qemu-arm/test-frzmpy/frozen_asm.py @@ -0,0 +1,29 @@ +# Test freezing inline-asm code. + +import micropython + + +@micropython.asm_thumb +def asm_add(r0, r1): + add(r0, r0, r1) + + +@micropython.asm_thumb +def asm_add1(r0) -> object: + lsl(r0, r0, 1) + add(r0, r0, 3) + + +@micropython.asm_thumb +def asm_cast_bool(r0) -> bool: + pass + + +@micropython.asm_thumb +def asm_shift_int(r0) -> int: + lsl(r0, r0, 29) + + +@micropython.asm_thumb +def asm_shift_uint(r0) -> uint: + lsl(r0, r0, 29) diff --git a/ports/qemu-arm/test-frzmpy/frozen_viper.py b/ports/qemu-arm/test-frzmpy/frozen_viper.py new file mode 100644 index 0000000000..71dfe7c3b6 --- /dev/null +++ b/ports/qemu-arm/test-frzmpy/frozen_viper.py @@ -0,0 +1,8 @@ +# Test freezing viper code. + +import micropython + + +@micropython.viper +def viper_add(x, y): + print(x + y) diff --git a/tests/qemu-arm/asm_test.py b/tests/qemu-arm/asm_test.py new file mode 100644 index 0000000000..26c7efe427 --- /dev/null +++ b/tests/qemu-arm/asm_test.py @@ -0,0 +1,7 @@ +import frozen_asm + +print(frozen_asm.asm_add(1, 2)) +print(frozen_asm.asm_add1(3)) +print(frozen_asm.asm_cast_bool(0), frozen_asm.asm_cast_bool(3)) +print(frozen_asm.asm_shift_int(4)) +print(frozen_asm.asm_shift_uint(4)) diff --git a/tests/qemu-arm/asm_test.py.exp b/tests/qemu-arm/asm_test.py.exp new file mode 100644 index 0000000000..b4be8b40e2 --- /dev/null +++ b/tests/qemu-arm/asm_test.py.exp @@ -0,0 +1,5 @@ +3 +4 +False True +-2147483648 +2147483648 diff --git a/tests/qemu-arm/viper_test.py b/tests/qemu-arm/viper_test.py new file mode 100644 index 0000000000..8e38a2b928 --- /dev/null +++ b/tests/qemu-arm/viper_test.py @@ -0,0 +1,3 @@ +import frozen_viper + +frozen_viper.viper_add(1, 2) diff --git a/tests/qemu-arm/viper_test.py.exp b/tests/qemu-arm/viper_test.py.exp new file mode 100644 index 0000000000..00750edc07 --- /dev/null +++ b/tests/qemu-arm/viper_test.py.exp @@ -0,0 +1 @@ +3 From ad1f523e7ebae5f260e9c586b28e0d75d715cf65 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 24 Feb 2022 14:05:51 +1100 Subject: [PATCH 156/619] tools/mpremote: Add "resume" and "soft-reset" commands. This makes the auto soft-reset behaviour of mpremote more logical, and now configurable via these new commands. Signed-off-by: Damien George --- docs/reference/mpremote.rst | 36 +++++++++++++++++++++++++++++++++ tools/mpremote/mpremote/main.py | 15 +++++++++++++- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/docs/reference/mpremote.rst b/docs/reference/mpremote.rst index 7a7d787a5a..8e3cf9aa5a 100644 --- a/docs/reference/mpremote.rst +++ b/docs/reference/mpremote.rst @@ -60,6 +60,25 @@ The full list of supported commands are: $ mpremote disconnect + After a disconnect, auto soft-reset is enabled. + +- resume a previous ``mpremote`` session: + + .. code-block:: bash + + $ mpremote resume + + This disables auto soft-reset. + +- perform a soft-reset of the device: + + .. code-block:: bash + + $ mpremote soft-reset + + This will clear out the Python heap and restart the interpreter. It also + disables auto soft-reset. + - enter the REPL on the connected device: .. code-block:: bash @@ -117,11 +136,28 @@ The full list of supported commands are: $ mpremote mount Multiple commands can be specified and they will be run sequentially. + + +Auto connection and soft-reset +------------------------------ + Connection and disconnection will be done automatically at the start and end of the execution of the tool, if such commands are not explicitly given. Automatic connection will search for the first available serial device. If no action is specified then the REPL will be entered. +Once connected to a device, ``mpremote`` will automatically soft-reset the +device if needed. This clears the Python heap and restarts the interpreter, +making sure that subsequent Python code executes in a fresh environment. Auto +soft-reset is performed the first time one of the following commands are +executed: ``mount``, ``eval``, ``exec``, ``run``, ``fs``. After doing a +soft-reset for the first time, it will not be done again automatically, until a +``disconnect`` command is issued. + +Auto soft-reset behaviour can be controlled by the ``resume`` command. And the +``soft-reset`` command can be used to perform an explicit soft reset. + + Shortcuts --------- diff --git a/tools/mpremote/mpremote/main.py b/tools/mpremote/mpremote/main.py index 1073bec7fa..a53ea66f39 100644 --- a/tools/mpremote/mpremote/main.py +++ b/tools/mpremote/mpremote/main.py @@ -39,6 +39,8 @@ _COMMANDS = { or any valid device name/path""", ), "disconnect": (False, False, 0, "disconnect current device"), + "resume": (False, False, 0, "resume a previous mpremote session (will not auto soft-reset)"), + "soft-reset": (False, True, 0, "perform a soft-reset of the device"), "mount": (True, False, 1, "mount local directory on device"), "repl": ( False, @@ -434,6 +436,7 @@ def main(): args = sys.argv[1:] pyb = None + auto_soft_reset = True did_action = False try: @@ -460,13 +463,19 @@ def main(): elif cmd == "help": print_help() sys.exit(0) + elif cmd == "resume": + auto_soft_reset = False + continue + + # The following commands need a connection, and either a raw or friendly REPL. if pyb is None: pyb = do_connect(["auto"]) if need_raw_repl: if not pyb.in_raw_repl: - pyb.enter_raw_repl() + pyb.enter_raw_repl(soft_reset=auto_soft_reset) + auto_soft_reset = False else: if pyb.in_raw_repl: pyb.exit_raw_repl() @@ -476,6 +485,10 @@ def main(): if cmd == "disconnect": do_disconnect(pyb) pyb = None + auto_soft_reset = True + elif cmd == "soft-reset": + pyb.enter_raw_repl(soft_reset=True) + auto_soft_reset = False elif cmd == "mount": path = args.pop(0) pyb.mount_local(path) From d6564a315967f80aafaa4c117892d0b21b0cb2bc Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 25 Feb 2022 13:10:45 +1100 Subject: [PATCH 157/619] tools/mpremote: Add "umount" command. Signed-off-by: Damien George --- docs/reference/mpremote.rst | 6 ++++++ tools/mpremote/mpremote/main.py | 3 +++ tools/mpremote/mpremote/pyboardextended.py | 1 + 3 files changed, 10 insertions(+) diff --git a/docs/reference/mpremote.rst b/docs/reference/mpremote.rst index 8e3cf9aa5a..345927ef7f 100644 --- a/docs/reference/mpremote.rst +++ b/docs/reference/mpremote.rst @@ -135,6 +135,12 @@ The full list of supported commands are: $ mpremote mount +- unmount the local directory from the remote device: + + .. code-block:: bash + + $ mpremote umount + Multiple commands can be specified and they will be run sequentially. diff --git a/tools/mpremote/mpremote/main.py b/tools/mpremote/mpremote/main.py index a53ea66f39..524b2ec80a 100644 --- a/tools/mpremote/mpremote/main.py +++ b/tools/mpremote/mpremote/main.py @@ -42,6 +42,7 @@ _COMMANDS = { "resume": (False, False, 0, "resume a previous mpremote session (will not auto soft-reset)"), "soft-reset": (False, True, 0, "perform a soft-reset of the device"), "mount": (True, False, 1, "mount local directory on device"), + "umount": (True, False, 0, "unmount the local directory"), "repl": ( False, True, @@ -493,6 +494,8 @@ def main(): path = args.pop(0) pyb.mount_local(path) print(f"Local directory {path} is mounted at /remote") + elif cmd == "umount": + pyb.umount_local() elif cmd in ("exec", "eval", "run"): follow = True if args[0] == "--no-follow": diff --git a/tools/mpremote/mpremote/pyboardextended.py b/tools/mpremote/mpremote/pyboardextended.py index 6817e7bbf4..69cbf02edd 100644 --- a/tools/mpremote/mpremote/pyboardextended.py +++ b/tools/mpremote/mpremote/pyboardextended.py @@ -687,3 +687,4 @@ class PyboardExtended(Pyboard): if self.mounted: self.exec_('uos.umount("/remote")') self.mounted = False + self.serial = self.serial.orig_serial From c2e8a5acd267e55a11a709d44f0385d0a40f3ec9 Mon Sep 17 00:00:00 2001 From: stijn Date: Tue, 22 Feb 2022 15:42:50 +0100 Subject: [PATCH 158/619] windows: Unify project file headers. The xmlns attribute is required for older msbuild version (e.g. for VS2015). Add it where needed, and reorder the attributes so all files look the same. --- mpy-cross/mpy-cross.vcxproj | 2 +- ports/windows/micropython.vcxproj | 2 +- ports/windows/msvc/genhdr.targets | 2 +- ports/windows/msvc/sources.props | 2 +- ports/windows/variants/dev/mpconfigvariant.props | 3 ++- ports/windows/variants/standard/mpconfigvariant.props | 3 ++- 6 files changed, 8 insertions(+), 6 deletions(-) diff --git a/mpy-cross/mpy-cross.vcxproj b/mpy-cross/mpy-cross.vcxproj index e70b29ae14..53cb0fa1fe 100644 --- a/mpy-cross/mpy-cross.vcxproj +++ b/mpy-cross/mpy-cross.vcxproj @@ -1,5 +1,5 @@  - + Debug diff --git a/ports/windows/micropython.vcxproj b/ports/windows/micropython.vcxproj index 6817d6ed13..d5e3f57d8b 100644 --- a/ports/windows/micropython.vcxproj +++ b/ports/windows/micropython.vcxproj @@ -1,5 +1,5 @@  - + Debug diff --git a/ports/windows/msvc/genhdr.targets b/ports/windows/msvc/genhdr.targets index ed97a455d3..b53894f21d 100644 --- a/ports/windows/msvc/genhdr.targets +++ b/ports/windows/msvc/genhdr.targets @@ -1,5 +1,5 @@ - + diff --git a/ports/windows/msvc/sources.props b/ports/windows/msvc/sources.props index e5109eecc8..a6dfd48aa6 100644 --- a/ports/windows/msvc/sources.props +++ b/ports/windows/msvc/sources.props @@ -1,5 +1,5 @@ - + diff --git a/ports/windows/variants/dev/mpconfigvariant.props b/ports/windows/variants/dev/mpconfigvariant.props index d01d74b441..5f78d6e62a 100644 --- a/ports/windows/variants/dev/mpconfigvariant.props +++ b/ports/windows/variants/dev/mpconfigvariant.props @@ -1,4 +1,5 @@ - + + micropython-dev diff --git a/ports/windows/variants/standard/mpconfigvariant.props b/ports/windows/variants/standard/mpconfigvariant.props index 0157102fdf..3cbb1821f4 100644 --- a/ports/windows/variants/standard/mpconfigvariant.props +++ b/ports/windows/variants/standard/mpconfigvariant.props @@ -1,4 +1,5 @@ - + + micropython From 546e213265872cc6393af83a94bb2af5092d4348 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 25 Feb 2022 16:52:58 +1100 Subject: [PATCH 159/619] esp32/boards: Add specific deploy instructions for S3 variant. Signed-off-by: Damien George --- ports/esp32/boards/GENERIC_S3/board.json | 2 +- ports/esp32/boards/GENERIC_S3_SPIRAM/board.json | 2 +- ports/esp32/boards/deploy_s3.md | 14 ++++++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 ports/esp32/boards/deploy_s3.md diff --git a/ports/esp32/boards/GENERIC_S3/board.json b/ports/esp32/boards/GENERIC_S3/board.json index 058fb7dff0..0db9b32503 100644 --- a/ports/esp32/boards/GENERIC_S3/board.json +++ b/ports/esp32/boards/GENERIC_S3/board.json @@ -1,6 +1,6 @@ { "deploy": [ - "../deploy.md" + "../deploy_s3.md" ], "docs": "", "features": [ diff --git a/ports/esp32/boards/GENERIC_S3_SPIRAM/board.json b/ports/esp32/boards/GENERIC_S3_SPIRAM/board.json index ee2cb8d3f4..3b405d4a12 100644 --- a/ports/esp32/boards/GENERIC_S3_SPIRAM/board.json +++ b/ports/esp32/boards/GENERIC_S3_SPIRAM/board.json @@ -1,6 +1,6 @@ { "deploy": [ - "../deploy.md" + "../deploy_s3.md" ], "docs": "", "features": [ diff --git a/ports/esp32/boards/deploy_s3.md b/ports/esp32/boards/deploy_s3.md new file mode 100644 index 0000000000..7f564a95e3 --- /dev/null +++ b/ports/esp32/boards/deploy_s3.md @@ -0,0 +1,14 @@ +Program your board using the esptool.py program, found [here](https://github.com/espressif/esptool). + +If you are putting MicroPython on your board for the first time then you should +first erase the entire flash using: + +```bash +esptool.py --chip esp32s3 --port /dev/ttyACM0 erase_flash +``` + +From then on program the firmware starting at address 0: + +```bash +esptool.py --chip esp32s3 --port /dev/ttyACM0 write_flash -z 0 board-20210902-v1.17.bin +``` From 0a217624e1d26899986d3e79eb604652da540b00 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 25 Feb 2022 18:24:03 +1100 Subject: [PATCH 160/619] tools/upip.py: Remove unused op_basename() function. It seems this was never used, at least not since its inclusion in this repository. Signed-off-by: Damien George --- tools/upip.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tools/upip.py b/tools/upip.py index 9fb8726420..2932cca50c 100644 --- a/tools/upip.py +++ b/tools/upip.py @@ -41,10 +41,6 @@ def op_split(path): return (head, r[1]) -def op_basename(path): - return op_split(path)[1] - - # Expects *file* name def _makedirs(name, mode=0o777): ret = False @@ -205,7 +201,6 @@ def install_pkg(pkg_spec, install_path): assert len(packages) == 1 package_url = packages[0]["url"] print("Installing %s %s from %s" % (pkg_spec, latest_ver, package_url)) - package_fname = op_basename(package_url) f1 = url_open(package_url) try: f2 = uzlib.DecompIO(f1, gzdict_sz) From 5c46721a1cc32bdb8f700955f9b441a08056f7b0 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sat, 26 Feb 2022 07:55:53 +0100 Subject: [PATCH 161/619] tools/mpy-tool.py: Fix frozen comment generation to escape chars. That caused the compile of frozen_content.c to fail if characters like backslash were in a short string. Thanks to @hippy for identifying the spot to change. --- tools/mpy-tool.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index da6352dab7..73753094ec 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -861,7 +861,7 @@ class RawCodeBytecode(RawCode): fmt, sz, arg = mp_opcode_decode(bc, ip) opcode_name = Opcodes.mapping[bc[ip]] if fmt == MP_BC_FORMAT_QSTR: - opcode_name += " " + self.qstr_table[arg].str + opcode_name += " " + repr(self.qstr_table[arg].str) elif fmt in (MP_BC_FORMAT_VAR_UINT, MP_BC_FORMAT_OFFSET): opcode_name += " %u" % arg print( From c4b8dae4387b3a01cf4139c75f0852c5a6ffc108 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 28 Feb 2022 18:51:45 +1100 Subject: [PATCH 162/619] tests/unix: Add coverage test for freezing various objects. Signed-off-by: Damien George --- ports/unix/variants/coverage/frzmpy/frzmpy3.py | 6 ++++++ tests/unix/extra_coverage.py | 3 +++ tests/unix/extra_coverage.py.exp | 5 +++++ 3 files changed, 14 insertions(+) create mode 100644 ports/unix/variants/coverage/frzmpy/frzmpy3.py diff --git a/ports/unix/variants/coverage/frzmpy/frzmpy3.py b/ports/unix/variants/coverage/frzmpy/frzmpy3.py new file mode 100644 index 0000000000..617fac5523 --- /dev/null +++ b/ports/unix/variants/coverage/frzmpy/frzmpy3.py @@ -0,0 +1,6 @@ +# Test freezing objects that may not be handled well by the build process. + +print("\\") +print("\nX") +print(repr("\x1b")) +print(b"\x00\xff") diff --git a/tests/unix/extra_coverage.py b/tests/unix/extra_coverage.py index 8ea27cbf2f..bb22485026 100644 --- a/tests/unix/extra_coverage.py +++ b/tests/unix/extra_coverage.py @@ -89,6 +89,9 @@ try: except ZeroDivisionError: print("ZeroDivisionError") +# test importing various objects +import frzmpy3 + # test for MP_QSTR_NULL regression from frzqstr import returns_NULL diff --git a/tests/unix/extra_coverage.py.exp b/tests/unix/extra_coverage.py.exp index 8ee233a712..1a5a2cde85 100644 --- a/tests/unix/extra_coverage.py.exp +++ b/tests/unix/extra_coverage.py.exp @@ -174,4 +174,9 @@ frzstr_pkg2.mod frzmpy_pkg2.mod 1 ZeroDivisionError +\ + +X +'\x1b' +b'\x00\xff' NULL From 8626dcd6238e6a85a366f64dccf33724142653b6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 28 Feb 2022 18:54:10 +1100 Subject: [PATCH 163/619] tools/ci.sh: Run performance benchmark as part of all unix test runs. This tests that the performance benchmarks run without error. Signed-off-by: Damien George --- .github/workflows/ports_unix.yml | 2 -- tools/ci.sh | 5 +---- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/ports_unix.yml b/.github/workflows/ports_unix.yml index 4e75172b64..dbd213cdff 100644 --- a/.github/workflows/ports_unix.yml +++ b/.github/workflows/ports_unix.yml @@ -45,8 +45,6 @@ jobs: run: source tools/ci.sh && ci_unix_standard_build - name: Run main test suite run: source tools/ci.sh && ci_unix_standard_run_tests - - name: Run performance benchmarks - run: source tools/ci.sh && ci_unix_standard_run_perfbench - name: Print failures if: failure() run: tests/run-tests.py --print-failures diff --git a/tools/ci.sh b/tools/ci.sh index 4ae21859db..dee4b387bf 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -385,6 +385,7 @@ function ci_unix_run_tests_full_helper { fi make -C ports/unix VARIANT=$variant "$@" test_full (cd tests && MICROPY_CPYTHON3=python3 MICROPY_MICROPYTHON=../ports/unix/$micropython ./run-multitests.py multi_net/*.py) + (cd tests && MICROPY_CPYTHON3=python3 MICROPY_MICROPYTHON=../ports/unix/$micropython ./run-perfbench.py 1000 1000) } function ci_native_mpy_modules_build { @@ -424,10 +425,6 @@ function ci_unix_standard_run_tests { ci_unix_run_tests_full_helper standard } -function ci_unix_standard_run_perfbench { - (cd tests && MICROPY_CPYTHON3=python3 MICROPY_MICROPYTHON=../ports/unix/micropython ./run-perfbench.py 1000 1000) -} - function ci_unix_dev_build { ci_unix_build_helper VARIANT=dev } From 3c2aa5ff93a3b12723c7ca794aa9c583891c349b Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 28 Feb 2022 18:58:54 +1100 Subject: [PATCH 164/619] unix/.gitignore: Remove *.py from ignore list. .py files are valid source files and shouldn't be ignored. This line was from the early days when .py files in the unix directory were used for testing. Signed-off-by: Damien George --- ports/unix/.gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/ports/unix/.gitignore b/ports/unix/.gitignore index 6745218688..3ca8f6cb27 100644 --- a/ports/unix/.gitignore +++ b/ports/unix/.gitignore @@ -1,4 +1,3 @@ micropython micropython-* -*.py *.gcov From 795370ca23ac8b0f3589ec1d4b34d3d7f3b175eb Mon Sep 17 00:00:00 2001 From: stijn Date: Mon, 28 Feb 2022 10:20:47 +0100 Subject: [PATCH 165/619] py/bc.h: Fix C++ compilation of public API. Casts between unrelated types must be explicit. Regression in f2040bfc7ee033e48acef9f289790f3b4e6b74e5 --- py/bc.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/py/bc.h b/py/bc.h index 7d761e30e6..5710f4d243 100644 --- a/py/bc.h +++ b/py/bc.h @@ -287,8 +287,8 @@ static inline void mp_module_context_alloc_tables(mp_module_context_t *context, size_t nq = (n_qstr * sizeof(qstr_short_t) + sizeof(mp_uint_t) - 1) / sizeof(mp_uint_t); size_t no = n_obj; mp_uint_t *mem = m_new(mp_uint_t, nq + no); - context->constants.qstr_table = (void *)(mem); - context->constants.obj_table = (void *)(mem + nq); + context->constants.qstr_table = (qstr_short_t *)mem; + context->constants.obj_table = (mp_obj_t *)(mem + nq); #else if (n_obj == 0) { context->constants.obj_table = NULL; From 2d47020e15b82b7f021e0b52dabe75baf5cb4c4d Mon Sep 17 00:00:00 2001 From: YoungJoon Chun Date: Wed, 16 Feb 2022 18:47:05 +0900 Subject: [PATCH 166/619] rp2/mpthreadport: Fix memory corruption when thread is created in core1. The stack (and arg) of core1 is itself a root pointer, not just the entries in it. Without this fix the GC could reclaim the entire stack (and argument object). Fixes issues #7124 and #7981. --- ports/rp2/mpthreadport.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ports/rp2/mpthreadport.c b/ports/rp2/mpthreadport.c index 8a36cfca75..cdb5945d21 100644 --- a/ports/rp2/mpthreadport.c +++ b/ports/rp2/mpthreadport.c @@ -52,12 +52,12 @@ void mp_thread_deinit(void) { } void mp_thread_gc_others(void) { - if (get_core_num() == 0) { - // GC running on core0, trace core1's stack, if it's running. - if (core1_entry != NULL) { - gc_collect_root((void **)core1_stack, core1_stack_num_words); - } - } else { + if (core1_entry != NULL) { + // Collect core1's stack if it is active. + gc_collect_root((void **)&core1_stack, 1); + gc_collect_root((void **)&core1_arg, 1); + } + if (get_core_num() == 1) { // GC running on core1, trace core0's stack. gc_collect_root((void **)&__StackBottom, (&__StackTop - &__StackBottom) / sizeof(uintptr_t)); } From 2cc9232781d818b8a317dc7b173ecd5b752867e7 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Thu, 24 Feb 2022 14:20:56 +1100 Subject: [PATCH 167/619] esp32/uart: Correctly init low-level UART driver for REPL. uart_driver_install() needs to be called to set up the UART correctly. --- ports/esp32/main.c | 2 +- ports/esp32/mphalport.c | 15 ++----------- ports/esp32/uart.c | 49 ++++++++++++++++++++++++++++++++++++++--- ports/esp32/uart.h | 3 ++- 4 files changed, 51 insertions(+), 18 deletions(-) diff --git a/ports/esp32/main.c b/ports/esp32/main.c index 6c5edbb356..a1c27c0a26 100644 --- a/ports/esp32/main.c +++ b/ports/esp32/main.c @@ -93,7 +93,7 @@ void mp_task(void *pvParameter) { #elif CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG usb_serial_jtag_init(); #else - uart_init(); + uart_stdout_init(); #endif machine_init(); diff --git a/ports/esp32/mphalport.c b/ports/esp32/mphalport.c index de1b173ac4..15a8dce1f3 100644 --- a/ports/esp32/mphalport.c +++ b/ports/esp32/mphalport.c @@ -33,16 +33,6 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" -#if CONFIG_IDF_TARGET_ESP32 -#include "esp32/rom/uart.h" -#elif CONFIG_IDF_TARGET_ESP32C3 -#include "esp32c3/rom/uart.h" -#elif CONFIG_IDF_TARGET_ESP32S2 -#include "esp32s2/rom/uart.h" -#elif CONFIG_IDF_TARGET_ESP32S3 -#include "esp32s3/rom/uart.h" -#endif - #include "py/obj.h" #include "py/objstr.h" #include "py/stream.h" @@ -54,6 +44,7 @@ #include "mphalport.h" #include "usb.h" #include "usb_serial_jtag.h" +#include "uart.h" TaskHandle_t mp_main_task_handle; @@ -122,9 +113,7 @@ void mp_hal_stdout_tx_strn(const char *str, size_t len) { #elif CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG usb_serial_jtag_tx_strn(str, len); #else - for (uint32_t i = 0; i < len; ++i) { - uart_tx_one_char(str[i]); - } + uart_stdout_tx_strn(str, len); #endif if (release_gil) { MP_THREAD_GIL_ENTER(); diff --git a/ports/esp32/uart.c b/ports/esp32/uart.c index 58fc0c6c61..ca4ac67cde 100644 --- a/ports/esp32/uart.c +++ b/ports/esp32/uart.c @@ -33,13 +33,56 @@ #include "py/runtime.h" #include "py/mphal.h" +#include "uart.h" + +#ifndef MICROPY_HW_UART_REPL +#define MICROPY_HW_UART_REPL (UART_NUM_0) +#endif + +#ifndef MICROPY_HW_UART_REPL_BAUD +#define MICROPY_HW_UART_REPL_BAUD (115200) +#endif STATIC void uart_irq_handler(void *arg); -void uart_init(void) { +void uart_stdout_init(void) { + uart_config_t uartcfg = { + .baud_rate = MICROPY_HW_UART_REPL_BAUD, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + .rx_flow_ctrl_thresh = 0 + }; + uart_param_config(MICROPY_HW_UART_REPL, &uartcfg); + + const uint32_t rxbuf = 129; // IDF requires > 128 min + const uint32_t txbuf = 0; + + uart_driver_install(MICROPY_HW_UART_REPL, rxbuf, txbuf, 0, NULL, 0); + uart_isr_handle_t handle; - uart_isr_register(UART_NUM_0, uart_irq_handler, NULL, ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM, &handle); - uart_enable_rx_intr(UART_NUM_0); + uart_isr_free(MICROPY_HW_UART_REPL); + uart_isr_register(MICROPY_HW_UART_REPL, uart_irq_handler, NULL, ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM, &handle); + uart_enable_rx_intr(MICROPY_HW_UART_REPL); +} + +int uart_stdout_tx_strn(const char *str, size_t len) { + size_t remaining = len; + // TODO add a timeout + for (;;) { + int ret = uart_tx_chars(MICROPY_HW_UART_REPL, str, remaining); + if (ret == -1) { + return -1; + } + remaining -= ret; + if (remaining <= 0) { + break; + } + str += ret; + ulTaskNotifyTake(pdFALSE, 1); + } + return len - remaining; } // all code executed in ISR must be in IRAM, and any const data must be in DRAM diff --git a/ports/esp32/uart.h b/ports/esp32/uart.h index 264c8b8949..439be521c4 100644 --- a/ports/esp32/uart.h +++ b/ports/esp32/uart.h @@ -28,6 +28,7 @@ #ifndef MICROPY_INCLUDED_ESP32_UART_H #define MICROPY_INCLUDED_ESP32_UART_H -void uart_init(void); +void uart_stdout_init(void); +int uart_stdout_tx_strn(const char *str, size_t len); #endif // MICROPY_INCLUDED_ESP32_UART_H From 919e586e46af67e7eaa819b0f187b600a3165526 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Thu, 24 Feb 2022 14:30:50 +1100 Subject: [PATCH 168/619] esp32/machine_uart: Allow limited configuration of REPL UART. Some applications may want to adjust the hard coded 115200 REPL buadrate, and this commit allows it to be changed dynamically via machine.UART(0). --- ports/esp32/machine_uart.c | 26 ++++++++++++++------------ ports/esp32/uart.c | 8 -------- ports/esp32/uart.h | 8 ++++++++ 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/ports/esp32/machine_uart.c b/ports/esp32/machine_uart.c index 3c90a72150..aa8d494ec8 100644 --- a/ports/esp32/machine_uart.c +++ b/ports/esp32/machine_uart.c @@ -35,6 +35,7 @@ #include "py/stream.h" #include "py/mperrno.h" #include "modmachine.h" +#include "uart.h" #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 1, 0) #define UART_INV_TX UART_INVERSE_TXD @@ -151,6 +152,10 @@ STATIC void machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, co if (args[ARG_txbuf].u_int >= 0 || args[ARG_rxbuf].u_int >= 0) { // must reinitialise driver to change the tx/rx buffer size + if (self->uart_num == MICROPY_HW_UART_REPL) { + mp_raise_ValueError(MP_ERROR_TEXT("UART buffer size is fixed")); + } + if (args[ARG_txbuf].u_int >= 0) { self->txbuf = args[ARG_txbuf].u_int; } @@ -291,12 +296,6 @@ STATIC mp_obj_t machine_uart_make_new(const mp_obj_type_t *type, size_t n_args, mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("UART(%d) does not exist"), uart_num); } - // Attempts to use UART0 from Python has resulted in all sorts of fun errors. - // FIXME: UART0 is disabled for now. - if (uart_num == UART_NUM_0) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("UART(%d) is disabled (dedicated to REPL)"), uart_num); - } - // Defaults uart_config_t uartcfg = { .baud_rate = 115200, @@ -338,14 +337,17 @@ STATIC mp_obj_t machine_uart_make_new(const mp_obj_type_t *type, size_t n_args, #endif } - // Remove any existing configuration - uart_driver_delete(self->uart_num); + // Only reset the driver if it's not the REPL UART. + if (uart_num != MICROPY_HW_UART_REPL) { + // Remove any existing configuration + uart_driver_delete(self->uart_num); - // init the peripheral - // Setup - uart_param_config(self->uart_num, &uartcfg); + // init the peripheral + // Setup + uart_param_config(self->uart_num, &uartcfg); - uart_driver_install(uart_num, self->rxbuf, self->txbuf, 0, NULL, 0); + uart_driver_install(uart_num, self->rxbuf, self->txbuf, 0, NULL, 0); + } mp_map_t kw_args; mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); diff --git a/ports/esp32/uart.c b/ports/esp32/uart.c index ca4ac67cde..f6493dc796 100644 --- a/ports/esp32/uart.c +++ b/ports/esp32/uart.c @@ -35,14 +35,6 @@ #include "py/mphal.h" #include "uart.h" -#ifndef MICROPY_HW_UART_REPL -#define MICROPY_HW_UART_REPL (UART_NUM_0) -#endif - -#ifndef MICROPY_HW_UART_REPL_BAUD -#define MICROPY_HW_UART_REPL_BAUD (115200) -#endif - STATIC void uart_irq_handler(void *arg); void uart_stdout_init(void) { diff --git a/ports/esp32/uart.h b/ports/esp32/uart.h index 439be521c4..f32d5cf342 100644 --- a/ports/esp32/uart.h +++ b/ports/esp32/uart.h @@ -28,6 +28,14 @@ #ifndef MICROPY_INCLUDED_ESP32_UART_H #define MICROPY_INCLUDED_ESP32_UART_H +#ifndef MICROPY_HW_UART_REPL +#define MICROPY_HW_UART_REPL (UART_NUM_0) +#endif + +#ifndef MICROPY_HW_UART_REPL_BAUD +#define MICROPY_HW_UART_REPL_BAUD (115200) +#endif + void uart_stdout_init(void); int uart_stdout_tx_strn(const char *str, size_t len); From 665f0e2a68dfa4944f60e85a00d0543bb3d7fee8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20de=20Giessen?= Date: Wed, 23 Feb 2022 17:28:32 +0100 Subject: [PATCH 169/619] esp32: Sleep one tick in MICROPY_EVENT_POLL_HOOK. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If MicroPython threads are enabled, loops waiting for an incoming event should release the GIL and suspend, allowing other tasks to run while they wait. Prior to this commit, the problem can easily be observed by running a thread that is both busy and regularly releases the GIL (for example a loop doing something then sleeping a few ms after each iteration). When the main task is at the REPL, the thread is significantly stalled. If the main task is manually made to release the GIL (for example, by calling utime.sleep_ms(500)) the other thread can be seen immediately working at the expected speed again. Additionally, there are various instances in where blocking functions run MICROPY_EVENT_POLL_HOOK in a loop while they wait for a certain event/ condition. For example the uselect methods poll objects to determine whether data is available, but uses 100% of CPU while it does, constantly calling MICROPY_EVENT_POLL_HOOK in the process. The MICROPY_EVENT_POLL_HOOK macro is only ever used in waiting loops, where (if threads are enabled) it makes sense to yield for a single tick so that these loops do not consume all CPU cycles but instead other threads may execute. (In fact, the thing these loops wait for may even indirectly or directly depend on another task being able to run.) This change moves the sleep that was inside the REPL input function to inside the MICROPY_EVENT_POLL_HOOK macro, where the GIL is already being released, solving both the blocking REPL issue and the 100% CPU use issue at the same time. Signed-off-by: Daniël van de Giessen --- ports/esp32/mpconfigport.h | 1 + ports/esp32/mphalport.c | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index 1ea19ada01..d4f542f668 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -195,6 +195,7 @@ void *esp_native_code_commit(void *, size_t, void *); mp_handle_pending(true); \ MICROPY_PY_USOCKET_EVENTS_HANDLER \ MP_THREAD_GIL_EXIT(); \ + ulTaskNotifyTake(pdFALSE, 1); \ MP_THREAD_GIL_ENTER(); \ } while (0); #else diff --git a/ports/esp32/mphalport.c b/ports/esp32/mphalport.c index 15a8dce1f3..41e6e6ec00 100644 --- a/ports/esp32/mphalport.c +++ b/ports/esp32/mphalport.c @@ -98,7 +98,6 @@ int mp_hal_stdin_rx_chr(void) { return c; } MICROPY_EVENT_POLL_HOOK - ulTaskNotifyTake(pdFALSE, 1); } } From 55a0125a154a9c13363d900e0caccd218bce0a54 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sun, 27 Feb 2022 15:22:21 +0100 Subject: [PATCH 170/619] esp32/machine_pwm: Always set the duty cycle when setting the frequency. If setting the frequency to a value used already by an existing timer, this timer will be used. But still, the duty cycle for that channel may have to be changed. Fixes issues #8306 and #8345. --- ports/esp32/machine_pwm.c | 58 +++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 23 deletions(-) mode change 100755 => 100644 ports/esp32/machine_pwm.c diff --git a/ports/esp32/machine_pwm.c b/ports/esp32/machine_pwm.c old mode 100755 new mode 100644 index 9308fcd3cb..5baaa693c6 --- a/ports/esp32/machine_pwm.c +++ b/ports/esp32/machine_pwm.c @@ -74,7 +74,8 @@ STATIC ledc_timer_config_t timers[PWM_TIMER_MAX]; // Params for PWM operation // 5khz is default frequency #define PWM_FREQ (5000) -// default 10-bit resolution (compatible with esp8266 PWM) + +// 10-bit resolution (compatible with esp8266 PWM) #define PWM_RES_10_BIT (LEDC_TIMER_10_BIT) // Maximum duty value on 10-bit resolution @@ -84,15 +85,17 @@ STATIC ledc_timer_config_t timers[PWM_TIMER_MAX]; // duty_u16() and duty_ns() use 16-bit resolution or less // Possible highest resolution in device -#if CONFIG_IDF_TARGET_ESP32 -#define HIGHEST_PWM_RES (LEDC_TIMER_16_BIT) // 20 bit for ESP32, but 16 bit is used +#if (LEDC_TIMER_BIT_MAX - 1) < LEDC_TIMER_16_BIT +#define HIGHEST_PWM_RES (LEDC_TIMER_BIT_MAX - 1) #else -#define HIGHEST_PWM_RES (LEDC_TIMER_BIT_MAX - 1) // 14 bit is used +#define HIGHEST_PWM_RES (LEDC_TIMER_16_BIT) // 20 bit for ESP32, but 16 bit is used #endif // Duty resolution of user interface in `duty_u16()` and `duty_u16` parameter in constructor/initializer #define UI_RES_16_BIT (16) // Maximum duty value on highest user interface resolution #define UI_MAX_DUTY ((1 << UI_RES_16_BIT) - 1) +// How much to shift from the HIGHEST_PWM_RES duty resolution to the user interface duty resolution UI_RES_16_BIT +#define UI_RES_SHIFT (UI_RES_16_BIT - HIGHEST_PWM_RES) // 0 for ESP32, 2 for S2, S3, C3 // If the PWM frequency is less than EMPIRIC_FREQ, then LEDC_REF_CLK_HZ(1 MHz) source is used, else LEDC_APB_CLK_HZ(80 MHz) source is used #define EMPIRIC_FREQ (10) // Hz @@ -108,7 +111,7 @@ typedef struct _machine_pwm_obj_t { int mode; int channel; int timer; - int duty_x; // PWM_RES_10_BIT if duty(), UI_RES_16_BIT if duty_u16(), -UI_RES_16_BIT if duty_ns() + int duty_x; // PWM_RES_10_BIT if duty(), HIGHEST_PWM_RES if duty_u16(), -HIGHEST_PWM_RES if duty_ns() int duty_u10; // stored values from previous duty setters int duty_u16; // - / - int duty_ns; // - / - @@ -237,7 +240,6 @@ STATIC void set_freq(machine_pwm_obj_t *self, unsigned int freq, ledc_timer_conf } // Configure the new resolution and frequency - unsigned int save_duty_resolution = timer->duty_resolution; timer->duty_resolution = res; timer->freq_hz = freq; timer->clk_cfg = LEDC_USE_APB_CLK; @@ -258,17 +260,15 @@ STATIC void set_freq(machine_pwm_obj_t *self, unsigned int freq, ledc_timer_conf if (self->mode == LEDC_LOW_SPEED_MODE) { check_esp_err(ledc_timer_rst(self->mode, self->timer)); } + } - // Save the same duty cycle when frequency is changed - if (save_duty_resolution != timer->duty_resolution) { - if (self->duty_x == UI_RES_16_BIT) { - set_duty_u16(self, self->duty_u16); - } else if (self->duty_x == PWM_RES_10_BIT) { - set_duty_u10(self, self->duty_u10); - } else if (self->duty_x == -UI_RES_16_BIT) { - set_duty_ns(self, self->duty_ns); - } - } + // Save the same duty cycle when frequency is changed + if (self->duty_x == HIGHEST_PWM_RES) { + set_duty_u16(self, self->duty_u16); + } else if (self->duty_x == PWM_RES_10_BIT) { + set_duty_u10(self, self->duty_u10); + } else if (self->duty_x == -HIGHEST_PWM_RES) { + set_duty_ns(self, self->duty_ns); } } @@ -293,11 +293,18 @@ STATIC int duty_to_ns(machine_pwm_obj_t *self, int duty) { #define get_duty_raw(self) ledc_get_duty(self->mode, self->channel) STATIC uint32_t get_duty_u16(machine_pwm_obj_t *self) { - return ledc_get_duty(self->mode, self->channel) << (UI_RES_16_BIT - timers[TIMER_IDX(self->mode, self->timer)].duty_resolution); + int resolution = timers[TIMER_IDX(self->mode, self->timer)].duty_resolution; + int duty = ledc_get_duty(self->mode, self->channel); + if (resolution <= UI_RES_16_BIT) { + duty <<= (UI_RES_16_BIT - resolution); + } else { + duty >>= (resolution - UI_RES_16_BIT); + } + return duty; } STATIC uint32_t get_duty_u10(machine_pwm_obj_t *self) { - return get_duty_u16(self) >> (UI_RES_16_BIT - LEDC_TIMER_10_BIT); + return get_duty_u16(self) >> 6; // Scale down from 16 bit to 10 bit resolution } STATIC uint32_t get_duty_ns(machine_pwm_obj_t *self) { @@ -309,7 +316,12 @@ STATIC void set_duty_u16(machine_pwm_obj_t *self, int duty) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty_u16 must be from 0 to %d"), UI_MAX_DUTY); } ledc_timer_config_t timer = timers[TIMER_IDX(self->mode, self->timer)]; - int channel_duty = duty >> (UI_RES_16_BIT - timer.duty_resolution); + int channel_duty; + if (timer.duty_resolution <= UI_RES_16_BIT) { + channel_duty = duty >> (UI_RES_16_BIT - timer.duty_resolution); + } else { + channel_duty = duty << (timer.duty_resolution - UI_RES_16_BIT); + } int max_duty = (1 << timer.duty_resolution) - 1; if (channel_duty < 0) { channel_duty = 0; @@ -333,7 +345,7 @@ STATIC void set_duty_u16(machine_pwm_obj_t *self, int duty) { } */ - self->duty_x = UI_RES_16_BIT; + self->duty_x = HIGHEST_PWM_RES; self->duty_u16 = duty; } @@ -341,7 +353,7 @@ STATIC void set_duty_u10(machine_pwm_obj_t *self, int duty) { if ((duty < 0) || (duty > MAX_DUTY_U10)) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty must be from 0 to %u"), MAX_DUTY_U10); } - set_duty_u16(self, duty << (UI_RES_16_BIT - LEDC_TIMER_10_BIT)); + set_duty_u16(self, duty << (UI_RES_16_BIT - PWM_RES_10_BIT)); self->duty_x = PWM_RES_10_BIT; self->duty_u10 = duty; } @@ -351,7 +363,7 @@ STATIC void set_duty_ns(machine_pwm_obj_t *self, int ns) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty_ns must be from 0 to %d ns"), duty_to_ns(self, UI_MAX_DUTY)); } set_duty_u16(self, ns_to_duty(self, ns)); - self->duty_x = -UI_RES_16_BIT; + self->duty_x = -HIGHEST_PWM_RES; self->duty_ns = ns; } @@ -424,7 +436,7 @@ STATIC void mp_machine_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_p if (self->duty_x == PWM_RES_10_BIT) { mp_printf(print, ", duty=%d", get_duty_u10(self)); - } else if (self->duty_x == -UI_RES_16_BIT) { + } else if (self->duty_x == -HIGHEST_PWM_RES) { mp_printf(print, ", duty_ns=%d", get_duty_ns(self)); } else { mp_printf(print, ", duty_u16=%d", get_duty_u16(self)); From d696d9141e29da524fc78a1044f90a6e9107892f Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sun, 27 Feb 2022 18:09:41 +0100 Subject: [PATCH 171/619] esp32/machine_uart: Make UART.init preserve unspecified parameters. Most of the settings behaved that way, except for baudate, timeout, timout_char, invert and flow. --- ports/esp32/machine_uart.c | 42 +++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/ports/esp32/machine_uart.c b/ports/esp32/machine_uart.c index aa8d494ec8..396455666f 100644 --- a/ports/esp32/machine_uart.c +++ b/ports/esp32/machine_uart.c @@ -139,10 +139,10 @@ STATIC void machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, co { MP_QSTR_cts, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = UART_PIN_NO_CHANGE} }, { MP_QSTR_txbuf, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_rxbuf, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, - { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, - { MP_QSTR_timeout_char, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, - { MP_QSTR_invert, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, - { MP_QSTR_flow, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_timeout_char, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_invert, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_flow, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); @@ -181,8 +181,8 @@ STATIC void machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, co uint32_t baudrate = 115200; if (args[ARG_baudrate].u_int > 0) { uart_set_baudrate(self->uart_num, args[ARG_baudrate].u_int); - uart_get_baudrate(self->uart_num, &baudrate); } + uart_get_baudrate(self->uart_num, &baudrate); uart_set_pin(self->uart_num, args[ARG_tx].u_int, args[ARG_rx].u_int, args[ARG_rts].u_int, args[ARG_cts].u_int); if (args[ARG_tx].u_int != UART_PIN_NO_CHANGE) { @@ -262,28 +262,36 @@ STATIC void machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, co } // set timeout - self->timeout = args[ARG_timeout].u_int; + if (args[ARG_timeout].u_int != -1) { + self->timeout = args[ARG_timeout].u_int; + } // set timeout_char // make sure it is at least as long as a whole character (13 bits to be safe) - self->timeout_char = args[ARG_timeout_char].u_int; - uint32_t min_timeout_char = 13000 / baudrate + 1; - if (self->timeout_char < min_timeout_char) { - self->timeout_char = min_timeout_char; + if (args[ARG_timeout_char].u_int != -1) { + self->timeout_char = args[ARG_timeout_char].u_int; + uint32_t min_timeout_char = 13000 / baudrate + 1; + if (self->timeout_char < min_timeout_char) { + self->timeout_char = min_timeout_char; + } } // set line inversion - if (args[ARG_invert].u_int & ~UART_INV_MASK) { - mp_raise_ValueError(MP_ERROR_TEXT("invalid inversion mask")); + if (args[ARG_invert].u_int != -1) { + if (args[ARG_invert].u_int & ~UART_INV_MASK) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid inversion mask")); + } + self->invert = args[ARG_invert].u_int; } - self->invert = args[ARG_invert].u_int; uart_set_line_inverse(self->uart_num, self->invert); // set hardware flow control - if (args[ARG_flow].u_int & ~UART_HW_FLOWCTRL_CTS_RTS) { - mp_raise_ValueError(MP_ERROR_TEXT("invalid flow control mask")); + if (args[ARG_flow].u_int != -1) { + if (args[ARG_flow].u_int & ~UART_HW_FLOWCTRL_CTS_RTS) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid flow control mask")); + } + self->flowcontrol = args[ARG_flow].u_int; } - self->flowcontrol = args[ARG_flow].u_int; uart_set_hw_flow_ctrl(self->uart_num, self->flowcontrol, UART_FIFO_LEN - UART_FIFO_LEN / 4); } @@ -319,6 +327,8 @@ STATIC mp_obj_t machine_uart_make_new(const mp_obj_type_t *type, size_t n_args, self->rxbuf = 256; // IDF minimum self->timeout = 0; self->timeout_char = 0; + self->invert = 0; + self->flowcontrol = 0; switch (uart_num) { case UART_NUM_0: From 5d9171b5ccf154a4c7c5f8e152ea4fd75a5f33a5 Mon Sep 17 00:00:00 2001 From: Jan Date: Thu, 3 Mar 2022 13:49:06 +0100 Subject: [PATCH 172/619] docs/library/machine.UART.rst: Add details for `invert` parameter. --- docs/library/machine.UART.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/library/machine.UART.rst b/docs/library/machine.UART.rst index 8fb42cfe87..e239525736 100644 --- a/docs/library/machine.UART.rst +++ b/docs/library/machine.UART.rst @@ -63,6 +63,12 @@ Methods - *timeout* specifies the time to wait for the first character (in ms). - *timeout_char* specifies the time to wait between characters (in ms). - *invert* specifies which lines to invert. + + - ``0`` will not invert lines (idle state of both lines is logic high). + - ``UART.INV_TX`` will invert TX line (idle state of TX line now logic low). + - ``UART.INV_RX`` will invert RX line (idle state of RX line now logic low). + - ``UART.INV_TX | UART.INV_RX`` will invert both lines (idle state at logic low). + - *flow* specifies which hardware flow control signals to use. The value is a bitmask. From eadc927baff938a7f561fe69e09a27f11514f96e Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sat, 5 Mar 2022 09:51:34 -0600 Subject: [PATCH 173/619] docs/differences/python_35: Mark PEP 486 as not applicable. This adds the "Not relevant" designation to PEP 486 since it has to do with the Python Launcher for Windows and not the Python language itself. Also fix a typo while we are touching this line. Signed-off-by: David Lechner --- docs/differences/python_35.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/differences/python_35.rst b/docs/differences/python_35.rst index 2a4c119a45..e88df25f95 100644 --- a/docs/differences/python_35.rst +++ b/docs/differences/python_35.rst @@ -33,7 +33,7 @@ Below is a list of finalised/accepted PEPs for Python 3.5 grouped into their imp +--------------------------------------------------------+-------------------------------------------------+---------------+ | `PEP 441 `_ | improved Python zip application support | | +--------------------------------------------------------+-------------------------------------------------+---------------+ - | `PEP 486 `_ | make the Python Laucher aware of virtual | | + | `PEP 486 `_ | make the Python Launcher aware of virtual | Not relevant | | | environments | | +--------------------------------------------------------+-------------------------------------------------+---------------+ | `PEP 484 `_ | type hints (advisory only) | In Progress | From bb2bd071f74c04237618958521e58d48d2dc35d4 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 7 Mar 2022 15:15:45 +1100 Subject: [PATCH 174/619] docs/library/esp32.rst: Mark esp32.Partition as not taking kw args. Fixes issue #8380. Signed-off-by: Damien George --- docs/library/esp32.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/library/esp32.rst b/docs/library/esp32.rst index 82e69e4964..da5fa8c3cb 100644 --- a/docs/library/esp32.rst +++ b/docs/library/esp32.rst @@ -68,7 +68,7 @@ Flash partitions This class gives access to the partitions in the device's flash memory and includes methods to enable over-the-air (OTA) updates. -.. class:: Partition(id, block_size=4096) +.. class:: Partition(id, block_size=4096, /) Create an object representing a partition. *id* can be a string which is the label of the partition to retrieve, or one of the constants: ``BOOT`` or ``RUNNING``. From 7cd166ff9244a720a6e72e651215db891dc1f85e Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 7 Mar 2022 12:29:17 +1100 Subject: [PATCH 175/619] tests/basics: Add test for creating small-ints in nan-box builds. Signed-off-by: Damien George --- tests/basics/nanbox_smallint.py | 42 +++++++++++++++++++++++++++++ tests/basics/nanbox_smallint.py.exp | 5 ++++ 2 files changed, 47 insertions(+) create mode 100644 tests/basics/nanbox_smallint.py create mode 100644 tests/basics/nanbox_smallint.py.exp diff --git a/tests/basics/nanbox_smallint.py b/tests/basics/nanbox_smallint.py new file mode 100644 index 0000000000..642ca77b37 --- /dev/null +++ b/tests/basics/nanbox_smallint.py @@ -0,0 +1,42 @@ +# Test creating small integers without heap allocation in nan-boxing mode. + +import micropython + +try: + # Test for nan-box build by allocating a float while heap is locked. + # This should pass on nan-box builds. + micropython.heap_lock() + float(123) + micropython.heap_unlock() +except: + print("SKIP") + raise SystemExit + +# Check that nan-boxing uses 64-bit floats (eg it's not object representation C). +if float("1e100") == float("inf"): + print("SKIP") + raise SystemExit + +micropython.heap_lock() +print(int("0x80000000")) +micropython.heap_unlock() + +# This is the most positive small integer. +micropython.heap_lock() +print(int("0x3fffffffffff")) +micropython.heap_unlock() + +# This is the most negative small integer. +micropython.heap_lock() +print(int("-0x3fffffffffff") - 1) +micropython.heap_unlock() + +x = 1 +micropython.heap_lock() +print((x << 31) + 1) +micropython.heap_unlock() + +x = 1 +micropython.heap_lock() +print((x << 45) + 1) +micropython.heap_unlock() diff --git a/tests/basics/nanbox_smallint.py.exp b/tests/basics/nanbox_smallint.py.exp new file mode 100644 index 0000000000..aad1f7b8b6 --- /dev/null +++ b/tests/basics/nanbox_smallint.py.exp @@ -0,0 +1,5 @@ +2147483648 +70368744177663 +-70368744177664 +2147483649 +35184372088833 From bf01671a96b454340a176d27696d575e2a93ad7c Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 7 Mar 2022 16:02:55 +1100 Subject: [PATCH 176/619] tools/mpremote: Bump version to 0.2.0. Signed-off-by: Damien George --- tools/mpremote/setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/mpremote/setup.cfg b/tools/mpremote/setup.cfg index bb2d0da52e..2bde6a8ebc 100644 --- a/tools/mpremote/setup.cfg +++ b/tools/mpremote/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = mpremote -version = 0.1.0 +version = 0.2.0 author = Damien George author_email = damien@micropython.org description = Tool for interacting remotely with MicroPython From 86a4a526700ad40b2e37e33c7ae08826cff2a6ac Mon Sep 17 00:00:00 2001 From: Maureen Helm Date: Fri, 4 Mar 2022 08:32:50 -0600 Subject: [PATCH 177/619] zephyr: Update include path to disk_access.h. The disk_access header was moved to a different path in Zephyr v2.6.0. The old path was deprecated for two releases (v2.6.0 and v2.7.0) and will no longer be supported after Zephyr v2.7.0. Signed-off-by: Maureen Helm --- ports/zephyr/zephyr_storage.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/zephyr/zephyr_storage.c b/ports/zephyr/zephyr_storage.c index 1c25b32771..5934f22813 100644 --- a/ports/zephyr/zephyr_storage.c +++ b/ports/zephyr/zephyr_storage.c @@ -32,7 +32,7 @@ #endif #ifdef CONFIG_DISK_ACCESS -#include +#include #endif #ifdef CONFIG_FLASH_MAP From 474d288e55503e135a0d86e6eaa733354583a5f2 Mon Sep 17 00:00:00 2001 From: Maureen Helm Date: Wed, 2 Mar 2022 20:45:20 -0600 Subject: [PATCH 178/619] zephyr: Upgrade to Zephyr v3.0.0. Updates the Zephyr port build instructions and CI to use the latest Zephyr release tag. Signed-off-by: Maureen Helm --- docs/zephyr/tutorial/repl.rst | 4 ++-- ports/zephyr/README.md | 6 +++--- tools/ci.sh | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/zephyr/tutorial/repl.rst b/docs/zephyr/tutorial/repl.rst index 71e732e610..6e11c04cb3 100644 --- a/docs/zephyr/tutorial/repl.rst +++ b/docs/zephyr/tutorial/repl.rst @@ -31,8 +31,8 @@ With your serial program open (PuTTY, screen, picocom, etc) you may see a blank screen with a flashing cursor. Press Enter (or reset the board) and you should be presented with the following text:: - *** Booting Zephyr OS build zephyr-v2.7.0 *** - MicroPython v1.17-288-gb695f5a70-dirty on 2022-01-03; zephyr-frdm_k64f with mk64f12 + *** Booting Zephyr OS build zephyr-v3.0.0 *** + MicroPython v1.18-169-g665f0e2a6-dirty on 2022-03-02; zephyr-frdm_k64f with mk64f12 Type "help()" for more information. >>> diff --git a/ports/zephyr/README.md b/ports/zephyr/README.md index 6b1c7ddd5f..6b2b54fa29 100644 --- a/ports/zephyr/README.md +++ b/ports/zephyr/README.md @@ -4,7 +4,7 @@ MicroPython port to Zephyr RTOS This is a work-in-progress port of MicroPython to Zephyr RTOS (http://zephyrproject.org). -This port requires Zephyr version v2.7.0, and may also work on higher +This port requires Zephyr version v3.0.0, and may also work on higher versions. All boards supported by Zephyr (with standard level of features support, like UART console) should work with MicroPython (but not all were tested). @@ -39,13 +39,13 @@ setup is correct. If you already have Zephyr installed but are having issues building the MicroPython port then try installing the correct version of Zephyr via: - $ west init zephyrproject -m https://github.com/zephyrproject-rtos/zephyr --mr v2.7.0 + $ west init zephyrproject -m https://github.com/zephyrproject-rtos/zephyr --mr v3.0.0 Alternatively, you don't have to redo the Zephyr installation to just switch from master to a tagged release, you can instead do: $ cd zephyrproject/zephyr - $ git checkout v2.7.0 + $ git checkout v3.0.0 $ west update With Zephyr installed you may then need to configure your environment, diff --git a/tools/ci.sh b/tools/ci.sh index dee4b387bf..4f6717fe93 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -618,7 +618,7 @@ function ci_windows_build { ZEPHYR_DOCKER_VERSION=v0.21.0 ZEPHYR_SDK_VERSION=0.13.2 -ZEPHYR_VERSION=v2.7.0 +ZEPHYR_VERSION=v3.0.0 function ci_zephyr_setup { docker pull zephyrprojectrtos/ci:${ZEPHYR_DOCKER_VERSION} From cced9a0128a5fba99868b6fa091b26fec3119f54 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 7 Mar 2022 16:43:29 +1100 Subject: [PATCH 179/619] zephyr/prj.conf: Enable CONFIG_BUILD_OUTPUT_HEX to generate .hex output. The .hex file contains more information than .bin, useful for flashing. Signed-off-by: Damien George --- ports/zephyr/prj.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/zephyr/prj.conf b/ports/zephyr/prj.conf index b6559e4f73..90211e3657 100644 --- a/ports/zephyr/prj.conf +++ b/ports/zephyr/prj.conf @@ -1,4 +1,5 @@ CONFIG_BUILD_OUTPUT_BIN=y +CONFIG_BUILD_OUTPUT_HEX=y CONFIG_REBOOT=y CONFIG_STDOUT_CONSOLE=y From 9a8ee6a5df8d59d0c88a14d81fe40ad95a31c1e4 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 7 Mar 2022 16:45:40 +1100 Subject: [PATCH 180/619] tests/run-tests.py: Include test files ending in _set as set tests. Signed-off-by: Damien George --- tests/run-tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/run-tests.py b/tests/run-tests.py index f9a16283bc..edd20b9bd5 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -548,7 +548,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): is_endian = test_name.endswith("_endian") is_int_big = test_name.startswith("int_big") or test_name.endswith("_intbig") is_bytearray = test_name.startswith("bytearray") or test_name.endswith("_bytearray") - is_set_type = test_name.startswith("set_") or test_name.startswith("frozenset") + is_set_type = test_name.startswith(("set_", "frozenset")) or test_name.endswith("_set") is_slice = test_name.find("slice") != -1 or test_name in misc_slice_tests is_async = test_name.startswith(("async_", "uasyncio_")) is_const = test_name.startswith("const") From 3440201e2e6861be77aecb1d5069a6cfe4ccc328 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 7 Mar 2022 16:47:33 +1100 Subject: [PATCH 181/619] tests/micropython: Switch from set.pop to raise-0 to test exc strings. To not rely on sets, which are an optional feature. Signed-off-by: Damien George --- tests/micropython/heapalloc_exc_compressed.py | 16 +++++++--------- .../micropython/heapalloc_exc_compressed.py.exp | 6 +++--- .../heapalloc_exc_compressed_emg_exc.py | 10 ++++------ .../heapalloc_exc_compressed_emg_exc.py.exp | 4 ++-- 4 files changed, 16 insertions(+), 20 deletions(-) diff --git a/tests/micropython/heapalloc_exc_compressed.py b/tests/micropython/heapalloc_exc_compressed.py index 79e423ca0f..aa071d641c 100644 --- a/tests/micropython/heapalloc_exc_compressed.py +++ b/tests/micropython/heapalloc_exc_compressed.py @@ -5,12 +5,10 @@ import micropython # mp_obj_new_exception_msg (decompression can be deferred) # NameError uses mp_obj_new_exception_msg_varg for NameError("name '%q' isn't defined") -# set.pop uses mp_obj_new_exception_msg for KeyError("pop from an empty set") +# `raise 0` uses mp_obj_new_exception_msg for TypeError("exceptions must derive from BaseException") # Tests that deferred decompression works both via print(e) and accessing the message directly via e.args. -a = set() - # First test the regular case (can use heap for allocating the decompression buffer). try: name() @@ -18,8 +16,8 @@ except NameError as e: print(type(e).__name__, e) try: - a.pop() -except KeyError as e: + raise 0 +except TypeError as e: print(type(e).__name__, e) try: @@ -28,8 +26,8 @@ except NameError as e: print(e.args[0]) try: - a.pop() -except KeyError as e: + raise 0 +except TypeError as e: print(e.args[0]) # Then test that it still works when the heap is locked (i.e. in ISR context). @@ -41,8 +39,8 @@ except NameError as e: print(type(e).__name__) try: - a.pop() -except KeyError as e: + raise 0 +except TypeError as e: print(type(e).__name__) micropython.heap_unlock() diff --git a/tests/micropython/heapalloc_exc_compressed.py.exp b/tests/micropython/heapalloc_exc_compressed.py.exp index 32d1642f86..c3e6e5dd9f 100644 --- a/tests/micropython/heapalloc_exc_compressed.py.exp +++ b/tests/micropython/heapalloc_exc_compressed.py.exp @@ -1,6 +1,6 @@ NameError name 'name' isn't defined -KeyError pop from an empty set +TypeError exceptions must derive from BaseException name 'name' isn't defined -pop from an empty set +exceptions must derive from BaseException NameError -KeyError +TypeError diff --git a/tests/micropython/heapalloc_exc_compressed_emg_exc.py b/tests/micropython/heapalloc_exc_compressed_emg_exc.py index 86ade07862..48ce9dd69e 100644 --- a/tests/micropython/heapalloc_exc_compressed_emg_exc.py +++ b/tests/micropython/heapalloc_exc_compressed_emg_exc.py @@ -9,8 +9,6 @@ try: except AttributeError: pass -a = set() - def test(): micropython.heap_lock() @@ -21,8 +19,8 @@ def test(): print(type(e).__name__, e) try: - a.pop() - except KeyError as e: + raise 0 + except TypeError as e: print(type(e).__name__, e) try: @@ -31,8 +29,8 @@ def test(): print(e.args[0]) try: - a.pop() - except KeyError as e: + raise 0 + except TypeError as e: print(e.args[0]) micropython.heap_unlock() diff --git a/tests/micropython/heapalloc_exc_compressed_emg_exc.py.exp b/tests/micropython/heapalloc_exc_compressed_emg_exc.py.exp index 7c368712a2..1fc8109339 100644 --- a/tests/micropython/heapalloc_exc_compressed_emg_exc.py.exp +++ b/tests/micropython/heapalloc_exc_compressed_emg_exc.py.exp @@ -1,4 +1,4 @@ NameError name 'name' isn't defined -KeyError pop from an empty set +TypeError exceptions must derive from BaseException name 'name' isn't defined -pop from an empty set +exceptions must derive from BaseException From 33083bf52730fa1c238a154685a895b2775cc355 Mon Sep 17 00:00:00 2001 From: Jonathan Hogg Date: Mon, 7 Mar 2022 08:55:48 +0000 Subject: [PATCH 182/619] esp32/machine_pin: Add support for pin drive strength. Add support for configuring drive strength of output pins with `drive` keyword argument and `DRIVE_*` constants. --- ports/esp32/machine_pin.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/ports/esp32/machine_pin.c b/ports/esp32/machine_pin.c index c0c7ddb705..1dad398524 100644 --- a/ports/esp32/machine_pin.c +++ b/ports/esp32/machine_pin.c @@ -253,13 +253,14 @@ STATIC void machine_pin_print(const mp_print_t *print, mp_obj_t self_in, mp_prin mp_printf(print, "Pin(%u)", self->id); } -// pin.init(mode, pull=None, *, value) +// pin.init(mode=None, pull=-1, *, value, drive) STATIC mp_obj_t machine_pin_obj_init_helper(const machine_pin_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_mode, ARG_pull, ARG_value }; + enum { ARG_mode, ARG_pull, ARG_value, ARG_drive }; static const mp_arg_t allowed_args[] = { { MP_QSTR_mode, MP_ARG_OBJ, {.u_obj = mp_const_none}}, { MP_QSTR_pull, MP_ARG_OBJ, {.u_obj = MP_OBJ_NEW_SMALL_INT(-1)}}, { MP_QSTR_value, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, + { MP_QSTR_drive, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, }; // parse args @@ -289,6 +290,14 @@ STATIC mp_obj_t machine_pin_obj_init_helper(const machine_pin_obj_t *self, size_ gpio_set_level(self->id, mp_obj_is_true(args[ARG_value].u_obj)); } + // set drive capability (do this before configuring mode) + if (args[ARG_drive].u_obj != MP_OBJ_NULL && GPIO_IS_VALID_OUTPUT_GPIO(self->id)) { + mp_int_t strength = mp_obj_get_int(args[ARG_drive].u_obj); + if (0 <= strength && strength < GPIO_DRIVE_CAP_MAX) { + gpio_set_drive_capability(self->id, strength); + } + } + // configure mode if (args[ARG_mode].u_obj != mp_const_none) { mp_int_t pin_io_mode = mp_obj_get_int(args[ARG_mode].u_obj); @@ -476,6 +485,10 @@ STATIC const mp_rom_map_elem_t machine_pin_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_IRQ_FALLING), MP_ROM_INT(GPIO_PIN_INTR_NEGEDGE) }, { MP_ROM_QSTR(MP_QSTR_WAKE_LOW), MP_ROM_INT(GPIO_PIN_INTR_LOLEVEL) }, { MP_ROM_QSTR(MP_QSTR_WAKE_HIGH), MP_ROM_INT(GPIO_PIN_INTR_HILEVEL) }, + { MP_ROM_QSTR(MP_QSTR_DRIVE_0), MP_ROM_INT(GPIO_DRIVE_CAP_0) }, + { MP_ROM_QSTR(MP_QSTR_DRIVE_1), MP_ROM_INT(GPIO_DRIVE_CAP_1) }, + { MP_ROM_QSTR(MP_QSTR_DRIVE_2), MP_ROM_INT(GPIO_DRIVE_CAP_2) }, + { MP_ROM_QSTR(MP_QSTR_DRIVE_3), MP_ROM_INT(GPIO_DRIVE_CAP_3) }, }; STATIC mp_uint_t pin_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) { From 4c7c80d6266c6362253755c92bf649dc85c47dae Mon Sep 17 00:00:00 2001 From: Jonathan Hogg Date: Mon, 7 Mar 2022 09:03:49 +0000 Subject: [PATCH 183/619] docs/esp32/quickref: Document GPIO drive strength. Add brief documentation of the new `drive` keyword argument. --- docs/esp32/quickref.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/esp32/quickref.rst b/docs/esp32/quickref.rst index da586819fd..56e765536e 100644 --- a/docs/esp32/quickref.rst +++ b/docs/esp32/quickref.rst @@ -160,6 +160,7 @@ Use the :ref:`machine.Pin ` class:: p4 = Pin(4, Pin.IN, Pin.PULL_UP) # enable internal pull-up resistor p5 = Pin(5, Pin.OUT, value=1) # set pin high on creation + p6 = Pin(6, Pin.OUT, drive=Pin.DRIVE_3) # set maximum drive strength Available Pins are from the following ranges (inclusive): 0-19, 21-23, 25-27, 32-39. These correspond to the actual GPIO pin numbers of ESP32 chip. Note that many @@ -167,6 +168,15 @@ end-user boards use their own adhoc pin numbering (marked e.g. D0, D1, ...). For mapping between board logical pins and physical chip pins consult your board documentation. +Four drive strengths are supported, using the ``drive`` keyword argument to the +``Pin()`` constructor or ``Pin.init()`` method, with different corresponding +safe maximum source/sink currents and approximate internal driver resistances: + + - ``Pin.DRIVE_0``: 5mA / 130 ohm + - ``Pin.DRIVE_1``: 10mA / 60 ohm + - ``Pin.DRIVE_2``: 20mA / 30 ohm (default strength if not configured) + - ``Pin.DRIVE_3``: 40mA / 15 ohm + Notes: * Pins 1 and 3 are REPL UART TX and RX respectively From 3ebc370344ce5583b8615881a80749f855622aa5 Mon Sep 17 00:00:00 2001 From: Jonathan Hogg Date: Mon, 7 Mar 2022 09:03:54 +0000 Subject: [PATCH 184/619] docs/library/machine.Pin: Update to use preferred DRIVE_x constants. Update documents with new common names for the drive strength constants. --- docs/library/machine.Pin.rst | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/docs/library/machine.Pin.rst b/docs/library/machine.Pin.rst index f80de11784..49fb66beb3 100644 --- a/docs/library/machine.Pin.rst +++ b/docs/library/machine.Pin.rst @@ -87,9 +87,9 @@ Constructors output pin value if given, otherwise the state of the pin peripheral remains unchanged. - - ``drive`` specifies the output power of the pin and can be one of: ``Pin.LOW_POWER``, - ``Pin.MED_POWER`` or ``Pin.HIGH_POWER``. The actual current driving capabilities - are port dependent. Not all ports implement this argument. + - ``drive`` specifies the output power of the pin and can be one of: ``Pin.DRIVE_0``, + ``Pin.DRIVE_1``, etc., increasing in drive strength. The actual current driving + capabilities are port dependent. Not all ports implement this argument. - ``alt`` specifies an alternate function for the pin and the values it can take are port dependent. This argument is valid only for ``Pin.ALT`` and ``Pin.ALT_OPEN_DRAIN`` @@ -260,11 +260,13 @@ not all constants are available on all ports. Selects whether there is a pull up/down resistor. Use the value ``None`` for no pull. -.. data:: Pin.LOW_POWER - Pin.MED_POWER - Pin.HIGH_POWER +.. data:: Pin.DRIVE_0 + Pin.DRIVE_1 + Pin.DRIVE_2 - Selects the pin drive strength. + Selects the pin drive strength. A port may define additional drive + constants with increasing number corresponding to increasing drive + strength. .. data:: Pin.IRQ_FALLING Pin.IRQ_RISING From 62cb2069bb107f32efedcd59481de6373c854db1 Mon Sep 17 00:00:00 2001 From: Philipp Ebensberger Date: Mon, 7 Mar 2022 16:56:14 +0100 Subject: [PATCH 185/619] mimxrt/machine_pin: Change pin drive constants to DRIVE_x naming. Updated DRIVE_x constants representing pin drive strength. Signed-off-by: Philipp Ebensberger --- ports/mimxrt/eth.c | 4 ++-- ports/mimxrt/machine_pin.c | 18 +++++++++--------- ports/mimxrt/machine_pwm.c | 4 ++-- ports/mimxrt/machine_uart.c | 4 ++-- ports/mimxrt/pin.c | 2 +- ports/mimxrt/pin.h | 28 ++++++++++++++-------------- ports/mimxrt/sdcard.c | 6 +++--- 7 files changed, 33 insertions(+), 33 deletions(-) diff --git a/ports/mimxrt/eth.c b/ports/mimxrt/eth.c index 5a5138443c..af0264d085 100644 --- a/ports/mimxrt/eth.c +++ b/ports/mimxrt/eth.c @@ -195,7 +195,7 @@ void eth_init(eth_t *self, int mac_idx, const phy_operations_t *phy_ops, int phy IOMUXC_SetPinMux(reset_pin->muxRegister, af_obj->af_mode, 0, 0, reset_pin->configRegister, 0U); IOMUXC_SetPinConfig(reset_pin->muxRegister, af_obj->af_mode, 0, 0, reset_pin->configRegister, - pin_generate_config(PIN_PULL_DISABLED, PIN_MODE_OUT, PIN_DRIVE_POWER_5, reset_pin->configRegister)); + pin_generate_config(PIN_PULL_DISABLED, PIN_MODE_OUT, PIN_DRIVE_5, reset_pin->configRegister)); GPIO_PinInit(reset_pin->gpio, reset_pin->pin, &gpio_config); #endif @@ -206,7 +206,7 @@ void eth_init(eth_t *self, int mac_idx, const phy_operations_t *phy_ops, int phy IOMUXC_SetPinMux(int_pin->muxRegister, af_obj->af_mode, 0, 0, int_pin->configRegister, 0U); IOMUXC_SetPinConfig(int_pin->muxRegister, af_obj->af_mode, 0, 0, int_pin->configRegister, - pin_generate_config(PIN_PULL_UP_47K, PIN_MODE_IN, PIN_DRIVE_POWER_5, int_pin->configRegister)); + pin_generate_config(PIN_PULL_UP_47K, PIN_MODE_IN, PIN_DRIVE_5, int_pin->configRegister)); GPIO_PinInit(int_pin->gpio, int_pin->pin, &gpio_config); #endif diff --git a/ports/mimxrt/machine_pin.c b/ports/mimxrt/machine_pin.c index 1f4afab97f..2424088f02 100644 --- a/ports/mimxrt/machine_pin.c +++ b/ports/mimxrt/machine_pin.c @@ -190,7 +190,7 @@ STATIC mp_obj_t machine_pin_obj_init_helper(const machine_pin_obj_t *self, size_ [PIN_INIT_ARG_MODE] { MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT }, [PIN_INIT_ARG_PULL] { MP_QSTR_pull, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE}}, [PIN_INIT_ARG_VALUE] { MP_QSTR_value, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, - [PIN_INIT_ARG_DRIVE] { MP_QSTR_drive, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = PIN_DRIVE_POWER_3}}, + [PIN_INIT_ARG_DRIVE] { MP_QSTR_drive, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = PIN_DRIVE_3}}, // TODO: Implement additional arguments /* { MP_QSTR_af, MP_ARG_INT, {.u_int = -1}}, // legacy @@ -381,14 +381,14 @@ STATIC const mp_rom_map_elem_t machine_pin_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_PULL_DOWN), MP_ROM_INT(PIN_PULL_DOWN_100K) }, { MP_ROM_QSTR(MP_QSTR_PULL_HOLD), MP_ROM_INT(PIN_PULL_HOLD) }, - { MP_ROM_QSTR(MP_QSTR_DRIVER_OFF), MP_ROM_INT(PIN_DRIVE_OFF) }, - { MP_ROM_QSTR(MP_QSTR_POWER_0), MP_ROM_INT(PIN_DRIVE_POWER_0) }, // R0 (150 Ohm @3.3V / 260 Ohm @ 1.8V) - { MP_ROM_QSTR(MP_QSTR_POWER_1), MP_ROM_INT(PIN_DRIVE_POWER_1) }, // R0/2 - { MP_ROM_QSTR(MP_QSTR_POWER_2), MP_ROM_INT(PIN_DRIVE_POWER_2) }, // R0/3 - { MP_ROM_QSTR(MP_QSTR_POWER_3), MP_ROM_INT(PIN_DRIVE_POWER_3) }, // R0/4 - { MP_ROM_QSTR(MP_QSTR_POWER_4), MP_ROM_INT(PIN_DRIVE_POWER_4) }, // R0/5 - { MP_ROM_QSTR(MP_QSTR_POWER_5), MP_ROM_INT(PIN_DRIVE_POWER_5) }, // R0/6 - { MP_ROM_QSTR(MP_QSTR_POWER_6), MP_ROM_INT(PIN_DRIVE_POWER_6) }, // R0/7 + { MP_ROM_QSTR(MP_QSTR_DRIVE_OFF), MP_ROM_INT(PIN_DRIVE_OFF) }, + { MP_ROM_QSTR(MP_QSTR_DRIVE_0), MP_ROM_INT(PIN_DRIVE_0) }, // R0 (150 Ohm @3.3V / 260 Ohm @ 1.8V) + { MP_ROM_QSTR(MP_QSTR_DRIVE_1), MP_ROM_INT(PIN_DRIVE_1) }, // R0/2 + { MP_ROM_QSTR(MP_QSTR_DRIVE_2), MP_ROM_INT(PIN_DRIVE_2) }, // R0/3 + { MP_ROM_QSTR(MP_QSTR_DRIVE_3), MP_ROM_INT(PIN_DRIVE_3) }, // R0/4 + { MP_ROM_QSTR(MP_QSTR_DRIVE_4), MP_ROM_INT(PIN_DRIVE_4) }, // R0/5 + { MP_ROM_QSTR(MP_QSTR_DRIVE_5), MP_ROM_INT(PIN_DRIVE_5) }, // R0/6 + { MP_ROM_QSTR(MP_QSTR_DRIVE_6), MP_ROM_INT(PIN_DRIVE_6) }, // R0/7 { MP_ROM_QSTR(MP_QSTR_IRQ_RISING), MP_ROM_INT(1) }, { MP_ROM_QSTR(MP_QSTR_IRQ_FALLING), MP_ROM_INT(2) }, diff --git a/ports/mimxrt/machine_pwm.c b/ports/mimxrt/machine_pwm.c index c21c7eb94b..9a498fbab8 100644 --- a/ports/mimxrt/machine_pwm.c +++ b/ports/mimxrt/machine_pwm.c @@ -518,7 +518,7 @@ STATIC mp_obj_t mp_machine_pwm_make_new(const mp_obj_type_t *type, size_t n_args IOMUXC_SetPinMux(pin1->muxRegister, af_obj1->af_mode, af_obj1->input_register, af_obj1->input_daisy, pin1->configRegister, 0U); IOMUXC_SetPinConfig(pin1->muxRegister, af_obj1->af_mode, af_obj1->input_register, af_obj1->input_daisy, - pin1->configRegister, pin_generate_config(PIN_PULL_DISABLED, PIN_MODE_OUT, PIN_DRIVE_POWER_5, pin1->configRegister)); + pin1->configRegister, pin_generate_config(PIN_PULL_DISABLED, PIN_MODE_OUT, PIN_DRIVE_5, pin1->configRegister)); // Settings for the second pin, if given. if (pin2 != NULL && pin2 != pin1) { @@ -529,7 +529,7 @@ STATIC mp_obj_t mp_machine_pwm_make_new(const mp_obj_type_t *type, size_t n_args IOMUXC_SetPinMux(pin2->muxRegister, af_obj2->af_mode, af_obj2->input_register, af_obj2->input_daisy, pin2->configRegister, 0U); IOMUXC_SetPinConfig(pin2->muxRegister, af_obj2->af_mode, af_obj2->input_register, af_obj2->input_daisy, - pin2->configRegister, pin_generate_config(PIN_PULL_DISABLED, PIN_MODE_OUT, PIN_DRIVE_POWER_5, pin2->configRegister)); + pin2->configRegister, pin_generate_config(PIN_PULL_DISABLED, PIN_MODE_OUT, PIN_DRIVE_5, pin2->configRegister)); } else { self->complementary = 0; } diff --git a/ports/mimxrt/machine_uart.c b/ports/mimxrt/machine_uart.c index 6807a994e1..d413778c8c 100644 --- a/ports/mimxrt/machine_uart.c +++ b/ports/mimxrt/machine_uart.c @@ -85,11 +85,11 @@ bool lpuart_set_iomux(int8_t uart) { if (TX.muxRegister != 0) { IOMUXC_SetPinMux(TX.muxRegister, TX.muxMode, TX.inputRegister, TX.inputDaisy, TX.configRegister, 0U); IOMUXC_SetPinConfig(TX.muxRegister, TX.muxMode, TX.inputRegister, TX.inputDaisy, TX.configRegister, - pin_generate_config(PIN_PULL_UP_100K, PIN_MODE_OUT, PIN_DRIVE_POWER_6, TX.configRegister)); + pin_generate_config(PIN_PULL_UP_100K, PIN_MODE_OUT, PIN_DRIVE_6, TX.configRegister)); IOMUXC_SetPinMux(RX.muxRegister, RX.muxMode, RX.inputRegister, RX.inputDaisy, RX.configRegister, 0U); IOMUXC_SetPinConfig(RX.muxRegister, RX.muxMode, RX.inputRegister, RX.inputDaisy, RX.configRegister, - pin_generate_config(PIN_PULL_UP_100K, PIN_MODE_IN, PIN_DRIVE_POWER_6, RX.configRegister)); + pin_generate_config(PIN_PULL_UP_100K, PIN_MODE_IN, PIN_DRIVE_6, RX.configRegister)); return true; } else { return false; diff --git a/ports/mimxrt/pin.c b/ports/mimxrt/pin.c index e4136bd1c3..fcc9863286 100644 --- a/ports/mimxrt/pin.c +++ b/ports/mimxrt/pin.c @@ -99,7 +99,7 @@ uint32_t pin_generate_config(const uint32_t pull, const uint32_t mode, const uin if (!IS_GPIO_DRIVE(drive)) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid drive strength: %d"), drive); } - pad_config |= IOMUXC_SW_PAD_CTL_PAD_DSE(drive >= PIN_DRIVE_POWER_4); + pad_config |= IOMUXC_SW_PAD_CTL_PAD_DSE(drive >= PIN_DRIVE_4); } #else diff --git a/ports/mimxrt/pin.h b/ports/mimxrt/pin.h index d1597fab9b..583d1c3023 100644 --- a/ports/mimxrt/pin.h +++ b/ports/mimxrt/pin.h @@ -40,13 +40,13 @@ ((MODE) == PIN_MODE_ALT)) #define IS_GPIO_DRIVE(DRIVE) (((DRIVE) == PIN_DRIVE_OFF) || \ - ((DRIVE) == PIN_DRIVE_POWER_0) || \ - ((DRIVE) == PIN_DRIVE_POWER_1) || \ - ((DRIVE) == PIN_DRIVE_POWER_2) || \ - ((DRIVE) == PIN_DRIVE_POWER_3) || \ - ((DRIVE) == PIN_DRIVE_POWER_4) || \ - ((DRIVE) == PIN_DRIVE_POWER_5) || \ - ((DRIVE) == PIN_DRIVE_POWER_6)) + ((DRIVE) == PIN_DRIVE_0) || \ + ((DRIVE) == PIN_DRIVE_1) || \ + ((DRIVE) == PIN_DRIVE_2) || \ + ((DRIVE) == PIN_DRIVE_3) || \ + ((DRIVE) == PIN_DRIVE_4) || \ + ((DRIVE) == PIN_DRIVE_5) || \ + ((DRIVE) == PIN_DRIVE_6)) // ------------------------------------------------------------------------------------------------------------------ // @@ -82,13 +82,13 @@ enum { enum { PIN_DRIVE_OFF = 0b000, - PIN_DRIVE_POWER_0, // R0 (150 Ohm @3.3V / 260 Ohm @ 1.8V) - PIN_DRIVE_POWER_1, // R0/2 - PIN_DRIVE_POWER_2, // R0/3 - PIN_DRIVE_POWER_3, // R0/4 - PIN_DRIVE_POWER_4, // R0/5 - PIN_DRIVE_POWER_5, // R0/6 - PIN_DRIVE_POWER_6, // R0/7 + PIN_DRIVE_0, // R0 (150 Ohm @3.3V / 260 Ohm @ 1.8V) + PIN_DRIVE_1, // R0/2 + PIN_DRIVE_2, // R0/3 + PIN_DRIVE_3, // R0/4 + PIN_DRIVE_4, // R0/5 + PIN_DRIVE_5, // R0/6 + PIN_DRIVE_6, // R0/7 }; diff --git a/ports/mimxrt/sdcard.c b/ports/mimxrt/sdcard.c index 5c523068ad..1a9a97b681 100644 --- a/ports/mimxrt/sdcard.c +++ b/ports/mimxrt/sdcard.c @@ -728,14 +728,14 @@ void sdcard_init_pins(mimxrt_sdcard_obj_t *card) { const mimxrt_sdcard_obj_pins_t *pins = card->pins; uint32_t default_config = pin_generate_config( - PIN_PULL_UP_47K, PIN_MODE_SKIP, PIN_DRIVE_POWER_6, card->pins->clk.pin->configRegister); + PIN_PULL_UP_47K, PIN_MODE_SKIP, PIN_DRIVE_6, card->pins->clk.pin->configRegister); #if USDHC_DATA3_PULL_DOWN_ON_BOARD // Pull down on the board -> must not enable internal PD. uint32_t no_cd_config = pin_generate_config( - PIN_PULL_DISABLED, PIN_MODE_SKIP, PIN_DRIVE_POWER_6, card->pins->data3.pin->configRegister); + PIN_PULL_DISABLED, PIN_MODE_SKIP, PIN_DRIVE_6, card->pins->data3.pin->configRegister); #else uint32_t no_cd_config = pin_generate_config( - PIN_PULL_DOWN_100K, PIN_MODE_SKIP, PIN_DRIVE_POWER_6, card->pins->data3.pin->configRegister); + PIN_PULL_DOWN_100K, PIN_MODE_SKIP, PIN_DRIVE_6, card->pins->data3.pin->configRegister); #endif // USDHC_DATA3_PULL_DOWN_ON_BOARD sdcard_init_pin(card->pins->clk.pin, card->pins->clk.af_idx, default_config); // USDHC1_CLK From 5cc50b9f1cdc2991008bedec6e012c3ec877999a Mon Sep 17 00:00:00 2001 From: robert-hh Date: Wed, 2 Feb 2022 08:52:37 +0100 Subject: [PATCH 186/619] mimxrt/machine_spi: Add omitted GPIO config. The method was changed, but not for the CSx pins. No functional change. --- ports/mimxrt/machine_spi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/mimxrt/machine_spi.c b/ports/mimxrt/machine_spi.c index 0332d9a9e8..e25e219c79 100644 --- a/ports/mimxrt/machine_spi.c +++ b/ports/mimxrt/machine_spi.c @@ -93,11 +93,11 @@ bool lpspi_set_iomux(int8_t spi, uint8_t drive, uint8_t cs) { if (cs == 0 && CS0.muxRegister != 0) { IOMUXC_SetPinMux(CS0.muxRegister, CS0.muxMode, CS0.inputRegister, CS0.inputDaisy, CS0.configRegister, 0U); IOMUXC_SetPinConfig(CS0.muxRegister, CS0.muxMode, CS0.inputRegister, CS0.inputDaisy, CS0.configRegister, - 0x1080u | drive << IOMUXC_SW_PAD_CTL_PAD_DSE_SHIFT); + pin_generate_config(PIN_PULL_UP_100K, PIN_MODE_OUT, drive, CS0.configRegister)); } else if (cs == 1 && CS1.muxRegister != 0) { IOMUXC_SetPinMux(CS1.muxRegister, CS1.muxMode, CS1.inputRegister, CS1.inputDaisy, CS1.configRegister, 0U); IOMUXC_SetPinConfig(CS1.muxRegister, CS1.muxMode, CS1.inputRegister, CS1.inputDaisy, CS1.configRegister, - 0x1080u | drive << IOMUXC_SW_PAD_CTL_PAD_DSE_SHIFT); + pin_generate_config(PIN_PULL_UP_100K, PIN_MODE_OUT, drive, CS1.configRegister)); } else { mp_raise_ValueError(MP_ERROR_TEXT("The chosen CS is not available")); } From 5ea85b7a852046783074be8a59534bd7f55d2553 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sat, 18 Dec 2021 21:16:07 +0100 Subject: [PATCH 187/619] mimxrt/boards: Add board files for MIMXRT1015 and MIMXRT1015_EVK. OCOTP_Init() has been removed from mphalport.c. The library files are missing for the MIMXRT1015, and for just reading the OCOTP the Init is not required. --- ports/mimxrt/Makefile | 1 - ports/mimxrt/boards/MIMXRT1015.ld | 38 +++++++ ports/mimxrt/boards/MIMXRT1015_EVK/board.json | 23 ++++ .../boards/MIMXRT1015_EVK/clock_config.h | 107 ++++++++++++++++++ .../boards/MIMXRT1015_EVK/mpconfigboard.h | 84 ++++++++++++++ .../boards/MIMXRT1015_EVK/mpconfigboard.mk | 9 ++ ports/mimxrt/boards/MIMXRT1015_EVK/pins.csv | 41 +++++++ ports/mimxrt/boards/MIMXRT1015_af.csv | 57 ++++++++++ ports/mimxrt/boards/make-flexram-config.py | 1 + ports/mimxrt/mphalport.c | 6 +- 10 files changed, 365 insertions(+), 2 deletions(-) create mode 100644 ports/mimxrt/boards/MIMXRT1015.ld create mode 100644 ports/mimxrt/boards/MIMXRT1015_EVK/board.json create mode 100644 ports/mimxrt/boards/MIMXRT1015_EVK/clock_config.h create mode 100644 ports/mimxrt/boards/MIMXRT1015_EVK/mpconfigboard.h create mode 100644 ports/mimxrt/boards/MIMXRT1015_EVK/mpconfigboard.mk create mode 100644 ports/mimxrt/boards/MIMXRT1015_EVK/pins.csv create mode 100644 ports/mimxrt/boards/MIMXRT1015_af.csv diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index 5b835d25b7..55e32433f5 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -186,7 +186,6 @@ SRC_HAL_IMX_C += \ $(MCU_DIR)/drivers/fsl_lpspi.c \ $(MCU_DIR)/drivers/fsl_lpspi_edma.c \ $(MCU_DIR)/drivers/fsl_lpuart.c \ - $(MCU_DIR)/drivers/fsl_ocotp.c \ $(MCU_DIR)/drivers/fsl_pit.c \ $(MCU_DIR)/drivers/fsl_pwm.c \ $(MCU_DIR)/drivers/fsl_snvs_lp.c \ diff --git a/ports/mimxrt/boards/MIMXRT1015.ld b/ports/mimxrt/boards/MIMXRT1015.ld new file mode 100644 index 0000000000..6d34200a9f --- /dev/null +++ b/ports/mimxrt/boards/MIMXRT1015.ld @@ -0,0 +1,38 @@ +/* Memory configuration */ +#if defined MICROPY_HW_FLASH_RESERVED +reserved_size = MICROPY_HW_FLASH_RESERVED; +#endif + +#if MICROPY_HW_FLASH_TYPE == qspi_nor +flash_start = 0x60000000; +#else +#error Unknown MICROPY_HW_FLASH_TYPE +#endif +flash_size = MICROPY_HW_FLASH_SIZE; +flash_end = DEFINED(reserved_size) ? ((flash_start) + (flash_size - reserved_size)) : ((flash_start) + (flash_size)); +flash_config_start = flash_start; +flash_config_size = 0x00001000; +ivt_start = flash_start + 0x00001000; +ivt_size = 0x00001000; +interrupts_start = flash_start + 0x00002000; +interrupts_size = 0x00000400; +text_start = flash_start + 0x00002400; +vfs_start = flash_start + 0x00100000; +text_size = ((vfs_start) - (text_start)); +vfs_size = ((flash_end) - (vfs_start)); +itcm_start = 0x00000000; +itcm_size = 0x00008000; +dtcm_start = 0x20000000; +dtcm_size = 0x00008000; +ocrm_start = 0x20200000; +ocrm_size = 0x00010000; + +/* 24kiB stack. */ +__stack_size__ = 0x5000; +_estack = __StackTop; +_sstack = __StackLimit; + +/* Use second OCRAM bank for GC heap. */ +/* Use all OCRAM for the GC heap. */ +_gc_heap_start = ORIGIN(m_ocrm); +_gc_heap_end = ORIGIN(m_ocrm) + LENGTH(m_ocrm); diff --git a/ports/mimxrt/boards/MIMXRT1015_EVK/board.json b/ports/mimxrt/boards/MIMXRT1015_EVK/board.json new file mode 100644 index 0000000000..78044c134e --- /dev/null +++ b/ports/mimxrt/boards/MIMXRT1015_EVK/board.json @@ -0,0 +1,23 @@ +{ + "deploy": [ + "../deploy_mimxrt.md" + ], + "docs": "", + "features": [ + "MicroSD", + "MicroUSB", + "Microphone", + "AudioCodec", + "CAN", + "OpenSDA", + "JLink" + ], + "images": [ + "MIMXRT1015-EVK-TOP.jpg" + ], + "mcu": "mimxrt", + "product": "MIMXRT1015_EVK", + "thumbnail": "", + "url": "https://www.nxp.com/design/development-boards/i-mx-evaluation-and-development-boards/i-mx-rt1015-evaluation-kit:MIMXRT1015-EVK", + "vendor": "NXP" +} diff --git a/ports/mimxrt/boards/MIMXRT1015_EVK/clock_config.h b/ports/mimxrt/boards/MIMXRT1015_EVK/clock_config.h new file mode 100644 index 0000000000..65944077e5 --- /dev/null +++ b/ports/mimxrt/boards/MIMXRT1015_EVK/clock_config.h @@ -0,0 +1,107 @@ +/* + * Copyright 2018-2019 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _CLOCK_CONFIG_H_ +#define _CLOCK_CONFIG_H_ + +#include "fsl_common.h" + +/******************************************************************************* + * Definitions + ******************************************************************************/ +#define BOARD_XTAL0_CLK_HZ 24000000U /*!< Board xtal0 frequency in Hz */ + +#define BOARD_XTAL32K_CLK_HZ 32768U /*!< Board xtal32k frequency in Hz */ +/******************************************************************************* + ************************ BOARD_InitBootClocks function ************************ + ******************************************************************************/ + +#if defined(__cplusplus) +extern "C" { +#endif /* __cplusplus*/ + +/*! + * @brief This function executes default configuration of clocks. + * + */ +void BOARD_InitBootClocks(void); + +#if defined(__cplusplus) +} +#endif /* __cplusplus*/ + +/******************************************************************************* + ********************** Configuration BOARD_BootClockRUN *********************** + ******************************************************************************/ +/******************************************************************************* + * Definitions for BOARD_BootClockRUN configuration + ******************************************************************************/ +#define BOARD_BOOTCLOCKRUN_CORE_CLOCK 500000000U /*!< Core clock frequency: 500000000Hz */ + +/* Clock outputs (values are in Hz): */ +#define BOARD_BOOTCLOCKRUN_AHB_CLK_ROOT 500000000UL +#define BOARD_BOOTCLOCKRUN_CKIL_SYNC_CLK_ROOT 32768UL +#define BOARD_BOOTCLOCKRUN_CLKO1_CLK 0UL +#define BOARD_BOOTCLOCKRUN_CLKO2_CLK 0UL +#define BOARD_BOOTCLOCKRUN_CLK_1M 1000000UL +#define BOARD_BOOTCLOCKRUN_CLK_24M 24000000UL +#define BOARD_BOOTCLOCKRUN_ENET_500M_REF_CLK 500000000UL +#define BOARD_BOOTCLOCKRUN_FLEXIO1_CLK_ROOT 30000000UL +#define BOARD_BOOTCLOCKRUN_FLEXSPI_CLK_ROOT 125000000UL +#define BOARD_BOOTCLOCKRUN_GPT1_IPG_CLK_HIGHFREQ 62500000UL +#define BOARD_BOOTCLOCKRUN_GPT2_IPG_CLK_HIGHFREQ 62500000UL +#define BOARD_BOOTCLOCKRUN_IPG_CLK_ROOT 125000000UL +#define BOARD_BOOTCLOCKRUN_LPI2C_CLK_ROOT 60000000UL +#define BOARD_BOOTCLOCKRUN_LPSPI_CLK_ROOT 105600000UL +#define BOARD_BOOTCLOCKRUN_MQS_MCLK 63529411UL +#define BOARD_BOOTCLOCKRUN_PERCLK_CLK_ROOT 62500000UL +#define BOARD_BOOTCLOCKRUN_SAI1_CLK_ROOT 63529411UL +#define BOARD_BOOTCLOCKRUN_SAI1_MCLK1 63529411UL +#define BOARD_BOOTCLOCKRUN_SAI1_MCLK2 63529411UL +#define BOARD_BOOTCLOCKRUN_SAI1_MCLK3 30000000UL +#define BOARD_BOOTCLOCKRUN_SAI2_CLK_ROOT 63529411UL +#define BOARD_BOOTCLOCKRUN_SAI2_MCLK1 63529411UL +#define BOARD_BOOTCLOCKRUN_SAI2_MCLK2 0UL +#define BOARD_BOOTCLOCKRUN_SAI2_MCLK3 30000000UL +#define BOARD_BOOTCLOCKRUN_SAI3_CLK_ROOT 63529411UL +#define BOARD_BOOTCLOCKRUN_SAI3_MCLK1 63529411UL +#define BOARD_BOOTCLOCKRUN_SAI3_MCLK2 0UL +#define BOARD_BOOTCLOCKRUN_SAI3_MCLK3 30000000UL +#define BOARD_BOOTCLOCKRUN_SPDIF0_CLK_ROOT 30000000UL +#define BOARD_BOOTCLOCKRUN_SPDIF0_EXTCLK_OUT 0UL +#define BOARD_BOOTCLOCKRUN_TRACE_CLK_ROOT 117333333UL +#define BOARD_BOOTCLOCKRUN_UART_CLK_ROOT 80000000UL +#define BOARD_BOOTCLOCKRUN_USBPHY1_CLK 0UL + +/*! @brief Usb1 PLL set for BOARD_BootClockRUN configuration. + */ +extern const clock_usb_pll_config_t usb1PllConfig_BOARD_BootClockRUN; +/*! @brief Sys PLL for BOARD_BootClockRUN configuration. + */ +extern const clock_sys_pll_config_t sysPllConfig_BOARD_BootClockRUN; +/*! @brief Enet PLL set for BOARD_BootClockRUN configuration. + */ +extern const clock_enet_pll_config_t enetPllConfig_BOARD_BootClockRUN; + +/******************************************************************************* + * API for BOARD_BootClockRUN configuration + ******************************************************************************/ +#if defined(__cplusplus) +extern "C" { +#endif /* __cplusplus*/ + +/*! + * @brief This function executes configuration of clocks. + * + */ +void BOARD_BootClockRUN(void); + +#if defined(__cplusplus) +} +#endif /* __cplusplus*/ + +#endif /* _CLOCK_CONFIG_H_ */ diff --git a/ports/mimxrt/boards/MIMXRT1015_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1015_EVK/mpconfigboard.h new file mode 100644 index 0000000000..17326cb485 --- /dev/null +++ b/ports/mimxrt/boards/MIMXRT1015_EVK/mpconfigboard.h @@ -0,0 +1,84 @@ +#define MICROPY_HW_BOARD_NAME "i.MX RT1015 EVK" +#define MICROPY_HW_MCU_NAME "MIMXRT1015DAF5A" + +// i.MX RT1015 EVK has 3 board LED +// Todo: think about replacing the define with searching in the generated pins? +#define MICROPY_HW_LED1_PIN (pin_GPIO_SD_B1_00) +#define MICROPY_HW_LED2_PIN (pin_GPIO_SD_B1_01) +#define MICROPY_HW_LED3_PIN (pin_GPIO_SD_B1_02) +#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_low(pin)) +#define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_high(pin)) + +#define MICROPY_HW_NUM_PIN_IRQS (3 * 32) + +// Define mapping logical UART # to hardware UART # +// RX/TX HW-UART Logical UART +// DEBUG USB LPUART1 -> 0 +// D3/D5 LPUART1 +// D0/D1 LPUART2 -> 1 +// D6/D9 LPUART3 -> 2 +// A0/A1 LPUART4 -> 5 + +#define MICROPY_HW_UART_NUM (sizeof(uart_index_table) / sizeof(uart_index_table)[0]) +#define MICROPY_HW_UART_INDEX { 1, 4, 3 } + +#define IOMUX_TABLE_UART \ + { IOMUXC_GPIO_AD_B0_06_LPUART1_TX }, { IOMUXC_GPIO_AD_B0_07_LPUART1_RX }, \ + { 0 }, { 0 }, \ + { IOMUXC_GPIO_AD_B0_14_LPUART3_TX }, { IOMUXC_GPIO_AD_B0_15_LPUART3_RX }, \ + { IOMUXC_GPIO_EMC_32_LPUART4_TX }, { IOMUXC_GPIO_EMC_33_LPUART4_RX }, \ + +#define MICROPY_HW_SPI_INDEX { 1 } + +#define IOMUX_TABLE_SPI \ + { IOMUXC_GPIO_AD_B0_10_LPSPI1_SCK }, { IOMUXC_GPIO_AD_B0_11_LPSPI1_PCS0 }, \ + { IOMUXC_GPIO_AD_B0_12_LPSPI1_SDO }, { IOMUXC_GPIO_AD_B0_13_LPSPI1_SDI }, \ + { 0 } + +#define DMA_REQ_SRC_RX { 0, kDmaRequestMuxLPSPI1Rx, kDmaRequestMuxLPSPI2Rx } + +#define DMA_REQ_SRC_TX { 0, kDmaRequestMuxLPSPI1Tx, kDmaRequestMuxLPSPI2Tx } + +// Define mapping hardware I2C # to logical I2C # +// SDA/SCL HW-I2C Logical I2C +// D14/D15 LPI2C4 -> 0 +// A4/A5 LPI2C1 -> 1 +// D0/D1 LPI2C2 -> 2 + +#define MICROPY_HW_I2C_INDEX { 1, 2 } + +#define IOMUX_TABLE_I2C \ + { IOMUXC_GPIO_AD_B1_14_LPI2C1_SCL }, { IOMUXC_GPIO_AD_B1_15_LPI2C1_SDA }, \ + { IOMUXC_GPIO_EMC_19_LPI2C2_SCL }, { IOMUXC_GPIO_EMC_18_LPI2C2_SDA }, + +#define MICROPY_PY_MACHINE_I2S (1) +#define MICROPY_HW_I2S_NUM (1) +#define I2S_CLOCK_MUX { 0, kCLOCK_Sai1Mux, kCLOCK_Sai2Mux } +#define I2S_CLOCK_PRE_DIV { 0, kCLOCK_Sai1PreDiv, kCLOCK_Sai2PreDiv } +#define I2S_CLOCK_DIV { 0, kCLOCK_Sai1Div, kCLOCK_Sai2Div } +#define I2S_IOMUXC_GPR_MODE { 0, kIOMUXC_GPR_SAI1MClkOutputDir, kIOMUXC_GPR_SAI2MClkOutputDir } +#define I2S_DMA_REQ_SRC_RX { 0, kDmaRequestMuxSai1Rx, kDmaRequestMuxSai2Rx } +#define I2S_DMA_REQ_SRC_TX { 0, kDmaRequestMuxSai1Tx, kDmaRequestMuxSai2Tx } + +#define I2S_GPIO(_hwid, _fn, _mode, _pin, _iomux) \ + { \ + .hw_id = _hwid, \ + .fn = _fn, \ + .mode = _mode, \ + .name = MP_QSTR_##_pin, \ + .iomux = {_iomux}, \ + } + +#define I2S_GPIO_MAP \ + { \ + I2S_GPIO(1, MCK, TX, GPIO_EMC_20, IOMUXC_GPIO_EMC_20_SAI1_MCLK), \ + I2S_GPIO(1, SCK, RX, GPIO_EMC_19, IOMUXC_GPIO_EMC_19_SAI1_RX_BCLK), \ + I2S_GPIO(1, WS, RX, GPIO_EMC_18, IOMUXC_GPIO_EMC_18_SAI1_RX_SYNC), \ + I2S_GPIO(1, SD, RX, GPIO_EMC_21, IOMUXC_GPIO_EMC_21_SAI1_RX_DATA00), \ + I2S_GPIO(1, SCK, TX, GPIO_EMC_26, IOMUXC_GPIO_EMC_26_SAI1_TX_BCLK), \ + I2S_GPIO(1, WS, TX, GPIO_EMC_27, IOMUXC_GPIO_EMC_27_SAI1_TX_SYNC), \ + I2S_GPIO(1, SD, TX, GPIO_EMC_25, IOMUXC_GPIO_EMC_25_SAI1_TX_DATA00), \ + } + +#define MICROPY_BOARD_ROOT_POINTERS \ + struct _machine_i2s_obj_t *machine_i2s_obj[MICROPY_HW_I2S_NUM]; diff --git a/ports/mimxrt/boards/MIMXRT1015_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1015_EVK/mpconfigboard.mk new file mode 100644 index 0000000000..5d959b31d4 --- /dev/null +++ b/ports/mimxrt/boards/MIMXRT1015_EVK/mpconfigboard.mk @@ -0,0 +1,9 @@ +MCU_SERIES = MIMXRT1015 +MCU_VARIANT = MIMXRT1015DAF5A + +MICROPY_FLOAT_IMPL = single +MICROPY_PY_MACHINE_SDCARD = 0 +MICROPY_HW_FLASH_TYPE ?= qspi_nor +MICROPY_HW_FLASH_SIZE ?= 0x1000000 # 16MB + +MICROPY_BOOT_BUFFER_SIZE = (32 * 1024) diff --git a/ports/mimxrt/boards/MIMXRT1015_EVK/pins.csv b/ports/mimxrt/boards/MIMXRT1015_EVK/pins.csv new file mode 100644 index 0000000000..2b50c7ca76 --- /dev/null +++ b/ports/mimxrt/boards/MIMXRT1015_EVK/pins.csv @@ -0,0 +1,41 @@ +D0,GPIO_EMC_33 +D1,GPIO_EMC_32 +D2,GPIO_EMC_20 +D3,GPIO_EMC_26 +D4,GPIO_EMC_34 +D5,GPIO_EMC_27 +D6,GPIO_AD_B1_11 +D7,GPIO_AD_B0_15 +D8,GPIO_EMC_21 +D9,GPIO_EMC_22 +D10,GPIO_AD_B0_11 +D11,GPIO_AD_B0_12 +D12,GPIO_AD_B0_13 +D13,GPIO_AD_B0_10 +D14,GPIO_AD_B1_15 +D15,GPIO_AD_B1_14 +A0,GPIO_AD_B1_13 +A1,GPIO_AD_B0_14 +A2,GPIO_AD_B1_12 +A3,GPIO_AD_B1_10 +A4,GPIO_AD_B1_15 +A5,GPIO_AD_B1_14 +RX,GPIO_EMC_33 +TX,GPIO_EMC_32 +SDA,GPIO_AD_B1_15 +SCL,GPIO_AD_B1_14 +SCK,GPIO_AD_B0_10 +SDI,GPIO_AD_B0_13 +SDO,GPIO_AD_B0_12 +CS,GPIO_AD_B0_11 +LED1,GPIO_SD_B1_00 +LED2,GPIO_SD_B1_01 +LED3,GPIO_SD_B1_02 +BUTTON,GPIO_EMC_09 +MCK,GPIO_EMC_20 +SCK_RX,GPIO_EMC_19 +WS_RX,GPIO_EMC_18 +SD_RX,GPIO_EMC_21 +SCK_TX,GPIO_EMC_26 +WS_TX,GPIO_EMC_27 +SD_TX,GPIO_EMC_25 \ No newline at end of file diff --git a/ports/mimxrt/boards/MIMXRT1015_af.csv b/ports/mimxrt/boards/MIMXRT1015_af.csv new file mode 100644 index 0000000000..d988662583 --- /dev/null +++ b/ports/mimxrt/boards/MIMXRT1015_af.csv @@ -0,0 +1,57 @@ +Pad,ALT0,ALT1,ALT2,ALT3,ALT4,ALT5,ALT6,ALT7,ALT8,ALT9,ADC,ACMP,Default +GPIO_AD_B0_00,JTAG_TMS,,,,,GPIO1_IO00,,GPT1_COMPARE1,,,,,ALT0 +GPIO_AD_B0_01,JTAG_TCK,,,,,GPIO1_IO01,,GPT1_CAPTURE2,,,,,ALT0 +GPIO_AD_B0_02,JTAG_MOD,,,,,GPIO1_IO02,,GPT1_CAPTURE1,,,,,ALT0 +GPIO_AD_B0_03,JTAG_TDI,,WDOG1_B,SAI1_MCLK,,GPIO1_IO03,USB_OTG1_OC,CCM_PMIC_RDY,,,,,ALT0 +GPIO_AD_B0_04,JTAG_TDO,,,,,GPIO1_IO04,USB_OTG1_PWR,EWM_OUT_B,,,,,ALT0 +GPIO_AD_B0_05,JTAG_TRSTB,,,,,GPIO1_IO05,USB_OTG1_ID,ARM_NMI,,,,,ALT0 +GPIO_AD_B0_06,PIT_TRIGGER0,MQS_RIGHT,LPUART1_TXD,,,GPIO1_IO06,REF_32K_OUT,,,,,,ALT5 +GPIO_AD_B0_07,PIT_TRIGGER1,MQS_LEFT,LPUART1_RXD,,,GPIO1_IO07,REF_24M_OUT,,,,,,ALT5 +GPIO_AD_B0_08,,,LPUART1_CTS_B,KPP_COL0,,GPIO1_IO08,ARM_CM7_TXEV,,,,,,ALT5 +GPIO_AD_B0_09,,,LPUART1_RTS_B,KPP_ROW0,CSU_CSU_INT_DEB,GPIO1_IO09,ARM_CM7_RXEV,,,,,,ALT5 +GPIO_AD_B0_10,,LPSPI1_SCK,,KPP_COL1,,GPIO1_IO10,ARM_TRACE_CLK,,,,,,ALT5 +GPIO_AD_B0_11,,LPSPI1_PCS0,,KPP_ROW1,,GPIO1_IO11,ARM_TRACE_SWO,,,,,,ALT5 +GPIO_AD_B0_12,,LPSPI1_SDO,LPUART3_CTS_B,KPP_COL2,,GPIO1_IO12,ARM_TRACE0,SNVS_VIO_5_CTL,,,ADC1_IN0,,ALT5 +GPIO_AD_B0_13,,LPSPI1_SDI,LPUART3_RTS_B,KPP_ROW2,,GPIO1_IO13,ARM_CM7_TRACE01,SNVS_VIO_5_B,,,,,ALT5 +GPIO_AD_B0_14,,,LPUART3_TXD,KPP_COL3,,GPIO1_IO14,ARM_CM7_TRACE02,WDOG1_ANY,,,ADC1_IN1,,ALT5 +GPIO_AD_B0_15,,,LPUART3_RXD,KPP_ROW3,,GPIO1_IO15,ARM_CM7_TRACE03,,,,ADC1_IN2,,ALT5 +GPIO_AD_B1_10,USB_OTG1_PWR,FLEXPWM1_PWM2_A,LPUART4_TXD,,FLEXIO1_D05,GPIO1_IO26,GPT2_CAPTURE1,,,,ADC1_IN10,,ALT5 +GPIO_AD_B1_11,USB_OTG1_ID,FLEXPWM1_PWM2_B,LPUART4_RXD,,FLEXIO1_D04,GPIO1_IO27,GPT2_COMPARE1,,,,ADC1_IN11,,ALT5 +GPIO_AD_B1_12,USB_OTG1_OC,ACMP1_OUT,,,FLEXIO1_D03,GPIO1_IO28,FLEXPWM1_PWM3_A,,,,ADC1_IN12,ACMP1_OUT,ALT5 +GPIO_AD_B1_13,LPI2C1_HREQ,ACMP2_OUT,,,FLEXIO1_D02,GPIO1_IO29,FLEXPWM1_PWM3_B,,,,ADC1_IN13,ACMP2_OUT,ALT5 +GPIO_AD_B1_14,LPI2C1_SCL,ACMP3_OUT,,,FLEXIO1_D01,GPIO1_IO30,,,,,ADC1_IN14,ACMP3_OUT,ALT5 +GPIO_AD_B1_15,LPI2C1_SDA,ACMP4_OUT,,,FLEXIO1_D00,GPIO1_IO31,,,,,ADC1_IN15,ACMP4_OUT,ALT5 +GPIO_EMC_04,,XBAR_INOUT04,SPDIF_OUT,SAI2_TX_BCLK,FLEXIO1_D16,GPIO2_IO04,,SJC_JTAG_ACT,,,,,ALT5 +GPIO_EMC_05,,XBAR_INOUT05,SPDIF_IN,SAI2_TX_SYNC,FLEXIO1_D17,GPIO2_IO05,,SJC_DE_B,,,,,ALT5 +GPIO_EMC_06,,XBAR_INOUT06,LPUART3_TXD,SAI2_TX_DATA,FLEXIO1_D18,GPIO2_IO06,,,,,,,ALT5 +GPIO_EMC_07,,XBAR_INOUT07,LPUART3_RXD,SAI2_RX_SYNC,FLEXIO1_D19,GPIO2_IO07,,,,,,,ALT5 +GPIO_EMC_08,,XBAR_INOUT08,,SAI2_RX_DATA,FLEXIO1_D20,GPIO2_IO08,,,,,,,ALT5 +GPIO_EMC_09,,XBAR_INOUT09,,SAI2_RX_BCLK,FLEXIO1_D21,GPIO2_IO09,,,,,,,ALT5 +GPIO_EMC_16,,,MQS_RIGHT,SAI2_MCLK,,GPIO2_IO16,SRC_BOOT_MODE0,,,,,,ALT5 +GPIO_EMC_17,,,MQS_LEFT,SAI3_MCLK,,GPIO2_IO17,SRC_BOOT_MODE1,,,,,,ALT5 +GPIO_EMC_18,,XBAR_INOUT16,LPI2C2_SDA,SAI1_RX_SYNC,FLEXIO1_D22,GPIO2_IO18,SRC_BT_CFG0,,,,,,ALT5 +GPIO_EMC_19,,XBAR_INOUT17,LPI2C2_SCL,SAI1_RX_BCLK,FLEXIO1_D23,GPIO2_IO19,SRC_BT_CFG1,,,,,,ALT5 +GPIO_EMC_20,,FLEXPWM1_PWM3_A,LPUART2_CTS_B,SAI1_MCLK,FLEXIO1_D24,GPIO2_IO20,SRC_BT_CFG2,,,,,,ALT5 +GPIO_EMC_21,,FLEXPWM1_PWM3_B,LPUART2_RTS_B,SAI1_RX_DATA0,FLEXIO1_D25,GPIO2_IO21,SRC_BT_CFG3,,,,,,ALT5 +GPIO_EMC_22,,FLEXPWM1_PWM2_A,LPUART2_TXD,SAI1_TX_DATA3,FLEXIO1_D26,GPIO2_IO22,SRC_BT_CFG4,,,,,,ALT5 +GPIO_EMC_23,,FLEXPWM1_PWM2_B,LPUART2_RXD,SAI1_TX_DATA2,FLEXIO1_D27,GPIO2_IO23,SRC_BT_CFG5,,,,,,ALT5 +GPIO_EMC_24,,FLEXPWM1_PWM1_A,,SAI1_TX_DATA1,FLEXIO1_D28,GPIO2_IO24,SRC_BT_CFG6,,,,,,ALT5 +GPIO_EMC_25,,FLEXPWM1_PWM1_B,,SAI1_TX_DATA0,FLEXIO1_D29,GPIO2_IO25,SRC_BT_CFG7,,,,,,ALT5 +GPIO_EMC_26,,FLEXPWM1_PWM0_A,,SAI1_TX_BCLK,FLEXIO1_D30,GPIO2_IO26,SRC_BT_CFG8,,,,,,ALT5 +GPIO_EMC_27,,FLEXPWM1_PWM0_B,,SAI1_TX_SYNC,FLEXIO1_D31,GPIO2_IO27,SRC_BT_CFG9,,,,,,ALT5 +GPIO_EMC_32,,TMR1_TIMER0,LPUART4_TXD,SAI3_TX_DATA,,GPIO3_IO00,,REF_24M_OUT,,,,,ALT5 +GPIO_EMC_33,,TMR1_TIMER1,LPUART4_RXD,SAI3_TX_BCLK,,GPIO3_IO01,,,,,,,ALT5 +GPIO_EMC_34,,TMR1_TIMER2,,SAI3_TX_SYNC,,GPIO3_IO02,,,,,,,ALT5 +GPIO_EMC_35,,TMR1_TIMER3,,,,GPIO3_IO03,,,,,,,ALT5 +GPIO_SD_B1_00,,FLEXSPI_B_DATA3,,XBAR_INOUT10,,GPIO3_IO20,,,,,,,ALT5 +GPIO_SD_B1_01,,FLEXSPI_B_SCLK,,FLEXSPI_A_SS1_B,,GPIO3_IO21,,,,,,,ALT5 +GPIO_SD_B1_02,,FLEXSPI_B_DATA0,,,,GPIO3_IO22,CCM_CLKO1,,,,,,ALT5 +GPIO_SD_B1_03,,FLEXSPI_B_DATA2,,,,GPIO3_IO23,CCM_CLKO2,,,,,,ALT5 +GPIO_SD_B1_04,,FLEXSPI_B_DATA1,,,EWM_OUT_B,GPIO3_IO24,CCM_WAIT,,,,,,ALT5 +GPIO_SD_B1_05,,FLEXSPI_A_DQS,,SAI3_MCLK,FLEXSPI_B_SS0_B,GPIO3_IO25,CCM_PMIC_RDY,,,,,,ALT5 +GPIO_SD_B1_06,,FLEXSPI_A_DATA3,,SAI3_TX_BCLK,LPSPI2_PCS0,GPIO3_IO26,CCM_STOP,,,,,,ALT5 +GPIO_SD_B1_07,,FLEXSPI_A_SCLK,,SAI3_TX_SYNC,LPSPI2_SCK,GPIO3_IO27,,,,,,,ALT5 +GPIO_SD_B1_08,,FLEXSPI_A_DATA0,,SAI3_TX_DATA,LPSPI2_SDO,GPIO3_IO28,,,,,,,ALT5 +GPIO_SD_B1_09,,FLEXSPI_A_DATA2,,SAI3_RX_BCLK,LPSPI2_SDI,GPIO3_IO29,CCM_REF_EN_B,,,,,,ALT5 +GPIO_SD_B1_10,,FLEXSPI_A_DATA1,,SAI3_RX_SYNC,LPSPI2_PCS2,GPIO3_IO30,SRC_SYSTEM_RESET,,,,,,ALT5 +GPIO_SD_B1_11,,FLEXSPI_A_SS0_B,,SAI3_RX_DATA,LPSPI2_PCS3,GPIO3_IO31,SRC_EARLY_RESET,,,,,,ALT5 diff --git a/ports/mimxrt/boards/make-flexram-config.py b/ports/mimxrt/boards/make-flexram-config.py index 0a667ff5b7..5d9c1a8c7c 100644 --- a/ports/mimxrt/boards/make-flexram-config.py +++ b/ports/mimxrt/boards/make-flexram-config.py @@ -172,6 +172,7 @@ def mimxrt_106x_gen_code(extract_dict): def main(defines_file, features_file, ld_script, controller): dispatcher = { "MIMXRT1011": (mimxrt_default_parser, mimxrt_default_gen_code), + "MIMXRT1015": (mimxrt_default_parser, mimxrt_default_gen_code), "MIMXRT1021": (mimxrt_default_parser, mimxrt_default_gen_code), "MIMXRT1052": (mimxrt_default_parser, mimxrt_default_gen_code), "MIMXRT1062": (mimxrt_default_parser, mimxrt_106x_gen_code), diff --git a/ports/mimxrt/mphalport.c b/ports/mimxrt/mphalport.c index 17a6898c40..673b58b23f 100644 --- a/ports/mimxrt/mphalport.c +++ b/ports/mimxrt/mphalport.c @@ -33,7 +33,12 @@ #include "ticks.h" #include "tusb.h" #include "fsl_snvs_lp.h" + +#if FSL_COMMON_DRIVER_VERSION != 0x020001 #include "fsl_ocotp.h" +#else +void OCOTP_Init(OCOTP_Type *base, uint32_t srcClock_Hz); +#endif #include CPU_HEADER_H @@ -124,7 +129,6 @@ uint64_t mp_hal_time_ns(void) { // MAC address void mp_hal_get_unique_id(uint8_t id[]) { - OCOTP_Init(OCOTP, CLOCK_GetFreq(kCLOCK_IpgClk)); *(uint32_t *)&id[0] = OCOTP->CFG0; *(uint32_t *)&id[4] = OCOTP->CFG1; } From b0d460cd7deb6f1cc3393f56c378943aca8d954b Mon Sep 17 00:00:00 2001 From: robert-hh Date: Thu, 3 Feb 2022 15:41:56 +0100 Subject: [PATCH 188/619] mimxrt/eth: Fix an Ethernet transmit error. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sometimes frames could not be sent immediately because the controller was still busy with previous frames. Then, an error was returned to lwip. This fix adds a limited number of retries for this busy state, waiting 100µs before the next attempt. Typically the transmit succeeds now at the second attempt. Second change: Reset the controller for a clean state after soft reset. --- ports/mimxrt/eth.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/ports/mimxrt/eth.c b/ports/mimxrt/eth.c index af0264d085..08e4dcc09d 100644 --- a/ports/mimxrt/eth.c +++ b/ports/mimxrt/eth.c @@ -174,7 +174,7 @@ void eth_irq_handler(ENET_Type *base, enet_handle_t *handle, enet_event_t event, } } while (status != kStatus_ENET_RxFrameEmpty); } else { - ENET_ClearInterruptStatus(base, kENET_TxFrameInterrupt); + ENET_ClearInterruptStatus(base, ENET_TX_INTERRUPT | ENET_ERR_INTERRUPT); } } @@ -279,6 +279,7 @@ void eth_init(eth_t *self, int mac_idx, const phy_operations_t *phy_ops, int phy mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("PHY Init failed.")); } + ENET_Reset(ENET); ENET_GetDefaultConfig(&enet_config); enet_config.miiSpeed = (enet_mii_speed_t)speed; enet_config.miiDuplex = (enet_mii_duplex_t)duplex; @@ -311,6 +312,22 @@ void eth_set_trace(eth_t *self, uint32_t value) { /*******************************************************************************/ // ETH-LwIP bindings +STATIC err_t eth_send_frame_blocking(ENET_Type *base, enet_handle_t *handle, uint8_t *buffer, int len) { + status_t status; + int i; + #define XMIT_LOOP 10 + + // Try a few times to send the frame + for (i = XMIT_LOOP; i > 0; i--) { + status = ENET_SendFrame(base, handle, buffer, len); + if (status != kStatus_ENET_TxFrameBusy) { + break; + } + ticks_delay_us64(100); + } + return status; +} + STATIC err_t eth_netif_output(struct netif *netif, struct pbuf *p) { // This function should always be called from a context where PendSV-level IRQs are disabled status_t status; @@ -319,7 +336,7 @@ STATIC err_t eth_netif_output(struct netif *netif, struct pbuf *p) { eth_trace(netif->state, (size_t)-1, p, NETUTILS_TRACE_IS_TX | NETUTILS_TRACE_NEWLINE); if (p->next == NULL) { - status = ENET_SendFrame(ENET, &g_handle, p->payload, p->len); + status = eth_send_frame_blocking(ENET, &g_handle, p->payload, p->len); } else { // frame consists of several parts. Copy them together and send them size_t length = 0; @@ -330,8 +347,8 @@ STATIC err_t eth_netif_output(struct netif *netif, struct pbuf *p) { length += p->len; p = p->next; } - status = ENET_SendFrame(ENET, &g_handle, tx_frame, length); - } + status = eth_send_frame_blocking(ENET, &g_handle, tx_frame, length); + } return status == kStatus_Success ? ERR_OK : ERR_BUF; } From c72dfbcef96ef137c8faf72f0ccdb2ce6aab514c Mon Sep 17 00:00:00 2001 From: robert-hh Date: Thu, 3 Feb 2022 18:37:54 +0100 Subject: [PATCH 189/619] mimxrt/boards/TEENSY41: Use the same SPI1 pins for Teensy 4.0 and 4.1. Teensy 4.1 used different pins for SPI1 than Teensy 4.0, which made the boards unnecessarily different. --- ports/mimxrt/boards/TEENSY41/mpconfigboard.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/mimxrt/boards/TEENSY41/mpconfigboard.h b/ports/mimxrt/boards/TEENSY41/mpconfigboard.h index 449f10babb..3b8d497c3f 100644 --- a/ports/mimxrt/boards/TEENSY41/mpconfigboard.h +++ b/ports/mimxrt/boards/TEENSY41/mpconfigboard.h @@ -31,8 +31,8 @@ { 0 }, { 0 }, \ { 0 }, { 0 }, \ { 0 }, \ - { IOMUXC_GPIO_AD_B1_15_LPSPI3_SCK }, { IOMUXC_GPIO_AD_B1_12_LPSPI3_PCS0 }, \ - { IOMUXC_GPIO_AD_B1_14_LPSPI3_SDO }, { IOMUXC_GPIO_AD_B1_13_LPSPI3_SDI }, \ + { IOMUXC_GPIO_AD_B1_15_LPSPI3_SCK }, { IOMUXC_GPIO_AD_B0_03_LPSPI3_PCS0 }, \ + { IOMUXC_GPIO_AD_B1_14_LPSPI3_SDO }, { IOMUXC_GPIO_AD_B0_02_LPSPI3_SDI }, \ { 0 }, \ { IOMUXC_GPIO_B0_03_LPSPI4_SCK }, { IOMUXC_GPIO_B0_00_LPSPI4_PCS0 }, \ { IOMUXC_GPIO_B0_02_LPSPI4_SDO }, { IOMUXC_GPIO_B0_01_LPSPI4_SDI }, \ From 04f92a28251058756d776e90c044441e8f876f3f Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sun, 6 Feb 2022 17:33:24 +0100 Subject: [PATCH 190/619] mimxrt/boards: Support using an optional board-specific manifest.py. If the board directory contains a manifest.py file, it will be included. File not found errors will be ignored. --- ports/mimxrt/boards/manifest.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ports/mimxrt/boards/manifest.py b/ports/mimxrt/boards/manifest.py index ccbd33cae8..2e1a1d63d4 100644 --- a/ports/mimxrt/boards/manifest.py +++ b/ports/mimxrt/boards/manifest.py @@ -2,3 +2,7 @@ freeze("$(PORT_DIR)/modules") freeze("$(MPY_DIR)/drivers/onewire") freeze("$(MPY_DIR)/drivers/dht", "dht.py") include("$(MPY_DIR)/extmod/uasyncio/manifest.py") +try: + include("$(BOARD_DIR)/manifest.py") +except FileNotFoundError: + pass From 4774501cab372b75ca263be91d5eb0fa3ba9d60d Mon Sep 17 00:00:00 2001 From: robert-hh Date: Wed, 23 Feb 2022 14:15:27 +0100 Subject: [PATCH 191/619] mimxrt/eth: Avoid a race condition for Ethernet. That caused Ethernet to lock up at high data rates after ~200MByte data average in a row. Tested now with data bursts up to 10 GByte and overall data rates of ~8MByte/s at the Eth100 port. --- ports/mimxrt/eth.c | 3 ++- ports/mimxrt/mpconfigport.h | 8 +++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/ports/mimxrt/eth.c b/ports/mimxrt/eth.c index 08e4dcc09d..288aafb6b3 100644 --- a/ports/mimxrt/eth.c +++ b/ports/mimxrt/eth.c @@ -291,6 +291,7 @@ void eth_init(eth_t *self, int mac_idx, const phy_operations_t *phy_ops, int phy ENET_Init(ENET, &g_handle, &enet_config, &buffConfig[0], hw_addr, CLOCK_GetFreq(kCLOCK_IpgClk)); ENET_SetCallback(&g_handle, eth_irq_handler, (void *)self); + NVIC_SetPriority(ENET_IRQn, IRQ_PRI_PENDSV); ENET_EnableInterrupts(ENET, ENET_RX_INTERRUPT); ENET_ClearInterruptStatus(ENET, ENET_TX_INTERRUPT | ENET_RX_INTERRUPT | ENET_ERR_INTERRUPT); ENET_ActiveRead(ENET); @@ -348,7 +349,7 @@ STATIC err_t eth_netif_output(struct netif *netif, struct pbuf *p) { p = p->next; } status = eth_send_frame_blocking(ENET, &g_handle, tx_frame, length); - } + } return status == kStatus_Success ? ERR_OK : ERR_BUF; } diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index 4462a18a61..7958874447 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -169,9 +169,11 @@ uint32_t trng_random_u32(void); // For regular code that wants to prevent "background tasks" from running. // These background tasks (LWIP, Bluetooth) run in PENDSV context. // TODO: Check for the settings of the STM32 port in irq.h -#define MICROPY_PY_PENDSV_ENTER uint32_t atomic_state = disable_irq(); -#define MICROPY_PY_PENDSV_REENTER atomic_state = disable_irq(); -#define MICROPY_PY_PENDSV_EXIT enable_irq(atomic_state); +#define NVIC_PRIORITYGROUP_4 ((uint32_t)0x00000003) +#define IRQ_PRI_PENDSV NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 15, 0) +#define MICROPY_PY_PENDSV_ENTER uint32_t atomic_state = raise_irq_pri(IRQ_PRI_PENDSV); +#define MICROPY_PY_PENDSV_REENTER atomic_state = raise_irq_pri(IRQ_PRI_PENDSV); +#define MICROPY_PY_PENDSV_EXIT restore_irq_pri(atomic_state); // Use VfsLfs2's types for fileio/textio #define mp_type_fileio mp_type_vfs_lfs2_fileio From e0b97013d0730a70d94922aca168f49b9f456e46 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Fri, 25 Feb 2022 20:18:24 +0100 Subject: [PATCH 192/619] mimxrt/hal/pwm_backport: Make PWM symmetric, and round division calcs. Ensure the symmetry of PWM: the duty rate of X and Q channels was not 50%, when it should have been. That is evident at high frequencies, like 15Mhz or 37.5 MHz. At low frequencies the deviation mattered less. The A/B channels were fine. Also round up or down non-integer division factors. Before, always the floor value was used. --- ports/mimxrt/hal/pwm_backport.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ports/mimxrt/hal/pwm_backport.c b/ports/mimxrt/hal/pwm_backport.c index cea6642120..a4663df4f1 100644 --- a/ports/mimxrt/hal/pwm_backport.c +++ b/ports/mimxrt/hal/pwm_backport.c @@ -53,7 +53,7 @@ void PWM_SetupPwm_u16(PWM_Type *base, pwm_submodule_t subModule, pwm_signal_para // Divide the clock by the prescale value pwmClock = (srcClock_Hz / (1U << ((base->SM[subModule].CTRL & PWM_CTRL_PRSC_MASK) >> PWM_CTRL_PRSC_SHIFT))); - pulseCnt = pwmClock / pwmFreq_Hz; + pulseCnt = (pwmClock + (pwmFreq_Hz - 1) / 2) / pwmFreq_Hz; base->SM[subModule].INIT = 0; base->SM[subModule].VAL1 = pulseCnt - 1; @@ -93,9 +93,9 @@ void PWM_SetupPwmx_u16(PWM_Type *base, pwm_submodule_t subModule, // Divide the clock by the prescale value pwmClock = (srcClock_Hz / (1U << ((base->SM[subModule].CTRL & PWM_CTRL_PRSC_MASK) >> PWM_CTRL_PRSC_SHIFT))); - pulseCnt = pwmClock / pwmFreq_Hz; + pulseCnt = (pwmClock + (pwmFreq_Hz - 1) / 2) / pwmFreq_Hz; base->SM[subModule].INIT = 0; - base->SM[subModule].VAL0 = ((uint32_t)duty_cycle * pulseCnt) / PWM_FULL_SCALE; + base->SM[subModule].VAL0 = ((uint32_t)duty_cycle * pulseCnt) / PWM_FULL_SCALE - 1; base->SM[subModule].VAL1 = pulseCnt - 1; base->SM[subModule].OCTRL = (base->SM[subModule].OCTRL & ~PWM_OCTRL_POLX_MASK) | PWM_OCTRL_POLX(!invert); @@ -137,7 +137,7 @@ status_t QTMR_SetupPwm_u16(TMR_Type *base, qtmr_channel_selection_t channel, uin } // Counter values to generate a PWM signal - periodCount = (srcClock_Hz / pwmFreqHz) - 1; + periodCount = ((srcClock_Hz + (pwmFreqHz - 1) / 2) / pwmFreqHz) - 2; highCount = (periodCount * dutyCycleU16) / PWM_FULL_SCALE; lowCount = periodCount - highCount; From e2513bfe8db91055e443e1804e3abb90c9998d86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20de=20Giessen?= Date: Thu, 27 Aug 2020 14:16:48 +0200 Subject: [PATCH 193/619] extmod/moduzlib: Fix parsing zlib header dict size. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From RFC 1950 section 2.2: "CINFO is the base-2 logarithm of the LZ77 window size, minus eight (CINFO=7 indicates a 32K window size)" Signed-off-by: Daniël van de Giessen --- extmod/moduzlib.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/extmod/moduzlib.c b/extmod/moduzlib.c index f78c349085..a6874fb4e3 100644 --- a/extmod/moduzlib.c +++ b/extmod/moduzlib.c @@ -77,7 +77,7 @@ STATIC mp_obj_t decompio_make_new(const mp_obj_type_t *type, size_t n_args, size o->eof = false; mp_int_t dict_opt = 0; - int dict_sz; + uint dict_sz; if (n_args > 1) { dict_opt = mp_obj_get_int(args[1]); } @@ -94,7 +94,10 @@ STATIC mp_obj_t decompio_make_new(const mp_obj_type_t *type, size_t n_args, size header_error: mp_raise_ValueError(MP_ERROR_TEXT("compression header")); } - dict_sz = 1 << dict_opt; + // RFC 1950 section 2.2: + // CINFO is the base-2 logarithm of the LZ77 window size, + // minus eight (CINFO=7 indicates a 32K window size) + dict_sz = 1 << (dict_opt + 8); } else { dict_sz = 1 << -dict_opt; } @@ -116,6 +119,7 @@ STATIC mp_uint_t decompio_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *er o->eof = true; } if (st < 0) { + DEBUG_printf("uncompress error=" INT_FMT "\n", st); *errcode = MP_EINVAL; return MP_STREAM_ERROR; } From 507ad03329983551635831c0b5ba76319eaca165 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Wed, 16 Jun 2021 00:55:09 +0200 Subject: [PATCH 194/619] rp2: Add USB MSC support. It is currently not enabled by default on any board. --- ports/rp2/CMakeLists.txt | 1 + ports/rp2/boards/PICO/mpconfigboard.h | 3 + ports/rp2/main.c | 4 + ports/rp2/modules/_boot_fat.py | 15 +++ ports/rp2/mpconfigport.h | 16 +++- ports/rp2/msc_disk.c | 127 ++++++++++++++++++++++++++ ports/rp2/tusb_config.h | 9 ++ ports/rp2/tusb_port.c | 19 ++++ 8 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 ports/rp2/modules/_boot_fat.py create mode 100644 ports/rp2/msc_disk.c diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt index 0009ab2cdb..a428b0c39b 100644 --- a/ports/rp2/CMakeLists.txt +++ b/ports/rp2/CMakeLists.txt @@ -105,6 +105,7 @@ set(MICROPY_SOURCE_PORT rp2_pio.c tusb_port.c uart.c + msc_disk.c ) set(MICROPY_SOURCE_QSTR diff --git a/ports/rp2/boards/PICO/mpconfigboard.h b/ports/rp2/boards/PICO/mpconfigboard.h index e6623374d0..0bab214e20 100644 --- a/ports/rp2/boards/PICO/mpconfigboard.h +++ b/ports/rp2/boards/PICO/mpconfigboard.h @@ -1,3 +1,6 @@ // Board and hardware specific configuration #define MICROPY_HW_BOARD_NAME "Raspberry Pi Pico" #define MICROPY_HW_FLASH_STORAGE_BYTES (1408 * 1024) + +// Enable USB Mass Storage with FatFS filesystem. +//#define MICROPY_HW_USB_MSC (1) diff --git a/ports/rp2/main.c b/ports/rp2/main.c index 84f23af23c..02bfb6dae2 100644 --- a/ports/rp2/main.c +++ b/ports/rp2/main.c @@ -116,7 +116,11 @@ int main(int argc, char **argv) { #endif // Execute _boot.py to set up the filesystem. + #if MICROPY_VFS_FAT && MICROPY_HW_USB_MSC + pyexec_frozen_module("_boot_fat.py"); + #else pyexec_frozen_module("_boot.py"); + #endif // Execute user scripts. int ret = pyexec_file_if_exists("boot.py"); diff --git a/ports/rp2/modules/_boot_fat.py b/ports/rp2/modules/_boot_fat.py new file mode 100644 index 0000000000..02a2df9bab --- /dev/null +++ b/ports/rp2/modules/_boot_fat.py @@ -0,0 +1,15 @@ +import os +import machine, rp2 + + +# Try to mount the filesystem, and format the flash if it doesn't exist. +bdev = rp2.Flash() +try: + vfs = os.VfsFat(bdev) + os.mount(vfs, "/") +except: + os.VfsFat.mkfs(bdev) + vfs = os.VfsFat(bdev) +os.mount(vfs, "/") + +del os, bdev, vfs diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h index 1ff9d5b511..9e839766eb 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -30,8 +30,10 @@ #include "hardware/spi.h" #include "hardware/sync.h" #include "pico/binary_info.h" - #include "mpconfigboard.h" +#if MICROPY_HW_USB_MSC +#include "hardware/flash.h" +#endif // Board and hardware specific configuration #define MICROPY_HW_MCU_NAME "RP2040" @@ -107,10 +109,22 @@ #define MICROPY_FATFS_ENABLE_LFN (1) #define MICROPY_FATFS_LFN_CODE_PAGE 437 /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */ #define MICROPY_FATFS_RPATH (2) +#if MICROPY_HW_USB_MSC +#define MICROPY_FATFS_USE_LABEL (1) +#define MICROPY_FATFS_MULTI_PARTITION (1) +// Set FatFS block size to flash sector size to avoid caching +// the flash sector in memory to support smaller block sizes. +#define MICROPY_FATFS_MAX_SS (FLASH_SECTOR_SIZE) +#endif +#if MICROPY_VFS_FAT && MICROPY_HW_USB_MSC +#define mp_type_fileio mp_type_vfs_fat_fileio +#define mp_type_textio mp_type_vfs_fat_textio +#elif MICROPY_VFS_LFS2 // Use VfsLfs2's types for fileio/textio #define mp_type_fileio mp_type_vfs_lfs2_fileio #define mp_type_textio mp_type_vfs_lfs2_textio +#endif // Use VFS's functions for import stat and builtin open #define mp_import_stat mp_vfs_import_stat diff --git a/ports/rp2/msc_disk.c b/ports/rp2/msc_disk.c new file mode 100644 index 0000000000..24bd51cec3 --- /dev/null +++ b/ports/rp2/msc_disk.c @@ -0,0 +1,127 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +#include "tusb.h" +#if CFG_TUD_MSC +#include "mpconfigboard.h" +#include "hardware/flash.h" +#include "hardware/sync.h" +#include "pico/stdlib.h" + +// This implementation does Not support Flash sector caching. +#if MICROPY_FATFS_MAX_SS != FLASH_SECTOR_SIZE +#error MICROPY_FATFS_MAX_SS must be the same size as FLASH_SECTOR_SIZE +#endif + +#define BLOCK_SIZE (FLASH_SECTOR_SIZE) +#define BLOCK_COUNT (MICROPY_HW_FLASH_STORAGE_BYTES / BLOCK_SIZE) +#define FLASH_BASE_ADDR (PICO_FLASH_SIZE_BYTES - MICROPY_HW_FLASH_STORAGE_BYTES) +#define FLASH_MMAP_ADDR (XIP_BASE + FLASH_BASE_ADDR) + +static bool ejected = false; + +// Invoked when received SCSI_CMD_INQUIRY +// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively +void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) { + const char vid[] = "Micropy"; + const char pid[] = "Mass Storage"; + const char rev[] = "1.0"; + + strncpy((char *)vendor_id, vid, 8); + strncpy((char *)product_id, pid, 16); + strncpy((char *)product_rev, rev, 4); +} + +// Invoked when received Test Unit Ready command. +// return true allowing host to read/write this LUN e.g SD card inserted +bool tud_msc_test_unit_ready_cb(uint8_t lun) { + if (ejected) { + tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3a, 0x00); + return false; + } + return true; +} + +// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size +// Application update block count and block size +void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count, uint16_t *block_size) { + *block_size = BLOCK_SIZE; + *block_count = BLOCK_COUNT; +} + +// Invoked when received Start Stop Unit command +// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage +// - Start = 1 : active mode, if load_eject = 1 : load disk storage +bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) { + if (load_eject) { + if (start) { + // load disk storage + ejected = false; + } else { + // unload disk storage + ejected = true; + } + } + return true; +} + +// Callback invoked when received READ10 command. +// Copy disk's data to buffer (up to bufsize) and return number of copied bytes. +int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize) { + uint32_t count = bufsize / BLOCK_SIZE; + memcpy(buffer, (void *)(FLASH_MMAP_ADDR + lba * BLOCK_SIZE), count * BLOCK_SIZE); + return count * BLOCK_SIZE; +} + +// Callback invoked when received WRITE10 command. +// Process data in buffer to disk's storage and return number of written bytes +int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize) { + uint32_t count = bufsize / BLOCK_SIZE; + uint32_t ints = save_and_disable_interrupts(); + flash_range_erase(FLASH_BASE_ADDR + lba * BLOCK_SIZE, count * BLOCK_SIZE); + flash_range_program(FLASH_BASE_ADDR + lba * BLOCK_SIZE, buffer, count * BLOCK_SIZE); + restore_interrupts(ints); + return count * BLOCK_SIZE; +} + +// Callback invoked when received an SCSI command not in built-in list below +// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE +// - READ10 and WRITE10 has their own callbacks +int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, uint16_t bufsize) { + int32_t resplen = 0; + switch (scsi_cmd[0]) { + case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL: + // Sync the logical unit if needed. + break; + + default: + // Set Sense = Invalid Command Operation + tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); + // negative means error -> tinyusb could stall and/or response with failed status + resplen = -1; + break; + } + return resplen; +} +#endif diff --git a/ports/rp2/tusb_config.h b/ports/rp2/tusb_config.h index 8c2a9f7560..ce321616a6 100644 --- a/ports/rp2/tusb_config.h +++ b/ports/rp2/tusb_config.h @@ -25,6 +25,8 @@ #ifndef MICROPY_INCLUDED_RP2_TUSB_CONFIG_H #define MICROPY_INCLUDED_RP2_TUSB_CONFIG_H +#include "mpconfigport.h" + #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE) #define CFG_TUD_CDC (1) @@ -32,4 +34,11 @@ #define CFG_TUD_CDC_RX_BUFSIZE (256) #define CFG_TUD_CDC_TX_BUFSIZE (256) +#if MICROPY_HW_USB_MSC +// Board and hardware specific configuration +#define CFG_TUD_MSC (1) +// Set MSC EP buffer size to FatFS block size to avoid partial read/writes (offset arg). +#define CFG_TUD_MSC_BUFSIZE (MICROPY_FATFS_MAX_SS) +#endif + #endif // MICROPY_INCLUDED_RP2_TUSB_CONFIG_H diff --git a/ports/rp2/tusb_port.c b/ports/rp2/tusb_port.c index 8896be9077..4aac08791e 100644 --- a/ports/rp2/tusb_port.c +++ b/ports/rp2/tusb_port.c @@ -34,11 +34,20 @@ #define MICROPY_HW_USB_PID (0x0005) // RP2 MicroPython #endif +#if CFG_TUD_MSC +#define USBD_DESC_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_MSC_DESC_LEN) +#else #define USBD_DESC_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN) +#endif #define USBD_MAX_POWER_MA (250) #define USBD_ITF_CDC (0) // needs 2 interfaces +#define USBD_ITF_MSC (2) +#if CFG_TUD_MSC +#define USBD_ITF_MAX (3) +#else #define USBD_ITF_MAX (2) +#endif #define USBD_CDC_EP_CMD (0x81) #define USBD_CDC_EP_OUT (0x02) @@ -46,11 +55,15 @@ #define USBD_CDC_CMD_MAX_SIZE (8) #define USBD_CDC_IN_OUT_MAX_SIZE (64) +#define EPNUM_MSC_OUT (0x03) +#define EPNUM_MSC_IN (0x83) + #define USBD_STR_0 (0x00) #define USBD_STR_MANUF (0x01) #define USBD_STR_PRODUCT (0x02) #define USBD_STR_SERIAL (0x03) #define USBD_STR_CDC (0x04) +#define USBD_STR_MSC (0x05) // Note: descriptors returned from callbacks must exist long enough for transfer to complete @@ -77,6 +90,9 @@ static const uint8_t usbd_desc_cfg[USBD_DESC_LEN] = { TUD_CDC_DESCRIPTOR(USBD_ITF_CDC, USBD_STR_CDC, USBD_CDC_EP_CMD, USBD_CDC_CMD_MAX_SIZE, USBD_CDC_EP_OUT, USBD_CDC_EP_IN, USBD_CDC_IN_OUT_MAX_SIZE), + #if CFG_TUD_MSC + TUD_MSC_DESCRIPTOR(USBD_ITF_MSC, 5, EPNUM_MSC_OUT, EPNUM_MSC_IN, 64), + #endif }; static const char *const usbd_desc_str[] = { @@ -84,6 +100,9 @@ static const char *const usbd_desc_str[] = { [USBD_STR_PRODUCT] = "Board in FS mode", [USBD_STR_SERIAL] = NULL, // generated dynamically [USBD_STR_CDC] = "Board CDC", + #if CFG_TUD_MSC + [USBD_STR_MSC] = "Board MSC", + #endif }; const uint8_t *tud_descriptor_device_cb(void) { From 926b554dafffa1e9bd80aa12fea5c621221c9d79 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 3 Mar 2022 17:59:30 +1100 Subject: [PATCH 195/619] extmod/moduos: Create general uos module to be used by all ports. Based on the rp2 port version, with the rp2 port converted to use this module. Signed-off-by: Damien George --- extmod/extmod.cmake | 1 + extmod/moduos.c | 134 ++++++++++++++++++++++++++++++++++++ ports/esp32/mpconfigport.h | 1 + ports/rp2/CMakeLists.txt | 1 - ports/rp2/moduos.c | 82 +--------------------- ports/rp2/mpconfigport.h | 4 +- ports/zephyr/moduos.c | 4 +- ports/zephyr/mpconfigport.h | 4 +- py/builtin.h | 1 + py/mpconfig.h | 4 ++ py/objmodule.c | 3 + 11 files changed, 152 insertions(+), 87 deletions(-) create mode 100644 extmod/moduos.c diff --git a/extmod/extmod.cmake b/extmod/extmod.cmake index 67f7d8fd39..9c05237774 100644 --- a/extmod/extmod.cmake +++ b/extmod/extmod.cmake @@ -25,6 +25,7 @@ set(MICROPY_SOURCE_EXTMOD ${MICROPY_EXTMOD_DIR}/moduhashlib.c ${MICROPY_EXTMOD_DIR}/moduheapq.c ${MICROPY_EXTMOD_DIR}/modujson.c + ${MICROPY_EXTMOD_DIR}/moduos.c ${MICROPY_EXTMOD_DIR}/moduplatform.c ${MICROPY_EXTMOD_DIR}/modurandom.c ${MICROPY_EXTMOD_DIR}/modure.c diff --git a/extmod/moduos.c b/extmod/moduos.c new file mode 100644 index 0000000000..6dcead0fc4 --- /dev/null +++ b/extmod/moduos.c @@ -0,0 +1,134 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016-2022 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/objstr.h" +#include "py/runtime.h" + +#if MICROPY_PY_UOS + +#include "extmod/misc.h" +#include "extmod/vfs.h" + +#if MICROPY_VFS_FAT +#include "extmod/vfs_fat.h" +#endif + +#if MICROPY_VFS_LFS1 || MICROPY_VFS_LFS2 +#include "extmod/vfs_lfs.h" +#endif + +#if MICROPY_PY_UOS_UNAME +#include "genhdr/mpversion.h" +#endif + +#ifdef MICROPY_PY_UOS_INCLUDEFILE +#include MICROPY_PY_UOS_INCLUDEFILE +#endif + +#ifdef MICROPY_BUILD_TYPE +#define MICROPY_BUILD_TYPE_PAREN " (" MICROPY_BUILD_TYPE ")" +#else +#define MICROPY_BUILD_TYPE_PAREN +#endif + +STATIC const qstr mp_uos_uname_info_fields[] = { + MP_QSTR_sysname, + MP_QSTR_nodename, + MP_QSTR_release, + MP_QSTR_version, + MP_QSTR_machine +}; +STATIC const MP_DEFINE_STR_OBJ(mp_uos_uname_info_sysname_obj, MICROPY_PY_SYS_PLATFORM); +STATIC const MP_DEFINE_STR_OBJ(mp_uos_uname_info_nodename_obj, MICROPY_PY_SYS_PLATFORM); +STATIC const MP_DEFINE_STR_OBJ(mp_uos_uname_info_release_obj, MICROPY_VERSION_STRING); +STATIC const MP_DEFINE_STR_OBJ(mp_uos_uname_info_version_obj, MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE MICROPY_BUILD_TYPE_PAREN); +STATIC const MP_DEFINE_STR_OBJ(mp_uos_uname_info_machine_obj, MICROPY_HW_BOARD_NAME " with " MICROPY_HW_MCU_NAME); + +STATIC MP_DEFINE_ATTRTUPLE( + mp_uos_uname_info_obj, + mp_uos_uname_info_fields, + 5, + MP_ROM_PTR(&mp_uos_uname_info_sysname_obj), + MP_ROM_PTR(&mp_uos_uname_info_nodename_obj), + MP_ROM_PTR(&mp_uos_uname_info_release_obj), + MP_ROM_PTR(&mp_uos_uname_info_version_obj), + MP_ROM_PTR(&mp_uos_uname_info_machine_obj) + ); + +STATIC mp_obj_t mp_uos_uname(void) { + return MP_OBJ_FROM_PTR(&mp_uos_uname_info_obj); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_uos_uname_obj, mp_uos_uname); + +STATIC const mp_rom_map_elem_t os_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uos) }, + + { MP_ROM_QSTR(MP_QSTR_uname), MP_ROM_PTR(&mp_uos_uname_obj) }, + #if MICROPY_PY_UOS_URANDOM + { MP_ROM_QSTR(MP_QSTR_urandom), MP_ROM_PTR(&mp_uos_urandom_obj) }, + #endif + + #if MICROPY_VFS + { MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&mp_vfs_chdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&mp_vfs_getcwd_obj) }, + { MP_ROM_QSTR(MP_QSTR_listdir), MP_ROM_PTR(&mp_vfs_listdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&mp_vfs_mkdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&mp_vfs_remove_obj) }, + { MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&mp_vfs_rename_obj) }, + { MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&mp_vfs_rmdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&mp_vfs_stat_obj) }, + { MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&mp_vfs_statvfs_obj) }, + #endif + + // The following are MicroPython extensions. + + #if MICROPY_PY_OS_DUPTERM + { MP_ROM_QSTR(MP_QSTR_dupterm), MP_ROM_PTR(&mp_uos_dupterm_obj) }, + #endif + + #if MICROPY_VFS + { MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&mp_vfs_ilistdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&mp_vfs_mount_obj) }, + { MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&mp_vfs_umount_obj) }, + #if MICROPY_VFS_FAT + { MP_ROM_QSTR(MP_QSTR_VfsFat), MP_ROM_PTR(&mp_fat_vfs_type) }, + #endif + #if MICROPY_VFS_LFS1 + { MP_ROM_QSTR(MP_QSTR_VfsLfs1), MP_ROM_PTR(&mp_type_vfs_lfs1) }, + #endif + #if MICROPY_VFS_LFS2 + { MP_ROM_QSTR(MP_QSTR_VfsLfs2), MP_ROM_PTR(&mp_type_vfs_lfs2) }, + #endif + #endif +}; +STATIC MP_DEFINE_CONST_DICT(os_module_globals, os_module_globals_table); + +const mp_obj_module_t mp_module_uos = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&os_module_globals, +}; + +#endif // MICROPY_PY_UOS diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index d4f542f668..c820e526a2 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -72,6 +72,7 @@ #define MICROPY_PY_UHASHLIB_SHA256 (1) #define MICROPY_PY_UCRYPTOLIB (1) #define MICROPY_PY_URANDOM_SEED_INIT_FUNC (esp_random()) +#define MICROPY_PY_UOS (0) #define MICROPY_PY_OS_DUPTERM (1) #define MICROPY_PY_MACHINE (1) #define MICROPY_PY_MACHINE_PIN_MAKE_NEW mp_pin_make_new diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt index a428b0c39b..0ceb039675 100644 --- a/ports/rp2/CMakeLists.txt +++ b/ports/rp2/CMakeLists.txt @@ -97,7 +97,6 @@ set(MICROPY_SOURCE_PORT main.c modmachine.c modrp2.c - moduos.c modutime.c mphalport.c mpthreadport.c diff --git a/ports/rp2/moduos.c b/ports/rp2/moduos.c index a9dc30df89..6288ff5696 100644 --- a/ports/rp2/moduos.c +++ b/ports/rp2/moduos.c @@ -24,43 +24,11 @@ * THE SOFTWARE. */ -#include "py/objstr.h" #include "py/runtime.h" -#include "extmod/misc.h" -#include "extmod/vfs.h" -#include "extmod/vfs_fat.h" -#include "extmod/vfs_lfs.h" -#include "genhdr/mpversion.h" uint8_t rosc_random_u8(size_t cycles); -STATIC const qstr os_uname_info_fields[] = { - MP_QSTR_sysname, MP_QSTR_nodename, - MP_QSTR_release, MP_QSTR_version, MP_QSTR_machine -}; -STATIC const MP_DEFINE_STR_OBJ(os_uname_info_sysname_obj, MICROPY_PY_SYS_PLATFORM); -STATIC const MP_DEFINE_STR_OBJ(os_uname_info_nodename_obj, MICROPY_PY_SYS_PLATFORM); -STATIC const MP_DEFINE_STR_OBJ(os_uname_info_release_obj, MICROPY_VERSION_STRING); -STATIC const MP_DEFINE_STR_OBJ(os_uname_info_version_obj, MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE " (" MICROPY_BUILD_TYPE ")"); -STATIC const MP_DEFINE_STR_OBJ(os_uname_info_machine_obj, MICROPY_HW_BOARD_NAME " with " MICROPY_HW_MCU_NAME); - -STATIC MP_DEFINE_ATTRTUPLE( - os_uname_info_obj, - os_uname_info_fields, - 5, - (mp_obj_t)&os_uname_info_sysname_obj, - (mp_obj_t)&os_uname_info_nodename_obj, - (mp_obj_t)&os_uname_info_release_obj, - (mp_obj_t)&os_uname_info_version_obj, - (mp_obj_t)&os_uname_info_machine_obj - ); - -STATIC mp_obj_t os_uname(void) { - return (mp_obj_t)&os_uname_info_obj; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_0(os_uname_obj, os_uname); - -STATIC mp_obj_t os_urandom(mp_obj_t num) { +STATIC mp_obj_t mp_uos_urandom(mp_obj_t num) { mp_int_t n = mp_obj_get_int(num); vstr_t vstr; vstr_init_len(&vstr, n); @@ -69,50 +37,4 @@ STATIC mp_obj_t os_urandom(mp_obj_t num) { } return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } -STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_urandom_obj, os_urandom); - -STATIC const mp_rom_map_elem_t os_module_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uos) }, - - { MP_ROM_QSTR(MP_QSTR_uname), MP_ROM_PTR(&os_uname_obj) }, - { MP_ROM_QSTR(MP_QSTR_urandom), MP_ROM_PTR(&os_urandom_obj) }, - - #if MICROPY_VFS - { MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&mp_vfs_chdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&mp_vfs_getcwd_obj) }, - { MP_ROM_QSTR(MP_QSTR_listdir), MP_ROM_PTR(&mp_vfs_listdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&mp_vfs_mkdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&mp_vfs_remove_obj) }, - { MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&mp_vfs_rename_obj) }, - { MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&mp_vfs_rmdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&mp_vfs_stat_obj) }, - { MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&mp_vfs_statvfs_obj) }, - #endif - - // The following are MicroPython extensions. - - #if MICROPY_PY_OS_DUPTERM - { MP_ROM_QSTR(MP_QSTR_dupterm), MP_ROM_PTR(&mp_uos_dupterm_obj) }, - #endif - - #if MICROPY_VFS - { MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&mp_vfs_ilistdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&mp_vfs_mount_obj) }, - { MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&mp_vfs_umount_obj) }, - #if MICROPY_VFS_FAT - { MP_ROM_QSTR(MP_QSTR_VfsFat), MP_ROM_PTR(&mp_fat_vfs_type) }, - #endif - #if MICROPY_VFS_LFS1 - { MP_ROM_QSTR(MP_QSTR_VfsLfs1), MP_ROM_PTR(&mp_type_vfs_lfs1) }, - #endif - #if MICROPY_VFS_LFS2 - { MP_ROM_QSTR(MP_QSTR_VfsLfs2), MP_ROM_PTR(&mp_type_vfs_lfs2) }, - #endif - #endif -}; -STATIC MP_DEFINE_CONST_DICT(os_module_globals, os_module_globals_table); - -const mp_obj_module_t mp_module_uos = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t *)&os_module_globals, -}; +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_uos_urandom_obj, mp_uos_urandom); diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h index 9e839766eb..5e78018ea3 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -83,6 +83,8 @@ // Extended modules #define MICROPY_EPOCH_IS_1970 (1) +#define MICROPY_PY_UOS_INCLUDEFILE "ports/rp2/moduos.c" +#define MICROPY_PY_UOS_URANDOM (1) #define MICROPY_PY_URE_MATCH_GROUPS (1) #define MICROPY_PY_URE_MATCH_SPAN_START_END (1) #define MICROPY_PY_UTIME_MP_HAL (1) @@ -138,7 +140,6 @@ extern const struct _mp_obj_module_t mp_module_network; extern const struct _mp_obj_module_t mp_module_onewire; extern const struct _mp_obj_module_t mp_module_rp2; -extern const struct _mp_obj_module_t mp_module_uos; extern const struct _mp_obj_module_t mp_module_usocket; extern const struct _mp_obj_module_t mp_module_utime; @@ -186,7 +187,6 @@ extern const struct _mod_network_nic_type_t mod_network_nic_type_nina; #define MICROPY_PORT_BUILTIN_MODULES \ { MP_OBJ_NEW_QSTR(MP_QSTR__onewire), (mp_obj_t)&mp_module_onewire }, \ { MP_OBJ_NEW_QSTR(MP_QSTR__rp2), (mp_obj_t)&mp_module_rp2 }, \ - { MP_ROM_QSTR(MP_QSTR_uos), MP_ROM_PTR(&mp_module_uos) }, \ { MP_ROM_QSTR(MP_QSTR_utime), MP_ROM_PTR(&mp_module_utime) }, \ SOCKET_BUILTIN_MODULE \ NETWORK_BUILTIN_MODULE \ diff --git a/ports/zephyr/moduos.c b/ports/zephyr/moduos.c index 49d5744af4..4d1eeb667e 100644 --- a/ports/zephyr/moduos.c +++ b/ports/zephyr/moduos.c @@ -36,7 +36,7 @@ #include "extmod/vfs_lfs.h" #endif -#if MICROPY_PY_UOS +#if MICROPY_PY_UOS_ZEPHYR STATIC const mp_rom_map_elem_t uos_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uos) }, @@ -72,4 +72,4 @@ const mp_obj_module_t mp_module_uos = { .globals = (mp_obj_dict_t *)&uos_module_globals, }; -#endif // MICROPY_PY_UOS +#endif // MICROPY_PY_UOS_ZEPHYR diff --git a/ports/zephyr/mpconfigport.h b/ports/zephyr/mpconfigport.h index b8e25faefd..fe124bace0 100644 --- a/ports/zephyr/mpconfigport.h +++ b/ports/zephyr/mpconfigport.h @@ -83,7 +83,7 @@ #endif #define MICROPY_PY_UBINASCII (1) #define MICROPY_PY_UHASHLIB (1) -#define MICROPY_PY_UOS (1) +#define MICROPY_PY_UOS_ZEPHYR (1) #define MICROPY_PY_UTIME (1) #define MICROPY_PY_UTIME_MP_HAL (1) #define MICROPY_PY_ZEPHYR (1) @@ -144,7 +144,7 @@ extern const struct _mp_obj_module_t mp_module_usocket; extern const struct _mp_obj_module_t mp_module_zephyr; extern const struct _mp_obj_module_t mp_module_zsensor; -#if MICROPY_PY_UOS +#if MICROPY_PY_UOS_ZEPHYR #define MICROPY_PY_UOS_DEF { MP_ROM_QSTR(MP_QSTR_uos), MP_ROM_PTR(&mp_module_uos) }, #else #define MICROPY_PY_UOS_DEF diff --git a/py/builtin.h b/py/builtin.h index 8639e978ff..162fed77cc 100644 --- a/py/builtin.h +++ b/py/builtin.h @@ -108,6 +108,7 @@ extern const mp_obj_module_t mp_module_uerrno; extern const mp_obj_module_t mp_module_uctypes; extern const mp_obj_module_t mp_module_uzlib; extern const mp_obj_module_t mp_module_ujson; +extern const mp_obj_module_t mp_module_uos; extern const mp_obj_module_t mp_module_ure; extern const mp_obj_module_t mp_module_uheapq; extern const mp_obj_module_t mp_module_uhashlib; diff --git a/py/mpconfig.h b/py/mpconfig.h index 5edff69dfd..8f9f0f02fb 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -1455,6 +1455,10 @@ typedef double mp_float_t; #define MICROPY_PY_UJSON_SEPARATORS (1) #endif +#ifndef MICROPY_PY_UOS +#define MICROPY_PY_UOS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + #ifndef MICROPY_PY_URE #define MICROPY_PY_URE (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) #endif diff --git a/py/objmodule.c b/py/objmodule.c index 4b04f7ca9c..f7cd437bac 100644 --- a/py/objmodule.c +++ b/py/objmodule.c @@ -190,6 +190,9 @@ STATIC const mp_rom_map_elem_t mp_builtin_module_table[] = { #if MICROPY_PY_UJSON { MP_ROM_QSTR(MP_QSTR_ujson), MP_ROM_PTR(&mp_module_ujson) }, #endif + #if MICROPY_PY_UOS + { MP_ROM_QSTR(MP_QSTR_uos), MP_ROM_PTR(&mp_module_uos) }, + #endif #if MICROPY_PY_URE { MP_ROM_QSTR(MP_QSTR_ure), MP_ROM_PTR(&mp_module_ure) }, #endif From 1c53d85162007750d0708cf665a8bb7c062053be Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 3 Mar 2022 19:09:01 +1100 Subject: [PATCH 196/619] esp32/moduos: Convert module to use extmod version. Signed-off-by: Damien George --- extmod/moduos.c | 3 ++ ports/esp32/main/CMakeLists.txt | 1 - ports/esp32/moduos.c | 86 ++------------------------------- ports/esp32/mpconfigport.h | 6 +-- 4 files changed, 11 insertions(+), 85 deletions(-) diff --git a/extmod/moduos.c b/extmod/moduos.c index 6dcead0fc4..87e2fcc703 100644 --- a/extmod/moduos.c +++ b/extmod/moduos.c @@ -108,6 +108,9 @@ STATIC const mp_rom_map_elem_t os_module_globals_table[] = { #if MICROPY_PY_OS_DUPTERM { MP_ROM_QSTR(MP_QSTR_dupterm), MP_ROM_PTR(&mp_uos_dupterm_obj) }, #endif + #if MICROPY_PY_UOS_DUPTERM_NOTIFY + { MP_ROM_QSTR(MP_QSTR_dupterm_notify), MP_ROM_PTR(&mp_uos_dupterm_notify_obj) }, + #endif #if MICROPY_VFS { MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&mp_vfs_ilistdir_obj) }, diff --git a/ports/esp32/main/CMakeLists.txt b/ports/esp32/main/CMakeLists.txt index c86ac4dadf..ab333c3290 100644 --- a/ports/esp32/main/CMakeLists.txt +++ b/ports/esp32/main/CMakeLists.txt @@ -53,7 +53,6 @@ set(MICROPY_SOURCE_PORT ${PROJECT_DIR}/fatfs_port.c ${PROJECT_DIR}/help.c ${PROJECT_DIR}/modutime.c - ${PROJECT_DIR}/moduos.c ${PROJECT_DIR}/machine_bitstream.c ${PROJECT_DIR}/machine_timer.c ${PROJECT_DIR}/machine_pin.c diff --git a/ports/esp32/moduos.c b/ports/esp32/moduos.c index 50c0d7ea5c..f307a9ffe0 100644 --- a/ports/esp32/moduos.c +++ b/ports/esp32/moduos.c @@ -27,49 +27,13 @@ * THE SOFTWARE. */ -#include - #include "esp_system.h" -#include "py/objstr.h" #include "py/runtime.h" -#include "py/mperrno.h" #include "py/mphal.h" #include "extmod/misc.h" -#include "extmod/vfs.h" -#include "extmod/vfs_fat.h" -#include "extmod/vfs_lfs.h" -#include "genhdr/mpversion.h" -extern const mp_obj_type_t mp_fat_vfs_type; - -STATIC const qstr os_uname_info_fields[] = { - MP_QSTR_sysname, MP_QSTR_nodename, - MP_QSTR_release, MP_QSTR_version, MP_QSTR_machine -}; -STATIC const MP_DEFINE_STR_OBJ(os_uname_info_sysname_obj, MICROPY_PY_SYS_PLATFORM); -STATIC const MP_DEFINE_STR_OBJ(os_uname_info_nodename_obj, MICROPY_PY_SYS_PLATFORM); -STATIC const MP_DEFINE_STR_OBJ(os_uname_info_release_obj, MICROPY_VERSION_STRING); -STATIC const MP_DEFINE_STR_OBJ(os_uname_info_version_obj, MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE); -STATIC const MP_DEFINE_STR_OBJ(os_uname_info_machine_obj, MICROPY_HW_BOARD_NAME " with " MICROPY_HW_MCU_NAME); - -STATIC MP_DEFINE_ATTRTUPLE( - os_uname_info_obj, - os_uname_info_fields, - 5, - (mp_obj_t)&os_uname_info_sysname_obj, - (mp_obj_t)&os_uname_info_nodename_obj, - (mp_obj_t)&os_uname_info_release_obj, - (mp_obj_t)&os_uname_info_version_obj, - (mp_obj_t)&os_uname_info_machine_obj - ); - -STATIC mp_obj_t os_uname(void) { - return (mp_obj_t)&os_uname_info_obj; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_0(os_uname_obj, os_uname); - -STATIC mp_obj_t os_urandom(mp_obj_t num) { +STATIC mp_obj_t mp_uos_urandom(mp_obj_t num) { mp_int_t n = mp_obj_get_int(num); vstr_t vstr; vstr_init_len(&vstr, n); @@ -83,10 +47,10 @@ STATIC mp_obj_t os_urandom(mp_obj_t num) { } return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } -STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_urandom_obj, os_urandom); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_uos_urandom_obj, mp_uos_urandom); -#if MICROPY_PY_OS_DUPTERM -STATIC mp_obj_t os_dupterm_notify(mp_obj_t obj_in) { +#if MICROPY_PY_UOS_DUPTERM_NOTIFY +STATIC mp_obj_t mp_uos_dupterm_notify(mp_obj_t obj_in) { (void)obj_in; for (;;) { int c = mp_uos_dupterm_rx_chr(); @@ -97,45 +61,5 @@ STATIC mp_obj_t os_dupterm_notify(mp_obj_t obj_in) { } return mp_const_none; } -STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_dupterm_notify_obj, os_dupterm_notify); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_uos_dupterm_notify_obj, mp_uos_dupterm_notify); #endif - -STATIC const mp_rom_map_elem_t os_module_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uos) }, - { MP_ROM_QSTR(MP_QSTR_uname), MP_ROM_PTR(&os_uname_obj) }, - { MP_ROM_QSTR(MP_QSTR_urandom), MP_ROM_PTR(&os_urandom_obj) }, - #if MICROPY_PY_OS_DUPTERM - { MP_ROM_QSTR(MP_QSTR_dupterm), MP_ROM_PTR(&mp_uos_dupterm_obj) }, - { MP_ROM_QSTR(MP_QSTR_dupterm_notify), MP_ROM_PTR(&os_dupterm_notify_obj) }, - #endif - #if MICROPY_VFS - { MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&mp_vfs_ilistdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_listdir), MP_ROM_PTR(&mp_vfs_listdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&mp_vfs_mkdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&mp_vfs_rmdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&mp_vfs_chdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&mp_vfs_getcwd_obj) }, - { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&mp_vfs_remove_obj) }, - { MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&mp_vfs_rename_obj) }, - { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&mp_vfs_stat_obj) }, - { MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&mp_vfs_statvfs_obj) }, - { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&mp_vfs_mount_obj) }, - { MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&mp_vfs_umount_obj) }, - #if MICROPY_VFS_FAT - { MP_ROM_QSTR(MP_QSTR_VfsFat), MP_ROM_PTR(&mp_fat_vfs_type) }, - #endif - #if MICROPY_VFS_LFS1 - { MP_ROM_QSTR(MP_QSTR_VfsLfs1), MP_ROM_PTR(&mp_type_vfs_lfs1) }, - #endif - #if MICROPY_VFS_LFS2 - { MP_ROM_QSTR(MP_QSTR_VfsLfs2), MP_ROM_PTR(&mp_type_vfs_lfs2) }, - #endif - #endif -}; - -STATIC MP_DEFINE_CONST_DICT(os_module_globals, os_module_globals_table); - -const mp_obj_module_t uos_module = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t *)&os_module_globals, -}; diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index c820e526a2..e06ee94d52 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -72,8 +72,10 @@ #define MICROPY_PY_UHASHLIB_SHA256 (1) #define MICROPY_PY_UCRYPTOLIB (1) #define MICROPY_PY_URANDOM_SEED_INIT_FUNC (esp_random()) -#define MICROPY_PY_UOS (0) +#define MICROPY_PY_UOS_INCLUDEFILE "ports/esp32/moduos.c" #define MICROPY_PY_OS_DUPTERM (1) +#define MICROPY_PY_UOS_DUPTERM_NOTIFY (1) +#define MICROPY_PY_UOS_URANDOM (1) #define MICROPY_PY_MACHINE (1) #define MICROPY_PY_MACHINE_PIN_MAKE_NEW mp_pin_make_new #define MICROPY_PY_MACHINE_BITSTREAM (1) @@ -137,7 +139,6 @@ extern const struct _mp_obj_module_t esp_module; extern const struct _mp_obj_module_t esp32_module; extern const struct _mp_obj_module_t utime_module; -extern const struct _mp_obj_module_t uos_module; extern const struct _mp_obj_module_t mp_module_usocket; extern const struct _mp_obj_module_t mp_module_network; extern const struct _mp_obj_module_t mp_module_onewire; @@ -146,7 +147,6 @@ extern const struct _mp_obj_module_t mp_module_onewire; { MP_OBJ_NEW_QSTR(MP_QSTR_esp), (mp_obj_t)&esp_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_esp32), (mp_obj_t)&esp32_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_utime), (mp_obj_t)&utime_module }, \ - { MP_OBJ_NEW_QSTR(MP_QSTR_uos), (mp_obj_t)&uos_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_usocket), (mp_obj_t)&mp_module_usocket }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_network), (mp_obj_t)&mp_module_network }, \ { MP_OBJ_NEW_QSTR(MP_QSTR__onewire), (mp_obj_t)&mp_module_onewire }, \ From 818be10bb557b6a8b975b414dd26d09dd50a7760 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 3 Mar 2022 19:17:12 +1100 Subject: [PATCH 197/619] zephyr/moduos: Convert module to use extmod version. This also adds uos.unlink(), for all ports that use extmod/moduos.c. Signed-off-by: Damien George --- extmod/moduos.c | 5 +++ ports/esp32/mpconfigport.h | 1 + ports/rp2/mpconfigport.h | 1 + ports/zephyr/CMakeLists.txt | 1 - ports/zephyr/moduos.c | 75 ------------------------------------- ports/zephyr/mpconfigport.h | 10 +---- 6 files changed, 8 insertions(+), 85 deletions(-) delete mode 100644 ports/zephyr/moduos.c diff --git a/extmod/moduos.c b/extmod/moduos.c index 87e2fcc703..0378d50302 100644 --- a/extmod/moduos.c +++ b/extmod/moduos.c @@ -54,6 +54,7 @@ #define MICROPY_BUILD_TYPE_PAREN #endif +#if MICROPY_PY_UOS_UNAME STATIC const qstr mp_uos_uname_info_fields[] = { MP_QSTR_sysname, MP_QSTR_nodename, @@ -82,11 +83,14 @@ STATIC mp_obj_t mp_uos_uname(void) { return MP_OBJ_FROM_PTR(&mp_uos_uname_info_obj); } STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_uos_uname_obj, mp_uos_uname); +#endif STATIC const mp_rom_map_elem_t os_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uos) }, + #if MICROPY_PY_UOS_UNAME { MP_ROM_QSTR(MP_QSTR_uname), MP_ROM_PTR(&mp_uos_uname_obj) }, + #endif #if MICROPY_PY_UOS_URANDOM { MP_ROM_QSTR(MP_QSTR_urandom), MP_ROM_PTR(&mp_uos_urandom_obj) }, #endif @@ -101,6 +105,7 @@ STATIC const mp_rom_map_elem_t os_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&mp_vfs_rmdir_obj) }, { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&mp_vfs_stat_obj) }, { MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&mp_vfs_statvfs_obj) }, + { MP_ROM_QSTR(MP_QSTR_unlink), MP_ROM_PTR(&mp_vfs_remove_obj) }, // unlink aliases to remove #endif // The following are MicroPython extensions. diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index e06ee94d52..ffc155360a 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -75,6 +75,7 @@ #define MICROPY_PY_UOS_INCLUDEFILE "ports/esp32/moduos.c" #define MICROPY_PY_OS_DUPTERM (1) #define MICROPY_PY_UOS_DUPTERM_NOTIFY (1) +#define MICROPY_PY_UOS_UNAME (1) #define MICROPY_PY_UOS_URANDOM (1) #define MICROPY_PY_MACHINE (1) #define MICROPY_PY_MACHINE_PIN_MAKE_NEW mp_pin_make_new diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h index 5e78018ea3..1fb0211e88 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -84,6 +84,7 @@ // Extended modules #define MICROPY_EPOCH_IS_1970 (1) #define MICROPY_PY_UOS_INCLUDEFILE "ports/rp2/moduos.c" +#define MICROPY_PY_UOS_UNAME (1) #define MICROPY_PY_UOS_URANDOM (1) #define MICROPY_PY_URE_MATCH_GROUPS (1) #define MICROPY_PY_URE_MATCH_SPAN_START_END (1) diff --git a/ports/zephyr/CMakeLists.txt b/ports/zephyr/CMakeLists.txt index d809b7007d..bd5068faf3 100644 --- a/ports/zephyr/CMakeLists.txt +++ b/ports/zephyr/CMakeLists.txt @@ -43,7 +43,6 @@ set(MICROPY_SOURCE_PORT machine_uart.c modbluetooth_zephyr.c modmachine.c - moduos.c modusocket.c modutime.c modzephyr.c diff --git a/ports/zephyr/moduos.c b/ports/zephyr/moduos.c deleted file mode 100644 index 4d1eeb667e..0000000000 --- a/ports/zephyr/moduos.c +++ /dev/null @@ -1,75 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 NXP - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "py/obj.h" - -#include "extmod/vfs.h" - -#if MICROPY_VFS_FAT -#include "extmod/vfs_fat.h" -#endif - -#if MICROPY_VFS_LFS1 || MICROPY_VFS_LFS2 -#include "extmod/vfs_lfs.h" -#endif - -#if MICROPY_PY_UOS_ZEPHYR - -STATIC const mp_rom_map_elem_t uos_module_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uos) }, - #if MICROPY_VFS - { MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&mp_vfs_chdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&mp_vfs_getcwd_obj) }, - { MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&mp_vfs_ilistdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_listdir), MP_ROM_PTR(&mp_vfs_listdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&mp_vfs_mkdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&mp_vfs_remove_obj) }, - { MP_ROM_QSTR(MP_QSTR_rename),MP_ROM_PTR(&mp_vfs_rename_obj)}, - { MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&mp_vfs_rmdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&mp_vfs_stat_obj) }, - { MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&mp_vfs_statvfs_obj) }, - { MP_ROM_QSTR(MP_QSTR_unlink), MP_ROM_PTR(&mp_vfs_remove_obj) }, - { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&mp_vfs_mount_obj) }, - { MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&mp_vfs_umount_obj) }, - #endif - #if MICROPY_VFS_FAT - { MP_ROM_QSTR(MP_QSTR_VfsFat), MP_ROM_PTR(&mp_fat_vfs_type) }, - #endif - #if MICROPY_VFS_LFS1 - { MP_ROM_QSTR(MP_QSTR_VfsLfs1), MP_ROM_PTR(&mp_type_vfs_lfs1) }, - #endif - #if MICROPY_VFS_LFS2 - { MP_ROM_QSTR(MP_QSTR_VfsLfs2), MP_ROM_PTR(&mp_type_vfs_lfs2) }, - #endif -}; -STATIC MP_DEFINE_CONST_DICT(uos_module_globals, uos_module_globals_table); - -const mp_obj_module_t mp_module_uos = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t *)&uos_module_globals, -}; - -#endif // MICROPY_PY_UOS_ZEPHYR diff --git a/ports/zephyr/mpconfigport.h b/ports/zephyr/mpconfigport.h index fe124bace0..fa1cb56ae6 100644 --- a/ports/zephyr/mpconfigport.h +++ b/ports/zephyr/mpconfigport.h @@ -83,7 +83,7 @@ #endif #define MICROPY_PY_UBINASCII (1) #define MICROPY_PY_UHASHLIB (1) -#define MICROPY_PY_UOS_ZEPHYR (1) +#define MICROPY_PY_UOS (1) #define MICROPY_PY_UTIME (1) #define MICROPY_PY_UTIME_MP_HAL (1) #define MICROPY_PY_ZEPHYR (1) @@ -139,17 +139,10 @@ typedef long mp_off_t; struct _mp_bluetooth_zephyr_root_pointers_t *bluetooth_zephyr_root_pointers; extern const struct _mp_obj_module_t mp_module_time; -extern const struct _mp_obj_module_t mp_module_uos; extern const struct _mp_obj_module_t mp_module_usocket; extern const struct _mp_obj_module_t mp_module_zephyr; extern const struct _mp_obj_module_t mp_module_zsensor; -#if MICROPY_PY_UOS_ZEPHYR -#define MICROPY_PY_UOS_DEF { MP_ROM_QSTR(MP_QSTR_uos), MP_ROM_PTR(&mp_module_uos) }, -#else -#define MICROPY_PY_UOS_DEF -#endif - #if MICROPY_PY_USOCKET #define MICROPY_PY_USOCKET_DEF { MP_ROM_QSTR(MP_QSTR_usocket), MP_ROM_PTR(&mp_module_usocket) }, #else @@ -175,7 +168,6 @@ extern const struct _mp_obj_module_t mp_module_zsensor; #endif #define MICROPY_PORT_BUILTIN_MODULES \ - MICROPY_PY_UOS_DEF \ MICROPY_PY_USOCKET_DEF \ MICROPY_PY_UTIME_DEF \ MICROPY_PY_ZEPHYR_DEF \ From 94077c6402f6fc1cbd70abcbb3a1960c94495ca7 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 3 Mar 2022 19:30:58 +1100 Subject: [PATCH 198/619] samd/moduos: Convert module to use extmod version. Signed-off-by: Damien George --- ports/samd/Makefile | 3 +- ports/samd/moduos.c | 75 -------------------------------------- ports/stm32/mpconfigport.h | 7 ++-- py/py.mk | 1 + 4 files changed, 6 insertions(+), 80 deletions(-) delete mode 100644 ports/samd/moduos.c diff --git a/ports/samd/Makefile b/ports/samd/Makefile index 8476eea4b8..ab74b80c16 100644 --- a/ports/samd/Makefile +++ b/ports/samd/Makefile @@ -75,7 +75,6 @@ endif SRC_C = \ main.c \ help.c \ - moduos.c \ modutime.c \ modmachine.c \ $(BOARD_DIR)/pins.c \ @@ -120,7 +119,7 @@ SRC_S = shared/runtime/gchelper_m3.s endif # List of sources for qstr extraction -SRC_QSTR += moduos.c \ +SRC_QSTR += \ modutime.c \ modmachine.c \ machine_pin.c \ diff --git a/ports/samd/moduos.c b/ports/samd/moduos.c deleted file mode 100644 index b884d5e7b5..0000000000 --- a/ports/samd/moduos.c +++ /dev/null @@ -1,75 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2016 Damien P. George - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "py/runtime.h" -#include "extmod/vfs.h" -#include "extmod/vfs_fat.h" -#include "extmod/vfs_lfs.h" - -STATIC const mp_rom_map_elem_t os_module_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uos) }, - - #if MICROPY_VFS - { MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&mp_vfs_chdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&mp_vfs_getcwd_obj) }, - { MP_ROM_QSTR(MP_QSTR_listdir), MP_ROM_PTR(&mp_vfs_listdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&mp_vfs_mkdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&mp_vfs_remove_obj) }, - { MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&mp_vfs_rename_obj) }, - { MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&mp_vfs_rmdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&mp_vfs_stat_obj) }, - { MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&mp_vfs_statvfs_obj) }, - #endif - - // The following are MicroPython extensions. - - #if MICROPY_PY_OS_DUPTERM - { MP_ROM_QSTR(MP_QSTR_dupterm), MP_ROM_PTR(&mp_uos_dupterm_obj) }, - #endif - - #if MICROPY_VFS - { MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&mp_vfs_ilistdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&mp_vfs_mount_obj) }, - { MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&mp_vfs_umount_obj) }, - #if MICROPY_VFS_FAT - { MP_ROM_QSTR(MP_QSTR_VfsFat), MP_ROM_PTR(&mp_fat_vfs_type) }, - #endif - #if MICROPY_VFS_LFS1 - { MP_ROM_QSTR(MP_QSTR_VfsLfs1), MP_ROM_PTR(&mp_type_vfs_lfs1) }, - #endif - #if MICROPY_VFS_LFS2 - { MP_ROM_QSTR(MP_QSTR_VfsLfs2), MP_ROM_PTR(&mp_type_vfs_lfs2) }, - #endif - #endif -}; -STATIC MP_DEFINE_CONST_DICT(os_module_globals, os_module_globals_table); - -const mp_obj_module_t mp_module_uos = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t *)&os_module_globals, -}; - -MP_REGISTER_MODULE(MP_QSTR_uos, mp_module_uos, MICROPY_PY_UOS); diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h index 3cca8f1925..98e5f2f36b 100644 --- a/ports/stm32/mpconfigport.h +++ b/ports/stm32/mpconfigport.h @@ -95,8 +95,9 @@ #define MICROPY_PY_UHASHLIB_MD5 (MICROPY_PY_USSL) #define MICROPY_PY_UHASHLIB_SHA1 (MICROPY_PY_USSL) #define MICROPY_PY_UCRYPTOLIB (MICROPY_PY_USSL) -#ifndef MICROPY_PY_UOS -#define MICROPY_PY_UOS (1) +#define MICROPY_PY_UOS (0) +#ifndef MICROPY_PY_UOS_STM32 +#define MICROPY_PY_UOS_STM32 (1) #endif #define MICROPY_PY_OS_DUPTERM (3) #define MICROPY_PY_UOS_DUPTERM_BUILTIN_STREAM (1) @@ -203,7 +204,7 @@ extern const struct _mp_obj_module_t mp_module_onewire; #define MACHINE_BUILTIN_MODULE_CONSTANTS #endif -#if MICROPY_PY_UOS +#if MICROPY_PY_UOS_STM32 #define UOS_BUILTIN_MODULE { MP_ROM_QSTR(MP_QSTR_uos), MP_ROM_PTR(&mp_module_uos) }, #else #define UOS_BUILTIN_MODULE diff --git a/py/py.mk b/py/py.mk index 122dea225a..518cd0a225 100644 --- a/py/py.mk +++ b/py/py.mk @@ -176,6 +176,7 @@ PY_EXTMOD_O_BASENAME = \ extmod/moduasyncio.o \ extmod/moductypes.o \ extmod/modujson.o \ + extmod/moduos.o \ extmod/modure.o \ extmod/moduzlib.o \ extmod/moduheapq.o \ From 20bfae1471fe86e153360dbf28d0292274f3978b Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 3 Mar 2022 19:34:15 +1100 Subject: [PATCH 199/619] qemu-arm/moduos: Convert module to use extmod version. Signed-off-by: Damien George --- ports/qemu-arm/Makefile | 1 - ports/qemu-arm/moduos.c | 53 ----------------------------------- ports/qemu-arm/mpconfigport.h | 7 +---- 3 files changed, 1 insertion(+), 60 deletions(-) delete mode 100644 ports/qemu-arm/moduos.c diff --git a/ports/qemu-arm/Makefile b/ports/qemu-arm/Makefile index 4a6d63a014..08c2d82498 100644 --- a/ports/qemu-arm/Makefile +++ b/ports/qemu-arm/Makefile @@ -77,7 +77,6 @@ LIBS = $(shell $(CC) $(CFLAGS) -print-libgcc-file-name) SRC_COMMON_C = \ startup.c \ uart.c \ - moduos.c \ modmachine.c \ shared/libc/string0.c \ shared/runtime/sys_stdio_mphal.c \ diff --git a/ports/qemu-arm/moduos.c b/ports/qemu-arm/moduos.c deleted file mode 100644 index 23a1241869..0000000000 --- a/ports/qemu-arm/moduos.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2017 Damien P. George - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "extmod/vfs.h" - -STATIC const mp_rom_map_elem_t os_module_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uos) }, - - { MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&mp_vfs_chdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&mp_vfs_getcwd_obj) }, - { MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&mp_vfs_ilistdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_listdir), MP_ROM_PTR(&mp_vfs_listdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&mp_vfs_mkdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&mp_vfs_remove_obj) }, - { MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&mp_vfs_rename_obj) }, - { MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&mp_vfs_rmdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&mp_vfs_stat_obj) }, - { MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&mp_vfs_statvfs_obj) }, - - // MicroPython extensions - { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&mp_vfs_mount_obj) }, - { MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&mp_vfs_umount_obj) }, -}; - -STATIC MP_DEFINE_CONST_DICT(os_module_globals, os_module_globals_table); - -const mp_obj_module_t mp_module_uos = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t *)&os_module_globals, -}; diff --git a/ports/qemu-arm/mpconfigport.h b/ports/qemu-arm/mpconfigport.h index 6bfea74ea0..cccfa1e180 100644 --- a/ports/qemu-arm/mpconfigport.h +++ b/ports/qemu-arm/mpconfigport.h @@ -40,6 +40,7 @@ #define MICROPY_PY_UCTYPES (1) #define MICROPY_PY_UZLIB (1) #define MICROPY_PY_UJSON (1) +#define MICROPY_PY_UOS (1) #define MICROPY_PY_URE (1) #define MICROPY_PY_UHEAPQ (1) #define MICROPY_PY_UHASHLIB (1) @@ -63,12 +64,6 @@ typedef long mp_off_t; #define MICROPY_PORT_BUILTINS \ { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mp_builtin_open_obj) }, -// extra built-in modules to add to the list of known ones -extern const struct _mp_obj_module_t mp_module_uos; - -#define MICROPY_PORT_BUILTIN_MODULES \ - { MP_ROM_QSTR(MP_QSTR_uos), MP_ROM_PTR(&mp_module_uos) }, \ - // We need to provide a declaration/definition of alloca() #include From 9a3e1a18081c8c6a08e2a2efcfefcc4d96e9a8da Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 3 Mar 2022 22:50:40 +1100 Subject: [PATCH 200/619] mimxrt/moduos: Convert module to use extmod version. Signed-off-by: Damien George --- ports/mimxrt/Makefile | 2 - ports/mimxrt/moduos.c | 91 +++---------------------------------- ports/mimxrt/mpconfigport.h | 7 ++- 3 files changed, 12 insertions(+), 88 deletions(-) diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index 55e32433f5..be5083965f 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -238,7 +238,6 @@ SRC_C += \ mimxrt_sdram.c \ modmachine.c \ modmimxrt.c \ - moduos.c \ modutime.c \ mphalport.c \ mpnetworkport.c \ @@ -409,7 +408,6 @@ SRC_QSTR += \ mimxrt_flash.c \ modmachine.c \ modmimxrt.c \ - moduos.c \ modutime.c \ pin.c \ shared/runtime/mpirq.c \ diff --git a/ports/mimxrt/moduos.c b/ports/mimxrt/moduos.c index b59a5f4483..34bd2b4248 100644 --- a/ports/mimxrt/moduos.c +++ b/ports/mimxrt/moduos.c @@ -28,42 +28,10 @@ * THE SOFTWARE. */ -#include "py/objstr.h" #include "py/runtime.h" #include "py/mphal.h" -#include "extmod/misc.h" -#include "extmod/vfs.h" -#include "extmod/vfs_fat.h" -#include "extmod/vfs_lfs.h" -#include "genhdr/mpversion.h" #include "fsl_trng.h" -STATIC const qstr os_uname_info_fields[] = { - MP_QSTR_sysname, MP_QSTR_nodename, - MP_QSTR_release, MP_QSTR_version, MP_QSTR_machine -}; -STATIC const MP_DEFINE_STR_OBJ(os_uname_info_sysname_obj, MICROPY_PY_SYS_PLATFORM); -STATIC const MP_DEFINE_STR_OBJ(os_uname_info_nodename_obj, MICROPY_PY_SYS_PLATFORM); -STATIC const MP_DEFINE_STR_OBJ(os_uname_info_release_obj, MICROPY_VERSION_STRING); -STATIC const MP_DEFINE_STR_OBJ(os_uname_info_version_obj, MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE); -STATIC const MP_DEFINE_STR_OBJ(os_uname_info_machine_obj, MICROPY_HW_BOARD_NAME " with " MICROPY_HW_MCU_NAME); - -STATIC MP_DEFINE_ATTRTUPLE( - os_uname_info_obj, - os_uname_info_fields, - 5, - (mp_obj_t)&os_uname_info_sysname_obj, - (mp_obj_t)&os_uname_info_nodename_obj, - (mp_obj_t)&os_uname_info_release_obj, - (mp_obj_t)&os_uname_info_version_obj, - (mp_obj_t)&os_uname_info_machine_obj - ); - -STATIC mp_obj_t os_uname(void) { - return (mp_obj_t)&os_uname_info_obj; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_0(os_uname_obj, os_uname); - static bool initialized = false; STATIC void trng_start(void) { @@ -86,7 +54,8 @@ uint32_t trng_random_u32(void) { return rngval; } -STATIC mp_obj_t os_urandom(mp_obj_t num) { +#if MICROPY_PY_UOS_URANDOM +STATIC mp_obj_t mp_uos_urandom(mp_obj_t num) { mp_int_t n = mp_obj_get_int(num); vstr_t vstr; vstr_init_len(&vstr, n); @@ -96,10 +65,11 @@ STATIC mp_obj_t os_urandom(mp_obj_t num) { return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } -STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_urandom_obj, os_urandom); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_uos_urandom_obj, mp_uos_urandom); +#endif -#if MICROPY_PY_OS_DUPTERM -STATIC mp_obj_t os_dupterm_notify(mp_obj_t obj_in) { +#if MICROPY_PY_UOS_DUPTERM_NOTIFY +STATIC mp_obj_t mp_uos_dupterm_notify(mp_obj_t obj_in) { (void)obj_in; for (;;) { int c = mp_uos_dupterm_rx_chr(); @@ -110,52 +80,5 @@ STATIC mp_obj_t os_dupterm_notify(mp_obj_t obj_in) { } return mp_const_none; } -STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_dupterm_notify_obj, os_dupterm_notify); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_uos_dupterm_notify_obj, mp_uos_dupterm_notify); #endif - -STATIC const mp_rom_map_elem_t os_module_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uos) }, - - { MP_ROM_QSTR(MP_QSTR_uname), MP_ROM_PTR(&os_uname_obj) }, - { MP_ROM_QSTR(MP_QSTR_urandom), MP_ROM_PTR(&os_urandom_obj) }, - - #if MICROPY_VFS - { MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&mp_vfs_chdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&mp_vfs_getcwd_obj) }, - { MP_ROM_QSTR(MP_QSTR_listdir), MP_ROM_PTR(&mp_vfs_listdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&mp_vfs_mkdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&mp_vfs_remove_obj) }, - { MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&mp_vfs_rename_obj) }, - { MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&mp_vfs_rmdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&mp_vfs_stat_obj) }, - { MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&mp_vfs_statvfs_obj) }, - #endif - - // The following are MicroPython extensions. - - #if MICROPY_PY_OS_DUPTERM - { MP_ROM_QSTR(MP_QSTR_dupterm), MP_ROM_PTR(&mp_uos_dupterm_obj) }, - { MP_ROM_QSTR(MP_QSTR_dupterm_notify), MP_ROM_PTR(&os_dupterm_notify_obj) }, - #endif - - #if MICROPY_VFS - { MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&mp_vfs_ilistdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&mp_vfs_mount_obj) }, - { MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&mp_vfs_umount_obj) }, - #if MICROPY_VFS_FAT - { MP_ROM_QSTR(MP_QSTR_VfsFat), MP_ROM_PTR(&mp_fat_vfs_type) }, - #endif - #if MICROPY_VFS_LFS1 - { MP_ROM_QSTR(MP_QSTR_VfsLfs1), MP_ROM_PTR(&mp_type_vfs_lfs1) }, - #endif - #if MICROPY_VFS_LFS2 - { MP_ROM_QSTR(MP_QSTR_VfsLfs2), MP_ROM_PTR(&mp_type_vfs_lfs2) }, - #endif - #endif -}; -STATIC MP_DEFINE_CONST_DICT(os_module_globals, os_module_globals_table); - -const mp_obj_module_t mp_module_uos = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t *)&os_module_globals, -}; diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index 7958874447..ca8be01f78 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -121,7 +121,12 @@ uint32_t trng_random_u32(void); #define MICROPY_PY_UBINASCII (1) #define MICROPY_PY_UBINASCII_CRC32 (1) #define MICROPY_PY_UTIME_MP_HAL (1) +#define MICROPY_PY_UOS (1) +#define MICROPY_PY_UOS_INCLUDEFILE "ports/mimxrt/moduos.c" #define MICROPY_PY_OS_DUPTERM (3) +#define MICROPY_PY_UOS_DUPTERM_NOTIFY (1) +#define MICROPY_PY_UOS_UNAME (1) +#define MICROPY_PY_UOS_URANDOM (1) #define MICROPY_PY_URANDOM (1) #define MICROPY_PY_URANDOM_EXTRA_FUNCS (1) #define MICROPY_PY_URANDOM_SEED_INIT_FUNC (trng_random_u32()) @@ -221,7 +226,6 @@ static inline void restore_irq_pri(uint32_t basepri) { extern const struct _mp_obj_module_t mp_module_mimxrt; extern const struct _mp_obj_module_t mp_module_onewire; -extern const struct _mp_obj_module_t mp_module_uos; extern const struct _mp_obj_module_t mp_module_utime; extern const struct _mp_obj_module_t mp_module_usocket; extern const struct _mp_obj_module_t mp_module_network; @@ -258,7 +262,6 @@ extern const struct _mp_obj_type_t network_lan_type; #define MICROPY_PORT_BUILTIN_MODULES \ { MP_ROM_QSTR(MP_QSTR_mimxrt), (mp_obj_t)&mp_module_mimxrt }, \ - { MP_ROM_QSTR(MP_QSTR_uos), MP_ROM_PTR(&mp_module_uos) }, \ { MP_ROM_QSTR(MP_QSTR_utime), MP_ROM_PTR(&mp_module_utime) }, \ { MP_ROM_QSTR(MP_QSTR__onewire), MP_ROM_PTR(&mp_module_onewire) }, \ SOCKET_BUILTIN_MODULE \ From 11b77263ef9e8f897c7186f10f4ef579adde9195 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 3 Mar 2022 23:15:59 +1100 Subject: [PATCH 201/619] stm32/moduos: Convert module to use extmod version. Signed-off-by: Damien George --- extmod/misc.h | 1 + extmod/moduos.c | 6 ++ extmod/uos_dupterm.c | 4 ++ ports/stm32/Makefile | 1 - ports/stm32/modpyb.c | 2 +- ports/stm32/moduos.c | 144 ++++++------------------------------- ports/stm32/mpconfigport.h | 18 ++--- ports/stm32/portmodules.h | 4 +- 8 files changed, 42 insertions(+), 138 deletions(-) diff --git a/extmod/misc.h b/extmod/misc.h index 40b091e5f7..a9392aa10b 100644 --- a/extmod/misc.h +++ b/extmod/misc.h @@ -36,6 +36,7 @@ MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_uos_dupterm_obj); #if MICROPY_PY_OS_DUPTERM bool mp_uos_dupterm_is_builtin_stream(mp_const_obj_t stream); +void mp_uos_dupterm_stream_detached_attached(mp_obj_t stream_detached, mp_obj_t stream_attached); uintptr_t mp_uos_dupterm_poll(uintptr_t poll_flags); int mp_uos_dupterm_rx_chr(void); void mp_uos_dupterm_tx_strn(const char *str, size_t len); diff --git a/extmod/moduos.c b/extmod/moduos.c index 0378d50302..28c482dd91 100644 --- a/extmod/moduos.c +++ b/extmod/moduos.c @@ -88,6 +88,12 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_uos_uname_obj, mp_uos_uname); STATIC const mp_rom_map_elem_t os_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uos) }, + #if MICROPY_PY_UOS_SEP + { MP_ROM_QSTR(MP_QSTR_sep), MP_ROM_QSTR(MP_QSTR__slash_) }, + #endif + #if MICROPY_PY_UOS_SYNC + { MP_ROM_QSTR(MP_QSTR_sync), MP_ROM_PTR(&mp_uos_sync_obj) }, + #endif #if MICROPY_PY_UOS_UNAME { MP_ROM_QSTR(MP_QSTR_uname), MP_ROM_PTR(&mp_uos_uname_obj) }, #endif diff --git a/extmod/uos_dupterm.c b/extmod/uos_dupterm.c index d55767de21..e18d133b91 100644 --- a/extmod/uos_dupterm.c +++ b/extmod/uos_dupterm.c @@ -193,6 +193,10 @@ STATIC mp_obj_t mp_uos_dupterm(size_t n_args, const mp_obj_t *args) { MP_STATE_VM(dupterm_objs[idx]) = args[0]; } + #if MICROPY_PY_UOS_DUPTERM_STREAM_DETACHED_ATTACHED + mp_uos_dupterm_stream_detached_attached(previous_obj, args[0]); + #endif + return previous_obj; } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_uos_dupterm_obj, 1, 2, mp_uos_dupterm); diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index dd86f1e3f3..3083c99570 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -297,7 +297,6 @@ SRC_C += \ modmachine.c \ modpyb.c \ modstm.c \ - moduos.c \ modutime.c \ network_lan.c \ extint.c \ diff --git a/ports/stm32/modpyb.c b/ports/stm32/modpyb.c index b9e2bac03f..a444654888 100644 --- a/ports/stm32/modpyb.c +++ b/ports/stm32/modpyb.c @@ -185,7 +185,7 @@ STATIC const mp_rom_map_elem_t pyb_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_elapsed_micros), MP_ROM_PTR(&pyb_elapsed_micros_obj) }, { MP_ROM_QSTR(MP_QSTR_delay), MP_ROM_PTR(&mp_utime_sleep_ms_obj) }, { MP_ROM_QSTR(MP_QSTR_udelay), MP_ROM_PTR(&mp_utime_sleep_us_obj) }, - { MP_ROM_QSTR(MP_QSTR_sync), MP_ROM_PTR(&mod_os_sync_obj) }, + { MP_ROM_QSTR(MP_QSTR_sync), MP_ROM_PTR(&mp_uos_sync_obj) }, { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&mp_vfs_mount_obj) }, #endif diff --git a/ports/stm32/moduos.c b/ports/stm32/moduos.c index 5b2335272f..1862564b61 100644 --- a/ports/stm32/moduos.c +++ b/ports/stm32/moduos.c @@ -24,66 +24,19 @@ * THE SOFTWARE. */ -#include -#include - #include "py/runtime.h" -#include "py/objtuple.h" -#include "py/objstr.h" -#include "shared/timeutils/timeutils.h" -#include "lib/oofatfs/ff.h" -#include "lib/oofatfs/diskio.h" -#include "extmod/misc.h" -#include "extmod/vfs.h" -#include "extmod/vfs_fat.h" -#include "extmod/vfs_lfs.h" -#include "genhdr/mpversion.h" #include "rng.h" #include "usb.h" #include "uart.h" -#include "portmodules.h" -/// \module os - basic "operating system" services -/// -/// The `os` module contains functions for filesystem access and `urandom`. -/// -/// The filesystem has `/` as the root directory, and the available physical -/// drives are accessible from here. They are currently: -/// -/// /flash -- the internal flash filesystem -/// /sd -- the SD card (if it exists) -/// -/// On boot up, the current directory is `/flash` if no SD card is inserted, -/// otherwise it is `/sd`. +#if MICROPY_VFS_FAT +#include "lib/oofatfs/ff.h" +#include "lib/oofatfs/diskio.h" +#endif -STATIC const qstr os_uname_info_fields[] = { - MP_QSTR_sysname, MP_QSTR_nodename, - MP_QSTR_release, MP_QSTR_version, MP_QSTR_machine -}; -STATIC const MP_DEFINE_STR_OBJ(os_uname_info_sysname_obj, "pyboard"); -STATIC const MP_DEFINE_STR_OBJ(os_uname_info_nodename_obj, "pyboard"); -STATIC const MP_DEFINE_STR_OBJ(os_uname_info_release_obj, MICROPY_VERSION_STRING); -STATIC const MP_DEFINE_STR_OBJ(os_uname_info_version_obj, MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE); -STATIC const MP_DEFINE_STR_OBJ(os_uname_info_machine_obj, MICROPY_HW_BOARD_NAME " with " MICROPY_HW_MCU_NAME); -STATIC MP_DEFINE_ATTRTUPLE( - os_uname_info_obj, - os_uname_info_fields, - 5, - MP_ROM_PTR(&os_uname_info_sysname_obj), - MP_ROM_PTR(&os_uname_info_nodename_obj), - MP_ROM_PTR(&os_uname_info_release_obj), - MP_ROM_PTR(&os_uname_info_version_obj), - MP_ROM_PTR(&os_uname_info_machine_obj) - ); - -STATIC mp_obj_t os_uname(void) { - return MP_OBJ_FROM_PTR(&os_uname_info_obj); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_0(os_uname_obj, os_uname); - -/// \function sync() -/// Sync all filesystems. -STATIC mp_obj_t os_sync(void) { +// sync() +// Sync all filesystems. +STATIC mp_obj_t mp_uos_sync(void) { #if MICROPY_VFS_FAT for (mp_vfs_mount_t *vfs = MP_STATE_VM(vfs_mount_table); vfs != NULL; vfs = vfs->next) { // this assumes that vfs->obj is fs_user_mount_t with block device functions @@ -92,13 +45,13 @@ STATIC mp_obj_t os_sync(void) { #endif return mp_const_none; } -MP_DEFINE_CONST_FUN_OBJ_0(mod_os_sync_obj, os_sync); +MP_DEFINE_CONST_FUN_OBJ_0(mp_uos_sync_obj, mp_uos_sync); -#if MICROPY_HW_ENABLE_RNG -/// \function urandom(n) -/// Return a bytes object with n random bytes, generated by the hardware -/// random number generator. -STATIC mp_obj_t os_urandom(mp_obj_t num) { +#if MICROPY_PY_UOS_URANDOM +// urandom(n) +// Return a bytes object with n random bytes, generated by the hardware +// random number generator. +STATIC mp_obj_t mp_uos_urandom(mp_obj_t num) { mp_int_t n = mp_obj_get_int(num); vstr_t vstr; vstr_init_len(&vstr, n); @@ -107,7 +60,7 @@ STATIC mp_obj_t os_urandom(mp_obj_t num) { } return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } -STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_urandom_obj, os_urandom); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_uos_urandom_obj, mp_uos_urandom); #endif bool mp_uos_dupterm_is_builtin_stream(mp_const_obj_t stream) { @@ -119,73 +72,22 @@ bool mp_uos_dupterm_is_builtin_stream(mp_const_obj_t stream) { ; } -STATIC mp_obj_t uos_dupterm(size_t n_args, const mp_obj_t *args) { - mp_obj_t prev_obj = mp_uos_dupterm_obj.fun.var(n_args, args); - if (mp_obj_get_type(prev_obj) == &pyb_uart_type) { - uart_attach_to_repl(MP_OBJ_TO_PTR(prev_obj), false); +void mp_uos_dupterm_stream_detached_attached(mp_obj_t stream_detached, mp_obj_t stream_attached) { + if (mp_obj_get_type(stream_detached) == &pyb_uart_type) { + uart_attach_to_repl(MP_OBJ_TO_PTR(stream_detached), false); } #if MICROPY_HW_ENABLE_USB - if (mp_obj_get_type(prev_obj) == &pyb_usb_vcp_type) { - usb_vcp_attach_to_repl(MP_OBJ_TO_PTR(prev_obj), false); + if (mp_obj_get_type(stream_detached) == &pyb_usb_vcp_type) { + usb_vcp_attach_to_repl(MP_OBJ_TO_PTR(stream_detached), false); } #endif - if (mp_obj_get_type(args[0]) == &pyb_uart_type) { - uart_attach_to_repl(MP_OBJ_TO_PTR(args[0]), true); + if (mp_obj_get_type(stream_attached) == &pyb_uart_type) { + uart_attach_to_repl(MP_OBJ_TO_PTR(stream_attached), true); } #if MICROPY_HW_ENABLE_USB - if (mp_obj_get_type(args[0]) == &pyb_usb_vcp_type) { - usb_vcp_attach_to_repl(MP_OBJ_TO_PTR(args[0]), true); + if (mp_obj_get_type(stream_attached) == &pyb_usb_vcp_type) { + usb_vcp_attach_to_repl(MP_OBJ_TO_PTR(stream_attached), true); } #endif - return prev_obj; } -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(uos_dupterm_obj, 1, 2, uos_dupterm); - -STATIC const mp_rom_map_elem_t os_module_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uos) }, - - { MP_ROM_QSTR(MP_QSTR_uname), MP_ROM_PTR(&os_uname_obj) }, - - { MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&mp_vfs_chdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&mp_vfs_getcwd_obj) }, - { MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&mp_vfs_ilistdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_listdir), MP_ROM_PTR(&mp_vfs_listdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&mp_vfs_mkdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&mp_vfs_remove_obj) }, - { MP_ROM_QSTR(MP_QSTR_rename),MP_ROM_PTR(&mp_vfs_rename_obj)}, - { MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&mp_vfs_rmdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&mp_vfs_stat_obj) }, - { MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&mp_vfs_statvfs_obj) }, - { MP_ROM_QSTR(MP_QSTR_unlink), MP_ROM_PTR(&mp_vfs_remove_obj) }, // unlink aliases to remove - - { MP_ROM_QSTR(MP_QSTR_sync), MP_ROM_PTR(&mod_os_sync_obj) }, - - /// \constant sep - separation character used in paths - { MP_ROM_QSTR(MP_QSTR_sep), MP_ROM_QSTR(MP_QSTR__slash_) }, - - #if MICROPY_HW_ENABLE_RNG - { MP_ROM_QSTR(MP_QSTR_urandom), MP_ROM_PTR(&os_urandom_obj) }, - #endif - - // these are MicroPython extensions - { MP_ROM_QSTR(MP_QSTR_dupterm), MP_ROM_PTR(&uos_dupterm_obj) }, - { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&mp_vfs_mount_obj) }, - { MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&mp_vfs_umount_obj) }, - #if MICROPY_VFS_FAT - { MP_ROM_QSTR(MP_QSTR_VfsFat), MP_ROM_PTR(&mp_fat_vfs_type) }, - #endif - #if MICROPY_VFS_LFS1 - { MP_ROM_QSTR(MP_QSTR_VfsLfs1), MP_ROM_PTR(&mp_type_vfs_lfs1) }, - #endif - #if MICROPY_VFS_LFS2 - { MP_ROM_QSTR(MP_QSTR_VfsLfs2), MP_ROM_PTR(&mp_type_vfs_lfs2) }, - #endif -}; - -STATIC MP_DEFINE_CONST_DICT(os_module_globals, os_module_globals_table); - -const mp_obj_module_t mp_module_uos = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t *)&os_module_globals, -}; diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h index 98e5f2f36b..60f6d45fc4 100644 --- a/ports/stm32/mpconfigport.h +++ b/ports/stm32/mpconfigport.h @@ -95,12 +95,14 @@ #define MICROPY_PY_UHASHLIB_MD5 (MICROPY_PY_USSL) #define MICROPY_PY_UHASHLIB_SHA1 (MICROPY_PY_USSL) #define MICROPY_PY_UCRYPTOLIB (MICROPY_PY_USSL) -#define MICROPY_PY_UOS (0) -#ifndef MICROPY_PY_UOS_STM32 -#define MICROPY_PY_UOS_STM32 (1) -#endif +#define MICROPY_PY_UOS_INCLUDEFILE "ports/stm32/moduos.c" #define MICROPY_PY_OS_DUPTERM (3) #define MICROPY_PY_UOS_DUPTERM_BUILTIN_STREAM (1) +#define MICROPY_PY_UOS_DUPTERM_STREAM_DETACHED_ATTACHED (1) +#define MICROPY_PY_UOS_SEP (1) +#define MICROPY_PY_UOS_SYNC (1) +#define MICROPY_PY_UOS_UNAME (1) +#define MICROPY_PY_UOS_URANDOM (MICROPY_HW_ENABLE_RNG) #define MICROPY_PY_URANDOM_SEED_INIT_FUNC (rng_get()) #ifndef MICROPY_PY_UTIME #define MICROPY_PY_UTIME (1) @@ -178,7 +180,6 @@ extern const struct _mp_obj_module_t mp_module_uzlib; extern const struct _mp_obj_module_t mp_module_ujson; extern const struct _mp_obj_module_t mp_module_uheapq; extern const struct _mp_obj_module_t mp_module_uhashlib; -extern const struct _mp_obj_module_t mp_module_uos; extern const struct _mp_obj_module_t mp_module_utime; extern const struct _mp_obj_module_t mp_module_usocket; extern const struct _mp_obj_module_t mp_module_network; @@ -204,12 +205,6 @@ extern const struct _mp_obj_module_t mp_module_onewire; #define MACHINE_BUILTIN_MODULE_CONSTANTS #endif -#if MICROPY_PY_UOS_STM32 -#define UOS_BUILTIN_MODULE { MP_ROM_QSTR(MP_QSTR_uos), MP_ROM_PTR(&mp_module_uos) }, -#else -#define UOS_BUILTIN_MODULE -#endif - #if MICROPY_PY_UTIME #define UTIME_BUILTIN_MODULE { MP_ROM_QSTR(MP_QSTR_utime), MP_ROM_PTR(&mp_module_utime) }, #else @@ -274,7 +269,6 @@ extern const struct _mod_network_nic_type_t mod_network_nic_type_cc3k; #define MICROPY_PORT_BUILTIN_MODULES \ PYB_BUILTIN_MODULE \ STM_BUILTIN_MODULE \ - UOS_BUILTIN_MODULE \ UTIME_BUILTIN_MODULE \ SOCKET_BUILTIN_MODULE \ NETWORK_BUILTIN_MODULE \ diff --git a/ports/stm32/portmodules.h b/ports/stm32/portmodules.h index 81cb7fd04a..f80b4fdf82 100644 --- a/ports/stm32/portmodules.h +++ b/ports/stm32/portmodules.h @@ -28,7 +28,6 @@ extern const mp_obj_module_t pyb_module; extern const mp_obj_module_t stm_module; -extern const mp_obj_module_t mp_module_uos; extern const mp_obj_module_t mp_module_utime; extern const mp_obj_module_t mp_module_usocket; @@ -37,7 +36,6 @@ extern const mp_obj_module_t mp_module_usocket; MP_DECLARE_CONST_FUN_OBJ_1(time_sleep_ms_obj); MP_DECLARE_CONST_FUN_OBJ_1(time_sleep_us_obj); -MP_DECLARE_CONST_FUN_OBJ_0(mod_os_sync_obj); -MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mod_os_dupterm_obj); +MP_DECLARE_CONST_FUN_OBJ_0(mp_uos_sync_obj); #endif // MICROPY_INCLUDED_STM32_PORTMODULES_H From ade2720e55e5960f6667d952c4482a369747e3a4 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 3 Mar 2022 23:46:16 +1100 Subject: [PATCH 202/619] esp8266/moduos: Convert module to use extmod version. Signed-off-by: Damien George --- extmod/moduos.c | 15 +++++- ports/esp8266/Makefile | 1 - ports/esp8266/main.c | 3 +- ports/esp8266/moduos.c | 88 ++++-------------------------------- ports/esp8266/mpconfigport.h | 9 +++- 5 files changed, 31 insertions(+), 85 deletions(-) diff --git a/extmod/moduos.c b/extmod/moduos.c index 28c482dd91..0ec38bff8a 100644 --- a/extmod/moduos.c +++ b/extmod/moduos.c @@ -55,6 +55,13 @@ #endif #if MICROPY_PY_UOS_UNAME + +#if MICROPY_PY_UOS_UNAME_RELEASE_DYNAMIC +#define CONST_RELEASE +#else +#define CONST_RELEASE const +#endif + STATIC const qstr mp_uos_uname_info_fields[] = { MP_QSTR_sysname, MP_QSTR_nodename, @@ -64,7 +71,7 @@ STATIC const qstr mp_uos_uname_info_fields[] = { }; STATIC const MP_DEFINE_STR_OBJ(mp_uos_uname_info_sysname_obj, MICROPY_PY_SYS_PLATFORM); STATIC const MP_DEFINE_STR_OBJ(mp_uos_uname_info_nodename_obj, MICROPY_PY_SYS_PLATFORM); -STATIC const MP_DEFINE_STR_OBJ(mp_uos_uname_info_release_obj, MICROPY_VERSION_STRING); +STATIC CONST_RELEASE MP_DEFINE_STR_OBJ(mp_uos_uname_info_release_obj, MICROPY_VERSION_STRING); STATIC const MP_DEFINE_STR_OBJ(mp_uos_uname_info_version_obj, MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE MICROPY_BUILD_TYPE_PAREN); STATIC const MP_DEFINE_STR_OBJ(mp_uos_uname_info_machine_obj, MICROPY_HW_BOARD_NAME " with " MICROPY_HW_MCU_NAME); @@ -80,9 +87,15 @@ STATIC MP_DEFINE_ATTRTUPLE( ); STATIC mp_obj_t mp_uos_uname(void) { + #if MICROPY_PY_UOS_UNAME_RELEASE_DYNAMIC + const char *release = mp_uos_uname_release(); + mp_uos_uname_info_release_obj.len = strlen(release); + mp_uos_uname_info_release_obj.data = (const byte *)release; + #endif return MP_OBJ_FROM_PTR(&mp_uos_uname_info_obj); } STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_uos_uname_obj, mp_uos_uname); + #endif STATIC const mp_rom_map_elem_t os_module_globals_table[] = { diff --git a/ports/esp8266/Makefile b/ports/esp8266/Makefile index d0afe73ea8..fd2346d515 100644 --- a/ports/esp8266/Makefile +++ b/ports/esp8266/Makefile @@ -102,7 +102,6 @@ SRC_C = \ modesp.c \ modnetwork.c \ modutime.c \ - moduos.c \ ets_alt_task.c \ fatfs_port.c \ posix_helpers.c \ diff --git a/ports/esp8266/main.c b/ports/esp8266/main.c index 5d7debced0..1ae3f85bff 100644 --- a/ports/esp8266/main.c +++ b/ports/esp8266/main.c @@ -69,8 +69,7 @@ STATIC void mp_reset(void) { args[1] = MP_OBJ_NEW_SMALL_INT(115200); args[0] = pyb_uart_type.make_new(&pyb_uart_type, 2, 0, args); args[1] = MP_OBJ_NEW_SMALL_INT(1); - extern mp_obj_t os_dupterm(size_t n_args, const mp_obj_t *args); - os_dupterm(2, args); + mp_uos_dupterm_obj.fun.var(2, args); } #if MICROPY_MODULE_FROZEN diff --git a/ports/esp8266/moduos.c b/ports/esp8266/moduos.c index f04094fbed..9a235e61b9 100644 --- a/ports/esp8266/moduos.c +++ b/ports/esp8266/moduos.c @@ -37,37 +37,11 @@ #include "esp_mphal.h" #include "user_interface.h" -STATIC const qstr os_uname_info_fields[] = { - MP_QSTR_sysname, MP_QSTR_nodename, - MP_QSTR_release, MP_QSTR_version, MP_QSTR_machine -}; -STATIC const MP_DEFINE_STR_OBJ(os_uname_info_sysname_obj, MICROPY_PY_SYS_PLATFORM); -STATIC const MP_DEFINE_STR_OBJ(os_uname_info_nodename_obj, MICROPY_PY_SYS_PLATFORM); -STATIC const MP_DEFINE_STR_OBJ(os_uname_info_version_obj, MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE); -STATIC const MP_DEFINE_STR_OBJ(os_uname_info_machine_obj, MICROPY_HW_BOARD_NAME " with " MICROPY_HW_MCU_NAME); - -STATIC mp_obj_tuple_t os_uname_info_obj = { - .base = {&mp_type_attrtuple}, - .len = 5, - .items = { - (mp_obj_t)&os_uname_info_sysname_obj, - (mp_obj_t)&os_uname_info_nodename_obj, - NULL, - (mp_obj_t)&os_uname_info_version_obj, - (mp_obj_t)&os_uname_info_machine_obj, - (void *)os_uname_info_fields, - } -}; - -STATIC mp_obj_t os_uname(void) { - // We must populate the "release" field each time in case it was GC'd since the last call. - const char *ver = system_get_sdk_version(); - os_uname_info_obj.items[2] = mp_obj_new_str(ver, strlen(ver)); - return (mp_obj_t)&os_uname_info_obj; +STATIC const char *mp_uos_uname_release(void) { + return system_get_sdk_version(); } -STATIC MP_DEFINE_CONST_FUN_OBJ_0(os_uname_obj, os_uname); -STATIC mp_obj_t os_urandom(mp_obj_t num) { +STATIC mp_obj_t mp_uos_urandom(mp_obj_t num) { mp_int_t n = mp_obj_get_int(num); vstr_t vstr; vstr_init_len(&vstr, n); @@ -76,64 +50,20 @@ STATIC mp_obj_t os_urandom(mp_obj_t num) { } return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } -STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_urandom_obj, os_urandom); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_uos_urandom_obj, mp_uos_urandom); -// We wrap the mp_uos_dupterm function to detect if a UART is attached or not -mp_obj_t os_dupterm(size_t n_args, const mp_obj_t *args) { - mp_obj_t prev_obj = mp_uos_dupterm_obj.fun.var(n_args, args); - if (mp_obj_get_type(args[0]) == &pyb_uart_type) { +void mp_uos_dupterm_stream_detached_attached(mp_obj_t stream_detached, mp_obj_t stream_attached) { + if (mp_obj_get_type(stream_attached) == &pyb_uart_type) { ++uart_attached_to_dupterm; } - if (mp_obj_get_type(prev_obj) == &pyb_uart_type) { + if (mp_obj_get_type(stream_detached) == &pyb_uart_type) { --uart_attached_to_dupterm; } - return prev_obj; } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(os_dupterm_obj, 1, 2, os_dupterm); -STATIC mp_obj_t os_dupterm_notify(mp_obj_t obj_in) { +STATIC mp_obj_t mp_uos_dupterm_notify(mp_obj_t obj_in) { (void)obj_in; mp_hal_signal_dupterm_input(); return mp_const_none; } -STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_dupterm_notify_obj, os_dupterm_notify); - -STATIC const mp_rom_map_elem_t os_module_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uos) }, - { MP_ROM_QSTR(MP_QSTR_uname), MP_ROM_PTR(&os_uname_obj) }, - { MP_ROM_QSTR(MP_QSTR_urandom), MP_ROM_PTR(&os_urandom_obj) }, - #if MICROPY_PY_OS_DUPTERM - { MP_ROM_QSTR(MP_QSTR_dupterm), MP_ROM_PTR(&os_dupterm_obj) }, - { MP_ROM_QSTR(MP_QSTR_dupterm_notify), MP_ROM_PTR(&os_dupterm_notify_obj) }, - #endif - #if MICROPY_VFS - { MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&mp_vfs_ilistdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_listdir), MP_ROM_PTR(&mp_vfs_listdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&mp_vfs_mkdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&mp_vfs_rmdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&mp_vfs_chdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&mp_vfs_getcwd_obj) }, - { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&mp_vfs_remove_obj) }, - { MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&mp_vfs_rename_obj) }, - { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&mp_vfs_stat_obj) }, - { MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&mp_vfs_statvfs_obj) }, - { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&mp_vfs_mount_obj) }, - { MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&mp_vfs_umount_obj) }, - #if MICROPY_VFS_FAT - { MP_ROM_QSTR(MP_QSTR_VfsFat), MP_ROM_PTR(&mp_fat_vfs_type) }, - #endif - #if MICROPY_VFS_LFS1 - { MP_ROM_QSTR(MP_QSTR_VfsLfs1), MP_ROM_PTR(&mp_type_vfs_lfs1) }, - #endif - #if MICROPY_VFS_LFS2 - { MP_ROM_QSTR(MP_QSTR_VfsLfs2), MP_ROM_PTR(&mp_type_vfs_lfs2) }, - #endif - #endif -}; - -STATIC MP_DEFINE_CONST_DICT(os_module_globals, os_module_globals_table); - -const mp_obj_module_t uos_module = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t *)&os_module_globals, -}; +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_uos_dupterm_notify_obj, mp_uos_dupterm_notify); diff --git a/ports/esp8266/mpconfigport.h b/ports/esp8266/mpconfigport.h index 49c53a4671..ad73f465a3 100644 --- a/ports/esp8266/mpconfigport.h +++ b/ports/esp8266/mpconfigport.h @@ -95,7 +95,14 @@ #define MICROPY_PY_WEBREPL_DELAY (20) #define MICROPY_PY_WEBREPL_STATIC_FILEBUF (1) #define MICROPY_PY_MICROPYTHON_MEM_INFO (1) +#define MICROPY_PY_UOS (1) +#define MICROPY_PY_UOS_INCLUDEFILE "ports/esp8266/moduos.c" #define MICROPY_PY_OS_DUPTERM (2) +#define MICROPY_PY_UOS_DUPTERM_NOTIFY (1) +#define MICROPY_PY_UOS_DUPTERM_STREAM_DETACHED_ATTACHED (1) +#define MICROPY_PY_UOS_UNAME (1) +#define MICROPY_PY_UOS_UNAME_RELEASE_DYNAMIC (1) +#define MICROPY_PY_UOS_URANDOM (1) #define MICROPY_CPYTHON_COMPAT (1) #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) @@ -172,7 +179,6 @@ extern const struct _mp_print_t mp_debug_print; extern const struct _mp_obj_module_t esp_module; extern const struct _mp_obj_module_t network_module; extern const struct _mp_obj_module_t utime_module; -extern const struct _mp_obj_module_t uos_module; extern const struct _mp_obj_module_t mp_module_lwip; extern const struct _mp_obj_module_t mp_module_onewire; @@ -181,7 +187,6 @@ extern const struct _mp_obj_module_t mp_module_onewire; { MP_ROM_QSTR(MP_QSTR_usocket), MP_ROM_PTR(&mp_module_lwip) }, \ { MP_ROM_QSTR(MP_QSTR_network), MP_ROM_PTR(&network_module) }, \ { MP_ROM_QSTR(MP_QSTR_utime), MP_ROM_PTR(&utime_module) }, \ - { MP_ROM_QSTR(MP_QSTR_uos), MP_ROM_PTR(&uos_module) }, \ { MP_ROM_QSTR(MP_QSTR__onewire), MP_ROM_PTR(&mp_module_onewire) }, \ #define MP_STATE_PORT MP_STATE_VM From 2b409ef8a46015f8f3bd20bc44e644637dbe9bd3 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 9 Mar 2022 12:32:44 +1100 Subject: [PATCH 203/619] unix/moduos: Convert module to use extmod version. All variants now use extmod/moduos.c as their uos module implementation. In particular this means they all have MICROPY_VFS enabled and use VfsPosix for their filesystem. As part of this, the available functions in uos become more consistent with other ports: - coverage variant gets uos.urandom - minimal and standard variant get: unlink, chdir, getcwd, listdir Signed-off-by: Damien George --- extmod/moduos.c | 18 + ports/unix/Makefile | 2 - ports/unix/main.c | 32 -- ports/unix/modos.c | 359 ------------------ ports/unix/moduos.c | 123 ++++++ ports/unix/moduos_vfs.c | 94 ----- ports/unix/modusocket.c | 3 +- ports/unix/mpconfigport.h | 22 +- .../unix/variants/coverage/mpconfigvariant.h | 10 - ports/unix/variants/dev/mpconfigvariant.h | 9 - ports/unix/variants/minimal/mpconfigvariant.h | 13 +- 11 files changed, 163 insertions(+), 522 deletions(-) delete mode 100644 ports/unix/modos.c create mode 100644 ports/unix/moduos.c delete mode 100644 ports/unix/moduos_vfs.c diff --git a/extmod/moduos.c b/extmod/moduos.c index 0ec38bff8a..ed278dd545 100644 --- a/extmod/moduos.c +++ b/extmod/moduos.c @@ -40,6 +40,10 @@ #include "extmod/vfs_lfs.h" #endif +#if MICROPY_VFS_POSIX +#include "extmod/vfs_posix.h" +#endif + #if MICROPY_PY_UOS_UNAME #include "genhdr/mpversion.h" #endif @@ -101,12 +105,20 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_uos_uname_obj, mp_uos_uname); STATIC const mp_rom_map_elem_t os_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uos) }, + #if MICROPY_PY_UOS_GETENV_PUTENV_UNSETENV + { MP_ROM_QSTR(MP_QSTR_getenv), MP_ROM_PTR(&mp_uos_getenv_obj) }, + { MP_ROM_QSTR(MP_QSTR_putenv), MP_ROM_PTR(&mp_uos_putenv_obj) }, + { MP_ROM_QSTR(MP_QSTR_unsetenv), MP_ROM_PTR(&mp_uos_unsetenv_obj) }, + #endif #if MICROPY_PY_UOS_SEP { MP_ROM_QSTR(MP_QSTR_sep), MP_ROM_QSTR(MP_QSTR__slash_) }, #endif #if MICROPY_PY_UOS_SYNC { MP_ROM_QSTR(MP_QSTR_sync), MP_ROM_PTR(&mp_uos_sync_obj) }, #endif + #if MICROPY_PY_UOS_SYSTEM + { MP_ROM_QSTR(MP_QSTR_system), MP_ROM_PTR(&mp_uos_system_obj) }, + #endif #if MICROPY_PY_UOS_UNAME { MP_ROM_QSTR(MP_QSTR_uname), MP_ROM_PTR(&mp_uos_uname_obj) }, #endif @@ -135,6 +147,9 @@ STATIC const mp_rom_map_elem_t os_module_globals_table[] = { #if MICROPY_PY_UOS_DUPTERM_NOTIFY { MP_ROM_QSTR(MP_QSTR_dupterm_notify), MP_ROM_PTR(&mp_uos_dupterm_notify_obj) }, #endif + #if MICROPY_PY_UOS_ERRNO + { MP_ROM_QSTR(MP_QSTR_errno), MP_ROM_PTR(&mp_uos_errno_obj) }, + #endif #if MICROPY_VFS { MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&mp_vfs_ilistdir_obj) }, @@ -149,6 +164,9 @@ STATIC const mp_rom_map_elem_t os_module_globals_table[] = { #if MICROPY_VFS_LFS2 { MP_ROM_QSTR(MP_QSTR_VfsLfs2), MP_ROM_PTR(&mp_type_vfs_lfs2) }, #endif + #if MICROPY_VFS_POSIX + { MP_ROM_QSTR(MP_QSTR_VfsPosix), MP_ROM_PTR(&mp_type_vfs_posix) }, + #endif #endif }; STATIC MP_DEFINE_CONST_DICT(os_module_globals, os_module_globals_table); diff --git a/ports/unix/Makefile b/ports/unix/Makefile index 7732211e2d..d93cdf5d7f 100644 --- a/ports/unix/Makefile +++ b/ports/unix/Makefile @@ -225,8 +225,6 @@ SRC_C += \ mpthreadport.c \ input.c \ modmachine.c \ - modos.c \ - moduos_vfs.c \ modtime.c \ moduselect.c \ alloc.c \ diff --git a/ports/unix/main.c b/ports/unix/main.c index 2769f33191..bde867f8c8 100644 --- a/ports/unix/main.c +++ b/ports/unix/main.c @@ -723,38 +723,6 @@ MP_NOINLINE int main_(int argc, char **argv) { return ret & 0xff; } -#if !MICROPY_VFS -uint mp_import_stat(const char *path) { - struct stat st; - if (stat(path, &st) == 0) { - if (S_ISDIR(st.st_mode)) { - return MP_IMPORT_STAT_DIR; - } else if (S_ISREG(st.st_mode)) { - return MP_IMPORT_STAT_FILE; - } - } - return MP_IMPORT_STAT_NO_EXIST; -} - -#if MICROPY_PY_IO -// Factory function for I/O stream classes, only needed if generic VFS subsystem isn't used. -// Note: buffering and encoding are currently ignored. -mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kwargs) { - enum { ARG_file, ARG_mode }; - STATIC const mp_arg_t allowed_args[] = { - { MP_QSTR_file, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_NONE} }, - { MP_QSTR_mode, MP_ARG_OBJ, {.u_obj = MP_OBJ_NEW_QSTR(MP_QSTR_r)} }, - { MP_QSTR_buffering, MP_ARG_INT, {.u_int = -1} }, - { MP_QSTR_encoding, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, - }; - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kwargs, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - return mp_vfs_posix_file_open(&mp_type_textio, args[ARG_file].u_obj, args[ARG_mode].u_obj); -} -MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, mp_builtin_open); -#endif -#endif - void nlr_jump_fail(void *val) { fprintf(stderr, "FATAL: uncaught NLR %p\n", val); exit(1); diff --git a/ports/unix/modos.c b/ports/unix/modos.c deleted file mode 100644 index 24e5c95b18..0000000000 --- a/ports/unix/modos.c +++ /dev/null @@ -1,359 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2014-2018 Paul Sokolovsky - * Copyright (c) 2014-2018 Damien P. George - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef _MSC_VER -#include // For mkdir -#endif -#include "py/mpconfig.h" - -#include "py/runtime.h" -#include "py/objtuple.h" -#include "py/mphal.h" -#include "py/mpthread.h" -#include "extmod/vfs.h" -#include "extmod/misc.h" - -#ifdef __ANDROID__ -#define USE_STATFS 1 -#endif - -#if defined(__GLIBC__) && defined(__GLIBC_PREREQ) -#if __GLIBC_PREREQ(2, 25) -#include -#define _HAVE_GETRANDOM -#endif -#endif - -STATIC mp_obj_t mod_os_urandom(mp_obj_t num) { - mp_int_t n = mp_obj_get_int(num); - vstr_t vstr; - vstr_init_len(&vstr, n); - #ifdef _HAVE_GETRANDOM - RAISE_ERRNO(getrandom(vstr.buf, n, 0), errno); - #else - int fd = open("/dev/urandom", O_RDONLY); - RAISE_ERRNO(fd, errno); - RAISE_ERRNO(read(fd, vstr.buf, n), errno); - close(fd); - #endif - return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_os_urandom_obj, mod_os_urandom); - -STATIC mp_obj_t mod_os_stat(mp_obj_t path_in) { - struct stat sb; - const char *path = mp_obj_str_get_str(path_in); - - int res; - MP_HAL_RETRY_SYSCALL(res, stat(path, &sb), mp_raise_OSError(err)); - - mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL)); - t->items[0] = MP_OBJ_NEW_SMALL_INT(sb.st_mode); - t->items[1] = mp_obj_new_int_from_uint(sb.st_ino); - t->items[2] = mp_obj_new_int_from_uint(sb.st_dev); - t->items[3] = mp_obj_new_int_from_uint(sb.st_nlink); - t->items[4] = mp_obj_new_int_from_uint(sb.st_uid); - t->items[5] = mp_obj_new_int_from_uint(sb.st_gid); - t->items[6] = mp_obj_new_int_from_uint(sb.st_size); - t->items[7] = mp_obj_new_int_from_uint(sb.st_atime); - t->items[8] = mp_obj_new_int_from_uint(sb.st_mtime); - t->items[9] = mp_obj_new_int_from_uint(sb.st_ctime); - return MP_OBJ_FROM_PTR(t); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_os_stat_obj, mod_os_stat); - -#if MICROPY_PY_OS_STATVFS - -#if USE_STATFS -#include -#define STRUCT_STATVFS struct statfs -#define STATVFS statfs -#define F_FAVAIL sb.f_ffree -#define F_NAMEMAX sb.f_namelen -#define F_FLAG sb.f_flags -#else -#include -#define STRUCT_STATVFS struct statvfs -#define STATVFS statvfs -#define F_FAVAIL sb.f_favail -#define F_NAMEMAX sb.f_namemax -#define F_FLAG sb.f_flag -#endif - -STATIC mp_obj_t mod_os_statvfs(mp_obj_t path_in) { - STRUCT_STATVFS sb; - const char *path = mp_obj_str_get_str(path_in); - - int res; - MP_HAL_RETRY_SYSCALL(res, STATVFS(path, &sb), mp_raise_OSError(err)); - - mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL)); - t->items[0] = MP_OBJ_NEW_SMALL_INT(sb.f_bsize); - t->items[1] = MP_OBJ_NEW_SMALL_INT(sb.f_frsize); - t->items[2] = MP_OBJ_NEW_SMALL_INT(sb.f_blocks); - t->items[3] = MP_OBJ_NEW_SMALL_INT(sb.f_bfree); - t->items[4] = MP_OBJ_NEW_SMALL_INT(sb.f_bavail); - t->items[5] = MP_OBJ_NEW_SMALL_INT(sb.f_files); - t->items[6] = MP_OBJ_NEW_SMALL_INT(sb.f_ffree); - t->items[7] = MP_OBJ_NEW_SMALL_INT(F_FAVAIL); - t->items[8] = MP_OBJ_NEW_SMALL_INT(F_FLAG); - t->items[9] = MP_OBJ_NEW_SMALL_INT(F_NAMEMAX); - return MP_OBJ_FROM_PTR(t); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_os_statvfs_obj, mod_os_statvfs); -#endif - -STATIC mp_obj_t mod_os_remove(mp_obj_t path_in) { - const char *path = mp_obj_str_get_str(path_in); - - // Note that POSIX requires remove() to be able to delete a directory - // too (act as rmdir()). This is POSIX extenstion to ANSI C semantics - // of that function. But Python remove() follows ANSI C, and explicitly - // required to raise exception on attempt to remove a directory. Thus, - // call POSIX unlink() here. - MP_THREAD_GIL_EXIT(); - int r = unlink(path); - MP_THREAD_GIL_ENTER(); - - RAISE_ERRNO(r, errno); - - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_os_remove_obj, mod_os_remove); - -STATIC mp_obj_t mod_os_rename(mp_obj_t old_path_in, mp_obj_t new_path_in) { - const char *old_path = mp_obj_str_get_str(old_path_in); - const char *new_path = mp_obj_str_get_str(new_path_in); - - MP_THREAD_GIL_EXIT(); - int r = rename(old_path, new_path); - MP_THREAD_GIL_ENTER(); - - RAISE_ERRNO(r, errno); - - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_os_rename_obj, mod_os_rename); - -STATIC mp_obj_t mod_os_rmdir(mp_obj_t path_in) { - const char *path = mp_obj_str_get_str(path_in); - - MP_THREAD_GIL_EXIT(); - int r = rmdir(path); - MP_THREAD_GIL_ENTER(); - - RAISE_ERRNO(r, errno); - - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_os_rmdir_obj, mod_os_rmdir); - -STATIC mp_obj_t mod_os_system(mp_obj_t cmd_in) { - const char *cmd = mp_obj_str_get_str(cmd_in); - - MP_THREAD_GIL_EXIT(); - int r = system(cmd); - MP_THREAD_GIL_ENTER(); - - RAISE_ERRNO(r, errno); - - return MP_OBJ_NEW_SMALL_INT(r); -} -MP_DEFINE_CONST_FUN_OBJ_1(mod_os_system_obj, mod_os_system); - -STATIC mp_obj_t mod_os_getenv(mp_obj_t var_in) { - const char *s = getenv(mp_obj_str_get_str(var_in)); - if (s == NULL) { - return mp_const_none; - } - return mp_obj_new_str(s, strlen(s)); -} -MP_DEFINE_CONST_FUN_OBJ_1(mod_os_getenv_obj, mod_os_getenv); - -STATIC mp_obj_t mod_os_putenv(mp_obj_t key_in, mp_obj_t value_in) { - const char *key = mp_obj_str_get_str(key_in); - const char *value = mp_obj_str_get_str(value_in); - int ret; - - #if _WIN32 - ret = _putenv_s(key, value); - #else - ret = setenv(key, value, 1); - #endif - - if (ret == -1) { - mp_raise_OSError(errno); - } - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_2(mod_os_putenv_obj, mod_os_putenv); - -STATIC mp_obj_t mod_os_unsetenv(mp_obj_t key_in) { - const char *key = mp_obj_str_get_str(key_in); - int ret; - - #if _WIN32 - ret = _putenv_s(key, ""); - #else - ret = unsetenv(key); - #endif - - if (ret == -1) { - mp_raise_OSError(errno); - } - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_1(mod_os_unsetenv_obj, mod_os_unsetenv); - -STATIC mp_obj_t mod_os_mkdir(mp_obj_t path_in) { - // TODO: Accept mode param - const char *path = mp_obj_str_get_str(path_in); - MP_THREAD_GIL_EXIT(); - #ifdef _WIN32 - int r = mkdir(path); - #else - int r = mkdir(path, 0777); - #endif - MP_THREAD_GIL_ENTER(); - RAISE_ERRNO(r, errno); - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_os_mkdir_obj, mod_os_mkdir); - -typedef struct _mp_obj_listdir_t { - mp_obj_base_t base; - mp_fun_1_t iternext; - DIR *dir; -} mp_obj_listdir_t; - -STATIC mp_obj_t listdir_next(mp_obj_t self_in) { - mp_obj_listdir_t *self = MP_OBJ_TO_PTR(self_in); - - if (self->dir == NULL) { - goto done; - } - MP_THREAD_GIL_EXIT(); - struct dirent *dirent = readdir(self->dir); - if (dirent == NULL) { - closedir(self->dir); - MP_THREAD_GIL_ENTER(); - self->dir = NULL; - done: - return MP_OBJ_STOP_ITERATION; - } - MP_THREAD_GIL_ENTER(); - - mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL)); - t->items[0] = mp_obj_new_str(dirent->d_name, strlen(dirent->d_name)); - - #ifdef _DIRENT_HAVE_D_TYPE - #ifdef DTTOIF - t->items[1] = MP_OBJ_NEW_SMALL_INT(DTTOIF(dirent->d_type)); - #else - if (dirent->d_type == DT_DIR) { - t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFDIR); - } else if (dirent->d_type == DT_REG) { - t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFREG); - } else { - t->items[1] = MP_OBJ_NEW_SMALL_INT(dirent->d_type); - } - #endif - #else - // DT_UNKNOWN should have 0 value on any reasonable system - t->items[1] = MP_OBJ_NEW_SMALL_INT(0); - #endif - - #ifdef _DIRENT_HAVE_D_INO - t->items[2] = MP_OBJ_NEW_SMALL_INT(dirent->d_ino); - #else - t->items[2] = MP_OBJ_NEW_SMALL_INT(0); - #endif - return MP_OBJ_FROM_PTR(t); -} - -STATIC mp_obj_t mod_os_ilistdir(size_t n_args, const mp_obj_t *args) { - const char *path = "."; - if (n_args > 0) { - path = mp_obj_str_get_str(args[0]); - } - mp_obj_listdir_t *o = m_new_obj(mp_obj_listdir_t); - o->base.type = &mp_type_polymorph_iter; - MP_THREAD_GIL_EXIT(); - o->dir = opendir(path); - MP_THREAD_GIL_ENTER(); - o->iternext = listdir_next; - return MP_OBJ_FROM_PTR(o); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_os_ilistdir_obj, 0, 1, mod_os_ilistdir); - -STATIC mp_obj_t mod_os_errno(size_t n_args, const mp_obj_t *args) { - if (n_args == 0) { - return MP_OBJ_NEW_SMALL_INT(errno); - } - - errno = mp_obj_get_int(args[0]); - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_os_errno_obj, 0, 1, mod_os_errno); - -STATIC const mp_rom_map_elem_t mp_module_os_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uos) }, - { MP_ROM_QSTR(MP_QSTR_errno), MP_ROM_PTR(&mod_os_errno_obj) }, - { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&mod_os_stat_obj) }, - { MP_ROM_QSTR(MP_QSTR_urandom), MP_ROM_PTR(&mod_os_urandom_obj) }, - #if MICROPY_PY_OS_STATVFS - { MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&mod_os_statvfs_obj) }, - #endif - { MP_ROM_QSTR(MP_QSTR_system), MP_ROM_PTR(&mod_os_system_obj) }, - { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&mod_os_remove_obj) }, - { MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&mod_os_rename_obj) }, - { MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&mod_os_rmdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_getenv), MP_ROM_PTR(&mod_os_getenv_obj) }, - { MP_ROM_QSTR(MP_QSTR_putenv), MP_ROM_PTR(&mod_os_putenv_obj) }, - { MP_ROM_QSTR(MP_QSTR_unsetenv), MP_ROM_PTR(&mod_os_unsetenv_obj) }, - { MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&mod_os_mkdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&mod_os_ilistdir_obj) }, - #if MICROPY_PY_OS_DUPTERM - { MP_ROM_QSTR(MP_QSTR_dupterm), MP_ROM_PTR(&mp_uos_dupterm_obj) }, - #endif -}; - -STATIC MP_DEFINE_CONST_DICT(mp_module_os_globals, mp_module_os_globals_table); - -const mp_obj_module_t mp_module_os = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t *)&mp_module_os_globals, -}; diff --git a/ports/unix/moduos.c b/ports/unix/moduos.c new file mode 100644 index 0000000000..1262cd2e29 --- /dev/null +++ b/ports/unix/moduos.c @@ -0,0 +1,123 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014-2018 Paul Sokolovsky + * Copyright (c) 2017-2022 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include + +#include "py/runtime.h" +#include "py/mphal.h" + +#if defined(__GLIBC__) && defined(__GLIBC_PREREQ) +#if __GLIBC_PREREQ(2, 25) +#include +#define _HAVE_GETRANDOM +#endif +#endif + +STATIC mp_obj_t mp_uos_getenv(mp_obj_t var_in) { + const char *s = getenv(mp_obj_str_get_str(var_in)); + if (s == NULL) { + return mp_const_none; + } + return mp_obj_new_str(s, strlen(s)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_uos_getenv_obj, mp_uos_getenv); + +STATIC mp_obj_t mp_uos_putenv(mp_obj_t key_in, mp_obj_t value_in) { + const char *key = mp_obj_str_get_str(key_in); + const char *value = mp_obj_str_get_str(value_in); + int ret; + + #if _WIN32 + ret = _putenv_s(key, value); + #else + ret = setenv(key, value, 1); + #endif + + if (ret == -1) { + mp_raise_OSError(errno); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(mp_uos_putenv_obj, mp_uos_putenv); + +STATIC mp_obj_t mp_uos_unsetenv(mp_obj_t key_in) { + const char *key = mp_obj_str_get_str(key_in); + int ret; + + #if _WIN32 + ret = _putenv_s(key, ""); + #else + ret = unsetenv(key); + #endif + + if (ret == -1) { + mp_raise_OSError(errno); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_uos_unsetenv_obj, mp_uos_unsetenv); + +STATIC mp_obj_t mp_uos_system(mp_obj_t cmd_in) { + const char *cmd = mp_obj_str_get_str(cmd_in); + + MP_THREAD_GIL_EXIT(); + int r = system(cmd); + MP_THREAD_GIL_ENTER(); + + RAISE_ERRNO(r, errno); + + return MP_OBJ_NEW_SMALL_INT(r); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_uos_system_obj, mp_uos_system); + +STATIC mp_obj_t mp_uos_urandom(mp_obj_t num) { + mp_int_t n = mp_obj_get_int(num); + vstr_t vstr; + vstr_init_len(&vstr, n); + #ifdef _HAVE_GETRANDOM + RAISE_ERRNO(getrandom(vstr.buf, n, 0), errno); + #else + int fd = open("/dev/urandom", O_RDONLY); + RAISE_ERRNO(fd, errno); + RAISE_ERRNO(read(fd, vstr.buf, n), errno); + close(fd); + #endif + return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_uos_urandom_obj, mp_uos_urandom); + +STATIC mp_obj_t mp_uos_errno(size_t n_args, const mp_obj_t *args) { + if (n_args == 0) { + return MP_OBJ_NEW_SMALL_INT(errno); + } + + errno = mp_obj_get_int(args[0]); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_uos_errno_obj, 0, 1, mp_uos_errno); diff --git a/ports/unix/moduos_vfs.c b/ports/unix/moduos_vfs.c deleted file mode 100644 index 6e4f352aad..0000000000 --- a/ports/unix/moduos_vfs.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2017 Damien P. George - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include -#include - -#include "extmod/vfs.h" -#include "extmod/vfs_posix.h" -#include "extmod/vfs_fat.h" -#include "extmod/vfs_lfs.h" - -#if MICROPY_VFS - -// These are defined in modos.c -MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mod_os_errno_obj); -MP_DECLARE_CONST_FUN_OBJ_1(mod_os_getenv_obj); -MP_DECLARE_CONST_FUN_OBJ_1(mod_os_putenv_obj); -MP_DECLARE_CONST_FUN_OBJ_1(mod_os_unsetenv_obj); -MP_DECLARE_CONST_FUN_OBJ_1(mod_os_system_obj); - -STATIC const mp_rom_map_elem_t uos_vfs_module_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uos_vfs) }, - { MP_ROM_QSTR(MP_QSTR_sep), MP_ROM_QSTR(MP_QSTR__slash_) }, - - { MP_ROM_QSTR(MP_QSTR_errno), MP_ROM_PTR(&mod_os_errno_obj) }, - { MP_ROM_QSTR(MP_QSTR_getenv), MP_ROM_PTR(&mod_os_getenv_obj) }, - { MP_ROM_QSTR(MP_QSTR_putenv), MP_ROM_PTR(&mod_os_putenv_obj) }, - { MP_ROM_QSTR(MP_QSTR_unsetenv), MP_ROM_PTR(&mod_os_unsetenv_obj) }, - { MP_ROM_QSTR(MP_QSTR_system), MP_ROM_PTR(&mod_os_system_obj) }, - - { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&mp_vfs_mount_obj) }, - { MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&mp_vfs_umount_obj) }, - - { MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&mp_vfs_chdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&mp_vfs_getcwd_obj) }, - { MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&mp_vfs_ilistdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_listdir), MP_ROM_PTR(&mp_vfs_listdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&mp_vfs_mkdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&mp_vfs_remove_obj) }, - { MP_ROM_QSTR(MP_QSTR_rename),MP_ROM_PTR(&mp_vfs_rename_obj) }, - { MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&mp_vfs_rmdir_obj) }, - { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&mp_vfs_stat_obj) }, - { MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&mp_vfs_statvfs_obj) }, - { MP_ROM_QSTR(MP_QSTR_unlink), MP_ROM_PTR(&mp_vfs_remove_obj) }, // unlink aliases to remove - - #if MICROPY_PY_OS_DUPTERM - { MP_ROM_QSTR(MP_QSTR_dupterm), MP_ROM_PTR(&mp_uos_dupterm_obj) }, - #endif - - #if MICROPY_VFS_POSIX - { MP_ROM_QSTR(MP_QSTR_VfsPosix), MP_ROM_PTR(&mp_type_vfs_posix) }, - #endif - #if MICROPY_VFS_FAT - { MP_ROM_QSTR(MP_QSTR_VfsFat), MP_ROM_PTR(&mp_fat_vfs_type) }, - #endif - #if MICROPY_VFS_LFS1 - { MP_ROM_QSTR(MP_QSTR_VfsLfs1), MP_ROM_PTR(&mp_type_vfs_lfs1) }, - #endif - #if MICROPY_VFS_LFS2 - { MP_ROM_QSTR(MP_QSTR_VfsLfs2), MP_ROM_PTR(&mp_type_vfs_lfs2) }, - #endif -}; - -STATIC MP_DEFINE_CONST_DICT(uos_vfs_module_globals, uos_vfs_module_globals_table); - -const mp_obj_module_t mp_module_uos_vfs = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t *)&uos_vfs_module_globals, -}; - -#endif // MICROPY_VFS diff --git a/ports/unix/modusocket.c b/ports/unix/modusocket.c index 951cb7a21c..efa182e393 100644 --- a/ports/unix/modusocket.c +++ b/ports/unix/modusocket.c @@ -46,6 +46,7 @@ #include "py/builtin.h" #include "py/mphal.h" #include "py/mpthread.h" +#include "extmod/vfs.h" #include /* @@ -445,7 +446,7 @@ STATIC mp_obj_t socket_makefile(size_t n_args, const mp_obj_t *args) { mp_obj_t *new_args = alloca(n_args * sizeof(mp_obj_t)); memcpy(new_args + 1, args + 1, (n_args - 1) * sizeof(mp_obj_t)); new_args[0] = MP_OBJ_NEW_SMALL_INT(self->fd); - return mp_builtin_open(n_args, new_args, (mp_map_t *)&mp_const_empty_map); + return mp_vfs_open(n_args, new_args, (mp_map_t *)&mp_const_empty_map); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_makefile_obj, 1, 3, socket_makefile); diff --git a/ports/unix/mpconfigport.h b/ports/unix/mpconfigport.h index d54d0b3ef3..f96abb9420 100644 --- a/ports/unix/mpconfigport.h +++ b/ports/unix/mpconfigport.h @@ -64,6 +64,7 @@ // check stdout a chance to pass, etc. #define MICROPY_DEBUG_PRINTER (&mp_stderr_print) #define MICROPY_READER_POSIX (1) +#define MICROPY_READER_VFS (1) #define MICROPY_USE_READLINE_HISTORY (1) #define MICROPY_HELPER_REPL (1) #define MICROPY_REPL_EMACS_KEYS (1) @@ -88,7 +89,8 @@ #define MICROPY_MODULE_WEAK_LINKS (1) #define MICROPY_MODULE_OVERRIDE_MAIN_IMPORT (1) #define MICROPY_CAN_OVERRIDE_BUILTINS (1) -#define MICROPY_VFS_POSIX_FILE (1) +#define MICROPY_VFS (1) +#define MICROPY_VFS_POSIX (1) #define MICROPY_PY_FUNCTION_ATTRS (1) #define MICROPY_PY_DESCRIPTORS (1) #define MICROPY_PY_DELATTR_SETATTR (1) @@ -146,7 +148,13 @@ #define MICROPY_STACKLESS_STRICT (0) #endif -#define MICROPY_PY_OS_STATVFS (1) +#define MICROPY_PY_UOS (1) +#define MICROPY_PY_UOS_INCLUDEFILE "ports/unix/moduos.c" +#define MICROPY_PY_UOS_ERRNO (1) +#define MICROPY_PY_UOS_GETENV_PUTENV_UNSETENV (1) +#define MICROPY_PY_UOS_SEP (1) +#define MICROPY_PY_UOS_SYSTEM (1) +#define MICROPY_PY_UOS_URANDOM (1) #define MICROPY_PY_UTIME (1) #define MICROPY_PY_UTIME_MP_HAL (1) #define MICROPY_PY_UERRNO (1) @@ -201,11 +209,11 @@ extern const struct _mp_print_t mp_stderr_print; #define MICROPY_KBD_EXCEPTION (1) #define MICROPY_ASYNC_KBD_INTR (1) +#define mp_import_stat mp_vfs_import_stat +#define mp_builtin_open_obj mp_vfs_open_obj #define mp_type_fileio mp_type_vfs_posix_fileio #define mp_type_textio mp_type_vfs_posix_textio -extern const struct _mp_obj_module_t mp_module_os; -extern const struct _mp_obj_module_t mp_module_uos_vfs; extern const struct _mp_obj_module_t mp_module_uselect; extern const struct _mp_obj_module_t mp_module_time; extern const struct _mp_obj_module_t mp_module_termios; @@ -213,11 +221,6 @@ extern const struct _mp_obj_module_t mp_module_socket; extern const struct _mp_obj_module_t mp_module_ffi; extern const struct _mp_obj_module_t mp_module_jni; -#if MICROPY_PY_UOS_VFS -#define MICROPY_PY_UOS_DEF { MP_ROM_QSTR(MP_QSTR_uos), MP_ROM_PTR(&mp_module_uos_vfs) }, -#else -#define MICROPY_PY_UOS_DEF { MP_ROM_QSTR(MP_QSTR_uos), MP_ROM_PTR(&mp_module_os) }, -#endif #if MICROPY_PY_FFI #define MICROPY_PY_FFI_DEF { MP_ROM_QSTR(MP_QSTR_ffi), MP_ROM_PTR(&mp_module_ffi) }, #else @@ -254,7 +257,6 @@ extern const struct _mp_obj_module_t mp_module_jni; MICROPY_PY_JNI_DEF \ MICROPY_PY_UTIME_DEF \ MICROPY_PY_SOCKET_DEF \ - MICROPY_PY_UOS_DEF \ MICROPY_PY_USELECT_DEF \ MICROPY_PY_TERMIOS_DEF \ diff --git a/ports/unix/variants/coverage/mpconfigvariant.h b/ports/unix/variants/coverage/mpconfigvariant.h index 5ce40edbb9..db01c4bcd5 100644 --- a/ports/unix/variants/coverage/mpconfigvariant.h +++ b/ports/unix/variants/coverage/mpconfigvariant.h @@ -27,14 +27,10 @@ // This config enables almost all possible features such that it can be used // for coverage testing. -#define MICROPY_VFS (1) -#define MICROPY_PY_UOS_VFS (1) - #define MICROPY_DEBUG_PARSE_RULE_NAME (1) #define MICROPY_OPT_MATH_FACTORIAL (1) #define MICROPY_FLOAT_HIGH_QUALITY_HASH (1) #define MICROPY_ENABLE_SCHEDULER (1) -#define MICROPY_READER_VFS (1) #define MICROPY_REPL_EMACS_WORDS_MOVE (1) #define MICROPY_REPL_EMACS_EXTRA_WORDS_MOVE (1) #define MICROPY_WARNINGS_CATEGORY (1) @@ -57,14 +53,8 @@ #define MICROPY_PY_URE_MATCH_GROUPS (1) #define MICROPY_PY_URE_MATCH_SPAN_START_END (1) #define MICROPY_PY_URE_SUB (1) -#define MICROPY_VFS_POSIX (1) #define MICROPY_PY_FRAMEBUF (1) #define MICROPY_PY_COLLECTIONS_NAMEDTUPLE__ASDICT (1) #define MICROPY_PY_UCRYPTOLIB (1) #define MICROPY_PY_UCRYPTOLIB_CTR (1) #define MICROPY_PY_MICROPYTHON_HEAP_LOCKED (1) - -// use vfs's functions for import stat and builtin open -#define mp_import_stat mp_vfs_import_stat -#define mp_builtin_open mp_vfs_open -#define mp_builtin_open_obj mp_vfs_open_obj diff --git a/ports/unix/variants/dev/mpconfigvariant.h b/ports/unix/variants/dev/mpconfigvariant.h index 35c24c5f00..280243e344 100644 --- a/ports/unix/variants/dev/mpconfigvariant.h +++ b/ports/unix/variants/dev/mpconfigvariant.h @@ -24,25 +24,16 @@ * THE SOFTWARE. */ -#define MICROPY_READER_VFS (1) #define MICROPY_REPL_EMACS_WORDS_MOVE (1) #define MICROPY_REPL_EMACS_EXTRA_WORDS_MOVE (1) #define MICROPY_ENABLE_SCHEDULER (1) -#define MICROPY_VFS (1) -#define MICROPY_VFS_POSIX (1) #define MICROPY_PY_BUILTINS_HELP (1) #define MICROPY_PY_BUILTINS_HELP_MODULES (1) #define MICROPY_PY_MATH_CONSTANTS (1) #define MICROPY_PY_SYS_SETTRACE (1) -#define MICROPY_PY_UOS_VFS (1) #define MICROPY_PY_URANDOM_EXTRA_FUNCS (1) #ifndef MICROPY_PY_UASYNCIO #define MICROPY_PY_UASYNCIO (1) #endif - -// Use vfs's functions for import stat and builtin open. -#define mp_import_stat mp_vfs_import_stat -#define mp_builtin_open mp_vfs_open -#define mp_builtin_open_obj mp_vfs_open_obj diff --git a/ports/unix/variants/minimal/mpconfigvariant.h b/ports/unix/variants/minimal/mpconfigvariant.h index e0db3756ca..f6be446920 100644 --- a/ports/unix/variants/minimal/mpconfigvariant.h +++ b/ports/unix/variants/minimal/mpconfigvariant.h @@ -44,6 +44,7 @@ #define MICROPY_MEM_STATS (0) #define MICROPY_DEBUG_PRINTERS (0) #define MICROPY_READER_POSIX (1) +#define MICROPY_READER_VFS (1) #define MICROPY_HELPER_REPL (1) #define MICROPY_HELPER_LEXER_UNIX (1) #define MICROPY_ENABLE_SOURCE_LINE (0) @@ -59,6 +60,8 @@ #define MICROPY_OPT_MAP_LOOKUP_CACHE (0) #define MICROPY_CAN_OVERRIDE_BUILTINS (0) #define MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG (0) +#define MICROPY_VFS (1) +#define MICROPY_VFS_POSIX (1) #define MICROPY_CPYTHON_COMPAT (0) #define MICROPY_PY_BUILTINS_BYTEARRAY (0) #define MICROPY_PY_BUILTINS_MEMORYVIEW (0) @@ -98,18 +101,18 @@ #define MICROPY_PY_UTIME (0) #define MICROPY_PY_UZLIB (0) #define MICROPY_PY_UJSON (0) +#define MICROPY_PY_UOS (1) #define MICROPY_PY_URE (0) #define MICROPY_PY_UHEAPQ (0) #define MICROPY_PY_UHASHLIB (0) #define MICROPY_PY_UBINASCII (0) -extern const struct _mp_obj_module_t mp_module_os; - -#define MICROPY_PORT_BUILTIN_MODULES \ - { MP_ROM_QSTR(MP_QSTR_uos), MP_ROM_PTR(&mp_module_os) }, \ - #define MICROPY_PORT_ROOT_POINTERS \ +#define mp_import_stat mp_vfs_import_stat +#define mp_type_fileio mp_type_vfs_posix_fileio +#define mp_type_textio mp_type_vfs_posix_textio + ////////////////////////////////////////// // Do not change anything beyond this line ////////////////////////////////////////// From 0149cd6b8b255907b49ec105146a2f238b07dbb8 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 9 Mar 2022 12:45:06 +1100 Subject: [PATCH 204/619] windows: Switch to VFS subsystem and use VfsPosix. Following the unix port. Signed-off-by: Damien George --- extmod/vfs_posix.c | 13 +++++++++++++ ports/windows/Makefile | 1 - ports/windows/micropython.vcxproj | 1 - ports/windows/mpconfigport.h | 16 +++++++++++++--- ports/windows/msvc/sources.props | 4 ++++ py/mpconfig.h | 4 ++++ 6 files changed, 34 insertions(+), 5 deletions(-) diff --git a/extmod/vfs_posix.c b/extmod/vfs_posix.c index 719afe28fe..5dcc9c03ec 100644 --- a/extmod/vfs_posix.c +++ b/extmod/vfs_posix.c @@ -37,6 +37,9 @@ #include #include #include +#ifdef _MSC_VER +#include // For mkdir etc. +#endif typedef struct _mp_obj_vfs_posix_t { mp_obj_base_t base; @@ -254,7 +257,11 @@ STATIC mp_obj_t vfs_posix_mkdir(mp_obj_t self_in, mp_obj_t path_in) { mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in); const char *path = vfs_posix_get_path_str(self, path_in); MP_THREAD_GIL_EXIT(); + #ifdef _WIN32 + int ret = mkdir(path); + #else int ret = mkdir(path, 0777); + #endif MP_THREAD_GIL_ENTER(); if (ret != 0) { mp_raise_OSError(errno); @@ -308,6 +315,8 @@ STATIC mp_obj_t vfs_posix_stat(mp_obj_t self_in, mp_obj_t path_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_stat_obj, vfs_posix_stat); +#if MICROPY_PY_UOS_STATVFS + #ifdef __ANDROID__ #define USE_STATFS 1 #endif @@ -349,6 +358,8 @@ STATIC mp_obj_t vfs_posix_statvfs(mp_obj_t self_in, mp_obj_t path_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_statvfs_obj, vfs_posix_statvfs); +#endif + STATIC const mp_rom_map_elem_t vfs_posix_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&vfs_posix_mount_obj) }, { MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&vfs_posix_umount_obj) }, @@ -362,7 +373,9 @@ STATIC const mp_rom_map_elem_t vfs_posix_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&vfs_posix_rename_obj) }, { MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&vfs_posix_rmdir_obj) }, { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&vfs_posix_stat_obj) }, + #if MICROPY_PY_UOS_STATVFS { MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&vfs_posix_statvfs_obj) }, + #endif }; STATIC MP_DEFINE_CONST_DICT(vfs_posix_locals_dict, vfs_posix_locals_dict_table); diff --git a/ports/windows/Makefile b/ports/windows/Makefile index 91744b7a5e..c9efc2353c 100644 --- a/ports/windows/Makefile +++ b/ports/windows/Makefile @@ -49,7 +49,6 @@ SRC_C = \ shared/runtime/gchelper_generic.c \ ports/unix/main.c \ ports/unix/input.c \ - ports/unix/modos.c \ ports/unix/modmachine.c \ ports/unix/modtime.c \ ports/unix/gccollect.c \ diff --git a/ports/windows/micropython.vcxproj b/ports/windows/micropython.vcxproj index d5e3f57d8b..4fa0c47df8 100644 --- a/ports/windows/micropython.vcxproj +++ b/ports/windows/micropython.vcxproj @@ -93,7 +93,6 @@ - diff --git a/ports/windows/mpconfigport.h b/ports/windows/mpconfigport.h index d589dac7a7..4679d13565 100644 --- a/ports/windows/mpconfigport.h +++ b/ports/windows/mpconfigport.h @@ -51,6 +51,7 @@ #define MICROPY_DEBUG_PRINTER (&mp_stderr_print) #define MICROPY_DEBUG_PRINTERS (1) #define MICROPY_READER_POSIX (1) +#define MICROPY_READER_VFS (1) #define MICROPY_USE_READLINE_HISTORY (1) #define MICROPY_HELPER_REPL (1) #define MICROPY_REPL_EMACS_KEYS (1) @@ -72,7 +73,8 @@ #ifndef MICROPY_ENABLE_SCHEDULER #define MICROPY_ENABLE_SCHEDULER (1) #endif -#define MICROPY_VFS_POSIX_FILE (1) +#define MICROPY_VFS (1) +#define MICROPY_VFS_POSIX (1) #define MICROPY_PY_FUNCTION_ATTRS (1) #define MICROPY_PY_DESCRIPTORS (1) #define MICROPY_PY_DELATTR_SETATTR (1) @@ -124,6 +126,14 @@ #define MICROPY_STACKLESS_STRICT (0) #endif +#define MICROPY_PY_UOS (1) +#define MICROPY_PY_UOS_INCLUDEFILE "ports/unix/moduos.c" +#define MICROPY_PY_UOS_ERRNO (1) +#define MICROPY_PY_UOS_GETENV_PUTENV_UNSETENV (1) +#define MICROPY_PY_UOS_SEP (1) +#define MICROPY_PY_UOS_STATVFS (0) +#define MICROPY_PY_UOS_SYSTEM (1) +#define MICROPY_PY_UOS_URANDOM (1) #define MICROPY_PY_UTIME (1) #define MICROPY_PY_UTIME_MP_HAL (1) #define MICROPY_PY_UERRNO (1) @@ -161,6 +171,8 @@ extern const struct _mp_print_t mp_stderr_print; #define MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE (256) #define MICROPY_KBD_EXCEPTION (1) +#define mp_import_stat mp_vfs_import_stat +#define mp_builtin_open_obj mp_vfs_open_obj #define mp_type_fileio mp_type_vfs_posix_fileio #define mp_type_textio mp_type_vfs_posix_textio @@ -203,11 +215,9 @@ typedef long mp_off_t; #define MICROPY_PORT_BUILTINS \ { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mp_builtin_open_obj) }, -extern const struct _mp_obj_module_t mp_module_os; extern const struct _mp_obj_module_t mp_module_time; #define MICROPY_PORT_BUILTIN_MODULES \ { MP_ROM_QSTR(MP_QSTR_utime), MP_ROM_PTR(&mp_module_time) }, \ - { MP_ROM_QSTR(MP_QSTR_uos), MP_ROM_PTR(&mp_module_os) }, \ #if MICROPY_USE_READLINE == 1 #define MICROPY_PORT_ROOT_POINTERS \ diff --git a/ports/windows/msvc/sources.props b/ports/windows/msvc/sources.props index a6dfd48aa6..b755049db0 100644 --- a/ports/windows/msvc/sources.props +++ b/ports/windows/msvc/sources.props @@ -13,6 +13,7 @@ + @@ -20,7 +21,10 @@ + + + diff --git a/py/mpconfig.h b/py/mpconfig.h index 8f9f0f02fb..a41e99b4c1 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -1459,6 +1459,10 @@ typedef double mp_float_t; #define MICROPY_PY_UOS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) #endif +#ifndef MICROPY_PY_UOS_STATVFS +#define MICROPY_PY_UOS_STATVFS (MICROPY_PY_UOS) +#endif + #ifndef MICROPY_PY_URE #define MICROPY_PY_URE (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) #endif From d470c5a5baa8660461b76585a57f2de28b30f5dc Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 10 Mar 2022 00:41:03 +1100 Subject: [PATCH 205/619] tests/extmod/vfs_posix.py: Only test statvfs if it exists. Signed-off-by: Damien George --- tests/extmod/vfs_posix.py | 5 +++-- tests/extmod/vfs_posix.py.exp | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/extmod/vfs_posix.py b/tests/extmod/vfs_posix.py index f8c4aae406..2a14fc2076 100644 --- a/tests/extmod/vfs_posix.py +++ b/tests/extmod/vfs_posix.py @@ -59,9 +59,10 @@ print(uos.listdir(temp_dir)) vfs = uos.VfsPosix(temp_dir) print(list(i[0] for i in vfs.ilistdir("."))) -# stat, statvfs +# stat, statvfs (statvfs may not exist) print(type(vfs.stat("."))) -print(type(vfs.statvfs("."))) +if hasattr(vfs, "statvfs"): + assert type(vfs.statvfs(".")) is tuple # check types of ilistdir with str/bytes arguments print(type(list(vfs.ilistdir("."))[0][0])) diff --git a/tests/extmod/vfs_posix.py.exp b/tests/extmod/vfs_posix.py.exp index e7d68f38ec..eb9ab43106 100644 --- a/tests/extmod/vfs_posix.py.exp +++ b/tests/extmod/vfs_posix.py.exp @@ -7,7 +7,6 @@ hello ['test2'] ['test2'] - [] From 3356b5ef8d1b232a1df493f70f0ba434f293fe11 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 27 Jul 2021 00:38:21 +1000 Subject: [PATCH 206/619] py/objmodule: Support delegating failed attr lookups. This commit adds generic support for mutable module attributes on built in modules, by adding support for an optional hook function for module attribute lookup. If a module wants to support additional attribute load/ store/delete (beyond what is in the constant, globals dict) then it should add at the very end of its globals dict MP_MODULE_ATTR_DELEGATION_ENTRY(). This should point to a custom function which will handle any additional attributes. The mp_module_generic_attr() function is provided as a helper function for additional attributes: it requires an array of qstrs (terminated in MP_QSTRnull) and a corresponding array of objects (with a 1-1 mapping between qstrs and objects). If the qstr is found in the array then the corresponding object is loaded/stored/deleted. Signed-off-by: Damien George --- py/mpconfig.h | 6 ++++++ py/objmodule.c | 36 ++++++++++++++++++++++++++++++++++++ py/objmodule.h | 5 +++++ 3 files changed, 47 insertions(+) diff --git a/py/mpconfig.h b/py/mpconfig.h index a41e99b4c1..2b03b93f4e 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -822,6 +822,12 @@ typedef double mp_float_t; #define MICROPY_STREAMS_POSIX_API (0) #endif +// Whether modules can use MP_MODULE_ATTR_DELEGATION_ENTRY() to delegate failed +// attribute lookups. +#ifndef MICROPY_MODULE_ATTR_DELEGATION +#define MICROPY_MODULE_ATTR_DELEGATION (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + // Whether to call __init__ when importing builtin modules for the first time #ifndef MICROPY_MODULE_BUILTIN_INIT #define MICROPY_MODULE_BUILTIN_INIT (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) diff --git a/py/objmodule.c b/py/objmodule.c index f7cd437bac..0cd10e61f0 100644 --- a/py/objmodule.c +++ b/py/objmodule.c @@ -62,6 +62,21 @@ STATIC void module_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kin mp_printf(print, "", module_name); } +STATIC void module_attr_try_delegation(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + #if MICROPY_MODULE_ATTR_DELEGATION + // Delegate lookup to a module's custom attr method (found in last lot of globals dict). + mp_obj_module_t *self = MP_OBJ_TO_PTR(self_in); + mp_map_t *map = &self->globals->map; + if (map->table[map->alloc - 1].key == MP_OBJ_NEW_QSTR(MP_QSTRnull)) { + ((mp_attr_fun_t)MP_OBJ_TO_PTR(map->table[map->alloc - 1].value))(self_in, attr, dest); + } + #else + (void)self_in; + (void)attr; + (void)dest; + #endif +} + STATIC void module_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { mp_obj_module_t *self = MP_OBJ_TO_PTR(self_in); if (dest[0] == MP_OBJ_NULL) { @@ -74,8 +89,12 @@ STATIC void module_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { elem = mp_map_lookup(&self->globals->map, MP_OBJ_NEW_QSTR(MP_QSTR___getattr__), MP_MAP_LOOKUP); if (elem != NULL) { dest[0] = mp_call_function_1(elem->value, MP_OBJ_NEW_QSTR(attr)); + } else { + module_attr_try_delegation(self_in, attr, dest); } #endif + } else { + module_attr_try_delegation(self_in, attr, dest); } } else { // delete/store attribute @@ -91,6 +110,7 @@ STATIC void module_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { #endif { // can't delete or store to fixed map + module_attr_try_delegation(self_in, attr, dest); return; } } @@ -319,3 +339,19 @@ STATIC void mp_module_call_init(mp_obj_t module_name, mp_obj_t module_obj) { } } #endif + +void mp_module_generic_attr(qstr attr, mp_obj_t *dest, const uint16_t *keys, mp_obj_t *values) { + for (size_t i = 0; keys[i] != MP_QSTRnull; ++i) { + if (attr == keys[i]) { + if (dest[0] == MP_OBJ_NULL) { + // load attribute (MP_OBJ_NULL returned for deleted items) + dest[0] = values[i]; + } else { + // delete or store (delete stores MP_OBJ_NULL) + values[i] = dest[1]; + dest[0] = MP_OBJ_NULL; // indicate success + } + return; + } + } +} diff --git a/py/objmodule.h b/py/objmodule.h index 8a82d13fe5..d11d5bcd74 100644 --- a/py/objmodule.h +++ b/py/objmodule.h @@ -28,6 +28,9 @@ #include "py/obj.h" +// Place at the very end of a module's globals_table. +#define MP_MODULE_ATTR_DELEGATION_ENTRY(ptr) { MP_ROM_QSTR(MP_QSTRnull), MP_ROM_PTR(ptr) } + extern const mp_map_t mp_builtin_module_map; mp_obj_t mp_module_get_loaded_or_builtin(qstr module_name); @@ -35,4 +38,6 @@ mp_obj_t mp_module_get_loaded_or_builtin(qstr module_name); mp_obj_t mp_module_get_builtin(qstr module_name); #endif +void mp_module_generic_attr(qstr attr, mp_obj_t *dest, const uint16_t *keys, mp_obj_t *values); + #endif // MICROPY_INCLUDED_PY_OBJMODULE_H From bc181550a4790f4dcfaf10e7e61ecfcdf346d5a8 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 27 Jul 2021 00:39:04 +1000 Subject: [PATCH 207/619] py/modsys: Add optional attribute delegation. To be enabled when needed by specific sys attributes. Signed-off-by: Damien George --- py/modsys.c | 18 ++++++++++++++++++ py/mpconfig.h | 6 ++++++ py/mpstate.h | 9 +++++++++ 3 files changed, 33 insertions(+) diff --git a/py/modsys.c b/py/modsys.c index 43666bc00e..a05709f8e6 100644 --- a/py/modsys.c +++ b/py/modsys.c @@ -27,6 +27,7 @@ #include "py/builtin.h" #include "py/objlist.h" +#include "py/objmodule.h" #include "py/objtuple.h" #include "py/objstr.h" #include "py/objint.h" @@ -182,6 +183,18 @@ STATIC mp_obj_t mp_sys_settrace(mp_obj_t obj) { MP_DEFINE_CONST_FUN_OBJ_1(mp_sys_settrace_obj, mp_sys_settrace); #endif // MICROPY_PY_SYS_SETTRACE +#if MICROPY_PY_SYS_ATTR_DELEGATION +STATIC const uint16_t sys_mutable_keys[] = { + MP_QSTRnull, +}; + +STATIC void mp_module_sys_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + MP_STATIC_ASSERT(MP_ARRAY_SIZE(sys_mutable_keys) == MP_SYS_MUTABLE_NUM + 1); + MP_STATIC_ASSERT(MP_ARRAY_SIZE(MP_STATE_VM(sys_mutable)) == MP_SYS_MUTABLE_NUM); + mp_module_generic_attr(attr, dest, sys_mutable_keys, MP_STATE_VM(sys_mutable)); +} +#endif + STATIC const mp_rom_map_elem_t mp_module_sys_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_sys) }, @@ -244,6 +257,11 @@ STATIC const mp_rom_map_elem_t mp_module_sys_globals_table[] = { #if MICROPY_PY_SYS_ATEXIT { MP_ROM_QSTR(MP_QSTR_atexit), MP_ROM_PTR(&mp_sys_atexit_obj) }, #endif + + #if MICROPY_PY_SYS_ATTR_DELEGATION + // Delegation of attr lookup. + MP_MODULE_ATTR_DELEGATION_ENTRY(&mp_module_sys_attr), + #endif }; STATIC MP_DEFINE_CONST_DICT(mp_module_sys_globals, mp_module_sys_globals_table); diff --git a/py/mpconfig.h b/py/mpconfig.h index 2b03b93f4e..617e897080 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -1377,6 +1377,12 @@ typedef double mp_float_t; #define MICROPY_PY_SYS_STDIO_BUFFER (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) #endif +// Whether the sys module supports attribute delegation +// This is enabled automatically when needed by other features +#ifndef MICROPY_PY_SYS_ATTR_DELEGATION +#define MICROPY_PY_SYS_ATTR_DELEGATION (0) +#endif + // Whether to provide "uerrno" module #ifndef MICROPY_PY_UERRNO #define MICROPY_PY_UERRNO (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) diff --git a/py/mpstate.h b/py/mpstate.h index 7f86399f57..f29e6be502 100644 --- a/py/mpstate.h +++ b/py/mpstate.h @@ -40,6 +40,10 @@ // memory system, runtime and virtual machine. The state is a global // variable, but in the future it is hoped that the state can become local. +enum { + MP_SYS_MUTABLE_NUM, +}; + // This structure contains dynamic configuration for the compiler. #if MICROPY_DYNAMIC_COMPILER typedef struct mp_dynamic_compiler_t { @@ -158,6 +162,11 @@ typedef struct _mp_state_vm_t { // must be initialised after the call to mp_init. mp_obj_list_t mp_sys_path_obj; mp_obj_list_t mp_sys_argv_obj; + + #if MICROPY_PY_SYS_ATTR_DELEGATION + // Contains mutable sys attributes. + mp_obj_t sys_mutable[MP_SYS_MUTABLE_NUM]; + #endif #endif // dictionary for overridden builtins From cac939ddc3625da7e6cf1cf0309daba25fc1cedb Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 27 Jul 2021 00:41:27 +1000 Subject: [PATCH 208/619] py/modsys: Add optional sys.tracebacklimit attribute. With behaviour as per CPython. Signed-off-by: Damien George --- docs/library/sys.rst | 8 ++ .../unix/variants/coverage/mpconfigvariant.h | 2 + py/modsys.c | 3 + py/mpconfig.h | 7 +- py/mpstate.h | 3 + py/objexcept.c | 10 +++ py/runtime.c | 4 + tests/basics/sys_tracebacklimit.py | 78 +++++++++++++++++++ tests/run-tests.py | 1 + tests/unix/extra_coverage.py.exp | 4 +- 10 files changed, 117 insertions(+), 3 deletions(-) create mode 100644 tests/basics/sys_tracebacklimit.py diff --git a/docs/library/sys.rst b/docs/library/sys.rst index d36394c88c..f4ff8786a2 100644 --- a/docs/library/sys.rst +++ b/docs/library/sys.rst @@ -144,6 +144,14 @@ Constants Standard output `stream`. +.. data:: tracebacklimit + + A mutable attribute holding an integer value which is the maximum number of traceback + entries to store in an exception. Set to 0 to disable adding tracebacks. Defaults + to 1000. + + Note: this is not available on all ports. + .. data:: version Python language version that this implementation conforms to, as a string. diff --git a/ports/unix/variants/coverage/mpconfigvariant.h b/ports/unix/variants/coverage/mpconfigvariant.h index db01c4bcd5..9b6b407754 100644 --- a/ports/unix/variants/coverage/mpconfigvariant.h +++ b/ports/unix/variants/coverage/mpconfigvariant.h @@ -34,6 +34,7 @@ #define MICROPY_REPL_EMACS_WORDS_MOVE (1) #define MICROPY_REPL_EMACS_EXTRA_WORDS_MOVE (1) #define MICROPY_WARNINGS_CATEGORY (1) +#define MICROPY_MODULE_ATTR_DELEGATION (1) #define MICROPY_MODULE_GETATTR (1) #define MICROPY_PY_DELATTR_SETATTR (1) #define MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS (1) @@ -44,6 +45,7 @@ #define MICROPY_PY_BUILTINS_HELP (1) #define MICROPY_PY_BUILTINS_HELP_MODULES (1) #define MICROPY_PY_SYS_GETSIZEOF (1) +#define MICROPY_PY_SYS_TRACEBACKLIMIT (1) #define MICROPY_PY_MATH_CONSTANTS (1) #define MICROPY_PY_MATH_FACTORIAL (1) #define MICROPY_PY_URANDOM_EXTRA_FUNCS (1) diff --git a/py/modsys.c b/py/modsys.c index a05709f8e6..c44c7ed450 100644 --- a/py/modsys.c +++ b/py/modsys.c @@ -185,6 +185,9 @@ MP_DEFINE_CONST_FUN_OBJ_1(mp_sys_settrace_obj, mp_sys_settrace); #if MICROPY_PY_SYS_ATTR_DELEGATION STATIC const uint16_t sys_mutable_keys[] = { + #if MICROPY_PY_SYS_TRACEBACKLIMIT + MP_QSTR_tracebacklimit, + #endif MP_QSTRnull, }; diff --git a/py/mpconfig.h b/py/mpconfig.h index 617e897080..be967e6980 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -1377,10 +1377,15 @@ typedef double mp_float_t; #define MICROPY_PY_SYS_STDIO_BUFFER (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) #endif +// Whether to provide sys.tracebacklimit mutable attribute +#ifndef MICROPY_PY_SYS_TRACEBACKLIMIT +#define MICROPY_PY_SYS_TRACEBACKLIMIT (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) +#endif + // Whether the sys module supports attribute delegation // This is enabled automatically when needed by other features #ifndef MICROPY_PY_SYS_ATTR_DELEGATION -#define MICROPY_PY_SYS_ATTR_DELEGATION (0) +#define MICROPY_PY_SYS_ATTR_DELEGATION (MICROPY_PY_SYS_TRACEBACKLIMIT) #endif // Whether to provide "uerrno" module diff --git a/py/mpstate.h b/py/mpstate.h index f29e6be502..499d863512 100644 --- a/py/mpstate.h +++ b/py/mpstate.h @@ -41,6 +41,9 @@ // variable, but in the future it is hoped that the state can become local. enum { + #if MICROPY_PY_SYS_TRACEBACKLIMIT + MP_SYS_MUTABLE_TRACEBACKLIMIT, + #endif MP_SYS_MUTABLE_NUM, }; diff --git a/py/objexcept.c b/py/objexcept.c index 7a86c36471..dca287bb6e 100644 --- a/py/objexcept.c +++ b/py/objexcept.c @@ -575,6 +575,16 @@ void mp_obj_exception_add_traceback(mp_obj_t self_in, qstr file, size_t line, qs // append this traceback info to traceback data // if memory allocation fails (eg because gc is locked), just return + #if MICROPY_PY_SYS_TRACEBACKLIMIT + mp_int_t max_traceback = MP_OBJ_SMALL_INT_VALUE(MP_STATE_VM(sys_mutable[MP_SYS_MUTABLE_TRACEBACKLIMIT])); + if (max_traceback <= 0) { + return; + } else if (self->traceback_data != NULL && self->traceback_len >= max_traceback * TRACEBACK_ENTRY_LEN) { + self->traceback_len -= TRACEBACK_ENTRY_LEN; + memmove(self->traceback_data, self->traceback_data + TRACEBACK_ENTRY_LEN, self->traceback_len * sizeof(self->traceback_data[0])); + } + #endif + if (self->traceback_data == NULL) { self->traceback_data = m_new_maybe(size_t, TRACEBACK_ENTRY_LEN); if (self->traceback_data == NULL) { diff --git a/py/runtime.c b/py/runtime.c index 8c93f539e0..665c9f2206 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -141,6 +141,10 @@ void mp_init(void) { MP_STATE_THREAD(current_code_state) = NULL; #endif + #if MICROPY_PY_SYS_TRACEBACKLIMIT + MP_STATE_VM(sys_mutable[MP_SYS_MUTABLE_TRACEBACKLIMIT]) = MP_OBJ_NEW_SMALL_INT(1000); + #endif + #if MICROPY_PY_BLUETOOTH MP_STATE_VM(bluetooth) = MP_OBJ_NULL; #endif diff --git a/tests/basics/sys_tracebacklimit.py b/tests/basics/sys_tracebacklimit.py new file mode 100644 index 0000000000..1ee638967f --- /dev/null +++ b/tests/basics/sys_tracebacklimit.py @@ -0,0 +1,78 @@ +# test sys.tracebacklimit + +try: + try: + import usys as sys + import uio as io + except ImportError: + import sys + import io +except ImportError: + print("SKIP") + raise SystemExit + +try: + sys.tracebacklimit = 1000 +except AttributeError: + print("SKIP") + raise SystemExit + +if hasattr(sys, "print_exception"): + print_exception = sys.print_exception +else: + import traceback + + print_exception = lambda e, f: traceback.print_exception(None, e, sys.exc_info()[2], file=f) + + +def print_exc(e): + buf = io.StringIO() + print_exception(e, buf) + s = buf.getvalue() + for l in s.split("\n"): + # Remove filename. + if l.startswith(" File "): + l = l.split('"') + print(l[0], l[2]) + # uPy and CPy tracebacks differ in that CPy prints a source line for + # each traceback entry. In this case, we know that offending line + # has 4-space indent, so filter it out. + elif not l.startswith(" "): + print(l) + + +def f0(): + raise ValueError("value") + + +def f1(): + f0() + + +def f2(): + f1() + + +def f3(): + f2() + + +def ftop(): + try: + f3() + except ValueError as er: + print_exc(er) + + +ftop() + +for limit in range(4, -2, -1): + print("limit", limit) + sys.tracebacklimit = limit + ftop() + + +# test deleting the attribute +print(hasattr(sys, "tracebacklimit")) +del sys.tracebacklimit +print(hasattr(sys, "tracebacklimit")) diff --git a/tests/run-tests.py b/tests/run-tests.py index edd20b9bd5..dfe0a8e55f 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -504,6 +504,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): skip_tests.add("basics/del_local.py") # requires checking for unbound local skip_tests.add("basics/exception_chain.py") # raise from is not supported skip_tests.add("basics/scope_implicit.py") # requires checking for unbound local + skip_tests.add("basics/sys_tracebacklimit.py") # requires traceback info skip_tests.add("basics/try_finally_return2.py") # requires raise_varargs skip_tests.add("basics/unboundlocal.py") # requires checking for unbound local skip_tests.add("extmod/uasyncio_event.py") # unknown issue diff --git a/tests/unix/extra_coverage.py.exp b/tests/unix/extra_coverage.py.exp index 1a5a2cde85..67d299bca1 100644 --- a/tests/unix/extra_coverage.py.exp +++ b/tests/unix/extra_coverage.py.exp @@ -45,8 +45,8 @@ utime utimeq argv atexit byteorder exc_info exit getsizeof implementation maxsize modules path platform print_exception -stderr stdin stdout version -version_info +stderr stdin stdout tracebacklimit +version version_info ementation # attrtuple (start=1, stop=2, step=3) From ac2293161e98e73d39434628f995e85bd97e52c2 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 27 Jul 2021 00:43:35 +1000 Subject: [PATCH 209/619] py/modsys: Add optional mutable attributes sys.ps1/ps2 and use them. This allows customising the REPL prompt strings. Signed-off-by: Damien George --- docs/library/sys.rst | 6 +++++ ports/unix/main.c | 8 +++--- .../unix/variants/coverage/mpconfigvariant.h | 1 + py/modsys.c | 4 +++ py/mpconfig.h | 7 ++++- py/mpstate.h | 4 +++ py/qstrdefs.h | 4 +++ py/repl.c | 10 +++++++ py/repl.h | 26 +++++++++++++++++++ py/runtime.c | 5 ++++ shared/runtime/pyexec.c | 10 +++---- tests/cmdline/repl_sys_ps1_ps2.py | 6 +++++ tests/cmdline/repl_sys_ps1_ps2.py.exp | 10 +++++++ tests/run-tests.py | 1 + tests/unix/extra_coverage.py.exp | 4 +-- 15 files changed, 94 insertions(+), 12 deletions(-) create mode 100644 tests/cmdline/repl_sys_ps1_ps2.py create mode 100644 tests/cmdline/repl_sys_ps1_ps2.py.exp diff --git a/docs/library/sys.rst b/docs/library/sys.rst index f4ff8786a2..a2d55fecbb 100644 --- a/docs/library/sys.rst +++ b/docs/library/sys.rst @@ -132,6 +132,12 @@ Constants If you need to check whether your program runs on MicroPython (vs other Python implementation), use `sys.implementation` instead. +.. data:: ps1 + ps2 + + Mutable attributes holding strings, which are used for the REPL prompt. The defaults + give the standard Python prompt of ``>>>`` and ``...``. + .. data:: stderr Standard error `stream`. diff --git a/ports/unix/main.c b/ports/unix/main.c index bde867f8c8..c2a6c8c6b9 100644 --- a/ports/unix/main.c +++ b/ports/unix/main.c @@ -193,7 +193,7 @@ STATIC int do_repl(void) { input_restart: vstr_reset(&line); - int ret = readline(&line, ">>> "); + int ret = readline(&line, mp_repl_get_ps1()); mp_parse_input_kind_t parse_input_kind = MP_PARSE_SINGLE_INPUT; if (ret == CHAR_CTRL_C) { @@ -240,7 +240,7 @@ STATIC int do_repl(void) { // got a line with non-zero length, see if it needs continuing while (mp_repl_continue_with_input(vstr_null_terminated_str(&line))) { vstr_add_byte(&line, '\n'); - ret = readline(&line, "... "); + ret = readline(&line, mp_repl_get_ps2()); if (ret == CHAR_CTRL_C) { // cancel everything printf("\n"); @@ -265,13 +265,13 @@ STATIC int do_repl(void) { // use simple readline for (;;) { - char *line = prompt(">>> "); + char *line = prompt((char *)mp_repl_get_ps1()); if (line == NULL) { // EOF return 0; } while (mp_repl_continue_with_input(line)) { - char *line2 = prompt("... "); + char *line2 = prompt((char *)mp_repl_get_ps2()); if (line2 == NULL) { break; } diff --git a/ports/unix/variants/coverage/mpconfigvariant.h b/ports/unix/variants/coverage/mpconfigvariant.h index 9b6b407754..e2640f71f7 100644 --- a/ports/unix/variants/coverage/mpconfigvariant.h +++ b/ports/unix/variants/coverage/mpconfigvariant.h @@ -45,6 +45,7 @@ #define MICROPY_PY_BUILTINS_HELP (1) #define MICROPY_PY_BUILTINS_HELP_MODULES (1) #define MICROPY_PY_SYS_GETSIZEOF (1) +#define MICROPY_PY_SYS_PS1_PS2 (1) #define MICROPY_PY_SYS_TRACEBACKLIMIT (1) #define MICROPY_PY_MATH_CONSTANTS (1) #define MICROPY_PY_MATH_FACTORIAL (1) diff --git a/py/modsys.c b/py/modsys.c index c44c7ed450..ac90776226 100644 --- a/py/modsys.c +++ b/py/modsys.c @@ -185,6 +185,10 @@ MP_DEFINE_CONST_FUN_OBJ_1(mp_sys_settrace_obj, mp_sys_settrace); #if MICROPY_PY_SYS_ATTR_DELEGATION STATIC const uint16_t sys_mutable_keys[] = { + #if MICROPY_PY_SYS_PS1_PS2 + MP_QSTR_ps1, + MP_QSTR_ps2, + #endif #if MICROPY_PY_SYS_TRACEBACKLIMIT MP_QSTR_tracebacklimit, #endif diff --git a/py/mpconfig.h b/py/mpconfig.h index be967e6980..47c16ed967 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -1356,6 +1356,11 @@ typedef double mp_float_t; #define MICROPY_PY_SYS_ATEXIT (0) #endif +// Whether to provide sys.{ps1,ps2} mutable attributes, to control REPL prompts +#ifndef MICROPY_PY_SYS_PS1_PS2 +#define MICROPY_PY_SYS_PS1_PS2 (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + // Whether to provide "sys.settrace" function #ifndef MICROPY_PY_SYS_SETTRACE #define MICROPY_PY_SYS_SETTRACE (0) @@ -1385,7 +1390,7 @@ typedef double mp_float_t; // Whether the sys module supports attribute delegation // This is enabled automatically when needed by other features #ifndef MICROPY_PY_SYS_ATTR_DELEGATION -#define MICROPY_PY_SYS_ATTR_DELEGATION (MICROPY_PY_SYS_TRACEBACKLIMIT) +#define MICROPY_PY_SYS_ATTR_DELEGATION (MICROPY_PY_SYS_PS1_PS2 || MICROPY_PY_SYS_TRACEBACKLIMIT) #endif // Whether to provide "uerrno" module diff --git a/py/mpstate.h b/py/mpstate.h index 499d863512..a493b780ab 100644 --- a/py/mpstate.h +++ b/py/mpstate.h @@ -41,6 +41,10 @@ // variable, but in the future it is hoped that the state can become local. enum { + #if MICROPY_PY_SYS_PS1_PS2 + MP_SYS_MUTABLE_PS1, + MP_SYS_MUTABLE_PS2, + #endif #if MICROPY_PY_SYS_TRACEBACKLIMIT MP_SYS_MUTABLE_TRACEBACKLIMIT, #endif diff --git a/py/qstrdefs.h b/py/qstrdefs.h index 405813941b..5003636df3 100644 --- a/py/qstrdefs.h +++ b/py/qstrdefs.h @@ -39,6 +39,10 @@ Q() Q(*) Q(_) Q(/) +#if MICROPY_PY_SYS_PS1_PS2 +Q(>>> ) +Q(... ) +#endif #if MICROPY_PY_BUILTINS_STR_OP_MODULO Q(%#o) Q(%#x) diff --git a/py/repl.c b/py/repl.c index 822e385abd..4e47cf784c 100644 --- a/py/repl.c +++ b/py/repl.c @@ -33,6 +33,16 @@ #if MICROPY_HELPER_REPL +#if MICROPY_PY_SYS_PS1_PS2 +const char *mp_repl_get_psx(unsigned int entry) { + if (mp_obj_is_str(MP_STATE_VM(sys_mutable)[entry])) { + return mp_obj_str_get_str(MP_STATE_VM(sys_mutable)[entry]); + } else { + return ""; + } +} +#endif + STATIC bool str_startswith_word(const char *str, const char *head) { size_t i; for (i = 0; str[i] && head[i]; i++) { diff --git a/py/repl.h b/py/repl.h index a7a4136cad..9e8f7f1dda 100644 --- a/py/repl.h +++ b/py/repl.h @@ -31,8 +31,34 @@ #include "py/mpprint.h" #if MICROPY_HELPER_REPL + +#if MICROPY_PY_SYS_PS1_PS2 + +const char *mp_repl_get_psx(unsigned int entry); + +static inline const char *mp_repl_get_ps1(void) { + return mp_repl_get_psx(MP_SYS_MUTABLE_PS1); +} + +static inline const char *mp_repl_get_ps2(void) { + return mp_repl_get_psx(MP_SYS_MUTABLE_PS2); +} + +#else + +static inline const char *mp_repl_get_ps1(void) { + return ">>> "; +} + +static inline const char *mp_repl_get_ps2(void) { + return "... "; +} + +#endif + bool mp_repl_continue_with_input(const char *input); size_t mp_repl_autocomplete(const char *str, size_t len, const mp_print_t *print, const char **compl_str); + #endif #endif // MICROPY_INCLUDED_PY_REPL_H diff --git a/py/runtime.c b/py/runtime.c index 665c9f2206..ba3fbe7fa5 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -135,6 +135,11 @@ void mp_init(void) { MP_STATE_VM(sys_exitfunc) = mp_const_none; #endif + #if MICROPY_PY_SYS_PS1_PS2 + MP_STATE_VM(sys_mutable[MP_SYS_MUTABLE_PS1]) = MP_OBJ_NEW_QSTR(MP_QSTR__gt__gt__gt__space_); + MP_STATE_VM(sys_mutable[MP_SYS_MUTABLE_PS2]) = MP_OBJ_NEW_QSTR(MP_QSTR__dot__dot__dot__space_); + #endif + #if MICROPY_PY_SYS_SETTRACE MP_STATE_THREAD(prof_trace_callback) = MP_OBJ_NULL; MP_STATE_THREAD(prof_callback_is_executing) = false; diff --git a/shared/runtime/pyexec.c b/shared/runtime/pyexec.c index ae6dd770b1..9fde987a4b 100644 --- a/shared/runtime/pyexec.c +++ b/shared/runtime/pyexec.c @@ -433,7 +433,7 @@ STATIC int pyexec_friendly_repl_process_char(int c) { vstr_add_byte(MP_STATE_VM(repl_line), '\n'); repl.cont_line = true; - readline_note_newline("... "); + readline_note_newline(mp_repl_get_ps2()); return 0; } else { @@ -454,7 +454,7 @@ STATIC int pyexec_friendly_repl_process_char(int c) { if (mp_repl_continue_with_input(vstr_null_terminated_str(MP_STATE_VM(repl_line)))) { vstr_add_byte(MP_STATE_VM(repl_line), '\n'); - readline_note_newline("... "); + readline_note_newline(mp_repl_get_ps2()); return 0; } @@ -468,7 +468,7 @@ STATIC int pyexec_friendly_repl_process_char(int c) { vstr_reset(MP_STATE_VM(repl_line)); repl.cont_line = false; repl.paste_mode = false; - readline_init(MP_STATE_VM(repl_line), ">>> "); + readline_init(MP_STATE_VM(repl_line), mp_repl_get_ps1()); return 0; } } @@ -598,7 +598,7 @@ friendly_repl_reset: } vstr_reset(&line); - int ret = readline(&line, ">>> "); + int ret = readline(&line, mp_repl_get_ps1()); mp_parse_input_kind_t parse_input_kind = MP_PARSE_SINGLE_INPUT; if (ret == CHAR_CTRL_A) { @@ -651,7 +651,7 @@ friendly_repl_reset: // got a line with non-zero length, see if it needs continuing while (mp_repl_continue_with_input(vstr_null_terminated_str(&line))) { vstr_add_byte(&line, '\n'); - ret = readline(&line, "... "); + ret = readline(&line, mp_repl_get_ps2()); if (ret == CHAR_CTRL_C) { // cancel everything mp_hal_stdout_tx_str("\r\n"); diff --git a/tests/cmdline/repl_sys_ps1_ps2.py b/tests/cmdline/repl_sys_ps1_ps2.py new file mode 100644 index 0000000000..4f96057c49 --- /dev/null +++ b/tests/cmdline/repl_sys_ps1_ps2.py @@ -0,0 +1,6 @@ +# test changing ps1/ps2 +import usys +usys.ps1 = "PS1" +usys.ps2 = "PS2" +(1 + +2) diff --git a/tests/cmdline/repl_sys_ps1_ps2.py.exp b/tests/cmdline/repl_sys_ps1_ps2.py.exp new file mode 100644 index 0000000000..e4a802d34d --- /dev/null +++ b/tests/cmdline/repl_sys_ps1_ps2.py.exp @@ -0,0 +1,10 @@ +MicroPython \.\+ version +Use \.\+ +>>> # test changing ps1/ps2 +>>> import usys +>>> usys.ps1 = "PS1" +PS1usys.ps2 = "PS2" +PS1(1 + +PS22) +3 +PS1 diff --git a/tests/run-tests.py b/tests/run-tests.py index dfe0a8e55f..9c298dae31 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -433,6 +433,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): if not has_coverage: skip_tests.add("cmdline/cmd_parsetree.py") + skip_tests.add("cmdline/repl_sys_ps1_ps2.py") # Some tests shouldn't be run on a PC if args.target == "unix": diff --git a/tests/unix/extra_coverage.py.exp b/tests/unix/extra_coverage.py.exp index 67d299bca1..f6681f4ac1 100644 --- a/tests/unix/extra_coverage.py.exp +++ b/tests/unix/extra_coverage.py.exp @@ -45,8 +45,8 @@ utime utimeq argv atexit byteorder exc_info exit getsizeof implementation maxsize modules path platform print_exception -stderr stdin stdout tracebacklimit -version version_info +ps1 ps2 stderr stdin +stdout tracebacklimit version version_info ementation # attrtuple (start=1, stop=2, step=3) From 0ac3191d8c3be9d043af3d75fd5264f8b2f995a1 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 10 Mar 2022 13:20:50 +1100 Subject: [PATCH 210/619] unix/mpconfigport.h: Collect together config options from extra level. This change is a no-op in terms of functionality. Signed-off-by: Damien George --- ports/unix/mpconfigport.h | 132 ++++++++++++++++++++------------------ 1 file changed, 68 insertions(+), 64 deletions(-) diff --git a/ports/unix/mpconfigport.h b/ports/unix/mpconfigport.h index f96abb9420..9987630382 100644 --- a/ports/unix/mpconfigport.h +++ b/ports/unix/mpconfigport.h @@ -34,6 +34,74 @@ // If we're building the minimal variant, ignore the rest of this file. #ifndef MICROPY_UNIX_MINIMAL +// If the variant did not set a feature level then configure a set of features. +#ifndef MICROPY_CONFIG_ROM_LEVEL +#define MICROPY_COMP_MODULE_CONST (1) +#define MICROPY_COMP_TRIPLE_TUPLE_ASSIGN (1) +#define MICROPY_COMP_RETURN_IF_EXPR (1) +#ifndef MICROPY_OPT_LOAD_ATTR_FAST_PATH +#define MICROPY_OPT_LOAD_ATTR_FAST_PATH (1) +#endif +#ifndef MICROPY_OPT_MAP_LOOKUP_CACHE +#define MICROPY_OPT_MAP_LOOKUP_CACHE (1) +#endif +#define MICROPY_ENABLE_FINALISER (1) +#define MICROPY_STACK_CHECK (1) +#define MICROPY_KBD_EXCEPTION (1) +#define MICROPY_HELPER_REPL (1) +#define MICROPY_REPL_EMACS_KEYS (1) +#define MICROPY_REPL_AUTO_INDENT (1) +#define MICROPY_ENABLE_SOURCE_LINE (1) +#ifndef MICROPY_STREAMS_NON_BLOCK +#define MICROPY_STREAMS_NON_BLOCK (1) +#endif +#define MICROPY_MODULE_WEAK_LINKS (1) +#define MICROPY_CAN_OVERRIDE_BUILTINS (1) +#define MICROPY_PY_FUNCTION_ATTRS (1) +#define MICROPY_PY_DESCRIPTORS (1) +#define MICROPY_PY_DELATTR_SETATTR (1) +#define MICROPY_PY_FSTRINGS (1) +#define MICROPY_PY_BUILTINS_STR_UNICODE (1) +#define MICROPY_PY_BUILTINS_STR_CENTER (1) +#define MICROPY_PY_BUILTINS_STR_PARTITION (1) +#define MICROPY_PY_BUILTINS_STR_SPLITLINES (1) +#define MICROPY_PY_BUILTINS_MEMORYVIEW (1) +#define MICROPY_PY_BUILTINS_SLICE_ATTRS (1) +#define MICROPY_PY_BUILTINS_SLICE_INDICES (1) +#define MICROPY_PY_BUILTINS_FROZENSET (1) +#define MICROPY_PY_BUILTINS_ROUND_INT (1) +#define MICROPY_PY_ALL_SPECIAL_METHODS (1) +#define MICROPY_PY_REVERSE_SPECIAL_METHODS (1) +#define MICROPY_PY_BUILTINS_COMPILE (1) +#define MICROPY_PY_BUILTINS_NOTIMPLEMENTED (1) +#define MICROPY_PY_BUILTINS_INPUT (1) +#define MICROPY_PY_BUILTINS_POW3 (1) +#define MICROPY_PY_MICROPYTHON_MEM_INFO (1) +#define MICROPY_PY_ARRAY_SLICE_ASSIGN (1) +#define MICROPY_PY_COLLECTIONS_DEQUE (1) +#define MICROPY_PY_COLLECTIONS_ORDEREDDICT (1) +#ifndef MICROPY_PY_MATH_SPECIAL_FUNCTIONS +#define MICROPY_PY_MATH_SPECIAL_FUNCTIONS (1) +#endif +#define MICROPY_PY_MATH_ISCLOSE (MICROPY_PY_MATH_SPECIAL_FUNCTIONS) +#define MICROPY_PY_CMATH (1) +#define MICROPY_PY_IO_IOBASE (1) +#define MICROPY_PY_IO_FILEIO (1) +#define MICROPY_PY_SYS_MAXSIZE (1) +#define MICROPY_PY_SYS_STDFILES (1) +#define MICROPY_PY_UERRNO (1) +#define MICROPY_PY_UCTYPES (1) +#define MICROPY_PY_UZLIB (1) +#define MICROPY_PY_UJSON (1) +#define MICROPY_PY_UOS (1) +#define MICROPY_PY_URE (1) +#define MICROPY_PY_UHEAPQ (1) +#define MICROPY_PY_UHASHLIB (1) +#define MICROPY_PY_UBINASCII (1) +#define MICROPY_PY_UBINASCII_CRC32 (1) +#define MICROPY_PY_URANDOM (1) +#endif + #define MICROPY_ALLOC_PATH_MAX (PATH_MAX) #define MICROPY_PERSISTENT_CODE_LOAD (1) #if !defined(MICROPY_EMIT_X64) && defined(__x86_64__) @@ -51,12 +119,7 @@ #if !defined(MICROPY_EMIT_ARM) && defined(__arm__) && !defined(__thumb2__) #define MICROPY_EMIT_ARM (1) #endif -#define MICROPY_COMP_MODULE_CONST (1) -#define MICROPY_COMP_TRIPLE_TUPLE_ASSIGN (1) -#define MICROPY_COMP_RETURN_IF_EXPR (1) #define MICROPY_ENABLE_GC (1) -#define MICROPY_ENABLE_FINALISER (1) -#define MICROPY_STACK_CHECK (1) #define MICROPY_MALLOC_USES_ALLOCATED_SIZE (1) #define MICROPY_MEM_STATS (1) #define MICROPY_DEBUG_PRINTERS (1) @@ -66,52 +129,16 @@ #define MICROPY_READER_POSIX (1) #define MICROPY_READER_VFS (1) #define MICROPY_USE_READLINE_HISTORY (1) -#define MICROPY_HELPER_REPL (1) -#define MICROPY_REPL_EMACS_KEYS (1) -#define MICROPY_REPL_AUTO_INDENT (1) #define MICROPY_HELPER_LEXER_UNIX (1) -#define MICROPY_ENABLE_SOURCE_LINE (1) #ifndef MICROPY_FLOAT_IMPL #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_DOUBLE) #endif #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) -#ifndef MICROPY_STREAMS_NON_BLOCK -#define MICROPY_STREAMS_NON_BLOCK (1) -#endif #define MICROPY_STREAMS_POSIX_API (1) #define MICROPY_OPT_COMPUTED_GOTO (1) -#ifndef MICROPY_OPT_LOAD_ATTR_FAST_PATH -#define MICROPY_OPT_LOAD_ATTR_FAST_PATH (1) -#endif -#ifndef MICROPY_OPT_MAP_LOOKUP_CACHE -#define MICROPY_OPT_MAP_LOOKUP_CACHE (1) -#endif -#define MICROPY_MODULE_WEAK_LINKS (1) #define MICROPY_MODULE_OVERRIDE_MAIN_IMPORT (1) -#define MICROPY_CAN_OVERRIDE_BUILTINS (1) #define MICROPY_VFS (1) #define MICROPY_VFS_POSIX (1) -#define MICROPY_PY_FUNCTION_ATTRS (1) -#define MICROPY_PY_DESCRIPTORS (1) -#define MICROPY_PY_DELATTR_SETATTR (1) -#define MICROPY_PY_FSTRINGS (1) -#define MICROPY_PY_BUILTINS_STR_UNICODE (1) -#define MICROPY_PY_BUILTINS_STR_CENTER (1) -#define MICROPY_PY_BUILTINS_STR_PARTITION (1) -#define MICROPY_PY_BUILTINS_STR_SPLITLINES (1) -#define MICROPY_PY_BUILTINS_MEMORYVIEW (1) -#define MICROPY_PY_BUILTINS_FROZENSET (1) -#define MICROPY_PY_BUILTINS_COMPILE (1) -#define MICROPY_PY_BUILTINS_NOTIMPLEMENTED (1) -#define MICROPY_PY_BUILTINS_INPUT (1) -#define MICROPY_PY_BUILTINS_POW3 (1) -#define MICROPY_PY_BUILTINS_ROUND_INT (1) -#define MICROPY_PY_MICROPYTHON_MEM_INFO (1) -#define MICROPY_PY_ALL_SPECIAL_METHODS (1) -#define MICROPY_PY_REVERSE_SPECIAL_METHODS (1) -#define MICROPY_PY_ARRAY_SLICE_ASSIGN (1) -#define MICROPY_PY_BUILTINS_SLICE_ATTRS (1) -#define MICROPY_PY_BUILTINS_SLICE_INDICES (1) #define MICROPY_PY_SYS_PATH_ARGV_DEFAULTS (0) #define MICROPY_PY_SYS_EXIT (1) #define MICROPY_PY_SYS_ATEXIT (1) @@ -129,18 +156,7 @@ #ifndef MICROPY_PY_SYS_PATH_DEFAULT #define MICROPY_PY_SYS_PATH_DEFAULT ".frozen:~/.micropython/lib:/usr/lib/micropython" #endif -#define MICROPY_PY_SYS_MAXSIZE (1) -#define MICROPY_PY_SYS_STDFILES (1) #define MICROPY_PY_SYS_EXC_INFO (1) -#define MICROPY_PY_COLLECTIONS_DEQUE (1) -#define MICROPY_PY_COLLECTIONS_ORDEREDDICT (1) -#ifndef MICROPY_PY_MATH_SPECIAL_FUNCTIONS -#define MICROPY_PY_MATH_SPECIAL_FUNCTIONS (1) -#endif -#define MICROPY_PY_MATH_ISCLOSE (MICROPY_PY_MATH_SPECIAL_FUNCTIONS) -#define MICROPY_PY_CMATH (1) -#define MICROPY_PY_IO_IOBASE (1) -#define MICROPY_PY_IO_FILEIO (1) #define MICROPY_PY_GC_COLLECT_RETVAL (1) #ifndef MICROPY_STACKLESS @@ -148,7 +164,6 @@ #define MICROPY_STACKLESS_STRICT (0) #endif -#define MICROPY_PY_UOS (1) #define MICROPY_PY_UOS_INCLUDEFILE "ports/unix/moduos.c" #define MICROPY_PY_UOS_ERRNO (1) #define MICROPY_PY_UOS_GETENV_PUTENV_UNSETENV (1) @@ -157,22 +172,12 @@ #define MICROPY_PY_UOS_URANDOM (1) #define MICROPY_PY_UTIME (1) #define MICROPY_PY_UTIME_MP_HAL (1) -#define MICROPY_PY_UERRNO (1) -#define MICROPY_PY_UCTYPES (1) -#define MICROPY_PY_UZLIB (1) -#define MICROPY_PY_UJSON (1) -#define MICROPY_PY_URE (1) -#define MICROPY_PY_UHEAPQ (1) #define MICROPY_PY_UTIMEQ (1) -#define MICROPY_PY_UHASHLIB (1) #if MICROPY_PY_USSL #define MICROPY_PY_UHASHLIB_MD5 (1) #define MICROPY_PY_UHASHLIB_SHA1 (1) #define MICROPY_PY_UCRYPTOLIB (1) #endif -#define MICROPY_PY_UBINASCII (1) -#define MICROPY_PY_UBINASCII_CRC32 (1) -#define MICROPY_PY_URANDOM (1) #ifndef MICROPY_PY_USELECT_POSIX #define MICROPY_PY_USELECT_POSIX (1) #endif @@ -206,7 +211,6 @@ extern const struct _mp_print_t mp_stderr_print; #define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1) #define MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE (256) -#define MICROPY_KBD_EXCEPTION (1) #define MICROPY_ASYNC_KBD_INTR (1) #define mp_import_stat mp_vfs_import_stat From 3c20ddb41adc177442b68a8fdff221c80e0b0b5e Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 10 Mar 2022 13:21:11 +1100 Subject: [PATCH 211/619] unix/variants: Use rom feature config for standard, dev, coverage. This change is a no-op in terms of functionality. Signed-off-by: Damien George --- .../unix/variants/coverage/mpconfigvariant.h | 26 ++++++++---------- ports/unix/variants/dev/mpconfigvariant.h | 27 ++++++++++++------- .../unix/variants/standard/mpconfigvariant.h | 21 +++++++++++++-- 3 files changed, 47 insertions(+), 27 deletions(-) diff --git a/ports/unix/variants/coverage/mpconfigvariant.h b/ports/unix/variants/coverage/mpconfigvariant.h index e2640f71f7..14f6b8331e 100644 --- a/ports/unix/variants/coverage/mpconfigvariant.h +++ b/ports/unix/variants/coverage/mpconfigvariant.h @@ -27,36 +27,32 @@ // This config enables almost all possible features such that it can be used // for coverage testing. +// Set base feature level. +#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES) + +// Disable some features that come enabled by default with the feature level. +#define MICROPY_OPT_MPZ_BITWISE (0) +#define MICROPY_MODULE_BUILTIN_INIT (0) +#define MICROPY_PY_BUILTINS_EXECFILE (0) +#define MICROPY_PY_SYS_STDIO_BUFFER (0) +#define MICROPY_PY_USELECT (0) + +// Enable additional features. #define MICROPY_DEBUG_PARSE_RULE_NAME (1) -#define MICROPY_OPT_MATH_FACTORIAL (1) #define MICROPY_FLOAT_HIGH_QUALITY_HASH (1) -#define MICROPY_ENABLE_SCHEDULER (1) #define MICROPY_REPL_EMACS_WORDS_MOVE (1) #define MICROPY_REPL_EMACS_EXTRA_WORDS_MOVE (1) #define MICROPY_WARNINGS_CATEGORY (1) -#define MICROPY_MODULE_ATTR_DELEGATION (1) -#define MICROPY_MODULE_GETATTR (1) -#define MICROPY_PY_DELATTR_SETATTR (1) #define MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS (1) -#define MICROPY_PY_REVERSE_SPECIAL_METHODS (1) #define MICROPY_PY_BUILTINS_MEMORYVIEW_ITEMSIZE (1) #define MICROPY_PY_BUILTINS_NEXT2 (1) #define MICROPY_PY_BUILTINS_RANGE_BINOP (1) -#define MICROPY_PY_BUILTINS_HELP (1) -#define MICROPY_PY_BUILTINS_HELP_MODULES (1) #define MICROPY_PY_SYS_GETSIZEOF (1) -#define MICROPY_PY_SYS_PS1_PS2 (1) #define MICROPY_PY_SYS_TRACEBACKLIMIT (1) -#define MICROPY_PY_MATH_CONSTANTS (1) -#define MICROPY_PY_MATH_FACTORIAL (1) -#define MICROPY_PY_URANDOM_EXTRA_FUNCS (1) #define MICROPY_PY_IO_BUFFEREDWRITER (1) -#define MICROPY_PY_UASYNCIO (1) #define MICROPY_PY_URE_DEBUG (1) #define MICROPY_PY_URE_MATCH_GROUPS (1) #define MICROPY_PY_URE_MATCH_SPAN_START_END (1) -#define MICROPY_PY_URE_SUB (1) -#define MICROPY_PY_FRAMEBUF (1) #define MICROPY_PY_COLLECTIONS_NAMEDTUPLE__ASDICT (1) #define MICROPY_PY_UCRYPTOLIB (1) #define MICROPY_PY_UCRYPTOLIB_CTR (1) diff --git a/ports/unix/variants/dev/mpconfigvariant.h b/ports/unix/variants/dev/mpconfigvariant.h index 280243e344..16799322ff 100644 --- a/ports/unix/variants/dev/mpconfigvariant.h +++ b/ports/unix/variants/dev/mpconfigvariant.h @@ -24,16 +24,23 @@ * THE SOFTWARE. */ +// Set base feature level. +#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES) + +// Disable some features that come enabled by default with the feature level. +#define MICROPY_OPT_MPZ_BITWISE (0) +#define MICROPY_OPT_MATH_FACTORIAL (0) +#define MICROPY_MODULE_ATTR_DELEGATION (0) +#define MICROPY_MODULE_BUILTIN_INIT (0) +#define MICROPY_PY_BUILTINS_EXECFILE (0) +#define MICROPY_PY_MATH_FACTORIAL (0) +#define MICROPY_PY_SYS_PS1_PS2 (0) +#define MICROPY_PY_SYS_STDIO_BUFFER (0) +#define MICROPY_PY_USELECT (0) +#define MICROPY_PY_URE_SUB (0) +#define MICROPY_PY_FRAMEBUF (0) + +// Enable some additional features. #define MICROPY_REPL_EMACS_WORDS_MOVE (1) #define MICROPY_REPL_EMACS_EXTRA_WORDS_MOVE (1) -#define MICROPY_ENABLE_SCHEDULER (1) - -#define MICROPY_PY_BUILTINS_HELP (1) -#define MICROPY_PY_BUILTINS_HELP_MODULES (1) -#define MICROPY_PY_MATH_CONSTANTS (1) #define MICROPY_PY_SYS_SETTRACE (1) -#define MICROPY_PY_URANDOM_EXTRA_FUNCS (1) - -#ifndef MICROPY_PY_UASYNCIO -#define MICROPY_PY_UASYNCIO (1) -#endif diff --git a/ports/unix/variants/standard/mpconfigvariant.h b/ports/unix/variants/standard/mpconfigvariant.h index 3cdcfa8e9b..1ec46ef92d 100644 --- a/ports/unix/variants/standard/mpconfigvariant.h +++ b/ports/unix/variants/standard/mpconfigvariant.h @@ -24,5 +24,22 @@ * THE SOFTWARE. */ -#define MICROPY_PY_BUILTINS_HELP (1) -#define MICROPY_PY_BUILTINS_HELP_MODULES (1) +// Set base feature level. +#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES) + +// Disable some features that come enabled by default with the feature level. +#define MICROPY_OPT_MPZ_BITWISE (0) +#define MICROPY_OPT_MATH_FACTORIAL (0) +#define MICROPY_MODULE_ATTR_DELEGATION (0) +#define MICROPY_MODULE_BUILTIN_INIT (0) +#define MICROPY_ENABLE_SCHEDULER (0) +#define MICROPY_PY_BUILTINS_EXECFILE (0) +#define MICROPY_PY_MATH_CONSTANTS (0) +#define MICROPY_PY_MATH_FACTORIAL (0) +#define MICROPY_PY_SYS_PS1_PS2 (0) +#define MICROPY_PY_SYS_STDIO_BUFFER (0) +#define MICROPY_PY_USELECT (0) +#define MICROPY_PY_UASYNCIO (0) +#define MICROPY_PY_URE_SUB (0) +#define MICROPY_PY_URANDOM_EXTRA_FUNCS (0) +#define MICROPY_PY_FRAMEBUF (0) From eec07332b127587a62f6555644f069620afa6091 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 10 Mar 2022 13:34:43 +1100 Subject: [PATCH 212/619] unix/variants: Enable a few optimisations and features on dev, coverage. Signed-off-by: Damien George --- ports/unix/variants/coverage/mpconfigvariant.h | 1 - ports/unix/variants/dev/mpconfigvariant.h | 7 ------- 2 files changed, 8 deletions(-) diff --git a/ports/unix/variants/coverage/mpconfigvariant.h b/ports/unix/variants/coverage/mpconfigvariant.h index 14f6b8331e..2e36355ca6 100644 --- a/ports/unix/variants/coverage/mpconfigvariant.h +++ b/ports/unix/variants/coverage/mpconfigvariant.h @@ -31,7 +31,6 @@ #define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES) // Disable some features that come enabled by default with the feature level. -#define MICROPY_OPT_MPZ_BITWISE (0) #define MICROPY_MODULE_BUILTIN_INIT (0) #define MICROPY_PY_BUILTINS_EXECFILE (0) #define MICROPY_PY_SYS_STDIO_BUFFER (0) diff --git a/ports/unix/variants/dev/mpconfigvariant.h b/ports/unix/variants/dev/mpconfigvariant.h index 16799322ff..87e89dc2a1 100644 --- a/ports/unix/variants/dev/mpconfigvariant.h +++ b/ports/unix/variants/dev/mpconfigvariant.h @@ -28,17 +28,10 @@ #define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES) // Disable some features that come enabled by default with the feature level. -#define MICROPY_OPT_MPZ_BITWISE (0) -#define MICROPY_OPT_MATH_FACTORIAL (0) -#define MICROPY_MODULE_ATTR_DELEGATION (0) #define MICROPY_MODULE_BUILTIN_INIT (0) #define MICROPY_PY_BUILTINS_EXECFILE (0) -#define MICROPY_PY_MATH_FACTORIAL (0) -#define MICROPY_PY_SYS_PS1_PS2 (0) #define MICROPY_PY_SYS_STDIO_BUFFER (0) #define MICROPY_PY_USELECT (0) -#define MICROPY_PY_URE_SUB (0) -#define MICROPY_PY_FRAMEBUF (0) // Enable some additional features. #define MICROPY_REPL_EMACS_WORDS_MOVE (1) From 65851ebb5126d4940e91c9f2c1d13b4e10753579 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 16 Mar 2022 00:00:25 +1100 Subject: [PATCH 213/619] py/parse: Simplify handling of const int parse nodes. Signed-off-by: Damien George --- py/parse.c | 42 +++++++++++++++++------------------------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/py/parse.c b/py/parse.c index 68c761734a..ff639122cb 100644 --- a/py/parse.c +++ b/py/parse.c @@ -463,16 +463,21 @@ STATIC mp_parse_node_t make_node_const_object(parser_t *parser, size_t src_line, return (mp_parse_node_t)pn; } -STATIC mp_parse_node_t mp_parse_node_new_small_int_checked(parser_t *parser, mp_obj_t o_val) { - (void)parser; - mp_int_t val = MP_OBJ_SMALL_INT_VALUE(o_val); - #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D - // A parse node is only 32-bits and the small-int value must fit in 31-bits - if (((val ^ (val << 1)) & 0xffffffff80000000) != 0) { - return make_node_const_object(parser, 0, o_val); +// Create a parse node represeting a constant integer value, possibly optimising +// it by putting the (small) integer value directly in the parse node itself. +STATIC mp_parse_node_t make_node_const_int(parser_t *parser, size_t src_line, mp_obj_t obj) { + if (mp_obj_is_small_int(obj)) { + mp_int_t val = MP_OBJ_SMALL_INT_VALUE(obj); + #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D + // A parse node is only 32-bits and the small-int value must fit in 31-bits + if (((val ^ (val << 1)) & 0xffffffff80000000) != 0) { + return make_node_const_object(parser, src_line, obj); + } + #endif + return mp_parse_node_new_small_int(val); + } else { + return make_node_const_object(parser, src_line, obj); } - #endif - return mp_parse_node_new_small_int(val); } STATIC void push_result_token(parser_t *parser, uint8_t rule_id) { @@ -485,11 +490,7 @@ STATIC void push_result_token(parser_t *parser, uint8_t rule_id) { mp_map_elem_t *elem; if (rule_id == RULE_atom && (elem = mp_map_lookup(&parser->consts, MP_OBJ_NEW_QSTR(id), MP_MAP_LOOKUP)) != NULL) { - if (mp_obj_is_small_int(elem->value)) { - pn = mp_parse_node_new_small_int_checked(parser, elem->value); - } else { - pn = make_node_const_object(parser, lex->tok_line, elem->value); - } + pn = make_node_const_int(parser, lex->tok_line, elem->value); } else { pn = mp_parse_node_new_leaf(MP_PARSE_NODE_ID, id); } @@ -499,11 +500,7 @@ STATIC void push_result_token(parser_t *parser, uint8_t rule_id) { #endif } else if (lex->tok_kind == MP_TOKEN_INTEGER) { mp_obj_t o = mp_parse_num_integer(lex->vstr.buf, lex->vstr.len, 0, lex); - if (mp_obj_is_small_int(o)) { - pn = mp_parse_node_new_small_int_checked(parser, o); - } else { - pn = make_node_const_object(parser, lex->tok_line, o); - } + pn = make_node_const_int(parser, lex->tok_line, o); } else if (lex->tok_kind == MP_TOKEN_FLOAT_OR_IMAG) { mp_obj_t o = mp_parse_num_decimal(lex->vstr.buf, lex->vstr.len, true, false, lex); pn = make_node_const_object(parser, lex->tok_line, o); @@ -784,12 +781,7 @@ STATIC bool fold_constants(parser_t *parser, uint8_t rule_id, size_t num_args) { for (size_t i = num_args; i > 0; i--) { pop_result(parser); } - if (mp_obj_is_small_int(arg0)) { - push_result_node(parser, mp_parse_node_new_small_int_checked(parser, arg0)); - } else { - // TODO reuse memory for parse node struct? - push_result_node(parser, make_node_const_object(parser, 0, arg0)); - } + push_result_node(parser, make_node_const_int(parser, 0, arg0)); return true; } From 3c7cab4e98a31649ed1bc2e728d610856382d6f5 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 16 Mar 2022 00:22:58 +1100 Subject: [PATCH 214/619] py/parse: Put const bytes objects in parse tree as const object. Instead of as an intermediate qstr, which may unnecessarily intern the data of the bytes object. Signed-off-by: Damien George --- py/compile.c | 10 ---------- py/parse.c | 19 +++++++++---------- py/parse.h | 6 ++---- tests/cmdline/cmd_parsetree.py.exp | 2 +- 4 files changed, 12 insertions(+), 25 deletions(-) diff --git a/py/compile.c b/py/compile.c index 92736e22e5..f9e8de4573 100644 --- a/py/compile.c +++ b/py/compile.c @@ -2816,16 +2816,6 @@ STATIC void compile_node(compiler_t *comp, mp_parse_node_t pn) { case MP_PARSE_NODE_STRING: EMIT_ARG(load_const_str, arg); break; - case MP_PARSE_NODE_BYTES: - // only create and load the actual bytes object on the last pass - if (comp->pass != MP_PASS_EMIT) { - EMIT_ARG(load_const_obj, mp_const_none); - } else { - size_t len; - const byte *data = qstr_data(arg, &len); - EMIT_ARG(load_const_obj, mp_obj_new_bytes(data, len)); - } - break; case MP_PARSE_NODE_TOKEN: default: if (arg == MP_TOKEN_NEWLINE) { diff --git a/py/parse.c b/py/parse.c index ff639122cb..f18f03b3f1 100644 --- a/py/parse.c +++ b/py/parse.c @@ -388,9 +388,6 @@ void mp_parse_node_print(const mp_print_t *print, mp_parse_node_t pn, size_t ind case MP_PARSE_NODE_STRING: mp_printf(print, "str(%s)\n", qstr_str(arg)); break; - case MP_PARSE_NODE_BYTES: - mp_printf(print, "bytes(%s)\n", qstr_str(arg)); - break; default: assert(MP_PARSE_NODE_LEAF_KIND(pn) == MP_PARSE_NODE_TOKEN); mp_printf(print, "tok(%u)\n", (uint)arg); @@ -504,8 +501,8 @@ STATIC void push_result_token(parser_t *parser, uint8_t rule_id) { } else if (lex->tok_kind == MP_TOKEN_FLOAT_OR_IMAG) { mp_obj_t o = mp_parse_num_decimal(lex->vstr.buf, lex->vstr.len, true, false, lex); pn = make_node_const_object(parser, lex->tok_line, o); - } else if (lex->tok_kind == MP_TOKEN_STRING || lex->tok_kind == MP_TOKEN_BYTES) { - // Don't automatically intern all strings/bytes. doc strings (which are usually large) + } else if (lex->tok_kind == MP_TOKEN_STRING) { + // Don't automatically intern all strings. Doc strings (which are usually large) // will be discarded by the compiler, and so we shouldn't intern them. qstr qst = MP_QSTRnull; if (lex->vstr.len <= MICROPY_ALLOC_PARSE_INTERN_STRING_LEN) { @@ -517,14 +514,16 @@ STATIC void push_result_token(parser_t *parser, uint8_t rule_id) { } if (qst != MP_QSTRnull) { // qstr exists, make a leaf node - pn = mp_parse_node_new_leaf(lex->tok_kind == MP_TOKEN_STRING ? MP_PARSE_NODE_STRING : MP_PARSE_NODE_BYTES, qst); + pn = mp_parse_node_new_leaf(MP_PARSE_NODE_STRING, qst); } else { - // not interned, make a node holding a pointer to the string/bytes object - mp_obj_t o = mp_obj_new_str_copy( - lex->tok_kind == MP_TOKEN_STRING ? &mp_type_str : &mp_type_bytes, - (const byte *)lex->vstr.buf, lex->vstr.len); + // not interned, make a node holding a pointer to the string object + mp_obj_t o = mp_obj_new_str_copy(&mp_type_str, (const byte *)lex->vstr.buf, lex->vstr.len); pn = make_node_const_object(parser, lex->tok_line, o); } + } else if (lex->tok_kind == MP_TOKEN_BYTES) { + // make a node holding a pointer to the bytes object + mp_obj_t o = mp_obj_new_bytes((const byte *)lex->vstr.buf, lex->vstr.len); + pn = make_node_const_object(parser, lex->tok_line, o); } else { pn = mp_parse_node_new_leaf(MP_PARSE_NODE_TOKEN, lex->tok_kind); } diff --git a/py/parse.h b/py/parse.h index a6eb380047..9c327c28b6 100644 --- a/py/parse.h +++ b/py/parse.h @@ -39,15 +39,13 @@ struct _mp_lexer_t; // - xxxx...xx00: pointer to mp_parse_node_struct_t // - xx...xx0010: an identifier; bits 4 and above are the qstr // - xx...xx0110: a string; bits 4 and above are the qstr holding the value -// - xx...xx1010: a string of bytes; bits 4 and above are the qstr holding the value -// - xx...xx1110: a token; bits 4 and above are mp_token_kind_t +// - xx...xx1010: a token; bits 4 and above are mp_token_kind_t #define MP_PARSE_NODE_NULL (0) #define MP_PARSE_NODE_SMALL_INT (0x1) #define MP_PARSE_NODE_ID (0x02) #define MP_PARSE_NODE_STRING (0x06) -#define MP_PARSE_NODE_BYTES (0x0a) -#define MP_PARSE_NODE_TOKEN (0x0e) +#define MP_PARSE_NODE_TOKEN (0x0a) typedef uintptr_t mp_parse_node_t; // must be pointer size diff --git a/tests/cmdline/cmd_parsetree.py.exp b/tests/cmdline/cmd_parsetree.py.exp index ac16f6d88d..53d62a5dbd 100644 --- a/tests/cmdline/cmd_parsetree.py.exp +++ b/tests/cmdline/cmd_parsetree.py.exp @@ -18,7 +18,7 @@ [ 8] literal \.\+ [ 9] \(rule\|expr_stmt\)(5) (n=2) id(d) - bytes(bytes) +[ 9] literal \.\+ [ 10] \(rule\|expr_stmt\)(5) (n=2) id(e) [ 10] literal \.\+ From 962ad8622e4c732f76ecbf8d5191ba8216d244d3 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 16 Mar 2022 00:33:44 +1100 Subject: [PATCH 215/619] py/parse: Handle check for target small-int size in parser. This means that all constants for EMIT_ARG(load_const_obj, obj) are created in the parser (rather than some in the compiler). Signed-off-by: Damien George --- py/compile.c | 16 ---------------- py/parse.c | 7 +++++++ py/persistentcode.c | 2 +- 3 files changed, 8 insertions(+), 17 deletions(-) diff --git a/py/compile.c b/py/compile.c index f9e8de4573..2e719206ff 100644 --- a/py/compile.c +++ b/py/compile.c @@ -2790,23 +2790,7 @@ STATIC void compile_node(compiler_t *comp, mp_parse_node_t pn) { // pass } else if (MP_PARSE_NODE_IS_SMALL_INT(pn)) { mp_int_t arg = MP_PARSE_NODE_LEAF_SMALL_INT(pn); - #if MICROPY_DYNAMIC_COMPILER - mp_uint_t sign_mask = -((mp_uint_t)1 << (mp_dynamic_compiler.small_int_bits - 1)); - if ((arg & sign_mask) == 0 || (arg & sign_mask) == sign_mask) { - // integer fits in target runtime's small-int - EMIT_ARG(load_const_small_int, arg); - } else { - // integer doesn't fit, so create a multi-precision int object - // (but only create the actual object on the last pass) - if (comp->pass != MP_PASS_EMIT) { - EMIT_ARG(load_const_obj, mp_const_none); - } else { - EMIT_ARG(load_const_obj, mp_obj_new_int_from_ll(arg)); - } - } - #else EMIT_ARG(load_const_small_int, arg); - #endif } else if (MP_PARSE_NODE_IS_LEAF(pn)) { uintptr_t arg = MP_PARSE_NODE_LEAF_ARG(pn); switch (MP_PARSE_NODE_LEAF_KIND(pn)) { diff --git a/py/parse.c b/py/parse.c index f18f03b3f1..233cba11b4 100644 --- a/py/parse.c +++ b/py/parse.c @@ -471,6 +471,13 @@ STATIC mp_parse_node_t make_node_const_int(parser_t *parser, size_t src_line, mp return make_node_const_object(parser, src_line, obj); } #endif + #if MICROPY_DYNAMIC_COMPILER + // Check that the integer value fits in target runtime's small-int + mp_uint_t sign_mask = -((mp_uint_t)1 << (mp_dynamic_compiler.small_int_bits - 1)); + if (!((val & sign_mask) == 0 || (val & sign_mask) == sign_mask)) { + return make_node_const_object(parser, src_line, obj); + } + #endif return mp_parse_node_new_small_int(val); } else { return make_node_const_object(parser, src_line, obj); diff --git a/py/persistentcode.c b/py/persistentcode.c index b473f18308..6110ae97f0 100644 --- a/py/persistentcode.c +++ b/py/persistentcode.c @@ -536,7 +536,7 @@ STATIC void save_obj(mp_print_t *print, mp_obj_t o) { // we save numbers using a simplistic text representation // TODO could be improved byte obj_type; - if (mp_obj_is_type(o, &mp_type_int)) { + if (mp_obj_is_int(o)) { obj_type = 'i'; #if MICROPY_PY_BUILTINS_COMPLEX } else if (mp_obj_is_type(o, &mp_type_complex)) { From 1692cad6733b82b1cb7631ce10c1ddb54e66058e Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 16 Mar 2022 11:39:27 +1100 Subject: [PATCH 216/619] py/showbc: Remove global variables and make DECODE_PTR work correctly. The bytecode state variables mp_showbc_code_start and mp_showbc_constants have been removed and made local variables passed into the various functions. As part of this, the DECODE_PTR macro is fixed so it extracts the relevant pointer from the child_table (a regression introduced in f2040bfc7ee033e48acef9f289790f3b4e6b74e5). Signed-off-by: Damien George --- py/bc.h | 6 ++--- py/compile.c | 2 +- py/showbc.c | 71 ++++++++++++++++++++++++++-------------------------- py/vm.c | 2 +- 4 files changed, 41 insertions(+), 40 deletions(-) diff --git a/py/bc.h b/py/bc.h index 5710f4d243..8709b42389 100644 --- a/py/bc.h +++ b/py/bc.h @@ -265,9 +265,9 @@ const byte *mp_decode_uint_skip(const byte *ptr); mp_vm_return_kind_t mp_execute_bytecode(mp_code_state_t *code_state, volatile mp_obj_t inject_exc); mp_code_state_t *mp_obj_fun_bc_prepare_codestate(mp_obj_t func, size_t n_args, size_t n_kw, const mp_obj_t *args); void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw, const mp_obj_t *args); -void mp_bytecode_print(const mp_print_t *print, const void *descr, const byte *code, mp_uint_t len, const mp_module_constants_t *cm); -void mp_bytecode_print2(const mp_print_t *print, const byte *code, size_t len, const mp_module_constants_t *cm); -const byte *mp_bytecode_print_str(const mp_print_t *print, const byte *ip); +void mp_bytecode_print(const mp_print_t *print, const struct _mp_raw_code_t *rc, const mp_module_constants_t *cm); +void mp_bytecode_print2(const mp_print_t *print, const byte *ip, size_t len, struct _mp_raw_code_t *const *child_table, const mp_module_constants_t *cm); +const byte *mp_bytecode_print_str(const mp_print_t *print, const byte *ip_start, const byte *ip, struct _mp_raw_code_t *const *child_table, const mp_module_constants_t *cm); #define mp_bytecode_print_inst(print, code, x_table) mp_bytecode_print2(print, code, 1, x_table) // Helper macros to access pointer with least significant bits holding flags diff --git a/py/compile.c b/py/compile.c index 2e719206ff..eb7389ec5f 100644 --- a/py/compile.c +++ b/py/compile.c @@ -3632,7 +3632,7 @@ mp_compiled_module_t mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr so if (mp_verbose_flag >= 2) { for (scope_t *s = comp->scope_head; s != NULL; s = s->next) { mp_raw_code_t *rc = s->raw_code; - mp_bytecode_print(&mp_plat_print, rc, rc->fun_data, rc->fun_data_len, &cm.context->constants); + mp_bytecode_print(&mp_plat_print, rc, &cm.context->constants); } } #endif diff --git a/py/showbc.c b/py/showbc.c index 1300ac1fa3..8430739d8e 100644 --- a/py/showbc.c +++ b/py/showbc.c @@ -28,7 +28,7 @@ #include #include "py/bc0.h" -#include "py/bc.h" +#include "py/emitglue.h" #if MICROPY_DEBUG_PRINTERS @@ -45,7 +45,7 @@ #define DECODE_QSTR \ DECODE_UINT; \ - qst = mp_showbc_constants->qstr_table[unum] + qst = qstr_table[unum] #else @@ -56,18 +56,16 @@ #endif #define DECODE_PTR \ - DECODE_UINT; + DECODE_UINT; \ + unum = (mp_uint_t)(uintptr_t)child_table[unum] #define DECODE_OBJ \ DECODE_UINT; \ - unum = (mp_uint_t)mp_showbc_constants->obj_table[unum] + unum = (mp_uint_t)obj_table[unum] -const byte * mp_showbc_code_start; -const mp_module_constants_t *mp_showbc_constants; - -#include "py/emitglue.h" -void mp_bytecode_print(const mp_print_t *print, const void *descr, const byte *ip, mp_uint_t len, const mp_module_constants_t *cm) { - mp_showbc_code_start = ip; +void mp_bytecode_print(const mp_print_t *print, const mp_raw_code_t *rc, const mp_module_constants_t *cm) { + const byte *ip_start = rc->fun_data; + const byte *ip = rc->fun_data; // Decode prelude MP_BC_PRELUDE_SIG_DECODE(ip); @@ -81,18 +79,18 @@ void mp_bytecode_print(const mp_print_t *print, const void *descr, const byte *i #else qstr source_file = cm->source_file; #endif - mp_printf(print, "File %s, code block '%s' (descriptor: %p, bytecode @%p " UINT_FMT " bytes)\n",// rct=%p\n", - qstr_str(source_file), qstr_str(block_name), descr, mp_showbc_code_start, len);// , ((mp_raw_code_t*)descr)->children); + mp_printf(print, "File %s, code block '%s' (descriptor: %p, bytecode @%p %u bytes)\n", + qstr_str(source_file), qstr_str(block_name), rc, ip_start, (unsigned)rc->fun_data_len); // raw bytecode dump - size_t prelude_size = ip - mp_showbc_code_start + n_info + n_cell; + size_t prelude_size = ip - ip_start + n_info + n_cell; mp_printf(print, "Raw bytecode (code_info_size=%u, bytecode_size=%u):\n", - (unsigned)prelude_size, (unsigned)(len - prelude_size)); - for (mp_uint_t i = 0; i < len; i++) { + (unsigned)prelude_size, (unsigned)(rc->fun_data_len - prelude_size)); + for (size_t i = 0; i < rc->fun_data_len; i++) { if (i > 0 && i % 16 == 0) { mp_printf(print, "\n"); } - mp_printf(print, " %02x", mp_showbc_code_start[i]); + mp_printf(print, " %02x", ip_start[i]); } mp_printf(print, "\n"); @@ -140,10 +138,14 @@ void mp_bytecode_print(const mp_print_t *print, const void *descr, const byte *i mp_printf(print, " bc=" INT_FMT " line=" UINT_FMT "\n", bc, source_line); } } - mp_bytecode_print2(print, ip, len - prelude_size, cm); + mp_bytecode_print2(print, ip, rc->fun_data_len - prelude_size, rc->children, cm); } -const byte *mp_bytecode_print_str(const mp_print_t *print, const byte *ip) { +const byte *mp_bytecode_print_str(const mp_print_t *print, const byte *ip_start, const byte *ip, mp_raw_code_t *const *child_table, const mp_module_constants_t *cm) { + #if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE + const qstr_short_t *qstr_table = cm->qstr_table; + #endif + const mp_obj_t *obj_table = cm->obj_table; mp_uint_t unum; qstr qst; @@ -302,32 +304,32 @@ const byte *mp_bytecode_print_str(const mp_print_t *print, const byte *ip) { case MP_BC_JUMP: DECODE_SLABEL; - mp_printf(print, "JUMP " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); + mp_printf(print, "JUMP " UINT_FMT, (mp_uint_t)(ip + unum - ip_start)); break; case MP_BC_POP_JUMP_IF_TRUE: DECODE_SLABEL; - mp_printf(print, "POP_JUMP_IF_TRUE " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); + mp_printf(print, "POP_JUMP_IF_TRUE " UINT_FMT, (mp_uint_t)(ip + unum - ip_start)); break; case MP_BC_POP_JUMP_IF_FALSE: DECODE_SLABEL; - mp_printf(print, "POP_JUMP_IF_FALSE " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); + mp_printf(print, "POP_JUMP_IF_FALSE " UINT_FMT, (mp_uint_t)(ip + unum - ip_start)); break; case MP_BC_JUMP_IF_TRUE_OR_POP: DECODE_SLABEL; - mp_printf(print, "JUMP_IF_TRUE_OR_POP " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); + mp_printf(print, "JUMP_IF_TRUE_OR_POP " UINT_FMT, (mp_uint_t)(ip + unum - ip_start)); break; case MP_BC_JUMP_IF_FALSE_OR_POP: DECODE_SLABEL; - mp_printf(print, "JUMP_IF_FALSE_OR_POP " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); + mp_printf(print, "JUMP_IF_FALSE_OR_POP " UINT_FMT, (mp_uint_t)(ip + unum - ip_start)); break; case MP_BC_SETUP_WITH: DECODE_ULABEL; // loop-like labels are always forward - mp_printf(print, "SETUP_WITH " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); + mp_printf(print, "SETUP_WITH " UINT_FMT, (mp_uint_t)(ip + unum - ip_start)); break; case MP_BC_WITH_CLEANUP: @@ -336,18 +338,18 @@ const byte *mp_bytecode_print_str(const mp_print_t *print, const byte *ip) { case MP_BC_UNWIND_JUMP: DECODE_SLABEL; - mp_printf(print, "UNWIND_JUMP " UINT_FMT " %d", (mp_uint_t)(ip + unum - mp_showbc_code_start), *ip); + mp_printf(print, "UNWIND_JUMP " UINT_FMT " %d", (mp_uint_t)(ip + unum - ip_start), *ip); ip += 1; break; case MP_BC_SETUP_EXCEPT: DECODE_ULABEL; // except labels are always forward - mp_printf(print, "SETUP_EXCEPT " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); + mp_printf(print, "SETUP_EXCEPT " UINT_FMT, (mp_uint_t)(ip + unum - ip_start)); break; case MP_BC_SETUP_FINALLY: DECODE_ULABEL; // except labels are always forward - mp_printf(print, "SETUP_FINALLY " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); + mp_printf(print, "SETUP_FINALLY " UINT_FMT, (mp_uint_t)(ip + unum - ip_start)); break; case MP_BC_END_FINALLY: @@ -368,12 +370,12 @@ const byte *mp_bytecode_print_str(const mp_print_t *print, const byte *ip) { case MP_BC_FOR_ITER: DECODE_ULABEL; // the jump offset if iteration finishes; for labels are always forward - mp_printf(print, "FOR_ITER " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); + mp_printf(print, "FOR_ITER " UINT_FMT, (mp_uint_t)(ip + unum - ip_start)); break; case MP_BC_POP_EXCEPT_JUMP: DECODE_ULABEL; // these labels are always forward - mp_printf(print, "POP_EXCEPT_JUMP " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); + mp_printf(print, "POP_EXCEPT_JUMP " UINT_FMT, (mp_uint_t)(ip + unum - ip_start)); break; case MP_BC_BUILD_TUPLE: @@ -528,12 +530,11 @@ const byte *mp_bytecode_print_str(const mp_print_t *print, const byte *ip) { return ip; } -void mp_bytecode_print2(const mp_print_t *print, const byte *ip, size_t len, const mp_module_constants_t *cm) { - mp_showbc_code_start = ip; - mp_showbc_constants = cm; - while (ip < len + mp_showbc_code_start) { - mp_printf(print, "%02u ", (uint)(ip - mp_showbc_code_start)); - ip = mp_bytecode_print_str(print, ip); +void mp_bytecode_print2(const mp_print_t *print, const byte *ip, size_t len, mp_raw_code_t *const *child_table, const mp_module_constants_t *cm) { + const byte *ip_start = ip; + while (ip < ip_start + len) { + mp_printf(print, "%02u ", (uint)(ip - ip_start)); + ip = mp_bytecode_print_str(print, ip_start, ip, child_table, cm); mp_printf(print, "\n"); } } diff --git a/py/vm.c b/py/vm.c index b762c92bf2..497a569622 100644 --- a/py/vm.c +++ b/py/vm.c @@ -44,7 +44,7 @@ #else #define TRACE_PREFIX mp_printf(&mp_plat_print, "sp=%d ", (int)(sp - &code_state->state[0] + 1)) #endif -#define TRACE(ip) TRACE_PREFIX; mp_bytecode_print2(&mp_plat_print, ip, 1, &code_state->fun_bc->context->constants); +#define TRACE(ip) TRACE_PREFIX; mp_bytecode_print2(&mp_plat_print, ip, 1, code_state->fun_bc->child_table, &code_state->fun_bc->context->constants); #else #define TRACE(ip) #endif From adfd57c5fedec4e64c2e34cbea89aa6ecdb40a50 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 10 Mar 2022 11:49:01 +1100 Subject: [PATCH 217/619] lib/re1.5: Distinguish between subject start-of-line and start-of-srch. Otherwise a repeated sub/split will continue to match ^ to the start of that search. Signed-off-by: Damien George --- lib/re1.5/re1.5.h | 1 + lib/re1.5/recursiveloop.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/re1.5/re1.5.h b/lib/re1.5/re1.5.h index ba6f97b743..81f43ed7f5 100644 --- a/lib/re1.5/re1.5.h +++ b/lib/re1.5/re1.5.h @@ -130,6 +130,7 @@ Sub *update(Sub*, int, const char*); void decref(Sub*); struct Subject { + const char *begin_line; const char *begin; const char *end; }; diff --git a/lib/re1.5/recursiveloop.c b/lib/re1.5/recursiveloop.c index f8cb926292..17ecea3378 100644 --- a/lib/re1.5/recursiveloop.c +++ b/lib/re1.5/recursiveloop.c @@ -68,7 +68,7 @@ recursiveloop(char *pc, const char *sp, Subject *input, const char **subp, int n subp[off] = old; return 0; case Bol: - if(sp != input->begin) + if(sp != input->begin_line) return 0; continue; case Eol: From 63f0e700f4e8927854ec1f70eeb114fa079632a3 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 10 Mar 2022 11:51:42 +1100 Subject: [PATCH 218/619] extmod/modure: Set subject begin_line so ^ doesn't match interior. Fixes issue #8402. Signed-off-by: Damien George --- extmod/modure.c | 6 +++--- tests/extmod/ure_split.py | 10 ++++++++++ tests/extmod/ure_sub.py | 4 ++++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/extmod/modure.c b/extmod/modure.c index 36c987a80d..d9e5451eb5 100644 --- a/extmod/modure.c +++ b/extmod/modure.c @@ -187,7 +187,7 @@ STATIC mp_obj_t ure_exec(bool is_anchored, uint n_args, const mp_obj_t *args) { } Subject subj; size_t len; - subj.begin = mp_obj_str_get_data(args[1], &len); + subj.begin_line = subj.begin = mp_obj_str_get_data(args[1], &len); subj.end = subj.begin + len; int caps_num = (self->re.sub + 1) * 2; mp_obj_match_t *match = m_new_obj_var(mp_obj_match_t, char *, caps_num); @@ -220,7 +220,7 @@ STATIC mp_obj_t re_split(size_t n_args, const mp_obj_t *args) { Subject subj; size_t len; const mp_obj_type_t *str_type = mp_obj_get_type(args[1]); - subj.begin = mp_obj_str_get_data(args[1], &len); + subj.begin_line = subj.begin = mp_obj_str_get_data(args[1], &len); subj.end = subj.begin + len; int caps_num = (self->re.sub + 1) * 2; @@ -280,7 +280,7 @@ STATIC mp_obj_t re_sub_helper(size_t n_args, const mp_obj_t *args) { size_t where_len; const char *where_str = mp_obj_str_get_data(where, &where_len); Subject subj; - subj.begin = where_str; + subj.begin_line = subj.begin = where_str; subj.end = subj.begin + where_len; int caps_num = (self->re.sub + 1) * 2; diff --git a/tests/extmod/ure_split.py b/tests/extmod/ure_split.py index a8b9c1686c..7e6ef3990f 100644 --- a/tests/extmod/ure_split.py +++ b/tests/extmod/ure_split.py @@ -31,3 +31,13 @@ print(s) r = re.compile(b"x") s = r.split(b"fooxbar") print(s) + +# using ^ +r = re.compile("^ab") +s = r.split("abababcabab") +print(s) + +# using ^ with | +r = re.compile("^ab|cab") +s = r.split("abababcabab") +print(s) diff --git a/tests/extmod/ure_sub.py b/tests/extmod/ure_sub.py index ae6ad28d62..806c389576 100644 --- a/tests/extmod/ure_sub.py +++ b/tests/extmod/ure_sub.py @@ -75,3 +75,7 @@ except TypeError: # Include \ in the sub replacement print(re.sub("b", "\\\\b", "abc")) + +# Using ^, make sure it doesn't repeatedly match +print(re.sub("^ab", "*", "abababcabab")) +print(re.sub("^ab|cab", "*", "abababcabab")) From a16dcc8136c8af47d69e7a4b3a55270db0e4c637 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sun, 13 Mar 2022 20:03:38 +0200 Subject: [PATCH 219/619] stm32/boards: Convert F4xx and F7xx to new flash FS config. Following on from 35e70c1698047170f9fb8b1edc65a7f7125f267f. Fixes issue #8390. --- ports/stm32/boards/stm32f401xd.ld | 11 +++++-- ports/stm32/boards/stm32f401xe.ld | 11 +++++-- ports/stm32/boards/stm32f405.ld | 6 ++++ ports/stm32/boards/stm32f411.ld | 11 +++++-- ports/stm32/boards/stm32f412zx.ld | 11 +++++-- ports/stm32/boards/stm32f427xi.ld | 7 ++++ ports/stm32/boards/stm32f429.ld | 7 ++++ ports/stm32/boards/stm32f722.ld | 6 ++++ ports/stm32/boards/stm32f746.ld | 6 ++++ ports/stm32/boards/stm32f767.ld | 6 ++++ ports/stm32/boards/stm32f769.ld | 6 ++++ ports/stm32/flashbdev.c | 55 +------------------------------ 12 files changed, 81 insertions(+), 62 deletions(-) diff --git a/ports/stm32/boards/stm32f401xd.ld b/ports/stm32/boards/stm32f401xd.ld index f4146abc6d..33b2912ace 100644 --- a/ports/stm32/boards/stm32f401xd.ld +++ b/ports/stm32/boards/stm32f401xd.ld @@ -7,9 +7,10 @@ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 384K /* entire flash */ FLASH_ISR (rx) : ORIGIN = 0x08000000, LENGTH = 16K /* sector 0 */ - FLASH_FS (rx) : ORIGIN = 0x08004000, LENGTH = 112K /* sectors 1,2,3 are 16K, 4 is 64K */ + FLASH_FS (rx) : ORIGIN = 0x08004000, LENGTH = 64K /* sectors 1,2,3,4: 16k+16k+16k+16k(of 64k)=64k */ FLASH_TEXT (rx) : ORIGIN = 0x08020000, LENGTH = 256K /* sectors 5,6 are 128K */ - RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 96K + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 80K + FS_CACHE (xrw) : ORIGIN = 0x20014000, LENGTH = 16K } /* produce a link error if there is not this amount of RAM for these sections */ @@ -26,3 +27,9 @@ _ram_start = ORIGIN(RAM); _ram_end = ORIGIN(RAM) + LENGTH(RAM); _heap_start = _ebss; /* heap starts just after statically allocated memory */ _heap_end = _sstack; + +/* Filesystem cache in RAM, and storage in flash */ +_micropy_hw_internal_flash_storage_ram_cache_start = ORIGIN(FS_CACHE); +_micropy_hw_internal_flash_storage_ram_cache_end = ORIGIN(FS_CACHE) + LENGTH(FS_CACHE); +_micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); +_micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); diff --git a/ports/stm32/boards/stm32f401xe.ld b/ports/stm32/boards/stm32f401xe.ld index e7bd8edfed..d783cd1875 100644 --- a/ports/stm32/boards/stm32f401xe.ld +++ b/ports/stm32/boards/stm32f401xe.ld @@ -7,9 +7,10 @@ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K /* entire flash */ FLASH_ISR (rx) : ORIGIN = 0x08000000, LENGTH = 16K /* sector 0 */ - FLASH_FS (rx) : ORIGIN = 0x08004000, LENGTH = 112K /* sectors 1,2,3 are 16K, 4 is 64K */ + FLASH_FS (rx) : ORIGIN = 0x08004000, LENGTH = 64K /* sectors 1,2,3,4: 16k+16k+16k+16k(of 64k)=64k */ FLASH_TEXT (rx) : ORIGIN = 0x08020000, LENGTH = 384K /* sectors 5,6,7 are 128K */ - RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 96K + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 80K + FS_CACHE (xrw) : ORIGIN = 0x20014000, LENGTH = 16K } /* produce a link error if there is not this amount of RAM for these sections */ @@ -26,3 +27,9 @@ _ram_start = ORIGIN(RAM); _ram_end = ORIGIN(RAM) + LENGTH(RAM); _heap_start = _ebss; /* heap starts just after statically allocated memory */ _heap_end = _sstack; + +/* Filesystem cache in RAM, and storage in flash */ +_micropy_hw_internal_flash_storage_ram_cache_start = ORIGIN(FS_CACHE); +_micropy_hw_internal_flash_storage_ram_cache_end = ORIGIN(FS_CACHE) + LENGTH(FS_CACHE); +_micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); +_micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); diff --git a/ports/stm32/boards/stm32f405.ld b/ports/stm32/boards/stm32f405.ld index b6f5d30578..6658c1e991 100644 --- a/ports/stm32/boards/stm32f405.ld +++ b/ports/stm32/boards/stm32f405.ld @@ -27,3 +27,9 @@ _ram_start = ORIGIN(RAM); _ram_end = ORIGIN(RAM) + LENGTH(RAM); _heap_start = _ebss; /* heap starts just after statically allocated memory */ _heap_end = _sstack; + +/* Filesystem cache in RAM, and storage in flash */ +_micropy_hw_internal_flash_storage_ram_cache_start = ORIGIN(CCMRAM); +_micropy_hw_internal_flash_storage_ram_cache_end = ORIGIN(CCMRAM) + LENGTH(CCMRAM); +_micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); +_micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); diff --git a/ports/stm32/boards/stm32f411.ld b/ports/stm32/boards/stm32f411.ld index 50633118eb..6e874f66c8 100644 --- a/ports/stm32/boards/stm32f411.ld +++ b/ports/stm32/boards/stm32f411.ld @@ -7,9 +7,10 @@ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K /* entire flash */ FLASH_ISR (rx) : ORIGIN = 0x08000000, LENGTH = 16K /* sector 0 */ - FLASH_FS (rx) : ORIGIN = 0x08004000, LENGTH = 112K /* sectors 1,2,3 are 16K, 4 is 64K */ + FLASH_FS (rx) : ORIGIN = 0x08004000, LENGTH = 64K /* sectors 1,2,3,4: 16k+16k+16k+16k(of 64k)=64k */ FLASH_TEXT (rx) : ORIGIN = 0x08020000, LENGTH = 384K /* sectors 5,6,7 are 128K */ - RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 112K + FS_CACHE (xrw) : ORIGIN = 0x2001c000, LENGTH = 16K } /* produce a link error if there is not this amount of RAM for these sections */ @@ -26,3 +27,9 @@ _ram_start = ORIGIN(RAM); _ram_end = ORIGIN(RAM) + LENGTH(RAM); _heap_start = _ebss; /* heap starts just after statically allocated memory */ _heap_end = _sstack; + +/* Filesystem cache in RAM, and storage in flash */ +_micropy_hw_internal_flash_storage_ram_cache_start = ORIGIN(FS_CACHE); +_micropy_hw_internal_flash_storage_ram_cache_end = ORIGIN(FS_CACHE) + LENGTH(FS_CACHE); +_micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); +_micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); diff --git a/ports/stm32/boards/stm32f412zx.ld b/ports/stm32/boards/stm32f412zx.ld index 55aa608657..c949d827a6 100644 --- a/ports/stm32/boards/stm32f412zx.ld +++ b/ports/stm32/boards/stm32f412zx.ld @@ -7,9 +7,10 @@ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K /* entire flash */ FLASH_ISR (rx) : ORIGIN = 0x08000000, LENGTH = 16K /* sector 0 */ - FLASH_FS (rx) : ORIGIN = 0x08004000, LENGTH = 112K /* sectors 1,2,3 are 16K, 4 is 64K */ + FLASH_FS (rx) : ORIGIN = 0x08004000, LENGTH = 64K /* sectors 1,2,3,4: 16k+16k+16k+16k(of 64k)=64k */ FLASH_TEXT (rx) : ORIGIN = 0x08020000, LENGTH = 896K /* sectors 5,6,7,8,9,10,11 are 128K */ - RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 256K + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 240K + FS_CACHE (xrw) : ORIGIN = 0x2003c000, LENGTH = 16K } /* produce a link error if there is not this amount of RAM for these sections */ @@ -26,3 +27,9 @@ _ram_start = ORIGIN(RAM); _ram_end = ORIGIN(RAM) + LENGTH(RAM); _heap_start = _ebss; /* heap starts just after statically allocated memory */ _heap_end = _sstack; + +/* Filesystem cache in RAM, and storage in flash */ +_micropy_hw_internal_flash_storage_ram_cache_start = ORIGIN(FS_CACHE); +_micropy_hw_internal_flash_storage_ram_cache_end = ORIGIN(FS_CACHE) + LENGTH(FS_CACHE); +_micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); +_micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); diff --git a/ports/stm32/boards/stm32f427xi.ld b/ports/stm32/boards/stm32f427xi.ld index 1197848af1..b405841609 100644 --- a/ports/stm32/boards/stm32f427xi.ld +++ b/ports/stm32/boards/stm32f427xi.ld @@ -9,6 +9,7 @@ MEMORY FLASH_ISR (rx) : ORIGIN = 0x08000000, LENGTH = 16K /* sector 0, 16 KiB */ FLASH_FS (rx) : ORIGIN = 0x08004000, LENGTH = 112K /* sectors 1-4: 3*16K+64K */ FLASH_TEXT (rx) : ORIGIN = 0x08020000, LENGTH = 896K /* sectors 5-11 are 128K */ + CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192K } @@ -26,3 +27,9 @@ _ram_start = ORIGIN(RAM); _ram_end = ORIGIN(RAM) + LENGTH(RAM); _heap_start = _ebss; /* heap starts just after statically allocated memory */ _heap_end = _sstack; + +/* Filesystem cache in RAM, and storage in flash */ +_micropy_hw_internal_flash_storage_ram_cache_start = ORIGIN(CCMRAM); +_micropy_hw_internal_flash_storage_ram_cache_end = ORIGIN(CCMRAM) + LENGTH(CCMRAM); +_micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); +_micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); diff --git a/ports/stm32/boards/stm32f429.ld b/ports/stm32/boards/stm32f429.ld index beeaa4df21..d081c190d3 100644 --- a/ports/stm32/boards/stm32f429.ld +++ b/ports/stm32/boards/stm32f429.ld @@ -9,6 +9,7 @@ MEMORY FLASH_ISR (rx) : ORIGIN = 0x08000000, LENGTH = 16K /* sector 0, 16 KiB */ FLASH_FS (rx) : ORIGIN = 0x08004000, LENGTH = 112K /* sectors 1-4: 3*16K+64K */ FLASH_TEXT (rx) : ORIGIN = 0x08020000, LENGTH = 896K /* sectors 5-11 are 128K */ + CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192K SDRAM(xrw) : ORIGIN = 0xC0000000, LENGTH = 8192K } @@ -27,3 +28,9 @@ _ram_start = ORIGIN(RAM); _ram_end = ORIGIN(RAM) + LENGTH(RAM); _heap_start = _ebss; /* heap starts just after statically allocated memory */ _heap_end = _sstack; + +/* Filesystem cache in RAM, and storage in flash */ +_micropy_hw_internal_flash_storage_ram_cache_start = ORIGIN(CCMRAM); +_micropy_hw_internal_flash_storage_ram_cache_end = ORIGIN(CCMRAM) + LENGTH(CCMRAM); +_micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); +_micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); diff --git a/ports/stm32/boards/stm32f722.ld b/ports/stm32/boards/stm32f722.ld index fe84a49637..0520f2e95f 100644 --- a/ports/stm32/boards/stm32f722.ld +++ b/ports/stm32/boards/stm32f722.ld @@ -27,3 +27,9 @@ _ram_start = ORIGIN(RAM); _ram_end = ORIGIN(RAM) + LENGTH(RAM); _heap_start = _ebss; /* heap starts just after statically allocated memory */ _heap_end = _sstack; + +/* Filesystem cache in RAM, and storage in flash */ +_micropy_hw_internal_flash_storage_ram_cache_start = ORIGIN(DTCM); +_micropy_hw_internal_flash_storage_ram_cache_end = ORIGIN(DTCM) + LENGTH(DTCM); +_micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); +_micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); diff --git a/ports/stm32/boards/stm32f746.ld b/ports/stm32/boards/stm32f746.ld index 0f1de26964..854b95463a 100644 --- a/ports/stm32/boards/stm32f746.ld +++ b/ports/stm32/boards/stm32f746.ld @@ -27,3 +27,9 @@ _ram_start = ORIGIN(RAM); _ram_end = ORIGIN(RAM) + LENGTH(RAM); _heap_start = _ebss; /* heap starts just after statically allocated memory */ _heap_end = _sstack; + +/* Filesystem cache in RAM, and storage in flash */ +_micropy_hw_internal_flash_storage_ram_cache_start = ORIGIN(DTCM); +_micropy_hw_internal_flash_storage_ram_cache_end = ORIGIN(DTCM) + LENGTH(DTCM); +_micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); +_micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); diff --git a/ports/stm32/boards/stm32f767.ld b/ports/stm32/boards/stm32f767.ld index d07f2ecbef..580be50dc9 100644 --- a/ports/stm32/boards/stm32f767.ld +++ b/ports/stm32/boards/stm32f767.ld @@ -28,3 +28,9 @@ _ram_start = ORIGIN(RAM); _ram_end = ORIGIN(RAM) + LENGTH(RAM); _heap_start = _ebss; /* heap starts just after statically allocated memory */ _heap_end = _sstack; + +/* Filesystem cache in RAM, and storage in flash */ +_micropy_hw_internal_flash_storage_ram_cache_start = ORIGIN(DTCM); +_micropy_hw_internal_flash_storage_ram_cache_end = ORIGIN(DTCM) + LENGTH(DTCM); +_micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); +_micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); diff --git a/ports/stm32/boards/stm32f769.ld b/ports/stm32/boards/stm32f769.ld index ebc6d033d9..9fc73c8596 100644 --- a/ports/stm32/boards/stm32f769.ld +++ b/ports/stm32/boards/stm32f769.ld @@ -27,3 +27,9 @@ _ram_start = ORIGIN(RAM); _ram_end = ORIGIN(RAM) + LENGTH(RAM); _heap_start = _ebss; /* heap starts just after statically allocated memory */ _heap_end = _sstack; + +/* Filesystem cache in RAM, and storage in flash */ +_micropy_hw_internal_flash_storage_ram_cache_start = ORIGIN(DTCM); +_micropy_hw_internal_flash_storage_ram_cache_end = ORIGIN(DTCM) + LENGTH(DTCM); +_micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); +_micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); diff --git a/ports/stm32/flashbdev.c b/ports/stm32/flashbdev.c index 946a9d8cc2..bcff94b15d 100644 --- a/ports/stm32/flashbdev.c +++ b/ports/stm32/flashbdev.c @@ -36,58 +36,7 @@ #if MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE -// Here we try to automatically configure the location and size of the flash -// pages to use for the internal storage. We also configure the location of the -// cache used for writing. - -#if defined(STM32F405xx) || defined(STM32F415xx) || defined(STM32F407xx) - -#define CACHE_MEM_START_ADDR (0x10000000) // CCM data RAM, 64k -#define FLASH_SECTOR_SIZE_MAX (0x10000) // 64k max, size of CCM -#define FLASH_MEM_SEG1_START_ADDR (0x08004000) // sector 1 -#define FLASH_MEM_SEG1_NUM_BLOCKS (224) // sectors 1,2,3,4: 16k+16k+16k+64k=112k - -// enable this to get an extra 64k of storage (uses the last sector of the flash) -#if 0 -#define FLASH_MEM_SEG2_START_ADDR (0x080e0000) // sector 11 -#define FLASH_MEM_SEG2_NUM_BLOCKS (128) // sector 11: 128k -#endif - -#elif defined(STM32F401xE) || defined(STM32F411xE) || defined(STM32F412Zx) || defined(STM32F446xx) - -STATIC byte flash_cache_mem[0x4000] __attribute__((aligned(4))); // 16k -#define CACHE_MEM_START_ADDR (&flash_cache_mem[0]) -#define FLASH_SECTOR_SIZE_MAX (0x4000) // 16k max due to size of cache buffer -#define FLASH_MEM_SEG1_START_ADDR (0x08004000) // sector 1 -#define FLASH_MEM_SEG1_NUM_BLOCKS (128) // sectors 1,2,3,4: 16k+16k+16k+16k(of 64k)=64k - -#elif defined(STM32F427xx) || defined(STM32F429xx) - -#define CACHE_MEM_START_ADDR (0x10000000) // CCM data RAM, 64k -#define FLASH_SECTOR_SIZE_MAX (0x10000) // 64k max, size of CCM -#define FLASH_MEM_SEG1_START_ADDR (0x08004000) // sector 1 -#define FLASH_MEM_SEG1_NUM_BLOCKS (224) // sectors 1,2,3,4: 16k+16k+16k+64k=112k - -#elif defined(STM32F722xx) || defined(STM32F723xx) || defined(STM32F732xx) || defined(STM32F733xx) - -#define CACHE_MEM_START_ADDR (0x20000000) // DTCM data RAM, 64k -#define FLASH_SECTOR_SIZE_MAX (0x10000) // 64k max -#define FLASH_MEM_SEG1_START_ADDR (0x08004000) // sector 1 -#define FLASH_MEM_SEG1_NUM_BLOCKS (224) // sectors 1,2,3,4: 16k+16k+16k+64k=112k - -#elif defined(STM32F746xx) || defined(STM32F765xx) || defined(STM32F767xx) || defined(STM32F769xx) - -// The STM32F746 doesn't really have CCRAM, so we use the 64K DTCM for this. - -#define CACHE_MEM_START_ADDR (0x20000000) // DTCM data RAM, 64k -#define FLASH_SECTOR_SIZE_MAX (0x08000) // 32k max -#define FLASH_MEM_SEG1_START_ADDR (0x08008000) // sector 1 -#define FLASH_MEM_SEG1_NUM_BLOCKS (192) // sectors 1,2,3: 32k+32k+32=96k - -#else - -// Generic configuration where the linker script specifies flash storage and RAM cache locations. - +// The linker script specifies flash storage and RAM cache locations. extern uint8_t _micropy_hw_internal_flash_storage_start; extern uint8_t _micropy_hw_internal_flash_storage_end; extern uint8_t _micropy_hw_internal_flash_storage2_start; @@ -111,8 +60,6 @@ extern uint8_t _micropy_hw_internal_flash_storage_ram_cache_end[]; ((&_micropy_hw_internal_flash_storage2_end - &_micropy_hw_internal_flash_storage2_start) / 512) #endif -#endif - #if !defined(FLASH_MEM_SEG2_START_ADDR) #define FLASH_MEM_SEG2_NUM_BLOCKS (0) // no second segment #endif From 95ee29f4f4e0f76fcca7567d26f3335957762d66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Z=C3=BCger?= Date: Wed, 16 Mar 2022 15:45:03 +0100 Subject: [PATCH 220/619] stm32/mbedtls: Add NULL pointer check in m_free_mbedtls. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the C standard the free(void *ptr) function: if ptr is a null pointer, no action occurs. Signed-off-by: Peter Züger --- ports/stm32/mbedtls/mbedtls_port.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ports/stm32/mbedtls/mbedtls_port.c b/ports/stm32/mbedtls/mbedtls_port.c index c31eb744ae..44e23a3b90 100644 --- a/ports/stm32/mbedtls/mbedtls_port.c +++ b/ports/stm32/mbedtls/mbedtls_port.c @@ -62,6 +62,9 @@ void *m_calloc_mbedtls(size_t nmemb, size_t size) { } void m_free_mbedtls(void *ptr_in) { + if (ptr_in == NULL) { + return; + } void **ptr = &((void**)ptr_in)[-2]; #if DEBUG uint32_t nb; From 94a9b5066827d7dc5b1844ae28fad79778314185 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Z=C3=BCger?= Date: Wed, 16 Mar 2022 16:41:23 +0100 Subject: [PATCH 221/619] mimxrt/mbedtls: Add NULL pointer check in m_free_mbedtls. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Peter Züger --- ports/mimxrt/mbedtls/mbedtls_port.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ports/mimxrt/mbedtls/mbedtls_port.c b/ports/mimxrt/mbedtls/mbedtls_port.c index eb11f5141b..928f12f3f1 100644 --- a/ports/mimxrt/mbedtls/mbedtls_port.c +++ b/ports/mimxrt/mbedtls/mbedtls_port.c @@ -64,6 +64,9 @@ void *m_calloc_mbedtls(size_t nmemb, size_t size) { } void m_free_mbedtls(void *ptr_in) { + if (ptr_in == NULL) { + return; + } void **ptr = &((void **)ptr_in)[-2]; #if DEBUG uint32_t nb; From 5887dfeea6a4899e8f30ac1435159d760a985642 Mon Sep 17 00:00:00 2001 From: Jonathan Hogg Date: Mon, 7 Mar 2022 09:15:05 +0000 Subject: [PATCH 222/619] docs/esp32/quickref: Refine deep-sleep power-saving notes. This attempts to better explain how pull-ups and pull-downs operate in deep-sleep mode. --- docs/esp32/quickref.rst | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/docs/esp32/quickref.rst b/docs/esp32/quickref.rst index 56e765536e..0778425f2f 100644 --- a/docs/esp32/quickref.rst +++ b/docs/esp32/quickref.rst @@ -186,8 +186,7 @@ Notes: * Pins 34-39 are input only, and also do not have internal pull-up resistors -* The pull value of some pins can be set to ``Pin.PULL_HOLD`` to reduce power - consumption during deepsleep. +* See :ref:`Deep_sleep_Mode` for a discussion of pin behaviour during sleep There's a higher-level abstraction :ref:`machine.Signal ` which can be used to invert a pin. Useful for illuminating active-low LEDs @@ -508,6 +507,8 @@ See :ref:`machine.WDT `. :: wdt = WDT(timeout=5000) wdt.feed() +.. _Deep_sleep_mode: + Deep-sleep mode --------------- @@ -527,15 +528,28 @@ Notes: * Calling ``deepsleep()`` without an argument will put the device to sleep indefinitely * A software reset does not change the reset cause -* There may be some leakage current flowing through enabled internal pullups. - To further reduce power consumption it is possible to disable the internal pullups:: - p1 = Pin(4, Pin.IN, Pin.PULL_HOLD) +Some ESP32 pins (0, 2, 4, 12-15, 25-27, 32-39) are connected to the RTC during +deep-sleep and can be used to wake the device with the ``wake_on_`` functions in +the :mod:`esp32` module. The output-capable RTC pins (all except 34-39) will +also retain their pull-up or pull-down resistor configuration when entering +deep-sleep. - After leaving deepsleep it may be necessary to un-hold the pin explicitly (e.g. if - it is an output pin) via:: +If the pull resistors are not actively required during deep-sleep and are likely +to cause current leakage (for example a pull-up resistor is connected to ground +through a switch), then they should be disabled to save power before entering +deep-sleep mode:: - p1 = Pin(4, Pin.OUT, None) + from machine import Pin, deepsleep + + # configure input RTC pin with pull-up on boot + pin = Pin(2, Pin.IN, Pin.PULL_UP) + + # disable pull-up and put the device to sleep for 10 seconds + pin.init(pull=None) + machine.deepsleep(10000) + +Non-RTC GPIO pins will be disconnected by default on entering deep-sleep. SD card ------- From 7684c996bc2b2521fa4bd023e4fa24622092cd5f Mon Sep 17 00:00:00 2001 From: Jonathan Hogg Date: Mon, 7 Mar 2022 09:21:11 +0000 Subject: [PATCH 223/619] esp32/machine_pin: Add new hold keyword argument and remove PULL_HOLD. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current pull=Pin.PULL_HOLD argument doesn't make a lot of sense in the context of what it actually does vs what the ESP32 quickref document says it does. This commit removes PULL_HOLD and adds a new hold=True|False keyword argument to Pin()/Pin.init(). Setting this to True will cause the ESP32 to lock the configuration of the pin – including direction, output value, drive strength, pull-up/-down – such that it can't be accidentally changed and will be retained through a watchdog or internal reset. Fixes issue #8283, and see also #8284. --- docs/esp32/quickref.rst | 12 ++++++++++++ ports/esp32/machine_pin.c | 18 +++++++++++------- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/docs/esp32/quickref.rst b/docs/esp32/quickref.rst index 0778425f2f..dd7e515f1b 100644 --- a/docs/esp32/quickref.rst +++ b/docs/esp32/quickref.rst @@ -177,6 +177,14 @@ safe maximum source/sink currents and approximate internal driver resistances: - ``Pin.DRIVE_2``: 20mA / 30 ohm (default strength if not configured) - ``Pin.DRIVE_3``: 40mA / 15 ohm +The ``hold=`` keyword argument to ``Pin()`` and ``Pin.init()`` will enable the +ESP32 "pad hold" feature. When set to ``True``, the pin configuration +(direction, pull resistors and output value) will be held and any further +changes (including changing the output level) will not be applied. Setting +``hold=False`` will immediately apply any outstanding pin configuration changes +and release the pin. Using ``hold=True`` while a pin is already held will apply +any configuration changes and then immediately reapply the hold. + Notes: * Pins 1 and 3 are REPL UART TX and RX respectively @@ -549,6 +557,10 @@ deep-sleep mode:: pin.init(pull=None) machine.deepsleep(10000) +Output-configured RTC pins will also retain their output direction and level in +deep-sleep if pad hold is enabled with the ``hold=True`` argument to +``Pin.init()``. + Non-RTC GPIO pins will be disconnected by default on entering deep-sleep. SD card diff --git a/ports/esp32/machine_pin.c b/ports/esp32/machine_pin.c index 1dad398524..90dc03e69a 100644 --- a/ports/esp32/machine_pin.c +++ b/ports/esp32/machine_pin.c @@ -47,7 +47,6 @@ // Used to implement a range of pull capabilities #define GPIO_PULL_DOWN (1) #define GPIO_PULL_UP (2) -#define GPIO_PULL_HOLD (4) #if CONFIG_IDF_TARGET_ESP32 #define GPIO_FIRST_NON_OUTPUT (34) @@ -253,14 +252,15 @@ STATIC void machine_pin_print(const mp_print_t *print, mp_obj_t self_in, mp_prin mp_printf(print, "Pin(%u)", self->id); } -// pin.init(mode=None, pull=-1, *, value, drive) +// pin.init(mode=None, pull=-1, *, value, drive, hold) STATIC mp_obj_t machine_pin_obj_init_helper(const machine_pin_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_mode, ARG_pull, ARG_value, ARG_drive }; + enum { ARG_mode, ARG_pull, ARG_value, ARG_drive, ARG_hold }; static const mp_arg_t allowed_args[] = { { MP_QSTR_mode, MP_ARG_OBJ, {.u_obj = mp_const_none}}, { MP_QSTR_pull, MP_ARG_OBJ, {.u_obj = MP_OBJ_NEW_SMALL_INT(-1)}}, { MP_QSTR_value, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, { MP_QSTR_drive, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, + { MP_QSTR_hold, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, }; // parse args @@ -325,10 +325,15 @@ STATIC mp_obj_t machine_pin_obj_init_helper(const machine_pin_obj_t *self, size_ } else { gpio_pullup_dis(self->id); } - if (mode & GPIO_PULL_HOLD) { + } + + // configure pad hold + if (args[ARG_hold].u_obj != MP_OBJ_NULL && GPIO_IS_VALID_OUTPUT_GPIO(self->id)) { + // always disable pad hold to apply outstanding config changes + gpio_hold_dis(self->id); + // (re-)enable pad hold if requested + if (mp_obj_is_true(args[ARG_hold].u_obj)) { gpio_hold_en(self->id); - } else if (GPIO_IS_VALID_OUTPUT_GPIO(self->id)) { - gpio_hold_dis(self->id); } } @@ -480,7 +485,6 @@ STATIC const mp_rom_map_elem_t machine_pin_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_OPEN_DRAIN), MP_ROM_INT(GPIO_MODE_INPUT_OUTPUT_OD) }, { MP_ROM_QSTR(MP_QSTR_PULL_UP), MP_ROM_INT(GPIO_PULL_UP) }, { MP_ROM_QSTR(MP_QSTR_PULL_DOWN), MP_ROM_INT(GPIO_PULL_DOWN) }, - { MP_ROM_QSTR(MP_QSTR_PULL_HOLD), MP_ROM_INT(GPIO_PULL_HOLD) }, { MP_ROM_QSTR(MP_QSTR_IRQ_RISING), MP_ROM_INT(GPIO_PIN_INTR_POSEDGE) }, { MP_ROM_QSTR(MP_QSTR_IRQ_FALLING), MP_ROM_INT(GPIO_PIN_INTR_NEGEDGE) }, { MP_ROM_QSTR(MP_QSTR_WAKE_LOW), MP_ROM_INT(GPIO_PIN_INTR_LOLEVEL) }, From 21d0599bd10aa567e3fdee5e39da1fe9a9f816e6 Mon Sep 17 00:00:00 2001 From: Jonathan Hogg Date: Mon, 7 Mar 2022 09:22:00 +0000 Subject: [PATCH 224/619] esp32/modesp32: Add new gpio_deep_sleep_hold function. Add a new function to control whether held pins will retain their function through deep-sleep. Also document this function and explain how to use this in quickref to retain pin configuration during deep-sleep. --- docs/esp32/quickref.rst | 17 +++++++++++++++++ docs/library/esp32.rst | 5 +++++ ports/esp32/modesp32.c | 11 +++++++++++ 3 files changed, 33 insertions(+) diff --git a/docs/esp32/quickref.rst b/docs/esp32/quickref.rst index dd7e515f1b..894508f4c2 100644 --- a/docs/esp32/quickref.rst +++ b/docs/esp32/quickref.rst @@ -562,6 +562,23 @@ deep-sleep if pad hold is enabled with the ``hold=True`` argument to ``Pin.init()``. Non-RTC GPIO pins will be disconnected by default on entering deep-sleep. +Configuration of non-RTC pins - including output level - can be retained by +enabling pad hold on the pin and enabling GPIO pad hold during deep-sleep:: + + from machine import Pin, deepsleep + import esp32 + + opin = Pin(19, Pin.OUT, value=1, hold=True) # hold output level + ipin = Pin(21, Pin.IN, Pin.PULL_UP, hold=True) # hold pull-up + + # enable pad hold in deep-sleep for non-RTC GPIO + esp32.gpio_deep_sleep_hold(True) + + # put the device to sleep for 10 seconds + deepsleep(10000) + +The pin configuration - including the pad hold - will be retained on wake from +sleep. See :ref:`Pins_and_GPIO` above for a further discussion of pad holding. SD card ------- diff --git a/docs/library/esp32.rst b/docs/library/esp32.rst index da5fa8c3cb..3348003499 100644 --- a/docs/library/esp32.rst +++ b/docs/library/esp32.rst @@ -30,6 +30,11 @@ Functions or a tuple/list of valid Pin objects. *level* should be ``esp32.WAKEUP_ALL_LOW`` or ``esp32.WAKEUP_ANY_HIGH``. +.. function:: gpio_deep_sleep_hold(enable) + + Configure whether non-RTC GPIO pin configuration is retained during + deep-sleep mode for held pads. *enable* should be a boolean value. + .. function:: raw_temperature() Read the raw value of the internal temperature sensor, returning an integer. diff --git a/ports/esp32/modesp32.c b/ports/esp32/modesp32.c index 75e5b3ed8b..4579c5de11 100644 --- a/ports/esp32/modesp32.c +++ b/ports/esp32/modesp32.c @@ -131,6 +131,16 @@ STATIC mp_obj_t esp32_wake_on_ext1(size_t n_args, const mp_obj_t *pos_args, mp_m } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(esp32_wake_on_ext1_obj, 0, esp32_wake_on_ext1); +STATIC mp_obj_t esp32_gpio_deep_sleep_hold(const mp_obj_t enable) { + if (mp_obj_is_true(enable)) { + gpio_deep_sleep_hold_en(); + } else { + gpio_deep_sleep_hold_dis(); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp32_gpio_deep_sleep_hold_obj, esp32_gpio_deep_sleep_hold); + #if CONFIG_IDF_TARGET_ESP32 #include "soc/sens_reg.h" @@ -187,6 +197,7 @@ STATIC const mp_rom_map_elem_t esp32_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_wake_on_touch), MP_ROM_PTR(&esp32_wake_on_touch_obj) }, { MP_ROM_QSTR(MP_QSTR_wake_on_ext0), MP_ROM_PTR(&esp32_wake_on_ext0_obj) }, { MP_ROM_QSTR(MP_QSTR_wake_on_ext1), MP_ROM_PTR(&esp32_wake_on_ext1_obj) }, + { MP_ROM_QSTR(MP_QSTR_gpio_deep_sleep_hold), MP_ROM_PTR(&esp32_gpio_deep_sleep_hold_obj) }, #if CONFIG_IDF_TARGET_ESP32 { MP_ROM_QSTR(MP_QSTR_raw_temperature), MP_ROM_PTR(&esp32_raw_temperature_obj) }, { MP_ROM_QSTR(MP_QSTR_hall_sensor), MP_ROM_PTR(&esp32_hall_sensor_obj) }, From 3b9de192be8177abd07c18cf58468939c4c5fd5d Mon Sep 17 00:00:00 2001 From: Jonathan Hogg Date: Thu, 10 Mar 2022 13:00:30 +0000 Subject: [PATCH 225/619] esp32/boards/UM_TINYPICO: Remove use of PULL_HOLD. Change APA102 power handling to not use the (now removed) PULL_HOLD constant. --- ports/esp32/boards/UM_TINYPICO/modules/tinypico.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ports/esp32/boards/UM_TINYPICO/modules/tinypico.py b/ports/esp32/boards/UM_TINYPICO/modules/tinypico.py index 04b274bb53..9545078842 100644 --- a/ports/esp32/boards/UM_TINYPICO/modules/tinypico.py +++ b/ports/esp32/boards/UM_TINYPICO/modules/tinypico.py @@ -83,10 +83,9 @@ def set_dotstar_power(state): """Set the power for the on-board Dotstar to allow no current draw when not needed.""" # Set the power pin to the inverse of state if state: - Pin(DOTSTAR_PWR, Pin.OUT, None) # Break the PULL_HOLD on the pin - Pin(DOTSTAR_PWR).value(False) # Set the pin to LOW to enable the Transistor + Pin(DOTSTAR_PWR, Pin.OUT, None, value=0) # Drive output to LOW to enable transistor else: - Pin(13, Pin.IN, Pin.PULL_HOLD) # Set PULL_HOLD on the pin to allow the 3V3 pull-up to work + Pin(DOTSTAR_PWR, Pin.IN, None) # Disable output, external pull-up will disable transistor Pin( DOTSTAR_CLK, Pin.OUT if state else Pin.IN From 1a0bd352d3bfb6674d470c4b07862ebaace0ca88 Mon Sep 17 00:00:00 2001 From: wemos Date: Wed, 9 Mar 2022 19:22:49 +0800 Subject: [PATCH 226/619] esp32/boards: Add LOLIN C3 MINI ESP32-C3 based board. --- ports/esp32/boards/LOLIN_C3_MINI/board.json | 19 ++++++++++++++ ports/esp32/boards/LOLIN_C3_MINI/manifest.py | 2 ++ .../boards/LOLIN_C3_MINI/modules/c3mini.py | 26 +++++++++++++++++++ .../boards/LOLIN_C3_MINI/mpconfigboard.cmake | 11 ++++++++ .../boards/LOLIN_C3_MINI/mpconfigboard.h | 13 ++++++++++ .../boards/LOLIN_C3_MINI/sdkconfig.board | 9 +++++++ 6 files changed, 80 insertions(+) create mode 100644 ports/esp32/boards/LOLIN_C3_MINI/board.json create mode 100644 ports/esp32/boards/LOLIN_C3_MINI/manifest.py create mode 100644 ports/esp32/boards/LOLIN_C3_MINI/modules/c3mini.py create mode 100644 ports/esp32/boards/LOLIN_C3_MINI/mpconfigboard.cmake create mode 100644 ports/esp32/boards/LOLIN_C3_MINI/mpconfigboard.h create mode 100644 ports/esp32/boards/LOLIN_C3_MINI/sdkconfig.board diff --git a/ports/esp32/boards/LOLIN_C3_MINI/board.json b/ports/esp32/boards/LOLIN_C3_MINI/board.json new file mode 100644 index 0000000000..f47c672fa4 --- /dev/null +++ b/ports/esp32/boards/LOLIN_C3_MINI/board.json @@ -0,0 +1,19 @@ +{ + "deploy": [ + "../deploy_c3.md" + ], + "docs": "", + "features": [ + "BLE", + "USB-C", + "WiFi" + ], + "images": [ + "lolin_c3_mini.jpg" + ], + "mcu": "esp32c3", + "product": "C3 mini", + "thumbnail": "", + "url": "https://www.wemos.cc/en/latest/c3/c3_mini.html", + "vendor": "Wemos" +} diff --git a/ports/esp32/boards/LOLIN_C3_MINI/manifest.py b/ports/esp32/boards/LOLIN_C3_MINI/manifest.py new file mode 100644 index 0000000000..f993d4fa6b --- /dev/null +++ b/ports/esp32/boards/LOLIN_C3_MINI/manifest.py @@ -0,0 +1,2 @@ +include("$(PORT_DIR)/boards/manifest.py") +freeze("./modules") diff --git a/ports/esp32/boards/LOLIN_C3_MINI/modules/c3mini.py b/ports/esp32/boards/LOLIN_C3_MINI/modules/c3mini.py new file mode 100644 index 0000000000..ce167303ee --- /dev/null +++ b/ports/esp32/boards/LOLIN_C3_MINI/modules/c3mini.py @@ -0,0 +1,26 @@ +# LOLIN C3 MINI MicroPython Helper Library + +from micropython import const +from machine import Pin + +# Pin Assignments + +# SPI +SPI_MOSI = const(4) +SPI_MISO = const(3) +SPI_CLK = const(2) + +# I2C +I2C_SDA = const(8) +I2C_SCL = const(10) + +# LED +LED = const(7) + +# BUTTON +BUTTON = const(0) + +# Built-in peripherals + +led = Pin(LED, Pin.OUT, value=0) +button = Pin(BUTTON, Pin.IN, Pin.PULL_UP) diff --git a/ports/esp32/boards/LOLIN_C3_MINI/mpconfigboard.cmake b/ports/esp32/boards/LOLIN_C3_MINI/mpconfigboard.cmake new file mode 100644 index 0000000000..9fe3b6411d --- /dev/null +++ b/ports/esp32/boards/LOLIN_C3_MINI/mpconfigboard.cmake @@ -0,0 +1,11 @@ +set(IDF_TARGET esp32c3) + +set(SDKCONFIG_DEFAULTS + boards/sdkconfig.base + boards/sdkconfig.ble + boards/LOLIN_C3_MINI/sdkconfig.board +) + +if(NOT MICROPY_FROZEN_MANIFEST) + set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py) +endif() diff --git a/ports/esp32/boards/LOLIN_C3_MINI/mpconfigboard.h b/ports/esp32/boards/LOLIN_C3_MINI/mpconfigboard.h new file mode 100644 index 0000000000..5872bdc8ae --- /dev/null +++ b/ports/esp32/boards/LOLIN_C3_MINI/mpconfigboard.h @@ -0,0 +1,13 @@ +#define MICROPY_HW_BOARD_NAME "LOLIN_C3_MINI" +#define MICROPY_HW_MCU_NAME "ESP32-C3FH4" + +#define MICROPY_HW_ENABLE_SDCARD (0) +#define MICROPY_PY_MACHINE_DAC (0) +#define MICROPY_PY_MACHINE_I2S (0) + +#define MICROPY_HW_I2C0_SCL (10) +#define MICROPY_HW_I2C0_SDA (8) + +#define MICROPY_HW_SPI1_MOSI (4) +#define MICROPY_HW_SPI1_MISO (3) +#define MICROPY_HW_SPI1_SCK (2) diff --git a/ports/esp32/boards/LOLIN_C3_MINI/sdkconfig.board b/ports/esp32/boards/LOLIN_C3_MINI/sdkconfig.board new file mode 100644 index 0000000000..f0cbad00e4 --- /dev/null +++ b/ports/esp32/boards/LOLIN_C3_MINI/sdkconfig.board @@ -0,0 +1,9 @@ +CONFIG_ESP32C3_REV_MIN_3=y +CONFIG_ESP32C3_REV_MIN=3 +CONFIG_ESP32C3_BROWNOUT_DET=y +CONFIG_ESP32C3_BROWNOUT_DET_LVL_SEL_7= +CONFIG_ESP32C3_BROWNOUT_DET_LVL_SEL_4=y +CONFIG_ESP32C3_BROWNOUT_DET_LVL=4 +CONFIG_ESP_CONSOLE_UART_DEFAULT= +CONFIG_ESP_CONSOLE_USB_CDC= +CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y From 09b55dc297da2b4dc3915f2a4cc0e3b53ea07359 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Tue, 8 Mar 2022 11:40:26 +0100 Subject: [PATCH 227/619] esp32/machine_hw_spi: Use automatic DMA channel selection for SPI on C3. Addresses issue #8204. --- ports/esp32/machine_hw_spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/esp32/machine_hw_spi.c b/ports/esp32/machine_hw_spi.c index 1515b00744..3e720adb1a 100644 --- a/ports/esp32/machine_hw_spi.c +++ b/ports/esp32/machine_hw_spi.c @@ -268,7 +268,7 @@ STATIC void machine_hw_spi_init_internal( // Select DMA channel based on the hardware SPI host int dma_chan = 0; if (self->host == HSPI_HOST) { - #if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 + #if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 dma_chan = 3; #else dma_chan = 1; From afceb56ee2fe7c1741c6437b5d0d3fb7dbb54340 Mon Sep 17 00:00:00 2001 From: Algy Tynan Date: Tue, 15 Mar 2022 18:18:42 +1100 Subject: [PATCH 228/619] esp32/boards: Add support for LilyGO LoRa32 boards. Boards use ESP32-PICO-D4. Added pins for hardware versions v1.0, v1.2, v1.6 and v2.0. Signed-off-by: Algy Tynan --- .../boards/LILYGO_TTGO_LORA32/board.json | 23 ++++++ .../esp32/boards/LILYGO_TTGO_LORA32/board.md | 3 + .../boards/LILYGO_TTGO_LORA32/manifest.py | 4 + .../LILYGO_TTGO_LORA32/modules/lilygo_oled.py | 36 +++++++++ .../LILYGO_TTGO_LORA32/modules/lora32.py | 79 +++++++++++++++++++ .../LILYGO_TTGO_LORA32/mpconfigboard.cmake | 7 ++ .../boards/LILYGO_TTGO_LORA32/mpconfigboard.h | 2 + 7 files changed, 154 insertions(+) create mode 100644 ports/esp32/boards/LILYGO_TTGO_LORA32/board.json create mode 100644 ports/esp32/boards/LILYGO_TTGO_LORA32/board.md create mode 100644 ports/esp32/boards/LILYGO_TTGO_LORA32/manifest.py create mode 100644 ports/esp32/boards/LILYGO_TTGO_LORA32/modules/lilygo_oled.py create mode 100644 ports/esp32/boards/LILYGO_TTGO_LORA32/modules/lora32.py create mode 100644 ports/esp32/boards/LILYGO_TTGO_LORA32/mpconfigboard.cmake create mode 100644 ports/esp32/boards/LILYGO_TTGO_LORA32/mpconfigboard.h diff --git a/ports/esp32/boards/LILYGO_TTGO_LORA32/board.json b/ports/esp32/boards/LILYGO_TTGO_LORA32/board.json new file mode 100644 index 0000000000..7f4b227abf --- /dev/null +++ b/ports/esp32/boards/LILYGO_TTGO_LORA32/board.json @@ -0,0 +1,23 @@ +{ + "deploy": [ + "../deploy.md" + ], + "docs": "", + "features": [ + "BLE", + "LoRa", + "OLED", + "SDCard", + "USB-MICRO", + "WiFi" + ], + "id": "esp32", + "images": [ + "lilygo-ttgo-lora-32-v1-6.jpg" + ], + "mcu": "esp32", + "product": "LILYGO TTGO LoRa32", + "thumbnail": "", + "url": "http://www.lilygo.cn/prod_view.aspx?TypeId=50060&Id=1270&FId=t3:50060:3", + "vendor": "LILYGO" +} diff --git a/ports/esp32/boards/LILYGO_TTGO_LORA32/board.md b/ports/esp32/boards/LILYGO_TTGO_LORA32/board.md new file mode 100644 index 0000000000..b7f5c21fec --- /dev/null +++ b/ports/esp32/boards/LILYGO_TTGO_LORA32/board.md @@ -0,0 +1,3 @@ +The following files are daily firmware for the LILYGO TTGO LoRa32. + +Support for hardware versions v1.0, v1.2, v1.6 and v2.0. diff --git a/ports/esp32/boards/LILYGO_TTGO_LORA32/manifest.py b/ports/esp32/boards/LILYGO_TTGO_LORA32/manifest.py new file mode 100644 index 0000000000..0709f8597c --- /dev/null +++ b/ports/esp32/boards/LILYGO_TTGO_LORA32/manifest.py @@ -0,0 +1,4 @@ +include("$(PORT_DIR)/boards/manifest.py") +freeze("modules") + +freeze("$(MPY_DIR)/drivers/display", "ssd1306.py") diff --git a/ports/esp32/boards/LILYGO_TTGO_LORA32/modules/lilygo_oled.py b/ports/esp32/boards/LILYGO_TTGO_LORA32/modules/lilygo_oled.py new file mode 100644 index 0000000000..58072ee1b5 --- /dev/null +++ b/ports/esp32/boards/LILYGO_TTGO_LORA32/modules/lilygo_oled.py @@ -0,0 +1,36 @@ +from time import sleep_ms +from ssd1306 import SSD1306_I2C +import network + + +class OLED(SSD1306_I2C): + def __init__(self, i2c): + super().__init__(128, 32, i2c) + + def test(self): + self.fill(0) + self.fill_rect(0, 0, 32, 32, 1) + self.fill_rect(2, 2, 28, 28, 0) + self.vline(9, 8, 22, 1) + self.vline(16, 2, 22, 1) + self.vline(23, 8, 22, 1) + self.fill_rect(26, 24, 2, 4, 1) + self.text("MicroPython", 40, 0, 1) + self.text("SSD1306", 40, 12, 1) + self.text("OLED 128x32", 40, 24, 1) + self.show() + + def display_wifi(self): + self.fill(0) + self.text("Scan...", 0, 0, 1) + self.show() + + sta_if = network.WLAN(network.STA_IF) + sta_if.active(True) + _wifi = sta_if.scan() + + self.fill(0) + self.text(str(len(_wifi)) + " Networks", 0, 0, 1) + self.text(str(_wifi[0][3]) + " " + (_wifi[0][0]).decode("utf-8"), 0, 12, 1) + self.text(str(_wifi[1][3]) + " " + (_wifi[1][0]).decode("utf-8"), 0, 24, 1) + self.show() diff --git a/ports/esp32/boards/LILYGO_TTGO_LORA32/modules/lora32.py b/ports/esp32/boards/LILYGO_TTGO_LORA32/modules/lora32.py new file mode 100644 index 0000000000..067982a228 --- /dev/null +++ b/ports/esp32/boards/LILYGO_TTGO_LORA32/modules/lora32.py @@ -0,0 +1,79 @@ +"""LILYGO TTGO LoRa32 MicroPython Helper Library.""" + +from machine import Pin, SoftI2C, Signal + +from lilygo_oled import OLED + +from micropython import const + + +class Lora32Base: + """Base class defining common pins.""" + + def __init__(self, define_helpers=True): + # LORA + self.LORA_MOSI = const(27) + self.LORA_MISO = const(19) + self.LORA_SCLK = const(5) + self.LORA_CS = const(18) + self.LORA_DIO = const(26) + self.LORA_RST = const(23) + + # DAC + self.DAC1 = const(26) + + # LED + self.LED = const(25) + + # OLED + self.OLED_SDA = const(21) + self.OLED_SCL = const(22) + + if define_helpers: + self.create_helpers() + + def create_helpers(self): + self.led = Pin(self.LED, Pin.OUT) + self.i2c = SoftI2C(scl=Pin(self.OLED_SCL), sda=Pin(self.OLED_SDA)) + self.oled = OLED(self.i2c) + + +class Lora32v1_0(Lora32Base): + """Device Support for LILYGO TTGO LoRa32 v1.0.""" + + def __init__(self): + super().__init__(define_helpers=False) + + # v1.0 has different pins for the following + self.LORA_RST = const(14) + self.OLED_SDA = const(4) + self.OLED_SCL = const(15) + + # Also has a reset for the OLED that the others don't have + self.OLED_RST = const(16) + + super().create_helpers() + + +class Lora32v1_2(Lora32Base): + """Device Support for LILYGO TTGO LoRa32 v1.2 (T-Fox).""" + + def __init__(self): + super().__init__() + + # v1.2 Has a DS3231 RTC + self.DS3231_SDA = const(21) + self.DS3231_SCL = const(22) + + +class Lora32(Lora32Base): + """Device Support for LILYGO TTGO LoRa32 v1.6 and v2.0.""" + + def __init__(self): + super().__init__() + + # v1.6 and v2.0 support an SDCard + self.SD_CS = const(13) + self.SD_MOSI = const(15) + self.SD_MISO = const(2) + self.SD_SCLK = const(14) diff --git a/ports/esp32/boards/LILYGO_TTGO_LORA32/mpconfigboard.cmake b/ports/esp32/boards/LILYGO_TTGO_LORA32/mpconfigboard.cmake new file mode 100644 index 0000000000..c35e4e0743 --- /dev/null +++ b/ports/esp32/boards/LILYGO_TTGO_LORA32/mpconfigboard.cmake @@ -0,0 +1,7 @@ +set(SDKCONFIG_DEFAULTS + boards/sdkconfig.base + boards/sdkconfig.ble +) +if(NOT MICROPY_FROZEN_MANIFEST) + set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py) +endif() diff --git a/ports/esp32/boards/LILYGO_TTGO_LORA32/mpconfigboard.h b/ports/esp32/boards/LILYGO_TTGO_LORA32/mpconfigboard.h new file mode 100644 index 0000000000..1c7b6fd557 --- /dev/null +++ b/ports/esp32/boards/LILYGO_TTGO_LORA32/mpconfigboard.h @@ -0,0 +1,2 @@ +#define MICROPY_HW_BOARD_NAME "LILYGO TTGO LoRa32" +#define MICROPY_HW_MCU_NAME "ESP32" From 61c02e6500be7b174bb1c257f4798969fadab206 Mon Sep 17 00:00:00 2001 From: Kattni Rembor Date: Thu, 10 Mar 2022 13:15:49 -0500 Subject: [PATCH 229/619] esp32/machine_pin: Expose pin 20 for ESP32. This pin is available on some ESP32 packages. Signed-off-by: Kattni Rembor --- ports/esp32/machine_pin.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ports/esp32/machine_pin.c b/ports/esp32/machine_pin.c index 90dc03e69a..c22eb5d847 100644 --- a/ports/esp32/machine_pin.c +++ b/ports/esp32/machine_pin.c @@ -92,7 +92,11 @@ STATIC const machine_pin_obj_t machine_pin_obj[] = { #endif {{&machine_pin_type}, GPIO_NUM_18}, {{&machine_pin_type}, GPIO_NUM_19}, + #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 1, 0) + {{&machine_pin_type}, GPIO_NUM_20}, + #else {{NULL}, -1}, + #endif {{&machine_pin_type}, GPIO_NUM_21}, {{&machine_pin_type}, GPIO_NUM_22}, {{&machine_pin_type}, GPIO_NUM_23}, @@ -560,7 +564,11 @@ STATIC const machine_pin_irq_obj_t machine_pin_irq_object[] = { #endif {{&machine_pin_irq_type}, GPIO_NUM_18}, {{&machine_pin_irq_type}, GPIO_NUM_19}, + #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 1, 0) + {{&machine_pin_irq_type}, GPIO_NUM_20}, + #else {{NULL}, -1}, + #endif {{&machine_pin_irq_type}, GPIO_NUM_21}, {{&machine_pin_irq_type}, GPIO_NUM_22}, {{&machine_pin_irq_type}, GPIO_NUM_23}, From 65be5e072fbce4529b0def95e5aa39c8cf630c95 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 22 Mar 2022 10:45:15 +1100 Subject: [PATCH 230/619] esp8266/mpconfigport.h: Remove config values that are the defaults. This commit is a no-op in terms of functionality. Signed-off-by: Damien George --- ports/esp8266/mpconfigport.h | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/ports/esp8266/mpconfigport.h b/ports/esp8266/mpconfigport.h index ad73f465a3..0f1da51671 100644 --- a/ports/esp8266/mpconfigport.h +++ b/ports/esp8266/mpconfigport.h @@ -14,7 +14,6 @@ #define MICROPY_ALLOC_PARSE_RULE_INC (8) #define MICROPY_ALLOC_PARSE_RESULT_INC (8) #define MICROPY_ALLOC_PARSE_CHUNK_INIT (64) -#define MICROPY_MEM_STATS (0) #define MICROPY_DEBUG_PRINTER (&mp_debug_print) #define MICROPY_ENABLE_GC (1) #define MICROPY_ENABLE_FINALISER (1) @@ -24,7 +23,6 @@ #define MICROPY_REPL_EVENT_DRIVEN (0) #define MICROPY_REPL_AUTO_INDENT (1) #define MICROPY_HELPER_REPL (1) -#define MICROPY_HELPER_LEXER_UNIX (0) #define MICROPY_ENABLE_SOURCE_LINE (1) #define MICROPY_MODULE_BUILTIN_INIT (1) #define MICROPY_MODULE_WEAK_LINKS (1) @@ -34,32 +32,19 @@ #define MICROPY_PY_DESCRIPTORS (1) #define MICROPY_PY_BUILTINS_COMPLEX (0) #define MICROPY_PY_BUILTINS_STR_UNICODE (1) -#define MICROPY_PY_BUILTINS_BYTEARRAY (1) #define MICROPY_PY_BUILTINS_MEMORYVIEW (1) #define MICROPY_PY_BUILTINS_FROZENSET (1) -#define MICROPY_PY_BUILTINS_SET (1) -#define MICROPY_PY_BUILTINS_SLICE (1) -#define MICROPY_PY_BUILTINS_PROPERTY (1) #define MICROPY_PY_BUILTINS_ROUND_INT (1) #define MICROPY_PY_BUILTINS_INPUT (1) #define MICROPY_PY_BUILTINS_HELP (1) #define MICROPY_PY_BUILTINS_HELP_TEXT esp_help_text #define MICROPY_PY_BUILTINS_HELP_MODULES (1) #define MICROPY_PY___FILE__ (0) -#define MICROPY_PY_GC (1) -#define MICROPY_PY_ARRAY (1) #define MICROPY_PY_ARRAY_SLICE_ASSIGN (1) -#define MICROPY_PY_COLLECTIONS (1) #define MICROPY_PY_COLLECTIONS_DEQUE (1) #define MICROPY_PY_COLLECTIONS_ORDEREDDICT (1) -#define MICROPY_PY_MATH (1) -#define MICROPY_PY_CMATH (0) -#define MICROPY_PY_IO (1) #define MICROPY_PY_IO_IOBASE (1) -#define MICROPY_PY_STRUCT (1) -#define MICROPY_PY_SYS (1) #define MICROPY_PY_SYS_MAXSIZE (1) -#define MICROPY_PY_SYS_EXIT (1) #define MICROPY_PY_SYS_STDFILES (1) #define MICROPY_PY_UERRNO (1) #define MICROPY_PY_UBINASCII (1) @@ -103,7 +88,6 @@ #define MICROPY_PY_UOS_UNAME (1) #define MICROPY_PY_UOS_UNAME_RELEASE_DYNAMIC (1) #define MICROPY_PY_UOS_URANDOM (1) -#define MICROPY_CPYTHON_COMPAT (1) #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) #define MICROPY_WARNINGS (1) From eef7eae6b299f5a6469b977aae6425e33ee895af Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 22 Mar 2022 11:01:02 +1100 Subject: [PATCH 231/619] esp8266/modesp: Remove esp.info() function. The main functionality of this info function is available via the existing micropython.mem_info() and micropython.qstr_info() functions. The printing of the address space layout doesn't add much and removing esp.info() saves about 600 bytes. Signed-off-by: Damien George --- ports/esp8266/Makefile | 1 - ports/esp8266/boards/esp8266_common.ld | 1 - ports/esp8266/modesp.c | 1 - ports/esp8266/modpyb.c | 79 -------------------------- 4 files changed, 82 deletions(-) delete mode 100644 ports/esp8266/modpyb.c diff --git a/ports/esp8266/Makefile b/ports/esp8266/Makefile index fd2346d515..705f4f1eeb 100644 --- a/ports/esp8266/Makefile +++ b/ports/esp8266/Makefile @@ -90,7 +90,6 @@ SRC_C = \ uart.c \ esppwm.c \ espapa102.c \ - modpyb.c \ modmachine.c \ machine_bitstream.c \ machine_pin.c \ diff --git a/ports/esp8266/boards/esp8266_common.ld b/ports/esp8266/boards/esp8266_common.ld index ae1509faee..c2d62e9dd4 100644 --- a/ports/esp8266/boards/esp8266_common.ld +++ b/ports/esp8266/boards/esp8266_common.ld @@ -151,7 +151,6 @@ SECTIONS *help.o(.literal* .text*) *lexerstr32.o(.literal* .text*) *utils.o(.literal* .text*) - *modpyb.o(.literal*, .text*) *machine_pin.o(.literal*, .text*) *machine_pwm.o(.literal*, .text*) *machine_rtc.o(.literal*, .text*) diff --git a/ports/esp8266/modesp.c b/ports/esp8266/modesp.c index eb79d20bc5..0e2acaf5ff 100644 --- a/ports/esp8266/modesp.c +++ b/ports/esp8266/modesp.c @@ -359,7 +359,6 @@ STATIC const mp_rom_map_elem_t esp_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_freemem), MP_ROM_PTR(&esp_freemem_obj) }, { MP_ROM_QSTR(MP_QSTR_meminfo), MP_ROM_PTR(&esp_meminfo_obj) }, { MP_ROM_QSTR(MP_QSTR_check_fw), MP_ROM_PTR(&esp_check_fw_obj) }, - { MP_ROM_QSTR(MP_QSTR_info), MP_ROM_PTR(&pyb_info_obj) }, // TODO delete/rename/move elsewhere { MP_ROM_QSTR(MP_QSTR_malloc), MP_ROM_PTR(&esp_malloc_obj) }, { MP_ROM_QSTR(MP_QSTR_free), MP_ROM_PTR(&esp_free_obj) }, { MP_ROM_QSTR(MP_QSTR_esf_free_bufs), MP_ROM_PTR(&esp_esf_free_bufs_obj) }, diff --git a/ports/esp8266/modpyb.c b/ports/esp8266/modpyb.c deleted file mode 100644 index 0a23f6f9da..0000000000 --- a/ports/esp8266/modpyb.c +++ /dev/null @@ -1,79 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2014 Damien P. George - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include - -#include "py/gc.h" -#include "gccollect.h" -#include "modmachine.h" - -// The pyb module no longer exists since all functionality now appears -// elsewhere, in more standard places (eg time, machine modules). The -// only remaining function is pyb.info() which has been moved to the -// esp module, pending deletion/renaming/moving elsewhere. - -STATIC mp_obj_t pyb_info(size_t n_args, const mp_obj_t *args) { - // print info about memory - { - printf("_text_start=%p\n", &_text_start); - printf("_text_end=%p\n", &_text_end); - printf("_irom0_text_start=%p\n", &_irom0_text_start); - printf("_irom0_text_end=%p\n", &_irom0_text_end); - printf("_data_start=%p\n", &_data_start); - printf("_data_end=%p\n", &_data_end); - printf("_rodata_start=%p\n", &_rodata_start); - printf("_rodata_end=%p\n", &_rodata_end); - printf("_bss_start=%p\n", &_bss_start); - printf("_bss_end=%p\n", &_bss_end); - printf("_heap_start=%p\n", &_heap_start); - printf("_heap_end=%p\n", &_heap_end); - } - - // qstr info - { - mp_uint_t n_pool, n_qstr, n_str_data_bytes, n_total_bytes; - qstr_pool_info(&n_pool, &n_qstr, &n_str_data_bytes, &n_total_bytes); - printf("qstr:\n n_pool=" UINT_FMT "\n n_qstr=" UINT_FMT "\n n_str_data_bytes=" UINT_FMT "\n n_total_bytes=" UINT_FMT "\n", n_pool, n_qstr, n_str_data_bytes, n_total_bytes); - } - - // GC info - { - gc_info_t info; - gc_info(&info); - printf("GC:\n"); - printf(" " UINT_FMT " total\n", info.total); - printf(" " UINT_FMT " : " UINT_FMT "\n", info.used, info.free); - printf(" 1=" UINT_FMT " 2=" UINT_FMT " m=" UINT_FMT "\n", info.num_1block, info.num_2block, info.max_block); - } - - if (n_args == 1) { - // arg given means dump gc allocation table - gc_dump_alloc_table(); - } - - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_info_obj, 0, 1, pyb_info); From 7a9cf1f0fb7c43de766e18465a15d451dda6ef58 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 17 Mar 2022 17:07:49 +1100 Subject: [PATCH 232/619] stm32/sdcard: Use mp_hal_pin_input instead of HAL function. Signed-off-by: Damien George --- ports/stm32/sdcard.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/stm32/sdcard.c b/ports/stm32/sdcard.c index cb773f29db..cdec576dc2 100644 --- a/ports/stm32/sdcard.c +++ b/ports/stm32/sdcard.c @@ -249,7 +249,7 @@ bool sdcard_is_present(void) { } #endif #if defined(MICROPY_HW_SDCARD_DETECT_PIN) - return HAL_GPIO_ReadPin(MICROPY_HW_SDCARD_DETECT_PIN->gpio, MICROPY_HW_SDCARD_DETECT_PIN->pin_mask) == MICROPY_HW_SDCARD_DETECT_PRESENT; + return mp_hal_pin_read(MICROPY_HW_SDCARD_DETECT_PIN) == MICROPY_HW_SDCARD_DETECT_PRESENT; #else return true; #endif From 75d2bfcccf04e2740c51c345bc8a3145c2c2c7c7 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 17 Mar 2022 17:08:14 +1100 Subject: [PATCH 233/619] stm32/sdcard: Add sdcard_select_sd/mmc functions. So that C code can select which of SD or MMC to use. Signed-off-by: Damien George --- ports/stm32/sdcard.c | 8 ++++++++ ports/stm32/sdcard.h | 2 ++ 2 files changed, 10 insertions(+) diff --git a/ports/stm32/sdcard.c b/ports/stm32/sdcard.c index cdec576dc2..12c9eb4446 100644 --- a/ports/stm32/sdcard.c +++ b/ports/stm32/sdcard.c @@ -200,6 +200,14 @@ void sdcard_init(void) { #endif } +void sdcard_select_sd(void) { + pyb_sdmmc_flags |= PYB_SDMMC_FLAG_SD; +} + +void sdcard_select_mmc(void) { + pyb_sdmmc_flags |= PYB_SDMMC_FLAG_MMC; +} + STATIC void sdmmc_msp_init(void) { // enable SDIO clock SDMMC_CLK_ENABLE(); diff --git a/ports/stm32/sdcard.h b/ports/stm32/sdcard.h index e436ffffe1..a2e4e4517c 100644 --- a/ports/stm32/sdcard.h +++ b/ports/stm32/sdcard.h @@ -30,6 +30,8 @@ #define SDCARD_BLOCK_SIZE (512) void sdcard_init(void); +void sdcard_select_sd(void); +void sdcard_select_mmc(void); bool sdcard_is_present(void); bool sdcard_power_on(void); void sdcard_power_off(void); From 63c7593df66533fc0c529d2ed2a5da38042c8970 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 17 Mar 2022 17:09:01 +1100 Subject: [PATCH 234/619] stm32/main: Support SD cards without a partition table. Signed-off-by: Damien George --- ports/stm32/main.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/ports/stm32/main.c b/ports/stm32/main.c index 3291a80184..875584be8a 100644 --- a/ports/stm32/main.c +++ b/ports/stm32/main.c @@ -233,7 +233,7 @@ MP_NOINLINE STATIC bool init_flash_fs(uint reset_mode) { #if MICROPY_HW_SDCARD_MOUNT_AT_BOOT STATIC bool init_sdcard_fs(void) { bool first_part = true; - for (int part_num = 1; part_num <= 4; ++part_num) { + for (int part_num = 1; part_num <= 5; ++part_num) { // create vfs object fs_user_mount_t *vfs_fat = m_new_obj_maybe(fs_user_mount_t); mp_vfs_mount_t *vfs = m_new_obj_maybe(mp_vfs_mount_t); @@ -241,7 +241,16 @@ STATIC bool init_sdcard_fs(void) { break; } vfs_fat->blockdev.flags = MP_BLOCKDEV_FLAG_FREE_OBJ; - sdcard_init_vfs(vfs_fat, part_num); + if (part_num == 5) { + if (!first_part) { + break; + } + // partitions 1-4 couldn't be mounted, so try FATFS auto-detect mode + // which will work if there is no partition table, just a filesystem + sdcard_init_vfs(vfs_fat, 0); + } else { + sdcard_init_vfs(vfs_fat, part_num); + } // try to mount the partition FRESULT res = f_mount(&vfs_fat->fatfs); From f0be0de34185e77141205499f5f7563c6450df5f Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 17 Mar 2022 17:09:28 +1100 Subject: [PATCH 235/619] stm32/dma: Add option to disable auto-DMA-turn-off. Signed-off-by: Damien George --- ports/stm32/dma.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ports/stm32/dma.c b/ports/stm32/dma.c index 75c34325d7..cb8e555df4 100644 --- a/ports/stm32/dma.c +++ b/ports/stm32/dma.c @@ -34,6 +34,12 @@ #include "dma.h" #include "irq.h" +// When this option is enabled, the DMA will turn off automatically after +// a period of inactivity. +#ifndef MICROPY_HW_DMA_ENABLE_AUTO_TURN_OFF +#define MICROPY_HW_DMA_ENABLE_AUTO_TURN_OFF (1) +#endif + #define DMA_IDLE_ENABLED() (dma_idle.enabled != 0) #define DMA_SYSTICK_LOG2 (3) #define DMA_SYSTICK_MASK ((1 << DMA_SYSTICK_LOG2) - 1) @@ -653,7 +659,9 @@ static const uint8_t dma_irqn[NSTREAM] = { static DMA_HandleTypeDef *dma_handle[NSTREAM] = {NULL}; static uint8_t dma_last_sub_instance[NSTREAM]; static volatile uint32_t dma_enable_mask = 0; +#if MICROPY_HW_DMA_ENABLE_AUTO_TURN_OFF volatile dma_idle_count_t dma_idle; +#endif #define DMA_INVALID_CHANNEL 0xff // Value stored in dma_last_channel which means invalid @@ -1082,6 +1090,7 @@ void DMA2_Channel7_IRQHandler(void) { #endif +#if MICROPY_HW_DMA_ENABLE_AUTO_TURN_OFF static void dma_idle_handler(uint32_t tick); // Resets the idle counter for the DMA controller associated with dma_id. @@ -1089,6 +1098,7 @@ static void dma_tickle(dma_id_t dma_id) { dma_idle.counter[(dma_id < NSTREAMS_PER_CONTROLLER) ? 0 : 1] = 1; systick_enable_dispatch(SYSTICK_DISPATCH_DMA, dma_idle_handler); } +#endif static void dma_enable_clock(dma_id_t dma_id) { // We don't want dma_tick_handler() to turn off the clock right after we @@ -1143,7 +1153,9 @@ static void dma_disable_clock(dma_id_t dma_id) { // We just mark the clock as disabled here, but we don't actually disable it. // We wait for the timer to expire first, which means that back-to-back // transfers don't have to initialize as much. + #if MICROPY_HW_DMA_ENABLE_AUTO_TURN_OFF dma_tickle(dma_id); + #endif dma_enable_mask &= ~(1 << dma_id); } @@ -1250,6 +1262,7 @@ void dma_invalidate_channel(const dma_descr_t *dma_descr) { } } +#if MICROPY_HW_DMA_ENABLE_AUTO_TURN_OFF // Called from the SysTick handler // We use LSB of tick to select which controller to process static void dma_idle_handler(uint32_t tick) { @@ -1302,6 +1315,7 @@ static void dma_idle_handler(uint32_t tick) { } } } +#endif #if defined(STM32F0) || defined(STM32G4) || defined(STM32L0) || defined(STM32L4) From dc91024a7312894997020e57502cccf0ebbca4ff Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 17 Mar 2022 17:10:45 +1100 Subject: [PATCH 236/619] stm32/mboot: Add pragma for GCC to ignore array bounds warning. Signed-off-by: Damien George --- ports/stm32/mboot/main.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ports/stm32/mboot/main.c b/ports/stm32/mboot/main.c index 8d6e28f189..ac564d1d06 100644 --- a/ports/stm32/mboot/main.c +++ b/ports/stm32/mboot/main.c @@ -788,7 +788,15 @@ void i2c_slave_process_rx_end(i2c_slave_t *i2c) { if (buf[0] == I2C_CMD_ECHO) { ++len; } else if (buf[0] == I2C_CMD_GETID && len == 0) { + #if __GNUC__ >= 11 + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Warray-bounds" + #pragma GCC diagnostic ignored "-Wstringop-overread" + #endif memcpy(buf, (uint8_t*)MP_HAL_UNIQUE_ID_ADDRESS, 12); + #if __GNUC__ >= 11 + #pragma GCC diagnostic pop + #endif memcpy(buf + 12, MICROPY_HW_MCU_NAME, sizeof(MICROPY_HW_MCU_NAME)); memcpy(buf + 12 + sizeof(MICROPY_HW_MCU_NAME), MICROPY_HW_BOARD_NAME, sizeof(MICROPY_HW_BOARD_NAME) - 1); len = 12 + sizeof(MICROPY_HW_MCU_NAME) + sizeof(MICROPY_HW_BOARD_NAME) - 1; From a92d45c3df893c845571506afe6fbbfb3e7a85aa Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 17 Mar 2022 17:11:03 +1100 Subject: [PATCH 237/619] stm32/mboot: Always check the magic number to enter filesystem loading. Even if MBOOT_FSLOAD is disabled, mboot should still check for 0x70ad0080 so it can immediately return to the application if this feature is not enabled. Otherwise mboot will get stuck in DFU mode. Signed-off-by: Damien George --- ports/stm32/mboot/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/stm32/mboot/main.c b/ports/stm32/mboot/main.c index ac564d1d06..341cf18179 100644 --- a/ports/stm32/mboot/main.c +++ b/ports/stm32/mboot/main.c @@ -1539,8 +1539,8 @@ enter_bootloader: mboot_pack_init(); #endif - #if MBOOT_FSLOAD if ((initial_r0 & 0xffffff80) == 0x70ad0080) { + #if MBOOT_FSLOAD // Application passed through elements, validate then process them const uint8_t *elem_end = elem_search(ELEM_DATA_START, ELEM_TYPE_END); if (elem_end != NULL && elem_end[-1] == 0) { @@ -1553,11 +1553,11 @@ enter_bootloader: *status_ptr = ret; } } + #endif // Always reset because the application is expecting to resume led_state_all(0); leave_bootloader(); } - #endif dfu_init(); From 9b07d38c7e5919c8f26ee12b7bba71214ac43289 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 18 Mar 2022 16:22:26 +1100 Subject: [PATCH 238/619] stm32/mboot: Add support for 64-bit mboot address space for reads. If enabled via MBOOT_ADDRESS_SPACE_64BIT (it's disabled by default) then read addresses will be 64-bit. Signed-off-by: Damien George --- ports/stm32/mboot/README.md | 6 +++++- ports/stm32/mboot/fsload.c | 27 ++++++++++++++++++--------- ports/stm32/mboot/fwupdate.py | 7 +++++-- ports/stm32/mboot/main.c | 11 ++++++++--- ports/stm32/mboot/mboot.h | 16 ++++++++++++++-- ports/stm32/mboot/vfs.h | 14 +++++++------- ports/stm32/mboot/vfs_fat.c | 11 ++++++----- ports/stm32/mboot/vfs_lfs.c | 5 +++-- 8 files changed, 66 insertions(+), 31 deletions(-) diff --git a/ports/stm32/mboot/README.md b/ports/stm32/mboot/README.md index b85bc3e2ee..49385047a4 100644 --- a/ports/stm32/mboot/README.md +++ b/ports/stm32/mboot/README.md @@ -137,9 +137,13 @@ are located and what filename to program. The elements to use are: * MOUNT: type=2, len=10, payload=( ) +* MOUNT: type=2, len=14, payload=( ) + +* MOUNT: type=2, len=22, payload=( ) + * FSLOAD: type=3, len=1+n, payload=( ) -`u32` means unsigned 32-bit little-endian integer. +`u32`/`u64` mean unsigned 32-bit/64-bit little-endian integers. The firmware to load must be a gzip'd DfuSe file (.dfu.gz) and stored within a FAT or littlefs formatted partition. diff --git a/ports/stm32/mboot/fsload.c b/ports/stm32/mboot/fsload.c index 9ecc25b0be..14864bb895 100644 --- a/ports/stm32/mboot/fsload.c +++ b/ports/stm32/mboot/fsload.c @@ -222,20 +222,29 @@ int fsload_process(void) { // End of elements. return -MBOOT_ERRNO_FSLOAD_NO_MOUNT; } - uint32_t block_size; - if (elem[-1] == 10) { - // No block size given, use default. - block_size = MBOOT_FSLOAD_DEFAULT_BLOCK_SIZE; - } else if (elem[-1] == 14) { - // Block size given, extract it. - block_size = get_le32(&elem[10]); + mboot_addr_t base_addr; + mboot_addr_t byte_len; + uint32_t block_size = MBOOT_FSLOAD_DEFAULT_BLOCK_SIZE; + if (elem[-1] == 10 || elem[-1] == 14) { + // 32-bit base and length given, extract them. + base_addr = get_le32(&elem[2]); + byte_len = get_le32(&elem[6]); + if (elem[-1] == 14) { + // Block size given, extract it. + block_size = get_le32(&elem[10]); + } + #if MBOOT_ADDRESS_SPACE_64BIT + } else if (elem[-1] == 22) { + // 64-bit base and length given, and block size, so extract them. + base_addr = get_le64(&elem[2]); + byte_len = get_le64(&elem[10]); + block_size = get_le32(&elem[18]); + #endif } else { // Invalid MOUNT element. return -MBOOT_ERRNO_FSLOAD_INVALID_MOUNT; } if (elem[0] == mount_point) { - uint32_t base_addr = get_le32(&elem[2]); - uint32_t byte_len = get_le32(&elem[6]); int ret; union { #if MBOOT_VFS_FAT diff --git a/ports/stm32/mboot/fwupdate.py b/ports/stm32/mboot/fwupdate.py index 0e7ea01418..df1ff62846 100644 --- a/ports/stm32/mboot/fwupdate.py +++ b/ports/stm32/mboot/fwupdate.py @@ -227,7 +227,9 @@ def _create_element(kind, body): return bytes([kind, len(body)]) + body -def update_mpy(filename, fs_base, fs_len, fs_type=VFS_FAT, fs_blocksize=0, status_addr=None): +def update_mpy( + filename, fs_base, fs_len, fs_type=VFS_FAT, fs_blocksize=0, status_addr=None, addr_64bit=False +): # Check firmware is of .dfu or .dfu.gz type try: with open(filename, "rb") as f: @@ -243,9 +245,10 @@ def update_mpy(filename, fs_base, fs_len, fs_type=VFS_FAT, fs_blocksize=0, statu raise Exception("littlefs requires fs_blocksize parameter") mount_point = 1 + mount_encoding = "bdev_byte_len / 512) { - hw_read(ctx->bdev_base_addr + sector * SECSIZE, count * SECSIZE, buf); + if (0 <= sector && sector < ctx->bdev_num_blocks) { + mboot_addr_t addr = ctx->bdev_base_addr + (mboot_addr_t)sector * (mboot_addr_t)SECSIZE; + hw_read(addr, count * SECSIZE, buf); return RES_OK; } @@ -57,7 +58,7 @@ DRESULT disk_ioctl(void *pdrv, BYTE cmd, void *buf) { return RES_OK; case GET_SECTOR_COUNT: - *((DWORD*)buf) = ctx->bdev_byte_len / SECSIZE; + *((DWORD*)buf) = ctx->bdev_num_blocks; return RES_OK; case GET_SECTOR_SIZE: @@ -78,9 +79,9 @@ DRESULT disk_ioctl(void *pdrv, BYTE cmd, void *buf) { } } -int vfs_fat_mount(vfs_fat_context_t *ctx, uint32_t base_addr, uint32_t byte_len) { +int vfs_fat_mount(vfs_fat_context_t *ctx, mboot_addr_t base_addr, mboot_addr_t byte_len) { ctx->bdev_base_addr = base_addr; - ctx->bdev_byte_len = byte_len; + ctx->bdev_num_blocks = byte_len / SECSIZE; ctx->fatfs.drv = ctx; FRESULT res = f_mount(&ctx->fatfs); if (res != FR_OK) { diff --git a/ports/stm32/mboot/vfs_lfs.c b/ports/stm32/mboot/vfs_lfs.c index e7fd8ce63c..5aa400df40 100644 --- a/ports/stm32/mboot/vfs_lfs.c +++ b/ports/stm32/mboot/vfs_lfs.c @@ -70,7 +70,8 @@ static uint8_t lfs_lookahead_buffer[LFS_LOOKAHEAD_SIZE]; static int dev_read(const struct LFSx_API (config) * c, LFSx_API(block_t) block, LFSx_API(off_t) off, void *buffer, LFSx_API(size_t) size) { VFS_LFSx_CONTEXT_T *ctx = c->context; if (0 <= block && block < ctx->config.block_count) { - hw_read(ctx->bdev_base_addr + block * ctx->config.block_size + off, size, buffer); + mboot_addr_t addr = ctx->bdev_base_addr + (mboot_addr_t)block * (mboot_addr_t)ctx->config.block_size + (mboot_addr_t)off; + hw_read(addr, size, buffer); return LFSx_MACRO(_ERR_OK); } return LFSx_MACRO(_ERR_IO); @@ -88,7 +89,7 @@ static int dev_sync(const struct LFSx_API (config) * c) { return LFSx_MACRO(_ERR_OK); } -int VFS_LFSx_MOUNT(VFS_LFSx_CONTEXT_T *ctx, uint32_t base_addr, uint32_t byte_len, uint32_t block_size) { +int VFS_LFSx_MOUNT(VFS_LFSx_CONTEXT_T *ctx, mboot_addr_t base_addr, mboot_addr_t byte_len, uint32_t block_size) { ctx->bdev_base_addr = base_addr; struct LFSx_API (config) *config = &ctx->config; From e316306546c9a0f76512692eaacfee9fe38892c5 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 17 Mar 2022 17:11:44 +1100 Subject: [PATCH 239/619] stm32/mboot: Add support for reading from SD card. Tested on PYBV10 and PYBD_SF6, with MBOOT_FSLOAD enabled and programming new firmware from a .dfu.gz file stored on the SD card. Signed-off-by: Damien George --- ports/stm32/mboot/Makefile | 10 ++++++++++ ports/stm32/mboot/README.md | 12 ++++++++++++ ports/stm32/mboot/main.c | 17 +++++++++++++++++ ports/stm32/mboot/sdcard.c | 13 +++++++++++++ ports/stm32/sdcard.c | 4 ++++ tools/ci.sh | 1 + 6 files changed, 57 insertions(+) create mode 100644 ports/stm32/mboot/sdcard.c diff --git a/ports/stm32/mboot/Makefile b/ports/stm32/mboot/Makefile index ee75fb2afe..61a102d02d 100755 --- a/ports/stm32/mboot/Makefile +++ b/ports/stm32/mboot/Makefile @@ -110,6 +110,7 @@ SRC_C += \ fsload.c \ gzstream.c \ pack.c \ + sdcard.c \ vfs_fat.c \ vfs_lfs.c \ drivers/bus/softspi.c \ @@ -142,6 +143,7 @@ endif $(BUILD)/$(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_ll_usb.o: CFLAGS += -Wno-attributes SRC_HAL += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\ hal_cortex.c \ + hal_dma.c \ hal_flash.c \ hal_flash_ex.c \ hal_pcd.c \ @@ -151,6 +153,14 @@ SRC_HAL += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\ ll_usb.c \ ) +ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f4 f7 h7)) +SRC_HAL += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\ + hal_mmc.c \ + hal_sd.c \ + ll_sdmmc.c \ + ) +endif + SRC_USBDEV += $(addprefix ports/stm32/$(USBDEV_DIR)/,\ core/src/usbd_core.c \ core/src/usbd_ctlreq.c \ diff --git a/ports/stm32/mboot/README.md b/ports/stm32/mboot/README.md index 49385047a4..936b587608 100644 --- a/ports/stm32/mboot/README.md +++ b/ports/stm32/mboot/README.md @@ -59,6 +59,13 @@ How to use second one use the same configuration names as above but with `SPIFLASH2`, ie `MBOOT_SPIFLASH2_ADDR` etc. + SD card support (read-only, useful in combination with `MBOOT_FSLOAD`) + can be enabled with the following options: + + #define MBOOT_ADDRESS_SPACE_64BIT (1) + #define MBOOT_SDCARD_ADDR (0x100000000ULL) + #define MBOOT_SDCARD_BYTE_SIZE (0x400000000ULL) + To enable loading firmware from a filesystem use: #define MBOOT_FSLOAD (1) @@ -159,6 +166,11 @@ firmware.dfu.gz stored on the default FAT filesystem: The 0x80000000 value is the address understood by Mboot as the location of the external SPI flash, configured via `MBOOT_SPIFLASH_ADDR`. +To load a file from the SD card (see `MBOOT_SDCARD_ADDR`), assuming it is a +16GiB card, use: + + fwupdate.update_mpy('firmware.dfu.gz', 0x1_00000000, 0x4_00000000, addr_64bit=True) + Signed and encrypted DFU support -------------------------------- diff --git a/ports/stm32/mboot/main.c b/ports/stm32/mboot/main.c index 714b20ff09..976f0e54d2 100644 --- a/ports/stm32/mboot/main.c +++ b/ports/stm32/mboot/main.c @@ -37,6 +37,7 @@ #include "irq.h" #include "mboot.h" #include "powerctrl.h" +#include "sdcard.h" #include "dfu.h" #include "pack.h" @@ -653,6 +654,16 @@ void hw_read(mboot_addr_t addr, size_t len, uint8_t *buf) { mp_spiflash_read(MBOOT_SPIFLASH2_SPIFLASH, addr - MBOOT_SPIFLASH2_ADDR, len, buf); } else #endif + #if defined(MBOOT_SDCARD_ADDR) + if (MBOOT_SDCARD_ADDR <= addr && addr < MBOOT_SDCARD_ADDR + MBOOT_SDCARD_BYTE_SIZE) { + // Read address and length must be aligned. + if (addr % SDCARD_BLOCK_SIZE == 0 && len % SDCARD_BLOCK_SIZE == 0) { + sdcard_read_blocks(buf, (addr - MBOOT_SDCARD_ADDR) / SDCARD_BLOCK_SIZE, len / SDCARD_BLOCK_SIZE); + } else { + memset(buf, 0xff, len); + } + } else + #endif { // Other addresses, just read directly from memory memcpy(buf, (void *)(uintptr_t)addr, len); @@ -1540,6 +1551,12 @@ enter_bootloader: mp_spiflash_init(MBOOT_SPIFLASH2_SPIFLASH); #endif + #if defined(MBOOT_SDCARD_ADDR) + sdcard_init(); + sdcard_select_sd(); + sdcard_power_on(); + #endif + #if MBOOT_ENABLE_PACKING mboot_pack_init(); #endif diff --git a/ports/stm32/mboot/sdcard.c b/ports/stm32/mboot/sdcard.c new file mode 100644 index 0000000000..caf3dbf3c0 --- /dev/null +++ b/ports/stm32/mboot/sdcard.c @@ -0,0 +1,13 @@ +// Include relevant source files for SD card only when it's needed and +// configured at the C level (in mpconfigboard.h). + +#include "py/mpconfig.h" + +#if defined(MBOOT_SDCARD_ADDR) + +#define MICROPY_HW_DMA_ENABLE_AUTO_TURN_OFF (0) + +#include "ports/stm32/dma.c" +#include "ports/stm32/sdcard.c" + +#endif diff --git a/ports/stm32/sdcard.c b/ports/stm32/sdcard.c index 12c9eb4446..6f5892570b 100644 --- a/ports/stm32/sdcard.c +++ b/ports/stm32/sdcard.c @@ -688,6 +688,8 @@ mp_uint_t sdcard_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t n // // Expose the SD card or MMC as an object with the block protocol. +#if !BUILDING_MBOOT + // There are singleton SDCard/MMCard objects #if MICROPY_HW_ENABLE_SDCARD const mp_obj_base_t pyb_sdcard_obj = {&pyb_sdcard_type}; @@ -905,4 +907,6 @@ void sdcard_init_vfs(fs_user_mount_t *vfs, int part) { vfs->blockdev.u.ioctl[1] = MP_OBJ_FROM_PTR(&pyb_sdcard_obj); } +#endif // !BUILDING_MBOOT + #endif // MICROPY_HW_ENABLE_SDCARD || MICROPY_HW_ENABLE_MMCARD diff --git a/tools/ci.sh b/tools/ci.sh index 4f6717fe93..5aed8a7dff 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -292,6 +292,7 @@ function ci_stm32_pyb_build { make ${MAKEOPTS} -C ports/stm32 BOARD=PYBD_SF6 NANBOX=1 MICROPY_BLUETOOTH_NIMBLE=0 MICROPY_BLUETOOTH_BTSTACK=1 make ${MAKEOPTS} -C ports/stm32/mboot BOARD=PYBV10 CFLAGS_EXTRA='-DMBOOT_FSLOAD=1 -DMBOOT_VFS_LFS2=1' make ${MAKEOPTS} -C ports/stm32/mboot BOARD=PYBD_SF6 + make ${MAKEOPTS} -C ports/stm32/mboot BOARD=STM32F769DISC CFLAGS_EXTRA='-DMBOOT_ADDRESS_SPACE_64BIT=1 -DMBOOT_SDCARD_ADDR=0x100000000ULL -DMBOOT_SDCARD_BYTE_SIZE=0x400000000ULL -DMBOOT_FSLOAD=1 -DMBOOT_VFS_FAT=1' } function ci_stm32_nucleo_build { From bf89e336b9e0b195eacfe26f3a72989a8a9c06f2 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 22 Mar 2022 12:14:34 +1100 Subject: [PATCH 240/619] stm32/boards/PYBD_SF2: Turn on SD card in mboot init if SD enabled. Signed-off-by: Damien George --- ports/stm32/boards/PYBD_SF2/board_init.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ports/stm32/boards/PYBD_SF2/board_init.c b/ports/stm32/boards/PYBD_SF2/board_init.c index c3b54fa2f1..ab15783a7e 100644 --- a/ports/stm32/boards/PYBD_SF2/board_init.c +++ b/ports/stm32/boards/PYBD_SF2/board_init.c @@ -44,6 +44,11 @@ typedef struct _pyb_otp_t { void mboot_board_early_init(void) { // Enable 500mA on WBUS-DIP28 mp_hal_pin_config(pyb_pin_W23, MP_HAL_PIN_MODE_INPUT, MP_HAL_PIN_PULL_UP, 0); + + #if defined(MBOOT_SDCARD_ADDR) + // Configure EN_3V3 as an output pin so that SD card can be used + mp_hal_pin_config(pyb_pin_EN_3V3, MP_HAL_PIN_MODE_OUTPUT, MP_HAL_PIN_PULL_NONE, 0); + #endif } void board_early_init(void) { From 80055c2cdc1a5376c609e50f3ae0710f27146b15 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 22 Mar 2022 11:42:52 +1100 Subject: [PATCH 241/619] stm32/mboot/fwupdate.py: Simplify calculation of CRC32. Signed-off-by: Damien George --- ports/stm32/mboot/fwupdate.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/ports/stm32/mboot/fwupdate.py b/ports/stm32/mboot/fwupdate.py index df1ff62846..3e25351370 100644 --- a/ports/stm32/mboot/fwupdate.py +++ b/ports/stm32/mboot/fwupdate.py @@ -75,12 +75,10 @@ def dfu_read(filename): return None hdr = f.read(16) - crc = crc32(hdr[:-4], crc) - hdr = struct.unpack(" Date: Thu, 17 Jun 2021 12:51:37 +1000 Subject: [PATCH 242/619] stm32/mboot: Verify signature of fsload packed DFU files before writing. When verifying the DFU contents, the signature of signed/encrypted files is also now checked in this initial, dry-run stage. --- ports/stm32/mboot/fsload.c | 10 ++++------ ports/stm32/mboot/main.c | 16 ++++++++++------ ports/stm32/mboot/mboot.h | 2 +- ports/stm32/mboot/pack.c | 5 ++++- ports/stm32/mboot/pack.h | 2 +- 5 files changed, 20 insertions(+), 15 deletions(-) diff --git a/ports/stm32/mboot/fsload.c b/ports/stm32/mboot/fsload.c index 14864bb895..a54cfe4c73 100644 --- a/ports/stm32/mboot/fsload.c +++ b/ports/stm32/mboot/fsload.c @@ -151,13 +151,11 @@ static int fsload_program_file(bool write_to_flash) { if (res != l) { return -MBOOT_ERRNO_DFU_READ_ERROR; } - if (write_to_flash) { - res = do_write(elem_addr, buf, l); - if (res != 0) { - return res; - } - elem_addr += l; + res = do_write(elem_addr, buf, l, !write_to_flash); + if (res != 0) { + return res; } + elem_addr += l; s -= l; } diff --git a/ports/stm32/mboot/main.c b/ports/stm32/mboot/main.c index 976f0e54d2..10a09fe3e1 100644 --- a/ports/stm32/mboot/main.c +++ b/ports/stm32/mboot/main.c @@ -715,11 +715,15 @@ void do_read(mboot_addr_t addr, size_t len, uint8_t *buf) { #endif } -int do_write(uint32_t addr, const uint8_t *src8, size_t len) { +int do_write(uint32_t addr, const uint8_t *src8, size_t len, bool dry_run) { #if MBOOT_ENABLE_PACKING - return mboot_pack_write(addr, src8, len); + return mboot_pack_write(addr, src8, len, dry_run); #else - return hw_write(addr, src8, len); + if (dry_run) { + return 0; + } else { + return hw_write(addr, src8, len); + } #endif } @@ -844,7 +848,7 @@ void i2c_slave_process_rx_end(i2c_slave_t *i2c) { // Mark the 2 lower bits to indicate invalid app firmware buf[1] |= APP_VALIDITY_BITS; } - int ret = do_write(i2c_obj.cmd_wraddr, buf + 1, len); + int ret = do_write(i2c_obj.cmd_wraddr, buf + 1, len, false); if (ret < 0) { len = ret; } else { @@ -866,7 +870,7 @@ void i2c_slave_process_rx_end(i2c_slave_t *i2c) { len = -1; } else { buf &= ~APP_VALIDITY_BITS; - int ret = do_write(APPLICATION_ADDR, (void*)&buf, 4); + int ret = do_write(APPLICATION_ADDR, (void*)&buf, 4, false); if (ret < 0) { len = ret; } else { @@ -940,7 +944,7 @@ static int dfu_process_dnload(void) { } else if (dfu_context.wBlockNum > 1) { // write data to memory uint32_t addr = (dfu_context.wBlockNum - 2) * DFU_XFER_SIZE + dfu_context.addr; - ret = do_write(addr, dfu_context.buf, dfu_context.wLength); + ret = do_write(addr, dfu_context.buf, dfu_context.wLength, false); } if (ret == 0) { return DFU_STATE_DNLOAD_IDLE; diff --git a/ports/stm32/mboot/mboot.h b/ports/stm32/mboot/mboot.h index 36acb313b6..e64835bb44 100644 --- a/ports/stm32/mboot/mboot.h +++ b/ports/stm32/mboot/mboot.h @@ -113,7 +113,7 @@ int hw_write(uint32_t addr, const uint8_t *src8, size_t len); int do_page_erase(uint32_t addr, uint32_t *next_addr); void do_read(mboot_addr_t addr, size_t len, uint8_t *buf); -int do_write(uint32_t addr, const uint8_t *src8, size_t len); +int do_write(uint32_t addr, const uint8_t *src8, size_t len, bool dry_run); const uint8_t *elem_search(const uint8_t *elem, uint8_t elem_id); int fsload_process(void); diff --git a/ports/stm32/mboot/pack.c b/ports/stm32/mboot/pack.c index 88529ec50a..03ac6a4dc5 100644 --- a/ports/stm32/mboot/pack.c +++ b/ports/stm32/mboot/pack.c @@ -206,7 +206,7 @@ static int mboot_pack_handle_firmware(void) { } } -int mboot_pack_write(uint32_t addr, const uint8_t *src8, size_t len) { +int mboot_pack_write(uint32_t addr, const uint8_t *src8, size_t len, bool dry_run) { if (addr == APPLICATION_ADDR) { // Base address of main firmware, reset any previous state firmware_chunk_base_addr = 0; @@ -274,6 +274,9 @@ int mboot_pack_write(uint32_t addr, const uint8_t *src8, size_t len) { } // Signature passed, we have valid chunk. + if (dry_run) { + return 0; + } if (firmware_chunk_buf.header.format == MBOOT_PACK_CHUNK_META) { // Ignore META chunks. diff --git a/ports/stm32/mboot/pack.h b/ports/stm32/mboot/pack.h index 195f297ca1..3578d9d127 100644 --- a/ports/stm32/mboot/pack.h +++ b/ports/stm32/mboot/pack.h @@ -75,7 +75,7 @@ extern const uint8_t mboot_pack_secretbox_key[hydro_secretbox_KEYBYTES]; // Implementation void mboot_pack_init(void); -int mboot_pack_write(uint32_t addr, const uint8_t *src8, size_t len); +int mboot_pack_write(uint32_t addr, const uint8_t *src8, size_t len, bool dry_run); #endif // MBOOT_ENABLE_PACKING From bb0ca00b77b3ccb77bad857373df39556612444a Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Thu, 17 Jun 2021 12:55:18 +1000 Subject: [PATCH 243/619] stm32/mboot: Verify CRC32 of fsload DFU files before writing. --- ports/stm32/mboot/fsload.c | 13 ++++++++++++- ports/stm32/mboot/mboot.h | 1 + 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/ports/stm32/mboot/fsload.c b/ports/stm32/mboot/fsload.c index a54cfe4c73..2b674369f0 100644 --- a/ports/stm32/mboot/fsload.c +++ b/ports/stm32/mboot/fsload.c @@ -27,6 +27,7 @@ #include #include "py/mphal.h" +#include "lib/uzlib/tinf.h" #include "mboot.h" #include "pack.h" #include "vfs.h" @@ -74,6 +75,7 @@ static inline int input_stream_read(size_t len, uint8_t *buf) { static int fsload_program_file(bool write_to_flash) { // Parse DFU + uint32_t crc = 0xffffffff; uint8_t buf[512]; size_t file_offset; @@ -83,6 +85,7 @@ static int fsload_program_file(bool write_to_flash) { return -MBOOT_ERRNO_DFU_READ_ERROR; } file_offset = 11; + crc = uzlib_crc32(buf, 11, crc); // Validate header, version 1 if (memcmp(buf, "DfuSe\x01", 6) != 0) { @@ -103,6 +106,7 @@ static int fsload_program_file(bool write_to_flash) { return -MBOOT_ERRNO_DFU_READ_ERROR; } file_offset += 274; + crc = uzlib_crc32(buf, 274, crc); // Validate target header, with alt being 0 if (memcmp(buf, "Target\x00", 7) != 0) { @@ -123,6 +127,7 @@ static int fsload_program_file(bool write_to_flash) { return -MBOOT_ERRNO_DFU_READ_ERROR; } file_offset += 8; + crc = uzlib_crc32(buf, 8, crc); // Get element destination address and size uint32_t elem_addr = get_le32(buf); @@ -151,6 +156,7 @@ static int fsload_program_file(bool write_to_flash) { if (res != l) { return -MBOOT_ERRNO_DFU_READ_ERROR; } + crc = uzlib_crc32(buf, l, crc); res = do_write(elem_addr, buf, l, !write_to_flash); if (res != 0) { return res; @@ -176,7 +182,12 @@ static int fsload_program_file(bool write_to_flash) { return -MBOOT_ERRNO_DFU_READ_ERROR; } - // TODO validate CRC32 + // The final 4 bytes of the file are the expected CRC32, so including these + // bytes in the CRC calculation should yield a final CRC32 of 0. + crc = uzlib_crc32(buf, 16, crc); + if (crc != 0) { + return -MBOOT_ERRNO_DFU_INVALID_CRC; + } return 0; } diff --git a/ports/stm32/mboot/mboot.h b/ports/stm32/mboot/mboot.h index e64835bb44..0e04f67f4a 100644 --- a/ports/stm32/mboot/mboot.h +++ b/ports/stm32/mboot/mboot.h @@ -56,6 +56,7 @@ enum { MBOOT_ERRNO_DFU_INVALID_SIZE, MBOOT_ERRNO_DFU_TOO_MANY_TARGETS, MBOOT_ERRNO_DFU_READ_ERROR, + MBOOT_ERRNO_DFU_INVALID_CRC, MBOOT_ERRNO_FSLOAD_NO_FSLOAD = 220, MBOOT_ERRNO_FSLOAD_NO_MOUNT, From 66fe3d5cb5b42a48c0b72480bc1fc54994a91af1 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 22 Mar 2022 16:01:09 +1100 Subject: [PATCH 244/619] stm32: Support building for STM32F745. Signed-off-by: Damien George --- ports/stm32/adc.c | 1 + ports/stm32/pyb_i2c.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ports/stm32/adc.c b/ports/stm32/adc.c index 1d4beafa45..c5ca6306e4 100644 --- a/ports/stm32/adc.c +++ b/ports/stm32/adc.c @@ -146,6 +146,7 @@ #define VBAT_DIV (4) #elif defined(STM32F722xx) || defined(STM32F723xx) || \ defined(STM32F732xx) || defined(STM32F733xx) || \ + defined(STM32F745xx) || \ defined(STM32F746xx) || defined(STM32F765xx) || \ defined(STM32F767xx) || defined(STM32F769xx) #define VBAT_DIV (4) diff --git a/ports/stm32/pyb_i2c.c b/ports/stm32/pyb_i2c.c index 70a8e9f091..5cd7c0c8b0 100644 --- a/ports/stm32/pyb_i2c.c +++ b/ports/stm32/pyb_i2c.c @@ -137,7 +137,7 @@ const pyb_i2c_obj_t pyb_i2c_obj[] = { #define PYB_I2C_TIMINGR (1) -#if defined(STM32F746xx) +#if defined(STM32F745xx) || defined(STM32F746xx) // The value 0x40912732 was obtained from the DISCOVERY_I2Cx_TIMING constant // defined in the STM32F7Cube file Drivers/BSP/STM32F746G-Discovery/stm32f7456g_discovery.h From fe8b47e29fb40566fa35e97e02ab06cfab19bf6e Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 10 Mar 2022 00:54:44 +0200 Subject: [PATCH 245/619] ports: Allow boards to define additional network interfaces. --- ports/mimxrt/mpconfigport.h | 5 +++++ ports/rp2/mpconfigport.h | 5 +++++ ports/stm32/mpconfigport.h | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index ca8be01f78..b4df533f4a 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -267,8 +267,13 @@ extern const struct _mp_obj_type_t network_lan_type; SOCKET_BUILTIN_MODULE \ NETWORK_BUILTIN_MODULE \ +#ifndef MICROPY_BOARD_NETWORK_INTERFACES +#define MICROPY_BOARD_NETWORK_INTERFACES +#endif + #define MICROPY_PORT_NETWORK_INTERFACES \ MICROPY_HW_NIC_ETH \ + MICROPY_BOARD_NETWORK_INTERFACES \ #define MICROPY_HW_PIT_NUM_CHANNELS 3 diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h index 1fb0211e88..2919ed0181 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -192,8 +192,13 @@ extern const struct _mod_network_nic_type_t mod_network_nic_type_nina; SOCKET_BUILTIN_MODULE \ NETWORK_BUILTIN_MODULE \ +#ifndef MICROPY_BOARD_NETWORK_INTERFACES +#define MICROPY_BOARD_NETWORK_INTERFACES +#endif + #define MICROPY_PORT_NETWORK_INTERFACES \ MICROPY_HW_NIC_NINAW10 \ + MICROPY_BOARD_NETWORK_INTERFACES \ #ifndef MICROPY_BOARD_ROOT_POINTERS #define MICROPY_BOARD_ROOT_POINTERS diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h index 60f6d45fc4..18a2edc426 100644 --- a/ports/stm32/mpconfigport.h +++ b/ports/stm32/mpconfigport.h @@ -280,11 +280,16 @@ extern const struct _mod_network_nic_type_t mod_network_nic_type_cc3k; PYB_BUILTIN_MODULE \ STM_BUILTIN_MODULE \ +#ifndef MICROPY_BOARD_NETWORK_INTERFACES +#define MICROPY_BOARD_NETWORK_INTERFACES +#endif + #define MICROPY_PORT_NETWORK_INTERFACES \ MICROPY_HW_NIC_ETH \ MICROPY_HW_NIC_CYW43 \ MICROPY_HW_NIC_WIZNET5K \ MICROPY_HW_NIC_CC3K \ + MICROPY_BOARD_NETWORK_INTERFACES \ #define MP_STATE_PORT MP_STATE_VM From df86cef59ac3f92fcdc433e4679c8243a8c3159d Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Wed, 9 Mar 2022 23:28:52 +0200 Subject: [PATCH 246/619] rp2: Allow Overriding cmake frozen manifest from the command line. If MICROPY_FROZEN_MANIFEST is set from the cmake command line, then it will override the default and any manifest set by the board. --- ports/rp2/CMakeLists.txt | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt index 0ceb039675..bf3a51be99 100644 --- a/ports/rp2/CMakeLists.txt +++ b/ports/rp2/CMakeLists.txt @@ -33,7 +33,9 @@ if(NOT EXISTS ${MICROPY_BOARD_DIR}/mpconfigboard.cmake) message(FATAL_ERROR "Invalid MICROPY_BOARD specified: ${MICROPY_BOARD}") endif() -# Include board config +set(MICROPY_USER_FROZEN_MANIFEST ${MICROPY_FROZEN_MANIFEST}) + +# Include board config, it may override MICROPY_FROZEN_MANIFEST include(${MICROPY_BOARD_DIR}/mpconfigboard.cmake) # Set the PICO_BOARD if it's not already set (allow a board to override it). @@ -216,9 +218,13 @@ if (MICROPY_PY_NETWORK_NINAW10) ) endif() -# Define mpy-cross flags and frozen manifest +# Define mpy-cross flags set(MICROPY_CROSS_FLAGS -march=armv7m) -if (NOT MICROPY_FROZEN_MANIFEST) + +# Set the frozen manifest file +if (MICROPY_USER_FROZEN_MANIFEST) + set(MICROPY_FROZEN_MANIFEST ${MICROPY_USER_FROZEN_MANIFEST}) +elseif (NOT MICROPY_FROZEN_MANIFEST) set(MICROPY_FROZEN_MANIFEST ${PROJECT_SOURCE_DIR}/boards/manifest.py) endif() From 73623d04d586d90a53836ba2e46cba271dc846c8 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 25 Mar 2022 11:38:19 +1100 Subject: [PATCH 247/619] github/workflows: Add new workflow to test embedding example. Signed-off-by: Damien George --- .github/workflows/examples.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/examples.yml diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml new file mode 100644 index 0000000000..53a817af2b --- /dev/null +++ b/.github/workflows/examples.yml @@ -0,0 +1,21 @@ +name: Check examples + +on: + push: + pull_request: + paths: + - '.github/workflows/*.yml' + - 'examples/**' + - 'ports/unix/**' + - 'py/**' + - 'shared/**' + +jobs: + embedding: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Build + run: make -C examples/embedding + - name: Run + run: test "$(./examples/embedding/hello-embed)" = "Hello world of easy embedding!" From b083cdba2b59e7ad68a718cec9d015ab7aede4c8 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 25 Mar 2022 11:47:30 +1100 Subject: [PATCH 248/619] examples/embedding: Fix build with updated sys and os modules. Signed-off-by: Damien George --- examples/embedding/Makefile.upylib | 1 - examples/embedding/mpconfigport_minimal.h | 7 ++----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/examples/embedding/Makefile.upylib b/examples/embedding/Makefile.upylib index 14260a719f..80f7eed159 100644 --- a/examples/embedding/Makefile.upylib +++ b/examples/embedding/Makefile.upylib @@ -137,7 +137,6 @@ SRC_C = $(addprefix ports/unix/,\ unix_mphal.c \ input.c \ modmachine.c \ - modos.c \ moduselect.c \ alloc.c \ coverage.c \ diff --git a/examples/embedding/mpconfigport_minimal.h b/examples/embedding/mpconfigport_minimal.h index 07180a3225..6cc84dcbc1 100644 --- a/examples/embedding/mpconfigport_minimal.h +++ b/examples/embedding/mpconfigport_minimal.h @@ -75,21 +75,18 @@ #define MICROPY_PY_SYS_EXIT (0) #define MICROPY_PY_SYS_PLATFORM "linux" #define MICROPY_PY_SYS_MAXSIZE (0) +#define MICROPY_PY_SYS_PATH_DEFAULT ".frozen" #define MICROPY_PY_SYS_STDFILES (0) #define MICROPY_PY_CMATH (0) #define MICROPY_PY_UCTYPES (0) #define MICROPY_PY_UZLIB (0) #define MICROPY_PY_UJSON (0) +#define MICROPY_PY_UOS (1) #define MICROPY_PY_URE (0) #define MICROPY_PY_UHEAPQ (0) #define MICROPY_PY_UHASHLIB (0) #define MICROPY_PY_UBINASCII (0) -extern const struct _mp_obj_module_t mp_module_os; - -#define MICROPY_PORT_BUILTIN_MODULES \ - { MP_ROM_QSTR(MP_QSTR_uos), MP_ROM_PTR(&mp_module_os) }, \ - #define MICROPY_PORT_ROOT_POINTERS \ ////////////////////////////////////////// From e7a92c0e699a7c95ea322e3105f587ae1a71bedf Mon Sep 17 00:00:00 2001 From: David Lechner Date: Wed, 23 Mar 2022 20:39:00 -0500 Subject: [PATCH 249/619] tests/cmdline/cmd_showbc: Fix spelling of sequence. --- tests/cmdline/cmd_showbc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/cmdline/cmd_showbc.py b/tests/cmdline/cmd_showbc.py index a960c15c4a..e5874f990a 100644 --- a/tests/cmdline/cmd_showbc.py +++ b/tests/cmdline/cmd_showbc.py @@ -40,7 +40,7 @@ def f(): # slice a = b[::] - # sequenc unpacking + # sequence unpacking a, b = c a, *a = a From e7f6b9f4f76142ecd554106984a8520a0ada9d03 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Wed, 23 Mar 2022 14:18:23 -0500 Subject: [PATCH 250/619] tools/gen-cpydiff: Skip Black fmt comments. Since cpydiff is code used as documentation, there are cases where we may want to use Black's `fmt: on/off/skip` comments to avoid automatic formatting. However, we don't want these comments to be distracting in the generated documentation. This rewrites the code to omit these comments when generating the docs. Signed-off-by: David Lechner --- tools/gen-cpydiff.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tools/gen-cpydiff.py b/tools/gen-cpydiff.py index 1d458d3dcd..d4c8a5736d 100644 --- a/tools/gen-cpydiff.py +++ b/tools/gen-cpydiff.py @@ -85,6 +85,16 @@ def readfiles(): class_, desc, cause, workaround, code = [ x.rstrip() for x in list(filter(None, re.split(SPLIT, text))) ] + + # remove black `fmt: on/off/skip` comments + code = "".join( + # skip comments are inline, so we replace just the comment + re.sub(r"\s*# fmt: skip", "", x) + for x in code.splitlines(keepends=True) + # on/off comments are on their own line, so we omit the entire line + if not re.match(r"\s*# fmt: (on|off)\s*", x) + ) + output = Output(test, class_, desc, cause, workaround, code, "", "", "") files.append(output) except IndexError: From 768879f999f4739e8507ae6013b5e9271618656e Mon Sep 17 00:00:00 2001 From: David Lechner Date: Wed, 23 Mar 2022 14:29:52 -0500 Subject: [PATCH 251/619] py/smallint: Introduce MP_SMALL_INT_BITS macro. This adds a new MP_SMALL_INT_BITS macro that is a compile-time constant that contains the number of bits available in an MP_SMALL_INT. We can use this in place of the runtime function mp_small_int_bits(). Signed-off-by: David Lechner --- py/persistentcode.c | 19 ++----------------- py/smallint.h | 7 +++++++ 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/py/persistentcode.c b/py/persistentcode.c index 6110ae97f0..f64e383a61 100644 --- a/py/persistentcode.c +++ b/py/persistentcode.c @@ -48,21 +48,6 @@ #define MPY_FEATURE_ARCH_DYNAMIC MPY_FEATURE_ARCH #endif -#if MICROPY_PERSISTENT_CODE_LOAD || (MICROPY_PERSISTENT_CODE_SAVE && !MICROPY_DYNAMIC_COMPILER) -// The bytecode will depend on the number of bits in a small-int, and -// this function computes that (could make it a fixed constant, but it -// would need to be defined in mpconfigport.h). -STATIC int mp_small_int_bits(void) { - mp_int_t i = MP_SMALL_INT_MAX; - int n = 1; - while (i != 0) { - i >>= 1; - ++n; - } - return n; -} -#endif - typedef struct _bytecode_prelude_t { uint n_state; uint n_exc_stack; @@ -420,7 +405,7 @@ mp_compiled_module_t mp_raw_code_load(mp_reader_t *reader, mp_module_context_t * if (header[0] != 'M' || header[1] != MPY_VERSION || MPY_FEATURE_DECODE_FLAGS(header[2]) != MPY_FEATURE_FLAGS - || header[3] > mp_small_int_bits()) { + || header[3] > MP_SMALL_INT_BITS) { mp_raise_ValueError(MP_ERROR_TEXT("incompatible .mpy file")); } if (MPY_FEATURE_DECODE_ARCH(header[2]) != MP_NATIVE_ARCH_NONE) { @@ -609,7 +594,7 @@ void mp_raw_code_save(mp_compiled_module_t *cm, mp_print_t *print) { #if MICROPY_DYNAMIC_COMPILER mp_dynamic_compiler.small_int_bits, #else - mp_small_int_bits(), + MP_SMALL_INT_BITS, #endif }; if (cm->has_native) { diff --git a/py/smallint.h b/py/smallint.h index 67daf9b9fa..584e0018d1 100644 --- a/py/smallint.h +++ b/py/smallint.h @@ -61,6 +61,13 @@ #define MP_SMALL_INT_MAX ((mp_int_t)(~(MP_SMALL_INT_MIN))) +// https://stackoverflow.com/a/4589384/1976323 +// Number of bits in inttype_MAX, or in any (1< Date: Fri, 25 Mar 2022 12:35:49 +1100 Subject: [PATCH 252/619] py/objgenerator: Fix unused variables when native gen extracts prelude. Some compilers will warn about unused variables like scope_flags. So use MP_BC_PRELUDE_SIG_DECODE() which will silence these warnings. Signed-off-by: Damien George --- py/objgenerator.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/py/objgenerator.c b/py/objgenerator.c index cbe79e66a5..850de3fb68 100644 --- a/py/objgenerator.c +++ b/py/objgenerator.c @@ -103,13 +103,10 @@ STATIC mp_obj_t native_gen_wrap_call(mp_obj_t self_in, size_t n_args, size_t n_k // Extract n_state from the prelude. const uint8_t *ip = prelude_ptr; - size_t n_state, n_exc_stack_unused, scope_flags, n_pos_args, n_kwonly_args, n_def_args; - MP_BC_PRELUDE_SIG_DECODE_INTO(ip, n_state, n_exc_stack_unused, scope_flags, n_pos_args, n_kwonly_args, n_def_args); - size_t n_exc_stack = 0; + MP_BC_PRELUDE_SIG_DECODE(ip); - // Allocate the generator object, with room for local stack and exception stack - mp_obj_gen_instance_t *o = m_new_obj_var(mp_obj_gen_instance_t, byte, - n_state * sizeof(mp_obj_t) + n_exc_stack * sizeof(mp_exc_stack_t)); + // Allocate the generator object, with room for local stack (exception stack not needed). + mp_obj_gen_instance_t *o = m_new_obj_var(mp_obj_gen_instance_t, byte, n_state * sizeof(mp_obj_t)); o->base.type = &mp_type_gen_instance; // Parse the input arguments and set up the code state From 538c3c0a5540b4018cedd442b585666130fc8def Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 16 Mar 2022 09:37:58 +1100 Subject: [PATCH 253/619] py: Change jump opcodes to emit 1-byte jump offset when possible. This commit introduces changes: - All jump opcodes are changed to have variable length arguments, of either 1 or 2 bytes (previously they were fixed at 2 bytes). In most cases only 1 byte is needed to encode the short jump offset, saving bytecode size. - The bytecode emitter now selects 1 byte jump arguments when the jump offset is guaranteed to fit in 1 byte. This is achieved by checking if the code size changed during the last pass and, if it did (if it shrank), then requesting that the compiler make another pass to get the correct offsets of the now-smaller code. This can continue multiple times until the code stabilises. The code can only ever shrink so this iteration is guaranteed to complete. In most cases no extra passes are needed, the original 4 passes are enough to get it right by the 4th pass (because the 2nd pass computes roughly the correct labels and the 3rd pass computes the correct size for the jump argument). This change to the jump opcode encoding reduces .mpy files and RAM usage (when bytecode is in RAM) by about 2% on average. The performance of the VM is not impacted, at least within measurment of the performance benchmark suite. Code size is reduced for builds that include a decent amount of frozen bytecode. ARM Cortex-M builds without any frozen code increase by about 350 bytes. Signed-off-by: Damien George --- py/bc.c | 6 +- py/bc0.h | 34 +- py/compile.c | 12 +- py/emit.h | 6 +- py/emitbc.c | 141 ++++--- py/emitnative.c | 4 +- py/showbc.c | 24 +- py/vm.c | 26 +- tests/cmdline/cmd_parsetree.py.exp | 78 ++-- tests/cmdline/cmd_showbc.py.exp | 614 ++++++++++++++--------------- tests/run-tests.py | 1 + tests/stress/bytecode_limit.py | 26 ++ tests/stress/bytecode_limit.py.exp | 1 + tools/mpy-tool.py | 49 ++- 14 files changed, 584 insertions(+), 438 deletions(-) create mode 100644 tests/stress/bytecode_limit.py create mode 100644 tests/stress/bytecode_limit.py.exp diff --git a/py/bc.c b/py/bc.c index b98df39e28..2a21ffd4b7 100644 --- a/py/bc.c +++ b/py/bc.c @@ -335,7 +335,11 @@ uint mp_opcode_format(const byte *ip, size_t *opcode_size, bool count_var_uint) } } } else if (f == MP_BC_FORMAT_OFFSET) { - ip += 2; + if ((*ip & 0x80) == 0) { + ip += 1; + } else { + ip += 2; + } } ip += extra_byte; } diff --git a/py/bc0.h b/py/bc0.h index 842034ebfb..50c4954b08 100644 --- a/py/bc0.h +++ b/py/bc0.h @@ -28,6 +28,18 @@ // MicroPython bytecode opcodes, grouped based on the format of the opcode +// All opcodes are encoded as a byte with an optional argument. Arguments are +// variable-length encoded so they can be as small as possible. The possible +// encodings for arguments are (ip[0] is the opcode): +// +// - unsigned relative bytecode offset: +// - if ip[1] high bit is clear then: arg = ip[1] +// - if ip[1] high bit is set then: arg = ip[1] & 0x7f | ip[2] << 7 +// +// - signed relative bytecode offset: +// - if ip[1] high bit is clear then: arg = ip[1] - 0x40 +// - if ip[1] high bit is set then: arg = (ip[1] & 0x7f | ip[2] << 7) - 0x4000 + #define MP_BC_MASK_FORMAT (0xf0) #define MP_BC_MASK_EXTRA_BYTE (0x9e) @@ -101,17 +113,17 @@ #define MP_BC_ROT_TWO (MP_BC_BASE_BYTE_O + 0x0a) #define MP_BC_ROT_THREE (MP_BC_BASE_BYTE_O + 0x0b) -#define MP_BC_JUMP (MP_BC_BASE_JUMP_E + 0x02) // rel byte code offset, 16-bit signed, in excess -#define MP_BC_POP_JUMP_IF_TRUE (MP_BC_BASE_JUMP_E + 0x03) // rel byte code offset, 16-bit signed, in excess -#define MP_BC_POP_JUMP_IF_FALSE (MP_BC_BASE_JUMP_E + 0x04) // rel byte code offset, 16-bit signed, in excess -#define MP_BC_JUMP_IF_TRUE_OR_POP (MP_BC_BASE_JUMP_E + 0x05) // rel byte code offset, 16-bit signed, in excess -#define MP_BC_JUMP_IF_FALSE_OR_POP (MP_BC_BASE_JUMP_E + 0x06) // rel byte code offset, 16-bit signed, in excess -#define MP_BC_UNWIND_JUMP (MP_BC_BASE_JUMP_E + 0x00) // rel byte code offset, 16-bit signed, in excess; then a byte -#define MP_BC_SETUP_WITH (MP_BC_BASE_JUMP_E + 0x07) // rel byte code offset, 16-bit unsigned -#define MP_BC_SETUP_EXCEPT (MP_BC_BASE_JUMP_E + 0x08) // rel byte code offset, 16-bit unsigned -#define MP_BC_SETUP_FINALLY (MP_BC_BASE_JUMP_E + 0x09) // rel byte code offset, 16-bit unsigned -#define MP_BC_POP_EXCEPT_JUMP (MP_BC_BASE_JUMP_E + 0x0a) // rel byte code offset, 16-bit unsigned -#define MP_BC_FOR_ITER (MP_BC_BASE_JUMP_E + 0x0b) // rel byte code offset, 16-bit unsigned +#define MP_BC_UNWIND_JUMP (MP_BC_BASE_JUMP_E + 0x00) // signed relative bytecode offset; then a byte +#define MP_BC_JUMP (MP_BC_BASE_JUMP_E + 0x02) // signed relative bytecode offset +#define MP_BC_POP_JUMP_IF_TRUE (MP_BC_BASE_JUMP_E + 0x03) // signed relative bytecode offset +#define MP_BC_POP_JUMP_IF_FALSE (MP_BC_BASE_JUMP_E + 0x04) // signed relative bytecode offset +#define MP_BC_JUMP_IF_TRUE_OR_POP (MP_BC_BASE_JUMP_E + 0x05) // signed relative bytecode offset +#define MP_BC_JUMP_IF_FALSE_OR_POP (MP_BC_BASE_JUMP_E + 0x06) // signed relative bytecode offset +#define MP_BC_SETUP_WITH (MP_BC_BASE_JUMP_E + 0x07) // unsigned relative bytecode offset +#define MP_BC_SETUP_EXCEPT (MP_BC_BASE_JUMP_E + 0x08) // unsigned relative bytecode offset +#define MP_BC_SETUP_FINALLY (MP_BC_BASE_JUMP_E + 0x09) // unsigned relative bytecode offset +#define MP_BC_POP_EXCEPT_JUMP (MP_BC_BASE_JUMP_E + 0x0a) // unsigned relative bytecode offset +#define MP_BC_FOR_ITER (MP_BC_BASE_JUMP_E + 0x0b) // unsigned relative bytecode offset #define MP_BC_WITH_CLEANUP (MP_BC_BASE_BYTE_O + 0x0c) #define MP_BC_END_FINALLY (MP_BC_BASE_BYTE_O + 0x0d) #define MP_BC_GET_ITER (MP_BC_BASE_BYTE_O + 0x0e) diff --git a/py/compile.c b/py/compile.c index eb7389ec5f..d61dabb9a5 100644 --- a/py/compile.c +++ b/py/compile.c @@ -219,7 +219,7 @@ STATIC void mp_emit_common_start_pass(mp_emit_common_t *emit, pass_kind_t pass) } else if (pass > MP_PASS_STACK_SIZE) { emit->ct_cur_obj = emit->ct_cur_obj_base; } - if (pass == MP_PASS_EMIT) { + if (pass == MP_PASS_CODE_SIZE) { if (emit->ct_cur_child == 0) { emit->children = NULL; } else { @@ -3020,7 +3020,7 @@ STATIC void check_for_doc_string(compiler_t *comp, mp_parse_node_t pn) { #endif } -STATIC void compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) { +STATIC bool compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) { comp->pass = pass; comp->scope_cur = scope; comp->next_label = 0; @@ -3187,10 +3187,12 @@ STATIC void compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) { EMIT(return_value); } - EMIT(end_pass); + bool pass_complete = EMIT(end_pass); // make sure we match all the exception levels assert(comp->cur_except_level == 0); + + return pass_complete; } #if MICROPY_EMIT_INLINE_ASM @@ -3600,8 +3602,10 @@ mp_compiled_module_t mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr so } // final pass: emit code + // the emitter can request multiple of these passes if (comp->compile_error == MP_OBJ_NULL) { - compile_scope(comp, s, MP_PASS_EMIT); + while (!compile_scope(comp, s, MP_PASS_EMIT)) { + } } } } diff --git a/py/emit.h b/py/emit.h index 6f3593a0e8..d4aea2e4db 100644 --- a/py/emit.h +++ b/py/emit.h @@ -43,7 +43,7 @@ typedef enum { MP_PASS_SCOPE = 1, // work out id's and their kind, and number of labels MP_PASS_STACK_SIZE = 2, // work out maximum stack size MP_PASS_CODE_SIZE = 3, // work out code size and label offsets - MP_PASS_EMIT = 4, // emit code + MP_PASS_EMIT = 4, // emit code (may be run multiple times if the emitter requests it) } pass_kind_t; #define MP_EMIT_STAR_FLAG_SINGLE (0x01) @@ -116,7 +116,7 @@ typedef struct _emit_method_table_t { #endif void (*start_pass)(emit_t *emit, pass_kind_t pass, scope_t *scope); - void (*end_pass)(emit_t *emit); + bool (*end_pass)(emit_t *emit); bool (*last_emit_was_return_value)(emit_t *emit); void (*adjust_stack_size)(emit_t *emit, mp_int_t delta); void (*set_source_line)(emit_t *emit, mp_uint_t line); @@ -233,7 +233,7 @@ void emit_native_xtensa_free(emit_t *emit); void emit_native_xtensawin_free(emit_t *emit); void mp_emit_bc_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope); -void mp_emit_bc_end_pass(emit_t *emit); +bool mp_emit_bc_end_pass(emit_t *emit); bool mp_emit_bc_last_emit_was_return_value(emit_t *emit); void mp_emit_bc_adjust_stack_size(emit_t *emit, mp_int_t delta); void mp_emit_bc_set_source_line(emit_t *emit, mp_uint_t line); diff --git a/py/emitbc.c b/py/emitbc.c index c04701ca78..14a72e2765 100644 --- a/py/emitbc.c +++ b/py/emitbc.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include "py/mpstate.h" @@ -55,8 +56,8 @@ struct _emit_t { mp_uint_t last_source_line_offset; mp_uint_t last_source_line; - mp_uint_t max_num_labels; - mp_uint_t *label_offsets; + size_t max_num_labels; + size_t *label_offsets; size_t code_info_offset; size_t code_info_size; @@ -76,11 +77,11 @@ emit_t *emit_bc_new(mp_emit_common_t *emit_common) { void emit_bc_set_max_num_labels(emit_t *emit, mp_uint_t max_num_labels) { emit->max_num_labels = max_num_labels; - emit->label_offsets = m_new(mp_uint_t, emit->max_num_labels); + emit->label_offsets = m_new(size_t, emit->max_num_labels); } void emit_bc_free(emit_t *emit) { - m_del(mp_uint_t, emit->label_offsets, emit->max_num_labels); + m_del(size_t, emit->label_offsets, emit->max_num_labels); m_del_obj(emit_t, emit); } @@ -213,34 +214,55 @@ STATIC void emit_write_bytecode_byte_child(emit_t *emit, int stack_adj, byte b, #endif } -// unsigned labels are relative to ip following this instruction, stored as 16 bits -STATIC void emit_write_bytecode_byte_unsigned_label(emit_t *emit, int stack_adj, byte b1, mp_uint_t label) { +// Emit a jump opcode to a destination label. +// The offset to the label is relative to the ip following this instruction. +// The offset is encoded as either 1 or 2 bytes, depending on how big it is. +// The encoding of this jump opcode can change size from one pass to the next, +// but it must only ever decrease in size on successive passes. +STATIC void emit_write_bytecode_byte_label(emit_t *emit, int stack_adj, byte b1, mp_uint_t label) { mp_emit_bc_adjust_stack_size(emit, stack_adj); - mp_uint_t bytecode_offset; - if (emit->pass < MP_PASS_EMIT) { - bytecode_offset = 0; - } else { - bytecode_offset = emit->label_offsets[label] - emit->bytecode_offset - 3; - } - byte *c = emit_get_cur_to_write_bytecode(emit, 3); - c[0] = b1; - c[1] = bytecode_offset; - c[2] = bytecode_offset >> 8; -} -// signed labels are relative to ip following this instruction, stored as 16 bits, in excess -STATIC void emit_write_bytecode_byte_signed_label(emit_t *emit, int stack_adj, byte b1, mp_uint_t label) { - mp_emit_bc_adjust_stack_size(emit, stack_adj); - int bytecode_offset; - if (emit->pass < MP_PASS_EMIT) { - bytecode_offset = 0; - } else { - bytecode_offset = emit->label_offsets[label] - emit->bytecode_offset - 3 + 0x8000; + // Determine if the jump offset is signed or unsigned, based on the opcode. + const bool is_signed = b1 <= MP_BC_JUMP_IF_FALSE_OR_POP; + + // Default to a 2-byte encoding (the largest) with an unknown jump offset. + unsigned int jump_encoding_size = 1; + ssize_t bytecode_offset = 0; + + // Compute the jump size and offset only when code size is known. + if (emit->pass >= MP_PASS_CODE_SIZE) { + // The -2 accounts for this jump opcode taking 2 bytes (at least). + bytecode_offset = emit->label_offsets[label] - emit->bytecode_offset - 2; + + // Check if the bytecode_offset is small enough to use a 1-byte encoding. + if ((is_signed && -64 <= bytecode_offset && bytecode_offset <= 63) + || (!is_signed && (size_t)bytecode_offset <= 127)) { + // Use a 1-byte jump offset. + jump_encoding_size = 0; + } + + // Adjust the offset depending on the size of the encoding of the offset. + bytecode_offset -= jump_encoding_size; + + assert(is_signed || bytecode_offset >= 0); } - byte *c = emit_get_cur_to_write_bytecode(emit, 3); + + // Emit the opcode. + byte *c = emit_get_cur_to_write_bytecode(emit, 2 + jump_encoding_size); c[0] = b1; - c[1] = bytecode_offset; - c[2] = bytecode_offset >> 8; + if (jump_encoding_size == 0) { + if (is_signed) { + bytecode_offset += 0x40; + } + assert(0 <= bytecode_offset && bytecode_offset <= 0x7f); + c[1] = bytecode_offset; + } else { + if (is_signed) { + bytecode_offset += 0x4000; + } + c[1] = 0x80 | (bytecode_offset & 0x7f); + c[2] = bytecode_offset >> 7; + } } void mp_emit_bc_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope) { @@ -250,12 +272,6 @@ void mp_emit_bc_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope) { emit->scope = scope; emit->last_source_line_offset = 0; emit->last_source_line = 1; - #ifndef NDEBUG - // With debugging enabled labels are checked for unique assignment - if (pass < MP_PASS_EMIT && emit->label_offsets != NULL) { - memset(emit->label_offsets, -1, emit->max_num_labels * sizeof(mp_uint_t)); - } - #endif emit->bytecode_offset = 0; emit->code_info_offset = 0; @@ -315,9 +331,9 @@ void mp_emit_bc_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope) { } } -void mp_emit_bc_end_pass(emit_t *emit) { +bool mp_emit_bc_end_pass(emit_t *emit) { if (emit->pass == MP_PASS_SCOPE) { - return; + return true; } // check stack is back to zero size @@ -344,6 +360,20 @@ void mp_emit_bc_end_pass(emit_t *emit) { emit->code_base = m_new0(byte, emit->code_info_size + emit->bytecode_size); } else if (emit->pass == MP_PASS_EMIT) { + // Code info and/or bytecode can shrink during this pass. + assert(emit->code_info_offset <= emit->code_info_size); + assert(emit->bytecode_offset <= emit->bytecode_size); + + if (emit->code_info_offset != emit->code_info_size + || emit->bytecode_offset != emit->bytecode_size) { + // Code info and/or bytecode changed size in this pass, so request the + // compiler to do another pass with these updated sizes. + emit->code_info_size = emit->code_info_offset; + emit->bytecode_size = emit->bytecode_offset; + return false; + } + + // Bytecode is finalised, assign it to the raw code object. mp_emit_glue_assign_bytecode(emit->scope->raw_code, emit->code_base, #if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS emit->code_info_size + emit->bytecode_size, @@ -354,6 +384,8 @@ void mp_emit_bc_end_pass(emit_t *emit) { #endif emit->scope->scope_flags); } + + return true; } bool mp_emit_bc_last_emit_was_return_value(emit_t *emit) { @@ -396,15 +428,16 @@ void mp_emit_bc_label_assign(emit_t *emit, mp_uint_t l) { if (emit->pass == MP_PASS_SCOPE) { return; } + + // Label offsets can change from one pass to the next, but they must only + // decrease (ie code can only shrink). There will be multiple MP_PASS_EMIT + // stages until the labels no longer change, which is when the code size + // stays constant after a MP_PASS_EMIT. assert(l < emit->max_num_labels); - if (emit->pass < MP_PASS_EMIT) { - // assign label offset - assert(emit->label_offsets[l] == (mp_uint_t)-1); - emit->label_offsets[l] = emit->bytecode_offset; - } else { - // ensure label offset has not changed from MP_PASS_CODE_SIZE to MP_PASS_EMIT - assert(emit->label_offsets[l] == emit->bytecode_offset); - } + assert(emit->pass == MP_PASS_STACK_SIZE || emit->bytecode_offset <= emit->label_offsets[l]); + + // Assign label offset. + emit->label_offsets[l] = emit->bytecode_offset; } void mp_emit_bc_import(emit_t *emit, qstr qst, int kind) { @@ -552,22 +585,22 @@ void mp_emit_bc_rot_three(emit_t *emit) { } void mp_emit_bc_jump(emit_t *emit, mp_uint_t label) { - emit_write_bytecode_byte_signed_label(emit, 0, MP_BC_JUMP, label); + emit_write_bytecode_byte_label(emit, 0, MP_BC_JUMP, label); } void mp_emit_bc_pop_jump_if(emit_t *emit, bool cond, mp_uint_t label) { if (cond) { - emit_write_bytecode_byte_signed_label(emit, -1, MP_BC_POP_JUMP_IF_TRUE, label); + emit_write_bytecode_byte_label(emit, -1, MP_BC_POP_JUMP_IF_TRUE, label); } else { - emit_write_bytecode_byte_signed_label(emit, -1, MP_BC_POP_JUMP_IF_FALSE, label); + emit_write_bytecode_byte_label(emit, -1, MP_BC_POP_JUMP_IF_FALSE, label); } } void mp_emit_bc_jump_if_or_pop(emit_t *emit, bool cond, mp_uint_t label) { if (cond) { - emit_write_bytecode_byte_signed_label(emit, -1, MP_BC_JUMP_IF_TRUE_OR_POP, label); + emit_write_bytecode_byte_label(emit, -1, MP_BC_JUMP_IF_TRUE_OR_POP, label); } else { - emit_write_bytecode_byte_signed_label(emit, -1, MP_BC_JUMP_IF_FALSE_OR_POP, label); + emit_write_bytecode_byte_label(emit, -1, MP_BC_JUMP_IF_FALSE_OR_POP, label); } } @@ -581,9 +614,9 @@ void mp_emit_bc_unwind_jump(emit_t *emit, mp_uint_t label, mp_uint_t except_dept emit_write_bytecode_raw_byte(emit, MP_BC_POP_TOP); } } - emit_write_bytecode_byte_signed_label(emit, 0, MP_BC_JUMP, label & ~MP_EMIT_BREAK_FROM_FOR); + emit_write_bytecode_byte_label(emit, 0, MP_BC_JUMP, label & ~MP_EMIT_BREAK_FROM_FOR); } else { - emit_write_bytecode_byte_signed_label(emit, 0, MP_BC_UNWIND_JUMP, label & ~MP_EMIT_BREAK_FROM_FOR); + emit_write_bytecode_byte_label(emit, 0, MP_BC_UNWIND_JUMP, label & ~MP_EMIT_BREAK_FROM_FOR); emit_write_bytecode_raw_byte(emit, ((label & MP_EMIT_BREAK_FROM_FOR) ? 0x80 : 0) | except_depth); } } @@ -595,7 +628,7 @@ void mp_emit_bc_setup_block(emit_t *emit, mp_uint_t label, int kind) { // The SETUP_WITH opcode pops ctx_mgr from the top of the stack // and then pushes 3 entries: __exit__, ctx_mgr, as_value. int stack_adj = kind == MP_EMIT_SETUP_BLOCK_WITH ? 2 : 0; - emit_write_bytecode_byte_unsigned_label(emit, stack_adj, MP_BC_SETUP_WITH + kind, label); + emit_write_bytecode_byte_label(emit, stack_adj, MP_BC_SETUP_WITH + kind, label); } void mp_emit_bc_with_cleanup(emit_t *emit, mp_uint_t label) { @@ -617,7 +650,7 @@ void mp_emit_bc_get_iter(emit_t *emit, bool use_stack) { } void mp_emit_bc_for_iter(emit_t *emit, mp_uint_t label) { - emit_write_bytecode_byte_unsigned_label(emit, 1, MP_BC_FOR_ITER, label); + emit_write_bytecode_byte_label(emit, 1, MP_BC_FOR_ITER, label); } void mp_emit_bc_for_iter_end(emit_t *emit) { @@ -626,7 +659,7 @@ void mp_emit_bc_for_iter_end(emit_t *emit) { void mp_emit_bc_pop_except_jump(emit_t *emit, mp_uint_t label, bool within_exc_handler) { (void)within_exc_handler; - emit_write_bytecode_byte_unsigned_label(emit, 0, MP_BC_POP_EXCEPT_JUMP, label); + emit_write_bytecode_byte_label(emit, 0, MP_BC_POP_EXCEPT_JUMP, label); } void mp_emit_bc_unary_op(emit_t *emit, mp_unary_op_t op) { diff --git a/py/emitnative.c b/py/emitnative.c index ca34e89f64..bddd661428 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -652,7 +652,7 @@ static inline void emit_native_write_code_info_qstr(emit_t *emit, qstr qst) { mp_encode_uint(&emit->as->base, mp_asm_base_get_cur_to_write_bytes, mp_emit_common_use_qstr(emit->emit_common, qst)); } -STATIC void emit_native_end_pass(emit_t *emit) { +STATIC bool emit_native_end_pass(emit_t *emit) { emit_native_global_exc_exit(emit); if (!emit->do_viper_types) { @@ -736,6 +736,8 @@ STATIC void emit_native_end_pass(emit_t *emit) { #endif emit->scope->scope_flags, 0, 0); } + + return true; } STATIC bool emit_native_last_emit_was_return_value(emit_t *emit) { diff --git a/py/showbc.c b/py/showbc.c index 8430739d8e..178fa451a2 100644 --- a/py/showbc.c +++ b/py/showbc.c @@ -38,8 +38,28 @@ unum = (unum << 7) + (*ip & 0x7f); \ } while ((*ip++ & 0x80) != 0); \ } -#define DECODE_ULABEL do { unum = (ip[0] | (ip[1] << 8)); ip += 2; } while (0) -#define DECODE_SLABEL do { unum = (ip[0] | (ip[1] << 8)) - 0x8000; ip += 2; } while (0) + +#define DECODE_ULABEL \ + do { \ + if (ip[0] & 0x80) { \ + unum = ((ip[0] & 0x7f) | (ip[1] << 7)); \ + ip += 2; \ + } else { \ + unum = ip[0]; \ + ip += 1; \ + } \ + } while (0) + +#define DECODE_SLABEL \ + do { \ + if (ip[0] & 0x80) { \ + unum = ((ip[0] & 0x7f) | (ip[1] << 7)) - 0x4000; \ + ip += 2; \ + } else { \ + unum = ip[0] - 0x40; \ + ip += 1; \ + } \ + } while (0) #if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE diff --git a/py/vm.c b/py/vm.c index 497a569622..990009c00c 100644 --- a/py/vm.c +++ b/py/vm.c @@ -61,8 +61,30 @@ do { \ unum = (unum << 7) + (*ip & 0x7f); \ } while ((*ip++ & 0x80) != 0) -#define DECODE_ULABEL size_t ulab = (ip[0] | (ip[1] << 8)); ip += 2 -#define DECODE_SLABEL size_t slab = (ip[0] | (ip[1] << 8)) - 0x8000; ip += 2 + +#define DECODE_ULABEL \ + size_t ulab; \ + do { \ + if (ip[0] & 0x80) { \ + ulab = ((ip[0] & 0x7f) | (ip[1] << 7)); \ + ip += 2; \ + } else { \ + ulab = ip[0]; \ + ip += 1; \ + } \ + } while (0) + +#define DECODE_SLABEL \ + size_t slab; \ + do { \ + if (ip[0] & 0x80) { \ + slab = ((ip[0] & 0x7f) | (ip[1] << 7)) - 0x4000; \ + ip += 2; \ + } else { \ + slab = ip[0] - 0x40; \ + ip += 1; \ + } \ + } while (0) #if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE diff --git a/tests/cmdline/cmd_parsetree.py.exp b/tests/cmdline/cmd_parsetree.py.exp index 53d62a5dbd..6ee96b7caf 100644 --- a/tests/cmdline/cmd_parsetree.py.exp +++ b/tests/cmdline/cmd_parsetree.py.exp @@ -39,52 +39,52 @@ [ 13] \(rule\|arglist\)(164) (n=1) id(b) ---------------- -File cmdline/cmd_parsetree.py, code block '' (descriptor: \.\+, bytecode @\.\+ 64 bytes) -Raw bytecode (code_info_size=13, bytecode_size=51): - 20 16 01 60 28 23 23 24 24 24 24 24 25 2a 00 5f - 4b 05 00 16 02 42 f8 7f 51 16 03 10 04 16 05 23 - 00 16 06 23 01 16 07 23 02 16 08 23 03 16 09 22 - 80 7b 16 0a 23 04 14 0b 11 05 36 01 16 0c 51 63 +File cmdline/cmd_parsetree.py, code block '' (descriptor: \.\+, bytecode @\.\+ 62 bytes) +Raw bytecode (code_info_size=13, bytecode_size=49): + 20 16 01 60 27 22 23 24 24 24 24 24 25 2a 00 5f + 4b 04 16 02 42 3a 51 16 03 10 04 16 05 23 00 16 + 06 23 01 16 07 23 02 16 08 23 03 16 09 22 80 7b + 16 0a 23 04 14 0b 11 05 36 01 16 0c 51 63 arg names: (N_STATE 5) (N_EXC_STACK 0) bc=0 line=1 bc=0 line=4 - bc=8 line=5 - bc=11 line=6 - bc=14 line=7 - bc=18 line=8 - bc=22 line=9 - bc=26 line=10 - bc=30 line=11 - bc=34 line=12 - bc=39 line=13 + bc=7 line=5 + bc=9 line=6 + bc=12 line=7 + bc=16 line=8 + bc=20 line=9 + bc=24 line=10 + bc=28 line=11 + bc=32 line=12 + bc=37 line=13 00 BUILD_TUPLE 0 02 GET_ITER_STACK -03 FOR_ITER 11 -06 STORE_NAME i -08 JUMP 3 -11 LOAD_CONST_NONE -12 STORE_NAME a -14 LOAD_CONST_STRING 'str' -16 STORE_NAME b -18 LOAD_CONST_OBJ \.\+='a very long str that will not be interned' -20 STORE_NAME c -22 LOAD_CONST_OBJ \.\+=b'bytes' -24 STORE_NAME d -26 LOAD_CONST_OBJ \.\+=b'a very long bytes that will not be interned' -28 STORE_NAME e -30 LOAD_CONST_OBJ \.\+=123456789012345678901234567890 -32 STORE_NAME f -34 LOAD_CONST_SMALL_INT 123 -37 STORE_NAME g -39 LOAD_CONST_OBJ \.\+="fstring: '{}'" -41 LOAD_METHOD format -43 LOAD_NAME b -45 CALL_METHOD n=1 nkw=0 -47 STORE_NAME h -49 LOAD_CONST_NONE -50 RETURN_VALUE +03 FOR_ITER 9 +05 STORE_NAME i +07 JUMP 3 +09 LOAD_CONST_NONE +10 STORE_NAME a +12 LOAD_CONST_STRING 'str' +14 STORE_NAME b +16 LOAD_CONST_OBJ \.\+='a very long str that will not be interned' +18 STORE_NAME c +20 LOAD_CONST_OBJ \.\+=b'bytes' +22 STORE_NAME d +24 LOAD_CONST_OBJ \.\+=b'a very long bytes that will not be interned' +26 STORE_NAME e +28 LOAD_CONST_OBJ \.\+=123456789012345678901234567890 +30 STORE_NAME f +32 LOAD_CONST_SMALL_INT 123 +35 STORE_NAME g +37 LOAD_CONST_OBJ \.\+="fstring: '{}'" +39 LOAD_METHOD format +41 LOAD_NAME b +43 CALL_METHOD n=1 nkw=0 +45 STORE_NAME h +47 LOAD_CONST_NONE +48 RETURN_VALUE mem: total=\\d\+, current=\\d\+, peak=\\d\+ stack: \\d\+ out of \\d\+ GC: total: \\d\+, used: \\d\+, free: \\d\+ diff --git a/tests/cmdline/cmd_showbc.py.exp b/tests/cmdline/cmd_showbc.py.exp index 031820fcd9..92501e1248 100644 --- a/tests/cmdline/cmd_showbc.py.exp +++ b/tests/cmdline/cmd_showbc.py.exp @@ -47,10 +47,10 @@ arg names: 42 IMPORT_STAR 43 LOAD_CONST_NONE 44 RETURN_VALUE -File cmdline/cmd_showbc.py, code block 'f' (descriptor: \.\+, bytecode @\.\+ 48\[24\] bytes) -Raw bytecode (code_info_size=8\[46\], bytecode_size=398): +File cmdline/cmd_showbc.py, code block 'f' (descriptor: \.\+, bytecode @\.\+ 45\[68\] bytes) +Raw bytecode (code_info_size=8\[46\], bytecode_size=372): a8 12 9\[bf\] 03 02 60 60 26 22 24 64 22 26 25 25 24 - 26 23 63 22 22 25 23 23 31 6d 25 65 25 25 69 68 + 26 23 63 22 22 25 23 23 2f 6c 25 65 25 25 69 68 26 65 27 6a 62 20 23 62 2a 29 69 24 25 28 67 26 ######## \.\+81 63 @@ -80,65 +80,65 @@ arg names: bc=59 line=26 bc=62 line=27 bc=65 line=28 - bc=82 line=29 - bc=95 line=32 - bc=100 line=33 - bc=105 line=36 - bc=110 line=37 - bc=115 line=38 - bc=124 line=41 - bc=132 line=44 - bc=138 line=45 - bc=143 line=48 - bc=150 line=49 - bc=160 line=52 - bc=162 line=55 - bc=162 line=56 - bc=165 line=57 - bc=167 line=60 - bc=177 line=61 - bc=186 line=62 - bc=195 line=65 - bc=199 line=66 - bc=204 line=67 - bc=212 line=68 - bc=219 line=71 - bc=225 line=72 - bc=232 line=73 - bc=242 line=74 - bc=250 line=77 - bc=254 line=78 - bc=260 line=80 - bc=263 line=81 - bc=266 line=82 - bc=273 line=83 - bc=276 line=84 - bc=283 line=85 - bc=289 line=88 - bc=296 line=89 - bc=301 line=92 - bc=307 line=93 - bc=310 line=94 + bc=80 line=29 + bc=92 line=32 + bc=97 line=33 + bc=102 line=36 + bc=107 line=37 + bc=112 line=38 + bc=121 line=41 + bc=129 line=44 + bc=135 line=45 + bc=140 line=48 + bc=147 line=49 + bc=157 line=52 + bc=159 line=55 + bc=159 line=56 + bc=162 line=57 + bc=164 line=60 + bc=174 line=61 + bc=183 line=62 + bc=192 line=65 + bc=196 line=66 + bc=201 line=67 + bc=209 line=68 + bc=216 line=71 + bc=222 line=72 + bc=229 line=73 + bc=239 line=74 + bc=247 line=77 + bc=250 line=78 + bc=255 line=80 + bc=258 line=81 + bc=260 line=82 + bc=266 line=83 + bc=268 line=84 + bc=274 line=85 + bc=279 line=88 + bc=285 line=89 + bc=289 line=92 + bc=293 line=93 + bc=295 line=94 ######## - bc=321 line=96 - bc=329 line=98 - bc=332 line=99 - bc=335 line=100 - bc=338 line=101 + bc=303 line=96 + bc=310 line=98 + bc=313 line=99 + bc=315 line=100 + bc=317 line=101 ######## - bc=346 line=103 - bc=354 line=106 - bc=359 line=107 - bc=365 line=110 - bc=368 line=111 - bc=374 line=114 - bc=374 line=117 - bc=379 line=118 - bc=391 line=121 - bc=391 line=122 - bc=392 line=123 - bc=394 line=126 - bc=396 line=127 + bc=323 line=103 + bc=329 line=106 + bc=333 line=107 + bc=339 line=110 + bc=342 line=111 + bc=348 line=114 + bc=348 line=117 + bc=353 line=118 + bc=365 line=121 + bc=365 line=122 + bc=366 line=123 + bc=368 line=126 + bc=370 line=127 00 LOAD_CONST_NONE 01 LOAD_CONST_FALSE 02 BINARY_OP 27 __add__ @@ -195,216 +195,216 @@ arg names: 68 DUP_TOP 69 ROT_THREE 70 BINARY_OP 2 __eq__ -71 JUMP_IF_FALSE_OR_POP 79 -74 LOAD_FAST 1 -75 BINARY_OP 2 __eq__ -76 JUMP 81 -79 ROT_TWO -80 POP_TOP -81 STORE_FAST 10 -82 LOAD_FAST 0 -83 LOAD_DEREF 14 -85 BINARY_OP 2 __eq__ -86 JUMP_IF_FALSE_OR_POP 93 -89 LOAD_DEREF 14 -91 LOAD_FAST 1 -92 BINARY_OP 2 __eq__ -93 UNARY_OP 3 -94 STORE_FAST 10 -95 LOAD_DEREF 14 -97 LOAD_ATTR c -99 STORE_FAST 11 -100 LOAD_FAST 11 -101 LOAD_DEREF 14 -103 STORE_ATTR c -105 LOAD_DEREF 14 -107 LOAD_CONST_SMALL_INT 0 -108 LOAD_SUBSCR -109 STORE_FAST 12 -110 LOAD_FAST 12 -111 LOAD_DEREF 14 -113 LOAD_CONST_SMALL_INT 0 -114 STORE_SUBSCR -115 LOAD_DEREF 14 -117 LOAD_CONST_SMALL_INT 0 -118 DUP_TOP_TWO -119 LOAD_SUBSCR -120 LOAD_FAST 12 -121 BINARY_OP 14 __iadd__ -122 ROT_THREE -123 STORE_SUBSCR -124 LOAD_DEREF 14 -126 LOAD_CONST_NONE -127 LOAD_CONST_NONE -128 BUILD_SLICE 2 -130 LOAD_SUBSCR -131 STORE_FAST 0 -132 LOAD_FAST 1 -133 UNPACK_SEQUENCE 2 -135 STORE_FAST 0 -136 STORE_DEREF 14 -138 LOAD_FAST 0 -139 UNPACK_EX 1 -141 STORE_FAST 0 -142 STORE_FAST 0 -143 LOAD_DEREF 14 -145 LOAD_FAST 0 -146 ROT_TWO -147 STORE_FAST 0 -148 STORE_DEREF 14 -150 LOAD_FAST 1 -151 LOAD_DEREF 14 -153 LOAD_FAST 0 -154 ROT_THREE -155 ROT_TWO -156 STORE_FAST 0 -157 STORE_DEREF 14 -159 STORE_FAST 1 -160 DELETE_FAST 0 -162 LOAD_FAST 0 -163 STORE_GLOBAL gl -165 DELETE_GLOBAL gl -167 LOAD_FAST 14 -168 LOAD_FAST 15 -169 MAKE_CLOSURE \.\+ 2 -172 LOAD_FAST 2 -173 GET_ITER -174 CALL_FUNCTION n=1 nkw=0 -176 STORE_FAST 0 -177 LOAD_FAST 14 -178 LOAD_FAST 15 -179 MAKE_CLOSURE \.\+ 2 -182 LOAD_FAST 2 -183 CALL_FUNCTION n=1 nkw=0 -185 STORE_FAST 0 -186 LOAD_FAST 14 -187 LOAD_FAST 15 -188 MAKE_CLOSURE \.\+ 2 -191 LOAD_FAST 2 -192 CALL_FUNCTION n=1 nkw=0 -194 STORE_FAST 0 -195 LOAD_FAST 0 -196 CALL_FUNCTION n=0 nkw=0 -198 POP_TOP -199 LOAD_FAST 0 -200 LOAD_CONST_SMALL_INT 1 -201 CALL_FUNCTION n=1 nkw=0 -203 POP_TOP -204 LOAD_FAST 0 -205 LOAD_CONST_STRING 'b' -207 LOAD_CONST_SMALL_INT 1 -208 CALL_FUNCTION n=0 nkw=1 -211 POP_TOP -212 LOAD_FAST 0 -213 LOAD_DEREF 14 -215 LOAD_NULL -216 CALL_FUNCTION_VAR_KW n=0 nkw=0 -218 POP_TOP -219 LOAD_FAST 0 -220 LOAD_METHOD b -222 CALL_METHOD n=0 nkw=0 -224 POP_TOP -225 LOAD_FAST 0 -226 LOAD_METHOD b -228 LOAD_CONST_SMALL_INT 1 -229 CALL_METHOD n=1 nkw=0 -231 POP_TOP -232 LOAD_FAST 0 -233 LOAD_METHOD b -235 LOAD_CONST_STRING 'c' -237 LOAD_CONST_SMALL_INT 1 -238 CALL_METHOD n=0 nkw=1 -241 POP_TOP -242 LOAD_FAST 0 -243 LOAD_METHOD b -245 LOAD_FAST 1 -246 LOAD_NULL -247 CALL_METHOD_VAR_KW n=0 nkw=0 -249 POP_TOP -250 LOAD_FAST 0 -251 POP_JUMP_IF_FALSE 260 -254 LOAD_DEREF 16 -256 POP_TOP -257 JUMP 263 -260 LOAD_GLOBAL y +71 JUMP_IF_FALSE_OR_POP 77 +73 LOAD_FAST 1 +74 BINARY_OP 2 __eq__ +75 JUMP 79 +77 ROT_TWO +78 POP_TOP +79 STORE_FAST 10 +80 LOAD_FAST 0 +81 LOAD_DEREF 14 +83 BINARY_OP 2 __eq__ +84 JUMP_IF_FALSE_OR_POP 90 +86 LOAD_DEREF 14 +88 LOAD_FAST 1 +89 BINARY_OP 2 __eq__ +90 UNARY_OP 3 +91 STORE_FAST 10 +92 LOAD_DEREF 14 +94 LOAD_ATTR c +96 STORE_FAST 11 +97 LOAD_FAST 11 +98 LOAD_DEREF 14 +100 STORE_ATTR c +102 LOAD_DEREF 14 +104 LOAD_CONST_SMALL_INT 0 +105 LOAD_SUBSCR +106 STORE_FAST 12 +107 LOAD_FAST 12 +108 LOAD_DEREF 14 +110 LOAD_CONST_SMALL_INT 0 +111 STORE_SUBSCR +112 LOAD_DEREF 14 +114 LOAD_CONST_SMALL_INT 0 +115 DUP_TOP_TWO +116 LOAD_SUBSCR +117 LOAD_FAST 12 +118 BINARY_OP 14 __iadd__ +119 ROT_THREE +120 STORE_SUBSCR +121 LOAD_DEREF 14 +123 LOAD_CONST_NONE +124 LOAD_CONST_NONE +125 BUILD_SLICE 2 +127 LOAD_SUBSCR +128 STORE_FAST 0 +129 LOAD_FAST 1 +130 UNPACK_SEQUENCE 2 +132 STORE_FAST 0 +133 STORE_DEREF 14 +135 LOAD_FAST 0 +136 UNPACK_EX 1 +138 STORE_FAST 0 +139 STORE_FAST 0 +140 LOAD_DEREF 14 +142 LOAD_FAST 0 +143 ROT_TWO +144 STORE_FAST 0 +145 STORE_DEREF 14 +147 LOAD_FAST 1 +148 LOAD_DEREF 14 +150 LOAD_FAST 0 +151 ROT_THREE +152 ROT_TWO +153 STORE_FAST 0 +154 STORE_DEREF 14 +156 STORE_FAST 1 +157 DELETE_FAST 0 +159 LOAD_FAST 0 +160 STORE_GLOBAL gl +162 DELETE_GLOBAL gl +164 LOAD_FAST 14 +165 LOAD_FAST 15 +166 MAKE_CLOSURE \.\+ 2 +169 LOAD_FAST 2 +170 GET_ITER +171 CALL_FUNCTION n=1 nkw=0 +173 STORE_FAST 0 +174 LOAD_FAST 14 +175 LOAD_FAST 15 +176 MAKE_CLOSURE \.\+ 2 +179 LOAD_FAST 2 +180 CALL_FUNCTION n=1 nkw=0 +182 STORE_FAST 0 +183 LOAD_FAST 14 +184 LOAD_FAST 15 +185 MAKE_CLOSURE \.\+ 2 +188 LOAD_FAST 2 +189 CALL_FUNCTION n=1 nkw=0 +191 STORE_FAST 0 +192 LOAD_FAST 0 +193 CALL_FUNCTION n=0 nkw=0 +195 POP_TOP +196 LOAD_FAST 0 +197 LOAD_CONST_SMALL_INT 1 +198 CALL_FUNCTION n=1 nkw=0 +200 POP_TOP +201 LOAD_FAST 0 +202 LOAD_CONST_STRING 'b' +204 LOAD_CONST_SMALL_INT 1 +205 CALL_FUNCTION n=0 nkw=1 +208 POP_TOP +209 LOAD_FAST 0 +210 LOAD_DEREF 14 +212 LOAD_NULL +213 CALL_FUNCTION_VAR_KW n=0 nkw=0 +215 POP_TOP +216 LOAD_FAST 0 +217 LOAD_METHOD b +219 CALL_METHOD n=0 nkw=0 +221 POP_TOP +222 LOAD_FAST 0 +223 LOAD_METHOD b +225 LOAD_CONST_SMALL_INT 1 +226 CALL_METHOD n=1 nkw=0 +228 POP_TOP +229 LOAD_FAST 0 +230 LOAD_METHOD b +232 LOAD_CONST_STRING 'c' +234 LOAD_CONST_SMALL_INT 1 +235 CALL_METHOD n=0 nkw=1 +238 POP_TOP +239 LOAD_FAST 0 +240 LOAD_METHOD b +242 LOAD_FAST 1 +243 LOAD_NULL +244 CALL_METHOD_VAR_KW n=0 nkw=0 +246 POP_TOP +247 LOAD_FAST 0 +248 POP_JUMP_IF_FALSE 255 +250 LOAD_DEREF 16 +252 POP_TOP +253 JUMP 258 +255 LOAD_GLOBAL y +257 POP_TOP +258 JUMP 263 +260 LOAD_DEREF 14 262 POP_TOP -263 JUMP 269 -266 LOAD_DEREF 14 -268 POP_TOP -269 LOAD_FAST 0 -270 POP_JUMP_IF_TRUE 266 -273 JUMP 279 -276 LOAD_DEREF 14 -278 POP_TOP -279 LOAD_FAST 0 -280 POP_JUMP_IF_FALSE 276 -283 LOAD_FAST 0 -284 JUMP_IF_TRUE_OR_POP 288 -287 LOAD_FAST 0 -288 STORE_FAST 0 -289 LOAD_DEREF 14 -291 GET_ITER_STACK -292 FOR_ITER 301 -295 STORE_FAST 0 -296 LOAD_FAST 1 -297 POP_TOP -298 JUMP 292 -301 SETUP_FINALLY 329 -304 SETUP_EXCEPT 320 -307 JUMP 313 -310 JUMP 317 -313 LOAD_FAST 0 -314 POP_JUMP_IF_TRUE 310 -317 POP_EXCEPT_JUMP 328 -320 POP_TOP -321 LOAD_DEREF 14 -323 POP_TOP -324 POP_EXCEPT_JUMP 328 -327 END_FINALLY -328 LOAD_CONST_NONE -329 LOAD_FAST 1 -330 POP_TOP -331 END_FINALLY -332 JUMP 350 -335 SETUP_EXCEPT 345 -338 UNWIND_JUMP 354 1 -342 POP_EXCEPT_JUMP 350 -345 POP_TOP -346 POP_EXCEPT_JUMP 350 -349 END_FINALLY -350 LOAD_FAST 0 -351 POP_JUMP_IF_TRUE 335 -354 LOAD_FAST 0 -355 SETUP_WITH 363 -358 POP_TOP -359 LOAD_DEREF 14 -361 POP_TOP -362 LOAD_CONST_NONE -363 WITH_CLEANUP -364 END_FINALLY -365 LOAD_CONST_SMALL_INT 1 -366 STORE_DEREF 16 -368 LOAD_FAST_N 16 -370 MAKE_CLOSURE \.\+ 1 -373 STORE_FAST 13 -374 LOAD_CONST_SMALL_INT 0 -375 LOAD_CONST_NONE -376 IMPORT_NAME 'a' -378 STORE_FAST 0 -379 LOAD_CONST_SMALL_INT 0 -380 LOAD_CONST_STRING 'b' -382 BUILD_TUPLE 1 -384 IMPORT_NAME 'a' -386 IMPORT_FROM 'b' -388 STORE_DEREF 14 -390 POP_TOP -391 RAISE_LAST -392 LOAD_CONST_SMALL_INT 1 -393 RAISE_OBJ -394 LOAD_CONST_NONE -395 RETURN_VALUE -396 LOAD_CONST_SMALL_INT 1 -397 RETURN_VALUE +263 LOAD_FAST 0 +264 POP_JUMP_IF_TRUE 260 +266 JUMP 271 +268 LOAD_DEREF 14 +270 POP_TOP +271 LOAD_FAST 0 +272 POP_JUMP_IF_FALSE 268 +274 LOAD_FAST 0 +275 JUMP_IF_TRUE_OR_POP 278 +277 LOAD_FAST 0 +278 STORE_FAST 0 +279 LOAD_DEREF 14 +281 GET_ITER_STACK +282 FOR_ITER 289 +284 STORE_FAST 0 +285 LOAD_FAST 1 +286 POP_TOP +287 JUMP 282 +289 SETUP_FINALLY 310 +291 SETUP_EXCEPT 302 +293 JUMP 297 +295 JUMP 300 +297 LOAD_FAST 0 +298 POP_JUMP_IF_TRUE 295 +300 POP_EXCEPT_JUMP 309 +302 POP_TOP +303 LOAD_DEREF 14 +305 POP_TOP +306 POP_EXCEPT_JUMP 309 +308 END_FINALLY +309 LOAD_CONST_NONE +310 LOAD_FAST 1 +311 POP_TOP +312 END_FINALLY +313 JUMP 326 +315 SETUP_EXCEPT 322 +317 UNWIND_JUMP 329 1 +320 POP_EXCEPT_JUMP 326 +322 POP_TOP +323 POP_EXCEPT_JUMP 326 +325 END_FINALLY +326 LOAD_FAST 0 +327 POP_JUMP_IF_TRUE 315 +329 LOAD_FAST 0 +330 SETUP_WITH 337 +332 POP_TOP +333 LOAD_DEREF 14 +335 POP_TOP +336 LOAD_CONST_NONE +337 WITH_CLEANUP +338 END_FINALLY +339 LOAD_CONST_SMALL_INT 1 +340 STORE_DEREF 16 +342 LOAD_FAST_N 16 +344 MAKE_CLOSURE \.\+ 1 +347 STORE_FAST 13 +348 LOAD_CONST_SMALL_INT 0 +349 LOAD_CONST_NONE +350 IMPORT_NAME 'a' +352 STORE_FAST 0 +353 LOAD_CONST_SMALL_INT 0 +354 LOAD_CONST_STRING 'b' +356 BUILD_TUPLE 1 +358 IMPORT_NAME 'a' +360 IMPORT_FROM 'b' +362 STORE_DEREF 14 +364 POP_TOP +365 RAISE_LAST +366 LOAD_CONST_SMALL_INT 1 +367 RAISE_OBJ +368 LOAD_CONST_NONE +369 RETURN_VALUE +370 LOAD_CONST_SMALL_INT 1 +371 RETURN_VALUE File cmdline/cmd_showbc.py, code block 'f' (descriptor: \.\+, bytecode @\.\+ 59 bytes) Raw bytecode (code_info_size=8, bytecode_size=51): a8 10 0a 02 80 82 34 38 81 57 c0 57 c1 57 c2 57 @@ -539,10 +539,10 @@ arg names: self 09 POP_TOP 10 LOAD_CONST_NONE 11 RETURN_VALUE -File cmdline/cmd_showbc.py, code block '' (descriptor: \.\+, bytecode @\.\+ 31 bytes) -Raw bytecode (code_info_size=9, bytecode_size=22): - c3 40 0c 12 04 04 04 80 3b 53 b2 53 53 4b 0d 00 - c3 25 01 44 f7 7f 25 00 67 59 42 f0 7f 51 63 +File cmdline/cmd_showbc.py, code block '' (descriptor: \.\+, bytecode @\.\+ 28 bytes) +Raw bytecode (code_info_size=9, bytecode_size=19): + c3 40 0c 12 04 04 04 80 3b 53 b2 53 53 4b 0b c3 + 25 01 44 39 25 00 67 59 42 33 51 63 arg names: * * * (N_STATE 9) (N_EXC_STACK 0) @@ -552,20 +552,20 @@ arg names: * * * 01 LOAD_FAST 2 02 LOAD_NULL 03 LOAD_NULL -04 FOR_ITER 20 -07 STORE_FAST 3 -08 LOAD_DEREF 1 -10 POP_JUMP_IF_FALSE 4 -13 LOAD_DEREF 0 -15 YIELD_VALUE -16 POP_TOP -17 JUMP 4 -20 LOAD_CONST_NONE -21 RETURN_VALUE -File cmdline/cmd_showbc.py, code block '' (descriptor: \.\+, bytecode @\.\+ 29 bytes) -Raw bytecode (code_info_size=8, bytecode_size=21): - 4b 0c 14 04 04 04 80 3c 2b 00 b2 5f 4b 0d 00 c3 - 25 01 44 f7 7f 25 00 2f 14 42 f0 7f 63 +04 FOR_ITER 17 +06 STORE_FAST 3 +07 LOAD_DEREF 1 +09 POP_JUMP_IF_FALSE 4 +11 LOAD_DEREF 0 +13 YIELD_VALUE +14 POP_TOP +15 JUMP 4 +17 LOAD_CONST_NONE +18 RETURN_VALUE +File cmdline/cmd_showbc.py, code block '' (descriptor: \.\+, bytecode @\.\+ 26 bytes) +Raw bytecode (code_info_size=8, bytecode_size=18): + 4b 0c 14 04 04 04 80 3c 2b 00 b2 5f 4b 0b c3 25 + 01 44 39 25 00 2f 14 42 33 63 arg names: * * * (N_STATE 10) (N_EXC_STACK 0) @@ -574,18 +574,18 @@ arg names: * * * 00 BUILD_LIST 0 02 LOAD_FAST 2 03 GET_ITER_STACK -04 FOR_ITER 20 -07 STORE_FAST 3 -08 LOAD_DEREF 1 -10 POP_JUMP_IF_FALSE 4 -13 LOAD_DEREF 0 -15 STORE_COMP 20 -17 JUMP 4 -20 RETURN_VALUE -File cmdline/cmd_showbc.py, code block '' (descriptor: \.\+, bytecode @\.\+ 31 bytes) -Raw bytecode (code_info_size=8, bytecode_size=23): - 53 0c 15 04 04 04 80 3d 2c 00 b2 5f 4b 0f 00 c3 - 25 01 44 f7 7f 25 00 25 00 2f 19 42 ee 7f 63 +04 FOR_ITER 17 +06 STORE_FAST 3 +07 LOAD_DEREF 1 +09 POP_JUMP_IF_FALSE 4 +11 LOAD_DEREF 0 +13 STORE_COMP 20 +15 JUMP 4 +17 RETURN_VALUE +File cmdline/cmd_showbc.py, code block '' (descriptor: \.\+, bytecode @\.\+ 28 bytes) +Raw bytecode (code_info_size=8, bytecode_size=20): + 53 0c 15 04 04 04 80 3d 2c 00 b2 5f 4b 0d c3 25 + 01 44 39 25 00 25 00 2f 19 42 31 63 arg names: * * * (N_STATE 11) (N_EXC_STACK 0) @@ -594,15 +594,15 @@ arg names: * * * 00 BUILD_MAP 0 02 LOAD_FAST 2 03 GET_ITER_STACK -04 FOR_ITER 22 -07 STORE_FAST 3 -08 LOAD_DEREF 1 -10 POP_JUMP_IF_FALSE 4 +04 FOR_ITER 19 +06 STORE_FAST 3 +07 LOAD_DEREF 1 +09 POP_JUMP_IF_FALSE 4 +11 LOAD_DEREF 0 13 LOAD_DEREF 0 -15 LOAD_DEREF 0 -17 STORE_COMP 25 -19 JUMP 4 -22 RETURN_VALUE +15 STORE_COMP 25 +17 JUMP 4 +19 RETURN_VALUE File cmdline/cmd_showbc.py, code block 'closure' (descriptor: \.\+, bytecode @\.\+ 20 bytes) Raw bytecode (code_info_size=8, bytecode_size=12): 19 0c 16 04 80 6f 25 23 25 00 81 f2 c1 81 27 00 diff --git a/tests/run-tests.py b/tests/run-tests.py index 9c298dae31..d0ecc74a9a 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -527,6 +527,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): "micropython/opt_level_lineno.py" ) # native doesn't have proper traceback info skip_tests.add("micropython/schedule.py") # native code doesn't check pending events + skip_tests.add("stress/bytecode_limit.py") # bytecode specific test def run_one_test(test_file): test_file = test_file.replace("\\", "/") diff --git a/tests/stress/bytecode_limit.py b/tests/stress/bytecode_limit.py new file mode 100644 index 0000000000..0cb0c21e40 --- /dev/null +++ b/tests/stress/bytecode_limit.py @@ -0,0 +1,26 @@ +# Test the limits of bytecode generation. + +body = " with f()()() as a:\n try:\n f()()()\n except Exception:\n pass\n" + +# Test changing size of code info (source line/bytecode mapping) due to changing +# bytecode size in the final passes. This test is very specific to how the +# code info is encoded, and how jump offsets shrink in the final passes. This +# test should fail if the bytecode emitter doesn't correctly handle shrinking of +# the code info section. +exec( + """ +x = 0 +if x: +""" + + body * 13 + + """ +x = [1 if x else 123] + + + + + + +print(x) +""" +) diff --git a/tests/stress/bytecode_limit.py.exp b/tests/stress/bytecode_limit.py.exp new file mode 100644 index 0000000000..3214bfe58c --- /dev/null +++ b/tests/stress/bytecode_limit.py.exp @@ -0,0 +1 @@ +[123] diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index 73753094ec..1ce301ab94 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -242,17 +242,17 @@ class Opcodes: MP_BC_ROT_TWO = (MP_BC_BASE_BYTE_O + 0x0a) MP_BC_ROT_THREE = (MP_BC_BASE_BYTE_O + 0x0b) - MP_BC_JUMP = (MP_BC_BASE_JUMP_E + 0x02) # rel byte code offset, 16-bit signed, in excess - MP_BC_POP_JUMP_IF_TRUE = (MP_BC_BASE_JUMP_E + 0x03) # rel byte code offset, 16-bit signed, in excess - MP_BC_POP_JUMP_IF_FALSE = (MP_BC_BASE_JUMP_E + 0x04) # rel byte code offset, 16-bit signed, in excess - MP_BC_JUMP_IF_TRUE_OR_POP = (MP_BC_BASE_JUMP_E + 0x05) # rel byte code offset, 16-bit signed, in excess - MP_BC_JUMP_IF_FALSE_OR_POP = (MP_BC_BASE_JUMP_E + 0x06) # rel byte code offset, 16-bit signed, in excess - MP_BC_UNWIND_JUMP = (MP_BC_BASE_JUMP_E + 0x00) # rel byte code offset, 16-bit signed, in excess; then a byte - MP_BC_SETUP_WITH = (MP_BC_BASE_JUMP_E + 0x07) # rel byte code offset, 16-bit unsigned - MP_BC_SETUP_EXCEPT = (MP_BC_BASE_JUMP_E + 0x08) # rel byte code offset, 16-bit unsigned - MP_BC_SETUP_FINALLY = (MP_BC_BASE_JUMP_E + 0x09) # rel byte code offset, 16-bit unsigned - MP_BC_POP_EXCEPT_JUMP = (MP_BC_BASE_JUMP_E + 0x0a) # rel byte code offset, 16-bit unsigned - MP_BC_FOR_ITER = (MP_BC_BASE_JUMP_E + 0x0b) # rel byte code offset, 16-bit unsigned + MP_BC_UNWIND_JUMP = (MP_BC_BASE_JUMP_E + 0x00) # signed relative bytecode offset; then a byte + MP_BC_JUMP = (MP_BC_BASE_JUMP_E + 0x02) # signed relative bytecode offset + MP_BC_POP_JUMP_IF_TRUE = (MP_BC_BASE_JUMP_E + 0x03) # signed relative bytecode offset + MP_BC_POP_JUMP_IF_FALSE = (MP_BC_BASE_JUMP_E + 0x04) # signed relative bytecode offset + MP_BC_JUMP_IF_TRUE_OR_POP = (MP_BC_BASE_JUMP_E + 0x05) # signed relative bytecode offset + MP_BC_JUMP_IF_FALSE_OR_POP = (MP_BC_BASE_JUMP_E + 0x06) # signed relative bytecode offset + MP_BC_SETUP_WITH = (MP_BC_BASE_JUMP_E + 0x07) # unsigned relative bytecode offset + MP_BC_SETUP_EXCEPT = (MP_BC_BASE_JUMP_E + 0x08) # unsigned relative bytecode offset + MP_BC_SETUP_FINALLY = (MP_BC_BASE_JUMP_E + 0x09) # unsigned relative bytecode offset + MP_BC_POP_EXCEPT_JUMP = (MP_BC_BASE_JUMP_E + 0x0a) # unsigned relative bytecode offset + MP_BC_FOR_ITER = (MP_BC_BASE_JUMP_E + 0x0b) # unsigned relative bytecode offset MP_BC_WITH_CLEANUP = (MP_BC_BASE_BYTE_O + 0x0c) MP_BC_END_FINALLY = (MP_BC_BASE_BYTE_O + 0x0d) MP_BC_GET_ITER = (MP_BC_BASE_BYTE_O + 0x0e) @@ -289,6 +289,16 @@ class Opcodes: MP_BC_IMPORT_STAR = (MP_BC_BASE_BYTE_E + 0x09) # fmt: on + # Create sets of related opcodes. + ALL_OFFSET_SIGNED = ( + MP_BC_UNWIND_JUMP, + MP_BC_JUMP, + MP_BC_POP_JUMP_IF_TRUE, + MP_BC_POP_JUMP_IF_FALSE, + MP_BC_JUMP_IF_TRUE_OR_POP, + MP_BC_JUMP_IF_FALSE_OR_POP, + ) + # Create a dict mapping opcode value to opcode name. mapping = ["unknown" for _ in range(256)] for op_name in list(locals()): @@ -323,7 +333,10 @@ def mp_opcode_format(bytecode, ip, count_var_uint): ip += 1 ip += 1 elif f == MP_BC_FORMAT_OFFSET: - ip += 2 + if bytecode[ip] & 0x80 == 0: + ip += 1 + else: + ip += 2 ip += extra_byte return f, ip - ip_start @@ -342,8 +355,16 @@ def mp_opcode_decode(bytecode, ip): arg = arg << 7 | bytecode[ip] & 0x7F ip += 1 elif f == MP_BC_FORMAT_OFFSET: - arg = bytecode[ip] | bytecode[ip + 1] << 8 - ip += 2 + if bytecode[ip] & 0x80 == 0: + arg = bytecode[ip] + ip += 1 + if opcode in Opcodes.ALL_OFFSET_SIGNED: + arg -= 0x40 + else: + arg = bytecode[ip] & 0x7F | bytecode[ip + 1] << 7 + ip += 2 + if opcode in Opcodes.ALL_OFFSET_SIGNED: + arg -= 0x4000 ip += extra_byte return f, ip - ip_start, arg From acd2c5c8349a4cd713b1b36c0e6ec6f39791ca19 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 18 Mar 2022 13:18:59 +1100 Subject: [PATCH 254/619] py/emitbc: Add check for bytecode jump offset overflow. Signed-off-by: Damien George --- py/emitbc.c | 9 +++++++++ tests/stress/bytecode_limit.py | 7 +++++++ tests/stress/bytecode_limit.py.exp | 4 ++++ 3 files changed, 20 insertions(+) diff --git a/py/emitbc.c b/py/emitbc.c index 14a72e2765..90ab6c0a92 100644 --- a/py/emitbc.c +++ b/py/emitbc.c @@ -64,6 +64,7 @@ struct _emit_t { size_t bytecode_offset; size_t bytecode_size; byte *code_base; // stores both byte code and code info + bool overflow; size_t n_info; size_t n_cell; @@ -260,6 +261,9 @@ STATIC void emit_write_bytecode_byte_label(emit_t *emit, int stack_adj, byte b1, if (is_signed) { bytecode_offset += 0x4000; } + if (emit->pass == MP_PASS_EMIT && !(0 <= bytecode_offset && bytecode_offset <= 0x7fff)) { + emit->overflow = true; + } c[1] = 0x80 | (bytecode_offset & 0x7f); c[2] = bytecode_offset >> 7; } @@ -274,6 +278,7 @@ void mp_emit_bc_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope) { emit->last_source_line = 1; emit->bytecode_offset = 0; emit->code_info_offset = 0; + emit->overflow = false; // Write local state size, exception stack size, scope flags and number of arguments { @@ -373,6 +378,10 @@ bool mp_emit_bc_end_pass(emit_t *emit) { return false; } + if (emit->overflow) { + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("bytecode overflow")); + } + // Bytecode is finalised, assign it to the raw code object. mp_emit_glue_assign_bytecode(emit->scope->raw_code, emit->code_base, #if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS diff --git a/tests/stress/bytecode_limit.py b/tests/stress/bytecode_limit.py index 0cb0c21e40..49f7cd2caf 100644 --- a/tests/stress/bytecode_limit.py +++ b/tests/stress/bytecode_limit.py @@ -24,3 +24,10 @@ x = [1 if x else 123] print(x) """ ) + +# Test overflow of jump offset. +for n in (430, 431, 432, 433): + try: + exec("cond = 0\nif cond:\n" + body * n + "else:\n print('cond false')\n") + except RuntimeError: + print("RuntimeError") diff --git a/tests/stress/bytecode_limit.py.exp b/tests/stress/bytecode_limit.py.exp index 3214bfe58c..b2d9737d80 100644 --- a/tests/stress/bytecode_limit.py.exp +++ b/tests/stress/bytecode_limit.py.exp @@ -1 +1,5 @@ [123] +cond false +cond false +RuntimeError +RuntimeError From 6d11c69983f8084459e5bb037d931d5e2d283c78 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 21 Mar 2022 16:36:13 +1100 Subject: [PATCH 255/619] py: Change jump-if-x-or-pop opcodes to have unsigned offset argument. These jumps are always forwards, and it's more efficient in the VM to decode an unsigned argument. These opcodes are already optimised versions of the sequence "dup-top pop-jump-if-x pop" so it doesn't hurt generality to optimise them further. Signed-off-by: Damien George --- py/bc0.h | 4 ++-- py/emitbc.c | 2 +- py/showbc.c | 4 ++-- py/vm.c | 8 ++++---- tools/mpy-tool.py | 6 ++---- 5 files changed, 11 insertions(+), 13 deletions(-) diff --git a/py/bc0.h b/py/bc0.h index 50c4954b08..a4a0acf937 100644 --- a/py/bc0.h +++ b/py/bc0.h @@ -117,8 +117,8 @@ #define MP_BC_JUMP (MP_BC_BASE_JUMP_E + 0x02) // signed relative bytecode offset #define MP_BC_POP_JUMP_IF_TRUE (MP_BC_BASE_JUMP_E + 0x03) // signed relative bytecode offset #define MP_BC_POP_JUMP_IF_FALSE (MP_BC_BASE_JUMP_E + 0x04) // signed relative bytecode offset -#define MP_BC_JUMP_IF_TRUE_OR_POP (MP_BC_BASE_JUMP_E + 0x05) // signed relative bytecode offset -#define MP_BC_JUMP_IF_FALSE_OR_POP (MP_BC_BASE_JUMP_E + 0x06) // signed relative bytecode offset +#define MP_BC_JUMP_IF_TRUE_OR_POP (MP_BC_BASE_JUMP_E + 0x05) // unsigned relative bytecode offset +#define MP_BC_JUMP_IF_FALSE_OR_POP (MP_BC_BASE_JUMP_E + 0x06) // unsigned relative bytecode offset #define MP_BC_SETUP_WITH (MP_BC_BASE_JUMP_E + 0x07) // unsigned relative bytecode offset #define MP_BC_SETUP_EXCEPT (MP_BC_BASE_JUMP_E + 0x08) // unsigned relative bytecode offset #define MP_BC_SETUP_FINALLY (MP_BC_BASE_JUMP_E + 0x09) // unsigned relative bytecode offset diff --git a/py/emitbc.c b/py/emitbc.c index 90ab6c0a92..1f5cd9d322 100644 --- a/py/emitbc.c +++ b/py/emitbc.c @@ -224,7 +224,7 @@ STATIC void emit_write_bytecode_byte_label(emit_t *emit, int stack_adj, byte b1, mp_emit_bc_adjust_stack_size(emit, stack_adj); // Determine if the jump offset is signed or unsigned, based on the opcode. - const bool is_signed = b1 <= MP_BC_JUMP_IF_FALSE_OR_POP; + const bool is_signed = b1 <= MP_BC_POP_JUMP_IF_FALSE; // Default to a 2-byte encoding (the largest) with an unknown jump offset. unsigned int jump_encoding_size = 1; diff --git a/py/showbc.c b/py/showbc.c index 178fa451a2..f9c334b93b 100644 --- a/py/showbc.c +++ b/py/showbc.c @@ -338,12 +338,12 @@ const byte *mp_bytecode_print_str(const mp_print_t *print, const byte *ip_start, break; case MP_BC_JUMP_IF_TRUE_OR_POP: - DECODE_SLABEL; + DECODE_ULABEL; mp_printf(print, "JUMP_IF_TRUE_OR_POP " UINT_FMT, (mp_uint_t)(ip + unum - ip_start)); break; case MP_BC_JUMP_IF_FALSE_OR_POP: - DECODE_SLABEL; + DECODE_ULABEL; mp_printf(print, "JUMP_IF_FALSE_OR_POP " UINT_FMT, (mp_uint_t)(ip + unum - ip_start)); break; diff --git a/py/vm.c b/py/vm.c index 990009c00c..50da90e7d2 100644 --- a/py/vm.c +++ b/py/vm.c @@ -560,9 +560,9 @@ dispatch_loop: } ENTRY(MP_BC_JUMP_IF_TRUE_OR_POP): { - DECODE_SLABEL; + DECODE_ULABEL; if (mp_obj_is_true(TOP())) { - ip += slab; + ip += ulab; } else { sp--; } @@ -570,11 +570,11 @@ dispatch_loop: } ENTRY(MP_BC_JUMP_IF_FALSE_OR_POP): { - DECODE_SLABEL; + DECODE_ULABEL; if (mp_obj_is_true(TOP())) { sp--; } else { - ip += slab; + ip += ulab; } DISPATCH_WITH_PEND_EXC_CHECK(); } diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index 1ce301ab94..84c09a0c68 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -246,8 +246,8 @@ class Opcodes: MP_BC_JUMP = (MP_BC_BASE_JUMP_E + 0x02) # signed relative bytecode offset MP_BC_POP_JUMP_IF_TRUE = (MP_BC_BASE_JUMP_E + 0x03) # signed relative bytecode offset MP_BC_POP_JUMP_IF_FALSE = (MP_BC_BASE_JUMP_E + 0x04) # signed relative bytecode offset - MP_BC_JUMP_IF_TRUE_OR_POP = (MP_BC_BASE_JUMP_E + 0x05) # signed relative bytecode offset - MP_BC_JUMP_IF_FALSE_OR_POP = (MP_BC_BASE_JUMP_E + 0x06) # signed relative bytecode offset + MP_BC_JUMP_IF_TRUE_OR_POP = (MP_BC_BASE_JUMP_E + 0x05) # unsigned relative bytecode offset + MP_BC_JUMP_IF_FALSE_OR_POP = (MP_BC_BASE_JUMP_E + 0x06) # unsigned relative bytecode offset MP_BC_SETUP_WITH = (MP_BC_BASE_JUMP_E + 0x07) # unsigned relative bytecode offset MP_BC_SETUP_EXCEPT = (MP_BC_BASE_JUMP_E + 0x08) # unsigned relative bytecode offset MP_BC_SETUP_FINALLY = (MP_BC_BASE_JUMP_E + 0x09) # unsigned relative bytecode offset @@ -295,8 +295,6 @@ class Opcodes: MP_BC_JUMP, MP_BC_POP_JUMP_IF_TRUE, MP_BC_POP_JUMP_IF_FALSE, - MP_BC_JUMP_IF_TRUE_OR_POP, - MP_BC_JUMP_IF_FALSE_OR_POP, ) # Create a dict mapping opcode value to opcode name. From b312a7abf5504d0e68846d5728c09899119f2621 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 25 Mar 2022 11:57:59 +1100 Subject: [PATCH 256/619] py/builtinimport: Alias sys to usys if import weak links aren't enabled. The sys module should always be available (if it's compiled in), eg to change sys.path for importing. So provide an explicit alias from "sys" to "usys" so that "import sys" can always work. Signed-off-by: Damien George --- py/builtinimport.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/py/builtinimport.c b/py/builtinimport.c index 094959f97d..cd9636ccdc 100644 --- a/py/builtinimport.c +++ b/py/builtinimport.c @@ -373,6 +373,10 @@ STATIC mp_obj_t process_import_at_level(qstr full_mod_name, qstr level_mod_name, qstr umodule_name = qstr_from_str(umodule_buf); module_obj = mp_module_get_builtin(umodule_name); } + #elif MICROPY_PY_SYS + if (stat == MP_IMPORT_STAT_NO_EXIST && module_obj == MP_OBJ_NULL && level_mod_name == MP_QSTR_sys) { + module_obj = MP_OBJ_FROM_PTR(&mp_module_sys); + } #endif } else { DEBUG_printf("Searching for sub-module\n"); From 35dbde163a8a253679c09b4646a9177cde5345f2 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 25 Mar 2022 12:39:12 +1100 Subject: [PATCH 257/619] tools/mpremote: Support any prompt string when detecting soft reset. The prompt may be changed by sys.ps1. Signed-off-by: Damien George --- tools/mpremote/mpremote/pyboardextended.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/tools/mpremote/mpremote/pyboardextended.py b/tools/mpremote/mpremote/pyboardextended.py index 69cbf02edd..95619b0eb6 100644 --- a/tools/mpremote/mpremote/pyboardextended.py +++ b/tools/mpremote/mpremote/pyboardextended.py @@ -653,12 +653,12 @@ class PyboardExtended(Pyboard): # Check if a soft reset occurred. if data_all.find(b"MPY: soft reboot") == -1: return - if data_all.endswith(b">>> "): - in_friendly_repl = True - elif data_all.endswith(b">"): + if data_all.endswith(b">"): in_friendly_repl = False + prompt = b">" else: - return + in_friendly_repl = True + prompt = data_all.rsplit(b"\r\n", 1)[-1] # Clear state while board remounts, it will be re-set once mounted. self.mounted = False @@ -676,9 +676,6 @@ class PyboardExtended(Pyboard): # Exit raw REPL if needed, and wait for the friendly REPL prompt. if in_friendly_repl: self.exit_raw_repl() - prompt = b">>> " - else: - prompt = b">" self.read_until(len(prompt), prompt) out_callback(prompt) self.serial = SerialIntercept(self.serial, self.cmd) From c90dfba04ca8d6e3eb12732dea2344f393f77531 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 23 Mar 2022 15:21:03 +1100 Subject: [PATCH 258/619] stm32/machine_i2s: Set FullDuplexMode to disabled on F4. Signed-off-by: Damien George --- ports/stm32/machine_i2s.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ports/stm32/machine_i2s.c b/ports/stm32/machine_i2s.c index 31b7d14bfd..0bb0f3e8f5 100644 --- a/ports/stm32/machine_i2s.c +++ b/ports/stm32/machine_i2s.c @@ -791,6 +791,9 @@ STATIC void machine_i2s_init_helper(machine_i2s_obj_t *self, size_t n_pos_args, init->AudioFreq = args[ARG_rate].u_int; init->CPOL = I2S_CPOL_LOW; init->ClockSource = I2S_CLOCK_PLL; + #if defined(STM32F4) + init->FullDuplexMode = I2S_FULLDUPLEXMODE_DISABLE; + #endif // init the I2S bus if (!i2s_init(self)) { From 2a91c8a888bc56b61876e6d01a2f144aee5686b4 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 25 Mar 2022 14:29:48 +1100 Subject: [PATCH 259/619] stm32/machine_i2s: Allow I2S.deinit to be called multiple times. In particular, it is called by the constructor if the instance already exists. So if the previous instance was deinit'd then it will be deinit'd a second time. Signed-off-by: Damien George --- ports/stm32/machine_i2s.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/ports/stm32/machine_i2s.c b/ports/stm32/machine_i2s.c index 0bb0f3e8f5..2120554c3a 100644 --- a/ports/stm32/machine_i2s.c +++ b/ports/stm32/machine_i2s.c @@ -887,25 +887,27 @@ STATIC mp_obj_t machine_i2s_init(size_t n_pos_args, const mp_obj_t *pos_args, mp STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2s_init_obj, 1, machine_i2s_init); STATIC mp_obj_t machine_i2s_deinit(mp_obj_t self_in) { - machine_i2s_obj_t *self = MP_OBJ_TO_PTR(self_in); - dma_deinit(self->dma_descr_tx); - dma_deinit(self->dma_descr_rx); - HAL_I2S_DeInit(&self->hi2s); + if (self->ring_buffer_storage != NULL) { + dma_deinit(self->dma_descr_tx); + dma_deinit(self->dma_descr_rx); + HAL_I2S_DeInit(&self->hi2s); - if (self->hi2s.Instance == I2S1) { - __SPI1_FORCE_RESET(); - __SPI1_RELEASE_RESET(); - __SPI1_CLK_DISABLE(); - } else if (self->hi2s.Instance == I2S2) { - __SPI2_FORCE_RESET(); - __SPI2_RELEASE_RESET(); - __SPI2_CLK_DISABLE(); + if (self->hi2s.Instance == I2S1) { + __SPI1_FORCE_RESET(); + __SPI1_RELEASE_RESET(); + __SPI1_CLK_DISABLE(); + } else if (self->hi2s.Instance == I2S2) { + __SPI2_FORCE_RESET(); + __SPI2_RELEASE_RESET(); + __SPI2_CLK_DISABLE(); + } + + m_free(self->ring_buffer_storage); + self->ring_buffer_storage = NULL; } - m_free(self->ring_buffer_storage); - return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_i2s_deinit_obj, machine_i2s_deinit); From 6804a8a891d61d7b4c2b613997021d12f960763c Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 26 Mar 2022 00:07:19 +1100 Subject: [PATCH 260/619] stm32/machine_i2s: Fix 16-bit stereo i2s_frame_map. Signed-off-by: Damien George --- ports/stm32/machine_i2s.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/stm32/machine_i2s.c b/ports/stm32/machine_i2s.c index 2120554c3a..2bb60a6a86 100644 --- a/ports/stm32/machine_i2s.c +++ b/ports/stm32/machine_i2s.c @@ -158,7 +158,7 @@ STATIC mp_obj_t machine_i2s_deinit(mp_obj_t self_in); STATIC const int8_t i2s_frame_map[NUM_I2S_USER_FORMATS][I2S_RX_FRAME_SIZE_IN_BYTES] = { { 0, 1, -1, -1, -1, -1, -1, -1 }, // Mono, 16-bits { 2, 3, 0, 1, -1, -1, -1, -1 }, // Mono, 32-bits - { 0, 1, 4, 5, -1, -1, -1, -1 }, // Stereo, 16-bits + { 0, 1, -1, -1, 2, 3, -1, -1 }, // Stereo, 16-bits { 2, 3, 0, 1, 6, 7, 4, 5 }, // Stereo, 32-bits }; From 726628584507d1b043fa28e9adf122acb4b40781 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 28 Mar 2022 16:53:03 +1100 Subject: [PATCH 261/619] tests/extmod: Add test for machine.I2S data rate. Signed-off-by: Damien George --- tests/extmod/machine_i2s_rate.py | 80 ++++++++++++++++++++++++++++ tests/extmod/machine_i2s_rate.py.exp | 8 +++ 2 files changed, 88 insertions(+) create mode 100644 tests/extmod/machine_i2s_rate.py create mode 100644 tests/extmod/machine_i2s_rate.py.exp diff --git a/tests/extmod/machine_i2s_rate.py b/tests/extmod/machine_i2s_rate.py new file mode 100644 index 0000000000..46e94585b7 --- /dev/null +++ b/tests/extmod/machine_i2s_rate.py @@ -0,0 +1,80 @@ +# Test machine.I2S data transfer rate, for both TX and RX. + +try: + from machine import Pin, I2S +except ImportError: + print("SKIP") + raise SystemExit + +import time, sys + +# Configure pins based on the board. +if "pyboard" in sys.platform: + i2s_id = 2 + sck_pin = Pin("Y6") + ws_pin = Pin("Y5") + sd_pin = Pin("Y8") +elif "rp2" in sys.platform: + i2s_id = 1 + sck_pin = Pin(0) + ws_pin = Pin(1) + sd_pin = Pin(2) + +TEST_BYTES = b"01234567" +RATE = 11025 # frames/sec + + +def test(mode, bits_per_sample, frame_format): + i2s = I2S( + i2s_id, + sck=sck_pin, + ws=ws_pin, + sd=sd_pin, + mode=mode, + bits=bits_per_sample, + format=frame_format, + rate=RATE, + ibuf=200, + ) + + if frame_format == I2S.MONO: + channels = 1 + else: + channels = 2 + bits_per_frame = bits_per_sample * channels + buf_len_250ms = bits_per_frame // 8 * RATE // 4 + + # Create test data and preload I2S buffers. + if mode == I2S.TX: + mode_str = "TX" + data = TEST_BYTES * (buf_len_250ms // len(TEST_BYTES)) + i2s.write(data) + else: + mode_str = "RX" + data = bytearray(len(TEST_BYTES) * (buf_len_250ms // len(TEST_BYTES))) + i2s.readinto(data) + + # Time how long it takes to read/write 2 lots of data. + t0 = time.ticks_ms() + for i in range(2): + if mode == I2S.TX: + i2s.write(data) + else: + i2s.readinto(data) + t1 = time.ticks_ms() + dt = time.ticks_diff(t1, t0) + + i2s.deinit() + + # Print out test result, time should be in range of 500ms. + print(mode_str, bits_per_sample, channels, abs(dt - 500) <= 4) + + +test(I2S.TX, 16, I2S.MONO) +test(I2S.TX, 16, I2S.STEREO) +test(I2S.TX, 32, I2S.MONO) +test(I2S.TX, 32, I2S.STEREO) +test(I2S.RX, 16, I2S.MONO) +test(I2S.RX, 16, I2S.STEREO) +test(I2S.RX, 32, I2S.MONO) +test(I2S.RX, 32, I2S.STEREO) diff --git a/tests/extmod/machine_i2s_rate.py.exp b/tests/extmod/machine_i2s_rate.py.exp new file mode 100644 index 0000000000..e160d92f6b --- /dev/null +++ b/tests/extmod/machine_i2s_rate.py.exp @@ -0,0 +1,8 @@ +TX 16 1 True +TX 16 2 True +TX 32 1 True +TX 32 2 True +RX 16 1 True +RX 16 2 True +RX 32 1 True +RX 32 2 True From 5e685a9c6faeb764c0b689bc736ed00736bfd6b6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 28 Mar 2022 17:10:33 +1100 Subject: [PATCH 262/619] docs/library/machine.I2S: Clarify what rate refers to. Signed-off-by: Damien George --- docs/library/machine.I2S.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/library/machine.I2S.rst b/docs/library/machine.I2S.rst index abfbb08780..d4e5ac9433 100644 --- a/docs/library/machine.I2S.rst +++ b/docs/library/machine.I2S.rst @@ -95,7 +95,8 @@ Constructor - ``mode`` specifies receive or transmit - ``bits`` specifies sample size (bits), 16 or 32 - ``format`` specifies channel format, STEREO or MONO - - ``rate`` specifies audio sampling rate (samples/s) + - ``rate`` specifies audio sampling rate (Hz); + this is the frequency of the ``ws`` signal - ``ibuf`` specifies internal buffer length (bytes) For all ports, DMA runs continuously in the background and allows user applications to perform other operations while From 1f6cb8f0476e699c1910ed59f1bb027fbef72ad7 Mon Sep 17 00:00:00 2001 From: MikeTeachman Date: Mon, 29 Nov 2021 09:50:34 -0800 Subject: [PATCH 263/619] mixmrt/machine_i2s: Add I2S protocol support. This commit adds support for machine.I2S on the mimxrt port. The I2S API is consistent with the existing stm32, esp32, and rp2 implementations. I2S features: - controller transmit and controller receive - 16-bit and 32-bit sample sizes - mono and stereo formats - sampling frequencies from 8kHz to 48kHz - 3 modes of operation: - blocking - non-blocking with callback - uasyncio - configurable internal buffer - optional MCK Tested with the following development boards: - MIMXRT1010_EVK, MIMXRT1015_EVK, MIMXRT1020_EVK, MIMXRT1050_EVK - Teensy 4.0, Teensy 4.1 - Olimex RT1010 - Seeed ARCH MIX Tested with the following I2S hardware peripherals: - UDA1334 - GY-SPH0645LM4H - WM8960 codec on board the MIMXRT boards and separate breakout board - INMP441 - PCM5102 - SGTL5000 on the Teensy audio shield Signed-off-by: Mike Teachman --- docs/library/machine.I2S.rst | 12 +- ports/mimxrt/Makefile | 9 +- ports/mimxrt/board_init.c | 4 + .../boards/MIMXRT1010_EVK/mpconfigboard.h | 33 + ports/mimxrt/boards/MIMXRT1010_EVK/pins.csv | 7 + .../boards/MIMXRT1020_EVK/mpconfigboard.h | 33 + ports/mimxrt/boards/MIMXRT1020_EVK/pins.csv | 9 +- .../boards/MIMXRT1050_EVK/mpconfigboard.h | 34 + ports/mimxrt/boards/MIMXRT1050_EVK/pins.csv | 7 + .../boards/MIMXRT1060_EVK/mpconfigboard.h | 33 + ports/mimxrt/boards/MIMXRT1060_EVK/pins.csv | 7 + .../boards/MIMXRT1064_EVK/mpconfigboard.h | 33 + ports/mimxrt/boards/MIMXRT1064_EVK/pins.csv | 7 + .../boards/OLIMEX_RT1010/mpconfigboard.h | 36 + ports/mimxrt/boards/OLIMEX_RT1010/pins.csv | 7 + .../boards/SEEED_ARCH_MIX/mpconfigboard.h | 32 + ports/mimxrt/boards/SEEED_ARCH_MIX/pins.csv | 7 + ports/mimxrt/boards/TEENSY40/mpconfigboard.h | 38 + ports/mimxrt/boards/TEENSY40/pins.csv | 9 +- ports/mimxrt/boards/TEENSY41/mpconfigboard.h | 40 + ports/mimxrt/boards/TEENSY41/pins.csv | 9 +- ports/mimxrt/{dma_channel.c => dma_manager.c} | 16 +- ports/mimxrt/{dma_channel.h => dma_manager.h} | 1 + ports/mimxrt/machine_i2s.c | 1228 +++++++++++++++++ ports/mimxrt/machine_spi.c | 7 +- ports/mimxrt/main.c | 3 + ports/mimxrt/modmachine.c | 3 + ports/mimxrt/modmachine.h | 3 + ports/mimxrt/mpconfigport.h | 13 + ports/mimxrt/sdcard.c | 2 +- 30 files changed, 1662 insertions(+), 20 deletions(-) rename ports/mimxrt/{dma_channel.c => dma_manager.c} (86%) rename ports/mimxrt/{dma_channel.h => dma_manager.h} (98%) create mode 100644 ports/mimxrt/machine_i2s.c diff --git a/docs/library/machine.I2S.rst b/docs/library/machine.I2S.rst index d4e5ac9433..e68a863d7c 100644 --- a/docs/library/machine.I2S.rst +++ b/docs/library/machine.I2S.rst @@ -75,23 +75,19 @@ uasyncio:: Constructor ----------- -.. class:: I2S(id, *, sck, ws, sd, mode, bits, format, rate, ibuf) +.. class:: I2S(id, *, sck, ws, sd, mck=None, mode, bits, format, rate, ibuf) Construct an I2S object of the given id: - - ``id`` identifies a particular I2S bus. - - ``id`` is board and port specific: - - - PYBv1.0/v1.1: has one I2S bus with id=2. - - PYBD-SFxW: has two I2S buses with id=1 and id=2. - - ESP32: has two I2S buses with id=0 and id=1. + - ``id`` identifies a particular I2S bus; it is board and port specific Keyword-only parameters that are supported on all ports: - ``sck`` is a pin object for the serial clock line - ``ws`` is a pin object for the word select line - ``sd`` is a pin object for the serial data line + - ``mck`` is a pin object for the master clock line; + master clock frequency is sampling rate * 256 - ``mode`` specifies receive or transmit - ``bits`` specifies sample size (bits), 16 or 32 - ``format`` specifies channel format, STEREO or MONO diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index be5083965f..f9c5bf402a 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -77,7 +77,7 @@ INC += -I$(TOP)/lib/tinyusb/hw INC += -I$(TOP)/lib/tinyusb/hw/bsp/teensy_40 INC += -I$(TOP)/lib/tinyusb/src -CFLAGS_MCU = -mtune=cortex-m7 -mcpu=cortex-m7 -mfloat-abi=hard -mfpu=fpv5-d16 +CFLAGS_MCU = -mtune=cortex-m7 -mcpu=cortex-m7 CFLAGS += $(INC) -Wall -Werror -Wdouble-promotion -Wfloat-conversion -std=c99 -nostdlib -mthumb $(CFLAGS_MCU) CFLAGS += -DCPU_$(MCU_SERIES) -DCPU_$(MCU_VARIANT) -DBOARD_$(BOARD) CFLAGS += -DXIP_EXTERNAL_FLASH=1 \ @@ -100,12 +100,14 @@ CFLAGS += $(CFLAGS_MOD) $(CFLAGS_EXTRA) # Configure floating point support ifeq ($(MICROPY_FLOAT_IMPL),double) CFLAGS += -DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_DOUBLE +CFLAGS_MCU += -mfloat-abi=hard -mfpu=fpv5-d16 else ifeq ($(MICROPY_FLOAT_IMPL),none) CFLAGS += -DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_NONE else CFLAGS += -DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_FLOAT CFLAGS += -fsingle-precision-constant +CFLAGS_MCU += -mfloat-abi=softfp -mfpu=fpv5-sp-d16 endif endif @@ -188,6 +190,7 @@ SRC_HAL_IMX_C += \ $(MCU_DIR)/drivers/fsl_lpuart.c \ $(MCU_DIR)/drivers/fsl_pit.c \ $(MCU_DIR)/drivers/fsl_pwm.c \ + $(MCU_DIR)/drivers/fsl_sai.c \ $(MCU_DIR)/drivers/fsl_snvs_lp.c \ $(MCU_DIR)/drivers/fsl_trng.c \ $(MCU_DIR)/drivers/fsl_wdog.c \ @@ -210,7 +213,7 @@ endif SRC_C += \ board_init.c \ - dma_channel.c \ + dma_manager.c \ drivers/bus/softspi.c \ drivers/dht/dht.c \ eth.c \ @@ -224,6 +227,7 @@ SRC_C += \ machine_adc.c \ machine_bitstream.c \ machine_i2c.c \ + machine_i2s.c \ machine_led.c \ machine_pin.c \ machine_rtc.c \ @@ -396,6 +400,7 @@ SRC_QSTR += \ extmod/modonewire.c \ extmod/uos_dupterm.c \ machine_adc.c \ + machine_i2s.c \ machine_led.c \ machine_pin.c \ machine_pwm.c \ diff --git a/ports/mimxrt/board_init.c b/ports/mimxrt/board_init.c index d96645feff..82af620a8d 100644 --- a/ports/mimxrt/board_init.c +++ b/ports/mimxrt/board_init.c @@ -96,6 +96,10 @@ void board_init(void) { #if MICROPY_PY_MACHINE_SDCARD machine_sdcard_init0(); #endif + + #if MICROPY_PY_MACHINE_I2S + machine_i2s_init0(); + #endif } void USB_OTG1_IRQHandler(void) { diff --git a/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.h index 726a63904d..75fda0cad1 100644 --- a/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.h @@ -44,3 +44,36 @@ #define IOMUX_TABLE_I2C \ { IOMUXC_GPIO_02_LPI2C1_SCL }, { IOMUXC_GPIO_01_LPI2C1_SDA }, \ { IOMUXC_GPIO_10_LPI2C2_SCL }, { IOMUXC_GPIO_09_LPI2C2_SDA }, + +#define MICROPY_PY_MACHINE_I2S (1) +#define MICROPY_HW_I2S_NUM (1) +#define I2S_CLOCK_MUX { 0, kCLOCK_Sai1Mux } +#define I2S_CLOCK_PRE_DIV { 0, kCLOCK_Sai1PreDiv } +#define I2S_CLOCK_DIV { 0, kCLOCK_Sai1Div } +#define I2S_IOMUXC_GPR_MODE { 0, kIOMUXC_GPR_SAI1MClkOutputDir } +#define I2S_DMA_REQ_SRC_RX { 0, kDmaRequestMuxSai1Rx } +#define I2S_DMA_REQ_SRC_TX { 0, kDmaRequestMuxSai1Tx } +#define I2S_WM8960_RX_MODE (1) + +#define I2S_GPIO(_hwid, _fn, _mode, _pin, _iomux) \ + { \ + .hw_id = _hwid, \ + .fn = _fn, \ + .mode = _mode, \ + .name = MP_QSTR_##_pin, \ + .iomux = {_iomux}, \ + } + +#define I2S_GPIO_MAP \ + { \ + I2S_GPIO(1, MCK, TX, GPIO_08, IOMUXC_GPIO_08_SAI1_MCLK), \ + I2S_GPIO(1, SCK, RX, GPIO_01, IOMUXC_GPIO_01_SAI1_RX_BCLK), \ + I2S_GPIO(1, WS, RX, GPIO_02, IOMUXC_GPIO_02_SAI1_RX_SYNC), \ + I2S_GPIO(1, SD, RX, GPIO_03, IOMUXC_GPIO_03_SAI1_RX_DATA00), \ + I2S_GPIO(1, SCK, TX, GPIO_06, IOMUXC_GPIO_06_SAI1_TX_BCLK), \ + I2S_GPIO(1, WS, TX, GPIO_07, IOMUXC_GPIO_07_SAI1_TX_SYNC), \ + I2S_GPIO(1, SD, TX, GPIO_04, IOMUXC_GPIO_04_SAI1_TX_DATA00), \ + } + +#define MICROPY_BOARD_ROOT_POINTERS \ + struct _machine_i2s_obj_t *machine_i2s_obj[MICROPY_HW_I2S_NUM]; diff --git a/ports/mimxrt/boards/MIMXRT1010_EVK/pins.csv b/ports/mimxrt/boards/MIMXRT1010_EVK/pins.csv index 1f01215ddd..d8710f8ce5 100644 --- a/ports/mimxrt/boards/MIMXRT1010_EVK/pins.csv +++ b/ports/mimxrt/boards/MIMXRT1010_EVK/pins.csv @@ -34,3 +34,10 @@ PWM_CB,GPIO_05 ENC_A,GPIO_AD_05 ENC_B,GPIO_AD_06 LED_GREEN,GPIO_11 +MCK,GPIO_08 +SCK_RX,GPIO_01 +WS_RX,GPIO_02 +SD_RX,GPIO_03 +SCK_TX,GPIO_06 +WS_TX,GPIO_07 +SD_TX,GPIO_04 diff --git a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h index 2e7ee34e8d..1dfe02b651 100644 --- a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h @@ -65,6 +65,36 @@ { 0 }, { 0 }, \ { IOMUXC_GPIO_SD_B1_02_LPI2C4_SCL }, { IOMUXC_GPIO_SD_B1_03_LPI2C4_SDA }, +#define MICROPY_PY_MACHINE_I2S (1) +#define MICROPY_HW_I2S_NUM (1) +#define I2S_CLOCK_MUX { 0, kCLOCK_Sai1Mux, kCLOCK_Sai2Mux } +#define I2S_CLOCK_PRE_DIV { 0, kCLOCK_Sai1PreDiv, kCLOCK_Sai2PreDiv } +#define I2S_CLOCK_DIV { 0, kCLOCK_Sai1Div, kCLOCK_Sai2Div } +#define I2S_IOMUXC_GPR_MODE { 0, kIOMUXC_GPR_SAI1MClkOutputDir, kIOMUXC_GPR_SAI2MClkOutputDir } +#define I2S_DMA_REQ_SRC_RX { 0, kDmaRequestMuxSai1Rx, kDmaRequestMuxSai2Rx } +#define I2S_DMA_REQ_SRC_TX { 0, kDmaRequestMuxSai1Tx, kDmaRequestMuxSai2Tx } + +#define I2S_GPIO(_hwid, _fn, _mode, _pin, _iomux) \ + { \ + .hw_id = _hwid, \ + .fn = _fn, \ + .mode = _mode, \ + .name = MP_QSTR_##_pin, \ + .iomux = {_iomux}, \ + } + +#define I2S_GPIO_MAP \ + { \ + I2S_GPIO(1, MCK, TX, GPIO_AD_B1_00, IOMUXC_GPIO_AD_B1_00_SAI1_MCLK), \ + I2S_GPIO(1, SCK, RX, GPIO_AD_B1_06, IOMUXC_GPIO_AD_B1_06_SAI1_RX_BCLK), \ + I2S_GPIO(1, WS, RX, GPIO_AD_B1_04, IOMUXC_GPIO_AD_B1_04_SAI1_RX_SYNC), \ + I2S_GPIO(1, SD, RX, GPIO_AD_B1_05, IOMUXC_GPIO_AD_B1_05_SAI1_RX_DATA00), \ + I2S_GPIO(1, SCK, TX, GPIO_AD_B1_01, IOMUXC_GPIO_AD_B1_01_SAI1_TX_BCLK), \ + I2S_GPIO(1, WS, TX, GPIO_AD_B1_02, IOMUXC_GPIO_AD_B1_02_SAI1_TX_SYNC), \ + I2S_GPIO(1, SD, TX, GPIO_AD_B1_03, IOMUXC_GPIO_AD_B1_03_SAI1_TX_DATA00), \ + } + + #define USDHC_DUMMY_PIN NULL, 0 #define MICROPY_USDHC1 \ { \ @@ -142,3 +172,6 @@ { IOMUXC_GPIO_AD_B0_15_ENET_TDATA01, 0, 0xB0E9u }, \ { IOMUXC_GPIO_EMC_40_ENET_MDIO, 0, 0xB0E9u }, \ { IOMUXC_GPIO_EMC_41_ENET_MDC, 0, 0xB0E9u }, + +#define MICROPY_BOARD_ROOT_POINTERS \ + struct _machine_i2s_obj_t *machine_i2s_obj[MICROPY_HW_I2S_NUM]; diff --git a/ports/mimxrt/boards/MIMXRT1020_EVK/pins.csv b/ports/mimxrt/boards/MIMXRT1020_EVK/pins.csv index 8420bbd821..e1f2669b08 100644 --- a/ports/mimxrt/boards/MIMXRT1020_EVK/pins.csv +++ b/ports/mimxrt/boards/MIMXRT1020_EVK/pins.csv @@ -30,4 +30,11 @@ SCK,GPIO_AD_B0_10 SDI,GPIO_AD_B0_13 SDO,GPIO_AD_B0_12 CS,GPIO_AD_B0_11 -LED_GREEN,GPIO_AD_B0_05 \ No newline at end of file +LED_GREEN,GPIO_AD_B0_05 +MCK,GPIO_AD_B1_00 +SCK_RX,GPIO_AD_B1_06 +WS_RX,GPIO_AD_B1_04 +SD_RX,GPIO_AD_B1_05 +SCK_TX,GPIO_AD_B1_01 +WS_TX,GPIO_AD_B1_02 +SD_TX,GPIO_AD_B1_03 diff --git a/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h index c2783a3e59..613095e67f 100644 --- a/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h @@ -53,6 +53,37 @@ { 0 }, { 0 }, \ { IOMUXC_GPIO_AD_B1_07_LPI2C3_SCL }, { IOMUXC_GPIO_AD_B1_06_LPI2C3_SDA }, +#define MICROPY_PY_MACHINE_I2S (1) +#define MICROPY_HW_I2S_NUM (1) +#define I2S_CLOCK_MUX { 0, kCLOCK_Sai1Mux, kCLOCK_Sai2Mux } +#define I2S_CLOCK_PRE_DIV { 0, kCLOCK_Sai1PreDiv, kCLOCK_Sai2PreDiv } +#define I2S_CLOCK_DIV { 0, kCLOCK_Sai1Div, kCLOCK_Sai2Div } +#define I2S_IOMUXC_GPR_MODE { 0, kIOMUXC_GPR_SAI1MClkOutputDir, kIOMUXC_GPR_SAI2MClkOutputDir } +#define I2S_DMA_REQ_SRC_RX { 0, kDmaRequestMuxSai1Rx, kDmaRequestMuxSai2Rx } +#define I2S_DMA_REQ_SRC_TX { 0, kDmaRequestMuxSai1Tx, kDmaRequestMuxSai2Tx } +#define I2S_WM8960_RX_MODE (1) + +#define I2S_GPIO(_hwid, _fn, _mode, _pin, _iomux) \ + { \ + .hw_id = _hwid, \ + .fn = _fn, \ + .mode = _mode, \ + .name = MP_QSTR_##_pin, \ + .iomux = {_iomux}, \ + } + +#define I2S_GPIO_MAP \ + { \ + I2S_GPIO(1, MCK, TX, GPIO_AD_B1_09, IOMUXC_GPIO_AD_B1_09_SAI1_MCLK), \ + I2S_GPIO(1, SCK, RX, GPIO_AD_B1_11, IOMUXC_GPIO_AD_B1_11_SAI1_RX_BCLK), \ + I2S_GPIO(1, WS, RX, GPIO_AD_B1_10, IOMUXC_GPIO_AD_B1_10_SAI1_RX_SYNC), \ + I2S_GPIO(1, SD, RX, GPIO_AD_B1_12, IOMUXC_GPIO_AD_B1_12_SAI1_RX_DATA00), \ + I2S_GPIO(1, SCK, TX, GPIO_AD_B1_14, IOMUXC_GPIO_AD_B1_14_SAI1_TX_BCLK), \ + I2S_GPIO(1, WS, TX, GPIO_AD_B1_15, IOMUXC_GPIO_AD_B1_15_SAI1_TX_SYNC), \ + I2S_GPIO(1, SD, TX, GPIO_AD_B1_13, IOMUXC_GPIO_AD_B1_13_SAI1_TX_DATA00), \ + } + + #define USDHC_DUMMY_PIN NULL, 0 #define MICROPY_USDHC1 \ @@ -131,3 +162,6 @@ { IOMUXC_GPIO_B1_11_ENET_RX_ER, 0, 0xB0E9u }, \ { IOMUXC_GPIO_EMC_41_ENET_MDIO, 0, 0xB0E9u }, \ { IOMUXC_GPIO_EMC_40_ENET_MDC, 0, 0xB0E9u }, + +#define MICROPY_BOARD_ROOT_POINTERS \ + struct _machine_i2s_obj_t *machine_i2s_obj[MICROPY_HW_I2S_NUM]; diff --git a/ports/mimxrt/boards/MIMXRT1050_EVK/pins.csv b/ports/mimxrt/boards/MIMXRT1050_EVK/pins.csv index 463079b129..4bdd9e4f03 100644 --- a/ports/mimxrt/boards/MIMXRT1050_EVK/pins.csv +++ b/ports/mimxrt/boards/MIMXRT1050_EVK/pins.csv @@ -29,3 +29,10 @@ SDI,GPIO_SD_B0_03 SDO,GPIO_SD_B0_02 CS,GPIO_SD_B0_01 LED_GREEN,GPIO_AD_B0_09 +MCK,GPIO_AD_B1_09 +SCK_RX,GPIO_AD_B1_11 +WS_RX,GPIO_AD_B1_10 +SD_RX,GPIO_AD_B1_12 +SCK_TX,GPIO_AD_B1_14 +WS_TX,GPIO_AD_B1_15 +SD_TX,GPIO_AD_B1_13 diff --git a/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h index 475d07ddb1..a3f0062389 100644 --- a/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h @@ -53,6 +53,36 @@ { 0 }, { 0 }, \ { IOMUXC_GPIO_AD_B1_07_LPI2C3_SCL }, { IOMUXC_GPIO_AD_B1_06_LPI2C3_SDA }, +#define MICROPY_PY_MACHINE_I2S (1) +#define MICROPY_HW_I2S_NUM (1) +#define I2S_CLOCK_MUX { 0, kCLOCK_Sai1Mux, kCLOCK_Sai2Mux } +#define I2S_CLOCK_PRE_DIV { 0, kCLOCK_Sai1PreDiv, kCLOCK_Sai2PreDiv } +#define I2S_CLOCK_DIV { 0, kCLOCK_Sai1Div, kCLOCK_Sai2Div } +#define I2S_IOMUXC_GPR_MODE { 0, kIOMUXC_GPR_SAI1MClkOutputDir, kIOMUXC_GPR_SAI2MClkOutputDir } +#define I2S_DMA_REQ_SRC_RX { 0, kDmaRequestMuxSai1Rx, kDmaRequestMuxSai2Rx } +#define I2S_DMA_REQ_SRC_TX { 0, kDmaRequestMuxSai1Tx, kDmaRequestMuxSai2Tx } +#define I2S_WM8960_RX_MODE (1) + +#define I2S_GPIO(_hwid, _fn, _mode, _pin, _iomux) \ + { \ + .hw_id = _hwid, \ + .fn = _fn, \ + .mode = _mode, \ + .name = MP_QSTR_##_pin, \ + .iomux = {_iomux}, \ + } + +#define I2S_GPIO_MAP \ + { \ + I2S_GPIO(1, MCK, TX, GPIO_AD_B1_09, IOMUXC_GPIO_AD_B1_09_SAI1_MCLK), \ + I2S_GPIO(1, SCK, RX, GPIO_AD_B1_11, IOMUXC_GPIO_AD_B1_11_SAI1_RX_BCLK), \ + I2S_GPIO(1, WS, RX, GPIO_AD_B1_10, IOMUXC_GPIO_AD_B1_10_SAI1_RX_SYNC), \ + I2S_GPIO(1, SD, RX, GPIO_AD_B1_12, IOMUXC_GPIO_AD_B1_12_SAI1_RX_DATA00), \ + I2S_GPIO(1, SCK, TX, GPIO_AD_B1_14, IOMUXC_GPIO_AD_B1_14_SAI1_TX_BCLK), \ + I2S_GPIO(1, WS, TX, GPIO_AD_B1_15, IOMUXC_GPIO_AD_B1_15_SAI1_TX_SYNC), \ + I2S_GPIO(1, SD, TX, GPIO_AD_B1_13, IOMUXC_GPIO_AD_B1_13_SAI1_TX_DATA00), \ + } + #define USDHC_DUMMY_PIN NULL, 0 #define MICROPY_USDHC1 \ { \ @@ -130,3 +160,6 @@ { IOMUXC_GPIO_B1_11_ENET_RX_ER, 0, 0xB0E9u }, \ { IOMUXC_GPIO_EMC_41_ENET_MDIO, 0, 0xB0E9u }, \ { IOMUXC_GPIO_EMC_40_ENET_MDC, 0, 0xB0E9u }, + +#define MICROPY_BOARD_ROOT_POINTERS \ + struct _machine_i2s_obj_t *machine_i2s_obj[MICROPY_HW_I2S_NUM]; diff --git a/ports/mimxrt/boards/MIMXRT1060_EVK/pins.csv b/ports/mimxrt/boards/MIMXRT1060_EVK/pins.csv index 463079b129..4bdd9e4f03 100644 --- a/ports/mimxrt/boards/MIMXRT1060_EVK/pins.csv +++ b/ports/mimxrt/boards/MIMXRT1060_EVK/pins.csv @@ -29,3 +29,10 @@ SDI,GPIO_SD_B0_03 SDO,GPIO_SD_B0_02 CS,GPIO_SD_B0_01 LED_GREEN,GPIO_AD_B0_09 +MCK,GPIO_AD_B1_09 +SCK_RX,GPIO_AD_B1_11 +WS_RX,GPIO_AD_B1_10 +SD_RX,GPIO_AD_B1_12 +SCK_TX,GPIO_AD_B1_14 +WS_TX,GPIO_AD_B1_15 +SD_TX,GPIO_AD_B1_13 diff --git a/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h index 874991ac65..fe1fb532b8 100644 --- a/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h @@ -53,6 +53,36 @@ { 0 }, { 0 }, \ { IOMUXC_GPIO_AD_B1_07_LPI2C3_SCL }, { IOMUXC_GPIO_AD_B1_06_LPI2C3_SDA }, +#define MICROPY_PY_MACHINE_I2S (1) +#define MICROPY_HW_I2S_NUM (1) +#define I2S_CLOCK_MUX { 0, kCLOCK_Sai1Mux, kCLOCK_Sai2Mux } +#define I2S_CLOCK_PRE_DIV { 0, kCLOCK_Sai1PreDiv, kCLOCK_Sai2PreDiv } +#define I2S_CLOCK_DIV { 0, kCLOCK_Sai1Div, kCLOCK_Sai2Div } +#define I2S_IOMUXC_GPR_MODE { 0, kIOMUXC_GPR_SAI1MClkOutputDir, kIOMUXC_GPR_SAI2MClkOutputDir } +#define I2S_DMA_REQ_SRC_RX { 0, kDmaRequestMuxSai1Rx, kDmaRequestMuxSai2Rx } +#define I2S_DMA_REQ_SRC_TX { 0, kDmaRequestMuxSai1Tx, kDmaRequestMuxSai2Tx } +#define I2S_WM8960_RX_MODE (1) + +#define I2S_GPIO(_hwid, _fn, _mode, _pin, _iomux) \ + { \ + .hw_id = _hwid, \ + .fn = _fn, \ + .mode = _mode, \ + .name = MP_QSTR_##_pin, \ + .iomux = {_iomux}, \ + } + +#define I2S_GPIO_MAP \ + { \ + I2S_GPIO(1, MCK, TX, GPIO_AD_B1_09, IOMUXC_GPIO_AD_B1_09_SAI1_MCLK), \ + I2S_GPIO(1, SCK, RX, GPIO_AD_B1_11, IOMUXC_GPIO_AD_B1_11_SAI1_RX_BCLK), \ + I2S_GPIO(1, WS, RX, GPIO_AD_B1_10, IOMUXC_GPIO_AD_B1_10_SAI1_RX_SYNC), \ + I2S_GPIO(1, SD, RX, GPIO_AD_B1_12, IOMUXC_GPIO_AD_B1_12_SAI1_RX_DATA00), \ + I2S_GPIO(1, SCK, TX, GPIO_AD_B1_14, IOMUXC_GPIO_AD_B1_14_SAI1_TX_BCLK), \ + I2S_GPIO(1, WS, TX, GPIO_AD_B1_15, IOMUXC_GPIO_AD_B1_15_SAI1_TX_SYNC), \ + I2S_GPIO(1, SD, TX, GPIO_AD_B1_13, IOMUXC_GPIO_AD_B1_13_SAI1_TX_DATA00), \ + } + #define USDHC_DUMMY_PIN NULL, 0 #define MICROPY_USDHC1 \ { \ @@ -130,3 +160,6 @@ { IOMUXC_GPIO_B1_11_ENET_RX_ER, 0, 0xB0E9u }, \ { IOMUXC_GPIO_EMC_41_ENET_MDIO, 0, 0xB0E9u }, \ { IOMUXC_GPIO_EMC_40_ENET_MDC, 0, 0xB0E9u }, + +#define MICROPY_BOARD_ROOT_POINTERS \ + struct _machine_i2s_obj_t *machine_i2s_obj[MICROPY_HW_I2S_NUM]; diff --git a/ports/mimxrt/boards/MIMXRT1064_EVK/pins.csv b/ports/mimxrt/boards/MIMXRT1064_EVK/pins.csv index 463079b129..4bdd9e4f03 100644 --- a/ports/mimxrt/boards/MIMXRT1064_EVK/pins.csv +++ b/ports/mimxrt/boards/MIMXRT1064_EVK/pins.csv @@ -29,3 +29,10 @@ SDI,GPIO_SD_B0_03 SDO,GPIO_SD_B0_02 CS,GPIO_SD_B0_01 LED_GREEN,GPIO_AD_B0_09 +MCK,GPIO_AD_B1_09 +SCK_RX,GPIO_AD_B1_11 +WS_RX,GPIO_AD_B1_10 +SD_RX,GPIO_AD_B1_12 +SCK_TX,GPIO_AD_B1_14 +WS_TX,GPIO_AD_B1_15 +SD_TX,GPIO_AD_B1_13 diff --git a/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.h b/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.h index e004050aee..c30caa0470 100644 --- a/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.h +++ b/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.h @@ -48,3 +48,39 @@ #define IOMUX_TABLE_I2C \ { IOMUXC_GPIO_AD_14_LPI2C1_SCL }, { IOMUXC_GPIO_AD_13_LPI2C1_SDA }, \ { IOMUXC_GPIO_AD_08_LPI2C2_SCL }, { IOMUXC_GPIO_AD_07_LPI2C2_SDA }, + +#define MICROPY_PY_MACHINE_I2S (1) +#define MICROPY_HW_I2S_NUM (3) +#define I2S_CLOCK_MUX { 0, kCLOCK_Sai1Mux, 0, kCLOCK_Sai3Mux } +#define I2S_CLOCK_PRE_DIV { 0, kCLOCK_Sai1PreDiv, 0, kCLOCK_Sai3PreDiv } +#define I2S_CLOCK_DIV { 0, kCLOCK_Sai1Div, 0, kCLOCK_Sai3Div } +#define I2S_IOMUXC_GPR_MODE { 0, kIOMUXC_GPR_SAI1MClkOutputDir, 0, kIOMUXC_GPR_SAI3MClkOutputDir } +#define I2S_DMA_REQ_SRC_RX { 0, kDmaRequestMuxSai1Rx, 0, kDmaRequestMuxSai3Rx } +#define I2S_DMA_REQ_SRC_TX { 0, kDmaRequestMuxSai1Tx, 0, kDmaRequestMuxSai3Tx } + +#define I2S_GPIO(_hwid, _fn, _mode, _pin, _iomux) \ + { \ + .hw_id = _hwid, \ + .fn = _fn, \ + .mode = _mode, \ + .name = MP_QSTR_##_pin, \ + .iomux = {_iomux}, \ + } + +#define I2S_GPIO_MAP \ + { \ + I2S_GPIO(1, MCK, TX, GPIO_08, IOMUXC_GPIO_08_SAI1_MCLK), /* pin D8 */ \ + I2S_GPIO(1, SCK, RX, GPIO_01, IOMUXC_GPIO_01_SAI1_RX_BCLK), /* pin D1 */ \ + I2S_GPIO(1, WS, RX, GPIO_02, IOMUXC_GPIO_02_SAI1_RX_SYNC), /* pin D2 */ \ + I2S_GPIO(1, SD, RX, GPIO_03, IOMUXC_GPIO_03_SAI1_RX_DATA00), /* pin D3 */ \ + I2S_GPIO(1, SCK, TX, GPIO_06, IOMUXC_GPIO_06_SAI1_TX_BCLK), /* pin D6 */ \ + I2S_GPIO(1, WS, TX, GPIO_07, IOMUXC_GPIO_07_SAI1_TX_SYNC), /* pin D7 */ \ + I2S_GPIO(1, SD, TX, GPIO_04, IOMUXC_GPIO_04_SAI1_TX_DATA00), /* pin D4 */ \ + I2S_GPIO(3, SCK, TX, GPIO_SD_01, IOMUXC_GPIO_SD_01_SAI3_TX_BCLK), /* pin D10 */ \ + I2S_GPIO(3, WS, TX, GPIO_SD_00, IOMUXC_GPIO_SD_00_SAI3_TX_SYNC), /* pin D9 */ \ + I2S_GPIO(3, SD, TX, GPIO_SD_02, IOMUXC_GPIO_SD_02_SAI3_TX_DATA) /* pin D11 */ \ + } + + +#define MICROPY_BOARD_ROOT_POINTERS \ + struct _machine_i2s_obj_t *machine_i2s_obj[MICROPY_HW_I2S_NUM]; diff --git a/ports/mimxrt/boards/OLIMEX_RT1010/pins.csv b/ports/mimxrt/boards/OLIMEX_RT1010/pins.csv index d294f86b6d..d3c68aba2d 100644 --- a/ports/mimxrt/boards/OLIMEX_RT1010/pins.csv +++ b/ports/mimxrt/boards/OLIMEX_RT1010/pins.csv @@ -34,3 +34,10 @@ RX, GPIO_09 TX, GPIO_10 RELAY1,GPIO_SD_12 RELAY2,GPIO_SD_13 +MCK,GPIO_08 +SCK_RX,GPIO_01 +WS_RX,GPIO_02 +SD_RX,GPIO_03 +SCK_TX,GPIO_06 +WS_TX,GPIO_07 +SD_TX,GPIO_04 diff --git a/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.h b/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.h index 53a3d4ed78..a6502d3353 100644 --- a/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.h +++ b/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.h @@ -65,6 +65,35 @@ { IOMUXC_GPIO_B0_04_LPI2C2_SCL }, { IOMUXC_GPIO_B0_05_LPI2C2_SDA }, \ { IOMUXC_GPIO_AD_B1_07_LPI2C3_SCL }, { IOMUXC_GPIO_AD_B1_06_LPI2C3_SDA } +#define MICROPY_PY_MACHINE_I2S (1) +#define MICROPY_HW_I2S_NUM (1) +#define I2S_CLOCK_MUX { 0, kCLOCK_Sai1Mux } +#define I2S_CLOCK_PRE_DIV { 0, kCLOCK_Sai1PreDiv } +#define I2S_CLOCK_DIV { 0, kCLOCK_Sai1Div } +#define I2S_IOMUXC_GPR_MODE { 0, kIOMUXC_GPR_SAI1MClkOutputDir } +#define I2S_DMA_REQ_SRC_RX { 0, kDmaRequestMuxSai1Rx } +#define I2S_DMA_REQ_SRC_TX { 0, kDmaRequestMuxSai1Tx } + +#define I2S_GPIO(_hwid, _fn, _mode, _pin, _iomux) \ + { \ + .hw_id = _hwid, \ + .fn = _fn, \ + .mode = _mode, \ + .name = MP_QSTR_##_pin, \ + .iomux = {_iomux}, \ + } + +#define I2S_GPIO_MAP \ + { \ + I2S_GPIO(1, MCK, TX, GPIO_AD_B1_09, IOMUXC_GPIO_AD_B1_09_SAI1_MCLK), /* pin J4 09 */ \ + I2S_GPIO(1, SCK, RX, GPIO_AD_B1_11, IOMUXC_GPIO_AD_B1_11_SAI1_RX_BCLK), /* pin J4 11 */ \ + I2S_GPIO(1, WS, RX, GPIO_AD_B1_10, IOMUXC_GPIO_AD_B1_10_SAI1_RX_SYNC), /* pin J4 10 */ \ + I2S_GPIO(1, SD, RX, GPIO_AD_B1_12, IOMUXC_GPIO_AD_B1_12_SAI1_RX_DATA00), /* pin J4 12 */ \ + I2S_GPIO(1, SCK, TX, GPIO_AD_B1_14, IOMUXC_GPIO_AD_B1_14_SAI1_TX_BCLK), /* pin J4 14 */ \ + I2S_GPIO(1, WS, TX, GPIO_AD_B1_15, IOMUXC_GPIO_AD_B1_15_SAI1_TX_SYNC), /* pin J4 15 */ \ + I2S_GPIO(1, SD, TX, GPIO_AD_B1_13, IOMUXC_GPIO_AD_B1_13_SAI1_TX_DATA00) /* pin J4 13 */ \ + } + #define USDHC_DUMMY_PIN NULL, 0 #define MICROPY_USDHC1 \ @@ -144,3 +173,6 @@ #define MIMXRT_IOMUXC_SEMC_WE IOMUXC_GPIO_EMC_28_SEMC_WE #define MIMXRT_IOMUXC_SEMC_CS0 IOMUXC_GPIO_EMC_29_SEMC_CS0 + +#define MICROPY_BOARD_ROOT_POINTERS \ + struct _machine_i2s_obj_t *machine_i2s_obj[MICROPY_HW_I2S_NUM]; diff --git a/ports/mimxrt/boards/SEEED_ARCH_MIX/pins.csv b/ports/mimxrt/boards/SEEED_ARCH_MIX/pins.csv index 0183795cd7..6b757fae4b 100644 --- a/ports/mimxrt/boards/SEEED_ARCH_MIX/pins.csv +++ b/ports/mimxrt/boards/SEEED_ARCH_MIX/pins.csv @@ -60,3 +60,10 @@ J5_50,GPIO_AD_B0_02 LED_RED,GPIO_AD_B0_09 LED_GREEN,GPIO_AD_B0_10 LED_BLUE,GPIO_AD_B0_11 +MCK,GPIO_AD_B1_09 +SCK_RX,GPIO_AD_B1_11 +WS_RX,GPIO_AD_B1_10 +SD_RX,GPIO_AD_B1_12 +SCK_TX,GPIO_AD_B1_14 +WS_TX,GPIO_AD_B1_15 +SD_TX,GPIO_AD_B1_13 diff --git a/ports/mimxrt/boards/TEENSY40/mpconfigboard.h b/ports/mimxrt/boards/TEENSY40/mpconfigboard.h index 08a774d886..f2ea86bd0e 100644 --- a/ports/mimxrt/boards/TEENSY40/mpconfigboard.h +++ b/ports/mimxrt/boards/TEENSY40/mpconfigboard.h @@ -58,6 +58,41 @@ { IOMUXC_GPIO_AD_B1_07_LPI2C3_SCL }, { IOMUXC_GPIO_AD_B1_06_LPI2C3_SDA }, \ { IOMUXC_GPIO_AD_B0_12_LPI2C4_SCL }, { IOMUXC_GPIO_AD_B0_13_LPI2C4_SDA }, +#define MICROPY_PY_MACHINE_I2S (1) +#define MICROPY_HW_I2S_NUM (2) +#define I2S_CLOCK_MUX { 0, kCLOCK_Sai1Mux, kCLOCK_Sai2Mux } +#define I2S_CLOCK_PRE_DIV { 0, kCLOCK_Sai1PreDiv, kCLOCK_Sai2PreDiv } +#define I2S_CLOCK_DIV { 0, kCLOCK_Sai1Div, kCLOCK_Sai2Div } +#define I2S_IOMUXC_GPR_MODE { 0, kIOMUXC_GPR_SAI1MClkOutputDir, kIOMUXC_GPR_SAI2MClkOutputDir } +#define I2S_DMA_REQ_SRC_RX { 0, kDmaRequestMuxSai1Rx, kDmaRequestMuxSai2Rx } +#define I2S_DMA_REQ_SRC_TX { 0, kDmaRequestMuxSai1Tx, kDmaRequestMuxSai2Tx } + +#define I2S_GPIO(_hwid, _fn, _mode, _pin, _iomux) \ + { \ + .hw_id = _hwid, \ + .fn = _fn, \ + .mode = _mode, \ + .name = MP_QSTR_##_pin, \ + .iomux = {_iomux}, \ + } + +#define I2S_GPIO_MAP \ + { \ + I2S_GPIO(1, SCK, RX, GPIO_AD_B1_11, IOMUXC_GPIO_AD_B1_11_SAI1_RX_BCLK), /* pin 21 */ \ + I2S_GPIO(1, WS, RX, GPIO_AD_B1_10, IOMUXC_GPIO_AD_B1_10_SAI1_RX_SYNC), /* pin 20 */ \ + I2S_GPIO(1, SD, RX, GPIO_B1_00, IOMUXC_GPIO_B1_00_SAI1_RX_DATA00), /* pin 8 */ \ + I2S_GPIO(1, SCK, TX, GPIO_AD_B1_14, IOMUXC_GPIO_AD_B1_14_SAI1_TX_BCLK), /* pin 26 */ \ + I2S_GPIO(1, SCK, TX, GPIO_B1_02, IOMUXC_GPIO_B1_02_SAI1_TX_BCLK), /* pin 36 */ \ + I2S_GPIO(1, WS, TX, GPIO_AD_B1_15, IOMUXC_GPIO_AD_B1_15_SAI1_TX_SYNC), /* pin 27 */ \ + I2S_GPIO(1, WS, TX, GPIO_B1_03, IOMUXC_GPIO_B1_03_SAI1_TX_SYNC), /* pin 37 */ \ + I2S_GPIO(1, SD, TX, GPIO_B1_01, IOMUXC_GPIO_B1_01_SAI1_TX_DATA00), /* pin 7 */ \ + I2S_GPIO(1, MCK, TX, GPIO_AD_B1_09, IOMUXC_GPIO_AD_B1_09_SAI1_MCLK), /* pin 23 */ \ + I2S_GPIO(2, SCK, TX, GPIO_EMC_06, IOMUXC_GPIO_EMC_06_SAI2_TX_BCLK), /* pin 4 */ \ + I2S_GPIO(2, WS, TX, GPIO_EMC_05, IOMUXC_GPIO_EMC_05_SAI2_TX_SYNC), /* pin 3 */ \ + I2S_GPIO(2, SD, TX, GPIO_EMC_04, IOMUXC_GPIO_EMC_04_SAI2_TX_DATA), /* pin 2 */ \ + I2S_GPIO(2, MCK, TX, GPIO_EMC_07, IOMUXC_GPIO_EMC_07_SAI2_MCLK) /* pin 33 */ \ + } + #define USDHC_DUMMY_PIN NULL, 0 #define MICROPY_USDHC1 \ { \ @@ -69,3 +104,6 @@ .data2 = { GPIO_SD_B0_04_USDHC1_DATA2 }, \ .data3 = { GPIO_SD_B0_05_USDHC1_DATA3 }, \ } + +#define MICROPY_BOARD_ROOT_POINTERS \ + struct _machine_i2s_obj_t *machine_i2s_obj[MICROPY_HW_I2S_NUM]; diff --git a/ports/mimxrt/boards/TEENSY40/pins.csv b/ports/mimxrt/boards/TEENSY40/pins.csv index 0ea4f13731..b4509e5848 100644 --- a/ports/mimxrt/boards/TEENSY40/pins.csv +++ b/ports/mimxrt/boards/TEENSY40/pins.csv @@ -52,4 +52,11 @@ A10,GPIO_AD_B0_12 A11,GPIO_AD_B0_13 A12,GPIO_AD_B1_14 A13,GPIO_AD_B1_15 -LED,GPIO_B0_03 \ No newline at end of file +LED,GPIO_B0_03 +MCK,GPIO_AD_B1_09 +SCK_RX,GPIO_AD_B1_11 +WS_RX,GPIO_AD_B1_10 +SD_RX,GPIO_B1_00 +SCK_TX,GPIO_EMC_06 +WS_TX,GPIO_EMC_05 +SD_TX,GPIO_EMC_04 diff --git a/ports/mimxrt/boards/TEENSY41/mpconfigboard.h b/ports/mimxrt/boards/TEENSY41/mpconfigboard.h index 3b8d497c3f..7d75f62f36 100644 --- a/ports/mimxrt/boards/TEENSY41/mpconfigboard.h +++ b/ports/mimxrt/boards/TEENSY41/mpconfigboard.h @@ -58,6 +58,43 @@ { IOMUXC_GPIO_AD_B1_07_LPI2C3_SCL }, { IOMUXC_GPIO_AD_B1_06_LPI2C3_SDA }, \ { IOMUXC_GPIO_AD_B0_12_LPI2C4_SCL }, { IOMUXC_GPIO_AD_B0_13_LPI2C4_SDA }, +#define MICROPY_PY_MACHINE_I2S (1) +#define MICROPY_HW_I2S_NUM (2) +#define I2S_CLOCK_MUX { 0, kCLOCK_Sai1Mux, kCLOCK_Sai2Mux } +#define I2S_CLOCK_PRE_DIV { 0, kCLOCK_Sai1PreDiv, kCLOCK_Sai2PreDiv } +#define I2S_CLOCK_DIV { 0, kCLOCK_Sai1Div, kCLOCK_Sai2Div } +#define I2S_IOMUXC_GPR_MODE { 0, kIOMUXC_GPR_SAI1MClkOutputDir, kIOMUXC_GPR_SAI2MClkOutputDir } +#define I2S_DMA_REQ_SRC_RX { 0, kDmaRequestMuxSai1Rx, kDmaRequestMuxSai2Rx } +#define I2S_DMA_REQ_SRC_TX { 0, kDmaRequestMuxSai1Tx, kDmaRequestMuxSai2Tx } + +#define I2S_GPIO(_hwid, _fn, _mode, _pin, _iomux) \ + { \ + .hw_id = _hwid, \ + .fn = _fn, \ + .mode = _mode, \ + .name = MP_QSTR_##_pin, \ + .iomux = {_iomux}, \ + } + +#define I2S_GPIO_MAP \ + { \ + I2S_GPIO(1, SCK, RX, GPIO_AD_B1_11, IOMUXC_GPIO_AD_B1_11_SAI1_RX_BCLK), /* pin 21 */ \ + I2S_GPIO(1, WS, RX, GPIO_AD_B1_10, IOMUXC_GPIO_AD_B1_10_SAI1_RX_SYNC), /* pin 20 */ \ + I2S_GPIO(1, SD, RX, GPIO_AD_B1_12, IOMUXC_GPIO_AD_B1_12_SAI1_RX_DATA00), /* pin 38 */ \ + I2S_GPIO(1, SD, RX, GPIO_B1_00, IOMUXC_GPIO_B1_00_SAI1_RX_DATA00), /* pin 8 */ \ + I2S_GPIO(1, SCK, TX, GPIO_AD_B1_14, IOMUXC_GPIO_AD_B1_14_SAI1_TX_BCLK), /* pin 26 */ \ + I2S_GPIO(1, SCK, TX, GPIO_B1_02, IOMUXC_GPIO_B1_02_SAI1_TX_BCLK), /* pin 36 */ \ + I2S_GPIO(1, WS, TX, GPIO_AD_B1_15, IOMUXC_GPIO_AD_B1_15_SAI1_TX_SYNC), /* pin 27 */ \ + I2S_GPIO(1, WS, TX, GPIO_B1_03, IOMUXC_GPIO_B1_03_SAI1_TX_SYNC), /* pin 37 */ \ + I2S_GPIO(1, SD, TX, GPIO_AD_B1_13, IOMUXC_GPIO_AD_B1_13_SAI1_TX_DATA00), /* pin 39 */ \ + I2S_GPIO(1, SD, TX, GPIO_B1_01, IOMUXC_GPIO_B1_01_SAI1_TX_DATA00), /* pin 7 */ \ + I2S_GPIO(1, MCK, TX, GPIO_AD_B1_09, IOMUXC_GPIO_AD_B1_09_SAI1_MCLK), /* pin 23 */ \ + I2S_GPIO(2, SCK, TX, GPIO_EMC_06, IOMUXC_GPIO_EMC_06_SAI2_TX_BCLK), /* pin 4 */ \ + I2S_GPIO(2, WS, TX, GPIO_EMC_05, IOMUXC_GPIO_EMC_05_SAI2_TX_SYNC), /* pin 3 */ \ + I2S_GPIO(2, SD, TX, GPIO_EMC_04, IOMUXC_GPIO_EMC_04_SAI2_TX_DATA), /* pin 2 */ \ + I2S_GPIO(2, MCK, TX, GPIO_EMC_07, IOMUXC_GPIO_EMC_07_SAI2_MCLK) /* pin 33 */ \ + } + #define USDHC_DUMMY_PIN NULL, 0 #define MICROPY_USDHC1 \ { \ @@ -90,3 +127,6 @@ { IOMUXC_GPIO_B1_11_ENET_RX_ER, 0, 0xB0E9u }, \ { IOMUXC_GPIO_B1_15_ENET_MDIO, 0, 0xB0E9u }, \ { IOMUXC_GPIO_B1_14_ENET_MDC, 0, 0xB0E9u }, + +#define MICROPY_BOARD_ROOT_POINTERS \ + struct _machine_i2s_obj_t *machine_i2s_obj[MICROPY_HW_I2S_NUM]; diff --git a/ports/mimxrt/boards/TEENSY41/pins.csv b/ports/mimxrt/boards/TEENSY41/pins.csv index 88b91f2540..b28a22312c 100755 --- a/ports/mimxrt/boards/TEENSY41/pins.csv +++ b/ports/mimxrt/boards/TEENSY41/pins.csv @@ -80,4 +80,11 @@ A10,GPIO_AD_B0_12 A11,GPIO_AD_B0_13 A12,GPIO_AD_B1_14 A13,GPIO_AD_B1_15 -LED,GPIO_B0_03 \ No newline at end of file +LED,GPIO_B0_03 +MCK,GPIO_AD_B1_09 +SCK_RX,GPIO_AD_B1_11 +WS_RX,GPIO_AD_B1_10 +SD_RX,GPIO_B1_00 +SCK_TX,GPIO_EMC_06 +WS_TX,GPIO_EMC_05 +SD_TX,GPIO_EMC_04 diff --git a/ports/mimxrt/dma_channel.c b/ports/mimxrt/dma_manager.c similarity index 86% rename from ports/mimxrt/dma_channel.c rename to ports/mimxrt/dma_manager.c index c6cae9da9c..2890a15586 100644 --- a/ports/mimxrt/dma_channel.c +++ b/ports/mimxrt/dma_manager.c @@ -24,7 +24,10 @@ * THE SOFTWARE. */ -#include "dma_channel.h" +#include +#include "py/mpconfig.h" +#include "fsl_edma.h" +#include "dma_manager.h" // List of channel flags: true: channel used, false: channel available static bool channel_list[FSL_FEATURE_DMAMUX_MODULE_CHANNEL] = { @@ -39,6 +42,8 @@ static bool channel_list[FSL_FEATURE_DMAMUX_MODULE_CHANNEL] = { #endif }; +STATIC bool dma_initialized = false; + // allocate_channel(): retrieve an available channel. Return the number or -1 int allocate_dma_channel(void) { for (int i = 0; i < ARRAY_SIZE(channel_list); i++) { @@ -56,3 +61,12 @@ void free_dma_channel(int n) { channel_list[n] = false; } } + +void dma_init(void) { + if (!dma_initialized) { + edma_config_t dmaConfig; + EDMA_GetDefaultConfig(&dmaConfig); + EDMA_Init(DMA0, &dmaConfig); + dma_initialized = true; + } +} diff --git a/ports/mimxrt/dma_channel.h b/ports/mimxrt/dma_manager.h similarity index 98% rename from ports/mimxrt/dma_channel.h rename to ports/mimxrt/dma_manager.h index 3fe66a3dbb..77e9689b22 100644 --- a/ports/mimxrt/dma_channel.h +++ b/ports/mimxrt/dma_manager.h @@ -30,5 +30,6 @@ int allocate_dma_channel(void); void free_dma_channel(int n); +void dma_init(void); #endif // MICROPY_INCLUDED_MIMXRT_DMACHANNEL_H diff --git a/ports/mimxrt/machine_i2s.c b/ports/mimxrt/machine_i2s.c new file mode 100644 index 0000000000..a41dfe94f1 --- /dev/null +++ b/ports/mimxrt/machine_i2s.c @@ -0,0 +1,1228 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 Mike Teachman + * Copyright (c) 2022 Robert Hammelrath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "py/obj.h" +#include "py/runtime.h" +#include "py/mphal.h" +#include "py/misc.h" +#include "py/stream.h" +#include "py/objstr.h" +#include "modmachine.h" +#include "dma_manager.h" + +#include "clock_config.h" +#include "fsl_iomuxc.h" +#include "fsl_dmamux.h" +#include "fsl_edma.h" +#include "fsl_sai.h" + +#if MICROPY_PY_MACHINE_I2S +// The I2S module has 3 modes of operation: +// +// Mode1: Blocking +// - readinto() and write() methods block until the supplied buffer is filled (read) or emptied (write) +// - this is the default mode of operation +// +// Mode2: Non-Blocking +// - readinto() and write() methods return immediately +// - buffer filling and emptying happens asynchronously to the main MicroPython task +// - a callback function is called when the supplied buffer has been filled (read) or emptied (write) +// - non-blocking mode is enabled when a callback is set with the irq() method +// - the DMA callback is used to implement the asynchronous background operations +// +// Mode3: Uasyncio +// - implements the stream protocol +// - uasyncio mode is enabled when the ioctl() function is called +// - the state of the internal ring buffer is used to detect that I2S samples can be read or written +// +// The samples contained in the app buffer supplied for the readinto() and write() methods have the following convention: +// Mono: little endian format +// Stereo: little endian format, left channel first +// +// I2S terms: +// "frame": consists of two audio samples (Left audio sample + Right audio sample) +// +// Misc: +// - for Mono configuration: +// - readinto method: samples are gathered from the L channel only +// - write method: every sample is output to both the L and R channels +// - for readinto method the I2S hardware is read using 8-byte frames +// (this is standard for almost all I2S hardware, such as MEMS microphones) +// - all 3 Modes of operation are implemented using the peripheral drivers in the NXP MCUXpresso SDK +// - all sample data transfers use DMA +// - the DMA ping-pong buffer needs to be aligned to a cache line size of 32 bytes. 32 byte +// alignment is needed to use the routines that clean and invalidate D-Cache which work on a +// 32 byte address boundary. +// - master clock frequency is sampling frequency * 256 + +// DMA ping-pong buffer size was empirically determined. It is a tradeoff between: +// 1. memory use (smaller buffer size desirable to reduce memory footprint) +// 2. interrupt frequency (larger buffer size desirable to reduce interrupt frequency) +// The sizeof 1/2 of the DMA buffer must be evenly divisible by the cache line size of 32 bytes. +#define SIZEOF_DMA_BUFFER_IN_BYTES (256) +#define SIZEOF_HALF_DMA_BUFFER_IN_BYTES (SIZEOF_DMA_BUFFER_IN_BYTES / 2) + +// For non-blocking mode, to avoid underflow/overflow, sample data is written/read to/from the ring buffer at a rate faster +// than the DMA transfer rate +#define NON_BLOCKING_RATE_MULTIPLIER (4) +#define SIZEOF_NON_BLOCKING_COPY_IN_BYTES (SIZEOF_HALF_DMA_BUFFER_IN_BYTES * NON_BLOCKING_RATE_MULTIPLIER) + +#define NUM_I2S_USER_FORMATS (4) +#define I2S_RX_FRAME_SIZE_IN_BYTES (8) +#define AUDIO_PLL_CLOCK (2U) +#define SAI_CHANNEL_0 (0) +#define SAI_NUM_AUDIO_CHANNELS (2U) + +typedef enum { + SCK, + WS, + SD, + MCK +} i2s_pin_function_t; + +typedef enum { + RX, + TX, +} i2s_mode_t; + +typedef enum { + MONO, + STEREO +} format_t; + +typedef enum { + BLOCKING, + NON_BLOCKING, + UASYNCIO +} io_mode_t; + +typedef enum { + TOP_HALF, + BOTTOM_HALF +} ping_pong_t; + +typedef struct _ring_buf_t { + uint8_t *buffer; + size_t head; + size_t tail; + size_t size; +} ring_buf_t; + +typedef struct _non_blocking_descriptor_t { + mp_buffer_info_t appbuf; + uint32_t index; + bool copy_in_progress; +} non_blocking_descriptor_t; + +typedef struct _machine_i2s_obj_t { + mp_obj_base_t base; + uint8_t i2s_id; + mp_hal_pin_obj_t sck; + mp_hal_pin_obj_t ws; + mp_hal_pin_obj_t sd; + mp_hal_pin_obj_t mck; + i2s_mode_t mode; + int8_t bits; + format_t format; + int32_t rate; + int32_t ibuf; + mp_obj_t callback_for_non_blocking; + uint8_t dma_buffer[SIZEOF_DMA_BUFFER_IN_BYTES + 0x1f]; // 0x1f related to D-Cache alignment + uint8_t *dma_buffer_dcache_aligned; + ring_buf_t ring_buffer; + uint8_t *ring_buffer_storage; + non_blocking_descriptor_t non_blocking_descriptor; + io_mode_t io_mode; + I2S_Type *i2s_inst; + int dma_channel; + edma_handle_t edmaHandle; + edma_tcd_t *edmaTcd; +} machine_i2s_obj_t; + +typedef struct _iomux_table_t { + uint32_t muxRegister; + uint32_t muxMode; + uint32_t inputRegister; + uint32_t inputDaisy; + uint32_t configRegister; +} iomux_table_t; + +typedef struct _gpio_map_t { + uint8_t hw_id; + i2s_pin_function_t fn; + i2s_mode_t mode; + qstr name; + iomux_table_t iomux; +} gpio_map_t; + +typedef struct _i2s_clock_config_t { + sai_sample_rate_t rate; + const clock_audio_pll_config_t *pll_config; + uint32_t clock_pre_divider; + uint32_t clock_divider; +} i2s_clock_config_t; + +STATIC mp_obj_t machine_i2s_deinit(mp_obj_t self_in); + +// The frame map is used with the readinto() method to transform the audio sample data coming +// from DMA memory (32-bit stereo) to the format specified +// in the I2S constructor. e.g. 16-bit mono +STATIC const int8_t i2s_frame_map[NUM_I2S_USER_FORMATS][I2S_RX_FRAME_SIZE_IN_BYTES] = { + {-1, -1, 0, 1, -1, -1, -1, -1 }, // Mono, 16-bits + { 0, 1, 2, 3, -1, -1, -1, -1 }, // Mono, 32-bits + {-1, -1, 0, 1, -1, -1, 2, 3 }, // Stereo, 16-bits + { 0, 1, 2, 3, 4, 5, 6, 7 }, // Stereo, 32-bits +}; + +// 2 PLL configurations +// PLL output frequency = 24MHz * (.loopDivider + .numerator/.denominator) + +// Configuration 1: for sampling frequencies [Hz]: 8000, 12000, 16000, 24000, 32000, 48000 +// Clock frequency = 786,432,480 Hz +STATIC const clock_audio_pll_config_t audioPllConfig_8000_48000 = { + .loopDivider = 32, // PLL loop divider. Valid range for DIV_SELECT divider value: 27~54 + .postDivider = 1, // Divider after the PLL, should only be 1, 2, 4, 8, 16 + .numerator = 76802, // 30 bit numerator of fractional loop divider + .denominator = 100000, // 30 bit denominator of fractional loop divider + .src = kCLOCK_PllClkSrc24M // Pll clock source +}; + +// Configuration 2: for sampling frequencies [Hz]: 11025, 22050, 44100 +// Clock frequency = 722,534,880 +STATIC const clock_audio_pll_config_t audioPllConfig_11025_44100 = { + .loopDivider = 30, // PLL loop divider. Valid range for DIV_SELECT divider value: 27~54 + .postDivider = 1, // Divider after the PLL, should only be 1, 2, 4, 8, 16 + .numerator = 10562, // 30 bit numerator of fractional loop divider + .denominator = 100000, // 30 bit denominator of fractional loop divider + .src = kCLOCK_PllClkSrc24M // Pll clock source +}; + +STATIC const i2s_clock_config_t clock_config_map[] = { + {kSAI_SampleRate8KHz, &audioPllConfig_8000_48000, 5, 63}, + {kSAI_SampleRate11025Hz, &audioPllConfig_11025_44100, 3, 63}, + {kSAI_SampleRate12KHz, &audioPllConfig_8000_48000, 3, 63}, + {kSAI_SampleRate16KHz, &audioPllConfig_8000_48000, 2, 63}, + {kSAI_SampleRate22050Hz, &audioPllConfig_11025_44100, 1, 63}, + {kSAI_SampleRate24KHz, &audioPllConfig_8000_48000, 1, 63}, + {kSAI_SampleRate32KHz, &audioPllConfig_8000_48000, 1, 47}, + {kSAI_SampleRate44100Hz, &audioPllConfig_11025_44100, 0, 63}, + {kSAI_SampleRate48KHz, &audioPllConfig_8000_48000, 0, 63} +}; + +STATIC const I2S_Type *i2s_base_ptr[] = I2S_BASE_PTRS; +STATIC const clock_mux_t i2s_clock_mux[] = I2S_CLOCK_MUX; +STATIC const clock_div_t i2s_clock_pre_div[] = I2S_CLOCK_PRE_DIV; +STATIC const clock_div_t i2s_clock_div[] = I2S_CLOCK_DIV; +STATIC const iomuxc_gpr_mode_t i2s_iomuxc_gpr_mode[] = I2S_IOMUXC_GPR_MODE; +STATIC const dma_request_source_t i2s_dma_req_src_tx[] = I2S_DMA_REQ_SRC_TX; +STATIC const dma_request_source_t i2s_dma_req_src_rx[] = I2S_DMA_REQ_SRC_RX; +STATIC const gpio_map_t i2s_gpio_map[] = I2S_GPIO_MAP; +AT_NONCACHEABLE_SECTION_ALIGN(STATIC edma_tcd_t edmaTcd[MICROPY_HW_I2S_NUM], 32); + +// called on processor reset +void machine_i2s_init0() { + for (uint8_t i = 0; i < MICROPY_HW_I2S_NUM; i++) { + MP_STATE_PORT(machine_i2s_obj)[i] = NULL; + } +} + +// called on soft reboot +void machine_i2s_deinit_all(void) { + for (uint8_t i = 0; i < MICROPY_HW_I2S_NUM; i++) { + machine_i2s_obj_t *i2s_obj = MP_STATE_PORT(machine_i2s_obj)[i]; + if (i2s_obj != NULL) { + machine_i2s_deinit(i2s_obj); + MP_STATE_PORT(machine_i2s_obj)[i] = NULL; + } + } +} + +// Ring Buffer +// Thread safe when used with these constraints: +// - Single Producer, Single Consumer +// - Sequential atomic operations +// One byte of capacity is used to detect buffer empty/full + +STATIC void ringbuf_init(ring_buf_t *rbuf, uint8_t *buffer, size_t size) { + rbuf->buffer = buffer; + rbuf->size = size; + rbuf->head = 0; + rbuf->tail = 0; +} + +STATIC bool ringbuf_push(ring_buf_t *rbuf, uint8_t data) { + size_t next_tail = (rbuf->tail + 1) % rbuf->size; + + if (next_tail != rbuf->head) { + rbuf->buffer[rbuf->tail] = data; + rbuf->tail = next_tail; + return true; + } + + // full + return false; +} + +STATIC bool ringbuf_pop(ring_buf_t *rbuf, uint8_t *data) { + if (rbuf->head == rbuf->tail) { + // empty + return false; + } + + *data = rbuf->buffer[rbuf->head]; + rbuf->head = (rbuf->head + 1) % rbuf->size; + return true; +} + +STATIC bool ringbuf_is_empty(ring_buf_t *rbuf) { + return rbuf->head == rbuf->tail; +} + +STATIC bool ringbuf_is_full(ring_buf_t *rbuf) { + return ((rbuf->tail + 1) % rbuf->size) == rbuf->head; +} + +STATIC size_t ringbuf_available_data(ring_buf_t *rbuf) { + return (rbuf->tail - rbuf->head + rbuf->size) % rbuf->size; +} + +STATIC size_t ringbuf_available_space(ring_buf_t *rbuf) { + return rbuf->size - ringbuf_available_data(rbuf) - 1; +} + +STATIC int8_t get_frame_mapping_index(int8_t bits, format_t format) { + if (format == MONO) { + if (bits == 16) { + return 0; + } else { // 32 bits + return 1; + } + } else { // STEREO + if (bits == 16) { + return 2; + } else { // 32 bits + return 3; + } + } +} + +STATIC int8_t get_dma_bits(uint16_t mode, int8_t bits) { + if (mode == TX) { + if (bits == 16) { + return 16; + } else { + return 32; + } + return bits; + } else { // RX + // always read 32 bit words for I2S e.g. I2S MEMS microphones + return 32; + } +} + +STATIC bool lookup_gpio(const machine_pin_obj_t *pin, i2s_pin_function_t fn, uint8_t hw_id, uint16_t *index) { + for (uint16_t i = 0; i < ARRAY_SIZE(i2s_gpio_map); i++) { + if ((pin->name == i2s_gpio_map[i].name) && + (i2s_gpio_map[i].fn == fn) && + (i2s_gpio_map[i].hw_id == hw_id)) { + *index = i; + return true; + } + } + return false; +} + +STATIC bool set_iomux(const machine_pin_obj_t *pin, i2s_pin_function_t fn, uint8_t hw_id) { + uint16_t mapping_index; + if (lookup_gpio(pin, fn, hw_id, &mapping_index)) { + iomux_table_t iom = i2s_gpio_map[mapping_index].iomux; + IOMUXC_SetPinMux(iom.muxRegister, iom.muxMode, iom.inputRegister, iom.inputDaisy, iom.configRegister, 1U); + IOMUXC_SetPinConfig(iom.muxRegister, iom.muxMode, iom.inputRegister, iom.inputDaisy, iom.configRegister, + pin_generate_config(PIN_PULL_DISABLED, PIN_MODE_OUT, 2, iom.configRegister)); + return true; + } else { + return false; + } +} + +STATIC bool is_rate_supported(int32_t rate) { + for (uint16_t i = 0; i < ARRAY_SIZE(clock_config_map); i++) { + if (clock_config_map[i].rate == rate) { + return true; + } + } + return false; +} + +STATIC const clock_audio_pll_config_t *get_pll_config(int32_t rate) { + for (uint16_t i = 0; i < ARRAY_SIZE(clock_config_map); i++) { + if (clock_config_map[i].rate == rate) { + return clock_config_map[i].pll_config; + } + } + return 0; +} + +STATIC const uint32_t get_clock_pre_divider(int32_t rate) { + for (uint16_t i = 0; i < ARRAY_SIZE(clock_config_map); i++) { + if (clock_config_map[i].rate == rate) { + return clock_config_map[i].clock_pre_divider; + } + } + return 0; +} + +STATIC const uint32_t get_clock_divider(int32_t rate) { + for (uint16_t i = 0; i < ARRAY_SIZE(clock_config_map); i++) { + if (clock_config_map[i].rate == rate) { + return clock_config_map[i].clock_divider; + } + } + return 0; +} + +STATIC uint32_t fill_appbuf_from_ringbuf(machine_i2s_obj_t *self, mp_buffer_info_t *appbuf) { + + // copy audio samples from the ring buffer to the app buffer + // loop, copying samples until the app buffer is filled + // For uasyncio mode, the loop will make an early exit if the ring buffer becomes empty + // Example: + // a MicroPython I2S object is configured for 16-bit mono (2 bytes per audio sample). + // For every frame coming from the ring buffer (8 bytes), 2 bytes are "cherry picked" and + // copied to the supplied app buffer. + // Thus, for every 1 byte copied to the app buffer, 4 bytes are read from the ring buffer. + // If a 8kB app buffer is supplied, 32kB of audio samples is read from the ring buffer. + + uint32_t num_bytes_copied_to_appbuf = 0; + uint8_t *app_p = (uint8_t *)appbuf->buf; + uint8_t appbuf_sample_size_in_bytes = (self->bits == 16? 2 : 4) * (self->format == STEREO ? 2: 1); + uint32_t num_bytes_needed_from_ringbuf = appbuf->len * (I2S_RX_FRAME_SIZE_IN_BYTES / appbuf_sample_size_in_bytes); + uint8_t discard_byte; + while (num_bytes_needed_from_ringbuf) { + + uint8_t f_index = get_frame_mapping_index(self->bits, self->format); + + for (uint8_t i = 0; i < I2S_RX_FRAME_SIZE_IN_BYTES; i++) { + int8_t r_to_a_mapping = i2s_frame_map[f_index][i]; + if (r_to_a_mapping != -1) { + if (self->io_mode == BLOCKING) { + // poll the ringbuf until a sample becomes available, copy into appbuf using the mapping transform + while (ringbuf_pop(&self->ring_buffer, app_p + r_to_a_mapping) == false) { + ; + } + num_bytes_copied_to_appbuf++; + } else if (self->io_mode == UASYNCIO) { + if (ringbuf_pop(&self->ring_buffer, app_p + r_to_a_mapping) == false) { + // ring buffer is empty, exit + goto exit; + } else { + num_bytes_copied_to_appbuf++; + } + } else { + return 0; // should never get here (non-blocking mode does not use this function) + } + } else { // r_a_mapping == -1 + // discard unused byte from ring buffer + if (self->io_mode == BLOCKING) { + // poll the ringbuf until a sample becomes available + while (ringbuf_pop(&self->ring_buffer, &discard_byte) == false) { + ; + } + } else if (self->io_mode == UASYNCIO) { + if (ringbuf_pop(&self->ring_buffer, &discard_byte) == false) { + // ring buffer is empty, exit + goto exit; + } + } else { + return 0; // should never get here (non-blocking mode does not use this function) + } + } + num_bytes_needed_from_ringbuf--; + } + app_p += appbuf_sample_size_in_bytes; + } +exit: + return num_bytes_copied_to_appbuf; +} + +// function is used in IRQ context +STATIC void fill_appbuf_from_ringbuf_non_blocking(machine_i2s_obj_t *self) { + + // attempt to copy a block of audio samples from the ring buffer to the supplied app buffer. + // audio samples will be formatted as part of the copy operation + + uint32_t num_bytes_copied_to_appbuf = 0; + uint8_t *app_p = &(((uint8_t *)self->non_blocking_descriptor.appbuf.buf)[self->non_blocking_descriptor.index]); + + uint8_t appbuf_sample_size_in_bytes = (self->bits == 16? 2 : 4) * (self->format == STEREO ? 2: 1); + uint32_t num_bytes_remaining_to_copy_to_appbuf = self->non_blocking_descriptor.appbuf.len - self->non_blocking_descriptor.index; + uint32_t num_bytes_remaining_to_copy_from_ring_buffer = num_bytes_remaining_to_copy_to_appbuf * + (I2S_RX_FRAME_SIZE_IN_BYTES / appbuf_sample_size_in_bytes); + uint32_t num_bytes_needed_from_ringbuf = MIN(SIZEOF_NON_BLOCKING_COPY_IN_BYTES, num_bytes_remaining_to_copy_from_ring_buffer); + uint8_t discard_byte; + if (ringbuf_available_data(&self->ring_buffer) >= num_bytes_needed_from_ringbuf) { + while (num_bytes_needed_from_ringbuf) { + + uint8_t f_index = get_frame_mapping_index(self->bits, self->format); + + for (uint8_t i = 0; i < I2S_RX_FRAME_SIZE_IN_BYTES; i++) { + int8_t r_to_a_mapping = i2s_frame_map[f_index][i]; + if (r_to_a_mapping != -1) { + ringbuf_pop(&self->ring_buffer, app_p + r_to_a_mapping); + num_bytes_copied_to_appbuf++; + } else { // r_a_mapping == -1 + // discard unused byte from ring buffer + ringbuf_pop(&self->ring_buffer, &discard_byte); + } + num_bytes_needed_from_ringbuf--; + } + app_p += appbuf_sample_size_in_bytes; + } + self->non_blocking_descriptor.index += num_bytes_copied_to_appbuf; + + if (self->non_blocking_descriptor.index >= self->non_blocking_descriptor.appbuf.len) { + self->non_blocking_descriptor.copy_in_progress = false; + mp_sched_schedule(self->callback_for_non_blocking, MP_OBJ_FROM_PTR(self)); + } + } +} + +STATIC uint32_t copy_appbuf_to_ringbuf(machine_i2s_obj_t *self, mp_buffer_info_t *appbuf) { + + // copy audio samples from the app buffer to the ring buffer + // loop, reading samples until the app buffer is emptied + // for uasyncio mode, the loop will make an early exit if the ring buffer becomes full + + uint32_t a_index = 0; + + while (a_index < appbuf->len) { + if (self->io_mode == BLOCKING) { + // copy a byte to the ringbuf when space becomes available + while (ringbuf_push(&self->ring_buffer, ((uint8_t *)appbuf->buf)[a_index]) == false) { + ; + } + a_index++; + } else if (self->io_mode == UASYNCIO) { + if (ringbuf_push(&self->ring_buffer, ((uint8_t *)appbuf->buf)[a_index]) == false) { + // ring buffer is full, exit + break; + } else { + a_index++; + } + } else { + return 0; // should never get here (non-blocking mode does not use this function) + } + } + + return a_index; +} + +// function is used in IRQ context +STATIC void copy_appbuf_to_ringbuf_non_blocking(machine_i2s_obj_t *self) { + + // copy audio samples from app buffer into ring buffer + uint32_t num_bytes_remaining_to_copy = self->non_blocking_descriptor.appbuf.len - self->non_blocking_descriptor.index; + uint32_t num_bytes_to_copy = MIN(SIZEOF_NON_BLOCKING_COPY_IN_BYTES, num_bytes_remaining_to_copy); + + if (ringbuf_available_space(&self->ring_buffer) >= num_bytes_to_copy) { + for (uint32_t i = 0; i < num_bytes_to_copy; i++) { + ringbuf_push(&self->ring_buffer, + ((uint8_t *)self->non_blocking_descriptor.appbuf.buf)[self->non_blocking_descriptor.index + i]); + } + + self->non_blocking_descriptor.index += num_bytes_to_copy; + if (self->non_blocking_descriptor.index >= self->non_blocking_descriptor.appbuf.len) { + self->non_blocking_descriptor.copy_in_progress = false; + mp_sched_schedule(self->callback_for_non_blocking, MP_OBJ_FROM_PTR(self)); + } + } +} + +// function is used in IRQ context +STATIC void empty_dma(machine_i2s_obj_t *self, ping_pong_t dma_ping_pong) { + uint16_t dma_buffer_offset = 0; + + if (dma_ping_pong == TOP_HALF) { + dma_buffer_offset = 0; + } else { // BOTTOM_HALF + dma_buffer_offset = SIZEOF_HALF_DMA_BUFFER_IN_BYTES; + } + + uint8_t *dma_buffer_p = &self->dma_buffer_dcache_aligned[dma_buffer_offset]; + + // flush and invalidate cache so the CPU reads data placed into RAM by DMA + MP_HAL_CLEANINVALIDATE_DCACHE(dma_buffer_p, SIZEOF_HALF_DMA_BUFFER_IN_BYTES); + + // when space exists, copy samples into ring buffer + if (ringbuf_available_space(&self->ring_buffer) >= SIZEOF_HALF_DMA_BUFFER_IN_BYTES) { + for (uint32_t i = 0; i < SIZEOF_HALF_DMA_BUFFER_IN_BYTES; i++) { + ringbuf_push(&self->ring_buffer, dma_buffer_p[i]); + } + } +} + +// function is used in IRQ context +STATIC void feed_dma(machine_i2s_obj_t *self, ping_pong_t dma_ping_pong) { + uint16_t dma_buffer_offset = 0; + + if (dma_ping_pong == TOP_HALF) { + dma_buffer_offset = 0; + } else { // BOTTOM_HALF + dma_buffer_offset = SIZEOF_HALF_DMA_BUFFER_IN_BYTES; + } + + uint8_t *dma_buffer_p = &self->dma_buffer_dcache_aligned[dma_buffer_offset]; + + // when data exists, copy samples from ring buffer + if (ringbuf_available_data(&self->ring_buffer) >= SIZEOF_HALF_DMA_BUFFER_IN_BYTES) { + + // copy a block of samples from the ring buffer to the dma buffer. + // mono format is implemented by duplicating each sample into both L and R channels. + if ((self->format == MONO) && (self->bits == 16)) { + for (uint32_t i = 0; i < SIZEOF_HALF_DMA_BUFFER_IN_BYTES / 4; i++) { + for (uint8_t b = 0; b < sizeof(uint16_t); b++) { + ringbuf_pop(&self->ring_buffer, &dma_buffer_p[i * 4 + b]); + dma_buffer_p[i * 4 + b + 2] = dma_buffer_p[i * 4 + b]; // duplicated mono sample + } + } + } else if ((self->format == MONO) && (self->bits == 32)) { + for (uint32_t i = 0; i < SIZEOF_HALF_DMA_BUFFER_IN_BYTES / 8; i++) { + for (uint8_t b = 0; b < sizeof(uint32_t); b++) { + ringbuf_pop(&self->ring_buffer, &dma_buffer_p[i * 8 + b]); + dma_buffer_p[i * 8 + b + 4] = dma_buffer_p[i * 8 + b]; // duplicated mono sample + } + } + } else { // STEREO, both 16-bit and 32-bit + for (uint32_t i = 0; i < SIZEOF_HALF_DMA_BUFFER_IN_BYTES; i++) { + ringbuf_pop(&self->ring_buffer, &dma_buffer_p[i]); + } + } + } else { + // underflow. clear buffer to transmit "silence" on the I2S bus + memset(dma_buffer_p, 0, SIZEOF_HALF_DMA_BUFFER_IN_BYTES); + } + + // flush cache to RAM so DMA can read the sample data + MP_HAL_CLEAN_DCACHE(dma_buffer_p, SIZEOF_HALF_DMA_BUFFER_IN_BYTES); +} + +STATIC void edma_i2s_callback(edma_handle_t *handle, void *userData, bool transferDone, uint32_t tcds) { + machine_i2s_obj_t *self = userData; + + if (self->mode == TX) { + // for non-blocking mode, sample copying (appbuf->ibuf) is initiated in this callback routine + if ((self->io_mode == NON_BLOCKING) && (self->non_blocking_descriptor.copy_in_progress)) { + copy_appbuf_to_ringbuf_non_blocking(self); + } + + if (transferDone) { + // bottom half of buffer now emptied, + // safe to fill the bottom half while the top half of buffer is being emptied + feed_dma(self, BOTTOM_HALF); + } else { + // top half of buffer now emptied, + // safe to fill the top half while the bottom half of buffer is being emptied + feed_dma(self, TOP_HALF); + } + } else { // RX + if (transferDone) { + // bottom half of buffer now filled, + // safe to empty the bottom half while the top half of buffer is being filled + empty_dma(self, BOTTOM_HALF); + } else { + // top half of buffer now filled, + // safe to empty the top half while the bottom half of buffer is being filled + empty_dma(self, TOP_HALF); + } + + // for non-blocking mode, sample copying (ibuf->appbuf) is initiated in this callback routine + if ((self->io_mode == NON_BLOCKING) && (self->non_blocking_descriptor.copy_in_progress)) { + fill_appbuf_from_ringbuf_non_blocking(self); + } + } +} + +STATIC bool i2s_init(machine_i2s_obj_t *self) { + + CLOCK_InitAudioPll(get_pll_config(self->rate)); + CLOCK_SetMux(i2s_clock_mux[self->i2s_id], AUDIO_PLL_CLOCK); + CLOCK_SetDiv(i2s_clock_pre_div[self->i2s_id], get_clock_pre_divider(self->rate)); + CLOCK_SetDiv(i2s_clock_div[self->i2s_id], get_clock_divider(self->rate)); + + if (!set_iomux(self->sck, SCK, self->i2s_id)) { + return false; + } + + if (!set_iomux(self->ws, WS, self->i2s_id)) { + return false; + } + + if (!set_iomux(self->sd, SD, self->i2s_id)) { + return false; + } + + if (self->mck) { + if (!set_iomux(self->mck, MCK, self->i2s_id)) { + return false; + } + IOMUXC_EnableMode(IOMUXC_GPR, i2s_iomuxc_gpr_mode[self->i2s_id], true); + } + + self->dma_channel = allocate_dma_channel(); + + DMAMUX_Init(DMAMUX); + if (self->mode == TX) { + DMAMUX_SetSource(DMAMUX, self->dma_channel, i2s_dma_req_src_tx[self->i2s_id]); + } else { // RX + DMAMUX_SetSource(DMAMUX, self->dma_channel, i2s_dma_req_src_rx[self->i2s_id]); + } + DMAMUX_EnableChannel(DMAMUX, self->dma_channel); + + dma_init(); + EDMA_CreateHandle(&self->edmaHandle, DMA0, self->dma_channel); + EDMA_SetCallback(&self->edmaHandle, edma_i2s_callback, self); + EDMA_ResetChannel(DMA0, self->dma_channel); + + SAI_Init(self->i2s_inst); + + sai_transceiver_t saiConfig; + SAI_GetClassicI2SConfig(&saiConfig, get_dma_bits(self->mode, self->bits), kSAI_Stereo, kSAI_Channel0Mask); + saiConfig.masterSlave = kSAI_Master; + + uint16_t sck_index; + lookup_gpio(self->sck, SCK, self->i2s_id, &sck_index); + + if ((self->mode == TX) && (i2s_gpio_map[sck_index].mode == TX)) { + saiConfig.syncMode = kSAI_ModeAsync; + SAI_TxSetConfig(self->i2s_inst, &saiConfig); + } else if ((self->mode == RX) && (i2s_gpio_map[sck_index].mode == RX)) { + saiConfig.syncMode = kSAI_ModeAsync; + SAI_RxSetConfig(self->i2s_inst, &saiConfig); + } else if ((self->mode == TX) && (i2s_gpio_map[sck_index].mode == RX)) { + saiConfig.syncMode = kSAI_ModeAsync; + SAI_RxSetConfig(self->i2s_inst, &saiConfig); + saiConfig.bitClock.bclkSrcSwap = true; + saiConfig.syncMode = kSAI_ModeSync; + SAI_TxSetConfig(self->i2s_inst, &saiConfig); + } else if ((self->mode == RX) && (i2s_gpio_map[sck_index].mode == TX)) { + saiConfig.syncMode = kSAI_ModeAsync; + SAI_TxSetConfig(self->i2s_inst, &saiConfig); + saiConfig.syncMode = kSAI_ModeSync; + SAI_RxSetConfig(self->i2s_inst, &saiConfig); + } else { + return false; // should never happen + } + + uint32_t clock_freq = + (CLOCK_GetFreq(kCLOCK_AudioPllClk) / (get_clock_divider(self->rate) + 1U) / + (get_clock_pre_divider(self->rate) + 1U)); + + SAI_TxSetBitClockRate(self->i2s_inst, clock_freq, self->rate, get_dma_bits(self->mode, self->bits), + SAI_NUM_AUDIO_CHANNELS); + SAI_RxSetBitClockRate(self->i2s_inst, clock_freq, self->rate, get_dma_bits(self->mode, self->bits), + SAI_NUM_AUDIO_CHANNELS); + + edma_transfer_config_t transferConfig; + uint8_t bytes_per_sample = get_dma_bits(self->mode, self->bits) / 8; + + if (self->mode == TX) { + uint32_t destAddr = SAI_TxGetDataRegisterAddress(self->i2s_inst, SAI_CHANNEL_0); + EDMA_PrepareTransfer(&transferConfig, + self->dma_buffer_dcache_aligned, bytes_per_sample, + (void *)destAddr, bytes_per_sample, + (FSL_FEATURE_SAI_FIFO_COUNT - saiConfig.fifo.fifoWatermark) * bytes_per_sample, + SIZEOF_DMA_BUFFER_IN_BYTES, kEDMA_MemoryToPeripheral); + } else { // RX + uint32_t srcAddr = SAI_RxGetDataRegisterAddress(self->i2s_inst, SAI_CHANNEL_0); + EDMA_PrepareTransfer(&transferConfig, + (void *)srcAddr, bytes_per_sample, + self->dma_buffer_dcache_aligned, bytes_per_sample, + (FSL_FEATURE_SAI_FIFO_COUNT - saiConfig.fifo.fifoWatermark) * bytes_per_sample, + SIZEOF_DMA_BUFFER_IN_BYTES, kEDMA_PeripheralToMemory); + } + + memset(self->edmaTcd, 0, sizeof(edma_tcd_t)); + + // continuous DMA operation is acheived using the scatter/gather feature, with one TCD linked back to itself + EDMA_TcdSetTransferConfig(self->edmaTcd, &transferConfig, self->edmaTcd); + EDMA_TcdEnableInterrupts(self->edmaTcd, kEDMA_MajorInterruptEnable | kEDMA_HalfInterruptEnable); + EDMA_InstallTCD(DMA0, self->dma_channel, self->edmaTcd); + EDMA_StartTransfer(&self->edmaHandle); + + if (self->mode == TX) { + SAI_TxEnableDMA(self->i2s_inst, kSAI_FIFORequestDMAEnable, true); + SAI_TxEnable(self->i2s_inst, true); + SAI_TxSetChannelFIFOMask(self->i2s_inst, kSAI_Channel0Mask); + } else { // RX + SAI_RxEnableDMA(self->i2s_inst, kSAI_FIFORequestDMAEnable, true); + SAI_RxEnable(self->i2s_inst, true); + SAI_RxSetChannelFIFOMask(self->i2s_inst, kSAI_Channel0Mask); + } + + return true; +} + +STATIC void machine_i2s_init_helper(machine_i2s_obj_t *self, size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + + enum { + ARG_sck, + ARG_ws, + ARG_sd, + ARG_mck, + ARG_mode, + ARG_bits, + ARG_format, + ARG_rate, + ARG_ibuf, + }; + + static const mp_arg_t allowed_args[] = { + { MP_QSTR_sck, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_ws, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_sd, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_mck, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_mode, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_format, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_rate, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_ibuf, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = -1} }, + }; + + 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); + + // + // ---- Check validity of arguments ---- + // + + // is Mode valid? + uint16_t i2s_mode = args[ARG_mode].u_int; + if ((i2s_mode != (RX)) && + (i2s_mode != (TX))) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid mode")); + } + + // are I2S pin assignments valid? + uint16_t not_used; + + // is SCK valid? + const machine_pin_obj_t *pin_sck = pin_find(args[ARG_sck].u_obj); + if (!lookup_gpio(pin_sck, SCK, self->i2s_id, ¬_used)) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid SCK pin")); + } + + // is WS valid? + const machine_pin_obj_t *pin_ws = pin_find(args[ARG_ws].u_obj); + if (!lookup_gpio(pin_ws, WS, self->i2s_id, ¬_used)) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid WS pin")); + } + + // is SD valid? + const machine_pin_obj_t *pin_sd = pin_find(args[ARG_sd].u_obj); + uint16_t mapping_index; + bool invalid_sd = true; + if (lookup_gpio(pin_sd, SD, self->i2s_id, &mapping_index)) { + if (i2s_mode == i2s_gpio_map[mapping_index].mode) { + invalid_sd = false; + } + } + + if (invalid_sd) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid SD pin")); + } + + // is MCK defined and valid? + const machine_pin_obj_t *pin_mck = NULL; + if (args[ARG_mck].u_obj != mp_const_none) { + pin_mck = pin_find(args[ARG_mck].u_obj); + if (!lookup_gpio(pin_mck, MCK, self->i2s_id, ¬_used)) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid MCK pin")); + } + } + + // is Bits valid? + int8_t i2s_bits = args[ARG_bits].u_int; + if ((i2s_bits != 16) && + (i2s_bits != 32)) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid bits")); + } + + // is Format valid? + format_t i2s_format = args[ARG_format].u_int; + if ((i2s_format != MONO) && + (i2s_format != STEREO)) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid format")); + } + + // is Rate valid? + int32_t i2s_rate = args[ARG_rate].u_int; + if (!is_rate_supported(i2s_rate)) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid rate")); + } + + // is Ibuf valid? + int32_t ring_buffer_len = args[ARG_ibuf].u_int; + if (ring_buffer_len > 0) { + uint8_t *buffer = m_new(uint8_t, ring_buffer_len); + self->ring_buffer_storage = buffer; + ringbuf_init(&self->ring_buffer, buffer, ring_buffer_len); + } else { + mp_raise_ValueError(MP_ERROR_TEXT("invalid ibuf")); + } + + self->sck = pin_sck; + self->ws = pin_ws; + self->sd = pin_sd; + self->mck = pin_mck; + self->mode = i2s_mode; + self->bits = i2s_bits; + self->format = i2s_format; + self->rate = i2s_rate; + self->ibuf = ring_buffer_len; + self->callback_for_non_blocking = MP_OBJ_NULL; + self->non_blocking_descriptor.copy_in_progress = false; + self->io_mode = BLOCKING; + self->i2s_inst = (I2S_Type *)i2s_base_ptr[self->i2s_id]; + + // init the I2S bus + if (!i2s_init(self)) { + mp_raise_msg_varg(&mp_type_OSError, MP_ERROR_TEXT("I2S init failed")); + } +} + +STATIC void machine_i2s_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_i2s_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "I2S(id=%u,\n" + "sck="MP_HAL_PIN_FMT ",\n" + "ws="MP_HAL_PIN_FMT ",\n" + "sd="MP_HAL_PIN_FMT ",\n" + "mck="MP_HAL_PIN_FMT ",\n" + "mode=%u,\n" + "bits=%u, format=%u,\n" + "rate=%d, ibuf=%d)", + self->i2s_id, + mp_hal_pin_name(self->sck), + mp_hal_pin_name(self->ws), + mp_hal_pin_name(self->sd), + mp_hal_pin_name(self->mck), + self->mode, + self->bits, self->format, + self->rate, self->ibuf + ); +} + +STATIC mp_obj_t machine_i2s_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); + uint8_t i2s_id = mp_obj_get_int(args[0]); + + if (i2s_id < 1 || i2s_id > MICROPY_HW_I2S_NUM) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("I2S(%d) does not exist"), i2s_id); + } + + uint8_t i2s_id_zero_base = i2s_id - 1; + + machine_i2s_obj_t *self; + if (MP_STATE_PORT(machine_i2s_obj)[i2s_id_zero_base] == NULL) { + self = m_new_obj(machine_i2s_obj_t); + MP_STATE_PORT(machine_i2s_obj)[i2s_id_zero_base] = self; + self->base.type = &machine_i2s_type; + self->i2s_id = i2s_id; + self->edmaTcd = &edmaTcd[i2s_id_zero_base]; + } else { + self = MP_STATE_PORT(machine_i2s_obj)[i2s_id_zero_base]; + machine_i2s_deinit(MP_OBJ_FROM_PTR(self)); + } + + // align DMA buffer to the cache line size (32 bytes) + self->dma_buffer_dcache_aligned = (uint8_t *)((uint32_t)(self->dma_buffer + 0x1f) & ~0x1f); + + // fill the DMA buffer with NULLs + memset(self->dma_buffer_dcache_aligned, 0, SIZEOF_DMA_BUFFER_IN_BYTES); + + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw_args, args + n_pos_args); + machine_i2s_init_helper(self, n_pos_args - 1, args + 1, &kw_args); + return MP_OBJ_FROM_PTR(self); +} + +STATIC mp_obj_t machine_i2s_init(size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + machine_i2s_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + machine_i2s_deinit(MP_OBJ_FROM_PTR(self)); + machine_i2s_init_helper(self, n_pos_args - 1, pos_args + 1, kw_args); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2s_init_obj, 1, machine_i2s_init); + +STATIC mp_obj_t machine_i2s_deinit(mp_obj_t self_in) { + machine_i2s_obj_t *self = MP_OBJ_TO_PTR(self_in); + + // use self->i2s_inst as in indication that I2S object has already been de-initialized + if (self->i2s_inst != NULL) { + EDMA_AbortTransfer(&self->edmaHandle); + + if (self->mode == TX) { + SAI_TxSetChannelFIFOMask(self->i2s_inst, 0); + SAI_TxEnableDMA(self->i2s_inst, kSAI_FIFORequestDMAEnable, false); + SAI_TxEnable(self->i2s_inst, false); + SAI_TxReset(self->i2s_inst); + } else { // RX + SAI_RxSetChannelFIFOMask(self->i2s_inst, 0); + SAI_RxEnableDMA(self->i2s_inst, kSAI_FIFORequestDMAEnable, false); + SAI_RxEnable(self->i2s_inst, false); + SAI_RxReset(self->i2s_inst); + } + + SAI_Deinit(self->i2s_inst); + free_dma_channel(self->dma_channel); + m_free(self->ring_buffer_storage); + self->i2s_inst = NULL; // flag object as de-initialized + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_i2s_deinit_obj, machine_i2s_deinit); + +STATIC mp_obj_t machine_i2s_irq(mp_obj_t self_in, mp_obj_t handler) { + machine_i2s_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (handler != mp_const_none && !mp_obj_is_callable(handler)) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid callback")); + } + + if (handler != mp_const_none) { + self->io_mode = NON_BLOCKING; + } else { + self->io_mode = BLOCKING; + } + + self->callback_for_non_blocking = handler; + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(machine_i2s_irq_obj, machine_i2s_irq); + +// Shift() is typically used as a volume control. +// shift=1 increases volume by 6dB, shift=-1 decreases volume by 6dB +STATIC mp_obj_t machine_i2s_shift(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_buf, ARG_bits, ARG_shift}; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_buf, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_bits, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_shift, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + }; + + // parse args + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_buf].u_obj, &bufinfo, MP_BUFFER_RW); + + int16_t *buf_16 = bufinfo.buf; + int32_t *buf_32 = bufinfo.buf; + + uint8_t bits = args[ARG_bits].u_int; + int8_t shift = args[ARG_shift].u_int; + + uint32_t num_audio_samples; + switch (bits) { + case 16: + num_audio_samples = bufinfo.len / sizeof(uint16_t); + break; + + case 32: + num_audio_samples = bufinfo.len / sizeof(uint32_t); + break; + + default: + mp_raise_ValueError(MP_ERROR_TEXT("invalid bits")); + break; + } + + for (uint32_t i = 0; i < num_audio_samples; i++) { + switch (bits) { + case 16: + if (shift >= 0) { + buf_16[i] = buf_16[i] << shift; + } else { + buf_16[i] = buf_16[i] >> abs(shift); + } + break; + case 32: + if (shift >= 0) { + buf_32[i] = buf_32[i] << shift; + } else { + buf_32[i] = buf_32[i] >> abs(shift); + } + break; + } + } + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2s_shift_fun_obj, 0, machine_i2s_shift); +STATIC MP_DEFINE_CONST_STATICMETHOD_OBJ(machine_i2s_shift_obj, MP_ROM_PTR(&machine_i2s_shift_fun_obj)); + +STATIC const mp_rom_map_elem_t machine_i2s_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_i2s_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, + { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_i2s_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_irq), MP_ROM_PTR(&machine_i2s_irq_obj) }, + + // Static method + { MP_ROM_QSTR(MP_QSTR_shift), MP_ROM_PTR(&machine_i2s_shift_obj) }, + + // Constants + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_INT(RX) }, + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_INT(TX) }, + { MP_ROM_QSTR(MP_QSTR_STEREO), MP_ROM_INT(STEREO) }, + { MP_ROM_QSTR(MP_QSTR_MONO), MP_ROM_INT(MONO) }, +}; +MP_DEFINE_CONST_DICT(machine_i2s_locals_dict, machine_i2s_locals_dict_table); + +STATIC mp_uint_t machine_i2s_stream_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { + machine_i2s_obj_t *self = MP_OBJ_TO_PTR(self_in); + + if (self->mode != RX) { + *errcode = MP_EPERM; + return MP_STREAM_ERROR; + } + + uint8_t appbuf_sample_size_in_bytes = (self->bits / 8) * (self->format == STEREO ? 2: 1); + if (size % appbuf_sample_size_in_bytes != 0) { + *errcode = MP_EINVAL; + return MP_STREAM_ERROR; + } + + if (size == 0) { + return 0; + } + + if (self->io_mode == NON_BLOCKING) { + self->non_blocking_descriptor.appbuf.buf = (void *)buf_in; + self->non_blocking_descriptor.appbuf.len = size; + self->non_blocking_descriptor.index = 0; + self->non_blocking_descriptor.copy_in_progress = true; + return size; + } else { // blocking or uasyncio mode + mp_buffer_info_t appbuf; + appbuf.buf = (void *)buf_in; + appbuf.len = size; + uint32_t num_bytes_read = fill_appbuf_from_ringbuf(self, &appbuf); + return num_bytes_read; + } +} + +STATIC mp_uint_t machine_i2s_stream_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) { + machine_i2s_obj_t *self = MP_OBJ_TO_PTR(self_in); + + if (self->mode != TX) { + *errcode = MP_EPERM; + return MP_STREAM_ERROR; + } + + if (size == 0) { + return 0; + } + + if (self->io_mode == NON_BLOCKING) { + self->non_blocking_descriptor.appbuf.buf = (void *)buf_in; + self->non_blocking_descriptor.appbuf.len = size; + self->non_blocking_descriptor.index = 0; + self->non_blocking_descriptor.copy_in_progress = true; + return size; + } else { // blocking or uasyncio mode + mp_buffer_info_t appbuf; + appbuf.buf = (void *)buf_in; + appbuf.len = size; + uint32_t num_bytes_written = copy_appbuf_to_ringbuf(self, &appbuf); + return num_bytes_written; + } +} + +STATIC mp_uint_t machine_i2s_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) { + machine_i2s_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_uint_t ret; + uintptr_t flags = arg; + self->io_mode = UASYNCIO; // a call to ioctl() is an indication that uasyncio is being used + + if (request == MP_STREAM_POLL) { + ret = 0; + + if (flags & MP_STREAM_POLL_RD) { + if (self->mode != RX) { + *errcode = MP_EPERM; + return MP_STREAM_ERROR; + } + + if (!ringbuf_is_empty(&self->ring_buffer)) { + ret |= MP_STREAM_POLL_RD; + } + } + + if (flags & MP_STREAM_POLL_WR) { + if (self->mode != TX) { + *errcode = MP_EPERM; + return MP_STREAM_ERROR; + } + + if (!ringbuf_is_full(&self->ring_buffer)) { + ret |= MP_STREAM_POLL_WR; + } + } + } else { + *errcode = MP_EINVAL; + ret = MP_STREAM_ERROR; + } + + return ret; +} + +STATIC const mp_stream_p_t i2s_stream_p = { + .read = machine_i2s_stream_read, + .write = machine_i2s_stream_write, + .ioctl = machine_i2s_ioctl, + .is_text = false, +}; + +const mp_obj_type_t machine_i2s_type = { + { &mp_type_type }, + .name = MP_QSTR_I2S, + .print = machine_i2s_print, + .getiter = mp_identity_getiter, + .iternext = mp_stream_unbuffered_iter, + .protocol = &i2s_stream_p, + .make_new = machine_i2s_make_new, + .locals_dict = (mp_obj_dict_t *)&machine_i2s_locals_dict, +}; + +#endif // MICROPY_PY_MACHINE_I2S diff --git a/ports/mimxrt/machine_spi.c b/ports/mimxrt/machine_spi.c index e25e219c79..b8b5a4d5b5 100644 --- a/ports/mimxrt/machine_spi.c +++ b/ports/mimxrt/machine_spi.c @@ -30,7 +30,7 @@ #include "py/mperrno.h" #include "extmod/machine_spi.h" #include "modmachine.h" -#include "dma_channel.h" +#include "dma_manager.h" #include "fsl_cache.h" #include "fsl_dmamux.h" @@ -256,8 +256,6 @@ STATIC void machine_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8 bool use_dma = chan_rx >= 0 && chan_tx >= 0; if (use_dma) { - edma_config_t userConfig; - /* DMA MUX init*/ DMAMUX_Init(DMAMUX); @@ -267,8 +265,7 @@ STATIC void machine_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8 DMAMUX_SetSource(DMAMUX, chan_tx, dma_req_src_tx[self->spi_hw_id]); DMAMUX_EnableChannel(DMAMUX, chan_tx); - EDMA_GetDefaultConfig(&userConfig); - EDMA_Init(DMA0, &userConfig); + dma_init(); lpspi_master_edma_handle_t g_master_edma_handle; edma_handle_t lpspiEdmaMasterRxRegToRxDataHandle; diff --git a/ports/mimxrt/main.c b/ports/mimxrt/main.c index a6a0d2e196..82e07868a1 100644 --- a/ports/mimxrt/main.c +++ b/ports/mimxrt/main.c @@ -113,6 +113,9 @@ int main(void) { soft_reset_exit: mp_printf(MP_PYTHON_PRINTER, "MPY: soft reboot\n"); machine_pin_irq_deinit(); + #if MICROPY_PY_MACHINE_I2S + machine_i2s_deinit_all(); + #endif #if MICROPY_PY_NETWORK mod_network_deinit(); #endif diff --git a/ports/mimxrt/modmachine.c b/ports/mimxrt/modmachine.c index 63cec05507..d2358c5069 100644 --- a/ports/mimxrt/modmachine.c +++ b/ports/mimxrt/modmachine.c @@ -131,6 +131,9 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_SoftI2C), MP_ROM_PTR(&mp_machine_soft_i2c_type) }, { MP_ROM_QSTR(MP_QSTR_SoftSPI), MP_ROM_PTR(&mp_machine_soft_spi_type) }, { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&machine_i2c_type) }, + #if MICROPY_PY_MACHINE_I2S + { MP_ROM_QSTR(MP_QSTR_I2S), MP_ROM_PTR(&machine_i2s_type) }, + #endif { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&machine_spi_type) }, { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&machine_uart_type) }, { MP_ROM_QSTR(MP_QSTR_WDT), MP_ROM_PTR(&machine_wdt_type) }, diff --git a/ports/mimxrt/modmachine.h b/ports/mimxrt/modmachine.h index d18a227624..6502eea09f 100644 --- a/ports/mimxrt/modmachine.h +++ b/ports/mimxrt/modmachine.h @@ -31,6 +31,7 @@ extern const mp_obj_type_t machine_adc_type; extern const mp_obj_type_t machine_i2c_type; +extern const mp_obj_type_t machine_i2s_type; extern const mp_obj_type_t machine_pwm_type; extern const mp_obj_type_t machine_rtc_type; extern const mp_obj_type_t machine_sdcard_type; @@ -45,5 +46,7 @@ void machine_pwm_deinit_all(void); void machine_timer_init_PIT(void); void machine_sdcard_init0(void); void mimxrt_sdram_init(void); +void machine_i2s_init0(); +void machine_i2s_deinit_all(void); #endif // MICROPY_INCLUDED_MIMXRT_MODMACHINE_H diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index b4df533f4a..8572f69354 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -140,6 +140,9 @@ uint32_t trng_random_u32(void); #define MICROPY_PY_MACHINE_PWM_DUTY_U16_NS (1) #define MICROPY_PY_MACHINE_PWM_INCLUDEFILE "ports/mimxrt/machine_pwm.c" #define MICROPY_PY_MACHINE_I2C (1) +#ifndef MICROPY_PY_MACHINE_I2S +#define MICROPY_PY_MACHINE_I2S (0) +#endif #define MICROPY_PY_MACHINE_SOFTI2C (1) #define MICROPY_PY_MACHINE_SPI (1) #define MICROPY_PY_MACHINE_SOFTSPI (1) @@ -277,6 +280,10 @@ extern const struct _mp_obj_type_t network_lan_type; #define MICROPY_HW_PIT_NUM_CHANNELS 3 +#ifndef MICROPY_BOARD_ROOT_POINTERS +#define MICROPY_BOARD_ROOT_POINTERS +#endif + #define MICROPY_PORT_ROOT_POINTERS \ const char *readline_hist[8]; \ struct _machine_timer_obj_t *timer_table[MICROPY_HW_PIT_NUM_CHANNELS]; \ @@ -285,6 +292,8 @@ extern const struct _mp_obj_type_t network_lan_type; mp_obj_list_t mod_network_nic_list; \ /* root pointers for sub-systems */ \ MICROPY_PORT_ROOT_POINTER_MBEDTLS \ + /* root pointers defined by a board */ \ + MICROPY_BOARD_ROOT_POINTERS \ #define MP_STATE_PORT MP_STATE_VM @@ -298,6 +307,10 @@ extern const struct _mp_obj_type_t network_lan_type; #define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)((mp_uint_t)(p) | 1)) +#define MP_HAL_CLEANINVALIDATE_DCACHE(addr, size) \ + (SCB_CleanInvalidateDCache_by_Addr((uint32_t *)((uint32_t)addr & ~0x1f), \ + ((uint32_t)((uint8_t *)addr + size + 0x1f) & ~0x1f) - ((uint32_t)addr & ~0x1f))) + #define MP_HAL_CLEAN_DCACHE(addr, size) \ (SCB_CleanDCache_by_Addr((uint32_t *)((uint32_t)addr & ~0x1f), \ ((uint32_t)((uint8_t *)addr + size + 0x1f) & ~0x1f) - ((uint32_t)addr & ~0x1f))) diff --git a/ports/mimxrt/sdcard.c b/ports/mimxrt/sdcard.c index 1a9a97b681..31853b400e 100644 --- a/ports/mimxrt/sdcard.c +++ b/ports/mimxrt/sdcard.c @@ -822,7 +822,7 @@ bool sdcard_write(mimxrt_sdcard_obj_t *card, uint8_t *buffer, uint32_t block_num .command = &command, }; - status_t status = sdcard_transfer_blocking(card->usdhc_inst, &card->handle, &transfer, 500); + status_t status = sdcard_transfer_blocking(card->usdhc_inst, &card->handle, &transfer, 3000); if (status == kStatus_Success) { card->status = command.response[0]; From 3e70be8ee9973cffb6b9e8b721bf13b42510b739 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 30 Mar 2022 14:10:31 +1100 Subject: [PATCH 264/619] tests/extmod: Update I2S rate test to work on mimxrt. Tested on Teensy 4.0. Signed-off-by: Damien George --- tests/extmod/machine_i2s_rate.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/tests/extmod/machine_i2s_rate.py b/tests/extmod/machine_i2s_rate.py index 46e94585b7..d2cd1ae105 100644 --- a/tests/extmod/machine_i2s_rate.py +++ b/tests/extmod/machine_i2s_rate.py @@ -13,18 +13,24 @@ if "pyboard" in sys.platform: i2s_id = 2 sck_pin = Pin("Y6") ws_pin = Pin("Y5") - sd_pin = Pin("Y8") + sd_tx_pin = sd_rx_pin = Pin("Y8") elif "rp2" in sys.platform: i2s_id = 1 sck_pin = Pin(0) ws_pin = Pin(1) - sd_pin = Pin(2) + sd_tx_pin = sd_rx_pin = Pin(2) +elif "mimxrt" in sys.platform: + i2s_id = 1 + sck_pin = Pin(26) + ws_pin = Pin(27) + sd_tx_pin = Pin(7) + sd_rx_pin = Pin(8) TEST_BYTES = b"01234567" RATE = 11025 # frames/sec -def test(mode, bits_per_sample, frame_format): +def test(mode, sd_pin, bits_per_sample, frame_format): i2s = I2S( i2s_id, sck=sck_pin, @@ -70,11 +76,11 @@ def test(mode, bits_per_sample, frame_format): print(mode_str, bits_per_sample, channels, abs(dt - 500) <= 4) -test(I2S.TX, 16, I2S.MONO) -test(I2S.TX, 16, I2S.STEREO) -test(I2S.TX, 32, I2S.MONO) -test(I2S.TX, 32, I2S.STEREO) -test(I2S.RX, 16, I2S.MONO) -test(I2S.RX, 16, I2S.STEREO) -test(I2S.RX, 32, I2S.MONO) -test(I2S.RX, 32, I2S.STEREO) +test(I2S.TX, sd_tx_pin, 16, I2S.MONO) +test(I2S.TX, sd_tx_pin, 16, I2S.STEREO) +test(I2S.TX, sd_tx_pin, 32, I2S.MONO) +test(I2S.TX, sd_tx_pin, 32, I2S.STEREO) +test(I2S.RX, sd_rx_pin, 16, I2S.MONO) +test(I2S.RX, sd_rx_pin, 16, I2S.STEREO) +test(I2S.RX, sd_rx_pin, 32, I2S.MONO) +test(I2S.RX, sd_rx_pin, 32, I2S.STEREO) From 4c252ae067a4798fe11c2ce56a9734c1e64846a8 Mon Sep 17 00:00:00 2001 From: Waterlens Date: Fri, 18 Mar 2022 17:47:24 +0800 Subject: [PATCH 265/619] tools/mpremote: Allow running mpremote with `python -m`. This is helpful because some scripts are likely to use mpremote with a specific python path. --- tools/mpremote/mpremote/__main__.py | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 tools/mpremote/mpremote/__main__.py diff --git a/tools/mpremote/mpremote/__main__.py b/tools/mpremote/mpremote/__main__.py new file mode 100644 index 0000000000..a91ff67b15 --- /dev/null +++ b/tools/mpremote/mpremote/__main__.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python3 + +import sys +from mpremote import main + +sys.exit(main.main()) From dc8b43adc721a253b9b4de655ef3c4b85937862a Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Thu, 17 Feb 2022 11:45:45 +1100 Subject: [PATCH 266/619] nrf/drivers/usb: Fix background events/scheduling while at USB REPL. --- ports/nrf/drivers/usb/usb_cdc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/nrf/drivers/usb/usb_cdc.c b/ports/nrf/drivers/usb/usb_cdc.c index 9d7c832e20..fa098c98b8 100644 --- a/ports/nrf/drivers/usb/usb_cdc.c +++ b/ports/nrf/drivers/usb/usb_cdc.c @@ -198,6 +198,7 @@ int mp_hal_stdin_rx_chr(void) { if (cdc_rx_any()) { return cdc_rx_char(); } + MICROPY_EVENT_POLL_HOOK } return 0; From f92da1adc41f8a354c1ca2b7c9b53f0c45d02e87 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Tue, 1 Mar 2022 21:34:05 +1100 Subject: [PATCH 267/619] nrf/drivers/usb: Fix MP_STREAM_POLL_RD support on USB CDC. This gets ipoll working on USB CDC stdin. --- ports/nrf/drivers/usb/usb_cdc.c | 12 ++++++++++++ ports/nrf/mphalport.c | 4 +--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/ports/nrf/drivers/usb/usb_cdc.c b/ports/nrf/drivers/usb/usb_cdc.c index fa098c98b8..a1d411a256 100644 --- a/ports/nrf/drivers/usb/usb_cdc.c +++ b/ports/nrf/drivers/usb/usb_cdc.c @@ -34,6 +34,7 @@ #include "nrfx_power.h" #include "nrfx_uart.h" #include "py/ringbuf.h" +#include "py/stream.h" #ifdef BLUETOOTH_SD #include "nrf_sdm.h" @@ -192,6 +193,17 @@ void usb_cdc_sd_event_handler(uint32_t soc_evt) { } #endif +uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { + uintptr_t ret = 0; + if (poll_flags & MP_STREAM_POLL_RD) { + usb_cdc_loop(); + if (cdc_rx_any()) { + ret |= MP_STREAM_POLL_RD; + } + } + return ret; +} + int mp_hal_stdin_rx_chr(void) { for (;;) { usb_cdc_loop(); diff --git a/ports/nrf/mphalport.c b/ports/nrf/mphalport.c index 8ffb256018..1b31ad5acc 100644 --- a/ports/nrf/mphalport.c +++ b/ports/nrf/mphalport.c @@ -172,7 +172,7 @@ void mp_hal_set_interrupt_char(int c) { } #endif -#if !MICROPY_PY_BLE_NUS +#if !MICROPY_PY_BLE_NUS && !MICROPY_HW_USB_CDC uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { uintptr_t ret = 0; if ((poll_flags & MP_STREAM_POLL_RD) && MP_STATE_PORT(board_stdio_uart) != NULL @@ -181,9 +181,7 @@ uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { } return ret; } -#endif -#if !MICROPY_PY_BLE_NUS && !MICROPY_HW_USB_CDC int mp_hal_stdin_rx_chr(void) { for (;;) { if (MP_STATE_PORT(board_stdio_uart) != NULL && uart_rx_any(MP_STATE_PORT(board_stdio_uart))) { From 303e222f70501664b7f8b1118ae8682064387cd6 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Tue, 1 Mar 2022 21:34:57 +1100 Subject: [PATCH 268/619] nrf/modules: Include uasyncio in default board manifest. --- ports/nrf/modules/manifest.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/nrf/modules/manifest.py b/ports/nrf/modules/manifest.py index 4e8482226b..b27d4648b7 100644 --- a/ports/nrf/modules/manifest.py +++ b/ports/nrf/modules/manifest.py @@ -1 +1,2 @@ freeze("$(PORT_DIR)/modules/scripts", "_mkfs.py") +include("$(MPY_DIR)/extmod/uasyncio/manifest.py") From 6b23f7d3011ce89aeb00bc510eba97e5e36cd180 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Meri=C3=A7=20SARII=C5=9EIK?= Date: Mon, 7 Feb 2022 15:41:17 +0100 Subject: [PATCH 269/619] stm32/sdio: Use runtime calculation for clock divider of sdio on H7. STM32H7 family has a different calculation compared to the current one for the SDMMC clock divider configuration. --- ports/stm32/sdio.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/ports/stm32/sdio.c b/ports/stm32/sdio.c index bedafcb138..99d05a5155 100644 --- a/ports/stm32/sdio.c +++ b/ports/stm32/sdio.c @@ -96,6 +96,19 @@ static volatile uint8_t *sdmmc_buf_top; #define MICROPY_HW_SDIO_CMD (pin_D2) #endif +#if defined(STM32H7) +static uint32_t safe_divide(uint32_t denom) { + uint32_t num = HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SDMMC); + uint32_t divres; + + divres = num / (2U * denom); + if ((num % (2U * denom)) > denom) { + divres++; + } + return divres; +} +#endif + void sdio_init(uint32_t irq_pri) { // configure IO pins mp_hal_pin_config_alt_static(MICROPY_HW_SDIO_D0, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, STATIC_AF_SDMMC_D0); @@ -110,6 +123,8 @@ void sdio_init(uint32_t irq_pri) { SDMMC_TypeDef *SDIO = SDMMC; #if defined(STM32F7) SDIO->CLKCR = SDMMC_CLKCR_HWFC_EN | SDMMC_CLKCR_PWRSAV | (120 - 2); // 1-bit, 400kHz + #elif defined(STM32H7) + SDIO->CLKCR = SDMMC_CLKCR_HWFC_EN | SDMMC_CLKCR_PWRSAV | safe_divide(400000U); // 1-bit, 400kHz #else SDIO->CLKCR = SDMMC_CLKCR_HWFC_EN | SDMMC_CLKCR_PWRSAV | (120 / 2); // 1-bit, 400kHz #endif @@ -157,6 +172,8 @@ void sdio_enable_high_speed_4bit(void) { mp_hal_delay_us(10); #if defined(STM32F7) SDIO->CLKCR = SDMMC_CLKCR_HWFC_EN | SDMMC_CLKCR_WIDBUS_0 | SDMMC_CLKCR_BYPASS /*| SDMMC_CLKCR_PWRSAV*/; // 4-bit, 48MHz + #elif defined(STM32H7) + SDIO->CLKCR = SDMMC_CLKCR_HWFC_EN | SDMMC_CLKCR_WIDBUS_0 | safe_divide(48000000U); // 4-bit, 48MHz #else SDIO->CLKCR = SDMMC_CLKCR_HWFC_EN | SDMMC_CLKCR_WIDBUS_0; // 4-bit, 48MHz #endif From a41bc5a7cadb5bea9b7800286b2f4ea4f9b26e1a Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Wed, 23 Feb 2022 09:37:48 +1100 Subject: [PATCH 270/619] stm32/modmachine: Add deepsleep support to reset_cause() for WB55. --- ports/stm32/modmachine.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ports/stm32/modmachine.c b/ports/stm32/modmachine.c index 57b8ef5e6b..0239ea64ca 100644 --- a/ports/stm32/modmachine.c +++ b/ports/stm32/modmachine.c @@ -119,6 +119,12 @@ void machine_init(void) { reset_cause = PYB_RESET_DEEPSLEEP; PWR->SCR |= PWR_SCR_CSBF; } else + #elif defined(STM32WB) + if (PWR->EXTSCR & PWR_EXTSCR_C1SBF) { + // came out of standby + reset_cause = PYB_RESET_DEEPSLEEP; + PWR->EXTSCR |= PWR_EXTSCR_C1CSSF; + } else #endif { // get reset cause from RCC flags From 335002a4c020850591122d763324599e5edbe045 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 29 Mar 2022 12:27:56 +1100 Subject: [PATCH 271/619] extmod/uasyncio: Allow task state to be a callable. This implements a form of CPython's "add_done_callback()", but at this stage it is a hidden feature and only intended to be used internally. Signed-off-by: Damien George --- extmod/moduasyncio.c | 3 +++ extmod/uasyncio/core.py | 5 +++++ extmod/uasyncio/task.py | 5 ++++- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/extmod/moduasyncio.c b/extmod/moduasyncio.c index dd2d1e7475..b0921b6eb1 100644 --- a/extmod/moduasyncio.c +++ b/extmod/moduasyncio.c @@ -265,6 +265,9 @@ STATIC mp_obj_t task_getiter(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) { } else if (self->state == TASK_STATE_RUNNING_NOT_WAITED_ON) { // Allocate the waiting queue. self->state = task_queue_make_new(&task_queue_type, 0, 0, NULL); + } else if (mp_obj_get_type(self->state) != &task_queue_type) { + // Task has state used for another purpose, so can't also wait on it. + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("can't wait")); } return self_in; } diff --git a/extmod/uasyncio/core.py b/extmod/uasyncio/core.py index 12833cf0cd..28b5e960ac 100644 --- a/extmod/uasyncio/core.py +++ b/extmod/uasyncio/core.py @@ -195,6 +195,11 @@ def run_until_complete(main_task=None): if t.state is True: # "None" indicates that the task is complete and not await'ed on (yet). t.state = None + elif callable(t.state): + # The task has a callback registered to be called on completion. + t.state(t, er) + t.state = False + waiting = True else: # Schedule any other tasks waiting on the completion of this task. while t.state.peek(): diff --git a/extmod/uasyncio/task.py b/extmod/uasyncio/task.py index 26df7b1725..d775164909 100644 --- a/extmod/uasyncio/task.py +++ b/extmod/uasyncio/task.py @@ -123,7 +123,7 @@ class Task: def __init__(self, coro, globals=None): self.coro = coro # Coroutine of this Task self.data = None # General data for queue it is waiting on - self.state = True # None, False, True or a TaskQueue instance + self.state = True # None, False, True, a callable, or a TaskQueue instance self.ph_key = 0 # Pairing heap self.ph_child = None # Paring heap self.ph_child_last = None # Paring heap @@ -137,6 +137,9 @@ class Task: elif self.state is True: # Allocated head of linked list of Tasks waiting on completion of this task. self.state = TaskQueue() + elif type(self.state) is not TaskQueue: + # Task has state used for another purpose, so can't also wait on it. + raise RuntimeError("can't wait") return self def __next__(self): From 90aaf2dbef657e5afb8855a42d26093c3ef2a38d Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 29 Mar 2022 12:57:04 +1100 Subject: [PATCH 272/619] extmod/uasyncio: Fix gather cancelling and handling of exceptions. The following fixes are made: - cancelling a gather now cancels all sub-tasks of the gather (previously it would only cancel the first) - if any sub-task of a gather raises an exception then the gather finishes (previously it would only finish if the first sub-task raised) Fixes issues #5798, #7807, #7901. Signed-off-by: Damien George --- extmod/uasyncio/funcs.py | 76 +++++++++++++++++---- tests/extmod/uasyncio_gather.py | 63 ++++++++++++++--- tests/extmod/uasyncio_gather.py.exp | 21 ++++++ tests/extmod/uasyncio_gather_notimpl.py | 53 ++++++++++++++ tests/extmod/uasyncio_gather_notimpl.py.exp | 14 ++++ 5 files changed, 202 insertions(+), 25 deletions(-) create mode 100644 tests/extmod/uasyncio_gather_notimpl.py create mode 100644 tests/extmod/uasyncio_gather_notimpl.py.exp diff --git a/extmod/uasyncio/funcs.py b/extmod/uasyncio/funcs.py index 0ce48b015c..b19edeb6ef 100644 --- a/extmod/uasyncio/funcs.py +++ b/extmod/uasyncio/funcs.py @@ -53,22 +53,68 @@ def wait_for_ms(aw, timeout): return wait_for(aw, timeout, core.sleep_ms) +class _Remove: + @staticmethod + def remove(t): + pass + + async def gather(*aws, return_exceptions=False): + def done(t, er): + nonlocal state + if type(state) is not int: + # A sub-task already raised an exception, so do nothing. + return + elif not return_exceptions and not isinstance(er, StopIteration): + # A sub-task raised an exception, indicate that to the gather task. + state = er + else: + state -= 1 + if state: + # Still some sub-tasks running. + return + # Gather waiting is done, schedule the main gather task. + core._task_queue.push_head(gather_task) + ts = [core._promote_to_task(aw) for aw in aws] for i in range(len(ts)): - try: - # TODO handle cancel of gather itself - # if ts[i].coro: - # iter(ts[i]).waiting.push_head(cur_task) - # try: - # yield - # except CancelledError as er: - # # cancel all waiting tasks - # raise er - ts[i] = await ts[i] - except (core.CancelledError, Exception) as er: - if return_exceptions: - ts[i] = er - else: - raise er + if ts[i].state is not True: + # Task is not running, gather not currently supported for this case. + raise RuntimeError("can't gather") + # Register the callback to call when the task is done. + ts[i].state = done + + # Set the state for execution of the gather. + gather_task = core.cur_task + state = len(ts) + cancel_all = False + + # Wait for the a sub-task to need attention. + gather_task.data = _Remove + try: + yield + except core.CancelledError as er: + cancel_all = True + state = er + + # Clean up tasks. + for i in range(len(ts)): + if ts[i].state is done: + # Sub-task is still running, deregister the callback and cancel if needed. + ts[i].state = True + if cancel_all: + ts[i].cancel() + elif isinstance(ts[i].data, StopIteration): + # Sub-task ran to completion, get its return value. + ts[i] = ts[i].data.value + else: + # Sub-task had an exception with return_exceptions==True, so get its exception. + ts[i] = ts[i].data + + # Either this gather was cancelled, or one of the sub-tasks raised an exception with + # return_exceptions==False, so reraise the exception here. + if state is not 0: + raise state + + # Return the list of return values of each sub-task. return ts diff --git a/tests/extmod/uasyncio_gather.py b/tests/extmod/uasyncio_gather.py index 6053873dbc..718e702be6 100644 --- a/tests/extmod/uasyncio_gather.py +++ b/tests/extmod/uasyncio_gather.py @@ -27,9 +27,22 @@ async def task(id): return id -async def gather_task(): +async def task_loop(id): + print("task_loop start", id) + while True: + await asyncio.sleep(0.02) + print("task_loop loop", id) + + +async def task_raise(id): + print("task_raise start", id) + await asyncio.sleep(0.02) + raise ValueError(id) + + +async def gather_task(t0, t1): print("gather_task") - await asyncio.gather(task(1), task(2)) + await asyncio.gather(t0, t1) print("gather_task2") @@ -37,19 +50,49 @@ async def main(): # Simple gather with return values print(await asyncio.gather(factorial("A", 2), factorial("B", 3), factorial("C", 4))) + print("====") + # Test return_exceptions, where one task is cancelled and the other finishes normally tasks = [asyncio.create_task(task(1)), asyncio.create_task(task(2))] tasks[0].cancel() print(await asyncio.gather(*tasks, return_exceptions=True)) - # Cancel a multi gather - # TODO doesn't work, Task should not forward cancellation from gather to sub-task - # but rather CancelledError should cancel the gather directly, which will then cancel - # all sub-tasks explicitly - # t = asyncio.create_task(gather_task()) - # await asyncio.sleep(0.01) - # t.cancel() - # await asyncio.sleep(0.01) + print("====") + + # Test return_exceptions, where one task raises an exception and the other finishes normally. + tasks = [asyncio.create_task(task(1)), asyncio.create_task(task_raise(2))] + print(await asyncio.gather(*tasks, return_exceptions=True)) + + print("====") + + # Test case where one task raises an exception and other task keeps running. + tasks = [asyncio.create_task(task_loop(1)), asyncio.create_task(task_raise(2))] + try: + await asyncio.gather(*tasks) + except ValueError as er: + print(repr(er)) + print(tasks[0].done(), tasks[1].done()) + for t in tasks: + t.cancel() + await asyncio.sleep(0.04) + + print("====") + + # Test case where both tasks raise an exception. + tasks = [asyncio.create_task(task_raise(1)), asyncio.create_task(task_raise(2))] + try: + await asyncio.gather(*tasks) + except ValueError as er: + print(repr(er)) + print(tasks[0].done(), tasks[1].done()) + + print("====") + + # Cancel a multi gather. + t = asyncio.create_task(gather_task(task(1), task(2))) + await asyncio.sleep(0.01) + t.cancel() + await asyncio.sleep(0.04) asyncio.run(main()) diff --git a/tests/extmod/uasyncio_gather.py.exp b/tests/extmod/uasyncio_gather.py.exp index 95310bbe1c..9b30c36b67 100644 --- a/tests/extmod/uasyncio_gather.py.exp +++ b/tests/extmod/uasyncio_gather.py.exp @@ -8,6 +8,27 @@ Task B: factorial(3) = 6 Task C: Compute factorial(4)... Task C: factorial(4) = 24 [2, 6, 24] +==== start 2 end 2 [CancelledError(), 2] +==== +start 1 +task_raise start 2 +end 1 +[1, ValueError(2,)] +==== +task_loop start 1 +task_raise start 2 +task_loop loop 1 +ValueError(2,) +False True +==== +task_raise start 1 +task_raise start 2 +ValueError(1,) +True True +==== +gather_task +start 1 +start 2 diff --git a/tests/extmod/uasyncio_gather_notimpl.py b/tests/extmod/uasyncio_gather_notimpl.py new file mode 100644 index 0000000000..3ebab9bad6 --- /dev/null +++ b/tests/extmod/uasyncio_gather_notimpl.py @@ -0,0 +1,53 @@ +# Test uasyncio.gather() function, features that are not implemented. + +try: + import uasyncio as asyncio +except ImportError: + try: + import asyncio + except ImportError: + print("SKIP") + raise SystemExit + + +def custom_handler(loop, context): + print(repr(context["exception"])) + + +async def task(id): + print("task start", id) + await asyncio.sleep(0.01) + print("task end", id) + return id + + +async def gather_task(t0, t1): + print("gather_task start") + await asyncio.gather(t0, t1) + print("gather_task end") + + +async def main(): + loop = asyncio.get_event_loop() + loop.set_exception_handler(custom_handler) + + # Test case where can't wait on a task being gathered. + tasks = [asyncio.create_task(task(1)), asyncio.create_task(task(2))] + gt = asyncio.create_task(gather_task(tasks[0], tasks[1])) + await asyncio.sleep(0) # let the gather start + try: + await tasks[0] # can't await because this task is part of the gather + except RuntimeError as er: + print(repr(er)) + await gt + + print("====") + + # Test case where can't gather on a task being waited. + tasks = [asyncio.create_task(task(1)), asyncio.create_task(task(2))] + asyncio.create_task(gather_task(tasks[0], tasks[1])) + await tasks[0] # wait on this task before the gather starts + await tasks[1] + + +asyncio.run(main()) diff --git a/tests/extmod/uasyncio_gather_notimpl.py.exp b/tests/extmod/uasyncio_gather_notimpl.py.exp new file mode 100644 index 0000000000..f21614ffbe --- /dev/null +++ b/tests/extmod/uasyncio_gather_notimpl.py.exp @@ -0,0 +1,14 @@ +task start 1 +task start 2 +gather_task start +RuntimeError("can't wait",) +task end 1 +task end 2 +gather_task end +==== +task start 1 +task start 2 +gather_task start +RuntimeError("can't gather",) +task end 1 +task end 2 From 594c753c27c07df2a1b36e4eb772a6a2165eee9c Mon Sep 17 00:00:00 2001 From: stijn Date: Thu, 17 Mar 2022 12:49:38 +0100 Subject: [PATCH 273/619] py/bc.h: Fix C++20 compilation with "volatile". C++20 is deprecating several usages of the volatile keyword so remove this one affected case in the codebase which causes such warning. --- py/bc.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/py/bc.h b/py/bc.h index 8709b42389..3b7b625fec 100644 --- a/py/bc.h +++ b/py/bc.h @@ -262,7 +262,11 @@ mp_uint_t mp_decode_uint(const byte **ptr); mp_uint_t mp_decode_uint_value(const byte *ptr); const byte *mp_decode_uint_skip(const byte *ptr); -mp_vm_return_kind_t mp_execute_bytecode(mp_code_state_t *code_state, volatile mp_obj_t inject_exc); +mp_vm_return_kind_t mp_execute_bytecode(mp_code_state_t *code_state, +#ifndef __cplusplus + volatile +#endif + mp_obj_t inject_exc); mp_code_state_t *mp_obj_fun_bc_prepare_codestate(mp_obj_t func, size_t n_args, size_t n_kw, const mp_obj_t *args); void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw, const mp_obj_t *args); void mp_bytecode_print(const mp_print_t *print, const struct _mp_raw_code_t *rc, const mp_module_constants_t *cm); From df9a4122062ff706ad4b6a76cb5bc16d1a23d384 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 29 Mar 2022 22:37:58 +1100 Subject: [PATCH 274/619] py/compile: Only show raw code that is bytecode. Signed-off-by: Damien George --- py/compile.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/py/compile.c b/py/compile.c index d61dabb9a5..36f33f97f7 100644 --- a/py/compile.c +++ b/py/compile.c @@ -3636,7 +3636,9 @@ mp_compiled_module_t mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr so if (mp_verbose_flag >= 2) { for (scope_t *s = comp->scope_head; s != NULL; s = s->next) { mp_raw_code_t *rc = s->raw_code; - mp_bytecode_print(&mp_plat_print, rc, &cm.context->constants); + if (rc->kind == MP_CODE_BYTECODE) { + mp_bytecode_print(&mp_plat_print, rc, &cm.context->constants); + } } } #endif From bf3585b33c4fcd47edcf39494b7bc464a018b4fc Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 29 Mar 2022 22:38:19 +1100 Subject: [PATCH 275/619] py/asmxtensa: Fix use of l32i/s32i when offset won't fit in encoding. This commit adds optimised l32i/s32i functions that select the best load/ store encoding based on the size of the offset, and uses the function when necessary in code generation. Without this, ASM_LOAD_REG_REG_OFFSET() could overflow the word offset (using a narrow encoding), for example when loading the prelude from the constant table when there are many (>16) constants. Fixes issue #8458. Signed-off-by: Damien George --- py/asmxtensa.c | 32 ++++++++++++++++++++++---------- py/asmxtensa.h | 6 ++++-- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/py/asmxtensa.c b/py/asmxtensa.c index 0956d50f3e..8ac914ec41 100644 --- a/py/asmxtensa.c +++ b/py/asmxtensa.c @@ -27,7 +27,7 @@ #include #include -#include "py/mpconfig.h" +#include "py/runtime.h" // wrapper around everything in this file #if MICROPY_EMIT_XTENSA || MICROPY_EMIT_INLINE_XTENSA || MICROPY_EMIT_XTENSAWIN @@ -232,21 +232,33 @@ void asm_xtensa_mov_reg_pcrel(asm_xtensa_t *as, uint reg_dest, uint label) { asm_xtensa_op_add_n(as, reg_dest, reg_dest, ASM_XTENSA_REG_A0); } -void asm_xtensa_call_ind(asm_xtensa_t *as, uint idx) { - if (idx < 16) { - asm_xtensa_op_l32i_n(as, ASM_XTENSA_REG_A0, ASM_XTENSA_REG_FUN_TABLE, idx); +void asm_xtensa_l32i_optimised(asm_xtensa_t *as, uint reg_dest, uint reg_base, uint word_offset) { + if (word_offset < 16) { + asm_xtensa_op_l32i_n(as, reg_dest, reg_base, word_offset); + } else if (word_offset < 256) { + asm_xtensa_op_l32i(as, reg_dest, reg_base, word_offset); } else { - asm_xtensa_op_l32i(as, ASM_XTENSA_REG_A0, ASM_XTENSA_REG_FUN_TABLE, idx); + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("asm overflow")); } +} + +void asm_xtensa_s32i_optimised(asm_xtensa_t *as, uint reg_src, uint reg_base, uint word_offset) { + if (word_offset < 16) { + asm_xtensa_op_s32i_n(as, reg_src, reg_base, word_offset); + } else if (word_offset < 256) { + asm_xtensa_op_s32i(as, reg_src, reg_base, word_offset); + } else { + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("asm overflow")); + } +} + +void asm_xtensa_call_ind(asm_xtensa_t *as, uint idx) { + asm_xtensa_l32i_optimised(as, ASM_XTENSA_REG_A0, ASM_XTENSA_REG_FUN_TABLE, idx); asm_xtensa_op_callx0(as, ASM_XTENSA_REG_A0); } void asm_xtensa_call_ind_win(asm_xtensa_t *as, uint idx) { - if (idx < 16) { - asm_xtensa_op_l32i_n(as, ASM_XTENSA_REG_A8, ASM_XTENSA_REG_FUN_TABLE_WIN, idx); - } else { - asm_xtensa_op_l32i(as, ASM_XTENSA_REG_A8, ASM_XTENSA_REG_FUN_TABLE_WIN, idx); - } + asm_xtensa_l32i_optimised(as, ASM_XTENSA_REG_A8, ASM_XTENSA_REG_FUN_TABLE_WIN, idx); asm_xtensa_op_callx8(as, ASM_XTENSA_REG_A8); } diff --git a/py/asmxtensa.h b/py/asmxtensa.h index 43f1b608ed..16a59c0ee0 100644 --- a/py/asmxtensa.h +++ b/py/asmxtensa.h @@ -278,6 +278,8 @@ void asm_xtensa_mov_local_reg(asm_xtensa_t *as, int local_num, uint reg_src); void asm_xtensa_mov_reg_local(asm_xtensa_t *as, uint reg_dest, int local_num); void asm_xtensa_mov_reg_local_addr(asm_xtensa_t *as, uint reg_dest, int local_num); void asm_xtensa_mov_reg_pcrel(asm_xtensa_t *as, uint reg_dest, uint label); +void asm_xtensa_l32i_optimised(asm_xtensa_t *as, uint reg_dest, uint reg_base, uint word_offset); +void asm_xtensa_s32i_optimised(asm_xtensa_t *as, uint reg_src, uint reg_base, uint word_offset); void asm_xtensa_call_ind(asm_xtensa_t *as, uint idx); void asm_xtensa_call_ind_win(asm_xtensa_t *as, uint idx); @@ -393,12 +395,12 @@ void asm_xtensa_call_ind_win(asm_xtensa_t *as, uint idx); #define ASM_SUB_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_sub((as), (reg_dest), (reg_dest), (reg_src)) #define ASM_MUL_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_mull((as), (reg_dest), (reg_dest), (reg_src)) -#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_xtensa_op_l32i_n((as), (reg_dest), (reg_base), (word_offset)) +#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_xtensa_l32i_optimised((as), (reg_dest), (reg_base), (word_offset)) #define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) asm_xtensa_op_l8ui((as), (reg_dest), (reg_base), 0) #define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) asm_xtensa_op_l16ui((as), (reg_dest), (reg_base), 0) #define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) asm_xtensa_op_l32i_n((as), (reg_dest), (reg_base), 0) -#define ASM_STORE_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_xtensa_op_s32i_n((as), (reg_dest), (reg_base), (word_offset)) +#define ASM_STORE_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_xtensa_s32i_optimised((as), (reg_dest), (reg_base), (word_offset)) #define ASM_STORE8_REG_REG(as, reg_src, reg_base) asm_xtensa_op_s8i((as), (reg_src), (reg_base), 0) #define ASM_STORE16_REG_REG(as, reg_src, reg_base) asm_xtensa_op_s16i((as), (reg_src), (reg_base), 0) #define ASM_STORE32_REG_REG(as, reg_src, reg_base) asm_xtensa_op_s32i_n((as), (reg_src), (reg_base), 0) From 7e8222ae063d78ae8f901bb0f9fef663edd2edb6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 29 Mar 2022 22:40:02 +1100 Subject: [PATCH 276/619] py/emitnative: Don't store prelude at end of machine code if not needed. Signed-off-by: Damien George --- py/emitnative.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/py/emitnative.c b/py/emitnative.c index bddd661428..a207dd30f7 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -723,7 +723,13 @@ STATIC bool emit_native_end_pass(emit_t *emit) { if (emit->pass == MP_PASS_EMIT) { void *f = mp_asm_base_get_code(&emit->as->base); + #if N_PRELUDE_AS_BYTES_OBJ + // Keep only the machine code, not the prelude, which is in a separate bytes object. + mp_uint_t f_len = emit->prelude_offset; + #else + // Keep both the machine code and the prelude. mp_uint_t f_len = mp_asm_base_get_code_size(&emit->as->base); + #endif mp_emit_glue_assign_native(emit->scope->raw_code, emit->do_viper_types ? MP_CODE_NATIVE_VIPER : MP_CODE_NATIVE_PY, From bb70874111dbb246624a68c013e8f1c3245ca0d8 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 31 Mar 2022 10:57:34 +1100 Subject: [PATCH 277/619] py/vm: Prevent array bound warning when using -MP_OBJ_ITER_BUF_NSLOTS. This warning can happen on clang 13.0.1 building mpy-cross: ../py/vm.c:748:25: error: array index -3 refers past the last possible element for an array in 64-bit address space containing 64-bit (8-byte) elements (max possible 2305843009213693952 elements) [-Werror,-Warray-bounds] sp[-MP_OBJ_ITER_BUF_NSLOTS + 1] = MP_OBJ_NULL; ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Using pointer access instead of array access works around this warning. Fixes issue #8467. Signed-off-by: Damien George --- py/vm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/py/vm.c b/py/vm.c index 50da90e7d2..fa163423ad 100644 --- a/py/vm.c +++ b/py/vm.c @@ -745,8 +745,8 @@ unwind_jump:; obj = mp_getiter(obj, iter_buf); if (obj != MP_OBJ_FROM_PTR(iter_buf)) { // Iterator didn't use the stack so indicate that with MP_OBJ_NULL. - sp[-MP_OBJ_ITER_BUF_NSLOTS + 1] = MP_OBJ_NULL; - sp[-MP_OBJ_ITER_BUF_NSLOTS + 2] = obj; + *(sp - MP_OBJ_ITER_BUF_NSLOTS + 1) = MP_OBJ_NULL; + *(sp - MP_OBJ_ITER_BUF_NSLOTS + 2) = obj; } DISPATCH(); } @@ -757,8 +757,8 @@ unwind_jump:; DECODE_ULABEL; // the jump offset if iteration finishes; for labels are always forward code_state->sp = sp; mp_obj_t obj; - if (sp[-MP_OBJ_ITER_BUF_NSLOTS + 1] == MP_OBJ_NULL) { - obj = sp[-MP_OBJ_ITER_BUF_NSLOTS + 2]; + if (*(sp - MP_OBJ_ITER_BUF_NSLOTS + 1) == MP_OBJ_NULL) { + obj = *(sp - MP_OBJ_ITER_BUF_NSLOTS + 2); } else { obj = MP_OBJ_FROM_PTR(&sp[-MP_OBJ_ITER_BUF_NSLOTS + 1]); } From 1e99d29f362f8cccc60a36fcbd8590404c719f40 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Tue, 24 Mar 2020 21:39:46 -0500 Subject: [PATCH 278/619] py/runtime: Allow multiple **args in a function call. This is a partial implementation of PEP 448 to allow multiple ** unpackings when calling a function or method. The compiler is modified to encode the argument as a None: obj key-value pair (similar to how regular keyword arguments are encoded as str: obj pairs). The extra object that was pushed on the stack to hold a single ** unpacking object is no longer used and is removed. The runtime is modified to decode this new format. Signed-off-by: David Lechner --- py/compile.c | 18 ++--- py/emitbc.c | 4 +- py/emitnative.c | 4 +- py/runtime.c | 126 +++++++++++++++++-------------- py/vm.c | 8 +- tests/basics/fun_calldblstar4.py | 33 ++++++++ tests/basics/python34.py | 1 - tests/basics/python34.py.exp | 1 - tests/cmdline/cmd_showbc.py.exp | 8 +- 9 files changed, 121 insertions(+), 82 deletions(-) create mode 100644 tests/basics/fun_calldblstar4.py diff --git a/py/compile.c b/py/compile.c index 36f33f97f7..1636bd157d 100644 --- a/py/compile.c +++ b/py/compile.c @@ -2397,7 +2397,7 @@ STATIC void compile_trailer_paren_helper(compiler_t *comp, mp_parse_node_t pn_ar int n_positional = n_positional_extra; uint n_keyword = 0; uint star_flags = 0; - mp_parse_node_struct_t *star_args_node = NULL, *dblstar_args_node = NULL; + mp_parse_node_struct_t *star_args_node = NULL; for (size_t i = 0; i < n_args; i++) { if (MP_PARSE_NODE_IS_STRUCT(args[i])) { mp_parse_node_struct_t *pns_arg = (mp_parse_node_struct_t *)args[i]; @@ -2409,12 +2409,11 @@ STATIC void compile_trailer_paren_helper(compiler_t *comp, mp_parse_node_t pn_ar star_flags |= MP_EMIT_STAR_FLAG_SINGLE; star_args_node = pns_arg; } else if (MP_PARSE_NODE_STRUCT_KIND(pns_arg) == PN_arglist_dbl_star) { - if (star_flags & MP_EMIT_STAR_FLAG_DOUBLE) { - compile_syntax_error(comp, (mp_parse_node_t)pns_arg, MP_ERROR_TEXT("can't have multiple **x")); - return; - } star_flags |= MP_EMIT_STAR_FLAG_DOUBLE; - dblstar_args_node = pns_arg; + // double-star args are stored as kw arg with key of None + EMIT(load_null); + compile_node(comp, pns_arg->nodes[0]); + n_keyword++; } else if (MP_PARSE_NODE_STRUCT_KIND(pns_arg) == PN_argument) { #if MICROPY_PY_ASSIGN_EXPR if (MP_PARSE_NODE_IS_STRUCT_KIND(pns_arg->nodes[1], PN_argument_3)) { @@ -2429,7 +2428,7 @@ STATIC void compile_trailer_paren_helper(compiler_t *comp, mp_parse_node_t pn_ar } EMIT_ARG(load_const_str, MP_PARSE_NODE_LEAF_ARG(pns_arg->nodes[0])); compile_node(comp, pns_arg->nodes[1]); - n_keyword += 1; + n_keyword++; } else { compile_comprehension(comp, pns_arg, SCOPE_GEN_EXPR); n_positional++; @@ -2460,11 +2459,6 @@ STATIC void compile_trailer_paren_helper(compiler_t *comp, mp_parse_node_t pn_ar } else { compile_node(comp, star_args_node->nodes[0]); } - if (dblstar_args_node == NULL) { - EMIT(load_null); - } else { - compile_node(comp, dblstar_args_node->nodes[0]); - } } // emit the function/method call diff --git a/py/emitbc.c b/py/emitbc.c index 1f5cd9d322..21ec121911 100644 --- a/py/emitbc.c +++ b/py/emitbc.c @@ -752,7 +752,9 @@ void mp_emit_bc_make_closure(emit_t *emit, scope_t *scope, mp_uint_t n_closed_ov STATIC void emit_bc_call_function_method_helper(emit_t *emit, int stack_adj, mp_uint_t bytecode_base, mp_uint_t n_positional, mp_uint_t n_keyword, mp_uint_t star_flags) { if (star_flags) { - stack_adj -= (int)n_positional + 2 * (int)n_keyword + 2; + // each positional arg is one object, each kwarg is two objects, the key + // and the value and one extra object for the star args bitmap. + stack_adj -= (int)n_positional + 2 * (int)n_keyword + 1; emit_write_bytecode_byte_uint(emit, stack_adj, bytecode_base + 1, (n_keyword << 8) | n_positional); // TODO make it 2 separate uints? } else { stack_adj -= (int)n_positional + 2 * (int)n_keyword; diff --git a/py/emitnative.c b/py/emitnative.c index a207dd30f7..2863984047 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -2752,7 +2752,7 @@ STATIC void emit_native_call_function(emit_t *emit, mp_uint_t n_positional, mp_u } else { assert(vtype_fun == VTYPE_PYOBJ); if (star_flags) { - emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, n_positional + 2 * n_keyword + 3); // pointer to args + emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, n_positional + 2 * n_keyword + 2); // pointer to args emit_call_with_2_imm_args(emit, MP_F_CALL_METHOD_N_KW_VAR, 0, REG_ARG_1, n_positional | (n_keyword << 8), REG_ARG_2); emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); } else { @@ -2768,7 +2768,7 @@ STATIC void emit_native_call_function(emit_t *emit, mp_uint_t n_positional, mp_u STATIC void emit_native_call_method(emit_t *emit, mp_uint_t n_positional, mp_uint_t n_keyword, mp_uint_t star_flags) { if (star_flags) { - emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, n_positional + 2 * n_keyword + 4); // pointer to args + emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, n_positional + 2 * n_keyword + 3); // pointer to args emit_call_with_2_imm_args(emit, MP_F_CALL_METHOD_N_KW_VAR, 1, REG_ARG_1, n_positional | (n_keyword << 8), REG_ARG_2); emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); } else { diff --git a/py/runtime.c b/py/runtime.c index ba3fbe7fa5..e6bfbda58d 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -702,9 +702,8 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ uint n_args = n_args_n_kw & 0xff; uint n_kw = (n_args_n_kw >> 8) & 0xff; mp_obj_t pos_seq = args[n_args + 2 * n_kw]; // may be MP_OBJ_NULL - mp_obj_t kw_dict = args[n_args + 2 * n_kw + 1]; // may be MP_OBJ_NULL - DEBUG_OP_printf("call method var (fun=%p, self=%p, n_args=%u, n_kw=%u, args=%p, seq=%p, dict=%p)\n", fun, self, n_args, n_kw, args, pos_seq, kw_dict); + DEBUG_OP_printf("call method var (fun=%p, self=%p, n_args=%u, n_kw=%u, args=%p, seq=%p)\n", fun, self, n_args, n_kw, args, pos_seq); // We need to create the following array of objects: // args[0 .. n_args] unpacked(pos_seq) args[n_args .. n_args + 2 * n_kw] unpacked(kw_dict) @@ -717,8 +716,13 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ // Try to get a hint for the size of the kw_dict uint kw_dict_len = 0; - if (kw_dict != MP_OBJ_NULL && mp_obj_is_type(kw_dict, &mp_type_dict)) { - kw_dict_len = mp_obj_dict_len(kw_dict); + + for (uint i = 0; i < n_kw; i++) { + mp_obj_t key = args[n_args + i * 2]; + mp_obj_t value = args[n_args + i * 2 + 1]; + if (key == MP_OBJ_NULL && value != MP_OBJ_NULL && mp_obj_is_type(value, &mp_type_dict)) { + kw_dict_len += mp_obj_dict_len(value); + } } // Extract the pos_seq sequence to the new args array. @@ -792,64 +796,72 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ // The size of the args2 array now is the number of positional args. uint pos_args_len = args2_len; - // Copy the fixed kw args. - mp_seq_copy(args2 + args2_len, args + n_args, 2 * n_kw, mp_obj_t); - args2_len += 2 * n_kw; - - // Extract (key,value) pairs from kw_dict dictionary and append to args2. - // Note that it can be arbitrary iterator. - if (kw_dict == MP_OBJ_NULL) { - // pass - } else if (mp_obj_is_type(kw_dict, &mp_type_dict)) { - // dictionary - mp_map_t *map = mp_obj_dict_get_map(kw_dict); - assert(args2_len + 2 * map->used <= args2_alloc); // should have enough, since kw_dict_len is in this case hinted correctly above - for (size_t i = 0; i < map->alloc; i++) { - if (mp_map_slot_is_filled(map, i)) { - // the key must be a qstr, so intern it if it's a string - mp_obj_t key = map->table[i].key; - if (!mp_obj_is_qstr(key)) { - key = mp_obj_str_intern_checked(key); + // Copy the kw args. + for (uint i = 0; i < n_kw; i++) { + mp_obj_t kw_key = args[n_args + i * 2]; + mp_obj_t kw_value = args[n_args + i * 2 + 1]; + if (kw_key == MP_OBJ_NULL) { + // double-star args + if (kw_value == MP_OBJ_NULL) { + // pass + } else if (mp_obj_is_type(kw_value, &mp_type_dict)) { + // dictionary + mp_map_t *map = mp_obj_dict_get_map(kw_value); + // should have enough, since kw_dict_len is in this case hinted correctly above + assert(args2_len + 2 * map->used <= args2_alloc); + for (size_t j = 0; j < map->alloc; j++) { + if (mp_map_slot_is_filled(map, j)) { + // the key must be a qstr, so intern it if it's a string + mp_obj_t key = map->table[j].key; + if (!mp_obj_is_qstr(key)) { + key = mp_obj_str_intern_checked(key); + } + args2[args2_len++] = key; + args2[args2_len++] = map->table[j].value; + } } - args2[args2_len++] = key; - args2[args2_len++] = map->table[i].value; - } - } - } else { - // generic mapping: - // - call keys() to get an iterable of all keys in the mapping - // - call __getitem__ for each key to get the corresponding value + } else { + // generic mapping: + // - call keys() to get an iterable of all keys in the mapping + // - call __getitem__ for each key to get the corresponding value - // get the keys iterable - mp_obj_t dest[3]; - mp_load_method(kw_dict, MP_QSTR_keys, dest); - mp_obj_t iterable = mp_getiter(mp_call_method_n_kw(0, 0, dest), NULL); + // get the keys iterable + mp_obj_t dest[3]; + mp_load_method(kw_value, MP_QSTR_keys, dest); + mp_obj_t iterable = mp_getiter(mp_call_method_n_kw(0, 0, dest), NULL); - mp_obj_t key; - while ((key = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { - // expand size of args array if needed - if (args2_len + 1 >= args2_alloc) { - uint new_alloc = args2_alloc * 2; - if (new_alloc < 4) { - new_alloc = 4; + mp_obj_t key; + while ((key = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + // expand size of args array if needed + if (args2_len + 1 >= args2_alloc) { + uint new_alloc = args2_alloc * 2; + if (new_alloc < 4) { + new_alloc = 4; + } + args2 = mp_nonlocal_realloc(args2, args2_alloc * sizeof(mp_obj_t), new_alloc * sizeof(mp_obj_t)); + args2_alloc = new_alloc; + } + + // the key must be a qstr, so intern it if it's a string + if (!mp_obj_is_qstr(key)) { + key = mp_obj_str_intern_checked(key); + } + + // get the value corresponding to the key + mp_load_method(kw_value, MP_QSTR___getitem__, dest); + dest[2] = key; + mp_obj_t value = mp_call_method_n_kw(1, 0, dest); + + // store the key/value pair in the argument array + args2[args2_len++] = key; + args2[args2_len++] = value; } - args2 = mp_nonlocal_realloc(args2, args2_alloc * sizeof(mp_obj_t), new_alloc * sizeof(mp_obj_t)); - args2_alloc = new_alloc; } - - // the key must be a qstr, so intern it if it's a string - if (!mp_obj_is_qstr(key)) { - key = mp_obj_str_intern_checked(key); - } - - // get the value corresponding to the key - mp_load_method(kw_dict, MP_QSTR___getitem__, dest); - dest[2] = key; - mp_obj_t value = mp_call_method_n_kw(1, 0, dest); - - // store the key/value pair in the argument array - args2[args2_len++] = key; - args2[args2_len++] = value; + } else { + // normal kwarg + assert(args2_len + 2 <= args2_alloc); + args2[args2_len++] = kw_key; + args2[args2_len++] = kw_value; } } diff --git a/py/vm.c b/py/vm.c index fa163423ad..1450004838 100644 --- a/py/vm.c +++ b/py/vm.c @@ -949,8 +949,8 @@ unwind_jump:; // unum & 0xff == n_positional // (unum >> 8) & 0xff == n_keyword // We have following stack layout here: - // fun arg0 arg1 ... kw0 val0 kw1 val1 ... seq dict <- TOS - sp -= (unum & 0xff) + ((unum >> 7) & 0x1fe) + 2; + // fun arg0 arg1 ... kw0 val0 kw1 val1 ... seq <- TOS + sp -= (unum & 0xff) + ((unum >> 7) & 0x1fe) + 1; #if MICROPY_STACKLESS if (mp_obj_get_type(*sp) == &mp_type_fun_bc) { code_state->ip = ip; @@ -1034,8 +1034,8 @@ unwind_jump:; // unum & 0xff == n_positional // (unum >> 8) & 0xff == n_keyword // We have following stack layout here: - // fun self arg0 arg1 ... kw0 val0 kw1 val1 ... seq dict <- TOS - sp -= (unum & 0xff) + ((unum >> 7) & 0x1fe) + 3; + // fun self arg0 arg1 ... kw0 val0 kw1 val1 ... seq <- TOS + sp -= (unum & 0xff) + ((unum >> 7) & 0x1fe) + 2; #if MICROPY_STACKLESS if (mp_obj_get_type(*sp) == &mp_type_fun_bc) { code_state->ip = ip; diff --git a/tests/basics/fun_calldblstar4.py b/tests/basics/fun_calldblstar4.py new file mode 100644 index 0000000000..acb332a8c2 --- /dev/null +++ b/tests/basics/fun_calldblstar4.py @@ -0,0 +1,33 @@ +# test calling a function with multiple **args + + +def f(a, b=None, c=None): + print(a, b, c) + + +f(**{"a": 1}, **{"b": 2}) +f(**{"a": 1}, **{"b": 2}, c=3) +f(**{"a": 1}, b=2, **{"c": 3}) + +try: + f(1, **{"b": 2}, **{"b": 3}) +except TypeError: + print("TypeError") + +# test calling a method with multiple **args + + +class A: + def f(self, a, b=None, c=None): + print(a, b, c) + + +a = A() +a.f(**{"a": 1}, **{"b": 2}) +a.f(**{"a": 1}, **{"b": 2}, c=3) +a.f(**{"a": 1}, b=2, **{"c": 3}) + +try: + a.f(1, **{"b": 2}, **{"b": 3}) +except TypeError: + print("TypeError") diff --git a/tests/basics/python34.py b/tests/basics/python34.py index 0f6e4bafd0..609a8b6b84 100644 --- a/tests/basics/python34.py +++ b/tests/basics/python34.py @@ -25,7 +25,6 @@ def test_syntax(code): except SyntaxError: print("SyntaxError") test_syntax("f(*a, *b)") # can't have multiple * (in 3.5 we can) -test_syntax("f(**a, **b)") # can't have multiple ** (in 3.5 we can) test_syntax("f(*a, b)") # can't have positional after * test_syntax("f(**a, b)") # can't have positional after ** test_syntax("() = []") # can't assign to empty tuple (in 3.6 we can) diff --git a/tests/basics/python34.py.exp b/tests/basics/python34.py.exp index 8480171307..75f1c2c056 100644 --- a/tests/basics/python34.py.exp +++ b/tests/basics/python34.py.exp @@ -8,7 +8,6 @@ SyntaxError SyntaxError SyntaxError SyntaxError -SyntaxError 3.4 3 4 IndexError('foo',) diff --git a/tests/cmdline/cmd_showbc.py.exp b/tests/cmdline/cmd_showbc.py.exp index 92501e1248..663bacda01 100644 --- a/tests/cmdline/cmd_showbc.py.exp +++ b/tests/cmdline/cmd_showbc.py.exp @@ -297,8 +297,8 @@ arg names: 208 POP_TOP 209 LOAD_FAST 0 210 LOAD_DEREF 14 -212 LOAD_NULL -213 CALL_FUNCTION_VAR_KW n=0 nkw=0 +212 LOAD_CONST_SMALL_INT 1 +213 CALL_FUNCTION_VAR_KW n=1 nkw=0 215 POP_TOP 216 LOAD_FAST 0 217 LOAD_METHOD b @@ -318,8 +318,8 @@ arg names: 239 LOAD_FAST 0 240 LOAD_METHOD b 242 LOAD_FAST 1 -243 LOAD_NULL -244 CALL_METHOD_VAR_KW n=0 nkw=0 +243 LOAD_CONST_SMALL_INT 1 +244 CALL_METHOD_VAR_KW n=1 nkw=0 246 POP_TOP 247 LOAD_FAST 0 248 POP_JUMP_IF_FALSE 255 From 783b1a868fb0f3c1fd6cf7231311c24801c33505 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Tue, 24 Mar 2020 23:54:45 -0500 Subject: [PATCH 279/619] py/runtime: Allow multiple *args in a function call. This is a partial implementation of PEP 448 to allow unpacking multiple star args in a function or method call. This is implemented by changing the emitted bytecodes so that both positional args and star args are stored as positional args. A bitmap is added to indicate if an argument at a given position is a positional argument or a star arg. In the generated code, this new bitmap takes the place of the old star arg. It is stored as a small int, so this means only the first N arguments can be star args where N is the number of bits in a small int. The runtime is modified to interpret this new bytecode format while still trying to perform as few memory reallocations as possible. Signed-off-by: David Lechner --- docs/differences/python_35.rst | 2 +- py/compile.c | 37 +++++---- py/runtime.c | 103 ++++++++++++++++---------- py/vm.c | 4 +- tests/basics/fun_callstar.py | 12 +++ tests/basics/fun_callstardblstar.py | 10 +++ tests/basics/fun_kwvarargs.py | 13 ++++ tests/basics/python34.py | 11 +-- tests/basics/python34.py.exp | 5 -- tests/cpydiff/syntax_arg_unpacking.py | 23 ++++++ 10 files changed, 151 insertions(+), 69 deletions(-) create mode 100644 tests/cpydiff/syntax_arg_unpacking.py diff --git a/docs/differences/python_35.rst b/docs/differences/python_35.rst index e88df25f95..06cfbfc03c 100644 --- a/docs/differences/python_35.rst +++ b/docs/differences/python_35.rst @@ -8,7 +8,7 @@ Below is a list of finalised/accepted PEPs for Python 3.5 grouped into their imp +----------------------------------------------------------------------------------------------------------+---------------+ | **Extensions to the syntax:** | **Status** | +--------------------------------------------------------+-------------------------------------------------+---------------+ - | `PEP 448 `_ | additional unpacking generalizations | | + | `PEP 448 `_ | additional unpacking generalizations | Partial | +--------------------------------------------------------+-------------------------------------------------+---------------+ | `PEP 465 `_ | a new matrix multiplication operator | Completed | +--------------------------------------------------------+-------------------------------------------------+---------------+ diff --git a/py/compile.c b/py/compile.c index 1636bd157d..fc11062705 100644 --- a/py/compile.c +++ b/py/compile.c @@ -37,6 +37,7 @@ #include "py/asmbase.h" #include "py/nativeglue.h" #include "py/persistentcode.h" +#include "py/smallint.h" #if MICROPY_ENABLE_COMPILER @@ -2397,17 +2398,30 @@ STATIC void compile_trailer_paren_helper(compiler_t *comp, mp_parse_node_t pn_ar int n_positional = n_positional_extra; uint n_keyword = 0; uint star_flags = 0; - mp_parse_node_struct_t *star_args_node = NULL; + mp_uint_t star_args = 0; for (size_t i = 0; i < n_args; i++) { if (MP_PARSE_NODE_IS_STRUCT(args[i])) { mp_parse_node_struct_t *pns_arg = (mp_parse_node_struct_t *)args[i]; if (MP_PARSE_NODE_STRUCT_KIND(pns_arg) == PN_arglist_star) { - if (star_flags & MP_EMIT_STAR_FLAG_SINGLE) { - compile_syntax_error(comp, (mp_parse_node_t)pns_arg, MP_ERROR_TEXT("can't have multiple *x")); + if (star_flags & MP_EMIT_STAR_FLAG_DOUBLE) { + compile_syntax_error(comp, (mp_parse_node_t)pns_arg, MP_ERROR_TEXT("* arg after **")); + return; + } + #if MICROPY_DYNAMIC_COMPILER + if (i > mp_dynamic_compiler.small_int_bits) + #else + if (i > MP_SMALL_INT_BITS) + #endif + { + // If there are not enough bits in a small int to fit the flag, then we consider + // it a syntax error. It should be unlikely to have this many args in practice. + compile_syntax_error(comp, (mp_parse_node_t)pns_arg, MP_ERROR_TEXT("too many args")); return; } star_flags |= MP_EMIT_STAR_FLAG_SINGLE; - star_args_node = pns_arg; + star_args |= 1 << i; + compile_node(comp, pns_arg->nodes[0]); + n_positional++; } else if (MP_PARSE_NODE_STRUCT_KIND(pns_arg) == PN_arglist_dbl_star) { star_flags |= MP_EMIT_STAR_FLAG_DOUBLE; // double-star args are stored as kw arg with key of None @@ -2438,12 +2452,12 @@ STATIC void compile_trailer_paren_helper(compiler_t *comp, mp_parse_node_t pn_ar } } else { normal_argument: - if (star_flags) { - compile_syntax_error(comp, args[i], MP_ERROR_TEXT("non-keyword arg after */**")); + if (star_flags & MP_EMIT_STAR_FLAG_DOUBLE) { + compile_syntax_error(comp, args[i], MP_ERROR_TEXT("positional arg after **")); return; } if (n_keyword > 0) { - compile_syntax_error(comp, args[i], MP_ERROR_TEXT("non-keyword arg after keyword arg")); + compile_syntax_error(comp, args[i], MP_ERROR_TEXT("positional arg after keyword arg")); return; } compile_node(comp, args[i]); @@ -2451,14 +2465,9 @@ STATIC void compile_trailer_paren_helper(compiler_t *comp, mp_parse_node_t pn_ar } } - // compile the star/double-star arguments if we had them - // if we had one but not the other then we load "null" as a place holder if (star_flags != 0) { - if (star_args_node == NULL) { - EMIT(load_null); - } else { - compile_node(comp, star_args_node->nodes[0]); - } + // one extra object that contains the star_args map + EMIT_ARG(load_const_small_int, star_args); } // emit the function/method call diff --git a/py/runtime.c b/py/runtime.c index e6bfbda58d..90722ee18a 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -701,9 +701,9 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ } uint n_args = n_args_n_kw & 0xff; uint n_kw = (n_args_n_kw >> 8) & 0xff; - mp_obj_t pos_seq = args[n_args + 2 * n_kw]; // may be MP_OBJ_NULL + mp_uint_t star_args = mp_obj_get_int_truncated(args[n_args + 2 * n_kw]); - DEBUG_OP_printf("call method var (fun=%p, self=%p, n_args=%u, n_kw=%u, args=%p, seq=%p)\n", fun, self, n_args, n_kw, args, pos_seq); + DEBUG_OP_printf("call method var (fun=%p, self=%p, n_args=%u, n_kw=%u, args=%p, map=%u)\n", fun, self, n_args, n_kw, args, star_args); // We need to create the following array of objects: // args[0 .. n_args] unpacked(pos_seq) args[n_args .. n_args + 2 * n_kw] unpacked(kw_dict) @@ -714,6 +714,20 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ uint args2_alloc; uint args2_len = 0; + // Try to get a hint for unpacked * args length + uint list_len = 0; + + if (star_args != 0) { + for (uint i = 0; i < n_args; i++) { + if (star_args & (1 << i)) { + mp_obj_t len = mp_obj_len_maybe(args[i]); + if (len != MP_OBJ_NULL) { + list_len += mp_obj_get_int(len); + } + } + } + } + // Try to get a hint for the size of the kw_dict uint kw_dict_len = 0; @@ -727,8 +741,8 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ // Extract the pos_seq sequence to the new args array. // Note that it can be arbitrary iterator. - if (pos_seq == MP_OBJ_NULL) { - // no sequence + if (star_args == 0) { + // no star args to unpack // allocate memory for the new array of args args2_alloc = 1 + n_args + 2 * (n_kw + kw_dict_len); @@ -742,33 +756,11 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ // copy the fixed pos args mp_seq_copy(args2 + args2_len, args, n_args, mp_obj_t); args2_len += n_args; - - } else if (mp_obj_is_type(pos_seq, &mp_type_tuple) || mp_obj_is_type(pos_seq, &mp_type_list)) { - // optimise the case of a tuple and list - - // get the items - size_t len; - mp_obj_t *items; - mp_obj_get_array(pos_seq, &len, &items); - - // allocate memory for the new array of args - args2_alloc = 1 + n_args + len + 2 * (n_kw + kw_dict_len); - args2 = mp_nonlocal_alloc(args2_alloc * sizeof(mp_obj_t)); - - // copy the self - if (self != MP_OBJ_NULL) { - args2[args2_len++] = self; - } - - // copy the fixed and variable position args - mp_seq_cat(args2 + args2_len, args, n_args, items, len, mp_obj_t); - args2_len += n_args + len; - } else { - // generic iterator + // at least one star arg to unpack // allocate memory for the new array of args - args2_alloc = 1 + n_args + 2 * (n_kw + kw_dict_len) + 3; + args2_alloc = 1 + n_args + list_len + 2 * (n_kw + kw_dict_len); args2 = mp_nonlocal_alloc(args2_alloc * sizeof(mp_obj_t)); // copy the self @@ -776,26 +768,57 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ args2[args2_len++] = self; } - // copy the fixed position args - mp_seq_copy(args2 + args2_len, args, n_args, mp_obj_t); - args2_len += n_args; + for (uint i = 0; i < n_args; i++) { + mp_obj_t arg = args[i]; + if (star_args & (1 << i)) { + // star arg + if (mp_obj_is_type(arg, &mp_type_tuple) || mp_obj_is_type(arg, &mp_type_list)) { + // optimise the case of a tuple and list - // extract the variable position args from the iterator - mp_obj_iter_buf_t iter_buf; - mp_obj_t iterable = mp_getiter(pos_seq, &iter_buf); - mp_obj_t item; - while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { - if (args2_len >= args2_alloc) { - args2 = mp_nonlocal_realloc(args2, args2_alloc * sizeof(mp_obj_t), args2_alloc * 2 * sizeof(mp_obj_t)); - args2_alloc *= 2; + // get the items + size_t len; + mp_obj_t *items; + mp_obj_get_array(arg, &len, &items); + + // copy the items + assert(args2_len + len <= args2_alloc); + mp_seq_copy(args2 + args2_len, items, len, mp_obj_t); + args2_len += len; + } else { + // generic iterator + + // extract the variable position args from the iterator + mp_obj_iter_buf_t iter_buf; + mp_obj_t iterable = mp_getiter(arg, &iter_buf); + mp_obj_t item; + while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + if (args2_len >= args2_alloc) { + args2 = mp_nonlocal_realloc(args2, args2_alloc * sizeof(mp_obj_t), + args2_alloc * 2 * sizeof(mp_obj_t)); + args2_alloc *= 2; + } + args2[args2_len++] = item; + } + } + } else { + // normal argument + assert(args2_len < args2_alloc); + args2[args2_len++] = arg; } - args2[args2_len++] = item; } } // The size of the args2 array now is the number of positional args. uint pos_args_len = args2_len; + // ensure there is still enough room for kw args + if (args2_len + 2 * (n_kw + kw_dict_len) > args2_alloc) { + uint new_alloc = args2_len + 2 * (n_kw + kw_dict_len); + args2 = mp_nonlocal_realloc(args2, args2_alloc * sizeof(mp_obj_t), + new_alloc * sizeof(mp_obj_t)); + args2_alloc = new_alloc; + } + // Copy the kw args. for (uint i = 0; i < n_kw; i++) { mp_obj_t kw_key = args[n_args + i * 2]; diff --git a/py/vm.c b/py/vm.c index 1450004838..02f8bc88c9 100644 --- a/py/vm.c +++ b/py/vm.c @@ -949,7 +949,7 @@ unwind_jump:; // unum & 0xff == n_positional // (unum >> 8) & 0xff == n_keyword // We have following stack layout here: - // fun arg0 arg1 ... kw0 val0 kw1 val1 ... seq <- TOS + // fun arg0 arg1 ... kw0 val0 kw1 val1 ... bitmap <- TOS sp -= (unum & 0xff) + ((unum >> 7) & 0x1fe) + 1; #if MICROPY_STACKLESS if (mp_obj_get_type(*sp) == &mp_type_fun_bc) { @@ -1034,7 +1034,7 @@ unwind_jump:; // unum & 0xff == n_positional // (unum >> 8) & 0xff == n_keyword // We have following stack layout here: - // fun self arg0 arg1 ... kw0 val0 kw1 val1 ... seq <- TOS + // fun self arg0 arg1 ... kw0 val0 kw1 val1 ... bitmap <- TOS sp -= (unum & 0xff) + ((unum >> 7) & 0x1fe) + 2; #if MICROPY_STACKLESS if (mp_obj_get_type(*sp) == &mp_type_fun_bc) { diff --git a/tests/basics/fun_callstar.py b/tests/basics/fun_callstar.py index a27a288a3c..53d2ece3e1 100644 --- a/tests/basics/fun_callstar.py +++ b/tests/basics/fun_callstar.py @@ -3,10 +3,16 @@ def foo(a, b, c): print(a, b, c) +foo(*(), 1, 2, 3) +foo(*(1,), 2, 3) +foo(*(1, 2), 3) foo(*(1, 2, 3)) foo(1, *(2, 3)) foo(1, 2, *(3,)) foo(1, 2, 3, *()) +foo(*(1,), 2, *(3,)) +foo(*(1, 2), *(3,)) +foo(*(1,), *(2, 3)) # Another sequence type foo(1, 2, *[100]) @@ -29,10 +35,16 @@ class A: print(a, b, c) a = A() +a.foo(*(), 1, 2, 3) +a.foo(*(1,), 2, 3) +a.foo(*(1, 2), 3) a.foo(*(1, 2, 3)) a.foo(1, *(2, 3)) a.foo(1, 2, *(3,)) a.foo(1, 2, 3, *()) +a.foo(*(1,), 2, *(3,)) +a.foo(*(1, 2), *(3,)) +a.foo(*(1,), *(2, 3)) # Another sequence type a.foo(1, 2, *[100]) diff --git a/tests/basics/fun_callstardblstar.py b/tests/basics/fun_callstardblstar.py index f2fd29107e..aceb04a843 100644 --- a/tests/basics/fun_callstardblstar.py +++ b/tests/basics/fun_callstardblstar.py @@ -6,6 +6,11 @@ def f(a, b, c, d): f(*(1, 2), **{'c':3, 'd':4}) f(*(1, 2), **{['c', 'd'][i]:(3 + i) for i in range(2)}) +try: + eval("f(**{'a': 1}, *(2, 3, 4))") +except SyntaxError: + print("SyntaxError") + # test calling a method with *tuple and **dict class A: @@ -15,3 +20,8 @@ class A: a = A() a.f(*(1, 2), **{'c':3, 'd':4}) a.f(*(1, 2), **{['c', 'd'][i]:(3 + i) for i in range(2)}) + +try: + eval("a.f(**{'a': 1}, *(2, 3, 4))") +except SyntaxError: + print("SyntaxError") diff --git a/tests/basics/fun_kwvarargs.py b/tests/basics/fun_kwvarargs.py index bdc10fcf14..e9fd0720e9 100644 --- a/tests/basics/fun_kwvarargs.py +++ b/tests/basics/fun_kwvarargs.py @@ -23,3 +23,16 @@ def f4(*vargs, **kwargs): f4(*(1, 2)) f4(kw_arg=3) f4(*(1, 2), kw_arg=3) + + +# test evaluation order of arguments +def f5(*vargs, **kwargs): + print(vargs, kwargs) + + +def print_ret(x): + print(x) + return x + + +f5(*print_ret(["a", "b"]), kw_arg=print_ret(None)) diff --git a/tests/basics/python34.py b/tests/basics/python34.py index 609a8b6b84..36e25e20dd 100644 --- a/tests/basics/python34.py +++ b/tests/basics/python34.py @@ -6,26 +6,23 @@ except NameError: print("SKIP") raise SystemExit -# from basics/fun_kwvarargs.py -# test evaluation order of arguments (in 3.4 it's backwards, 3.5 it's fixed) -def f4(*vargs, **kwargs): - print(vargs, kwargs) + def print_ret(x): print(x) return x -f4(*print_ret(['a', 'b']), kw_arg=print_ret(None)) # test evaluation order of dictionary key/value pair (in 3.4 it's backwards) {print_ret(1):print_ret(2)} + # from basics/syntaxerror.py def test_syntax(code): try: exec(code) except SyntaxError: print("SyntaxError") -test_syntax("f(*a, *b)") # can't have multiple * (in 3.5 we can) -test_syntax("f(*a, b)") # can't have positional after * + + test_syntax("f(**a, b)") # can't have positional after ** test_syntax("() = []") # can't assign to empty tuple (in 3.6 we can) test_syntax("del ()") # can't delete empty tuple (in 3.6 we can) diff --git a/tests/basics/python34.py.exp b/tests/basics/python34.py.exp index 75f1c2c056..a56c1a50b6 100644 --- a/tests/basics/python34.py.exp +++ b/tests/basics/python34.py.exp @@ -1,13 +1,8 @@ -None -['a', 'b'] -('a', 'b') {'kw_arg': None} 2 1 SyntaxError SyntaxError SyntaxError -SyntaxError -SyntaxError 3.4 3 4 IndexError('foo',) diff --git a/tests/cpydiff/syntax_arg_unpacking.py b/tests/cpydiff/syntax_arg_unpacking.py new file mode 100644 index 0000000000..e54832ddb9 --- /dev/null +++ b/tests/cpydiff/syntax_arg_unpacking.py @@ -0,0 +1,23 @@ +""" +categories: Syntax +description: Argument unpacking does not work if the argument being unpacked is the nth or greater argument where n is the number of bits in an MP_SMALL_INT. +cause: The implementation uses an MP_SMALL_INT to flag args that need to be unpacked. +workaround: Use fewer arguments. +""" + + +def example(*args): + print(len(args)) + + +MORE = ["a", "b", "c"] + +# fmt: off +example( + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + *MORE, +) +# fmt: on From 3679a47eb064850406f473bc435c36d95017c16e Mon Sep 17 00:00:00 2001 From: David Lechner Date: Tue, 29 Mar 2022 12:11:42 -0500 Subject: [PATCH 280/619] py/runtime: Do not overallocate when len is known. This fixes overallocating an extra mp_obj_t when the length of *args and **args is known. Previously we were allocating 1 mp_obj_t for each n_args and n_kw plus the length of each *arg and **arg (if they are known). Since n_args includes *args and n_kw includes **args, this was allocating an extra mp_obj_t in addition to the length of these args when unpacked. To fix this, we just subtract 1 from the length to account for the 1 already implicitly allocated by n_args and n_kw. Signed-off-by: David Lechner --- py/runtime.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/py/runtime.c b/py/runtime.c index 90722ee18a..95990197d0 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -715,27 +715,29 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ uint args2_len = 0; // Try to get a hint for unpacked * args length - uint list_len = 0; + int list_len = 0; if (star_args != 0) { for (uint i = 0; i < n_args; i++) { if (star_args & (1 << i)) { mp_obj_t len = mp_obj_len_maybe(args[i]); if (len != MP_OBJ_NULL) { - list_len += mp_obj_get_int(len); + // -1 accounts for 1 of n_args occupied by this arg + list_len += mp_obj_get_int(len) - 1; } } } } // Try to get a hint for the size of the kw_dict - uint kw_dict_len = 0; + int kw_dict_len = 0; for (uint i = 0; i < n_kw; i++) { mp_obj_t key = args[n_args + i * 2]; mp_obj_t value = args[n_args + i * 2 + 1]; if (key == MP_OBJ_NULL && value != MP_OBJ_NULL && mp_obj_is_type(value, &mp_type_dict)) { - kw_dict_len += mp_obj_dict_len(value); + // -1 accounts for 1 of n_kw occupied by this arg + kw_dict_len += mp_obj_dict_len(value) - 1; } } From 9b74d71aa74d368e0679616173edb4be49e03684 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Tue, 29 Mar 2022 12:44:58 -0500 Subject: [PATCH 281/619] py/runtime: Drop new_alloc < 4 check. To reach this check, n_kw has to be >= 1 and therefore args2_alloc has to be >= 2. Therefore new_alloc will always be >= 4. So this check will never be true and can be removed. Signed-off-by: David Lechner --- py/runtime.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/py/runtime.c b/py/runtime.c index 95990197d0..7355817b69 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -860,9 +860,6 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ // expand size of args array if needed if (args2_len + 1 >= args2_alloc) { uint new_alloc = args2_alloc * 2; - if (new_alloc < 4) { - new_alloc = 4; - } args2 = mp_nonlocal_realloc(args2, args2_alloc * sizeof(mp_obj_t), new_alloc * sizeof(mp_obj_t)); args2_alloc = new_alloc; } From 47685180f02254a9d2dfd2eb9dafc24888daeb33 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Tue, 29 Mar 2022 13:15:15 -0500 Subject: [PATCH 282/619] tests/basics/fun_callstardblstar: Add coverage test. This fixes code coverage for the case where a *arg without __len__ is unpacked and uses exactly the amount of memory that was allocated for kw args. This triggers the code branch where the memory for the kw args gets reallocated since it was used already by the *arg unpacking. Signed-off-by: David Lechner --- tests/basics/fun_callstardblstar.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/basics/fun_callstardblstar.py b/tests/basics/fun_callstardblstar.py index aceb04a843..7db458b561 100644 --- a/tests/basics/fun_callstardblstar.py +++ b/tests/basics/fun_callstardblstar.py @@ -25,3 +25,12 @@ try: eval("a.f(**{'a': 1}, *(2, 3, 4))") except SyntaxError: print("SyntaxError") + + +# coverage test for arg allocation corner case + +def f2(*args, **kwargs): + print(len(args), len(kwargs)) + + +f2(*iter(range(4)), **{'a': 1}) From 2e3f2045f9a7511987e69a8be0d3d117502e4bb1 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Wed, 30 Mar 2022 10:47:06 -0500 Subject: [PATCH 283/619] py/runtime: Use size_t/ssize_t instead of uint/int. This replaces instances of uint with size_t and int with ssize_t in the mp_call_prepare_args_n_kw_var() function since all of the variables are used as array offsets. Also sort headers while we are touching this. Signed-off-by: David Lechner --- py/runtime.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/py/runtime.c b/py/runtime.c index 7355817b69..594e63082b 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -25,10 +25,11 @@ * THE SOFTWARE. */ +#include #include #include #include -#include +#include #include "py/parsenum.h" #include "py/compile.h" @@ -699,8 +700,8 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ if (have_self) { self = *args++; // may be MP_OBJ_NULL } - uint n_args = n_args_n_kw & 0xff; - uint n_kw = (n_args_n_kw >> 8) & 0xff; + size_t n_args = n_args_n_kw & 0xff; + size_t n_kw = (n_args_n_kw >> 8) & 0xff; mp_uint_t star_args = mp_obj_get_int_truncated(args[n_args + 2 * n_kw]); DEBUG_OP_printf("call method var (fun=%p, self=%p, n_args=%u, n_kw=%u, args=%p, map=%u)\n", fun, self, n_args, n_kw, args, star_args); @@ -711,14 +712,14 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ // The new args array mp_obj_t *args2; - uint args2_alloc; - uint args2_len = 0; + size_t args2_alloc; + size_t args2_len = 0; // Try to get a hint for unpacked * args length - int list_len = 0; + ssize_t list_len = 0; if (star_args != 0) { - for (uint i = 0; i < n_args; i++) { + for (size_t i = 0; i < n_args; i++) { if (star_args & (1 << i)) { mp_obj_t len = mp_obj_len_maybe(args[i]); if (len != MP_OBJ_NULL) { @@ -730,9 +731,9 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ } // Try to get a hint for the size of the kw_dict - int kw_dict_len = 0; + ssize_t kw_dict_len = 0; - for (uint i = 0; i < n_kw; i++) { + for (size_t i = 0; i < n_kw; i++) { mp_obj_t key = args[n_args + i * 2]; mp_obj_t value = args[n_args + i * 2 + 1]; if (key == MP_OBJ_NULL && value != MP_OBJ_NULL && mp_obj_is_type(value, &mp_type_dict)) { @@ -770,7 +771,7 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ args2[args2_len++] = self; } - for (uint i = 0; i < n_args; i++) { + for (size_t i = 0; i < n_args; i++) { mp_obj_t arg = args[i]; if (star_args & (1 << i)) { // star arg @@ -811,18 +812,18 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ } // The size of the args2 array now is the number of positional args. - uint pos_args_len = args2_len; + size_t pos_args_len = args2_len; // ensure there is still enough room for kw args if (args2_len + 2 * (n_kw + kw_dict_len) > args2_alloc) { - uint new_alloc = args2_len + 2 * (n_kw + kw_dict_len); + size_t new_alloc = args2_len + 2 * (n_kw + kw_dict_len); args2 = mp_nonlocal_realloc(args2, args2_alloc * sizeof(mp_obj_t), new_alloc * sizeof(mp_obj_t)); args2_alloc = new_alloc; } // Copy the kw args. - for (uint i = 0; i < n_kw; i++) { + for (size_t i = 0; i < n_kw; i++) { mp_obj_t kw_key = args[n_args + i * 2]; mp_obj_t kw_value = args[n_args + i * 2 + 1]; if (kw_key == MP_OBJ_NULL) { @@ -859,7 +860,7 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ while ((key = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { // expand size of args array if needed if (args2_len + 1 >= args2_alloc) { - uint new_alloc = args2_alloc * 2; + size_t new_alloc = args2_alloc * 2; args2 = mp_nonlocal_realloc(args2, args2_alloc * sizeof(mp_obj_t), new_alloc * sizeof(mp_obj_t)); args2_alloc = new_alloc; } From e3de723e2d955ca89c8b06bb2c4ae86724aad77b Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 31 Mar 2022 23:59:10 +1100 Subject: [PATCH 284/619] py/emitbc: Assert that a small int fits its encoding when emitting one. Signed-off-by: Damien George --- py/emitbc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/py/emitbc.c b/py/emitbc.c index 21ec121911..a9d5b3799e 100644 --- a/py/emitbc.c +++ b/py/emitbc.c @@ -32,6 +32,7 @@ #include #include "py/mpstate.h" +#include "py/smallint.h" #include "py/emit.h" #include "py/bc0.h" @@ -471,6 +472,7 @@ void mp_emit_bc_load_const_tok(emit_t *emit, mp_token_kind_t tok) { } void mp_emit_bc_load_const_small_int(emit_t *emit, mp_int_t arg) { + assert(MP_SMALL_INT_FITS(arg)); if (-MP_BC_LOAD_CONST_SMALL_INT_MULTI_EXCESS <= arg && arg < MP_BC_LOAD_CONST_SMALL_INT_MULTI_NUM - MP_BC_LOAD_CONST_SMALL_INT_MULTI_EXCESS) { emit_write_bytecode_byte(emit, 1, From bd556b69960cafc97353e736f825eca5c00b0c29 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 1 Apr 2022 00:00:58 +1100 Subject: [PATCH 285/619] py: Fix compiling and decoding of *args at large arg positions. There were two issues with the existing code: 1. "1 << i" is computed as a 32-bit number so would overflow when executed on 64-bit machines (when mp_uint_t is 64-bit). This meant that *args beyond 32 positions would not be handled correctly. 2. star_args must fit as a positive small int so that it is encoded correctly in the emitted code. MP_SMALL_INT_BITS is too big because it overflows a small int by 1 bit. MP_SMALL_INT_BITS - 1 does not work because it produces a signed small int which is then sign extended when extracted (even by mp_obj_get_int_truncated), and this sign extension means that any position arg after *args is also treated as a star-arg. So the maximum bit position is MP_SMALL_INT_BITS - 2. This means that MP_OBJ_SMALL_INT_VALUE() can be used instead of mp_obj_get_int_truncated() to get the value of star_args. These issues are fixed by this commit, and a test added. Signed-off-by: Damien George --- py/compile.c | 6 ++--- py/runtime.c | 6 ++--- tests/stress/fun_call_limit.py | 36 ++++++++++++++++++++++++++++++ tests/stress/fun_call_limit.py.exp | 2 ++ 4 files changed, 44 insertions(+), 6 deletions(-) create mode 100644 tests/stress/fun_call_limit.py create mode 100644 tests/stress/fun_call_limit.py.exp diff --git a/py/compile.c b/py/compile.c index fc11062705..e4ead7f1ac 100644 --- a/py/compile.c +++ b/py/compile.c @@ -2408,9 +2408,9 @@ STATIC void compile_trailer_paren_helper(compiler_t *comp, mp_parse_node_t pn_ar return; } #if MICROPY_DYNAMIC_COMPILER - if (i > mp_dynamic_compiler.small_int_bits) + if (i >= (size_t)mp_dynamic_compiler.small_int_bits - 1) #else - if (i > MP_SMALL_INT_BITS) + if (i >= MP_SMALL_INT_BITS - 1) #endif { // If there are not enough bits in a small int to fit the flag, then we consider @@ -2419,7 +2419,7 @@ STATIC void compile_trailer_paren_helper(compiler_t *comp, mp_parse_node_t pn_ar return; } star_flags |= MP_EMIT_STAR_FLAG_SINGLE; - star_args |= 1 << i; + star_args |= (mp_uint_t)1 << i; compile_node(comp, pns_arg->nodes[0]); n_positional++; } else if (MP_PARSE_NODE_STRUCT_KIND(pns_arg) == PN_arglist_dbl_star) { diff --git a/py/runtime.c b/py/runtime.c index 594e63082b..ad066acb16 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -702,7 +702,7 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ } size_t n_args = n_args_n_kw & 0xff; size_t n_kw = (n_args_n_kw >> 8) & 0xff; - mp_uint_t star_args = mp_obj_get_int_truncated(args[n_args + 2 * n_kw]); + mp_uint_t star_args = MP_OBJ_SMALL_INT_VALUE(args[n_args + 2 * n_kw]); DEBUG_OP_printf("call method var (fun=%p, self=%p, n_args=%u, n_kw=%u, args=%p, map=%u)\n", fun, self, n_args, n_kw, args, star_args); @@ -720,7 +720,7 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ if (star_args != 0) { for (size_t i = 0; i < n_args; i++) { - if (star_args & (1 << i)) { + if ((star_args >> i) & 1) { mp_obj_t len = mp_obj_len_maybe(args[i]); if (len != MP_OBJ_NULL) { // -1 accounts for 1 of n_args occupied by this arg @@ -773,7 +773,7 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ for (size_t i = 0; i < n_args; i++) { mp_obj_t arg = args[i]; - if (star_args & (1 << i)) { + if ((star_args >> i) & 1) { // star arg if (mp_obj_is_type(arg, &mp_type_tuple) || mp_obj_is_type(arg, &mp_type_list)) { // optimise the case of a tuple and list diff --git a/tests/stress/fun_call_limit.py b/tests/stress/fun_call_limit.py new file mode 100644 index 0000000000..b802aadd55 --- /dev/null +++ b/tests/stress/fun_call_limit.py @@ -0,0 +1,36 @@ +# Test the limit of the number of arguments to a function call. +# This currently tests the case of *args after many positional args. + + +def f(*args): + return len(args) + + +def test(n): + pos_args = ",".join(str(i) for i in range(n)) + s = "f({}, *(100, 101), 102, 103)".format(pos_args) + try: + return eval(s) + except SyntaxError: + return "SyntaxError" + + +# If the port has at least 32-bits then this test should pass. +print(test(29)) + +# This test should fail on all ports (overflows a small int). +print(test(70)) + +# Check that there is a correct transition to the limit of too many args before *args. +reached_limit = False +for i in range(30, 70): + result = test(i) + if reached_limit: + if result != "SyntaxError": + print("FAIL") + else: + if result == "SyntaxError": + reached_limit = True + else: + if result != i + 4: + print("FAIL") diff --git a/tests/stress/fun_call_limit.py.exp b/tests/stress/fun_call_limit.py.exp new file mode 100644 index 0000000000..53d2b28043 --- /dev/null +++ b/tests/stress/fun_call_limit.py.exp @@ -0,0 +1,2 @@ +33 +SyntaxError From 40f5c743db7f16a0b898fe1697c626e53aa6500d Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 1 Apr 2022 00:21:03 +1100 Subject: [PATCH 286/619] py/runtime: Remove unnecessary check for kw_value == MP_OBJ_NULL. The values are always real objects, only the key can be MP_OBJ_NULL to indicate a **kwargs entry. Signed-off-by: Damien George --- py/runtime.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/py/runtime.c b/py/runtime.c index ad066acb16..2a07df6642 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -828,9 +828,7 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ mp_obj_t kw_value = args[n_args + i * 2 + 1]; if (kw_key == MP_OBJ_NULL) { // double-star args - if (kw_value == MP_OBJ_NULL) { - // pass - } else if (mp_obj_is_type(kw_value, &mp_type_dict)) { + if (mp_obj_is_type(kw_value, &mp_type_dict)) { // dictionary mp_map_t *map = mp_obj_dict_get_map(kw_value); // should have enough, since kw_dict_len is in this case hinted correctly above From 1dbf393962428bdab1362c77f59745f28d2cdd53 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 1 Apr 2022 00:21:58 +1100 Subject: [PATCH 287/619] tests/basics/fun_callstardblstar: Add test for large arg allocation. Signed-off-by: Damien George --- tests/basics/fun_callstardblstar.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/basics/fun_callstardblstar.py b/tests/basics/fun_callstardblstar.py index 7db458b561..f395df3333 100644 --- a/tests/basics/fun_callstardblstar.py +++ b/tests/basics/fun_callstardblstar.py @@ -34,3 +34,6 @@ def f2(*args, **kwargs): f2(*iter(range(4)), **{'a': 1}) + +# case where *args is not a tuple/list and takes up most of the memory allocated for **kwargs +f2(*iter(range(100)), **{str(i): i for i in range(100)}) From 8baf05af8c8011d586ce767da70b4c6f47f55d6c Mon Sep 17 00:00:00 2001 From: Daniel Jour Date: Tue, 29 Mar 2022 13:42:19 +0200 Subject: [PATCH 288/619] py/makeqstrdefs: Cleanup and extend source file classification. - The classification of source files in makeqstrdefs.py has been moved into functions to consolidate the logic for that classification into a single place. - Classification of source files (into C or C++ or "other" files) is based on the filename extension. - For C++ there are many more common filename extensions than just ".cpp"; see "Options Controlling the Kind of Output" in man gcc for example. All common extensions for C++ source files which need preprocessing have been added. --- py/makeqstrdefs.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/py/makeqstrdefs.py b/py/makeqstrdefs.py index 187a9aeeaa..1a63bd39a9 100644 --- a/py/makeqstrdefs.py +++ b/py/makeqstrdefs.py @@ -22,6 +22,14 @@ _MODE_QSTR = "qstr" _MODE_COMPRESS = "compress" +def is_c_source(fname): + return os.path.splitext(fname)[1] in [".c"] + + +def is_cxx_source(fname): + return os.path.splitext(fname)[1] in [".cc", ".cp", ".cxx", ".cpp", ".CPP", ".c++", ".C"] + + def preprocess(): if any(src in args.dependencies for src in args.changed_sources): sources = args.sources @@ -32,9 +40,9 @@ def preprocess(): csources = [] cxxsources = [] for source in sources: - if source.endswith(".cpp"): + if is_cxx_source(source): cxxsources.append(source) - elif source.endswith(".c"): + elif is_c_source(source): csources.append(source) try: os.makedirs(os.path.dirname(args.output[0])) @@ -87,7 +95,7 @@ def process_file(f): m = re_line.match(line) assert m is not None fname = m.group(1) - if os.path.splitext(fname)[1] not in [".c", ".cpp"]: + if not is_c_source(fname) and not is_cxx_source(fname): continue if fname != last_fname: write_out(last_fname, output) From ff287d085f95ab0ca3fdf67ee9d8cce6f046b01e Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Mon, 17 Jan 2022 00:12:01 +0200 Subject: [PATCH 289/619] stm32/pyb_can: Enable CAN FD frame support and BRS. - Enable CAN FD frame support and BRS. - Optimize the message RAM usage per FDCAN instance. - Document the usage and different sections of the Message RAM. --- ports/stm32/fdcan.c | 94 +++++++++++++++++------------ ports/stm32/pyb_can.c | 133 +++++++++++++++++++++++++++++------------- 2 files changed, 149 insertions(+), 78 deletions(-) diff --git a/ports/stm32/fdcan.c b/ports/stm32/fdcan.c index 8465b806ae..63c6c31238 100644 --- a/ports/stm32/fdcan.c +++ b/ports/stm32/fdcan.c @@ -56,14 +56,19 @@ #define FDCAN_IT_GROUP_RX_FIFO1 (FDCAN_ILS_RF1NL | FDCAN_ILS_RF1FL | FDCAN_ILS_RF1LL) #endif +// The dedicated Message RAM should be 2560 words, but the way it's defined in stm32h7xx_hal_fdcan.c +// as (SRAMCAN_BASE + FDCAN_MESSAGE_RAM_SIZE - 0x4U) limits the usable number of words to 2559 words. +#define FDCAN_MESSAGE_RAM_SIZE (2560 - 1) + // also defined in _hal_fdcan.c, but not able to declare extern and reach the variable -static const uint8_t DLCtoBytes[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64}; +const uint8_t DLCtoBytes[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64}; bool can_init(pyb_can_obj_t *can_obj, uint32_t mode, uint32_t prescaler, uint32_t sjw, uint32_t bs1, uint32_t bs2, bool auto_restart) { (void)auto_restart; FDCAN_InitTypeDef *init = &can_obj->can.Init; - init->FrameFormat = FDCAN_FRAME_CLASSIC; + // Configure FDCAN with FD frame and BRS support. + init->FrameFormat = FDCAN_FRAME_FD_BRS; init->Mode = mode; init->NominalPrescaler = prescaler; // tq = NominalPrescaler x (1/fdcan_ker_ck) @@ -81,46 +86,58 @@ bool can_init(pyb_can_obj_t *can_obj, uint32_t mode, uint32_t prescaler, uint32_ init->DataSyncJumpWidth = 1; init->DataTimeSeg1 = 1; init->DataTimeSeg2 = 1; - #endif - - #if defined(STM32H7) - // variable used to specify RAM address in HAL, only for H7, G4 uses defined offset address in HAL - // The Message RAM is shared between CAN1 and CAN2. Setting the offset to half - // the Message RAM for the second CAN and using half the resources for each CAN. + init->StdFiltersNbr = 28; // /2 ? if FDCAN2 is used !!? + init->ExtFiltersNbr = 0; // Not used + init->TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION; + #elif defined(STM32H7) + // The dedicated FDCAN RAM is 2560 32-bit words and shared between the FDCAN instances. + // To support 2 FDCAN instances simultaneously, the Message RAM is divided in half by + // setting the second FDCAN memory offset to half the RAM size. With this configuration, + // the maximum words per FDCAN instance is 1280 32-bit words. if (can_obj->can_id == PYB_CAN_1) { init->MessageRAMOffset = 0; } else { - init->MessageRAMOffset = 2560 / 2; + init->MessageRAMOffset = FDCAN_MESSAGE_RAM_SIZE / 2; } - #endif + // An element stored in the Message RAM contains an identifier, DLC, control bits, the + // data field and the specific transmission or reception bits field for control. + // The following code configures the different Message RAM sections per FDCAN instance. - #if defined(STM32G4) + // The RAM filtering section is configured for 64 x 1 word elements for 11-bit standard + // identifiers, and 31 x 2 words elements for 29-bit extended identifiers. + // The total number of words reserved for the filtering per FDCAN instance is 126 words. + init->StdFiltersNbr = 64; + // Note extended identifiers are Not used in pyb_can.c and Not handled correctly. + // Disable the extended identifiers filters for now until this is fixed properly. + init->ExtFiltersNbr = 0 /*31*/; - init->StdFiltersNbr = 28; // /2 ? if FDCAN2 is used !!? - init->ExtFiltersNbr = 0; // Not used - - #elif defined(STM32H7) - - init->StdFiltersNbr = 64; // 128 / 2 - init->ExtFiltersNbr = 0; // Not used - - init->TxEventsNbr = 16; // 32 / 2 - init->RxBuffersNbr = 32; // 64 / 2 - init->TxBuffersNbr = 16; // 32 / 2 - - init->RxFifo0ElmtsNbr = 64; // 128 / 2 - init->RxFifo0ElmtSize = FDCAN_DATA_BYTES_8; - - init->RxFifo1ElmtsNbr = 64; // 128 / 2 - init->RxFifo1ElmtSize = FDCAN_DATA_BYTES_8; - - init->TxFifoQueueElmtsNbr = 16; // Tx fifo elements - init->TxElmtSize = FDCAN_DATA_BYTES_8; - - #endif + // The Tx event FIFO is used to store the message ID and the timestamp of successfully + // transmitted elements. The Tx event FIFO can store a maximum of 32 (2 words) elements. + // NOTE: Events are stored in Tx event FIFO only if tx_msg.TxEventFifoControl is enabled. + init->TxEventsNbr = 0; + // Transmission section is configured in FIFO mode operation, with no dedicated Tx buffers. + // The Tx FIFO can store a maximum of 32 elements (or 576 words), each element is 18 words + // long (to support a maximum of 64 bytes data field): + // 2 words header + 16 words data field (to support up to 64 bytes of data). + // The total number of words reserved for the Tx FIFO per FDCAN instance is 288 words. + init->TxBuffersNbr = 0; + init->TxFifoQueueElmtsNbr = 16; + init->TxElmtSize = FDCAN_DATA_BYTES_64; init->TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION; + // Reception section is configured to use Rx FIFO 0 and Rx FIFO1, with no dedicated Rx buffers. + // Each Rx FIFO can store a maximum of 64 elements (1152 words), each element is 18 words + // long (to support a maximum of 64 bytes data field): + // 2 words header + 16 words data field (to support up to 64 bytes of data). + // The total number of words reserved for the Rx FIFOs per FDCAN instance is 864 words. + init->RxBuffersNbr = 0; + init->RxFifo0ElmtsNbr = 24; + init->RxFifo0ElmtSize = FDCAN_DATA_BYTES_64; + init->RxFifo1ElmtsNbr = 24; + init->RxFifo1ElmtSize = FDCAN_DATA_BYTES_64; + #endif + FDCAN_GlobalTypeDef *CANx = NULL; const pin_obj_t *pins[2]; @@ -159,7 +176,10 @@ bool can_init(pyb_can_obj_t *can_obj, uint32_t mode, uint32_t prescaler, uint32_ // init CANx can_obj->can.Instance = CANx; - HAL_FDCAN_Init(&can_obj->can); + // catch bad configuration errors. + if (HAL_FDCAN_Init(&can_obj->can) != HAL_OK) { + return false; + } // Disable acceptance of non-matching frames (enabled by default) HAL_FDCAN_ConfigGlobalFilter(&can_obj->can, FDCAN_REJECT, FDCAN_REJECT, DISABLE, DISABLE); @@ -168,7 +188,7 @@ bool can_init(pyb_can_obj_t *can_obj, uint32_t mode, uint32_t prescaler, uint32_ HAL_FDCAN_Start(&can_obj->can); // Reset all filters - for (int f = 0; f < 64; ++f) { + for (int f = 0; f < init->StdFiltersNbr; ++f) { can_clearfilter(can_obj, f, 0); } @@ -299,10 +319,12 @@ int can_receive(FDCAN_HandleTypeDef *can, int fifo, FDCAN_RxHeaderTypeDef *hdr, hdr->FDFormat = *address & FDCAN_ELEMENT_MASK_FDF; hdr->FilterIndex = (*address & FDCAN_ELEMENT_MASK_FIDX) >> 24; hdr->IsFilterMatchingFrame = (*address++ & FDCAN_ELEMENT_MASK_ANMF) >> 31; + // Convert DLC to Bytes. + hdr->DataLength = DLCtoBytes[hdr->DataLength]; // Copy data uint8_t *pdata = (uint8_t *)address; - for (uint32_t i = 0; i < DLCtoBytes[hdr->DataLength]; ++i) { + for (uint32_t i = 0; i < hdr->DataLength; ++i) { *data++ = *pdata++; } diff --git a/ports/stm32/pyb_can.c b/ports/stm32/pyb_can.c index 8007fd9e3c..0c5c60255d 100644 --- a/ports/stm32/pyb_can.c +++ b/ports/stm32/pyb_can.c @@ -42,6 +42,7 @@ #if MICROPY_HW_ENABLE_FDCAN #define CAN_MAX_FILTER (64) +#define CAN_MAX_DATA_FRAME (64) #define CAN_FIFO0 FDCAN_RX_FIFO0 #define CAN_FIFO1 FDCAN_RX_FIFO1 @@ -89,10 +90,11 @@ // Both banks start at 0 STATIC uint8_t can2_start_bank = 0; - +extern const uint8_t DLCtoBytes[16]; #else #define CAN_MAX_FILTER (28) +#define CAN_MAX_DATA_FRAME (8) #define CAN_DEFAULT_PRESCALER (100) #define CAN_DEFAULT_SJW (1) @@ -180,19 +182,50 @@ STATIC uint32_t pyb_can_get_source_freq() { return can_kern_clk; } +STATIC void pyb_can_get_bit_timing(mp_uint_t baudrate, mp_uint_t sample_point, + mp_int_t *bs1_out, mp_int_t *bs2_out, mp_int_t *prescaler_out) { + uint32_t can_kern_clk = pyb_can_get_source_freq(); + + // The following max values work on all MCUs for classical CAN. + for (int brp = 1; brp < 512; brp++) { + for (int bs1 = 1; bs1 < 16; bs1++) { + for (int bs2 = 1; bs2 < 8; bs2++) { + if ((baudrate == (can_kern_clk / (brp * (1 + bs1 + bs2)))) && + ((sample_point * 10) == (((1 + bs1) * 1000) / (1 + bs1 + bs2)))) { + *bs1_out = bs1; + *bs2_out = bs2; + *prescaler_out = brp; + return; + } + } + } + } + + mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("couldn't match baudrate and sample point")); +} + // init(mode, extframe=False, prescaler=100, *, sjw=1, bs1=6, bs2=8) STATIC mp_obj_t pyb_can_init_helper(pyb_can_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_mode, ARG_extframe, ARG_prescaler, ARG_sjw, ARG_bs1, ARG_bs2, ARG_auto_restart, ARG_baudrate, ARG_sample_point }; + enum { ARG_mode, ARG_extframe, ARG_prescaler, ARG_sjw, ARG_bs1, ARG_bs2, ARG_auto_restart, ARG_baudrate, ARG_sample_point, + ARG_brs_prescaler, ARG_brs_sjw, ARG_brs_bs1, ARG_brs_bs2, ARG_brs_baudrate, ARG_brs_sample_point }; static const mp_arg_t allowed_args[] = { - { MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = CAN_MODE_NORMAL} }, - { MP_QSTR_extframe, MP_ARG_BOOL, {.u_bool = false} }, - { MP_QSTR_prescaler, MP_ARG_INT, {.u_int = CAN_DEFAULT_PRESCALER} }, - { MP_QSTR_sjw, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = CAN_DEFAULT_SJW} }, - { MP_QSTR_bs1, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = CAN_DEFAULT_BS1} }, - { MP_QSTR_bs2, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = CAN_DEFAULT_BS2} }, - { MP_QSTR_auto_restart, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, - { MP_QSTR_baudrate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, - { MP_QSTR_sample_point, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 75} }, // 75% sampling point + { MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = CAN_MODE_NORMAL} }, + { MP_QSTR_extframe, MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_prescaler, MP_ARG_INT, {.u_int = CAN_DEFAULT_PRESCALER} }, + { MP_QSTR_sjw, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = CAN_DEFAULT_SJW} }, + { MP_QSTR_bs1, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = CAN_DEFAULT_BS1} }, + { MP_QSTR_bs2, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = CAN_DEFAULT_BS2} }, + { MP_QSTR_auto_restart, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_baudrate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_sample_point, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 75} }, // 75% sampling point + #if MICROPY_HW_ENABLE_FDCAN + { MP_QSTR_brs_prescaler, MP_ARG_INT, {.u_int = CAN_DEFAULT_PRESCALER} }, + { MP_QSTR_brs_sjw, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = CAN_DEFAULT_SJW} }, + { MP_QSTR_brs_bs1, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = CAN_DEFAULT_BS1} }, + { MP_QSTR_brs_bs2, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = CAN_DEFAULT_BS2} }, + { MP_QSTR_brs_baudrate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_brs_sample_point, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} } + #endif }; // parse args @@ -206,34 +239,30 @@ STATIC mp_obj_t pyb_can_init_helper(pyb_can_obj_t *self, size_t n_args, const mp // Calculate CAN bit timing from baudrate if provided if (args[ARG_baudrate].u_int != 0) { - uint32_t baudrate = args[ARG_baudrate].u_int; - uint32_t sampoint = args[ARG_sample_point].u_int; - uint32_t can_kern_clk = pyb_can_get_source_freq(); - bool timing_found = false; - - // The following max values work on all MCUs for classical CAN. - for (int brp = 1; brp < 512 && !timing_found; brp++) { - for (int bs1 = 1; bs1 < 16 && !timing_found; bs1++) { - for (int bs2 = 1; bs2 < 8 && !timing_found; bs2++) { - if ((baudrate == (can_kern_clk / (brp * (1 + bs1 + bs2)))) && - ((sampoint * 10) == (((1 + bs1) * 1000) / (1 + bs1 + bs2)))) { - args[ARG_bs1].u_int = bs1; - args[ARG_bs2].u_int = bs2; - args[ARG_prescaler].u_int = brp; - timing_found = true; - } - } - } - } - if (!timing_found) { - mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("couldn't match baudrate and sample point")); - } + pyb_can_get_bit_timing(args[ARG_baudrate].u_int, args[ARG_sample_point].u_int, + &args[ARG_bs1].u_int, &args[ARG_bs2].u_int, &args[ARG_prescaler].u_int); } - // init CAN (if it fails, it's because the port doesn't exist) + #if MICROPY_HW_ENABLE_FDCAN + // If no sample point is provided for data bit timing, use the nominal sample point. + if (args[ARG_brs_sample_point].u_int == 0) { + args[ARG_brs_sample_point].u_int = args[ARG_sample_point].u_int; + } + // Calculate BRS CAN bit timing from baudrate if provided + if (args[ARG_brs_baudrate].u_int != 0) { + pyb_can_get_bit_timing(args[ARG_brs_baudrate].u_int, args[ARG_brs_sample_point].u_int, + &args[ARG_brs_bs1].u_int, &args[ARG_brs_bs2].u_int, &args[ARG_brs_prescaler].u_int); + } + // Set BRS bit timings. + self->can.Init.DataPrescaler = args[ARG_brs_prescaler].u_int; + self->can.Init.DataSyncJumpWidth = args[ARG_brs_sjw].u_int; + self->can.Init.DataTimeSeg1 = args[ARG_bs1].u_int; // DataTimeSeg1 = Propagation_segment + Phase_segment_1 + self->can.Init.DataTimeSeg2 = args[ARG_bs2].u_int; + #endif + if (!can_init(self, args[ARG_mode].u_int, args[ARG_prescaler].u_int, args[ARG_sjw].u_int, args[ARG_bs1].u_int, args[ARG_bs2].u_int, args[ARG_auto_restart].u_bool)) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("CAN(%d) doesn't exist"), self->can_id); + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("CAN(%d) init failure"), self->can_id); } return mp_const_none; @@ -450,12 +479,16 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(pyb_can_any_obj, pyb_can_any); // send(send, addr, *, timeout=5000) STATIC mp_obj_t pyb_can_send(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_data, ARG_id, ARG_timeout, ARG_rtr }; + enum { ARG_data, ARG_id, ARG_timeout, ARG_rtr, ARG_fdf, ARG_brs }; static const mp_arg_t allowed_args[] = { { MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_rtr, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + #if MICROPY_HW_ENABLE_FDCAN + { MP_QSTR_fdf, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_brs, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + #endif }; // parse args @@ -468,7 +501,7 @@ STATIC mp_obj_t pyb_can_send(size_t n_args, const mp_obj_t *pos_args, mp_map_t * uint8_t data[1]; pyb_buf_get_for_send(args[ARG_data].u_obj, &bufinfo, data); - if (bufinfo.len > 8) { + if (bufinfo.len > CAN_MAX_DATA_FRAME) { mp_raise_ValueError(MP_ERROR_TEXT("CAN data field too long")); } @@ -476,13 +509,12 @@ STATIC mp_obj_t pyb_can_send(size_t n_args, const mp_obj_t *pos_args, mp_map_t * CanTxMsgTypeDef tx_msg; #if MICROPY_HW_ENABLE_FDCAN - uint8_t tx_data[8]; + uint8_t tx_data[CAN_MAX_DATA_FRAME]; + memset(tx_data, 0, sizeof(tx_data)); + tx_msg.MessageMarker = 0; tx_msg.ErrorStateIndicator = FDCAN_ESI_ACTIVE; - tx_msg.BitRateSwitch = FDCAN_BRS_OFF; - tx_msg.FDFormat = FDCAN_CLASSIC_CAN; tx_msg.TxEventFifoControl = FDCAN_NO_TX_EVENTS; - tx_msg.DataLength = (bufinfo.len << 16); // TODO DLC for len > 8 if (self->extframe) { tx_msg.Identifier = args[ARG_id].u_int & 0x1FFFFFFF; @@ -496,6 +528,23 @@ STATIC mp_obj_t pyb_can_send(size_t n_args, const mp_obj_t *pos_args, mp_map_t * } else { tx_msg.TxFrameType = FDCAN_REMOTE_FRAME; } + if (args[ARG_fdf].u_bool == false) { + tx_msg.FDFormat = FDCAN_CLASSIC_CAN; + } else { + tx_msg.FDFormat = FDCAN_FD_CAN; + } + if (args[ARG_brs].u_bool == false) { + tx_msg.BitRateSwitch = FDCAN_BRS_OFF; + } else { + tx_msg.BitRateSwitch = FDCAN_BRS_ON; + } + // Roundup DataLength to next DLC size and encode to DLC. + for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(DLCtoBytes); i++) { + if (bufinfo.len <= DLCtoBytes[i]) { + tx_msg.DataLength = (i << 16); + break; + } + } #else tx_msg.DLC = bufinfo.len; uint8_t *tx_data = tx_msg.Data; // Data is uint32_t but holds only 1 byte @@ -565,7 +614,7 @@ STATIC mp_obj_t pyb_can_recv(size_t n_args, const mp_obj_t *pos_args, mp_map_t * // receive the data CanRxMsgTypeDef rx_msg; #if MICROPY_HW_ENABLE_FDCAN - uint8_t rx_data[8]; + uint8_t rx_data[CAN_MAX_DATA_FRAME]; #else uint8_t *rx_data = rx_msg.Data; #endif From 3320ec44ed7d1ba36e59a065f6941cd46608acc4 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Mon, 17 Jan 2022 20:51:31 +0200 Subject: [PATCH 290/619] stm32/pyb_can: Add support for CAN FD extended frame ID. --- ports/stm32/fdcan.c | 18 ++++++++++------ ports/stm32/pyb_can.c | 50 +++++++++++++++++++++++++++++++------------ 2 files changed, 48 insertions(+), 20 deletions(-) diff --git a/ports/stm32/fdcan.c b/ports/stm32/fdcan.c index 63c6c31238..bcf3e8d6a3 100644 --- a/ports/stm32/fdcan.c +++ b/ports/stm32/fdcan.c @@ -107,9 +107,7 @@ bool can_init(pyb_can_obj_t *can_obj, uint32_t mode, uint32_t prescaler, uint32_ // identifiers, and 31 x 2 words elements for 29-bit extended identifiers. // The total number of words reserved for the filtering per FDCAN instance is 126 words. init->StdFiltersNbr = 64; - // Note extended identifiers are Not used in pyb_can.c and Not handled correctly. - // Disable the extended identifiers filters for now until this is fixed properly. - init->ExtFiltersNbr = 0 /*31*/; + init->ExtFiltersNbr = 31; // The Tx event FIFO is used to store the message ID and the timestamp of successfully // transmitted elements. The Tx event FIFO can store a maximum of 32 (2 words) elements. @@ -189,7 +187,11 @@ bool can_init(pyb_can_obj_t *can_obj, uint32_t mode, uint32_t prescaler, uint32_ // Reset all filters for (int f = 0; f < init->StdFiltersNbr; ++f) { - can_clearfilter(can_obj, f, 0); + can_clearfilter(can_obj, f, false); + } + + for (int f = 0; f < init->ExtFiltersNbr; ++f) { + can_clearfilter(can_obj, f, true); } can_obj->is_enabled = true; @@ -250,10 +252,14 @@ void can_deinit(pyb_can_obj_t *self) { } } -void can_clearfilter(pyb_can_obj_t *self, uint32_t f, uint8_t bank) { +void can_clearfilter(pyb_can_obj_t *self, uint32_t f, uint8_t extid) { if (self && self->can.Instance) { FDCAN_FilterTypeDef filter = {0}; - filter.IdType = FDCAN_STANDARD_ID; + if (extid == 1) { + filter.IdType = FDCAN_EXTENDED_ID; + } else { + filter.IdType = FDCAN_STANDARD_ID; + } filter.FilterIndex = f; filter.FilterConfig = FDCAN_FILTER_DISABLE; HAL_FDCAN_ConfigFilter(&self->can, &filter); diff --git a/ports/stm32/pyb_can.c b/ports/stm32/pyb_can.c index 0c5c60255d..323b04d692 100644 --- a/ports/stm32/pyb_can.c +++ b/ports/stm32/pyb_can.c @@ -41,7 +41,6 @@ #if MICROPY_HW_ENABLE_FDCAN -#define CAN_MAX_FILTER (64) #define CAN_MAX_DATA_FRAME (64) #define CAN_FIFO0 FDCAN_RX_FIFO0 @@ -87,9 +86,6 @@ #define __HAL_CAN_DISABLE_IT __HAL_FDCAN_DISABLE_IT #define __HAL_CAN_CLEAR_FLAG __HAL_FDCAN_CLEAR_FLAG #define __HAL_CAN_MSG_PENDING HAL_FDCAN_GetRxFifoFillLevel - -// Both banks start at 0 -STATIC uint8_t can2_start_bank = 0; extern const uint8_t DLCtoBytes[16]; #else @@ -715,16 +711,29 @@ STATIC mp_obj_t pyb_can_recv(size_t n_args, const mp_obj_t *pos_args, mp_map_t * STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_can_recv_obj, 1, pyb_can_recv); // initfilterbanks(n) -STATIC mp_obj_t pyb_can_initfilterbanks(mp_obj_t self, mp_obj_t bank_in) { +STATIC mp_obj_t pyb_can_initfilterbanks(mp_obj_t self_in, mp_obj_t bank_in) { + pyb_can_obj_t *self = MP_OBJ_TO_PTR(self_in); #if MICROPY_HW_ENABLE_FDCAN - can2_start_bank = 0; - #else - can2_start_bank = mp_obj_get_int(bank_in); - #endif - - for (int f = 0; f < CAN_MAX_FILTER; f++) { - can_clearfilter(MP_OBJ_TO_PTR(self), f, can2_start_bank); + (void)self; + #if 0 + FDCAN_InitTypeDef *init = &self->can.Init; + // Clear standard ID filters. + for (int f = 0; f < init->StdFiltersNbr; ++f) { + can_clearfilter(self, f, false); } + // Clear extended ID filters. + for (int f = 0; f < init->ExtFiltersNbr; ++f) { + can_clearfilter(self, f, true); + } + #endif + #else + // NOTE: For classic CAN, this function calls HAL_CAN_ConfigFilter(NULL, &filter); + // if CAN3 is defined, ConfigFilter() will dereference a NULL pointer. + can2_start_bank = mp_obj_get_int(bank_in); + for (int f = 0; f < CAN_MAX_FILTER; f++) { + can_clearfilter(self, f, can2_start_bank); + } + #endif return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_2(pyb_can_initfilterbanks_fun_obj, pyb_can_initfilterbanks); @@ -733,10 +742,14 @@ STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(pyb_can_initfilterbanks_obj, MP_ROM_PTR(& STATIC mp_obj_t pyb_can_clearfilter(mp_obj_t self_in, mp_obj_t bank_in) { pyb_can_obj_t *self = MP_OBJ_TO_PTR(self_in); mp_int_t f = mp_obj_get_int(bank_in); + #if MICROPY_HW_ENABLE_FDCAN + can_clearfilter(self, f, self->extframe); + #else if (self->can_id == 2) { f += can2_start_bank; } can_clearfilter(self, f, can2_start_bank); + #endif return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_2(pyb_can_clearfilter_obj, pyb_can_clearfilter); @@ -760,9 +773,18 @@ STATIC mp_obj_t pyb_can_setfilter(size_t n_args, const mp_obj_t *pos_args, mp_ma #if MICROPY_HW_ENABLE_FDCAN FDCAN_FilterTypeDef filter = {0}; - filter.IdType = FDCAN_STANDARD_ID; - // TODO check filter index + if (self->extframe) { + filter.IdType = FDCAN_EXTENDED_ID; + } else { + filter.IdType = FDCAN_STANDARD_ID; + } + filter.FilterIndex = args[ARG_bank].u_int; + // Check filter index. + if ((filter.IdType == FDCAN_STANDARD_ID && filter.FilterIndex >= self->can.Init.StdFiltersNbr) || + (filter.IdType == FDCAN_EXTENDED_ID && filter.FilterIndex >= self->can.Init.ExtFiltersNbr)) { + goto error; + } // Check filter mode if (((args[ARG_mode].u_int != FDCAN_FILTER_RANGE) && From e7264e9532549560304be33ce7971609baba5bec Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sat, 29 Jan 2022 03:16:26 +0200 Subject: [PATCH 291/619] stm32/pyb_can: Add support for bus with mixed Classic/FD nodes. A CAN bus can have mixed classic/FD nodes. Prior to this patch the CAN API could be configured for either standard or extended ID, but not both/mixed operation. This patch allows extended IDs to be filtered and enabled on a per-message basis, in send(), setfilter() and clearfilter(). This is a breaking change to the API: init() no longer accepts the extframe keyword argument. --- ports/stm32/can.h | 1 - ports/stm32/pyb_can.c | 55 ++++++++++++++++++++++++------------------- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/ports/stm32/can.h b/ports/stm32/can.h index bb26c0b7e2..45ac7187e0 100644 --- a/ports/stm32/can.h +++ b/ports/stm32/can.h @@ -67,7 +67,6 @@ typedef struct _pyb_can_obj_t { mp_obj_t rxcallback1; mp_uint_t can_id : 8; bool is_enabled : 1; - bool extframe : 1; byte rx_state0; byte rx_state1; uint16_t num_error_warning; diff --git a/ports/stm32/pyb_can.c b/ports/stm32/pyb_can.c index 323b04d692..4d97e21f57 100644 --- a/ports/stm32/pyb_can.c +++ b/ports/stm32/pyb_can.c @@ -133,10 +133,9 @@ STATIC void pyb_can_print(const mp_print_t *print, mp_obj_t self_in, mp_print_ki mode = MP_QSTR_SILENT_LOOPBACK; break; } - mp_printf(print, "CAN(%u, CAN.%q, extframe=%q, auto_restart=%q)", + mp_printf(print, "CAN(%u, CAN.%q, auto_restart=%q)", self->can_id, mode, - self->extframe ? MP_QSTR_True : MP_QSTR_False, #if MICROPY_HW_ENABLE_FDCAN (self->can.Instance->CCCR & FDCAN_CCCR_DAR) ? MP_QSTR_True : MP_QSTR_False #else @@ -200,13 +199,12 @@ STATIC void pyb_can_get_bit_timing(mp_uint_t baudrate, mp_uint_t sample_point, mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("couldn't match baudrate and sample point")); } -// init(mode, extframe=False, prescaler=100, *, sjw=1, bs1=6, bs2=8) +// init(mode, prescaler=100, *, sjw=1, bs1=6, bs2=8) STATIC mp_obj_t pyb_can_init_helper(pyb_can_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_mode, ARG_extframe, ARG_prescaler, ARG_sjw, ARG_bs1, ARG_bs2, ARG_auto_restart, ARG_baudrate, ARG_sample_point, + enum { ARG_mode, ARG_prescaler, ARG_sjw, ARG_bs1, ARG_bs2, ARG_auto_restart, ARG_baudrate, ARG_sample_point, ARG_brs_prescaler, ARG_brs_sjw, ARG_brs_bs1, ARG_brs_bs2, ARG_brs_baudrate, ARG_brs_sample_point }; static const mp_arg_t allowed_args[] = { { MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = CAN_MODE_NORMAL} }, - { MP_QSTR_extframe, MP_ARG_BOOL, {.u_bool = false} }, { MP_QSTR_prescaler, MP_ARG_INT, {.u_int = CAN_DEFAULT_PRESCALER} }, { MP_QSTR_sjw, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = CAN_DEFAULT_SJW} }, { MP_QSTR_bs1, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = CAN_DEFAULT_BS1} }, @@ -228,8 +226,6 @@ STATIC mp_obj_t pyb_can_init_helper(pyb_can_obj_t *self, size_t n_args, const mp mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - self->extframe = args[ARG_extframe].u_bool; - // set the CAN configuration values memset(&self->can, 0, sizeof(self->can)); @@ -475,15 +471,16 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(pyb_can_any_obj, pyb_can_any); // send(send, addr, *, timeout=5000) STATIC mp_obj_t pyb_can_send(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_data, ARG_id, ARG_timeout, ARG_rtr, ARG_fdf, ARG_brs }; + enum { ARG_data, ARG_id, ARG_timeout, ARG_rtr, ARG_extframe, ARG_fdf, ARG_brs }; static const mp_arg_t allowed_args[] = { - { MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, - { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, - { MP_QSTR_rtr, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_rtr, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_extframe, MP_ARG_BOOL, {.u_bool = false} }, #if MICROPY_HW_ENABLE_FDCAN - { MP_QSTR_fdf, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, - { MP_QSTR_brs, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_fdf, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_brs, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, #endif }; @@ -512,7 +509,7 @@ STATIC mp_obj_t pyb_can_send(size_t n_args, const mp_obj_t *pos_args, mp_map_t * tx_msg.ErrorStateIndicator = FDCAN_ESI_ACTIVE; tx_msg.TxEventFifoControl = FDCAN_NO_TX_EVENTS; - if (self->extframe) { + if (args[ARG_extframe].u_bool == true) { tx_msg.Identifier = args[ARG_id].u_int & 0x1FFFFFFF; tx_msg.IdType = FDCAN_EXTENDED_ID; } else { @@ -545,7 +542,7 @@ STATIC mp_obj_t pyb_can_send(size_t n_args, const mp_obj_t *pos_args, mp_map_t * tx_msg.DLC = bufinfo.len; uint8_t *tx_data = tx_msg.Data; // Data is uint32_t but holds only 1 byte - if (self->extframe) { + if (args[ARG_extframe].u_bool == true) { tx_msg.ExtId = args[ARG_id].u_int & 0x1FFFFFFF; tx_msg.IDE = CAN_ID_EXT; } else { @@ -739,11 +736,20 @@ STATIC mp_obj_t pyb_can_initfilterbanks(mp_obj_t self_in, mp_obj_t bank_in) { STATIC MP_DEFINE_CONST_FUN_OBJ_2(pyb_can_initfilterbanks_fun_obj, pyb_can_initfilterbanks); STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(pyb_can_initfilterbanks_obj, MP_ROM_PTR(&pyb_can_initfilterbanks_fun_obj)); -STATIC mp_obj_t pyb_can_clearfilter(mp_obj_t self_in, mp_obj_t bank_in) { - pyb_can_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_int_t f = mp_obj_get_int(bank_in); +STATIC mp_obj_t pyb_can_clearfilter(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_extframe }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_extframe, MP_ARG_BOOL, {.u_bool = false} }, + }; + + // parse args + pyb_can_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + mp_int_t f = mp_obj_get_int(pos_args[1]); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 2, pos_args + 2, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + #if MICROPY_HW_ENABLE_FDCAN - can_clearfilter(self, f, self->extframe); + can_clearfilter(self, f, args[ARG_extframe].u_bool); #else if (self->can_id == 2) { f += can2_start_bank; @@ -752,18 +758,19 @@ STATIC mp_obj_t pyb_can_clearfilter(mp_obj_t self_in, mp_obj_t bank_in) { #endif return mp_const_none; } -STATIC MP_DEFINE_CONST_FUN_OBJ_2(pyb_can_clearfilter_obj, pyb_can_clearfilter); +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_can_clearfilter_obj, 2, pyb_can_clearfilter); // setfilter(bank, mode, fifo, params, *, rtr) #define EXTENDED_ID_TO_16BIT_FILTER(id) (((id & 0xC00000) >> 13) | ((id & 0x38000) >> 15)) | 8 STATIC mp_obj_t pyb_can_setfilter(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_bank, ARG_mode, ARG_fifo, ARG_params, ARG_rtr }; + enum { ARG_bank, ARG_mode, ARG_fifo, ARG_params, ARG_rtr, ARG_extframe }; static const mp_arg_t allowed_args[] = { { MP_QSTR_bank, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_fifo, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = CAN_FILTER_FIFO0} }, { MP_QSTR_params, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_rtr, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_extframe, MP_ARG_BOOL, {.u_bool = false} }, }; // parse args @@ -773,7 +780,7 @@ STATIC mp_obj_t pyb_can_setfilter(size_t n_args, const mp_obj_t *pos_args, mp_ma #if MICROPY_HW_ENABLE_FDCAN FDCAN_FilterTypeDef filter = {0}; - if (self->extframe) { + if (args[ARG_extframe].u_bool == true) { filter.IdType = FDCAN_EXTENDED_ID; } else { filter.IdType = FDCAN_STANDARD_ID; @@ -831,7 +838,7 @@ STATIC mp_obj_t pyb_can_setfilter(size_t n_args, const mp_obj_t *pos_args, mp_ma goto error; } filter.FilterScale = CAN_FILTERSCALE_16BIT; - if (self->extframe) { + if (args[ARG_extframe].u_bool == true) { if (args[ARG_rtr].u_obj != MP_OBJ_NULL) { if (args[ARG_mode].u_int == MASK16) { rtr_masks[0] = mp_obj_get_int(rtr_flags[0]) ? 0x02 : 0; From 95104c9f6e8a3dac884e803103e38163d5caf2aa Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sat, 29 Jan 2022 03:47:47 +0200 Subject: [PATCH 292/619] stm32/pyb_can: Return the filter ID and type of received messages. CAN.recv() now returns a 5-tuple, with the new element in the second position being a boolean, True if the ID is extended. This is a breaking change of the API for CAN.recv(). --- ports/stm32/pyb_can.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/ports/stm32/pyb_can.c b/ports/stm32/pyb_can.c index 4d97e21f57..bdb0e9034b 100644 --- a/ports/stm32/pyb_can.c +++ b/ports/stm32/pyb_can.c @@ -660,29 +660,29 @@ STATIC mp_obj_t pyb_can_recv(size_t n_args, const mp_obj_t *pos_args, mp_map_t * } // Create the tuple, or get the list, that will hold the return values - // Also populate the fourth element, either a new bytes or reuse existing memoryview + // Also populate the fifth element, either a new bytes or reuse existing memoryview mp_obj_t ret_obj = args[ARG_list].u_obj; mp_obj_t *items; if (ret_obj == mp_const_none) { - ret_obj = mp_obj_new_tuple(4, NULL); + ret_obj = mp_obj_new_tuple(5, NULL); items = ((mp_obj_tuple_t *)MP_OBJ_TO_PTR(ret_obj))->items; - items[3] = mp_obj_new_bytes(rx_data, rx_dlc); + items[4] = mp_obj_new_bytes(rx_data, rx_dlc); } else { - // User should provide a list of length at least 4 to hold the values + // User should provide a list of length at least 5 to hold the values if (!mp_obj_is_type(ret_obj, &mp_type_list)) { mp_raise_TypeError(NULL); } mp_obj_list_t *list = MP_OBJ_TO_PTR(ret_obj); - if (list->len < 4) { + if (list->len < 5) { mp_raise_ValueError(NULL); } items = list->items; - // Fourth element must be a memoryview which we assume points to a + // Fifth element must be a memoryview which we assume points to a // byte-like array which is large enough, and then we resize it inplace - if (!mp_obj_is_type(items[3], &mp_type_memoryview)) { + if (!mp_obj_is_type(items[4], &mp_type_memoryview)) { mp_raise_TypeError(NULL); } - mp_obj_array_t *mv = MP_OBJ_TO_PTR(items[3]); + mp_obj_array_t *mv = MP_OBJ_TO_PTR(items[4]); if (!(mv->typecode == (MP_OBJ_ARRAY_TYPECODE_FLAG_RW | BYTEARRAY_TYPECODE) || (mv->typecode | 0x20) == (MP_OBJ_ARRAY_TYPECODE_FLAG_RW | 'b'))) { mp_raise_ValueError(NULL); @@ -691,15 +691,17 @@ STATIC mp_obj_t pyb_can_recv(size_t n_args, const mp_obj_t *pos_args, mp_map_t * memcpy(mv->items, rx_data, rx_dlc); } - // Populate the first 3 values of the tuple/list + // Populate the first 4 values of the tuple/list #if MICROPY_HW_ENABLE_FDCAN items[0] = MP_OBJ_NEW_SMALL_INT(rx_msg.Identifier); - items[1] = rx_msg.RxFrameType == FDCAN_REMOTE_FRAME ? mp_const_true : mp_const_false; - items[2] = MP_OBJ_NEW_SMALL_INT(rx_msg.FilterIndex); + items[1] = mp_obj_new_bool(rx_msg.IdType == FDCAN_EXTENDED_ID); + items[2] = rx_msg.RxFrameType == FDCAN_REMOTE_FRAME ? mp_const_true : mp_const_false; + items[3] = MP_OBJ_NEW_SMALL_INT(rx_msg.FilterIndex); #else items[0] = MP_OBJ_NEW_SMALL_INT((rx_msg.IDE == CAN_ID_STD ? rx_msg.StdId : rx_msg.ExtId)); - items[1] = rx_msg.RTR == CAN_RTR_REMOTE ? mp_const_true : mp_const_false; - items[2] = MP_OBJ_NEW_SMALL_INT(rx_msg.FMI); + items[1] = mp_obj_new_bool(rx_msg.IDE == CAN_ID_EXT); + items[2] = rx_msg.RTR == CAN_RTR_REMOTE ? mp_const_true : mp_const_false; + items[3] = MP_OBJ_NEW_SMALL_INT(rx_msg.FMI); #endif // Return the result From a79706fb397b5763eff9fa0a2c1b3f1dbc906fff Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sun, 30 Jan 2022 19:16:17 +0200 Subject: [PATCH 293/619] stm32/pyb_can: Define the maximum bit timing parameters. Define the maximum parameters for CAN/FDCAN nominal bit timing, and for FDCAN data bit timing, for bit timing calculations. --- ports/stm32/pyb_can.c | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/ports/stm32/pyb_can.c b/ports/stm32/pyb_can.c index bdb0e9034b..aa33129325 100644 --- a/ports/stm32/pyb_can.c +++ b/ports/stm32/pyb_can.c @@ -59,6 +59,16 @@ #define CAN_DEFAULT_BS1 (8) #define CAN_DEFAULT_BS2 (3) +#define CAN_MAXIMUM_NBRP (512) +#define CAN_MAXIMUM_NBS1 (256) +#define CAN_MAXIMUM_NBS2 (128) +// Minimum Nominal time segment for FDCAN is 2. +#define CAN_MINIMUM_TSEG (2) + +#define CAN_MAXIMUM_DBRP (32) +#define CAN_MAXIMUM_DBS1 (32) +#define CAN_MAXIMUM_DBS2 (16) + #define CAN_MODE_NORMAL FDCAN_MODE_NORMAL #define CAN_MODE_LOOPBACK FDCAN_MODE_EXTERNAL_LOOPBACK #define CAN_MODE_SILENT FDCAN_MODE_BUS_MONITORING @@ -97,6 +107,11 @@ extern const uint8_t DLCtoBytes[16]; #define CAN_DEFAULT_BS1 (6) #define CAN_DEFAULT_BS2 (8) +#define CAN_MAXIMUM_NBRP (1024) +#define CAN_MAXIMUM_NBS1 (16) +#define CAN_MAXIMUM_NBS2 (8) +#define CAN_MINIMUM_TSEG (1) + #define CAN_IT_FIFO0_FULL CAN_IT_FF0 #define CAN_IT_FIFO1_FULL CAN_IT_FF1 #define CAN_IT_FIFO0_OVRF CAN_IT_FOV0 @@ -178,13 +193,13 @@ STATIC uint32_t pyb_can_get_source_freq() { } STATIC void pyb_can_get_bit_timing(mp_uint_t baudrate, mp_uint_t sample_point, + uint32_t max_brp, uint32_t max_bs1, uint32_t max_bs2, uint32_t min_tseg, mp_int_t *bs1_out, mp_int_t *bs2_out, mp_int_t *prescaler_out) { uint32_t can_kern_clk = pyb_can_get_source_freq(); - - // The following max values work on all MCUs for classical CAN. - for (int brp = 1; brp < 512; brp++) { - for (int bs1 = 1; bs1 < 16; bs1++) { - for (int bs2 = 1; bs2 < 8; bs2++) { + // Calculate CAN bit timing. + for (uint32_t brp = 1; brp < max_brp; brp++) { + for (uint32_t bs1 = min_tseg; bs1 < max_bs1; bs1++) { + for (uint32_t bs2 = min_tseg; bs2 < max_bs2; bs2++) { if ((baudrate == (can_kern_clk / (brp * (1 + bs1 + bs2)))) && ((sample_point * 10) == (((1 + bs1) * 1000) / (1 + bs1 + bs2)))) { *bs1_out = bs1; @@ -229,9 +244,10 @@ STATIC mp_obj_t pyb_can_init_helper(pyb_can_obj_t *self, size_t n_args, const mp // set the CAN configuration values memset(&self->can, 0, sizeof(self->can)); - // Calculate CAN bit timing from baudrate if provided + // Calculate CAN nominal bit timing from baudrate if provided if (args[ARG_baudrate].u_int != 0) { pyb_can_get_bit_timing(args[ARG_baudrate].u_int, args[ARG_sample_point].u_int, + CAN_MAXIMUM_NBRP, CAN_MAXIMUM_NBS1, CAN_MAXIMUM_NBS2, CAN_MINIMUM_TSEG, &args[ARG_bs1].u_int, &args[ARG_bs2].u_int, &args[ARG_prescaler].u_int); } @@ -240,9 +256,10 @@ STATIC mp_obj_t pyb_can_init_helper(pyb_can_obj_t *self, size_t n_args, const mp if (args[ARG_brs_sample_point].u_int == 0) { args[ARG_brs_sample_point].u_int = args[ARG_sample_point].u_int; } - // Calculate BRS CAN bit timing from baudrate if provided + // Calculate CAN data bit timing from baudrate if provided if (args[ARG_brs_baudrate].u_int != 0) { pyb_can_get_bit_timing(args[ARG_brs_baudrate].u_int, args[ARG_brs_sample_point].u_int, + CAN_MAXIMUM_DBRP, CAN_MAXIMUM_DBS1, CAN_MAXIMUM_DBS2, 1, &args[ARG_brs_bs1].u_int, &args[ARG_brs_bs2].u_int, &args[ARG_brs_prescaler].u_int); } // Set BRS bit timings. From 5562ed3f43f1a9327bf8634f16e2592774eb7907 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Wed, 30 Mar 2022 17:40:44 +0200 Subject: [PATCH 294/619] stm32/pyb_can: Replace CAN.initfilterbanks with CAN.init keyword arg. The CAN.initfilterbanks() class method is removed, and its functionality is replaced with the "num_filter_banks" keyword argument to the CAN constructor and CAN.init(). This configures the filter bank split. This new approach provides more flexibility configuring the resources used by a given CAN instance, allowing other MCUs like H7 to fit the API. It also brings CAN closer to how other machine peripherals are configured, where everything is done in the constructor/init method. This is a breaking change to the CAN API. --- ports/stm32/pyb_can.c | 39 ++++++++------------------------------- 1 file changed, 8 insertions(+), 31 deletions(-) diff --git a/ports/stm32/pyb_can.c b/ports/stm32/pyb_can.c index aa33129325..aa2d83a64e 100644 --- a/ports/stm32/pyb_can.c +++ b/ports/stm32/pyb_can.c @@ -217,7 +217,7 @@ STATIC void pyb_can_get_bit_timing(mp_uint_t baudrate, mp_uint_t sample_point, // init(mode, prescaler=100, *, sjw=1, bs1=6, bs2=8) STATIC mp_obj_t pyb_can_init_helper(pyb_can_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_mode, ARG_prescaler, ARG_sjw, ARG_bs1, ARG_bs2, ARG_auto_restart, ARG_baudrate, ARG_sample_point, - ARG_brs_prescaler, ARG_brs_sjw, ARG_brs_bs1, ARG_brs_bs2, ARG_brs_baudrate, ARG_brs_sample_point }; + ARG_num_filter_banks, ARG_brs_prescaler, ARG_brs_sjw, ARG_brs_bs1, ARG_brs_bs2, ARG_brs_baudrate, ARG_brs_sample_point }; static const mp_arg_t allowed_args[] = { { MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = CAN_MODE_NORMAL} }, { MP_QSTR_prescaler, MP_ARG_INT, {.u_int = CAN_DEFAULT_PRESCALER} }, @@ -227,6 +227,7 @@ STATIC mp_obj_t pyb_can_init_helper(pyb_can_obj_t *self, size_t n_args, const mp { MP_QSTR_auto_restart, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, { MP_QSTR_baudrate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_sample_point, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 75} }, // 75% sampling point + { MP_QSTR_num_filter_banks, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 14} }, #if MICROPY_HW_ENABLE_FDCAN { MP_QSTR_brs_prescaler, MP_ARG_INT, {.u_int = CAN_DEFAULT_PRESCALER} }, { MP_QSTR_brs_sjw, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = CAN_DEFAULT_SJW} }, @@ -267,6 +268,12 @@ STATIC mp_obj_t pyb_can_init_helper(pyb_can_obj_t *self, size_t n_args, const mp self->can.Init.DataSyncJumpWidth = args[ARG_brs_sjw].u_int; self->can.Init.DataTimeSeg1 = args[ARG_bs1].u_int; // DataTimeSeg1 = Propagation_segment + Phase_segment_1 self->can.Init.DataTimeSeg2 = args[ARG_bs2].u_int; + #else + // Init filter banks for classic CAN. + can2_start_bank = args[ARG_num_filter_banks].u_int; + for (int f = 0; f < CAN_MAX_FILTER; f++) { + can_clearfilter(self, f, can2_start_bank); + } #endif if (!can_init(self, args[ARG_mode].u_int, args[ARG_prescaler].u_int, args[ARG_sjw].u_int, @@ -726,35 +733,6 @@ STATIC mp_obj_t pyb_can_recv(size_t n_args, const mp_obj_t *pos_args, mp_map_t * } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_can_recv_obj, 1, pyb_can_recv); -// initfilterbanks(n) -STATIC mp_obj_t pyb_can_initfilterbanks(mp_obj_t self_in, mp_obj_t bank_in) { - pyb_can_obj_t *self = MP_OBJ_TO_PTR(self_in); - #if MICROPY_HW_ENABLE_FDCAN - (void)self; - #if 0 - FDCAN_InitTypeDef *init = &self->can.Init; - // Clear standard ID filters. - for (int f = 0; f < init->StdFiltersNbr; ++f) { - can_clearfilter(self, f, false); - } - // Clear extended ID filters. - for (int f = 0; f < init->ExtFiltersNbr; ++f) { - can_clearfilter(self, f, true); - } - #endif - #else - // NOTE: For classic CAN, this function calls HAL_CAN_ConfigFilter(NULL, &filter); - // if CAN3 is defined, ConfigFilter() will dereference a NULL pointer. - can2_start_bank = mp_obj_get_int(bank_in); - for (int f = 0; f < CAN_MAX_FILTER; f++) { - can_clearfilter(self, f, can2_start_bank); - } - #endif - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_2(pyb_can_initfilterbanks_fun_obj, pyb_can_initfilterbanks); -STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(pyb_can_initfilterbanks_obj, MP_ROM_PTR(&pyb_can_initfilterbanks_fun_obj)); - STATIC mp_obj_t pyb_can_clearfilter(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_extframe }; static const mp_arg_t allowed_args[] = { @@ -1006,7 +984,6 @@ STATIC const mp_rom_map_elem_t pyb_can_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_any), MP_ROM_PTR(&pyb_can_any_obj) }, { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&pyb_can_send_obj) }, { MP_ROM_QSTR(MP_QSTR_recv), MP_ROM_PTR(&pyb_can_recv_obj) }, - { MP_ROM_QSTR(MP_QSTR_initfilterbanks), MP_ROM_PTR(&pyb_can_initfilterbanks_obj) }, { MP_ROM_QSTR(MP_QSTR_setfilter), MP_ROM_PTR(&pyb_can_setfilter_obj) }, { MP_ROM_QSTR(MP_QSTR_clearfilter), MP_ROM_PTR(&pyb_can_clearfilter_obj) }, { MP_ROM_QSTR(MP_QSTR_rxcallback), MP_ROM_PTR(&pyb_can_rxcallback_obj) }, From 5cdf9645711cc12b45e602ef5795c33895116fc4 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sun, 20 Feb 2022 20:29:32 +0200 Subject: [PATCH 295/619] docs/library/pyb.CAN: Update CAN docs to match revised API. --- docs/library/pyb.CAN.rst | 140 +++++++++++++++++++++++++-------------- 1 file changed, 92 insertions(+), 48 deletions(-) diff --git a/docs/library/pyb.CAN.rst b/docs/library/pyb.CAN.rst index bcaeeba9df..69704d09b2 100644 --- a/docs/library/pyb.CAN.rst +++ b/docs/library/pyb.CAN.rst @@ -4,13 +4,12 @@ class CAN -- controller area network communication bus ====================================================== -CAN implements the standard CAN communications protocol. At -the physical level it consists of 2 lines: RX and TX. Note that -to connect the pyboard to a CAN bus you must use a CAN transceiver -to convert the CAN logic signals from the pyboard to the correct +CAN implements support for classic CAN (available on F4, F7 MCUs) and CAN FD (H7 series) controllers. +At the physical level CAN bus consists of 2 lines: RX and TX. Note that to connect the pyboard to a +CAN bus you must use a CAN transceiver to convert the CAN logic signals from the pyboard to the correct voltage levels on the bus. -Example usage (works without anything connected):: +Example usage for classic CAN controller in Loopback (transceiver-less) mode:: from pyb import CAN can = CAN(1, CAN.LOOPBACK) @@ -18,6 +17,16 @@ Example usage (works without anything connected):: can.send('message!', 123) # send a message with id 123 can.recv(0) # receive message on FIFO 0 +Example usage for CAN FD controller with all of the possible options enabled:: + + # FD frame + BRS mode + Extended frame ID. 500 Kbit/s for arbitration phase, 1Mbit/s for data phase. + can = CAN(1, CAN.NORMAL, baudrate=500_000, brs_baudrate=1_000_000, sample_point=80) + can.setfilter(0, CAN.RANGE, 0, (0xFFF0, 0xFFFF)) + can.send('a'*64, 0xFFFF, fdf=True, brs=True, extframe=True) + can.recv(0) + +The following CAN module functions and their arguments are available +for both classic and FD CAN controllers, unless otherwise stated. Constructors ------------ @@ -35,43 +44,48 @@ Constructors - ``CAN(1)`` is on ``YA``: ``(RX, TX) = (Y3, Y4) = (PB8, PB9)`` - ``CAN(2)`` is on ``YB``: ``(RX, TX) = (Y5, Y6) = (PB12, PB13)`` -Class Methods -------------- -.. classmethod:: CAN.initfilterbanks(nr) - - Reset and disable all filter banks and assign how many banks should be available for CAN(1). - - STM32F405 has 28 filter banks that are shared between the two available CAN bus controllers. - This function configures how many filter banks should be assigned to each. *nr* is the number of banks - that will be assigned to CAN(1), the rest of the 28 are assigned to CAN(2). - At boot, 14 banks are assigned to each controller. - Methods ------- -.. method:: CAN.init(mode, extframe=False, prescaler=100, *, sjw=1, bs1=6, bs2=8, auto_restart=False, baudrate=0, sample_point=75) +.. method:: CAN.init(mode, prescaler=100, *, sjw=1, bs1=6, bs2=8, auto_restart=False, baudrate=0, sample_point=75, + num_filter_banks=14, brs_sjw=1, brs_bs1=8, brs_bs2=3, brs_baudrate=0, brs_sample_point=75) Initialise the CAN bus with the given parameters: - *mode* is one of: NORMAL, LOOPBACK, SILENT, SILENT_LOOPBACK - - if *extframe* is True then the bus uses extended identifiers in the frames - (29 bits); otherwise it uses standard 11 bit identifiers - - *prescaler* is used to set the duration of 1 time quanta; the time quanta - will be the input clock (PCLK1, see :meth:`pyb.freq()`) divided by the prescaler - - *sjw* is the resynchronisation jump width in units of the time quanta; - it can be 1, 2, 3, 4 - - *bs1* defines the location of the sample point in units of the time quanta; - it can be between 1 and 1024 inclusive - - *bs2* defines the location of the transmit point in units of the time quanta; - it can be between 1 and 16 inclusive + - *prescaler* is the value by which the CAN input clock is divided to generate the + nominal bit time quanta. The prescaler can be a value between 1 and 1024 inclusive + for classic CAN, and between 1 and 512 inclusive for CAN FD. + - *sjw* is the resynchronisation jump width in units of time quanta for nominal bits; + it can be a value between 1 and 4 inclusive for classic CAN, and between 1 and 128 inclusive for CAN FD. + - *bs1* defines the location of the sample point in units of the time quanta for nominal bits; + it can be a value between 1 and 16 inclusive for classic CAN, and between 2 and 256 inclusive for CAN FD. + - *bs2* defines the location of the transmit point in units of the time quanta for nominal bits; + it can be a value between 1 and 8 inclusive for classic CAN, and between 2 and 128 inclusive for CAN FD. - *auto_restart* sets whether the controller will automatically try and restart communications after entering the bus-off state; if this is disabled then :meth:`~CAN.restart()` can be used to leave the bus-off state - *baudrate* if a baudrate other than 0 is provided, this function will try to automatically - calculate a CAN bit-timing (overriding *prescaler*, *bs1* and *bs2*) that satisfies both - the baudrate and the desired *sample_point*. - - *sample_point* given in a percentage of the bit time, the *sample_point* specifies the position - of the last bit sample with respect to the whole bit time. The default *sample_point* is 75%. + calculate the CAN nominal bit time (overriding *prescaler*, *bs1* and *bs2*) that satisfies + both the baudrate and the desired *sample_point*. + - *sample_point* given in a percentage of the nominal bit time, the *sample_point* specifies the position + of the bit sample with respect to the whole nominal bit time. The default *sample_point* is 75%. + - *num_filter_banks* for classic CAN, this is the number of banks that will be assigned to CAN(1), + the rest of the 28 are assigned to CAN(2). + - *brs_prescaler* is the value by which the CAN FD input clock is divided to generate the + data bit time quanta. The prescaler can be a value between 1 and 32 inclusive. + - *brs_sjw* is the resynchronisation jump width in units of time quanta for data bits; + it can be a value between 1 and 16 inclusive + - *brs_bs1* defines the location of the sample point in units of the time quanta for data bits; + it can be a value between 1 and 32 inclusive + - *brs_bs2* defines the location of the transmit point in units of the time quanta for data bits; + it can be a value between 1 and 16 inclusive + - *brs_baudrate* if a baudrate other than 0 is provided, this function will try to automatically + calculate the CAN data bit time (overriding *brs_prescaler*, *brs_bs1* and *brs_bs2*) that satisfies + both the baudrate and the desired *brs_sample_point*. + - *brs_sample_point* given in a percentage of the data bit time, the *brs_sample_point* specifies the position + of the bit sample with respect to the whole data bit time. The default *brs_sample_point* is 75%. + The time quanta tq is the basic unit of time for the CAN bus. tq is the CAN prescaler value divided by PCLK1 (the frequency of internal peripheral bus 1); @@ -140,17 +154,17 @@ Methods - number of pending RX messages on fifo 0 - number of pending RX messages on fifo 1 -.. method:: CAN.setfilter(bank, mode, fifo, params, *, rtr) +.. method:: CAN.setfilter(bank, mode, fifo, params, *, rtr, extframe=False) Configure a filter bank: - - *bank* is the filter bank that is to be configured. - - *mode* is the mode the filter should operate in. + - *bank* is the classic CAN controller filter bank, or CAN FD filter index, to configure. + - *mode* is the mode the filter should operate in, see the tables below. - *fifo* is which fifo (0 or 1) a message should be stored in, if it is accepted by this filter. - *params* is an array of values the defines the filter. The contents of the array depends on the *mode* argument. +-----------+---------------------------------------------------------+ - |*mode* |contents of *params* array | + |*mode* |Contents of *params* array for classic CAN controller | +===========+=========================================================+ |CAN.LIST16 |Four 16 bit ids that will be accepted | +-----------+---------------------------------------------------------+ @@ -165,10 +179,20 @@ Methods |CAN.MASK32 |As with CAN.MASK16 but with only one 32 bit id/mask pair.| +-----------+---------------------------------------------------------+ - - *rtr* is an array of booleans that states if a filter should accept a - remote transmission request message. If this argument is not given - then it defaults to ``False`` for all entries. The length of the array - depends on the *mode* argument. + +-----------+---------------------------------------------------------+ + |*mode* |Contents of *params* array for CAN FD controller | + +===========+=========================================================+ + |CAN.RANGE |Two ids that represent a range of accepted ids. | + +-----------+---------------------------------------------------------+ + |CAN.DUAL |Two ids that will be accepted. For example (1, 2) | + +-----------+---------------------------------------------------------+ + |CAN.MASK |One filter ID and a mask. For example (0x111, 0x7FF) | + +-----------+---------------------------------------------------------+ + + - *rtr* For classic CAN controllers, this is an array of booleans that states if + a filter should accept a remote transmission request message. If this argument + is not given then it defaults to ``False`` for all entries. The length of the + array depends on the *mode* argument. For CAN FD, this argument is ignored. +-----------+----------------------+ |*mode* |length of *rtr* array | @@ -182,11 +206,17 @@ Methods |CAN.MASK32 |1 | +-----------+----------------------+ -.. method:: CAN.clearfilter(bank) + - *extframe* If True the frame will have an extended identifier (29 bits), + otherwise a standard identifier (11 bits) is used. + + +.. method:: CAN.clearfilter(bank, extframe=False) Clear and disables a filter bank: - - *bank* is the filter bank that is to be cleared. + - *bank* is the classic CAN controller filter bank, or CAN FD filter index, to clear. + - *extframe* For CAN FD controllers, if True, clear an extended filter (configured with extframe=True), + otherwise the clear a standard identifier (configured with extframe=False). .. method:: CAN.any(fifo) @@ -200,21 +230,22 @@ Methods - *list* is an optional list object to be used as the return value - *timeout* is the timeout in milliseconds to wait for the receive. - Return value: A tuple containing four values. + Return value: A tuple containing five values. - The id of the message. + - A boolean that indicates if the message ID is standard or extended. - A boolean that indicates if the message is an RTR message. - The FMI (Filter Match Index) value. - An array containing the data. If *list* is ``None`` then a new tuple will be allocated, as well as a new - bytes object to contain the data (as the fourth element in the tuple). + bytes object to contain the data (as the fifth element in the tuple). - If *list* is not ``None`` then it should be a list object with a least four - elements. The fourth element should be a memoryview object which is created + If *list* is not ``None`` then it should be a list object with a least five + elements. The fifth element should be a memoryview object which is created from either a bytearray or an array of type 'B' or 'b', and this array must have enough room for at least 8 bytes. The list object will then be - populated with the first three return values above, and the memoryview object + populated with the first four return values above, and the memoryview object will be resized inplace to the size of the data and filled in with that data. The same list and memoryview objects can be reused in subsequent calls to this method, providing a way of receiving data without using the heap. @@ -225,7 +256,7 @@ Methods # No heap memory is allocated in the following call can.recv(0, lst) -.. method:: CAN.send(data, id, *, timeout=0, rtr=False) +.. method:: CAN.send(data, id, *, timeout=0, rtr=False, extframe=False, fdf=False, brs=False) Send a message on the bus: @@ -236,6 +267,13 @@ Methods a remote transmission request. If *rtr* is True then only the length of *data* is used to fill in the DLC slot of the frame; the actual bytes in *data* are unused. + - *extframe* if True the frame will have an extended identifier (29 bits), + otherwise a standard identifier (11 bits) is used. + - *fdf* for CAN FD controllers, if set to True, the frame will have an FD + frame format, which supports data payloads up to 64 bytes. + - *brs* for CAN FD controllers, if set to True, the bitrate switching mode + is enabled, in which the data phase is transmitted at a differet bitrate. + See :meth:`CAN.init` for the data bit timing configuration parameters. If timeout is 0 the message is placed in a buffer in one of three hardware buffers and the method returns immediately. If all three buffers are in use @@ -302,4 +340,10 @@ Constants CAN.LIST32 CAN.MASK32 - The operation mode of a filter used in :meth:`~CAN.setfilter()`. + The operation mode of a filter used in :meth:`~CAN.setfilter()` for classic CAN. + +.. data:: CAN.DUAL + CAN.RANGE + CAN.MASK + + The operation mode of a filter used in :meth:`~CAN.setfilter()` for CAN FD. From 71344c15f4ef6efc62fbaeef830fa357ee30dce0 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 2 Apr 2022 22:14:33 +1100 Subject: [PATCH 296/619] tests/pyb: Update CAN tests to match revised CAN API. Signed-off-by: Damien George --- tests/pyb/can.py | 51 ++++++++++++++++++++--------------- tests/pyb/can.py.exp | 63 +++++++++++++++++++++++-------------------- tests/pyb/can2.py | 18 ++++++------- tests/pyb/can2.py.exp | 6 ++--- 4 files changed, 76 insertions(+), 62 deletions(-) diff --git a/tests/pyb/can.py b/tests/pyb/can.py index 4ea29b0f63..020efae053 100644 --- a/tests/pyb/can.py +++ b/tests/pyb/can.py @@ -17,14 +17,13 @@ for bus in (-1, 0, 1, 3): print("ValueError", bus) CAN(1).deinit() -CAN.initfilterbanks(14) can = CAN(1) print(can) # Test state when de-init'd print(can.state() == can.STOPPED) -can.init(CAN.LOOPBACK) +can.init(CAN.LOOPBACK, num_filter_banks=14) print(can) print(can.any(0)) @@ -61,7 +60,7 @@ else: # Test that recv can work without allocating memory on the heap buf = bytearray(10) -l = [0, 0, 0, memoryview(buf)] +l = [0, 0, 0, 0, memoryview(buf)] l2 = None micropython.heap_lock() @@ -69,30 +68,30 @@ micropython.heap_lock() can.send("", 42) l2 = can.recv(0, l) assert l is l2 -print(l, len(l[3]), buf) +print(l, len(l[4]), buf) can.send("1234", 42) l2 = can.recv(0, l) assert l is l2 -print(l, len(l[3]), buf) +print(l, len(l[4]), buf) can.send("01234567", 42) l2 = can.recv(0, l) assert l is l2 -print(l, len(l[3]), buf) +print(l, len(l[4]), buf) can.send("abc", 42) l2 = can.recv(0, l) assert l is l2 -print(l, len(l[3]), buf) +print(l, len(l[4]), buf) micropython.heap_unlock() # Test that recv can work with different arrays behind the memoryview can.send("abc", 1) -print(bytes(can.recv(0, [0, 0, 0, memoryview(array("B", range(8)))])[3])) +print(bytes(can.recv(0, [0, 0, 0, 0, memoryview(array("B", range(8)))])[4])) can.send("def", 1) -print(bytes(can.recv(0, [0, 0, 0, memoryview(array("b", range(8)))])[3])) +print(bytes(can.recv(0, [0, 0, 0, 0, memoryview(array("b", range(8)))])[4])) # Test for non-list passed as second arg to recv can.send("abc", 1) @@ -111,7 +110,7 @@ except ValueError: # Test for non-memoryview passed as 4th element to recv can.send("abc", 1) try: - can.recv(0, [0, 0, 0, 0]) + can.recv(0, [0, 0, 0, 0, 0]) except TypeError: print("TypeError") @@ -132,19 +131,21 @@ except ValueError: del can # Testing extended IDs -can = CAN(1, CAN.LOOPBACK, extframe=True) -# Catch all filter -can.setfilter(0, CAN.MASK32, 0, (0, 0)) +print("==== TEST extframe=True ====") + +can = CAN(1, CAN.LOOPBACK) +# Catch all filter, but only for extframe's +can.setfilter(0, CAN.MASK32, 0, (0, 0), extframe=True) print(can) try: - can.send("abcde", 0x7FF + 1, timeout=5000) + can.send("abcde", 0x7FF + 1, timeout=5000, extframe=True) except ValueError: print("failed") else: r = can.recv(0) - if r[0] == 0x7FF + 1 and r[3] == b"abcde": + if r[0] == 0x7FF + 1 and r[4] == b"abcde": print("passed") else: print("failed, wrong data received") @@ -156,22 +157,24 @@ for n in [0, 8, 16, 24]: id_ok = 0b00001010 << n id_fail = 0b00011010 << n - can.clearfilter(0) - can.setfilter(0, pyb.CAN.MASK32, 0, (filter_id, filter_mask)) + can.clearfilter(0, extframe=True) + can.setfilter(0, pyb.CAN.MASK32, 0, (filter_id, filter_mask), extframe=True) - can.send("ok", id_ok, timeout=3) + can.send("ok", id_ok, timeout=3, extframe=True) if can.any(0): msg = can.recv(0) - print((hex(filter_id), hex(filter_mask), hex(msg[0]), msg[3])) + print((hex(filter_id), hex(filter_mask), hex(msg[0]), msg[1], msg[4])) - can.send("fail", id_fail, timeout=3) + can.send("fail", id_fail, timeout=3, extframe=True) if can.any(0): msg = can.recv(0) - print((hex(filter_id), hex(filter_mask), hex(msg[0]), msg[3])) + print((hex(filter_id), hex(filter_mask), hex(msg[0]), msg[1], msg[4])) del can # Test RxCallbacks +print("==== TEST rx callbacks ====") + can = CAN(1, CAN.LOOPBACK) can.setfilter(0, CAN.LIST16, 0, (1, 2, 3, 4)) can.setfilter(1, CAN.LIST16, 1, (5, 6, 7, 8)) @@ -248,6 +251,8 @@ print(can.recv(1)) del can # Testing asynchronous send +print("==== TEST async send ====") + can = CAN(1, CAN.LOOPBACK) can.setfilter(0, CAN.MASK16, 0, (0, 0, 0, 0)) @@ -277,6 +282,8 @@ while can.any(0): print(can.recv(0)) # Testing rtr messages +print("==== TEST rtr messages ====") + bus1 = CAN(1, CAN.LOOPBACK) while bus1.any(0): bus1.recv(0) @@ -298,6 +305,8 @@ bus1.send("", 32, rtr=True) print(bus1.recv(0)) # test HAL error, timeout +print("==== TEST errors ====") + can = pyb.CAN(1, pyb.CAN.NORMAL) try: can.send("1", 1, timeout=50) diff --git a/tests/pyb/can.py.exp b/tests/pyb/can.py.exp index a27907cc52..bd8f6d60b6 100644 --- a/tests/pyb/can.py.exp +++ b/tests/pyb/can.py.exp @@ -4,19 +4,19 @@ CAN 1 ValueError 3 CAN(1) True -CAN(1, CAN.LOOPBACK, extframe=False, auto_restart=False) +CAN(1, CAN.LOOPBACK, auto_restart=False) False True [0, 0, 0, 0, 0, 0, 0, 0] True [0, 0, 0, 0, 0, 0, 1, 0] -(123, False, 0, b'abcd') -(2047, False, 0, b'abcd') -(0, False, 0, b'abcd') +(123, False, False, 0, b'abcd') +(2047, False, False, 0, b'abcd') +(0, False, False, 0, b'abcd') passed -[42, False, 0, ] 0 bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') -[42, False, 0, ] 4 bytearray(b'1234\x00\x00\x00\x00\x00\x00') -[42, False, 0, ] 8 bytearray(b'01234567\x00\x00') -[42, False, 0, ] 3 bytearray(b'abc34567\x00\x00') +[42, False, False, 0, ] 0 bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') +[42, False, False, 0, ] 4 bytearray(b'1234\x00\x00\x00\x00\x00\x00') +[42, False, False, 0, ] 8 bytearray(b'01234567\x00\x00') +[42, False, False, 0, ] 3 bytearray(b'abc34567\x00\x00') b'abc' b'def' TypeError @@ -24,12 +24,14 @@ ValueError TypeError ValueError ValueError -CAN(1, CAN.LOOPBACK, extframe=True, auto_restart=False) +==== TEST extframe=True ==== +CAN(1, CAN.LOOPBACK, auto_restart=False) passed -('0x8', '0x1c', '0xa', b'ok') -('0x800', '0x1c00', '0xa00', b'ok') -('0x80000', '0x1c0000', '0xa0000', b'ok') -('0x8000000', '0x1c000000', '0xa000000', b'ok') +('0x8', '0x1c', '0xa', True, b'ok') +('0x800', '0x1c00', '0xa00', True, b'ok') +('0x80000', '0x1c0000', '0xa0000', True, b'ok') +('0x8000000', '0x1c000000', '0xa000000', True, b'ok') +==== TEST rx callbacks ==== cb0 pending cb0 @@ -42,28 +44,31 @@ cb1 full cb1a overflow -(1, False, 0, b'11111111') -(2, False, 1, b'22222222') -(4, False, 3, b'44444444') -(5, False, 0, b'55555555') -(6, False, 1, b'66666666') -(8, False, 3, b'88888888') +(1, False, False, 0, b'11111111') +(2, False, False, 1, b'22222222') +(4, False, False, 3, b'44444444') +(5, False, False, 0, b'55555555') +(6, False, False, 1, b'66666666') +(8, False, False, 3, b'88888888') cb0a pending cb1a pending -(1, False, 0, b'11111111') -(5, False, 0, b'55555555') +(1, False, False, 0, b'11111111') +(5, False, False, 0, b'55555555') +==== TEST async send ==== False -(1, False, 0, b'abcde') +(1, False, False, 0, b'abcde') passed -(2, False, 0, b'abcde') -(3, False, 0, b'abcde') -(4, False, 0, b'abcde') +(2, False, False, 0, b'abcde') +(3, False, False, 0, b'abcde') +(4, False, False, 0, b'abcde') +==== TEST rtr messages ==== False -(5, True, 4, b'') -(6, True, 5, b'') -(7, True, 6, b'') +(5, False, True, 4, b'') +(6, False, True, 5, b'') +(7, False, True, 6, b'') False -(32, True, 9, b'') +(32, False, True, 9, b'') +==== TEST errors ==== OSError(110,) diff --git a/tests/pyb/can2.py b/tests/pyb/can2.py index 46237ad84f..2ce438f1af 100644 --- a/tests/pyb/can2.py +++ b/tests/pyb/can2.py @@ -7,19 +7,19 @@ except (ImportError, ValueError): raise SystemExit # Testing rtr messages -bus2 = CAN(2, CAN.LOOPBACK, extframe=True) +bus2 = CAN(2, CAN.LOOPBACK) while bus2.any(0): bus2.recv(0) -bus2.setfilter(0, CAN.LIST32, 0, (1, 2), rtr=(True, True)) -bus2.setfilter(1, CAN.LIST32, 0, (3, 4), rtr=(True, False)) -bus2.setfilter(2, CAN.MASK32, 0, (16, 16), rtr=(False,)) -bus2.setfilter(2, CAN.MASK32, 0, (32, 32), rtr=(True,)) +bus2.setfilter(0, CAN.LIST32, 0, (1, 2), rtr=(True, True), extframe=True) +bus2.setfilter(1, CAN.LIST32, 0, (3, 4), rtr=(True, False), extframe=True) +bus2.setfilter(2, CAN.MASK32, 0, (16, 16), rtr=(False,), extframe=True) +bus2.setfilter(2, CAN.MASK32, 0, (32, 32), rtr=(True,), extframe=True) -bus2.send("", 1, rtr=True) +bus2.send("", 1, rtr=True, extframe=True) print(bus2.recv(0)) -bus2.send("", 2, rtr=True) +bus2.send("", 2, rtr=True, extframe=True) print(bus2.recv(0)) -bus2.send("", 3, rtr=True) +bus2.send("", 3, rtr=True, extframe=True) print(bus2.recv(0)) -bus2.send("", 4, rtr=True) +bus2.send("", 4, rtr=True, extframe=True) print(bus2.any(0)) diff --git a/tests/pyb/can2.py.exp b/tests/pyb/can2.py.exp index 371ad2bdc4..3339e5cbec 100644 --- a/tests/pyb/can2.py.exp +++ b/tests/pyb/can2.py.exp @@ -1,4 +1,4 @@ -(1, True, 0, b'') -(2, True, 1, b'') -(3, True, 2, b'') +(1, True, True, 0, b'') +(2, True, True, 1, b'') +(3, True, True, 2, b'') False From 7a447e08b23f47fac6ebcbe5d60c4e1d0fe0d157 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Fri, 18 Jun 2021 17:12:44 +0200 Subject: [PATCH 297/619] docs: Add quickref and docs for mimxrt, including network.LAN docs. --- docs/index.rst | 1 + docs/library/machine.PWM.rst | 12 + docs/library/machine.SDCard.rst | 43 ++ docs/library/network.LAN.rst | 93 ++++ docs/library/network.rst | 1 + docs/mimxrt/general.rst | 92 ++++ docs/mimxrt/img/teensy_4.1.jpg | Bin 0 -> 127829 bytes docs/mimxrt/quickref.rst | 864 ++++++++++++++++++++++++++++++++ docs/mimxrt/tutorial/intro.rst | 125 +++++ docs/templates/topindex.html | 4 + 10 files changed, 1235 insertions(+) create mode 100644 docs/library/network.LAN.rst create mode 100644 docs/mimxrt/general.rst create mode 100644 docs/mimxrt/img/teensy_4.1.jpg create mode 100644 docs/mimxrt/quickref.rst create mode 100644 docs/mimxrt/tutorial/intro.rst diff --git a/docs/index.rst b/docs/index.rst index a97bff1c84..3bc78e9023 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -12,6 +12,7 @@ MicroPython documentation and references esp8266/quickref.rst esp32/quickref.rst rp2/quickref.rst + mimxrt/quickref.rst wipy/quickref.rst unix/quickref.rst zephyr/quickref.rst diff --git a/docs/library/machine.PWM.rst b/docs/library/machine.PWM.rst index 793c074a36..4b74355775 100644 --- a/docs/library/machine.PWM.rst +++ b/docs/library/machine.PWM.rst @@ -78,6 +78,13 @@ Methods With a single *value* argument the pulse width is set to that value. +Specific PWM class implementations +---------------------------------- + +The following concrete class(es) implement enhancements to the PWM class. + + | :ref:`pyb.Timer for PyBoard ` + Limitations of PWM ------------------ @@ -90,6 +97,11 @@ Limitations of PWM 80000000 / 267 = 299625.5 Hz, not 300kHz. If the divider is set to 266 then the PWM frequency will be 80000000 / 266 = 300751.9 Hz, but again not 300kHz. + Some ports like the RP2040 one use a fractional divider, which allow a finer + granularity of the frequency at higher frequencies by switching the PWM + pulse duration between two adjacent values, such that the resulting average + frequency is more close to the intended one, at the cost of spectral purity. + * The duty cycle has the same discrete nature and its absolute accuracy is not achievable. On most hardware platforms the duty will be applied at the next frequency period. Therefore, you should wait more than "1/frequency" before diff --git a/docs/library/machine.SDCard.rst b/docs/library/machine.SDCard.rst index b07ad37d39..cde0bd1d14 100644 --- a/docs/library/machine.SDCard.rst +++ b/docs/library/machine.SDCard.rst @@ -122,3 +122,46 @@ You can set the pins used for SPI access by passing a tuple as the *Note:* The current cc3200 SD card implementation names the this class :class:`machine.SD` rather than :class:`machine.SDCard` . + +mimxrt +`````` + +The SDCard module for the mimxrt port only supports access via dedicated SD/MMC +peripheral (USDHC) in 4-bit mode with 50MHz clock frequency exclusively. +Unfortunately the MIMXRT1011 controller does not support the USDHC peripheral. +Hence this controller does not feature the ``machine.SDCard`` module. + +Due to the decision to only support 4-bit mode with 50MHz clock frequency the +interface has been simplified, and the constructor signature is: + +.. class:: SDCard(slot=1) + :noindex: + +The pins used for the USDHC peripheral have to be configured in ``mpconfigboard.h``. +Most of the controllers supported by the mimxrt port provide up to two USDHC +peripherals. Therefore the pin configuration is performed using the macro +``MICROPY_USDHCx`` with x being 1 or 2 respectively. + +The following shows an example configuration for USDHC1:: + + #define MICROPY_USDHC1 \ + { \ + .cmd = { GPIO_SD_B0_02_USDHC1_CMD}, \ + .clk = { GPIO_SD_B0_03_USDHC1_CLK }, \ + .cd_b = { GPIO_SD_B0_06_USDHC1_CD_B },\ + .data0 = { GPIO_SD_B0_04_USDHC1_DATA0 },\ + .data1 = { GPIO_SD_B0_05_USDHC1_DATA1 },\ + .data2 = { GPIO_SD_B0_00_USDHC1_DATA2 },\ + .data3 = { GPIO_SD_B0_01_USDHC1_DATA3 },\ + } + +If the card detect pin is not used (cb_b pin) then the respective entry has to be +filled with the following dummy value:: + + #define USDHC_DUMMY_PIN NULL , 0 + +Based on the definition of macro ``MICROPY_USDHC1`` and/or ``MICROPY_USDHC2`` +the ``machine.SDCard`` module either supports one or two slots. If only one of +the defines is provided, calling ``machine.SDCard()`` or ``machine.SDCard(1)`` +will return an instance using the respective USDHC peripheral. When both macros +are defined, calling ``machine.SDCard(2)`` returns an instance using USDHC2. diff --git a/docs/library/network.LAN.rst b/docs/library/network.LAN.rst new file mode 100644 index 0000000000..58bd61ebbd --- /dev/null +++ b/docs/library/network.LAN.rst @@ -0,0 +1,93 @@ +.. currentmodule:: network +.. _network.LAN: + +class LAN -- control an Ethernet module +======================================= + +This class allows you to control the Ethernet interface. The PHY hardware type is board-specific. + +Example usage:: + + import network + nic = network.LAN(0) + print(nic.ifconfig()) + + # now use socket as usual + ... + + +Constructors +------------ + +.. class:: LAN(id, *, phy_type=, phy_addr=, phy_clock=) + + Create a LAN driver object, initialise the LAN module using the given + PHY driver name, and return the LAN object. + + Arguments are: + + - *id* is the number of the Ethernet port, either 0 or 1. + - *phy_type* is the name of the PHY driver. For most board the on-board PHY has to be used and + is the default. Suitable values are port specific. + - *phy_addr* specifies the address of the PHY interface. As with *phy_type*, the hardwired value has + to be used for most boards and that value is the default. + - *phy_clock* specifies, whether the data clock is provided by the Ethernet controller or the PYH interface. + The default value is the one that matches the board. If set to ``True``, the clock is driven by the + Ethernet controller, otherwise by the PHY interface. + + For example, with the Seeed Arch Mix board you can use:: + + nic = LAN(0, phy_type=LAN.PHY_LAN8720, phy_addr=2, phy_clock=False) + +Methods +------- + +.. method:: LAN.active([state]) + + With a parameter, it sets the interface active if *state* is true, otherwise it + sets it inactive. + Without a parameter, it returns the state. + +.. method:: LAN.isconnected() + + Returns ``True`` if the physical Ethernet link is connected and up. + Returns ``False`` otherwise. + +.. method:: LAN.status() + + Returns the LAN status. + +.. method:: LAN.ifconfig([(ip, subnet, gateway, dns)]) + + Get/set IP address, subnet mask, gateway and DNS. + + When called with no arguments, this method returns a 4-tuple with the above information. + + To set the above values, pass a 4-tuple with the required information. For example:: + + nic.ifconfig(('192.168.0.4', '255.255.255.0', '192.168.0.1', '8.8.8.8')) + +.. method:: LAN.config(config_parameters) + + Sets or gets parameters of the LAN interface. The only parameter that can be + retrieved is the MAC address, using:: + + mac = LAN.config("mac") + + The parameters that can be set are: + + - ``trace=n`` sets trace levels; suitable values are: + + - 2: trace TX + - 4: trace RX + - 8: full trace + + - ``low_power=bool`` sets or clears low power mode, valid values being ``False`` + or ``True``. + + +Specific LAN class implementations +---------------------------------- + +On the mimxrt port, suitable values for the *phy_type* constructor argument are: +``PHY_KSZ8081``, ``PHY_DP83825``, ``PHY_DP83848``, ``PHY_LAN8720``, ``PHY_RTL8211F``. diff --git a/docs/library/network.rst b/docs/library/network.rst index 1bd7e303bb..361e664b54 100644 --- a/docs/library/network.rst +++ b/docs/library/network.rst @@ -152,6 +152,7 @@ provide a way to control networking interfaces of various kinds. network.WLANWiPy.rst network.CC3K.rst network.WIZNET5K.rst + network.LAN.rst Network functions ================= diff --git a/docs/mimxrt/general.rst b/docs/mimxrt/general.rst new file mode 100644 index 0000000000..d08e180e7f --- /dev/null +++ b/docs/mimxrt/general.rst @@ -0,0 +1,92 @@ +.. _mimxrt_general: + +General information about the MIMXRT port +========================================= + +The i.MXRT MCU family is a high performance family of devices made by NXP. +Based on an ARM7 core, they provide many on-chip I/O units for building +small to medium sized devices. + +Multitude of boards +------------------- + +There is a multitude of modules and boards from different sources which carry +an i.MXRT chip. MicroPython aims to provide a generic port which runs on +as many boards/modules as possible, but there may be limitations. The +NXP IMXRT1020-EVK and the Teensy 4.0 and Teensy 4.1 development boards are taken +as reference for the port (for example, testing is performed on them). +For any board you are using please make sure you have a data sheet, schematics +and other reference materials so you can look up any board-specific functions. + +The following boards are supported by the port: + +- MIMXRT1010-EVK +- MIMXRT1020-EVK +- MIMXRT1050-EVK +- MIMXRT1060-EVK +- MIMXRT1064-EVK +- Teensy 4.0 +- Teensy 4.1 + +Supported MCUs +-------------- + ++-------------+--------------------+-------------------------+ +| Product | CPU | Memory | ++=============+====================+=========================+ +| i.MX RT1064 | Cortex-M7 @600 MHz | 1 MB SRAM, 4 MB Flash | ++-------------+--------------------+-------------------------+ +| i.MX RT1061 | Cortex-M7 @600 MHz | 1 MB SRAM | ++-------------+--------------------+-------------------------+ +| i.MX RT1062 | Cortex-M7 @600 MHz | 1 MB SRAM | ++-------------+--------------------+-------------------------+ +| i.MX RT1050 | Cortex-M7 @600 MHz | 512 kB SRAM | ++-------------+--------------------+-------------------------+ +| i.MX RT1020 | Cortex-M7 @500 MHz | 256 kB SRAM | ++-------------+--------------------+-------------------------+ +| i.MX RT1010 | Cortex-M7 @500 MHz | 128 kB SRAM | ++-------------+--------------------+-------------------------+ + +Note: Most of the controllers do not have internal flash memory. Therefore +their flash capacity is dependent on an external flash chip. + +To make a generic MIMXRT port and support as many boards as possible the +following design and implementation decision were made: + +* GPIO pin numbering is based on the board numbering as well as on the + MCU numbering. Please have the manual/pin diagram of your board at hand + to find correspondence between your board pins and actual i.MXRT pins. +* All MCU pins are supported by MicroPython but not all are usable on any given board. + +Technical specifications and SoC datasheets +------------------------------------------- + +The data sheets and other reference material for i.MXRT chip are available +from the vendor site: https://www.nxp.com/products/processors-and-microcontrollers/arm-microcontrollers/i-mx-rt-crossover-mcus:IMX-RT-SERIES . +They are the primary reference for the chip technical specifications, capabilities, +operating modes, internal functioning, etc. + +For your convenience, a few technical specifications are provided below: + +* Architecture: ARM Cortex M7 +* CPU frequency: up to 600MHz +* Total RAM available: up to 1 MByte (see table) +* BootROM: 96KB +* External FlashROM: code and data, via SPI Flash; usual size 2 - 8 MB + Some boards provide additional external RAM and SPI flash. +* GPIO: up to 124 (GPIOs are multiplexed with other functions, including + external FlashROM, UART, etc.) +* UART: 4 or 8 RX/TX UART. Hardware handshaking is supported by the MCU, + but the boards used for testing do not expose the signals. +* SPI: 2 or 4 low power SPI interfaces (software implementation available on every pin) +* I2C: 2 or 4 low power I2C interfaces (software implementation available on every pin) +* I2S: 3 I2S interfaces +* ADC: one or two 12-bit SAR ADC converters +* Ethernet controller +* Programming: using BootROM bootloader from USB - due to external FlashROM + and always-available BootROM bootloader, the MIMXRT is not brickable + +The lower numbers for UART, SPI and I2C apply to the i.MXRT 101x MCU. + +For more information see the i.MXRT data sheets or reference manuals. +NXP provides software support through it's SDK packages. diff --git a/docs/mimxrt/img/teensy_4.1.jpg b/docs/mimxrt/img/teensy_4.1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0a09f68ea4f3e201fd606a10fe8bda30e6bf7b31 GIT binary patch literal 127829 zcmd42XIzs_6EA!v1VWHr6p#`iBm@L$O0QCq&^w~iyMR)qNCyR^gwRm|Av8fL(o_&o zkRnJg0wU6j6sbymqxb!u=Q-ue`F1X{zx}g2vzgu5-D_uNb2ff91JL6%uo?ge1OhOm zKj7>;Q@Ogoiz5JFv4Q|4006YWIRFgMkWd{d0U#IvB4vscB=|3;Cj$UF5Ey`w%G*eZ zo`nCVrugZ#Dg^NkZUb!MGDg|%@*%ANru$SfJ`9GEIvH`#{NiJm?G9d54pH}{9=TA?YuUI;BslF!^^tc>iG0zw}HbS!qp_ z|Dmz^Q?CDC*!nLGIf;hH>7Vu)NEqn^`ddy~ULYwa@LvoTCGk3u{l#R)h9nI6i~q{t zME)0(1_Gq)1pSNu@Zy{({^EalDFIS{0MOYPi5K<{1|vwA>K{y6l7JKSfBHqD1^$Nud$UwXRm;pq_06J>}I7xDn zk^h}4|8xQ%Nkv+)U@~$Dsal2}0E55~G71QUg0xGM9#Rd20U)Q~fij9Q@rtV&*ibU_ zsUQqJ!&xp!BoBPImAqz@Url0VC#i~zv^@UnAtN@WB@=A5SSc7qWGu&4>1D| zqXC3TOvQ%RGr1~!i&@<8``H9QO(Fp^kTH-%CrtRMK+GJrlA7zpitU~4IEU)%1O*Cy z@V-Gs1!xx0Pt?c1VS2E`?ymFHXhFPPhtoL1B7ha4+intT0>71-bgMG*+H)6wYw~#@ zgL$B1{KW|E(hmh)LDmAXoLT?J&y$S8Q9gGBg|hL@%INiw!L>GZw0J>nI46I z>yI~)YO8=vKN5c8+KqfD5n!6oE>ARORyfsN+<*5gLFX$%=e)wTr7ujp5sIdVE^~JT z_S*A{_l zex7jSGXLrsKqp2oT=9+P(pAW5NB^v^lrPx7gj44?Wd;Ysj@E8Q$8XKNk77gWedr$< zZSn>E);F7(j(M-I*}B_hlnuditZh>LJn?mo8*Vfm2dsUUf2DobwaYXzG<1EAi+eu5 zUYd_{d0nV1W#h9_N2Bu<>|A+8YM$54XCTYr5Ib_eV2JgFq=t(k!N*GAprz>XY(eA> zD>2F3K-@$dJ;R%Q8w&?mI6v37Kc0OY`IG> zO*+51&H&7Fbb&2l2Lq!DWAc~6@|}F3dX?M6>%X92EE2kBl?=-s@1)L*gnT7 zJlosHU<~)WOW&A}5iCAG6AYNJ@<_~p^CJH+=Q!|G;UuYu&*+}%lKPPT@9nrgErzEd zgkQQ-1;chXQw_564{^|-PwU~zzbd{ny^voio%1jBhPb7Aa|K-f&N=;-dp*Hd>3k!1 zgiLXYq2Jv>UG#Id5)>GH(ZO`X4LfN#4%*jMLEQ`#$9%#VXu|JxBE5J8w&L-KST(>hF(!3X;=)zCSP7lhps0T)M&!xP*mu6th z@O^XK=N);9iHgCo4kK%RY1!S?P+?beQF656NU3gpc?-bm}xL;R+_ zWh8t>Uexr`OKb5RdenKtkMk)5cWf$KJ~!+2vFIq6oWG-)xxZYE)x)fQjg+zZB6d{@ zxLMh-NwX!YUpVi`;Jqj^EV&*W_$_c%s8fh?&pFCOc0S834y{c^=rI9Rk-y(LKKCw{q8-GZu< zNjvwn#GfmBnPiKY8qg1(Yz`i^29 z*EFd{J`2j^nHlLhA`qzDhR2f`gX60|owS!MB=i+cN(6F6dJTRr6{n44X`S2b#vDE2 zn@vh{!VnpS$Y0T^dKM(e~L>leeE`Vrk9*df9j>!>0*y zfZPK6eDIIa)_V^u))WWXeTL`Xq-UVhtZlZpby%N{D0}f~7~aUtor-{JJI<<5@A~Qx zL^z|1OrOfUX=?Pl)l>F>VCs5)XxC?Jk!jwSMI3hiiqmuF1=uGk7hZ1-%DZ-}Q1Fz%JIX1M@1yEBN`2i9 zE3RZ}a^AP8O?M2GJ(>e>u)|_GaY;>CFHkD zOz)3hpQ}e_s~LQ$5Bwfx!%*q?CP%Iz!b7fC;1nu?{K_xmFYP%Pua|ZXoB8o0sn0aJy`KIU@bmR#Q=Ft-bGdtKL-;opT?s8I;qj zbZ8o(424o?&DV!n?nA7{Jj45qT)IcYpiGls%gz#1t{3aCim|-fb@xga#_ihndTuu# zw-?9qVzTEiNZz6&eyP1fxvk5DOSqcIULf4#x>)>4x2T4l~xTEdkX4ms;wxQ32oA;VampS(hc!wQ~ zp;;AQ+Lx44ZF>~+ncCc4WR-9DvbS%Gmo`ur`q>SRCrvBfS0COoip?gNb`Qya7OHk0 zvC9r@PA$c%m?l*-GAE>J)ZG>F2(&qLZBRPL|7cgy`}FSncf2(Zi0)PYpo?QC0Tf<~JupsjcUzIgS`{LUrW8o+#oBeY=&(i8^ zV`mp_^H~zFm*zV=Mo8nc=v*!$Y(R_pw3A=-lyyb2pZosS@00JR95>a#6??Sp8Iq4E zn;$iA)n9A8@TIkIz7N)(fpx3i85@?THoLR_%QF4@U82RIL!>mpxzrx5psKN2nELX& z-387ODM;Q^TqDay!^Umb>F$H4_O|~a`*w|f@>qxEFQpx%g3(DcLYD2 zWPbbMsofWsm*VB>Q?gCBV&wcQe3yQOf41%O^%`w8GBBgAZ2P2uV@c~knHplD2h!{+ z`jtBZmLAp=M?O;T&j881u2&_<2l)?lJ1YiFo`%QW0B+(%W+tYJZ9;GQqiaT0IJUY( z%mpLB$hhnF3u*PQZkj*xwn}xsEA}F#DuL#U&)nkD4H|+jsJ!9V2@LMKfqtirEzu|lPWzPjn5&-IPAB)tqOzVjWBI1LbG9*H=QPc0be(Ss5X^=da%^Yb85(wT;=RZaglL?LNP2?=?(-BCIN zbZh&DZ%y2-rfcatMwozkg`vg$4|iwx6LVf&PN`y)7-z71XmU%{K0>Q!=}oYLk~Y#? zho8khc!lk46HSwnwrJhx+kyf0kkappJ|p4?pY6=4op8GQ!*W@GD=XJNGR`&bd-t z`-Odzd-)wdMQ-$N5&xHvQ_ea%m& zRoK>Gm3zmCNvVZ1z&sW6%La4p$GfZB6%{Lw)IU%e4*K7B>U=jno{0GhHi{Q`Ye};T zZ2rRfDP@cY*=9s$M?O6RU;-YXT> zzw=u?_fou~;jOUk(|6_JGp5&#(j8Lq7@U{6d2twi{WH3QHQz}_KN-E-eD>x4F-sG)Mz;LFS(=dl$si;r znUWMBC?x z!cve!NtIFzBqJU~1||nnkUVMtltlICTOo`*+#uQC^Ow+T zMAWK0J*8fH-fuW6M(Nnde|3XQXTky^h+KnXxSm=}-EU+w2BwzsV7E z<->?CRb$WG*a@^NoI+@F!I=%fhvl-3|CM90&V*A$U8druEOm7gxk5b`(@|-9IXlx0##IVU%*BaL=X8kX->9lr z)q@mPq7!uSx^VIzWbgq5>*SD!0(?YI2g6I(G4Kc=EEHSJKospHzsAvOtX3+) z{W$?LN{Q8G>a!5P3~mks`&e?4-*CGK+4f5Vo!q7xkoHwg$iw@?Jg*%uAE&7?w8IgF zMvQXdsfo@_;a1q3jbzvpA zyqZT75WZyJnq;crd+=pWUk_WTnMtJG>wl(!5{l0UL;(emaFaN5Kz$zUX{yOP3$EF1uNhYu6 z42+Uo7|ju7se^U#wwK>?zD@R+4ZpE!e5j)kJJ=%=Ut`rf4K`QdxlFGluq^P>z4GqC zT<))P3C+9N_b5;-_Ku-p-^9)Ufg*bg*JC%kadycvEw}K{WQP9ZfinPSQ8VL@5Ih5T zae+D=+=Gb%kz)OR<+Qfsm$Hv*LjiS{LNw}hyIF-3%r$ptgc*WpFL)s$d`=@6`Wq^pux|}rKvQ^2m7y}x*fT(| z5&%DJ@;}s*WH-&Gq?l&tj|QbQAQEAiauk^#HopD6U4uJC=rv8+{uIs?0b_xfFii{S z!WtPh|6EY&wBvWd1M2$IwC@qkK2cA4yQ4gX3r+-P)tK5MryFBk{nu2uh0S4&SGGs- zEM#YW8jmNPh7XD^3)4+i@R29u4$9=bQuqomNh_*b=4ERs z1hd)AtLi(|^b<1k-koLH&up-K_&|M5K_(e7fFa}ny&P!Nl^*CX#5Z-Ag0B2&`(^BAQtpqv<#1ig9+^{b#`Ma9#FO3|q#VyZ0X~9U2K+-{~jN-fHLe70(fD z(L*GTL>&@zz}uCe5Qe}Ov<&%c&fzdv-ahNvn8a%$b-2pZ7=PZ24kvEsvh0*v)O3U` zJD*IZpgvkes`C(4sAP8~+kZ1rQTxiHE)PXV?NU*I3&XvH)4hOTrtD!rWz};k1xhCr z}fFX#6a}l^Bius-P68^1(s1MZ?p9@7T2SitR;{H?o6%E&BUJ z&F+qcuuy1sPl$LaS;S)>khum*5a0C0q4d_XsJKcs5by0S1MQZ@%;R!JbLdt)VU$o_ zwAh9-?aW%zCcO0MYvTY1? zB%XXc1E@a1a9%O6!^tl0&#!)I7<>P!rd&hUlO3;qfZmlirDD|N=y$eo4pTbU)gj~l z>I`Us9+@7h*`c z8J@!Fe@Z`_SX|r`D8nnw{BR);#CktdEd&WV*baCWpzWv5<|e9Z_A7jelD`yiQ+Pga z!`UavBx(5MBRj9wr}Z;{r^sR0W%k*(udGfiDHwBafG7me6q-9;*yy6^b!Fbl_^T=ivJ{Jxg_;QGaRa6ru+X5P45u@lF!@G{ zQslgCSRe#x#C;48{Tx3@^CAo*@5{N;0(@0iNqLY>D*%o^ zuN)(DPeH*r-4I#zi)Hh`Ym;0%v_&wWuaT9>)2NITrArAL0g`yEy5!kl4x#c*n<4?hPg*OlkjmCbbJ zZV|hRbhL#D_kk9|liX@}AY4>iuiubGQbS*xf+RVZCG9%H7h<9dclh)MM&{mJ19idS zNE9uUVYh;*hLDWRzp*e2I%YhfH%Rj@D?MerE%4!1SblkAoWW)#An*kjM>NZ8>$55Z zE#yO_%ktq?!W2p^vV)-%Z?-#4)o=Qoa&k=g8=RE#-+0JOHq^njAUpkWMds6R{r$?i z!C12liFSGo37? zyVhVTKlFfZKF|IZ6zMZF}48(qb1T^^OkXic=~> zak5eTOyFd3e>|Nlq-F~oudt#*ae~YB@u@)3`0G2KQtsbLIbQBzVTA5fxQ|n2ErxCX z6b!gzGG~?^b*ryD42P6H8eXh2>*RVo8?sIngd|*cySIDJtZX;m8G>M1pCCHD%-qED zOTS%UVF<2M`lVX+>(&`ia{Nnv-3(O$cK>Ti*YEE&MX>FLwSz+Q**o_i%x*~2U!^Qy^Kj$lb#hZ# zzC8+iY)^6{z-A+*!nrSSM5e_y$NU~Xad_4nn8+^|c$&7VermzHKFIQX^J=%rb_o^N z=__W>nvIxcS^e7zT_YKvk~F*PvJ{U}Z4=02!!+etOPPi)A*F9H=I11LvOLX6L|1Wy z>2X_l$`e7YoU6!I5U+pkXlevZE-Iv^5RAyl4fIejYGY8Q!lgz8%Mo1C!UfSAE-eKk znvrel!q>iK<(Uh8xuCRHz%apnpJB75;Csl-j&j8Wu9izoEtb+B#H})&grI3o&27ST z>5WLdM+Q{5Q1oJ-mZk-tNpppsL_TN;603+hf@7pMU?<=`T`ld#siaEpcvJ>G^p$RtKbTqCxBJw`7AxWyJY5bOgL8F)1vOW0Px!%D^c0OY!9-XUrL} z#_U4&3Cocw)=n`R2OGNA?x=tcR|r;3EQw0li&P;9?abQ<@dze16;;QITPL~Vq7bQ> zxvmz*{!9~**Av<>aLgL>)8fqXvXNE_r`iDVSUhh$C^dzzvYa^+SzUM+emaat1|Ic? zg+E2uUiRDDHanrYO!e%SdPKeF_Ik@1@E+Zy9M;JHOPkHc8XVLv7ptrxGJpn?QF8no zfE$Tdj37fo{A0>J6w2DwC^O?QK!?O0b5==b813A2DuIro@q8LxNZTM-MG&ck!$1|# z3`|(G#O~&GRYJhQTy}S=GG_z32usFeL@6F1$5_93)dUZIB0PY0APZPurk zo+MBWbP2CKu{DP2*Q+1{ha^J3CXGuVPOX6oBo&4)sWt#Bgp}TAS)Wp4BRZ?%z=7VJ z(P(AnpXNF5Qcw*QEX>8fz0mR7%xaNZ(4nDP9t5p%pkYKe7P6(et_%}V^OWxo792Qc ztIU(SU^TZ zt36&;RX^>l+>qg>um}vN>>y5xZ8>Blb0+pg;7SmyXr9{ce)**iunRVZ%$ed<=d1vz zl5*LWpwYL4MWe2aU}0_?G5twF;vgReCh@lI9c zw2XNHs3$_VL+c|IkdZZ^d*NH!A7Jp1#sRM| zl|+T*iw@U}r-*oiZTAwg(R%{=4zIsg%u6P}!I~+K%qKL-2D@N*to+xm^WEQR$s2qN zOAQ}N+*=={9qtW)O{H*uPFIF^5Kop_CzyU0lvMEBd8Qni>f;&xPu9L-7}|lW{*W+j zzr5FepR(drk9gX1E6}`2kkk@vQxyzD6R%cs2# zu-(=L;}`ETC3`8ge;{&%AS|(`CQqkQ!<8&r%nX$KDBgo(*2fL)JUMpDRS3alZ~?Xb zO=XxuTWdD7oc#=F$cc7;$euE+nTaf?$E=KouP%Iq?H&l-{u4tKkob0}E$k5y6_ofs zW6TqpJkdB9%vEq`*bSQJQs*7NjANwO2rd(cl3O{K6O=AnXv|_6IA52f$U~7(h1;zP zdXh8Rq_v%jWz_C-b*4sAqIQc?bM?5MjdQQ)zJMRh7&%2{<{)xw#l;WJFW~A~O{Ey= z!^Cn8bDpBMakX(Hb; zDEUMP$Xw~QE9GF|T)Fma?I4dL9T!XQ!b1U5fHukeHkO$mWJYu#BQDW7R!U^d?QmQo@1vaPy<7;%?id)nr~}Eh;in)!$QaA9yRHoKntT`dQCYD%;K)GZ zHztRi?i2C(_9y2V7A!Kh=2|R=Uy(z_sv~Cir{x4@DG~>du;opcf%p+!`AX$tp_zrW z)V}*y){84fn~s+mA~ZgO9sFSb6RR^KWwDYZHy< zDbc(+WCx(ByfeV%65S%q;mZ%|_KT`%RC944ul%Ri?WvwA32v_5qbw*zyOZsBxn#L~ zbU4m3I5*{#`@$AR&dbE>0#BII9#j*w*R*!YCP&i0)ZU*>#jIf6!at59pJ=(j6gr0Y z@*s&j;&P^{PK~CT6`q@x6Mp2h%+%~ag{u#-;=&u13+wcnb`*(MDb1P@!6jGo1*N3( zGBLl~2$ZYvf%(MdugFM#tDDgXR)wTmR5t}i4BY4d$0AA7{N7!FIl+m5eNq+1p?r}5F1Sp z)ozKVE3{0XmSMdG5s<*dy%%={NSF%$At0{4_~jfHCe|VO5a9TMI6;RHq|5V?QN`0) zmG8|O)OIGSRYak80##Js5L-BJD^~k2$g=}mU%#!3v4ma48c1wTq#^cJ#!b&-Q3)+a z{Xnl8u^5WKiJ>k@R!}pJ*FOX9)@pBG)Y~nYNK1V}p}}lekzuu4n%UMKY1B%IIW-Pq zGh0)d2}Xxu*QN$P*eZk*QyRD$#|ZPput0rR(Fz=qPREwg;4rS+Y(;`;qLC^l=4g-% z25jtMvu`p+ZlEhEMd_fG<%QKxr-ndfc_Fu2OcrYQ@Zt?i&3Kbb&lxMHm}7E;CNWI%W6h#5*%S^MvMRid#vvmy7h`e<8&h+p zieKk3%q;y>7JunXo2H`kqT}@&|IRa@d|)|xcVCiE8}V4`5cVc0>DV!nKevp&!9cO6&yGXt1f#osSqaIODq!WF+z?o!FqtC|B}RnU?4-As@UV zs7B06S+|4YzX-jn^u?h+t<$a-QqdUpQ&T z&wwrRE0Yv*Aq@{yTPX}(ZZym?9h9@TdC2cz#_i!zZ9BUafOgPWm_9|IDP_)Ln1*&K z-)jy}?4;su0Zh;G>Dpwl(i$eXO+ZOmkJa#x7wMl9*Y!86lEm8-`vobA5zI&FQX zmAi8&TEi}ecoP+*MV3gnTS}k3E2^6|@#M-IeI7WMl9BpSgn-9W3~SMOI3pr_G8>6F z0}92TheTPkK_WYFEqXxC?QRGqxQc3(ZhOVxvV2I+aa_xCDPr)=iDqDje!z_1Gq@-t zD!WCuC8Q=Vxdlnc^TRO6>qu52VRgDcMpopvvf#l24a&*82YC$(vNIq( zk*FL;JrP<+a>9~qc)T*bQko1%GX-G(iHY1CjBYKtV;{@>l$i8FS^+I~yvEV9zi9wU zSuy6SCA?JTb-C{`Lxo8}n@Ga1I(b!_e@DP>YL5Hn_mWjIk>8qq;K1~X&)zPqP;)E} znYsZa^3_WG-GeC+TOnzw05V5-EU;qEUe2I4cd@Jae4D4`GM&w)4iA|(HvU#esOIm%s>Vsw-Svs8Y<*pk`S6Z( z9g#MjYU-?TNj8h)fvsR%)M?J%s?U~eiY3vdWf~E>M(dVBbqXk1VGFt}H}Zm?XT^`O zo&oE$HtGvN1p@xtRW1yh^R+0Uz;LdITVA!{B0+PnRDvRV*==*sA zfu?yV*+!82E3z@cCQ_gK;L*iHdKN8;W-+TyAE7(JL!?*xxp}hPyyl4>S>|WI5}G;V z)|4j74QIuv5xKrY+)2<4RznDZP7+Uvq+512+mGa8Xs{9`o&jT{jlLpRl$qy+O(q{| zO15oA9Pr)>YjUtA1n4}P*F3=%JX1l>=on9b`a*NvvH3~8YV_lsWBZ@0YGi#zz3jRk zU9gm3_d~_py}T~i7+_m{srUsv-CMQp2 zdIarkrq0M4`+yjFr8XKHDFLOU;dNfk=V3o>wyB2Uf;-eKD_;Xi3s1FV?tVl;rvkaz z&S&y@Q5!*A_&W^JQR%@O$xk1gp+gKk`u5K|f2GU^UrdwesI2hpVB%b<$W)nOLVYU~ zn1XcjD%M=Qk2!vXge^nvl_R`SFAyd4)bE2=c$VP=?cMOvuy5SS@9no_h!K?m5wfPt zFW!UTN17I`_W`hEta$O6Q!;V8 ziPjy{_?t^B=KN^JK)-AX;yd-IF#7`r%e&giT}OkA-wi0>X7$CMa>e8N7c)BL3ggJy8>L5t^?~ zn;3C_P@Q(lUNK|dvksiw)A!VLdBj2&ChqP`2UsU_(Z?A<%=X9$4c%ZjY{fzhqYzf@9c$zbsChYyV=LIm z=__-KcZ*RKoG$0|f#|?!f0XpF9Ij`vYzBd!ppP(f8<`Y^Fa(|Yf7XmO3B!=F+Hq_vG9gHSd zS5R2t->_BOO$bmOzDFx00OFf6vS}k~$C>M=AO)yn4PX%Ou5xm&0n1oK;?p(q;Y2!4 zQXEQ9(S`dvGh2ad&WRY`0=wM9JBAJUttv{cG}c+euff6kpHu_&j1K$b$L_`W&<>f; z(v`iizXW#5*-fL*SMyoH^ygA2g9A=(%SrhX!JxIyLMYsuY(;rHfUktJR0zTKa{1Q2&R6;m!T`*M`5x(NiQh6LYWL~nF&uz+_x$M;F`O6 zT_uYGlBv6hpA%?R?Urz?TX%q{(hK`mEPW`B2Ah*2N_l9ibuC4H5Zw4qvn=lOo^OQ0 zN_ggW>mF7i0qGy9rT_KFmpP&9X6p)u4#6Dl&3wUF<_x&USZUobp8IlDN}Te5`J5^E zi5=;uOcX14mCe;6gP`NOi!NMY*rgXTEt|cfUwA|vHJhYi28us5&RLHRwFF|0NAQG~ z1I(#uAfRLLiFRzZT08`igBDP}7`3xkPEdi|RVYPpZ!$<$O0+RQw`RcjI(;&LC&K2| zbuT4`4bd@G-EW-;3{DV#%l#~$HeIfAv@C|oO^|N%5m}!F9Wmpr-gs8hciI|z1et;h zzF=83P;<8s(kBQpWj5+!nZ+^&8eW!%`z}_iB?2p0L7bs>djO<$F$%1S8qT?WP9Me# zH+p2o;Y%b?g%m|7wrDYzvi-)!77ywHuMtmZ;(xH50uacYPveQ z%3V2%B`dv>`gEu7)MP^@#-k&*NV-R*y z2Aj}i+ApOrh=FrOR4pYh77z{y+;cwWs@9f{gA4ug`R=HX8wJv8%SCUN1>0)0(IoTXL(|q2d95Nip=$K*t3LM;oP3Q)R+@q{JBRX==pVzVZ0pH?9|v@ zE@qJz%_i`iErV-jAgsL+KT}kXDBc?}$scUuz%Yue(gCX$ zj+IL|?3biE(k1;5S>-6q_g0AAxM5Sb^CuT{&OPosuIAhrB^;$c%Y7h&C`R79#5G_} zHjD@*h472`ZLq(u)fzNFSx#$Q-~t3xQeejdJm?@VBmBlbI-rT?q0TAnmE#R;W0>Lg z?_Ht9X4lInkJ@6;wP88v(W7!Tttriv2 z=3qI$C@kr?j8e@DD+$er5GvMfhI-tF(;XLboX(~uNFS=`f3~Nx%a05_O)!^2?HC$o zNsPwhxBDHm{1NxG!Ie1#u+VwJY5&t}t_Jm3cc(i3Or4=hlt(_ync31_$`n z3tDCa@=f18Hm0pijXgonf8x9(?{o}S0yn#r6e#%R2MNRY4ymrOa>Oa`NO(wF-!;cD z`=iz@shEHWSueExXiYyDMVA`=*+DH)?M8m(=mof5nfQ;RbAD&Q=R~A^3_fTSXSB

O^LHN? zEIxjH5#aS~UPPajW6PxxDTVrF@?^eK5yyj^qGb4%QxhtX@ayd?6SIo|$cypa7>oVVZmczL4*MwYT) zac6A<%`RUaVotZ!*n$WV8UMyLWcku$uF zb;}e#f)1t!RQy;^cH;A3s0@p~L3p;RaA4AV(n~BWryb_QQVxwOPEyky zlRO$VKktcH?O%P&@rgj8F;4g5SuQCWkAF-(fcLtjy`|oK>T4~h>WDcqWg5Y)lfsY% zq#uP;(swKx-s6Y_G||-ePSyl#KS2nOKN5mJS^XYp!LD~S@guO>+CvX}kFP>$U^b_KpIgeMj48^zOC+FgPd z;?l1_d?8WntbS4nn@{lwIQ1c3Ujl&w&N=ikvcfetH35Dx4nx8OuDBjYqow`MoPNT= z47epuP?(e-z4@vqlk#mXX*CHKaxXiN_8W(!%}BAyd-h&RYO%WX@_C4o)H9dR zHA|huJk!~?pWvFxY*u^Om7QQzv9~3b3R{@`Lf-1_Y4=js(!B@|cizDBX{R-O;8BN; z>S(F+%K7Wg`dY4g+#$6AX7KQ?!+sk1y!z@nSHou^^Ch%J&{N|fu`k7Ob@Ek~x-LTW zCw1SRQ;(&NdIZy5?>ML@8S;WMWN%7caK_ZFg(*%MR+lR3KHrVz+(-D~-*k`n-j$!^ z?5Gh*1l3_AD5{ecmOBGNzlKHE-Q_(uzWuTq{EIo-V(|c6INq{>e=QjT%2X<}0f*%C zh6jY6h*Gijs!YF?=)0);M*kB}wrSy~LdvS$>aMB$XX4cAH?@?n>M;t?L>Qfvc5j;; zBk3%k2qu2K;oO#AFCk1ln|kjeS7;-1#R_vYO5X=l6((1DlO%2>KaUH9R((iR9Wp8L;pTzS6u9=xOdg80Y<%bU7$3!j?%=2u>SXZE4y*I|S4{o3l;CSu~%LU6|}QFq{oT^{9BvSWUoQ&aLG>DJwD3%0(u z5I6PKglrHQwMyX)Zl(Bs^)dM;F)H5R;FIU`*2KQuyzE?8&-KKB*G{E#JBc-8-U_=R zH}=FX@kvrO?~9_so2*uFG>dha=ToNckmPw3g-4Yf8Wt)<8Fjg#cPorQ47uDX^Pwgp zl`OgQEaqpxh){MVn%AC@uGSwQ-MBna=k~XigP+j*|^mE|vy)JDmHvox(++ z%1@-2*-PHU8EQ6C=2=>u0ove;pGx-X09t?flTz9O(IQi5_ZFRDz|6pICbVKY>$R$h z$2(MnfOgb!8#lihF8-B8$jC)ulUjPbX&`pdxqPwt%_Ds|aBXOz6Rw&msuru1#&Enm zXoJq3$iWQhJqrwTkf{yz>!*7@qD1nLF2xwyept>g9H`k{5f3tY%c$1Dn?asK7CPU( zqTy3E=r))d4-ow4R51$ZH;+iIc36}=VBIBPJ{$h_>xKQ+cTgaQlCM8 z^#)!~XYp0NIAeu+r5FGuMOqqu_#c_2@vSQhE1kxfVjMMaAB}ETn~*|8Nwv(XuWtBz zjK!>eCmryTvmusq@m#kgIF-=(33c?awRQ@NiRbJ+u*}kp##8t*mSc$M}0==bz0Ey!qX}r z;Rn`v;pcJ}nBQ8p8+hQO#4ZMv^s@JGU%q(P$z|MDVdcDrxyAsWbCKhgRct~J2UicX zn{d^;zzxyc!!E**n59I=CWEemmQZhoVzW1AfJ{T1C1?k>McS zJSj)+QpS?~a*3;i2_i~@OH+n|!OEFS!?L5erkjb1BMZ!%2-uNXstR_$=(gwu4pdeJ z6Bx}e2Qzhr>BLoF%I-Tc5v~ zI{y<+)AG(~`D%{yDm|~ilAbGhrtZ`zy)T#dlhEI_X$`5(N#`l-hkGRX2MhhQ~HKYsZrT? zIX}YN`LEgUMGp60Vp%t@^eoX{7iw?;5~I@Mq%*^i0W)#1=lvJ#+!sg}2kOMe>R%>8 z1Q=RGLtQnbl|JKVaB6)ajFVMMvYMOwTZ?s zN>RmKAOiy84K0KdfZ34YW1L-mtxekxJ+KZKAI;|#)k#tK!y#$y$`LI;j|8Xe=v6%F4vsk!+|wM_5RpRi zR;co2p)%Jj!O*glOqY5)I50(7?0kuF%zY|#41fEFmNZN!J)7;7J^`s|&dlTE* zm$%I17>dY7(tTd9tuYx0;J}kgN>WE1yHb+_->*^Lu&_X$pK1r}MZHlL?)?GjL$s?t zUIxWIe}_GOrifmj_g8lS07AJxV^DOe3bfudl?4LANF$-*h1kdc0C)NcY}yC}tM#P> zNh9e>2#;v?uO%cIj%YOr2WT-pd(uoOkY{(iQ-luun)C+BiKG}38&u5C74IvLRCViF zD;tMA8dwHBB80{?q=K*@^%7_%lR!9T)RK0WU5~Hv6e#y_6gcCh0MY47I16x+N=n@# zdh+V3NtmVj(f$KSNk~^x4{C5AAaT(9n!;qGn3~T{c8~|XMA$`0d(U@qNM;7WCTE$S zYglL?v;lF!wEZ-NOOX=xjhds2L|q{tFG z%_=9-I>iRGc_^6?2i~wz1fCBR=P42V>Akyp_oOokOoDs;E5sjeYdsIs-j-1NWddr2hbFa|2=Ft9U*7Dy$@go+3W93d@~KnO4)wYyhL~0ihW#`lI}X(hnwM z?@CDu1`Nk)@(_3g{;^631P}nA*tkMK+co0^NI$JCAS6ke@>9Exf`N^u1jl})*MfI< zZ|r^zYSp z8 z)Q~#QdQ=%4{*{of36Ip!2uK7IGHWPK>En*onBrRkaHBmUgKEM50CqGJ z)8-lD>AX!H+6nJLwK)RCpGsB!@Ye_1bp<#6n+PRAAp3h)qpuMiXvMOJb&Lg9^fe= zN7{_f9Vj(~BQuczpUj@UI#FaKq)~%C9hkCs;B!gmbQlTxQqY++u%GV6;YiXmwofK2 z)7!VbDQM)7IjkiiC-k6@kOIf+ULb?P;PFb3nfI@4p2U&Tfp-IC+bh1YgXs$jJSM<8j;_|x!(o=e{Qm(TCX#U~|9jTeLyult)ND>tS zvCK$=?@B=gnD1DPq{rTaSdt|2XO1SZ&>25jr31TyKT1ghkW&JHJeUBgW<_~w0R+!K zYC$0+r6K_O(n;n=s6_-othxUHt?u;y0PT1jj=#j_k8lz}0DUUH&xIl{~RBqF^Ke)S96; z@kQZsf{HadRol|h;M5$_?#WM8O>b=ElQ!8@b>PyQ3MnL~iLFKd0BU~?5;@dLbKhmT zG^V~Pyf0Ep&ZtTws9R(ZKYE9WACY;wah>Sf$wH4nD=KV}x@%Q`+LOaHr3yNUMJwS7 zO}dCz+AAmGi^DSEZ0dHx!Ge`GK?mvyp;*31y=I*(xv%WK<(c1_!*!7BlOO%;(T0+s zm^|@Xe*XaCFT<7OF_);-HtV-x#g(ifP$eV|+?k?@_?Pg|r0vx$6Yu1#kLgn0jMVCC zbCXj>&M7efaDD3=OvdWfiuk4Q!H-Ltt)0<4vavsEcGu!}!waSDPOY}&PbpVQThXoR z=v+CYXKWjK^!Kdo+<6gNz0Zm73tLvt?R}!YiAs$)J|_GvC<;2x#Q^l*B8})vS3_Ce zjhobQ#UWF;j;6Jk_?+;uz@nXNVF~JeM1LyFKN8*-C~X?X$woSatNke6gUwU-qTi}o zCdS$mzHeB7IGxMQSl++kV1*p z7C?j8DE|PJOLD=QH`J|d$r~sG%u-38uiCY@{iVDwD+&JFW5GY-3m@fKPsEpo5yrBy zJoSY~^r-GezcW(*03>XHnFd53r6dwKkG*Sy@h9P!5_Nr%kt1kP4;to=+dO3TS?2uelo6dy&n+X8!vX0(#0Q57M4f4i2<)#g z@_%Y;{?OhVB{DUhG=bNZQr^BswN$_JMcXyJ;%@o_<){uh=~=?3Za&!tVJ#$c*QIOx zx5N*J4&rV@TF)KkDFh^LAeDCFDcpaL{v0)I(`(jP-RW&IH!4RTYLb%2b1#~uTb=0H zVkRf_q^Tib`&PaG0JJZLWpVwr#sy4mB~w4Od4CZe8NvSm*Bp3{%_Q;ssc(!Fj)T(O9CjR4I_KgW!*w#^POz~~;=~7dU46@W!8~Kv#ZKU)u&wBRo6nj>`SHu^G2$|Lv zNRAAJe%&cQ_J{D)rhm28Kt#+D`$(m}KPH8%qwinYu*X?F4Pjub+ACf+@fYE{iS+8L zbw_a8&-qe6?Hl2!Fs)l^nd8>l!2XnPkI844tL{d~4bNaJ#DD@v@~w8Sh`$Vxu(_;k zS}IQDhXRsHeZ-ERT5aEn9}Am6D%N=TS3(qNZbg1_h5l>%HWR~QWFBZnl=b0QFbA!{ z07v)DQ`PY&;g;nm`(tFa1oR0~zoiS){{Rs_8CYXeZsxDF4y9`Jl&X1AKHplBk}}Uz zSBRy#VB4v0l4F`sAol)z*0V>%w}s>%{c^J%`AUcRSC7P>hC6qEY%I!8LII_|CH{9# z{{YQ@X38ujK#jchn#Yz$dEzTp{{Y&D!n7=dtgTc?k+me#pZ1{ey(tA+wSqp4tD0NU zHLIdsrj3}|PqasRU*;rqtysT`UkV#pAKNQZI{s7pQkC&T;a~%Hv$GS@L8rbVddegI zYx_2NPpD5xpn7GWEm;*z@4In-y;2?cr0}VtP`zPl6qCr>Xf`k6Z^FztyG#19k>}hk z)F~|~LJ9X2D-n`fx*X!v*Y6=}=Ns0ORXHG094E z`AJDhA-}gnRGeqTmW9o@<9O?=h3cD){za0Nx7vNGxaY;kh8S$NL+7&>Y&xKjT6Wb* zQb*X-l#z|2&Bqx}q1i<>%hF`|tgvsP(?@1;h zHcBR)PsVSCvADmMC_mr%C2>o71+UY-RMEWvAt$-|&`Y!@`&VEA^&4k+?bKGGN5ovIAplY!8?95>HsEZ$?Y?`e`NWUCxP2cNOB$ z-8_A(pYA?5JTkTlBk@;)I;|`XvF|fUPscBZ3VX>7W>)bM5Svh|pW-FdY$1jGdLKd`T+d||ePFp}R*0KKpZvOxZ!)jN}UO?;Ui2hV~ zvH2JLmXbV5#B>=DVj(q@@++R3`0Mb>@n~w$R^ZhR+{*)8F>U(?mJt>3v8-CR8oBt4!bhI6!B%iH#4zd=z4KQLt+FTmCX3x zj4KQ-82OqXC2Y;G2tPG&lq{aWAo5A5XXDQaY?N=&**{!_{{R>r$J>v+9wbU()ikuh zDbEK}fCz$XBdV21h+3rE*Qj`$O=SZiZaZ86dKqz(#OlXv}yRjUgDNpkZfhDOsceO+dc7C$86)pWM6M6z@>ketpyk_akHeK_ew(1d_z zpQUn*zs8adMdvN_i&w5awEqCRWe}u}I!Nkxq>qnwElEYjh<%pm>kU2H&-0NIz3tqR+>l%$1fj1=7Mtsb4T6rP%yy<}z@r9}99fO_3>a4;_Si zgAq%`M5EPov-)Or7%)g6L8i(8J$HR8mu~(xvX;t9E<87O)x*&bUnK;OvF4e}{BdNk zWwCkT+pKwlQ8z=>KJ>g;mP@H=qPdaOfiW}6t1=0n>s+2bJ8qE4dEXE2QiJ(aZe%O% zUbFb?y3Aw~V_~2iy1a5i8gBFzan!_8@nTfEwvoIk@^r>(6?#-&2kQN zzY9`NPC%Hc7M~u@WDBClM5gREozjcgQpeDBH2g@A{{WDp z4pMJ}u2ICyeJcS85yMZI(3UNfu)P$&Pad1O2kFvlr93m%>+u6dVs#0(O{#JX zhkS&Y*~t+-`csXmyc#sULr<)2Q#|0eM&ZX3#e2#T&mypsx=1}bQi(CmTZ7UQw2u4^ z)s+sAe@gHrTp0c9DG+&zVuXyQI9W9^7e5OFA@r0n? zI-pkaK?Bnr{*-h3L5v8KlC=a0<>Zg^sUCis>eXwIiX|k2)3?^Mw4P5&8jVMeSp;8O zOi%n&{{XEl=IrO(Knb|#(cM2VJ$cq z+qvRYe=#zk1cG9;9&g61cZqsaC1a`eE%MoM!Q>%0(tOl|z$9`({pqMA`8un*ex1^m z)X_q|)wli}YFyST)#g;I8?`bn7+6vr+sal4NFam9y+In$cdz`-!TFqn>ML>g0Ga)& zl%|sP^~-|c>zO;e+k|aey9pg)0Ew>^p(-g;>2HZl_amUGI?`MpGP6Nl+K&U>KGKuepY`zLdK+nEg=pO2snsiDJPCQc?Zopl$U) z<~!9>;Y-JDC*h7;BW0?r?R5qfY$4R4=8pE9Dg|n69u!XD{{YKdW5#cY?+sqpdU5Oy zO|30pyn54e9<(mDNnC-o3Oi43pIV00z0aX(dS04}5>dfZv()&X8}!N)i=V?agDYEh z&nC?XMe0D1r6lpQ{9MQ<^sNDr$*U2Wxv{yj7I${ec`H(SPeqiN=`;FNi|u!1rt*1r zG`>~bv9r3kHW?-yLrkG7PVJ$kjmk`@?)rfpD_>vgjHW{GBX@Ut>-RYWy5VjBt;clm zIw$msl36(}siUP_qo3&7cua-~_m-TJ&7H3)W&37#xZw%_1kZEN+P1q|hXs_uy1SfZ zlk7IHEev1Sk$3bfaWMn0Bbw$sE~2@$XXX)li+5~UcJT$kAg}VI0o;lGE3|C>B)mSm z@b5KYcaFg;P}$+q&8_eYD|X>X8x*n?6THvqM$DZ?gQn@~rwtnS7vUJ!uf6@6Lsq%v z+Z)SlMe39ry=S2fz1cty%CLIyYe?Q|cJ0#Px5peU)w+voWV^kP!#wt~=2^Ug|~4k(4J zs7l8Q;LPzePi#^-?z5q$sHxY+OGBP0>L)BwWX@vs>sQoMHQS4n3&;e7$tnYDGmg@1Ws0zmdWlU*C| z*{eK0(3*nnzY-bzV*_%O{Ek(Htw!C*3s_J}rU~GWr6m-4eHW+KDwURU&eOK=8&Bsv zHcm?8L*lHmX#OXec%t6+f%K@Y2aYn?x2}tK7_-RSsN3G8palhPkW!+f2t6n1S~uc1 zCw}Iy#iH%&M{kXjiMY72c`Iqu2Vns_PY?ui1o1T`6pdY1PgB#$!FC4Y{87KgI32fh z_@i=C-)h{`OtXz^Ka*)CLA&PC-Mz3Jl`m{=Bt!$nbN%D~QzgQs zanGi*p73f=pkgc*z<#Ly?d>6 zj%VgeSSyQ*g{4el+^}zC5AKAC0G@f7_o-ee>Wwd`G*kIZTez%lqQ(;K^@1#mC(GOd zlBU%sa3_K|G*9AFE2WjBS9xKlt+eJ@Id+CR{Y?38Ic)@y>2QM(ME42zHK6=n`OIsW zy-R19$2u~|`8S%!x>e?<%aT@-B!M0M#T!3krs`T6l`SuUyibX(+B#Z!mLIis3T5rg zg%txZr1aWmDeNAzPD>|YjmBf#xOnbD+ZKVrv?Th;o@-Y&xoq=W2f*w~vRQnatYodR zw?W1k58PY^foy`MAbZL7rLkb*dS-??o7HlBBfI<$(VidMyw(d_kRT4An)ewBz#j?>4A)E~Et|L=q-qM?Jr#D8>)i`Mk8Smbuj~Q)fsu zrE&T{J(0v^aSdd(9j(gPRkLrD+Jr#ZqGPE%fFOW*ir!kf&nw_m-%@xgZ^mI8O#SpKe6gG3m`kn!@TV4dzDv*CT6|aXB07cvH%`-cmL#Gr3_Yk-?5W zmAZU()mECPPiahc7S78BYOI2+u3cN|Ls!Tt{tBULFjMOs@;L*JDk>{DJNi0yo|^Fp zn-j$S4WacuHT!#fMl(xh1-G216z1j&Wh8~6eFr`N0F`lCbu(;~mu*pShTJUgBlt=n zkrFzC6ZWEhG-Psk#?F?=>K5|H7`BwcY@ycnDG*y*lRlW>bmFy(PA=oiwn>C7Ns-!n z82eSKD+kZI&YBX`ZA%8nz-Ham8Jl|xTUmUlkcL}I!k|b@cKh-LY?Ns+r>+p?)|;}) zJqcwNPkE{@IJHE`DTp1%6{0k~-cLwlFY`9nsmA1k3U!dKTqshA8+htMN#?u10k6Cz z%=jT_i>z{gwT@}uoEd=mQ;EAnWkEtzR3Shfk=lh)XHC<3UY@GGODxxVJsuk8vsYSk zQ0d&Ri_bDrrkuuqzM;ZN8$wnL4xM=CtpxGELf-gmduMfRd1Zh=bpx&+03|?+~8Sxw|b@k-f0buIA0#2T;?VYtAhC&{hvfASda?Qv7B3fYhEN z{wB?|W;4oPV+zj<35zp3|gnJR{-9DI6oN`qh)a~24goc_>K?8bQ3JXv_ zm-VgDq!p`mcgp1KZmjg46e>~j45eyO2dN|yvD$i9E2iyt1;CXdv@s5nw*qEP(ggM5 zw!eVhDWe6h92sq5_M63gGaGNT;9 zBbqVsN8`hOB5G_EzB?yt@;0ox+wJ3SJoc2J;R#a6>ZmjQD@Cj@&B{@1>aJLAWl3zL zhQT076Yu_1)Kfv!^z=RJ9HV7mb)|rHi)V{AN_k33E*$8EM2Y$n$Lm+`3Nf$ijN)u< zF;=+;UAJoG#Xgu~+;U}V9LfIx2qZdc%^-tFM$L@3{tFzU*Ud=1R>x@O6+J?bmn-nt9 z9Uv%!9EkN*HuJ`fQ1%Eht1a<4yim6DFPj!^H*su$t$|)@ISU8WCxR+t{{Rq{=fW$t z8`ccHb!~2j8^fVV^PWNg+*DMaHx7QBjQ<^og7Qeq?! zK=%4nd^V1sS)`>yYItRl?+|Qk^3J-#8pzp|6tcUY^k8!0lSqPp;u^JQ^rUiBtgrGupNRA`x`Rw(*+wTFjK;TSo#|E8khyu-leua< zPdxzocBXPZEn8~~<72F_mey|M$}L;eT7KWieUeNht*(kp^^R&M!_UNSB|tlls;2h+HTg>n=MNF%YKT6*Qo z?poU)j*WPL*?nbJ+RBdSzDXyfyN5){O9j51{cD+1+VvX_V9#C(N6{E^VtKC`>2l0VX=dbWe+45?DO;j5sE|LBM9yns(WRF4zRR$tpXX zDcTBB2HrZ3X{fCBK9{8Fx;m@y+*bs@i5~-Weu%&;RA^k|mNrVaP3m#^IB^#(6oqa? zi5wV@aTUuCYU^9aU3u0SDcTfLgoMHN6?pOAC7iXry}HM*Ak~|W+1spnib7;?M+fguV)i&Lub`!B6kxb!z|$EUeC5&( zL9tT%AWx=~`_(RCeXRc5PivskF|f)LDE5{*}!(yjuu-<*Z%uon-(> zTJ6jLl#;01?OHEX>fge31?_`*L!7i>@V2g2Idl|)3WRm!54_QBMdBr!DjSg@_w>u^t+w$ZsvAV%c@)R1HMt^47tIW0q@SCX>1wbHm}Qwzx5GKcT)Rotm+ z;UJO1K(0V-^Kp2FywKVnUe_Em=uC~uKR_zyVd7oxnDG-#KV_)eYReaN(vY&za@`V6 z_ZErdbI%nmg!(>$n&PJ=oSg;Y2Z-9=1BkkB8R8Z8UeV#GS%p&%tZf~ZmTZ6IwU-83kk&s5V>vElYW z_cc=O-f_m2aFnRYA;kqKbJh)Q-wteX*T}I|m7}*hp53(GU)^O;w{7w1-3SS9pEQ}^ zA9~TBgWNlXa`ZOhm}MK(=R|?;HF)r|#TT?bA3Eb1cCBx6X=xsqTY)K&D3jOsr?E4Y z={icCj>#7NsI=8C^s z@)^Zk+T&jzHIBV=a$IDRKZiV+>pgj(z6<xOf1k+Q2QI_#>1D9m9huywk~#t+qVbfHJDpt=PK>37UbXLu zwpoo!Ym&xe#x19;!kn~H_cr3zqN54unGwg$ex>hLR+n zdJ)nDO;hi3&tKdo%ok0Cxhg|PXeDHFvPXX3YAfNM0gU##7B~5JaJYvUA>6g#CALVF z0B5R?0x9gt!0GgLYIRtdy0pvLCYf&|hEK~oxH8?}l!sL52}v*%lgyr=cZxReiQX$| zT;mw~`}8ix`9KHrc+lH6o_jYvXo(IgU(dJre|6{j*DD_-5Kul^&UAoTUg*rZso16aH!sw9YIQ;g?hH32i3?QrD~UM(`wOh5}#!#as>DN zDrse$Cs5Ky##MVA72%FAC8@HPHf=86`Artyb0DHr%PQMGQc=iXS(C+V2Q=;FrLE1| z8GEJw01)|&TC|vXL0+_PN_Y{+q>i=Bwp!^LV?t!@taC0Z_anpJQ)@|N0#Bs$CujrS zw{OG$0K~?R!+23=i_K~_wC-lk&XC*yi{cj9d3t3r>?&Zq*1kB ziA%R$QxSH`?UiQuHY+5kC?C#}o}xP+-l>-wz1G-q)E3-Ol0s5>`-%~-wGJmguXP4x zte4^z&g338o=7AuvmqoNIO#%|x(&6z%L`mgOhrvWVB`9pl9zsPbgp{)58*DJ(>d)s zomQaAWi8b4x#c8TY$STyDu>tDNPlg#QIqZu5lW&2D$mBN<((vW%% zoDQFAH1nKt}#(qX7SWGO>N*mk*e|6*Y#h{iEW>th z18~DlskY!Cq&c-j%82TER)EOdv-7Ph9C9hLkKv;Ia-k!b<{*(YS1r-(SJkxiD&(Q; zUv72wKsXR?6kC84lqvx}h%!(3RH)$9dqddayh+Vo>3i1> ziPrY=S}gL_=}Nsx*m(<29F7PacNMSv8ThHf_(h;B@ebrJoWFD_7ig93VMNaKZQ?tg z=Bam?`(|@1)b_WR7x{Etx&_MA18WQ1L#Q(+f<$(u7^eFj7OP99wx_vvHaCh+-}rlx zU6M5gwr%#nOUr4Lg`r2Cs{?>|;woQ+z9m^HyoJlUdeb?X`I4Jt5pIIQjQe$~aQ z{mYz*DGiVBr)sxu0t|cBh4BvUm;6b^Tey+(rA?)eG4!PhZ6RSx-Vdat!TX4bfPkfdO7Yf^#HrP+0MkIhS>iw*^sPsbyuQ=fB_-Dv zW4Q%v`K3r}lL`cxC!f}8se;O{67rt%4ax&$B|EnAL~G%( z^gEi*EVo=L{{VAAH;+FGZ+xmyndu(&vGINq-D@#;&YHz!GO4y!)jx)CjjBBXP(8;n zTn`5)yfzc#b&hS1*Mxh};i^z8&OoI*(Uh>GkBV*7$cG z!k=5L?Af(Z`Dd8+rL}w=cAH11t_rmkfX)`{DS`1V&kyyJ2`xTW+pEbm6LD$+N{~{t zncS{8Gg`AVt8a1nyW1Np^Koxz`kQUo60j6t$Q;oR5IkeRXS_zqY8)n?eRl3GE!o{Z z@of1iY$Pd3f=Y=JW~mFsURW`0khmYhzDaqXT$q!=_Nk~R>~eiuO-yC^W!p-4t7oO} zFE29K@5gfGC~p|cs0Q7a6i8CmpYKG770~VI>pMJEwqsPT-#dMMv9z1scBw`~O*Q@B zXcfo!Jwc3bIcafmk5&4tuNrm6HwaQh@%5EQ6>iraB&ylwO7V{4Z!R(oU5AwVi^*Xw z0(`J|Oh@t*{4|e$rqf+D#|d(8UmE@%F51MqnYz+hZ7=c-rr}CuwFUt9S3Z**4&aLC zmwZCJ{twM$Zd_~;4XJ4>LPUWv(}=Et@jv6Wjc4I?{)55WKX$v_p;-%?mAi3rk;=l7 zsVFhuf%L9WylUF;yyd<%R%%Mbl{i+Yh#>LrOKGF1!l_i^kZ$W!Upm91=3QF}{vbeb z!4uDV>8sBg@?JG%@OVs9*P4F`V~ieUhCG{KR+NuY-0mi8k{b>SZ!ErxX4H16*93%* zatB|vZQl>|ewV=Mtx<1mmvXv}@q{;PaHY#-l|U$u?w?6K^T?wZXN}SH@KKY&x3o8h zxZfDH9rl&N-{CHA?b-^scK-GXd4aWNO4UE%t!?ABfYd(^xtlyrTO`HUbtEC%ONBZX zBaos`AE#QUXn!AY8VkfXbC(z;V{QYg54~>w=f`BJv=IYn0tbF*w~jtM;rwUH<8n55 zo9mWXw^AFs$*3X694aS%)bZTVqs!p;HB{3&Uhd^9`(1kKxw&)MB&tGa=9%#>`$Ic; z?V}DHzCodA#p+T4w?dWbS3PHn*Xwi2LX@E??oAMXTOk^Y9dUbNGHJF$L8~yZhKp&R zN{Hl=dFS<`2Opu+#@q7!j?B@z4^L>lIxd;CMrQ3u4_#io*m-MM{KuXW2R~XLsJto3 zYimVivDiERxT2Jvc@^lL!fOYk z^HvZbeJMl%G?goe0Lb^IERixGn+N?xK7s+Jv5kaUpd`o%1N5gdp1TWx2dK7xr89|C zShN8qQaXxeXZob=kO80Cy-G-wa(M4vq-xFp+HVtRt1HIbv$s+gcIwIUQ_riV6;_?c zEdK!PY$y+Q(tpaK=^sux@JIz$Zy6kj+-UTj-3;{NgDn+9XJA6K)-6O$g{dL{DnU%1ds8p)VekezTM^0pHq)N4&;Zg-mBercGT8uf z2xw7&2YSkgh@&KY3jP3!(io=!{{V4gp8I(;fur$FsBN)q4`DI=Xy{k88fcBXxqqQQW#3&GK&;bx(N(7JIvt(2?PDv#~epQkO z{v~yzaA10V^vqOITZ{Djvy=Wlbj^k<;rT1Hwk<4Xtz?B2c3gF@p16M#GM*RaF}ciI z68+VyggTdsq1IJ0-yMveV~YAi{?JKRfB;AUW^HG8Pw!XGY$b-B$Y-ssbv{Dl6@_@I zFJ0QT<~W1e2eJPED#c`XO*4qs-t>J@cwXo&oADac#G8#lon_}Ss>qgGxw3u4ET?J+ zAOpgOvFlpE>&NwCehRE6yuS;XGQ5=4i&~1DaGp$`NcyXyLHARzcmlmv+`(oK6 zfPcEF_pKtV7Dq_rb6RfchVl0n&x?D@cF0ik6aN4=da`lwAUAvUa z+&Oqc9lLd~(~dNRw1og5arU9v7gu9s-KG7i8o8e~)k^_wI01r^w3+MFS6+M|cqgW{ zrYipcRBCG<#w~3wzE0a7>x4772~ySsPf-GDXU6A%8aE4}?6sbg#-|B$ZFb$Q-ZhmH z%gJ^?QW8(4K!GvTS2G^2rFVmlPO1JGrU_e?<=-b4V{`cYb1yfMWh%UGbXo+fyF*N1xZH>RJ+ zD(O~BNM_--geeLVkiL*WiHfY-MsR)i2ai3t=%6$9eu$X3&}=5w7yr zcm zy+yinHJlo^PHJ}WS!V7mQk#{s+49n*7(&E=vP2%FnD0|`7LL5u+M>q=jW+hzSFWh} zmkZe2X_->F_LIk2fu=O|kK?$!b=~@GtS)ZbV(sLD3rH%389o01rEk9$yfg6b>qY9E zmVs*G(;=hZWvi=!a#r2zYYTi~@=loZ(sQh2>&i??x{{RqNz+c`q zc>8Uo=5H<1q&E5%1c5!l6{)oa`_5e@0c%B=JSDC+p5*)2b71s_rSTubM+b-r#oTD- zaOGIs!WMyY!;PoVrK)C6B0iPIFX^jmm@MB1LME-=acUjur=5yMEC-_S(GjZgu!gx!Y0qONKG$o!4`mJ--CtNs2yvTey zYnMDRZ(9s)yO?*FHwj@kAMYiY&kUY-V?%7RV5#-1aC{PdKCTA_Mil$D3t&QL08`ousF+{-aV|MAAb-oTKTsbK}t~8 z@wlm7giwoheMS~V#<$#uhQr=wTr5~yJ%4dO@NH4OQSTKZ(H zjjkmmrCmR2>D_OV(>53^?zQmi5&e_O>5PMT^gWL$Q+Mnt08-F+BC6gn3_p#!2FJvQ z?OeoaEO<^oHD6>-!(2OT#?vB>b(OBwr^j>g=<(xtojYdz+T-?$zcz zE#6WPwI1N6I??YB?(cA^e)lS%=xC+k=cJP6(n?#A%%5{gzR=i0{{We8=52=n3Q|_W zPpjTiWK@f6W-*YYzjK{>NZf>5gZjZd#Za2h2SGlo2Omm#8Mt=iFOj;tS_jNtJGeC5 zl2on&N55fPAH^?*?RDXHiLupLOFKs_ikop})$Q(CxOqT%F~B_}d)D^T-V*p*f2DBv zOjauA`*7YNI)SX)cM?3rLGqkT@iEi2Y>g_Hd|A#XR^V+DlG7QxA&=D>e){Do@|**2 zni8YZJ-Uk33X?~JT~by%CCff{-)fd_@sBrho10sf!cDujODS;&cc~>)`FSUZs5ahP z_X)ab{I*4#LJN%DFU(*}fMA&%l4{%HXNTCl2CdKI@y=W?8)tm;EUm;C2ujFE4h|Q` z^s4?o$XQw3vcr3vb!l3k^2(h^1AqwV54BM6o)2gKO-3#<&P3xB`cZJLilpRF}iahb*J>6V(AyS%+56V=njq-YU~1F$EJSO zI74yd*Eemlw#T2?;9RB6uAj{1F>KvAlfyFmjWGHWTT|!?AQ8GsRX(5(y#PyAPTpL3 z7f&?9i!K)3b7@IF;6VL4b6qp>XWXha z{icCoD|PFpFJ8Lj2A621r?e}R)|b=4=&gKV=(E4V>0M}9QqnlX$_#DZW};} z=eeo&tY2rYtgM-7+qa5hFr62CN4}NEZ$eQn8 zh}{K@*ZMNX!ox*pt{YPaDP^gZaFsJ|wfdYxsy!kjY3gDtg7R7)5pd~wwC5$l1bm

OYMGF z+n*qwNk_5z(EK)zO@;bNv&b~uerGMoaQ&-xcm<+#R-*AH&mXQ@zO~ct*}RhV#IS(* z03ekPk;xxgf?0S+pRBvbvu%Fmbv_Df7d$Sw@dRN~G~Ju1JMN4)>L>mxTO*VLM4Hx^ zT$Uj>H&!{MD%Ec(OBW2UY6y-Xb|Z+c-op(~r+jO3!_8fh$!a|@mdCkaYTL+5RxVrZ zK2wP)F}sL8=0T?% z2HNGef%SQfC~Y5dD(Q%_#pAIHWvgt3)%t-2NVXdj`_sy6g8fdWJ6^|@70~(*QESWO zVzk~V+_mDA1Fg28pDiT*WfRbSdQ~G_X?%{5)R}C3>kG%^?H(+?*Rqm(k=Ty)^asIx zGvQ8}x5nzNEqJSpE!Bf~olMHE6dkvDL=?QU!jLygRkx0vs;Tis;TDxni_2$N((Yle zao5SWx43fXk&|V~g(bxkxb!^J@UXID(#7eqV(xIZXE}(!yHfGj*O*qHKsOlWtcK9% zeS~!e0sU$HH;}QmwSA^Tm$z>#XnB5?+fTUeO=BbB^TqPEn+=2g)jWa+KYBl=G}VSB zw%*E>wP0NcH!P?)8doq>2QgIfgVeuS7Y|2ph8EGQr`Wamq+?xl?M2IpQj(Q+ouHn2 zOd7R#rQz1Dz&VCzP!Eq^>r06p77Bn#_U5O|3Pco-U}z2<3Tr29>bKlN!76iX zBz<_~Rp-RbG~K4F&ErtDI;#ukt%IaVT2QhQXYEysJSwf+3REa3fI*sAXy{|qVYTPE zx4bX#^DprMsqHXW7VRx^O%_ITPsp* zu_{Z}?w|v?N>En@I}tUg{4&)S+CN-j@|f(c#vSIa5{GYXR|-Q!fWM&;@9kZIrhG-z z7(a+zGkclVF6tc>khOH$GqyTH0N| zYTemO&o+H24ZyWw2esY>`aZ(5T!Z@L=uLr44wL(4} zp2eq5sib&v<<-5Vs-0EXW#^RXQ`Y=M4*sq_ooN`m7qV9@+1awYi~ENX(>Cq$rxbVq zQZSZUa}|%d^Rk&0T)cpQEbW3?PzFZr#!MRD{s!s)01owbhsa{^_ZJqvC$Vbyl^fp=@2Sn4j@)J$i8!-C8X-o;PU$p|w7i%IX|_i|$&xsP5Yh zTw~qj@}hdDiH_C7`pZY*?6cRlR+(i!h8xRNk?sU-8(K<`|cr$mVpvb zB4!CcYMY8nB(&{NvilnCTAJ0{n~TKVf4n{vg{ZWY9f!A1-|tS_Wn0O*%S<$enr$E` z_1s5OP+suj_TAm$&i&@6g^O1|p%r7qXL)gro!kJNS)l=8%DvYd}FVrOrLH8|s zGfUs7-S7Et1!1NIGlpVmvHGyjP5-_=tT6bYn{zt{6p~XH>I_~ z%OgyilyX$k@qWNeXjlKfz9I!V6_W9(8 zt=tD_2~v9*=C)fJPSV#Yi}2??fBl!%x?m>b4VQN05c>rFUBU-$p4FldPnLM94#_)b z?r__=gQNi7Kq0@|%HXvIAxeLDmWWozSy=R|liP{{*ukR3$5yFuC;-W9sYmNI(7zb= z7#y#PFPwYKTwkWuk&#`LEtef2%9HGU=jmMj#+0>b=G<*d0RCxDmCk#dg-;C1iw%LdYb0{*zk=Lg0w?qPAY9v81 z!Rb+SMn@B%^#(I2_9+^l7TSv4VZsyUs16dKL;w$N_1E}qVWxaXde>O!%dBE^R+a_K zj*Po$DGwniDN<6cvXunLv>!Trst*g@THi zx=fif*+bf?x4NSpmBCoqWtdB!horW`(2y3P*eLxgy1ZV?Xzge4t#OoIFAsreC_9L; zx4H9f+kbfqN~fVb@Co83xM1n5d#;r(-A^pW*-;$QloDsT4P8{hLVSbq8Oslsy0^E+ zm+qcV@a}@4Ovgd?_p25j-CKO0HKZv}g*^P0m?yfFCupad(rCq7sck!NxDXs^heDLu z_E4^q_;&HHL*sNk_lBCk6>WvYWSMl_=eGqo>!k2cra^Gc)N{{Rp={@YA=t(>&Nrx&O! z?k$>K(w5%qblw3AS^(qjDpRRN8uZskNbii%%$1_Si;z-I8v^vCOAwEPqkE9#W*ev!)Q&ioKaW|CtysO^nuV- z!$4|HPoZ@pGCES>OlHe2Hib6Gbz4s=FnV$?zv-wm9VKMii9}xOR*A3!kLn(wxF<8r+cCr>sG=P@8rKFf2 zS?ybAQRML-6L3;x$eq7w0QCw3~atg!<5;IO`;Ltx>FXEtiOX7wG$8jlFYF z;8JZa?vjNT!yN3~p~tsht!>)2nDo^2^dTjojeS{{WR{;cPS%h=8dP`G}TxD&UUdJvNQcGc7;Zq0gT?OE$!Umbv zS_=C)tGq!vS5UIIQ}`>*p#`v++a*R-xCxZ>p+_f#w7R`TyW);j(^i&^>g-U|iF*G4 z3Wt!Jn|9pVr&wD=h4}3(v@K-;k zwYGs9h_aU>$TEDN-v?aHR-`Wd%8+_&&caK;Aq+Yhx;*&gXJn>jc%Q24mH!Uu$oLnj(o>LJ$`Qo`#{{TdqvdN(1xE#fT7MWo2_U&TR)pnXWt}UG3o7I|g z@as!Enncv+$ram}ANF$i zV&;=ZcRQ&wY})xn;$KruDX+Q1r$9oyr34=H=}D>_ z5!3C|_-#cjoSkWfyt-rY3#U`(T$15&!|HKI`^0@~>3_mDS=Rgo((Siq^XWq?Np&Cu zm|3o3_=fm)(b^{!Wp&}ov)Y2~#Fajm5T&-ue_u%F`d3r@88G8t587R&pryp6(5BBD ze~6*YQ@0i)oax$4MLOF&PVIL#=*-*&fUJA}08dQ*^rdF5PvytlD0`(eShw5x(t==m zYhl+atowk<&RwKH$6TH!HV^ftm7cj+6OOu*{{SHW09rT@LZBytE8bc}NR#xY9vz=? zT))fRF2?CUA#fqH>QDop-kQYbUt-muY@S*|1d-mX83ZI_?ujKJw6ER`M!_^yV#y>G z6azt>)jt(2#8I1hU;aY$`|%a(HW?p6E7Tf%D?Z{5lR{UDSV}e^T|Nl+JItCnB?JWX z$9f5(!A==Kk~dtwKqt4Dd(qgAPihNj{WM}okZB}s<_Yaf-2ihxdcao)X_M{Rg7E=;XS_^7aH6BP5l&8pO1MYoBf1Mag`tiqNY5N&8{{Tz=PG$c9Xga_A zKma%j%-czy`m2JxF2w3fopWVzr!Y3T?1KzG`*!!p+{p<{ll4&I2iCj4{h`WG@EXX1 zKpES8-)*a|3DDjpXS^lU?P?4Qto+k$t+K_)Drsp{Zr~9Ifyk;eiJvm-l8kzsT>D_& z)>t1CyhyXhD<$GqJHzYTy}OQC;4Rwub(Dz!K!eGyU3(RKrnN?651wtt^6WacZoJj* zQG6K-QjaAjW_^gQg{i(JH3qBkJ5K97_8S#>ae@j%HjD)!E)ygvN;AkXMQd$ot1=qz zT4ps4L^RdrPT;=c8l8yZ5|KW@4r@iYvN$BQYPm+*`=)K*1blkV_$1}^s~G0=5^ur(Eb3;)k^GKDDOo^Ea)x zu}>=DumD0jS`l(VPROx~v*kD_V6 z7Cl^Jr-Xe^g2&+>gj)XqSZc=d`X=A-#aOw$IfXstG?ftq1rg8Gb6ruWbnnD+G}-(= zBc*crW?SiQ+D+#G{z9MuSI|@+93Rx$J$OwGplz3quxYk!&~20aS6p8SNI$3~{c885 zJ{o*Lz+)}1?|ejz`cfSEMTI!h9RC0cmC*g-v)8EC&+{sNlh>C>(ZVfU-uLc&;ndCM zRj6{qQ6lVW18}2$FM&(GZ+Qo&)7mOSZhX?3H(1+t6|o`Ggb-ax;3_5o;&}G1u=wQC zcKVA&>2DHKY>Qm#ZvOxW$t9D+!)mq?NRm>dC`s)e)yNi$7MF-|Z2ZC$b}5obE)L)k z$UfE3X|)m5!+v=_Rn@w5v^u#_7nJ!%==f;RT9WfsYAc;LrSQ7S;>O7fWqC(}TWl#t z3IX7#$A4p6b69B)8*|!T?!@a{E}qJzg7S2JAf>02h$N&D$S0qrXbc9c&G=cSwFRD* zYVQkcnnD|L?d7OE*3wSoE&%Bwq39nNvU(?6WHnvwulAc!Tw8fI_gN>@RKmF%pmp@X zj>3KG)P$nqSCvg_5=~^*Yqu?Vh;<4}iBU?>;PnLpcLjZBx1YmDi<&>eO)svQ)i_n= zS<5Aq+t?NZh`4~M9Pz=9O=*#G?n@x+w@wAWc>0`mU_esVqE8)!nnPjCmPwYBnQ=-C zHlD+nNFLQ%c;y~tcDm%;c_wO&NpYUbWS(UT#A2?VE;oj@d0#HVdn6`c{@rK~luU92 z@F>OgqS~|u;>p~5=~-;F;pE4Er9@)wR;@mtC6}9U1gp(NsQ?+29>nye-;rihmLx`C zhAx>?o08a2-WrY3-afTz(pju-16tr+ONz|eF_U89clS&by3#_#z#Nb{{pfY$7L8ln zmU6b)6fbLyG=>UR!e@|AW6VcADg}zuX6kN~pD_(6e+a5P!#|Y!168M&lK3}U!P6fS zo-^tFQ>ZO2v`vMk4)<}mYgO2xK7GUCB`RfPas(0A>0HS8xVm!Y?e|x8aO$_*KJxc1 zYAWj|lg&s=)``4AEHUE7%`5Xwx0+n$B4pG>ZQ3Gt0m&w0`_!ITwHHdq`x05Qv0EfP za`G;K3QK5batJfj&j*7|Ti-szZ?@BpB`&sF+LaW-WFDQ&59dy=CwLe|m|IJ2AOw)w zN`ULME+#&dLgwvO*t8`Vc=s(HOgUms>z3V)RnvjMgE2`IMQ5?IdM~OrpuB+*BN}dc>3vYe?l~%VYS!N0Gy-6*6b};+ zNfA{-7)A^Tl5buuc?-A5vPx8w6C`pZaaUgtek1L1c{Xfv z`d1r{cIDNr*VVl^=TIeE94Sy#_N`8wvs+YGi@0;lDJ3pkfOe#b>(5A_ndLQ-X6=UT z%PgoBp)NhX6bDo9$Q5+GQNe5>1oMP$Zx}pa$LgnXh|^fyCYrTPv&ePqDN~7YQdAVA zxwr!#tp@Oa$Gd$BZq=N?;5BubC{r!vY(kt^CKa{xfe<-`16l`6YFF^L>^dGocDd+6 z`AAnVK=+!qK0MG)(4?+P_9#Z?NwJ=iosR1*EZauzoVvpBefQX*4k%5YfUMH zI0R%_xn}ay5TMyY+araJoO8`Sllg0dX3gW)&0~sXu;-M5vop|zC$F_h$6d2-E>~%lLA_-@WBB(OAr2-W zjs46XpIWw9i{Sb0wRl>IczuZ)yI*R3f0J(`sI!+jOSan$wzVr_TTgJ5r-42F=v&Ci zGK4I@c81;I5Vd-~)cxx=$+K%IX2bsgBwzBBm866;uq93I=uCZS{N2T#DW`2ME^-SO zDMiAW$RM^`>ZI)lsDe2h5CvB9`N8J+cD`B%{Ju#H2bBHdTP8OtO8}p}7tq?DFQm1W zD?f{Q$1)ZVrR27yDM@q#$`u^J6%PLM9ctUCv%knX<;qd#TVAKq)N(eS{LCL}i;ujq zSDw?kmvJjlN}9O0PUADsj<9}|li(CAgw_p*;vbIsn^rNM!&zKEgwnY?pO|wK)S+ey zz*khHs1vk%pL*8z*0;ASOBT-D6@P)XDDU@4h#0$(T7b%3atb;3BF5K)CMC~f+ z+O~eE_-4)Me-7?74nsB^w@7K*wsA5p@$x+hbqAoS(Eek z*Q~4$OK2po^^?U&Y1;)Lw&rs#l&jK0Q7KUNBXu@P@^|mtKW}w*pn&3#1Ulg0On#DS z>x^BimZ`aF{@TZSHybO(l)jVuK?kv@CbCr*KVx&_{7c4aF95X_joWvZS)B^gSD0+l zoGmzlw?a~ti94W;1+H2uRW@c#hC z6t=!wv9dr}{H{&UAP)Riv2j65N)nI~2T18&h7*cAE}vSRUbT?t_NM;fO7lo^Ozia( z{i18i)R!$gy|$3n<2(ZZ{W+lvO4U1$sH%A4l!Zu%+>zF3NglG&)ApMOO?d07w6e15 z$32c|8CC_g<4dqhiMU9fRVS16rnL`_de>Cx`wc&s$+eNgSrnEnUbg`#DG;9g$7!k} zHmN`-kURTU6!1UXB=@O%55TSQfSAgyX^)l1NHZePf2}UlQr+1p^svQ)D0ROn*tJY8 zKT$D4e=CH@<~XNL#W%ExgV(vJY>OJxQoBSukMRRDid}%PU7JG1@pW&y*LZ2bWE9Ec z?N;n(#Qy+^_@u2vWj;eY|!Ob7x5dz#hdfZBi(L5QZ8y)Jd0YQ@69 zEj>%LLf3kZ_Vv}S;l>_QiYsw`SE*|xl1V=EOt+uBwE#BFp#pz3x>Y@Bd$6Ij0#9$P zWI&R#cpWCIamf!YPII&%y-#@R04THy=u|C|AbWArjUN>?3z}i=Kl2u+61#zLt7TcB zfZ!1+GawK-imJ9kq6rB+o+gx12!w%6M3PMcZ&=#h+pI0O*~YS!Aw#-I=~Y6opi)p0 zp3zPxauy6FDLhBl-kM1Q)777)8#SMaPZFAQDdlRXm>13m-)R=jgu1Q)DuLVp54=@b zpAvJTtJ1q`?QtG5q49pqZNbAfYF;*!Zn#yfxCVF*@Cx zW6n3RO0d(8B0=n9tq8u#SY3;iKdUX8dBpl|&H>gIh0iJrOc_64dIP6*Caupt&4w#8 ze|LK0Wx&m{!BGhkq6cUdXt%NP5MeHJ`5WxcKTlh!D1Q3sDNx*aNJ11nM3L9(D+7MV zA&E(692NC2%G(NGiMI#`{5JP|iglY-R04;I_ltq~MWxLC)pEu=l)TdE%r7;rP86+8 zjDj%8RkrcfA!F?n)*|aMoP4*7zSoy(kP;ikAd~x6ciht&Y{KA1oYlEKFx{IN-xY5e zwrb#977c~LLf71{+!zulc3U-<$YpOYaeB_OF_KHCb~%r&ijM8`Vwj^!bX)@7eVOzuK~`6Pi`3Zn4)H9KC|l zR*Q>kwI~$#Do)@*5z?fj`xNeqj@vQlwZ=;F z+S=bJynWr!Q7o#&pzLlq+*8&_;(4XvF!uRJT5A69D{!aY@*Ac)`ECatLw0Kb=|h-@|UX)VRm) zEU=G*Zd9kOU$`E65IPXn={)_a`+M+vsPR^o1??fJt{uB+DoM7zYl1|OK>VlZdQ@dL z`<5*&ll3SwPhDu8K;tS)T07*fRkzN#MaoK?kD*ivJn>df4!k?mcr8_q$z?Pk)R?3d zCHn`i8il1vi4w2OCPdIpQ=>6>E3cEe#_9}J@GC+WkZ~(zOGoBJiWQ4$*4c(x{0ZEs zeJU0&!Vf;VSxn=2$WsQ;*b9H8bZXG9sw4Ulzl6u#V)HOFL#)^B$ z>H02;y?d3n{V3(7d<<~&pyJ!IL0gn4?;xpkxQ}}yetzV+GmR~5FM+3KP+&Zs4u|J(89!Juz zH_zSpoj&HbINC1tLZvRX>y~6jylG8 z^{#O~8Gm3{$y>94DJ^bG5dOksqi`|wn4W8+{u;bSy3t<_HrTb}ES$H_=3ZgP)X4Kn zL!c{hBm5!(?f0%QQ41SUAQ9B|u8Z))P&24J62@Si)YiD{&9-Ihjn5`sEQQEY6CC#a ztE=AJKhec>X(-;~H`v+!GwAIDi_lr^L5Rn`yxWUy1gT)RxTs0m1aLl-Kf(_bFuoo5 zWpSBr2+nIgcFU|SO@h|J2hyPhJPxJIf-J#@s%k}^V9SVZ5 zQaAvBD@SU7g)bN{u%g1tU7L#r$tbr|i)9MtAaVDsmBz&K+1sUBTp)Ij)T+rREswN# z<1F31a_OsdIAKJQyPi0iG4-O$ynf}Yri+2?tS{23CwF1E9w)h}8VBK{UEysQwa8ny zk$Ibis#sW3))mn}^!+BS2=FsmTBhi;g(_{yE-h|^kLUcW&{B>HHoUwWU*U7{OTP_O zdu4L|10ZsD45^G|!rN>FgpNe>?Eq$ z$v+Vv5xJcip1m#%zCp`pK1uI4Dzt^EDJODcsDeLg>lCu9hRP(NN7@hbu21-n_;=NM z`%pUW1y)ZmyQJLt&nzWL^g&7Y=A{;s%M4{22IJ!Wh7{9h1D<_b0{phKPQrei9=ue% z$HfbJxA4q$&I>Veg0!Ht{$XlC3n3saJ^KFuD(#;EGPHy>uwldfUoU{{Xf(rQ*E@nmP?!4ARdNAEjI2aY8FHj%9HIt zuxq~^H9mhG;od_#m(;ndq~cpIrMr8e+Q~kd5gmp7L7_UN}>51PFuqVn`?HS1${64nJ67 z+SPiQ_wRD5R?&HLlyE5sJZ%IK#Ezd@;pKcr=5BN~o*BIlk~a*jt;fn15~V1~jwiKe z!|YDH@v~FoaBSM;?Xj&m67|EVc^3AKi6#MnRUCfPRJAPf%XYymq~QDlyi!iM?;s8!2)OdQ~UyP{V!L-0~09n!jCLS>9VYt1>9J7S5@A zQrHR`4;&`855eQgUxAoi%G}_7;1e_d0K7eGPk4>uU51X+Sr;!{*rlOtHqrpzT2>x`^Wh&-PE_%=eYt-zuxj&j4R|@IUqG5;olM~lq9CWF7 zvGUIa07k_V?b4#)oC?P>Kp^ZK8Vry1m+%opZ6I-8q@h5`A_?nWpx2Tnj0yY&wEza^ zT_6tM#8(t(NlzfqO(u8XZPb({C@NXt{{YNr+;Bgo1yn!FLP|n;;12bK9#sRs?M?&4 zPa~5^B%XW33JdZi0+|Dtr4zVE0H10}1P`SoCu(`1JBuqg7za#A@^_u>sU)(q6ar1En|`#>Ek@J8LUv(=bt0?bdLUF(Dy!6U@IL5{*{j0<=h z=Tf;<-a$_^uoFE0~Z9DGp^4nf8GbO}Fr~tZoX> z-d9ScbG0c}SbC3W;<{5u_*s9hE}y<^IxVbSHoj`xN>g*xMq3H0vAZ$_ zlm+Y^Rp}wO+dan8IIAtv@7T9_^K9ZvAgC?jv;*}u^FP7gihWDrUM*G@uOlH~l|n3^ z-3!n<_%_oP*o>+*rf(L_ z;%_&n9*oOqHkV~8_3d!albbzEdq!OVZ*Q2J>Jw1C@bDq;`;nX$qy{L_hM$ugyMGxVf3v?25>I%nbAQ(|vz-*?1Y+@lv(cLvmu%P86i4t7ZeQa$Tg6&8ty zLX*>W?mpC@{{Wzn06#%7OKLrZqM?6^?So@xa+(Ft19avq6vL?3%E`HPmA`h`Uurw@m^&tz|Lw-$-Ev_|#9(*;mDAa|)_2c4d8-@#hWhx)eeB@H+I0Xy`gTTDO9^m%X*iqTMzwikjjO!@Dqer=aM^AT!vcC;M~Li z05IBuyte{2jmM<^z3SzErgT6J2CuH(D3G-_iH>;Wq<{7blc&5f(|X4)j8+YKY=!fv zTFVSIu}&0@N=o$I+cf?p8g;;lRE4BZKUwWoynds@V-(+=ewS_lQ*EK5 z0YN;KEA@yp?!544C89F7c3B&1+%gu}QVrTdmXREgK_Oiyi0fL;=fnK9t(d)Sfqi1B zCF@hUNCW(6>YAN$O=cx_EE}=>d(3!mgt&0Frtb4Mt|4e%;GhtJGx|{`++#1Ht?w@K ztud5!Zl>DZn*gslw$-*BaB@0zJ%vA#$6Vxe--bF`-TZbvhOvU=D)s*WDJ5?1Dq4(m zg#PA*WHAqElWmE1^1|!MZBHr8eb~4`D_K2YM^2RN#_Sx$dPr(+oTj+;Hf}e>{4KA- z{l|xI2d;BBI)vnkMX~bynCree&u%(Ldf582#@`QhkHm(9)%RA8Td*+qh>#)!wNDhPTg)=+bMc>q6t!<%<@mTtjM})DO-tjr;wszs!&L+(Ob_3%h4Dd zUZK)?{EhBs9g582ZN7DjX0eIbq@c`|p8J65???6L#V%XK{S|3~%CO!U)s~I*{fjW| zz(?f@C!m-=rBb{v@a6vi74x^)8(i&3p)yD-rhss>>+qnp3vd*(5d!n7^=44v={O zpK6VBfl}NnxWZ>%eP?o=TF^?q{8AjnG|C)x%LiL%1G&pf{l%f~QacI`{yoO7nq<1Q zHt8N)p~WaHdyabjDI{~zYq4j>W3DXxI`J4M9d~1Ht7|tDZx)hO4?uuBeQR2&2t3S~ zkTPt}-d7z6`A;l>Hqyr$& z<{sU+_oOzGp*+a{09wOkT<#}u?^tPEs1Qtu3MQ;ar-Lo+^QK4ybxEFU&K=1r2>}V@ zbJDR2+X;X=_326t5h>g@$4Y2e5S1c8Af4ROlIN_2Cush3(Mr1$6V{z+g{450k=QDk zBiodu@??&blo|CcJb+?pD=9(uQ099Y^eCf+!98dL1#+?zxDn8Hr683lK##3t2|-yL zk8gUy;Yi|2pdKg|1t1_K6dlK^ff3DlL?vB1@x>qrazKe9Nv}t#NtuH)K(cxWO85j3 z(vs6;gS40=Qo>HA*lsJxT=arloY7oRj0#CrMIfXh-@uM#@?)@Y21BYMh-o&DxR8}fLV)z8Tn@hVv3??Twx7%C zyF6x?(^;Gw3cX90jNV=wmX5$-TZiTA9PwHwL7?d!eWfxQrKj+EZvFkQ-P^PrC`nHn z6Fhb3YkYX!pVH{CI6n+E9z#;!kJh}I@jeWiXenr=p5gxh zhy8Dp&iHkqd^B%8duxZxT|aYpT1s{>gspBV2q&Jz{{Z$H?|%+$G0kaq9mVQ3<1AW0 zXghGWL(1YoS=qrLg9qNee|$OVo4qG%;iH*08)D+(?)CQ2hXVfqgr;3mNg_nWbRL6c ztd<#fZ=iXrbis3>0SS)3LtNdnZ(X-OoOU}+H8JuLpSiiUuZA8Uc!QjCC#bb8u2S>l z{zN$C@&q(YkN_T&Jjmv+xGS{T;w`bb&1GWpSZyq_UVRQO0DuWRtAIYW&tx{#rAbNd zLQ^O26zZ%SHoKKaV1xoDi(%eiH#aQ3cTS-oK3 z@?JnvLx@shWXS99UoJKM{r*>1=WjA@T-@I-`NI3opzR#+COEI4dX<94GcMwtdDN=G z#Qy*hWl#VH2eokbiQ11nr#xEK*3D$zZrz-dgXIVcVPN(tJ!C~yps~)|dEqmj{7pe;{lBU>gN{LYb#K8irn)eHU(i-aNygIY?ty+>X%_)^3 z7bE+}a-hcIM@l!~=Z+d%N$TszbEt(9_=$51QUIX@=~^nN0In&$~}?mtjsowjvtUMB4; zE;eV%k8-rc#m^nYj2K!50s;4iO=2SZ(nK4L2SzkQl&y56WrH3YcCRQZlA&>?IB^3 zO}(>Cz4K1F^8yx5&_r!1neR<_mGEZAgc%(nYOQOC%G~E34ZN#lD{ep32K64`Ptexg zYv7v?2}1m6!k}ysHq$0O_f%q`E^=?xS$&B@6_kx9@jHU>!^B-eoYF2auHPXD`LvZd zHc0(X9E#ofpO4QBtnt?FSZWKG?-I1Q+kR5hAOqHZ{p+9gzrq$PAL1U7z@HJZd30y) zlV<)!mdW`P&*xB+Bko0QTaWfc=8iU>S2u4q?d8+$w{!|rgs-97)0(H-xVCNX7TZ@>Q3J@c z@vF6lUs+}@GMSu{`776zxlddxddKVA>?=Qmex^&tSV-t~9cGAEB}>a-&oK?$GzgZ- zmlCt?c>s#C<1B77xXnRvd2elr{3jNxn-$|#v1is3Db1|)KjiDq%I2J9s!ApMY9NNa51>5H$?=`G}2I{!pelt0tu;2 zqs&@~gaO=x6&$p;R+8GAPQ%$m8c4Wzd~zqLRL~g|ZQi+B)o$Vx2L%a4e>!MdwY2YT zEvu-GX>GQoD3GPKJ?YmFR+uG3`^5-ZBKav=43Gga?N7ICg`|j5f_v@;ezcGsM)VfL zfj;00c`me+LW7N{bJCcGJfO)1roAJnQGrq#7W+vl1#156o++K-K$gn4cM2Yw^4?fb zQD~4mCX4}vA!=HJTUbCnM>1*aP3{G5aDachpL;22 z%6D74#BTSFl-J7nj-)_%^Z{u}KhnI@O7jvM01_vxkyG13k_jma`YXLG420#Ci)Beu z3j_~PN>j<7wM5BhEHw5e^8WVK4A|PVUUk*4XhKpCJas=xn(0g< zYf=1EwxoC$s)`*UDs2cV5&;~O)9q4<&jOReIDf{!j4fxcwI$OUb0+==0@I}}c`N=a z!=~k#{z9r6M_uZi#$L+S=U?Qkt}dTL2`hO=a8jVef1ObA8glD8t(NPzw@^q?`KOZ5 z{moi5_6=AaSAuIIn;VAFddk&y%bS7{6eblgKD0EE;@fwAP5rEJ4r#qa?gu-nFE4HF zZIb<=ak=x9uXD+tervwI1z$q^1H}XaP@9LqC$IcN+PH7U>zr<9#SPy)g)pl$5Vj8D zRtE{5)!Y970t){C@eyc{2nfHqKvDki3-9{XO)n7f`efxjZ1Vk%jX^y7ML3Qm#8wih z5O}X1;PkDi?B8q(OOgzAq=HH7Bzsm0k3kVx2_SWvV?!6MmD@ZI9)dU@N`-+(m5;ZA zOdt+=nwxf1ZQhv!Y2;K5JGkQQ`TpRb$o~LYe*rKG<~aJ-sbkpmuTsIOG8T_WPM@>^ zJ3$J8{{W29t@{>D4Hf6N=cI-2|sF<4Ajm(8st{}vS*X^ zq`FiUx_I_A9!`;u4ggBqUD)wD-kr&a2B@X}k`H)n9?;opIH^!d_E;YkPepkUhbuXqAZr$)FM*)!6Vv9ty89aK=B{(e5IbG z$=O>qZDP&krTo0+}_<-?I?1=`;Rr1bKEF)_MjTK!#@c09jY%grxAc*#Sq(< zX=MRNtPRQLtD4(ZWHhTP@*KK#L@X-u-WKNZKA0U0a{yNj{6Xp`{A14P>pZRP_CpVB z>&xHA|4+wL3U&O4l`1`vMLVWu*vOpy82udV=wZtDE z`CTce9Mb60nK78Nzf<08NeCY<;#Iam2?TM+J*!>#RgnJx(e>^}6Nk0DHn)i0H#@n* zwL}m^#0c&0NYq{yWc*~-nsZJwZdo}Srvodxd&>!Qwn&8uiH`hpQcXr!NTT{9Wo;## znu+#VEIsm>t4mvi@)dT`30NbPg9ZpcO8Not+^jkHTf$>5E}TQTb}yMjb}Sd}78Io= zOKq{(4td~v)~43K1Nv`8_)n`dPv&#%YFrjQ$DFjhakQZ?B|CwDpHdHKFh@$~KMp=6 zb(e>lOEaVN(k(1*Z|#Sdnz*`G%vrO#RMR7(WT>7!=cuVkOAP1wc9+Ut<7dBP=%to7 z1)}@{%xUax<=93a33_(rtxdb?NlM99)4>3cIQJFu_f4wRojHkptewrB^GQ3b+i!X< z5`E7>->q#+Z;HPWzw~tOnZCQl=War78*3|)c}loLP9UXi^TLD^)cbo@ozh0#c9u=t zy6w9jKuVrMLX?9X81Ky}IqkzL>-%D&;=>Ix(evJY7Y;Jocd5Hng0dz$o&^@iTV<_s z7LMtc?;i6YHpk2coEyXxC{G8TqK7ed)w0|2GH$bQf>JL6c7+2x5!SHY1>A+=3UWBt zZdWS{ySWKZ0IPC|>A?O~VtF{ND*VKk$ZN#hMz+9Y@yzSHhaA3nl{sdIE)wx=jnTA} z^dNyV)+(a3nJ{Hrzq&XzWY>B=<3{AHZLakVuBE!Vvlk-b&GkNpk-U#9a)~5QKHSh2 z!ELpAcefB!Q^E}WDaWz(dXsQj5|)aQ>4c?SONkqVb%X4Gdh(PTDojG6c_J$fJeMU( zgr)*d-?!=Y6s060Tk`=603}=z$G>`!azhAGQh$iOiBuiYS6>LM^?plUSleh<13H7t zc}%{S64R}tXCx??j*=kts$x`ytKH>VX_xZl9w*+FN>%>pp{QNbmy%=y}>Ts zNeQ~NeYHl{7C}F;3_o+`dbvZ!dwYOyH@&??@=V0kC#_cHvr28n5JHk<0k_-_ zdNp>#4B07BaMP(jna00{w1Wp~yPEc9(;k6x#g*bNY|24E2Xex=FnV`2Bzs*MZLx(l zcaAS=t2eRNIEJvyt-E$n<-Dg-=a!)>gZW8;#bVa!3bbUk0oB1JTm>FTKecPkM`+Ep z%L~}0Z$SbG;P#_hel9R*OScZ8=G+Ns2ua~4XVvr_DoE{RCq7tlR`-`4U>1fyHsvS) z1pWE@SFLqT#(>BSvayK2eWr^>-EQp&-w>nMiWuZJk_%I~6s1Md0s8&wJF7>@+**XN z*mWc+VbWYs_JTO+?@h-m3`w+3zSQk&jM2GW-^n3X&9b(R<)tMFB7ah7ZaU%i5SCE! zam1v{jVEG&PxoXF)ltH%wRp*N`E3P+x}sohCJ*aZl-Rp%a<{F}l2E7#ji4u|Pk82~ z6}N)9Jm)3F%|dk(T7A2h7R#91V`R058A*MB;Z#cC1&JLyP}jc?H;=hsE(;3@O4WAb zYf{uC6DZ<4Q`oIW-ka1IyoH`7-tWnEwJBGsN_HS0;sk|ocT7*PqFTc*Z=cJ*le5R# z;+eTxfw#9%SC|9<=^&KJ=>j+uKIRQN<+N8^{{W7^PUOT&dw!I*lu8g5l&B=iNdUxB zPWK5Q#BuBL}cWEfeO*;eF%lB0Z_=Q?(Gb5?d+RwMHYK%n!fv zrjqJGKr5931a0CgAQ0rJ%pcIx0{7uS(RTWg(XH*Soy9q7v@v^V_m<_e#X=R&q)6gV z5^0@yG=0u~#f><;qxM0SG~1awmK6l7z$LNyN#;qb2gB!z7M>Psvy;8SV|6u)_N!v* z%OKYIZbE@arBFhX)QIP|T4%<+ZDFrGLvrTN1!;xGyxL8bs#MZNxg*q)QN$JV&%H&S zCs~jG056i4lSR%MR^i)gdmNi%rL1jnxahD6AHQ0Rw-Iq@Tx_YUVCYk=Heq(knGPfV zU+GFsg4$iBrNy%iWE^;){JWdC3G|LQpK5ORWrI!LGih~=xoN`Mk#Phr2M7H{R&Xmj zvJ=8&UCL;$5uJM|{z*lf6#4fJgp`jz@{W1qn%CDVhZI38Or57}0xIc*z4`qgu2Qy| zVFjBssTR$IlP?u+B>PoP_RzG4Jh0k=3=}2gtw|o{D`i|G=o$%bp%GRjiBJY94LTqr zm`s34fmjPuiH>+3X$_EtNrDGjy!0|>%}=Xqn~CTTHGpIi2};1{o+$|dNh<6(gGo|U z3EQ?uX`nJX+S3F`GxepR;*v!AhrL8eJ5vxK^ru7Xae|T)u^ylTKtHLVV}vQg>h#9| z(W_Sto-*F)w6O9}BH^_i#F^vm_Y@7ag%CnS5gbuwu^wf$k#68AcRSaxB%P>1f@utz z<~1p?y}M;ea>;vkI69$3kWxYBM-&vOCI;k{L=Zmo`;1wt(aW5D!;c^@YSOmKB=#bD z(BPskpOT{jX_1G2|i33*;9F zouJ#SO;$>Z43WU($*UKJ?Kg9+ZQFG&`9j9{ugQ)3eqD36d(qh$wHS4>4XvfM%eJpx zy>RPoq%X=!r7Bn)5($nd=366ckOkyfy4C4xo8#APCwQd5% zPbfKj_sStUg|w#C8`A~FlyWvik9jq>d}Z)!NqAwTT78{OfYu9feq(=yx={LE5iLpl z&6JbJTJxub&jyvfeLqO+lZ83IObD6%iN^KhmUz{zM>Iocd;E#SQ zqWnC!ZsSU_=H$3b%SyetN&!DkhPl7tby~1u^bNzfOJ;PD&tExjkZRx+w&7{i>{DGp zkfvC0@~<5;@7yS;Tly7QxQpiCtq zV`$>J8$szyyG$pDIY(W$YRS9^w^&ojE)}@oDI0qBKe+d;{{Z35%Wh?rkByfp#9Fu! z{pE;HL0(3wIa?UM$8pf3vq0p#o1M=Rv8W|!&^lfw2ziw(lh!K@_^*m|I1h)qOr|#o zwqHK{RY0|Jk0M%zt>tM7L=t~#`hK*^QGDgIp$w;SSDCd)2cCYPT09EZvFIsD_b&&; zoO`VQ01v~RCWeB4IJZ(r9l4s9j`4j~s)lry2`vH!#+L8{^xYLjcPVt1Uu9MX=`I1Y zdPk)|aG(d&2_Dt!G~Hb(S1;PG*uWbU2Pe9??M^jCJfXKFw&d487+HK?sMEhUZ-rwU zbj`v7mu{V&U@6>W{{VUnMRKy7FO;~W17c(j+(mSc#9<54S!69OxSLf1uUru!xId>g z%}`RyX;=48Q7OMEf-O^Mcd>?zb9 zAm-Xj*6b;;rLifLw%qWDNd-#xr>fQX2}LZo*z)>kOnpXpUrO%I9ecvvWX3<|Rira% zv?5E&uy7B*O0Fre+6yJJ6bLGmkffeL1XpQl5B7$5jiR+K?DmVdvcx!cr3-mScn9)@ z2^*uePVx6g=?@(|9=GwcL0dD5%i2P+R;?r?Di5fsAV6A7bKDuMRcowLQMMS#(&~Q> zLb{ylmT+*T!a`e2DJlz0kPjfq5NPLxs+K%S5T#C>RFsK`a8wqtkL7PgqY!Q#SsR=o zAP%IH(kkuX&JbAZ4P%e<*|-b9y4s5>+?7IyBloX;a=1DD7pByi)xQSLrt9IKM6LGD z!fuH2+)_{cVg$!?6}oL%D0lcL&_^i*{{T7fT4Uhhl>Y$P%SWTkQ7wy+v?@S8daHM~ z9Q?#4I&LX7TRW)sJmSviE$xm$07(l?|bL(mlb6r-2w3 zjjR-_dPhh-E5hY!QA27wM-Y4dw3T^n1^UvUNRl}dNi3~o1Qhj=CMW?#%cj908-MZR@=6!H z3Hy#|=XN;IQ-x|+?0M#(HibB{M1ni}(-ujrDMiTv37$sL2A@jFPSkQi5=f}ZS%5cz z$0m}ig|+~IK=!EMLh=}EY@4kr?Z24vmAMI5M4x)C_@CkSN5q{Ef2Hw`+S^^{ z%Y?Fq(I<3n>R|9H$McH|N}F?HyPo2;=Z{*#<3xB}me1L^k$Yz2N@>Ni92=zdPu7}E zb_a*UHBXFx?C{V&8|b9p+3GdlKW>fJmvI+>;(m&o4a-n>?MvVbvxIxdvmm+v{Ap;n$BCyRBd~R(}1Qd%=er zO`A6*#HmVvCy62}slE{McG+zQl)ANb4z|3r$INV!m2e^_o+@x|O*T=duS*%XPBL$= z?ug|3SCAz}Bvv6MGcon7q@^IoKWg+*=!)bojzo2@as<{8s3b=Ob*~iz$3J=iV(G#d zc6$P7ex6WHI^7~vHq4RMr(L6Uv$4dagHZ5XW0`{>Cxh=#*vT)|K~X=Yda~Kz^VYpc z+LCCj=`AUXb4=qm+QAKSN|ZmmdrcJ@GY$f`T5bf-E-U?M{VtHK9iOBn8zw!3nkq-6 ze}~$olLa(au{;&kXq-55MV~+2K4SR(bhT*wYLHNryphzXTr3#<4IMD4^vAKRDQO+~ z`cgD1EBGtx(fHxX7e!nF7fT#|^pEU@IV6Pq%0V3k(*FR~j;InklT8G~_xsWpmapKi zFGpDI^)B*9MGMCPAI__~qffD8XJN71hZ|l>-77=SDPa1(ziPd>qqm+9V^y7C;c=`& zLiRWKgDL=ZQ4(USlfoq$As3X3M~2O%INP@R zjFe}Ve8AUwqS1{+x|!RLk=aq)QG4Oxq#3$58@9R;-#WXRhDkF8iOJRfv_k@cRpEev)_Ey<*xI zBe?9L^&D1hpDfg@j(Z$4rhIxjpEMTZd3&pu*=EQ&akPfoNq`cO&NF{HtJC@kdxJ9pKd<2UJ@kQpJZCIdK(d|FsWq`^WDUkA48cwH z_ULNn;7Rh(2ODI8BbZOQtt;{Mm1|O9^dos@S769DPlA$0=R;_Olh_`3>sxAYmj+2i z#Nn++tL*glr?$JlaM|NFD$mR*Q9>54P+O76{GdpyzJtiWlGbcr>HF(Ug^jWW%3rd% zE+r2o1cAr|cd9PE@b2eXUSq?V)|<3y5W94;r0-b=XCip!Cbx%(-WOti8T=K{OUt~w zcXF0jOHO3NP<8SCZ@Y&;(ErZ*KB25DZkkU4cT{K+*DL{h~{Rj_H6l0r2#&a zi1woGVccD+pG%C)@Xqn(5$_$vM@wgSK}|fI#rmr*!kftbrMBi@f92e`43x?mOrA*+ zPhmtYan@JaD^$IiHZCc$v?$ss(%OKKjibL>v3PsoABZ~b=I%6RCELdqRM>LT@d02* zB%~6cBiwhY{-@D-y&;}XFC}i1lXq!DcJ~3|;);rP$mp%YAoEFb_%E)DYm97g;Ad0d zbhe(kwQo#jozw_GFnSc?&YwUe%)q}*353=46D-UU^ ztx<=$zGcghkms0{sIQ?{K%hE`rMIau2~yCUow9h|ON%Mn^fN<8(sZ_NsbnP5QbA9o zD03odZLP9o?LmoAQR9jZ)~3R{=6~LnVM*kqEQ$MZ&0uFSE`+b~OGeU61(#>Pe`8viJy|B}|g*t;6hheMZXjl#t*Ms#jG&d`VxU*wr<(h2nK`;{FP;o!vrtOzt zt5hY%Zq%*`QCdLh&u{+8tSI8yQon zT0)3~iWio$df!In^OlN8e;s(sD}>5cm875KYJL6NNX84Qb;S4wQE-CkI?Z6}U)BoBClRa;F- zk-@a{Y+c--6!Ed%6YNq4^Qj}XacHHzq_t~_-{iOudE4@MqE&^7V=^doaTKp!wR;|@e+voDx_VDf7t^1RSN>=oa z69ImZCX}VMWRxQ0@MsHr!;V%#=j@hMYLXscz*>2ZWO2`Uq0ab?mVGX^(t{SOsO}sjk}v$%6)h-mQw zL?2NGwJSNcJf^O4S2lLHH!ipklr!a|rL(k>dG&q!RGZ}S^sZ~bmdCFQLAzKOFW4}K1qpGl>QTUy@mPEvn{#6szK|~h|VIe8nIRm(* zD0zw|V3>eEoj_K598)JEz*@j5M*jd(fiNIo z+k33+b?xKY9}A1jWRqaDzQ$DvAT(4zCgM4mkUMpxc*^#!ztqWhQt2#CoqaatF4`i= zi)0>TtI(oIKKxZn!#zCiy0+B$EGDP5!!J(bK7Gg373IDQfgz{_nuOTwA`W}Y{?^d{bG}6H`Z)%g01_I@00SWXg6(W$WY#gtyWYc=w_W~36jsT zadhR|MHcA=OT2y#-*7qNu&bK~mX*0|?mp6XF>h*1o>`tsl&(#B_Zj3g5cW3K>hS=U zZvK1?sO=~Rl0B*!IOs<*=di{M+OI}QONf9edVR;7SPsdr}j@E9uPu zAwfxk4+56f?Sf1R z$&vP~`AX`q+Ps!rOG#SLpn`U%o)l>*QdH{HQ?h@$4;3L`^0-KZ@iEOU2`K;%U%3@O z7@KRQ{JPq5Ry`_5B`59NRLAfM#Wm+$0_#dlp-pl=l4)cT0l1!^E7cqtv|GDs`&Pr| zn{9x?8(UjpNpKI$N$V!7DFi-M0s$R58ZYHorPjs6Yi$oC5c5j(qI;R@DlDE1UU}%p zFXpaMaH7BAr35Qz?>^KWUbf{u2T1Kit}!lS>@Jl0R)W&fgs3ObpnHQt+k0ww^sOXz zkZBAsfSFYAf4e-zS61SU z!j$1EQjaA^y;;09yYe-XR+yDo-Jk$E%SWXXd=*J27n1X#v6fo%#oPidlFiNK{y;Zs zR`Oc4ZP+4Kr4EElNUh=VLE#$wC(#LeS6XA%^~fm2J&L^!7VbeDt{{OEGr_J$yuw`H z-=pL%K2);dakVMn%F!JU-mG?6EYlV&-{f+?jk<;GX`Abm+y;S31o?B{fhVPT?PT(4 z)WRBBN8Ozb@TZl%b5Ce{A27JU)VInIWh3}Y;E&dSTIe4K^DX4CmzeK1-8a_vO7j{* zwv{DGNdyDbaBGk>e~uc5MCq*#{hpe!aX9>%u2b?Yga=aGp+#VHQn`~v_0Ni$3sGmD z%3Ij#+;Y~Uep=k78a(ioM1=h-oz+6f>bywDD;PV?1nQoKFqE*--VU(yt$Ar^Bo$8N zo@u4F;&(|x6V#QhB0pKLovZO5MvIRjSq)sp&cf(z`>>O=clM4e3fDS8)wb$xwN>Ak zfZ-t}9d?pB zcdi51R}Q=42B}h z*o26a0)Ex1b$zOx*P4y{=V1KNQo<7jN`b;hxvP3~Y*Vs!EM=Uh;+aBvdgBM218l7l zvLAU%X5Bo64ivNOpY^F`@%u-VrgsONLW69y3WF%_PaKo=rEGl&ws~nlQj(z4Wl}9b zF)AMY>c!!%nAN)5HF1g2C~Q^7VWESx`i!HxE4BqBy{S1xT*M`icAYucDDJQA(FYt+c1k^e`cc7t8E{Y z6Tt*@HMq3*!gr1OwWPgadv5i=tIW7gSQgMnRI84^YOQL|gl`nI<>+fB_C2Rp%2YA{ zC28l7VzFAN)Z=%t-gM1Iw?$Wn;k*@g{w{RmDnU+|)ZUXDS9r1r_A^b~{9NhPx>T(< zsZqUCA3UTFwGvOmmbhUl->Gn{Z;&@+B$7Jzrf$9&b$(To=V$OYOrc-iyIB%@&f3rA zwBIlLXSTYYuiCHw06^D%E^tXoi+XoFl@T0eAOrdu7=A1)Ur(z8o>OK}3i5|(2m7-} zN5UpyOM|HNs8}E=CiIP?-}2StN&F>h{Pt%En|^&9QRNlNRCX^k<-w= zf3h#A>mU7p{*6O#;#Ib{)4BXxSgX4cb`!NPyj)Q`fh$voumS*}Hw$TMUd1Q)K`D{n zw;uJ>`pf;F{v=>c+BDWP5dJhnif-EY?^%vVtCRMwcGtQCJK@f8+$N*2ILw=9-llg2 zdQU|HI3U)l6>!Rd$Mv5zcjnt104)n`IE5KlN`lBEhy)t?fAJfNyVM>W=}lXPWmov? zJ7kvyu-;PBh)D|(*hCXwG?8tNLnhg2zBk@im|qQ#Aa~ji>(;w#;Ctd@5scC<=*?N1 zMUBmh_G4YWKf-q0^W~KuI5GtGHHWAt2}b0zgMZ=OOe#{Md^gz9TB|Q>-N}x%wsnV!9t3!A zlR2!NwwxJ+512r)WIpiyR1ZP$tl}T4(M4H2QZYf9wC|XAX2H)PKqTQ?2DJWWCM5LaT zmvKH*dyrI+e`)}25B~u0cAt5gc@Mkg^4{=A2^AeS&XEPsPzU$b$)z%#==CHdo)QvB zpb3nH+-ysUQaO%AHFCmPQBK04@7|be&jw41f$XYNo@@|5meANrq!ki6*OKZ&WjqLoh@fbWfUW-k#0dkipq?P;27FG@ISn)~ z>~Wtow#m52DM7FSB=P};M@gejyXr{cXTKdkTBCT|s$bC_6Xx@_S1j6ITPd=`lf7YE zzruQtr+PW|B$9o~#;o`~_;t`4KJz7esxR|-2AYt~t@=q?{{ZDk9VhEr_eK0Nczvp| zOyt_f<*saQB~2|^CfkIA21IogqVj(en!`P3bk0RKmkc-*g#5le4Jk=I&oVyMV$k0f zd95R*vG_|}G~L=6L2Lyp@{&x+o@cFj*2?tp1W==)?KX7&FX2PMT=qdY{3Xs_!zXT; zxVLiTIH08TGXxNOMQu-n<}rFVNarxNmrg3$-zx8xr;n(D0qggg=T8T|Cv=UTLi1B< zb&EXx%VDJB*+KHq2SQ1oSNhjR{2=D+^(K|nxhrKQ_Fc=bYRY$T8gQjmu6U zyP4pQI@ByKZq1t(zmAZCBy&dIvt%;lHlNC1B_IkNgtTQA4Fxui{B0d3gF9CEi;?#A z9g3&;=n?Ly=Dkysk6?nJ({exM73x_p+kdkarIo)=W0q6O*h;zWB8kdZz#xyM4AVtU zU@?SqHm#G-L$-;ig#Ns7OwmW_qbg8>CYdQpxCgI1Q-~0gx;Qj~s3&(%c%aaRR7ym1 z9gQ@olM3dYE+nXeN79*8DP#fq`_dU5(4`)xJ*uazB#bzM58`bd=b(hB{`^&~^$on3 z6I9K1@*DgJs1Qy`Cv@~4TI!hrZt#i1yb{zzZdq3c+?u%GHmj>g5ZZ!PdKGk#XP>1| z{1a`&X_pJC3t4c0K#n&U{#A9maJCeq`BTq7&Z^wf!AeL8vaav09albb#^uRBSP}H7 z;GRer9X94YZDAdGD{({dT%g*Nwx<-YLoK9m6SztL0H^Cyb&eL>FN#|kJT5msY=sw+ z#z~;0p?%bo)KAj0r4s6wd6K(Bh<_xO%WW(T?XZNxCOfK1qu7ckmvxlcI$cWElnEgs zNl$(%QOtP-&vRVr4+pf(?u1@d?GdF|aV!@Sq_(!pXP7cN#c(%{`rrQmNp}qC&0C#K zrYjuViLp}BhQ+#EFhN=W0H6Uiy8bWzF7>yDH@F=YgvDd-tS+(NF?JsWEyQmjAaWys z4RXGB19{_p)#I5(v8S$cOWkajkqn^?Eh09KyvgUetr>5YW3@|bNZ7c{Ps5UYJiEu_ zG8w#$<%PL@Ib3aV@KVRa^xI}UPDY;97Iuz3blx%77SqmvRFYdz*~kNcL7pp>arQ{N^2<(q^3ZkrcAO55eqpj4zKWmEJMLT4tv zwY~nwXnYoGWOy0jYBfS^koCr>zj3cQ-hem4T1f}FGw<}`w3o#OO4NKu=WZ`S!Qb6H z)$6a>AZ@cz5L6S?sFcY*=4*L;CwPsb-S~de`hP8&!L^pL%f7#q%D$8-3oDR9N$v>d zx%=Yl$jf{{X7bh#TRmrX$`URZx=V<0Xd6^h1c4j~9@ALqvTkxs@O>kxZI4Zc-`Lgu zH*0*Ki1=(y2k|HJ_PDEu9%n6jvR{96fFrOR)eA)F2eFqqC$D^mS$fIHL(26PBodWJ zCL|G8cd|=L)SFiAnn6fH${GMF1V{&^B@SBLxXEtOD9MBt6?B(|8P*UB#?6WBwAs9D^R;(vfAQ+g6%?3U-U9?oji0B_a|4{HpKQ zy=^;&t*nxiCi>+UHmX}IwP~^B-2>5Ey}N#t5ly)2656>+0RRMn0Gggzq4kU6)=GWG zgvz~$#NrWPTe-7&#~io{ND7{xsI2MTIeCq`aaY$)vX!KvZ6wK1nSuvuy4zu%y;wm? zQkK)o-Ebu!eb0Jg_ZF;m(~PYUq#nj(b~LhPyQ^DlTwi86XvL!3Z3uL_wGaXpHYayr zNjwT`Fw?gFS}v^EvAgvJBH3X{P@WG{Oso>oNB2RPii+X@NrAL|C_IQfric$DNr(sB z_N1I^ps@LEDQRtm1@4|?@g1qhfGH*h2tE0;k zX@N6(uQR9IIPaP-*s0-iTUp)|u|4O4K9zd4)ofnjarX>ewC@T}%dH?Ul){oD>J4kX zY2{=dYW*l_2y<}U#0JTdXO5q0HK#QOp0LDPwx?STw%j;MKv6P#^&^^^M`XJeXD-`T z@cs?g(ji2l)Th);^FwmlivIux%j(4wWaEQ|WlMz5Ne7>LiuS0*ByLVqVJZZr9;yET zDhH6yTRt2u-j`6Bg_OikZaAT!To37Od2@HD9DefS%e#3BPo7h|%=C~EAJUE4CDqy~ zCf&B3eIZG9VM$kVK=-Onmatkf_SY$l9{r7{O9JytVYbA2hvLsu$y`X@!ZdXy#dRC0p%w4MOPs@`;%F#N8)qbI7=HD{-Xy9z#hR&>+HqJ$>oXV~uE9u*K827U5@g#nV7}Wc1=m`qgel@#}bB zNFz6cKl*0DgaD+ri)OAuRGqA^YNa6b;6dZ-K#`@_jnaCF4qTy1ye&Ks5<-$v`3p%0 z^dsC=?MrF3Sv7Thi{B+gD&>Ztr6-_><~r6gR`%tVZN>UC7Y(gTH{mKwqm8MB4x%UZ zsHr?)a_dQ_gjcj%Su0I7afe=crZG>tkmZVpl&92_=}MInQj%wB?rPI%adL}Kl`XK4 z&@*zT#`F)N{o#RryhFmWwo3w z@Df4;{SzNrp_HhY9&Vh;wf>PS)UM`KBC;7X3r z38`fC@MX0@De26b^NAaYN~T93aaptl`+$v#Sm8?B%n9a;WN?WWEh&Q*?(|U|AoIub zqa*AF2n9P-Iqf|uO%i~lo#9eI?wykTr9NDUQkPzV72^#LI)gStsQDt-O!i;R>pX5t@Gr_5T$&<9`#^`YZ( zlei9?L8O4lbOc0`9P``%0J13re|U4pT6MN2ca5j(?OswsB_NX~dG@D}2w+!w>KV;=za=ZX+$jw z2dJWNWbM=P_a7?y?+~57YQPCs924BsUvNMSrc`8Z5bIWrW6*>pzbTU-kv^mKsqAC2 z7kHjk{^S7Vf0YwIuQ5Uv+^s~xB$9u-&sslam?GFYaKp<}XiFv5ln7c!KtZ5rP5SaI z;aFkY#NA{3&p~a#u!00_J;xoW8I&lF0%wXZk;+=XZl-N56KLAXRN({jC~+t2^`R&D zfT$0rGy^iy(j+A$_vF=&!-q-MEC#{?{!Z+rPKiJvl4V2KcdGD6OM9U8~ri(I}A)rInwFRqcfKE+5WFk*`!YE6kY?_jQ*l3aC=!rMUi zG9(VYtIcl=;HH;3W0nqOtT0ygSnWTjGj`1dlwKunIOyDDsE;s5JoKTh<1_CsW#%o> z$x%(Jl9^9o>QU)g$T-EZ3%kHz@0RSV=IkGFw6C6hC{O_L?O3!nZ?vT&OxU=PKfQMF z{iR&gaG$Yq>K4ImRuD5D8GmT6nz+bdvVkqP}4SV zD}AFWh#b#qLRDhw)RuI$lZxYOX64eO$@=q2d|FyP4(W?qcZhxP&H?R8p}jrpU!3UQy3d2TuTdo z=E~ebCIQ-1XWE3*WbX4e!*tWZSko+6U241rKQPR@;e*TDjyvCIPN-(oQ%1xzlQW!jvl_-scY_7*{{M zR>!o?H-=DYC(pVe$JR%wN3fp24%On# zy57`wFp8HVaee_JdPr>_>x}b$_W+A-vYFS;#~4nn>Wffhf!pZ0UzGr%+{#a zo*HUD4A3&zchK`pJeOL&w**8V$l&YEZ^mwxiRlwe-rf0)ywiwFj5v~&w60R1eZ^TD zB=`-f6Fd{~mmT76D6)mOWt+7~Mb)FnYLWi{IzjAfiT*b)^jR@>$0lb+5(^ja#+UA%23Lm zJU|^(conx(d?}7*e8Weiw^?S|mnhKpEL!=CU8kHHC(?&GSMTjnLq$sT0>e*4w(ZW0vk&|paHdFw|$8J!>c zNzD);BL?{Apc{nMHt!&&#mmoIFt@+=MQEQYPgMT^m0P|V!cT}fXe%zO333P{h>>2p zX_z!0sF8a3WbwnrKMhfX(oXn)dz#ZXxe~i8w6e6R4LaeHl>rgon(u3`A!=AjTHPxt zBqa7fwQJu4?=G6~yG?m-Jkw_$gekd>k~saQwi+*z(|lVc$o}fyDXL#A`XpA=dR745 zuz(Ck;y~xUVB|wC6slb3^MUoOT;F-DLl&i%4_%AjyY3BmToS-a$mB|rqyfzYj5%^# zC3`}UPXwo$%muUx+jM{kPQxF{n@gc$HZ4g5^F&2^=bpAgf*&CSkNAKPDoF^!np@1u zcPEJ2;F+ZPjj6qbuPjIRL{b-WTc0eg(x{l=1KyG@P=!Lc>?4|D$m?lgA{IY}p*(e@ zwBfgKwuf9(Q3188LTB$uSxaq#w>BV{8;4J+rif@Sa$f_91cGP+TAbZI2M`X*^J*X! z70O~b6_B+lwE1o&(f}li%PI`|h*F6^;o^Y}TQ~}okgkyfy*#ufy~Qq-0ys*OAfK&a z>x8Mb8Ak{5kpz9J5psA)On->TCV>+|b`!95=ZceijBwhEmL=9FY2pY!S{MVZD9VU2 zAfW#MDw%DOcF;?1s#GOg%+K6VG($m2G5JhTEp?=ew=B&EL-7%KT-{v=jmY8wS6T94kI#kUi@eQjq&4V^}_O_)h zsO?}O2W*l_2fbpq$8XSzl(bR$`<;g8g#HTZtyle?!0A+C@^;q#ZOZMhTv`+)2||zN z>)ck+_&mW|X}vqDadu(A!x?3%Dsf$XAV>19PVige0|AQ3t5fQ?EQej`XsJeZ$w}Y@qfI|qF`&8wWlheNjDn(SRR)$mz9R`0%{}5B%Z^)OC$vK-S();fiqEHL~%fw5z;xT zUcOJ5<^%$FeDa7f*fgoDo5}0geQJf`Le|&ze1&ZW`9@|U8dv(&Obge9GIZk49;K2y z_cdd<-rJ-sgFQO&Ro@5*UYQE!Nsp&fR$SG?c6RQ*v?tl(OD=L3pG#!ciln{*&r+JM{yf(TmqQ8VooQE@LXVRf9!S~UAd@at6z zrSCu?_kr}M{*n2Q)+t)8#V-c?#06||r&N+8DjXeuew3x+8Vx9U*J_^MG`NqiHS4T; z9K|lkD2sit6yt?T>mU+(fM^*lZWOIM)S?ooJ4Fb|W!SubD`RY>mn_;rxOZ=*1#=C8 zf4v-^X{mYa#l8m*cagpS0Ecehn53Zc;p~z1CL`WxZl1+l*lG15-DTLENjs^JN#DGsQYj`v-bxs3MY4x*W@8O>? z0^IV_wHKvdm%kmWdidqwPFGv&eKoHWl*eO{j!I<@ZYEC)Oz0LF^r2AAi?j5>)$6s+yy0a=TQoxVTg6al-2g}YZOKrB&Nq~Erw!LTIqhSWyDXp%O z4-SOeq%YWkGY72HqgNS6K?^myx_l(F6l(k)kI3aRHdf49w{WLkKrJcsq^&!Ir>}7} z^;1iDDd6HfJhIhe!hCPxjoFtjTzLsAOKC){&8krf5*6Ni*Uha&_Ljw6T3zUyjKkMW zJPVEH%K@+;`jDimcLQCAl=!0XeLoIQ=<7SIsLd}1Z4lFFSN_nXa!R*t02MiF9*}%Z;9D0IgPux%wyYWr|oVo0z-uhOv2-d1RA2ZfqAF=tCyR!1a2UZ z;-l;hMWml6i!~^}4qm{;6qz0VhM6eyln{X`JVjj^@XhD6Mo(7^n_~}QeASDjuuFwY z?h+3pk^9t))+<0_ygISF!)m*Xrmwhs2W=g;Ei}?3%#sO&f$z;2!MjhPy4bnJ6i2aE zjI?~quhR~vI`yn-*5+lX-xg*H*gdcJ-Yq+ih)8?26? zeOkP%i(GpwD6_N}3Q>sXp8Qo6{VR{TvdBAemo43)ux(kkXsjsn!pR?}6p_(rbhlL{ zCW=E@mNCE!5qD_dGr1~HAEBalUMFU3ZG$Lw-DpVKlJ-57kUsH2FR)jZ)@!+1N0Nty zy45`ww{(8gKgucUd8<*=sWe(&@jE+o5`lEvX#j{tlPRCS6gll_nOaL{BKSviB?w6U zfT((V4(6cD+T+`BGOL9YrDS)4f69ov;r>|`_g4-$z)i78b{8-Si5~Q_T9l>8In6_P zbjp~1<;B~nQl^lEq!ap8he=stZf;w5CxXb|+68muFUv^-sGoWvkDm zYKxuQV5Aa9`BjS_e|Kz%MbrC09*;1A`%@t(wyxxfkE-ux7B9b=M44DyQHO8h+H9+b_9F<=xbTqrtQYtyO!MrN}o5-L~tUvaPT(? zh||6rJ#CS=$YixE2!8!JXOxQ+DSwng2qGtrXNrW|XE&+n(z7;0lS-gC!@JrlLetcW zn%;XP!-Z*m2~p}4(g;6rM>X5;d?5HWqqL5tZ&PYVT-A2rX|QV-l2A&|m$|<}lgTDz z6A(VN!pJFgq$`3w$m{i}_c^_Gg3Siq@3>d-`-hual$0>yi00X#5`LS1(!9=Q^e52h z`>C|?{HePeCd$&6OND9#p|*gN_nrlH`hE(0H`3T_Bf96sN3c0OmAYT~e=Ve>0wjSv zz?hRwMZU+KRW@ofwB$>SmG$Cwzi_1jAb^l3=wMV&CyoaKy9Yh+t>DGAl5VlSHLdN= zrJ$81l@7f06Vi%W{33W7?WuTp&iULU)>#63@kY~P>3W9n&LdJJ@z2myD}=z0>s{l7 z_#E(?Rl4ntp7CPiC4J?oY}+y9l@bS12P9V-YOE$ZO=Pk-tHyGFGi7k)yN)2YwZfGY zlAbzoMle+^Ea`rx0>Pl8M{CCn`DwSaZCgb3dSm%jD5YMtB$5(ikMgaLhDD>A2gEel zHu*?u#Y*{=BpLIFfw#Bbweo=qDFm~m8$we8qxgU8u#q zm8!m8)zImu6Lh3poz8GVeYtHHODR)peKL)p@(;BW zxp6F^Z#$4}#uO!1%1}^~{t!8e35$3Xsk3C=*B{>oKUk^vA0o?WE?G8*N=j18Yd(|D zB=bNZ9kXWfZ;vz~2|}GD2@pF`p_N^`R<7D!Hd{(UT(ejl`^fG-vJ}~2`H*>$6b+z39n;!q#$y

s_b7=+dPK|++ul-pm9N)fM8GDbfg&^^8y4$0sQK<)hDTr^nb&Qlen!^-QzBn9BR_-ZMr(4L83waRG(ut{M8`dIggdcl{%bx zv`c5r`G^mPKd&CuZ1AhY4ME~=dCLtor>?WNp@Oq#ZjFI$lL}Ilr~xC6W~f(}uU=m} zdvF_7)2;^=ppZh8W0)L$YpVVU{6xsz_}`ujy;7BBhguqW2?-(`NHPRZO7q%DJe;*1pn&Ex;bhr_>Z+kQ8V1 zBE0uwZwToB0Qi4R^=@So#Ge7R?}H1qcUtMI%w?7GTtKC5d=sj#|XIW^)}m%V1l zxcZ1;r7Xtc2UL!=*113Sap(LWdCgG0Z-~g-TQa8<&&s&c*R+Kcr>K)0Nv}!e!Lym^ zbj{CnJ7lqEo@&C;;dY|yt~koV!eUY+kbb<@=fUJ#RikAJ3Q0<0J)^B^ZxFPWXA`6C zud=r9TUuQrS-D!0xg(T?k@-*1);uin6^r{TY<0@oYWVs81EHZxjO$jy*?wBY`tKRjc8~=gRR) z=!3gXyj~ zl^7~9$JlqE?xXbNmdius%8Efxa1AfSsuomAa*ENGb^j zrqC&(V$$$Qh_RPt0{Qd0U{b7=)p({B(dMc5Eqa1Nl8+K>)B^PXCh<)LUQ z5{MJ~)Kb#cl_f4E5y76qmgVfI1a3%)h=Sz zEDaJjg#{A{K;~kE>Rcw1)Y>N{hRoz|@e94VLYI-WVRr{)M$arBJM&MW4=fGtB?*tq zGsRFmbj!Vo@QVQKKoh@HUE8MUJi=O14Vw12m2QBfpP{3lVeU(~Z(V3Vhiw(%UP9Lw zr7f=W0^Sv*jVahiHT95(g%_XW=7VwXeUnIhI>m8g~5( z@|Kt>Q9&l74DjXL&sDlfSo|L6OEx9A>U-1eEF>B0Q5s(m1wPo1y?SWgPhr4%)4=cy zf%;Td7sp7alA+2((;4fJTS_3u91%ja*EVP!i6(zqDC(sPN@hswL$v4y$tF5?{b(~w z^-yIgfJA<^>cJpPnEO|$8c7(GgyU_8U96x%`&5|*M9B82whsRQyR--Y0CG>}JfBFX ziG;`}^zBTAQd<$_m=FX~3U?-C{e^frAjmR1#WY?CnJ@|V=A!ajxdQSWehmIqiG}bsxOF23cu6s@wHCAeZ+>3YPh)e#F;;}QT4if z?4|~Hk{58I5`Xwm5A&+zJfmayNT2;vWS*p+efc$MdOX9}Rcpeb<>@9<1Jm!Qjod)2&^!i*!HpmoBL&R^mqhdlAi1F2p1C8#e0Ix0FM}bQu#-MY8PB zuPlVLzEqF^AA092Z;dx?9eFv;GSdx&klXf7E#0c;l_P^5@M-%$j(q`a!V{Rgx@~Jn zlFm8Qr6-w<#GSGF)h3KHe7Z<6$T8oiT4DW=Je0L0w5 z#NhJlb0Dx*+c!+9qM$vetz2~;AMr+(%dd-j7nF~f4DjQr6DZyN(0UW?K%W4s0_L>5 zXESnx_&uA2Sj%OI`IU*AmlNCr`3LJ%UmSiYbYFqS$&FEyRlY*~3w4&Dle8GzVqjI@ zHK{E${es9zZ4IMpQh+w3bP43}0I!}O6;~MF7CcwQTwdBGl{toNyEZ#M&+csClA)VI;(xOl1;8z^$ol~kYI*T!z(C;~t2wE5y zmhBgO#GlTsqoC*q+O--~^3etL?Hr?t=h`B|} zmbQvPPxuT73 z_j#=&iFBMrw3F~sE*JI_c zzptM)?-?#uwvypEyCX9lTQ#Vb`!}jj%VT_;g1?5h(CItV{{TWvzBh382 zg=5pf_Gpf^uQBYZ_1mj99Ywp(wou!yt7u980IRhYuTk?2M;FW6Cfjb;BZv4$&NAv6 z1@DO?IsVVw3^VT6tohd1aLsvPO7WJ3I-hFL>6^Y|*r0NiObc@}`MpE}XWEH!k=^vK(HvGb*|rwt!|81V5(*C|k|GTW$Ybr% zZX9jjF?-5NMEXQb&)$tN^|~&1D%~m@LPA#D_JjKHO$BjgVt=Q%+O?JToVl`63#{x*l2W;lW3Niy0~BsP zA1)Bvs!E*Iz^Q0Z3IoZBA6~UUxW+kSWR_jH^TzU|1#+mK3HrrZoE+`iwOpSiZx@HS z!@rKnG`6lDacc5y+m$FN2;>fDo|S0D+v@FakGrwLX_qgMw z5CK|}lLQ_~9rsjQSv`#;-SuzXgPHM8CfXIcJj@a8dv*^t*Nq6uonj@gz{wdk ztJhld(&_U6Dr@roVaGM}8^m2?%V~`{WR2TUQD}!s z$UVYP2=}VSnfSlZj@qFaEj?(%gvRSOn<)C2Qb7^=)r$AxtHY+6Nwvfx>?s?Jw99Zk zxuIPTX&*rFHmq$m8n7BS2l1VxhK>9=yIYJ^)7J>aq#?AZrErx1dcYsOeEac?{{Z;M z)<}XEq7{+lns>MEELvAGRa(3>4z%YAzR{?mvxOkVW zK^v5F7i9Z?#aA_Z#G{mOePXp;ya2E)m%KaiD5Q`Zx5t%7PHsr&`Beqb60_=vJQ~DODSw$lL4tW5=)ocOkd- z$g_<@Yqtu1+ z_4b-0m9}>g?ZqcCde%<#y4-{Y(us)NK!T|H#Q;vzNyt23X2JH@^HH^4WDr2+B=o0} z?dytpwk~Y$T-tZA(h^*V=6L#cp?JK4Y|!N@Sdcf!PrGQ0rB=fX^Jt(NOv0<>QtA~onos6bnea}i5g!&>=2Z;XwIzESyd9inP zw(Kd3bfCIP0YFaK1Haaoh&fv|KW%MyZ+hkG3dOifvQ&fEAE)|KGY&K6w`*+ruC46R zx02$R)O9Y*PbdE%Fl%`)aQ?@@h|@XL;fdUXEb|je!}M8 zU~TmYTF)v$?-C>$E2Xf^YP`lj7}la_TjZQ&mXiBLKQAUHZ*o0>=ia&#zz>9v4YhxY z8C@@`vzNK1tt}s2!*o>FNmE}sB%|% z8}=>j?_Yd4^}}G0P!JN71fG!{ezg+^i+;2J{3F{ z(z^RSgU8zHR9jiCC_$NWCq{sN>n%8+I-p}t{mA9Dfx>-*p)u5miNthjKw)W;3Aw?)q5f#~A z5&r-Mo*L*|>?2wuId_9q-L1-&jEjr7Le|g)AaDw2)%#Z`w8kpI#u>S_yvnWr0M7lI z_9du2Q@9>}^`T)boEKG%i%`5nd;Bfy#xOn%I3KjQb*8d6O7j?N_-&vlA6kizx9wMr zbK?b-?}R#sPVQG~L;2_$pC5ASe^=e`@ACtR|jbnzyvU<{!pcCAECrwo4)t z=PgSrB}hSo?bEdi$lBZDbV}`P^TT<$KsI)H6)l$=RPP18JHb3xpDi})9Fsc_JzO-K zBK?WF3shU;wKf>!F=sHg?oe%>^9o&U#DY*_V2K>fZmds>8lS|b7k`?~-(G=}QweEr z5`;F5hC*kOK{d?w85HBPR+s7iE4Gg%)C0S2@iEee>HU9yqP3c{2K7j_OQrN#?*0jM zo>jDT=~{29Q-;{H&*+-CR@`OseY?{)*pCvl6E{{qPD>AT$V06Jg_hI=ZYez!4>iJn z5grw0JTTSEde|oG6LzBIt=*bDqJRe}Qb!>}zuvj;MEr1TFAXzyxg9x@vaw?5mAj6G zD%_~`AWsMCDqefz<5y~{$1d{?;WCR+Dnedu&5Z-W2>|!+HRQbvq`gS(Yt<^-+RcNC z@w-#uno}+0oN)jI!|jJ){m*)q*Tfd920nF^$F;Ige`@FKkHr2;_0_nWTq`Z^kkfXg zr9Y`NO||hCc2o+{g>Pt$sk1VEwL7-1^-bXccaeI(;z?PbL%0cKi9_D*=A6n)cFNzDCdI<=@KIDFNS|}=h!YIw} z#!hko%|O4CB%l+v3^_wS?| zo0N$E0KyOWL<-y{ya>R2t=GqfZ%!XaH$k~#frT{O*4 z)Q=0P>g%shDCd?|4Dan8$E^^0s>ZY(bvAx*z*;X>2;j^9I2Qa$)jzj~ix zh`h{S+u7r6?lM=e7;4(pvbPoXNUuWOn_m9M%qsDGOw*kH#?W)(v&CmINVYo4>v2g$ zr)Y5_q?H8EAl9wQ<}Y(Mj$Pi|Idc1kRO5>(Qi z&oRF=kNPHSROWCQEjy8Z!zqrs#$Q{Qa^CT>+SB?P!%vk@^6`_>)qlviV*KOx$U7h# zXC8)-va5%N_Y8hS&!h^lcmSm_;+Z>1ABMga0(g|rv59{9_8Mg=Tdp8}WdH#8C)%&y z4OssG!@P7=tNvU60QY6ecOc3SPJXrNN4^K3mL`$aKMBDXJ^^7{wP{VHWbz6LaETws zHMzy9YTPBj5e7Q)39Uu&-s)NKQq4}S7L7A`d`FZyQ1i+A^)*G-e;i&PX-z?Qg1<$< ze$C%5;YqYxhj1K#LZF(dCf^6p;q;v_!fozW^H%M+@kY_-Bq${&u-j$3gdt@x6lAFM zu4>Z196lTBH<4v&aOJc9SSRL*Xenh-l)?PRq=8ax{yaQ5e>BD>;k%n_pz*g`%Vj~b zsfh+)3X;lrCx+Hh(urN3^pvm9K;=*f=9;>H%7wO#r7Du1pi>Ooy7jAfRzeslOPin& zQhn)4b#5T2h$NCoMM)6A>vv=BT{b`^Ai4GJuJVKOz&YErYHV)JnAM;H*R*Uxcd3zKD4afYdLJ;W|=uk-tgAkKV z`6a~?uu=y=4_aD0#>oXFEBth~+FOiBcWS|ipkpO0Y}!(^{{Yhw`_R2ZrmQtSo4>{7 zuCJ}}kEyWp*yd8}>EyrxL=I@JxOJh=masp?)~Oyi-(K4IN2!%*J!)3& z2qTb3T8WDp@g#J+XHg?AfA`PG*R<6g1E zVwuI`ZV_j0iG*)ig>|3GBC**ErS!8i)M+7=HOcg^1*7~e@j~Mwhcq@97%_=Yoa+{x zx)(7C9U>@q!(Kke2dFjOmMN6mW7B@wX|yIFY#}^$u3YdJ<0|LFd^P(SoM6c0o?n@9 zdh!a(vBFX&I(t__{3Gk;yhpy*Ih$*9XxrCpsZ6dtCu!%cI`f6QxkYzrr*GvhPMiw# z@Ot&9P_F4T%1KER0TI{Uqy-=(!Rua$utb&7$2Bv>UdU@2XG^))0fa5OVcI1RTzPUAnhpvy1SLCvFVM>Xn7V96W~ zhP_PG3};Q3ZSD7Ea!LiXepf(_2hyagO{{R7wowTjN6ZiOqSn*owZssFfC+Vc@+?u5xvl6|XI_{Qd9+~BYAn5&$V zHm^7?o3%#w8*OQTLPsZ>i{v9jVDgRA;i}R!xY`LQ0H^`aNv>+w9yDCqYJB1|Cf^~8 zTv;aC@iK&koxFPnvX{03=3Tec89bxp8I)!%%N$2&gLE=Bd?Y@6h<}x_! zan9LaY<&q`+5#C-{KzO$hjAd*NgAN>CeNSQ%}c3oa~IZl>vwOh4aT-E~g1+m}Q*1G~4vOcFh- z>s`_tRfEgg;b{rq(~}j+-VgjX>Hh!@d_=L&F{{_ApRiKAw%W5SG|?lgi&s+}g>?dT zJ19aoIDj_=1kc~C7-E}?>{EPH@P|`s%x#d8$+DVNzhnsI(=w;(H_JP8%PsKs(&mgewd;fLV&N_hET{;k&tySZl` z;Iqf$*n7%tlL2d7q;h3L_N|ZOSBAHkjen=HEb3%j+&7E~w7ko(l_tTIy90sNU;=va zTVLRpPK`J4^`{$gDns`{%cldKNm8I6v;kb}@t@+(F9GBGm`s)9mX_~eTDNZT4Hco6 zRJEujM0B1H>qL^0$tNe5A7W?3COn^o&kz-x$71bu4Wbcj^R_Ib%5(V5sSdWSvNt5n zeCSXGO7#Lk{_TAo_?5hE?}Uv>aGUn>es(I%D(#`^TEfthKD>ipIvu+g2x&VTY&MOs z*p1wtPu803eCP25RBl)p+qCjJ4)lO{JbuEI;-jGcbc7O8L7r)&m0u$*4Hn*R%L+q3tfwdy#;-X-seSjt-nyz`r2Vv1QbyD1Ict))8pF`i^we8byTcHBw zlw709P*WyA3fQ5+0M$&pnL|a7n|L+1ZCf+X5DR8rmWo)HSer0Z4`=4qQ z&H~MDHq-Ya+TrQ))8+04J^iY1ZAX)Ry;dsLtK}7c2DE+dU&JhPEvDTYQnjg9iU?$g zA{0*meQRv^o8iiv(~soZU9-)hrqQ<@beB+nia7y1%*f~3f$1x2TuMO{fim>%&=u#_S<1#~eTK9w@a*%nqZ z7k74H3rmfgM#B8aGElBW53O4A`DX=}Wt_g{yZb?9W#Bc&m~l@G#m;>@}~HvW8aKZ0#!LQe;<3d}{dNr2JLz*Gl5FYF)X?W45eaw%3$x&=U#(n37Cz zC!wx6%W5lgrkWN{Wi4}RQ)zrHM701AqLcOD^r^nc`Ug&wAzsxYboP3=KXTrN%&{Ec`Y^+bz(=D6AB-}0=0-`N>Yl9_9WA| z4NI6-h-XeccBe6d=wB_RAZ}B;VoJKe_NhYML<7X1tu}Bv+f%jeWX%($ZuM@h#%bFd z)+{da&U~6~E>u#|5}-=Ak=h6AS3P&|kCf35-dyTZ_0}@kDR1Yr;sfYQgn9zhvH~Z+ zwDZ=emRoG{wvVkZDGez~QDDNr+CVAfatu}PKk*%NZ=&)zTOB`;x5r&5D&=n7k1fUR zPf;ZEw3w}r=Z4ogAbT;np0i)YUeuL9KT5l4Z-uP(BQ>V3wN3go?Y4OyWumN`H#cRK2nq;K0(Suf z_pNf~dmQ@@A-5$(Bmk5UO#ORS)xP+)dpVWR_X*S280+0S$}LOw*8#$`r633gj;Wv4 zo9tCT4aM(f@WlTBvzuAnX?*T)6PvelG5{V8|EpS&UG*D{*aI1t>V6+$1EnVnX@_00`=9NZaHC_G~+O zeRK0IBX7$D$`}6t**y(K;c$*GtSvrb>XukC;qA*>(qdc+^r!w|U3cRqL3uA2TWMo^ zY~ZaFjmM5bpXpqdi}yvEQqD<>1tGv)Wla*Ri6RmVbpHTa>aP@cC&i##fK`IC;C`Ui z9czAGN2mB|3&zet?XZX?AP5|h!4%-PY?x5rzy^9&TQWF2(}W&o5501#$$Xzsa7`9p z3Z6DzI_N(oI|AFf3)#lt5h8xZxnIR+OP(X^6+tTeUg~7@{vocX@WsJz(xm(4XbRB=I?it+T4r1%fL_uu%h5`D0Yp~EZ^r^GvUx=W*egV2^{{Sz;uM$B{ z?abM5JCNz(wffYQ2nst*T`)*=Xgz#W{{Rd(VzmO996V#6tVL3mjfyFqTiB~6JtOm`KC+N1obprT1J`qYm^Sumr}itr{TNj<9pMNlFS+Lo22 zOMnj0dXYgSD1^L5(o6{dkwH{&s>Dq!wZzj{}wJKeM zZA!uOHzfwrS0C~1$CjVDNufD5TerJYY&zY-1VYecAGJ+?8td;ki@c?> zpCk>gyZz(arxZQ=2dwWEXrxQ2;DDhYy$D$&2vOicry8|W>e$*8>x4*30VnIlWU{mk z%i9uQB|!B1Q;c7|Z07bIEiY+Kf7P9x?k1^Ze^osbW~j6l=(`T_Tbf9YC$nYw2j-=nuI*UWx;834;>TbnLnVK zB)BQH?XFC-T3%mi8;fi8TjPFhl0HbGNp&SE;3$Ah5y1!Ex9`Dcia6~VtgXuG%8w56u4&{Ve-*d;0CpP;CE!$|9EJxS(d zF!{SX`-YRfSN6+EEso@h77f#opvRr%If5-rSTQ`GGJ9 zj+H+n;J&lP*^9UI#rtd_2yi&aO3Fb#lj$-H_N=}PbDW(|o67o5_V}Ig4#Qq$G^-j{ zFpJCkF?8+Ht=#hqX(%9qdUFJO^IW9*?k(7dGG^7M5+St`6hZ7j>H5`ULZ>mHZm*kp zo9u1tt%Ci%$XhCLC#26Y25QdEMWora_=8L@%V?WKp-0VWN0GXJGEcGQuZqVA;QFnr zQ+FQ4>%iPL8vg)Mw}KdPxg0H7 z2MLd<_o`Nw@XtA^@i~mI{*Z9GRp#u!i)(p!q`d1&k{eKnNHO(^6|E3T+}MQz4_&TE zN3{(#apfaiUt*BTl*@Q@GDkz^ERUp5T8fM?Z3b-j^9B?_2DW|CTleU=MY7nq*|jA` zK{LXH)J%o5mrIw6YPedAsRVk@*mR*9Fp)t{pF)PyY>>X=a;BfN(#W`RDP6jGAOI9* zt;@wdYBoCB_Rr^8AGI;DC{t=}3TeQgkWx=_0RD8-BGIjGEnK&`d99^e7Ni0@fJmm0 zs1l%4+P0Rd&@bt%p|raJ$I5)t>ql}7%)-0#GwoIToDH4pTQ07ZX5k?zcgrgPPb7iu zODB}|DmHj*#+}lOxmCOW05Zr&rD4UPN=tun9Q~_h-#6#70$#Q(umC?TXXGVH=v5s_ znyBM7MxDS|3sq#6;6~6E+a#3~_keimMe+KQ!!pgw z&~lkMrpQW(B!HRYt#;OhxWRlQ{65<2ckEMjtF!H)X?57%jp>v&lhq&&Oji!2KwG>M zFjAuwV zw>u{Lqm}OdIebgG%p8MRw(N%`HtjbceGHk3-#1!|74fg|_m$1Pk8a;x>3)3lM&|$% z1obH(cj* z6)|Ayp~l^DMeEqM-_(Zs-;F$zE0YBx~W3-#ttOAT3xkT6oS7&Y1aA??g#Bx zUxv%uba>Bd-Ib!O?bD37X(&^h)HnpFp7lb%Sl+dz3POsv5=_UvT|OI~w{ymeGF@R)4e8M{4~2&S9*k zR}J%SDa(sXWdfbVl1fU4A8Jr(A3?@r!Sgq2kmz2>Ajnrj54bhEobfK(H{dP{Pvk7@ zRhzNRAllub1LhRbRFbpbk_4W$)nomdIu&<6{{U8JOK|}!2ps+5iDUl&+0EhJ9ou)F zyUg7