From ca920f72184c50f61002aa9d5cd01555b1e28b7b Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 10 May 2021 21:53:22 -0500 Subject: [PATCH 001/264] py/mpstate: Make exceptions thread-local. This moves mp_pending_exception from mp_state_vm_t to mp_state_thread_t. This allows exceptions to be scheduled on a specific thread. Signed-off-by: David Lechner --- ports/esp8266/modnetwork.c | 2 +- .../nrf/boards/microbit/modules/microbitdisplay.c | 2 +- ports/nrf/modules/music/modmusic.c | 2 +- ports/pic16bit/board.c | 2 +- ports/stm32/pendsv.c | 4 ++-- ports/unix/unix_mphal.c | 2 +- ports/windows/windows_mphal.c | 2 +- py/modthread.c | 3 ++- py/mpstate.h | 6 +++--- py/profile.c | 2 +- py/runtime.c | 2 +- py/scheduler.c | 14 +++++++------- py/vm.c | 10 +++++----- 13 files changed, 27 insertions(+), 26 deletions(-) diff --git a/ports/esp8266/modnetwork.c b/ports/esp8266/modnetwork.c index 3b8ef4b21f..8d153acf0f 100644 --- a/ports/esp8266/modnetwork.c +++ b/ports/esp8266/modnetwork.c @@ -237,7 +237,7 @@ STATIC mp_obj_t esp_scan(mp_obj_t self_in) { while (esp_scan_list != NULL) { // our esp_scan_cb is called via ets_loop_iter so it's safe to set the // esp_scan_list variable to NULL without disabling interrupts - if (MP_STATE_VM(mp_pending_exception) != NULL) { + if (MP_STATE_THREAD(mp_pending_exception) != NULL) { esp_scan_list = NULL; mp_handle_pending(true); } diff --git a/ports/nrf/boards/microbit/modules/microbitdisplay.c b/ports/nrf/boards/microbit/modules/microbitdisplay.c index c2eaf4179b..a0508886d7 100644 --- a/ports/nrf/boards/microbit/modules/microbitdisplay.c +++ b/ports/nrf/boards/microbit/modules/microbitdisplay.c @@ -149,7 +149,7 @@ STATIC void async_stop(void) { STATIC void wait_for_event(void) { while (!wakeup_event) { // allow CTRL-C to stop the animation - if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL) { + if (MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL) { async_stop(); return; } diff --git a/ports/nrf/modules/music/modmusic.c b/ports/nrf/modules/music/modmusic.c index 168e7613f5..e7e351f859 100644 --- a/ports/nrf/modules/music/modmusic.c +++ b/ports/nrf/modules/music/modmusic.c @@ -140,7 +140,7 @@ STATIC void wait_async_music_idle(void) { // wait for the async music state to become idle while (music_data->async_state != ASYNC_MUSIC_STATE_IDLE) { // allow CTRL-C to stop the music - if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL) { + if (MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL) { music_data->async_state = ASYNC_MUSIC_STATE_IDLE; pwm_set_duty_cycle(music_data->async_pin->pin, 0); // TODO: remove pin setting. break; diff --git a/ports/pic16bit/board.c b/ports/pic16bit/board.c index 977dd2cfa8..b0e9d17270 100644 --- a/ports/pic16bit/board.c +++ b/ports/pic16bit/board.c @@ -140,7 +140,7 @@ int switch_get(int sw) { // TODO need an irq void uart_rx_irq(void) { if (c == interrupt_char) { - MP_STATE_VM(mp_pending_exception) = MP_STATE_PORT(keyboard_interrupt_obj); + MP_STATE_THREAD(mp_pending_exception) = MP_STATE_PORT(keyboard_interrupt_obj); } } */ diff --git a/ports/stm32/pendsv.c b/ports/stm32/pendsv.c index 3181ba616a..2887ac5723 100644 --- a/ports/stm32/pendsv.c +++ b/ports/stm32/pendsv.c @@ -60,10 +60,10 @@ void pendsv_init(void) { // the given exception object using nlr_jump in the context of the top-level // thread. void pendsv_kbd_intr(void) { - if (MP_STATE_VM(mp_pending_exception) == MP_OBJ_NULL) { + if (MP_STATE_THREAD(mp_pending_exception) == MP_OBJ_NULL) { mp_sched_keyboard_interrupt(); } else { - MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL; + MP_STATE_THREAD(mp_pending_exception) = MP_OBJ_NULL; pendsv_object = &MP_STATE_VM(mp_kbd_exception); SCB->ICSR = SCB_ICSR_PENDSVSET_Msk; } diff --git a/ports/unix/unix_mphal.c b/ports/unix/unix_mphal.c index d62991d34d..111844abc4 100644 --- a/ports/unix/unix_mphal.c +++ b/ports/unix/unix_mphal.c @@ -54,7 +54,7 @@ STATIC void sighandler(int signum) { sigprocmask(SIG_SETMASK, &mask, NULL); nlr_raise(MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception))); #else - if (MP_STATE_VM(mp_pending_exception) == MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception))) { + if (MP_STATE_THREAD(mp_pending_exception) == MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception))) { // this is the second time we are called, so die straight away exit(1); } diff --git a/ports/windows/windows_mphal.c b/ports/windows/windows_mphal.c index dd196f67af..ba1d982ad9 100644 --- a/ports/windows/windows_mphal.c +++ b/ports/windows/windows_mphal.c @@ -80,7 +80,7 @@ void mp_hal_stdio_mode_orig(void) { // the thread created for handling it might not be running yet so we'd miss the notification. BOOL WINAPI console_sighandler(DWORD evt) { if (evt == CTRL_C_EVENT) { - if (MP_STATE_VM(mp_pending_exception) == MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception))) { + if (MP_STATE_THREAD(mp_pending_exception) == MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception))) { // this is the second time we are called, so die straight away exit(1); } diff --git a/py/modthread.c b/py/modthread.c index 64fbb3f198..29b765493a 100644 --- a/py/modthread.c +++ b/py/modthread.c @@ -174,6 +174,8 @@ STATIC void *thread_entry(void *args_in) { // The GC starts off unlocked on this thread. ts.gc_lock_depth = 0; + ts.mp_pending_exception = MP_OBJ_NULL; + // set locals and globals from the calling context mp_locals_set(args->dict_locals); mp_globals_set(args->dict_globals); @@ -184,7 +186,6 @@ STATIC void *thread_entry(void *args_in) { mp_thread_start(); // TODO set more thread-specific state here: - // mp_pending_exception? (root pointer) // cur_exception (root pointer) DEBUG_printf("[thread] start ts=%p args=%p stack=%p\n", &ts, &args, MP_STATE_THREAD(stack_top)); diff --git a/py/mpstate.h b/py/mpstate.h index a0e3d4f143..f5fb5b707b 100644 --- a/py/mpstate.h +++ b/py/mpstate.h @@ -137,9 +137,6 @@ typedef struct _mp_state_vm_t { // dictionary with loaded modules (may be exposed as sys.modules) mp_obj_dict_t mp_loaded_modules_dict; - // pending exception object (MP_OBJ_NULL if not pending) - volatile mp_obj_t mp_pending_exception; - #if MICROPY_ENABLE_SCHEDULER mp_sched_item_t sched_queue[MICROPY_SCHEDULER_DEPTH]; #endif @@ -266,6 +263,9 @@ typedef struct _mp_state_thread_t { nlr_buf_t *nlr_top; + // pending exception object (MP_OBJ_NULL if not pending) + volatile mp_obj_t mp_pending_exception; + #if MICROPY_PY_SYS_SETTRACE mp_obj_t prof_trace_callback; bool prof_callback_is_executing; diff --git a/py/profile.c b/py/profile.c index e5fb35f0ed..054a0f9e61 100644 --- a/py/profile.c +++ b/py/profile.c @@ -297,7 +297,7 @@ STATIC mp_obj_t mp_prof_callback_invoke(mp_obj_t callback, prof_callback_args_t mp_prof_is_executing = false; - if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL) { + if (MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL) { mp_handle_pending(true); } return top; diff --git a/py/runtime.c b/py/runtime.c index 1ca0702e19..cda26a9cf8 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -62,7 +62,7 @@ void mp_init(void) { qstr_init(); // no pending exceptions to start with - MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL; + MP_STATE_THREAD(mp_pending_exception) = MP_OBJ_NULL; #if MICROPY_ENABLE_SCHEDULER MP_STATE_VM(sched_state) = MP_SCHED_IDLE; MP_STATE_VM(sched_idx) = 0; diff --git a/py/scheduler.c b/py/scheduler.c index 0671a34a8c..0114a7a58b 100644 --- a/py/scheduler.c +++ b/py/scheduler.c @@ -29,7 +29,7 @@ #include "py/runtime.h" void MICROPY_WRAP_MP_SCHED_EXCEPTION(mp_sched_exception)(mp_obj_t exc) { - MP_STATE_VM(mp_pending_exception) = exc; + MP_STATE_THREAD(mp_pending_exception) = exc; #if MICROPY_ENABLE_SCHEDULER if (MP_STATE_VM(sched_state) == MP_SCHED_IDLE) { MP_STATE_VM(sched_state) = MP_SCHED_PENDING; @@ -66,9 +66,9 @@ void mp_handle_pending(bool raise_exc) { mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); // Re-check state is still pending now that we're in the atomic section. if (MP_STATE_VM(sched_state) == MP_SCHED_PENDING) { - mp_obj_t obj = MP_STATE_VM(mp_pending_exception); + mp_obj_t obj = MP_STATE_THREAD(mp_pending_exception); if (obj != MP_OBJ_NULL) { - MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL; + MP_STATE_THREAD(mp_pending_exception) = MP_OBJ_NULL; if (!mp_sched_num_pending()) { MP_STATE_VM(sched_state) = MP_SCHED_IDLE; } @@ -115,7 +115,7 @@ void mp_sched_unlock(void) { assert(MP_STATE_VM(sched_state) < 0); if (++MP_STATE_VM(sched_state) == 0) { // vm became unlocked - if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL || mp_sched_num_pending()) { + if (MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL || mp_sched_num_pending()) { MP_STATE_VM(sched_state) = MP_SCHED_PENDING; } else { MP_STATE_VM(sched_state) = MP_SCHED_IDLE; @@ -148,9 +148,9 @@ bool MICROPY_WRAP_MP_SCHED_SCHEDULE(mp_sched_schedule)(mp_obj_t function, mp_obj // A variant of this is inlined in the VM at the pending exception check void mp_handle_pending(bool raise_exc) { - if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL) { - mp_obj_t obj = MP_STATE_VM(mp_pending_exception); - MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL; + if (MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL) { + mp_obj_t obj = MP_STATE_THREAD(mp_pending_exception); + MP_STATE_THREAD(mp_pending_exception) = MP_OBJ_NULL; if (raise_exc) { nlr_raise(obj); } diff --git a/py/vm.c b/py/vm.c index c97070b78b..f9a589c9d1 100644 --- a/py/vm.c +++ b/py/vm.c @@ -1375,9 +1375,9 @@ pending_exception_check: // Re-check state is still pending now that we're in the atomic section. if (MP_STATE_VM(sched_state) == MP_SCHED_PENDING) { MARK_EXC_IP_SELECTIVE(); - mp_obj_t obj = MP_STATE_VM(mp_pending_exception); + mp_obj_t obj = MP_STATE_THREAD(mp_pending_exception); if (obj != MP_OBJ_NULL) { - MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL; + MP_STATE_THREAD(mp_pending_exception) = MP_OBJ_NULL; if (!mp_sched_num_pending()) { MP_STATE_VM(sched_state) = MP_SCHED_IDLE; } @@ -1391,10 +1391,10 @@ pending_exception_check: } #else // This is an inlined variant of mp_handle_pending - if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL) { + if (MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL) { MARK_EXC_IP_SELECTIVE(); - mp_obj_t obj = MP_STATE_VM(mp_pending_exception); - MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL; + mp_obj_t obj = MP_STATE_THREAD(mp_pending_exception); + MP_STATE_THREAD(mp_pending_exception) = MP_OBJ_NULL; RAISE(obj); } #endif From 259d9b69fe1f0e5c473bad85755d59233e0d182e Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 10 May 2021 22:18:17 -0500 Subject: [PATCH 002/264] py/mpstate: Schedule KeyboardInterrupt on main thread. This introduces a new macro to get the main thread and uses it to ensure that asynchronous exceptions such as KeyboardInterrupt (CTRL+C) are only scheduled on the main thread. This is more deterministic than being scheduled on a random thread and is more in line with CPython that only allow signal handlers to run on the main thread. Fixes issue #7026. Signed-off-by: David Lechner --- ports/pic16bit/board.c | 2 +- ports/stm32/pendsv.c | 4 ++-- ports/unix/unix_mphal.c | 2 +- ports/windows/windows_mphal.c | 2 +- py/mpstate.h | 3 ++- py/scheduler.c | 4 +++- 6 files changed, 10 insertions(+), 7 deletions(-) diff --git a/ports/pic16bit/board.c b/ports/pic16bit/board.c index b0e9d17270..8cc2e88f34 100644 --- a/ports/pic16bit/board.c +++ b/ports/pic16bit/board.c @@ -140,7 +140,7 @@ int switch_get(int sw) { // TODO need an irq void uart_rx_irq(void) { if (c == interrupt_char) { - MP_STATE_THREAD(mp_pending_exception) = MP_STATE_PORT(keyboard_interrupt_obj); + MP_STATE_MAIN_THREAD(mp_pending_exception) = MP_STATE_PORT(keyboard_interrupt_obj); } } */ diff --git a/ports/stm32/pendsv.c b/ports/stm32/pendsv.c index 2887ac5723..c5b2fcf92a 100644 --- a/ports/stm32/pendsv.c +++ b/ports/stm32/pendsv.c @@ -60,10 +60,10 @@ void pendsv_init(void) { // the given exception object using nlr_jump in the context of the top-level // thread. void pendsv_kbd_intr(void) { - if (MP_STATE_THREAD(mp_pending_exception) == MP_OBJ_NULL) { + if (MP_STATE_MAIN_THREAD(mp_pending_exception) == MP_OBJ_NULL) { mp_sched_keyboard_interrupt(); } else { - MP_STATE_THREAD(mp_pending_exception) = MP_OBJ_NULL; + MP_STATE_MAIN_THREAD(mp_pending_exception) = MP_OBJ_NULL; pendsv_object = &MP_STATE_VM(mp_kbd_exception); SCB->ICSR = SCB_ICSR_PENDSVSET_Msk; } diff --git a/ports/unix/unix_mphal.c b/ports/unix/unix_mphal.c index 111844abc4..1d3c52bf16 100644 --- a/ports/unix/unix_mphal.c +++ b/ports/unix/unix_mphal.c @@ -54,7 +54,7 @@ STATIC void sighandler(int signum) { sigprocmask(SIG_SETMASK, &mask, NULL); nlr_raise(MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception))); #else - if (MP_STATE_THREAD(mp_pending_exception) == MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception))) { + if (MP_STATE_MAIN_THREAD(mp_pending_exception) == MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception))) { // this is the second time we are called, so die straight away exit(1); } diff --git a/ports/windows/windows_mphal.c b/ports/windows/windows_mphal.c index ba1d982ad9..7a3d881a34 100644 --- a/ports/windows/windows_mphal.c +++ b/ports/windows/windows_mphal.c @@ -80,7 +80,7 @@ void mp_hal_stdio_mode_orig(void) { // the thread created for handling it might not be running yet so we'd miss the notification. BOOL WINAPI console_sighandler(DWORD evt) { if (evt == CTRL_C_EVENT) { - if (MP_STATE_THREAD(mp_pending_exception) == MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception))) { + if (MP_STATE_MAIN_THREAD(mp_pending_exception) == MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception))) { // this is the second time we are called, so die straight away exit(1); } diff --git a/py/mpstate.h b/py/mpstate.h index f5fb5b707b..e42d13fb20 100644 --- a/py/mpstate.h +++ b/py/mpstate.h @@ -285,12 +285,13 @@ extern mp_state_ctx_t mp_state_ctx; #define MP_STATE_VM(x) (mp_state_ctx.vm.x) #define MP_STATE_MEM(x) (mp_state_ctx.mem.x) +#define MP_STATE_MAIN_THREAD(x) (mp_state_ctx.thread.x) #if MICROPY_PY_THREAD extern mp_state_thread_t *mp_thread_get_state(void); #define MP_STATE_THREAD(x) (mp_thread_get_state()->x) #else -#define MP_STATE_THREAD(x) (mp_state_ctx.thread.x) +#define MP_STATE_THREAD(x) MP_STATE_MAIN_THREAD(x) #endif #endif // MICROPY_INCLUDED_PY_MPSTATE_H diff --git a/py/scheduler.c b/py/scheduler.c index 0114a7a58b..bd0bbf2078 100644 --- a/py/scheduler.c +++ b/py/scheduler.c @@ -28,8 +28,10 @@ #include "py/runtime.h" +// Schedules an exception on the main thread (for exceptions "thrown" by async +// sources such as interrupts and UNIX signal handlers). void MICROPY_WRAP_MP_SCHED_EXCEPTION(mp_sched_exception)(mp_obj_t exc) { - MP_STATE_THREAD(mp_pending_exception) = exc; + MP_STATE_MAIN_THREAD(mp_pending_exception) = exc; #if MICROPY_ENABLE_SCHEDULER if (MP_STATE_VM(sched_state) == MP_SCHED_IDLE) { MP_STATE_VM(sched_state) = MP_SCHED_PENDING; From bbf9dd849a61c1f3e2d53d0a54fc61012f0dae1e Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 19 Jun 2021 09:08:15 +1000 Subject: [PATCH 003/264] esp32/boards/sdkconfig.base: Disable MEMPROT_FEATURE to alloc from IRAM. Dynamically generate/loaded native code (eg from @micropython.native or native .mpy files) needs to be able allocate from IRAM, and the memory protection feature must be disabled for that to work. Disabling it is needed to get native code working on ESP32-S2 and -C3. Signed-off-by: Damien George --- ports/esp32/boards/sdkconfig.base | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ports/esp32/boards/sdkconfig.base b/ports/esp32/boards/sdkconfig.base index 4d9e38077d..5ca3f6a1a2 100644 --- a/ports/esp32/boards/sdkconfig.base +++ b/ports/esp32/boards/sdkconfig.base @@ -29,6 +29,10 @@ CONFIG_ESP32_XTAL_FREQ_AUTO=y # Power Management CONFIG_PM_ENABLE=y +# Memory protection +# This is required to allow allocating IRAM +CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=n + # FreeRTOS CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=2 CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y From 2fa975c26465a1afed93b5723435e7cfe96e129f Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Tue, 21 May 2019 20:02:18 +1000 Subject: [PATCH 004/264] LICENSE: Reference third-party licenses. This is to provide a summary of the licenses used by MicroPython. - Add SPDX identifier for every directory that includes non-MIT-licensed content. - Add brief summary. - Update docs license to be more explicit. Signed-off-by: Jim Mussared --- LICENSE | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++ docs/conf.py | 2 +- 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 3193eb8cec..7f39931ba7 100644 --- a/LICENSE +++ b/LICENSE @@ -19,3 +19,68 @@ 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. + +-------------------------------------------------------------------------------- + +Unless specified otherwise (see below), the above license and copyright applies +to all files in this repository. + +Individual files may include additional copyright holders. + +The various ports of MicroPython may include third-party software that is +licensed under different terms. These licenses are summarised in the tree +below, please refer to these files and directories for further license and +copyright information. Note that (L)GPL-licensed code listed below is only +used during the build process and is not part of the compiled source code. + +/ (MIT) + /drivers + /cc3000 (BSD-3-clause) + /cc3100 (BSD-3-clause) + /wiznet5k (BSD-3-clause) + /extmod + /crypto-algorithms (NONE) + /re15 (BSD-3-clause) + /uzlib (Zlib) + /lib + /asf4 (Apache-2.0) + /axtls (BSD-3-clause) + /config + /scripts + /config (GPL-2.0-or-later) + /Rules.mak (GPL-2.0) + /berkeley-db-1xx (BSD-4-clause) + /btstack (See btstack/LICENSE) + /cmsis (BSD-3-clause) + /libhydrogen (ISC) + /littlefs (BSD-3-clause) + /lwip (BSD-3-clause) + /mynewt-nimble (Apache-2.0) + /nrfx (BSD-3-clause) + /nxp_driver (BSD-3-Clause) + /oofatfs (BSD-1-clause) + /pico-sdk (BSD-3-clause) + /stm32lib (BSD-3-clause) + /tinytest (BSD-3-clause) + /tinyusb (MIT) + /logo (uses OFL-1.1) + /ports + /cc3200 + /hal (BSD-3-clause) + /simplelink (BSD-3-clause) + /FreeRTOS (GPL-2.0 with FreeRTOS exception) + /stm32 + /usbd*.c (MCD-ST Liberty SW License Agreement V2) + /stm32_it.* (MIT + BSD-3-clause) + /system_stm32*.c (MIT + BSD-3-clause) + /boards + /startup_stm32*.s (BSD-3-clause) + /*/stm32*.h (BSD-3-clause) + /usbdev (MCD-ST Liberty SW License Agreement V2) + /usbhost (MCD-ST Liberty SW License Agreement V2) + /teensy + /core (PJRC.COM) + /zephyr + /src (Apache-2.0) + /tools + /dfu.py (LGPL-3.0-only) diff --git a/docs/conf.py b/docs/conf.py index fcaadfaf0a..239934bfa7 100755 --- a/docs/conf.py +++ b/docs/conf.py @@ -66,7 +66,7 @@ master_doc = 'index' # General information about the project. project = 'MicroPython' -copyright = '2014-2021, Damien P. George, Paul Sokolovsky, and contributors' +copyright = '- The MicroPython Documentation is Copyright © 2014-2021, Damien P. George, Paul Sokolovsky, and contributors' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the From b3a34dde36485581a89de105ca7c0dd0d0fe49bb Mon Sep 17 00:00:00 2001 From: Michael Weiss Date: Fri, 26 Mar 2021 00:12:13 -0700 Subject: [PATCH 005/264] esp32,esp8266: Add __len__ to NeoPixel driver to support iterating. Signed-off-by: mishafarms --- ports/esp32/modules/neopixel.py | 3 +++ ports/esp8266/modules/neopixel.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/ports/esp32/modules/neopixel.py b/ports/esp32/modules/neopixel.py index aa0de8112b..f5c9193988 100644 --- a/ports/esp32/modules/neopixel.py +++ b/ports/esp32/modules/neopixel.py @@ -15,6 +15,9 @@ class NeoPixel: self.pin.init(pin.OUT) self.timing = timing + def __len__(self): + return self.n + def __setitem__(self, index, val): offset = index * self.bpp for i in range(self.bpp): diff --git a/ports/esp8266/modules/neopixel.py b/ports/esp8266/modules/neopixel.py index 501a2689e7..9dc153372f 100644 --- a/ports/esp8266/modules/neopixel.py +++ b/ports/esp8266/modules/neopixel.py @@ -15,6 +15,9 @@ class NeoPixel: self.pin.init(pin.OUT) self.timing = timing + def __len__(self): + return self.n + def __setitem__(self, index, val): offset = index * self.bpp for i in range(self.bpp): From 60f1f769840db954825631206dc1b4f54ffe1468 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 19 May 2021 16:54:43 +1000 Subject: [PATCH 006/264] stm32/softtimer: Add soft_timer_reinsert() helper function. And call mp_pairheap_init_node() in soft_timer_static_init() so that reinsert can be called after static_init. Signed-off-by: Damien George --- ports/stm32/softtimer.c | 1 + ports/stm32/softtimer.h | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/ports/stm32/softtimer.c b/ports/stm32/softtimer.c index 7f19eccd2d..c9c59cab0e 100644 --- a/ports/stm32/softtimer.c +++ b/ports/stm32/softtimer.c @@ -122,6 +122,7 @@ void soft_timer_gc_mark_all(void) { } void soft_timer_static_init(soft_timer_entry_t *entry, uint16_t mode, uint32_t delta_ms, void (*cb)(soft_timer_entry_t *)) { + mp_pairheap_init_node(soft_timer_lt, &entry->pairheap); entry->flags = 0; entry->mode = mode; entry->delta_ms = delta_ms; diff --git a/ports/stm32/softtimer.h b/ports/stm32/softtimer.h index 25d828a9fd..9c14f16d61 100644 --- a/ports/stm32/softtimer.h +++ b/ports/stm32/softtimer.h @@ -56,4 +56,11 @@ void soft_timer_static_init(soft_timer_entry_t *entry, uint16_t mode, uint32_t d void soft_timer_insert(soft_timer_entry_t *entry, uint32_t initial_delta_ms); void soft_timer_remove(soft_timer_entry_t *entry); +// The timer will be reinserted into the heap so that it is called after initial_delta_ms milliseconds. +// After that, if it's periodic, it will continue to be called every entry->delta_ms milliseconds. +static inline void soft_timer_reinsert(soft_timer_entry_t *entry, uint32_t initial_delta_ms) { + soft_timer_remove(entry); + soft_timer_insert(entry, initial_delta_ms); +} + #endif // MICROPY_INCLUDED_STM32_SOFTTIMER_H From 74c2c31811271c76cd55aa53296003ca8f6a1e16 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 12 May 2021 17:18:22 +1000 Subject: [PATCH 007/264] stm32/mpbthciport: Change from systick to soft-timer for BT scheduling. Instead of using systick the BT subsystem is now scheduled using a soft timer. This means it is scheduled only when it is enabled. Signed-off-by: Damien George --- ports/stm32/main.c | 4 +- ports/stm32/mpbthciport.c | 85 +++++++++++++++++++++++-------------- ports/stm32/mpbthciport.h | 42 ++++++++++++++++++ ports/stm32/mpbtstackport.c | 5 +++ ports/stm32/mpnimbleport.c | 7 +++ ports/stm32/rfcore.c | 4 +- ports/stm32/systick.h | 3 -- 7 files changed, 111 insertions(+), 39 deletions(-) create mode 100644 ports/stm32/mpbthciport.h diff --git a/ports/stm32/main.c b/ports/stm32/main.c index ca80daaceb..c8de2fd868 100644 --- a/ports/stm32/main.c +++ b/ports/stm32/main.c @@ -54,6 +54,7 @@ #endif #include "boardctrl.h" +#include "mpbthciport.h" #include "mpu.h" #include "rfcore.h" #include "systick.h" @@ -440,8 +441,7 @@ void stm32_main(uint32_t reset_mode) { systick_enable_dispatch(SYSTICK_DISPATCH_LWIP, mod_network_lwip_poll_wrapper); #endif #if MICROPY_PY_BLUETOOTH - extern void mp_bluetooth_hci_systick(uint32_t ticks_ms); - systick_enable_dispatch(SYSTICK_DISPATCH_BLUETOOTH_HCI, mp_bluetooth_hci_systick); + mp_bluetooth_hci_init(); #endif #if MICROPY_PY_NETWORK_CYW43 diff --git a/ports/stm32/mpbthciport.c b/ports/stm32/mpbthciport.c index c3cd864e2b..accb913e1f 100644 --- a/ports/stm32/mpbthciport.c +++ b/ports/stm32/mpbthciport.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2018-2020 Damien P. George + * Copyright (c) 2018-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 @@ -28,7 +28,8 @@ #include "py/mphal.h" #include "extmod/mpbthci.h" #include "extmod/modbluetooth.h" -#include "systick.h" +#include "mpbthciport.h" +#include "softtimer.h" #include "pendsv.h" #include "lib/utils/mpirq.h" @@ -38,22 +39,48 @@ uint8_t mp_bluetooth_hci_cmd_buf[4 + 256]; -// Must be provided by the stack bindings (e.g. mpnimbleport.c or mpbtstackport.c). -// Request new data from the uart and pass to the stack, and run pending events/callouts. -extern void mp_bluetooth_hci_poll(void); +// Soft timer for scheduling a HCI poll. +STATIC soft_timer_entry_t mp_bluetooth_hci_soft_timer; -// Hook for pendsv poller to run this periodically every 128ms -#define BLUETOOTH_HCI_TICK(tick) (((tick) & ~(SYSTICK_DISPATCH_NUM_SLOTS - 1) & 0x7f) == 0) +#if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS +// Prevent double-enqueuing of the scheduled task. +STATIC volatile bool events_task_is_scheduled; +#endif + +// This is called by soft_timer and executes at IRQ_PRI_PENDSV. +STATIC void mp_bluetooth_hci_soft_timer_callback(soft_timer_entry_t *self) { + mp_bluetooth_hci_poll_now(); +} + +void mp_bluetooth_hci_init(void) { + #if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS + events_task_is_scheduled = false; + #endif + soft_timer_static_init( + &mp_bluetooth_hci_soft_timer, + SOFT_TIMER_MODE_ONE_SHOT, + 0, + mp_bluetooth_hci_soft_timer_callback + ); +} + +STATIC void mp_bluetooth_hci_start_polling(void) { + #if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS + events_task_is_scheduled = false; + #endif + mp_bluetooth_hci_poll_now(); +} + +void mp_bluetooth_hci_poll_in_ms(uint32_t ms) { + soft_timer_reinsert(&mp_bluetooth_hci_soft_timer, ms); +} #if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS // For synchronous mode, we run all BLE stack code inside a scheduled task. -// This task is scheduled periodically (every 128ms) via SysTick, or +// This task is scheduled periodically via a soft timer, or // immediately on HCI UART RXIDLE. -// Prevent double-enqueuing of the scheduled task. -STATIC volatile bool events_task_is_scheduled = false; - STATIC mp_obj_t run_events_scheduled_task(mp_obj_t none_in) { (void)none_in; events_task_is_scheduled = false; @@ -65,23 +92,20 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(run_events_scheduled_task_obj, run_events_sched // Called periodically (systick) or directly (e.g. UART RX IRQ) in order to // request that processing happens ASAP in the scheduler. -void mp_bluetooth_hci_systick(uint32_t ticks_ms) { - if (events_task_is_scheduled) { - return; - } - - if (ticks_ms == 0 || BLUETOOTH_HCI_TICK(ticks_ms)) { +void mp_bluetooth_hci_poll_now(void) { + if (!events_task_is_scheduled) { events_task_is_scheduled = mp_sched_schedule(MP_OBJ_FROM_PTR(&run_events_scheduled_task_obj), mp_const_none); + if (!events_task_is_scheduled) { + // The schedule queue is full, set callback to try again soon. + mp_bluetooth_hci_poll_in_ms(5); + } } } #else // !MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS -// Called periodically (systick) or directly (e.g. uart irq). -void mp_bluetooth_hci_systick(uint32_t ticks_ms) { - if (ticks_ms == 0 || BLUETOOTH_HCI_TICK(ticks_ms)) { - pendsv_schedule_dispatch(PENDSV_DISPATCH_BLUETOOTH_HCI, mp_bluetooth_hci_poll); - } +void mp_bluetooth_hci_poll_now(void) { + pendsv_schedule_dispatch(PENDSV_DISPATCH_BLUETOOTH_HCI, mp_bluetooth_hci_poll); } #endif @@ -104,14 +128,13 @@ int mp_bluetooth_hci_uart_init(uint32_t port, uint32_t baudrate) { DEBUG_printf("mp_bluetooth_hci_uart_init (stm32 rfcore)\n"); - #if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS - events_task_is_scheduled = false; - #endif - rfcore_ble_init(); hci_uart_rx_buf_cur = 0; hci_uart_rx_buf_len = 0; + // Start the HCI polling to process any initial events/packets. + mp_bluetooth_hci_start_polling(); + return 0; } @@ -163,7 +186,6 @@ int mp_bluetooth_hci_uart_readchar(void) { /******************************************************************************/ // HCI over UART -#include "pendsv.h" #include "uart.h" pyb_uart_obj_t mp_bluetooth_hci_uart_obj; @@ -173,7 +195,7 @@ static uint8_t hci_uart_rxbuf[768]; mp_obj_t mp_uart_interrupt(mp_obj_t self_in) { // Queue up the scheduler to run the HCI UART and event processing ASAP. - mp_bluetooth_hci_systick(0); + mp_bluetooth_hci_poll_now(); return mp_const_none; } @@ -182,10 +204,6 @@ MP_DEFINE_CONST_FUN_OBJ_1(mp_uart_interrupt_obj, mp_uart_interrupt); int mp_bluetooth_hci_uart_init(uint32_t port, uint32_t baudrate) { DEBUG_printf("mp_bluetooth_hci_uart_init (stm32)\n"); - #if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS - events_task_is_scheduled = false; - #endif - // bits (8), stop (1), parity (none) and flow (rts/cts) are assumed to match MYNEWT_VAL_BLE_HCI_UART_ constants in syscfg.h. mp_bluetooth_hci_uart_obj.base.type = &pyb_uart_type; mp_bluetooth_hci_uart_obj.uart_id = port; @@ -208,6 +226,9 @@ int mp_bluetooth_hci_uart_init(uint32_t port, uint32_t baudrate) { mp_bluetooth_hci_uart_irq_obj.ishard = true; uart_irq_config(&mp_bluetooth_hci_uart_obj, true); + // Start the HCI polling to process any initial events/packets. + mp_bluetooth_hci_start_polling(); + return 0; } diff --git a/ports/stm32/mpbthciport.h b/ports/stm32/mpbthciport.h new file mode 100644 index 0000000000..77919ee0e6 --- /dev/null +++ b/ports/stm32/mpbthciport.h @@ -0,0 +1,42 @@ +/* + * 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_STM32_MPBTHCIPORT_H +#define MICROPY_INCLUDED_STM32_MPBTHCIPORT_H + +// Initialise the HCI subsystem (should be called once, early on). +void mp_bluetooth_hci_init(void); + +// Poll the HCI now, or after a certain timeout. +void mp_bluetooth_hci_poll_now(void); +void mp_bluetooth_hci_poll_in_ms(uint32_t ms); + +// Must be provided by the stack bindings (e.g. mpnimbleport.c or mpbtstackport.c). +// Request new data from the uart and pass to the stack, and run pending events/callouts. +// This is a low-level function and should not be called directly, use +// mp_bluetooth_hci_poll_now/mp_bluetooth_hci_poll_in_ms instead. +void mp_bluetooth_hci_poll(void); + +#endif // MICROPY_INCLUDED_STM32_MPBTHCIPORT_H diff --git a/ports/stm32/mpbtstackport.c b/ports/stm32/mpbtstackport.c index 3dfd8cb3c4..abe4f6d3e2 100644 --- a/ports/stm32/mpbtstackport.c +++ b/ports/stm32/mpbtstackport.c @@ -38,6 +38,7 @@ #include "extmod/mpbthci.h" #include "extmod/btstack/btstack_hci_uart.h" #include "extmod/btstack/modbluetooth_btstack.h" +#include "mpbthciport.h" // The IRQ functionality in btstack_run_loop_embedded.c is not used, so the // following three functions are empty. @@ -75,6 +76,10 @@ void mp_bluetooth_hci_poll(void) { // Call the BTstack run loop. btstack_run_loop_embedded_execute_once(); + + // Call this function again in 128ms to check for new events. + // TODO: improve this by only calling back when needed. + mp_bluetooth_hci_poll_in_ms(128); } void mp_bluetooth_btstack_port_init(void) { diff --git a/ports/stm32/mpnimbleport.c b/ports/stm32/mpnimbleport.c index 0ba76fb277..e2d39e271c 100644 --- a/ports/stm32/mpnimbleport.c +++ b/ports/stm32/mpnimbleport.c @@ -40,6 +40,7 @@ #include "extmod/modbluetooth.h" #include "extmod/nimble/modbluetooth_nimble.h" #include "extmod/nimble/hal/hal_uart.h" +#include "mpbthciport.h" // Get any pending data from the UART and send it to NimBLE's HCI buffers. // Any further processing by NimBLE will be run via its event queue. @@ -56,6 +57,12 @@ void mp_bluetooth_hci_poll(void) { // Run any remaining events (e.g. if there was no UART data). mp_bluetooth_nimble_os_eventq_run_all(); } + + if (mp_bluetooth_nimble_ble_state != MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF) { + // Call this function again in 128ms to check for new events. + // TODO: improve this by only calling back when needed. + mp_bluetooth_hci_poll_in_ms(128); + } } // --- Port-specific helpers for the generic NimBLE bindings. ----------------- diff --git a/ports/stm32/rfcore.c b/ports/stm32/rfcore.c index 049f6bdc10..67b63679a7 100644 --- a/ports/stm32/rfcore.c +++ b/ports/stm32/rfcore.c @@ -32,6 +32,7 @@ #include "py/mphal.h" #include "py/runtime.h" #include "extmod/modbluetooth.h" +#include "mpbthciport.h" #include "rtc.h" #include "rfcore.h" @@ -714,8 +715,7 @@ void IPCC_C1_RX_IRQHandler(void) { #if MICROPY_PY_BLUETOOTH // Queue up the scheduler to process UART data and run events. - extern void mp_bluetooth_hci_systick(uint32_t ticks_ms); - mp_bluetooth_hci_systick(0); + mp_bluetooth_hci_poll_now(); #endif } diff --git a/ports/stm32/systick.h b/ports/stm32/systick.h index 6aef3f2e4e..6a05a4990a 100644 --- a/ports/stm32/systick.h +++ b/ports/stm32/systick.h @@ -37,9 +37,6 @@ enum { #if MICROPY_PY_NETWORK && MICROPY_PY_LWIP SYSTICK_DISPATCH_LWIP, #endif - #if MICROPY_PY_BLUETOOTH - SYSTICK_DISPATCH_BLUETOOTH_HCI, - #endif SYSTICK_DISPATCH_MAX }; From 38bc5a9f67b9dd05833de36a4ae2e74afd10a0e9 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 13 May 2021 23:33:32 +1000 Subject: [PATCH 008/264] stm32: Provide a custom BTstack runloop that integrates with soft timer. It reschedules the BT HCI poll soft timer so that it is called exactly when the next timer expires. Signed-off-by: Damien George --- extmod/btstack/btstack.mk | 1 - ports/stm32/mpbtstackport.c | 94 +++++++++++++++++++++++++++++-------- ports/unix/Makefile | 1 + 3 files changed, 75 insertions(+), 21 deletions(-) diff --git a/extmod/btstack/btstack.mk b/extmod/btstack/btstack.mk index 17bbb16f88..9e1857263b 100644 --- a/extmod/btstack/btstack.mk +++ b/extmod/btstack/btstack.mk @@ -30,7 +30,6 @@ INC += -I$(BTSTACK_DIR)/3rd-party/yxml SRC_BTSTACK = \ $(addprefix lib/btstack/src/, $(SRC_FILES)) \ $(addprefix lib/btstack/src/ble/, $(filter-out %_tlv.c, $(SRC_BLE_FILES))) \ - lib/btstack/platform/embedded/btstack_run_loop_embedded.c ifeq ($(MICROPY_BLUETOOTH_BTSTACK_USB),1) ifeq ($(MICROPY_BLUETOOTH_BTSTACK_H4),1) diff --git a/ports/stm32/mpbtstackport.c b/ports/stm32/mpbtstackport.c index abe4f6d3e2..795534042e 100644 --- a/ports/stm32/mpbtstackport.c +++ b/ports/stm32/mpbtstackport.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2020 Damien P. George + * 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 @@ -31,31 +31,79 @@ #if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK #include "lib/btstack/src/btstack.h" -#include "lib/btstack/platform/embedded/btstack_run_loop_embedded.h" -#include "lib/btstack/platform/embedded/hal_cpu.h" -#include "lib/btstack/platform/embedded/hal_time_ms.h" - #include "extmod/mpbthci.h" #include "extmod/btstack/btstack_hci_uart.h" #include "extmod/btstack/modbluetooth_btstack.h" #include "mpbthciport.h" -// The IRQ functionality in btstack_run_loop_embedded.c is not used, so the -// following three functions are empty. +void mp_bluetooth_hci_poll_in_ms(uint32_t ms); -void hal_cpu_disable_irqs(void) { +static btstack_linked_list_t mp_btstack_runloop_timers; + +static void mp_btstack_runloop_init(void) { + mp_btstack_runloop_timers = NULL; } -void hal_cpu_enable_irqs(void) { +static void mp_btstack_runloop_set_timer(btstack_timer_source_t *tim, uint32_t timeout_ms) { + tim->timeout = mp_hal_ticks_ms() + timeout_ms + 1; } -void hal_cpu_enable_irqs_and_sleep(void) { +static void mp_btstack_runloop_add_timer(btstack_timer_source_t *tim) { + btstack_linked_item_t **node = &mp_btstack_runloop_timers; + for (; *node; node = &(*node)->next) { + btstack_timer_source_t *node_tim = (btstack_timer_source_t *)*node; + if (node_tim == tim) { + // Timer is already in the list, don't add it. + return; + } + int32_t delta = btstack_time_delta(tim->timeout, node_tim->timeout); + if (delta < 0) { + // Found sorted location in list. + break; + } + } + + // Insert timer into list in sorted location. + tim->item.next = *node; + *node = &tim->item; + + // Reschedule the HCI poll if this timer is at the head of the list. + if (mp_btstack_runloop_timers == &tim->item) { + int32_t delta_ms = btstack_time_delta(tim->timeout, mp_hal_ticks_ms()); + mp_bluetooth_hci_poll_in_ms(delta_ms); + } } -uint32_t hal_time_ms(void) { +static bool mp_btstack_runloop_remove_timer(btstack_timer_source_t *tim) { + return btstack_linked_list_remove(&mp_btstack_runloop_timers, (btstack_linked_item_t *)tim); +} + +static void mp_btstack_runloop_execute(void) { + // Should not be called. +} + +static void mp_btstack_runloop_dump_timer(void) { + // Not implemented/needed. +} + +static uint32_t mp_btstack_runloop_get_time_ms(void) { return mp_hal_ticks_ms(); } +static const btstack_run_loop_t mp_btstack_runloop_stm32 = { + &mp_btstack_runloop_init, + NULL, // add_data_source, + NULL, // remove_data_source, + NULL, // enable_data_source_callbacks, + NULL, // disable_data_source_callbacks, + &mp_btstack_runloop_set_timer, + &mp_btstack_runloop_add_timer, + &mp_btstack_runloop_remove_timer, + &mp_btstack_runloop_execute, + &mp_btstack_runloop_dump_timer, + &mp_btstack_runloop_get_time_ms, +}; + STATIC const hci_transport_config_uart_t hci_transport_config_uart = { HCI_TRANSPORT_CONFIG_UART, MICROPY_HW_BLE_UART_BAUDRATE, @@ -69,26 +117,32 @@ void mp_bluetooth_hci_poll(void) { return; } - // Process uart data. + // Process UART data. if (mp_bluetooth_btstack_state != MP_BLUETOOTH_BTSTACK_STATE_HALTING) { mp_bluetooth_btstack_hci_uart_process(); } - // Call the BTstack run loop. - btstack_run_loop_embedded_execute_once(); - - // Call this function again in 128ms to check for new events. - // TODO: improve this by only calling back when needed. - mp_bluetooth_hci_poll_in_ms(128); + // Process any BTstack timers. + while (mp_btstack_runloop_timers != NULL) { + btstack_timer_source_t *tim = (btstack_timer_source_t *)mp_btstack_runloop_timers; + int32_t delta_ms = btstack_time_delta(tim->timeout, mp_hal_ticks_ms()); + if (delta_ms > 0) { + // Timer has not expired yet, reschedule HCI poll for this timer. + mp_bluetooth_hci_poll_in_ms(delta_ms); + break; + } + btstack_linked_list_pop(&mp_btstack_runloop_timers); + tim->process(tim); + } } void mp_bluetooth_btstack_port_init(void) { static bool run_loop_init = false; if (!run_loop_init) { run_loop_init = true; - btstack_run_loop_init(btstack_run_loop_embedded_get_instance()); + btstack_run_loop_init(&mp_btstack_runloop_stm32); } else { - btstack_run_loop_embedded_get_instance()->init(); + mp_btstack_runloop_stm32.init(); } // hci_dump_open(NULL, HCI_DUMP_STDOUT); diff --git a/ports/unix/Makefile b/ports/unix/Makefile index fe86018aa0..d97e4bc7e0 100644 --- a/ports/unix/Makefile +++ b/ports/unix/Makefile @@ -177,6 +177,7 @@ endif # BTstack is enabled. GIT_SUBMODULES += lib/btstack include $(TOP)/extmod/btstack/btstack.mk +SRC_BTSTACK += lib/btstack/platform/embedded/btstack_run_loop_embedded.c else From 0fc0f7536b9d73935f22c3e74747eacad7d3de74 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 20 May 2021 12:19:34 +1000 Subject: [PATCH 009/264] extmod/btstack: Add missing call to mp_bluetooth_hci_uart_deinit. Signed-off-by: Damien George --- extmod/btstack/btstack_hci_uart.c | 1 + 1 file changed, 1 insertion(+) diff --git a/extmod/btstack/btstack_hci_uart.c b/extmod/btstack/btstack_hci_uart.c index 7205dba069..83e865b71d 100644 --- a/extmod/btstack/btstack_hci_uart.c +++ b/extmod/btstack/btstack_hci_uart.c @@ -86,6 +86,7 @@ STATIC int btstack_uart_open(void) { STATIC int btstack_uart_close(void) { mp_bluetooth_hci_controller_deinit(); + mp_bluetooth_hci_uart_deinit(); return 0; } From 6ed5b843cf55a96156328b08b011ba919af22fb4 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 23 Jun 2021 14:58:39 +1000 Subject: [PATCH 010/264] extmod/btstack: Check that BLE is active before performing operations. Otherwise it can easily lead to a hard crash. Signed-off-by: Damien George --- extmod/btstack/modbluetooth_btstack.c | 69 +++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/extmod/btstack/modbluetooth_btstack.c b/extmod/btstack/modbluetooth_btstack.c index 5f047e3254..9c88878038 100644 --- a/extmod/btstack/modbluetooth_btstack.c +++ b/extmod/btstack/modbluetooth_btstack.c @@ -849,6 +849,11 @@ int mp_bluetooth_gap_set_device_name(const uint8_t *buf, size_t len) { int mp_bluetooth_gap_advertise_start(bool connectable, int32_t interval_us, const uint8_t *adv_data, size_t adv_data_len, const uint8_t *sr_data, size_t sr_data_len) { DEBUG_printf("mp_bluetooth_gap_advertise_start\n"); + + if (!mp_bluetooth_is_active()) { + return ERRNO_BLUETOOTH_NOT_ACTIVE; + } + uint16_t adv_int_min = interval_us / 625; uint16_t adv_int_max = interval_us / 625; uint8_t adv_type = connectable ? 0 : 2; @@ -885,6 +890,11 @@ int mp_bluetooth_gap_advertise_start(bool connectable, int32_t interval_us, cons void mp_bluetooth_gap_advertise_stop(void) { DEBUG_printf("mp_bluetooth_gap_advertise_stop\n"); + + if (!mp_bluetooth_is_active()) { + return; + } + gap_advertisements_enable(false); MP_STATE_PORT(bluetooth_btstack_root_pointers)->adv_data_alloc = 0; MP_STATE_PORT(bluetooth_btstack_root_pointers)->adv_data = NULL; @@ -892,6 +902,11 @@ void mp_bluetooth_gap_advertise_stop(void) { int mp_bluetooth_gatts_register_service_begin(bool append) { DEBUG_printf("mp_bluetooth_gatts_register_service_begin\n"); + + if (!mp_bluetooth_is_active()) { + return ERRNO_BLUETOOTH_NOT_ACTIVE; + } + if (!append) { // This will reset the DB. // Becase the DB is statically allocated, there's no problem with just re-initing it. @@ -1064,16 +1079,27 @@ int mp_bluetooth_gatts_register_service_end(void) { int mp_bluetooth_gatts_read(uint16_t value_handle, uint8_t **value, size_t *value_len) { DEBUG_printf("mp_bluetooth_gatts_read\n"); + if (!mp_bluetooth_is_active()) { + return ERRNO_BLUETOOTH_NOT_ACTIVE; + } return mp_bluetooth_gatts_db_read(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, value, value_len); } int mp_bluetooth_gatts_write(uint16_t value_handle, const uint8_t *value, size_t value_len) { DEBUG_printf("mp_bluetooth_gatts_write\n"); + if (!mp_bluetooth_is_active()) { + return ERRNO_BLUETOOTH_NOT_ACTIVE; + } return mp_bluetooth_gatts_db_write(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, value, value_len); } int mp_bluetooth_gatts_notify(uint16_t conn_handle, uint16_t value_handle) { DEBUG_printf("mp_bluetooth_gatts_notify\n"); + + if (!mp_bluetooth_is_active()) { + return ERRNO_BLUETOOTH_NOT_ACTIVE; + } + // Note: btstack doesn't appear to support sending a notification without a value, so include the stored value. uint8_t *data = NULL; size_t len = 0; @@ -1084,6 +1110,10 @@ int mp_bluetooth_gatts_notify(uint16_t conn_handle, uint16_t value_handle) { int mp_bluetooth_gatts_notify_send(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t value_len) { DEBUG_printf("mp_bluetooth_gatts_notify_send\n"); + if (!mp_bluetooth_is_active()) { + return ERRNO_BLUETOOTH_NOT_ACTIVE; + } + // Attempt to send immediately. If it succeeds, btstack will copy the buffer. MICROPY_PY_BLUETOOTH_ENTER int err = att_server_notify(conn_handle, value_handle, value, value_len); @@ -1110,6 +1140,10 @@ int mp_bluetooth_gatts_notify_send(uint16_t conn_handle, uint16_t value_handle, int mp_bluetooth_gatts_indicate(uint16_t conn_handle, uint16_t value_handle) { DEBUG_printf("mp_bluetooth_gatts_indicate\n"); + if (!mp_bluetooth_is_active()) { + return ERRNO_BLUETOOTH_NOT_ACTIVE; + } + uint8_t *data = NULL; size_t len = 0; mp_bluetooth_gatts_db_read(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, &data, &len); @@ -1142,6 +1176,9 @@ int mp_bluetooth_gatts_indicate(uint16_t conn_handle, uint16_t value_handle) { int mp_bluetooth_gatts_set_buffer(uint16_t value_handle, size_t len, bool append) { DEBUG_printf("mp_bluetooth_gatts_set_buffer\n"); + if (!mp_bluetooth_is_active()) { + return ERRNO_BLUETOOTH_NOT_ACTIVE; + } return mp_bluetooth_gatts_db_resize(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, len, append); } @@ -1165,6 +1202,9 @@ int mp_bluetooth_set_preferred_mtu(uint16_t mtu) { int mp_bluetooth_gap_disconnect(uint16_t conn_handle) { DEBUG_printf("mp_bluetooth_gap_disconnect\n"); + if (!mp_bluetooth_is_active()) { + return ERRNO_BLUETOOTH_NOT_ACTIVE; + } gap_disconnect(conn_handle); return 0; } @@ -1195,6 +1235,10 @@ STATIC void scan_duration_timeout_handler(btstack_timer_source_t *ds) { int mp_bluetooth_gap_scan_start(int32_t duration_ms, int32_t interval_us, int32_t window_us, bool active_scan) { DEBUG_printf("mp_bluetooth_gap_scan_start\n"); + if (!mp_bluetooth_is_active()) { + return ERRNO_BLUETOOTH_NOT_ACTIVE; + } + if (duration_ms > 0) { btstack_run_loop_set_timer(&scan_duration_timeout, duration_ms); btstack_run_loop_set_timer_handler(&scan_duration_timeout, scan_duration_timeout_handler); @@ -1209,6 +1253,9 @@ int mp_bluetooth_gap_scan_start(int32_t duration_ms, int32_t interval_us, int32_ int mp_bluetooth_gap_scan_stop(void) { DEBUG_printf("mp_bluetooth_gap_scan_stop\n"); + if (!mp_bluetooth_is_active()) { + return ERRNO_BLUETOOTH_NOT_ACTIVE; + } btstack_run_loop_remove_timer(&scan_duration_timeout); gap_stop_scan(); mp_bluetooth_gap_on_scan_complete(); @@ -1240,6 +1287,11 @@ int mp_bluetooth_gap_peripheral_connect(uint8_t addr_type, const uint8_t *addr, int mp_bluetooth_gattc_discover_primary_services(uint16_t conn_handle, const mp_obj_bluetooth_uuid_t *uuid) { DEBUG_printf("mp_bluetooth_gattc_discover_primary_services\n"); + + if (!mp_bluetooth_is_active()) { + return ERRNO_BLUETOOTH_NOT_ACTIVE; + } + uint8_t err; if (uuid) { if (uuid->type == MP_BLUETOOTH_UUID_TYPE_16) { @@ -1260,6 +1312,11 @@ int mp_bluetooth_gattc_discover_primary_services(uint16_t conn_handle, const mp_ int mp_bluetooth_gattc_discover_characteristics(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle, const mp_obj_bluetooth_uuid_t *uuid) { DEBUG_printf("mp_bluetooth_gattc_discover_characteristics\n"); + + if (!mp_bluetooth_is_active()) { + return ERRNO_BLUETOOTH_NOT_ACTIVE; + } + gatt_client_service_t service = { // Only start/end handles needed for gatt_client_discover_characteristics_for_service. .start_group_handle = start_handle, @@ -1287,6 +1344,11 @@ int mp_bluetooth_gattc_discover_characteristics(uint16_t conn_handle, uint16_t s int mp_bluetooth_gattc_discover_descriptors(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle) { DEBUG_printf("mp_bluetooth_gattc_discover_descriptors\n"); + + if (!mp_bluetooth_is_active()) { + return ERRNO_BLUETOOTH_NOT_ACTIVE; + } + gatt_client_characteristic_t characteristic = { // Only start/end handles needed for gatt_client_discover_characteristic_descriptors. .start_handle = start_handle, @@ -1301,12 +1363,19 @@ int mp_bluetooth_gattc_discover_descriptors(uint16_t conn_handle, uint16_t start int mp_bluetooth_gattc_read(uint16_t conn_handle, uint16_t value_handle) { DEBUG_printf("mp_bluetooth_gattc_read\n"); + if (!mp_bluetooth_is_active()) { + return ERRNO_BLUETOOTH_NOT_ACTIVE; + } return btstack_error_to_errno(gatt_client_read_value_of_characteristic_using_value_handle(&btstack_packet_handler_read, conn_handle, value_handle)); } int mp_bluetooth_gattc_write(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t *value_len, unsigned int mode) { DEBUG_printf("mp_bluetooth_gattc_write\n"); + if (!mp_bluetooth_is_active()) { + return ERRNO_BLUETOOTH_NOT_ACTIVE; + } + // We should be distinguishing between gatt_client_write_value_of_characteristic vs // gatt_client_write_characteristic_descriptor_using_descriptor_handle. // However both are implemented using send_gatt_write_attribute_value_request under the hood, From 63b8b796103327cdaf61936ef25046a696e92db6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 23 Jun 2021 14:37:33 +1000 Subject: [PATCH 011/264] stm32/usb: Make irq's default trigger enable all events. Following how other .irq() methods work on other objects. Signed-off-by: Damien George --- docs/library/pyb.USB_VCP.rst | 2 +- ports/stm32/usb.c | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/library/pyb.USB_VCP.rst b/docs/library/pyb.USB_VCP.rst index 1e44e53fd8..1cca8dcadf 100644 --- a/docs/library/pyb.USB_VCP.rst +++ b/docs/library/pyb.USB_VCP.rst @@ -109,7 +109,7 @@ Methods Return value: number of bytes sent. -.. method:: USB_VCP.irq(handler=None, trigger=0, hard=False) +.. method:: USB_VCP.irq(handler=None, trigger=IRQ_RX, hard=False) Register *handler* to be called whenever an event specified by *trigger* occurs. The *handler* function must take exactly one argument, which will diff --git a/ports/stm32/usb.c b/ports/stm32/usb.c index eba95de49f..5e3802651b 100644 --- a/ports/stm32/usb.c +++ b/ports/stm32/usb.c @@ -829,11 +829,17 @@ STATIC mp_obj_t pyb_usb_vcp_recv(size_t n_args, const mp_obj_t *args, mp_map_t * } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_usb_vcp_recv_obj, 1, pyb_usb_vcp_recv); -// irq(handler=None, trigger=0, hard=False) +// irq(handler=None, trigger=IRQ_RX, hard=False) STATIC mp_obj_t pyb_usb_vcp_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - mp_arg_val_t args[MP_IRQ_ARG_INIT_NUM_ARGS]; - mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_IRQ_ARG_INIT_NUM_ARGS, mp_irq_init_args, args); + enum { ARG_handler, ARG_trigger, ARG_hard }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_handler, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_trigger, MP_ARG_INT, {.u_int = USBD_CDC_IRQ_RX} }, + { MP_QSTR_hard, MP_ARG_BOOL, {.u_bool = false} }, + }; pyb_usb_vcp_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); if (n_args > 1 || kw_args->used != 0) { // Check the handler. From 3588d14ae6a611764a0f83867754da691fba7127 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 21 Jun 2021 14:56:37 +1000 Subject: [PATCH 012/264] tools/autobuild: Add scripts to build release firmware. Signed-off-by: Damien George --- tools/autobuild/autobuild.sh | 90 +++++++++++++++++++++++++ tools/autobuild/build-cc3200-latest.sh | 32 +++++++++ tools/autobuild/build-esp32-latest.sh | 45 +++++++++++++ tools/autobuild/build-esp8266-latest.sh | 66 ++++++++++++++++++ tools/autobuild/build-rp2-latest.sh | 32 +++++++++ tools/autobuild/build-stm32-latest.sh | 56 +++++++++++++++ tools/autobuild/remove_old_firmware.py | 72 ++++++++++++++++++++ 7 files changed, 393 insertions(+) create mode 100755 tools/autobuild/autobuild.sh create mode 100755 tools/autobuild/build-cc3200-latest.sh create mode 100755 tools/autobuild/build-esp32-latest.sh create mode 100755 tools/autobuild/build-esp8266-latest.sh create mode 100755 tools/autobuild/build-rp2-latest.sh create mode 100755 tools/autobuild/build-stm32-latest.sh create mode 100755 tools/autobuild/remove_old_firmware.py diff --git a/tools/autobuild/autobuild.sh b/tools/autobuild/autobuild.sh new file mode 100755 index 0000000000..45822f2b7d --- /dev/null +++ b/tools/autobuild/autobuild.sh @@ -0,0 +1,90 @@ +#!/bin/bash +# +# Build firmware for ports. +# +# Requirements: +# - All toolchains must be in path (arm-none-eabi-gcc, xtensa-lx106-elf) +# - IDF_PATH_V4 must be set +# - MICROPY_AUTOBUILD_MICROPYTHON_REPO must be set to location of micropython repository +# - MICROPY_AUTOBUILD_MAKE must be set to the make command to use, eg "make -j2" +# +# Optional settings: +# - MICROPY_AUTOBUILD_REMOTE_MACHINE can be set to a remote ssh machine to copy files to +# - MICROPY_AUTOBUILD_REMOTE_DIR can be set to destination directory on remote machine + +if [ ! -d "$IDF_PATH_V4" ]; then + echo "must set IDF_PATH_V4" + exit 1 +fi + +if [ ! -d "$MICROPY_AUTOBUILD_MICROPYTHON_REPO" ]; then + echo "must set MICROPY_AUTOBUILD_MICROPYTHON_REPO" + exit 1 +fi + +if [ -z "$MICROPY_AUTOBUILD_MAKE" ]; then + echo "must set MICROPY_AUTOBUILD_MAKE" + exit 1 +fi + +######################################## +# Initialisation + +# get directory of this script for access to other build scripts +AUTODIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +# make local directory to put firmware +LOCAL_FIRMWARE=/tmp/autobuild-firmware-$$ +mkdir -p ${LOCAL_FIRMWARE} + +# get latest MicroPython +git -C ${MICROPY_AUTOBUILD_MICROPYTHON_REPO} pull +git -C ${MICROPY_AUTOBUILD_MICROPYTHON_REPO} submodule update --init +git -C ${MICROPY_AUTOBUILD_MICROPYTHON_REPO}/lib/pico-sdk submodule update --init + +######################################## +# Build all firmware + +pushd ${MICROPY_AUTOBUILD_MICROPYTHON_REPO} + +# build cross compiler +make -C mpy-cross + +# make the firmware tag +FW_DATE=$(date '+%Y%m%d') +FW_GIT="$(git describe --dirty || echo unknown)" +FW_TAG="-$FW_DATE-unstable-$FW_GIT" + +# build new firmware +cd ports/stm32 +${AUTODIR}/build-stm32-latest.sh ${FW_TAG} ${LOCAL_FIRMWARE} +cd ../cc3200 +${AUTODIR}/build-cc3200-latest.sh ${FW_TAG} ${LOCAL_FIRMWARE} +cd ../esp8266 +${AUTODIR}/build-esp8266-latest.sh ${FW_TAG} ${LOCAL_FIRMWARE} +cd ../esp32 +${AUTODIR}/build-esp32-latest.sh ${IDF_PATH_V4} ${FW_TAG} ${LOCAL_FIRMWARE} +cd ../rp2 +${AUTODIR}/build-rp2-latest.sh ${FW_TAG} ${LOCAL_FIRMWARE} + +popd + +######################################## +# Copy firmware to remote machine + +if [ -z "$MICROPY_AUTOBUILD_REMOTE_MACHINE" -o -z "$MICROPY_AUTOBUILD_REMOTE_DIR" ]; then + echo "No remote given, leaving firmware in ${LOCAL_FIRMWARE}" + exit 0 +fi + +# copy new firmware to remote machine +scp ${LOCAL_FIRMWARE}/* ${MICROPY_AUTOBUILD_REMOTE_MACHINE}:${MICROPY_AUTOBUILD_REMOTE_DIR}/ + +# remove old firmware +${AUTODIR}/remove_old_firmware.py ${MICROPY_AUTOBUILD_REMOTE_MACHINE} ${MICROPY_AUTOBUILD_REMOTE_DIR} + +######################################## +# Clean up + +/bin/rm -v ${LOCAL_FIRMWARE}/* +/bin/rmdir ${LOCAL_FIRMWARE} diff --git a/tools/autobuild/build-cc3200-latest.sh b/tools/autobuild/build-cc3200-latest.sh new file mode 100755 index 0000000000..3c6d400365 --- /dev/null +++ b/tools/autobuild/build-cc3200-latest.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +# function for building firmware +function do_build() { + descr=$1 + board=$2 + shift + shift + echo "building $descr $board" + build_dir=/tmp/cc3200-build-$board + $MICROPY_AUTOBUILD_MAKE -B $@ BTARGET=application BOARD=$board BUILD=$build_dir || exit 1 + zip $dest_dir/$descr$fw_tag.zip $build_dir/mcuimg.bin + rm -rf $build_dir +} + +# check/get parameters +if [ $# != 2 ]; then + echo "usage: $0 " + exit 1 +fi + +fw_tag=$1 +dest_dir=$2 + +# check we are in the correct directory +if [ ! -r application.mk ]; then + echo "must be in cc3200 directory" + exit 1 +fi + +# build the versions +do_build wipy WIPY diff --git a/tools/autobuild/build-esp32-latest.sh b/tools/autobuild/build-esp32-latest.sh new file mode 100755 index 0000000000..e433332c9e --- /dev/null +++ b/tools/autobuild/build-esp32-latest.sh @@ -0,0 +1,45 @@ +#!/bin/bash +# +# Build esp32 port + +# for debugging +#exec &> /tmp/esp-log-$$.txt + +# function for building firmware +function do_build() { + descr=$1 + board=$2 + shift + shift + echo "building $descr $board" + build_dir=/tmp/esp32-build-$board + rm -rf $build_dir # be sure we don't have anything leftover from a previous build + make $@ BOARD=$board BUILD=$build_dir || exit 1 + mv $build_dir/firmware.bin $dest_dir/$descr$fw_tag.bin + mv $build_dir/micropython.elf $dest_dir/$descr$fw_tag.elf + mv $build_dir/micropython.map $dest_dir/$descr$fw_tag.map + rm -rf $build_dir +} + +# check/get parameters +if [ $# != 3 ]; then + echo "usage: $0 " + exit 1 +fi + +idf_path=$1 +fw_tag=$2 +dest_dir=$3 + +# check we are in the correct directory +if [ ! -r modesp32.c ]; then + echo "must be in esp32 directory" + exit 1 +fi + +source $idf_path/export.sh + +# build the versions +do_build esp32 GENERIC FROZEN_MANIFEST=$(pwd)/boards/manifest_release.py +do_build esp32spiram GENERIC_SPIRAM FROZEN_MANIFEST=$(pwd)/boards/manifest_release.py +do_build tinypico UM_TINYPICO diff --git a/tools/autobuild/build-esp8266-latest.sh b/tools/autobuild/build-esp8266-latest.sh new file mode 100755 index 0000000000..fe79587fd1 --- /dev/null +++ b/tools/autobuild/build-esp8266-latest.sh @@ -0,0 +1,66 @@ +#!/bin/bash + +PYTHON3=python3 +yaota8266=$HOME/yaota8266 + +# for debugging +#exec &> /tmp/esp-log-$$.txt + +# function for building firmware +function do_build() { + descr=$1 + board=$2 + shift + shift + echo "building $descr $board" + #build_dir=/tmp/esp8266-build-$board + build_dir=build-$board # until esp8266.ld is fixed + rm -rf $build_dir # be sure we don't have anything leftover from a previous build + $MICROPY_AUTOBUILD_MAKE $@ BOARD=$board BUILD=$build_dir || exit 1 + mv $build_dir/firmware-combined.bin $dest_dir/$descr$fw_tag.bin + mv $build_dir/firmware.elf $dest_dir/$descr$fw_tag.elf + mv $build_dir/firmware.map $dest_dir/$descr$fw_tag.map + rm -rf $build_dir +} + +function do_build_ota() { + descr=$1 + board=$2 + shift + shift + echo "building $descr $board" + #build_dir=/tmp/esp8266-build-$board + build_dir=build-$board # until esp8266.ld is fixed + rm -rf $build_dir # be sure we don't have anything leftover from a previous build + $MICROPY_AUTOBUILD_MAKE $@ BOARD=$board BUILD=$build_dir || exit 1 + cat $yaota8266/yaota8266.bin $build_dir/firmware-ota.bin > $dest_dir/$descr$fw_tag.bin + cwd=$(pwd) + pushd $yaota8266/ota-client + $PYTHON3 ota_client.py sign $cwd/$build_dir/firmware-ota.bin + popd + mv $build_dir/firmware-ota.bin.ota $dest_dir/$descr$fw_tag.ota + mv $build_dir/firmware.elf $dest_dir/$descr$fw_tag.elf + mv $build_dir/firmware.map $dest_dir/$descr$fw_tag.map + #rm -rf $build_dir +} + +# check/get parameters +if [ $# != 2 ]; then + echo "usage: $0 " + exit 1 +fi + +fw_tag=$1 +dest_dir=$2 + +# check we are in the correct directory +if [ ! -r boards/esp8266_common.ld ]; then + echo "must be in esp8266 directory" + exit 1 +fi + +# build the versions +do_build esp8266 GENERIC +do_build esp8266-512k GENERIC_512K +do_build esp8266-1m GENERIC_1M +do_build_ota esp8266-ota GENERIC ota diff --git a/tools/autobuild/build-rp2-latest.sh b/tools/autobuild/build-rp2-latest.sh new file mode 100755 index 0000000000..5124686e21 --- /dev/null +++ b/tools/autobuild/build-rp2-latest.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +# function for building firmware +function do_build() { + descr=$1 + board=$2 + shift + shift + echo "building $descr $board" + build_dir=/tmp/rp2-build-$board + $MICROPY_AUTOBUILD_MAKE $@ BOARD=$board BUILD=$build_dir || exit 1 + mv $build_dir/firmware.uf2 $dest_dir/$descr$fw_tag.uf2 + rm -rf $build_dir +} + +# check/get parameters +if [ $# != 2 ]; then + echo "usage: $0 " + exit 1 +fi + +fw_tag=$1 +dest_dir=$2 + +# check we are in the correct directory +if [ ! -r modrp2.c ]; then + echo "must be in rp2 directory" + exit 1 +fi + +# build the boards +do_build rp2-pico PICO diff --git a/tools/autobuild/build-stm32-latest.sh b/tools/autobuild/build-stm32-latest.sh new file mode 100755 index 0000000000..226c14a41e --- /dev/null +++ b/tools/autobuild/build-stm32-latest.sh @@ -0,0 +1,56 @@ +#!/bin/bash + +# function for building firmware +function do_build() { + descr=$1 + board=$2 + shift + shift + echo "building $descr $board" + build_dir=/tmp/stm-build-$board + $MICROPY_AUTOBUILD_MAKE -B $@ BOARD=$board BUILD=$build_dir || exit 1 + mv $build_dir/firmware.dfu $dest_dir/$descr$fw_tag.dfu + rm -rf $build_dir +} + +# check/get parameters +if [ $# != 2 ]; then + echo "usage: $0 " + exit 1 +fi + +fw_tag=$1 +dest_dir=$2 + +# check we are in the correct directory +if [ ! -r modpyb.c ]; then + echo "must be in stm directory" + exit 1 +fi + +# build the versions +do_build pybv3 PYBV3 +do_build pybv3-network PYBV3 MICROPY_PY_WIZNET5K=5200 MICROPY_PY_CC3K=1 +do_build pybv10 PYBV10 +do_build pybv10-dp PYBV10 MICROPY_FLOAT_IMPL=double +do_build pybv10-thread PYBV10 CFLAGS_EXTRA='-DMICROPY_PY_THREAD=1' +do_build pybv10-dp-thread PYBV10 MICROPY_FLOAT_IMPL=double CFLAGS_EXTRA='-DMICROPY_PY_THREAD=1' +do_build pybv10-network PYBV10 MICROPY_PY_WIZNET5K=5200 MICROPY_PY_CC3K=1 +do_build pybv11 PYBV11 +do_build pybv11-dp PYBV11 MICROPY_FLOAT_IMPL=double +do_build pybv11-thread PYBV11 CFLAGS_EXTRA='-DMICROPY_PY_THREAD=1' +do_build pybv11-dp-thread PYBV11 MICROPY_FLOAT_IMPL=double CFLAGS_EXTRA='-DMICROPY_PY_THREAD=1' +do_build pybv11-network PYBV11 MICROPY_PY_WIZNET5K=5200 MICROPY_PY_CC3K=1 +do_build pyblitev10 PYBLITEV10 +do_build pyblitev10-dp PYBLITEV10 MICROPY_FLOAT_IMPL=double +do_build pyblitev10-thread PYBLITEV10 CFLAGS_EXTRA='-DMICROPY_PY_THREAD=1' +do_build pyblitev10-dp-thread PYBLITEV10 MICROPY_FLOAT_IMPL=double CFLAGS_EXTRA='-DMICROPY_PY_THREAD=1' +do_build pyblitev10-network PYBLITEV10 MICROPY_PY_WIZNET5K=5200 MICROPY_PY_CC3K=1 +do_build PYBD-SF2 PYBD_SF2 +do_build PYBD-SF3 PYBD_SF3 +do_build PYBD-SF6 PYBD_SF6 + +for board in boards/{NUCLEO_*,STM32F*DISC,B_L*,USBDONGLE_WB55,ESPRUINO_PICO} ; do + bd=$(basename $board) + do_build $bd $bd USE_MBOOT=0 MBOOT_ENABLE_PACKING=0 +done diff --git a/tools/autobuild/remove_old_firmware.py b/tools/autobuild/remove_old_firmware.py new file mode 100755 index 0000000000..e9d2e8aae8 --- /dev/null +++ b/tools/autobuild/remove_old_firmware.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 + +import re, subprocess, sys + + +DEBUG = False +DRY_RUN = False +NUM_KEEP_PER_BOARD = 4 + + +def main(): + ssh_machine = sys.argv[1] + ssh_firmware_dir = sys.argv[2] + + # SSH to get list of existing files. + p = subprocess.run( + ["ssh", ssh_machine, "find", ssh_firmware_dir, "-name", "\\*-unstable-v\\*"], + capture_output=True, + ) + if p.returncode != 0: + print(p.stderr) + return + all_files = p.stdout.split(b"\n") + + # Parse all files to organise into boards/date/version. + boards = {} + for file in all_files: + m = re.match( + rb"([a-z/.]+)/([A-Za-z0-9_-]+)-(20[0-9]{6})-unstable-(v[0-9.-]+-g[0-9a-f]+).", + file, + ) + if not m: + continue + dir, board, date, version = m.groups() + if board not in boards: + boards[board] = {} + if (date, version) not in boards[board]: + boards[board][(date, version)] = [] + boards[board][(date, version)].append(file) + + # Collect files to remove based on date and version. + remove = [] + for board in boards.values(): + filelist = [(date, version, files) for (date, version), files in board.items()] + filelist.sort(reverse=True) + keep = [] + for date, version, files in filelist: + if keep and version == keep[-1]: + remove.extend(files) + elif len(keep) >= NUM_KEEP_PER_BOARD: + remove.extend(files) + else: + keep.append(version) + + if DEBUG: + all_files.sort(reverse=True) + for file in all_files: + print(file, file in remove) + print(len(remove), "/", len(all_files)) + + # Do removal of files. + for file in remove: + file = str(file, "ascii") + print("remove:", file) + if not DRY_RUN: + p = subprocess.run(["ssh", ssh_machine, "/bin/rm", file], capture_output=True) + if p.returncode != 0: + print(p.stderr) + + +if __name__ == "__main__": + main() From c99e4995fd6bb19fa4d0d587f1b72f46efcf9785 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 21 Jun 2021 14:59:20 +1000 Subject: [PATCH 013/264] tools: Remove obsolete build-stm-latest.sh script. The tools/autobuild/ scripts replace this. Signed-off-by: Damien George --- tools/build-stm-latest.sh | 42 --------------------------------------- 1 file changed, 42 deletions(-) delete mode 100755 tools/build-stm-latest.sh diff --git a/tools/build-stm-latest.sh b/tools/build-stm-latest.sh deleted file mode 100755 index 07cb168daa..0000000000 --- a/tools/build-stm-latest.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash - -# function for building firmware -function do_build() { - descr=$1 - board=$2 - shift - shift - echo "building $descr $board" - build_dir=/tmp/stm-build-$board - make -B $@ BOARD=$board BUILD=$build_dir || exit 1 - mv $build_dir/firmware.dfu $dest_dir/$descr-$date-$git_tag.dfu - rm -rf $build_dir -} - -# check/get parameters -if [ $# != 1 ]; then - echo "usage: $0 " - exit 1 -fi - -dest_dir=$1 - -# check we are in the correct directory -if [ ! -r modpyb.c ]; then - echo "must be in stm directory" - exit 1 -fi - -# get the date -date=$(date '+%Y-%m-%d') - -# get the git tag -git_tag="$(git describe --dirty || echo unknown)" - -# build the versions -do_build pybv3 PYBV3 -do_build pybv3-network PYBV3 MICROPY_PY_WIZNET5K=1 MICROPY_PY_CC3K=1 -do_build pybv10 PYBV10 -do_build pybv10-network PYBV10 MICROPY_PY_WIZNET5K=1 MICROPY_PY_CC3K=1 -do_build stm32f4disc STM32F4DISC -do_build espruino-pico ESPRUINO_PICO From 0009a7dc305b9cce693b2e3fe94174c85dc1ef2e Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 24 Jun 2021 16:03:25 +1000 Subject: [PATCH 014/264] esp32/main: Allow MICROPY_DIR to be overridden. This is necessary when building a custom out-of-tree board. Signed-off-by: Damien George --- ports/esp32/main/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ports/esp32/main/CMakeLists.txt b/ports/esp32/main/CMakeLists.txt index 1cc30b71ec..d01656e567 100644 --- a/ports/esp32/main/CMakeLists.txt +++ b/ports/esp32/main/CMakeLists.txt @@ -1,5 +1,7 @@ # Set location of base MicroPython directory. -get_filename_component(MICROPY_DIR ${PROJECT_DIR}/../.. ABSOLUTE) +if(NOT MICROPY_DIR) + get_filename_component(MICROPY_DIR ${PROJECT_DIR}/../.. ABSOLUTE) +endif() # Include core source components. include(${MICROPY_DIR}/py/py.cmake) From 413f34cd8f244d1ce8b3c73386fdd57241165a1b Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Tue, 8 Jun 2021 07:45:56 -0500 Subject: [PATCH 015/264] all: Fix signed shifts and NULL access errors from -fsanitize=undefined. Fixes the following (the line numbers match commit 0e87459e2bfd07): ../../extmod/crypto-algorithms/sha256.c:49:19: runtime error: left shif... ../../extmod/moduasyncio.c:106:35: runtime error: member access within ... ../../py/binary.c:210:13: runtime error: left shift of negative value -... ../../py/mpz.c:744:16: runtime error: negation of -9223372036854775808 ... ../../py/objint.c:109:22: runtime error: left shift of 1 by 31 places c... ../../py/objint_mpz.c:374:9: runtime error: left shift of 4611686018427... ../../py/objint_mpz.c:374:9: runtime error: left shift of negative valu... ../../py/parsenum.c:106:14: runtime error: left shift of 46116860184273... ../../py/runtime.c:395:33: runtime error: left shift of negative value ... ../../py/showbc.c:177:28: runtime error: left shift of negative value -... ../../py/vm.c:321:36: runtime error: left shift of negative value -1``` Testing was done on an amd64 Debian Buster system using gcc-8.3 and these settings: CFLAGS += -g3 -Og -fsanitize=undefined LDFLAGS += -fsanitize=undefined The introduced TASK_PAIRHEAP macro's conditional (x ? &x->i : NULL) assembles (under amd64 gcc 8.3 -Os) to the same as &x->i, since i is the initial field of the struct. However, for the purposes of undefined behavior analysis the conditional is needed. Signed-off-by: Jeff Epler --- extmod/crypto-algorithms/sha256.c | 2 +- extmod/moduasyncio.c | 5 ++++- py/binary.c | 2 +- py/mpz.c | 2 +- py/objint.c | 6 +++--- py/runtime.c | 2 +- py/showbc.c | 2 +- py/smallint.h | 2 +- py/vm.c | 2 +- 9 files changed, 14 insertions(+), 11 deletions(-) diff --git a/extmod/crypto-algorithms/sha256.c b/extmod/crypto-algorithms/sha256.c index 24e964749b..ce0377208d 100644 --- a/extmod/crypto-algorithms/sha256.c +++ b/extmod/crypto-algorithms/sha256.c @@ -46,7 +46,7 @@ static void sha256_transform(CRYAL_SHA256_CTX *ctx, const BYTE data[]) WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; for (i = 0, j = 0; i < 16; ++i, j += 4) - m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]); + m[i] = ((uint32_t)data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]); for ( ; i < 64; ++i) m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16]; diff --git a/extmod/moduasyncio.c b/extmod/moduasyncio.c index 9717e38567..dd2d1e7475 100644 --- a/extmod/moduasyncio.c +++ b/extmod/moduasyncio.c @@ -31,6 +31,9 @@ #if MICROPY_PY_UASYNCIO +// Used when task cannot be guaranteed to be non-NULL. +#define TASK_PAIRHEAP(task) ((task) ? &(task)->pairheap : NULL) + #define TASK_STATE_RUNNING_NOT_WAITED_ON (mp_const_true) #define TASK_STATE_DONE_NOT_WAITED_ON (mp_const_none) #define TASK_STATE_DONE_WAS_WAITED_ON (mp_const_false) @@ -110,7 +113,7 @@ STATIC mp_obj_t task_queue_push_sorted(size_t n_args, const mp_obj_t *args) { assert(mp_obj_is_small_int(args[2])); task->ph_key = args[2]; } - self->heap = (mp_obj_task_t *)mp_pairheap_push(task_lt, &self->heap->pairheap, &task->pairheap); + self->heap = (mp_obj_task_t *)mp_pairheap_push(task_lt, TASK_PAIRHEAP(self->heap), TASK_PAIRHEAP(task)); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(task_queue_push_sorted_obj, 2, 3, task_queue_push_sorted); diff --git a/py/binary.c b/py/binary.c index 5c098b223d..05e658c952 100644 --- a/py/binary.c +++ b/py/binary.c @@ -202,7 +202,7 @@ long long mp_binary_get_int(size_t size, bool is_signed, bool big_endian, const delta = 1; } - long long val = 0; + unsigned long long val = 0; if (is_signed && *src & 0x80) { val = -1; } diff --git a/py/mpz.c b/py/mpz.c index e0d249c214..75e1fb1fdb 100644 --- a/py/mpz.c +++ b/py/mpz.c @@ -741,7 +741,7 @@ void mpz_set_from_ll(mpz_t *z, long long val, bool is_signed) { unsigned long long uval; if (is_signed && val < 0) { z->neg = 1; - uval = -val; + uval = -(unsigned long long)val; } else { z->neg = 0; uval = val; diff --git a/py/objint.c b/py/objint.c index 00375d388e..5ff5e7de4b 100644 --- a/py/objint.c +++ b/py/objint.c @@ -106,14 +106,14 @@ STATIC mp_fp_as_int_class_t mp_classify_fp_as_int(mp_float_t val) { #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE e |= u.i[MP_ENDIANNESS_BIG] != 0; #endif - if ((e & ~(1 << MP_FLOAT_SIGN_SHIFT_I32)) == 0) { + if ((e & ~(1U << MP_FLOAT_SIGN_SHIFT_I32)) == 0) { // handle case of -0 (when sign is set but rest of bits are zero) e = 0; } else { - e += ((1 << MP_FLOAT_EXP_BITS) - 1) << MP_FLOAT_EXP_SHIFT_I32; + e += ((1U << MP_FLOAT_EXP_BITS) - 1) << MP_FLOAT_EXP_SHIFT_I32; } } else { - e &= ~((1 << MP_FLOAT_EXP_SHIFT_I32) - 1); + e &= ~((1U << MP_FLOAT_EXP_SHIFT_I32) - 1); } // 8 * sizeof(uintptr_t) counts the number of bits for a small int // TODO provide a way to configure this properly diff --git a/py/runtime.c b/py/runtime.c index cda26a9cf8..261670d4f3 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -392,7 +392,7 @@ mp_obj_t mp_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) { goto generic_binary_op; } else { // use standard precision - lhs_val <<= rhs_val; + lhs_val = (mp_uint_t)lhs_val << rhs_val; } break; } diff --git a/py/showbc.c b/py/showbc.c index 8941dd7544..cb81b88359 100644 --- a/py/showbc.c +++ b/py/showbc.c @@ -174,7 +174,7 @@ const byte *mp_bytecode_print_str(const mp_print_t *print, const byte *ip) { num--; } do { - num = (num << 7) | (*ip & 0x7f); + num = ((mp_uint_t)num << 7) | (*ip & 0x7f); } while ((*ip++ & 0x80) != 0); mp_printf(print, "LOAD_CONST_SMALL_INT " INT_FMT, num); break; diff --git a/py/smallint.h b/py/smallint.h index 58d843e8a1..67daf9b9fa 100644 --- a/py/smallint.h +++ b/py/smallint.h @@ -37,7 +37,7 @@ #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_A || MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C #define MP_SMALL_INT_MIN ((mp_int_t)(((mp_int_t)MP_OBJ_WORD_MSBIT_HIGH) >> 1)) -#define MP_SMALL_INT_FITS(n) ((((n) ^ ((n) << 1)) & MP_OBJ_WORD_MSBIT_HIGH) == 0) +#define MP_SMALL_INT_FITS(n) ((((n) ^ ((mp_uint_t)(n) << 1)) & MP_OBJ_WORD_MSBIT_HIGH) == 0) // Mask to truncate mp_int_t to positive value #define MP_SMALL_INT_POSITIVE_MASK ~(MP_OBJ_WORD_MSBIT_HIGH | (MP_OBJ_WORD_MSBIT_HIGH >> 1)) diff --git a/py/vm.c b/py/vm.c index f9a589c9d1..5365014fcb 100644 --- a/py/vm.c +++ b/py/vm.c @@ -312,7 +312,7 @@ dispatch_loop: DISPATCH(); ENTRY(MP_BC_LOAD_CONST_SMALL_INT): { - mp_int_t num = 0; + mp_uint_t num = 0; if ((ip[0] & 0x40) != 0) { // Number is negative num--; From cb332ddae8075001a1a9e6af0fa8aa191602dee8 Mon Sep 17 00:00:00 2001 From: Amir Gonnen Date: Tue, 15 Jun 2021 09:02:50 +0300 Subject: [PATCH 016/264] unix/modffi: Add option to lock GC in callback, and cfun access. Add an optional 'lock' kwarg to callback that locks GC and scheduler. This allows the callback to be invoked asynchronously in 'interrupt context', for example as a signal handler. Also add the 'cfun' member function to callback, that allows retrieving the C callback function address. This is needed when the callback should be set to a struct field. See related #7373. Signed-off-by: Amir Gonnen --- ports/unix/modffi.c | 76 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 71 insertions(+), 5 deletions(-) diff --git a/ports/unix/modffi.c b/ports/unix/modffi.c index 12a33619e0..0ff37a99f5 100644 --- a/ports/unix/modffi.c +++ b/ports/unix/modffi.c @@ -36,6 +36,7 @@ #include "py/binary.h" #include "py/mperrno.h" #include "py/objint.h" +#include "py/gc.h" /* * modffi uses character codes to encode a value type, based on "struct" @@ -100,6 +101,7 @@ typedef struct _mp_obj_fficallback_t { void *func; ffi_closure *clo; char rettype; + mp_obj_t pyfunc; ffi_cif cif; ffi_type *params[]; } mp_obj_fficallback_t; @@ -266,19 +268,69 @@ STATIC mp_obj_t mod_ffi_func(mp_obj_t rettype, mp_obj_t addr_in, mp_obj_t argtyp } MP_DEFINE_CONST_FUN_OBJ_3(mod_ffi_func_obj, mod_ffi_func); -STATIC void call_py_func(ffi_cif *cif, void *ret, void **args, void *func) { +STATIC void call_py_func(ffi_cif *cif, void *ret, void **args, void *user_data) { mp_obj_t pyargs[cif->nargs]; + mp_obj_fficallback_t *o = user_data; + mp_obj_t pyfunc = o->pyfunc; + for (uint i = 0; i < cif->nargs; i++) { pyargs[i] = mp_obj_new_int(*(mp_int_t *)args[i]); } - mp_obj_t res = mp_call_function_n_kw(MP_OBJ_FROM_PTR(func), cif->nargs, 0, pyargs); + mp_obj_t res = mp_call_function_n_kw(pyfunc, cif->nargs, 0, pyargs); if (res != mp_const_none) { *(ffi_arg *)ret = mp_obj_int_get_truncated(res); } } -STATIC mp_obj_t mod_ffi_callback(mp_obj_t rettype_in, mp_obj_t func_in, mp_obj_t paramtypes_in) { +STATIC void call_py_func_with_lock(ffi_cif *cif, void *ret, void **args, void *user_data) { + mp_obj_t pyargs[cif->nargs]; + mp_obj_fficallback_t *o = user_data; + mp_obj_t pyfunc = o->pyfunc; + nlr_buf_t nlr; + + #if MICROPY_ENABLE_SCHEDULER + mp_sched_lock(); + #endif + gc_lock(); + + if (nlr_push(&nlr) == 0) { + for (uint i = 0; i < cif->nargs; i++) { + pyargs[i] = mp_obj_new_int(*(mp_int_t *)args[i]); + } + mp_obj_t res = mp_call_function_n_kw(pyfunc, cif->nargs, 0, pyargs); + + if (res != mp_const_none) { + *(ffi_arg *)ret = mp_obj_int_get_truncated(res); + } + nlr_pop(); + } else { + // Uncaught exception + mp_printf(MICROPY_ERROR_PRINTER, "Uncaught exception in FFI callback\n"); + mp_obj_print_exception(MICROPY_ERROR_PRINTER, MP_OBJ_FROM_PTR(nlr.ret_val)); + } + + gc_unlock(); + #if MICROPY_ENABLE_SCHEDULER + mp_sched_unlock(); + #endif +} + +STATIC mp_obj_t mod_ffi_callback(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + // first 3 args are positional: retttype, func, paramtypes. + mp_obj_t rettype_in = pos_args[0]; + mp_obj_t func_in = pos_args[1]; + mp_obj_t paramtypes_in = pos_args[2]; + + // arg parsing is used only for additional kwargs + enum { ARG_lock }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_lock, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 3, pos_args + 3, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + bool lock_in = args[ARG_lock].u_bool; + const char *rettype = mp_obj_str_get_str(rettype_in); mp_int_t nparams = MP_OBJ_SMALL_INT_VALUE(mp_obj_len_maybe(paramtypes_in)); @@ -288,6 +340,7 @@ STATIC mp_obj_t mod_ffi_callback(mp_obj_t rettype_in, mp_obj_t func_in, mp_obj_t o->clo = ffi_closure_alloc(sizeof(ffi_closure), &o->func); o->rettype = *rettype; + o->pyfunc = func_in; mp_obj_iter_buf_t iter_buf; mp_obj_t iterable = mp_getiter(paramtypes_in, &iter_buf); @@ -302,14 +355,15 @@ STATIC mp_obj_t mod_ffi_callback(mp_obj_t rettype_in, mp_obj_t func_in, mp_obj_t mp_raise_ValueError(MP_ERROR_TEXT("error in ffi_prep_cif")); } - res = ffi_prep_closure_loc(o->clo, &o->cif, call_py_func, MP_OBJ_TO_PTR(func_in), o->func); + res = ffi_prep_closure_loc(o->clo, &o->cif, + lock_in? call_py_func_with_lock: call_py_func, o, o->func); if (res != FFI_OK) { mp_raise_ValueError(MP_ERROR_TEXT("ffi_prep_closure_loc")); } return MP_OBJ_FROM_PTR(o); } -MP_DEFINE_CONST_FUN_OBJ_3(mod_ffi_callback_obj, mod_ffi_callback); +MP_DEFINE_CONST_FUN_OBJ_KW(mod_ffi_callback_obj, 3, mod_ffi_callback); STATIC mp_obj_t ffimod_var(mp_obj_t self_in, mp_obj_t vartype_in, mp_obj_t symname_in) { mp_obj_ffimod_t *self = MP_OBJ_TO_PTR(self_in); @@ -493,10 +547,22 @@ STATIC void fficallback_print(const mp_print_t *print, mp_obj_t self_in, mp_prin mp_printf(print, "", self->func); } +STATIC mp_obj_t fficallback_cfun(mp_obj_t self_in) { + mp_obj_fficallback_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_int_from_ull((uintptr_t)self->func); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(fficallback_cfun_obj, fficallback_cfun); + +STATIC const mp_rom_map_elem_t fficallback_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_cfun), MP_ROM_PTR(&fficallback_cfun_obj) } +}; +STATIC MP_DEFINE_CONST_DICT(fficallback_locals_dict, fficallback_locals_dict_table); + STATIC const mp_obj_type_t fficallback_type = { { &mp_type_type }, .name = MP_QSTR_fficallback, .print = fficallback_print, + .locals_dict = (mp_obj_dict_t *)&fficallback_locals_dict }; // FFI variable From b51ae20c0714ea987ac0988c5d02b59e08048632 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sun, 2 Jun 2019 15:57:27 -0500 Subject: [PATCH 017/264] py/mperrno: Add MP_ECANCELED error code. This is useful when binding asynchronous functions in C. Signed-off-by: David Lechner --- py/mperrno.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/py/mperrno.h b/py/mperrno.h index 0cad75a17c..6f6d816b8f 100644 --- a/py/mperrno.h +++ b/py/mperrno.h @@ -81,6 +81,7 @@ #define MP_EHOSTUNREACH (113) // No route to host #define MP_EALREADY (114) // Operation already in progress #define MP_EINPROGRESS (115) // Operation now in progress +#define MP_ECANCELED (125) // Operation canceled #else @@ -136,6 +137,7 @@ #define MP_EHOSTUNREACH EHOSTUNREACH #define MP_EALREADY EALREADY #define MP_EINPROGRESS EINPROGRESS +#define MP_ECANCELED ECANCELED #endif From 6409bbcb720be6c83d1fef3b7da14094b174a783 Mon Sep 17 00:00:00 2001 From: Krzysztof Adamski Date: Sun, 20 Jun 2021 11:36:55 +0200 Subject: [PATCH 018/264] mimxrt: Move calc_weekday helper function to timeutils. This function may be useful for other ports as well so lets move it to timeutils so it can be reused. Signed-off-by: Krzysztof Adamski --- lib/timeutils/timeutils.c | 7 +++++++ lib/timeutils/timeutils.h | 2 ++ ports/mimxrt/machine_rtc.c | 10 ++-------- ports/mimxrt/modutime.c | 4 +--- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/lib/timeutils/timeutils.c b/lib/timeutils/timeutils.c index af210d9943..7c74f5fc37 100644 --- a/lib/timeutils/timeutils.c +++ b/lib/timeutils/timeutils.c @@ -213,3 +213,10 @@ mp_uint_t timeutils_mktime_2000(mp_uint_t year, mp_int_t month, mp_int_t mday, } return timeutils_seconds_since_2000(year, month, mday, hours, minutes, seconds); } + +// Calculate the weekday from the date. +// The result is zero based with 0 = Monday. +// by Michael Keith and Tom Craver, 1990. +int timeutils_calc_weekday(int y, int m, int d) { + return ((d += m < 3 ? y-- : y - 2, 23 * m / 9 + d + 4 + y / 4 - y / 100 + y / 400) + 6) % 7; +} diff --git a/lib/timeutils/timeutils.h b/lib/timeutils/timeutils.h index 2d40f773cc..66e2a77f13 100644 --- a/lib/timeutils/timeutils.h +++ b/lib/timeutils/timeutils.h @@ -100,4 +100,6 @@ static inline int64_t timeutils_nanoseconds_since_epoch_to_nanoseconds_since_197 #endif +int timeutils_calc_weekday(int y, int m, int d); + #endif // MICROPY_INCLUDED_LIB_TIMEUTILS_TIMEUTILS_H diff --git a/ports/mimxrt/machine_rtc.c b/ports/mimxrt/machine_rtc.c index 76f414925e..d00d139f79 100644 --- a/ports/mimxrt/machine_rtc.c +++ b/ports/mimxrt/machine_rtc.c @@ -26,6 +26,7 @@ */ #include "py/runtime.h" +#include "lib/timeutils/timeutils.h" #include "modmachine.h" #include "ticks.h" #include "fsl_snvs_lp.h" @@ -39,13 +40,6 @@ typedef struct _machine_rtc_obj_t { STATIC const machine_rtc_obj_t machine_rtc_obj = {{&machine_rtc_type}}; uint32_t us_offset = 0; -// Calculate the weekday from the date. -// The result is zero based with 0 = Monday. -// by Michael Keith and Tom Craver, 1990. -int calc_weekday(int y, int m, int d) { - return ((d += m < 3 ? y-- : y - 2, 23 * m / 9 + d + 4 + y / 4 - y / 100 + y / 400) + 6) % 7; -} - STATIC mp_obj_t machine_rtc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { // Check arguments. mp_arg_check_num(n_args, n_kw, 0, 0, false); @@ -67,7 +61,7 @@ STATIC mp_obj_t machine_rtc_datetime_helper(size_t n_args, const mp_obj_t *args) mp_obj_new_int(srtc_date.year), mp_obj_new_int(srtc_date.month), mp_obj_new_int(srtc_date.day), - mp_obj_new_int(calc_weekday(srtc_date.year, srtc_date.month, srtc_date.day)), + mp_obj_new_int(timeutils_calc_weekday(srtc_date.year, srtc_date.month, srtc_date.day)), mp_obj_new_int(srtc_date.hour), mp_obj_new_int(srtc_date.minute), mp_obj_new_int(srtc_date.second), diff --git a/ports/mimxrt/modutime.c b/ports/mimxrt/modutime.c index a47bc073f2..2a88a4224b 100644 --- a/ports/mimxrt/modutime.c +++ b/ports/mimxrt/modutime.c @@ -30,8 +30,6 @@ #include "extmod/utime_mphal.h" #include "fsl_snvs_lp.h" -extern int calc_weekday(int y, int m, int d); - // localtime([secs]) // Convert a time expressed in seconds since the Epoch into an 8-tuple which // contains: (year, month, mday, hour, minute, second, weekday, yearday) @@ -48,7 +46,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.minute), mp_obj_new_int(t.second), - mp_obj_new_int(calc_weekday(t.year, t.month, t.day)), + mp_obj_new_int(timeutils_calc_weekday(t.year, t.month, t.day)), mp_obj_new_int(timeutils_year_day(t.year, t.month, t.day)), }; return mp_obj_new_tuple(8, tuple); From 35b1359a3ab082515e537bbb9129573e9dda1162 Mon Sep 17 00:00:00 2001 From: Krzysztof Adamski Date: Sun, 20 Jun 2021 11:44:16 +0200 Subject: [PATCH 019/264] rp2: Use 0=Monday datetime convention in RTC. The RTC in rp2 can store any, even wrong, number as a weekday in RTC. It was, however, discussed in #7394 that we would like to unify all ports and use 0 as Monday, not Sunday in the machine.RTC implementation. This patch makes sure that the default date set in RTC is adheres to this convention. It also fixes the example in quickref to use proper weekday to avoid confusion. Signed-off-by: Krzysztof Adamski --- docs/rp2/quickref.rst | 2 +- ports/rp2/main.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/rp2/quickref.rst b/docs/rp2/quickref.rst index 6bbe179541..4c8c02dc0c 100644 --- a/docs/rp2/quickref.rst +++ b/docs/rp2/quickref.rst @@ -202,7 +202,7 @@ See :ref:`machine.RTC ` :: from machine import RTC rtc = RTC() - rtc.datetime((2017, 8, 23, 1, 12, 48, 0, 0)) # set a specific date and time + rtc.datetime((2017, 8, 23, 2, 12, 48, 0, 0)) # set a specific date and time rtc.datetime() # get date and time WDT (Watchdog timer) diff --git a/ports/rp2/main.c b/ports/rp2/main.c index 7709a478bc..8c5772171a 100644 --- a/ports/rp2/main.c +++ b/ports/rp2/main.c @@ -80,7 +80,7 @@ int main(int argc, char **argv) { .year = 2021, .month = 1, .day = 1, - .dotw = 5, // 0 is Sunday, so 5 is Friday + .dotw = 4, // 0 is Monday, so 4 is Friday .hour = 0, .min = 0, .sec = 0, From feb7e2e864639d82cc22729d4011b8ea663e9d1d Mon Sep 17 00:00:00 2001 From: Krzysztof Adamski Date: Sun, 20 Jun 2021 11:49:56 +0200 Subject: [PATCH 020/264] rp2/machine_rtc: In RTC.datetime, compute weekday automatically. Calculating the weekday each time you want to set a date is error prone and tiresome. MicroPython can do it on its own - hardware on some ports do not support storing weekday in hardware and always computes it on the fly, ignoring the value given to the constructor. During discussion for #7432 the conclusion was that there seems to be no obvious reason to let user set the weekday to an incorrect value so it makes sense to just ignore the provided weekday value and always compute the correct value. This patch introduces this change for the rp2 port. Signed-off-by: Krzysztof Adamski --- ports/rp2/machine_rtc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ports/rp2/machine_rtc.c b/ports/rp2/machine_rtc.c index 797bee5ed3..a7c55b616b 100644 --- a/ports/rp2/machine_rtc.c +++ b/ports/rp2/machine_rtc.c @@ -94,11 +94,12 @@ STATIC mp_obj_t machine_rtc_datetime(mp_uint_t n_args, const mp_obj_t *args) { .year = mp_obj_get_int(items[0]), .month = mp_obj_get_int(items[1]), .day = mp_obj_get_int(items[2]), - .dotw = mp_obj_get_int(items[3]), .hour = mp_obj_get_int(items[4]), .min = mp_obj_get_int(items[5]), .sec = mp_obj_get_int(items[6]), }; + // Deliberately ignore the weekday argument and compute the proper value + t.dotw = timeutils_calc_weekday(t.year, t.month, t.day); if (!rtc_set_datetime(&t)) { mp_raise_OSError(MP_EINVAL); From 8cebd56a11be1d3fbc87344f4e18fcdec6382b37 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Thu, 24 Jun 2021 10:52:47 -0500 Subject: [PATCH 021/264] tools/ci.sh: Run apt-get update in ci_powerpc_setup. This fixes failing builds when the GitHub CI image lags behind Ubuntu security updates. --- tools/ci.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/ci.sh b/tools/ci.sh index 3039573322..6d7c643f5d 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -172,6 +172,7 @@ function ci_nrf_build { # ports/powerpc function ci_powerpc_setup { + sudo apt-get update sudo apt-get install gcc-powerpc64le-linux-gnu libc6-dev-ppc64el-cross } From 115acadf9211fddc8b5857af287daa622dc615c1 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 1 Jun 2020 12:18:58 -0500 Subject: [PATCH 022/264] mpy-cross: Disable stack check when building with Emscripten. `mpy-cross` can be compiled to WASM using Emscripten, but it is not happy unless the stack check is disabled. Signed-off-by: David Lechner --- mpy-cross/mpconfigport.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mpy-cross/mpconfigport.h b/mpy-cross/mpconfigport.h index 7ff3d1b6b4..96d3726562 100644 --- a/mpy-cross/mpconfigport.h +++ b/mpy-cross/mpconfigport.h @@ -62,7 +62,9 @@ #define MICROPY_READER_POSIX (1) #define MICROPY_ENABLE_RUNTIME (0) #define MICROPY_ENABLE_GC (1) +#ifndef __EMSCRIPTEN__ #define MICROPY_STACK_CHECK (1) +#endif #define MICROPY_HELPER_LEXER_UNIX (1) #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) #define MICROPY_ENABLE_SOURCE_LINE (1) From 08e0e065f4fa26cb1f52567ad53052310bc656e6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 24 Jun 2021 09:43:54 +1000 Subject: [PATCH 023/264] py/makeqstrdefs.py: Don't include .h files explicitly in preprocessing. Only include .c and .cpp files explicitly in the list of files passed to the preprocessor for QSTR extraction. All relevant .h files will be included in this process by "#include" from the .c(pp) files. In particular for moduledefs.h, this is included by py/objmodule.c (and doesn't actually contain any extractable MP_QSTR_xxx, but rather defines macros with MP_QSTR_xxx's in them which are then part of py/objmodule.c). The main reason for this change is to simplify the preprocessing step on the javascript port, which tries to compile .h files as C++ precompiled headers if they are passed with -E to clang. Signed-off-by: Damien George --- py/makeqstrdefs.py | 2 +- py/mkrules.mk | 2 +- py/py.mk | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/py/makeqstrdefs.py b/py/makeqstrdefs.py index ad4f22d5e4..187a9aeeaa 100644 --- a/py/makeqstrdefs.py +++ b/py/makeqstrdefs.py @@ -34,7 +34,7 @@ def preprocess(): for source in sources: if source.endswith(".cpp"): cxxsources.append(source) - else: + elif source.endswith(".c"): csources.append(source) try: os.makedirs(os.path.dirname(args.output[0])) diff --git a/py/mkrules.mk b/py/mkrules.mk index eb8477cbb1..b8d07ef56d 100644 --- a/py/mkrules.mk +++ b/py/mkrules.mk @@ -100,7 +100,7 @@ $(OBJ): | $(HEADER_BUILD)/qstrdefs.generated.h $(HEADER_BUILD)/mpversion.h $(OBJ # - else, if list of newer prerequisites ($?) is not empty, then process just these ($?) # - else, process all source files ($^) [this covers "make -B" which can set $? to empty] # See more information about this process in docs/develop/qstr.rst. -$(HEADER_BUILD)/qstr.i.last: $(SRC_QSTR) $(QSTR_GLOBAL_DEPENDENCIES) | $(QSTR_GLOBAL_REQUIREMENTS) +$(HEADER_BUILD)/qstr.i.last: $(SRC_QSTR) $(QSTR_GLOBAL_DEPENDENCIES) $(HEADER_BUILD)/moduledefs.h | $(QSTR_GLOBAL_REQUIREMENTS) $(ECHO) "GEN $@" $(Q)$(PYTHON) $(PY_SRC)/makeqstrdefs.py pp $(CPP) output $(HEADER_BUILD)/qstr.i.last cflags $(QSTR_GEN_CFLAGS) cxxflags $(QSTR_GEN_CXXFLAGS) sources $^ dependencies $(QSTR_GLOBAL_DEPENDENCIES) changed_sources $? diff --git a/py/py.mk b/py/py.mk index 59abc8f503..ab01b0ec21 100644 --- a/py/py.mk +++ b/py/py.mk @@ -269,8 +269,6 @@ $(HEADER_BUILD)/moduledefs.h: $(SRC_QSTR) $(QSTR_GLOBAL_DEPENDENCIES) | $(HEADER @$(ECHO) "GEN $@" $(Q)$(PYTHON) $(PY_SRC)/makemoduledefs.py --vpath="., $(TOP), $(USER_C_MODULES)" $(SRC_QSTR) > $@ -SRC_QSTR += $(HEADER_BUILD)/moduledefs.h - # 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 From cfd08448a1cba0040ad3c62bce02b5f8bd76dd1d Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 24 Jun 2021 11:45:13 +1000 Subject: [PATCH 024/264] py: Mark unused arguments from bytecode decoding macros. Signed-off-by: Damien George --- py/bc.h | 7 +++++-- py/objfun.c | 3 ++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/py/bc.h b/py/bc.h index 16f314e199..ef5afeae16 100644 --- a/py/bc.h +++ b/py/bc.h @@ -128,7 +128,9 @@ #define MP_BC_PRELUDE_SIG_DECODE(ip) \ size_t n_state, n_exc_stack, scope_flags, n_pos_args, n_kwonly_args, n_def_pos_args; \ - MP_BC_PRELUDE_SIG_DECODE_INTO(ip, n_state, n_exc_stack, scope_flags, n_pos_args, n_kwonly_args, n_def_pos_args) + MP_BC_PRELUDE_SIG_DECODE_INTO(ip, n_state, n_exc_stack, scope_flags, n_pos_args, n_kwonly_args, n_def_pos_args); \ + (void)n_state; (void)n_exc_stack; (void)scope_flags; \ + (void)n_pos_args; (void)n_kwonly_args; (void)n_def_pos_args #define MP_BC_PRELUDE_SIZE_ENCODE(I, C, out_byte, out_env) \ do { \ @@ -163,7 +165,8 @@ #define MP_BC_PRELUDE_SIZE_DECODE(ip) \ size_t n_info, n_cell; \ - MP_BC_PRELUDE_SIZE_DECODE_INTO(ip, n_info, n_cell) + MP_BC_PRELUDE_SIZE_DECODE_INTO(ip, n_info, n_cell); \ + (void)n_info; (void)n_cell // Sentinel value for mp_code_state_t.exc_sp_idx #define MP_CODE_STATE_EXC_SP_IDX_SENTINEL ((uint16_t)-1) diff --git a/py/objfun.c b/py/objfun.c index 178f834431..d86a4d235a 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -200,7 +200,8 @@ STATIC void dump_args(const mp_obj_t *a, size_t sz) { const uint8_t *ip = bytecode; \ size_t n_exc_stack, scope_flags, n_pos_args, n_kwonly_args, n_def_args; \ MP_BC_PRELUDE_SIG_DECODE_INTO(ip, n_state_out_var, n_exc_stack, scope_flags, n_pos_args, n_kwonly_args, n_def_args); \ - \ + (void)scope_flags; (void)n_pos_args; (void)n_kwonly_args; (void)n_def_args; \ + \ /* state size in bytes */ \ state_size_out_var = n_state_out_var * sizeof(mp_obj_t) \ + n_exc_stack * sizeof(mp_exc_stack_t); \ From c13853f4dab9c65ee6d1af99b25f999b16ef2827 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 24 Jun 2021 10:04:15 +1000 Subject: [PATCH 025/264] javascript: Rework Makefile and GC so it works with latest Emscripten. The GC now works correctly using asyncify and the functions emscripten_scan_stack() and emscripten_scan_registers(). Stack/call depth is monitored via the use of the pystack option. Fixes issue #6738. Signed-off-by: Damien George --- ports/javascript/JSBackend.patch | 10 ---------- ports/javascript/Makefile | 26 +++++++++----------------- ports/javascript/README.md | 22 ++++++---------------- ports/javascript/main.c | 21 ++++++++------------- 4 files changed, 23 insertions(+), 56 deletions(-) delete mode 100644 ports/javascript/JSBackend.patch diff --git a/ports/javascript/JSBackend.patch b/ports/javascript/JSBackend.patch deleted file mode 100644 index a5fd41a3fb..0000000000 --- a/ports/javascript/JSBackend.patch +++ /dev/null @@ -1,10 +0,0 @@ ---- JSBackend.cpp 2018-01-10 16:35:07.331418145 +1100 -+++ JSBackend_mp_js.cpp 2018-01-10 16:40:04.804633134 +1100 -@@ -4280,6 +4280,7 @@ - - void JSWriter::calculateNativizedVars(const Function *F) { - NativizedVars.clear(); -+ return; - - for (Function::const_iterator I = F->begin(), BE = F->end(); I != BE; ++I) { - auto BI = &*I; diff --git a/ports/javascript/Makefile b/ports/javascript/Makefile index 9723675546..171f53b952 100644 --- a/ports/javascript/Makefile +++ b/ports/javascript/Makefile @@ -6,24 +6,16 @@ QSTR_DEFS = qstrdefsport.h include $(TOP)/py/py.mk -CC = emcc -g4 -LD = emcc -g4 +CC = emcc +LD = emcc INC += -I. INC += -I$(TOP) INC += -I$(BUILD) -CPP = clang -E - -ifdef EMSCRIPTEN - CPP += -isystem $(EMSCRIPTEN)/system/include/libc -cxx-isystem $(EMSCRIPTEN)/system/include/libcxx -endif - -CFLAGS = -m32 -Wall -Werror -Wdouble-promotion -Wfloat-conversion $(INC) -std=c99 $(COPT) -LDFLAGS = -m32 -Wl,-Map=$@.map,--cref -Wl,--gc-sections - -CFLAGS += -O0 -DNDEBUG -CFLAGS += -fdata-sections -ffunction-sections +CFLAGS += -std=c99 -Wall -Werror -Wdouble-promotion -Wfloat-conversion +CFLAGS += -O3 -DNDEBUG +CFLAGS += $(INC) ifneq ($(FROZEN_MPY_DIR),) # To use frozen bytecode, put your .py files in a subdirectory (eg frozen/) and @@ -46,12 +38,12 @@ SRC_C = \ SRC_QSTR += $(SRC_C) -OBJ = -OBJ = $(PY_O) +OBJ += $(PY_O) OBJ += $(addprefix $(BUILD)/, $(SRC_LIB:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) -JSFLAGS = -O0 -s EXPORTED_FUNCTIONS="['_mp_js_init', '_mp_js_init_repl', '_mp_js_do_str', '_mp_js_process_char', '_mp_hal_get_interrupt_char', '_mp_sched_keyboard_interrupt']" -s EXTRA_EXPORTED_RUNTIME_METHODS="['ccall', 'cwrap']" -s "BINARYEN_TRAP_MODE='clamp'" --memory-init-file 0 --js-library library.js +JSFLAGS += -s ASYNCIFY +JSFLAGS += -s EXPORTED_FUNCTIONS="['_mp_js_init', '_mp_js_init_repl', '_mp_js_do_str', '_mp_js_process_char', '_mp_hal_get_interrupt_char', '_mp_sched_keyboard_interrupt']" -s EXTRA_EXPORTED_RUNTIME_METHODS="['ccall', 'cwrap']" -s --memory-init-file 0 --js-library library.js all: $(BUILD)/micropython.js @@ -65,6 +57,6 @@ min: $(BUILD)/micropython.js test: $(BUILD)/micropython.js $(TOP)/tests/run-tests.py $(eval DIRNAME=ports/$(notdir $(CURDIR))) - cd $(TOP)/tests && MICROPY_MICROPYTHON=../ports/javascript/node_run.sh ./run-tests.py + cd $(TOP)/tests && MICROPY_MICROPYTHON=../ports/javascript/node_run.sh ./run-tests.py -j1 include $(TOP)/py/mkrules.mk diff --git a/ports/javascript/README.md b/ports/javascript/README.md index ac268bb3bf..39808943b1 100644 --- a/ports/javascript/README.md +++ b/ports/javascript/README.md @@ -7,17 +7,7 @@ Dependencies ------------ Building micropython.js bears the same requirements as the standard MicroPython -ports with the addition of Emscripten (and uglify-js for the minified file). - -A standard installation of Emscripten should provide functional code, however -if memory errors are encountered it may be worthwhile to modify the tool. -`emscripten-fastcomp/lib/Target/JSBackend.cpp` may require the minor fix -found in JSBackend.patch. This patch attempts to address situations where -C code running through Emscripten is denied access to Javascript variables -leading to false-positives in the MicroPython garbage collector as variables -with pointers exclusively in Javascript will be erased prematurely. -Refer to Emscripten documentation for instructions on building Emscripten -from source. +ports with the addition of Emscripten (and uglify-js for the minified file). Build instructions ------------------ @@ -39,15 +29,15 @@ Access the repl with: Stack size may be modified using: - $ node build/micropython.js -X stack=64K + $ node build/micropython.js -X stack=64K Where stack size may be represented in Bytes, KiB or MiB. MicroPython scripts may be executed using: - $ node build/micropython.js hello.py + $ node build/micropython.js hello.py -Alternatively micropython.js may by accessed by other javascript programs in node +Alternatively micropython.js may by accessed by other javascript programs in node using the require command and the general API outlined below. For example: ```javascript @@ -85,7 +75,7 @@ demonstrates basic functionality: ``` MicroPython code execution will suspend the browser so be sure to atomize usage -within this environment. Unfortunately interrupts have not been implemented for the +within this environment. Unfortunately interrupts have not been implemented for the browser. Testing @@ -124,5 +114,5 @@ the repl. mp_js_process_char(char) ``` -Input character into MicroPython repl. `char` must be of type `number`. This +Input character into MicroPython repl. `char` must be of type `number`. This will execute MicroPython code when necessary. diff --git a/ports/javascript/main.c b/ports/javascript/main.c index 5d295f9295..c56f1a2cba 100644 --- a/ports/javascript/main.c +++ b/ports/javascript/main.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2013, 2014 Damien P. George and 2017, 2018 Rami Ali + * Copyright (c) 2013-2021 Damien P. George and 2017, 2018 Rami Ali * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -36,6 +36,7 @@ #include "py/mperrno.h" #include "lib/utils/pyexec.h" +#include "emscripten.h" #include "library.h" #if MICROPY_ENABLE_COMPILER @@ -70,8 +71,6 @@ int do_str(const char *src, mp_parse_input_kind_t input_kind) { } #endif -static char *stack_top; - int mp_js_do_str(const char *code) { return do_str(code, MP_PARSE_FILE_INPUT); } @@ -81,9 +80,6 @@ int mp_js_process_char(int c) { } void mp_js_init(int heap_size) { - int stack_dummy; - stack_top = (char *)&stack_dummy; - #if MICROPY_ENABLE_GC char *heap = (char *)malloc(heap_size * sizeof(char)); gc_init(heap, heap + heap_size); @@ -105,15 +101,14 @@ void mp_js_init_repl() { pyexec_event_repl_init(); } +STATIC void gc_scan_func(void *begin, void *end) { + gc_collect_root((void **)begin, (void **)end - (void **)begin + 1); +} + void gc_collect(void) { - // WARNING: This gc_collect implementation doesn't try to get root - // pointers from CPU registers, and thus may function incorrectly. - jmp_buf dummy; - if (setjmp(dummy) == 0) { - longjmp(dummy, 1); - } gc_collect_start(); - gc_collect_root((void *)stack_top, ((mp_uint_t)(void *)(&dummy + 1) - (mp_uint_t)stack_top) / sizeof(mp_uint_t)); + emscripten_scan_stack(gc_scan_func); + emscripten_scan_registers(gc_scan_func); gc_collect_end(); } From 2dc4f843bcadeeeeda3f2af81dd700733babe5eb Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 24 Jun 2021 10:09:57 +1000 Subject: [PATCH 026/264] github/workflows: Add workflow to build and test javascript port. Signed-off-by: Damien George --- .github/workflows/ports_javascript.yml | 24 ++++++++++++++++++++++++ tools/ci.sh | 18 ++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 .github/workflows/ports_javascript.yml diff --git a/.github/workflows/ports_javascript.yml b/.github/workflows/ports_javascript.yml new file mode 100644 index 0000000000..244dc966aa --- /dev/null +++ b/.github/workflows/ports_javascript.yml @@ -0,0 +1,24 @@ +name: javascript port + +on: + push: + pull_request: + paths: + - '.github/workflows/*.yml' + - 'tools/**' + - 'py/**' + - 'extmod/**' + - 'lib/**' + - 'ports/javascript/**' + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install packages + run: source tools/ci.sh && ci_javascript_setup + - name: Build + run: source tools/ci.sh && ci_javascript_build + - name: Run tests + run: source tools/ci.sh && ci_javascript_run_tests diff --git a/tools/ci.sh b/tools/ci.sh index 6d7c643f5d..a82eedd20d 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -138,6 +138,24 @@ function ci_esp8266_build { make ${MAKEOPTS} -C ports/esp8266 BOARD=GENERIC_1M } +######################################################################################## +# ports/javascript + +function ci_javascript_setup { + git clone https://github.com/emscripten-core/emsdk.git + (cd emsdk && ./emsdk install latest && ./emsdk activate latest) +} + +function ci_javascript_build { + source emsdk/emsdk_env.sh + make ${MAKEOPTS} -C ports/javascript +} + +function ci_javascript_run_tests { + # This port is very slow at running, so only run a few of the tests. + (cd tests && MICROPY_MICROPYTHON=../ports/javascript/node_run.sh ./run-tests.py -j1 basics/builtin_*.py) +} + ######################################################################################## # ports/mimxrt From 180c54d6cc4e744082f7f76f484646db47d25adb Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 25 Jun 2021 11:09:31 +1000 Subject: [PATCH 027/264] tests/extmod: Make uasyncio_heaplock test more deterministic. This helps the test pass on systems with an inaccurate sleep time. Signed-off-by: Damien George --- tests/extmod/uasyncio_heaplock.py | 6 +++--- tests/extmod/uasyncio_heaplock.py.exp | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/extmod/uasyncio_heaplock.py b/tests/extmod/uasyncio_heaplock.py index 771d3f0d97..3a92d36c9f 100644 --- a/tests/extmod/uasyncio_heaplock.py +++ b/tests/extmod/uasyncio_heaplock.py @@ -29,15 +29,15 @@ async def task(id, n, t): async def main(): - t1 = asyncio.create_task(task(1, 4, 10)) - t2 = asyncio.create_task(task(2, 4, 25)) + t1 = asyncio.create_task(task(1, 4, 20)) + t2 = asyncio.create_task(task(2, 2, 50)) micropython.heap_lock() print("start") await asyncio.sleep_ms(1) print("sleep") - await asyncio.sleep_ms(100) + await asyncio.sleep_ms(70) print("finish") micropython.heap_unlock() diff --git a/tests/extmod/uasyncio_heaplock.py.exp b/tests/extmod/uasyncio_heaplock.py.exp index a967cc3197..68c6366c6f 100644 --- a/tests/extmod/uasyncio_heaplock.py.exp +++ b/tests/extmod/uasyncio_heaplock.py.exp @@ -6,6 +6,4 @@ sleep 1 2 2 1 1 3 -2 2 -2 3 finish From 30422ca7c24f02ea34fdbc61466d5fb4baa0af8a Mon Sep 17 00:00:00 2001 From: Jonathan Hogg Date: Thu, 24 Jun 2021 15:34:16 +0100 Subject: [PATCH 028/264] esp32/esp32_rmt: Fix RMT looping in newer IDF versions. When looping, now disable the TX interrupt after calling rmt_write_items() function to handle change in IDF behaviour (since v4.1). Also check length of pulses to ensure it fits hardware limit. Fixes issue #7403. --- ports/esp32/esp32_rmt.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ports/esp32/esp32_rmt.c b/ports/esp32/esp32_rmt.c index 9619b1dd5b..8c8f0f2e06 100644 --- a/ports/esp32/esp32_rmt.c +++ b/ports/esp32/esp32_rmt.c @@ -218,6 +218,10 @@ STATIC mp_obj_t esp32_rmt_write_pulses(size_t n_args, const mp_obj_t *pos_args, mp_obj_get_array(pulses, &pulses_length, &pulses_ptr); mp_uint_t num_items = (pulses_length / 2) + (pulses_length % 2); + if (self->loop_en && num_items > 63) { + mp_raise_ValueError(MP_ERROR_TEXT("too many pulses for loop")); + } + if (num_items > self->num_items) { self->items = (rmt_item32_t *)m_realloc(self->items, num_items * sizeof(rmt_item32_t *)); self->num_items = num_items; @@ -241,12 +245,12 @@ STATIC mp_obj_t esp32_rmt_write_pulses(size_t n_args, const mp_obj_t *pos_args, check_esp_err(rmt_set_tx_loop_mode(self->channel_id, false)); } check_esp_err(rmt_wait_tx_done(self->channel_id, portMAX_DELAY)); - check_esp_err(rmt_set_tx_intr_en(self->channel_id, false)); } check_esp_err(rmt_write_items(self->channel_id, self->items, num_items, false /* non-blocking */)); if (self->loop_en) { + check_esp_err(rmt_set_tx_intr_en(self->channel_id, false)); check_esp_err(rmt_set_tx_loop_mode(self->channel_id, true)); } From c940597314e504e1e3d59bfb6f6e667c9708ba79 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 25 Jun 2021 12:01:46 +1000 Subject: [PATCH 029/264] github/workflows: Switch from Coveralls to Codecov. As discussed in #7455, Coveralls doesn't work properly anymore, it has many spurious errors with reduced coverage. Signed-off-by: Damien George --- .github/workflows/ports_unix.yml | 8 +++++--- README.md | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ports_unix.yml b/.github/workflows/ports_unix.yml index c6ddd53037..ffba437f16 100644 --- a/.github/workflows/ports_unix.yml +++ b/.github/workflows/ports_unix.yml @@ -70,10 +70,12 @@ jobs: mkdir -p coverage lcov --rc lcov_branch_coverage=1 --directory ports/unix/build-coverage --capture --output-file coverage/lcov.info.all lcov --remove coverage/lcov.info.all '*/lib/*' '*/ports/unix/*' '*/utils/*' --output-file coverage/lcov.info - - name: Send to coveralls - uses: coverallsapp/github-action@master + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v1 with: - github-token: ${{ secrets.GITHUB_TOKEN }} + files: coverage/lcov.info + fail_ci_if_error: true + verbose: true - name: Print failures if: failure() run: tests/run-tests.py --print-failures diff --git a/README.md b/README.md index f75590a5ba..d197924f0e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![CI badge](https://github.com/micropython/micropython/workflows/unix%20port/badge.svg)](https://github.com/micropython/micropython/actions?query=branch%3Amaster+event%3Apush) [![Coverage badge](https://coveralls.io/repos/micropython/micropython/badge.png?branch=master)](https://coveralls.io/r/micropython/micropython?branch=master) +[![CI badge](https://github.com/micropython/micropython/workflows/unix%20port/badge.svg)](https://github.com/micropython/micropython/actions?query=branch%3Amaster+event%3Apush) [![codecov](https://codecov.io/gh/micropython/micropython/branch/master/graph/badge.svg?token=I92PfD05sD)](https://codecov.io/gh/micropython/micropython) The MicroPython project ======================= From f3e4ed82a11243dfed0e0370ab0d77e947ff6d34 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 26 Jun 2021 12:16:31 +1000 Subject: [PATCH 030/264] github/workflows: Switch from lcov to gcov. Coverage calculated by Codecov has the same reliability/deterministic issues as Coveralls did, so the problem is likely to do with the output of lcov/gcov, rather than the analysis and display of the data. Switch from lcov to gcov for data generation to try and simplify this process of computing coverage. Signed-off-by: Damien George --- .github/workflows/ports_unix.yml | 8 +++----- tools/ci.sh | 1 - 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ports_unix.yml b/.github/workflows/ports_unix.yml index ffba437f16..673e6152fa 100644 --- a/.github/workflows/ports_unix.yml +++ b/.github/workflows/ports_unix.yml @@ -65,15 +65,13 @@ jobs: run: source tools/ci.sh && ci_native_mpy_modules_build - name: Test importing .mpy generated by mpy_ld.py run: source tools/ci.sh && ci_unix_coverage_run_native_mpy_tests - - name: Run lcov coverage analysis + - name: Run gcov coverage analysis run: | - mkdir -p coverage - lcov --rc lcov_branch_coverage=1 --directory ports/unix/build-coverage --capture --output-file coverage/lcov.info.all - lcov --remove coverage/lcov.info.all '*/lib/*' '*/ports/unix/*' '*/utils/*' --output-file coverage/lcov.info + (cd ports/unix && gcov -o build-coverage/py ../../py/*.c || true) + (cd ports/unix && gcov -o build-coverage/extmod ../../extmod/*.c || true) - name: Upload coverage to Codecov uses: codecov/codecov-action@v1 with: - files: coverage/lcov.info fail_ci_if_error: true verbose: true - name: Print failures diff --git a/tools/ci.sh b/tools/ci.sh index a82eedd20d..ec122129dd 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -394,7 +394,6 @@ function ci_unix_standard_run_perfbench { } function ci_unix_coverage_setup { - sudo apt-get install lcov sudo pip3 install setuptools sudo pip3 install pyelftools gcc --version From f45412793e78cd5013b83b8de5219183eb53eca5 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Wed, 9 Jun 2021 10:47:18 +0200 Subject: [PATCH 031/264] mimxrt/machine_spi: Add the SPI class to the machine module. This class supports SPI bus controller mode, with blocking transfers. SPI device numbers start at 0, to comply with the pinout of the Teensy boards. With the configured clock frequency the fastest baud rate is 33MHz. For messages longer 16 bytes DMA is used. The class uses the existing framework with extmod/machine_spi.c. Extended driver options: - drive=n with n being between 1 and 6 or PIN.POWER_1 to PIN.POWER_6. Since the pins used by the SPI are fixed, no Pin settings can be made. Thus the drive option is added allowing to control ringing and crosstalk on the connection. - gap_ns=nnnnn is the time between sent data items in a frame given in ns. Default is 2 clock cycles. --- ports/mimxrt/Makefile | 8 + .../boards/MIMXRT1010_EVK/mpconfigboard.h | 9 + .../boards/MIMXRT1020_EVK/mpconfigboard.h | 16 + .../boards/MIMXRT1050_EVK/mpconfigboard.h | 12 + .../boards/MIMXRT1060_EVK/mpconfigboard.h | 12 + .../boards/MIMXRT1064_EVK/mpconfigboard.h | 12 + ports/mimxrt/boards/TEENSY40/mpconfigboard.h | 18 + ports/mimxrt/boards/TEENSY41/mpconfigboard.h | 18 + ports/mimxrt/dma_channel.c | 51 +++ ports/mimxrt/dma_channel.h | 34 ++ ports/mimxrt/machine_spi.c | 334 ++++++++++++++++++ ports/mimxrt/modmachine.c | 1 + ports/mimxrt/modmachine.h | 1 + 13 files changed, 526 insertions(+) create mode 100644 ports/mimxrt/dma_channel.c create mode 100644 ports/mimxrt/dma_channel.h create mode 100644 ports/mimxrt/machine_spi.c diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index 3b530a9700..870248373e 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -116,10 +116,15 @@ SRC_HAL_IMX_C += \ $(MCU_DIR)/xip/fsl_flexspi_nor_boot.c \ $(MCU_DIR)/project_template/clock_config.c \ $(MCU_DIR)/drivers/fsl_adc.c \ + $(MCU_DIR)/drivers/fsl_cache.c \ $(MCU_DIR)/drivers/fsl_clock.c \ + $(MCU_DIR)/drivers/fsl_dmamux.c \ + $(MCU_DIR)/drivers/fsl_edma.c \ $(MCU_DIR)/drivers/fsl_gpio.c \ $(MCU_DIR)/drivers/fsl_gpt.c \ $(MCU_DIR)/drivers/fsl_common.c \ + $(MCU_DIR)/drivers/fsl_lpspi.c \ + $(MCU_DIR)/drivers/fsl_lpspi_edma.c \ $(MCU_DIR)/drivers/fsl_lpuart.c \ $(MCU_DIR)/drivers/fsl_flexram.c \ $(MCU_DIR)/drivers/fsl_flexspi.c \ @@ -134,11 +139,13 @@ SRC_C = \ ticks.c \ tusb_port.c \ board_init.c \ + dma_channel.c \ $(BOARD_DIR)/flash_config.c \ machine_adc.c \ machine_led.c \ machine_pin.c \ machine_rtc.c \ + machine_spi.c \ machine_timer.c \ machine_uart.c \ mimxrt_flash.c \ @@ -265,6 +272,7 @@ SRC_QSTR += \ machine_led.c \ machine_pin.c \ machine_rtc.c \ + machine_spi.c \ machine_timer.c \ machine_uart.c \ mimxrt_flash.c \ diff --git a/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.h index fdaeac7eed..3576706264 100644 --- a/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.h @@ -24,3 +24,12 @@ { 0 }, { 0 }, \ { IOMUXC_GPIO_08_LPUART3_TXD }, { IOMUXC_GPIO_AD_07_LPUART3_RXD }, \ { IOMUXC_GPIO_AD_02_LPUART4_TXD }, { IOMUXC_GPIO_AD_01_LPUART4_RXD }, + +#define MICROPY_HW_SPI_INDEX { 1 } + +#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 }, + +#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 8238762a0b..55254d58f6 100644 --- a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h @@ -32,3 +32,19 @@ { 0 }, { 0 }, \ { 0 }, { 0 }, \ { IOMUXC_GPIO_SD_B1_02_LPUART8_TX }, { IOMUXC_GPIO_SD_B1_03_LPUART8_RX }, + +#define MICROPY_HW_SPI_INDEX { 1, 3 } + +#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 }, \ + { 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 }, + +#define DMA_REQ_SRC_RX { 0, kDmaRequestMuxLPSPI1Rx, kDmaRequestMuxLPSPI2Rx, \ + kDmaRequestMuxLPSPI3Rx, kDmaRequestMuxLPSPI4Rx } + +#define DMA_REQ_SRC_TX { 0, kDmaRequestMuxLPSPI1Tx, kDmaRequestMuxLPSPI2Tx, \ + kDmaRequestMuxLPSPI3Tx, kDmaRequestMuxLPSPI4Tx } diff --git a/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h index 659c33263e..db08b1064d 100644 --- a/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h @@ -29,3 +29,15 @@ { IOMUXC_GPIO_AD_B0_02_LPUART6_TX }, { IOMUXC_GPIO_AD_B0_03_LPUART6_RX }, \ { 0 }, { 0 }, \ { IOMUXC_GPIO_AD_B1_10_LPUART8_TX }, { IOMUXC_GPIO_AD_B1_11_LPUART8_RX }, + +#define MICROPY_HW_SPI_INDEX { 1 } + +#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 }, + +#define DMA_REQ_SRC_RX { 0, kDmaRequestMuxLPSPI1Rx, kDmaRequestMuxLPSPI2Rx, \ + kDmaRequestMuxLPSPI3Rx, kDmaRequestMuxLPSPI4Rx } + +#define DMA_REQ_SRC_TX { 0, kDmaRequestMuxLPSPI1Tx, kDmaRequestMuxLPSPI2Tx, \ + kDmaRequestMuxLPSPI3Tx, kDmaRequestMuxLPSPI4Tx } diff --git a/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h index 4fcc697e26..451d53dafd 100644 --- a/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h @@ -29,3 +29,15 @@ { IOMUXC_GPIO_AD_B0_02_LPUART6_TX }, { IOMUXC_GPIO_AD_B0_03_LPUART6_RX }, \ { 0 }, { 0 }, \ { IOMUXC_GPIO_AD_B1_10_LPUART8_TX }, { IOMUXC_GPIO_AD_B1_11_LPUART8_RX }, + +#define MICROPY_HW_SPI_INDEX { 1 } + +#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 }, + +#define DMA_REQ_SRC_RX { 0, kDmaRequestMuxLPSPI1Rx, kDmaRequestMuxLPSPI2Rx, \ + kDmaRequestMuxLPSPI3Rx, kDmaRequestMuxLPSPI4Rx } + +#define DMA_REQ_SRC_TX { 0, kDmaRequestMuxLPSPI1Tx, kDmaRequestMuxLPSPI2Tx, \ + kDmaRequestMuxLPSPI3Tx, kDmaRequestMuxLPSPI4Tx } diff --git a/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h index 7549899512..e300efef32 100644 --- a/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h @@ -27,3 +27,15 @@ { IOMUXC_GPIO_AD_B0_02_LPUART6_TX }, { IOMUXC_GPIO_AD_B0_03_LPUART6_RX }, \ { 0 }, { 0 }, \ { IOMUXC_GPIO_AD_B1_10_LPUART8_TX }, { IOMUXC_GPIO_AD_B1_11_LPUART8_RX }, + +#define MICROPY_HW_SPI_INDEX { 1 } + +#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 }, + +#define DMA_REQ_SRC_RX { 0, kDmaRequestMuxLPSPI1Rx, kDmaRequestMuxLPSPI2Rx, \ + kDmaRequestMuxLPSPI3Rx, kDmaRequestMuxLPSPI4Rx } + +#define DMA_REQ_SRC_TX { 0, kDmaRequestMuxLPSPI1Tx, kDmaRequestMuxLPSPI2Tx, \ + kDmaRequestMuxLPSPI3Tx, kDmaRequestMuxLPSPI4Tx } diff --git a/ports/mimxrt/boards/TEENSY40/mpconfigboard.h b/ports/mimxrt/boards/TEENSY40/mpconfigboard.h index 316116b2b9..8b38d047f8 100644 --- a/ports/mimxrt/boards/TEENSY40/mpconfigboard.h +++ b/ports/mimxrt/boards/TEENSY40/mpconfigboard.h @@ -24,3 +24,21 @@ { IOMUXC_GPIO_AD_B0_02_LPUART6_TX }, { IOMUXC_GPIO_AD_B0_03_LPUART6_RX }, \ { IOMUXC_GPIO_EMC_31_LPUART7_TX }, { IOMUXC_GPIO_EMC_32_LPUART7_RX }, \ { IOMUXC_GPIO_AD_B1_10_LPUART8_TX }, { IOMUXC_GPIO_AD_B1_11_LPUART8_RX }, + +#define MICROPY_HW_SPI_INDEX { 4, 3} + +#define IOMUX_TABLE_SPI \ + { 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 }, \ + { IOMUXC_GPIO_B0_03_LPSPI4_SCK }, { IOMUXC_GPIO_B0_00_LPSPI4_PCS0 }, \ + { IOMUXC_GPIO_B0_02_LPSPI4_SDO }, { IOMUXC_GPIO_B0_01_LPSPI4_SDI }, + +#define DMA_REQ_SRC_RX { 0, kDmaRequestMuxLPSPI1Rx, kDmaRequestMuxLPSPI2Rx, \ + kDmaRequestMuxLPSPI3Rx, kDmaRequestMuxLPSPI4Rx } + +#define DMA_REQ_SRC_TX { 0, kDmaRequestMuxLPSPI1Tx, kDmaRequestMuxLPSPI2Tx, \ + kDmaRequestMuxLPSPI3Tx, kDmaRequestMuxLPSPI4Tx } diff --git a/ports/mimxrt/boards/TEENSY41/mpconfigboard.h b/ports/mimxrt/boards/TEENSY41/mpconfigboard.h index 4bbb41f309..455bf8d69c 100644 --- a/ports/mimxrt/boards/TEENSY41/mpconfigboard.h +++ b/ports/mimxrt/boards/TEENSY41/mpconfigboard.h @@ -24,3 +24,21 @@ { IOMUXC_GPIO_AD_B0_02_LPUART6_TX }, { IOMUXC_GPIO_AD_B0_03_LPUART6_RX }, \ { IOMUXC_GPIO_EMC_31_LPUART7_TX }, { IOMUXC_GPIO_EMC_32_LPUART7_RX }, \ { IOMUXC_GPIO_AD_B1_10_LPUART8_TX }, { IOMUXC_GPIO_AD_B1_11_LPUART8_RX }, + +#define MICROPY_HW_SPI_INDEX { 4, 3, 1 } + +#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 }, \ + { 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_B0_03_LPSPI4_SCK }, { IOMUXC_GPIO_B0_00_LPSPI4_PCS0 }, \ + { IOMUXC_GPIO_B0_02_LPSPI4_SDO }, { IOMUXC_GPIO_B0_01_LPSPI4_SDI }, + +#define DMA_REQ_SRC_RX { 0, kDmaRequestMuxLPSPI1Rx, kDmaRequestMuxLPSPI2Rx, \ + kDmaRequestMuxLPSPI3Rx, kDmaRequestMuxLPSPI4Rx } + +#define DMA_REQ_SRC_TX { 0, kDmaRequestMuxLPSPI1Tx, kDmaRequestMuxLPSPI2Tx, \ + kDmaRequestMuxLPSPI3Tx, kDmaRequestMuxLPSPI4Tx } diff --git a/ports/mimxrt/dma_channel.c b/ports/mimxrt/dma_channel.c new file mode 100644 index 0000000000..3dd043a66b --- /dev/null +++ b/ports/mimxrt/dma_channel.c @@ -0,0 +1,51 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 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 "dma_channel.h" + +// List of channel flags: true: channel used, false: channel available +static bool channel_list[32] = { true, true, true, true, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, 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++) { + if (channel_list[i] == false) { // Channel available + channel_list[i] = true; + return i; + } + } + return -1; +} + +// free_channel(n): Declare channel as free +void free_dma_channel(int n) { + if (n >= 0 && n <= ARRAY_SIZE(channel_list)) { + channel_list[n] = false; + } +} diff --git a/ports/mimxrt/dma_channel.h b/ports/mimxrt/dma_channel.h new file mode 100644 index 0000000000..3fe66a3dbb --- /dev/null +++ b/ports/mimxrt/dma_channel.h @@ -0,0 +1,34 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 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. + */ +#ifndef MICROPY_INCLUDED_MIMXRT_DMACHANNEL_H +#define MICROPY_INCLUDED_MIMXRT_DMACHANNEL_H + +#include "py/runtime.h" + +int allocate_dma_channel(void); +void free_dma_channel(int n); + +#endif // MICROPY_INCLUDED_MIMXRT_DMACHANNEL_H diff --git a/ports/mimxrt/machine_spi.c b/ports/mimxrt/machine_spi.c new file mode 100644 index 0000000000..41843b06d9 --- /dev/null +++ b/ports/mimxrt/machine_spi.c @@ -0,0 +1,334 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * Copyright (c) 2021 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 "py/runtime.h" +#include "py/mphal.h" +#include "py/mperrno.h" +#include "extmod/machine_spi.h" +#include "modmachine.h" +#include "dma_channel.h" + +#include "fsl_cache.h" +#include "fsl_dmamux.h" +#include "fsl_iomuxc.h" +#include "fsl_lpspi.h" +#include "fsl_lpspi_edma.h" + +#define DEFAULT_SPI_BAUDRATE (1000000) +#define DEFAULT_SPI_POLARITY (0) +#define DEFAULT_SPI_PHASE (0) +#define DEFAULT_SPI_BITS (8) +#define DEFAULT_SPI_FIRSTBIT (kLPSPI_MsbFirst) +#define DEFAULT_SPI_DRIVE (6) + +#define CLOCK_DIVIDER (1) + +#define MICROPY_HW_SPI_NUM MP_ARRAY_SIZE(spi_index_table) + +#define SCK (iomux_table[index]) +#define CS0 (iomux_table[index + 1]) +#define SDO (iomux_table[index + 2]) +#define SDI (iomux_table[index + 3]) + +typedef struct _machine_spi_obj_t { + mp_obj_base_t base; + uint8_t spi_id; + uint8_t mode; + uint8_t spi_hw_id; + bool transfer_busy; + LPSPI_Type *spi_inst; + lpspi_master_config_t *master_config; +} machine_spi_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; + +STATIC const uint8_t spi_index_table[] = MICROPY_HW_SPI_INDEX; +STATIC LPSPI_Type *spi_base_ptr_table[] = LPSPI_BASE_PTRS; +static const iomux_table_t iomux_table[] = { + IOMUX_TABLE_SPI +}; + +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; + + 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); + + 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); + + 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); + + 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); + + return true; + } else { + return false; + } +} + +STATIC void machine_spi_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + static const char *firstbit_str[] = {"MSB", "LSB"}; + machine_spi_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "SPI(%u, baudrate=%u, polarity=%u, phase=%u, bits=%u, firstbit=%s, gap_ns=%d)", + self->spi_id, self->master_config->baudRate, self->master_config->cpol, + self->master_config->cpha, self->master_config->bitsPerFrame, + firstbit_str[self->master_config->direction], self->master_config->betweenTransferDelayInNanoSec); +} + +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 }; + 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} }, + { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_SPI_POLARITY} }, + { MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_SPI_PHASE} }, + { MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_SPI_BITS} }, + { 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} }, + }; + + static bool clk_init = true; + + // Parse the arguments. + 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 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)) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("SPI(%d) doesn't exist"), spi_id); + } + + // Get peripheral object. + uint8_t spi_hw_id = spi_index_table[spi_id]; // the hw spi number 1..n + machine_spi_obj_t *self = m_new_obj(machine_spi_obj_t); + self->base.type = &machine_spi_type; + self->spi_id = spi_id; + self->spi_inst = spi_base_ptr_table[spi_hw_id]; + self->spi_hw_id = spi_hw_id; + + uint8_t drive = args[ARG_drive].u_int; + if (drive < 1 || drive > 7) { + drive = DEFAULT_SPI_DRIVE; + } + + if (clk_init) { + clk_init = false; + /*Set clock source for LPSPI*/ + 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 + + self->master_config = m_new_obj(lpspi_master_config_t); + LPSPI_MasterGetDefaultConfig(self->master_config); + // Initialise the SPI peripheral. + self->master_config->baudRate = args[ARG_baudrate].u_int; + self->master_config->betweenTransferDelayInNanoSec = 1000000000 / self->master_config->baudRate * 2; + self->master_config->cpol = args[ARG_polarity].u_int; + self->master_config->cpha = args[ARG_phase].u_int; + self->master_config->bitsPerFrame = args[ARG_bits].u_int; + self->master_config->direction = args[ARG_firstbit].u_int; + if (args[ARG_gap_ns].u_int != -1) { + self->master_config->betweenTransferDelayInNanoSec = args[ARG_gap_ns].u_int; + } + LPSPI_MasterInit(self->spi_inst, self->master_config, CLOCK_GetFreq(kCLOCK_Usb1PllPfd0Clk) / (CLOCK_DIVIDER + 1)); + + return MP_OBJ_FROM_PTR(self); +} + +STATIC void machine_spi_init(mp_obj_base_t *self_in, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit, ARG_gap_ns }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_baudrate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_firstbit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_gap_ns, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + }; + + // Parse the arguments. + machine_spi_obj_t *self = (machine_spi_obj_t *)self_in; + 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); + + // Reconfigure the baudrate if requested. + if (args[ARG_baudrate].u_int != -1) { + self->master_config->baudRate = args[ARG_baudrate].u_int; + self->master_config->betweenTransferDelayInNanoSec = 1000000000 / self->master_config->baudRate * 2; + } + // Reconfigure the format if requested. + if (args[ARG_polarity].u_int != -1) { + self->master_config->cpol = args[ARG_polarity].u_int; + } + if (args[ARG_phase].u_int != -1) { + self->master_config->cpha = args[ARG_phase].u_int; + } + if (args[ARG_bits].u_int != -1) { + self->master_config->bitsPerFrame = args[ARG_bits].u_int; + } + if (args[ARG_firstbit].u_int != -1) { + self->master_config->direction = args[ARG_firstbit].u_int; + } + if (args[ARG_gap_ns].u_int != -1) { + self->master_config->betweenTransferDelayInNanoSec = args[ARG_gap_ns].u_int; + } + LPSPI_Enable(self->spi_inst, false); // Disable first before new settings are applies + LPSPI_MasterInit(self->spi_inst, self->master_config, CLOCK_GetFreq(kCLOCK_Usb1PllPfd0Clk) / (CLOCK_DIVIDER + 1)); +} + +void LPSPI_EDMAMasterCallback(LPSPI_Type *base, lpspi_master_edma_handle_t *handle, status_t status, void *self_in) { + machine_spi_obj_t *self = (machine_spi_obj_t *)self_in; + self->transfer_busy = false; +} + +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 + + int chan_tx = -1; + int chan_rx = -1; + if (len >= dma_min_size_threshold) { + // Use two DMA channels to service the two FIFOs + chan_rx = allocate_dma_channel(); + chan_tx = allocate_dma_channel(); + } + bool use_dma = chan_rx >= 0 && chan_tx >= 0; + + if (use_dma) { + edma_config_t userConfig; + + /* DMA MUX init*/ + DMAMUX_Init(DMAMUX); + + DMAMUX_SetSource(DMAMUX, chan_rx, dma_req_src_rx[self->spi_hw_id]); // ## SPIn source + DMAMUX_EnableChannel(DMAMUX, chan_rx); + + 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); + + lpspi_master_edma_handle_t g_master_edma_handle; + edma_handle_t lpspiEdmaMasterRxRegToRxDataHandle; + edma_handle_t lpspiEdmaMasterTxDataToTxRegHandle; + + // Set up lpspi EDMA master + EDMA_CreateHandle(&(lpspiEdmaMasterRxRegToRxDataHandle), DMA0, chan_rx); + EDMA_CreateHandle(&(lpspiEdmaMasterTxDataToTxRegHandle), DMA0, chan_tx); + LPSPI_MasterTransferCreateHandleEDMA(self->spi_inst, &g_master_edma_handle, LPSPI_EDMAMasterCallback, self, + &lpspiEdmaMasterRxRegToRxDataHandle, + &lpspiEdmaMasterTxDataToTxRegHandle); + // Start master transfer + lpspi_transfer_t masterXfer; + masterXfer.txData = (uint8_t *)src; + masterXfer.rxData = (uint8_t *)dest; + masterXfer.dataSize = len; + masterXfer.configFlags = kLPSPI_MasterPcs0 | 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 + self->spi_inst->TCR = LPSPI_TCR_CPOL(self->master_config->cpol) | LPSPI_TCR_CPHA(self->master_config->cpha) | + LPSPI_TCR_LSBF(self->master_config->direction) | LPSPI_TCR_FRAMESZ(self->master_config->bitsPerFrame - 1) | + (self->spi_inst->TCR & LPSPI_TCR_PRESCALE_MASK) | LPSPI_TCR_PCS(self->master_config->whichPcs); + LPSPI_Enable(self->spi_inst, true); + + self->transfer_busy = true; + if (dest) { + L1CACHE_DisableDCache(); + } else if (src) { + DCACHE_CleanByRange((uint32_t)src, len); + } + LPSPI_MasterTransferEDMA(self->spi_inst, &g_master_edma_handle, &masterXfer); + + while (self->transfer_busy) { + MICROPY_EVENT_POLL_HOOK + } + L1CACHE_EnableDCache(); + } + // Release DMA channels, even if never allocated. + if (chan_rx >= 0) { + free_dma_channel(chan_rx); + } + if (chan_tx >= 0) { + free_dma_channel(chan_tx); + } + + if (!use_dma) { + // 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) | + LPSPI_TCR_LSBF(self->master_config->direction) | LPSPI_TCR_FRAMESZ(self->master_config->bitsPerFrame - 1) | + (self->spi_inst->TCR & LPSPI_TCR_PRESCALE_MASK) | LPSPI_TCR_PCS(self->master_config->whichPcs); + LPSPI_Enable(self->spi_inst, true); + + lpspi_transfer_t masterXfer; + masterXfer.txData = (uint8_t *)src; + masterXfer.rxData = (uint8_t *)dest; + masterXfer.dataSize = len; + masterXfer.configFlags = kLPSPI_MasterPcs0 | kLPSPI_MasterPcsContinuous | kLPSPI_MasterByteSwap; + + LPSPI_MasterTransferBlocking(self->spi_inst, &masterXfer); + } +} + +STATIC const mp_machine_spi_p_t machine_spi_p = { + .init = machine_spi_init, + .transfer = machine_spi_transfer, +}; + +const mp_obj_type_t machine_spi_type = { + { &mp_type_type }, + .name = MP_QSTR_SPI, + .print = machine_spi_print, + .make_new = machine_spi_make_new, + .protocol = &machine_spi_p, + .locals_dict = (mp_obj_dict_t *)&mp_machine_spi_locals_dict, +}; diff --git a/ports/mimxrt/modmachine.c b/ports/mimxrt/modmachine.c index 09aa250c5f..ed23510cff 100644 --- a/ports/mimxrt/modmachine.c +++ b/ports/mimxrt/modmachine.c @@ -65,6 +65,7 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_Signal), MP_ROM_PTR(&machine_signal_type) }, { 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_SPI), MP_ROM_PTR(&machine_spi_type) }, { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&machine_uart_type) }, }; STATIC MP_DEFINE_CONST_DICT(machine_module_globals, machine_module_globals_table); diff --git a/ports/mimxrt/modmachine.h b/ports/mimxrt/modmachine.h index 5ac8655764..eba8e6db8f 100644 --- a/ports/mimxrt/modmachine.h +++ b/ports/mimxrt/modmachine.h @@ -32,6 +32,7 @@ extern const mp_obj_type_t machine_adc_type; extern const mp_obj_type_t machine_timer_type; extern const mp_obj_type_t machine_rtc_type; +extern const mp_obj_type_t machine_spi_type; extern const mp_obj_type_t machine_uart_type; void machine_adc_init(void); From a262faa2274e4c6297e735afe2a6c944ce58fd70 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sun, 6 Jun 2021 08:11:20 +0200 Subject: [PATCH 032/264] mimxrt/moduos: Seed the PRNG on boot using the TRNG. --- ports/mimxrt/moduos.c | 27 ++++++++++++++++++++------- ports/mimxrt/mpconfigport.h | 3 +++ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/ports/mimxrt/moduos.c b/ports/mimxrt/moduos.c index fe914ce610..225cd4ddd2 100644 --- a/ports/mimxrt/moduos.c +++ b/ports/mimxrt/moduos.c @@ -61,21 +61,34 @@ STATIC mp_obj_t os_uname(void) { } STATIC MP_DEFINE_CONST_FUN_OBJ_0(os_uname_obj, os_uname); -STATIC mp_obj_t os_urandom(mp_obj_t num) { - mp_int_t n = mp_obj_get_int(num); - static bool initialized = false; - vstr_t vstr; - vstr_init_len(&vstr, n); +static bool initialized = false; + +STATIC void trng_start(void) { + trng_config_t trngConfig; if (!initialized) { - trng_config_t trngConfig; - TRNG_GetDefaultConfig(&trngConfig); trngConfig.sampleMode = kTRNG_SampleModeVonNeumann; TRNG_Init(TRNG, &trngConfig); initialized = true; } +} + +uint32_t trng_random_u32(void) { + uint32_t rngval; + + trng_start(); + TRNG_GetRandomData(TRNG, (uint8_t *)&rngval, 4); + return rngval; +} + +STATIC mp_obj_t os_urandom(mp_obj_t num) { + mp_int_t n = mp_obj_get_int(num); + vstr_t vstr; + vstr_init_len(&vstr, n); + + trng_start(); TRNG_GetRandomData(TRNG, vstr.buf, n); return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index 49953bb57e..8afdc8502c 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -30,6 +30,8 @@ #include "mpconfigboard.h" #include "fsl_common.h" +uint32_t trng_random_u32(void); + // Memory allocation policies #define MICROPY_GC_STACK_ENTRY_TYPE uint16_t #define MICROPY_GC_ALLOC_THRESHOLD (0) @@ -118,6 +120,7 @@ #define MICROPY_PY_UTIME_MP_HAL (1) #define MICROPY_PY_URANDOM (1) #define MICROPY_PY_URANDOM_EXTRA_FUNCS (1) +#define MICROPY_PY_URANDOM_SEED_INIT_FUNC (trng_random_u32()) #define MICROPY_PY_USELECT (1) #define MICROPY_PY_MACHINE (1) #define MICROPY_PY_MACHINE_PIN_MAKE_NEW mp_pin_make_new From b35566af799ccb76711d228f1314f7df53ee42bc Mon Sep 17 00:00:00 2001 From: robert-hh Date: Tue, 8 Jun 2021 13:34:53 +0200 Subject: [PATCH 033/264] mimxrt/boards: Set vfs partition start to 1 MBbyte. This avoids the irritation of the PJRC HalfKay loader on Teensy 4.0. Block 0 and 1 are properly erased and the additional formatting in the make script is not required anymore. --- ports/mimxrt/boards/MIMXRT1062.ld | 4 ++-- ports/mimxrt/boards/TEENSY40/mpconfigboard.mk | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/ports/mimxrt/boards/MIMXRT1062.ld b/ports/mimxrt/boards/MIMXRT1062.ld index 5ab7e15332..5edf70ee46 100644 --- a/ports/mimxrt/boards/MIMXRT1062.ld +++ b/ports/mimxrt/boards/MIMXRT1062.ld @@ -8,8 +8,8 @@ ivt_size = 0x00001000; interrupts_start = 0x60002000; interrupts_size = 0x00000400; text_start = 0x60002400; -text_size = ((((text_start) + 1M) + (4k - 1)) & ~(4k - 1)) - (text_start); /* reserve 1M for code but align on 4k boundary */ -vfs_start = (text_start) + (text_size); +vfs_start = 0x60100000; +text_size = ((vfs_start) - (text_start)); vfs_size = ((flash_end) - (vfs_start)); itcm_start = 0x00000000; itcm_size = 0x00020000; diff --git a/ports/mimxrt/boards/TEENSY40/mpconfigboard.mk b/ports/mimxrt/boards/TEENSY40/mpconfigboard.mk index 0f818af42c..beab16c086 100644 --- a/ports/mimxrt/boards/TEENSY40/mpconfigboard.mk +++ b/ports/mimxrt/boards/TEENSY40/mpconfigboard.mk @@ -5,5 +5,3 @@ MICROPY_FLOAT_IMPL = double deploy: $(BUILD)/firmware.hex teensy_loader_cli --mcu=imxrt1062 -v -w $< - sleep 1 - $(PYTHON) $(TOP)/tools/pyboard.py --device $(PORT) $(BOARD_DIR)/format.py From 4eabff53cb390f555758ac9c326f75e900dc5a11 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Wed, 16 Jun 2021 09:06:05 +0200 Subject: [PATCH 034/264] mimxrt/main: Skip running main.py if boot.py failed. See related #7379. --- ports/mimxrt/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ports/mimxrt/main.c b/ports/mimxrt/main.c index bd1c3e8884..d605c91555 100644 --- a/ports/mimxrt/main.c +++ b/ports/mimxrt/main.c @@ -70,7 +70,8 @@ int main(void) { if (ret & PYEXEC_FORCED_EXIT) { goto soft_reset_exit; } - if (pyexec_mode_kind == PYEXEC_MODE_FRIENDLY_REPL) { + // Do not execute main.py if boot.py failed + if (pyexec_mode_kind == PYEXEC_MODE_FRIENDLY_REPL && ret != 0) { ret = pyexec_file_if_exists("main.py"); if (ret & PYEXEC_FORCED_EXIT) { goto soft_reset_exit; From c8284c9b5045023803d3c0492954509e7b997aa5 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Fri, 18 Jun 2021 12:52:22 +0200 Subject: [PATCH 035/264] mimxrt/main: Extend the information returned by help(). --- ports/mimxrt/main.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/ports/mimxrt/main.c b/ports/mimxrt/main.c index d605c91555..c93e89c8ab 100644 --- a/ports/mimxrt/main.c +++ b/ports/mimxrt/main.c @@ -131,8 +131,23 @@ const char mimxrt_help_text[] = " machine.Pin(pin, m, [p]) -- get a pin and configure it for IO mode m, pull mode p\n" " methods: init(..), value([v]), high(), low())\n" "\n" - "Pin IO modes are: Pin.IN, Pin.OUT, Pin.OPEN_DRAIN\n" - "Pin pull modes are: Pin.PULL_UP, Pin.PULL_UP_47K, Pin.PULL_UP_22K, Pin.PULL_DOWN, Pin.PULL_HOLD\n" + " Pins are numbered board specific, either 0-n, or 'D0'-'Dn', or 'A0' - 'An',\n" + " according to the boards's pinout sheet.\n" + " Pin IO modes are: Pin.IN, Pin.OUT, Pin.OPEN_DRAIN\n" + " Pin pull modes are: Pin.PULL_UP, Pin.PULL_UP_47K, Pin.PULL_UP_22K, Pin.PULL_DOWN, Pin.PULL_HOLD\n" + " machine.ADC(pin) -- make an analog object from a pin\n" + " methods: read_u16()\n" + " machine.UART(id, baudrate=115200) -- create an UART object (id=1 - 8)\n" + " methods: init(), write(buf), any()\n" + " buf=read(n), readinto(buf), buf=readline()\n" + " The RX and TX pins are fixed and board-specific.\n" + " machine.SoftI2C() -- create an Soft I2C object\n" + " methods: readfrom(addr, buf, stop=True), writeto(addr, buf, stop=True)\n" + " readfrom_mem(addr, memaddr, arg), writeto_mem(addr, memaddr, arg)\n" + " machine.SoftSPI(baudrate=1000000) -- create an SPI object ()\n" + " methods: read(nbytes, write=0x00), write(buf), write_readinto(wr_buf, rd_buf)\n" + " machine.Timer(id, freq, callback) -- create a hardware timer object (id=0,1,2)\n" + " eg: machine.Timer(freq=1, callback=lambda t:print(t))\n" "\n" "Useful control commands:\n" " CTRL-C -- interrupt a running program\n" From 552e11bf64f4cc5da6601d74e754694ad6da07ea Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sat, 19 Jun 2021 07:55:03 +0200 Subject: [PATCH 036/264] mimxrt/mimxrt_flash: Remove commented-out code. --- ports/mimxrt/mimxrt_flash.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/ports/mimxrt/mimxrt_flash.c b/ports/mimxrt/mimxrt_flash.c index 6b89bfa532..bf054c87f4 100644 --- a/ports/mimxrt/mimxrt_flash.c +++ b/ports/mimxrt/mimxrt_flash.c @@ -127,13 +127,6 @@ STATIC mp_obj_t mimxrt_flash_readblocks(size_t n_args, const mp_obj_t *args) { mimxrt_flash_obj_t *self = MP_OBJ_TO_PTR(args[0]); mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_WRITE); - // if (n_args == 4) { - // mp_printf(MP_PYTHON_PRINTER, "readblocks: nargs = %d, block = %d, offset = %d, len = %d\n", - // n_args, mp_obj_get_int(args[1]), mp_obj_get_int(args[3]), bufinfo.len); - // } else { - // mp_printf(MP_PYTHON_PRINTER, "readblocks: nargs = %d, block = %d, len = %d\n", - // n_args, mp_obj_get_int(args[1]), bufinfo.len); - // } // Calculate read offset from block number. uint32_t offset = mp_obj_get_int(args[1]) * SECTOR_SIZE_BYTES; // Add optional offset From cbc9a591a437177ac978302c16b56dd6cb46104f Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sat, 19 Jun 2021 10:51:45 +0200 Subject: [PATCH 037/264] mimxrt/modmachine: Add a few minor methods to the machine module. Mostly for compatibility reasons: - idle() - disable_irq() - enable_irq() - time_pulse_us() --- ports/mimxrt/modmachine.c | 27 +++++++++++++++++++++++++++ ports/mimxrt/mpconfigport.h | 1 + 2 files changed, 28 insertions(+) diff --git a/ports/mimxrt/modmachine.c b/ports/mimxrt/modmachine.c index ed23510cff..e821f15070 100644 --- a/ports/mimxrt/modmachine.c +++ b/ports/mimxrt/modmachine.c @@ -28,6 +28,7 @@ #include "py/runtime.h" #include "extmod/machine_mem.h" #include "extmod/machine_i2c.h" +#include "extmod/machine_pulse.h" #include "extmod/machine_signal.h" #include "extmod/machine_spi.h" #include "led.h" @@ -48,6 +49,25 @@ STATIC mp_obj_t machine_freq(void) { } MP_DEFINE_CONST_FUN_OBJ_0(machine_freq_obj, machine_freq); +STATIC mp_obj_t machine_idle(void) { + MICROPY_EVENT_POLL_HOOK; + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_idle_obj, machine_idle); + +STATIC mp_obj_t machine_disable_irq(void) { + uint32_t state = MICROPY_BEGIN_ATOMIC_SECTION(); + return mp_obj_new_int(state); +} +MP_DEFINE_CONST_FUN_OBJ_0(machine_disable_irq_obj, machine_disable_irq); + +STATIC mp_obj_t machine_enable_irq(mp_obj_t state_in) { + uint32_t state = mp_obj_get_int(state_in); + MICROPY_END_ATOMIC_SECTION(state); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(machine_enable_irq_obj, machine_enable_irq); + STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_umachine) }, { MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&machine_reset_obj) }, @@ -67,6 +87,13 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_SoftSPI), MP_ROM_PTR(&mp_machine_soft_spi_type) }, { 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_idle), MP_ROM_PTR(&machine_idle_obj) }, + + { MP_ROM_QSTR(MP_QSTR_disable_irq), MP_ROM_PTR(&machine_disable_irq_obj) }, + { MP_ROM_QSTR(MP_QSTR_enable_irq), MP_ROM_PTR(&machine_enable_irq_obj) }, + + { MP_ROM_QSTR(MP_QSTR_time_pulse_us), MP_ROM_PTR(&machine_time_pulse_us_obj) }, }; STATIC MP_DEFINE_CONST_DICT(machine_module_globals, machine_module_globals_table); diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index 8afdc8502c..e1e102797a 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -124,6 +124,7 @@ uint32_t trng_random_u32(void); #define MICROPY_PY_USELECT (1) #define MICROPY_PY_MACHINE (1) #define MICROPY_PY_MACHINE_PIN_MAKE_NEW mp_pin_make_new +#define MICROPY_PY_MACHINE_PULSE (1) #define MICROPY_PY_MACHINE_I2C (1) #define MICROPY_PY_MACHINE_SPI (1) #define MICROPY_PY_FRAMEBUF (1) From 7ec95c2768793c28351d13a9c471991bb2d6dcff Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 25 Jun 2021 16:53:20 +1000 Subject: [PATCH 038/264] extmod/uasyncio: Get addr and bind server socket before creating task. Currently when using uasyncio.start_server() the socket configuration is done inside a uasyncio.create_task() background function. If the address and port are already in use however this throws an OSError which cannot be cleanly caught behind the create_task(). This commit moves the getaddrinfo and socket binding to the start_server() function, and only creates the task if that succeeds. This means that any OSError from the initial socket configuration is propagated directly up the call stack, compatible with CPython behaviour. See #7444. Signed-off-by: Damien George --- extmod/uasyncio/stream.py | 27 +++++++++------- tests/net_hosted/uasyncio_start_server.py | 31 +++++++++++++++++++ tests/net_hosted/uasyncio_start_server.py.exp | 5 +++ 3 files changed, 51 insertions(+), 12 deletions(-) create mode 100644 tests/net_hosted/uasyncio_start_server.py create mode 100644 tests/net_hosted/uasyncio_start_server.py.exp diff --git a/extmod/uasyncio/stream.py b/extmod/uasyncio/stream.py index 3a68881da3..8de2d2599f 100644 --- a/extmod/uasyncio/stream.py +++ b/extmod/uasyncio/stream.py @@ -107,15 +107,7 @@ class Server: async def wait_closed(self): await self.task - async def _serve(self, cb, host, port, backlog): - import usocket as socket - - ai = socket.getaddrinfo(host, port)[0] # TODO this is blocking! - s = socket.socket() - s.setblocking(False) - s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - s.bind(ai[-1]) - s.listen(backlog) + async def _serve(self, s, cb): # Accept incoming connections while True: try: @@ -137,9 +129,20 @@ class Server: # Helper function to start a TCP stream server, running as a new task # TODO could use an accept-callback on socket read activity instead of creating a task async def start_server(cb, host, port, backlog=5): - s = Server() - s.task = core.create_task(s._serve(cb, host, port, backlog)) - return s + import usocket as socket + + # Create and bind server socket. + host = socket.getaddrinfo(host, port)[0] # TODO this is blocking! + s = socket.socket() + s.setblocking(False) + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + s.bind(host[-1]) + s.listen(backlog) + + # Create and return server object and task. + srv = Server() + srv.task = core.create_task(srv._serve(s, cb)) + return srv ################################################################################ diff --git a/tests/net_hosted/uasyncio_start_server.py b/tests/net_hosted/uasyncio_start_server.py new file mode 100644 index 0000000000..f13402979f --- /dev/null +++ b/tests/net_hosted/uasyncio_start_server.py @@ -0,0 +1,31 @@ +# Test basic behaviour of uasyncio.start_server() + +try: + import uasyncio as asyncio +except ImportError: + try: + import asyncio + except ImportError: + print("SKIP") + raise SystemExit + + +async def test(): + # Test creating 2 servers using the same address + print("create server1") + server1 = await asyncio.start_server(None, "0.0.0.0", 8000) + try: + print("create server2") + await asyncio.start_server(None, "0.0.0.0", 8000) + except OSError as er: + print("OSError") + + # Wait for server to close. + async with server1: + print("sleep") + await asyncio.sleep(0) + + print("done") + + +asyncio.run(test()) diff --git a/tests/net_hosted/uasyncio_start_server.py.exp b/tests/net_hosted/uasyncio_start_server.py.exp new file mode 100644 index 0000000000..0fb8e6a63b --- /dev/null +++ b/tests/net_hosted/uasyncio_start_server.py.exp @@ -0,0 +1,5 @@ +create server1 +create server2 +OSError +sleep +done From 4ada56d4cb4c518d2a78f7d7a600a4537188310a Mon Sep 17 00:00:00 2001 From: Yonatan Goldschmidt Date: Thu, 12 Dec 2019 00:09:20 +0200 Subject: [PATCH 039/264] tools/makemanifest.py: Allow passing flags to mpy-tool.py. --- py/mkrules.mk | 2 +- tools/makemanifest.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/py/mkrules.mk b/py/mkrules.mk index b8d07ef56d..038a49329b 100644 --- a/py/mkrules.mk +++ b/py/mkrules.mk @@ -139,7 +139,7 @@ $(HEADER_BUILD): ifneq ($(FROZEN_MANIFEST),) # to build frozen_content.c from a manifest $(BUILD)/frozen_content.c: FORCE $(BUILD)/genhdr/qstrdefs.generated.h - $(Q)$(MAKE_MANIFEST) -o $@ -v "MPY_DIR=$(TOP)" -v "MPY_LIB_DIR=$(MPY_LIB_DIR)" -v "PORT_DIR=$(shell pwd)" -v "BOARD_DIR=$(BOARD_DIR)" -b "$(BUILD)" $(if $(MPY_CROSS_FLAGS),-f"$(MPY_CROSS_FLAGS)",) $(FROZEN_MANIFEST) + $(Q)$(MAKE_MANIFEST) -o $@ -v "MPY_DIR=$(TOP)" -v "MPY_LIB_DIR=$(MPY_LIB_DIR)" -v "PORT_DIR=$(shell pwd)" -v "BOARD_DIR=$(BOARD_DIR)" -b "$(BUILD)" $(if $(MPY_CROSS_FLAGS),-f"$(MPY_CROSS_FLAGS)",) --mpy-tool-flags="$(MPY_TOOL_FLAGS)" $(FROZEN_MANIFEST) ifneq ($(FROZEN_DIR),) $(error FROZEN_DIR cannot be used in conjunction with FROZEN_MANIFEST) diff --git a/tools/makemanifest.py b/tools/makemanifest.py index 51de01dd8a..7897a83c6e 100644 --- a/tools/makemanifest.py +++ b/tools/makemanifest.py @@ -248,6 +248,7 @@ def main(): "-f", "--mpy-cross-flags", default="", help="flags to pass to mpy-cross" ) cmd_parser.add_argument("-v", "--var", action="append", help="variables to substitute") + cmd_parser.add_argument("--mpy-tool-flags", default="", help="flags to pass to mpy-tool") cmd_parser.add_argument("files", nargs="+", help="input manifest list") args = cmd_parser.parse_args() @@ -341,6 +342,7 @@ def main(): "-q", args.build_dir + "/genhdr/qstrdefs.preprocessed.h", ] + + args.mpy_tool_flags.split() + mpy_files ) if res != 0: From 87985fc7e98454f16e8e054d846fa871688a2ef5 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 11 Jun 2021 10:47:51 +1000 Subject: [PATCH 040/264] stm32/boardctrl: Skip running main.py if boot.py had an error. Previous behaviour was: if boot.py had an exception then main.py would still run, which is arguably unexpected behaviour. This commit changes the behaviour so main.py is not run if boot.py has an error. Signed-off-by: Damien George --- ports/stm32/boardctrl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ports/stm32/boardctrl.c b/ports/stm32/boardctrl.c index f1b7a4e81c..fa71d8e7f4 100644 --- a/ports/stm32/boardctrl.c +++ b/ports/stm32/boardctrl.c @@ -155,6 +155,8 @@ int boardctrl_run_boot_py(boardctrl_state_t *state) { return BOARDCTRL_GOTO_SOFT_RESET_EXIT; } if (!ret) { + // There was an error, prevent main.py from running and flash LEDs. + state->reset_mode = BOARDCTRL_RESET_MODE_SAFE_MODE; flash_error(4); } } From f4dce15c87c9ff6c51d8a9a7a80347465468b7af Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sat, 19 Jun 2021 15:30:46 +0200 Subject: [PATCH 041/264] stm32/sdio: Fix undefined reference to DMA stream on H7. Follow up to a96afae90f6e5d693173382561d06e583b0b5fa5 --- ports/stm32/sdio.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ports/stm32/sdio.c b/ports/stm32/sdio.c index 0291e3e81d..3fb84a4d81 100644 --- a/ports/stm32/sdio.c +++ b/ports/stm32/sdio.c @@ -451,9 +451,11 @@ int sdio_transfer_cmd53(bool write, uint32_t block_size, uint32_t arg, size_t le #else printf("sdio_transfer_cmd53: timeout wr=%d len=%u dma=%u buf_idx=%u STA=%08x SDMMC=%08x:%08x IDMA=%08x\n", write, (uint)len, (uint)dma, sdmmc_buf_cur - buf, (uint)SDMMC->STA, (uint)SDMMC->DCOUNT, (uint)SDMMC->DCTRL, (uint)SDMMC->IDMACTRL); #endif + #if defined(STM32F7) if (sdmmc_dma) { dma_nohal_deinit(&dma_SDIO_0); } + #endif return -MP_ETIMEDOUT; } } @@ -466,9 +468,11 @@ int sdio_transfer_cmd53(bool write, uint32_t block_size, uint32_t arg, size_t le #else printf("sdio_transfer_cmd53: error=%08lx wr=%d len=%u dma=%u buf_idx=%u STA=%08x SDMMC=%08x:%08x IDMA=%08x\n", sdmmc_error, write, (uint)len, (uint)dma, sdmmc_buf_cur - buf, (uint)SDMMC->STA, (uint)SDMMC->DCOUNT, (uint)SDMMC->DCTRL, (uint)SDMMC->IDMACTRL); #endif + #if defined(STM32F7) if (sdmmc_dma) { dma_nohal_deinit(&dma_SDIO_0); } + #endif return -(0x1000000 | sdmmc_error); } @@ -478,7 +482,9 @@ int sdio_transfer_cmd53(bool write, uint32_t block_size, uint32_t arg, size_t le return -MP_EIO; } } else { + #if defined(STM32F7) dma_nohal_deinit(&dma_SDIO_0); + #endif } return 0; From a32a7421d668c355629f28275eb756068633c41a Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sat, 19 Jun 2021 19:56:49 +0200 Subject: [PATCH 042/264] stm32/dma: Add DMAMUX configuration for H7 to fix dma_nohal_init. Fixes issue #5619. --- ports/stm32/dma.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/ports/stm32/dma.c b/ports/stm32/dma.c index d4b41771b0..de30886917 100644 --- a/ports/stm32/dma.c +++ b/ports/stm32/dma.c @@ -1120,6 +1120,36 @@ void dma_nohal_init(const dma_descr_t *descr, uint32_t config) { init->FIFOMode // DMDIS | init->FIFOThreshold // FTH ; + + #if defined(STM32H7) + // Configure DMAMUX + uint32_t request = descr->sub_instance & DMAMUX_CxCR_DMAREQ_ID; + if ((dma >= DMA1_Stream0 && dma <= DMA1_Stream7) || (dma >= DMA2_Stream0 && dma <= DMA2_Stream7)) { + // DMA1/2 channels 0 to 7 are hardwired to DMAMUX1 channels 0-15. + uint32_t stream = (((uint32_t)dma & 0xff) - 16) / 24 + ((dma >= DMA2_Stream0) ? 8 : 0); + // Set DMAMUX channel peripheral request. + ((DMAMUX_Channel_TypeDef *)(((uint32_t)DMAMUX1_Channel0) + (stream * 4)))->CCR = request; + DMAMUX1_ChannelStatus->CFR = 1 << (stream & 0x1F); + // Configure DMAMUX request generator if needed. + if (request >= DMA_REQUEST_GENERATOR0 && request <= DMA_REQUEST_GENERATOR7) { + ((DMAMUX_RequestGen_TypeDef *)(((uint32_t)DMAMUX1_RequestGenerator0) + ((request - 1) * 4)))->RGCR = 0; + DMAMUX1_RequestGenStatus->RGCFR = 1 << (request - 1); + } + #if ENABLE_BDMA // TODO: BDMA is Not currently supported by this driver. + } else if (dma >= BDMA_Channel0 && dma <= BDMA_Channel7) { + // BDMA channels 0 to 7 are hardwired to DMAMUX2 channels 0-7. + uint32_t stream = (((uint32_t)dma & 0xff) - 8) / 20; + // Set DMAMUX channel peripheral request. + ((DMAMUX_Channel_TypeDef *)(((uint32_t)DMAMUX2_Channel0) + (stream * 4)))->CCR = request; + DMAMUX2_ChannelStatus->CFR = 1 << (stream & 0x1F); + // Configure DMAMUX request generator if needed. + if (request >= DMA_REQUEST_GENERATOR0 && request <= DMA_REQUEST_GENERATOR7) { + ((DMAMUX_RequestGen_TypeDef *)(((uint32_t)DMAMUX2_RequestGenerator0) + ((request - 1) * 4)))->RGCR = 0; + DMAMUX2_RequestGenStatus->RGCFR = 1 << (request - 1); + } + #endif // ENABLE_BDMA + } + #endif // STM32H7 } void dma_nohal_deinit(const dma_descr_t *descr) { From 8182f34584d577da4ff8e26acbe677765cfbca23 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 28 Jun 2021 12:03:01 -0500 Subject: [PATCH 043/264] stm32/main: Call mp_deinit() at end of main. This adds a call to mp_deinit() in the main function of the STM32 port. This enables the use of MICROPY_PORT_DEINIT_FUNC on that port, as well as cleaning up the GIL if threading is enabled. --- ports/stm32/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/stm32/main.c b/ports/stm32/main.c index c8de2fd868..6df7374cce 100644 --- a/ports/stm32/main.c +++ b/ports/stm32/main.c @@ -662,6 +662,7 @@ soft_reset_exit: MICROPY_BOARD_END_SOFT_RESET(&state); gc_sweep_all(); + mp_deinit(); goto soft_reset; } From efa97beb992049978f0e7be6e4e2f01249054362 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Wed, 23 Jun 2021 21:18:45 +0200 Subject: [PATCH 044/264] tools/autobuild: Add mimxrt port to build scripts for nightly builds. The firmware for Teensy 4.0, Teensy 4.1 and MIMXRT1020_EVK are created. Users of other MIMXRT10xx_EVK boards should be able to build the firmware themselves, they might need specific DEBUG settings. The Makefile had to be changed in order to build the .bin file as well. --- ports/mimxrt/Makefile | 2 +- tools/autobuild/autobuild.sh | 2 ++ tools/autobuild/build-mimxrt-latest.sh | 36 ++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) create mode 100755 tools/autobuild/build-mimxrt-latest.sh diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index 870248373e..fdb1325fdd 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -297,7 +297,7 @@ OBJ += $(BUILD)/pins_gen.o # Workaround for bug in older gcc, warning on "static usbd_device_t _usbd_dev = { 0 };" $(BUILD)/lib/tinyusb/src/device/usbd.o: CFLAGS += -Wno-missing-braces -all: $(BUILD)/firmware.hex +all: $(BUILD)/firmware.hex $(BUILD)/firmware.bin $(BUILD)/firmware.elf: $(OBJ) $(ECHO) "LINK $@" diff --git a/tools/autobuild/autobuild.sh b/tools/autobuild/autobuild.sh index 45822f2b7d..0498443811 100755 --- a/tools/autobuild/autobuild.sh +++ b/tools/autobuild/autobuild.sh @@ -66,6 +66,8 @@ cd ../esp32 ${AUTODIR}/build-esp32-latest.sh ${IDF_PATH_V4} ${FW_TAG} ${LOCAL_FIRMWARE} cd ../rp2 ${AUTODIR}/build-rp2-latest.sh ${FW_TAG} ${LOCAL_FIRMWARE} +cd ../mimxrt +${AUTODIR}/build-mimxrt-latest.sh ${FW_TAG} ${LOCAL_FIRMWARE} popd diff --git a/tools/autobuild/build-mimxrt-latest.sh b/tools/autobuild/build-mimxrt-latest.sh new file mode 100755 index 0000000000..e36b294aca --- /dev/null +++ b/tools/autobuild/build-mimxrt-latest.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# function for building firmware +function do_build() { + descr=$1 + board=$2 + ext=$3 + shift + shift + shift + echo "building $descr $board" + build_dir=/tmp/mimxrt-build-$board + $MICROPY_AUTOBUILD_MAKE $@ BOARD=$board BUILD=$build_dir || exit 1 + mv $build_dir/firmware.$ext $dest_dir/$descr$fw_tag.$ext + rm -rf $build_dir +} + +# check/get parameters +if [ $# != 2 ]; then + echo "usage: $0 " + exit 1 +fi + +fw_tag=$1 +dest_dir=$2 + +# check we are in the correct directory +if [ ! -r modmimxrt.c ]; then + echo "must be in mimxrt directory" + exit 1 +fi + +# build the boards +do_build TEENSY40 TEENSY40 hex +do_build TEENSY41 TEENSY41 hex +do_build MIMXRT1020_EVK MIMXRT1020_EVK bin From 41adf178309759d5965c15972f04987a2635314c Mon Sep 17 00:00:00 2001 From: Frank Pilhofer Date: Fri, 25 Jun 2021 01:51:27 -0700 Subject: [PATCH 045/264] tools/pyboard.py: Add cmd-line option to make soft reset configurable. Leaves the default as-is, but allows using --no-soft-reset to disable the soft reset when connecting. --- tools/pyboard.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/pyboard.py b/tools/pyboard.py index 7d06aa847d..e460c69e24 100755 --- a/tools/pyboard.py +++ b/tools/pyboard.py @@ -656,6 +656,12 @@ def main(): help="seconds to wait for USB connected board to become available", ) group = cmd_parser.add_mutually_exclusive_group() + group.add_argument( + "--soft-reset", + default=True, + action=argparse.BooleanOptionalAction, + help="Whether to perform a soft reset when connecting to the board.", + ) group.add_argument( "--follow", action="store_true", @@ -695,7 +701,7 @@ def main(): # we must enter raw-REPL mode to execute commands # this will do a soft-reset of the board try: - pyb.enter_raw_repl() + pyb.enter_raw_repl(args.soft_reset) except PyboardError as er: print(er) pyb.close() From 58e4d723383dfd9856520728b0920ff20fa76407 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Wed, 8 Apr 2020 12:17:28 -0500 Subject: [PATCH 046/264] py/objexcept: Pretty print OSError also when it has 2 arguments. This extends pretty-printing of OSError's to handle two arguments when the exception name is known. Signed-off-by: David Lechner --- py/objexcept.c | 25 ++++++++++++++++--------- tests/basics/errno1.py | 4 ++++ tests/basics/errno1.py.exp | 2 ++ 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/py/objexcept.c b/py/objexcept.c index f03bb1b416..a10bd2c187 100644 --- a/py/objexcept.c +++ b/py/objexcept.c @@ -163,17 +163,24 @@ void mp_obj_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kin if (o->args == NULL || o->args->len == 0) { mp_print_str(print, ""); return; - } else if (o->args->len == 1) { - #if MICROPY_PY_UERRNO - // try to provide a nice OSError error message - if (o->base.type == &mp_type_OSError && mp_obj_is_small_int(o->args->items[0])) { - qstr qst = mp_errno_to_str(o->args->items[0]); - if (qst != MP_QSTRnull) { - mp_printf(print, "[Errno " INT_FMT "] %q", MP_OBJ_SMALL_INT_VALUE(o->args->items[0]), qst); - return; + } + + #if MICROPY_PY_UERRNO + // try to provide a nice OSError error message + if (o->base.type == &mp_type_OSError && o->args->len > 0 && o->args->len < 3 && mp_obj_is_small_int(o->args->items[0])) { + qstr qst = mp_errno_to_str(o->args->items[0]); + if (qst != MP_QSTRnull) { + mp_printf(print, "[Errno " INT_FMT "] %q", MP_OBJ_SMALL_INT_VALUE(o->args->items[0]), qst); + if (o->args->len > 1) { + mp_print_str(print, ": "); + mp_obj_print_helper(print, o->args->items[1], PRINT_STR); } + return; } - #endif + } + #endif + + if (o->args->len == 1) { mp_obj_print_helper(print, o->args->items[0], PRINT_STR); return; } diff --git a/tests/basics/errno1.py b/tests/basics/errno1.py index d7a5ccd542..d9a895a972 100644 --- a/tests/basics/errno1.py +++ b/tests/basics/errno1.py @@ -12,6 +12,10 @@ print(type(uerrno.EIO)) # check that errors are rendered in a nice way msg = str(OSError(uerrno.EIO)) print(msg[:7], msg[-5:]) +msg = str(OSError(uerrno.EIO, "details")) +print(msg[:7], msg[-14:]) +msg = str(OSError(uerrno.EIO, "details", "more details")) +print(msg[:1], msg[-28:]) # check that unknown errno is still rendered print(str(OSError(9999))) diff --git a/tests/basics/errno1.py.exp b/tests/basics/errno1.py.exp index 7dd22757dd..58605b4767 100644 --- a/tests/basics/errno1.py.exp +++ b/tests/basics/errno1.py.exp @@ -1,4 +1,6 @@ [Errno ] EIO +[Errno ] EIO: details +( , 'details', 'more details') 9999 uerrno From 18e48a71ee69557a5340c8652f2e73e586063be3 Mon Sep 17 00:00:00 2001 From: Jonathan Hogg Date: Wed, 30 Jun 2021 19:00:50 +0100 Subject: [PATCH 047/264] esp32/esp32_rmt: Enhance RMT with idle_level and write_pulses modes. This change allows specification of the idle level and TX carrier output level (through changed initialisation API), and more flexible specification of pulses for write_pulses. This is a breaking change for the esp32.RMT constructor API. Previous code of this form: esp32.RMT(..., carrier_duty_percent=D, carrier_freq=F) will now raise an exception and should be changed to: esp32.RMT(..., tx_carrier=(F, D, 1)) --- ports/esp32/esp32_rmt.c | 143 +++++++++++++++++++++++----------------- 1 file changed, 82 insertions(+), 61 deletions(-) diff --git a/ports/esp32/esp32_rmt.c b/ports/esp32/esp32_rmt.c index 8c8f0f2e06..37ecef3248 100644 --- a/ports/esp32/esp32_rmt.c +++ b/ports/esp32/esp32_rmt.c @@ -52,8 +52,6 @@ typedef struct _esp32_rmt_obj_t { uint8_t channel_id; gpio_num_t pin; uint8_t clock_div; - uint16_t carrier_duty_percent; - uint32_t carrier_freq; mp_uint_t num_items; rmt_item32_t *items; bool loop_en; @@ -64,24 +62,16 @@ STATIC mp_obj_t esp32_rmt_make_new(const mp_obj_type_t *type, size_t n_args, siz { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_pin, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, { MP_QSTR_clock_div, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} }, // 100ns resolution - { MP_QSTR_carrier_duty_percent, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 50} }, - { MP_QSTR_carrier_freq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_idle_level, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, // low voltage + { MP_QSTR_tx_carrier, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, // no carrier }; 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); mp_uint_t channel_id = args[0].u_int; gpio_num_t pin_id = machine_pin_get_id(args[1].u_obj); mp_uint_t clock_div = args[2].u_int; - - bool carrier_en = false; - mp_uint_t carrier_duty_percent = 0; - mp_uint_t carrier_freq = 0; - - if (args[4].u_int > 0) { - carrier_en = true; - carrier_duty_percent = args[3].u_int; - carrier_freq = args[4].u_int; - } + mp_uint_t idle_level = args[3].u_bool; + mp_obj_t tx_carrier_obj = args[4].u_obj; if (clock_div < 1 || clock_div > 255) { mp_raise_ValueError(MP_ERROR_TEXT("clock_div must be between 1 and 255")); @@ -92,8 +82,6 @@ STATIC mp_obj_t esp32_rmt_make_new(const mp_obj_type_t *type, size_t n_args, siz self->channel_id = channel_id; self->pin = pin_id; self->clock_div = clock_div; - self->carrier_duty_percent = carrier_duty_percent; - self->carrier_freq = carrier_freq; self->loop_en = false; rmt_config_t config = {0}; @@ -103,12 +91,30 @@ STATIC mp_obj_t esp32_rmt_make_new(const mp_obj_type_t *type, size_t n_args, siz config.mem_block_num = 1; config.tx_config.loop_en = 0; - config.tx_config.carrier_en = carrier_en; + if (tx_carrier_obj != mp_const_none) { + mp_obj_t *tx_carrier_details = NULL; + mp_obj_get_array_fixed_n(tx_carrier_obj, 3, &tx_carrier_details); + mp_uint_t frequency = mp_obj_get_int(tx_carrier_details[0]); + mp_uint_t duty = mp_obj_get_int(tx_carrier_details[1]); + mp_uint_t level = mp_obj_is_true(tx_carrier_details[2]); + + if (frequency == 0) { + mp_raise_ValueError(MP_ERROR_TEXT("tx_carrier frequency must be >0")); + } + if (duty > 100) { + mp_raise_ValueError(MP_ERROR_TEXT("tx_carrier duty must be 0..100")); + } + + config.tx_config.carrier_en = 1; + config.tx_config.carrier_freq_hz = frequency; + config.tx_config.carrier_duty_percent = duty; + config.tx_config.carrier_level = level; + } else { + config.tx_config.carrier_en = 0; + } + config.tx_config.idle_output_en = 1; - config.tx_config.idle_level = 0; - config.tx_config.carrier_duty_percent = self->carrier_duty_percent; - config.tx_config.carrier_freq_hz = self->carrier_freq; - config.tx_config.carrier_level = 1; + config.tx_config.idle_level = idle_level; config.clk_div = self->clock_div; @@ -121,14 +127,11 @@ STATIC mp_obj_t esp32_rmt_make_new(const mp_obj_type_t *type, size_t n_args, siz STATIC void esp32_rmt_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { esp32_rmt_obj_t *self = MP_OBJ_TO_PTR(self_in); if (self->pin != -1) { - mp_printf(print, "RMT(channel=%u, pin=%u, source_freq=%u, clock_div=%u", - self->channel_id, self->pin, APB_CLK_FREQ, self->clock_div); - if (self->carrier_freq > 0) { - mp_printf(print, ", carrier_freq=%u, carrier_duty_percent=%u)", - self->carrier_freq, self->carrier_duty_percent); - } else { - mp_printf(print, ")"); - } + bool idle_output_en; + rmt_idle_level_t idle_level; + check_esp_err(rmt_get_idle_level(self->channel_id, &idle_output_en, &idle_level)); + mp_printf(print, "RMT(channel=%u, pin=%u, source_freq=%u, clock_div=%u, idle_level=%u)", + self->channel_id, self->pin, APB_CLK_FREQ, self->clock_div, idle_level); } else { mp_printf(print, "RMT()"); } @@ -162,7 +165,7 @@ STATIC mp_obj_t esp32_rmt_clock_div(mp_obj_t self_in) { STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp32_rmt_clock_div_obj, esp32_rmt_clock_div); // Query whether the channel has finished sending pulses. Takes an optional -// timeout (in ticks of the 80MHz clock), returning true if the pulse stream has +// timeout (in milliseconds), returning true if the pulse stream has // completed or false if they are still transmitting (or timeout is reached). STATIC mp_obj_t esp32_rmt_wait_done(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { @@ -175,7 +178,7 @@ STATIC mp_obj_t esp32_rmt_wait_done(size_t n_args, const mp_obj_t *pos_args, mp_ esp32_rmt_obj_t *self = MP_OBJ_TO_PTR(args[0].u_obj); - esp_err_t err = rmt_wait_tx_done(self->channel_id, args[1].u_int); + esp_err_t err = rmt_wait_tx_done(self->channel_id, args[1].u_int / portTICK_PERIOD_MS); return err == ESP_OK ? mp_const_true : mp_const_false; } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(esp32_rmt_wait_done_obj, 1, esp32_rmt_wait_done); @@ -195,45 +198,63 @@ STATIC mp_obj_t esp32_rmt_loop(mp_obj_t self_in, mp_obj_t loop) { } STATIC MP_DEFINE_CONST_FUN_OBJ_2(esp32_rmt_loop_obj, esp32_rmt_loop); -STATIC mp_obj_t esp32_rmt_write_pulses(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - static const mp_arg_t allowed_args[] = { - { MP_QSTR_self, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = mp_const_none} }, - { MP_QSTR_pulses, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = mp_const_none} }, - { MP_QSTR_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} }, - }; +STATIC mp_obj_t esp32_rmt_write_pulses(size_t n_args, const mp_obj_t *args) { + esp32_rmt_obj_t *self = MP_OBJ_TO_PTR(args[0]); + mp_obj_t duration_obj = args[1]; + mp_obj_t data_obj = n_args > 2 ? args[2] : mp_const_true; - 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_uint_t duration = 0; + size_t duration_length = 0; + mp_obj_t *duration_ptr = NULL; + mp_uint_t data = 0; + size_t data_length = 0; + mp_obj_t *data_ptr = NULL; + mp_uint_t num_pulses = 0; - esp32_rmt_obj_t *self = MP_OBJ_TO_PTR(args[0].u_obj); - mp_obj_t pulses = args[1].u_obj; - mp_uint_t start = args[2].u_int; - - if (start > 1) { - mp_raise_ValueError(MP_ERROR_TEXT("start must be 0 or 1")); + if (!(mp_obj_is_type(data_obj, &mp_type_tuple) || mp_obj_is_type(data_obj, &mp_type_list))) { + // Mode 1: array of durations, toggle initial data value + mp_obj_get_array(duration_obj, &duration_length, &duration_ptr); + data = mp_obj_is_true(data_obj); + num_pulses = duration_length; + } else if (mp_obj_is_int(duration_obj)) { + // Mode 2: constant duration, array of data values + duration = mp_obj_get_int(duration_obj); + mp_obj_get_array(data_obj, &data_length, &data_ptr); + num_pulses = data_length; + } else { + // Mode 3: arrays of durations and data values + mp_obj_get_array(duration_obj, &duration_length, &duration_ptr); + mp_obj_get_array(data_obj, &data_length, &data_ptr); + if (duration_length != data_length) { + mp_raise_ValueError(MP_ERROR_TEXT("duration and data must have same length")); + } + num_pulses = duration_length; } - size_t pulses_length = 0; - mp_obj_t *pulses_ptr = NULL; - mp_obj_get_array(pulses, &pulses_length, &pulses_ptr); - - mp_uint_t num_items = (pulses_length / 2) + (pulses_length % 2); - if (self->loop_en && num_items > 63) { - mp_raise_ValueError(MP_ERROR_TEXT("too many pulses for loop")); + if (num_pulses == 0) { + mp_raise_ValueError(MP_ERROR_TEXT("No pulses")); + } + if (self->loop_en && num_pulses > 126) { + mp_raise_ValueError(MP_ERROR_TEXT("Too many pulses for loop")); } + mp_uint_t num_items = (num_pulses / 2) + (num_pulses % 2); if (num_items > self->num_items) { self->items = (rmt_item32_t *)m_realloc(self->items, num_items * sizeof(rmt_item32_t *)); self->num_items = num_items; } - for (mp_uint_t item_index = 0; item_index < num_items; item_index++) { - mp_uint_t pulse_index = item_index * 2; - self->items[item_index].duration0 = mp_obj_get_int(pulses_ptr[pulse_index++]); - self->items[item_index].level0 = start++; // Note that start _could_ wrap. - if (pulse_index < pulses_length) { - self->items[item_index].duration1 = mp_obj_get_int(pulses_ptr[pulse_index]); - self->items[item_index].level1 = start++; + for (mp_uint_t item_index = 0, pulse_index = 0; item_index < num_items; item_index++) { + self->items[item_index].duration0 = duration_length ? mp_obj_get_int(duration_ptr[pulse_index]) : duration; + self->items[item_index].level0 = data_length ? mp_obj_is_true(data_ptr[pulse_index]) : data++; + pulse_index++; + if (pulse_index < num_pulses) { + self->items[item_index].duration1 = duration_length ? mp_obj_get_int(duration_ptr[pulse_index]) : duration; + self->items[item_index].level1 = data_length ? mp_obj_is_true(data_ptr[pulse_index]) : data++; + pulse_index++; + } else { + self->items[item_index].duration1 = 0; + self->items[item_index].level1 = 0; } } @@ -247,7 +268,7 @@ STATIC mp_obj_t esp32_rmt_write_pulses(size_t n_args, const mp_obj_t *pos_args, check_esp_err(rmt_wait_tx_done(self->channel_id, portMAX_DELAY)); } - check_esp_err(rmt_write_items(self->channel_id, self->items, num_items, false /* non-blocking */)); + check_esp_err(rmt_write_items(self->channel_id, self->items, num_items, false)); if (self->loop_en) { check_esp_err(rmt_set_tx_intr_en(self->channel_id, false)); @@ -256,7 +277,7 @@ STATIC mp_obj_t esp32_rmt_write_pulses(size_t n_args, const mp_obj_t *pos_args, return mp_const_none; } -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(esp32_rmt_write_pulses_obj, 2, esp32_rmt_write_pulses); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp32_rmt_write_pulses_obj, 2, 3, esp32_rmt_write_pulses); STATIC const mp_rom_map_elem_t esp32_rmt_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&esp32_rmt_deinit_obj) }, From 0b3332c8e14db8a229070224955062f143b138cc Mon Sep 17 00:00:00 2001 From: Jonathan Hogg Date: Wed, 30 Jun 2021 19:01:16 +0100 Subject: [PATCH 048/264] docs/library: Document new esp32.RMT features and fix wait_done. Add new API for specifying the idle level and TX carrier output level, and new write_pulses modes of operation. Also fix wait_done documentation which was inverted and wrong about timing. --- docs/library/esp32.rst | 81 +++++++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 36 deletions(-) diff --git a/docs/library/esp32.rst b/docs/library/esp32.rst index 1cfb304c14..fc90968037 100644 --- a/docs/library/esp32.rst +++ b/docs/library/esp32.rst @@ -150,14 +150,13 @@ used to transmit or receive many other types of digital signals:: from machine import Pin r = esp32.RMT(0, pin=Pin(18), clock_div=8) - r # RMT(channel=0, pin=18, source_freq=80000000, clock_div=8) + r # RMT(channel=0, pin=18, source_freq=80000000, clock_div=8, idle_level=0) - # To use carrier frequency - r = esp32.RMT(0, pin=Pin(18), clock_div=8, carrier_freq=38000) - r # RMT(channel=0, pin=18, source_freq=80000000, clock_div=8, carrier_freq=38000, carrier_duty_percent=50) + # To apply a carrier frequency to the high output + r = esp32.RMT(0, pin=Pin(18), clock_div=8, tx_carrier=(38000, 50, 1)) # The channel resolution is 100ns (1/(source_freq/clock_div)). - r.write_pulses((1, 20, 2, 40), start=0) # Send 0 for 100ns, 1 for 2000ns, 0 for 200ns, 1 for 4000ns + r.write_pulses((1, 20, 2, 40), 0) # Send 0 for 100ns, 1 for 2000ns, 0 for 200ns, 1 for 4000ns The input to the RMT module is an 80MHz clock (in the future it may be able to configure the input clock but, for now, it's fixed). ``clock_div`` *divides* @@ -169,9 +168,6 @@ define the pulses. multiplying the resolution by a 15-bit (0-32,768) number. There are eight channels (0-7) and each can have a different clock divider. -To enable the carrier frequency feature of the esp32 hardware, specify the -``carrier_freq`` as something like 38000, a typical IR carrier frequency. - So, in the example above, the 80MHz clock is divided by 8. Thus the resolution is (1/(80Mhz/8)) 100ns. Since the ``start`` level is 0 and toggles with each number, the bitstream is ``0101`` with durations of [100ns, 2000ns, @@ -186,16 +182,21 @@ For more details see Espressif's `ESP-IDF RMT documentation. *beta feature* and the interface may change in the future. -.. class:: RMT(channel, *, pin=None, clock_div=8, carrier_freq=0, carrier_duty_percent=50) +.. class:: RMT(channel, *, pin=None, clock_div=8, idle_level=False, tx_carrier=None) This class provides access to one of the eight RMT channels. *channel* is required and identifies which RMT channel (0-7) will be configured. *pin*, also required, configures which Pin is bound to the RMT channel. *clock_div* is an 8-bit clock divider that divides the source clock (80MHz) to the RMT - channel allowing the resolution to be specified. *carrier_freq* is used to - enable the carrier feature and specify its frequency, default value is ``0`` - (not enabled). To enable, specify a positive integer. *carrier_duty_percent* - defaults to 50. + channel allowing the resolution to be specified. *idle_level* specifies + what level the output will be when no transmission is in progress and can + be any value that converts to a boolean, with ``True`` representing high + voltage and ``False`` representing low. + + To enable the transmission carrier feature, *tx_carrier* should be a tuple + of three positive integers: carrier frequency, duty percent (``0`` to + ``100``) and the output level to apply the carrier to (a boolean as per + *idle_level*). .. method:: RMT.source_freq() @@ -207,39 +208,47 @@ For more details see Espressif's `ESP-IDF RMT documentation. Return the clock divider. Note that the channel resolution is ``1 / (source_freq / clock_div)``. -.. method:: RMT.wait_done(timeout=0) +.. method:: RMT.wait_done(*, timeout=0) - Returns ``True`` if the channel is currently transmitting a stream of pulses - started with a call to `RMT.write_pulses`. - - If *timeout* (defined in ticks of ``source_freq / clock_div``) is specified - the method will wait for *timeout* or until transmission is complete, - returning ``False`` if the channel continues to transmit. If looping is - enabled with `RMT.loop` and a stream has started, then this method will - always (wait and) return ``False``. + Returns ``True`` if the channel is idle or ``False`` if a sequence of + pulses started with `RMT.write_pulses` is being transmitted. If the + *timeout* keyword argument is given then block for up to this many + milliseconds for transmission to complete. .. method:: RMT.loop(enable_loop) Configure looping on the channel. *enable_loop* is bool, set to ``True`` to enable looping on the *next* call to `RMT.write_pulses`. If called with - ``False`` while a looping stream is currently being transmitted then the - current set of pulses will be completed before transmission stops. + ``False`` while a looping sequence is currently being transmitted then the + current loop iteration will be completed and then transmission will stop. -.. method:: RMT.write_pulses(pulses, start) +.. method:: RMT.write_pulses(duration, data=True) - Begin sending *pulses*, a list or tuple defining the stream of pulses. The - length of each pulse is defined by a number to be multiplied by the channel - resolution ``(1 / (source_freq / clock_div))``. *start* defines whether the - stream starts at 0 or 1. + Begin transmitting a sequence. There are three ways to specify this: - If transmission of a stream is currently in progress then this method will - block until transmission of that stream has ended before beginning sending - *pulses*. + **Mode 1:** *duration* is a list or tuple of durations. The optional *data* + argument specifies the initial output level. The output level will toggle + after each duration. - If looping is enabled with `RMT.loop`, the stream of pulses will be repeated - indefinitely. Further calls to `RMT.write_pulses` will end the previous - stream - blocking until the last set of pulses has been transmitted - - before starting the next stream. + **Mode 2:** *duration* is a positive integer and *data* is a list or tuple + of output levels. *duration* specifies a fixed duration for each. + + **Mode 3:** *duration* and *data* are lists or tuples of equal length, + specifying individual durations and the output level for each. + + Durations are in integer units of the channel resolution (as described + above), between 1 and 32767 units. Output levels are any value that can + be converted to a boolean, with ``True`` representing high voltage and + ``False`` representing low. + + If transmission of an earlier sequence is in progress then this method will + block until that transmission is complete before beginning the new sequence. + + If looping has been enabled with `RMT.loop`, the sequence will be + repeated indefinitely. Further calls to this method will block until the + end of the current loop iteration before immediately beginning to loop the + new sequence of pulses. Looping sequences longer than 126 pulses is not + supported by the hardware. Ultra-Low-Power co-processor From 076caf317e0a2ea689723d4295b5a6236a09d010 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 2 Jul 2021 23:10:22 +1000 Subject: [PATCH 049/264] javascript/Makefile: Suppress compiler errors from array bounds. Signed-off-by: Damien George --- ports/javascript/Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ports/javascript/Makefile b/ports/javascript/Makefile index 171f53b952..9cc45d3ef9 100644 --- a/ports/javascript/Makefile +++ b/ports/javascript/Makefile @@ -59,4 +59,7 @@ test: $(BUILD)/micropython.js $(TOP)/tests/run-tests.py $(eval DIRNAME=ports/$(notdir $(CURDIR))) cd $(TOP)/tests && MICROPY_MICROPYTHON=../ports/javascript/node_run.sh ./run-tests.py -j1 +# Disable errors for array-bounds warnings on "sp[-MP_OBJ_ITER_BUF_NSLOTS + 2]" access. +$(BUILD)/py/vm.o: CFLAGS += -Wno-error=array-bounds + include $(TOP)/py/mkrules.mk From d1decdfa937368bc83adf6829eba6743d75136d0 Mon Sep 17 00:00:00 2001 From: Andrew Scheller Date: Fri, 2 Jul 2021 09:50:25 +0100 Subject: [PATCH 050/264] tools/mpremote: Swap order of PID and VID in connect-list output. Fixes issue #7481. --- tools/mpremote/mpremote/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/mpremote/mpremote/main.py b/tools/mpremote/mpremote/main.py index b7c46a1f11..d225bf2e41 100644 --- a/tools/mpremote/mpremote/main.py +++ b/tools/mpremote/mpremote/main.py @@ -166,7 +166,7 @@ def do_connect(args): for p in sorted(serial.tools.list_ports.comports()): print( "{} {} {:04x}:{:04x} {} {}".format( - p.device, p.serial_number, p.pid, p.vid, p.manufacturer, p.product + p.device, p.serial_number, p.vid, p.pid, p.manufacturer, p.product ) ) return None From ff7be31f264c359b6f11fcc2cec8489dfe4905bd Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Wed, 16 Jun 2021 23:44:52 +0200 Subject: [PATCH 051/264] stm32/adc: Allow using ADC12 and ADC3 for H7. * Modify common functions in adc.c to accept ADC handle. * Most external channels are connected to ADC12 which is used by default. * For ADCAll (internal channels) ADC3 is used instead. * Issue #4435 is possibly related (at least partially fixed). --- ports/stm32/adc.c | 68 ++++++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 28 deletions(-) diff --git a/ports/stm32/adc.c b/ports/stm32/adc.c index 5fa22d7c78..6f709e9098 100644 --- a/ports/stm32/adc.c +++ b/ports/stm32/adc.c @@ -51,16 +51,9 @@ /// val = adc.read_core_vref() # read MCU VREF /* ADC defintions */ - -#if defined(STM32H7) -#define ADCx (ADC3) -#define PIN_ADC_MASK PIN_ADC3 -#define pin_adc_table pin_adc3 -#else #define ADCx (ADC1) #define PIN_ADC_MASK PIN_ADC1 #define pin_adc_table pin_adc1 -#endif #define ADCx_CLK_ENABLE __HAL_RCC_ADC1_CLK_ENABLE @@ -142,7 +135,7 @@ defined(STM32F746xx) || defined(STM32F765xx) || \ defined(STM32F767xx) || defined(STM32F769xx) #define VBAT_DIV (4) -#elif defined(STM32H743xx) +#elif defined(STM32H743xx) || defined(STM32H747xx) #define VBAT_DIV (4) #elif defined(STM32L432xx) || \ defined(STM32L451xx) || defined(STM32L452xx) || \ @@ -214,12 +207,12 @@ STATIC bool is_adcx_channel(int channel) { #endif } -STATIC void adc_wait_for_eoc_or_timeout(int32_t timeout) { +STATIC void adc_wait_for_eoc_or_timeout(ADC_HandleTypeDef *adcHandle, int32_t timeout) { uint32_t tickstart = HAL_GetTick(); #if defined(STM32F4) || defined(STM32F7) - while ((ADCx->SR & ADC_FLAG_EOC) != ADC_FLAG_EOC) { + while ((adcHandle->Instance->SR & ADC_FLAG_EOC) != ADC_FLAG_EOC) { #elif defined(STM32F0) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB) - while (READ_BIT(ADCx->ISR, ADC_FLAG_EOC) != ADC_FLAG_EOC) { + while (READ_BIT(adcHandle->Instance->ISR, ADC_FLAG_EOC) != ADC_FLAG_EOC) { #else #error Unsupported processor #endif @@ -229,11 +222,15 @@ STATIC void adc_wait_for_eoc_or_timeout(int32_t timeout) { } } -STATIC void adcx_clock_enable(void) { +STATIC void adcx_clock_enable(ADC_HandleTypeDef *adch) { #if defined(STM32F0) || defined(STM32F4) || defined(STM32F7) ADCx_CLK_ENABLE(); #elif defined(STM32H7) - __HAL_RCC_ADC3_CLK_ENABLE(); + if (adch->Instance == ADC3) { + __HAL_RCC_ADC3_CLK_ENABLE(); + } else { + __HAL_RCC_ADC12_CLK_ENABLE(); + } __HAL_RCC_ADC_CONFIG(RCC_ADCCLKSOURCE_CLKP); #elif defined(STM32L4) || defined(STM32WB) if (__HAL_RCC_GET_ADC_SOURCE() == RCC_ADCCLKSOURCE_NONE) { @@ -246,9 +243,8 @@ STATIC void adcx_clock_enable(void) { } STATIC void adcx_init_periph(ADC_HandleTypeDef *adch, uint32_t resolution) { - adcx_clock_enable(); + adcx_clock_enable(adch); - adch->Instance = ADCx; adch->Init.Resolution = resolution; adch->Init.ContinuousConvMode = DISABLE; adch->Init.DiscontinuousConvMode = DISABLE; @@ -308,6 +304,7 @@ STATIC void adc_init_single(pyb_obj_adc_t *adc_obj) { mp_hal_pin_config(pin, MP_HAL_PIN_MODE_ADC, MP_HAL_PIN_PULL_NONE, 0); } + adc_obj->handle.Instance = ADCx; adcx_init_periph(&adc_obj->handle, ADC_RESOLUTION_12B); #if defined(STM32L4) && defined(ADC_DUALMODE_REGSIMULT_INJECSIMULT) @@ -335,7 +332,11 @@ STATIC void adc_config_channel(ADC_HandleTypeDef *adc_handle, uint32_t channel) #if defined(STM32F0) sConfig.SamplingTime = ADC_SAMPLETIME_55CYCLES_5; #elif defined(STM32F4) || defined(STM32F7) - sConfig.SamplingTime = ADC_SAMPLETIME_15CYCLES; + if (__HAL_ADC_IS_CHANNEL_INTERNAL(channel)) { + sConfig.SamplingTime = ADC_SAMPLETIME_480CYCLES; + } else { + sConfig.SamplingTime = ADC_SAMPLETIME_15CYCLES; + } #elif defined(STM32H7) if (__HAL_ADC_IS_CHANNEL_INTERNAL(channel)) { sConfig.SamplingTime = ADC_SAMPLETIME_810CYCLES_5; @@ -370,8 +371,8 @@ STATIC void adc_config_channel(ADC_HandleTypeDef *adc_handle, uint32_t channel) STATIC uint32_t adc_read_channel(ADC_HandleTypeDef *adcHandle) { HAL_ADC_Start(adcHandle); - adc_wait_for_eoc_or_timeout(EOC_TIMEOUT); - uint32_t value = ADCx->DR; + adc_wait_for_eoc_or_timeout(adcHandle, EOC_TIMEOUT); + uint32_t value = adcHandle->Instance->DR; HAL_ADC_Stop(adcHandle); return value; } @@ -529,19 +530,19 @@ STATIC mp_obj_t adc_read_timed(mp_obj_t self_in, mp_obj_t buf_in, mp_obj_t freq_ } else { // for subsequent samples we can just set the "start sample" bit #if defined(STM32F4) || defined(STM32F7) - ADCx->CR2 |= (uint32_t)ADC_CR2_SWSTART; + self->handle.Instance->CR2 |= (uint32_t)ADC_CR2_SWSTART; #elif defined(STM32F0) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB) - SET_BIT(ADCx->CR, ADC_CR_ADSTART); + SET_BIT(self->handle.Instance->CR, ADC_CR_ADSTART); #else #error Unsupported processor #endif } // wait for sample to complete - adc_wait_for_eoc_or_timeout(EOC_TIMEOUT); + adc_wait_for_eoc_or_timeout(&self->handle, EOC_TIMEOUT); // read value - uint value = ADCx->DR; + uint value = self->handle.Instance->DR; // store value in buffer if (typesize == 1) { @@ -608,9 +609,9 @@ STATIC mp_obj_t adc_read_timed_multi(mp_obj_t adc_array_in, mp_obj_t buf_array_i adc_config_channel(&adc0->handle, adc0->channel); HAL_ADC_Start(&adc0->handle); // Wait for sample to complete and discard - adc_wait_for_eoc_or_timeout(EOC_TIMEOUT); + adc_wait_for_eoc_or_timeout(&adc0->handle, EOC_TIMEOUT); // Read (and discard) value - uint value = ADCx->DR; + uint value = adc0->handle.Instance->DR; // Ensure first sample is on a timer tick __HAL_TIM_CLEAR_FLAG(tim, TIM_FLAG_UPDATE); @@ -639,17 +640,17 @@ STATIC mp_obj_t adc_read_timed_multi(mp_obj_t adc_array_in, mp_obj_t buf_array_i // for the first sample we need to turn the ADC on // ADC is started: set the "start sample" bit #if defined(STM32F4) || defined(STM32F7) - ADCx->CR2 |= (uint32_t)ADC_CR2_SWSTART; + adc->handle.Instance->CR2 |= (uint32_t)ADC_CR2_SWSTART; #elif defined(STM32F0) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB) - SET_BIT(ADCx->CR, ADC_CR_ADSTART); + SET_BIT(adc->handle.Instance->CR, ADC_CR_ADSTART); #else #error Unsupported processor #endif // wait for sample to complete - adc_wait_for_eoc_or_timeout(EOC_TIMEOUT); + adc_wait_for_eoc_or_timeout(&adc->handle, EOC_TIMEOUT); // read value - value = ADCx->DR; + value = adc->handle.Instance->DR; // store values in buffer if (typesize == 1) { @@ -723,13 +724,24 @@ void adc_init_all(pyb_adc_all_obj_t *adc_all, uint32_t resolution, uint32_t en_m if (en_mask & (1 << channel)) { // Channels 0-16 correspond to real pins. Configure the GPIO pin in // ADC mode. + #if defined(STM32H7) + const pin_obj_t *pin = pin_adc3[channel]; + #else const pin_obj_t *pin = pin_adc_table[channel]; + #endif if (pin) { mp_hal_pin_config(pin, MP_HAL_PIN_MODE_ADC, MP_HAL_PIN_PULL_NONE, 0); } } } + #if defined(STM32H7) + // On the H7 the internal channels are connected to ADC3. To read internal channels + // with ADCAll, ADC3 must be used here, and ADC12 is used to read the GPIO channels. + adc_all->handle.Instance = ADC3; + #else + adc_all->handle.Instance = ADCx; + #endif adcx_init_periph(&adc_all->handle, resolution); } From 0e11966ce9ea2345458d2e6127ee28486c4cb65d Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Thu, 1 Jul 2021 16:15:31 +0200 Subject: [PATCH 052/264] stm32/adc: Define the ADC instance used for internal channels. --- ports/stm32/adc.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/ports/stm32/adc.c b/ports/stm32/adc.c index 6f709e9098..55dc3083b6 100644 --- a/ports/stm32/adc.c +++ b/ports/stm32/adc.c @@ -55,6 +55,17 @@ #define PIN_ADC_MASK PIN_ADC1 #define pin_adc_table pin_adc1 +#if defined(STM32H7) +// On the H7 ADC3 is used for ADCAll to be able to read internal +// channels. For all other GPIO channels, ADC12 is used instead. +#define ADCALLx (ADC3) +#define pin_adcall_table pin_adc3 +#else +// Use ADC1 for ADCAll instance by default for all other MCUs. +#define ADCALLx (ADC1) +#define pin_adcall_table pin_adc1 +#endif + #define ADCx_CLK_ENABLE __HAL_RCC_ADC1_CLK_ENABLE #if defined(STM32F0) @@ -724,24 +735,14 @@ void adc_init_all(pyb_adc_all_obj_t *adc_all, uint32_t resolution, uint32_t en_m if (en_mask & (1 << channel)) { // Channels 0-16 correspond to real pins. Configure the GPIO pin in // ADC mode. - #if defined(STM32H7) - const pin_obj_t *pin = pin_adc3[channel]; - #else - const pin_obj_t *pin = pin_adc_table[channel]; - #endif + const pin_obj_t *pin = pin_adcall_table[channel]; if (pin) { mp_hal_pin_config(pin, MP_HAL_PIN_MODE_ADC, MP_HAL_PIN_PULL_NONE, 0); } } } - #if defined(STM32H7) - // On the H7 the internal channels are connected to ADC3. To read internal channels - // with ADCAll, ADC3 must be used here, and ADC12 is used to read the GPIO channels. - adc_all->handle.Instance = ADC3; - #else - adc_all->handle.Instance = ADCx; - #endif + adc_all->handle.Instance = ADCALLx; adcx_init_periph(&adc_all->handle, resolution); } From 86ef96535295de130185dcf41f0c514cf5192ae4 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 1 Jul 2021 12:04:48 +1000 Subject: [PATCH 053/264] stm32/adc: Simplify and generalise how pin_adcX table is defined. The ADC_FIRST_GPIO_CHANNEL and ADC_LAST_GPIO_CHANNEL macros are no longer needed. Instead the pin_adcX table (X = 1, 2, 3) is now generated to be the exact size needed for a given MCU, and MP_ARRAY_SIZE(pin_adcX) is used to determine the upper bound. This commit also allows CPU pins to be excluded from ADC configuration if they are hidden by prefixing their name with a "-". Signed-off-by: Damien George --- ports/stm32/adc.c | 38 ++++++---------------- ports/stm32/boards/make-pins.py | 57 +++++++++++++++++---------------- 2 files changed, 40 insertions(+), 55 deletions(-) diff --git a/ports/stm32/adc.c b/ports/stm32/adc.c index 55dc3083b6..09cd3306bb 100644 --- a/ports/stm32/adc.c +++ b/ports/stm32/adc.c @@ -70,8 +70,6 @@ #if defined(STM32F0) -#define ADC_FIRST_GPIO_CHANNEL (0) -#define ADC_LAST_GPIO_CHANNEL (15) #define ADC_SCALE_V (3.3f) #define ADC_CAL_ADDRESS (0x1ffff7ba) #define ADC_CAL1 ((uint16_t *)0x1ffff7b8) @@ -80,8 +78,6 @@ #elif defined(STM32F4) -#define ADC_FIRST_GPIO_CHANNEL (0) -#define ADC_LAST_GPIO_CHANNEL (15) #define ADC_SCALE_V (3.3f) #define ADC_CAL_ADDRESS (0x1fff7a2a) #define ADC_CAL1 ((uint16_t *)(ADC_CAL_ADDRESS + 2)) @@ -90,8 +86,6 @@ #elif defined(STM32F7) -#define ADC_FIRST_GPIO_CHANNEL (0) -#define ADC_LAST_GPIO_CHANNEL (15) #define ADC_SCALE_V (3.3f) #if defined(STM32F722xx) || defined(STM32F723xx) || \ defined(STM32F732xx) || defined(STM32F733xx) @@ -106,8 +100,6 @@ #elif defined(STM32H7) -#define ADC_FIRST_GPIO_CHANNEL (0) -#define ADC_LAST_GPIO_CHANNEL (16) #define ADC_SCALE_V (3.3f) #define ADC_CAL_ADDRESS (0x1FF1E860) #define ADC_CAL1 ((uint16_t *)(0x1FF1E820)) @@ -116,8 +108,6 @@ #elif defined(STM32L4) || defined(STM32WB) -#define ADC_FIRST_GPIO_CHANNEL (1) -#define ADC_LAST_GPIO_CHANNEL (16) #define ADC_SCALE_V (VREFINT_CAL_VREF / 1000.0f) #define ADC_CAL_ADDRESS (VREFINT_CAL_ADDR) #define ADC_CAL1 (TEMPSENSOR_CAL1_ADDR) @@ -179,7 +169,7 @@ typedef struct _pyb_obj_adc_t { mp_obj_base_t base; mp_obj_t pin_name; - int channel; + uint32_t channel; ADC_HandleTypeDef handle; } pyb_obj_adc_t; @@ -308,13 +298,6 @@ STATIC void adcx_init_periph(ADC_HandleTypeDef *adch, uint32_t resolution) { } STATIC void adc_init_single(pyb_obj_adc_t *adc_obj) { - - if (ADC_FIRST_GPIO_CHANNEL <= adc_obj->channel && adc_obj->channel <= ADC_LAST_GPIO_CHANNEL) { - // Channels 0-16 correspond to real pins. Configure the GPIO pin in ADC mode. - const pin_obj_t *pin = pin_adc_table[adc_obj->channel]; - mp_hal_pin_config(pin, MP_HAL_PIN_MODE_ADC, MP_HAL_PIN_PULL_NONE, 0); - } - adc_obj->handle.Instance = ADCx; adcx_init_periph(&adc_obj->handle, ADC_RESOLUTION_12B); @@ -431,8 +414,8 @@ STATIC mp_obj_t adc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_ } else { const pin_obj_t *pin = pin_find(pin_obj); if ((pin->adc_num & PIN_ADC_MASK) == 0) { - // No ADC1 function on that pin - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("pin %q does not have ADC capabilities"), pin->name); + // No ADC function on the given pin. + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("Pin(%q) doesn't have ADC capabilities"), pin->name); } channel = pin->adc_channel; } @@ -441,11 +424,11 @@ STATIC mp_obj_t adc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_ mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("not a valid ADC Channel: %d"), channel); } - - if (ADC_FIRST_GPIO_CHANNEL <= channel && channel <= ADC_LAST_GPIO_CHANNEL) { - // these channels correspond to physical GPIO ports so make sure they exist - if (pin_adc_table[channel] == NULL) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("channel %d not available on this board"), channel); + // If this channel corresponds to a pin then configure the pin in ADC mode. + if (channel < MP_ARRAY_SIZE(pin_adc_table)) { + const pin_obj_t *pin = pin_adc_table[channel]; + if (pin != NULL) { + mp_hal_pin_config(pin, MP_HAL_PIN_MODE_ADC, MP_HAL_PIN_PULL_NONE, 0); } } @@ -730,11 +713,10 @@ void adc_init_all(pyb_adc_all_obj_t *adc_all, uint32_t resolution, uint32_t en_m mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("resolution %d not supported"), resolution); } - for (uint32_t channel = ADC_FIRST_GPIO_CHANNEL; channel <= ADC_LAST_GPIO_CHANNEL; ++channel) { + for (uint32_t channel = 0; channel < MP_ARRAY_SIZE(pin_adcall_table); ++channel) { // only initialise those channels that are selected with the en_mask if (en_mask & (1 << channel)) { - // Channels 0-16 correspond to real pins. Configure the GPIO pin in - // ADC mode. + // If this channel corresponds to a pin then configure the pin in ADC mode. const pin_obj_t *pin = pin_adcall_table[channel]; if (pin) { mp_hal_pin_config(pin, MP_HAL_PIN_MODE_ADC, MP_HAL_PIN_PULL_NONE, 0); diff --git a/ports/stm32/boards/make-pins.py b/ports/stm32/boards/make-pins.py index a195323318..70213de6ac 100755 --- a/ports/stm32/boards/make-pins.py +++ b/ports/stm32/boards/make-pins.py @@ -287,6 +287,7 @@ class Pins(object): def __init__(self): self.cpu_pins = [] # list of NamedPin objects self.board_pins = [] # list of NamedPin objects + self.adc_table_size = {} # maps ADC number X to size of pin_adcX table def find_pin(self, port_num, pin_num): for named_pin in self.cpu_pins: @@ -353,27 +354,27 @@ class Pins(object): self.print_named("board", self.board_pins) def print_adc(self, adc_num): - print("") - print("const pin_obj_t * const pin_adc{:d}[] = {{".format(adc_num)) - for channel in range(17): - if channel == 16: - print("#if defined(STM32L4)") - adc_found = False - for named_pin in self.cpu_pins: - pin = named_pin.pin() - if ( - pin.is_board_pin() - and (pin.adc_num & (1 << (adc_num - 1))) - and (pin.adc_channel == channel) - ): - print(" &pin_{:s}_obj, // {:d}".format(pin.cpu_pin_name(), channel)) - adc_found = True - break - if not adc_found: - print(" NULL, // {:d}".format(channel)) - if channel == 16: - print("#endif") - print("};") + adc_pins = {} + for named_pin in self.cpu_pins: + pin = named_pin.pin() + if ( + pin.is_board_pin() + and not named_pin.is_hidden() + and (pin.adc_num & (1 << (adc_num - 1))) + ): + adc_pins[pin.adc_channel] = pin + if adc_pins: + table_size = max(adc_pins) + 1 + self.adc_table_size[adc_num] = table_size + print("") + print("const pin_obj_t * const pin_adc{:d}[{:d}] = {{".format(adc_num, table_size)) + for channel in range(table_size): + if channel in adc_pins: + obj = "&pin_{:s}_obj".format(adc_pins[channel].cpu_pin_name()) + else: + obj = "NULL" + print(" [{:d}] = {},".format(channel, obj)) + print("};") def print_header(self, hdr_filename, obj_decls): with open(hdr_filename, "wt") as hdr_file: @@ -382,9 +383,12 @@ class Pins(object): pin = named_pin.pin() if pin.is_board_pin(): pin.print_header(hdr_file) - hdr_file.write("extern const pin_obj_t * const pin_adc1[];\n") - hdr_file.write("extern const pin_obj_t * const pin_adc2[];\n") - hdr_file.write("extern const pin_obj_t * const pin_adc3[];\n") + for adc_num, table_size in self.adc_table_size.items(): + hdr_file.write( + "extern const pin_obj_t * const pin_adc{:d}[{:d}];\n".format( + adc_num, table_size + ) + ) # provide #define's mapping board to cpu name for named_pin in self.board_pins: hdr_file.write( @@ -569,9 +573,8 @@ def main(): with open(args.prefix_filename, "r") as prefix_file: print(prefix_file.read()) pins.print() - pins.print_adc(1) - pins.print_adc(2) - pins.print_adc(3) + for i in range(1, 4): + pins.print_adc(i) pins.print_header(args.hdr_filename, args.hdr_obj_decls) pins.print_qstr(args.qstr_filename) pins.print_af_hdr(args.af_const_filename) From c8e9e04541e9aae0168d644979472cacb8b84d68 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 30 Jun 2021 12:46:19 +1000 Subject: [PATCH 054/264] lib/tinyusb: Update to version 0.10.1. Signed-off-by: Damien George --- lib/tinyusb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tinyusb b/lib/tinyusb index 7b62c71dd5..d49938d0f5 160000 --- a/lib/tinyusb +++ b/lib/tinyusb @@ -1 +1 @@ -Subproject commit 7b62c71dd5ec42e61499d2d83902df9484842670 +Subproject commit d49938d0f5052bce70e55c652b657c0a6a7e84fe From d67bd494b5eca14b1e981e51b30580b1e7d07c43 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 30 Jun 2021 12:47:06 +1000 Subject: [PATCH 055/264] lib/pico-sdk: Update to version 1.2.0. Signed-off-by: Damien George --- lib/pico-sdk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pico-sdk b/lib/pico-sdk index fc10a97c38..bfcbefafc5 160000 --- a/lib/pico-sdk +++ b/lib/pico-sdk @@ -1 +1 @@ -Subproject commit fc10a97c386f65c1a44c68684fe52a56aaf50df0 +Subproject commit bfcbefafc5d2a210551a4d9d80b4303d4ae0adf7 From 031fe0f144e4bc37fc35d682cbb3bcffc79886b1 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 30 Jun 2021 12:47:40 +1000 Subject: [PATCH 056/264] rp2/CMakeLists.txt: Suppress compiler errors for pico-sdk and tinyusb. These warnings appear with GCC 11. Keep them as warnings but not as compiler errors so they can be dealt with properly in the future. Signed-off-by: Damien George --- ports/rp2/CMakeLists.txt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt index 2d6037712e..eb2b3d2a25 100644 --- a/ports/rp2/CMakeLists.txt +++ b/ports/rp2/CMakeLists.txt @@ -188,6 +188,20 @@ target_compile_options(${MICROPY_TARGET} PRIVATE -Werror ) +set_source_files_properties( + ${PICO_SDK_PATH}/src/rp2_common/pico_double/double_math.c + ${PICO_SDK_PATH}/src/rp2_common/pico_float/float_math.c + PROPERTIES + COMPILE_OPTIONS "-Wno-error=uninitialized" +) + +set_source_files_properties( + ${PICO_TINYUSB_PATH}/src/portable/raspberrypi/rp2040/dcd_rp2040.c + ${PICO_TINYUSB_PATH}/src/portable/raspberrypi/rp2040/rp2040_usb.c + PROPERTIES + COMPILE_OPTIONS "-Wno-error=array-bounds;-Wno-error=unused-but-set-variable" +) + target_compile_definitions(${MICROPY_TARGET} PRIVATE FFCONF_H=\"${MICROPY_OOFATFS_DIR}/ffconf.h\" LFS1_NO_MALLOC LFS1_NO_DEBUG LFS1_NO_WARN LFS1_NO_ERROR LFS1_NO_ASSERT From 8a5bfe44a5b686b479ef0638e25fe968099f2a28 Mon Sep 17 00:00:00 2001 From: Mike Teachman Date: Fri, 16 Apr 2021 21:27:40 -0700 Subject: [PATCH 057/264] esp32,stm32: Add new machine.I2S class for I2S protocol support. This commit adds I2S protocol support for the esp32 and stm32 ports, via a new machine.I2S class. It builds on the stm32 work of blmorris, #1361. Features include: - a consistent I2S API across the esp32 and stm32 ports - I2S configurations supported: - master transmit and master receive - 16-bit and 32-bit sample sizes - mono and stereo formats - sampling frequency - 3 modes of operation: - blocking - non-blocking with callback - uasyncio - internal ring buffer size can be tuned - documentation for Pyboards and esp32-based boards - tested on the following development boards: - Pyboard D SF2W - Pyboard V1.1 - ESP32 with SPIRAM - ESP32 Signed-off-by: Mike Teachman --- docs/esp32/quickref.rst | 18 + docs/library/machine.I2S.rst | 162 +++ docs/library/machine.rst | 1 + docs/pyboard/quickref.rst | 20 + ports/esp32/machine_i2s.c | 809 +++++++++++++ ports/esp32/main.c | 1 + ports/esp32/main/CMakeLists.txt | 1 + ports/esp32/modmachine.c | 1 + ports/esp32/modmachine.h | 2 + ports/stm32/Makefile | 13 + ports/stm32/boards/PYBD_SF2/mpconfigboard.h | 5 + ports/stm32/boards/PYBV10/mpconfigboard.h | 4 + ports/stm32/boards/PYBV11/mpconfigboard.h | 4 + ports/stm32/boards/make-pins.py | 2 +- ports/stm32/dma.c | 35 + ports/stm32/dma.h | 4 + ports/stm32/machine_i2s.c | 1126 +++++++++++++++++++ ports/stm32/main.c | 4 + ports/stm32/modmachine.c | 3 + ports/stm32/modmachine.h | 2 + ports/stm32/pin_defs_stm32.h | 2 + 21 files changed, 2218 insertions(+), 1 deletion(-) create mode 100644 docs/library/machine.I2S.rst create mode 100644 ports/esp32/machine_i2s.c create mode 100644 ports/stm32/machine_i2s.c diff --git a/docs/esp32/quickref.rst b/docs/esp32/quickref.rst index 77c7027c40..0b825d5208 100644 --- a/docs/esp32/quickref.rst +++ b/docs/esp32/quickref.rst @@ -385,6 +385,24 @@ has the same methods as software I2C above:: i2c = I2C(0) i2c = I2C(1, scl=Pin(5), sda=Pin(4), freq=400000) +I2S bus +------- + +See :ref:`machine.I2S `. :: + + from machine import I2S, Pin + + i2s = I2S(0, sck=Pin(13), ws=Pin(14), sd=Pin(34), mode=I2S.TX, bits=16, format=I2S.STEREO, rate=44100, ibuf=40000) # create I2S object + i2s.write(buf) # write buffer of audio samples to I2S device + + i2s = I2S(1, sck=Pin(33), ws=Pin(25), sd=Pin(32), mode=I2S.RX, bits=16, format=I2S.MONO, rate=22050, ibuf=40000) # create I2S object + i2s.readinto(buf) # fill buffer with audio samples from I2S device + +The I2S class is currently available as a Technical Preview. During the preview period, feedback from +users is encouraged. Based on this feedback, the I2S class API and implementation may be changed. + +ESP32 has two I2S buses with id=0 and id=1 + Real time clock (RTC) --------------------- diff --git a/docs/library/machine.I2S.rst b/docs/library/machine.I2S.rst new file mode 100644 index 0000000000..48f03b419d --- /dev/null +++ b/docs/library/machine.I2S.rst @@ -0,0 +1,162 @@ +.. currentmodule:: machine +.. _machine.I2S: + +class I2S -- Inter-IC Sound bus protocol +======================================== + +I2S is a synchronous serial protocol used to connect digital audio devices. +At the physical level, a bus consists of 3 lines: SCK, WS, SD. +The I2S class supports Master operation. Slave operation is not supported. + +The I2S class is currently available as a Technical Preview. During the preview period, feedback from +users is encouraged. Based on this feedback, the I2S class API and implementation may be changed. + +I2S objects can be created and initialized using:: + + from machine import I2S + from machine import Pin + + # ESP32 + sck_pin = Pin(14) # Serial clock output + ws_pin = Pin(13) # Word clock output + sdout_pin = Pin(12) # Serial data output + + or + + # PyBoards + sck_pin = Pin("Y6") # Serial clock output + ws_pin = Pin("Y5") # Word clock output + sdout_pin = Pin("Y8") # Serial data output + + audio_out = I2S(2, + sck=sck_pin, ws=ws_pin, sdin=sdin_pin, + mode=I2S.TX, + bits=16, + format=I2S.MONO, + rate=44100, + ibuf=20000) + + audio_in = I2S(2, + sck=sck_pin, ws=ws_pin, sdin=sdin_pin, + mode=I2S.RX, + bits=32, + format=I2S.STEREO, + rate=22050, + ibuf=20000) + +3 modes of operation are supported: + - blocking + - non-blocking + - uasyncio + +blocking:: + + num_written = audio_out.write(buf) # blocks until buf emptied + + num_read = audio_in.readinto(buf) # blocks until buf filled + +non-blocking:: + + audio_out.irq(i2s_callback) # i2s_callback is called when buf is emptied + num_written = audio_out.write(buf) # returns immediately + + audio_in.irq(i2s_callback) # i2s_callback is called when buf is filled + num_read = audio_in.readinto(buf) # returns immediately + +uasyncio:: + + swriter = uasyncio.StreamWriter(audio_out) + swriter.write(buf) + await swriter.drain() + + sreader = uasyncio.StreamReader(audio_in) + num_read = await sreader.readinto(buf) + +Constructor +----------- + +.. class:: I2S(id, *, sck, ws, sd, 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. + + 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 + - ``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) + - ``ibuf`` specifies internal buffer length (bytes) + + For all ports, DMA runs continuously in the background and allows user applications to perform other operations while + sample data is transfered between the internal buffer and the I2S peripheral unit. + Increasing the size of the internal buffer has the potential to increase the time that user applications can perform non-I2S operations + before underflow (e.g. ``write`` method) or overflow (e.g. ``readinto`` method). + +Methods +------- + +.. method:: I2S.init(sck, ...) + + see Constructor for argument descriptions + +.. method:: I2S.deinit() + + Deinitialize the I2S bus + +.. method:: I2S.readinto(buf) + + Read audio samples into the buffer specified by ``buf``. ``buf`` must support the buffer protocol, such as bytearray or array. + "buf" byte ordering is little-endian. For Stereo format, left channel sample precedes right channel sample. For Mono format, + the left channel sample data is used. + Returns number of bytes read + +.. method:: I2S.write(buf) + + Write audio samples contained in ``buf``. ``buf`` must support the buffer protocol, such as bytearray or array. + "buf" byte ordering is little-endian. For Stereo format, left channel sample precedes right channel sample. For Mono format, + the sample data is written to both the right and left channels. + Returns number of bytes written + +.. method:: I2S.irq(handler) + + Set a callback. ``handler`` is called when ``buf`` is emptied (``write`` method) or becomes full (``readinto`` method). + Setting a callback changes the ``write`` and ``readinto`` methods to non-blocking operation. + ``handler`` is called in the context of the MicroPython scheduler. + +.. staticmethod:: I2S.shift(buf, bits, shift) + + bitwise shift of all samples contained in ``buf``. ``bits`` specifies sample size in bits. ``shift`` specifies the number of bits to shift each sample. + Positive for left shift, negative for right shift. + Typically used for volume control. Each bit shift changes sample volume by 6dB. + +Constants +--------- + +.. data:: I2S.RX + + for initialising the I2S bus ``mode`` to receive + +.. data:: I2S.TX + + for initialising the I2S bus ``mode`` to transmit + +.. data:: I2S.STEREO + + for initialising the I2S bus ``format`` to stereo + +.. data:: I2S.MONO + + for initialising the I2S bus ``format`` to mono + + diff --git a/docs/library/machine.rst b/docs/library/machine.rst index 0a1c1c9530..26f99680a0 100644 --- a/docs/library/machine.rst +++ b/docs/library/machine.rst @@ -181,6 +181,7 @@ Classes machine.UART.rst machine.SPI.rst machine.I2C.rst + machine.I2S.rst machine.RTC.rst machine.Timer.rst machine.WDT.rst diff --git a/docs/pyboard/quickref.rst b/docs/pyboard/quickref.rst index 3dbd093043..3ea3190999 100644 --- a/docs/pyboard/quickref.rst +++ b/docs/pyboard/quickref.rst @@ -219,6 +219,26 @@ eg ``I2C(1)``. Software I2C is also available by explicitly specifying the Note: for legacy I2C support see :ref:`pyb.I2C `. +I2S bus +------- + +See :ref:`machine.I2S `. :: + + from machine import I2S, Pin + + i2s = I2S(2, sck=Pin('Y6'), ws=Pin('Y5'), sd=Pin('Y8'), mode=I2S.TX, bits=16, format=I2S.STEREO, rate=44100, ibuf=40000) # create I2S object + i2s.write(buf) # write buffer of audio samples to I2S device + + i2s = I2S(1, sck=Pin('X5'), ws=Pin('X6'), sd=Pin('Y4'), mode=I2S.RX, bits=16, format=I2S.MONO, rate=22050, ibuf=40000) # create I2S object + i2s.readinto(buf) # fill buffer with audio samples from I2S device + +The I2S class is currently available as a Technical Preview. During the preview period, feedback from +users is encouraged. Based on this feedback, the I2S class API and implementation may be changed. + +PYBv1.0/v1.1 has one I2S bus with id=2. +PYBD-SFxW has two I2S buses with id=1 and id=2. +I2S is shared with SPI. + CAN bus (controller area network) --------------------------------- diff --git a/ports/esp32/machine_i2s.c b/ports/esp32/machine_i2s.c new file mode 100644 index 0000000000..b0d02e74f6 --- /dev/null +++ b/ports/esp32/machine_i2s.c @@ -0,0 +1,809 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Mike Teachman + * + * 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/misc.h" +#include "py/stream.h" +#include "py/objstr.h" +#include "modmachine.h" +#include "mphalport.h" + +#include "driver/i2s.h" +#include "soc/i2s_reg.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "esp_task.h" + +// 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 +// - a FreeRTOS task is created to implement the asynchronous background operations +// - a FreeRTOS queue is used to transfer the supplied buffer to the background task +// +// Mode3: Uasyncio +// - implements the stream protocol +// - uasyncio mode is enabled when the ioctl() function is called +// - the I2S event queue is used to detect that I2S samples can be read or written from/to DMA memory +// +// 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 sample data transfers use DMA + +#define I2S_TASK_PRIORITY (ESP_TASK_PRIO_MIN + 1) +#define I2S_TASK_STACK_SIZE (2048) + +#define DMA_BUF_LEN_IN_I2S_FRAMES (256) + +// The transform buffer is used with the readinto() method to bridge the opaque DMA memory on the ESP devices +// with the app buffer. It facilitates audio sample transformations. e.g. 32-bits samples to 16-bit samples. +// The size of 240 bytes is an engineering optimum that balances transfer performance with an acceptable use of heap space +#define SIZEOF_TRANSFORM_BUFFER_IN_BYTES (240) + +#define NUM_I2S_USER_FORMATS (4) +#define I2S_RX_FRAME_SIZE_IN_BYTES (8) + +typedef enum { + MONO, + STEREO +} format_t; + +typedef enum { + BLOCKING, + NON_BLOCKING, + UASYNCIO +} io_mode_t; + +typedef enum { + I2S_TX_TRANSFER, + I2S_RX_TRANSFER, +} direction_t; + +typedef struct _non_blocking_descriptor_t { + mp_buffer_info_t appbuf; + mp_obj_t callback; + direction_t direction; +} non_blocking_descriptor_t; + +typedef struct _machine_i2s_obj_t { + mp_obj_base_t base; + i2s_port_t port; + mp_hal_pin_obj_t sck; + mp_hal_pin_obj_t ws; + mp_hal_pin_obj_t sd; + int8_t mode; + i2s_bits_per_sample_t bits; + format_t format; + int32_t rate; + int32_t ibuf; + mp_obj_t callback_for_non_blocking; + io_mode_t io_mode; + uint8_t transform_buffer[SIZEOF_TRANSFORM_BUFFER_IN_BYTES]; + QueueHandle_t i2s_event_queue; + QueueHandle_t non_blocking_mode_queue; + TaskHandle_t non_blocking_mode_task; +} machine_i2s_obj_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, with the L and R channels reversed) 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] = { + { 6, 7, -1, -1, -1, -1, -1, -1 }, // Mono, 16-bits + { 4, 5, 6, 7, -1, -1, -1, -1 }, // Mono, 32-bits + { 6, 7, 2, 3, -1, -1, -1, -1 }, // Stereo, 16-bits + { 4, 5, 6, 7, 0, 1, 2, 3 }, // Stereo, 32-bits +}; + +STATIC machine_i2s_obj_t *machine_i2s_obj[I2S_NUM_MAX]; + +void machine_i2s_init0() { + for (i2s_port_t p = 0; p < I2S_NUM_MAX; p++) { + machine_i2s_obj[p] = NULL; + } +} + +// The following function takes a sample buffer and swaps L/R channels +// +// Background: For 32-bit stereo, the ESP-IDF API has a L/R channel orientation that breaks +// convention with other ESP32 channel formats +// +// appbuf[] = [L_0-7, L_8-15, L_16-23, L_24-31, R_0-7, R_8-15, R_16-23, R_24-31] = [Left channel, Right channel] +// dma[] = [R_0-7, R_8-15, R_16-23, R_24-31, L_0-7, L_8-15, L_16-23, L_24-31] = [Right channel, Left channel] +// +// where: +// L_0-7 is the least significant byte of the 32 bit sample in the Left channel +// L_24-31 is the most significant byte of the 32 bit sample in the Left channel +// +// Example: +// +// appbuf[] = [0x99, 0xBB, 0x11, 0x22, 0x44, 0x55, 0xAB, 0x77] = [Left channel, Right channel] +// dma[] = [0x44, 0x55, 0xAB, 0x77, 0x99, 0xBB, 0x11, 0x22] = [Right channel, Left channel] +// where: +// LEFT Channel = 0x99, 0xBB, 0x11, 0x22 +// RIGHT Channel = 0x44, 0x55, 0xAB, 0x77 +// +// samples in appbuf are in little endian format: +// 0x77 is the most significant byte of the 32-bit sample +// 0x44 is the least significant byte of the 32-bit sample +STATIC void swap_32_bit_stereo_channels(mp_buffer_info_t *bufinfo) { + int32_t swap_sample; + int32_t *sample = bufinfo->buf; + uint32_t num_samples = bufinfo->len / 4; + for (uint32_t i = 0; i < num_samples; i += 2) { + swap_sample = sample[i + 1]; + sample[i + 1] = sample[i]; + sample[i] = swap_sample; + } +} + +STATIC int8_t get_frame_mapping_index(i2s_bits_per_sample_t bits, format_t format) { + if (format == MONO) { + if (bits == I2S_BITS_PER_SAMPLE_16BIT) { + return 0; + } else { // 32 bits + return 1; + } + } else { // STEREO + if (bits == I2S_BITS_PER_SAMPLE_16BIT) { + return 2; + } else { // 32 bits + return 3; + } + } +} + +STATIC i2s_bits_per_sample_t get_dma_bits(uint8_t mode, i2s_bits_per_sample_t bits) { + if (mode == (I2S_MODE_MASTER | I2S_MODE_TX)) { + return bits; + } else { // Master Rx + // read 32 bit samples for I2S hardware. e.g. MEMS microphones + return I2S_BITS_PER_SAMPLE_32BIT; + } +} + +STATIC i2s_channel_fmt_t get_dma_format(uint8_t mode, format_t format) { + if (mode == (I2S_MODE_MASTER | I2S_MODE_TX)) { + if (format == MONO) { + return I2S_CHANNEL_FMT_ONLY_LEFT; + } else { // STEREO + return I2S_CHANNEL_FMT_RIGHT_LEFT; + } + } else { // Master Rx + // read stereo frames for all I2S hardware + return I2S_CHANNEL_FMT_RIGHT_LEFT; + } +} + +STATIC uint32_t get_dma_buf_count(uint8_t mode, i2s_bits_per_sample_t bits, format_t format, int32_t ibuf) { + // calculate how many DMA buffers need to be allocated + uint32_t dma_frame_size_in_bytes = + (get_dma_bits(mode, bits) / 8) * (get_dma_format(mode, format) == I2S_CHANNEL_FMT_RIGHT_LEFT ? 2: 1); + + uint32_t dma_buf_count = ibuf / (DMA_BUF_LEN_IN_I2S_FRAMES * dma_frame_size_in_bytes); + + return dma_buf_count; +} + +STATIC uint32_t fill_appbuf_from_dma(machine_i2s_obj_t *self, mp_buffer_info_t *appbuf) { + + // copy audio samples from DMA memory to the app buffer + // audio samples are read from DMA memory in chunks + // loop, reading and copying chunks until the app buffer is filled + // For uasyncio mode, the loop will make an early exit if DMA memory becomes empty + // Example: + // a MicroPython I2S object is configured for 16-bit mono (2 bytes per audio sample). + // For every frame coming from DMA (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 DMA memory. + // If a 8kB app buffer is supplied, 32kB of audio samples is read from DMA memory. + + uint32_t a_index = 0; + uint8_t *app_p = appbuf->buf; + uint8_t appbuf_sample_size_in_bytes = (self->bits / 8) * (self->format == STEREO ? 2: 1); + uint32_t num_bytes_needed_from_dma = appbuf->len * (I2S_RX_FRAME_SIZE_IN_BYTES / appbuf_sample_size_in_bytes); + while (num_bytes_needed_from_dma) { + uint32_t num_bytes_requested_from_dma = MIN(sizeof(self->transform_buffer), num_bytes_needed_from_dma); + uint32_t num_bytes_received_from_dma = 0; + + TickType_t delay; + if (self->io_mode == UASYNCIO) { + delay = 0; // stop i2s_read() operation if DMA memory becomes empty + } else { + delay = portMAX_DELAY; // block until supplied buffer is filled + } + + // read a chunk of audio samples from DMA memory + check_esp_err(i2s_read( + self->port, + self->transform_buffer, + num_bytes_requested_from_dma, + &num_bytes_received_from_dma, + delay)); + + // process the transform buffer one frame at a time. + // copy selected bytes from the transform buffer into the user supplied appbuf. + // Example: + // a MicroPython I2S object is configured for 16-bit mono. This configuration associates to + // a frame map index of 0 = { 6, 7, -1, -1, -1, -1, -1, -1 } in the i2s_frame_map array + // This mapping indicates: + // appbuf[x+0] = frame[6] + // appbuf[x+1] = frame[7] + // frame bytes 0-5 are not used + + uint32_t t_index = 0; + uint8_t f_index = get_frame_mapping_index(self->bits, self->format); + while (t_index < num_bytes_received_from_dma) { + uint8_t *transform_p = self->transform_buffer + t_index; + + for (uint8_t i = 0; i < I2S_RX_FRAME_SIZE_IN_BYTES; i++) { + int8_t t_to_a_mapping = i2s_frame_map[f_index][i]; + if (t_to_a_mapping != -1) { + *app_p++ = transform_p[t_to_a_mapping]; + a_index++; + } + t_index++; + } + } + + num_bytes_needed_from_dma -= num_bytes_received_from_dma; + + if ((self->io_mode == UASYNCIO) && (num_bytes_received_from_dma < num_bytes_requested_from_dma)) { + // Unable to fill the entire app buffer from DMA memory. This indicates all DMA RX buffers are empty. + // Clear the I2S event queue so ioctl() indicates that the I2S object cannot currently + // supply more audio samples + xQueueReset(self->i2s_event_queue); + break; + } + } + + return a_index; +} + +STATIC uint32_t copy_appbuf_to_dma(machine_i2s_obj_t *self, mp_buffer_info_t *appbuf) { + if ((self->bits == I2S_BITS_PER_SAMPLE_32BIT) && (self->format == STEREO)) { + swap_32_bit_stereo_channels(appbuf); + } + + uint32_t num_bytes_written = 0; + + TickType_t delay; + if (self->io_mode == UASYNCIO) { + delay = 0; // stop i2s_write() operation if DMA memory becomes full + } else { + delay = portMAX_DELAY; // block until supplied buffer is emptied + } + + check_esp_err(i2s_write(self->port, appbuf->buf, appbuf->len, &num_bytes_written, delay)); + + if ((self->io_mode == UASYNCIO) && (num_bytes_written < appbuf->len)) { + // Unable to empty the entire app buffer into DMA memory. This indicates all DMA TX buffers are full. + // Clear the I2S event queue so ioctl() indicates that the I2S object cannot currently + // accept more audio samples + xQueueReset(self->i2s_event_queue); + + // Undo the swap transformation as the buffer has not been completely emptied. + // The uasyncio stream writer will use the same buffer in a future write call. + if ((self->bits == I2S_BITS_PER_SAMPLE_32BIT) && (self->format == STEREO)) { + swap_32_bit_stereo_channels(appbuf); + } + } + return num_bytes_written; +} + +// FreeRTOS task used for non-blocking mode +STATIC void task_for_non_blocking_mode(void *self_in) { + machine_i2s_obj_t *self = (machine_i2s_obj_t *)self_in; + + non_blocking_descriptor_t descriptor; + + for (;;) { + if (xQueueReceive(self->non_blocking_mode_queue, &descriptor, portMAX_DELAY)) { + if (descriptor.direction == I2S_TX_TRANSFER) { + copy_appbuf_to_dma(self, &descriptor.appbuf); + } else { // RX + fill_appbuf_from_dma(self, &descriptor.appbuf); + } + mp_sched_schedule(descriptor.callback, MP_OBJ_FROM_PTR(self)); + } + } +} + +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_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_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 ---- + // + + // are Pins valid? + int8_t sck = args[ARG_sck].u_obj == MP_OBJ_NULL ? -1 : mp_hal_get_pin_obj(args[ARG_sck].u_obj); + int8_t ws = args[ARG_ws].u_obj == MP_OBJ_NULL ? -1 : mp_hal_get_pin_obj(args[ARG_ws].u_obj); + int8_t sd = args[ARG_sd].u_obj == MP_OBJ_NULL ? -1 : mp_hal_get_pin_obj(args[ARG_sd].u_obj); + + // is Mode valid? + i2s_mode_t mode = args[ARG_mode].u_int; + if ((mode != (I2S_MODE_MASTER | I2S_MODE_RX)) && + (mode != (I2S_MODE_MASTER | I2S_MODE_TX))) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid mode")); + } + + // is Bits valid? + i2s_bits_per_sample_t bits = args[ARG_bits].u_int; + if ((bits != I2S_BITS_PER_SAMPLE_16BIT) && + (bits != I2S_BITS_PER_SAMPLE_32BIT)) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid bits")); + } + + // is Format valid? + format_t format = args[ARG_format].u_int; + if ((format != STEREO) && + (format != MONO)) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid format")); + } + + // is Rate valid? + // Not checked: ESP-IDF I2S API does not indicate a valid range for sample rate + + // is Ibuf valid? + // Not checked: ESP-IDF I2S API will return error if requested buffer size exceeds available memory + + self->sck = sck; + self->ws = ws; + self->sd = sd; + self->mode = mode; + self->bits = bits; + self->format = format; + self->rate = args[ARG_rate].u_int; + self->ibuf = args[ARG_ibuf].u_int; + self->callback_for_non_blocking = MP_OBJ_NULL; + self->i2s_event_queue = NULL; + self->non_blocking_mode_queue = NULL; + self->non_blocking_mode_task = NULL; + self->io_mode = BLOCKING; + + i2s_config_t i2s_config; + i2s_config.communication_format = I2S_COMM_FORMAT_I2S; + i2s_config.mode = mode; + i2s_config.bits_per_sample = get_dma_bits(mode, bits); + i2s_config.channel_format = get_dma_format(mode, format); + i2s_config.sample_rate = self->rate; + i2s_config.intr_alloc_flags = ESP_INTR_FLAG_LOWMED; + i2s_config.dma_buf_count = get_dma_buf_count(mode, bits, format, self->ibuf); + i2s_config.dma_buf_len = DMA_BUF_LEN_IN_I2S_FRAMES; + i2s_config.use_apll = false; + + // I2S queue size equals the number of DMA buffers + check_esp_err(i2s_driver_install(self->port, &i2s_config, i2s_config.dma_buf_count, &self->i2s_event_queue)); + + // apply low-level workaround for bug in some ESP-IDF versions that swap + // the left and right channels + // https://github.com/espressif/esp-idf/issues/6625 + REG_SET_BIT(I2S_CONF_REG(self->port), I2S_TX_MSB_RIGHT); + REG_SET_BIT(I2S_CONF_REG(self->port), I2S_RX_MSB_RIGHT); + + i2s_pin_config_t pin_config; + pin_config.bck_io_num = self->sck; + pin_config.ws_io_num = self->ws; + + if (mode == (I2S_MODE_MASTER | I2S_MODE_RX)) { + pin_config.data_in_num = self->sd; + pin_config.data_out_num = -1; + } else { // TX + pin_config.data_in_num = -1; + pin_config.data_out_num = self->sd; + } + + check_esp_err(i2s_set_pin(self->port, &pin_config)); +} + +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" + "mode=%u,\n" + "bits=%u, format=%u,\n" + "rate=%d, ibuf=%d)", + self->port, + mp_hal_pin_name(self->sck), + mp_hal_pin_name(self->ws), + mp_hal_pin_name(self->sd), + 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); + + i2s_port_t port = mp_obj_get_int(args[0]); + if (port < 0 || port >= I2S_NUM_MAX) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid id")); + } + + machine_i2s_obj_t *self; + if (machine_i2s_obj[port] == NULL) { + self = m_new_obj(machine_i2s_obj_t); + machine_i2s_obj[port] = self; + self->base.type = &machine_i2s_type; + self->port = port; + } else { + self = machine_i2s_obj[port]; + machine_i2s_deinit(self); + } + + 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_obj_init(mp_uint_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + machine_i2s_obj_t *self = pos_args[0]; + machine_i2s_deinit(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_obj_init); + +STATIC mp_obj_t machine_i2s_deinit(mp_obj_t self_in) { + machine_i2s_obj_t *self = MP_OBJ_TO_PTR(self_in); + i2s_driver_uninstall(self->port); + + if (self->non_blocking_mode_task != NULL) { + vTaskDelete(self->non_blocking_mode_task); + self->non_blocking_mode_task = NULL; + } + + if (self->non_blocking_mode_queue != NULL) { + vQueueDelete(self->non_blocking_mode_queue); + self->non_blocking_mode_queue = NULL; + } + + self->i2s_event_queue = NULL; + 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; + + // create a queue linking the MicroPython task to a FreeRTOS task + // that manages the non blocking mode of operation + self->non_blocking_mode_queue = xQueueCreate(1, sizeof(non_blocking_descriptor_t)); + + // non-blocking mode requires a background FreeRTOS task + if (xTaskCreatePinnedToCore( + task_for_non_blocking_mode, + "i2s_non_blocking", + I2S_TASK_STACK_SIZE, + self, + I2S_TASK_PRIORITY, + (TaskHandle_t *)&self->non_blocking_mode_task, + MP_TASK_COREID) != pdPASS) { + + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("failed to create I2S task")); + } + } else { + if (self->non_blocking_mode_task != NULL) { + vTaskDelete(self->non_blocking_mode_task); + self->non_blocking_mode_task = NULL; + } + + if (self->non_blocking_mode_queue != NULL) { + vQueueDelete(self->non_blocking_mode_queue); + self->non_blocking_mode_queue = NULL; + } + + 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_const_none} }, + { 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 / 2; + break; + + case 32: + num_audio_samples = bufinfo.len / 4; + 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(I2S_MODE_MASTER | I2S_MODE_RX) }, + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_INT(I2S_MODE_MASTER | I2S_MODE_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 != (I2S_MODE_MASTER | I2S_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) { + non_blocking_descriptor_t descriptor; + descriptor.appbuf.buf = (void *)buf_in; + descriptor.appbuf.len = size; + descriptor.callback = self->callback_for_non_blocking; + descriptor.direction = I2S_RX_TRANSFER; + // send the descriptor to the task that handles non-blocking mode + xQueueSend(self->non_blocking_mode_queue, &descriptor, 0); + 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_dma(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 != (I2S_MODE_MASTER | I2S_MODE_TX)) { + *errcode = MP_EPERM; + return MP_STREAM_ERROR; + } + + if (size == 0) { + return 0; + } + + if (self->io_mode == NON_BLOCKING) { + non_blocking_descriptor_t descriptor; + descriptor.appbuf.buf = (void *)buf_in; + descriptor.appbuf.len = size; + descriptor.callback = self->callback_for_non_blocking; + descriptor.direction = I2S_TX_TRANSFER; + // send the descriptor to the task that handles non-blocking mode + xQueueSend(self->non_blocking_mode_queue, &descriptor, 0); + 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_dma(self, &appbuf); + return num_bytes_written; + } +} + +STATIC mp_uint_t machine_i2s_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { + machine_i2s_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_uint_t ret; + mp_uint_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 != (I2S_MODE_MASTER | I2S_MODE_RX)) { + *errcode = MP_EPERM; + return MP_STREAM_ERROR; + } + + i2s_event_t i2s_event; + + // check event queue to determine if a DMA buffer has been filled + // (which is an indication that at least one DMA buffer is available to be read) + // note: timeout = 0 so the call is non-blocking + if (xQueueReceive(self->i2s_event_queue, &i2s_event, 0)) { + if (i2s_event.type == I2S_EVENT_RX_DONE) { + // getting here means that at least one DMA buffer is now full + // indicating that audio samples can be read from the I2S object + ret |= MP_STREAM_POLL_RD; + } + } + } + + if (flags & MP_STREAM_POLL_WR) { + if (self->mode != (I2S_MODE_MASTER | I2S_MODE_TX)) { + *errcode = MP_EPERM; + return MP_STREAM_ERROR; + } + + i2s_event_t i2s_event; + + // check event queue to determine if a DMA buffer has been emptied + // (which is an indication that at least one DMA buffer is available to be written) + // note: timeout = 0 so the call is non-blocking + if (xQueueReceive(self->i2s_event_queue, &i2s_event, 0)) { + if (i2s_event.type == I2S_EVENT_TX_DONE) { + // getting here means that at least one DMA buffer is now empty + // indicating that audio samples can be written to the I2S object + 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, +}; diff --git a/ports/esp32/main.c b/ports/esp32/main.c index ff6dd69574..b04831fff1 100644 --- a/ports/esp32/main.c +++ b/ports/esp32/main.c @@ -138,6 +138,7 @@ soft_reset: // initialise peripherals machine_pins_init(); + machine_i2s_init0(); // run boot-up scripts pyexec_frozen_module("_boot.py"); diff --git a/ports/esp32/main/CMakeLists.txt b/ports/esp32/main/CMakeLists.txt index d01656e567..fb08c2759e 100644 --- a/ports/esp32/main/CMakeLists.txt +++ b/ports/esp32/main/CMakeLists.txt @@ -52,6 +52,7 @@ set(MICROPY_SOURCE_PORT ${PROJECT_DIR}/machine_adc.c ${PROJECT_DIR}/machine_dac.c ${PROJECT_DIR}/machine_i2c.c + ${PROJECT_DIR}/machine_i2s.c ${PROJECT_DIR}/machine_pwm.c ${PROJECT_DIR}/machine_uart.c ${PROJECT_DIR}/modmachine.c diff --git a/ports/esp32/modmachine.c b/ports/esp32/modmachine.c index bbe7fae033..c46f8ab8d7 100644 --- a/ports/esp32/modmachine.c +++ b/ports/esp32/modmachine.c @@ -280,6 +280,7 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_DAC), MP_ROM_PTR(&machine_dac_type) }, { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&machine_hw_i2c_type) }, { MP_ROM_QSTR(MP_QSTR_SoftI2C), MP_ROM_PTR(&mp_machine_soft_i2c_type) }, + { MP_ROM_QSTR(MP_QSTR_I2S), MP_ROM_PTR(&machine_i2s_type) }, { MP_ROM_QSTR(MP_QSTR_PWM), MP_ROM_PTR(&machine_pwm_type) }, { MP_ROM_QSTR(MP_QSTR_RTC), MP_ROM_PTR(&machine_rtc_type) }, { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&machine_hw_spi_type) }, diff --git a/ports/esp32/modmachine.h b/ports/esp32/modmachine.h index 3e99e11205..7bf03b0cab 100644 --- a/ports/esp32/modmachine.h +++ b/ports/esp32/modmachine.h @@ -18,6 +18,7 @@ extern const mp_obj_type_t machine_dac_type; extern const mp_obj_type_t machine_pwm_type; extern const mp_obj_type_t machine_hw_i2c_type; extern const mp_obj_type_t machine_hw_spi_type; +extern const mp_obj_type_t machine_i2s_type; extern const mp_obj_type_t machine_uart_type; extern const mp_obj_type_t machine_rtc_type; extern const mp_obj_type_t machine_sdcard_type; @@ -27,5 +28,6 @@ void machine_deinit(void); void machine_pins_init(void); void machine_pins_deinit(void); void machine_timer_deinit_all(void); +void machine_i2s_init0(); #endif // MICROPY_INCLUDED_ESP32_MODMACHINE_H diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index e94e7d72f3..4ec03c1746 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -318,6 +318,7 @@ SRC_C += \ help.c \ machine_adc.c \ machine_i2c.c \ + machine_i2s.c \ machine_spi.c \ machine_timer.c \ machine_uart.c \ @@ -427,6 +428,18 @@ ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f0 f4 f7 h7 l4)) endif endif +ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f0 f4 f7 l0)) +HAL_SRC_C += $(addprefix $(HAL_DIR)/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_i2s_ex.c \ + ) +endif + USBDEV_SRC_C += $(addprefix $(USBDEV_DIR)/,\ core/src/usbd_core.c \ core/src/usbd_ctlreq.c \ diff --git a/ports/stm32/boards/PYBD_SF2/mpconfigboard.h b/ports/stm32/boards/PYBD_SF2/mpconfigboard.h index 48b3ee3eb9..ed76b3f971 100644 --- a/ports/stm32/boards/PYBD_SF2/mpconfigboard.h +++ b/ports/stm32/boards/PYBD_SF2/mpconfigboard.h @@ -40,6 +40,7 @@ #define MICROPY_HW_ENABLE_SDCARD (1) #define MICROPY_HW_ENABLE_MMCARD (1) #define MICROPY_HW_ENABLE_RF_SWITCH (1) +#define MICROPY_HW_ENABLE_I2S (1) #define MICROPY_BOARD_EARLY_INIT board_early_init #define MICROPY_BOARD_ENTER_STOP board_sleep(1); @@ -146,6 +147,10 @@ extern struct _spi_bdev_t spi_bdev2; #define MICROPY_HW_SPI3_MISO (pyb_pin_W50) #define MICROPY_HW_SPI3_MOSI (pyb_pin_W46) +// I2S buses +#define MICROPY_HW_I2S1 (1) +#define MICROPY_HW_I2S2 (1) + // CAN buses #define MICROPY_HW_CAN1_NAME "X" #define MICROPY_HW_CAN1_TX (pyb_pin_X10) diff --git a/ports/stm32/boards/PYBV10/mpconfigboard.h b/ports/stm32/boards/PYBV10/mpconfigboard.h index 52382f44d5..50ef3ae269 100644 --- a/ports/stm32/boards/PYBV10/mpconfigboard.h +++ b/ports/stm32/boards/PYBV10/mpconfigboard.h @@ -11,6 +11,7 @@ #define MICROPY_HW_ENABLE_DAC (1) #define MICROPY_HW_ENABLE_USB (1) #define MICROPY_HW_ENABLE_SDCARD (1) +#define MICROPY_HW_ENABLE_I2S (1) // HSE is 8MHz #define MICROPY_HW_CLK_PLLM (8) @@ -64,6 +65,9 @@ #define MICROPY_HW_SPI2_MISO (pin_B14) // Y7 #define MICROPY_HW_SPI2_MOSI (pin_B15) // Y8 +// I2S buses +#define MICROPY_HW_I2S2 (1) + // CAN buses #define MICROPY_HW_CAN1_NAME "YA" #define MICROPY_HW_CAN1_TX (pin_B9) // Y4 diff --git a/ports/stm32/boards/PYBV11/mpconfigboard.h b/ports/stm32/boards/PYBV11/mpconfigboard.h index 8e84694048..aec83d1349 100644 --- a/ports/stm32/boards/PYBV11/mpconfigboard.h +++ b/ports/stm32/boards/PYBV11/mpconfigboard.h @@ -11,6 +11,7 @@ #define MICROPY_HW_ENABLE_DAC (1) #define MICROPY_HW_ENABLE_USB (1) #define MICROPY_HW_ENABLE_SDCARD (1) +#define MICROPY_HW_ENABLE_I2S (1) // HSE is 12MHz #define MICROPY_HW_CLK_PLLM (12) @@ -64,6 +65,9 @@ #define MICROPY_HW_SPI2_MISO (pin_B14) // Y7 #define MICROPY_HW_SPI2_MOSI (pin_B15) // Y8 +// I2S buses +#define MICROPY_HW_I2S2 (1) + // CAN buses #define MICROPY_HW_CAN1_NAME "YA" #define MICROPY_HW_CAN1_TX (pin_B9) // Y4 diff --git a/ports/stm32/boards/make-pins.py b/ports/stm32/boards/make-pins.py index 70213de6ac..d898832f09 100755 --- a/ports/stm32/boards/make-pins.py +++ b/ports/stm32/boards/make-pins.py @@ -22,7 +22,7 @@ SUPPORTED_FN = { CONDITIONAL_VAR = { "I2C": "MICROPY_HW_I2C{num}_SCL", - "I2S": "MICROPY_HW_ENABLE_I2S{num}", + "I2S": "MICROPY_HW_I2S{num}", "SPI": "MICROPY_HW_SPI{num}_SCK", "UART": "MICROPY_HW_UART{num}_TX", "LPUART": "MICROPY_HW_LPUART{num}_TX", diff --git a/ports/stm32/dma.c b/ports/stm32/dma.c index de30886917..ba09dc1707 100644 --- a/ports/stm32/dma.c +++ b/ports/stm32/dma.c @@ -103,6 +103,31 @@ static const DMA_InitTypeDef dma_init_struct_spi_i2c = { #endif }; +#if MICROPY_HW_ENABLE_I2S +// Default parameters to dma_init() for i2s; Channel and Direction +// vary depending on the peripheral instance so they get passed separately +static const DMA_InitTypeDef dma_init_struct_i2s = { + #if defined(STM32F4) || defined(STM32F7) + .Channel = 0, + #elif defined(STM32H7) || defined(STM32L0) || defined(STM32L4) + .Request = 0, + #endif + .Direction = DMA_MEMORY_TO_PERIPH, + .PeriphInc = DMA_PINC_DISABLE, + .MemInc = DMA_MINC_ENABLE, + .PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD, + .MemDataAlignment = DMA_MDATAALIGN_HALFWORD, + .Mode = DMA_CIRCULAR, + .Priority = DMA_PRIORITY_LOW, + #if defined(STM32F4) || defined(STM32F7) || defined(STM32H7) + .FIFOMode = DMA_FIFOMODE_DISABLE, + .FIFOThreshold = DMA_FIFO_THRESHOLD_FULL, + .MemBurst = DMA_MBURST_SINGLE, + .PeriphBurst = DMA_PBURST_SINGLE + #endif +}; +#endif + #if ENABLE_SDIO && !defined(STM32H7) // Parameters to dma_init() for SDIO tx and rx. static const DMA_InitTypeDef dma_init_struct_sdio = { @@ -242,6 +267,10 @@ const dma_descr_t dma_I2C_3_RX = { DMA1_Stream2, DMA_CHANNEL_3, dma_id_2, &dma const dma_descr_t dma_I2C_2_RX = { DMA1_Stream2, DMA_CHANNEL_7, dma_id_2, &dma_init_struct_spi_i2c }; const dma_descr_t dma_SPI_2_RX = { DMA1_Stream3, DMA_CHANNEL_0, dma_id_3, &dma_init_struct_spi_i2c }; const dma_descr_t dma_SPI_2_TX = { DMA1_Stream4, DMA_CHANNEL_0, dma_id_4, &dma_init_struct_spi_i2c }; +#if MICROPY_HW_ENABLE_I2S +const dma_descr_t dma_I2S_2_RX = { DMA1_Stream3, DMA_CHANNEL_0, dma_id_3, &dma_init_struct_i2s }; +const dma_descr_t dma_I2S_2_TX = { DMA1_Stream4, DMA_CHANNEL_0, dma_id_4, &dma_init_struct_i2s }; +#endif const dma_descr_t dma_I2C_3_TX = { DMA1_Stream4, DMA_CHANNEL_3, dma_id_4, &dma_init_struct_spi_i2c }; #if defined(STM32F7) const dma_descr_t dma_I2C_4_TX = { DMA1_Stream5, DMA_CHANNEL_2, dma_id_5, &dma_init_struct_spi_i2c }; @@ -266,6 +295,9 @@ const dma_descr_t dma_SDMMC_2 = { DMA2_Stream0, DMA_CHANNEL_11, dma_id_8, &dma_ const dma_descr_t dma_DCMI_0 = { DMA2_Stream1, DMA_CHANNEL_1, dma_id_9, &dma_init_struct_dcmi }; #endif const dma_descr_t dma_SPI_1_RX = { DMA2_Stream2, DMA_CHANNEL_3, dma_id_10, &dma_init_struct_spi_i2c }; +#if MICROPY_HW_ENABLE_I2S +const dma_descr_t dma_I2S_1_RX = { DMA2_Stream2, DMA_CHANNEL_3, dma_id_10, &dma_init_struct_i2s }; +#endif const dma_descr_t dma_SPI_5_RX = { DMA2_Stream3, DMA_CHANNEL_2, dma_id_11, &dma_init_struct_spi_i2c }; #if ENABLE_SDIO const dma_descr_t dma_SDIO_0 = { DMA2_Stream3, DMA_CHANNEL_4, dma_id_11, &dma_init_struct_sdio }; @@ -275,6 +307,9 @@ const dma_descr_t dma_SPI_5_TX = { DMA2_Stream4, DMA_CHANNEL_2, dma_id_12, &dma const dma_descr_t dma_SPI_4_TX = { DMA2_Stream4, DMA_CHANNEL_5, dma_id_12, &dma_init_struct_spi_i2c }; const dma_descr_t dma_SPI_6_TX = { DMA2_Stream5, DMA_CHANNEL_1, dma_id_13, &dma_init_struct_spi_i2c }; const dma_descr_t dma_SPI_1_TX = { DMA2_Stream5, DMA_CHANNEL_3, dma_id_13, &dma_init_struct_spi_i2c }; +#if MICROPY_HW_ENABLE_I2S +const dma_descr_t dma_I2S_1_TX = { DMA2_Stream5, DMA_CHANNEL_3, dma_id_13, &dma_init_struct_i2s }; +#endif // #if defined(STM32F7) && defined(SDMMC2) && ENABLE_SDIO // const dma_descr_t dma_SDMMC_2 = { DMA2_Stream5, DMA_CHANNEL_11, dma_id_13, &dma_init_struct_sdio }; // #endif diff --git a/ports/stm32/dma.h b/ports/stm32/dma.h index 5a17276a49..8bd101a611 100644 --- a/ports/stm32/dma.h +++ b/ports/stm32/dma.h @@ -57,6 +57,10 @@ extern const dma_descr_t dma_SDMMC_2; extern const dma_descr_t dma_SPI_6_RX; extern const dma_descr_t dma_SDIO_0; extern const dma_descr_t dma_DCMI_0; +extern const dma_descr_t dma_I2S_1_RX; +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(STM32L0) diff --git a/ports/stm32/machine_i2s.c b/ports/stm32/machine_i2s.c new file mode 100644 index 0000000000..d663767bf3 --- /dev/null +++ b/ports/stm32/machine_i2s.c @@ -0,0 +1,1126 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2015 Bryan Morrissey + * Copyright (c) 2021 Mike Teachman + * + * 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 "pin.h" +#include "dma.h" + +#if MICROPY_HW_ENABLE_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 callbacks (1/2 complete and complete) are 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 HAL I2S Generic Driver +// - all sample data transfers use DMA +// - the DMA controller is configured in Circular mode to fulfil continuous and gapless sample flows +// - 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. Not all STM32 devices have a D-Cache. Buffer alignment +// will still happen on these devices to keep this code simple. + +#define MAX_I2S_STM32 (2) + +// 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) + +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; + uint16_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_HandleTypeDef hi2s; + DMA_HandleTypeDef hdma_tx; + DMA_HandleTypeDef hdma_rx; + const dma_descr_t *dma_descr_tx; + const dma_descr_t *dma_descr_rx; +} machine_i2s_obj_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] = { + { 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 + { 2, 3, 0, 1, 6, 7, 4, 5 }, // Stereo, 32-bits +}; + +STATIC machine_i2s_obj_t *machine_i2s_obj[MAX_I2S_STM32]; + +void machine_i2s_init0() { + for (uint8_t i = 0; i < MAX_I2S_STM32; i++) { + 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; +} + +// For 32-bit audio samples, the STM32 HAL API expects each 32-bit sample to be encoded +// in an unusual byte ordering: Byte_2, Byte_3, Byte_0, Byte_1 +// where: Byte_0 is the least significant byte of the 32-bit sample +// +// The following function takes a buffer containing 32-bits sample values formatted as little endian +// and performs an in-place modification into the STM32 HAL API convention +// +// Example: +// +// wav_samples[] = [L_0-7, L_8-15, L_16-23, L_24-31, R_0-7, R_8-15, R_16-23, R_24-31] = [Left channel, Right channel] +// stm_api[] = [L_16-23, L_24-31, L_0-7, L_8-15, R_16-23, R_24-31, R_0-7, R_8-15] = [Left channel, Right channel] +// +// where: +// L_0-7 is the least significant byte of the 32 bit sample in the Left channel +// L_24-31 is the most significant byte of the 32 bit sample in the Left channel +// +// wav_samples[] = [0x99, 0xBB, 0x11, 0x22, 0x44, 0x55, 0xAB, 0x77] = [Left channel, Right channel] +// stm_api[] = [0x11, 0x22, 0x99, 0xBB, 0xAB, 0x77, 0x44, 0x55] = [Left channel, Right channel] +// +// where: +// LEFT Channel = 0x99, 0xBB, 0x11, 0x22 +// RIGHT Channel = 0x44, 0x55, 0xAB, 0x77 +STATIC void reformat_32_bit_samples(int32_t *sample, uint32_t num_samples) { + int16_t sample_ms; + int16_t sample_ls; + for (uint32_t i = 0; i < num_samples; i++) { + sample_ls = sample[i] & 0xFFFF; + sample_ms = sample[i] >> 16; + sample[i] = (sample_ls << 16) + sample_ms; + } +} + +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 == I2S_MODE_MASTER_TX) { + if (bits == 16) { + return I2S_DATAFORMAT_16B; + } else { + return I2S_DATAFORMAT_32B; + } + return bits; + } else { // Master Rx + // always read 32 bit words for I2S e.g. I2S MEMS microphones + return I2S_DATAFORMAT_32B; + } +} + +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. + // STM32 HAL API has a stereo I2S implementation, but not mono + // 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]); + } + } + + // reformat 32 bit samples to match STM32 HAL API format + if (self->bits == 32) { + reformat_32_bit_samples((int32_t *)dma_buffer_p, SIZEOF_HALF_DMA_BUFFER_IN_BYTES / (sizeof(uint32_t))); + } + } + + // 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 bool i2s_init(machine_i2s_obj_t *self) { + + // init the GPIO lines + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; + GPIO_InitStructure.Speed = GPIO_SPEED_FAST; + GPIO_InitStructure.Pull = GPIO_PULLUP; + + if (self->i2s_id == 1) { + self->hi2s.Instance = I2S1; + __SPI1_CLK_ENABLE(); + // configure DMA streams + if (self->mode == I2S_MODE_MASTER_RX) { + self->dma_descr_rx = &dma_I2S_1_RX; + } else { + self->dma_descr_tx = &dma_I2S_1_TX; + } + } else if (self->i2s_id == 2) { + self->hi2s.Instance = I2S2; + __SPI2_CLK_ENABLE(); + // configure DMA streams + if (self->mode == I2S_MODE_MASTER_RX) { + self->dma_descr_rx = &dma_I2S_2_RX; + } else { + self->dma_descr_tx = &dma_I2S_2_TX; + } + } else { + // invalid id number; should not get here as i2s object should not + // have been created without setting a valid i2s instance number + return false; + } + + // GPIO Pin initialization + if (self->sck != MP_OBJ_TO_PTR(MP_OBJ_NULL)) { + GPIO_InitStructure.Pin = self->sck->pin_mask; + const pin_af_obj_t *af = pin_find_af(self->sck, AF_FN_I2S, self->i2s_id); + GPIO_InitStructure.Alternate = (uint8_t)af->idx; + HAL_GPIO_Init(self->sck->gpio, &GPIO_InitStructure); + } + + if (self->ws != MP_OBJ_TO_PTR(MP_OBJ_NULL)) { + GPIO_InitStructure.Pin = self->ws->pin_mask; + const pin_af_obj_t *af = pin_find_af(self->ws, AF_FN_I2S, self->i2s_id); + GPIO_InitStructure.Alternate = (uint8_t)af->idx; + HAL_GPIO_Init(self->ws->gpio, &GPIO_InitStructure); + } + + if (self->sd != MP_OBJ_TO_PTR(MP_OBJ_NULL)) { + GPIO_InitStructure.Pin = self->sd->pin_mask; + const pin_af_obj_t *af = pin_find_af(self->sd, AF_FN_I2S, self->i2s_id); + GPIO_InitStructure.Alternate = (uint8_t)af->idx; + HAL_GPIO_Init(self->sd->gpio, &GPIO_InitStructure); + } + + if (HAL_I2S_Init(&self->hi2s) == HAL_OK) { + // Reset and initialize Tx and Rx DMA channels + if (self->mode == I2S_MODE_MASTER_RX) { + dma_invalidate_channel(self->dma_descr_rx); + dma_init(&self->hdma_rx, self->dma_descr_rx, DMA_PERIPH_TO_MEMORY, &self->hi2s); + self->hi2s.hdmarx = &self->hdma_rx; + } else { // I2S_MODE_MASTER_TX + dma_invalidate_channel(self->dma_descr_tx); + dma_init(&self->hdma_tx, self->dma_descr_tx, DMA_MEMORY_TO_PERIPH, &self->hi2s); + self->hi2s.hdmatx = &self->hdma_tx; + } + + __HAL_RCC_PLLI2S_ENABLE(); // start I2S clock + + return true; + } else { + return false; + } + +} + +void HAL_I2S_ErrorCallback(I2S_HandleTypeDef *hi2s) { + uint32_t errorCode = HAL_I2S_GetError(hi2s); + printf("I2S Error = %ld\n", errorCode); +} + +void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *hi2s) { + machine_i2s_obj_t *self; + if (hi2s->Instance == I2S1) { + self = machine_i2s_obj[0]; + } else { + self = machine_i2s_obj[1]; + } + + // 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); + + // for non-blocking operation, this IRQ-based callback handles + // the readinto() method requests. + if ((self->io_mode == NON_BLOCKING) && (self->non_blocking_descriptor.copy_in_progress)) { + fill_appbuf_from_ringbuf_non_blocking(self); + } +} + +void HAL_I2S_RxHalfCpltCallback(I2S_HandleTypeDef *hi2s) { + machine_i2s_obj_t *self; + if (hi2s->Instance == I2S1) { + self = machine_i2s_obj[0]; + } else { + self = machine_i2s_obj[1]; + } + + // 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 operation, this IRQ-based callback handles + // the readinto() method requests. + if ((self->io_mode == NON_BLOCKING) && (self->non_blocking_descriptor.copy_in_progress)) { + fill_appbuf_from_ringbuf_non_blocking(self); + } +} + +void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s) { + machine_i2s_obj_t *self; + + if (hi2s->Instance == I2S1) { + self = machine_i2s_obj[0]; + } else { + self = machine_i2s_obj[1]; + } + + // for non-blocking operation, this IRQ-based callback handles + // the write() method requests. + if ((self->io_mode == NON_BLOCKING) && (self->non_blocking_descriptor.copy_in_progress)) { + copy_appbuf_to_ringbuf_non_blocking(self); + } + + // 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); +} + +void HAL_I2S_TxHalfCpltCallback(I2S_HandleTypeDef *hi2s) { + machine_i2s_obj_t *self; + if (hi2s->Instance == I2S1) { + self = machine_i2s_obj[0]; + } else { + self = machine_i2s_obj[1]; + } + + // for non-blocking operation, this IRQ-based callback handles + // the write() method requests. + if ((self->io_mode == NON_BLOCKING) && (self->non_blocking_descriptor.copy_in_progress)) { + copy_appbuf_to_ringbuf_non_blocking(self); + } + + // 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); +} + +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_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_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); + + memset(&self->hi2s, 0, sizeof(self->hi2s)); + + // + // ---- Check validity of arguments ---- + // + + // are I2S pin assignments valid? + const pin_af_obj_t *pin_af; + + // is SCK valid? + if (mp_obj_is_type(args[ARG_sck].u_obj, &pin_type)) { + pin_af = pin_find_af(MP_OBJ_TO_PTR(args[ARG_sck].u_obj), AF_FN_I2S, self->i2s_id); + if (pin_af->type != AF_PIN_TYPE_I2S_CK) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid SCK pin")); + } + } else { + mp_raise_ValueError(MP_ERROR_TEXT("SCK not a Pin type")); + } + + // is WS valid? + if (mp_obj_is_type(args[ARG_ws].u_obj, &pin_type)) { + pin_af = pin_find_af(MP_OBJ_TO_PTR(args[ARG_ws].u_obj), AF_FN_I2S, self->i2s_id); + if (pin_af->type != AF_PIN_TYPE_I2S_WS) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid WS pin")); + } + } else { + mp_raise_ValueError(MP_ERROR_TEXT("WS not a Pin type")); + } + + // is SD valid? + if (mp_obj_is_type(args[ARG_sd].u_obj, &pin_type)) { + pin_af = pin_find_af(MP_OBJ_TO_PTR(args[ARG_sd].u_obj), AF_FN_I2S, self->i2s_id); + if (pin_af->type != AF_PIN_TYPE_I2S_SD) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid SD pin")); + } + } else { + mp_raise_ValueError(MP_ERROR_TEXT("SD not a Pin type")); + } + + // is Mode valid? + uint16_t i2s_mode = args[ARG_mode].u_int; + if ((i2s_mode != (I2S_MODE_MASTER_RX)) && + (i2s_mode != (I2S_MODE_MASTER_TX))) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid mode")); + } + + // 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? + // Not checked + + // 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 = MP_OBJ_TO_PTR(args[ARG_sck].u_obj); + self->ws = MP_OBJ_TO_PTR(args[ARG_ws].u_obj); + self->sd = MP_OBJ_TO_PTR(args[ARG_sd].u_obj); + self->mode = i2s_mode; + self->bits = i2s_bits; + self->format = i2s_format; + self->rate = args[ARG_rate].u_int; + 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; + + I2S_InitTypeDef *init = &self->hi2s.Init; + init->Mode = i2s_mode; + init->Standard = I2S_STANDARD_PHILIPS; + init->DataFormat = get_dma_bits(self->mode, self->bits); + init->MCLKOutput = I2S_MCLKOUTPUT_DISABLE; + init->AudioFreq = args[ARG_rate].u_int; + init->CPOL = I2S_CPOL_LOW; + init->ClockSource = I2S_CLOCK_PLL; + + // init the I2S bus + if (!i2s_init(self)) { + mp_raise_msg_varg(&mp_type_OSError, MP_ERROR_TEXT("I2S init failed")); + } + + // start DMA. DMA is configured to run continuously, using a circular buffer configuration + uint32_t number_of_samples = 0; + if (init->DataFormat == I2S_DATAFORMAT_16B) { + number_of_samples = SIZEOF_DMA_BUFFER_IN_BYTES / sizeof(uint16_t); + } else { // 32 bits + number_of_samples = SIZEOF_DMA_BUFFER_IN_BYTES / sizeof(uint32_t); + } + + HAL_StatusTypeDef status; + if (self->mode == I2S_MODE_MASTER_TX) { + status = HAL_I2S_Transmit_DMA(&self->hi2s, (void *)self->dma_buffer_dcache_aligned, number_of_samples); + } else { // RX + status = HAL_I2S_Receive_DMA(&self->hi2s, (void *)self->dma_buffer_dcache_aligned, number_of_samples); + } + + if (status != HAL_OK) { + mp_raise_msg_varg(&mp_type_OSError, MP_ERROR_TEXT("DMA 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" + "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), + 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]); + uint8_t i2s_id_zero_base = 0; + + if (0) { + #ifdef MICROPY_HW_I2S1 + } else if (i2s_id == 1) { + i2s_id_zero_base = 0; + #endif + #ifdef MICROPY_HW_I2S2 + } else if (i2s_id == 2) { + i2s_id_zero_base = 1; + #endif + } else { + mp_raise_ValueError(MP_ERROR_TEXT("invalid id")); + } + + machine_i2s_obj_t *self; + if (machine_i2s_obj[i2s_id_zero_base] == NULL) { + self = m_new_obj(machine_i2s_obj_t); + machine_i2s_obj[i2s_id_zero_base] = self; + self->base.type = &machine_i2s_type; + self->i2s_id = i2s_id; + } else { + self = machine_i2s_obj[i2s_id_zero_base]; + machine_i2s_deinit(MP_OBJ_FROM_PTR(self)); + } + + // align DMA buffer start to the cache line size (32 bytes) + self->dma_buffer_dcache_aligned = (uint8_t *)((uint32_t)(self->dma_buffer + 0x1f) & ~0x1f); + + 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); + + 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(); + } + + m_free(self->ring_buffer_storage); + + 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(I2S_MODE_MASTER_RX) }, + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_INT(I2S_MODE_MASTER_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 != I2S_MODE_MASTER_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 != I2S_MODE_MASTER_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 != I2S_MODE_MASTER_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 != I2S_MODE_MASTER_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_HW_ENABLE_I2S + diff --git a/ports/stm32/main.c b/ports/stm32/main.c index 6df7374cce..86b8fb7972 100644 --- a/ports/stm32/main.c +++ b/ports/stm32/main.c @@ -527,6 +527,10 @@ soft_reset: pyb_usb_init0(); #endif + #if MICROPY_HW_ENABLE_I2S + machine_i2s_init0(); + #endif + // Initialise the local flash filesystem. // Create it if needed, mount in on /flash, and set it as current dir. bool mounted_flash = false; diff --git a/ports/stm32/modmachine.c b/ports/stm32/modmachine.c index aee563ee88..66c696982b 100644 --- a/ports/stm32/modmachine.c +++ b/ports/stm32/modmachine.c @@ -431,6 +431,9 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&machine_hard_spi_type) }, { MP_ROM_QSTR(MP_QSTR_SoftSPI), MP_ROM_PTR(&mp_machine_soft_spi_type) }, #endif + #if MICROPY_HW_ENABLE_I2S + { MP_ROM_QSTR(MP_QSTR_I2S), MP_ROM_PTR(&machine_i2s_type) }, + #endif { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&pyb_uart_type) }, { MP_ROM_QSTR(MP_QSTR_WDT), MP_ROM_PTR(&pyb_wdt_type) }, { MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&machine_timer_type) }, diff --git a/ports/stm32/modmachine.h b/ports/stm32/modmachine.h index e846208540..0e6c000a80 100644 --- a/ports/stm32/modmachine.h +++ b/ports/stm32/modmachine.h @@ -31,9 +31,11 @@ extern const mp_obj_type_t machine_adc_type; extern const mp_obj_type_t machine_timer_type; extern const mp_obj_type_t machine_hard_i2c_type; +extern const mp_obj_type_t machine_i2s_type; void machine_init(void); void machine_deinit(void); +void machine_i2s_init0(); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(machine_info_obj); MP_DECLARE_CONST_FUN_OBJ_0(machine_unique_id_obj); diff --git a/ports/stm32/pin_defs_stm32.h b/ports/stm32/pin_defs_stm32.h index 9285c190a6..d90ef1bb6d 100644 --- a/ports/stm32/pin_defs_stm32.h +++ b/ports/stm32/pin_defs_stm32.h @@ -109,6 +109,7 @@ enum { // some #defines to massage things. Also I2S and SPI share the same // peripheral. +#define GPIO_AF5_I2S1 GPIO_AF5_SPI1 #define GPIO_AF5_I2S2 GPIO_AF5_SPI2 #define GPIO_AF5_I2S3 GPIO_AF5_I2S3ext #define GPIO_AF6_I2S2 GPIO_AF6_I2S2ext @@ -116,6 +117,7 @@ enum { #define GPIO_AF7_I2S2 GPIO_AF7_SPI2 #define GPIO_AF7_I2S3 GPIO_AF7_I2S3ext +#define I2S1 SPI1 #define I2S2 SPI2 #define I2S3 SPI3 From 3966f67746abef0bf475003c7fe889648be38991 Mon Sep 17 00:00:00 2001 From: Julia Date: Mon, 14 Jun 2021 16:57:29 -0500 Subject: [PATCH 058/264] zephyr/machine_spi: Add support for hardware SPI. Adds support for hardware SPI to the zephyr port. Consistent with other ports, such as rp2 and stm32, we only implement the SPI protocol functions (init and transfer). Explicit sck/mosi/miso selection is not supported and new SPI instances are initialized with default values. --- ports/zephyr/CMakeLists.txt | 1 + ports/zephyr/README.md | 9 + ports/zephyr/boards/frdm_k64f.conf | 1 + ports/zephyr/boards/mimxrt685_evk_cm33.conf | 2 + ports/zephyr/machine_spi.c | 211 ++++++++++++++++++++ ports/zephyr/modmachine.c | 4 + ports/zephyr/modmachine.h | 1 + ports/zephyr/mpconfigport.h | 4 + 8 files changed, 233 insertions(+) create mode 100644 ports/zephyr/boards/mimxrt685_evk_cm33.conf create mode 100644 ports/zephyr/machine_spi.c diff --git a/ports/zephyr/CMakeLists.txt b/ports/zephyr/CMakeLists.txt index 80adea5d86..5c09fc76ee 100644 --- a/ports/zephyr/CMakeLists.txt +++ b/ports/zephyr/CMakeLists.txt @@ -38,6 +38,7 @@ set(MICROPY_SOURCE_PORT main.c help.c machine_i2c.c + machine_spi.c machine_pin.c machine_uart.c modbluetooth_zephyr.c diff --git a/ports/zephyr/README.md b/ports/zephyr/README.md index 9d6c223b47..e0b02af222 100644 --- a/ports/zephyr/README.md +++ b/ports/zephyr/README.md @@ -15,6 +15,7 @@ Features supported at this time: * `utime` module for time measurements and delays. * `machine.Pin` class for GPIO control, with IRQ support. * `machine.I2C` class for I2C control. +* `machine.SPI` class for SPI control. * `usocket` module for networking (IPv4/IPv6). * "Frozen modules" support to allow to bundle Python modules together with firmware. Including complete applications, including with @@ -135,6 +136,14 @@ Example of using I2C to scan for I2C slaves: i2c = I2C("I2C_0") i2c.scan() +Example of using SPI to write a buffer to the MOSI pin: + + from machine import SPI + + spi = SPI("SPI_0") + spi.init(baudrate=500000, polarity=1, phase=1, bits=8, firstbit=SPI.MSB) + spi.write(b'abcd') + Minimal build ------------- diff --git a/ports/zephyr/boards/frdm_k64f.conf b/ports/zephyr/boards/frdm_k64f.conf index 483e9a29bb..c164c0c32c 100644 --- a/ports/zephyr/boards/frdm_k64f.conf +++ b/ports/zephyr/boards/frdm_k64f.conf @@ -3,6 +3,7 @@ CONFIG_NET_L2_ETHERNET=y # Hardware features CONFIG_I2C=y +CONFIG_SPI=y # Sensor drivers CONFIG_FXOS8700=y diff --git a/ports/zephyr/boards/mimxrt685_evk_cm33.conf b/ports/zephyr/boards/mimxrt685_evk_cm33.conf new file mode 100644 index 0000000000..827222b887 --- /dev/null +++ b/ports/zephyr/boards/mimxrt685_evk_cm33.conf @@ -0,0 +1,2 @@ +# Hardware features +CONFIG_SPI=y diff --git a/ports/zephyr/machine_spi.c b/ports/zephyr/machine_spi.c new file mode 100644 index 0000000000..2add7ff60b --- /dev/null +++ b/ports/zephyr/machine_spi.c @@ -0,0 +1,211 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright 2021 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 +#include +#include + +#include +#include + +#include "py/runtime.h" +#include "py/gc.h" +#include "py/mphal.h" +#include "py/mperrno.h" +#include "extmod/machine_spi.h" +#include "modmachine.h" + +#if MICROPY_PY_MACHINE_SPI + +#define DEFAULT_SPI_BAUDRATE (50000) +#define DEFAULT_SPI_POLARITY (0) +#define DEFAULT_SPI_PHASE (0) +#define DEFAULT_SPI_BITS (8) +#define DEFAULT_SPI_FIRSTBIT (SPI_TRANSFER_MSB) +#define SPI_LOOP (0) // For testing, enable loop mode by setting SPI_LOOP (1) + +typedef struct _machine_hard_spi_obj_t { + mp_obj_base_t base; + const struct device *dev; + struct spi_config config; +} machine_hard_spi_obj_t; + +STATIC void machine_hard_spi_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_hard_spi_obj_t *self = self_in; + mp_printf(print, "SPI(%s, baudrate=%u, polarity=%u, phase=%u, bits=%u, firstbit=%s)", + self->dev->name, + self->config.frequency, + (self->config.operation & 0x2) >> 1, + (self->config.operation & 0x4) >> 2, + (self->config.operation & ~0x1F) >> 5, + ((self->config.operation & 0x10) >> 4) == SPI_TRANSFER_MSB ? "MSB" : "LSB"); +} + +mp_obj_t machine_hard_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_sck, ARG_mosi, ARG_miso}; + + 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} }, + { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_SPI_POLARITY} }, + { MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_SPI_PHASE} }, + { MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_SPI_BITS} }, + { MP_QSTR_firstbit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_SPI_FIRSTBIT} }, + { MP_QSTR_sck, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_mosi, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_miso, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + }; + + 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); + + const char *dev_name = mp_obj_str_get_str(args[ARG_id].u_obj); + const struct device *dev = device_get_binding(dev_name); + + if (dev == NULL) { + mp_raise_ValueError(MP_ERROR_TEXT("device not found")); + } + + if ((args[ARG_sck].u_obj != MP_OBJ_NULL) || (args[ARG_miso].u_obj != MP_OBJ_NULL) || (args[ARG_mosi].u_obj != MP_OBJ_NULL)) { + mp_raise_NotImplementedError(MP_ERROR_TEXT("explicit choice of sck/miso/mosi is not implemented")); + } + + struct spi_config cfg = { + .frequency = args[ARG_baudrate].u_int, + .operation = (SPI_OP_MODE_MASTER | + args[ARG_polarity].u_int << 1 | + args[ARG_phase].u_int << 2 | + SPI_LOOP << 3 | + args[ARG_firstbit].u_int << 4 | + args[ARG_bits].u_int << 5 | + SPI_LINES_SINGLE), + .slave = 0, + .cs = NULL + }; + + machine_hard_spi_obj_t *self = m_new_obj(machine_hard_spi_obj_t); + + self->base.type = &machine_hard_spi_type; + self->dev = dev; + self->config = cfg; + + return MP_OBJ_FROM_PTR(self); +} + +STATIC void machine_hard_spi_init(mp_obj_base_t *obj, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum {ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit}; + + static const mp_arg_t allowed_args[] = { + { MP_QSTR_baudrate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_firstbit, 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); + + machine_hard_spi_obj_t *self = (machine_hard_spi_obj_t *)obj; + + uint32_t baudrate; + uint16_t operation = self->config.operation; + + if (args[ARG_baudrate].u_int != -1) { + baudrate = args[ARG_baudrate].u_int; + } else { + baudrate = self->config.frequency; + } + + if (args[ARG_polarity].u_int != -1) { + operation = (operation & ~0x2) | (args[ARG_polarity].u_int << 1); + } + + if (args[ARG_phase].u_int != -1) { + operation = (operation & ~0x4) | (args[ARG_phase].u_int << 2); + } + + if (args[ARG_bits].u_int != -1) { + operation = (operation & 0x1F) | (args[ARG_bits].u_int << 5); + } + + if (args[ARG_firstbit].u_int != -1) { + operation = (operation & ~0x10) | (args[ARG_firstbit].u_int << 4); + } + + struct spi_config cfg = { + .frequency = baudrate, + .operation = operation, + .slave = 0, + .cs = NULL + }; + + self->config = cfg; +} + +STATIC void machine_hard_spi_transfer(mp_obj_base_t *obj, size_t len, const uint8_t *src, uint8_t *dest) { + machine_hard_spi_obj_t *self = (machine_hard_spi_obj_t *)obj; + + int ret; + + struct spi_buf tx_bufs[1]; + tx_bufs[0].buf = (uint8_t *)src; + tx_bufs[0].len = len; + const struct spi_buf_set tx = { + .buffers = tx_bufs, + .count = ARRAY_SIZE(tx_bufs) + }; + + struct spi_buf rx_bufs[1]; + rx_bufs[0].buf = dest; + rx_bufs[0].len = len; + const struct spi_buf_set rx = { + .buffers = rx_bufs, + .count = ARRAY_SIZE(rx_bufs) + }; + + ret = spi_transceive(self->dev, &self->config, &tx, &rx); + + if (ret < 0) { + mp_raise_OSError(-ret); + } +} + +STATIC const mp_machine_spi_p_t machine_hard_spi_p = { + .init = machine_hard_spi_init, + .transfer = machine_hard_spi_transfer, +}; + +const mp_obj_type_t machine_hard_spi_type = { + { &mp_type_type }, + .name = MP_QSTR_SPI, + .print = machine_hard_spi_print, + .make_new = machine_hard_spi_make_new, + .protocol = &machine_hard_spi_p, + .locals_dict = (mp_obj_dict_t *)&mp_machine_spi_locals_dict, +}; + +#endif // MICROPY_PY_MACHINE_SPI diff --git a/ports/zephyr/modmachine.c b/ports/zephyr/modmachine.c index 4353083eeb..adec9160be 100644 --- a/ports/zephyr/modmachine.c +++ b/ports/zephyr/modmachine.c @@ -36,6 +36,7 @@ #include "extmod/machine_signal.h" #include "extmod/machine_pulse.h" #include "extmod/machine_i2c.h" +#include "extmod/machine_spi.h" #include "modmachine.h" #if MICROPY_PY_MACHINE @@ -70,6 +71,9 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { #if MICROPY_PY_MACHINE_I2C { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&machine_hard_i2c_type) }, #endif + #if MICROPY_PY_MACHINE_SPI + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&machine_hard_spi_type) }, + #endif { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&machine_uart_type) }, { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&machine_pin_type) }, { MP_ROM_QSTR(MP_QSTR_Signal), MP_ROM_PTR(&machine_signal_type) }, diff --git a/ports/zephyr/modmachine.h b/ports/zephyr/modmachine.h index 957f2dd4f9..a3cdd2b30f 100644 --- a/ports/zephyr/modmachine.h +++ b/ports/zephyr/modmachine.h @@ -5,6 +5,7 @@ extern const mp_obj_type_t machine_pin_type; extern const mp_obj_type_t machine_hard_i2c_type; +extern const mp_obj_type_t machine_hard_spi_type; extern const mp_obj_type_t machine_uart_type; MP_DECLARE_CONST_FUN_OBJ_0(machine_info_obj); diff --git a/ports/zephyr/mpconfigport.h b/ports/zephyr/mpconfigport.h index c7f4653003..501c39a1bb 100644 --- a/ports/zephyr/mpconfigport.h +++ b/ports/zephyr/mpconfigport.h @@ -29,6 +29,7 @@ #include "autoconf.h" // Included here to get basic Zephyr environment (macros, etc.) #include +#include // Usually passed from Makefile #ifndef MICROPY_HEAP_SIZE @@ -62,6 +63,9 @@ #define MICROPY_PY_MICROPYTHON_MEM_INFO (1) #define MICROPY_PY_MACHINE (1) #define MICROPY_PY_MACHINE_I2C (1) +#define MICROPY_PY_MACHINE_SPI (1) +#define MICROPY_PY_MACHINE_SPI_MSB (SPI_TRANSFER_MSB) +#define MICROPY_PY_MACHINE_SPI_LSB (SPI_TRANSFER_LSB) #define MICROPY_PY_MACHINE_PIN_MAKE_NEW mp_pin_make_new #define MICROPY_MODULE_WEAK_LINKS (1) #define MICROPY_PY_STRUCT (0) From d934f8c8a8bb896595a6b976126222b438f383ea Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 5 Jul 2021 10:37:34 -0500 Subject: [PATCH 059/264] py/makeversionhdr: Add --tags arg to git describe. This adds the --tags argument to the git describe command that is used to define the MICROPY_GIT_TAG macro. This makes it match non-annotated tags. This is useful for MicroPython derivatives that don't use annotated tags. Signed-off-by: David Lechner --- py/makeversionhdr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/makeversionhdr.py b/py/makeversionhdr.py index 2f4bc91826..54b7fa9ab7 100644 --- a/py/makeversionhdr.py +++ b/py/makeversionhdr.py @@ -23,7 +23,7 @@ def get_version_info_from_git(): # Note: git describe doesn't work if no tag is available try: git_tag = subprocess.check_output( - ["git", "describe", "--dirty", "--always", "--match", "v[1-9].*"], + ["git", "describe", "--tags", "--dirty", "--always", "--match", "v[1-9].*"], stderr=subprocess.STDOUT, universal_newlines=True, ).strip() From c1f74b300548912f92edb4ae004dc486e0b7c8bf Mon Sep 17 00:00:00 2001 From: Tom McDermott Date: Mon, 5 Aug 2019 21:09:14 +1000 Subject: [PATCH 060/264] docs/library: Warn that ustruct doesn't handle spaces in format strings. And also add a test to capture the CPython difference. --- docs/library/ustruct.rst | 5 +++++ .../cpydiff/modules_struct_whitespace_in_format.py | 13 +++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 tests/cpydiff/modules_struct_whitespace_in_format.py diff --git a/docs/library/ustruct.rst b/docs/library/ustruct.rst index 357d622b2b..bfcd84e2d8 100644 --- a/docs/library/ustruct.rst +++ b/docs/library/ustruct.rst @@ -12,6 +12,11 @@ Supported format codes: ``b``, ``B``, ``h``, ``H``, ``i``, ``I``, ``l``, ``L``, ``q``, ``Q``, ``s``, ``P``, ``f``, ``d`` (the latter 2 depending on the floating-point support). +.. admonition:: Difference to CPython + :class: attention + + Whitespace is not supported in format strings. + Functions --------- diff --git a/tests/cpydiff/modules_struct_whitespace_in_format.py b/tests/cpydiff/modules_struct_whitespace_in_format.py new file mode 100644 index 0000000000..dd0f8b48fc --- /dev/null +++ b/tests/cpydiff/modules_struct_whitespace_in_format.py @@ -0,0 +1,13 @@ +""" +categories: Modules,struct +description: Struct pack with whitespace in format, whitespace ignored by CPython, error on uPy +cause: MicroPython is optimised for code size. +workaround: Don't use spaces in format strings. +""" +import struct + +try: + print(struct.pack('b b', 1, 2)) + print('Should have worked') +except: + print('struct.error') From cd506d6220d322a80d634fcb00d35d7d1aa11989 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Tue, 6 Jul 2021 18:18:57 -0500 Subject: [PATCH 061/264] tests/cpydiff/modules_struct_whitespace_in_format: Run black. This test snuck through without proper formatting and is causing CI for other unrelated changes to fail. Signed-off-by: David Lechner --- tests/cpydiff/modules_struct_whitespace_in_format.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/cpydiff/modules_struct_whitespace_in_format.py b/tests/cpydiff/modules_struct_whitespace_in_format.py index dd0f8b48fc..a882b38569 100644 --- a/tests/cpydiff/modules_struct_whitespace_in_format.py +++ b/tests/cpydiff/modules_struct_whitespace_in_format.py @@ -7,7 +7,7 @@ workaround: Don't use spaces in format strings. import struct try: - print(struct.pack('b b', 1, 2)) - print('Should have worked') + print(struct.pack("b b", 1, 2)) + print("Should have worked") except: - print('struct.error') + print("struct.error") From e10a044d7cdd0aad4c21f1d3cfca03b4a48de8bb Mon Sep 17 00:00:00 2001 From: finefoot <33361833+finefoot@users.noreply.github.com> Date: Mon, 28 Jun 2021 23:36:01 +0200 Subject: [PATCH 062/264] docs/esp8266/tutorial: Change flash mode from dio to dout. For some boards, even -fm dio is too fast and they require -fm dout. This commit links to the esptool wiki about available flash modes and changes dio to dout. --- docs/esp8266/tutorial/intro.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/esp8266/tutorial/intro.rst b/docs/esp8266/tutorial/intro.rst index bbf0457fff..a5deb3532f 100644 --- a/docs/esp8266/tutorial/intro.rst +++ b/docs/esp8266/tutorial/intro.rst @@ -109,10 +109,12 @@ PC. You may also need to reduce the baudrate if you get errors when flashing that you have. For some boards with a particular FlashROM configuration (e.g. some variants of -a NodeMCU board) you may need to use the following command to deploy -the firmware (note the ``-fm dio`` option):: +a NodeMCU board) you may need to manually set a compatible +`SPI Flash Mode `_. +You'd usually pick the fastest option that is compatible with your device, but +the ``-fm dout`` option (the slowest option) should have the best compatibility:: - esptool.py --port /dev/ttyUSB0 --baud 460800 write_flash --flash_size=detect -fm dio 0 esp8266-20170108-v1.8.7.bin + esptool.py --port /dev/ttyUSB0 --baud 460800 write_flash --flash_size=detect -fm dout 0 esp8266-20170108-v1.8.7.bin If the above commands run without error then MicroPython should be installed on your board! From 3d9af877215c9f904a7d64c3fa4da79e5780a818 Mon Sep 17 00:00:00 2001 From: Bryan Tong Minh Date: Sat, 10 Aug 2019 10:47:56 +0200 Subject: [PATCH 063/264] windows/Makefile: Add .exe extension to executables name. Uses the same logic applied in 5b57ae985ff7064dd7b09b0ce891697bfaa5dae2 to determine when to add .exe. See related: #3310, #3361, #3370, #4143, #5727. --- ports/windows/.appveyor.yml | 5 +---- ports/windows/Makefile | 2 +- py/mkrules.mk | 11 +++++++++-- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/ports/windows/.appveyor.yml b/ports/windows/.appveyor.yml index 1ec72bbb38..a4cd1f1e8c 100644 --- a/ports/windows/.appveyor.yml +++ b/ports/windows/.appveyor.yml @@ -63,10 +63,7 @@ after_test: throw "$env:MSYSTEM build exited with code $LASTEXITCODE" } cd (Join-Path $env:APPVEYOR_BUILD_FOLDER 'mpy-cross') - # Building of mpy-cross hasn't been fixed across all possible windows/WSL/... - # variations and the STRIP step tries to strip mpy-cross whereas that should be - # mpy-cross.exe. Workaround for now by skipping actual strip and size commands. - C:\msys64\usr\bin\bash.exe -l -c "make -B -j4 V=1 STRIP=echo SIZE=echo" + C:\msys64\usr\bin\bash.exe -l -c "make -B -j4 V=1" if ($LASTEXITCODE -ne 0) { throw "$env:MSYSTEM mpy_cross build exited with code $LASTEXITCODE" } diff --git a/ports/windows/Makefile b/ports/windows/Makefile index 3bfbc18304..b48c9378b5 100644 --- a/ports/windows/Makefile +++ b/ports/windows/Makefile @@ -2,7 +2,7 @@ include ../../py/mkenv.mk -include mpconfigport.mk # define main target -PROG = micropython.exe +PROG = micropython # qstr definitions (must come before including py.mk) QSTR_DEFS = ../unix/qstrdefsport.h diff --git a/py/mkrules.mk b/py/mkrules.mk index 038a49329b..63f704967f 100644 --- a/py/mkrules.mk +++ b/py/mkrules.mk @@ -178,6 +178,13 @@ endif ifneq ($(PROG),) # Build a standalone executable (unix does this) +# The executable should have an .exe extension for builds targetting 'pure' +# Windows, i.e. msvc or mingw builds, but not when using msys or cygwin's gcc. +COMPILER_TARGET := $(shell $(CC) -dumpmachine) +ifneq (,$(findstring mingw,$(COMPILER_TARGET))) +PROG := $(PROG).exe +endif + all: $(PROG) $(PROG): $(OBJ) @@ -186,9 +193,9 @@ $(PROG): $(OBJ) # we may want to compile using Thumb, but link with non-Thumb libc. $(Q)$(CC) -o $@ $^ $(LIB) $(LDFLAGS) ifndef DEBUG - $(Q)$(STRIP) $(STRIPFLAGS_EXTRA) $(PROG) + $(Q)$(STRIP) $(STRIPFLAGS_EXTRA) $@ endif - $(Q)$(SIZE) $$(find $(BUILD) -path "$(BUILD)/build/frozen*.o") $(PROG) + $(Q)$(SIZE) $$(find $(BUILD) -path "$(BUILD)/build/frozen*.o") $@ clean: clean-prog clean-prog: From 20a6044952281a4f55a5ee83bcaca88ec1b870f9 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 6 Jul 2021 11:23:21 +1000 Subject: [PATCH 064/264] lib/utils/stdout_helpers: Make mp_hal_stdout_tx_strn_cooked efficient. To reduce the number of calls to mp_hal_stdout_tx_strn and improve the overall throughput of printing data. This implementation is taken from ports/stm32/mphalport.c. Signed-off-by: Damien George --- lib/utils/stdout_helpers.c | 46 +++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/lib/utils/stdout_helpers.c b/lib/utils/stdout_helpers.c index 3de1197571..6a24cc2eba 100644 --- a/lib/utils/stdout_helpers.c +++ b/lib/utils/stdout_helpers.c @@ -1,6 +1,30 @@ +/* + * 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. + */ + #include -#include -#include "py/mpconfig.h" #include "py/mphal.h" /* @@ -10,13 +34,25 @@ */ // Send "cooked" string of given length, where every occurrence of -// LF character is replaced with CR LF. +// LF character is replaced with CR LF ("\n" is converted to "\r\n"). +// This is an optimised version to reduce the number of calls made +// to mp_hal_stdout_tx_strn. void mp_hal_stdout_tx_strn_cooked(const char *str, size_t len) { + const char *last = str; while (len--) { if (*str == '\n') { - mp_hal_stdout_tx_strn("\r", 1); + if (str > last) { + mp_hal_stdout_tx_strn(last, str - last); + } + mp_hal_stdout_tx_strn("\r\n", 2); + ++str; + last = str; + } else { + ++str; } - mp_hal_stdout_tx_strn(str++, 1); + } + if (str > last) { + mp_hal_stdout_tx_strn(last, str - last); } } From 132d93886fd7431a739957b0d2e3bd1b7fdcf404 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 6 Jul 2021 11:25:41 +1000 Subject: [PATCH 065/264] ports: Use common mp_hal_stdout_tx_strn_cooked instead of custom one. To reduce code duplication. Signed-off-by: Damien George --- ports/cc3200/application.mk | 1 + ports/cc3200/hal/cc3200_hal.c | 23 ----------------------- ports/esp32/main/CMakeLists.txt | 1 + ports/esp32/mphalport.c | 24 ------------------------ ports/esp8266/Makefile | 1 + ports/esp8266/esp_mphal.c | 23 ----------------------- ports/stm32/Makefile | 1 + ports/stm32/mphalport.c | 24 ------------------------ 8 files changed, 4 insertions(+), 94 deletions(-) diff --git a/ports/cc3200/application.mk b/ports/cc3200/application.mk index 2125274ae1..5c8fc9e40e 100644 --- a/ports/cc3200/application.mk +++ b/ports/cc3200/application.mk @@ -147,6 +147,7 @@ APP_LIB_SRC_C = $(addprefix lib/,\ utils/gchelper_native.c \ utils/pyexec.c \ utils/interrupt_char.c \ + utils/stdout_helpers.c \ utils/sys_stdio_mphal.c \ ) diff --git a/ports/cc3200/hal/cc3200_hal.c b/ports/cc3200/hal/cc3200_hal.c index bdb7d33d4a..3a35e39e1f 100644 --- a/ports/cc3200/hal/cc3200_hal.c +++ b/ports/cc3200/hal/cc3200_hal.c @@ -141,10 +141,6 @@ void mp_hal_delay_ms(mp_uint_t delay) { } } -void mp_hal_stdout_tx_str(const char *str) { - mp_hal_stdout_tx_strn(str, strlen(str)); -} - void mp_hal_stdout_tx_strn(const char *str, size_t len) { if (MP_STATE_PORT(os_term_dup_obj)) { if (mp_obj_is_type(MP_STATE_PORT(os_term_dup_obj)->stream_o, &pyb_uart_type)) { @@ -158,25 +154,6 @@ void mp_hal_stdout_tx_strn(const char *str, size_t len) { telnet_tx_strn(str, len); } -void mp_hal_stdout_tx_strn_cooked (const char *str, size_t len) { - int32_t nslen = 0; - const char *_str = str; - - for (int i = 0; i < len; i++) { - if (str[i] == '\n') { - mp_hal_stdout_tx_strn(_str, nslen); - mp_hal_stdout_tx_strn("\r\n", 2); - _str += nslen + 1; - nslen = 0; - } else { - nslen++; - } - } - if (_str < str + len) { - mp_hal_stdout_tx_strn(_str, nslen); - } -} - int mp_hal_stdin_rx_chr(void) { for ( ;; ) { // read telnet first diff --git a/ports/esp32/main/CMakeLists.txt b/ports/esp32/main/CMakeLists.txt index fb08c2759e..ed72a8d68b 100644 --- a/ports/esp32/main/CMakeLists.txt +++ b/ports/esp32/main/CMakeLists.txt @@ -27,6 +27,7 @@ set(MICROPY_SOURCE_LIB ${MICROPY_DIR}/lib/oofatfs/ffunicode.c ${MICROPY_DIR}/lib/timeutils/timeutils.c ${MICROPY_DIR}/lib/utils/interrupt_char.c + ${MICROPY_DIR}/lib/utils/stdout_helpers.c ${MICROPY_DIR}/lib/utils/sys_stdio_mphal.c ${MICROPY_DIR}/lib/utils/pyexec.c ) diff --git a/ports/esp32/mphalport.c b/ports/esp32/mphalport.c index a0bafb755e..db2146d93d 100644 --- a/ports/esp32/mphalport.c +++ b/ports/esp32/mphalport.c @@ -108,10 +108,6 @@ int mp_hal_stdin_rx_chr(void) { } } -void mp_hal_stdout_tx_str(const char *str) { - mp_hal_stdout_tx_strn(str, strlen(str)); -} - void mp_hal_stdout_tx_strn(const char *str, uint32_t len) { // Only release the GIL if many characters are being sent bool release_gil = len > 20; @@ -131,26 +127,6 @@ void mp_hal_stdout_tx_strn(const char *str, uint32_t len) { mp_uos_dupterm_tx_strn(str, len); } -// Efficiently convert "\n" to "\r\n" -void mp_hal_stdout_tx_strn_cooked(const char *str, size_t len) { - const char *last = str; - while (len--) { - if (*str == '\n') { - if (str > last) { - mp_hal_stdout_tx_strn(last, str - last); - } - mp_hal_stdout_tx_strn("\r\n", 2); - ++str; - last = str; - } else { - ++str; - } - } - if (str > last) { - mp_hal_stdout_tx_strn(last, str - last); - } -} - uint32_t mp_hal_ticks_ms(void) { return esp_timer_get_time() / 1000; } diff --git a/ports/esp8266/Makefile b/ports/esp8266/Makefile index 02ea05f76e..9d6aea603a 100644 --- a/ports/esp8266/Makefile +++ b/ports/esp8266/Makefile @@ -145,6 +145,7 @@ LIB_SRC_C = $(addprefix lib/,\ timeutils/timeutils.c \ utils/pyexec.c \ utils/interrupt_char.c \ + utils/stdout_helpers.c \ utils/sys_stdio_mphal.c \ ) diff --git a/ports/esp8266/esp_mphal.c b/ports/esp8266/esp_mphal.c index 54f9611e56..06e6cd0af5 100644 --- a/ports/esp8266/esp_mphal.c +++ b/ports/esp8266/esp_mphal.c @@ -91,33 +91,10 @@ void mp_hal_debug_str(const char *str) { } #endif -void mp_hal_stdout_tx_str(const char *str) { - mp_uos_dupterm_tx_strn(str, strlen(str)); -} - void mp_hal_stdout_tx_strn(const char *str, uint32_t len) { mp_uos_dupterm_tx_strn(str, len); } -void mp_hal_stdout_tx_strn_cooked(const char *str, uint32_t len) { - const char *last = str; - while (len--) { - if (*str == '\n') { - if (str > last) { - mp_uos_dupterm_tx_strn(last, str - last); - } - mp_uos_dupterm_tx_strn("\r\n", 2); - ++str; - last = str; - } else { - ++str; - } - } - if (str > last) { - mp_uos_dupterm_tx_strn(last, str - last); - } -} - void mp_hal_debug_tx_strn_cooked(void *env, const char *str, uint32_t len) { (void)env; while (len--) { diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index 4ec03c1746..9c63707071 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -167,6 +167,7 @@ LIB_SRC_C += $(addprefix lib/,\ utils/gchelper_native.c \ utils/pyexec.c \ utils/interrupt_char.c \ + utils/stdout_helpers.c \ utils/sys_stdio_mphal.c \ utils/mpirq.c \ ) diff --git a/ports/stm32/mphalport.c b/ports/stm32/mphalport.c index 0e40911ed1..81e84ef933 100644 --- a/ports/stm32/mphalport.c +++ b/ports/stm32/mphalport.c @@ -53,10 +53,6 @@ MP_WEAK int mp_hal_stdin_rx_chr(void) { } } -void mp_hal_stdout_tx_str(const char *str) { - mp_hal_stdout_tx_strn(str, strlen(str)); -} - MP_WEAK void mp_hal_stdout_tx_strn(const char *str, size_t len) { if (MP_STATE_PORT(pyb_stdio_uart) != NULL) { uart_tx_strn(MP_STATE_PORT(pyb_stdio_uart), str, len); @@ -67,26 +63,6 @@ MP_WEAK void mp_hal_stdout_tx_strn(const char *str, size_t len) { mp_uos_dupterm_tx_strn(str, len); } -// Efficiently convert "\n" to "\r\n" -void mp_hal_stdout_tx_strn_cooked(const char *str, size_t len) { - const char *last = str; - while (len--) { - if (*str == '\n') { - if (str > last) { - mp_hal_stdout_tx_strn(last, str - last); - } - mp_hal_stdout_tx_strn("\r\n", 2); - ++str; - last = str; - } else { - ++str; - } - } - if (str > last) { - mp_hal_stdout_tx_strn(last, str - last); - } -} - #if __CORTEX_M >= 0x03 void mp_hal_ticks_cpu_enable(void) { if (!(DWT->CTRL & DWT_CTRL_CYCCNTENA_Msk)) { From 0613d3e3561a82fffa36594647ef916b19bc912b Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 6 Jul 2021 11:34:19 +1000 Subject: [PATCH 066/264] rp2/tusb_config.h: Set CFG_TUD_CDC_EP_BUFSIZE to 256. This improves the speed of data going out of the MCU. See related #7479. Signed-off-by: Damien George --- ports/rp2/tusb_config.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/rp2/tusb_config.h b/ports/rp2/tusb_config.h index 1402edf379..8c2a9f7560 100644 --- a/ports/rp2/tusb_config.h +++ b/ports/rp2/tusb_config.h @@ -28,6 +28,7 @@ #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE) #define CFG_TUD_CDC (1) +#define CFG_TUD_CDC_EP_BUFSIZE (256) #define CFG_TUD_CDC_RX_BUFSIZE (256) #define CFG_TUD_CDC_TX_BUFSIZE (256) From 2e634d5ead9bfce8cdacf317470534a16d8e6631 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 8 Jul 2021 23:40:00 +1000 Subject: [PATCH 067/264] lib/axtls: Switch to repo at micropython/axtls. Signed-off-by: Damien George --- .gitmodules | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index faa89c4f5c..9fd45dfecf 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,7 +1,6 @@ [submodule "lib/axtls"] path = lib/axtls - url = https://github.com/pfalcon/axtls - branch = micropython + url = https://github.com/micropython/axtls.git [submodule "lib/libffi"] path = lib/libffi url = https://github.com/atgreen/libffi From 75c1609c3a36f40f2dc9d5f92a8b35678eb6e6d8 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 8 Jul 2021 23:40:33 +1000 Subject: [PATCH 068/264] lib/axtls: Update to latest axtls 2.1.5 wih additional commits. Changes are: - update axTLS from 2.1.3 to 2.1.5 - os_port.h is now provided by the user of the library - PLATFORM_RNG_U8 can be defined for get_random - fix -fsanitize=undefined diagnostics Signed-off-by: Damien George --- lib/axtls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/axtls b/lib/axtls index 43a6e6bd3b..531cab9c27 160000 --- a/lib/axtls +++ b/lib/axtls @@ -1 +1 @@ -Subproject commit 43a6e6bd3bbc03dc501e16b89fba0ef042ed3ea0 +Subproject commit 531cab9c278c947d268bd4c94ecab9153a961b43 From e2f0b181f94c7f0843102b02912680d916b0b7d0 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 8 Jul 2021 23:51:49 +1000 Subject: [PATCH 069/264] extmod/axtls-include: Add axtls_os_port.h to customise axTLS. Signed-off-by: Damien George --- extmod/axtls-include/axtls_os_port.h | 55 ++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 extmod/axtls-include/axtls_os_port.h diff --git a/extmod/axtls-include/axtls_os_port.h b/extmod/axtls-include/axtls_os_port.h new file mode 100644 index 0000000000..a58b13d7cc --- /dev/null +++ b/extmod/axtls-include/axtls_os_port.h @@ -0,0 +1,55 @@ +/* + * 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 AXTLS_OS_PORT_H +#define AXTLS_OS_PORT_H + +#include +#include "py/stream.h" +#include "extmod/crypto-algorithms/sha256.h" + +#define SSL_CTX_MUTEX_INIT(mutex) +#define SSL_CTX_MUTEX_DESTROY(mutex) +#define SSL_CTX_LOCK(mutex) +#define SSL_CTX_UNLOCK(mutex) + +#define SOCKET_READ(s, buf, size) mp_stream_posix_read((void *)s, buf, size) +#define SOCKET_WRITE(s, buf, size) mp_stream_posix_write((void *)s, buf, size) +#define SOCKET_CLOSE(A) UNUSED +#define SOCKET_ERRNO() errno + +#define SHA256_CTX CRYAL_SHA256_CTX +#define SHA256_Init(ctx) sha256_init(ctx) +#define SHA256_Update(ctx, buf, size) sha256_update(ctx, buf, size) +#define SHA256_Final(hash, ctx) sha256_final(ctx, hash) + +#define TTY_FLUSH() + +#ifdef WDEV_HWRNG +// For esp8266 port: use the hardware RNG. +#define PLATFORM_RNG_U8() (*WDEV_HWRNG) +#endif + +#endif // AXTLS_OS_PORT_H From d1d172f536247df4716b76b105a875477ea75d65 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 12 Jul 2021 16:33:19 +1000 Subject: [PATCH 070/264] lib/re1.5: Move re1.5 code from extmod to lib. It's third-party code, and not necessarily tied to extmod. Signed-off-by: Damien George --- {extmod => lib}/re1.5/charclass.c | 0 {extmod => lib}/re1.5/compilecode.c | 0 {extmod => lib}/re1.5/dumpcode.c | 0 {extmod => lib}/re1.5/re1.5.h | 0 {extmod => lib}/re1.5/recursiveloop.c | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename {extmod => lib}/re1.5/charclass.c (100%) rename {extmod => lib}/re1.5/compilecode.c (100%) rename {extmod => lib}/re1.5/dumpcode.c (100%) rename {extmod => lib}/re1.5/re1.5.h (100%) rename {extmod => lib}/re1.5/recursiveloop.c (100%) diff --git a/extmod/re1.5/charclass.c b/lib/re1.5/charclass.c similarity index 100% rename from extmod/re1.5/charclass.c rename to lib/re1.5/charclass.c diff --git a/extmod/re1.5/compilecode.c b/lib/re1.5/compilecode.c similarity index 100% rename from extmod/re1.5/compilecode.c rename to lib/re1.5/compilecode.c diff --git a/extmod/re1.5/dumpcode.c b/lib/re1.5/dumpcode.c similarity index 100% rename from extmod/re1.5/dumpcode.c rename to lib/re1.5/dumpcode.c diff --git a/extmod/re1.5/re1.5.h b/lib/re1.5/re1.5.h similarity index 100% rename from extmod/re1.5/re1.5.h rename to lib/re1.5/re1.5.h diff --git a/extmod/re1.5/recursiveloop.c b/lib/re1.5/recursiveloop.c similarity index 100% rename from extmod/re1.5/recursiveloop.c rename to lib/re1.5/recursiveloop.c From d1bfb271d7686708fe8711a177629c8bf6e7f6a6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 12 Jul 2021 16:33:28 +1000 Subject: [PATCH 071/264] lib/uzlib: Move uzlib code from extmod to lib. It's third-party code, and not necessarily tied to extmod. Signed-off-by: Damien George --- {extmod => lib}/uzlib/adler32.c | 0 {extmod => lib}/uzlib/crc32.c | 0 {extmod => lib}/uzlib/defl_static.h | 0 {extmod => lib}/uzlib/tinf.h | 0 {extmod => lib}/uzlib/tinf_compat.h | 0 {extmod => lib}/uzlib/tinfgzip.c | 0 {extmod => lib}/uzlib/tinflate.c | 0 {extmod => lib}/uzlib/tinfzlib.c | 0 {extmod => lib}/uzlib/uzlib.h | 0 {extmod => lib}/uzlib/uzlib_conf.h | 0 10 files changed, 0 insertions(+), 0 deletions(-) rename {extmod => lib}/uzlib/adler32.c (100%) rename {extmod => lib}/uzlib/crc32.c (100%) rename {extmod => lib}/uzlib/defl_static.h (100%) rename {extmod => lib}/uzlib/tinf.h (100%) rename {extmod => lib}/uzlib/tinf_compat.h (100%) rename {extmod => lib}/uzlib/tinfgzip.c (100%) rename {extmod => lib}/uzlib/tinflate.c (100%) rename {extmod => lib}/uzlib/tinfzlib.c (100%) rename {extmod => lib}/uzlib/uzlib.h (100%) rename {extmod => lib}/uzlib/uzlib_conf.h (100%) diff --git a/extmod/uzlib/adler32.c b/lib/uzlib/adler32.c similarity index 100% rename from extmod/uzlib/adler32.c rename to lib/uzlib/adler32.c diff --git a/extmod/uzlib/crc32.c b/lib/uzlib/crc32.c similarity index 100% rename from extmod/uzlib/crc32.c rename to lib/uzlib/crc32.c diff --git a/extmod/uzlib/defl_static.h b/lib/uzlib/defl_static.h similarity index 100% rename from extmod/uzlib/defl_static.h rename to lib/uzlib/defl_static.h diff --git a/extmod/uzlib/tinf.h b/lib/uzlib/tinf.h similarity index 100% rename from extmod/uzlib/tinf.h rename to lib/uzlib/tinf.h diff --git a/extmod/uzlib/tinf_compat.h b/lib/uzlib/tinf_compat.h similarity index 100% rename from extmod/uzlib/tinf_compat.h rename to lib/uzlib/tinf_compat.h diff --git a/extmod/uzlib/tinfgzip.c b/lib/uzlib/tinfgzip.c similarity index 100% rename from extmod/uzlib/tinfgzip.c rename to lib/uzlib/tinfgzip.c diff --git a/extmod/uzlib/tinflate.c b/lib/uzlib/tinflate.c similarity index 100% rename from extmod/uzlib/tinflate.c rename to lib/uzlib/tinflate.c diff --git a/extmod/uzlib/tinfzlib.c b/lib/uzlib/tinfzlib.c similarity index 100% rename from extmod/uzlib/tinfzlib.c rename to lib/uzlib/tinfzlib.c diff --git a/extmod/uzlib/uzlib.h b/lib/uzlib/uzlib.h similarity index 100% rename from extmod/uzlib/uzlib.h rename to lib/uzlib/uzlib.h diff --git a/extmod/uzlib/uzlib_conf.h b/lib/uzlib/uzlib_conf.h similarity index 100% rename from extmod/uzlib/uzlib_conf.h rename to lib/uzlib/uzlib_conf.h From 6dd92d7742adf06c699c52f6b7830efe2f7cc3bd Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 12 Jul 2021 16:33:33 +1000 Subject: [PATCH 072/264] lib/crypto-algorithms: Move crypto-algorithms code from extmod to lib. It's third-party code, and not necessarily tied to extmod. Signed-off-by: Damien George --- {extmod => lib}/crypto-algorithms/sha256.c | 0 {extmod => lib}/crypto-algorithms/sha256.h | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {extmod => lib}/crypto-algorithms/sha256.c (100%) rename {extmod => lib}/crypto-algorithms/sha256.h (100%) diff --git a/extmod/crypto-algorithms/sha256.c b/lib/crypto-algorithms/sha256.c similarity index 100% rename from extmod/crypto-algorithms/sha256.c rename to lib/crypto-algorithms/sha256.c diff --git a/extmod/crypto-algorithms/sha256.h b/lib/crypto-algorithms/sha256.h similarity index 100% rename from extmod/crypto-algorithms/sha256.h rename to lib/crypto-algorithms/sha256.h From 966e8bf934273056af3162887fe1df014601a66d Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 12 Jul 2021 16:33:39 +1000 Subject: [PATCH 073/264] LICENSE: Update for move of crypto-algorithms, re1.5, uzlib to lib. Signed-off-by: Damien George --- LICENSE | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/LICENSE b/LICENSE index 7f39931ba7..5b5c37f7d1 100644 --- a/LICENSE +++ b/LICENSE @@ -38,10 +38,6 @@ used during the build process and is not part of the compiled source code. /cc3000 (BSD-3-clause) /cc3100 (BSD-3-clause) /wiznet5k (BSD-3-clause) - /extmod - /crypto-algorithms (NONE) - /re15 (BSD-3-clause) - /uzlib (Zlib) /lib /asf4 (Apache-2.0) /axtls (BSD-3-clause) @@ -52,6 +48,7 @@ used during the build process and is not part of the compiled source code. /berkeley-db-1xx (BSD-4-clause) /btstack (See btstack/LICENSE) /cmsis (BSD-3-clause) + /crypto-algorithms (NONE) /libhydrogen (ISC) /littlefs (BSD-3-clause) /lwip (BSD-3-clause) @@ -60,9 +57,11 @@ used during the build process and is not part of the compiled source code. /nxp_driver (BSD-3-Clause) /oofatfs (BSD-1-clause) /pico-sdk (BSD-3-clause) + /re15 (BSD-3-clause) /stm32lib (BSD-3-clause) /tinytest (BSD-3-clause) /tinyusb (MIT) + /uzlib (Zlib) /logo (uses OFL-1.1) /ports /cc3200 From 94dfaff18bd1941c773f89359e40db5b555cd3ef Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 12 Jul 2021 16:34:27 +1000 Subject: [PATCH 074/264] extmod: Update for move of crypto-algorithms, re1.5, uzlib to lib. Signed-off-by: Damien George --- extmod/axtls-include/axtls_os_port.h | 2 +- extmod/modubinascii.c | 2 +- extmod/moduhashlib.c | 4 ++-- extmod/modure.c | 10 +++++----- extmod/moduzlib.c | 12 ++++++------ 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/extmod/axtls-include/axtls_os_port.h b/extmod/axtls-include/axtls_os_port.h index a58b13d7cc..ef2683acfc 100644 --- a/extmod/axtls-include/axtls_os_port.h +++ b/extmod/axtls-include/axtls_os_port.h @@ -28,7 +28,7 @@ #include #include "py/stream.h" -#include "extmod/crypto-algorithms/sha256.h" +#include "lib/crypto-algorithms/sha256.h" #define SSL_CTX_MUTEX_INIT(mutex) #define SSL_CTX_MUTEX_DESTROY(mutex) diff --git a/extmod/modubinascii.c b/extmod/modubinascii.c index 9e4f86fbd2..fab7717c4e 100644 --- a/extmod/modubinascii.c +++ b/extmod/modubinascii.c @@ -218,7 +218,7 @@ STATIC mp_obj_t mod_binascii_b2a_base64(mp_obj_t data) { STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_binascii_b2a_base64_obj, mod_binascii_b2a_base64); #if MICROPY_PY_UBINASCII_CRC32 -#include "uzlib/tinf.h" +#include "lib/uzlib/tinf.h" STATIC mp_obj_t mod_binascii_crc32(size_t n_args, const mp_obj_t *args) { mp_buffer_info_t bufinfo; diff --git a/extmod/moduhashlib.c b/extmod/moduhashlib.c index b170de9e58..e97e8cb75d 100644 --- a/extmod/moduhashlib.c +++ b/extmod/moduhashlib.c @@ -40,7 +40,7 @@ #if MICROPY_SSL_MBEDTLS #include "mbedtls/sha256.h" #else -#include "crypto-algorithms/sha256.h" +#include "lib/crypto-algorithms/sha256.h" #endif #endif @@ -115,7 +115,7 @@ STATIC mp_obj_t uhashlib_sha256_digest(mp_obj_t self_in) { #else -#include "crypto-algorithms/sha256.c" +#include "lib/crypto-algorithms/sha256.c" STATIC mp_obj_t uhashlib_sha256_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_check_num(n_args, n_kw, 0, 1, false); diff --git a/extmod/modure.c b/extmod/modure.c index 220587b42f..d0829e8c5d 100644 --- a/extmod/modure.c +++ b/extmod/modure.c @@ -37,7 +37,7 @@ #define re1_5_stack_chk() MP_STACK_CHECK() -#include "re1.5/re1.5.h" +#include "lib/re1.5/re1.5.h" #define FLAG_DEBUG 0x1000 @@ -454,11 +454,11 @@ const mp_obj_module_t mp_module_ure = { // only if module is enabled by config setting. #define re1_5_fatal(x) assert(!x) -#include "re1.5/compilecode.c" +#include "lib/re1.5/compilecode.c" #if MICROPY_PY_URE_DEBUG -#include "re1.5/dumpcode.c" +#include "lib/re1.5/dumpcode.c" #endif -#include "re1.5/recursiveloop.c" -#include "re1.5/charclass.c" +#include "lib/re1.5/recursiveloop.c" +#include "lib/re1.5/charclass.c" #endif // MICROPY_PY_URE diff --git a/extmod/moduzlib.c b/extmod/moduzlib.c index ab70d67479..999cb48131 100644 --- a/extmod/moduzlib.c +++ b/extmod/moduzlib.c @@ -33,7 +33,7 @@ #if MICROPY_PY_UZLIB -#include "uzlib/tinf.h" +#include "lib/uzlib/tinf.h" #if 0 // print debugging info #define DEBUG_printf DEBUG_printf @@ -223,10 +223,10 @@ const mp_obj_module_t mp_module_uzlib = { // Source files #include'd here to make sure they're compiled in // only if module is enabled by config setting. -#include "uzlib/tinflate.c" -#include "uzlib/tinfzlib.c" -#include "uzlib/tinfgzip.c" -#include "uzlib/adler32.c" -#include "uzlib/crc32.c" +#include "lib/uzlib/tinflate.c" +#include "lib/uzlib/tinfzlib.c" +#include "lib/uzlib/tinfgzip.c" +#include "lib/uzlib/adler32.c" +#include "lib/uzlib/crc32.c" #endif // MICROPY_PY_UZLIB From 925878b2f83e09987bd1688746b92898ee8bbcac Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 12 Jul 2021 16:34:43 +1000 Subject: [PATCH 075/264] ports: Update for move of crypto-algorithms, uzlib to lib. Signed-off-by: Damien George --- ports/esp32/memory.h | 2 +- ports/stm32/mboot/Makefile | 8 ++++---- ports/stm32/mboot/gzstream.c | 2 +- ports/stm32/mboot/main.c | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ports/esp32/memory.h b/ports/esp32/memory.h index f3777b0e39..1f07fe409d 100644 --- a/ports/esp32/memory.h +++ b/ports/esp32/memory.h @@ -1,2 +1,2 @@ -// this is needed for extmod/crypto-algorithms/sha256.c +// this is needed for lib/crypto-algorithms/sha256.c #include diff --git a/ports/stm32/mboot/Makefile b/ports/stm32/mboot/Makefile index 17de685a61..41a1a73bc5 100755 --- a/ports/stm32/mboot/Makefile +++ b/ports/stm32/mboot/Makefile @@ -114,10 +114,10 @@ LIB_SRC_C += \ lib/littlefs/lfs2_util.c \ lib/oofatfs/ff.c \ lib/oofatfs/ffunicode.c \ - extmod/uzlib/crc32.c \ - extmod/uzlib/adler32.c \ - extmod/uzlib/tinflate.c \ - extmod/uzlib/tinfgzip.c + lib/uzlib/adler32.c \ + lib/uzlib/crc32.c \ + lib/uzlib/tinfgzip.c \ + lib/uzlib/tinflate.c SRC_C += \ main.c \ diff --git a/ports/stm32/mboot/gzstream.c b/ports/stm32/mboot/gzstream.c index 652302e42b..6ed8a21d99 100644 --- a/ports/stm32/mboot/gzstream.c +++ b/ports/stm32/mboot/gzstream.c @@ -27,7 +27,7 @@ #include #include "py/mphal.h" -#include "extmod/uzlib/uzlib.h" +#include "lib/uzlib/uzlib.h" #include "gzstream.h" #include "mboot.h" diff --git a/ports/stm32/mboot/main.c b/ports/stm32/mboot/main.c index c1e1d59d23..6c7d2c7714 100644 --- a/ports/stm32/mboot/main.c +++ b/ports/stm32/mboot/main.c @@ -28,7 +28,7 @@ #include #include "py/mphal.h" -#include "extmod/crypto-algorithms/sha256.c" +#include "lib/crypto-algorithms/sha256.c" #include "boardctrl.h" #include "usbd_core.h" #include "storage.h" From 4d546713ec8858cbf908de45de11cbfc46a20971 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 9 Jul 2021 12:38:25 +1000 Subject: [PATCH 076/264] shared: Introduce new top-level dir and move 1st party lib code there. This commit moves all first-party code developed for this project from lib/ to shared/, so that lib/ now only contains third-party code. The following directories are moved as-is from lib to shared: lib/libc -> shared/libc lib/memzip -> shared/memzip lib/netutils -> shared/netutils lib/timeutils -> shared/timeutils lib/upytesthelper -> shared/upytesthelper All files in lib/embed/ have been moved to shared/libc/. lib/mp-readline has been moved to shared/readline. lib/utils has been moved to shared/runtime, with the exception of lib/utils/printf.c which has been moved to shared/libc/printf.c. Signed-off-by: Damien George --- {lib/embed => shared/libc}/__errno.c | 0 {lib/embed => shared/libc}/abort_.c | 0 {lib/utils => shared/libc}/printf.c | 0 {lib => shared}/libc/string0.c | 0 {lib => shared}/memzip/README.md | 0 {lib => shared}/memzip/import.c | 0 {lib => shared}/memzip/lexermemzip.c | 0 {lib => shared}/memzip/make-memzip.py | 0 {lib => shared}/memzip/memzip.c | 0 {lib => shared}/memzip/memzip.h | 0 {lib => shared}/netutils/dhcpserver.c | 0 {lib => shared}/netutils/dhcpserver.h | 0 {lib => shared}/netutils/netutils.c | 0 {lib => shared}/netutils/netutils.h | 0 {lib => shared}/netutils/trace.c | 0 {lib/mp-readline => shared/readline}/readline.c | 0 {lib/mp-readline => shared/readline}/readline.h | 0 {lib/utils => shared/runtime}/gchelper.h | 0 {lib/utils => shared/runtime}/gchelper_generic.c | 0 {lib/utils => shared/runtime}/gchelper_m0.s | 0 {lib/utils => shared/runtime}/gchelper_m3.s | 0 {lib/utils => shared/runtime}/gchelper_native.c | 0 {lib/utils => shared/runtime}/interrupt_char.c | 0 {lib/utils => shared/runtime}/interrupt_char.h | 0 {lib/utils => shared/runtime}/mpirq.c | 0 {lib/utils => shared/runtime}/mpirq.h | 0 {lib/utils => shared/runtime}/pyexec.c | 0 {lib/utils => shared/runtime}/pyexec.h | 0 {lib/utils => shared/runtime}/semihosting.c | 0 {lib/utils => shared/runtime}/semihosting.h | 0 {lib/utils => shared/runtime}/stdout_helpers.c | 0 {lib/utils => shared/runtime}/sys_stdio_mphal.c | 0 {lib => shared}/timeutils/timeutils.c | 0 {lib => shared}/timeutils/timeutils.h | 0 {lib => shared}/upytesthelper/upytesthelper.c | 0 {lib => shared}/upytesthelper/upytesthelper.h | 0 36 files changed, 0 insertions(+), 0 deletions(-) rename {lib/embed => shared/libc}/__errno.c (100%) rename {lib/embed => shared/libc}/abort_.c (100%) rename {lib/utils => shared/libc}/printf.c (100%) rename {lib => shared}/libc/string0.c (100%) rename {lib => shared}/memzip/README.md (100%) rename {lib => shared}/memzip/import.c (100%) rename {lib => shared}/memzip/lexermemzip.c (100%) rename {lib => shared}/memzip/make-memzip.py (100%) rename {lib => shared}/memzip/memzip.c (100%) rename {lib => shared}/memzip/memzip.h (100%) rename {lib => shared}/netutils/dhcpserver.c (100%) rename {lib => shared}/netutils/dhcpserver.h (100%) rename {lib => shared}/netutils/netutils.c (100%) rename {lib => shared}/netutils/netutils.h (100%) rename {lib => shared}/netutils/trace.c (100%) rename {lib/mp-readline => shared/readline}/readline.c (100%) rename {lib/mp-readline => shared/readline}/readline.h (100%) rename {lib/utils => shared/runtime}/gchelper.h (100%) rename {lib/utils => shared/runtime}/gchelper_generic.c (100%) rename {lib/utils => shared/runtime}/gchelper_m0.s (100%) rename {lib/utils => shared/runtime}/gchelper_m3.s (100%) rename {lib/utils => shared/runtime}/gchelper_native.c (100%) rename {lib/utils => shared/runtime}/interrupt_char.c (100%) rename {lib/utils => shared/runtime}/interrupt_char.h (100%) rename {lib/utils => shared/runtime}/mpirq.c (100%) rename {lib/utils => shared/runtime}/mpirq.h (100%) rename {lib/utils => shared/runtime}/pyexec.c (100%) rename {lib/utils => shared/runtime}/pyexec.h (100%) rename {lib/utils => shared/runtime}/semihosting.c (100%) rename {lib/utils => shared/runtime}/semihosting.h (100%) rename {lib/utils => shared/runtime}/stdout_helpers.c (100%) rename {lib/utils => shared/runtime}/sys_stdio_mphal.c (100%) rename {lib => shared}/timeutils/timeutils.c (100%) rename {lib => shared}/timeutils/timeutils.h (100%) rename {lib => shared}/upytesthelper/upytesthelper.c (100%) rename {lib => shared}/upytesthelper/upytesthelper.h (100%) diff --git a/lib/embed/__errno.c b/shared/libc/__errno.c similarity index 100% rename from lib/embed/__errno.c rename to shared/libc/__errno.c diff --git a/lib/embed/abort_.c b/shared/libc/abort_.c similarity index 100% rename from lib/embed/abort_.c rename to shared/libc/abort_.c diff --git a/lib/utils/printf.c b/shared/libc/printf.c similarity index 100% rename from lib/utils/printf.c rename to shared/libc/printf.c diff --git a/lib/libc/string0.c b/shared/libc/string0.c similarity index 100% rename from lib/libc/string0.c rename to shared/libc/string0.c diff --git a/lib/memzip/README.md b/shared/memzip/README.md similarity index 100% rename from lib/memzip/README.md rename to shared/memzip/README.md diff --git a/lib/memzip/import.c b/shared/memzip/import.c similarity index 100% rename from lib/memzip/import.c rename to shared/memzip/import.c diff --git a/lib/memzip/lexermemzip.c b/shared/memzip/lexermemzip.c similarity index 100% rename from lib/memzip/lexermemzip.c rename to shared/memzip/lexermemzip.c diff --git a/lib/memzip/make-memzip.py b/shared/memzip/make-memzip.py similarity index 100% rename from lib/memzip/make-memzip.py rename to shared/memzip/make-memzip.py diff --git a/lib/memzip/memzip.c b/shared/memzip/memzip.c similarity index 100% rename from lib/memzip/memzip.c rename to shared/memzip/memzip.c diff --git a/lib/memzip/memzip.h b/shared/memzip/memzip.h similarity index 100% rename from lib/memzip/memzip.h rename to shared/memzip/memzip.h diff --git a/lib/netutils/dhcpserver.c b/shared/netutils/dhcpserver.c similarity index 100% rename from lib/netutils/dhcpserver.c rename to shared/netutils/dhcpserver.c diff --git a/lib/netutils/dhcpserver.h b/shared/netutils/dhcpserver.h similarity index 100% rename from lib/netutils/dhcpserver.h rename to shared/netutils/dhcpserver.h diff --git a/lib/netutils/netutils.c b/shared/netutils/netutils.c similarity index 100% rename from lib/netutils/netutils.c rename to shared/netutils/netutils.c diff --git a/lib/netutils/netutils.h b/shared/netutils/netutils.h similarity index 100% rename from lib/netutils/netutils.h rename to shared/netutils/netutils.h diff --git a/lib/netutils/trace.c b/shared/netutils/trace.c similarity index 100% rename from lib/netutils/trace.c rename to shared/netutils/trace.c diff --git a/lib/mp-readline/readline.c b/shared/readline/readline.c similarity index 100% rename from lib/mp-readline/readline.c rename to shared/readline/readline.c diff --git a/lib/mp-readline/readline.h b/shared/readline/readline.h similarity index 100% rename from lib/mp-readline/readline.h rename to shared/readline/readline.h diff --git a/lib/utils/gchelper.h b/shared/runtime/gchelper.h similarity index 100% rename from lib/utils/gchelper.h rename to shared/runtime/gchelper.h diff --git a/lib/utils/gchelper_generic.c b/shared/runtime/gchelper_generic.c similarity index 100% rename from lib/utils/gchelper_generic.c rename to shared/runtime/gchelper_generic.c diff --git a/lib/utils/gchelper_m0.s b/shared/runtime/gchelper_m0.s similarity index 100% rename from lib/utils/gchelper_m0.s rename to shared/runtime/gchelper_m0.s diff --git a/lib/utils/gchelper_m3.s b/shared/runtime/gchelper_m3.s similarity index 100% rename from lib/utils/gchelper_m3.s rename to shared/runtime/gchelper_m3.s diff --git a/lib/utils/gchelper_native.c b/shared/runtime/gchelper_native.c similarity index 100% rename from lib/utils/gchelper_native.c rename to shared/runtime/gchelper_native.c diff --git a/lib/utils/interrupt_char.c b/shared/runtime/interrupt_char.c similarity index 100% rename from lib/utils/interrupt_char.c rename to shared/runtime/interrupt_char.c diff --git a/lib/utils/interrupt_char.h b/shared/runtime/interrupt_char.h similarity index 100% rename from lib/utils/interrupt_char.h rename to shared/runtime/interrupt_char.h diff --git a/lib/utils/mpirq.c b/shared/runtime/mpirq.c similarity index 100% rename from lib/utils/mpirq.c rename to shared/runtime/mpirq.c diff --git a/lib/utils/mpirq.h b/shared/runtime/mpirq.h similarity index 100% rename from lib/utils/mpirq.h rename to shared/runtime/mpirq.h diff --git a/lib/utils/pyexec.c b/shared/runtime/pyexec.c similarity index 100% rename from lib/utils/pyexec.c rename to shared/runtime/pyexec.c diff --git a/lib/utils/pyexec.h b/shared/runtime/pyexec.h similarity index 100% rename from lib/utils/pyexec.h rename to shared/runtime/pyexec.h diff --git a/lib/utils/semihosting.c b/shared/runtime/semihosting.c similarity index 100% rename from lib/utils/semihosting.c rename to shared/runtime/semihosting.c diff --git a/lib/utils/semihosting.h b/shared/runtime/semihosting.h similarity index 100% rename from lib/utils/semihosting.h rename to shared/runtime/semihosting.h diff --git a/lib/utils/stdout_helpers.c b/shared/runtime/stdout_helpers.c similarity index 100% rename from lib/utils/stdout_helpers.c rename to shared/runtime/stdout_helpers.c diff --git a/lib/utils/sys_stdio_mphal.c b/shared/runtime/sys_stdio_mphal.c similarity index 100% rename from lib/utils/sys_stdio_mphal.c rename to shared/runtime/sys_stdio_mphal.c diff --git a/lib/timeutils/timeutils.c b/shared/timeutils/timeutils.c similarity index 100% rename from lib/timeutils/timeutils.c rename to shared/timeutils/timeutils.c diff --git a/lib/timeutils/timeutils.h b/shared/timeutils/timeutils.h similarity index 100% rename from lib/timeutils/timeutils.h rename to shared/timeutils/timeutils.h diff --git a/lib/upytesthelper/upytesthelper.c b/shared/upytesthelper/upytesthelper.c similarity index 100% rename from lib/upytesthelper/upytesthelper.c rename to shared/upytesthelper/upytesthelper.c diff --git a/lib/upytesthelper/upytesthelper.h b/shared/upytesthelper/upytesthelper.h similarity index 100% rename from lib/upytesthelper/upytesthelper.h rename to shared/upytesthelper/upytesthelper.h From 136369d72f5b99ec23c9c9f178a590bde968e2ee Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 9 Jul 2021 14:19:15 +1000 Subject: [PATCH 077/264] all: Update to point to files in new shared/ directory. Signed-off-by: Damien George --- docs/develop/porting.rst | 12 +++++----- drivers/cyw43/cyw43.h | 2 +- drivers/cyw43/cyw43_lwip.c | 2 +- examples/embedding/Makefile.upylib | 16 ++++++------- extmod/extmod.cmake | 4 ++-- extmod/extmod.mk | 2 +- extmod/modlwip.c | 2 +- extmod/uos_dupterm.c | 2 +- extmod/vfs_fat.c | 2 +- extmod/vfs_lfs.c | 2 +- extmod/vfs_lfsx.c | 2 +- mpy-cross/Makefile | 2 +- mpy-cross/gccollect.c | 2 +- mpy-cross/mpy-cross.vcxproj | 4 ++-- ports/bare-arm/lib.c | 2 +- ports/cc3200/application.mk | 21 +++++++++------- ports/cc3200/bootmgr/bootloader.mk | 6 ++--- ports/cc3200/fatfs_port.c | 2 +- ports/cc3200/ftp/ftp.c | 2 +- ports/cc3200/mods/moduos.c | 2 +- ports/cc3200/mods/modusocket.c | 2 +- ports/cc3200/mods/modutime.c | 2 +- ports/cc3200/mods/modwlan.c | 4 ++-- ports/cc3200/mods/pybrtc.c | 2 +- ports/cc3200/mods/pybuart.c | 2 +- ports/cc3200/mptask.c | 6 ++--- ports/cc3200/telnet/telnet.c | 2 +- ports/cc3200/util/gccollect.c | 2 +- ports/esp32/fatfs_port.c | 2 +- ports/esp32/machine_rtc.c | 2 +- ports/esp32/main.c | 4 ++-- ports/esp32/main/CMakeLists.txt | 19 +++++++++------ ports/esp32/modesp32.c | 2 +- ports/esp32/modmachine.c | 2 +- ports/esp32/modnetwork.c | 2 +- ports/esp32/modsocket.c | 2 +- ports/esp32/modutime.c | 2 +- ports/esp32/mphalport.c | 4 ++-- ports/esp32/mphalport.h | 2 +- ports/esp32/network_ppp.c | 2 +- ports/esp8266/Makefile | 20 +++++++++------- ports/esp8266/boards/esp8266_common.ld | 16 ++++++------- ports/esp8266/esp_mphal.c | 2 +- ports/esp8266/esp_mphal.h | 2 +- ports/esp8266/fatfs_port.c | 2 +- ports/esp8266/machine_rtc.c | 2 +- ports/esp8266/main.c | 4 ++-- ports/esp8266/modmachine.c | 2 +- ports/esp8266/modnetwork.c | 2 +- ports/esp8266/modutime.c | 2 +- ports/esp8266/uart.c | 2 +- ports/javascript/Makefile | 12 +++++----- ports/javascript/main.c | 2 +- ports/javascript/modutime.c | 2 +- ports/javascript/mphalport.h | 2 +- ports/mimxrt/Makefile | 24 +++++++++---------- ports/mimxrt/machine_pin.c | 2 +- ports/mimxrt/machine_rtc.c | 2 +- ports/mimxrt/main.c | 6 ++--- ports/mimxrt/modutime.c | 2 +- ports/mimxrt/mphalport.c | 2 +- ports/mimxrt/pin.h | 2 +- ports/minimal/Makefile | 10 ++++---- ports/minimal/main.c | 2 +- ports/nrf/Makefile | 12 +++++----- ports/nrf/drivers/bluetooth/ble_uart.c | 2 +- ports/nrf/main.c | 2 +- ports/nrf/modules/board/modboard.c | 2 +- ports/nrf/modules/machine/modmachine.c | 2 +- ports/nrf/modules/machine/uart.c | 2 +- ports/pic16bit/Makefile | 6 ++--- ports/pic16bit/main.c | 4 ++-- ports/powerpc/Makefile | 10 ++++---- ports/powerpc/main.c | 2 +- ports/qemu-arm/Makefile | 12 +++++----- ports/qemu-arm/Makefile.test | 2 +- ports/qemu-arm/mpconfigport.h | 2 +- ports/qemu-arm/test_main.c | 2 +- ports/rp2/CMakeLists.txt | 20 ++++++++-------- ports/rp2/machine_pin.c | 2 +- ports/rp2/machine_rtc.c | 2 +- ports/rp2/main.c | 6 ++--- ports/rp2/modmachine.c | 2 +- ports/rp2/modutime.c | 2 +- ports/rp2/mphalport.c | 2 +- ports/rp2/rp2_pio.c | 2 +- ports/samd/Makefile | 16 ++++++------- ports/samd/main.c | 4 ++-- ports/stm32/Makefile | 29 ++++++++++++----------- ports/stm32/boardctrl.c | 2 +- ports/stm32/eth.c | 2 +- ports/stm32/gccollect.c | 2 +- ports/stm32/machine_uart.c | 4 ++-- ports/stm32/main.c | 4 ++-- ports/stm32/mboot/Makefile | 4 ++-- ports/stm32/modmachine.c | 2 +- ports/stm32/modnetwork.c | 2 +- ports/stm32/modnwcc3k.c | 2 +- ports/stm32/modnwwiznet5k.c | 2 +- ports/stm32/modpyb.c | 2 +- ports/stm32/moduos.c | 2 +- ports/stm32/modusocket.c | 2 +- ports/stm32/modutime.c | 2 +- ports/stm32/mpbthciport.c | 2 +- ports/stm32/network_wiznet5k.c | 2 +- ports/stm32/pendsv.c | 2 +- ports/stm32/rtc.c | 2 +- ports/stm32/uart.c | 4 ++-- ports/stm32/uart.h | 2 +- ports/stm32/usb.c | 2 +- ports/stm32/usbd_cdc_interface.c | 2 +- ports/teensy/Makefile | 24 +++++++++---------- ports/teensy/main.c | 6 ++--- ports/teensy/modpyb.c | 2 +- ports/unix/Makefile | 14 +++++------ ports/unix/gccollect.c | 2 +- ports/unix/input.c | 2 +- ports/unix/main.c | 2 +- ports/unix/mphalport.h | 2 +- ports/unix/mpthreadport.c | 2 +- ports/windows/Makefile | 6 ++--- ports/windows/micropython.vcxproj | 6 ++--- ports/zephyr/CMakeLists.txt | 19 +++++++++------ ports/zephyr/machine_pin.c | 2 +- ports/zephyr/main.c | 8 +++---- ports/zephyr/mpconfigport_bin_testsuite.h | 2 +- ports/zephyr/mphalport.h | 2 +- py/modbuiltins.c | 2 +- py/mpconfig.h | 2 +- py/py.mk | 6 ++--- shared/memzip/README.md | 8 +++---- shared/netutils/dhcpserver.c | 2 +- shared/netutils/netutils.c | 2 +- shared/netutils/trace.c | 2 +- shared/readline/readline.c | 2 +- shared/runtime/gchelper_generic.c | 2 +- shared/runtime/gchelper_native.c | 2 +- shared/runtime/mpirq.c | 2 +- shared/runtime/pyexec.c | 4 ++-- shared/timeutils/timeutils.c | 2 +- tools/codeformat.py | 6 ++--- 141 files changed, 324 insertions(+), 306 deletions(-) diff --git a/docs/develop/porting.rst b/docs/develop/porting.rst index 59dd570008..08f3557f18 100644 --- a/docs/develop/porting.rst +++ b/docs/develop/porting.rst @@ -42,8 +42,8 @@ The basic MicroPython firmware is implemented in the main port file, e.g ``main. #include "py/gc.h" #include "py/mperrno.h" #include "py/stackctrl.h" - #include "lib/utils/gchelper.h" - #include "lib/utils/pyexec.h" + #include "shared/runtime/gchelper.h" + #include "shared/runtime/pyexec.h" // Allocate memory for the MicroPython GC heap. static char heap[4096]; @@ -106,10 +106,10 @@ We also need a Makefile at this point for the port: SRC_C = \ main.c \ mphalport.c \ - lib/mp-readline/readline.c \ - lib/utils/gchelper_generic.c \ - lib/utils/pyexec.c \ - lib/utils/stdout_helpers.c \ + shared/readline/readline.c \ + shared/runtime/gchelper_generic.c \ + shared/runtime/pyexec.c \ + shared/runtime/stdout_helpers.c \ # Define the required object files. OBJ = $(PY_CORE_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) diff --git a/drivers/cyw43/cyw43.h b/drivers/cyw43/cyw43.h index d7f08cb5df..7d3e30f5d4 100644 --- a/drivers/cyw43/cyw43.h +++ b/drivers/cyw43/cyw43.h @@ -28,7 +28,7 @@ #include "lwip/netif.h" #include "lwip/dhcp.h" -#include "lib/netutils/dhcpserver.h" +#include "shared/netutils/dhcpserver.h" #include "drivers/cyw43/cyw43_ll.h" // For trace_flags diff --git a/drivers/cyw43/cyw43_lwip.c b/drivers/cyw43/cyw43_lwip.c index f3ca59e3a4..16ae602375 100644 --- a/drivers/cyw43/cyw43_lwip.c +++ b/drivers/cyw43/cyw43_lwip.c @@ -28,7 +28,7 @@ #include #include "py/mphal.h" -#include "lib/netutils/netutils.h" +#include "shared/netutils/netutils.h" #include "lwip/etharp.h" #include "lwip/dns.h" #include "lwip/apps/mdns.h" diff --git a/examples/embedding/Makefile.upylib b/examples/embedding/Makefile.upylib index 00def493d6..14260a719f 100644 --- a/examples/embedding/Makefile.upylib +++ b/examples/embedding/Makefile.upylib @@ -82,9 +82,9 @@ endif endif ifeq ($(MICROPY_USE_READLINE),1) -INC += -I$(MPTOP)/lib/mp-readline +INC += -I$(MPTOP)/shared/readline CFLAGS_MOD += -DMICROPY_USE_READLINE=1 -LIB_SRC_C_EXTRA += mp-readline/readline.c +SHARED_SRC_C_EXTRA += readline/readline.c endif ifeq ($(MICROPY_USE_READLINE),2) CFLAGS_MOD += -DMICROPY_USE_READLINE=2 @@ -145,19 +145,19 @@ SRC_C = $(addprefix ports/unix/,\ $(SRC_MOD) \ ) -LIB_SRC_C = $(addprefix lib/,\ - $(LIB_SRC_C_EXTRA) \ - utils/printf.c \ - utils/gchelper_generic.c \ +SHARED_SRC_C = $(addprefix shared/,\ + libc/printf.c \ + runtime/gchelper_generic.c \ timeutils/timeutils.c \ + $(SHARED_SRC_C_EXTRA) \ ) OBJ = $(PY_O) OBJ += $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) -OBJ += $(addprefix $(BUILD)/, $(LIB_SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(SHARED_SRC_C:.c=.o)) # List of sources for qstr extraction -SRC_QSTR += $(SRC_C) $(LIB_SRC_C) +SRC_QSTR += $(SRC_C) $(SHARED_SRC_C) # Append any auto-generated sources that are needed by sources listed in # SRC_QSTR SRC_QSTR_AUTO_DEPS += diff --git a/extmod/extmod.cmake b/extmod/extmod.cmake index a54047519d..9ea2ba0af9 100644 --- a/extmod/extmod.cmake +++ b/extmod/extmod.cmake @@ -4,8 +4,8 @@ set(MICROPY_EXTMOD_DIR "${MICROPY_DIR}/extmod") set(MICROPY_OOFATFS_DIR "${MICROPY_DIR}/lib/oofatfs") set(MICROPY_SOURCE_EXTMOD - ${MICROPY_DIR}/lib/embed/abort_.c - ${MICROPY_DIR}/lib/utils/printf.c + ${MICROPY_DIR}/shared/libc/abort_.c + ${MICROPY_DIR}/shared/libc/printf.c ${MICROPY_EXTMOD_DIR}/machine_i2c.c ${MICROPY_EXTMOD_DIR}/machine_mem.c ${MICROPY_EXTMOD_DIR}/machine_pulse.c diff --git a/extmod/extmod.mk b/extmod/extmod.mk index b000b058d7..ff24a549a9 100644 --- a/extmod/extmod.mk +++ b/extmod/extmod.mk @@ -153,7 +153,7 @@ LWIP_DIR = lib/lwip/src INC += -I$(TOP)/$(LWIP_DIR)/include CFLAGS_MOD += -DMICROPY_PY_LWIP=1 $(BUILD)/$(LWIP_DIR)/core/ipv4/dhcp.o: CFLAGS_MOD += -Wno-address -SRC_MOD += extmod/modlwip.c lib/netutils/netutils.c +SRC_MOD += extmod/modlwip.c shared/netutils/netutils.c SRC_MOD += $(addprefix $(LWIP_DIR)/,\ apps/mdns/mdns.c \ core/def.c \ diff --git a/extmod/modlwip.c b/extmod/modlwip.c index 1d557a6a84..00fd109645 100644 --- a/extmod/modlwip.c +++ b/extmod/modlwip.c @@ -35,7 +35,7 @@ #include "py/mperrno.h" #include "py/mphal.h" -#include "lib/netutils/netutils.h" +#include "shared/netutils/netutils.h" #include "lwip/init.h" #include "lwip/tcp.h" diff --git a/extmod/uos_dupterm.c b/extmod/uos_dupterm.c index b661803875..d55767de21 100644 --- a/extmod/uos_dupterm.c +++ b/extmod/uos_dupterm.c @@ -33,7 +33,7 @@ #include "py/objarray.h" #include "py/stream.h" #include "extmod/misc.h" -#include "lib/utils/interrupt_char.h" +#include "shared/runtime/interrupt_char.h" #if MICROPY_PY_OS_DUPTERM diff --git a/extmod/vfs_fat.c b/extmod/vfs_fat.c index 644be57ae7..41284500b4 100644 --- a/extmod/vfs_fat.c +++ b/extmod/vfs_fat.c @@ -37,7 +37,7 @@ #include "py/mperrno.h" #include "lib/oofatfs/ff.h" #include "extmod/vfs_fat.h" -#include "lib/timeutils/timeutils.h" +#include "shared/timeutils/timeutils.h" #if FF_MAX_SS == FF_MIN_SS #define SECSIZE(fs) (FF_MIN_SS) diff --git a/extmod/vfs_lfs.c b/extmod/vfs_lfs.c index dd78269a46..f6a9a24623 100644 --- a/extmod/vfs_lfs.c +++ b/extmod/vfs_lfs.c @@ -26,7 +26,7 @@ #include "py/runtime.h" #include "py/mphal.h" -#include "lib/timeutils/timeutils.h" +#include "shared/timeutils/timeutils.h" #include "extmod/vfs.h" #include "extmod/vfs_lfs.h" diff --git a/extmod/vfs_lfsx.c b/extmod/vfs_lfsx.c index f865e46060..e1324f82c7 100644 --- a/extmod/vfs_lfsx.c +++ b/extmod/vfs_lfsx.c @@ -34,7 +34,7 @@ #include "py/objstr.h" #include "py/mperrno.h" #include "extmod/vfs.h" -#include "lib/timeutils/timeutils.h" +#include "shared/timeutils/timeutils.h" STATIC int MP_VFS_LFSx(dev_ioctl)(const struct LFSx_API (config) * c, int cmd, int arg, bool must_return_int) { mp_obj_t ret = mp_vfs_blockdev_ioctl(c->context, cmd, arg); diff --git a/mpy-cross/Makefile b/mpy-cross/Makefile index 971f2f81aa..2189dff905 100644 --- a/mpy-cross/Makefile +++ b/mpy-cross/Makefile @@ -48,7 +48,7 @@ LDFLAGS = $(LDFLAGS_MOD) $(LDFLAGS_ARCH) -lm $(LDFLAGS_EXTRA) SRC_C = \ main.c \ gccollect.c \ - lib/utils/gchelper_generic.c \ + shared/runtime/gchelper_generic.c \ # Add fmode when compiling with mingw gcc COMPILER_TARGET := $(shell $(CC) -dumpmachine) diff --git a/mpy-cross/gccollect.c b/mpy-cross/gccollect.c index 120f1a2252..72d4204c28 100644 --- a/mpy-cross/gccollect.c +++ b/mpy-cross/gccollect.c @@ -29,7 +29,7 @@ #include "py/mpstate.h" #include "py/gc.h" -#include "lib/utils/gchelper.h" +#include "shared/runtime/gchelper.h" #if MICROPY_ENABLE_GC diff --git a/mpy-cross/mpy-cross.vcxproj b/mpy-cross/mpy-cross.vcxproj index 74a366712a..e70b29ae14 100644 --- a/mpy-cross/mpy-cross.vcxproj +++ b/mpy-cross/mpy-cross.vcxproj @@ -89,7 +89,7 @@ - + MICROPY_GCREGS_SETJMP @@ -103,4 +103,4 @@ - \ No newline at end of file + diff --git a/ports/bare-arm/lib.c b/ports/bare-arm/lib.c index ee7c1d765e..6ef450bea0 100644 --- a/ports/bare-arm/lib.c +++ b/ports/bare-arm/lib.c @@ -54,7 +54,7 @@ void free(void *p) { } // These standard string functions are needed by the runtime, and can be -// provided either by the system or lib/libc/string0.c. The implementations +// provided either by the system or shared/libc/string0.c. The implementations // here are very simple. int memcmp(const void *s1, const void *s2, size_t n) { diff --git a/ports/cc3200/application.mk b/ports/cc3200/application.mk index 5c8fc9e40e..25a9925ea0 100644 --- a/ports/cc3200/application.mk +++ b/ports/cc3200/application.mk @@ -140,15 +140,18 @@ APP_MAIN_SRC_C = \ APP_LIB_SRC_C = $(addprefix lib/,\ oofatfs/ff.c \ oofatfs/ffunicode.c \ + ) + +APP_SHARED_SRC_C = $(addprefix shared/,\ libc/string0.c \ - mp-readline/readline.c \ + readline/readline.c \ netutils/netutils.c \ timeutils/timeutils.c \ - utils/gchelper_native.c \ - utils/pyexec.c \ - utils/interrupt_char.c \ - utils/stdout_helpers.c \ - utils/sys_stdio_mphal.c \ + runtime/gchelper_native.c \ + runtime/pyexec.c \ + runtime/interrupt_char.c \ + runtime/stdout_helpers.c \ + runtime/sys_stdio_mphal.c \ ) APP_STM_SRC_C = $(addprefix ports/stm32/,\ @@ -158,12 +161,12 @@ APP_STM_SRC_C = $(addprefix ports/stm32/,\ OBJ = $(PY_O) $(addprefix $(BUILD)/, $(APP_FATFS_SRC_C:.c=.o) $(APP_RTOS_SRC_C:.c=.o) $(APP_FTP_SRC_C:.c=.o) $(APP_HAL_SRC_C:.c=.o) $(APP_MISC_SRC_C:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(APP_MODS_SRC_C:.c=.o) $(APP_CC3100_SRC_C:.c=.o) $(APP_SL_SRC_C:.c=.o) $(APP_TELNET_SRC_C:.c=.o) $(APP_UTIL_SRC_C:.c=.o) $(APP_UTIL_SRC_S:.s=.o)) -OBJ += $(addprefix $(BUILD)/, $(APP_MAIN_SRC_C:.c=.o) $(APP_LIB_SRC_C:.c=.o) $(APP_STM_SRC_C:.c=.o)) -OBJ += $(BUILD)/lib/utils/gchelper_m3.o +OBJ += $(addprefix $(BUILD)/, $(APP_MAIN_SRC_C:.c=.o) $(APP_SHARED_SRC_C:.c=.o) $(APP_LIB_SRC_C:.c=.o) $(APP_STM_SRC_C:.c=.o)) +OBJ += $(BUILD)/shared/runtime/gchelper_m3.o OBJ += $(BUILD)/pins.o # List of sources for qstr extraction -SRC_QSTR += $(APP_MODS_SRC_C) $(APP_MISC_SRC_C) $(APP_STM_SRC_C) +SRC_QSTR += $(APP_MODS_SRC_C) $(APP_MISC_SRC_C) $(APP_STM_SRC_C) $(APP_SHARED_SRC_C) # Append any auto-generated sources that are needed by sources listed in # SRC_QSTR SRC_QSTR_AUTO_DEPS += diff --git a/ports/cc3200/bootmgr/bootloader.mk b/ports/cc3200/bootmgr/bootloader.mk index 44f1b7f42d..1dc2f82860 100644 --- a/ports/cc3200/bootmgr/bootloader.mk +++ b/ports/cc3200/bootmgr/bootloader.mk @@ -65,14 +65,14 @@ BOOT_PY_SRC_C = $(addprefix py/,\ mpprint.c \ ) -BOOT_LIB_SRC_C = $(addprefix lib/,\ +BOOT_SHARED_SRC_C = $(addprefix shared/,\ + libc/printf.c \ libc/string0.c \ - utils/printf.c \ ) OBJ = $(addprefix $(BUILD)/, $(BOOT_HAL_SRC_C:.c=.o) $(BOOT_SL_SRC_C:.c=.o) $(BOOT_CC3100_SRC_C:.c=.o) $(BOOT_UTIL_SRC_C:.c=.o) $(BOOT_MISC_SRC_C:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(BOOT_MAIN_SRC_C:.c=.o) $(BOOT_MAIN_SRC_S:.s=.o) $(BOOT_PY_SRC_C:.c=.o)) -OBJ += $(addprefix $(BUILD)/, $(BOOT_LIB_SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(BOOT_SHARED_SRC_C:.c=.o)) # Add the linker script LINKER_SCRIPT = bootmgr/bootmgr.lds diff --git a/ports/cc3200/fatfs_port.c b/ports/cc3200/fatfs_port.c index 993684d8c6..39b350db60 100644 --- a/ports/cc3200/fatfs_port.c +++ b/ports/cc3200/fatfs_port.c @@ -27,7 +27,7 @@ #include "py/runtime.h" #include "lib/oofatfs/ff.h" -#include "lib/timeutils/timeutils.h" +#include "shared/timeutils/timeutils.h" #include "mods/pybrtc.h" #if FF_FS_REENTRANT diff --git a/ports/cc3200/ftp/ftp.c b/ports/cc3200/ftp/ftp.c index 37680bc939..d999e810d9 100644 --- a/ports/cc3200/ftp/ftp.c +++ b/ports/cc3200/ftp/ftp.c @@ -28,7 +28,7 @@ #include #include "py/runtime.h" -#include "lib/timeutils/timeutils.h" +#include "shared/timeutils/timeutils.h" #include "lib/oofatfs/ff.h" #include "extmod/vfs.h" #include "extmod/vfs_fat.h" diff --git a/ports/cc3200/mods/moduos.c b/ports/cc3200/mods/moduos.c index 53521fe66c..8c3dcf696a 100644 --- a/ports/cc3200/mods/moduos.c +++ b/ports/cc3200/mods/moduos.c @@ -31,7 +31,7 @@ #include "py/objtuple.h" #include "py/objstr.h" #include "py/runtime.h" -#include "lib/timeutils/timeutils.h" +#include "shared/timeutils/timeutils.h" #include "lib/oofatfs/ff.h" #include "lib/oofatfs/diskio.h" #include "genhdr/mpversion.h" diff --git a/ports/cc3200/mods/modusocket.c b/ports/cc3200/mods/modusocket.c index 5dac0a0102..51815a31f9 100644 --- a/ports/cc3200/mods/modusocket.c +++ b/ports/cc3200/mods/modusocket.c @@ -35,7 +35,7 @@ #include "py/runtime.h" #include "py/stream.h" #include "py/mphal.h" -#include "lib/netutils/netutils.h" +#include "shared/netutils/netutils.h" #include "modnetwork.h" #include "modusocket.h" diff --git a/ports/cc3200/mods/modutime.c b/ports/cc3200/mods/modutime.c index e77065ef45..c5ff6072f5 100644 --- a/ports/cc3200/mods/modutime.c +++ b/ports/cc3200/mods/modutime.c @@ -33,7 +33,7 @@ #include "py/obj.h" #include "py/smallint.h" #include "py/mphal.h" -#include "lib/timeutils/timeutils.h" +#include "shared/timeutils/timeutils.h" #include "extmod/utime_mphal.h" #include "inc/hw_types.h" #include "inc/hw_ints.h" diff --git a/ports/cc3200/mods/modwlan.c b/ports/cc3200/mods/modwlan.c index 7aa8f0e2c4..920079b589 100644 --- a/ports/cc3200/mods/modwlan.c +++ b/ports/cc3200/mods/modwlan.c @@ -35,8 +35,8 @@ #include "py/runtime.h" #include "py/stream.h" #include "py/mphal.h" -#include "lib/timeutils/timeutils.h" -#include "lib/netutils/netutils.h" +#include "shared/timeutils/timeutils.h" +#include "shared/netutils/netutils.h" #include "modnetwork.h" #include "modusocket.h" #include "modwlan.h" diff --git a/ports/cc3200/mods/pybrtc.c b/ports/cc3200/mods/pybrtc.c index f0704eecda..6c1918831c 100644 --- a/ports/cc3200/mods/pybrtc.c +++ b/ports/cc3200/mods/pybrtc.c @@ -29,7 +29,7 @@ #include "py/obj.h" #include "py/runtime.h" #include "py/mperrno.h" -#include "lib/timeutils/timeutils.h" +#include "shared/timeutils/timeutils.h" #include "inc/hw_types.h" #include "inc/hw_ints.h" #include "inc/hw_memmap.h" diff --git a/ports/cc3200/mods/pybuart.c b/ports/cc3200/mods/pybuart.c index ac2af7032e..b8e8cbb682 100644 --- a/ports/cc3200/mods/pybuart.c +++ b/ports/cc3200/mods/pybuart.c @@ -33,7 +33,7 @@ #include "py/objlist.h" #include "py/stream.h" #include "py/mphal.h" -#include "lib/utils/interrupt_char.h" +#include "shared/runtime/interrupt_char.h" #include "inc/hw_types.h" #include "inc/hw_ints.h" #include "inc/hw_memmap.h" diff --git a/ports/cc3200/mptask.c b/ports/cc3200/mptask.c index 71b650ff85..b764c4712c 100644 --- a/ports/cc3200/mptask.c +++ b/ports/cc3200/mptask.c @@ -33,7 +33,7 @@ #include "py/runtime.h" #include "py/gc.h" #include "py/mphal.h" -#include "lib/mp-readline/readline.h" +#include "shared/readline/readline.h" #include "lib/oofatfs/ff.h" #include "lib/oofatfs/diskio.h" #include "extmod/vfs.h" @@ -49,8 +49,8 @@ #include "pybuart.h" #include "pybpin.h" #include "pybrtc.h" -#include "lib/utils/pyexec.h" -#include "lib/utils/gchelper.h" +#include "shared/runtime/pyexec.h" +#include "shared/runtime/gchelper.h" #include "gccollect.h" #include "mperror.h" #include "simplelink.h" diff --git a/ports/cc3200/telnet/telnet.c b/ports/cc3200/telnet/telnet.c index 9f51d4cd85..c4daac3426 100644 --- a/ports/cc3200/telnet/telnet.c +++ b/ports/cc3200/telnet/telnet.c @@ -28,7 +28,7 @@ #include "py/runtime.h" #include "py/mphal.h" -#include "lib/utils/interrupt_char.h" +#include "shared/runtime/interrupt_char.h" #include "telnet.h" #include "simplelink.h" #include "modnetwork.h" diff --git a/ports/cc3200/util/gccollect.c b/ports/cc3200/util/gccollect.c index eac0c86b02..0ff179e48b 100644 --- a/ports/cc3200/util/gccollect.c +++ b/ports/cc3200/util/gccollect.c @@ -30,7 +30,7 @@ #include "py/gc.h" #include "py/mpthread.h" -#include "lib/utils/gchelper.h" +#include "shared/runtime/gchelper.h" #include "gccollect.h" /****************************************************************************** diff --git a/ports/esp32/fatfs_port.c b/ports/esp32/fatfs_port.c index 7fce654c09..b9ad30a128 100644 --- a/ports/esp32/fatfs_port.c +++ b/ports/esp32/fatfs_port.c @@ -28,7 +28,7 @@ #include #include "lib/oofatfs/ff.h" -#include "lib/timeutils/timeutils.h" +#include "shared/timeutils/timeutils.h" DWORD get_fattime(void) { struct timeval tv; diff --git a/ports/esp32/machine_rtc.c b/ports/esp32/machine_rtc.c index 1b6a71b5b4..52227c93b8 100644 --- a/ports/esp32/machine_rtc.c +++ b/ports/esp32/machine_rtc.c @@ -36,7 +36,7 @@ #include "py/obj.h" #include "py/runtime.h" #include "py/mphal.h" -#include "lib/timeutils/timeutils.h" +#include "shared/timeutils/timeutils.h" #include "modmachine.h" #include "machine_rtc.h" diff --git a/ports/esp32/main.c b/ports/esp32/main.c index b04831fff1..c1b8e8a31d 100644 --- a/ports/esp32/main.c +++ b/ports/esp32/main.c @@ -54,8 +54,8 @@ #include "py/repl.h" #include "py/gc.h" #include "py/mphal.h" -#include "lib/mp-readline/readline.h" -#include "lib/utils/pyexec.h" +#include "shared/readline/readline.h" +#include "shared/runtime/pyexec.h" #include "uart.h" #include "usb.h" #include "modmachine.h" diff --git a/ports/esp32/main/CMakeLists.txt b/ports/esp32/main/CMakeLists.txt index ed72a8d68b..f2b3dd4a4f 100644 --- a/ports/esp32/main/CMakeLists.txt +++ b/ports/esp32/main/CMakeLists.txt @@ -15,21 +15,24 @@ set(MICROPY_QSTRDEFS_PORT ${PROJECT_DIR}/qstrdefsport.h ) +set(MICROPY_SOURCE_SHARED + ${MICROPY_DIR}/shared/readline/readline.c + ${MICROPY_DIR}/shared/netutils/netutils.c + ${MICROPY_DIR}/shared/timeutils/timeutils.c + ${MICROPY_DIR}/shared/runtime/interrupt_char.c + ${MICROPY_DIR}/shared/runtime/stdout_helpers.c + ${MICROPY_DIR}/shared/runtime/sys_stdio_mphal.c + ${MICROPY_DIR}/shared/runtime/pyexec.c +) + set(MICROPY_SOURCE_LIB ${MICROPY_DIR}/lib/littlefs/lfs1.c ${MICROPY_DIR}/lib/littlefs/lfs1_util.c ${MICROPY_DIR}/lib/littlefs/lfs2.c ${MICROPY_DIR}/lib/littlefs/lfs2_util.c ${MICROPY_DIR}/lib/mbedtls_errors/mp_mbedtls_errors.c - ${MICROPY_DIR}/lib/mp-readline/readline.c - ${MICROPY_DIR}/lib/netutils/netutils.c ${MICROPY_DIR}/lib/oofatfs/ff.c ${MICROPY_DIR}/lib/oofatfs/ffunicode.c - ${MICROPY_DIR}/lib/timeutils/timeutils.c - ${MICROPY_DIR}/lib/utils/interrupt_char.c - ${MICROPY_DIR}/lib/utils/stdout_helpers.c - ${MICROPY_DIR}/lib/utils/sys_stdio_mphal.c - ${MICROPY_DIR}/lib/utils/pyexec.c ) set(MICROPY_SOURCE_DRIVERS @@ -80,6 +83,7 @@ set(MICROPY_SOURCE_QSTR ${MICROPY_SOURCE_PY} ${MICROPY_SOURCE_EXTMOD} ${MICROPY_SOURCE_USERMOD} + ${MICROPY_SOURCE_SHARED} ${MICROPY_SOURCE_LIB} ${MICROPY_SOURCE_PORT} ) @@ -142,6 +146,7 @@ idf_component_register( SRCS ${MICROPY_SOURCE_PY} ${MICROPY_SOURCE_EXTMOD} + ${MICROPY_SOURCE_SHARED} ${MICROPY_SOURCE_LIB} ${MICROPY_SOURCE_DRIVERS} ${MICROPY_SOURCE_PORT} diff --git a/ports/esp32/modesp32.c b/ports/esp32/modesp32.c index 3ed5343380..042c8b8566 100644 --- a/ports/esp32/modesp32.c +++ b/ports/esp32/modesp32.c @@ -40,7 +40,7 @@ #include "py/obj.h" #include "py/runtime.h" #include "py/mphal.h" -#include "lib/timeutils/timeutils.h" +#include "shared/timeutils/timeutils.h" #include "modmachine.h" #include "machine_rtc.h" #include "modesp32.h" diff --git a/ports/esp32/modmachine.c b/ports/esp32/modmachine.c index c46f8ab8d7..a4e0724cee 100644 --- a/ports/esp32/modmachine.c +++ b/ports/esp32/modmachine.c @@ -49,7 +49,7 @@ #include "py/obj.h" #include "py/runtime.h" -#include "lib/utils/pyexec.h" +#include "shared/runtime/pyexec.h" #include "extmod/machine_mem.h" #include "extmod/machine_signal.h" #include "extmod/machine_pulse.h" diff --git a/ports/esp32/modnetwork.c b/ports/esp32/modnetwork.c index e2bd20a1a9..3977256166 100644 --- a/ports/esp32/modnetwork.c +++ b/ports/esp32/modnetwork.c @@ -40,7 +40,7 @@ #include "py/runtime.h" #include "py/mphal.h" #include "py/mperrno.h" -#include "lib/netutils/netutils.h" +#include "shared/netutils/netutils.h" #include "esp_eth.h" #include "esp_wifi.h" #include "esp_log.h" diff --git a/ports/esp32/modsocket.c b/ports/esp32/modsocket.c index 67b1d7d6f0..be950ac1f6 100644 --- a/ports/esp32/modsocket.c +++ b/ports/esp32/modsocket.c @@ -45,7 +45,7 @@ #include "py/mphal.h" #include "py/stream.h" #include "py/mperrno.h" -#include "lib/netutils/netutils.h" +#include "shared/netutils/netutils.h" #include "mdns.h" #include "modnetwork.h" diff --git a/ports/esp32/modutime.c b/ports/esp32/modutime.c index cf7178e0b1..f864df678f 100644 --- a/ports/esp32/modutime.c +++ b/ports/esp32/modutime.c @@ -31,7 +31,7 @@ #include #include "py/runtime.h" -#include "lib/timeutils/timeutils.h" +#include "shared/timeutils/timeutils.h" #include "extmod/utime_mphal.h" STATIC mp_obj_t time_localtime(size_t n_args, const mp_obj_t *args) { diff --git a/ports/esp32/mphalport.c b/ports/esp32/mphalport.c index db2146d93d..de7fdfcec3 100644 --- a/ports/esp32/mphalport.c +++ b/ports/esp32/mphalport.c @@ -47,8 +47,8 @@ #include "py/mpstate.h" #include "py/mphal.h" #include "extmod/misc.h" -#include "lib/timeutils/timeutils.h" -#include "lib/utils/pyexec.h" +#include "shared/timeutils/timeutils.h" +#include "shared/runtime/pyexec.h" #include "mphalport.h" #include "usb.h" diff --git a/ports/esp32/mphalport.h b/ports/esp32/mphalport.h index 60cc308d68..2ba6f96b6a 100644 --- a/ports/esp32/mphalport.h +++ b/ports/esp32/mphalport.h @@ -30,7 +30,7 @@ #define INCLUDED_MPHALPORT_H #include "py/ringbuf.h" -#include "lib/utils/interrupt_char.h" +#include "shared/runtime/interrupt_char.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" diff --git a/ports/esp32/network_ppp.c b/ports/esp32/network_ppp.c index db2292ca03..d74283c19c 100644 --- a/ports/esp32/network_ppp.c +++ b/ports/esp32/network_ppp.c @@ -30,7 +30,7 @@ #include "py/mphal.h" #include "py/objtype.h" #include "py/stream.h" -#include "lib/netutils/netutils.h" +#include "shared/netutils/netutils.h" #include "modmachine.h" #include "netif/ppp/ppp.h" diff --git a/ports/esp8266/Makefile b/ports/esp8266/Makefile index 9d6aea603a..b54193afef 100644 --- a/ports/esp8266/Makefile +++ b/ports/esp8266/Makefile @@ -119,8 +119,6 @@ EXTMOD_SRC_C = $(addprefix extmod/,\ ) LIB_SRC_C = $(addprefix lib/,\ - embed/__errno.c \ - libc/string0.c \ libm/math.c \ libm/fmodf.c \ libm/nearbyintf.c \ @@ -140,13 +138,18 @@ LIB_SRC_C = $(addprefix lib/,\ libm/atanf.c \ libm/atan2f.c \ libm/roundf.c \ - mp-readline/readline.c \ + ) + +SHARED_SRC_C = $(addprefix shared/,\ + libc/__errno.c \ + libc/string0.c \ netutils/netutils.c \ + readline/readline.c \ + runtime/interrupt_char.c \ + runtime/pyexec.c \ + runtime/stdout_helpers.c \ + runtime/sys_stdio_mphal.c \ timeutils/timeutils.c \ - utils/pyexec.c \ - utils/interrupt_char.c \ - utils/stdout_helpers.c \ - utils/sys_stdio_mphal.c \ ) DRIVERS_SRC_C = $(addprefix drivers/,\ @@ -162,11 +165,12 @@ OBJ += $(PY_O) OBJ += $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_S:.s=.o)) OBJ += $(addprefix $(BUILD)/, $(EXTMOD_SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(SHARED_SRC_C:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(LIB_SRC_C:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(DRIVERS_SRC_C:.c=.o)) # List of sources for qstr extraction -SRC_QSTR += $(SRC_C) $(EXTMOD_SRC_C) $(LIB_SRC_C) $(DRIVERS_SRC_C) +SRC_QSTR += $(SRC_C) $(EXTMOD_SRC_C) $(SHARED_SRC_C) $(DRIVERS_SRC_C) # Append any auto-generated sources that are needed by sources listed in SRC_QSTR SRC_QSTR_AUTO_DEPS += diff --git a/ports/esp8266/boards/esp8266_common.ld b/ports/esp8266/boards/esp8266_common.ld index c6e37d942c..f40ff1e5fc 100644 --- a/ports/esp8266/boards/esp8266_common.ld +++ b/ports/esp8266/boards/esp8266_common.ld @@ -134,14 +134,14 @@ SECTIONS *lib/berkeley-db-1.xx/*.o(.literal*, .text*) *lib/libm/*.o*(.literal*, .text*) *lib/littlefs/*.o*(.literal*, .text*) - *lib/mp-readline/*.o(.literal*, .text*) - *lib/netutils/*.o*(.literal*, .text*) - *lib/timeutils/*.o*(.literal*, .text*) - *lib/utils/printf.o*(.literal*, .text*) - *lib/utils/sys_stdio_mphal.o*(.literal*, .text*) - *lib/utils/pyexec.o*(.literal*, .text*) - *lib/utils/stdout_helpers.o*(.literal*, .text*) - *lib/utils/interrupt_char.o*(.literal*, .text*) + *shared/libc/printf.o*(.literal*, .text*) + *shared/netutils/*.o*(.literal*, .text*) + *shared/readline/*.o(.literal*, .text*) + *shared/runtime/interrupt_char.o*(.literal*, .text*) + *shared/runtime/pyexec.o*(.literal*, .text*) + *shared/runtime/stdout_helpers.o*(.literal*, .text*) + *shared/runtime/sys_stdio_mphal.o*(.literal*, .text*) + *shared/timeutils/*.o*(.literal*, .text*) *drivers/bus/*.o(.literal* .text*) build-*/main.o(.literal* .text*) diff --git a/ports/esp8266/esp_mphal.c b/ports/esp8266/esp_mphal.c index 06e6cd0af5..3cb4807333 100644 --- a/ports/esp8266/esp_mphal.c +++ b/ports/esp8266/esp_mphal.c @@ -34,7 +34,7 @@ #include "py/runtime.h" #include "py/stream.h" #include "extmod/misc.h" -#include "lib/utils/pyexec.h" +#include "shared/runtime/pyexec.h" STATIC byte stdin_ringbuf_array[256]; ringbuf_t stdin_ringbuf = {stdin_ringbuf_array, sizeof(stdin_ringbuf_array), 0, 0}; diff --git a/ports/esp8266/esp_mphal.h b/ports/esp8266/esp_mphal.h index e9cf7e548d..b0351877e8 100644 --- a/ports/esp8266/esp_mphal.h +++ b/ports/esp8266/esp_mphal.h @@ -26,7 +26,7 @@ #include "user_interface.h" #include "py/ringbuf.h" -#include "lib/utils/interrupt_char.h" +#include "shared/runtime/interrupt_char.h" #include "xtirq.h" void mp_sched_keyboard_interrupt(void); diff --git a/ports/esp8266/fatfs_port.c b/ports/esp8266/fatfs_port.c index bbd1051935..3ffc90040c 100644 --- a/ports/esp8266/fatfs_port.c +++ b/ports/esp8266/fatfs_port.c @@ -25,7 +25,7 @@ */ #include "py/obj.h" -#include "lib/timeutils/timeutils.h" +#include "shared/timeutils/timeutils.h" #include "lib/oofatfs/ff.h" #include "modmachine.h" diff --git a/ports/esp8266/machine_rtc.c b/ports/esp8266/machine_rtc.c index e7b750fd92..38049ce724 100644 --- a/ports/esp8266/machine_rtc.c +++ b/ports/esp8266/machine_rtc.c @@ -28,7 +28,7 @@ #include #include "py/runtime.h" -#include "lib/timeutils/timeutils.h" +#include "shared/timeutils/timeutils.h" #include "user_interface.h" #include "modmachine.h" diff --git a/ports/esp8266/main.c b/ports/esp8266/main.c index a73fbbcf5f..404188346c 100644 --- a/ports/esp8266/main.c +++ b/ports/esp8266/main.c @@ -39,8 +39,8 @@ #define USE_US_TIMER 1 #include "extmod/misc.h" -#include "lib/mp-readline/readline.h" -#include "lib/utils/pyexec.h" +#include "shared/readline/readline.h" +#include "shared/runtime/pyexec.h" #include "gccollect.h" #include "user_interface.h" diff --git a/ports/esp8266/modmachine.c b/ports/esp8266/modmachine.c index 86c1d728d5..2b1f0f0835 100644 --- a/ports/esp8266/modmachine.c +++ b/ports/esp8266/modmachine.c @@ -30,7 +30,7 @@ #include "py/obj.h" #include "py/runtime.h" -#include "lib/utils/pyexec.h" +#include "shared/runtime/pyexec.h" // This needs to be set before we include the RTOS headers #define USE_US_TIMER 1 diff --git a/ports/esp8266/modnetwork.c b/ports/esp8266/modnetwork.c index 8d153acf0f..949aeba106 100644 --- a/ports/esp8266/modnetwork.c +++ b/ports/esp8266/modnetwork.c @@ -31,7 +31,7 @@ #include "py/objlist.h" #include "py/runtime.h" #include "py/mphal.h" -#include "lib/netutils/netutils.h" +#include "shared/netutils/netutils.h" #include "queue.h" #include "user_interface.h" #include "espconn.h" diff --git a/ports/esp8266/modutime.c b/ports/esp8266/modutime.c index bcfbf7baf2..1d4ecc05f2 100644 --- a/ports/esp8266/modutime.c +++ b/ports/esp8266/modutime.c @@ -32,7 +32,7 @@ #include "py/runtime.h" #include "py/mphal.h" #include "py/smallint.h" -#include "lib/timeutils/timeutils.h" +#include "shared/timeutils/timeutils.h" #include "modmachine.h" #include "user_interface.h" #include "extmod/utime_mphal.h" diff --git a/ports/esp8266/uart.c b/ports/esp8266/uart.c index 5ac71da07d..978a7efc38 100644 --- a/ports/esp8266/uart.c +++ b/ports/esp8266/uart.c @@ -285,7 +285,7 @@ void ICACHE_FLASH_ATTR uart0_set_rxbuf(uint8 *buf, int len) { // Task-based UART interface #include "py/obj.h" -#include "lib/utils/pyexec.h" +#include "shared/runtime/pyexec.h" #if MICROPY_REPL_EVENT_DRIVEN void ICACHE_FLASH_ATTR uart_task_handler(os_event_t *evt) { diff --git a/ports/javascript/Makefile b/ports/javascript/Makefile index 9cc45d3ef9..34bbe5f2f7 100644 --- a/ports/javascript/Makefile +++ b/ports/javascript/Makefile @@ -24,11 +24,11 @@ CFLAGS += -DMICROPY_QSTR_EXTRA_POOL=mp_qstr_frozen_const_pool CFLAGS += -DMICROPY_MODULE_FROZEN_MPY endif -SRC_LIB = $(addprefix lib/,\ - utils/interrupt_char.c \ - utils/stdout_helpers.c \ - utils/pyexec.c \ - mp-readline/readline.c \ +SRC_SHARED = $(addprefix shared/,\ + runtime/interrupt_char.c \ + runtime/stdout_helpers.c \ + runtime/pyexec.c \ + readline/readline.c \ ) SRC_C = \ @@ -39,7 +39,7 @@ SRC_C = \ SRC_QSTR += $(SRC_C) OBJ += $(PY_O) -OBJ += $(addprefix $(BUILD)/, $(SRC_LIB:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(SRC_SHARED:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) JSFLAGS += -s ASYNCIFY diff --git a/ports/javascript/main.c b/ports/javascript/main.c index c56f1a2cba..7a04b8eea3 100644 --- a/ports/javascript/main.c +++ b/ports/javascript/main.c @@ -34,7 +34,7 @@ #include "py/repl.h" #include "py/gc.h" #include "py/mperrno.h" -#include "lib/utils/pyexec.h" +#include "shared/runtime/pyexec.h" #include "emscripten.h" #include "library.h" diff --git a/ports/javascript/modutime.c b/ports/javascript/modutime.c index d05d832ab1..05ff8b22f3 100644 --- a/ports/javascript/modutime.c +++ b/ports/javascript/modutime.c @@ -32,7 +32,7 @@ #include "py/smallint.h" #include "py/obj.h" #include "py/runtime.h" -#include "lib/timeutils/timeutils.h" +#include "shared/timeutils/timeutils.h" #include "extmod/utime_mphal.h" STATIC const mp_rom_map_elem_t time_module_globals_table[] = { diff --git a/ports/javascript/mphalport.h b/ports/javascript/mphalport.h index 614ce8526a..3e2c439f32 100644 --- a/ports/javascript/mphalport.h +++ b/ports/javascript/mphalport.h @@ -25,7 +25,7 @@ */ #include "py/obj.h" -#include "lib/utils/interrupt_char.h" +#include "shared/runtime/interrupt_char.h" #define mp_hal_stdin_rx_chr() (0) void mp_hal_stdout_tx_strn(const char *str, size_t len); diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index fdb1325fdd..64d9864cbb 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -155,15 +155,15 @@ SRC_C = \ moduos.c \ mphalport.c \ hal/flexspi_nor_flash.c \ - lib/mp-readline/readline.c \ - lib/libc/string0.c \ - lib/timeutils/timeutils.c \ - lib/utils/gchelper_native.c \ - lib/utils/mpirq.c \ - lib/utils/printf.c \ - lib/utils/pyexec.c \ - lib/utils/stdout_helpers.c \ - lib/utils/sys_stdio_mphal.c \ + shared/libc/printf.c \ + shared/libc/string0.c \ + shared/readline/readline.c \ + shared/runtime/gchelper_native.c \ + shared/runtime/mpirq.c \ + shared/runtime/pyexec.c \ + shared/runtime/stdout_helpers.c \ + shared/runtime/sys_stdio_mphal.c \ + shared/timeutils/timeutils.c \ drivers/bus/softspi.c \ extmod/modonewire.c \ $(SRC_TINYUSB_C) \ @@ -264,7 +264,7 @@ endif SRC_SS = $(MCU_DIR)/gcc/startup_$(MCU_SERIES).S -SRC_S = lib/utils/gchelper_m3.s \ +SRC_S = shared/runtime/gchelper_m3.s \ # List of sources for qstr extraction SRC_QSTR += \ @@ -281,8 +281,8 @@ SRC_QSTR += \ modmimxrt.c \ moduos.c \ pin.c \ - lib/utils/mpirq.c \ - lib/utils/sys_stdio_mphal.c \ + shared/runtime/mpirq.c \ + shared/runtime/sys_stdio_mphal.c \ extmod/modonewire.c \ $(GEN_PINS_SRC) \ diff --git a/ports/mimxrt/machine_pin.c b/ports/mimxrt/machine_pin.c index dc26cdfc12..3a1151ba81 100644 --- a/ports/mimxrt/machine_pin.c +++ b/ports/mimxrt/machine_pin.c @@ -31,7 +31,7 @@ #include "py/runtime.h" #include "py/mphal.h" -#include "lib/utils/mpirq.h" +#include "shared/runtime/mpirq.h" #include "pin.h" // Local functions diff --git a/ports/mimxrt/machine_rtc.c b/ports/mimxrt/machine_rtc.c index d00d139f79..f08e73fa55 100644 --- a/ports/mimxrt/machine_rtc.c +++ b/ports/mimxrt/machine_rtc.c @@ -26,7 +26,7 @@ */ #include "py/runtime.h" -#include "lib/timeutils/timeutils.h" +#include "shared/timeutils/timeutils.h" #include "modmachine.h" #include "ticks.h" #include "fsl_snvs_lp.h" diff --git a/ports/mimxrt/main.c b/ports/mimxrt/main.c index c93e89c8ab..e81974afaa 100644 --- a/ports/mimxrt/main.c +++ b/ports/mimxrt/main.c @@ -30,9 +30,9 @@ #include "py/gc.h" #include "py/mperrno.h" #include "py/stackctrl.h" -#include "lib/mp-readline/readline.h" -#include "lib/utils/gchelper.h" -#include "lib/utils/pyexec.h" +#include "shared/readline/readline.h" +#include "shared/runtime/gchelper.h" +#include "shared/runtime/pyexec.h" #include "ticks.h" #include "tusb.h" #include "led.h" diff --git a/ports/mimxrt/modutime.c b/ports/mimxrt/modutime.c index 2a88a4224b..d3be7c2ea6 100644 --- a/ports/mimxrt/modutime.c +++ b/ports/mimxrt/modutime.c @@ -26,7 +26,7 @@ */ #include "py/runtime.h" -#include "lib/timeutils/timeutils.h" +#include "shared/timeutils/timeutils.h" #include "extmod/utime_mphal.h" #include "fsl_snvs_lp.h" diff --git a/ports/mimxrt/mphalport.c b/ports/mimxrt/mphalport.c index 942deae5b7..ff7e988dff 100644 --- a/ports/mimxrt/mphalport.c +++ b/ports/mimxrt/mphalport.c @@ -28,7 +28,7 @@ #include "py/runtime.h" #include "py/stream.h" #include "py/mphal.h" -#include "lib/timeutils/timeutils.h" +#include "shared/timeutils/timeutils.h" #include "ticks.h" #include "tusb.h" #include "fsl_snvs_lp.h" diff --git a/ports/mimxrt/pin.h b/ports/mimxrt/pin.h index ace4d18436..8d80490d1c 100644 --- a/ports/mimxrt/pin.h +++ b/ports/mimxrt/pin.h @@ -29,7 +29,7 @@ #include #include "py/obj.h" -#include "lib/utils/mpirq.h" +#include "shared/runtime/mpirq.h" #include "fsl_gpio.h" // ------------------------------------------------------------------------------------------------------------------ // diff --git a/ports/minimal/Makefile b/ports/minimal/Makefile index bce544ec17..0e79d581be 100644 --- a/ports/minimal/Makefile +++ b/ports/minimal/Makefile @@ -46,14 +46,14 @@ LIBS = SRC_C = \ main.c \ uart_core.c \ - lib/utils/printf.c \ - lib/utils/stdout_helpers.c \ - lib/utils/pyexec.c \ - lib/mp-readline/readline.c \ + shared/libc/printf.c \ + shared/readline/readline.c \ + shared/runtime/pyexec.c \ + shared/runtime/stdout_helpers.c \ $(BUILD)/_frozen_mpy.c \ ifeq ($(CROSS), 1) -SRC_C += lib/libc/string0.c +SRC_C += shared/libc/string0.c endif OBJ = $(PY_CORE_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) diff --git a/ports/minimal/main.c b/ports/minimal/main.c index aa6d10ee37..006ca0a4d0 100644 --- a/ports/minimal/main.c +++ b/ports/minimal/main.c @@ -7,7 +7,7 @@ #include "py/repl.h" #include "py/gc.h" #include "py/mperrno.h" -#include "lib/utils/pyexec.h" +#include "shared/runtime/pyexec.h" #if MICROPY_ENABLE_COMPILER void do_str(const char *src, mp_parse_input_kind_t input_kind) { diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index a3f914dbc1..7428ab82c0 100644 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -70,7 +70,7 @@ INC += -I./modules/ubluepy INC += -I./modules/music INC += -I./modules/ble INC += -I./modules/board -INC += -I../../lib/mp-readline +INC += -I../../shared/readline INC += -I./drivers/bluetooth INC += -I./drivers INC += -I../../lib/nrfx/ @@ -216,12 +216,12 @@ include drivers/secureboot/secureboot.mk endif -SRC_LIB += $(addprefix lib/,\ +SRC_LIB += $(addprefix shared/,\ libc/string0.c \ - mp-readline/readline.c \ - utils/pyexec.c \ - utils/sys_stdio_mphal.c \ - utils/interrupt_char.c \ + readline/readline.c \ + runtime/pyexec.c \ + runtime/sys_stdio_mphal.c \ + runtime/interrupt_char.c \ timeutils/timeutils.c \ ) diff --git a/ports/nrf/drivers/bluetooth/ble_uart.c b/ports/nrf/drivers/bluetooth/ble_uart.c index 96c2025d0f..c3712fe8c9 100644 --- a/ports/nrf/drivers/bluetooth/ble_uart.c +++ b/ports/nrf/drivers/bluetooth/ble_uart.c @@ -30,7 +30,7 @@ #include "ble_uart.h" #include "ringbuffer.h" #include "mphalport.h" -#include "lib/utils/interrupt_char.h" +#include "shared/runtime/interrupt_char.h" #include "py/runtime.h" #if MICROPY_PY_SYS_STDFILES diff --git a/ports/nrf/main.c b/ports/nrf/main.c index 86ba83342b..3de5a310f0 100644 --- a/ports/nrf/main.c +++ b/ports/nrf/main.c @@ -38,7 +38,7 @@ #include "py/stackctrl.h" #include "py/gc.h" #include "py/compile.h" -#include "lib/utils/pyexec.h" +#include "shared/runtime/pyexec.h" #include "readline.h" #include "gccollect.h" #include "modmachine.h" diff --git a/ports/nrf/modules/board/modboard.c b/ports/nrf/modules/board/modboard.c index 5f59a52a6b..647af5035b 100644 --- a/ports/nrf/modules/board/modboard.c +++ b/ports/nrf/modules/board/modboard.c @@ -25,7 +25,7 @@ */ #include "py/builtin.h" -#include "lib/utils/pyexec.h" +#include "shared/runtime/pyexec.h" #include "py/runtime.h" #include "py/obj.h" #include "led.h" diff --git a/ports/nrf/modules/machine/modmachine.c b/ports/nrf/modules/machine/modmachine.c index 7e45b83df8..fb3267e0db 100644 --- a/ports/nrf/modules/machine/modmachine.c +++ b/ports/nrf/modules/machine/modmachine.c @@ -33,7 +33,7 @@ #include "extmod/machine_mem.h" #include "extmod/machine_pulse.h" #include "extmod/machine_i2c.h" -#include "lib/utils/pyexec.h" +#include "shared/runtime/pyexec.h" #include "lib/oofatfs/ff.h" #include "lib/oofatfs/diskio.h" #include "gccollect.h" diff --git a/ports/nrf/modules/machine/uart.c b/ports/nrf/modules/machine/uart.c index af95ae4b8d..2cc421aa1f 100644 --- a/ports/nrf/modules/machine/uart.c +++ b/ports/nrf/modules/machine/uart.c @@ -38,7 +38,7 @@ #include "py/ringbuf.h" #include "pin.h" #include "genhdr/pins.h" -#include "lib/utils/interrupt_char.h" +#include "shared/runtime/interrupt_char.h" #include "uart.h" #include "mpconfigboard.h" diff --git a/ports/pic16bit/Makefile b/ports/pic16bit/Makefile index 3c2bd54ea2..f0d55ec875 100644 --- a/ports/pic16bit/Makefile +++ b/ports/pic16bit/Makefile @@ -39,9 +39,9 @@ SRC_C = \ modpyb.c \ modpybled.c \ modpybswitch.c \ - lib/utils/pyexec.c \ - lib/utils/sys_stdio_mphal.c \ - lib/mp-readline/readline.c \ + shared/runtime/pyexec.c \ + shared/runtime/sys_stdio_mphal.c \ + shared/readline/readline.c \ SRC_S = \ # gchelper.s \ diff --git a/ports/pic16bit/main.c b/ports/pic16bit/main.c index 07db3f5827..adb9276071 100644 --- a/ports/pic16bit/main.c +++ b/ports/pic16bit/main.c @@ -34,8 +34,8 @@ #include "py/gc.h" #include "py/mphal.h" #include "py/mperrno.h" -#include "lib/utils/pyexec.h" -#include "lib/mp-readline/readline.h" +#include "shared/runtime/pyexec.h" +#include "shared/readline/readline.h" #include "board.h" #include "modpyb.h" diff --git a/ports/powerpc/Makefile b/ports/powerpc/Makefile index 1f5ec80d43..cca170dc02 100644 --- a/ports/powerpc/Makefile +++ b/ports/powerpc/Makefile @@ -34,11 +34,11 @@ LIBS = SRC_C = \ main.c \ uart_$(UART).c \ - lib/utils/printf.c \ - lib/utils/stdout_helpers.c \ - lib/utils/pyexec.c \ - lib/libc/string0.c \ - lib/mp-readline/readline.c \ + shared/libc/printf.c \ + shared/libc/string0.c \ + shared/readline/readline.c \ + shared/runtime/pyexec.c \ + shared/runtime/stdout_helpers.c \ $(BUILD)/_frozen_mpy.c \ OBJ = $(PY_CORE_O) diff --git a/ports/powerpc/main.c b/ports/powerpc/main.c index fdeec13abe..a66d737c13 100644 --- a/ports/powerpc/main.c +++ b/ports/powerpc/main.c @@ -32,7 +32,7 @@ #include "py/gc.h" #include "py/mperrno.h" #include "py/stackctrl.h" -#include "lib/utils/pyexec.h" +#include "shared/runtime/pyexec.h" void __stack_chk_fail(void); void __stack_chk_fail(void) { diff --git a/ports/qemu-arm/Makefile b/ports/qemu-arm/Makefile index 22d0bf39cd..4a6d63a014 100644 --- a/ports/qemu-arm/Makefile +++ b/ports/qemu-arm/Makefile @@ -16,7 +16,7 @@ ifeq ($(BOARD),netduino2) CFLAGS += -mthumb -mcpu=cortex-m3 -mfloat-abi=soft CFLAGS += -DQEMU_SOC_STM32 LDSCRIPT = stm32.ld -SRC_BOARD_O = lib/utils/gchelper_native.o lib/utils/gchelper_m3.o +SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_m3.o MPY_CROSS_FLAGS += -march=armv7m endif @@ -25,7 +25,7 @@ CFLAGS += -mthumb -mcpu=cortex-m0 -mfloat-abi=soft CFLAGS += -DQEMU_SOC_NRF51 LDSCRIPT = nrf51.ld QEMU_EXTRA = -global nrf51-soc.flash-size=1048576 -global nrf51-soc.sram-size=262144 -SRC_BOARD_O = lib/utils/gchelper_native.o lib/utils/gchelper_m0.o +SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_m0.o MPY_CROSS_FLAGS += -march=armv7m endif @@ -33,7 +33,7 @@ ifeq ($(BOARD),mps2-an385) CFLAGS += -mthumb -mcpu=cortex-m3 -mfloat-abi=soft CFLAGS += -DQEMU_SOC_MPS2 LDSCRIPT = mps2.ld -SRC_BOARD_O = lib/utils/gchelper_native.o lib/utils/gchelper_m3.o +SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_m3.o MPY_CROSS_FLAGS += -march=armv7m endif @@ -42,7 +42,7 @@ CFLAGS += -mcpu=cortex-a9 CFLAGS += -DQEMU_SOC_IMX6 LDSCRIPT = imx6.ld QEMU_EXTRA = -m 128M -SRC_BOARD_O = lib/utils/gchelper_generic.o +SRC_BOARD_O = shared/runtime/gchelper_generic.o # It's really armv7a but closest supported value is armv6. MPY_CROSS_FLAGS += -march=armv6 endif @@ -79,6 +79,8 @@ SRC_COMMON_C = \ uart.c \ moduos.c \ modmachine.c \ + shared/libc/string0.c \ + shared/runtime/sys_stdio_mphal.c \ SRC_RUN_C = \ main.c \ @@ -88,7 +90,6 @@ SRC_TEST_C = \ lib/tinytest/tinytest.c \ LIB_SRC_C += $(addprefix lib/,\ - libc/string0.c \ libm/math.c \ libm/fmodf.c \ libm/nearbyintf.c \ @@ -108,7 +109,6 @@ LIB_SRC_C += $(addprefix lib/,\ libm/atanf.c \ libm/atan2f.c \ libm/roundf.c \ - utils/sys_stdio_mphal.c \ ) OBJ_COMMON = diff --git a/ports/qemu-arm/Makefile.test b/ports/qemu-arm/Makefile.test index b4ad6114b8..e0f3a54169 100644 --- a/ports/qemu-arm/Makefile.test +++ b/ports/qemu-arm/Makefile.test @@ -1,4 +1,4 @@ -LIB_SRC_C = lib/upytesthelper/upytesthelper.c +LIB_SRC_C = shared/upytesthelper/upytesthelper.c FROZEN_MANIFEST ?= "freeze('test-frzmpy')" diff --git a/ports/qemu-arm/mpconfigport.h b/ports/qemu-arm/mpconfigport.h index 1f05719caa..270995979c 100644 --- a/ports/qemu-arm/mpconfigport.h +++ b/ports/qemu-arm/mpconfigport.h @@ -74,7 +74,7 @@ extern const struct _mp_obj_module_t mp_module_uos; #include #ifdef TEST -#include "lib/upytesthelper/upytesthelper.h" +#include "shared/upytesthelper/upytesthelper.h" #undef MP_PLAT_PRINT_STRN #define MP_PLAT_PRINT_STRN(str, len) upytest_output(str, len) #endif diff --git a/ports/qemu-arm/test_main.c b/ports/qemu-arm/test_main.c index a59a8de0ad..284b287311 100644 --- a/ports/qemu-arm/test_main.c +++ b/ports/qemu-arm/test_main.c @@ -11,7 +11,7 @@ #include "py/stackctrl.h" #include "py/gc.h" #include "py/mperrno.h" -#include "lib/utils/gchelper.h" +#include "shared/runtime/gchelper.h" #include "lib/tinytest/tinytest.h" #include "lib/tinytest/tinytest_macros.h" diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt index eb2b3d2a25..3dea793ef9 100644 --- a/ports/rp2/CMakeLists.txt +++ b/ports/rp2/CMakeLists.txt @@ -64,16 +64,16 @@ set(MICROPY_SOURCE_LIB ${MICROPY_DIR}/lib/littlefs/lfs1_util.c ${MICROPY_DIR}/lib/littlefs/lfs2.c ${MICROPY_DIR}/lib/littlefs/lfs2_util.c - ${MICROPY_DIR}/lib/mp-readline/readline.c ${MICROPY_DIR}/lib/oofatfs/ff.c ${MICROPY_DIR}/lib/oofatfs/ffunicode.c - ${MICROPY_DIR}/lib/timeutils/timeutils.c - ${MICROPY_DIR}/lib/utils/gchelper_m0.s - ${MICROPY_DIR}/lib/utils/gchelper_native.c - ${MICROPY_DIR}/lib/utils/mpirq.c - ${MICROPY_DIR}/lib/utils/pyexec.c - ${MICROPY_DIR}/lib/utils/stdout_helpers.c - ${MICROPY_DIR}/lib/utils/sys_stdio_mphal.c + ${MICROPY_DIR}/shared/readline/readline.c + ${MICROPY_DIR}/shared/runtime/gchelper_m0.s + ${MICROPY_DIR}/shared/runtime/gchelper_native.c + ${MICROPY_DIR}/shared/runtime/mpirq.c + ${MICROPY_DIR}/shared/runtime/pyexec.c + ${MICROPY_DIR}/shared/runtime/stdout_helpers.c + ${MICROPY_DIR}/shared/runtime/sys_stdio_mphal.c + ${MICROPY_DIR}/shared/timeutils/timeutils.c ) set(MICROPY_SOURCE_DRIVERS @@ -108,8 +108,8 @@ set(MICROPY_SOURCE_QSTR ${MICROPY_SOURCE_PY} ${MICROPY_SOURCE_EXTMOD} ${MICROPY_SOURCE_USERMOD} - ${MICROPY_DIR}/lib/utils/mpirq.c - ${MICROPY_DIR}/lib/utils/sys_stdio_mphal.c + ${MICROPY_DIR}/shared/runtime/mpirq.c + ${MICROPY_DIR}/shared/runtime/sys_stdio_mphal.c ${PROJECT_SOURCE_DIR}/machine_adc.c ${PROJECT_SOURCE_DIR}/machine_i2c.c ${PROJECT_SOURCE_DIR}/machine_pin.c diff --git a/ports/rp2/machine_pin.c b/ports/rp2/machine_pin.c index 0525e0f9f6..aa3cad6ef0 100644 --- a/ports/rp2/machine_pin.c +++ b/ports/rp2/machine_pin.c @@ -29,7 +29,7 @@ #include "py/runtime.h" #include "py/mphal.h" -#include "lib/utils/mpirq.h" +#include "shared/runtime/mpirq.h" #include "modmachine.h" #include "extmod/virtpin.h" diff --git a/ports/rp2/machine_rtc.c b/ports/rp2/machine_rtc.c index a7c55b616b..9d59124a66 100644 --- a/ports/rp2/machine_rtc.c +++ b/ports/rp2/machine_rtc.c @@ -35,7 +35,7 @@ #include "py/runtime.h" #include "py/mphal.h" #include "py/mperrno.h" -#include "lib/timeutils/timeutils.h" +#include "shared/timeutils/timeutils.h" #include "hardware/rtc.h" #include "pico/util/datetime.h" #include "modmachine.h" diff --git a/ports/rp2/main.c b/ports/rp2/main.c index 8c5772171a..e615f69112 100644 --- a/ports/rp2/main.c +++ b/ports/rp2/main.c @@ -32,9 +32,9 @@ #include "py/mperrno.h" #include "py/mphal.h" #include "py/stackctrl.h" -#include "lib/mp-readline/readline.h" -#include "lib/utils/gchelper.h" -#include "lib/utils/pyexec.h" +#include "shared/readline/readline.h" +#include "shared/runtime/gchelper.h" +#include "shared/runtime/pyexec.h" #include "tusb.h" #include "uart.h" #include "modmachine.h" diff --git a/ports/rp2/modmachine.c b/ports/rp2/modmachine.c index 3656a7db5a..1b2b2adad7 100644 --- a/ports/rp2/modmachine.c +++ b/ports/rp2/modmachine.c @@ -26,7 +26,7 @@ #include "py/runtime.h" #include "py/mphal.h" -#include "lib/utils/pyexec.h" +#include "shared/runtime/pyexec.h" #include "extmod/machine_i2c.h" #include "extmod/machine_mem.h" #include "extmod/machine_pulse.h" diff --git a/ports/rp2/modutime.c b/ports/rp2/modutime.c index 4835a0ee11..8041ae65b6 100644 --- a/ports/rp2/modutime.c +++ b/ports/rp2/modutime.c @@ -25,7 +25,7 @@ */ #include "py/runtime.h" -#include "lib/timeutils/timeutils.h" +#include "shared/timeutils/timeutils.h" #include "extmod/utime_mphal.h" #include "hardware/rtc.h" diff --git a/ports/rp2/mphalport.c b/ports/rp2/mphalport.c index 1d77bc8f5d..3385323db0 100644 --- a/ports/rp2/mphalport.c +++ b/ports/rp2/mphalport.c @@ -27,7 +27,7 @@ #include "py/runtime.h" #include "py/stream.h" #include "py/mphal.h" -#include "lib/timeutils/timeutils.h" +#include "shared/timeutils/timeutils.h" #include "tusb.h" #include "uart.h" #include "hardware/rtc.h" diff --git a/ports/rp2/rp2_pio.c b/ports/rp2/rp2_pio.c index 44928c0a88..414fa8bd70 100644 --- a/ports/rp2/rp2_pio.c +++ b/ports/rp2/rp2_pio.c @@ -30,7 +30,7 @@ #include "py/runtime.h" #include "py/mperrno.h" #include "py/mphal.h" -#include "lib/utils/mpirq.h" +#include "shared/runtime/mpirq.h" #include "modrp2.h" #include "hardware/clocks.h" diff --git a/ports/samd/Makefile b/ports/samd/Makefile index ffd0f06a3f..d29d9c5b24 100644 --- a/ports/samd/Makefile +++ b/ports/samd/Makefile @@ -53,27 +53,27 @@ SRC_C = \ samd_isr.c \ samd_soc.c \ tusb_port.c \ - lib/libc/string0.c \ lib/libm/ef_sqrt.c \ lib/libm/fmodf.c \ lib/libm/math.c \ lib/libm/nearbyintf.c \ - lib/mp-readline/readline.c \ lib/tinyusb/src/class/cdc/cdc_device.c \ lib/tinyusb/src/common/tusb_fifo.c \ lib/tinyusb/src/device/usbd.c \ lib/tinyusb/src/device/usbd_control.c \ lib/tinyusb/src/portable/microchip/samd/dcd_samd.c \ lib/tinyusb/src/tusb.c \ - lib/utils/gchelper_native.c \ - lib/utils/printf.c \ - lib/utils/pyexec.c \ - lib/utils/stdout_helpers.c \ + shared/libc/printf.c \ + shared/libc/string0.c \ + shared/readline/readline.c \ + shared/runtime/gchelper_native.c \ + shared/runtime/pyexec.c \ + shared/runtime/stdout_helpers.c \ ifeq ($(MCU_SERIES),SAMD21) -SRC_S = lib/utils/gchelper_m0.s +SRC_S = shared/runtime/gchelper_m0.s else -SRC_S = lib/utils/gchelper_m3.s +SRC_S = shared/runtime/gchelper_m3.s endif # List of sources for qstr extraction diff --git a/ports/samd/main.c b/ports/samd/main.c index 038373ceb4..1c72f04ab8 100644 --- a/ports/samd/main.c +++ b/ports/samd/main.c @@ -29,8 +29,8 @@ #include "py/gc.h" #include "py/mperrno.h" #include "py/stackctrl.h" -#include "lib/utils/gchelper.h" -#include "lib/utils/pyexec.h" +#include "shared/runtime/gchelper.h" +#include "shared/runtime/pyexec.h" extern uint8_t _sstack, _estack, _sheap, _eheap; diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index 9c63707071..ebf1466118 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -157,19 +157,19 @@ endif # Options for mpy-cross MPY_CROSS_FLAGS += -march=armv7m -LIB_SRC_C += $(addprefix lib/,\ +SHARED_SRC_C += $(addprefix shared/,\ libc/string0.c \ - mp-readline/readline.c \ + netutils/dhcpserver.c \ netutils/netutils.c \ netutils/trace.c \ - netutils/dhcpserver.c \ + readline/readline.c \ + runtime/gchelper_native.c \ + runtime/interrupt_char.c \ + runtime/mpirq.c \ + runtime/pyexec.c \ + runtime/stdout_helpers.c \ + runtime/sys_stdio_mphal.c \ timeutils/timeutils.c \ - utils/gchelper_native.c \ - utils/pyexec.c \ - utils/interrupt_char.c \ - utils/stdout_helpers.c \ - utils/sys_stdio_mphal.c \ - utils/mpirq.c \ ) ifeq ($(MICROPY_FLOAT_IMPL),double) @@ -359,18 +359,18 @@ SRC_O += \ ifeq ($(MCU_SERIES),f0) SRC_O += \ resethandler_m0.o \ - lib/utils/gchelper_m0.o + shared/runtime/gchelper_m0.o else ifeq ($(MCU_SERIES),l0) CSUPEROPT = -Os # save some code space SRC_O += \ resethandler_m0.o \ - lib/utils/gchelper_m0.o + shared/runtime/gchelper_m0.o else SRC_O += \ system_stm32.o \ resethandler.o \ - lib/utils/gchelper_m3.o + shared/runtime/gchelper_m3.o endif endif @@ -542,6 +542,7 @@ endif OBJ += $(PY_O) OBJ += $(addprefix $(BUILD)/, $(LIB_SRC_C:.c=.o)) OBJ += $(LIBM_O) +OBJ += $(addprefix $(BUILD)/, $(SHARED_SRC_C:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(EXTMOD_SRC_C:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(DRIVERS_SRC_C:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(HAL_SRC_C:.c=.o)) @@ -555,7 +556,7 @@ OBJ += $(BUILD)/pins_$(BOARD).o # This file contains performance critical functions so turn up the optimisation # level. It doesn't add much to the code size and improves performance a bit. # Don't use -O3 with this file because gcc tries to optimise memset in terms of itself. -$(BUILD)/lib/libc/string0.o: COPT += -O2 +$(BUILD)/shared/libc/string0.o: COPT += -O2 # We put several files into the first 16K section with the ISRs. # If we compile these using -O0 then it won't fit. So if you really want these @@ -728,7 +729,7 @@ GEN_CDCINF_FILE = $(HEADER_BUILD)/pybcdc.inf GEN_CDCINF_HEADER = $(HEADER_BUILD)/pybcdc_inf.h # List of sources for qstr extraction -SRC_QSTR += $(SRC_C) $(SRC_CXX) $(SRC_MOD) $(LIB_SRC_C) $(EXTMOD_SRC_C) +SRC_QSTR += $(SRC_C) $(SRC_CXX) $(SRC_MOD) $(SHARED_SRC_C) $(EXTMOD_SRC_C) # Append any auto-generated sources that are needed by sources listed in # SRC_QSTR SRC_QSTR_AUTO_DEPS += $(GEN_CDCINF_HEADER) diff --git a/ports/stm32/boardctrl.c b/ports/stm32/boardctrl.c index fa71d8e7f4..922f218e92 100644 --- a/ports/stm32/boardctrl.c +++ b/ports/stm32/boardctrl.c @@ -26,7 +26,7 @@ #include "py/runtime.h" #include "py/mphal.h" -#include "lib/utils/pyexec.h" +#include "shared/runtime/pyexec.h" #include "boardctrl.h" #include "led.h" #include "usrsw.h" diff --git a/ports/stm32/eth.c b/ports/stm32/eth.c index 79165e1191..f174b6af0d 100644 --- a/ports/stm32/eth.c +++ b/ports/stm32/eth.c @@ -27,7 +27,7 @@ #include #include "py/mphal.h" #include "py/mperrno.h" -#include "lib/netutils/netutils.h" +#include "shared/netutils/netutils.h" #include "pin_static_af.h" #include "modnetwork.h" #include "mpu.h" diff --git a/ports/stm32/gccollect.c b/ports/stm32/gccollect.c index 8b47b121ec..bd697a2af3 100644 --- a/ports/stm32/gccollect.c +++ b/ports/stm32/gccollect.c @@ -30,7 +30,7 @@ #include "py/mpstate.h" #include "py/gc.h" #include "py/mpthread.h" -#include "lib/utils/gchelper.h" +#include "shared/runtime/gchelper.h" #include "gccollect.h" #include "softtimer.h" #include "systick.h" diff --git a/ports/stm32/machine_uart.c b/ports/stm32/machine_uart.c index 1b8db2e3e6..354a1e26ac 100644 --- a/ports/stm32/machine_uart.c +++ b/ports/stm32/machine_uart.c @@ -32,8 +32,8 @@ #include "py/stream.h" #include "py/mperrno.h" #include "py/mphal.h" -#include "lib/utils/interrupt_char.h" -#include "lib/utils/mpirq.h" +#include "shared/runtime/interrupt_char.h" +#include "shared/runtime/mpirq.h" #include "uart.h" #include "irq.h" #include "pendsv.h" diff --git a/ports/stm32/main.c b/ports/stm32/main.c index 86b8fb7972..d7afb9e4af 100644 --- a/ports/stm32/main.c +++ b/ports/stm32/main.c @@ -32,8 +32,8 @@ #include "py/gc.h" #include "py/mperrno.h" #include "py/mphal.h" -#include "lib/mp-readline/readline.h" -#include "lib/utils/pyexec.h" +#include "shared/readline/readline.h" +#include "shared/runtime/pyexec.h" #include "lib/oofatfs/ff.h" #include "lib/littlefs/lfs1.h" #include "lib/littlefs/lfs1_util.h" diff --git a/ports/stm32/mboot/Makefile b/ports/stm32/mboot/Makefile index 41a1a73bc5..553fc4ac66 100755 --- a/ports/stm32/mboot/Makefile +++ b/ports/stm32/mboot/Makefile @@ -105,9 +105,9 @@ else COPT += -Os -DNDEBUG endif -$(BUILD)/lib/libc/string0.o: CFLAGS += $(CFLAGS_BUILTIN) +$(BUILD)/shared/libc/string0.o: CFLAGS += $(CFLAGS_BUILTIN) LIB_SRC_C += \ - lib/libc/string0.c \ + shared/libc/string0.c \ lib/littlefs/lfs1.c \ lib/littlefs/lfs1_util.c \ lib/littlefs/lfs2.c \ diff --git a/ports/stm32/modmachine.c b/ports/stm32/modmachine.c index 66c696982b..2b7e22d13a 100644 --- a/ports/stm32/modmachine.c +++ b/ports/stm32/modmachine.c @@ -38,7 +38,7 @@ #include "extmod/machine_pulse.h" #include "extmod/machine_i2c.h" #include "extmod/machine_spi.h" -#include "lib/utils/pyexec.h" +#include "shared/runtime/pyexec.h" #include "lib/oofatfs/ff.h" #include "extmod/vfs.h" #include "extmod/vfs_fat.h" diff --git a/ports/stm32/modnetwork.c b/ports/stm32/modnetwork.c index 4279df0884..06c4eb05db 100644 --- a/ports/stm32/modnetwork.c +++ b/ports/stm32/modnetwork.c @@ -31,7 +31,7 @@ #include "py/objlist.h" #include "py/runtime.h" #include "py/mphal.h" -#include "lib/netutils/netutils.h" +#include "shared/netutils/netutils.h" #include "systick.h" #include "pendsv.h" #include "modnetwork.h" diff --git a/ports/stm32/modnwcc3k.c b/ports/stm32/modnwcc3k.c index b52958ba22..07178b65c7 100644 --- a/ports/stm32/modnwcc3k.c +++ b/ports/stm32/modnwcc3k.c @@ -36,7 +36,7 @@ #include "py/runtime.h" #include "py/mperrno.h" #include "py/mphal.h" -#include "lib/netutils/netutils.h" +#include "shared/netutils/netutils.h" #include "modnetwork.h" #include "pin.h" #include "spi.h" diff --git a/ports/stm32/modnwwiznet5k.c b/ports/stm32/modnwwiznet5k.c index 204b9fcc3f..1e18d03ca1 100644 --- a/ports/stm32/modnwwiznet5k.c +++ b/ports/stm32/modnwwiznet5k.c @@ -33,7 +33,7 @@ #include "py/stream.h" #include "py/mperrno.h" #include "py/mphal.h" -#include "lib/netutils/netutils.h" +#include "shared/netutils/netutils.h" #include "modnetwork.h" #include "pin.h" #include "spi.h" diff --git a/ports/stm32/modpyb.c b/ports/stm32/modpyb.c index c920906991..b9e2bac03f 100644 --- a/ports/stm32/modpyb.c +++ b/ports/stm32/modpyb.c @@ -29,7 +29,7 @@ #include "py/runtime.h" #include "py/mphal.h" -#include "lib/utils/pyexec.h" +#include "shared/runtime/pyexec.h" #include "drivers/dht/dht.h" #include "stm32_it.h" #include "irq.h" diff --git a/ports/stm32/moduos.c b/ports/stm32/moduos.c index 6f9740c645..5b2335272f 100644 --- a/ports/stm32/moduos.c +++ b/ports/stm32/moduos.c @@ -30,7 +30,7 @@ #include "py/runtime.h" #include "py/objtuple.h" #include "py/objstr.h" -#include "lib/timeutils/timeutils.h" +#include "shared/timeutils/timeutils.h" #include "lib/oofatfs/ff.h" #include "lib/oofatfs/diskio.h" #include "extmod/misc.h" diff --git a/ports/stm32/modusocket.c b/ports/stm32/modusocket.c index 2732d472b5..c59fc85226 100644 --- a/ports/stm32/modusocket.c +++ b/ports/stm32/modusocket.c @@ -32,7 +32,7 @@ #include "py/runtime.h" #include "py/stream.h" #include "py/mperrno.h" -#include "lib/netutils/netutils.h" +#include "shared/netutils/netutils.h" #include "modnetwork.h" #if MICROPY_PY_USOCKET && !MICROPY_PY_LWIP diff --git a/ports/stm32/modutime.c b/ports/stm32/modutime.c index 4bce45eb33..1a22c34b6b 100644 --- a/ports/stm32/modutime.c +++ b/ports/stm32/modutime.c @@ -30,7 +30,7 @@ #include "py/runtime.h" #include "py/smallint.h" #include "py/obj.h" -#include "lib/timeutils/timeutils.h" +#include "shared/timeutils/timeutils.h" #include "extmod/utime_mphal.h" #include "systick.h" #include "portmodules.h" diff --git a/ports/stm32/mpbthciport.c b/ports/stm32/mpbthciport.c index accb913e1f..369c91e303 100644 --- a/ports/stm32/mpbthciport.c +++ b/ports/stm32/mpbthciport.c @@ -31,7 +31,7 @@ #include "mpbthciport.h" #include "softtimer.h" #include "pendsv.h" -#include "lib/utils/mpirq.h" +#include "shared/runtime/mpirq.h" #if MICROPY_PY_BLUETOOTH diff --git a/ports/stm32/network_wiznet5k.c b/ports/stm32/network_wiznet5k.c index c85ef5e88e..bd4c02cb05 100644 --- a/ports/stm32/network_wiznet5k.c +++ b/ports/stm32/network_wiznet5k.c @@ -34,7 +34,7 @@ #if MICROPY_PY_WIZNET5K && MICROPY_PY_LWIP -#include "lib/netutils/netutils.h" +#include "shared/netutils/netutils.h" #include "drivers/wiznet5k/ethernet/socket.h" #include "lwip/err.h" #include "lwip/dns.h" diff --git a/ports/stm32/pendsv.c b/ports/stm32/pendsv.c index c5b2fcf92a..d4c4496f1b 100644 --- a/ports/stm32/pendsv.c +++ b/ports/stm32/pendsv.c @@ -27,7 +27,7 @@ #include #include "py/runtime.h" -#include "lib/utils/interrupt_char.h" +#include "shared/runtime/interrupt_char.h" #include "pendsv.h" #include "irq.h" diff --git a/ports/stm32/rtc.c b/ports/stm32/rtc.c index 02b0f2dbd1..ab8b49e188 100644 --- a/ports/stm32/rtc.c +++ b/ports/stm32/rtc.c @@ -27,7 +27,7 @@ #include #include "py/runtime.h" -#include "lib/timeutils/timeutils.h" +#include "shared/timeutils/timeutils.h" #include "extint.h" #include "rtc.h" #include "irq.h" diff --git a/ports/stm32/uart.c b/ports/stm32/uart.c index 9f74416170..b15be85336 100644 --- a/ports/stm32/uart.c +++ b/ports/stm32/uart.c @@ -32,8 +32,8 @@ #include "py/stream.h" #include "py/mperrno.h" #include "py/mphal.h" -#include "lib/utils/interrupt_char.h" -#include "lib/utils/mpirq.h" +#include "shared/runtime/interrupt_char.h" +#include "shared/runtime/mpirq.h" #include "uart.h" #include "irq.h" #include "pendsv.h" diff --git a/ports/stm32/uart.h b/ports/stm32/uart.h index 0490a617f6..286b6cdec0 100644 --- a/ports/stm32/uart.h +++ b/ports/stm32/uart.h @@ -26,7 +26,7 @@ #ifndef MICROPY_INCLUDED_STM32_UART_H #define MICROPY_INCLUDED_STM32_UART_H -#include "lib/utils/mpirq.h" +#include "shared/runtime/mpirq.h" typedef enum { PYB_UART_NONE = 0, diff --git a/ports/stm32/usb.c b/ports/stm32/usb.c index 5e3802651b..838613f283 100644 --- a/ports/stm32/usb.c +++ b/ports/stm32/usb.c @@ -39,7 +39,7 @@ #include "py/stream.h" #include "py/mperrno.h" #include "py/mphal.h" -#include "lib/utils/mpirq.h" +#include "shared/runtime/mpirq.h" #include "bufhelper.h" #include "storage.h" #include "sdcard.h" diff --git a/ports/stm32/usbd_cdc_interface.c b/ports/stm32/usbd_cdc_interface.c index a1ac5fd22f..5f27bbe29a 100644 --- a/ports/stm32/usbd_cdc_interface.c +++ b/ports/stm32/usbd_cdc_interface.c @@ -42,7 +42,7 @@ #include "pendsv.h" #include "py/obj.h" -#include "lib/utils/interrupt_char.h" +#include "shared/runtime/interrupt_char.h" #include "irq.h" #if MICROPY_HW_ENABLE_USB diff --git a/ports/teensy/Makefile b/ports/teensy/Makefile index cf9529442a..d1ff421621 100644 --- a/ports/teensy/Makefile +++ b/ports/teensy/Makefile @@ -100,12 +100,12 @@ STM_SRC_C = $(addprefix ports/stm32/,\ pin_named_pins.c \ ) -LIB_SRC_C = $(addprefix lib/,\ +SHARED_SRC_C = $(addprefix shared/,\ libc/string0.c \ - mp-readline/readline.c \ - utils/gchelper_native.c \ - utils/pyexec.c \ - utils/sys_stdio_mphal.c \ + readline/readline.c \ + runtime/gchelper_native.c \ + runtime/pyexec.c \ + runtime/sys_stdio_mphal.c \ ) SRC_TEENSY = $(addprefix core/,\ @@ -120,8 +120,8 @@ SRC_TEENSY = $(addprefix core/,\ ) OBJ = $(PY_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o) $(STM_SRC_C:.c=.o) $(SRC_TEENSY:.c=.o)) -OBJ += $(addprefix $(BUILD)/, $(LIB_SRC_C:.c=.o)) -OBJ += $(BUILD)/lib/utils/gchelper_m3.o +OBJ += $(addprefix $(BUILD)/, $(SHARED_SRC_C:.c=.o)) +OBJ += $(BUILD)/shared/runtime/gchelper_m3.o OBJ += $(BUILD)/pins_gen.o all: hex @@ -129,13 +129,13 @@ hex: $(BUILD)/micropython.hex ifeq ($(USE_MEMZIP),1) SRC_C += \ - lib/memzip/import.c \ - lib/memzip/lexermemzip.c \ - lib/memzip/memzip.c \ + shared/memzip/import.c \ + shared/memzip/lexermemzip.c \ + shared/memzip/memzip.c \ OBJ += $(BUILD)/memzip-files.o -MAKE_MEMZIP = $(TOP)/lib/memzip/make-memzip.py +MAKE_MEMZIP = $(TOP)/shared/memzip/make-memzip.py ifeq ($(MEMZIP_DIR),) MEMZIP_DIR = memzip_files endif @@ -207,7 +207,7 @@ GEN_PINS_AF_CONST = $(HEADER_BUILD)/pins_af_const.h GEN_PINS_AF_PY = $(BUILD)/pins_af.py # List of sources for qstr extraction -SRC_QSTR += $(SRC_C) $(STM_SRC_C) $(LIB_SRC_C) +SRC_QSTR += $(SRC_C) $(STM_SRC_C) $(SHARED_SRC_C) # Append any auto-generated sources that are needed by sources listed in # SRC_QSTR SRC_QSTR_AUTO_DEPS += diff --git a/ports/teensy/main.c b/ports/teensy/main.c index d4c5f0396f..6ebdcde21c 100644 --- a/ports/teensy/main.c +++ b/ports/teensy/main.c @@ -9,9 +9,9 @@ #include "py/gc.h" #include "py/mphal.h" #include "gccollect.h" -#include "lib/utils/gchelper.h" -#include "lib/utils/pyexec.h" -#include "lib/mp-readline/readline.h" +#include "shared/readline/readline.h" +#include "shared/runtime/gchelper.h" +#include "shared/runtime/pyexec.h" #include "lexermemzip.h" #include "Arduino.h" diff --git a/ports/teensy/modpyb.c b/ports/teensy/modpyb.c index 6671b3abdc..f5bb3ea7b8 100644 --- a/ports/teensy/modpyb.c +++ b/ports/teensy/modpyb.c @@ -33,7 +33,7 @@ #include "py/gc.h" #include "py/mphal.h" -#include "lib/utils/pyexec.h" +#include "shared/runtime/pyexec.h" #include "gccollect.h" #include "systick.h" diff --git a/ports/unix/Makefile b/ports/unix/Makefile index d97e4bc7e0..faa978f3f8 100644 --- a/ports/unix/Makefile +++ b/ports/unix/Makefile @@ -117,9 +117,9 @@ endif endif ifeq ($(MICROPY_USE_READLINE),1) -INC += -I$(TOP)/lib/mp-readline +INC += -I$(TOP)/shared/readline CFLAGS_MOD += -DMICROPY_USE_READLINE=1 -LIB_SRC_C_EXTRA += mp-readline/readline.c +SHARED_SRC_C_EXTRA += readline/readline.c endif ifeq ($(MICROPY_PY_TERMIOS),1) CFLAGS_MOD += -DMICROPY_PY_TERMIOS=1 @@ -241,10 +241,10 @@ SRC_C += \ $(SRC_MOD) \ $(wildcard $(VARIANT_DIR)/*.c) -LIB_SRC_C += $(addprefix lib/,\ - $(LIB_SRC_C_EXTRA) \ +SHARED_SRC_C += $(addprefix shared/,\ + runtime/gchelper_generic.c \ timeutils/timeutils.c \ - utils/gchelper_generic.c \ + $(SHARED_SRC_C_EXTRA) \ ) SRC_CXX += \ @@ -253,11 +253,11 @@ SRC_CXX += \ OBJ = $(PY_O) OBJ += $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_CXX:.cpp=.o)) -OBJ += $(addprefix $(BUILD)/, $(LIB_SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(SHARED_SRC_C:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(EXTMOD_SRC_C:.c=.o)) # List of sources for qstr extraction -SRC_QSTR += $(SRC_C) $(SRC_CXX) $(LIB_SRC_C) $(EXTMOD_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 += diff --git a/ports/unix/gccollect.c b/ports/unix/gccollect.c index f0441e4ea3..79b17663c3 100644 --- a/ports/unix/gccollect.c +++ b/ports/unix/gccollect.c @@ -29,7 +29,7 @@ #include "py/mpstate.h" #include "py/gc.h" -#include "lib/utils/gchelper.h" +#include "shared/runtime/gchelper.h" #if MICROPY_ENABLE_GC diff --git a/ports/unix/input.c b/ports/unix/input.c index 4a77d1b278..c5bf719738 100644 --- a/ports/unix/input.c +++ b/ports/unix/input.c @@ -35,7 +35,7 @@ #include "input.h" #if MICROPY_USE_READLINE == 1 -#include "lib/mp-readline/readline.h" +#include "shared/readline/readline.h" #endif #if MICROPY_USE_READLINE == 0 diff --git a/ports/unix/main.c b/ports/unix/main.c index a5db0869df..031bdd75d1 100644 --- a/ports/unix/main.c +++ b/ports/unix/main.c @@ -159,7 +159,7 @@ STATIC int execute_from_lexer(int source_kind, const void *source, mp_parse_inpu } #if MICROPY_USE_READLINE == 1 -#include "lib/mp-readline/readline.h" +#include "shared/readline/readline.h" #else STATIC char *strjoin(const char *s1, int sep_char, const char *s2) { int l1 = strlen(s1); diff --git a/ports/unix/mphalport.h b/ports/unix/mphalport.h index 89d23ca5d2..510f9d939d 100644 --- a/ports/unix/mphalport.h +++ b/ports/unix/mphalport.h @@ -52,7 +52,7 @@ static inline int mp_hal_readline(vstr_t *vstr, const char *p) { #elif MICROPY_PY_BUILTINS_INPUT && MICROPY_USE_READLINE == 1 #include "py/misc.h" -#include "lib/mp-readline/readline.h" +#include "shared/readline/readline.h" // For built-in input() we need to wrap the standard readline() to enable raw mode #define mp_hal_readline mp_hal_readline static inline int mp_hal_readline(vstr_t *vstr, const char *p) { diff --git a/ports/unix/mpthreadport.c b/ports/unix/mpthreadport.c index d3243b337f..63aac1da38 100644 --- a/ports/unix/mpthreadport.c +++ b/ports/unix/mpthreadport.c @@ -39,7 +39,7 @@ #include #include -#include "lib/utils/gchelper.h" +#include "shared/runtime/gchelper.h" // Some platforms don't have SIGRTMIN but if we do have it, use it to avoid // potential conflict with other uses of the more commonly used SIGUSR1. diff --git a/ports/windows/Makefile b/ports/windows/Makefile index b48c9378b5..d87affa15c 100644 --- a/ports/windows/Makefile +++ b/ports/windows/Makefile @@ -28,8 +28,8 @@ endif # source files SRC_C = \ - lib/utils/gchelper_generic.c \ - lib/utils/printf.c \ + shared/libc/printf.c \ + shared/runtime/gchelper_generic.c \ ports/unix/main.c \ ports/unix/input.c \ ports/unix/modos.c \ @@ -47,7 +47,7 @@ OBJ = $(PY_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) ifeq ($(MICROPY_USE_READLINE),1) CFLAGS_MOD += -DMICROPY_USE_READLINE=1 -SRC_C += lib/mp-readline/readline.c +SRC_C += shared/readline/readline.c endif LIB += -lws2_32 diff --git a/ports/windows/micropython.vcxproj b/ports/windows/micropython.vcxproj index 73a837a840..1beca9e508 100644 --- a/ports/windows/micropython.vcxproj +++ b/ports/windows/micropython.vcxproj @@ -85,8 +85,8 @@ - - + + @@ -109,4 +109,4 @@ - \ No newline at end of file + diff --git a/ports/zephyr/CMakeLists.txt b/ports/zephyr/CMakeLists.txt index 5c09fc76ee..ca18f2cb91 100644 --- a/ports/zephyr/CMakeLists.txt +++ b/ports/zephyr/CMakeLists.txt @@ -54,14 +54,18 @@ set(MICROPY_SOURCE_PORT ) list(TRANSFORM MICROPY_SOURCE_PORT PREPEND ${MICROPY_PORT_DIR}/) -set(MICROPY_SOURCE_LIB +set(MICROPY_SOURCE_SHARED + libc/printf.c + readline/readline.c + runtime/interrupt_char.c + runtime/mpirq.c + runtime/pyexec.c + runtime/stdout_helpers.c timeutils/timeutils.c - utils/mpirq.c - utils/stdout_helpers.c - utils/printf.c - utils/pyexec.c - utils/interrupt_char.c - mp-readline/readline.c +) +list(TRANSFORM MICROPY_SOURCE_SHARED PREPEND ${MICROPY_DIR}/shared/) + +set(MICROPY_SOURCE_LIB oofatfs/ff.c oofatfs/ffunicode.c littlefs/lfs1.c @@ -74,6 +78,7 @@ list(TRANSFORM MICROPY_SOURCE_LIB PREPEND ${MICROPY_DIR}/lib/) set(MICROPY_SOURCE_QSTR ${MICROPY_SOURCE_PY} ${MICROPY_SOURCE_EXTMOD} + ${MICROPY_SOURCE_SHARED} ${MICROPY_SOURCE_LIB} ${MICROPY_SOURCE_PORT} ) diff --git a/ports/zephyr/machine_pin.c b/ports/zephyr/machine_pin.c index 34c822c2f0..f9da2433c0 100644 --- a/ports/zephyr/machine_pin.c +++ b/ports/zephyr/machine_pin.c @@ -35,7 +35,7 @@ #include "py/runtime.h" #include "py/gc.h" #include "py/mphal.h" -#include "lib/utils/mpirq.h" +#include "shared/runtime/mpirq.h" #include "modmachine.h" #if MICROPY_PY_MACHINE diff --git a/ports/zephyr/main.c b/ports/zephyr/main.c index 38285ad944..63190bd5ed 100644 --- a/ports/zephyr/main.c +++ b/ports/zephyr/main.c @@ -47,8 +47,8 @@ #include "py/gc.h" #include "py/mphal.h" #include "py/stackctrl.h" -#include "lib/utils/pyexec.h" -#include "lib/mp-readline/readline.h" +#include "shared/runtime/pyexec.h" +#include "shared/readline/readline.h" #include "extmod/modbluetooth.h" #if MICROPY_VFS @@ -59,9 +59,9 @@ #include "modzephyr.h" #ifdef TEST -#include "lib/upytesthelper/upytesthelper.h" +#include "shared/upytesthelper/upytesthelper.h" #include "lib/tinytest/tinytest.c" -#include "lib/upytesthelper/upytesthelper.c" +#include "shared/upytesthelper/upytesthelper.c" #include TEST #endif diff --git a/ports/zephyr/mpconfigport_bin_testsuite.h b/ports/zephyr/mpconfigport_bin_testsuite.h index 684b4f41c2..4367067af3 100644 --- a/ports/zephyr/mpconfigport_bin_testsuite.h +++ b/ports/zephyr/mpconfigport_bin_testsuite.h @@ -26,6 +26,6 @@ #include "mpconfigport.h" #ifdef TEST -#include "lib/upytesthelper/upytesthelper.h" +#include "shared/upytesthelper/upytesthelper.h" #define MP_PLAT_PRINT_STRN(str, len) upytest_output(str, len) #endif diff --git a/ports/zephyr/mphalport.h b/ports/zephyr/mphalport.h index 63a18e1388..ba902a76aa 100644 --- a/ports/zephyr/mphalport.h +++ b/ports/zephyr/mphalport.h @@ -1,5 +1,5 @@ #include -#include "lib/utils/interrupt_char.h" +#include "shared/runtime/interrupt_char.h" void mp_hal_init(void); void mp_hal_wait_sem(struct k_sem *sem, uint32_t timeout_ms); diff --git a/py/modbuiltins.c b/py/modbuiltins.c index ac41942b9c..c9a49685a4 100644 --- a/py/modbuiltins.c +++ b/py/modbuiltins.c @@ -230,7 +230,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_hex_obj, mp_builtin_hex); #if MICROPY_PY_BUILTINS_INPUT #include "py/mphal.h" -#include "lib/mp-readline/readline.h" +#include "shared/readline/readline.h" // A port can define mp_hal_readline if they want to use a custom function here #ifndef mp_hal_readline diff --git a/py/mpconfig.h b/py/mpconfig.h index d40637dfaf..48c4c6d347 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -1063,7 +1063,7 @@ typedef double mp_float_t; #endif // Whether to provide the built-in input() function. The implementation of this -// uses mp-readline, so can only be enabled if the port uses this readline. +// uses shared/readline, so can only be enabled if the port uses this readline. #ifndef MICROPY_PY_BUILTINS_INPUT #define MICROPY_PY_BUILTINS_INPUT (0) #endif diff --git a/py/py.mk b/py/py.mk index ab01b0ec21..bc9232ef4f 100644 --- a/py/py.mk +++ b/py/py.mk @@ -209,8 +209,8 @@ PY_EXTMOD_O_BASENAME = \ extmod/vfs_lfs.o \ extmod/utime_mphal.o \ extmod/uos_dupterm.o \ - lib/embed/abort_.o \ - lib/utils/printf.o \ + shared/libc/abort_.o \ + shared/libc/printf.o \ # prepend the build destination prefix to the py object files PY_CORE_O = $(addprefix $(BUILD)/, $(PY_CORE_O_BASENAME)) @@ -272,7 +272,7 @@ $(HEADER_BUILD)/moduledefs.h: $(SRC_QSTR) $(QSTR_GLOBAL_DEPENDENCIES) | $(HEADER # 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 -$(BUILD)/lib/libc/string0.o: CFLAGS += $(CFLAGS_BUILTIN) +$(BUILD)/shared/libc/string0.o: CFLAGS += $(CFLAGS_BUILTIN) # Force nlr code to always be compiled with space-saving optimisation so # that the function preludes are of a minimal and predictable form. diff --git a/shared/memzip/README.md b/shared/memzip/README.md index 287d0fc489..3938e3198c 100644 --- a/shared/memzip/README.md +++ b/shared/memzip/README.md @@ -10,13 +10,13 @@ a C file which contains the data from the zip file. A typical addition to a makefile would look like: ``` SRC_C += \ - lib/memzip/import.c \ - lib/memzip/lexermemzip.c \ - lib/memzip/memzip.c \ + shared/memzip/import.c \ + shared/memzip/lexermemzip.c \ + shared/memzip/memzip.c \ OBJ += $(BUILD)/memzip-files.o -MAKE_MEMZIP = ../lib/memzip/make-memzip.py +MAKE_MEMZIP = ../shared/memzip/make-memzip.py $(BUILD)/memzip-files.o: $(BUILD)/memzip-files.c $(call compile_c) diff --git a/shared/netutils/dhcpserver.c b/shared/netutils/dhcpserver.c index 7f97ee6e46..9db42b3fd9 100644 --- a/shared/netutils/dhcpserver.c +++ b/shared/netutils/dhcpserver.c @@ -35,7 +35,7 @@ #if MICROPY_PY_LWIP -#include "lib/netutils/dhcpserver.h" +#include "shared/netutils/dhcpserver.h" #include "lwip/udp.h" #define DHCPDISCOVER (1) diff --git a/shared/netutils/netutils.c b/shared/netutils/netutils.c index 40fbd5bca3..84b4405c41 100644 --- a/shared/netutils/netutils.c +++ b/shared/netutils/netutils.c @@ -30,7 +30,7 @@ #include #include "py/runtime.h" -#include "lib/netutils/netutils.h" +#include "shared/netutils/netutils.h" // Takes an array with a raw IPv4 address and returns something like '192.168.0.1'. mp_obj_t netutils_format_ipv4_addr(uint8_t *ip, netutils_endian_t endian) { diff --git a/shared/netutils/trace.c b/shared/netutils/trace.c index 1610966c2d..a6dfb42c28 100644 --- a/shared/netutils/trace.c +++ b/shared/netutils/trace.c @@ -25,7 +25,7 @@ */ #include "py/mphal.h" -#include "lib/netutils/netutils.h" +#include "shared/netutils/netutils.h" static uint32_t get_be16(const uint8_t *buf) { return buf[0] << 8 | buf[1]; diff --git a/shared/readline/readline.c b/shared/readline/readline.c index 296c8aa4ab..cad85b4e6b 100644 --- a/shared/readline/readline.c +++ b/shared/readline/readline.c @@ -31,7 +31,7 @@ #include "py/mpstate.h" #include "py/repl.h" #include "py/mphal.h" -#include "lib/mp-readline/readline.h" +#include "shared/readline/readline.h" #if 0 // print debugging info #define DEBUG_PRINT (1) diff --git a/shared/runtime/gchelper_generic.c b/shared/runtime/gchelper_generic.c index 3e7e33ab18..dcd35f9c7e 100644 --- a/shared/runtime/gchelper_generic.c +++ b/shared/runtime/gchelper_generic.c @@ -28,7 +28,7 @@ #include "py/mpstate.h" #include "py/gc.h" -#include "lib/utils/gchelper.h" +#include "shared/runtime/gchelper.h" #if MICROPY_ENABLE_GC diff --git a/shared/runtime/gchelper_native.c b/shared/runtime/gchelper_native.c index 6bf386b519..1e4af9c844 100644 --- a/shared/runtime/gchelper_native.c +++ b/shared/runtime/gchelper_native.c @@ -28,7 +28,7 @@ #include "py/mpstate.h" #include "py/gc.h" -#include "lib/utils/gchelper.h" +#include "shared/runtime/gchelper.h" #if MICROPY_ENABLE_GC diff --git a/shared/runtime/mpirq.c b/shared/runtime/mpirq.c index 02139f24dc..8e474bf5a2 100644 --- a/shared/runtime/mpirq.c +++ b/shared/runtime/mpirq.c @@ -29,7 +29,7 @@ #include "py/runtime.h" #include "py/gc.h" -#include "lib/utils/mpirq.h" +#include "shared/runtime/mpirq.h" #if MICROPY_ENABLE_SCHEDULER diff --git a/shared/runtime/pyexec.c b/shared/runtime/pyexec.c index 4446b36b61..006ec096f7 100644 --- a/shared/runtime/pyexec.c +++ b/shared/runtime/pyexec.c @@ -39,8 +39,8 @@ #include "irq.h" #include "usb.h" #endif -#include "lib/mp-readline/readline.h" -#include "lib/utils/pyexec.h" +#include "shared/readline/readline.h" +#include "shared/runtime/pyexec.h" #include "genhdr/mpversion.h" pyexec_mode_kind_t pyexec_mode_kind = PYEXEC_MODE_FRIENDLY_REPL; diff --git a/shared/timeutils/timeutils.c b/shared/timeutils/timeutils.c index 7c74f5fc37..6bf3eca84a 100644 --- a/shared/timeutils/timeutils.c +++ b/shared/timeutils/timeutils.c @@ -27,7 +27,7 @@ #include "py/obj.h" -#include "lib/timeutils/timeutils.h" +#include "shared/timeutils/timeutils.h" // LEAPOCH corresponds to 2000-03-01, which is a mod-400 year, immediately // after Feb 29. We calculate seconds as a signed integer relative to that. diff --git a/tools/codeformat.py b/tools/codeformat.py index 81a3cdcf8e..f8202c66b9 100755 --- a/tools/codeformat.py +++ b/tools/codeformat.py @@ -39,9 +39,9 @@ PATHS = [ "extmod/btstack/*.[ch]", "extmod/nimble/*.[ch]", "lib/mbedtls_errors/tester.c", - "lib/netutils/*.[ch]", - "lib/timeutils/*.[ch]", - "lib/utils/*.[ch]", + "shared/netutils/*.[ch]", + "shared/timeutils/*.[ch]", + "shared/runtime/*.[ch]", "mpy-cross/*.[ch]", "ports/*/*.[ch]", "ports/windows/msvc/**/*.[ch]", From e3291e1801f27e49ac0e6be275c742777507207f Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 13 Jul 2021 00:17:02 +1000 Subject: [PATCH 078/264] lib,shared: Update README's based on contents of these dirs. Signed-off-by: Damien George --- lib/README.md | 5 +++-- shared/README.md | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 shared/README.md diff --git a/lib/README.md b/lib/README.md index e719821bfc..fd7cba910a 100644 --- a/lib/README.md +++ b/lib/README.md @@ -1,2 +1,3 @@ -This directory contains standard, low-level C libraries with emphasis on -being independent and efficient. They can be used by any port. +This directory contains third-party, low-level C libraries and SDKs. +Libraries that do not target any specific platform are generally chosen +based on them being independent and efficient. diff --git a/shared/README.md b/shared/README.md new file mode 100644 index 0000000000..073187b249 --- /dev/null +++ b/shared/README.md @@ -0,0 +1,3 @@ +This directory contains libraries, utilities and helper code developed +specifically for this project. The code is intended to be portable and +usable by any port. From 831cc4a61da2d5958858d8e1016e951fb248a882 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 13 Jul 2021 15:22:39 +1000 Subject: [PATCH 079/264] stm32/boards/NUCLEO_F446RE: Fix I2C1 pin assignment to match datasheet. Thanks to @aureliano2nd. Signed-off-by: Damien George --- ports/stm32/boards/NUCLEO_F446RE/mpconfigboard.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/stm32/boards/NUCLEO_F446RE/mpconfigboard.h b/ports/stm32/boards/NUCLEO_F446RE/mpconfigboard.h index 27845b33aa..30044ef2b4 100644 --- a/ports/stm32/boards/NUCLEO_F446RE/mpconfigboard.h +++ b/ports/stm32/boards/NUCLEO_F446RE/mpconfigboard.h @@ -25,8 +25,8 @@ #define MICROPY_HW_UART_REPL_BAUD 115200 // I2C buses -#define MICROPY_HW_I2C1_SCL (pin_B6) // Arduino D10, pin 17 on CN10 -#define MICROPY_HW_I2C1_SDA (pin_B7) // pin 21 on CN7 +#define MICROPY_HW_I2C1_SCL (pin_B8) // Arduino D15, pin 3 on CN10 +#define MICROPY_HW_I2C1_SDA (pin_B9) // Arduino D14, pin 5 on CN10 #define MICROPY_HW_I2C2_SCL (pin_B10) // Arduino D6, pin 25 on CN10 #define MICROPY_HW_I2C2_SDA (pin_B3) // Arduino D3, pin 31 on CN10 #define MICROPY_HW_I2C3_SCL (pin_A8) // Arduino D7, pin 23 on CN10 From 240888a0d2caa2e8ac8518c7ce0743ddec7e2a84 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 13 Jul 2021 15:56:41 +1000 Subject: [PATCH 080/264] unix/Makefile: Add back LIB_SRC_C to list of object files. This fixes the dev build (it needs LIB_SRC_C for Bluetooth) which was broken by 136369d72f5b99ec23c9c9f178a590bde968e2ee. Signed-off-by: Damien George --- ports/unix/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/unix/Makefile b/ports/unix/Makefile index faa978f3f8..36b6322d86 100644 --- a/ports/unix/Makefile +++ b/ports/unix/Makefile @@ -255,6 +255,7 @@ OBJ += $(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)) # List of sources for qstr extraction SRC_QSTR += $(SRC_C) $(SRC_CXX) $(SHARED_SRC_C) $(EXTMOD_SRC_C) From 0ee256b8b163863d065e6fe203bc384772694075 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 13 Jul 2021 15:58:04 +1000 Subject: [PATCH 081/264] github/workflows: Add workflow to build and test unix dev variant. Signed-off-by: Damien George --- .github/workflows/ports_unix.yml | 12 ++++++++++++ tools/ci.sh | 8 ++++++++ 2 files changed, 20 insertions(+) diff --git a/.github/workflows/ports_unix.yml b/.github/workflows/ports_unix.yml index 673e6152fa..0e6a98f6b2 100644 --- a/.github/workflows/ports_unix.yml +++ b/.github/workflows/ports_unix.yml @@ -51,6 +51,18 @@ jobs: if: failure() run: tests/run-tests.py --print-failures + dev: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Build + run: source tools/ci.sh && ci_unix_dev_build + - name: Run main test suite + run: source tools/ci.sh && ci_unix_dev_run_tests + - name: Print failures + if: failure() + run: tests/run-tests.py --print-failures + coverage: runs-on: ubuntu-latest steps: diff --git a/tools/ci.sh b/tools/ci.sh index ec122129dd..815e728757 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -393,6 +393,14 @@ 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 +} + +function ci_unix_dev_run_tests { + ci_unix_run_tests_helper VARIANT=dev +} + function ci_unix_coverage_setup { sudo pip3 install setuptools sudo pip3 install pyelftools From 6430cd3e02d4ec4a8fc949754b434f745b1973e6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 13 Jul 2021 16:18:15 +1000 Subject: [PATCH 082/264] unix/variants: Enable help and help("modules") on standard and dev. See related #1354, #2906, #3436. Signed-off-by: Damien George --- ports/unix/variants/dev/mpconfigvariant.h | 2 ++ ports/unix/variants/standard/mpconfigvariant.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/ports/unix/variants/dev/mpconfigvariant.h b/ports/unix/variants/dev/mpconfigvariant.h index 7c3e84cc44..54ad993236 100644 --- a/ports/unix/variants/dev/mpconfigvariant.h +++ b/ports/unix/variants/dev/mpconfigvariant.h @@ -31,6 +31,8 @@ #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_SYS_SETTRACE (1) #define MICROPY_PY_UOS_VFS (1) #define MICROPY_PY_URANDOM_EXTRA_FUNCS (1) diff --git a/ports/unix/variants/standard/mpconfigvariant.h b/ports/unix/variants/standard/mpconfigvariant.h index 79b8fe2a3b..3cdcfa8e9b 100644 --- a/ports/unix/variants/standard/mpconfigvariant.h +++ b/ports/unix/variants/standard/mpconfigvariant.h @@ -24,3 +24,5 @@ * THE SOFTWARE. */ +#define MICROPY_PY_BUILTINS_HELP (1) +#define MICROPY_PY_BUILTINS_HELP_MODULES (1) From 22fdb2130284ddea3ebe1d271d05d670546e788f Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Wed, 14 Jul 2021 00:18:35 +1000 Subject: [PATCH 083/264] windows/appveyor: Update to VS 2017 and use Python 3.8 for build/test. MicroPython implements some 3.5+ features, and this change helps to reduce the need for some .exp files in the test suite. Signed-off-by: Jim Mussared --- ports/windows/.appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/windows/.appveyor.yml b/ports/windows/.appveyor.yml index a4cd1f1e8c..40fdff2934 100644 --- a/ports/windows/.appveyor.yml +++ b/ports/windows/.appveyor.yml @@ -1,10 +1,10 @@ -image: Visual Studio 2013 +image: Visual Studio 2017 clone_depth: 1 skip_tags: true environment: # Python version used - MICROPY_CPYTHON3: c:/python34/python.exe + MICROPY_CPYTHON3: c:/python38/python.exe init: # Set build version number to commit to be travis-like From b8255dd2e00f926106083de7a9b41869b226e96b Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 29 Jun 2021 22:39:24 +1000 Subject: [PATCH 084/264] py/vm: Simplify handling of MP_OBJ_STOP_ITERATION in yield-from opcode. Signed-off-by: Damien George --- py/objgenerator.c | 6 ++++-- py/runtime.c | 6 +++--- py/vm.c | 13 +++---------- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/py/objgenerator.c b/py/objgenerator.c index 543685fac7..1ee7b8b1db 100644 --- a/py/objgenerator.c +++ b/py/objgenerator.c @@ -152,8 +152,9 @@ mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_ mp_check_self(mp_obj_is_type(self_in, &mp_type_gen_instance)); mp_obj_gen_instance_t *self = MP_OBJ_TO_PTR(self_in); if (self->code_state.ip == 0) { - // Trying to resume already stopped generator - *ret_val = MP_OBJ_STOP_ITERATION; + // Trying to resume an already stopped generator. + // This is an optimised "raise StopIteration(None)". + *ret_val = mp_const_none; return MP_VM_RETURN_NORMAL; } @@ -212,6 +213,7 @@ mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_ // subsequent next() may re-execute statements after last yield // again and again, leading to side effects. self->code_state.ip = 0; + // This is an optimised "raise StopIteration(*ret_val)". *ret_val = *self->code_state.sp; break; diff --git a/py/runtime.c b/py/runtime.c index 261670d4f3..b53711bbe2 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -1272,7 +1272,6 @@ mp_obj_t mp_iternext(mp_obj_t o_in) { } } -// TODO: Unclear what to do with StopIterarion exception here. mp_vm_return_kind_t mp_resume(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t throw_value, mp_obj_t *ret_val) { assert((send_value != MP_OBJ_NULL) ^ (throw_value != MP_OBJ_NULL)); const mp_obj_type_t *type = mp_obj_get_type(self_in); @@ -1287,8 +1286,9 @@ mp_vm_return_kind_t mp_resume(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t th if (ret != MP_OBJ_STOP_ITERATION) { return MP_VM_RETURN_YIELD; } else { - // Emulate raise StopIteration() - // Special case, handled in vm.c + // The generator is finished. + // This is an optimised "raise StopIteration(None)". + *ret_val = mp_const_none; return MP_VM_RETURN_NORMAL; } } diff --git a/py/vm.c b/py/vm.c index 5365014fcb..bbfc9914eb 100644 --- a/py/vm.c +++ b/py/vm.c @@ -1257,16 +1257,9 @@ yield: PUSH(ret_value); goto yield; } else if (ret_kind == MP_VM_RETURN_NORMAL) { - // Pop exhausted gen - sp--; - if (ret_value == MP_OBJ_STOP_ITERATION) { - // Optimize StopIteration - // TODO: get StopIteration's value - PUSH(mp_const_none); - } else { - PUSH(ret_value); - } - + // The generator has finished, and returned a value via StopIteration + // Replace exhausted generator with the returned value + SET_TOP(ret_value); // If we injected GeneratorExit downstream, then even // if it was swallowed, we re-raise GeneratorExit GENERATOR_EXIT_IF_NEEDED(t_exc); From e3825e28e61561427fd5811c1167e05ee3372eb4 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 29 Jun 2021 17:32:18 +1000 Subject: [PATCH 085/264] py/objexcept: Make mp_obj_exception_get_value support subclassed excs. Signed-off-by: Damien George --- py/objexcept.c | 27 +++++++++++++-------------- tests/basics/subclass_native3.py | 20 ++++++++++++++++++++ 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/py/objexcept.c b/py/objexcept.c index a10bd2c187..b4f5018c06 100644 --- a/py/objexcept.c +++ b/py/objexcept.c @@ -111,6 +111,15 @@ mp_obj_t mp_alloc_emergency_exception_buf(mp_obj_t size_in) { #endif #endif // MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF +STATIC mp_obj_exception_t *get_native_exception(mp_obj_t self_in) { + assert(mp_obj_is_exception_instance(self_in)); + if (mp_obj_is_native_exception_instance(self_in)) { + return MP_OBJ_TO_PTR(self_in); + } else { + return MP_OBJ_TO_PTR(((mp_obj_instance_t *)MP_OBJ_TO_PTR(self_in))->subobj[0]); + } +} + STATIC void decompress_error_text_maybe(mp_obj_exception_t *o) { #if MICROPY_ROM_TEXT_COMPRESSION if (o->args->len == 1 && mp_obj_is_type(o->args->items[0], &mp_type_str)) { @@ -240,7 +249,7 @@ mp_obj_t mp_obj_exception_make_new(const mp_obj_type_t *type, size_t n_args, siz // Get exception "value" - that is, first argument, or None mp_obj_t mp_obj_exception_get_value(mp_obj_t self_in) { - mp_obj_exception_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_exception_t *self = get_native_exception(self_in); if (self->args->len == 0) { return mp_const_none; } else { @@ -559,25 +568,15 @@ bool mp_obj_exception_match(mp_obj_t exc, mp_const_obj_t exc_type) { // traceback handling functions -#define GET_NATIVE_EXCEPTION(self, self_in) \ - /* make sure self_in is an exception instance */ \ - assert(mp_obj_is_exception_instance(self_in)); \ - mp_obj_exception_t *self; \ - if (mp_obj_is_native_exception_instance(self_in)) { \ - self = MP_OBJ_TO_PTR(self_in); \ - } else { \ - self = MP_OBJ_TO_PTR(((mp_obj_instance_t *)MP_OBJ_TO_PTR(self_in))->subobj[0]); \ - } - void mp_obj_exception_clear_traceback(mp_obj_t self_in) { - GET_NATIVE_EXCEPTION(self, self_in); + mp_obj_exception_t *self = get_native_exception(self_in); // just set the traceback to the null object // we don't want to call any memory management functions here self->traceback_data = NULL; } void mp_obj_exception_add_traceback(mp_obj_t self_in, qstr file, size_t line, qstr block) { - GET_NATIVE_EXCEPTION(self, self_in); + mp_obj_exception_t *self = get_native_exception(self_in); // append this traceback info to traceback data // if memory allocation fails (eg because gc is locked), just return @@ -630,7 +629,7 @@ void mp_obj_exception_add_traceback(mp_obj_t self_in, qstr file, size_t line, qs } void mp_obj_exception_get_traceback(mp_obj_t self_in, size_t *n, size_t **values) { - GET_NATIVE_EXCEPTION(self, self_in); + mp_obj_exception_t *self = get_native_exception(self_in); if (self->traceback_data == NULL) { *n = 0; diff --git a/tests/basics/subclass_native3.py b/tests/basics/subclass_native3.py index ac5aabfed7..0c45c4924f 100644 --- a/tests/basics/subclass_native3.py +++ b/tests/basics/subclass_native3.py @@ -34,6 +34,26 @@ print(MyStopIteration().value) print(MyStopIteration(1).value) +class Iter: + def __iter__(self): + return self + + def __next__(self): + # This exception will stop the "yield from", with a value of 3 + raise MyStopIteration(3) + + +def gen(): + print((yield from Iter())) + return 4 + + +try: + next(gen()) +except StopIteration as er: + print(er.args) + + class MyOSError(OSError): pass From bb00125aaac8376b8cc4c8f3da2423fcf6dae496 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 29 Jun 2021 17:34:34 +1000 Subject: [PATCH 086/264] py: Support single argument to optimised MP_OBJ_STOP_ITERATION. The MP_OBJ_STOP_ITERATION optimisation is a shortcut for creating a StopIteration() exception object, and means that heap memory does not need to be allocated for the exception (in cases where it can be used). This commit allows this optimised object to take an optional argument (before, it could only have no argument). The commit also adds some new tests to cover corner cases with StopIteration and generators that previously did not work. Signed-off-by: Damien George --- py/modbuiltins.c | 4 +- py/mpstate.h | 3 ++ py/objgenerator.c | 30 +++++------- py/objgetitemiter.c | 1 - py/runtime.c | 20 ++++++-- py/runtime.h | 6 +++ tests/basics/gen_yield_from_stopped.py | 12 +++++ tests/basics/stopiteration.py | 63 ++++++++++++++++++++++++++ 8 files changed, 115 insertions(+), 24 deletions(-) create mode 100644 tests/basics/stopiteration.py diff --git a/py/modbuiltins.c b/py/modbuiltins.c index c9a49685a4..a7e49a1ed9 100644 --- a/py/modbuiltins.c +++ b/py/modbuiltins.c @@ -322,7 +322,7 @@ STATIC mp_obj_t mp_builtin_next(size_t n_args, const mp_obj_t *args) { if (n_args == 1) { mp_obj_t ret = mp_iternext_allow_raise(args[0]); if (ret == MP_OBJ_STOP_ITERATION) { - mp_raise_type(&mp_type_StopIteration); + mp_raise_StopIteration(MP_STATE_THREAD(stop_iteration_arg)); } else { return ret; } @@ -336,7 +336,7 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_next_obj, 1, 2, mp_builtin_next); STATIC mp_obj_t mp_builtin_next(mp_obj_t o) { mp_obj_t ret = mp_iternext_allow_raise(o); if (ret == MP_OBJ_STOP_ITERATION) { - mp_raise_type(&mp_type_StopIteration); + mp_raise_StopIteration(MP_STATE_THREAD(stop_iteration_arg)); } else { return ret; } diff --git a/py/mpstate.h b/py/mpstate.h index e42d13fb20..07335bae4c 100644 --- a/py/mpstate.h +++ b/py/mpstate.h @@ -266,6 +266,9 @@ typedef struct _mp_state_thread_t { // pending exception object (MP_OBJ_NULL if not pending) volatile mp_obj_t mp_pending_exception; + // If MP_OBJ_STOP_ITERATION is propagated then this holds its argument. + mp_obj_t stop_iteration_arg; + #if MICROPY_PY_SYS_SETTRACE mp_obj_t prof_trace_callback; bool prof_callback_is_executing; diff --git a/py/objgenerator.c b/py/objgenerator.c index 1ee7b8b1db..784310092e 100644 --- a/py/objgenerator.c +++ b/py/objgenerator.c @@ -238,16 +238,20 @@ mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_ return ret_kind; } -STATIC mp_obj_t gen_resume_and_raise(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t throw_value) { +STATIC mp_obj_t gen_resume_and_raise(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t throw_value, bool raise_stop_iteration) { mp_obj_t ret; switch (mp_obj_gen_resume(self_in, send_value, throw_value, &ret)) { case MP_VM_RETURN_NORMAL: default: - // Optimize return w/o value in case generator is used in for loop - if (ret == mp_const_none || ret == MP_OBJ_STOP_ITERATION) { - return MP_OBJ_STOP_ITERATION; + // A normal return is a StopIteration, either raise it or return + // MP_OBJ_STOP_ITERATION as an optimisation. + if (ret == mp_const_none) { + ret = MP_OBJ_NULL; + } + if (raise_stop_iteration) { + mp_raise_StopIteration(ret); } else { - nlr_raise(mp_obj_new_exception_arg1(&mp_type_StopIteration, ret)); + return mp_make_stop_iteration(ret); } case MP_VM_RETURN_YIELD: @@ -259,16 +263,11 @@ STATIC mp_obj_t gen_resume_and_raise(mp_obj_t self_in, mp_obj_t send_value, mp_o } STATIC mp_obj_t gen_instance_iternext(mp_obj_t self_in) { - return gen_resume_and_raise(self_in, mp_const_none, MP_OBJ_NULL); + return gen_resume_and_raise(self_in, mp_const_none, MP_OBJ_NULL, false); } STATIC mp_obj_t gen_instance_send(mp_obj_t self_in, mp_obj_t send_value) { - mp_obj_t ret = gen_resume_and_raise(self_in, send_value, MP_OBJ_NULL); - if (ret == MP_OBJ_STOP_ITERATION) { - mp_raise_type(&mp_type_StopIteration); - } else { - return ret; - } + return gen_resume_and_raise(self_in, send_value, MP_OBJ_NULL, true); } STATIC MP_DEFINE_CONST_FUN_OBJ_2(gen_instance_send_obj, gen_instance_send); @@ -290,12 +289,7 @@ STATIC mp_obj_t gen_instance_throw(size_t n_args, const mp_obj_t *args) { exc = args[2]; } - mp_obj_t ret = gen_resume_and_raise(args[0], mp_const_none, exc); - if (ret == MP_OBJ_STOP_ITERATION) { - mp_raise_type(&mp_type_StopIteration); - } else { - return ret; - } + return gen_resume_and_raise(args[0], mp_const_none, exc, true); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(gen_instance_throw_obj, 2, 4, gen_instance_throw); diff --git a/py/objgetitemiter.c b/py/objgetitemiter.c index 2670314ab2..31ed4a9228 100644 --- a/py/objgetitemiter.c +++ b/py/objgetitemiter.c @@ -48,7 +48,6 @@ STATIC mp_obj_t it_iternext(mp_obj_t self_in) { // an exception was raised mp_obj_type_t *t = (mp_obj_type_t *)((mp_obj_base_t *)nlr.ret_val)->type; if (t == &mp_type_StopIteration || t == &mp_type_IndexError) { - // return MP_OBJ_STOP_ITERATION instead of raising return MP_OBJ_STOP_ITERATION; } else { // re-raise exception diff --git a/py/runtime.c b/py/runtime.c index b53711bbe2..2c849fe950 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -1217,6 +1217,7 @@ mp_obj_t mp_getiter(mp_obj_t o_in, mp_obj_iter_buf_t *iter_buf) { mp_obj_t mp_iternext_allow_raise(mp_obj_t o_in) { const mp_obj_type_t *type = mp_obj_get_type(o_in); if (type->iternext != NULL) { + MP_STATE_THREAD(stop_iteration_arg) = MP_OBJ_NULL; return type->iternext(o_in); } else { // check for __next__ method @@ -1242,6 +1243,7 @@ mp_obj_t mp_iternext(mp_obj_t o_in) { MP_STACK_CHECK(); // enumerate, filter, map and zip can recursively call mp_iternext const mp_obj_type_t *type = mp_obj_get_type(o_in); if (type->iternext != NULL) { + MP_STATE_THREAD(stop_iteration_arg) = MP_OBJ_NULL; return type->iternext(o_in); } else { // check for __next__ method @@ -1256,7 +1258,7 @@ mp_obj_t mp_iternext(mp_obj_t o_in) { return ret; } else { if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(((mp_obj_base_t *)nlr.ret_val)->type), MP_OBJ_FROM_PTR(&mp_type_StopIteration))) { - return MP_OBJ_STOP_ITERATION; + return mp_make_stop_iteration(mp_obj_exception_get_value(MP_OBJ_FROM_PTR(nlr.ret_val))); } else { nlr_jump(nlr.ret_val); } @@ -1281,14 +1283,18 @@ mp_vm_return_kind_t mp_resume(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t th } if (type->iternext != NULL && send_value == mp_const_none) { + MP_STATE_THREAD(stop_iteration_arg) = MP_OBJ_NULL; mp_obj_t ret = type->iternext(self_in); *ret_val = ret; if (ret != MP_OBJ_STOP_ITERATION) { return MP_VM_RETURN_YIELD; } else { // The generator is finished. - // This is an optimised "raise StopIteration(None)". - *ret_val = mp_const_none; + // This is an optimised "raise StopIteration(*ret_val)". + *ret_val = MP_STATE_THREAD(stop_iteration_arg); + if (*ret_val == MP_OBJ_NULL) { + *ret_val = mp_const_none; + } return MP_VM_RETURN_NORMAL; } } @@ -1559,6 +1565,14 @@ NORETURN void mp_raise_NotImplementedError(mp_rom_error_text_t msg) { #endif +NORETURN void mp_raise_StopIteration(mp_obj_t arg) { + if (arg == MP_OBJ_NULL) { + mp_raise_type(&mp_type_StopIteration); + } else { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_StopIteration, arg)); + } +} + NORETURN void mp_raise_OSError(int errno_) { nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(errno_))); } diff --git a/py/runtime.h b/py/runtime.h index 7d2cb94e84..8484479a54 100644 --- a/py/runtime.h +++ b/py/runtime.h @@ -154,6 +154,11 @@ mp_obj_t mp_iternext_allow_raise(mp_obj_t o); // may return MP_OBJ_STOP_ITERATIO mp_obj_t mp_iternext(mp_obj_t o); // will always return MP_OBJ_STOP_ITERATION instead of raising StopIteration(...) mp_vm_return_kind_t mp_resume(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t throw_value, mp_obj_t *ret_val); +static inline mp_obj_t mp_make_stop_iteration(mp_obj_t o) { + MP_STATE_THREAD(stop_iteration_arg) = o; + return MP_OBJ_STOP_ITERATION; +} + mp_obj_t mp_make_raise_obj(mp_obj_t o); mp_obj_t mp_import_name(qstr name, mp_obj_t fromlist, mp_obj_t level); @@ -179,6 +184,7 @@ NORETURN void mp_raise_TypeError(mp_rom_error_text_t msg); NORETURN void mp_raise_NotImplementedError(mp_rom_error_text_t msg); #endif +NORETURN void mp_raise_StopIteration(mp_obj_t arg); NORETURN void mp_raise_OSError(int errno_); NORETURN void mp_raise_recursion_depth(void); diff --git a/tests/basics/gen_yield_from_stopped.py b/tests/basics/gen_yield_from_stopped.py index 468679b615..82feefed08 100644 --- a/tests/basics/gen_yield_from_stopped.py +++ b/tests/basics/gen_yield_from_stopped.py @@ -16,3 +16,15 @@ try: next(run()) except StopIteration: print("StopIteration") + + +# Where "f" is a native generator +def run(): + print((yield from f)) + + +f = zip() +try: + next(run()) +except StopIteration: + print("StopIteration") diff --git a/tests/basics/stopiteration.py b/tests/basics/stopiteration.py new file mode 100644 index 0000000000..d4719c9bc3 --- /dev/null +++ b/tests/basics/stopiteration.py @@ -0,0 +1,63 @@ +# test StopIteration interaction with generators + +try: + enumerate, exec +except: + print("SKIP") + raise SystemExit + + +def get_stop_iter_arg(msg, code): + try: + exec(code) + print("FAIL") + except StopIteration as er: + print(msg, er.args) + + +class A: + def __iter__(self): + return self + + def __next__(self): + raise StopIteration(42) + + +class B: + def __getitem__(self, index): + # argument to StopIteration should get ignored + raise StopIteration(42) + + +def gen(x): + return x + yield + + +def gen2(x): + try: + yield + except ValueError: + pass + return x + + +get_stop_iter_arg("next", "next(A())") +get_stop_iter_arg("iter", "next(iter(B()))") +get_stop_iter_arg("enumerate", "next(enumerate(A()))") +get_stop_iter_arg("map", "next(map(lambda x:x, A()))") +get_stop_iter_arg("zip", "next(zip(A()))") +g = gen(None) +get_stop_iter_arg("generator0", "next(g)") +get_stop_iter_arg("generator1", "next(g)") +g = gen(42) +get_stop_iter_arg("generator0", "next(g)") +get_stop_iter_arg("generator1", "next(g)") +get_stop_iter_arg("send", "gen(None).send(None)") +get_stop_iter_arg("send", "gen(42).send(None)") +g = gen2(None) +next(g) +get_stop_iter_arg("throw", "g.throw(ValueError)") +g = gen2(42) +next(g) +get_stop_iter_arg("throw", "g.throw(ValueError)") From 38a204ed9605a9233a66c86538562fab821ce63a Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 14 Jul 2021 22:56:52 +1000 Subject: [PATCH 087/264] py: Introduce and use mp_raise_type_arg helper. To reduce code size. Signed-off-by: Damien George --- extmod/modure.c | 6 +++--- extmod/moduzlib.c | 2 +- ports/unix/modjni.c | 4 ++-- py/dynruntime.h | 1 + py/objdict.c | 6 +++--- py/objset.c | 2 +- py/objstr.c | 2 +- py/pystack.c | 3 +-- py/runtime.c | 11 +++++++---- py/runtime.h | 1 + 10 files changed, 21 insertions(+), 17 deletions(-) diff --git a/extmod/modure.c b/extmod/modure.c index d0829e8c5d..738a2b9843 100644 --- a/extmod/modure.c +++ b/extmod/modure.c @@ -68,7 +68,7 @@ STATIC mp_obj_t match_group(mp_obj_t self_in, mp_obj_t no_in) { mp_obj_match_t *self = MP_OBJ_TO_PTR(self_in); mp_int_t no = mp_obj_get_int(no_in); if (no < 0 || no >= self->num_matches) { - nlr_raise(mp_obj_new_exception_arg1(&mp_type_IndexError, no_in)); + mp_raise_type_arg(&mp_type_IndexError, no_in); } const char *start = self->caps[no * 2]; @@ -107,7 +107,7 @@ STATIC void match_span_helper(size_t n_args, const mp_obj_t *args, mp_obj_t span if (n_args == 2) { no = mp_obj_get_int(args[1]); if (no < 0 || no >= self->num_matches) { - nlr_raise(mp_obj_new_exception_arg1(&mp_type_IndexError, args[1])); + mp_raise_type_arg(&mp_type_IndexError, args[1]); } } @@ -334,7 +334,7 @@ STATIC mp_obj_t re_sub_helper(size_t n_args, const mp_obj_t *args) { } if (match_no >= (unsigned int)match->num_matches) { - nlr_raise(mp_obj_new_exception_arg1(&mp_type_IndexError, MP_OBJ_NEW_SMALL_INT(match_no))); + mp_raise_type_arg(&mp_type_IndexError, MP_OBJ_NEW_SMALL_INT(match_no)); } const char *start_match = match->caps[match_no * 2]; diff --git a/extmod/moduzlib.c b/extmod/moduzlib.c index 999cb48131..f78c349085 100644 --- a/extmod/moduzlib.c +++ b/extmod/moduzlib.c @@ -201,7 +201,7 @@ STATIC mp_obj_t mod_uzlib_decompress(size_t n_args, const mp_obj_t *args) { return res; error: - nlr_raise(mp_obj_new_exception_arg1(&mp_type_ValueError, MP_OBJ_NEW_SMALL_INT(st))); + mp_raise_type_arg(&mp_type_ValueError, MP_OBJ_NEW_SMALL_INT(st)); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_uzlib_decompress_obj, 1, 3, mod_uzlib_decompress); diff --git a/ports/unix/modjni.c b/ports/unix/modjni.c index 74e7221de3..bd151050ea 100644 --- a/ports/unix/modjni.c +++ b/ports/unix/modjni.c @@ -102,9 +102,9 @@ STATIC void check_exception(void) { mp_obj_t py_e = new_jobject(exc); JJ1(ExceptionClear); if (JJ(IsInstanceOf, exc, IndexException_class)) { - nlr_raise(mp_obj_new_exception_arg1(&mp_type_IndexError, py_e)); + mp_raise_type_arg(&mp_type_IndexError, py_e); } - nlr_raise(mp_obj_new_exception_arg1(&mp_type_Exception, py_e)); + mp_raise_type_arg(&mp_type_Exception, py_e); } } diff --git a/py/dynruntime.h b/py/dynruntime.h index a8256c1948..fdb91ed37e 100644 --- a/py/dynruntime.h +++ b/py/dynruntime.h @@ -215,6 +215,7 @@ static inline mp_obj_t mp_obj_len_dyn(mp_obj_t o) { #define mp_obj_new_exception_arg1(e_type, arg) (mp_obj_new_exception_arg1_dyn((e_type), (arg))) #define nlr_raise(o) (mp_raise_dyn(o)) +#define mp_raise_type_arg(type, arg) (mp_raise_dyn(mp_obj_new_exception_arg1_dyn((type), (arg)))) #define mp_raise_msg(type, msg) (mp_fun_table.raise_msg((type), (msg))) #define mp_raise_OSError(er) (mp_raise_OSError_dyn(er)) #define mp_raise_NotImplementedError(msg) (mp_raise_msg(&mp_type_NotImplementedError, (msg))) diff --git a/py/objdict.c b/py/objdict.c index aea5952d3d..63e5381c66 100644 --- a/py/objdict.c +++ b/py/objdict.c @@ -190,7 +190,7 @@ mp_obj_t mp_obj_dict_get(mp_obj_t self_in, mp_obj_t index) { mp_obj_dict_t *self = MP_OBJ_TO_PTR(self_in); mp_map_elem_t *elem = mp_map_lookup(&self->map, index, MP_MAP_LOOKUP); if (elem == NULL) { - nlr_raise(mp_obj_new_exception_arg1(&mp_type_KeyError, index)); + mp_raise_type_arg(&mp_type_KeyError, index); } else { return elem->value; } @@ -206,7 +206,7 @@ STATIC mp_obj_t dict_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { mp_obj_dict_t *self = MP_OBJ_TO_PTR(self_in); mp_map_elem_t *elem = mp_map_lookup(&self->map, index, MP_MAP_LOOKUP); if (elem == NULL) { - nlr_raise(mp_obj_new_exception_arg1(&mp_type_KeyError, index)); + mp_raise_type_arg(&mp_type_KeyError, index); } else { return elem->value; } @@ -295,7 +295,7 @@ STATIC mp_obj_t dict_get_helper(size_t n_args, const mp_obj_t *args, mp_map_look if (elem == NULL || elem->value == MP_OBJ_NULL) { if (n_args == 2) { if (lookup_kind == MP_MAP_LOOKUP_REMOVE_IF_FOUND) { - nlr_raise(mp_obj_new_exception_arg1(&mp_type_KeyError, args[1])); + mp_raise_type_arg(&mp_type_KeyError, args[1]); } else { value = mp_const_none; } diff --git a/py/objset.c b/py/objset.c index dac9b11382..d2508bfbf9 100644 --- a/py/objset.c +++ b/py/objset.c @@ -372,7 +372,7 @@ STATIC mp_obj_t set_remove(mp_obj_t self_in, mp_obj_t item) { check_set(self_in); mp_obj_set_t *self = MP_OBJ_TO_PTR(self_in); if (mp_set_lookup(&self->set, item, MP_MAP_LOOKUP_REMOVE_IF_FOUND) == MP_OBJ_NULL) { - nlr_raise(mp_obj_new_exception_arg1(&mp_type_KeyError, item)); + mp_raise_type_arg(&mp_type_KeyError, item); } return mp_const_none; } diff --git a/py/objstr.c b/py/objstr.c index 98657bd217..7d7f0e1dfa 100644 --- a/py/objstr.c +++ b/py/objstr.c @@ -1081,7 +1081,7 @@ STATIC vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *ar field_name = lookup; mp_map_elem_t *key_elem = mp_map_lookup(kwargs, field_q, MP_MAP_LOOKUP); if (key_elem == NULL) { - nlr_raise(mp_obj_new_exception_arg1(&mp_type_KeyError, field_q)); + mp_raise_type_arg(&mp_type_KeyError, field_q); } arg = key_elem->value; } diff --git a/py/pystack.c b/py/pystack.c index f7323fd740..ea5f0d9d11 100644 --- a/py/pystack.c +++ b/py/pystack.c @@ -43,8 +43,7 @@ void *mp_pystack_alloc(size_t n_bytes) { #endif if (MP_STATE_THREAD(pystack_cur) + n_bytes > MP_STATE_THREAD(pystack_end)) { // out of memory in the pystack - nlr_raise(mp_obj_new_exception_arg1(&mp_type_RuntimeError, - MP_OBJ_NEW_QSTR(MP_QSTR_pystack_space_exhausted))); + mp_raise_type_arg(&mp_type_RuntimeError, MP_OBJ_NEW_QSTR(MP_QSTR_pystack_space_exhausted)); } void *ptr = MP_STATE_THREAD(pystack_cur); MP_STATE_THREAD(pystack_cur) += n_bytes; diff --git a/py/runtime.c b/py/runtime.c index 2c849fe950..19686c310b 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -1565,21 +1565,24 @@ NORETURN void mp_raise_NotImplementedError(mp_rom_error_text_t msg) { #endif +NORETURN void mp_raise_type_arg(const mp_obj_type_t *exc_type, mp_obj_t arg) { + nlr_raise(mp_obj_new_exception_arg1(exc_type, arg)); +} + NORETURN void mp_raise_StopIteration(mp_obj_t arg) { if (arg == MP_OBJ_NULL) { mp_raise_type(&mp_type_StopIteration); } else { - nlr_raise(mp_obj_new_exception_arg1(&mp_type_StopIteration, arg)); + mp_raise_type_arg(&mp_type_StopIteration, arg); } } NORETURN void mp_raise_OSError(int errno_) { - nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(errno_))); + mp_raise_type_arg(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(errno_)); } #if MICROPY_STACK_CHECK || MICROPY_ENABLE_PYSTACK NORETURN void mp_raise_recursion_depth(void) { - nlr_raise(mp_obj_new_exception_arg1(&mp_type_RuntimeError, - MP_OBJ_NEW_QSTR(MP_QSTR_maximum_space_recursion_space_depth_space_exceeded))); + mp_raise_type_arg(&mp_type_RuntimeError, MP_OBJ_NEW_QSTR(MP_QSTR_maximum_space_recursion_space_depth_space_exceeded)); } #endif diff --git a/py/runtime.h b/py/runtime.h index 8484479a54..f0d41f38df 100644 --- a/py/runtime.h +++ b/py/runtime.h @@ -184,6 +184,7 @@ NORETURN void mp_raise_TypeError(mp_rom_error_text_t msg); NORETURN void mp_raise_NotImplementedError(mp_rom_error_text_t msg); #endif +NORETURN void mp_raise_type_arg(const mp_obj_type_t *exc_type, mp_obj_t arg); NORETURN void mp_raise_StopIteration(mp_obj_t arg); NORETURN void mp_raise_OSError(int errno_); NORETURN void mp_raise_recursion_depth(void); From 74085f167e09694f1d32d3fb3d212066091f9f7d Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 14 Jul 2021 23:25:18 +1000 Subject: [PATCH 088/264] py/modsys: Optimise sys.exit for code size by using exception helpers. Signed-off-by: Damien George --- py/modsys.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/py/modsys.c b/py/modsys.c index 586b13e747..64349f3c30 100644 --- a/py/modsys.c +++ b/py/modsys.c @@ -110,13 +110,11 @@ STATIC const MP_DEFINE_STR_OBJ(mp_sys_platform_obj, MICROPY_PY_SYS_PLATFORM); // exit([retval]): raise SystemExit, with optional argument given to the exception STATIC mp_obj_t mp_sys_exit(size_t n_args, const mp_obj_t *args) { - mp_obj_t exc; if (n_args == 0) { - exc = mp_obj_new_exception(&mp_type_SystemExit); + mp_raise_type(&mp_type_SystemExit); } else { - exc = mp_obj_new_exception_arg1(&mp_type_SystemExit, args[0]); + mp_raise_type_arg(&mp_type_SystemExit, args[0]); } - nlr_raise(exc); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_sys_exit_obj, 0, 1, mp_sys_exit); From 022b8a7fea5e74b6b83895b2ff555ceab9af478c Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 14 Jul 2021 23:28:25 +1000 Subject: [PATCH 089/264] py/objexcept: Make mp_obj_new_exception_arg1 inline. This function is rarely used so making it inline reduces code size. Signed-off-by: Damien George --- py/obj.h | 5 ++++- py/objexcept.c | 6 ------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/py/obj.h b/py/obj.h index 86e5d83125..578c55eefb 100644 --- a/py/obj.h +++ b/py/obj.h @@ -739,7 +739,6 @@ mp_obj_t mp_obj_new_int_from_float(mp_float_t val); mp_obj_t mp_obj_new_complex(mp_float_t real, mp_float_t imag); #endif mp_obj_t mp_obj_new_exception(const mp_obj_type_t *exc_type); -mp_obj_t mp_obj_new_exception_arg1(const mp_obj_type_t *exc_type, mp_obj_t arg); mp_obj_t mp_obj_new_exception_args(const mp_obj_type_t *exc_type, size_t n_args, const mp_obj_t *args); #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_NONE #define mp_obj_new_exception_msg(exc_type, msg) mp_obj_new_exception(exc_type) @@ -825,6 +824,10 @@ mp_obj_t mp_obj_exception_get_value(mp_obj_t self_in); mp_obj_t mp_obj_exception_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args); mp_obj_t mp_alloc_emergency_exception_buf(mp_obj_t size_in); void mp_init_emergency_exception_buf(void); +static inline mp_obj_t mp_obj_new_exception_arg1(const mp_obj_type_t *exc_type, mp_obj_t arg) { + assert(exc_type->make_new == mp_obj_exception_make_new); + return mp_obj_exception_make_new(exc_type, 1, 0, &arg); +} // str bool mp_obj_str_equal(mp_obj_t s1, mp_obj_t s2); diff --git a/py/objexcept.c b/py/objexcept.c index b4f5018c06..7a86c36471 100644 --- a/py/objexcept.c +++ b/py/objexcept.c @@ -378,12 +378,6 @@ mp_obj_t mp_obj_new_exception(const mp_obj_type_t *exc_type) { return mp_obj_exception_make_new(exc_type, 0, 0, NULL); } -// "Optimized" version for common(?) case of having 1 exception arg -mp_obj_t mp_obj_new_exception_arg1(const mp_obj_type_t *exc_type, mp_obj_t arg) { - assert(exc_type->make_new == mp_obj_exception_make_new); - return mp_obj_exception_make_new(exc_type, 1, 0, &arg); -} - mp_obj_t mp_obj_new_exception_args(const mp_obj_type_t *exc_type, size_t n_args, const mp_obj_t *args) { assert(exc_type->make_new == mp_obj_exception_make_new); return mp_obj_exception_make_new(exc_type, n_args, 0, args); From 70b8e1d1f5df9b8f80992daa5d60460d45998a8e Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 14 Jul 2021 23:38:49 +1000 Subject: [PATCH 090/264] py/obj: Fix formatting of comment for mp_obj_is_integer. Signed-off-by: Damien George --- py/obj.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/py/obj.h b/py/obj.h index 578c55eefb..11918ba176 100644 --- a/py/obj.h +++ b/py/obj.h @@ -779,9 +779,11 @@ bool mp_obj_is_callable(mp_obj_t o_in); mp_obj_t mp_obj_equal_not_equal(mp_binary_op_t op, mp_obj_t o1, mp_obj_t o2); bool mp_obj_equal(mp_obj_t o1, mp_obj_t o2); +// returns true if o is bool, small int or long int static inline bool mp_obj_is_integer(mp_const_obj_t o) { return mp_obj_is_int(o) || mp_obj_is_bool(o); -} // returns true if o is bool, small int or long int +} + mp_int_t mp_obj_get_int(mp_const_obj_t arg); mp_int_t mp_obj_get_int_truncated(mp_const_obj_t arg); bool mp_obj_get_int_maybe(mp_const_obj_t arg, mp_int_t *value); From 06277a916940eee1064eebb491c3e13d67fe6a18 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 14 Jul 2021 23:40:24 +1000 Subject: [PATCH 091/264] mimxrt/machine_led: Use mp_raise_msg_varg helper. Signed-off-by: Damien George --- ports/mimxrt/machine_led.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/mimxrt/machine_led.c b/ports/mimxrt/machine_led.c index 07c7b180a1..4082eb34bb 100644 --- a/ports/mimxrt/machine_led.c +++ b/ports/mimxrt/machine_led.c @@ -44,7 +44,7 @@ STATIC mp_obj_t led_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_ // Check led id is in range if (!(1 <= led_id && led_id <= NUM_LEDS)) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "LED(%d) doesn't exist", led_id)); + mp_raise_msg_varg(&mp_type_ValueError, "LED(%d) doesn't exist", led_id); } // Return reference to static object From 98c5703027e7db9a030f747f4dd1684407230656 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sat, 19 Jun 2021 22:09:40 +0200 Subject: [PATCH 092/264] mimxrt/machine_i2c: Add hardware-based machine.I2C to machine module. It uses non-blocking transfer of data. Advantage over SoftI2C: - Higher data rate up to ~3 MHZ. - Full protocol support. --- ports/mimxrt/Makefile | 2 + .../boards/MIMXRT1010_EVK/mpconfigboard.h | 12 + .../boards/MIMXRT1020_EVK/mpconfigboard.h | 17 +- .../boards/MIMXRT1050_EVK/mpconfigboard.h | 12 + .../boards/MIMXRT1060_EVK/mpconfigboard.h | 12 + .../boards/MIMXRT1064_EVK/mpconfigboard.h | 12 + ports/mimxrt/boards/TEENSY40/mpconfigboard.h | 14 ++ ports/mimxrt/boards/TEENSY41/mpconfigboard.h | 14 ++ ports/mimxrt/machine_i2c.c | 217 ++++++++++++++++++ ports/mimxrt/modmachine.c | 1 + ports/mimxrt/modmachine.h | 1 + 11 files changed, 313 insertions(+), 1 deletion(-) create mode 100644 ports/mimxrt/machine_i2c.c diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index 64d9864cbb..580dc32c7e 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -123,6 +123,7 @@ SRC_HAL_IMX_C += \ $(MCU_DIR)/drivers/fsl_gpio.c \ $(MCU_DIR)/drivers/fsl_gpt.c \ $(MCU_DIR)/drivers/fsl_common.c \ + $(MCU_DIR)/drivers/fsl_lpi2c.c \ $(MCU_DIR)/drivers/fsl_lpspi.c \ $(MCU_DIR)/drivers/fsl_lpspi_edma.c \ $(MCU_DIR)/drivers/fsl_lpuart.c \ @@ -142,6 +143,7 @@ SRC_C = \ dma_channel.c \ $(BOARD_DIR)/flash_config.c \ machine_adc.c \ + machine_i2c.c \ machine_led.c \ machine_pin.c \ machine_rtc.c \ diff --git a/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.h index 3576706264..756eaf85eb 100644 --- a/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.h @@ -33,3 +33,15 @@ #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 LPI2C1 -> 0 +// D0/D1 LPI2C2 -> 1 +// D6/D7 LPI2C2 -> 1 Alternatively possible GPIO_AD_01, GPIO_AD_02 + +#define MICROPY_HW_I2C_INDEX { 1, 2 } + +#define IOMUX_TABLE_I2C \ + { IOMUXC_GPIO_02_LPI2C1_SCL }, { IOMUXC_GPIO_01_LPI2C1_SDA }, \ + { IOMUXC_GPIO_10_LPI2C2_SCL }, { IOMUXC_GPIO_09_LPI2C2_SDA }, diff --git a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h index 55254d58f6..b0574fad0f 100644 --- a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h @@ -13,7 +13,8 @@ #define MICROPY_HW_NUM_PIN_IRQS (3 * 32) // Define mapping logical UART # to hardware UART # -// D3/D5 LPUART1 Not usable, Since D3 is blocked. +// RX/TX HW-UART Logical UART +// D3/D5 LPUART1 Not usable, Since D3 is blocked. // D0/D1 LPUART2 -> 1 // D6/D9 LPUART3 -> 2 // D10/D12 LPUART5 -> 3 @@ -48,3 +49,17 @@ #define DMA_REQ_SRC_TX { 0, kDmaRequestMuxLPSPI1Tx, kDmaRequestMuxLPSPI2Tx, \ kDmaRequestMuxLPSPI3Tx, kDmaRequestMuxLPSPI4Tx } + +// 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 { 4, 1, 2 } + +#define IOMUX_TABLE_I2C \ + { IOMUXC_GPIO_AD_B1_14_LPI2C1_SCL }, { IOMUXC_GPIO_AD_B1_15_LPI2C1_SDA }, \ + { IOMUXC_GPIO_AD_B1_08_LPI2C2_SCL }, { IOMUXC_GPIO_AD_B1_09_LPI2C2_SDA }, \ + { 0 }, { 0 }, \ + { IOMUXC_GPIO_SD_B1_02_LPI2C4_SCL }, { IOMUXC_GPIO_SD_B1_03_LPI2C4_SDA }, diff --git a/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h index db08b1064d..5d920d5286 100644 --- a/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h @@ -41,3 +41,15 @@ #define DMA_REQ_SRC_TX { 0, kDmaRequestMuxLPSPI1Tx, kDmaRequestMuxLPSPI2Tx, \ kDmaRequestMuxLPSPI3Tx, kDmaRequestMuxLPSPI4Tx } + +// Define the mapping hardware I2C # to logical I2C # +// SDA/SCL HW-I2C Logical I2C +// D14/D15 LPI2C1 -> 0 +// D1/D0 LPI2C3 -> 1 + +#define MICROPY_HW_I2C_INDEX { 1, 3 } + +#define IOMUX_TABLE_I2C \ + { IOMUXC_GPIO_AD_B1_00_LPI2C1_SCL }, { IOMUXC_GPIO_AD_B1_01_LPI2C1_SDA }, \ + { 0 }, { 0 }, \ + { IOMUXC_GPIO_AD_B1_07_LPI2C3_SCL }, { IOMUXC_GPIO_AD_B1_06_LPI2C3_SDA }, diff --git a/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h index 451d53dafd..f849cba966 100644 --- a/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h @@ -41,3 +41,15 @@ #define DMA_REQ_SRC_TX { 0, kDmaRequestMuxLPSPI1Tx, kDmaRequestMuxLPSPI2Tx, \ kDmaRequestMuxLPSPI3Tx, kDmaRequestMuxLPSPI4Tx } + +// Define the mapping hardware I2C # to logical I2C # +// SDA/SCL HW-I2C Logical I2C +// D14/D15 LPI2C1 -> 0 +// D1/D0 LPI2C3 -> 1 + +#define MICROPY_HW_I2C_INDEX { 1, 3 } + +#define IOMUX_TABLE_I2C \ + { IOMUXC_GPIO_AD_B1_00_LPI2C1_SCL }, { IOMUXC_GPIO_AD_B1_01_LPI2C1_SDA }, \ + { 0 }, { 0 }, \ + { IOMUXC_GPIO_AD_B1_07_LPI2C3_SCL }, { IOMUXC_GPIO_AD_B1_06_LPI2C3_SDA }, diff --git a/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h index e300efef32..2301f34131 100644 --- a/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h @@ -39,3 +39,15 @@ #define DMA_REQ_SRC_TX { 0, kDmaRequestMuxLPSPI1Tx, kDmaRequestMuxLPSPI2Tx, \ kDmaRequestMuxLPSPI3Tx, kDmaRequestMuxLPSPI4Tx } + +// Define the mapping hardware I2C # to logical I2C # +// SDA/SCL HW-I2C Logical I2C +// D14/D15 LPI2C1 -> 0 +// D1/D0 LPI2C3 -> 1 + +#define MICROPY_HW_I2C_INDEX { 1, 3 } + +#define IOMUX_TABLE_I2C \ + { IOMUXC_GPIO_AD_B1_00_LPI2C1_SCL }, { IOMUXC_GPIO_AD_B1_01_LPI2C1_SDA }, \ + { 0 }, { 0 }, \ + { IOMUXC_GPIO_AD_B1_07_LPI2C3_SCL }, { IOMUXC_GPIO_AD_B1_06_LPI2C3_SDA }, diff --git a/ports/mimxrt/boards/TEENSY40/mpconfigboard.h b/ports/mimxrt/boards/TEENSY40/mpconfigboard.h index 8b38d047f8..b6c81a4229 100644 --- a/ports/mimxrt/boards/TEENSY40/mpconfigboard.h +++ b/ports/mimxrt/boards/TEENSY40/mpconfigboard.h @@ -42,3 +42,17 @@ #define DMA_REQ_SRC_TX { 0, kDmaRequestMuxLPSPI1Tx, kDmaRequestMuxLPSPI2Tx, \ kDmaRequestMuxLPSPI3Tx, kDmaRequestMuxLPSPI4Tx } + +// Define mapping hardware I2C # to logical I2C # +// SDA/SCL HW-I2C Logical I2C +// 17/16 LPI2C3 -> 0 +// 18/19 LPI2C1 -> 1 +// 25/24 LPI2C4 -> 2 + +#define MICROPY_HW_I2C_INDEX { 1, 3, 4 } + +#define IOMUX_TABLE_I2C \ + { IOMUXC_GPIO_AD_B1_00_LPI2C1_SCL }, { IOMUXC_GPIO_AD_B1_01_LPI2C1_SDA }, \ + { 0 }, { 0 }, \ + { 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 }, diff --git a/ports/mimxrt/boards/TEENSY41/mpconfigboard.h b/ports/mimxrt/boards/TEENSY41/mpconfigboard.h index 455bf8d69c..17d2751682 100644 --- a/ports/mimxrt/boards/TEENSY41/mpconfigboard.h +++ b/ports/mimxrt/boards/TEENSY41/mpconfigboard.h @@ -42,3 +42,17 @@ #define DMA_REQ_SRC_TX { 0, kDmaRequestMuxLPSPI1Tx, kDmaRequestMuxLPSPI2Tx, \ kDmaRequestMuxLPSPI3Tx, kDmaRequestMuxLPSPI4Tx } + +// Define mapping hardware I2C # to logical I2C # +// SDA/SCL HW-I2C Logical I2C +// 17/16 LPI2C3 -> 0 +// 18/19 LPI2C1 -> 1 +// 25/24 LPI2C4 -> 2 + +#define MICROPY_HW_I2C_INDEX { 1, 3, 4 } + +#define IOMUX_TABLE_I2C \ + { IOMUXC_GPIO_AD_B1_00_LPI2C1_SCL }, { IOMUXC_GPIO_AD_B1_01_LPI2C1_SDA }, \ + { 0 }, { 0 }, \ + { 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 }, diff --git a/ports/mimxrt/machine_i2c.c b/ports/mimxrt/machine_i2c.c new file mode 100644 index 0000000000..618d0a2581 --- /dev/null +++ b/ports/mimxrt/machine_i2c.c @@ -0,0 +1,217 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * 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 "py/runtime.h" +#include "py/mphal.h" +#include "py/mperrno.h" +#include "extmod/machine_i2c.h" +#include "modmachine.h" + +#include "fsl_iomuxc.h" +#include "fsl_lpi2c.h" + +#define DEFAULT_I2C_FREQ (400000) +#define DEFAULT_I2C_DRIVE (6) + +// Select USB1 PLL (480 MHz) as master lpi2c clock source +#define LPI2C_CLOCK_SOURCE_SELECT (0U) +// Clock divider for master lpi2c clock source +#define LPI2C_CLOCK_SOURCE_DIVIDER (1U) +// Get frequency of lpi2c clock = 30 MHz +#define LPI2C_CLOCK_FREQUENCY ((CLOCK_GetFreq(kCLOCK_Usb1PllClk) / 8) / (LPI2C_CLOCK_SOURCE_DIVIDER + 1U)) + +typedef struct _machine_i2c_obj_t { + mp_obj_base_t base; + LPI2C_Type *i2c_inst; + uint8_t i2c_id; + uint8_t i2c_hw_id; + bool transfer_busy; + status_t transfer_status; + lpi2c_master_config_t *master_config; +} machine_i2c_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; + +STATIC const uint8_t i2c_index_table[] = MICROPY_HW_I2C_INDEX; +STATIC LPI2C_Type *i2c_base_ptr_table[] = LPI2C_BASE_PTRS; +static const iomux_table_t iomux_table[] = { IOMUX_TABLE_I2C }; + +#define MICROPY_HW_I2C_NUM ARRAY_SIZE(i2c_index_table) + +#define SCL (iomux_table[index]) +#define SDA (iomux_table[index + 1]) + +bool lpi2c_set_iomux(int8_t hw_i2c, uint8_t drive) { + int index = (hw_i2c - 1) * 2; + + 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_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); + return true; + } else { + return false; + } +} + +STATIC void machine_i2c_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "I2C(%u, freq=%u)", + self->i2c_id, self->master_config->baudRate_Hz); +} + +mp_obj_t machine_i2c_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_freq, ARG_drive}; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_freq, MP_ARG_INT, {.u_int = DEFAULT_I2C_FREQ} }, + { MP_QSTR_drive, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_I2C_DRIVE} }, + }; + + static bool clk_init = true; + + // 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 I2C bus. + int i2c_id = mp_obj_get_int(args[ARG_id].u_obj); + if (i2c_id < 0 || i2c_id >= MICROPY_HW_I2C_NUM) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("I2C(%d) doesn't exist"), i2c_id); + } + + // Get I2C Object. + machine_i2c_obj_t *self = m_new_obj(machine_i2c_obj_t); + self->base.type = &machine_i2c_type; + self->i2c_id = i2c_id; + self->i2c_hw_id = i2c_index_table[i2c_id]; // the hw i2c number 1..n + self->i2c_inst = i2c_base_ptr_table[self->i2c_hw_id]; + + uint8_t drive = args[ARG_drive].u_int; + if (drive < 1 || drive > 7) { + drive = DEFAULT_I2C_DRIVE; + } + + if (clk_init) { + clk_init = false; + // Set clock source for LPI2C + CLOCK_SetMux(kCLOCK_Lpi2cMux, LPI2C_CLOCK_SOURCE_SELECT); // USB1 PLL (480 MHz) + CLOCK_SetDiv(kCLOCK_Lpi2cDiv, LPI2C_CLOCK_SOURCE_DIVIDER); + } + + // Initialise the I2C peripheral if any arguments given, or it was not initialised previously. + lpi2c_set_iomux(self->i2c_hw_id, drive); + self->master_config = m_new_obj(lpi2c_master_config_t); + LPI2C_MasterGetDefaultConfig(self->master_config); + // Initialise the I2C peripheral. + self->master_config->baudRate_Hz = args[ARG_freq].u_int; + LPI2C_MasterInit(self->i2c_inst, self->master_config, LPI2C_CLOCK_FREQUENCY); + + return MP_OBJ_FROM_PTR(self); +} + +static void lpi2c_master_callback(LPI2C_Type *base, lpi2c_master_handle_t *handle, status_t status, void *self_in) { + machine_i2c_obj_t *self = (machine_i2c_obj_t *)self_in; + + self->transfer_busy = false; + self->transfer_status = status; +} + +STATIC int machine_i2c_transfer_single(mp_obj_base_t *self_in, uint16_t addr, size_t len, uint8_t *buf, unsigned int flags) { + machine_i2c_obj_t *self = (machine_i2c_obj_t *)self_in; + status_t ret; + lpi2c_master_handle_t g_master_handle; + lpi2c_master_transfer_t masterXfer = {0}; + LPI2C_MasterTransferCreateHandle(self->i2c_inst, &g_master_handle, lpi2c_master_callback, self); + + if (flags & MP_MACHINE_I2C_FLAG_READ) { + masterXfer.direction = kLPI2C_Read; + } else { + masterXfer.direction = kLPI2C_Write; + } + if (addr < 0x80) { // 7 or 10 bit address? + masterXfer.slaveAddress = addr; + } else { + masterXfer.slaveAddress = 0x78 | ((addr >> 8) & 0x03); + masterXfer.subaddress = addr & 0xff; + masterXfer.subaddressSize = 1; + } + masterXfer.data = buf; + masterXfer.dataSize = len; + if (flags & MP_MACHINE_I2C_FLAG_STOP) { + masterXfer.flags = kLPI2C_TransferDefaultFlag; + } else { + masterXfer.flags = kLPI2C_TransferNoStopFlag; + } + self->transfer_busy = true; + + // Send master data to slave in non-blocking mode + ret = LPI2C_MasterTransferNonBlocking(self->i2c_inst, &g_master_handle, &masterXfer); + if (ret != kStatus_Success) { + return -MP_EIO; + } + // Wait for the transfer to complete + while (self->transfer_busy) { + MICROPY_EVENT_POLL_HOOK + } + + // Transfer will not send a stop in case of errors like NAK. So it's done here. + if (flags & MP_MACHINE_I2C_FLAG_STOP && self->transfer_status != kStatus_Success) { + LPI2C_MasterStop(self->i2c_inst); + } + + if (self->transfer_status == kStatus_Success) { + return len; + } else if (self->transfer_status == kStatus_LPI2C_Nak) { + return -MP_ENODEV; + } else { + return -MP_EIO; + } +} + +STATIC const mp_machine_i2c_p_t machine_i2c_p = { + .transfer = mp_machine_i2c_transfer_adaptor, + .transfer_single = machine_i2c_transfer_single, +}; + +const mp_obj_type_t machine_i2c_type = { + { &mp_type_type }, + .name = MP_QSTR_I2C, + .print = machine_i2c_print, + .make_new = machine_i2c_make_new, + .protocol = &machine_i2c_p, + .locals_dict = (mp_obj_dict_t *)&mp_machine_i2c_locals_dict, +}; diff --git a/ports/mimxrt/modmachine.c b/ports/mimxrt/modmachine.c index e821f15070..150615ca03 100644 --- a/ports/mimxrt/modmachine.c +++ b/ports/mimxrt/modmachine.c @@ -85,6 +85,7 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_Signal), MP_ROM_PTR(&machine_signal_type) }, { 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) }, { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&machine_spi_type) }, { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&machine_uart_type) }, diff --git a/ports/mimxrt/modmachine.h b/ports/mimxrt/modmachine.h index eba8e6db8f..9732093bd8 100644 --- a/ports/mimxrt/modmachine.h +++ b/ports/mimxrt/modmachine.h @@ -32,6 +32,7 @@ extern const mp_obj_type_t machine_adc_type; extern const mp_obj_type_t machine_timer_type; extern const mp_obj_type_t machine_rtc_type; +extern const mp_obj_type_t machine_i2c_type; extern const mp_obj_type_t machine_spi_type; extern const mp_obj_type_t machine_uart_type; From eb3029c6697007eac7a76a8bd34452cedbd2c526 Mon Sep 17 00:00:00 2001 From: Jonathan Hogg Date: Mon, 12 Jul 2021 11:35:25 +0100 Subject: [PATCH 093/264] esp32/machine_spi: Calculate actual attained baudrate. Calculate the actual baudrate that the hardware is capable of achieving and remember it so that printing the SPI object will show this. Fixes #7530. --- ports/esp32/machine_hw_spi.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ports/esp32/machine_hw_spi.c b/ports/esp32/machine_hw_spi.c index 98c0abaefd..2792216cc0 100644 --- a/ports/esp32/machine_hw_spi.c +++ b/ports/esp32/machine_hw_spi.c @@ -144,9 +144,13 @@ STATIC void machine_hw_spi_init_internal( changed = true; } - if (baudrate != -1 && baudrate != self->baudrate) { - self->baudrate = baudrate; - changed = true; + if (baudrate != -1) { + // calculate the actual clock frequency that the SPI peripheral can produce + baudrate = spi_get_actual_clock(APB_CLK_FREQ, baudrate, 0); + if (baudrate != self->baudrate) { + self->baudrate = baudrate; + changed = true; + } } if (polarity != -1 && polarity != self->polarity) { From 8be29b9b1b0fc2709d6ace3a9ff32092052359f6 Mon Sep 17 00:00:00 2001 From: Jonathan Hogg Date: Fri, 16 Jul 2021 16:12:45 +0100 Subject: [PATCH 094/264] esp32/machine_hw_spi: Use a 2 item SPI queue for long transfers. Using a 2-item transaction queue instead of 1 allows long transfers to be executed with the minimum inter-transaction delay. Limit maximum transaction length to ensure an integer multiple of the SPI `bits` setting are transferred. Fixes #7511. --- ports/esp32/machine_hw_spi.c | 52 ++++++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/ports/esp32/machine_hw_spi.c b/ports/esp32/machine_hw_spi.c index 2792216cc0..5b59f2431c 100644 --- a/ports/esp32/machine_hw_spi.c +++ b/ports/esp32/machine_hw_spi.c @@ -217,7 +217,7 @@ STATIC void machine_hw_spi_init_internal( .clock_speed_hz = self->baudrate, .mode = self->phase | (self->polarity << 1), .spics_io_num = -1, // No CS pin - .queue_size = 1, + .queue_size = 2, .flags = self->firstbit == MICROPY_PY_MACHINE_SPI_LSB ? SPI_DEVICE_TXBIT_LSBFIRST | SPI_DEVICE_RXBIT_LSBFIRST : 0, .pre_cb = NULL }; @@ -273,6 +273,17 @@ STATIC void machine_hw_spi_deinit(mp_obj_base_t *self_in) { } } +STATIC mp_uint_t gcd(mp_uint_t x, mp_uint_t y) { + while (x != y) { + if (x > y) { + x -= y; + } else { + y -= x; + } + } + return x; +} + STATIC void machine_hw_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest) { machine_hw_spi_obj_t *self = MP_OBJ_TO_PTR(self_in); @@ -281,13 +292,16 @@ STATIC void machine_hw_spi_transfer(mp_obj_base_t *self_in, size_t len, const ui return; } - struct spi_transaction_t transaction = { 0 }; - // Round to nearest whole set of bits int bits_to_send = len * 8 / self->bits * self->bits; + if (!bits_to_send) { + mp_raise_ValueError(MP_ERROR_TEXT("buffer too short")); + } if (len <= 4) { + spi_transaction_t transaction = { 0 }; + if (src != NULL) { memcpy(&transaction.tx_data, src, len); } @@ -302,26 +316,42 @@ STATIC void machine_hw_spi_transfer(mp_obj_base_t *self_in, size_t len, const ui } else { int offset = 0; int bits_remaining = bits_to_send; + int optimum_word_size = 8 * self->bits / gcd(8, self->bits); + int max_transaction_bits = MP_HW_SPI_MAX_XFER_BITS / optimum_word_size * optimum_word_size; + spi_transaction_t *transaction, *result, transactions[2]; + int i = 0; + + spi_device_acquire_bus(self->spi, portMAX_DELAY); while (bits_remaining) { - memset(&transaction, 0, sizeof(transaction)); + transaction = transactions + i++ % 2; + memset(transaction, 0, sizeof(spi_transaction_t)); - transaction.length = - bits_remaining > MP_HW_SPI_MAX_XFER_BITS ? MP_HW_SPI_MAX_XFER_BITS : bits_remaining; + transaction->length = + bits_remaining > max_transaction_bits ? max_transaction_bits : bits_remaining; if (src != NULL) { - transaction.tx_buffer = src + offset; + transaction->tx_buffer = src + offset; } if (dest != NULL) { - transaction.rx_buffer = dest + offset; + transaction->rx_buffer = dest + offset; } - spi_device_transmit(self->spi, &transaction); - bits_remaining -= transaction.length; + spi_device_queue_trans(self->spi, transaction, portMAX_DELAY); + bits_remaining -= transaction->length; + + if (offset > 0) { + // wait for previously queued transaction + spi_device_get_trans_result(self->spi, &result, portMAX_DELAY); + } // doesn't need ceil(); loop ends when bits_remaining is 0 - offset += transaction.length / 8; + offset += transaction->length / 8; } + + // wait for last transaction + spi_device_get_trans_result(self->spi, &result, portMAX_DELAY); + spi_device_release_bus(self->spi); } } From 8758504f0f1c300ec2bc40f4e3b9627c8e8562dd Mon Sep 17 00:00:00 2001 From: David Lechner Date: Tue, 6 Jul 2021 18:08:18 -0500 Subject: [PATCH 095/264] extmod/moduselect: Conditionally compile select(). This adds #if MICROPY_PY_USELECT_SELECT around the uselect.select() function. According to the docs, this function is only for CPython compatibility and should not normally be used. So we can disable it and save a few bytes of flash space where possible. Signed-off-by: David Lechner --- extmod/moduselect.c | 4 ++++ py/mpconfig.h | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/extmod/moduselect.c b/extmod/moduselect.c index fbd51960d5..cf47f02afe 100644 --- a/extmod/moduselect.c +++ b/extmod/moduselect.c @@ -107,6 +107,7 @@ STATIC mp_uint_t poll_map_poll(mp_map_t *poll_map, size_t *rwx_num) { return n_ready; } +#if MICROPY_PY_USELECT_SELECT // select(rlist, wlist, xlist[, timeout]) STATIC mp_obj_t select_select(size_t n_args, const mp_obj_t *args) { // get array data from tuple/list arguments @@ -173,6 +174,7 @@ STATIC mp_obj_t select_select(size_t n_args, const mp_obj_t *args) { } } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_select_select_obj, 3, 4, select_select); +#endif // MICROPY_PY_USELECT_SELECT typedef struct _mp_obj_poll_t { mp_obj_base_t base; @@ -355,7 +357,9 @@ MP_DEFINE_CONST_FUN_OBJ_0(mp_select_poll_obj, select_poll); STATIC const mp_rom_map_elem_t mp_module_select_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uselect) }, + #if MICROPY_PY_USELECT_SELECT { MP_ROM_QSTR(MP_QSTR_select), MP_ROM_PTR(&mp_select_select_obj) }, + #endif { MP_ROM_QSTR(MP_QSTR_poll), MP_ROM_PTR(&mp_select_poll_obj) }, { MP_ROM_QSTR(MP_QSTR_POLLIN), MP_ROM_INT(MP_STREAM_POLL_RD) }, { MP_ROM_QSTR(MP_QSTR_POLLOUT), MP_ROM_INT(MP_STREAM_POLL_WR) }, diff --git a/py/mpconfig.h b/py/mpconfig.h index 48c4c6d347..f67e11cd4b 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -1316,6 +1316,13 @@ typedef double mp_float_t; #define MICROPY_PY_USELECT (0) #endif +// Whether to enable the select() function in the "uselect" module (baremetal +// implementation). This is present for compatibility but can be disabled to +// save space. +#ifndef MICROPY_PY_USELECT_SELECT +#define MICROPY_PY_USELECT_SELECT (1) +#endif + // Whether to provide "utime" module functions implementation // in terms of mp_hal_* functions. #ifndef MICROPY_PY_UTIME_MP_HAL From 12e3fcc78575295a881a10553adc979e9a530a21 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Thu, 8 Jul 2021 16:25:12 +1000 Subject: [PATCH 096/264] extmod/nimble: Fix leak in l2cap_send if send-while-stalled. A correctly-behaved application shouldn't do this, but in the case where the channel is stalled, there's still enough room in the mbuf pool, then we'll fail the send (BLE_HS_EBUSY) so the mbuf needs to be freed. Signed-off-by: Jim Mussared --- extmod/nimble/modbluetooth_nimble.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c index f4b9ccb600..8bbec54881 100644 --- a/extmod/nimble/modbluetooth_nimble.c +++ b/extmod/nimble/modbluetooth_nimble.c @@ -1682,7 +1682,19 @@ int mp_bluetooth_l2cap_send(uint16_t conn_handle, uint16_t cid, const uint8_t *b *stalled = true; err = 0; } else { - *stalled = false; + if (err) { + // Anything except stalled means it won't attempt to send, + // so free the mbuf (we're failing the op entirely). + os_mbuf_free_chain(sdu_tx); + } else { + *stalled = false; + } + } + + // Sometimes we see what looks like BLE_HS_EAGAIN (but it's actually + // OS_ENOMEM in disguise). Fixed in NimBLE v1.4. + if (err == OS_ENOMEM) { + return MP_ENOMEM; } // Other error codes such as BLE_HS_EBUSY (we're stalled) or BLE_HS_EBADDATA (bigger than MTU). From 74db526cf059d1bc30be38c28c36cf5d1156ae53 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 17 Jul 2021 23:25:07 +1000 Subject: [PATCH 097/264] extmod/btstack/btstack.mk: Use -Wno-implicit-fallthrough, not =0. In 2ae3c890bd923b4c39bba3d2e2f2d75eca5dcc06, -Wimplicit-fallthrough=0 was added to get the build to pass. This option is equivalent to -Wno-implicit-fallthrough, and the latter is compatible with clang (while the former is not). Fixes issue #7546. Signed-off-by: Damien George --- extmod/btstack/btstack.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extmod/btstack/btstack.mk b/extmod/btstack/btstack.mk index 9e1857263b..7ecc230003 100644 --- a/extmod/btstack/btstack.mk +++ b/extmod/btstack/btstack.mk @@ -67,7 +67,7 @@ endif LIB_SRC_C += $(SRC_BTSTACK) # Suppress some warnings. -BTSTACK_WARNING_CFLAGS = -Wno-old-style-definition -Wno-unused-variable -Wno-unused-parameter -Wimplicit-fallthrough=0 +BTSTACK_WARNING_CFLAGS = -Wno-old-style-definition -Wno-unused-variable -Wno-unused-parameter -Wno-implicit-fallthrough ifneq ($(CC),clang) BTSTACK_WARNING_CFLAGS += -Wno-format endif From cbc8d5b61f0742e810ad45bd27d8daa11f3dbbf8 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 17 Jul 2021 23:05:49 +1000 Subject: [PATCH 098/264] tools/ci.sh: Build unix dev variant as part of macOS CI. To test BTstack build on macOS. Signed-off-by: Damien George --- tools/ci.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/ci.sh b/tools/ci.sh index 815e728757..4ff4ba35ba 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -515,6 +515,8 @@ function ci_unix_macos_build { #make ${MAKEOPTS} -C ports/unix deplibs make ${MAKEOPTS} -C ports/unix # check for additional compiler errors/warnings + make ${MAKEOPTS} -C ports/unix VARIANT=dev submodules + make ${MAKEOPTS} -C ports/unix VARIANT=dev make ${MAKEOPTS} -C ports/unix VARIANT=coverage submodules make ${MAKEOPTS} -C ports/unix VARIANT=coverage } From fdd5b18133e9d9f5a4e76be22ece6632d11d7fee Mon Sep 17 00:00:00 2001 From: David P Date: Sat, 12 Jun 2021 14:51:05 +1000 Subject: [PATCH 099/264] docs: Replace master/slave with controller/peripheral in I2C and SPI. See https://www.oshwa.org/a-resolution-to-redefine-spi-signal-names --- docs/esp32/quickref.rst | 2 +- docs/esp8266/quickref.rst | 6 ++-- docs/library/machine.I2C.rst | 38 ++++++++++----------- docs/library/machine.I2S.rst | 2 +- docs/library/machine.SPI.rst | 14 ++++---- docs/library/pyb.I2C.rst | 54 +++++++++++++++--------------- docs/library/pyb.SPI.rst | 20 +++++------ docs/pyboard/quickref.rst | 12 +++---- docs/pyboard/tutorial/amp_skin.rst | 2 +- docs/pyboard/tutorial/lcd_skin.rst | 6 ++-- docs/rp2/quickref.rst | 2 +- docs/wipy/quickref.rst | 14 ++++---- 12 files changed, 86 insertions(+), 86 deletions(-) diff --git a/docs/esp32/quickref.rst b/docs/esp32/quickref.rst index 0b825d5208..56c3721148 100644 --- a/docs/esp32/quickref.rst +++ b/docs/esp32/quickref.rst @@ -361,7 +361,7 @@ accessed via the :ref:`machine.SoftI2C ` class:: i2c.writeto(0x3a, '12') # write '12' to device with address 0x3a buf = bytearray(10) # create a buffer with 10 bytes - i2c.writeto(0x3a, buf) # write the given buffer to the slave + i2c.writeto(0x3a, buf) # write the given buffer to the peripheral Hardware I2C bus ---------------- diff --git a/docs/esp8266/quickref.rst b/docs/esp8266/quickref.rst index bc648a348a..72b3596697 100644 --- a/docs/esp8266/quickref.rst +++ b/docs/esp8266/quickref.rst @@ -270,11 +270,11 @@ alias of :ref:`machine.SoftI2C `):: # construct an I2C bus i2c = I2C(scl=Pin(5), sda=Pin(4), freq=100000) - i2c.readfrom(0x3a, 4) # read 4 bytes from slave device with address 0x3a - i2c.writeto(0x3a, '12') # write '12' to slave device with address 0x3a + i2c.readfrom(0x3a, 4) # read 4 bytes from peripheral device with address 0x3a + i2c.writeto(0x3a, '12') # write '12' to peripheral device with address 0x3a buf = bytearray(10) # create a buffer with 10 bytes - i2c.writeto(0x3a, buf) # write the given buffer to the slave + i2c.writeto(0x3a, buf) # write the given buffer to the peripheral Real time clock (RTC) --------------------- diff --git a/docs/library/machine.I2C.rst b/docs/library/machine.I2C.rst index f9b9515646..f46d64ef51 100644 --- a/docs/library/machine.I2C.rst +++ b/docs/library/machine.I2C.rst @@ -28,15 +28,15 @@ Example usage:: # depending on the port, extra parameters may be required # to select the peripheral and/or pins to use - i2c.scan() # scan for slaves, returning a list of 7-bit addresses + i2c.scan() # scan for peripherals, returning a list of 7-bit addresses - i2c.writeto(42, b'123') # write 3 bytes to slave with 7-bit address 42 - i2c.readfrom(42, 4) # read 4 bytes from slave with 7-bit address 42 + i2c.writeto(42, b'123') # write 3 bytes to peripheral with 7-bit address 42 + i2c.readfrom(42, 4) # read 4 bytes from peripheral with 7-bit address 42 - i2c.readfrom_mem(42, 8, 3) # read 3 bytes from memory of slave 42, - # starting at memory-address 8 in the slave - i2c.writeto_mem(42, 2, b'\x10') # write 1 byte to memory of slave 42 - # starting at address 2 in the slave + i2c.readfrom_mem(42, 8, 3) # read 3 bytes from memory of peripheral 42, + # starting at memory-address 8 in the peripheral + i2c.writeto_mem(42, 2, b'\x10') # write 1 byte to memory of peripheral 42 + # starting at address 2 in the peripheral Constructors ------------ @@ -95,7 +95,7 @@ General Methods Primitive I2C operations ------------------------ -The following methods implement the primitive I2C master bus operations and can +The following methods implement the primitive I2C controller bus operations and can be combined to make any I2C transaction. They are provided if you need more control over the bus, otherwise the standard methods (see below) can be used. @@ -115,7 +115,7 @@ These methods are only available on the `machine.SoftI2C` class. read is the length of *buf*. An ACK will be sent on the bus after receiving all but the last byte. After the last byte is received, if *nack* is true then a NACK will be sent, otherwise an ACK will be sent (and in this - case the slave assumes more bytes are going to be read in a later call). + case the peripheral assumes more bytes are going to be read in a later call). .. method:: I2C.write(buf) @@ -126,18 +126,18 @@ These methods are only available on the `machine.SoftI2C` class. Standard bus operations ----------------------- -The following methods implement the standard I2C master read and write -operations that target a given slave device. +The following methods implement the standard I2C controller read and write +operations that target a given peripheral device. .. method:: I2C.readfrom(addr, nbytes, stop=True, /) - Read *nbytes* from the slave specified by *addr*. + Read *nbytes* from the peripheral specified by *addr*. If *stop* is true then a STOP condition is generated at the end of the transfer. Returns a `bytes` object with the data read. .. method:: I2C.readfrom_into(addr, buf, stop=True, /) - Read into *buf* from the slave specified by *addr*. + Read into *buf* from the peripheral specified by *addr*. The number of bytes read will be the length of *buf*. If *stop* is true then a STOP condition is generated at the end of the transfer. @@ -145,7 +145,7 @@ operations that target a given slave device. .. method:: I2C.writeto(addr, buf, stop=True, /) - Write the bytes from *buf* to the slave specified by *addr*. If a + Write the bytes from *buf* to the peripheral specified by *addr*. If a NACK is received following the write of a byte from *buf* then the remaining bytes are not sent. If *stop* is true then a STOP condition is generated at the end of the transfer, even if a NACK is received. @@ -153,7 +153,7 @@ operations that target a given slave device. .. method:: I2C.writevto(addr, vector, stop=True, /) - Write the bytes contained in *vector* to the slave specified by *addr*. + Write the bytes contained in *vector* to the peripheral specified by *addr*. *vector* should be a tuple or list of objects with the buffer protocol. The *addr* is sent once and then the bytes from each object in *vector* are written out sequentially. The objects in *vector* may be zero bytes @@ -170,19 +170,19 @@ Memory operations Some I2C devices act as a memory device (or set of registers) that can be read from and written to. In this case there are two addresses associated with an -I2C transaction: the slave address and the memory address. The following +I2C transaction: the peripheral address and the memory address. The following methods are convenience functions to communicate with such devices. .. method:: I2C.readfrom_mem(addr, memaddr, nbytes, *, addrsize=8) - Read *nbytes* from the slave specified by *addr* starting from the memory + Read *nbytes* from the peripheral specified by *addr* starting from the memory address specified by *memaddr*. The argument *addrsize* specifies the address size in bits. Returns a `bytes` object with the data read. .. method:: I2C.readfrom_mem_into(addr, memaddr, buf, *, addrsize=8) - Read into *buf* from the slave specified by *addr* starting from the + Read into *buf* from the peripheral specified by *addr* starting from the memory address specified by *memaddr*. The number of bytes read is the length of *buf*. The argument *addrsize* specifies the address size in bits (on ESP8266 @@ -192,7 +192,7 @@ methods are convenience functions to communicate with such devices. .. method:: I2C.writeto_mem(addr, memaddr, buf, *, addrsize=8) - Write *buf* to the slave specified by *addr* starting from the + Write *buf* to the peripheral specified by *addr* starting from the memory address specified by *memaddr*. The argument *addrsize* specifies the address size in bits (on ESP8266 this argument is not recognised and the address size is always 8 bits). diff --git a/docs/library/machine.I2S.rst b/docs/library/machine.I2S.rst index 48f03b419d..e1c6af990c 100644 --- a/docs/library/machine.I2S.rst +++ b/docs/library/machine.I2S.rst @@ -6,7 +6,7 @@ class I2S -- Inter-IC Sound bus protocol I2S is a synchronous serial protocol used to connect digital audio devices. At the physical level, a bus consists of 3 lines: SCK, WS, SD. -The I2S class supports Master operation. Slave operation is not supported. +The I2S class supports controller operation. Peripheral operation is not supported. The I2S class is currently available as a Technical Preview. During the preview period, feedback from users is encouraged. Based on this feedback, the I2S class API and implementation may be changed. diff --git a/docs/library/machine.SPI.rst b/docs/library/machine.SPI.rst index 7565241eb1..46ac2ec74c 100644 --- a/docs/library/machine.SPI.rst +++ b/docs/library/machine.SPI.rst @@ -1,14 +1,14 @@ .. currentmodule:: machine .. _machine.SPI: -class SPI -- a Serial Peripheral Interface bus protocol (master side) -===================================================================== +class SPI -- a Serial Peripheral Interface bus protocol (controller side) +========================================================================= -SPI is a synchronous serial protocol that is driven by a master. At the +SPI is a synchronous serial protocol that is driven by a controller. At the physical level, a bus consists of 3 lines: SCK, MOSI, MISO. Multiple devices can share the same bus. Each device should have a separate, 4th signal, -SS (Slave Select), to select a particular device on a bus with which -communication takes place. Management of an SS signal should happen in +CS (Chip Select), to select a particular device on a bus with which +communication takes place. Management of a CS signal should happen in user code (via machine.Pin class). Both hardware and software SPI implementations exist via the @@ -102,9 +102,9 @@ Methods Constants --------- -.. data:: SPI.MASTER +.. data:: SPI.CONTROLLER - for initialising the SPI bus to master; this is only used for the WiPy + for initialising the SPI bus to controller; this is only used for the WiPy .. data:: SPI.MSB diff --git a/docs/library/pyb.I2C.rst b/docs/library/pyb.I2C.rst index 56b036e074..f60b506861 100644 --- a/docs/library/pyb.I2C.rst +++ b/docs/library/pyb.I2C.rst @@ -14,11 +14,11 @@ Example:: from pyb import I2C - i2c = I2C(1) # create on bus 1 - i2c = I2C(1, I2C.MASTER) # create and init as a master - i2c.init(I2C.MASTER, baudrate=20000) # init as a master - i2c.init(I2C.SLAVE, addr=0x42) # init as a slave with given address - i2c.deinit() # turn off the peripheral + i2c = I2C(1) # create on bus 1 + i2c = I2C(1, I2C.CONTROLLER) # create and init as a controller + i2c.init(I2C.CONTROLLER, baudrate=20000) # init as a controller + i2c.init(I2C.PERIPHERAL, addr=0x42) # init as a peripheral with given address + i2c.deinit() # turn off the I2C unit Printing the i2c object gives you information about its configuration. @@ -37,21 +37,21 @@ You can specify a timeout (in ms):: i2c.send(b'123', timeout=2000) # timeout after 2 seconds -A master must specify the recipient's address:: +A controller must specify the recipient's address:: - i2c.init(I2C.MASTER) - i2c.send('123', 0x42) # send 3 bytes to slave with address 0x42 + i2c.init(I2C.CONTROLLER) + i2c.send('123', 0x42) # send 3 bytes to peripheral with address 0x42 i2c.send(b'456', addr=0x42) # keyword for address Master also has other methods:: - i2c.is_ready(0x42) # check if slave 0x42 is ready - i2c.scan() # scan for slaves on the bus, returning + i2c.is_ready(0x42) # check if peripheral 0x42 is ready + i2c.scan() # scan for peripherals on the bus, returning # a list of valid addresses - i2c.mem_read(3, 0x42, 2) # read 3 bytes from memory of slave 0x42, - # starting at address 2 in the slave - i2c.mem_write('abc', 0x42, 2, timeout=1000) # write 'abc' (3 bytes) to memory of slave 0x42 - # starting at address 2 in the slave, timeout after 1 second + i2c.mem_read(3, 0x42, 2) # read 3 bytes from memory of peripheral 0x42, + # starting at address 2 in the peripheral + i2c.mem_write('abc', 0x42, 2, timeout=1000) # write 'abc' (3 bytes) to memory of peripheral 0x42 + # starting at address 2 in the peripheral, timeout after 1 second Constructors ------------ @@ -88,9 +88,9 @@ Methods Initialise the I2C bus with the given parameters: - - ``mode`` must be either ``I2C.MASTER`` or ``I2C.SLAVE`` - - ``addr`` is the 7-bit address (only sensible for a slave) - - ``baudrate`` is the SCL clock rate (only sensible for a master) + - ``mode`` must be either ``I2C.CONTROLLER`` or ``I2C.PERIPHERAL`` + - ``addr`` is the 7-bit address (only sensible for a peripheral) + - ``baudrate`` is the SCL clock rate (only sensible for a controller) - ``gencall`` is whether to support general call mode - ``dma`` is whether to allow the use of DMA for the I2C transfers (note that DMA transfers have more precise timing but currently do not handle bus @@ -98,7 +98,7 @@ Methods .. method:: I2C.is_ready(addr) - Check if an I2C device responds to the given address. Only valid when in master mode. + Check if an I2C device responds to the given address. Only valid when in controller mode. .. method:: I2C.mem_read(data, addr, memaddr, *, timeout=5000, addr_size=8) @@ -111,7 +111,7 @@ Methods - ``addr_size`` selects width of memaddr: 8 or 16 bits Returns the read data. - This is only valid in master mode. + This is only valid in controller mode. .. method:: I2C.mem_write(data, addr, memaddr, *, timeout=5000, addr_size=8) @@ -124,7 +124,7 @@ Methods - ``addr_size`` selects width of memaddr: 8 or 16 bits Returns ``None``. - This is only valid in master mode. + This is only valid in controller mode. .. method:: I2C.recv(recv, addr=0x00, *, timeout=5000) @@ -132,7 +132,7 @@ Methods - ``recv`` can be an integer, which is the number of bytes to receive, or a mutable buffer, which will be filled with received bytes - - ``addr`` is the address to receive from (only required in master mode) + - ``addr`` is the address to receive from (only required in controller mode) - ``timeout`` is the timeout in milliseconds to wait for the receive Return value: if ``recv`` is an integer then a new buffer of the bytes received, @@ -143,7 +143,7 @@ Methods Send data on the bus: - ``send`` is the data to send (an integer to send, or a buffer object) - - ``addr`` is the address to send to (only required in master mode) + - ``addr`` is the address to send to (only required in controller mode) - ``timeout`` is the timeout in milliseconds to wait for the send Return value: ``None``. @@ -151,15 +151,15 @@ Methods .. method:: I2C.scan() Scan all I2C addresses from 0x01 to 0x7f and return a list of those that respond. - Only valid when in master mode. + Only valid when in controller mode. Constants --------- -.. data:: I2C.MASTER +.. data:: I2C.CONTROLLER - for initialising the bus to master mode + for initialising the bus to controller mode -.. data:: I2C.SLAVE +.. data:: I2C.PERIPHERAL - for initialising the bus to slave mode + for initialising the bus to peripheral mode diff --git a/docs/library/pyb.SPI.rst b/docs/library/pyb.SPI.rst index c76b16789a..1bdb73a5dd 100644 --- a/docs/library/pyb.SPI.rst +++ b/docs/library/pyb.SPI.rst @@ -1,19 +1,19 @@ .. currentmodule:: pyb .. _pyb.SPI: -class SPI -- a master-driven serial protocol -============================================ +class SPI -- a controller-driven serial protocol +================================================ -SPI is a serial protocol that is driven by a master. At the physical level +SPI is a serial protocol that is driven by a controller. At the physical level there are 3 lines: SCK, MOSI, MISO. See usage model of I2C; SPI is very similar. Main difference is parameters to init the SPI bus:: from pyb import SPI - spi = SPI(1, SPI.MASTER, baudrate=600000, polarity=1, phase=0, crc=0x7) + spi = SPI(1, SPI.CONTROLLER, baudrate=600000, polarity=1, phase=0, crc=0x7) -Only required parameter is mode, SPI.MASTER or SPI.SLAVE. Polarity can be +Only required parameter is mode, SPI.CONTROLLER or SPI.PERIPHERAL. Polarity can be 0 or 1, and is the level the idle clock line sits at. Phase can be 0 or 1 to sample data on the first or second clock edge respectively. Crc can be None for no CRC, or a polynomial specifier. @@ -55,8 +55,8 @@ Methods Initialise the SPI bus with the given parameters: - - ``mode`` must be either ``SPI.MASTER`` or ``SPI.SLAVE``. - - ``baudrate`` is the SCK clock rate (only sensible for a master). + - ``mode`` must be either ``SPI.CONTROLLER`` or ``SPI.PERIPHERAL``. + - ``baudrate`` is the SCK clock rate (only sensible for a controller). - ``prescaler`` is the prescaler to use to derive SCK from the APB bus frequency; use of ``prescaler`` overrides ``baudrate``. - ``polarity`` can be 0 or 1, and is the level the idle clock line sits at. @@ -112,10 +112,10 @@ Methods Constants --------- -.. data:: SPI.MASTER -.. data:: SPI.SLAVE +.. data:: SPI.CONTROLLER +.. data:: SPI.PERIPHERAL - for initialising the SPI bus to master or slave mode + for initialising the SPI bus to controller or peripheral mode .. data:: SPI.LSB .. data:: SPI.MSB diff --git a/docs/pyboard/quickref.rst b/docs/pyboard/quickref.rst index 3ea3190999..49b67eee42 100644 --- a/docs/pyboard/quickref.rst +++ b/docs/pyboard/quickref.rst @@ -191,7 +191,7 @@ See :ref:`pyb.SPI `. :: from pyb import SPI - spi = SPI(1, SPI.MASTER, baudrate=200000, polarity=1, phase=0) + spi = SPI(1, SPI.CONTROLLER, baudrate=200000, polarity=1, phase=0) spi.send('hello') spi.recv(5) # receive 5 bytes on the bus spi.send_recv('hello') # send and receive 5 bytes @@ -210,12 +210,12 @@ eg ``I2C(1)``. Software I2C is also available by explicitly specifying the i2c = I2C('X', freq=400000) # create hardware I2c object i2c = I2C(scl='X1', sda='X2', freq=100000) # create software I2C object - i2c.scan() # returns list of slave addresses - i2c.writeto(0x42, 'hello') # write 5 bytes to slave with address 0x42 - i2c.readfrom(0x42, 5) # read 5 bytes from slave + i2c.scan() # returns list of peripheral addresses + i2c.writeto(0x42, 'hello') # write 5 bytes to peripheral with address 0x42 + i2c.readfrom(0x42, 5) # read 5 bytes from peripheral - i2c.readfrom_mem(0x42, 0x10, 2) # read 2 bytes from slave 0x42, slave memory 0x10 - i2c.writeto_mem(0x42, 0x10, 'xy') # write 2 bytes to slave 0x42, slave memory 0x10 + i2c.readfrom_mem(0x42, 0x10, 2) # read 2 bytes from peripheral 0x42, peripheral memory 0x10 + i2c.writeto_mem(0x42, 0x10, 'xy') # write 2 bytes to peripheral 0x42, peripheral memory 0x10 Note: for legacy I2C support see :ref:`pyb.I2C `. diff --git a/docs/pyboard/tutorial/amp_skin.rst b/docs/pyboard/tutorial/amp_skin.rst index bcb5832613..b6558959ea 100644 --- a/docs/pyboard/tutorial/amp_skin.rst +++ b/docs/pyboard/tutorial/amp_skin.rst @@ -30,7 +30,7 @@ To set the volume, define the following function:: import pyb def volume(val): - pyb.I2C(1, pyb.I2C.MASTER).mem_write(val, 46, 0) + pyb.I2C(1, pyb.I2C.CONTROLLER).mem_write(val, 46, 0) Then you can do:: diff --git a/docs/pyboard/tutorial/lcd_skin.rst b/docs/pyboard/tutorial/lcd_skin.rst index 288ac1bf08..4df0041607 100644 --- a/docs/pyboard/tutorial/lcd_skin.rst +++ b/docs/pyboard/tutorial/lcd_skin.rst @@ -51,7 +51,7 @@ MPR121 capacitive touch sensor has address 90. To get started, try:: >>> import pyb - >>> i2c = pyb.I2C(1, pyb.I2C.MASTER) + >>> i2c = pyb.I2C(1, pyb.I2C.CONTROLLER) >>> i2c.mem_write(4, 90, 0x5e) >>> touch = i2c.mem_read(1, 90, 0)[0] @@ -68,7 +68,7 @@ directory or ``lib/`` directory) and then try:: >>> import pyb >>> import mpr121 - >>> m = mpr121.MPR121(pyb.I2C(1, pyb.I2C.MASTER)) + >>> m = mpr121.MPR121(pyb.I2C(1, pyb.I2C.CONTROLLER)) >>> for i in range(100): ... print(m.touch_status()) ... pyb.delay(100) @@ -80,7 +80,7 @@ Try touching each one in turn. Note that if you put the LCD skin in the Y-position, then you need to initialise the I2C bus using:: - >>> m = mpr121.MPR121(pyb.I2C(2, pyb.I2C.MASTER)) + >>> m = mpr121.MPR121(pyb.I2C(2, pyb.I2C.CONTROLLER)) There is also a demo which uses the LCD and the touch sensors together, and can be found `here `__. diff --git a/docs/rp2/quickref.rst b/docs/rp2/quickref.rst index 4c8c02dc0c..c18eb4a0b9 100644 --- a/docs/rp2/quickref.rst +++ b/docs/rp2/quickref.rst @@ -181,7 +181,7 @@ accessed via the :ref:`machine.SoftI2C ` class:: i2c.writeto(0x3a, '12') # write '12' to device with address 0x3a buf = bytearray(10) # create a buffer with 10 bytes - i2c.writeto(0x3a, buf) # write the given buffer to the slave + i2c.writeto(0x3a, buf) # write the given buffer to the peripheral Hardware I2C bus ---------------- diff --git a/docs/wipy/quickref.rst b/docs/wipy/quickref.rst index a22ea45b55..f9ea3d501e 100644 --- a/docs/wipy/quickref.rst +++ b/docs/wipy/quickref.rst @@ -107,8 +107,8 @@ See :ref:`machine.SPI `. :: from machine import SPI - # configure the SPI master @ 2MHz - spi = SPI(0, SPI.MASTER, baudrate=200000, polarity=0, phase=0) + # configure the SPI controller @ 2MHz + spi = SPI(0, SPI.CONTROLLER, baudrate=2_000_000, polarity=0, phase=0) spi.write('hello') spi.read(5) # receive 5 bytes on the bus rbuf = bytearray(5) @@ -122,11 +122,11 @@ See :ref:`machine.I2C `. :: from machine import I2C # configure the I2C bus i2c = I2C(baudrate=100000) - i2c.scan() # returns list of slave addresses - i2c.writeto(0x42, 'hello') # send 5 bytes to slave with address 0x42 - i2c.readfrom(0x42, 5) # receive 5 bytes from slave - i2c.readfrom_mem(0x42, 0x10, 2) # read 2 bytes from slave 0x42, slave memory 0x10 - i2c.writeto_mem(0x42, 0x10, 'xy') # write 2 bytes to slave 0x42, slave memory 0x10 + i2c.scan() # returns list of peripheral addresses + i2c.writeto(0x42, 'hello') # send 5 bytes to peripheral with address 0x42 + i2c.readfrom(0x42, 5) # receive 5 bytes from peripheral + i2c.readfrom_mem(0x42, 0x10, 2) # read 2 bytes from peripheral 0x42, peripheral memory 0x10 + i2c.writeto_mem(0x42, 0x10, 'xy') # write 2 bytes to peripheral 0x42, peripheral memory 0x10 Watchdog timer (WDT) -------------------- From f365025c9c699a4a42c103b9ff01071ebc8d57c8 Mon Sep 17 00:00:00 2001 From: David P Date: Sat, 12 Jun 2021 14:53:44 +1000 Subject: [PATCH 100/264] stm32: Replace master/slave with controller/peripheral in I2C and SPI. Replace "master" with "controller" and "slave" with "peripheral" in comments, errors, and debug messages. Add CONTROLLER and PERIPHERAL constants to pyb.SPI and pyb.I2C classes; retain MASTER and SLAVE constants for backward compatiblity. --- ports/stm32/pyb_i2c.c | 63 ++++++++++++++++++++++-------------------- ports/stm32/pyb_spi.c | 19 +++++++------ ports/stm32/spi.c | 4 +-- tests/pyb/i2c.py | 2 +- tests/pyb/i2c_accel.py | 2 +- tests/pyb/i2c_error.py | 2 +- tests/pyb/pyb_f405.py | 2 +- tests/pyb/spi.py | 14 ++++++---- tests/pyb/spi.py.exp | 4 +-- 9 files changed, 60 insertions(+), 52 deletions(-) diff --git a/ports/stm32/pyb_i2c.c b/ports/stm32/pyb_i2c.c index f0bd38333d..dbcf4bcb81 100644 --- a/ports/stm32/pyb_i2c.c +++ b/ports/stm32/pyb_i2c.c @@ -49,14 +49,14 @@ /// from pyb import I2C /// /// i2c = I2C(1) # create on bus 1 -/// i2c = I2C(1, I2C.MASTER) # create and init as a master -/// i2c.init(I2C.MASTER, baudrate=20000) # init as a master -/// i2c.init(I2C.SLAVE, addr=0x42) # init as a slave with given address -/// i2c.deinit() # turn off the peripheral +/// i2c = I2C(1, I2C.CONTROLLER) # create and init as a controller +/// i2c.init(I2C.CONTROLLER, baudrate=20000) # init as a controller +/// i2c.init(I2C.PERIPHERAL, addr=0x42) # init as a peripheral with given address +/// i2c.deinit() # turn off the I2C unit /// /// Printing the i2c object gives you information about its configuration. /// -/// Basic methods for slave are send and recv: +/// Basic methods for peripheral are send and recv: /// /// i2c.send('abc') # send 3 bytes /// i2c.send(0x42) # send a single byte, given by the number @@ -71,19 +71,19 @@ /// /// i2c.send(b'123', timeout=2000) # timout after 2 seconds /// -/// A master must specify the recipient's address: +/// A controller must specify the recipient's address: /// -/// i2c.init(I2C.MASTER) -/// i2c.send('123', 0x42) # send 3 bytes to slave with address 0x42 +/// i2c.init(I2C.CONTROLLER) +/// i2c.send('123', 0x42) # send 3 bytes to peripheral with address 0x42 /// i2c.send(b'456', addr=0x42) # keyword for address /// /// Master also has other methods: /// -/// i2c.is_ready(0x42) # check if slave 0x42 is ready -/// i2c.scan() # scan for slaves on the bus, returning +/// i2c.is_ready(0x42) # check if peripheral 0x42 is ready +/// i2c.scan() # scan for peripherals on the bus, returning /// # a list of valid addresses -/// i2c.mem_read(3, 0x42, 2) # read 3 bytes from memory of slave 0x42, -/// # starting at address 2 in the slave +/// i2c.mem_read(3, 0x42, 2) # read 3 bytes from memory of peripheral 0x42, +/// # starting at address 2 in the peripheral /// i2c.mem_write('abc', 0x42, 2, timeout=1000) #define PYB_I2C_MASTER (0) #define PYB_I2C_SLAVE (1) @@ -578,7 +578,7 @@ STATIC void pyb_i2c_print(const mp_print_t *print, mp_obj_t self_in, mp_print_ki mp_printf(print, "I2C(%u)", i2c_num); } else { if (in_master_mode(self)) { - mp_printf(print, "I2C(%u, I2C.MASTER, baudrate=%u" + mp_printf(print, "I2C(%u, I2C.CONTROLLER, baudrate=%u" #if PYB_I2C_TIMINGR ", timingr=0x%08x" #endif @@ -588,7 +588,7 @@ STATIC void pyb_i2c_print(const mp_print_t *print, mp_obj_t self_in, mp_print_ki #endif ); } else { - mp_printf(print, "I2C(%u, I2C.SLAVE, addr=0x%02x)", i2c_num, (self->i2c->Instance->OAR1 >> 1) & 0x7f); + mp_printf(print, "I2C(%u, I2C.PERIPHERAL, addr=0x%02x)", i2c_num, (self->i2c->Instance->OAR1 >> 1) & 0x7f); } } } @@ -597,9 +597,9 @@ STATIC void pyb_i2c_print(const mp_print_t *print, mp_obj_t self_in, mp_print_ki /// /// Initialise the I2C bus with the given parameters: /// -/// - `mode` must be either `I2C.MASTER` or `I2C.SLAVE` -/// - `addr` is the 7-bit address (only sensible for a slave) -/// - `baudrate` is the SCL clock rate (only sensible for a master) +/// - `mode` must be either `I2C.CONTROLLER` or `I2C.PERIPHERAL` +/// - `addr` is the 7-bit address (only sensible for a peripheral) +/// - `baudrate` is the SCL clock rate (only sensible for a controller) /// - `gencall` is whether to support general call mode STATIC mp_obj_t pyb_i2c_init_helper(const pyb_i2c_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[] = { @@ -621,7 +621,7 @@ STATIC mp_obj_t pyb_i2c_init_helper(const pyb_i2c_obj_t *self, size_t n_args, co I2C_InitTypeDef *init = &self->i2c->Init; if (args[0].u_int == PYB_I2C_MASTER) { - // use a special address to indicate we are a master + // use a special address to indicate we are a controller init->OwnAddress1 = PYB_I2C_MASTER_ADDRESS; } else { init->OwnAddress1 = (args[1].u_int << 1) & 0xfe; @@ -697,12 +697,12 @@ STATIC mp_obj_t pyb_i2c_deinit(mp_obj_t self_in) { STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_i2c_deinit_obj, pyb_i2c_deinit); /// \method is_ready(addr) -/// Check if an I2C device responds to the given address. Only valid when in master mode. +/// Check if an I2C device responds to the given address. Only valid when in controller mode. STATIC mp_obj_t pyb_i2c_is_ready(mp_obj_t self_in, mp_obj_t i2c_addr_o) { pyb_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in); if (!in_master_mode(self)) { - mp_raise_TypeError(MP_ERROR_TEXT("I2C must be a master")); + mp_raise_TypeError(MP_ERROR_TEXT("I2C must be a controller")); } mp_uint_t i2c_addr = mp_obj_get_int(i2c_addr_o) << 1; @@ -720,12 +720,12 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(pyb_i2c_is_ready_obj, pyb_i2c_is_ready); /// \method scan() /// Scan all I2C addresses from 0x08 to 0x77 and return a list of those that respond. -/// Only valid when in master mode. +/// Only valid when in controller mode. STATIC mp_obj_t pyb_i2c_scan(mp_obj_t self_in) { pyb_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in); if (!in_master_mode(self)) { - mp_raise_TypeError(MP_ERROR_TEXT("I2C must be a master")); + mp_raise_TypeError(MP_ERROR_TEXT("I2C must be a controller")); } mp_obj_t list = mp_obj_new_list(0, NULL); @@ -745,7 +745,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_i2c_scan_obj, pyb_i2c_scan); /// Send data on the bus: /// /// - `send` is the data to send (an integer to send, or a buffer object) -/// - `addr` is the address to send to (only required in master mode) +/// - `addr` is the address to send to (only required in controller mode) /// - `timeout` is the timeout in milliseconds to wait for the send /// /// Return value: `None`. @@ -824,7 +824,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2c_send_obj, 1, pyb_i2c_send); /// /// - `recv` can be an integer, which is the number of bytes to receive, /// or a mutable buffer, which will be filled with received bytes -/// - `addr` is the address to receive from (only required in master mode) +/// - `addr` is the address to receive from (only required in controller mode) /// - `timeout` is the timeout in milliseconds to wait for the receive /// /// Return value: if `recv` is an integer then a new buffer of the bytes received, @@ -910,7 +910,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2c_recv_obj, 1, pyb_i2c_recv); /// - `addr_size` selects width of memaddr: 8 or 16 bits /// /// Returns the read data. -/// This is only valid in master mode. +/// This is only valid in controller mode. STATIC const mp_arg_t pyb_i2c_mem_read_allowed_args[] = { { MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_addr, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, @@ -926,7 +926,7 @@ STATIC mp_obj_t pyb_i2c_mem_read(size_t n_args, const mp_obj_t *pos_args, mp_map mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(pyb_i2c_mem_read_allowed_args), pyb_i2c_mem_read_allowed_args, args); if (!in_master_mode(self)) { - mp_raise_TypeError(MP_ERROR_TEXT("I2C must be a master")); + mp_raise_TypeError(MP_ERROR_TEXT("I2C must be a controller")); } // get the buffer to read into @@ -986,7 +986,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2c_mem_read_obj, 1, pyb_i2c_mem_read); /// - `addr_size` selects width of memaddr: 8 or 16 bits /// /// Returns `None`. -/// This is only valid in master mode. +/// This is only valid in controller mode. STATIC mp_obj_t pyb_i2c_mem_write(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { // parse args (same as mem_read) pyb_i2c_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); @@ -994,7 +994,7 @@ STATIC mp_obj_t pyb_i2c_mem_write(size_t n_args, const mp_obj_t *pos_args, mp_ma mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(pyb_i2c_mem_read_allowed_args), pyb_i2c_mem_read_allowed_args, args); if (!in_master_mode(self)) { - mp_raise_TypeError(MP_ERROR_TEXT("I2C must be a master")); + mp_raise_TypeError(MP_ERROR_TEXT("I2C must be a controller")); } // get the buffer to write from @@ -1051,8 +1051,11 @@ STATIC const mp_rom_map_elem_t pyb_i2c_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_mem_write), MP_ROM_PTR(&pyb_i2c_mem_write_obj) }, // class constants - /// \constant MASTER - for initialising the bus to master mode - /// \constant SLAVE - for initialising the bus to slave mode + /// \constant CONTROLLER - for initialising the bus to controller mode + /// \constant PERIPHERAL - for initialising the bus to peripheral mode + { MP_ROM_QSTR(MP_QSTR_CONTROLLER), MP_ROM_INT(PYB_I2C_MASTER) }, + { MP_ROM_QSTR(MP_QSTR_PERIPHERAL), MP_ROM_INT(PYB_I2C_SLAVE) }, + // TODO - remove MASTER/SLAVE when CONTROLLER/PERIPHERAL gain wide adoption { MP_ROM_QSTR(MP_QSTR_MASTER), MP_ROM_INT(PYB_I2C_MASTER) }, { MP_ROM_QSTR(MP_QSTR_SLAVE), MP_ROM_INT(PYB_I2C_SLAVE) }, }; diff --git a/ports/stm32/pyb_spi.c b/ports/stm32/pyb_spi.c index 99a1cd77b4..abb7ba41db 100644 --- a/ports/stm32/pyb_spi.c +++ b/ports/stm32/pyb_spi.c @@ -32,18 +32,18 @@ /******************************************************************************/ // MicroPython bindings for legacy pyb API -// class pyb.SPI - a master-driven serial protocol +// class pyb.SPI - a controller-driven serial protocol // -// SPI is a serial protocol that is driven by a master. At the physical level +// SPI is a serial protocol that is driven by a controller. At the physical level // there are 3 lines: SCK, MOSI, MISO. // // See usage model of I2C; SPI is very similar. Main difference is // parameters to init the SPI bus: // // from pyb import SPI -// spi = SPI(1, SPI.MASTER, baudrate=600000, polarity=1, phase=0, crc=0x7) +// spi = SPI(1, SPI.CONTROLLER, baudrate=600000, polarity=1, phase=0, crc=0x7) // -// Only required parameter is mode, SPI.MASTER or SPI.SLAVE. Polarity can be +// Only required parameter is mode, SPI.CONTROLLER or SPI.PERIPHERAL. Polarity can be // 0 or 1, and is the level the idle clock line sits at. Phase can be 0 or 1 // to sample data on the first or second clock edge respectively. Crc can be // None for no CRC, or a polynomial specifier. @@ -72,8 +72,8 @@ STATIC void pyb_spi_print(const mp_print_t *print, mp_obj_t self_in, mp_print_ki // init(mode, baudrate=328125, *, polarity=1, phase=0, bits=8, firstbit=SPI.MSB, ti=False, crc=None) // // Initialise the SPI bus with the given parameters: -// - `mode` must be either `SPI.MASTER` or `SPI.SLAVE`. -// - `baudrate` is the SCK clock rate (only sensible for a master). +// - `mode` must be either `SPI.CONTROLLER` or `SPI.PERIPHERAL`. +// - `baudrate` is the SCK clock rate (only sensible for a controller). STATIC mp_obj_t pyb_spi_init_helper(const pyb_spi_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, {.u_int = 0} }, @@ -319,10 +319,13 @@ STATIC const mp_rom_map_elem_t pyb_spi_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_send_recv), MP_ROM_PTR(&pyb_spi_send_recv_obj) }, // class constants - /// \constant MASTER - for initialising the bus to master mode - /// \constant SLAVE - for initialising the bus to slave mode + /// \constant CONTROLLER - for initialising the bus to controller mode + /// \constant PERIPHERAL - for initialising the bus to peripheral mode /// \constant MSB - set the first bit to MSB /// \constant LSB - set the first bit to LSB + { MP_ROM_QSTR(MP_QSTR_CONTROLLER), MP_ROM_INT(SPI_MODE_MASTER) }, + { MP_ROM_QSTR(MP_QSTR_PERIPHERAL), MP_ROM_INT(SPI_MODE_SLAVE) }, + // TODO - remove MASTER/SLAVE when CONTROLLER/PERIPHERAL gain wide adoption { MP_ROM_QSTR(MP_QSTR_MASTER), MP_ROM_INT(SPI_MODE_MASTER) }, { MP_ROM_QSTR(MP_QSTR_SLAVE), MP_ROM_INT(SPI_MODE_SLAVE) }, { MP_ROM_QSTR(MP_QSTR_MSB), MP_ROM_INT(SPI_FIRSTBIT_MSB) }, diff --git a/ports/stm32/spi.c b/ports/stm32/spi.c index d3b3a784e8..0ce6b5abc6 100644 --- a/ports/stm32/spi.c +++ b/ports/stm32/spi.c @@ -640,14 +640,14 @@ void spi_print(const mp_print_t *print, const spi_t *spi_obj, bool legacy) { #endif uint baudrate = spi_get_source_freq(spi) >> log_prescaler; if (legacy) { - mp_printf(print, ", SPI.MASTER"); + mp_printf(print, ", SPI.CONTROLLER"); } mp_printf(print, ", baudrate=%u", baudrate); if (legacy) { mp_printf(print, ", prescaler=%u", 1 << log_prescaler); } } else { - mp_printf(print, ", SPI.SLAVE"); + mp_printf(print, ", SPI.PERIPHERAL"); } mp_printf(print, ", polarity=%u, phase=%u, bits=%u", spi->Init.CLKPolarity == SPI_POLARITY_LOW ? 0 : 1, spi->Init.CLKPhase == SPI_PHASE_1EDGE ? 0 : 1, spi->Init.DataSize == SPI_DATASIZE_8BIT ? 8 : 16); if (spi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE) { diff --git a/tests/pyb/i2c.py b/tests/pyb/i2c.py index bc9dba8781..c968843273 100644 --- a/tests/pyb/i2c.py +++ b/tests/pyb/i2c.py @@ -11,6 +11,6 @@ for bus in (-1, 0, 1): i2c = I2C(1) -i2c.init(I2C.MASTER, baudrate=400000) +i2c.init(I2C.CONTROLLER, baudrate=400000) print(i2c.scan()) i2c.deinit() diff --git a/tests/pyb/i2c_accel.py b/tests/pyb/i2c_accel.py index 8c74fa60e6..8b87d406d0 100644 --- a/tests/pyb/i2c_accel.py +++ b/tests/pyb/i2c_accel.py @@ -11,7 +11,7 @@ accel_addr = 76 pyb.Accel() # this will init the MMA for us -i2c = I2C(1, I2C.MASTER, baudrate=400000) +i2c = I2C(1, I2C.CONTROLLER, baudrate=400000) print(i2c.scan()) print(i2c.is_ready(accel_addr)) diff --git a/tests/pyb/i2c_error.py b/tests/pyb/i2c_error.py index b17a26325d..1228962f5f 100644 --- a/tests/pyb/i2c_error.py +++ b/tests/pyb/i2c_error.py @@ -11,7 +11,7 @@ if not hasattr(pyb, "Accel"): pyb.Accel() # get I2C bus -i2c = I2C(1, I2C.MASTER, dma=True) +i2c = I2C(1, I2C.CONTROLLER, dma=True) # test polling mem_read pyb.disable_irq() diff --git a/tests/pyb/pyb_f405.py b/tests/pyb/pyb_f405.py index 243381056e..d7431a11a1 100644 --- a/tests/pyb/pyb_f405.py +++ b/tests/pyb/pyb_f405.py @@ -10,7 +10,7 @@ print(pyb.freq()) print(type(pyb.rng())) # test HAL error specific to F405 -i2c = pyb.I2C(2, pyb.I2C.MASTER) +i2c = pyb.I2C(2, pyb.I2C.CONTROLLER) try: i2c.recv(1, 1) except OSError as e: diff --git a/tests/pyb/spi.py b/tests/pyb/spi.py index 73d39e7245..7df6aeb457 100644 --- a/tests/pyb/spi.py +++ b/tests/pyb/spi.py @@ -11,12 +11,14 @@ for bus in (-1, 0, 1, 2): spi = SPI(1) print(spi) -spi = SPI(1, SPI.MASTER) -spi = SPI(1, SPI.MASTER, baudrate=500000) -spi = SPI(1, SPI.MASTER, 500000, polarity=1, phase=0, bits=8, firstbit=SPI.MSB, ti=False, crc=None) -print(str(spi)[:28], str(spi)[49:]) # don't print baudrate/prescaler +spi = SPI(1, SPI.CONTROLLER) +spi = SPI(1, SPI.CONTROLLER, baudrate=500000) +spi = SPI( + 1, SPI.CONTROLLER, 500000, polarity=1, phase=0, bits=8, firstbit=SPI.MSB, ti=False, crc=None +) +print(str(spi)[:32], str(spi)[53:]) # don't print baudrate/prescaler -spi.init(SPI.SLAVE, phase=1) +spi.init(SPI.PERIPHERAL, phase=1) print(spi) try: # need to flush input before we get an error (error is what we want to test) @@ -25,7 +27,7 @@ try: except OSError: print("OSError") -spi.init(SPI.MASTER) +spi.init(SPI.CONTROLLER) spi.send(1, timeout=100) print(spi.recv(1, timeout=100)) print(spi.send_recv(1, timeout=100)) diff --git a/tests/pyb/spi.py.exp b/tests/pyb/spi.py.exp index 473c173a58..661bace900 100644 --- a/tests/pyb/spi.py.exp +++ b/tests/pyb/spi.py.exp @@ -3,8 +3,8 @@ ValueError 0 SPI 1 SPI 2 SPI(1) -SPI(1, SPI.MASTER, baudrate= , polarity=1, phase=0, bits=8) -SPI(1, SPI.SLAVE, polarity=1, phase=1, bits=8) +SPI(1, SPI.CONTROLLER, baudrate= , polarity=1, phase=0, bits=8) +SPI(1, SPI.PERIPHERAL, polarity=1, phase=1, bits=8) OSError b'\xff' b'\xff' From d3f6ce7dc31542d0962e3ca4c753f47a4ec4f818 Mon Sep 17 00:00:00 2001 From: David P Date: Sat, 12 Jun 2021 14:54:47 +1000 Subject: [PATCH 101/264] nrf/modules: Replace master/slave with controller/peripheral in SPI. Also remove mistaken usage of MASTER/SLAVE constants in comments. --- ports/nrf/modules/machine/spi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ports/nrf/modules/machine/spi.c b/ports/nrf/modules/machine/spi.c index 2510c27502..880d946a29 100644 --- a/ports/nrf/modules/machine/spi.c +++ b/ports/nrf/modules/machine/spi.c @@ -46,18 +46,18 @@ #endif /// \moduleref machine -/// \class SPI - a master-driven serial protocol +/// \class SPI - a controller-driven serial protocol /// -/// SPI is a serial protocol that is driven by a master. At the physical level +/// SPI is a serial protocol that is driven by a controller. At the physical level /// there are 3 lines: SCK, MOSI, MISO. /// /// See usage model of I2C; SPI is very similar. Main difference is /// parameters to init the SPI bus: /// /// from machine import SPI -/// spi = SPI(1, SPI.MASTER, baudrate=600000, polarity=1, phase=0, crc=0x7) +/// spi = SPI(1, baudrate=600000, polarity=1, phase=0, crc=0x7) /// -/// Only required parameter is mode, SPI.MASTER or SPI.SLAVE. Polarity can be +/// Polarity can be /// 0 or 1, and is the level the idle clock line sits at. Phase can be 0 or 1 /// to sample data on the first or second clock edge respectively. Crc can be /// None for no CRC, or a polynomial specifier. From 1f9243f8d41885dd5ddf5a5c8f45af15e50ed100 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 18 Jul 2021 12:11:10 +1000 Subject: [PATCH 102/264] esp32/machine_dac: Add MICROPY_PY_MACHINE_DAC option, enable by default. Signed-off-by: Damien George --- ports/esp32/machine_dac.c | 12 +++++++----- ports/esp32/modmachine.c | 2 ++ ports/esp32/mpconfigport.h | 3 +++ 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/ports/esp32/machine_dac.c b/ports/esp32/machine_dac.c index 146ef60aa8..35826d4a99 100644 --- a/ports/esp32/machine_dac.c +++ b/ports/esp32/machine_dac.c @@ -27,15 +27,15 @@ #include -#include "esp_log.h" - -#include "driver/gpio.h" -#include "driver/dac.h" - #include "py/runtime.h" #include "py/mphal.h" #include "modmachine.h" +#if MICROPY_PY_MACHINE_DAC + +#include "driver/gpio.h" +#include "driver/dac.h" + typedef struct _mdac_obj_t { mp_obj_base_t base; gpio_num_t gpio_id; @@ -111,3 +111,5 @@ const mp_obj_type_t machine_dac_type = { .make_new = mdac_make_new, .locals_dict = (mp_obj_t)&mdac_locals_dict, }; + +#endif // MICROPY_PY_MACHINE_DAC diff --git a/ports/esp32/modmachine.c b/ports/esp32/modmachine.c index a4e0724cee..5a7658dbbb 100644 --- a/ports/esp32/modmachine.c +++ b/ports/esp32/modmachine.c @@ -277,7 +277,9 @@ 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) }, + #if MICROPY_PY_MACHINE_DAC { MP_ROM_QSTR(MP_QSTR_DAC), MP_ROM_PTR(&machine_dac_type) }, + #endif { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&machine_hw_i2c_type) }, { MP_ROM_QSTR(MP_QSTR_SoftI2C), MP_ROM_PTR(&mp_machine_soft_i2c_type) }, { MP_ROM_QSTR(MP_QSTR_I2S), MP_ROM_PTR(&machine_i2s_type) }, diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index a06c910470..aadf4620de 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -153,6 +153,9 @@ #define MICROPY_PY_MACHINE_SPI (1) #define MICROPY_PY_MACHINE_SPI_MSB (0) #define MICROPY_PY_MACHINE_SPI_LSB (1) +#ifndef MICROPY_PY_MACHINE_DAC +#define MICROPY_PY_MACHINE_DAC (1) +#endif #ifndef MICROPY_HW_ENABLE_SDCARD #define MICROPY_HW_ENABLE_SDCARD (1) #endif From 0fc0ccabec9b919b7984f418f49537acec5f39c9 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 18 Jul 2021 12:11:15 +1000 Subject: [PATCH 103/264] esp32/machine_i2s: Add MICROPY_PY_MACHINE_I2S option, enable by default. Signed-off-by: Damien George --- ports/esp32/machine_i2s.c | 4 ++++ ports/esp32/main.c | 2 ++ ports/esp32/modmachine.c | 2 ++ ports/esp32/mpconfigport.h | 3 +++ 4 files changed, 11 insertions(+) diff --git a/ports/esp32/machine_i2s.c b/ports/esp32/machine_i2s.c index b0d02e74f6..099796cf43 100644 --- a/ports/esp32/machine_i2s.c +++ b/ports/esp32/machine_i2s.c @@ -38,6 +38,8 @@ #include "modmachine.h" #include "mphalport.h" +#if MICROPY_PY_MACHINE_I2S + #include "driver/i2s.h" #include "soc/i2s_reg.h" #include "freertos/FreeRTOS.h" @@ -807,3 +809,5 @@ const mp_obj_type_t machine_i2s_type = { .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/esp32/main.c b/ports/esp32/main.c index c1b8e8a31d..66068ccffa 100644 --- a/ports/esp32/main.c +++ b/ports/esp32/main.c @@ -138,7 +138,9 @@ soft_reset: // initialise peripherals machine_pins_init(); + #if MICROPY_PY_MACHINE_I2S machine_i2s_init0(); + #endif // run boot-up scripts pyexec_frozen_module("_boot.py"); diff --git a/ports/esp32/modmachine.c b/ports/esp32/modmachine.c index 5a7658dbbb..aa009944bd 100644 --- a/ports/esp32/modmachine.c +++ b/ports/esp32/modmachine.c @@ -282,7 +282,9 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { #endif { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&machine_hw_i2c_type) }, { MP_ROM_QSTR(MP_QSTR_SoftI2C), MP_ROM_PTR(&mp_machine_soft_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_PWM), MP_ROM_PTR(&machine_pwm_type) }, { MP_ROM_QSTR(MP_QSTR_RTC), MP_ROM_PTR(&machine_rtc_type) }, { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&machine_hw_spi_type) }, diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index aadf4620de..e4375bd0be 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -156,6 +156,9 @@ #ifndef MICROPY_PY_MACHINE_DAC #define MICROPY_PY_MACHINE_DAC (1) #endif +#ifndef MICROPY_PY_MACHINE_I2S +#define MICROPY_PY_MACHINE_I2S (1) +#endif #ifndef MICROPY_HW_ENABLE_SDCARD #define MICROPY_HW_ENABLE_SDCARD (1) #endif From 59dbbe9be7f91f53f60803559db88cfb75cf1c2a Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 18 Jul 2021 12:11:18 +1000 Subject: [PATCH 104/264] esp32: Fix use of mp_int_t, size_t and uintptr_t. Signed-off-by: Damien George --- ports/esp32/machine_i2s.c | 14 +++++++------- ports/esp32/machine_rtc.c | 4 ++-- ports/esp32/machine_timer.c | 2 +- ports/esp32/machine_uart.c | 2 +- ports/esp32/modesp32.c | 2 +- ports/esp32/mphalport.c | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/ports/esp32/machine_i2s.c b/ports/esp32/machine_i2s.c index 099796cf43..c650a33bef 100644 --- a/ports/esp32/machine_i2s.c +++ b/ports/esp32/machine_i2s.c @@ -255,8 +255,8 @@ STATIC uint32_t fill_appbuf_from_dma(machine_i2s_obj_t *self, mp_buffer_info_t * uint8_t appbuf_sample_size_in_bytes = (self->bits / 8) * (self->format == STEREO ? 2: 1); uint32_t num_bytes_needed_from_dma = appbuf->len * (I2S_RX_FRAME_SIZE_IN_BYTES / appbuf_sample_size_in_bytes); while (num_bytes_needed_from_dma) { - uint32_t num_bytes_requested_from_dma = MIN(sizeof(self->transform_buffer), num_bytes_needed_from_dma); - uint32_t num_bytes_received_from_dma = 0; + size_t num_bytes_requested_from_dma = MIN(sizeof(self->transform_buffer), num_bytes_needed_from_dma); + size_t num_bytes_received_from_dma = 0; TickType_t delay; if (self->io_mode == UASYNCIO) { @@ -312,12 +312,12 @@ STATIC uint32_t fill_appbuf_from_dma(machine_i2s_obj_t *self, mp_buffer_info_t * return a_index; } -STATIC uint32_t copy_appbuf_to_dma(machine_i2s_obj_t *self, mp_buffer_info_t *appbuf) { +STATIC size_t copy_appbuf_to_dma(machine_i2s_obj_t *self, mp_buffer_info_t *appbuf) { if ((self->bits == I2S_BITS_PER_SAMPLE_32BIT) && (self->format == STEREO)) { swap_32_bit_stereo_channels(appbuf); } - uint32_t num_bytes_written = 0; + size_t num_bytes_written = 0; TickType_t delay; if (self->io_mode == UASYNCIO) { @@ -518,7 +518,7 @@ STATIC mp_obj_t machine_i2s_make_new(const mp_obj_type_t *type, size_t n_pos_arg return MP_OBJ_FROM_PTR(self); } -STATIC mp_obj_t machine_i2s_obj_init(mp_uint_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { +STATIC mp_obj_t machine_i2s_obj_init(size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { machine_i2s_obj_t *self = pos_args[0]; machine_i2s_deinit(self); machine_i2s_init_helper(self, n_pos_args - 1, pos_args + 1, kw_args); @@ -731,12 +731,12 @@ STATIC mp_uint_t machine_i2s_stream_write(mp_obj_t self_in, const void *buf_in, mp_buffer_info_t appbuf; appbuf.buf = (void *)buf_in; appbuf.len = size; - uint32_t num_bytes_written = copy_appbuf_to_dma(self, &appbuf); + size_t num_bytes_written = copy_appbuf_to_dma(self, &appbuf); return num_bytes_written; } } -STATIC mp_uint_t machine_i2s_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { +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; mp_uint_t flags = arg; diff --git a/ports/esp32/machine_rtc.c b/ports/esp32/machine_rtc.c index 52227c93b8..72d7b5c828 100644 --- a/ports/esp32/machine_rtc.c +++ b/ports/esp32/machine_rtc.c @@ -121,7 +121,7 @@ STATIC mp_obj_t machine_rtc_datetime_helper(mp_uint_t n_args, const mp_obj_t *ar return mp_const_none; } } -STATIC mp_obj_t machine_rtc_datetime(mp_uint_t n_args, const mp_obj_t *args) { +STATIC mp_obj_t machine_rtc_datetime(size_t n_args, const mp_obj_t *args) { return machine_rtc_datetime_helper(n_args, args); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_rtc_datetime_obj, 1, 2, machine_rtc_datetime); @@ -142,7 +142,7 @@ STATIC mp_obj_t machine_rtc_init(mp_obj_t self_in, mp_obj_t date) { STATIC MP_DEFINE_CONST_FUN_OBJ_2(machine_rtc_init_obj, machine_rtc_init); #if MICROPY_HW_RTC_USER_MEM_MAX > 0 -STATIC mp_obj_t machine_rtc_memory(mp_uint_t n_args, const mp_obj_t *args) { +STATIC mp_obj_t machine_rtc_memory(size_t n_args, const mp_obj_t *args) { if (n_args == 1) { // read RTC memory uint8_t rtcram[MICROPY_HW_RTC_USER_MEM_MAX]; diff --git a/ports/esp32/machine_timer.c b/ports/esp32/machine_timer.c index 696127af77..3b14581498 100644 --- a/ports/esp32/machine_timer.c +++ b/ports/esp32/machine_timer.c @@ -229,7 +229,7 @@ STATIC mp_obj_t machine_timer_deinit(mp_obj_t self_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_timer_deinit_obj, machine_timer_deinit); -STATIC mp_obj_t machine_timer_init(mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { +STATIC mp_obj_t machine_timer_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { return machine_timer_init_helper(args[0], n_args - 1, args + 1, kw_args); } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_timer_init_obj, 1, machine_timer_init); diff --git a/ports/esp32/machine_uart.c b/ports/esp32/machine_uart.c index e256b9be43..2953ac171c 100644 --- a/ports/esp32/machine_uart.c +++ b/ports/esp32/machine_uart.c @@ -443,7 +443,7 @@ STATIC mp_uint_t machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_uin return bytes_written; } -STATIC mp_uint_t machine_uart_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { +STATIC mp_uint_t machine_uart_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) { machine_uart_obj_t *self = self_in; mp_uint_t ret; if (request == MP_STREAM_POLL) { diff --git a/ports/esp32/modesp32.c b/ports/esp32/modesp32.c index 042c8b8566..7faff32046 100644 --- a/ports/esp32/modesp32.c +++ b/ports/esp32/modesp32.c @@ -109,7 +109,7 @@ STATIC mp_obj_t esp32_wake_on_ext1(size_t n_args, const mp_obj_t *pos_args, mp_m // Check that all pins are allowed if (args[ARG_pins].u_obj != mp_const_none) { - mp_uint_t len = 0; + size_t len = 0; mp_obj_t *elem; mp_obj_get_array(args[ARG_pins].u_obj, &len, &elem); ext1_pins = 0; diff --git a/ports/esp32/mphalport.c b/ports/esp32/mphalport.c index de7fdfcec3..0f94e129bc 100644 --- a/ports/esp32/mphalport.c +++ b/ports/esp32/mphalport.c @@ -108,7 +108,7 @@ int mp_hal_stdin_rx_chr(void) { } } -void mp_hal_stdout_tx_strn(const char *str, uint32_t len) { +void mp_hal_stdout_tx_strn(const char *str, size_t len) { // Only release the GIL if many characters are being sent bool release_gil = len > 20; if (release_gil) { From 6823514845c13339efb0e3ff716d2cfa72297d72 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 18 Jul 2021 12:11:21 +1000 Subject: [PATCH 105/264] esp32: Add initial support for ESP32C3 SoCs. Supported features for this SoC are: - UART REPL, filesystem - Pin, ADC, PWM, SoftI2C, SoftSPI, Timer, RTC - OneWire, DHT, NeoPixel - RMT - WiFi, Bluetooth Signed-off-by: Damien George --- ports/esp32/espneopixel.c | 13 ++++--- ports/esp32/gccollect.c | 19 +++++++++- ports/esp32/machine_adc.c | 6 ++++ ports/esp32/machine_hw_spi.c | 4 +++ ports/esp32/machine_pin.c | 62 +++++++++++++++++++++++++++++++++ ports/esp32/main.c | 9 ++++- ports/esp32/main/CMakeLists.txt | 6 ++++ ports/esp32/modesp32.c | 3 +- ports/esp32/modmachine.c | 11 +++++- ports/esp32/mpconfigport.h | 5 +++ ports/esp32/mphalport.c | 2 ++ ports/esp32/mphalport.h | 4 +++ ports/esp32/uart.c | 2 +- 13 files changed, 136 insertions(+), 10 deletions(-) diff --git a/ports/esp32/espneopixel.c b/ports/esp32/espneopixel.c index a5937b9c6b..0b9308e2c0 100644 --- a/ports/esp32/espneopixel.c +++ b/ports/esp32/espneopixel.c @@ -13,14 +13,17 @@ void IRAM_ATTR esp_neopixel_write(uint8_t pin, uint8_t *pixels, uint32_t numByte uint8_t *p, *end, pix, mask; uint32_t t, time0, time1, period, c, startTime, pinMask, gpio_reg_set, gpio_reg_clear; - if (pin < 32) { - pinMask = 1 << pin; - gpio_reg_set = GPIO_OUT_W1TS_REG; - gpio_reg_clear = GPIO_OUT_W1TC_REG; - } else { + #if !CONFIG_IDF_TARGET_ESP32C3 + if (pin >= 32) { pinMask = 1 << (pin - 32); gpio_reg_set = GPIO_OUT1_W1TS_REG; gpio_reg_clear = GPIO_OUT1_W1TC_REG; + } else + #endif + { + pinMask = 1 << pin; + gpio_reg_set = GPIO_OUT_W1TS_REG; + gpio_reg_clear = GPIO_OUT_W1TC_REG; } p = pixels; end = p + numBytes; diff --git a/ports/esp32/gccollect.c b/ports/esp32/gccollect.c index 7d5da57e76..403a3c7dfa 100644 --- a/ports/esp32/gccollect.c +++ b/ports/esp32/gccollect.c @@ -35,8 +35,10 @@ #include "py/mpthread.h" #include "gccollect.h" #include "soc/cpu.h" -#include "xtensa/hal.h" +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 + +#include "xtensa/hal.h" static void gc_collect_inner(int level) { if (level < XCHAL_NUM_AREGS / 8) { @@ -64,3 +66,18 @@ void gc_collect(void) { gc_collect_inner(0); gc_collect_end(); } + +#elif CONFIG_IDF_TARGET_ESP32C3 + +#include "shared/runtime/gchelper.h" + +void gc_collect(void) { + gc_collect_start(); + gc_helper_collect_regs_and_stack(); + #if MICROPY_PY_THREAD + mp_thread_gc_others(); + #endif + gc_collect_end(); +} + +#endif diff --git a/ports/esp32/machine_adc.c b/ports/esp32/machine_adc.c index 739d47da50..5ac05d56b5 100644 --- a/ports/esp32/machine_adc.c +++ b/ports/esp32/machine_adc.c @@ -52,6 +52,12 @@ STATIC const madc_obj_t madc_obj[] = { {{&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}, + #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}, #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}, diff --git a/ports/esp32/machine_hw_spi.c b/ports/esp32/machine_hw_spi.c index 5b59f2431c..76b1896bba 100644 --- a/ports/esp32/machine_hw_spi.c +++ b/ports/esp32/machine_hw_spi.c @@ -53,6 +53,10 @@ #define MP_HW_SPI_MAX_XFER_BYTES (4092) #define MP_HW_SPI_MAX_XFER_BITS (MP_HW_SPI_MAX_XFER_BYTES * 8) // Has to be an even multiple of 8 +#if CONFIG_IDF_TARGET_ESP32C3 +#define HSPI_HOST SPI2_HOST +#endif + typedef struct _machine_hw_spi_default_pins_t { int8_t sck; int8_t mosi; diff --git a/ports/esp32/machine_pin.c b/ports/esp32/machine_pin.c index bd623d0413..477623796a 100644 --- a/ports/esp32/machine_pin.c +++ b/ports/esp32/machine_pin.c @@ -40,6 +40,10 @@ #include "machine_rtc.h" #include "modesp32.h" +#if CONFIG_IDF_TARGET_ESP32C3 +#include "hal/gpio_ll.h" +#endif + // Used to implement a range of pull capabilities #define GPIO_PULL_DOWN (1) #define GPIO_PULL_UP (2) @@ -99,6 +103,31 @@ STATIC const machine_pin_obj_t machine_pin_obj[] = { {{&machine_pin_type}, GPIO_NUM_38}, {{&machine_pin_type}, GPIO_NUM_39}, + #elif CONFIG_IDF_TARGET_ESP32C3 + + {{&machine_pin_type}, GPIO_NUM_0}, + {{&machine_pin_type}, GPIO_NUM_1}, + {{&machine_pin_type}, GPIO_NUM_2}, + {{&machine_pin_type}, GPIO_NUM_3}, + {{&machine_pin_type}, GPIO_NUM_4}, + {{&machine_pin_type}, GPIO_NUM_5}, + {{&machine_pin_type}, GPIO_NUM_6}, + {{&machine_pin_type}, GPIO_NUM_7}, + {{&machine_pin_type}, GPIO_NUM_8}, + {{&machine_pin_type}, GPIO_NUM_9}, + {{&machine_pin_type}, GPIO_NUM_10}, + {{&machine_pin_type}, GPIO_NUM_11}, + {{&machine_pin_type}, GPIO_NUM_12}, + {{&machine_pin_type}, GPIO_NUM_13}, + {{&machine_pin_type}, GPIO_NUM_14}, + {{&machine_pin_type}, GPIO_NUM_15}, + {{&machine_pin_type}, GPIO_NUM_16}, + {{&machine_pin_type}, GPIO_NUM_17}, + {{&machine_pin_type}, GPIO_NUM_18}, + {{&machine_pin_type}, GPIO_NUM_19}, + {{&machine_pin_type}, GPIO_NUM_20}, + {{&machine_pin_type}, GPIO_NUM_21}, + #elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 {{&machine_pin_type}, GPIO_NUM_0}, @@ -213,10 +242,18 @@ STATIC mp_obj_t machine_pin_obj_init_helper(const machine_pin_obj_t *self, size_ // reset the pin to digital if this is a mode-setting init (grab it back from ADC) if (args[ARG_mode].u_obj != mp_const_none) { if (rtc_gpio_is_valid_gpio(self->id)) { + #if !CONFIG_IDF_TARGET_ESP32C3 rtc_gpio_deinit(self->id); + #endif } } + #if CONFIG_IDF_TARGET_ESP32C3 + if (self->id == 18 || self->id == 19) { + CLEAR_PERI_REG_MASK(USB_DEVICE_CONF0_REG, USB_DEVICE_USB_PAD_ENABLE); + } + #endif + // configure the pin for gpio gpio_pad_select_gpio(self->id); @@ -494,6 +531,31 @@ STATIC const machine_pin_irq_obj_t machine_pin_irq_object[] = { {{&machine_pin_irq_type}, GPIO_NUM_38}, {{&machine_pin_irq_type}, GPIO_NUM_39}, + #elif CONFIG_IDF_TARGET_ESP32C3 + + {{&machine_pin_irq_type}, GPIO_NUM_0}, + {{&machine_pin_irq_type}, GPIO_NUM_1}, + {{&machine_pin_irq_type}, GPIO_NUM_2}, + {{&machine_pin_irq_type}, GPIO_NUM_3}, + {{&machine_pin_irq_type}, GPIO_NUM_4}, + {{&machine_pin_irq_type}, GPIO_NUM_5}, + {{&machine_pin_irq_type}, GPIO_NUM_6}, + {{&machine_pin_irq_type}, GPIO_NUM_7}, + {{&machine_pin_irq_type}, GPIO_NUM_8}, + {{&machine_pin_irq_type}, GPIO_NUM_9}, + {{&machine_pin_irq_type}, GPIO_NUM_10}, + {{&machine_pin_irq_type}, GPIO_NUM_11}, + {{&machine_pin_irq_type}, GPIO_NUM_12}, + {{&machine_pin_irq_type}, GPIO_NUM_13}, + {{&machine_pin_irq_type}, GPIO_NUM_14}, + {{&machine_pin_irq_type}, GPIO_NUM_15}, + {{&machine_pin_irq_type}, GPIO_NUM_16}, + {{&machine_pin_irq_type}, GPIO_NUM_17}, + {{&machine_pin_irq_type}, GPIO_NUM_18}, + {{&machine_pin_irq_type}, GPIO_NUM_19}, + {{&machine_pin_irq_type}, GPIO_NUM_20}, + {{&machine_pin_irq_type}, GPIO_NUM_21}, + #elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 {{&machine_pin_irq_type}, GPIO_NUM_0}, diff --git a/ports/esp32/main.c b/ports/esp32/main.c index 66068ccffa..0650c8d71d 100644 --- a/ports/esp32/main.c +++ b/ports/esp32/main.c @@ -70,6 +70,13 @@ #define MP_TASK_PRIORITY (ESP_TASK_PRIO_MIN + 1) #define MP_TASK_STACK_SIZE (16 * 1024) +// Set the margin for detecting stack overflow, depending on the CPU architecture. +#if CONFIG_IDF_TARGET_ESP32C3 +#define MP_TASK_STACK_LIMIT_MARGIN (2048) +#else +#define MP_TASK_STACK_LIMIT_MARGIN (1024) +#endif + int vprintf_null(const char *format, va_list ap) { // do nothing: this is used as a log target during raw repl mode return 0; @@ -127,7 +134,7 @@ void mp_task(void *pvParameter) { soft_reset: // initialise the stack pointer for the main thread mp_stack_set_top((void *)sp); - mp_stack_set_limit(MP_TASK_STACK_SIZE - 1024); + mp_stack_set_limit(MP_TASK_STACK_SIZE - MP_TASK_STACK_LIMIT_MARGIN); gc_init(mp_task_heap, mp_task_heap + mp_task_heap_size); mp_init(); mp_obj_list_init(mp_sys_path, 0); diff --git a/ports/esp32/main/CMakeLists.txt b/ports/esp32/main/CMakeLists.txt index f2b3dd4a4f..1ca30b0cb9 100644 --- a/ports/esp32/main/CMakeLists.txt +++ b/ports/esp32/main/CMakeLists.txt @@ -34,6 +34,9 @@ set(MICROPY_SOURCE_LIB ${MICROPY_DIR}/lib/oofatfs/ff.c ${MICROPY_DIR}/lib/oofatfs/ffunicode.c ) +if(IDF_TARGET STREQUAL "esp32c3") + list(APPEND MICROPY_SOURCE_LIB ${MICROPY_DIR}/shared/runtime/gchelper_generic.c) +endif() set(MICROPY_SOURCE_DRIVERS ${MICROPY_DIR}/drivers/bus/softspi.c @@ -133,6 +136,9 @@ endif() if(IDF_TARGET STREQUAL "esp32") list(APPEND IDF_COMPONENTS esp32) +elseif(IDF_TARGET STREQUAL "esp32c3") + list(APPEND IDF_COMPONENTS esp32c3) + list(APPEND IDF_COMPONENTS riscv) elseif(IDF_TARGET STREQUAL "esp32s2") list(APPEND IDF_COMPONENTS esp32s2) list(APPEND IDF_COMPONENTS tinyusb) diff --git a/ports/esp32/modesp32.c b/ports/esp32/modesp32.c index 7faff32046..75e5b3ed8b 100644 --- a/ports/esp32/modesp32.c +++ b/ports/esp32/modesp32.c @@ -30,7 +30,6 @@ #include #include #include "soc/rtc_cntl_reg.h" -#include "soc/sens_reg.h" #include "driver/gpio.h" #include "driver/adc.h" #include "esp_heap_caps.h" @@ -134,6 +133,8 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(esp32_wake_on_ext1_obj, 0, esp32_wake_on_ext1) #if CONFIG_IDF_TARGET_ESP32 +#include "soc/sens_reg.h" + STATIC mp_obj_t esp32_raw_temperature(void) { SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR, 3, SENS_FORCE_XPD_SAR_S); SET_PERI_REG_BITS(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_CLK_DIV, 10, SENS_TSENS_CLK_DIV_S); diff --git a/ports/esp32/modmachine.c b/ports/esp32/modmachine.c index aa009944bd..50c0cff990 100644 --- a/ports/esp32/modmachine.c +++ b/ports/esp32/modmachine.c @@ -34,7 +34,6 @@ #include "freertos/task.h" #include "esp_sleep.h" #include "esp_pm.h" -#include "driver/touch_pad.h" #if CONFIG_IDF_TARGET_ESP32 #include "esp32/rom/rtc.h" @@ -70,6 +69,10 @@ typedef enum { STATIC bool is_soft_reset = 0; +#if CONFIG_IDF_TARGET_ESP32C3 +int esp_clk_cpu_freq(void); +#endif + STATIC mp_obj_t machine_freq(size_t n_args, const mp_obj_t *args) { if (n_args == 0) { // get @@ -82,6 +85,8 @@ STATIC mp_obj_t machine_freq(size_t n_args, const mp_obj_t *args) { } #if CONFIG_IDF_TARGET_ESP32 esp_pm_config_esp32_t pm; + #elif CONFIG_IDF_TARGET_ESP32C3 + esp_pm_config_esp32c3_t pm; #elif CONFIG_IDF_TARGET_ESP32S2 esp_pm_config_esp32s2_t pm; #endif @@ -117,6 +122,8 @@ STATIC mp_obj_t machine_sleep_helper(wake_type_t wake_type, size_t n_args, const esp_sleep_enable_timer_wakeup(((uint64_t)expiry) * 1000); } + #if !CONFIG_IDF_TARGET_ESP32C3 + if (machine_rtc_config.ext0_pin != -1 && (machine_rtc_config.ext0_wake_types & wake_type)) { esp_sleep_enable_ext0_wakeup(machine_rtc_config.ext0_pin, machine_rtc_config.ext0_level ? 1 : 0); } @@ -133,6 +140,8 @@ STATIC mp_obj_t machine_sleep_helper(wake_type_t wake_type, size_t n_args, const } } + #endif + switch (wake_type) { case MACHINE_WAKE_SLEEP: esp_light_sleep_start(); diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index e4375bd0be..46d8a38af1 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -11,13 +11,18 @@ // object representation and NLR handling #define MICROPY_OBJ_REPR (MICROPY_OBJ_REPR_A) #define MICROPY_NLR_SETJMP (1) +#if CONFIG_IDF_TARGET_ESP32C3 +#define MICROPY_GCREGS_SETJMP (1) +#endif // memory allocation policies #define MICROPY_ALLOC_PATH_MAX (128) // emitters #define MICROPY_PERSISTENT_CODE_LOAD (1) +#if !CONFIG_IDF_TARGET_ESP32C3 #define MICROPY_EMIT_XTENSAWIN (1) +#endif // compiler configuration #define MICROPY_COMP_MODULE_CONST (1) diff --git a/ports/esp32/mphalport.c b/ports/esp32/mphalport.c index 0f94e129bc..e795b5c991 100644 --- a/ports/esp32/mphalport.c +++ b/ports/esp32/mphalport.c @@ -35,6 +35,8 @@ #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 diff --git a/ports/esp32/mphalport.h b/ports/esp32/mphalport.h index 2ba6f96b6a..2dad8fa924 100644 --- a/ports/esp32/mphalport.h +++ b/ports/esp32/mphalport.h @@ -51,7 +51,11 @@ void check_esp_err(esp_err_t code); uint32_t mp_hal_ticks_us(void); __attribute__((always_inline)) static inline uint32_t mp_hal_ticks_cpu(void) { uint32_t ccount; + #if CONFIG_IDF_TARGET_ESP32C3 + __asm__ __volatile__ ("csrr %0, 0x7E2" : "=r" (ccount)); // Machine Performance Counter Value + #else __asm__ __volatile__ ("rsr %0,ccount" : "=a" (ccount)); + #endif return ccount; } diff --git a/ports/esp32/uart.c b/ports/esp32/uart.c index c4fe41eaff..480b364a59 100644 --- a/ports/esp32/uart.c +++ b/ports/esp32/uart.c @@ -51,7 +51,7 @@ STATIC void IRAM_ATTR uart_irq_handler(void *arg) { while (uart->status.rxfifo_cnt) { #if CONFIG_IDF_TARGET_ESP32 uint8_t c = uart->fifo.rw_byte; - #elif CONFIG_IDF_TARGET_ESP32S2 + #elif CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 uint8_t c = READ_PERI_REG(UART_FIFO_AHB_REG(0)); // UART0 #endif if (c == mp_interrupt_char) { From c77225ae5b043af67338ec8cc6d52f6fc3448150 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 18 Jul 2021 12:11:23 +1000 Subject: [PATCH 106/264] esp32/boards/GENERIC_C3: Add generic C3-based board. Signed-off-by: Damien George --- ports/esp32/boards/GENERIC_C3/mpconfigboard.cmake | 10 ++++++++++ ports/esp32/boards/GENERIC_C3/mpconfigboard.h | 8 ++++++++ 2 files changed, 18 insertions(+) create mode 100644 ports/esp32/boards/GENERIC_C3/mpconfigboard.cmake create mode 100644 ports/esp32/boards/GENERIC_C3/mpconfigboard.h diff --git a/ports/esp32/boards/GENERIC_C3/mpconfigboard.cmake b/ports/esp32/boards/GENERIC_C3/mpconfigboard.cmake new file mode 100644 index 0000000000..4f31884231 --- /dev/null +++ b/ports/esp32/boards/GENERIC_C3/mpconfigboard.cmake @@ -0,0 +1,10 @@ +set(IDF_TARGET esp32c3) + +set(SDKCONFIG_DEFAULTS + boards/sdkconfig.base + boards/sdkconfig.ble +) + +if(NOT MICROPY_FROZEN_MANIFEST) + set(MICROPY_FROZEN_MANIFEST ${MICROPY_PORT_DIR}/boards/manifest.py) +endif() diff --git a/ports/esp32/boards/GENERIC_C3/mpconfigboard.h b/ports/esp32/boards/GENERIC_C3/mpconfigboard.h new file mode 100644 index 0000000000..d403e70e46 --- /dev/null +++ b/ports/esp32/boards/GENERIC_C3/mpconfigboard.h @@ -0,0 +1,8 @@ +// This configuration is for a generic ESP32C3 board with 4MiB (or more) of flash. + +#define MICROPY_HW_BOARD_NAME "ESP32C3 module" +#define MICROPY_HW_MCU_NAME "ESP32C3" + +#define MICROPY_HW_ENABLE_SDCARD (0) +#define MICROPY_PY_MACHINE_DAC (0) +#define MICROPY_PY_MACHINE_I2S (0) From 9f71a11d3fd18f811301d36fd37e2c2bb0c9d406 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 18 Jul 2021 22:57:19 +1000 Subject: [PATCH 107/264] tools/ci.sh: Build GENERIC_C3 board as part of esp32 CI. Signed-off-by: Damien George --- tools/ci.sh | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tools/ci.sh b/tools/ci.sh index 4ff4ba35ba..a6e38b70ea 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -86,12 +86,19 @@ function ci_esp32_setup_helper { git clone https://github.com/espressif/esp-idf.git git -C esp-idf checkout $1 git -C esp-idf submodule update --init \ - components/bt/controller/lib \ components/bt/host/nimble/nimble \ components/esp_wifi \ components/esptool_py/esptool \ components/lwip/lwip \ components/mbedtls/mbedtls + if [ -d esp-idf/components/bt/controller/esp32 ]; then + git -C esp-idf submodule update --init \ + components/bt/controller/lib_esp32 \ + components/bt/controller/lib_esp32c3_family + else + git -C esp-idf submodule update --init \ + components/bt/controller/lib + fi ./esp-idf/install.sh } @@ -100,7 +107,7 @@ function ci_esp32_idf402_setup { } function ci_esp32_idf43_setup { - ci_esp32_setup_helper v4.3-beta2 + ci_esp32_setup_helper v4.3 } function ci_esp32_build { @@ -110,6 +117,9 @@ function ci_esp32_build { make ${MAKEOPTS} -C ports/esp32 make ${MAKEOPTS} -C ports/esp32 clean make ${MAKEOPTS} -C ports/esp32 USER_C_MODULES=../../../examples/usercmodule/micropython.cmake FROZEN_MANIFEST=$(pwd)/ports/esp32/boards/manifest.py + if [ -d $IDF_PATH/components/esp32c3 ]; then + make ${MAKEOPTS} -C ports/esp32 BOARD=GENERIC_C3 + fi if [ -d $IDF_PATH/components/esp32s2 ]; then make ${MAKEOPTS} -C ports/esp32 BOARD=GENERIC_S2 fi From 8599f7a68d07624a6759f73a694d769b956ae79d Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Wed, 7 Jul 2021 23:47:20 +0200 Subject: [PATCH 108/264] rp2/machine_uart: Add hardware flow control support. --- ports/rp2/machine_uart.c | 59 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 4 deletions(-) diff --git a/ports/rp2/machine_uart.c b/ports/rp2/machine_uart.c index 2da3ab41f8..b26a162c2f 100644 --- a/ports/rp2/machine_uart.c +++ b/ports/rp2/machine_uart.c @@ -40,8 +40,12 @@ #define DEFAULT_UART_STOP (1) #define DEFAULT_UART0_TX (0) #define DEFAULT_UART0_RX (1) +#define DEFAULT_UART0_CTS (2) +#define DEFAULT_UART0_RTS (3) #define DEFAULT_UART1_TX (4) #define DEFAULT_UART1_RX (5) +#define DEFAULT_UART1_CTS (6) +#define DEFAULT_UART1_RTS (7) #define DEFAULT_BUFFER_SIZE (256) #define MIN_BUFFER_SIZE (32) #define MAX_BUFFER_SIZE (32766) @@ -49,11 +53,16 @@ #define IS_VALID_PERIPH(uart, pin) (((((pin) + 4) & 8) >> 3) == (uart)) #define IS_VALID_TX(uart, pin) (((pin) & 3) == 0 && IS_VALID_PERIPH(uart, pin)) #define IS_VALID_RX(uart, pin) (((pin) & 3) == 1 && IS_VALID_PERIPH(uart, pin)) +#define IS_VALID_CTS(uart, pin) (((pin) & 3) == 2 && IS_VALID_PERIPH(uart, pin)) +#define IS_VALID_RTS(uart, pin) (((pin) & 3) == 3 && IS_VALID_PERIPH(uart, pin)) #define UART_INVERT_TX (1) #define UART_INVERT_RX (2) #define UART_INVERT_MASK (UART_INVERT_TX | UART_INVERT_RX) +#define UART_HWCONTROL_CTS (1) +#define UART_HWCONTROL_RTS (2) + typedef struct _machine_uart_obj_t { mp_obj_base_t base; uart_inst_t *const uart; @@ -64,9 +73,12 @@ typedef struct _machine_uart_obj_t { uint8_t stop; uint8_t tx; uint8_t rx; + uint8_t cts; + uint8_t rts; uint16_t timeout; // timeout waiting for first char (in ms) uint16_t timeout_char; // timeout waiting between chars (in ms) uint8_t invert; + uint8_t flow; ringbuf_t read_buffer; bool read_lock; ringbuf_t write_buffer; @@ -75,9 +87,9 @@ typedef struct _machine_uart_obj_t { STATIC machine_uart_obj_t machine_uart_obj[] = { {{&machine_uart_type}, uart0, 0, 0, DEFAULT_UART_BITS, UART_PARITY_NONE, DEFAULT_UART_STOP, - DEFAULT_UART0_TX, DEFAULT_UART0_RX, 0, 0, 0, {NULL, 1, 0, 0}, 0, {NULL, 1, 0, 0}, 0}, + DEFAULT_UART0_TX, DEFAULT_UART0_RX, DEFAULT_UART0_CTS, DEFAULT_UART0_RTS, 0, 0, 0, 0, {NULL, 1, 0, 0}, 0, {NULL, 1, 0, 0}, 0}, {{&machine_uart_type}, uart1, 1, 0, DEFAULT_UART_BITS, UART_PARITY_NONE, DEFAULT_UART_STOP, - DEFAULT_UART1_TX, DEFAULT_UART1_RX, 0, 0, 0, {NULL, 1, 0, 0}, 0, {NULL, 1, 0, 0}, 0,}, + DEFAULT_UART1_TX, DEFAULT_UART1_RX, DEFAULT_UART1_CTS, DEFAULT_UART1_RTS, 0, 0, 0, 0, {NULL, 1, 0, 0}, 0, {NULL, 1, 0, 0}, 0}, }; STATIC const char *_parity_name[] = {"None", "0", "1"}; @@ -140,8 +152,8 @@ STATIC void machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_pri } 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_timeout, ARG_timeout_char, ARG_invert, ARG_txbuf, ARG_rxbuf}; + enum { ARG_id, 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} }, @@ -150,9 +162,12 @@ STATIC mp_obj_t machine_uart_make_new(const mp_obj_type_t *type, size_t n_args, { MP_QSTR_stop, MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_tx, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, { MP_QSTR_rx, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_cts, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_rts, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, { 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_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} }, }; @@ -212,6 +227,22 @@ STATIC mp_obj_t machine_uart_make_new(const mp_obj_type_t *type, size_t n_args, self->rx = rx; } + // Set CTS/RTS pins if configured. + if (args[ARG_cts].u_obj != mp_const_none) { + int cts = mp_hal_get_pin_obj(args[ARG_cts].u_obj); + if (!IS_VALID_CTS(self->uart_id, cts)) { + mp_raise_ValueError(MP_ERROR_TEXT("bad CTS pin")); + } + self->cts = cts; + } + if (args[ARG_rts].u_obj != mp_const_none) { + int rts = mp_hal_get_pin_obj(args[ARG_rts].u_obj); + if (!IS_VALID_RTS(self->uart_id, rts)) { + mp_raise_ValueError(MP_ERROR_TEXT("bad RTS pin")); + } + self->rts = rts; + } + // Set timeout if configured. if (args[ARG_timeout].u_int >= 0) { self->timeout = args[ARG_timeout].u_int; @@ -230,6 +261,14 @@ STATIC mp_obj_t machine_uart_make_new(const mp_obj_type_t *type, size_t n_args, self->invert = args[ARG_invert].u_int; } + // Set hardware flow control if configured. + if (args[ARG_flow].u_int >= 0) { + if (args[ARG_flow].u_int & ~(UART_HWCONTROL_CTS | UART_HWCONTROL_RTS)) { + mp_raise_ValueError(MP_ERROR_TEXT("bad hardware flow control mask")); + } + self->flow = args[ARG_flow].u_int; + } + self->read_lock = false; // Set the RX buffer size if configured. @@ -278,6 +317,15 @@ STATIC mp_obj_t machine_uart_make_new(const mp_obj_type_t *type, size_t n_args, gpio_set_outover(self->tx, GPIO_OVERRIDE_INVERT); } + // Set hardware flow control if configured. + if (self->flow & UART_HWCONTROL_CTS) { + gpio_set_function(self->cts, GPIO_FUNC_UART); + } + if (self->flow & UART_HWCONTROL_RTS) { + gpio_set_function(self->rts, GPIO_FUNC_UART); + } + uart_set_hw_flow(self->uart, self->flow & UART_HWCONTROL_CTS, self->flow & UART_HWCONTROL_RTS); + // 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; @@ -333,6 +381,9 @@ STATIC const mp_rom_map_elem_t machine_uart_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_INV_TX), MP_ROM_INT(UART_INVERT_TX) }, { MP_ROM_QSTR(MP_QSTR_INV_RX), MP_ROM_INT(UART_INVERT_RX) }, + { MP_ROM_QSTR(MP_QSTR_CTS), MP_ROM_INT(UART_HWCONTROL_CTS) }, + { MP_ROM_QSTR(MP_QSTR_RTS), MP_ROM_INT(UART_HWCONTROL_RTS) }, + }; STATIC MP_DEFINE_CONST_DICT(machine_uart_locals_dict, machine_uart_locals_dict_table); From 4f2a10bfc91d5d49837de581eb09d21293f84e2c Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Wed, 7 Jul 2021 23:56:29 +0200 Subject: [PATCH 109/264] rp2/machine_uart: Allow overriding default machine UART pins. --- ports/rp2/machine_uart.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/ports/rp2/machine_uart.c b/ports/rp2/machine_uart.c index b26a162c2f..c6206cebe6 100644 --- a/ports/rp2/machine_uart.c +++ b/ports/rp2/machine_uart.c @@ -38,14 +38,23 @@ #define DEFAULT_UART_BAUDRATE (115200) #define DEFAULT_UART_BITS (8) #define DEFAULT_UART_STOP (1) -#define DEFAULT_UART0_TX (0) -#define DEFAULT_UART0_RX (1) -#define DEFAULT_UART0_CTS (2) -#define DEFAULT_UART0_RTS (3) -#define DEFAULT_UART1_TX (4) -#define DEFAULT_UART1_RX (5) -#define DEFAULT_UART1_CTS (6) -#define DEFAULT_UART1_RTS (7) + +// UART 0 default pins +#if !defined(MICROPY_HW_UART0_TX) +#define MICROPY_HW_UART0_TX (0) +#define MICROPY_HW_UART0_RX (1) +#define MICROPY_HW_UART0_CTS (2) +#define MICROPY_HW_UART0_RTS (3) +#endif + +// UART 1 default pins +#if !defined(MICROPY_HW_UART1_TX) +#define MICROPY_HW_UART1_TX (4) +#define MICROPY_HW_UART1_RX (5) +#define MICROPY_HW_UART1_CTS (6) +#define MICROPY_HW_UART1_RTS (7) +#endif + #define DEFAULT_BUFFER_SIZE (256) #define MIN_BUFFER_SIZE (32) #define MAX_BUFFER_SIZE (32766) @@ -87,9 +96,11 @@ typedef struct _machine_uart_obj_t { STATIC machine_uart_obj_t machine_uart_obj[] = { {{&machine_uart_type}, uart0, 0, 0, DEFAULT_UART_BITS, UART_PARITY_NONE, DEFAULT_UART_STOP, - DEFAULT_UART0_TX, DEFAULT_UART0_RX, DEFAULT_UART0_CTS, DEFAULT_UART0_RTS, 0, 0, 0, 0, {NULL, 1, 0, 0}, 0, {NULL, 1, 0, 0}, 0}, + MICROPY_HW_UART0_TX, MICROPY_HW_UART0_RX, MICROPY_HW_UART0_CTS, MICROPY_HW_UART0_RTS, + 0, 0, 0, 0, {NULL, 1, 0, 0}, 0, {NULL, 1, 0, 0}, 0}, {{&machine_uart_type}, uart1, 1, 0, DEFAULT_UART_BITS, UART_PARITY_NONE, DEFAULT_UART_STOP, - DEFAULT_UART1_TX, DEFAULT_UART1_RX, DEFAULT_UART1_CTS, DEFAULT_UART1_RTS, 0, 0, 0, 0, {NULL, 1, 0, 0}, 0, {NULL, 1, 0, 0}, 0}, + MICROPY_HW_UART1_TX, MICROPY_HW_UART1_RX, MICROPY_HW_UART1_CTS, MICROPY_HW_UART1_RTS, + 0, 0, 0, 0, {NULL, 1, 0, 0}, 0, {NULL, 1, 0, 0}, 0}, }; STATIC const char *_parity_name[] = {"None", "0", "1"}; From 79da7757cc942f202b2f6a276ddcc1d42b484328 Mon Sep 17 00:00:00 2001 From: Mike Causer Date: Fri, 9 Jul 2021 09:51:59 +1000 Subject: [PATCH 110/264] rp2/machine_i2c: Allow boards to configure I2C pins using new macros. --- ports/rp2/machine_i2c.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/ports/rp2/machine_i2c.c b/ports/rp2/machine_i2c.c index 0852cc1993..3b895ba4d4 100644 --- a/ports/rp2/machine_i2c.c +++ b/ports/rp2/machine_i2c.c @@ -33,10 +33,16 @@ #include "hardware/i2c.h" #define DEFAULT_I2C_FREQ (400000) -#define DEFAULT_I2C0_SCL (9) -#define DEFAULT_I2C0_SDA (8) -#define DEFAULT_I2C1_SCL (7) -#define DEFAULT_I2C1_SDA (6) + +#ifndef MICROPY_HW_I2C0_SCL +#define MICROPY_HW_I2C0_SCL (9) +#define MICROPY_HW_I2C0_SDA (8) +#endif + +#ifndef MICROPY_HW_I2C1_SCL +#define MICROPY_HW_I2C1_SCL (7) +#define MICROPY_HW_I2C1_SDA (6) +#endif // SDA/SCL on even/odd pins, I2C0/I2C1 on even/odd pairs of pins. #define IS_VALID_SCL(i2c, pin) (((pin) & 1) == 1 && (((pin) & 2) >> 1) == (i2c)) @@ -52,8 +58,8 @@ typedef struct _machine_i2c_obj_t { } machine_i2c_obj_t; STATIC machine_i2c_obj_t machine_i2c_obj[] = { - {{&machine_hw_i2c_type}, i2c0, 0, DEFAULT_I2C0_SCL, DEFAULT_I2C0_SDA, 0}, - {{&machine_hw_i2c_type}, i2c1, 1, DEFAULT_I2C1_SCL, DEFAULT_I2C1_SDA, 0}, + {{&machine_hw_i2c_type}, i2c0, 0, MICROPY_HW_I2C0_SCL, MICROPY_HW_I2C0_SDA, 0}, + {{&machine_hw_i2c_type}, i2c1, 1, MICROPY_HW_I2C1_SCL, MICROPY_HW_I2C1_SDA, 0}, }; STATIC void machine_i2c_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { From cc77b306396ce7979baaac69b5d7abef7da29110 Mon Sep 17 00:00:00 2001 From: Mike Causer Date: Fri, 9 Jul 2021 11:07:37 +1000 Subject: [PATCH 111/264] rp2/machine_spi: Allow boards to configure SPI pins using new macros. --- ports/rp2/machine_spi.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/ports/rp2/machine_spi.c b/ports/rp2/machine_spi.c index 478c061455..332f446945 100644 --- a/ports/rp2/machine_spi.c +++ b/ports/rp2/machine_spi.c @@ -38,12 +38,18 @@ #define DEFAULT_SPI_PHASE (0) #define DEFAULT_SPI_BITS (8) #define DEFAULT_SPI_FIRSTBIT (SPI_MSB_FIRST) -#define DEFAULT_SPI0_SCK (6) -#define DEFAULT_SPI0_MOSI (7) -#define DEFAULT_SPI0_MISO (4) -#define DEFAULT_SPI1_SCK (10) -#define DEFAULT_SPI1_MOSI (11) -#define DEFAULT_SPI1_MISO (8) + +#ifndef MICROPY_HW_SPI0_SCK +#define MICROPY_HW_SPI0_SCK (6) +#define MICROPY_HW_SPI0_MOSI (7) +#define MICROPY_HW_SPI0_MISO (4) +#endif + +#ifndef MICROPY_HW_SPI1_SCK +#define MICROPY_HW_SPI1_SCK (10) +#define MICROPY_HW_SPI1_MOSI (11) +#define MICROPY_HW_SPI1_MISO (8) +#endif #define IS_VALID_PERIPH(spi, pin) ((((pin) & 8) >> 3) == (spi)) #define IS_VALID_SCK(spi, pin) (((pin) & 3) == 2 && IS_VALID_PERIPH(spi, pin)) @@ -68,13 +74,13 @@ STATIC machine_spi_obj_t machine_spi_obj[] = { { {&machine_spi_type}, spi0, 0, DEFAULT_SPI_POLARITY, DEFAULT_SPI_PHASE, DEFAULT_SPI_BITS, DEFAULT_SPI_FIRSTBIT, - DEFAULT_SPI0_SCK, DEFAULT_SPI0_MOSI, DEFAULT_SPI0_MISO, + MICROPY_HW_SPI0_SCK, MICROPY_HW_SPI0_MOSI, MICROPY_HW_SPI0_MISO, 0, }, { {&machine_spi_type}, spi1, 1, DEFAULT_SPI_POLARITY, DEFAULT_SPI_PHASE, DEFAULT_SPI_BITS, DEFAULT_SPI_FIRSTBIT, - DEFAULT_SPI1_SCK, DEFAULT_SPI1_MOSI, DEFAULT_SPI1_MISO, + MICROPY_HW_SPI1_SCK, MICROPY_HW_SPI1_MOSI, MICROPY_HW_SPI1_MISO, 0, }, }; From d0227d5862ce57ec064563febf5b644cb39d2c07 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 17 Jul 2021 23:18:40 +1000 Subject: [PATCH 112/264] py/emitnative: Reuse need_reg_all func in need_stack_settled. To reduce code size and code duplication. Signed-off-by: Damien George --- py/emitnative.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/py/emitnative.c b/py/emitnative.c index 425ba2d33e..a40d690f2d 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -841,10 +841,13 @@ STATIC void need_reg_single(emit_t *emit, int reg_needed, int skip_stack_pos) { } } +// Ensures all unsettled registers that hold Python values are copied to the +// concrete Python stack. All registers are then free to use. STATIC void need_reg_all(emit_t *emit) { for (int i = 0; i < emit->stack_size; i++) { stack_info_t *si = &emit->stack_info[i]; if (si->kind == STACK_REG) { + DEBUG_printf(" reg(%u) to local(%u)\n", si->data.u_reg, emit->stack_start + i); si->kind = STACK_VALUE; emit_native_mov_state_reg(emit, emit->stack_start + i, si->data.u_reg); } @@ -871,16 +874,13 @@ STATIC vtype_kind_t load_reg_stack_imm(emit_t *emit, int reg_dest, const stack_i } } +// Copies all unsettled registers and immediates that are Python values into the +// concrete Python stack. This ensures the concrete Python stack holds valid +// values for the current stack_size. +// This function may clobber REG_TEMP0. STATIC void need_stack_settled(emit_t *emit) { DEBUG_printf(" need_stack_settled; stack_size=%d\n", emit->stack_size); - for (int i = 0; i < emit->stack_size; i++) { - stack_info_t *si = &emit->stack_info[i]; - if (si->kind == STACK_REG) { - DEBUG_printf(" reg(%u) to local(%u)\n", si->data.u_reg, emit->stack_start + i); - si->kind = STACK_VALUE; - emit_native_mov_state_reg(emit, emit->stack_start + i, si->data.u_reg); - } - } + need_reg_all(emit); for (int i = 0; i < emit->stack_size; i++) { stack_info_t *si = &emit->stack_info[i]; if (si->kind == STACK_IMM) { From 0e3752e82a61b2378547457c4041270db3909c39 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Mon, 19 Jul 2021 13:13:32 +1000 Subject: [PATCH 113/264] py/emitnative: Ensure stack settling is safe mid-branch. And add a test for the case where REG_RET could be in use. Fixes #7523. Signed-off-by: Jim Mussared --- py/emitnative.c | 7 ++++--- tests/micropython/native_misc.py | 10 ++++++++++ tests/micropython/native_misc.py.exp | 2 ++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/py/emitnative.c b/py/emitnative.c index a40d690f2d..7c7c342839 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -877,7 +877,7 @@ STATIC vtype_kind_t load_reg_stack_imm(emit_t *emit, int reg_dest, const stack_i // Copies all unsettled registers and immediates that are Python values into the // concrete Python stack. This ensures the concrete Python stack holds valid // values for the current stack_size. -// This function may clobber REG_TEMP0. +// This function may clobber REG_TEMP1. STATIC void need_stack_settled(emit_t *emit) { DEBUG_printf(" need_stack_settled; stack_size=%d\n", emit->stack_size); need_reg_all(emit); @@ -886,8 +886,9 @@ STATIC void need_stack_settled(emit_t *emit) { if (si->kind == STACK_IMM) { DEBUG_printf(" imm(" INT_FMT ") to local(%u)\n", si->data.u_imm, emit->stack_start + i); si->kind = STACK_VALUE; - si->vtype = load_reg_stack_imm(emit, REG_TEMP0, si, false); - emit_native_mov_state_reg(emit, emit->stack_start + i, REG_TEMP0); + // using REG_TEMP1 to avoid clobbering REG_TEMP0 (aka REG_RET) + si->vtype = load_reg_stack_imm(emit, REG_TEMP1, si, false); + emit_native_mov_state_reg(emit, emit->stack_start + i, REG_TEMP1); } } } diff --git a/tests/micropython/native_misc.py b/tests/micropython/native_misc.py index 7c5415375e..f5ef807fe1 100644 --- a/tests/micropython/native_misc.py +++ b/tests/micropython/native_misc.py @@ -38,3 +38,13 @@ def f(a): f(False) f(True) + + +# stack settling in branch +@micropython.native +def f(a): + print(1, 2, 3, 4 if a else 5) + + +f(False) +f(True) diff --git a/tests/micropython/native_misc.py.exp b/tests/micropython/native_misc.py.exp index b3e1389ef7..8eec04228f 100644 --- a/tests/micropython/native_misc.py.exp +++ b/tests/micropython/native_misc.py.exp @@ -4,3 +4,5 @@ 6 True False +1 2 3 5 +1 2 3 4 From da74ef661587011132570abfe70114ca8ccd679c Mon Sep 17 00:00:00 2001 From: NitiKaur Date: Sat, 10 Jul 2021 13:35:52 +0530 Subject: [PATCH 114/264] docs/rp2: Enhance quickref and change image to Pico pinout. --- docs/rp2/img/pico_pinout.png | Bin 0 -> 186434 bytes docs/rp2/img/rpipico.jpg | Bin 89259 -> 0 bytes docs/rp2/quickref.rst | 94 ++++++++++++++++++++--------------- 3 files changed, 53 insertions(+), 41 deletions(-) create mode 100644 docs/rp2/img/pico_pinout.png delete mode 100644 docs/rp2/img/rpipico.jpg diff --git a/docs/rp2/img/pico_pinout.png b/docs/rp2/img/pico_pinout.png new file mode 100644 index 0000000000000000000000000000000000000000..7bee29274e9d23096b8bbc8f9b9af00d18dacf2b GIT binary patch literal 186434 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 + + From 218606351c6f9688a3f90dad791bcb2109adcf1b Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Thu, 12 Aug 2021 13:56:13 +1000 Subject: [PATCH 202/264] docs: Rename ufoo.rst to foo.rst. This is a simple rename of the files, no content changes (other than updating index.rst to use the new paths) Signed-off-by: Jim Mussared --- docs/library/{uarray.rst => array.rst} | 0 docs/library/{ubinascii.rst => binascii.rst} | 0 .../library/{ubluetooth.rst => bluetooth.rst} | 0 .../{ucollections.rst => collections.rst} | 0 .../library/{ucryptolib.rst => cryptolib.rst} | 0 docs/library/{uerrno.rst => errno.rst} | 0 docs/library/{uhashlib.rst => hashlib.rst} | 0 docs/library/{uheapq.rst => heapq.rst} | 0 docs/library/index.rst | 38 +++++++++---------- docs/library/{uio.rst => io.rst} | 0 docs/library/{ujson.rst => json.rst} | 0 docs/library/{uos.rst => os.rst} | 0 docs/library/{ure.rst => re.rst} | 0 docs/library/{uselect.rst => select.rst} | 0 docs/library/{usocket.rst => socket.rst} | 0 docs/library/{ussl.rst => ssl.rst} | 0 docs/library/{ustruct.rst => struct.rst} | 0 docs/library/{usys.rst => sys.rst} | 0 docs/library/{utime.rst => time.rst} | 0 docs/library/{uzlib.rst => zlib.rst} | 0 20 files changed, 19 insertions(+), 19 deletions(-) rename docs/library/{uarray.rst => array.rst} (100%) rename docs/library/{ubinascii.rst => binascii.rst} (100%) rename docs/library/{ubluetooth.rst => bluetooth.rst} (100%) rename docs/library/{ucollections.rst => collections.rst} (100%) rename docs/library/{ucryptolib.rst => cryptolib.rst} (100%) rename docs/library/{uerrno.rst => errno.rst} (100%) rename docs/library/{uhashlib.rst => hashlib.rst} (100%) rename docs/library/{uheapq.rst => heapq.rst} (100%) rename docs/library/{uio.rst => io.rst} (100%) rename docs/library/{ujson.rst => json.rst} (100%) rename docs/library/{uos.rst => os.rst} (100%) rename docs/library/{ure.rst => re.rst} (100%) rename docs/library/{uselect.rst => select.rst} (100%) rename docs/library/{usocket.rst => socket.rst} (100%) rename docs/library/{ussl.rst => ssl.rst} (100%) rename docs/library/{ustruct.rst => struct.rst} (100%) rename docs/library/{usys.rst => sys.rst} (100%) rename docs/library/{utime.rst => time.rst} (100%) rename docs/library/{uzlib.rst => zlib.rst} (100%) diff --git a/docs/library/uarray.rst b/docs/library/array.rst similarity index 100% rename from docs/library/uarray.rst rename to docs/library/array.rst diff --git a/docs/library/ubinascii.rst b/docs/library/binascii.rst similarity index 100% rename from docs/library/ubinascii.rst rename to docs/library/binascii.rst diff --git a/docs/library/ubluetooth.rst b/docs/library/bluetooth.rst similarity index 100% rename from docs/library/ubluetooth.rst rename to docs/library/bluetooth.rst diff --git a/docs/library/ucollections.rst b/docs/library/collections.rst similarity index 100% rename from docs/library/ucollections.rst rename to docs/library/collections.rst diff --git a/docs/library/ucryptolib.rst b/docs/library/cryptolib.rst similarity index 100% rename from docs/library/ucryptolib.rst rename to docs/library/cryptolib.rst diff --git a/docs/library/uerrno.rst b/docs/library/errno.rst similarity index 100% rename from docs/library/uerrno.rst rename to docs/library/errno.rst diff --git a/docs/library/uhashlib.rst b/docs/library/hashlib.rst similarity index 100% rename from docs/library/uhashlib.rst rename to docs/library/hashlib.rst diff --git a/docs/library/uheapq.rst b/docs/library/heapq.rst similarity index 100% rename from docs/library/uheapq.rst rename to docs/library/heapq.rst diff --git a/docs/library/index.rst b/docs/library/index.rst index 5fd4a8f77e..638941fa5e 100644 --- a/docs/library/index.rst +++ b/docs/library/index.rst @@ -73,28 +73,28 @@ it will fallback to loading the built-in ``ujson`` module. .. toctree:: :maxdepth: 1 + array.rst + binascii.rst builtins.rst cmath.rst + collections.rst + errno.rst gc.rst + hashlib.rst + heapq.rst + io.rst + json.rst math.rst - uarray.rst + os.rst + re.rst + select.rst + socket.rst + ssl.rst + struct.rst + sys.rst + time.rst uasyncio.rst - ubinascii.rst - ucollections.rst - uerrno.rst - uhashlib.rst - uheapq.rst - uio.rst - ujson.rst - uos.rst - ure.rst - uselect.rst - usocket.rst - ussl.rst - ustruct.rst - usys.rst - utime.rst - uzlib.rst + zlib.rst _thread.rst @@ -107,13 +107,13 @@ the following libraries. .. toctree:: :maxdepth: 1 + bluetooth.rst btree.rst + cryptolib.rst framebuf.rst machine.rst micropython.rst network.rst - ubluetooth.rst - ucryptolib.rst uctypes.rst diff --git a/docs/library/uio.rst b/docs/library/io.rst similarity index 100% rename from docs/library/uio.rst rename to docs/library/io.rst diff --git a/docs/library/ujson.rst b/docs/library/json.rst similarity index 100% rename from docs/library/ujson.rst rename to docs/library/json.rst diff --git a/docs/library/uos.rst b/docs/library/os.rst similarity index 100% rename from docs/library/uos.rst rename to docs/library/os.rst diff --git a/docs/library/ure.rst b/docs/library/re.rst similarity index 100% rename from docs/library/ure.rst rename to docs/library/re.rst diff --git a/docs/library/uselect.rst b/docs/library/select.rst similarity index 100% rename from docs/library/uselect.rst rename to docs/library/select.rst diff --git a/docs/library/usocket.rst b/docs/library/socket.rst similarity index 100% rename from docs/library/usocket.rst rename to docs/library/socket.rst diff --git a/docs/library/ussl.rst b/docs/library/ssl.rst similarity index 100% rename from docs/library/ussl.rst rename to docs/library/ssl.rst diff --git a/docs/library/ustruct.rst b/docs/library/struct.rst similarity index 100% rename from docs/library/ustruct.rst rename to docs/library/struct.rst diff --git a/docs/library/usys.rst b/docs/library/sys.rst similarity index 100% rename from docs/library/usys.rst rename to docs/library/sys.rst diff --git a/docs/library/utime.rst b/docs/library/time.rst similarity index 100% rename from docs/library/utime.rst rename to docs/library/time.rst diff --git a/docs/library/uzlib.rst b/docs/library/zlib.rst similarity index 100% rename from docs/library/uzlib.rst rename to docs/library/zlib.rst From c737cde9472741337be0c0a66e8e99695c6a9b14 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Thu, 12 Aug 2021 13:59:29 +1000 Subject: [PATCH 203/264] docs: Replace ufoo with foo in all docs. Anywhere a module is mentioned, use its "non-u" name for consistency. The "import module" vs "import umodule" is something of a FAQ, and this commit intends to help clear that up. As a first approximation MicroPython is Python, and so imports should work the same as Python and use the same name, to a first approximation. The u-version of a module is a detail that can be learned later on, when the user wants to understand more and have finer control over importing. Existing Python code should just work, as much as it is possible to do that within the constraints of embedded systems, and the MicroPython documentation should match the idiomatic way to write Python code. With universal weak links for modules (via MICROPY_MODULE_WEAK_LINKS) users can consistently use "import foo" across all ports (with the exception of the minimal ports). And the ability to override/extend via "foo.py" continues to work well. Signed-off-by: Jim Mussared --- docs/develop/library.rst | 2 +- docs/develop/porting.rst | 4 +-- docs/esp32/quickref.rst | 12 ++++---- docs/esp8266/general.rst | 4 +-- docs/esp8266/quickref.rst | 12 ++++---- docs/library/array.rst | 6 ++-- docs/library/binascii.rst | 6 ++-- docs/library/bluetooth.rst | 10 +++---- docs/library/btree.rst | 2 +- docs/library/collections.rst | 10 +++---- docs/library/cryptolib.rst | 12 ++++---- docs/library/errno.rst | 12 ++++---- docs/library/esp32.rst | 2 +- docs/library/hashlib.rst | 14 +++++----- docs/library/heapq.rst | 6 ++-- docs/library/index.rst | 4 +-- docs/library/io.rst | 6 ++-- docs/library/json.rst | 6 ++-- docs/library/machine.SDCard.rst | 4 +-- docs/library/network.WLAN.rst | 2 +- docs/library/network.rst | 10 +++---- docs/library/os.rst | 14 +++++----- docs/library/pyb.Flash.rst | 2 +- docs/library/pyb.rst | 4 +-- docs/library/re.rst | 14 +++++----- docs/library/rp2.Flash.rst | 2 +- docs/library/select.rst | 18 ++++++------ docs/library/socket.rst | 48 ++++++++++++++++---------------- docs/library/ssl.rst | 20 ++++++------- docs/library/struct.rst | 6 ++-- docs/library/sys.rst | 14 +++++----- docs/library/time.rst | 10 +++---- docs/library/uctypes.rst | 2 +- docs/library/zlib.rst | 6 ++-- docs/pyboard/quickref.rst | 2 +- docs/reference/constrained.rst | 6 ++-- docs/reference/filesystem.rst | 12 ++++---- docs/reference/glossary.rst | 2 +- docs/reference/speed_python.rst | 6 ++-- docs/rp2/quickref.rst | 2 +- docs/wipy/general.rst | 8 +++--- docs/zephyr/tutorial/storage.rst | 6 ++-- 42 files changed, 175 insertions(+), 175 deletions(-) diff --git a/docs/develop/library.rst b/docs/develop/library.rst index bebddcc8a3..47ea2dc8d2 100644 --- a/docs/develop/library.rst +++ b/docs/develop/library.rst @@ -34,7 +34,7 @@ An example is the ``gc`` module discussed in :ref:`memorymanagement`. >>> gc.enable() >>> -MicroPython has several other builtin standard/core modules like ``io``, ``uarray`` etc. +MicroPython has several other builtin standard/core modules like ``io``, ``array`` etc. Adding a new core module involves several modifications. First, create the ``C`` file in the ``py/`` directory. In this example we are adding a diff --git a/docs/develop/porting.rst b/docs/develop/porting.rst index 08f3557f18..549227d762 100644 --- a/docs/develop/porting.rst +++ b/docs/develop/porting.rst @@ -245,8 +245,8 @@ That should give a MicroPython REPL. You can then run commands like: .. code-block:: bash MicroPython v1.13 on 2021-01-01; example-board with unknown-cpu - >>> import usys - >>> usys.implementation + >>> import sys + >>> sys.implementation ('micropython', (1, 13, 0)) >>> diff --git a/docs/esp32/quickref.rst b/docs/esp32/quickref.rst index 56c3721148..3153ebd571 100644 --- a/docs/esp32/quickref.rst +++ b/docs/esp32/quickref.rst @@ -98,7 +98,7 @@ A useful function for connecting to your local WiFi network is:: pass print('network config:', wlan.ifconfig()) -Once the network is established the :mod:`socket ` module can be used +Once the network is established the :mod:`socket ` module can be used to create and use TCP/UDP sockets as usual, and the ``urequests`` module for convenient HTTP requests. @@ -113,7 +113,7 @@ to reconnect forever). Delay and timing ---------------- -Use the :mod:`time ` module:: +Use the :mod:`time

K-1jJY3$+GU-yjS3GRNw~91)}iDd@xy ziaet3tGT52Oi8U&@o^IFirRQG=zqz`H>MG;9%!U6Czt3|xdI^?nN3WDijA!Q|&GKHdRh$D%0AA^IwoC+Wpzl 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 TdD^?c0}x-tzlv3h7zF+w6~>!c literal 0 HcmV?d00001 diff --git a/docs/rp2/img/rpipico.jpg b/docs/rp2/img/rpipico.jpg deleted file mode 100644 index 336d3582775b8701d670403b0e63df8fcefc478c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 89259 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 y{b8d64j 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 diff --git a/docs/rp2/quickref.rst b/docs/rp2/quickref.rst index c18eb4a0b9..65e98d700b 100644 --- a/docs/rp2/quickref.rst +++ b/docs/rp2/quickref.rst @@ -3,7 +3,7 @@ Quick reference for the RP2 =========================== -.. image:: img/rpipico.jpg +.. image:: img/pico_pinout.png :alt: Raspberry Pi Pico :width: 640px @@ -27,10 +27,9 @@ a troubleshooting subsection. General board control --------------------- -The MicroPython REPL is on the USB serial port. -Tab-completion is useful to find out what methods an object has. -Paste mode (ctrl-E) is useful to paste a large slab of Python code into -the REPL. +The MicroPython REPL is accessed via the USB serial port. Tab-completion is useful to +find out what methods an object has. Paste mode (ctrl-E) is useful to paste a +large slab of Python code into the REPL. The :mod:`machine` module:: @@ -59,7 +58,19 @@ Use the :mod:`time ` module:: Timers ------ -How do they work? +RP2040's system timer peripheral provides a global microsecond timebase and +generates interrupts for it. The software timer is available currently, +and there are unlimited number of them (memory permitting). There is no need +to specify the timer id (id=-1 is supported at the moment) as it will default +to this. + +Use the :mod:`machine.Timer` class:: + + from machine import Timer + + tim = Timer(period=5000, mode=Timer.ONE_SHOT, callback=lambda t:print(1)) + tim.init(period=2000, mode=Timer.PERIODIC, callback=lambda t:print(2)) + .. _rp2_Pins_and_GPIO: @@ -84,19 +95,28 @@ Use the :ref:`machine.Pin ` class:: UART (serial bus) ----------------- +There are two UARTs, UART0 and UART1. UART0 can be mapped to GPIO 0/1, 12/13 +and 16/17, and UART1 to GPIO 4/5 and 8/9. + + See :ref:`machine.UART `. :: - from machine import UART - - uart1 = UART(1, baudrate=9600, tx=33, rx=32) + from machine import UART, Pin + uart1 = UART(1, baudrate=9600, tx=Pin(4), rx=Pin(5)) uart1.write('hello') # write 5 bytes uart1.read(5) # read up to 5 bytes +.. note:: + + REPL over UART is disabled by default. You can see the :ref:`rp2_intro` for + details on how to enable REPL over UART. + PWM (pulse width modulation) ---------------------------- -How does PWM work on the RPi RP2xxx? +There are 8 independent channels each of which have 2 outputs making it 16 +PWM channels in total which can be clocked from 7Hz to 125Mhz. Use the ``machine.PWM`` class:: @@ -112,14 +132,18 @@ Use the ``machine.PWM`` class:: ADC (analog to digital conversion) ---------------------------------- -How does the ADC module work? +RP2040 has five ADC channels in total, four of which are 12-bit SAR based +ADCs: GP26, GP27, GP28 and GP29. The input signal for ADC0, ADC1, ADC2 and +ADC3 can be connected with GP26, GP27, GP28, GP29 respectively (On Pico board, +GP29 is connected to VSYS). The standard ADC range is 0-3.3V. The fifth +channel is connected to the in-built temperature sensor and can be used for +measuring the temperature. Use the :ref:`machine.ADC ` class:: - from machine import ADC - - adc = ADC(Pin(32)) # create ADC object on ADC pin - adc.read_u16() # read value, 0-65535 across voltage range 0.0v - 3.3v + from machine import ADC, Pin + adc = ADC(Pin(26)) # create ADC object on ADC pin + adc.read_u16() # read value, 0-65535 across voltage range 0.0v - 3.3v Software SPI bus ---------------- @@ -132,7 +156,7 @@ Software SPI (using bit-banging) works on all pins, and is accessed via the # construct a SoftSPI bus on the given pins # polarity is the idle state of SCK # phase=0 means sample on the first edge of SCK, phase=1 means the second - spi = SoftSPI(baudrate=100000, polarity=1, phase=0, sck=Pin(0), mosi=Pin(2), miso=Pin(4)) + spi = SoftSPI(baudrate=100_000, polarity=1, phase=0, sck=Pin(0), mosi=Pin(2), miso=Pin(4)) spi.init(baudrate=200000) # set the baudrate @@ -156,14 +180,15 @@ Software SPI (using bit-banging) works on all pins, and is accessed via the Hardware SPI bus ---------------- -Hardware SPI is accessed via the :ref:`machine.SPI ` class and -has the same methods as software SPI above:: +The RP2040 has 2 hardware SPI buses which is accessed via the +:ref:`machine.SPI ` class and has the same methods as software +SPI above:: from machine import Pin, SPI - spi = SPI(1, 10000000) - spi = SPI(1, 10000000, sck=Pin(14), mosi=Pin(13), miso=Pin(12)) - spi = SPI(2, baudrate=80000000, polarity=0, phase=0, bits=8, firstbit=0, sck=Pin(18), mosi=Pin(23), miso=Pin(19)) + spi = SPI(1, 10_000_000) # Default assignment: sck=Pin(10), mosi=Pin(11), miso=Pin(8) + spi = SPI(1, 10_000_000, sck=Pin(14), mosi=Pin(15), miso=Pin(12)) + spi = SPI(0, baudrate=80_000_000, polarity=0, phase=0, bits=8, sck=Pin(6), mosi=Pin(7), miso=Pin(4)) Software I2C bus ---------------- @@ -173,7 +198,7 @@ accessed via the :ref:`machine.SoftI2C ` class:: from machine import Pin, SoftI2C - i2c = SoftI2C(scl=Pin(5), sda=Pin(4), freq=100000) + i2c = SoftI2C(scl=Pin(5), sda=Pin(4), freq=100_000) i2c.scan() # scan for devices @@ -191,8 +216,8 @@ has the same methods as software I2C above:: from machine import Pin, I2C - i2c = I2C(0) - i2c = I2C(1, scl=Pin(5), sda=Pin(4), freq=400000) + i2c = I2C(0) # default assignment: scl=Pin(9), sda=Pin(8) + i2c = I2C(1, scl=Pin(3), sda=Pin(2), freq=400_000) Real time clock (RTC) --------------------- @@ -202,13 +227,15 @@ See :ref:`machine.RTC ` :: from machine import RTC rtc = RTC() - rtc.datetime((2017, 8, 23, 2, 12, 48, 0, 0)) # set a specific date and time + rtc.datetime((2017, 8, 23, 2, 12, 48, 0, 0)) # set a specific date and + # time, eg. 2017/8/23 1:12:48 rtc.datetime() # get date and time WDT (Watchdog timer) -------------------- -Is there a watchdog timer? +The RP2040 has a watchdog which is a countdown timer that can restart +parts of the chip if it reaches zero. See :ref:`machine.WDT `. :: @@ -218,21 +245,6 @@ See :ref:`machine.WDT `. :: wdt = WDT(timeout=5000) wdt.feed() -Deep-sleep mode ---------------- - -Is there deep-sleep support for the rp2? - -The following code can be used to sleep, wake and check the reset cause:: - - import machine - - # check if the device woke from a deep sleep - if machine.reset_cause() == machine.DEEPSLEEP_RESET: - print('woke from a deep sleep') - - # put the device to sleep for 10 seconds - machine.deepsleep(10000) OneWire driver -------------- From 6a9133a8b5fc2c19635ae0260569837aaaad2b37 Mon Sep 17 00:00:00 2001 From: NitiKaur Date: Sat, 10 Jul 2021 17:41:53 +0530 Subject: [PATCH 115/264] docs/rp2: Update general section to give a brief technical overview. --- docs/rp2/general.rst | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/docs/rp2/general.rst b/docs/rp2/general.rst index 9ff83a9656..7042d0d25b 100644 --- a/docs/rp2/general.rst +++ b/docs/rp2/general.rst @@ -10,9 +10,27 @@ the RP2040. Technical specifications and SoC datasheets ------------------------------------------- -Datasheets! +For detailed technical specifications, please refer to the `datasheets +`_ -Short summary of tech specs! +The RP2040 microcontroller is manufactured on a 40 nm silicon process in a 7x7mm +QFN-56 SMD package. The key features include: -Description of general structure of the port (it's built on top of the APIs -provided by the Raspberry Pi SDK). +* 133 MHz dual ARM Cortex-M0+ cores (overclockable to over 400 MHz) +* 264KB SRAM in six independent banks +* No internal Flash or EEPROM memory (after reset, the bootloader loads + firmware from either the external flash memory or USB bus into internal SRAM) +* QSPI bus controller, which + supports up to 16 MB of external Flash memory +* On-chip programmable LDO togenerate core voltage +* 2 on-chip PLLs to generate USB and core clocks +* 30 GPIOpins, of which 4 can optionally be used as analog inputs + +The peripherals include: + +* 2 UARTs +* 2 SPI controllers +* 2 I2C contollers +* 16 PWM channels +* USB 1.1 controller +* 8 PIO state machines From db6d60b07967f6d75d74349b021b262cb8fee628 Mon Sep 17 00:00:00 2001 From: Josh Lloyd Date: Wed, 20 Nov 2019 17:21:35 +1300 Subject: [PATCH 116/264] extmod/utime: Always invoke mp_hal_delay_ms when >= to 0ms. This makes sleep_ms(0) useful as a "yield" so event processing and thread switching can take place. Fixes issue #5345. --- extmod/utime_mphal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extmod/utime_mphal.c b/extmod/utime_mphal.c index d053cf128b..3d1cdfd820 100644 --- a/extmod/utime_mphal.c +++ b/extmod/utime_mphal.c @@ -48,7 +48,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(mp_utime_sleep_obj, time_sleep); STATIC mp_obj_t time_sleep_ms(mp_obj_t arg) { mp_int_t ms = mp_obj_get_int(arg); - if (ms > 0) { + if (ms >= 0) { mp_hal_delay_ms(ms); } return mp_const_none; From a5221c47eb60500506abf2c9967429879277714b Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 22 Jul 2021 00:04:01 +1000 Subject: [PATCH 117/264] docs/library/utime.rst: Clarify behaviour and precision of sleep ms/us. This description is based on the existing bare-metal ports implementations. Signed-off-by: Damien George --- docs/library/utime.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/library/utime.rst b/docs/library/utime.rst index b7c604dc7b..ef2a572aa4 100644 --- a/docs/library/utime.rst +++ b/docs/library/utime.rst @@ -74,10 +74,19 @@ Functions Delay for given number of milliseconds, should be positive or 0. + This function will delay for at least the given number of milliseconds, but + may take longer than that if other processing must take place, for example + interrupt handlers or other threads. Passing in 0 for *ms* will still allow + this other processing to occur. Use `sleep_us()` for more precise delays. + .. function:: sleep_us(us) Delay for given number of microseconds, should be positive or 0. + This function attempts to provide an accurate delay of at least *us* + microseconds, but it may take longer if the system has other higher priority + processing to perform. + .. function:: ticks_ms() Returns an increasing millisecond counter with an arbitrary reference point, that From 6bc50c4fa9da1d97f1ff14b1331d8d497962ed17 Mon Sep 17 00:00:00 2001 From: Josh Lloyd Date: Wed, 20 Nov 2019 17:18:09 +1300 Subject: [PATCH 118/264] stm32/systick: Always POLL_HOOK when delaying for milliseconds. Call MICROPY_EVENT_POLL_HOOK even on very short delays so that busy loops that call sleep_ms still yield to events and other threads. See related issue #5344. --- ports/stm32/systick.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/stm32/systick.c b/ports/stm32/systick.c index d70fc00539..7d1e318ac0 100644 --- a/ports/stm32/systick.c +++ b/ports/stm32/systick.c @@ -96,12 +96,12 @@ void mp_hal_delay_ms(mp_uint_t Delay) { // IRQs enabled, so can use systick counter to do the delay uint32_t start = uwTick; // Wraparound of tick is taken care of by 2's complement arithmetic. - while (uwTick - start < Delay) { + do { // This macro will execute the necessary idle behaviour. It may // raise an exception, switch threads or enter sleep mode (waiting for // (at least) the SysTick interrupt). MICROPY_EVENT_POLL_HOOK - } + } while (uwTick - start < Delay); } else { // IRQs disabled, so need to use a busy loop for the delay. // To prevent possible overflow of the counter we use a double loop. From 2cfbe5bc0fe2c8ed24c6f35abbc82ef8415d17e4 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 21 Jul 2021 01:32:15 +1000 Subject: [PATCH 119/264] esp32/modmachine: Release the GIL in machine.idle(). So that other threads get a chance to run when taskYIELD() is called. See issue #5344. Signed-off-by: Damien George --- ports/esp32/modmachine.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ports/esp32/modmachine.c b/ports/esp32/modmachine.c index 50c0cff990..34fe8600ae 100644 --- a/ports/esp32/modmachine.c +++ b/ports/esp32/modmachine.c @@ -232,7 +232,9 @@ STATIC mp_obj_t machine_unique_id(void) { STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_unique_id_obj, machine_unique_id); STATIC mp_obj_t machine_idle(void) { + MP_THREAD_GIL_EXIT(); taskYIELD(); + MP_THREAD_GIL_ENTER(); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_idle_obj, machine_idle); From 6214fa3f9ecdfb146aaec09afc9d586e76257d5a Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 21 Jul 2021 01:33:16 +1000 Subject: [PATCH 120/264] esp32/mphalport: Always yield at least once in delay_ms. This helps the OS switch to and give other threads processing time during the sleep. It also ensures that pending events are handled, even when sleeping for 0ms. Fixes issue #5344. Signed-off-by: Damien George --- ports/esp32/mphalport.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ports/esp32/mphalport.c b/ports/esp32/mphalport.c index e795b5c991..6e6fd26ef4 100644 --- a/ports/esp32/mphalport.c +++ b/ports/esp32/mphalport.c @@ -142,14 +142,22 @@ void mp_hal_delay_ms(uint32_t ms) { uint64_t dt; uint64_t t0 = esp_timer_get_time(); for (;;) { + mp_handle_pending(true); + MICROPY_PY_USOCKET_EVENTS_HANDLER + MP_THREAD_GIL_EXIT(); uint64_t t1 = esp_timer_get_time(); dt = t1 - t0; if (dt + portTICK_PERIOD_MS * 1000 >= us) { // doing a vTaskDelay would take us beyond requested delay time + taskYIELD(); + MP_THREAD_GIL_ENTER(); + t1 = esp_timer_get_time(); + dt = t1 - t0; break; + } else { + ulTaskNotifyTake(pdFALSE, 1); + MP_THREAD_GIL_ENTER(); } - MICROPY_EVENT_POLL_HOOK - ulTaskNotifyTake(pdFALSE, 1); } if (dt < us) { // do the remaining delay accurately From 7649f5fbd2fb2e9b9153da47a4af4b538ca3ae65 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Fri, 9 Jul 2021 15:49:25 +0200 Subject: [PATCH 121/264] stm32/sdram: Make SDRAM test cache aware, and optional failure with msg. * Make SDRAM test cache-aware for newer MCUs. * Use the defined data bus width (instead of the fixed 8-bits). * Allow optional failure on error with verbose error messages. * Test speed is now inverted (test accepts exhaustive instead fast). --- ports/stm32/main.c | 2 +- ports/stm32/sdram.c | 88 ++++++++++++++++++++++++++++++++++++--------- ports/stm32/sdram.h | 2 +- 3 files changed, 73 insertions(+), 19 deletions(-) diff --git a/ports/stm32/main.c b/ports/stm32/main.c index d7afb9e4af..3ff7077ac2 100644 --- a/ports/stm32/main.c +++ b/ports/stm32/main.c @@ -404,7 +404,7 @@ void stm32_main(uint32_t reset_mode) { bool sdram_valid = true; UNUSED(sdram_valid); #if MICROPY_HW_SDRAM_STARTUP_TEST - sdram_valid = sdram_test(true); + sdram_valid = sdram_test(false); #endif #endif #if MICROPY_PY_THREAD diff --git a/ports/stm32/sdram.c b/ports/stm32/sdram.c index 514192519a..e0e3500836 100644 --- a/ports/stm32/sdram.c +++ b/ports/stm32/sdram.c @@ -283,52 +283,106 @@ void sdram_leave_low_power(void) { #pragma GCC diagnostic ignored "-Wstringop-overflow" #endif -bool sdram_test(bool fast) { +bool __attribute__((optimize("O0"))) sdram_test(bool exhaustive) { uint8_t const pattern = 0xaa; uint8_t const antipattern = 0x55; uint8_t *const mem_base = (uint8_t *)sdram_start(); - /* test data bus */ - for (uint8_t i = 1; i; i <<= 1) { - *mem_base = i; - if (*mem_base != i) { - printf("data bus lines test failed! data (%d)\n", i); + #if MICROPY_HW_SDRAM_TEST_FAIL_ON_ERROR + char error_buffer[1024]; + #endif + + #if (__DCACHE_PRESENT == 1) + bool i_cache_disabled = false; + bool d_cache_disabled = false; + + // Disable caches for testing. + if (SCB->CCR & (uint32_t)SCB_CCR_IC_Msk) { + SCB_DisableICache(); + i_cache_disabled = true; + } + + if (SCB->CCR & (uint32_t)SCB_CCR_DC_Msk) { + SCB_DisableDCache(); + d_cache_disabled = true; + } + #endif + + // Test data bus + for (uint32_t i = 0; i < MICROPY_HW_SDRAM_MEM_BUS_WIDTH; i++) { + *((uint32_t *)mem_base) = (1 << i); + if (*((uint32_t *)mem_base) != (1 << i)) { + #if MICROPY_HW_SDRAM_TEST_FAIL_ON_ERROR + snprintf(error_buffer, sizeof(error_buffer), + "Data bus test failed at 0x%p expected 0x%x found 0x%lx", + &mem_base[0], (1 << i), ((uint32_t *)mem_base)[0]); + __fatal_error(error_buffer); + #endif return false; } } - /* test address bus */ - /* Check individual address lines */ + // Test address bus for (uint32_t i = 1; i < MICROPY_HW_SDRAM_SIZE; i <<= 1) { mem_base[i] = pattern; if (mem_base[i] != pattern) { - printf("address bus lines test failed! address (%p)\n", &mem_base[i]); + #if MICROPY_HW_SDRAM_TEST_FAIL_ON_ERROR + snprintf(error_buffer, sizeof(error_buffer), + "Address bus test failed at 0x%p expected 0x%x found 0x%x", + &mem_base[i], pattern, mem_base[i]); + __fatal_error(error_buffer); + #endif return false; } } - /* Check for aliasing (overlaping addresses) */ + // Check for aliasing (overlaping addresses) mem_base[0] = antipattern; for (uint32_t i = 1; i < MICROPY_HW_SDRAM_SIZE; i <<= 1) { if (mem_base[i] != pattern) { - printf("address bus overlap %p\n", &mem_base[i]); + #if MICROPY_HW_SDRAM_TEST_FAIL_ON_ERROR + snprintf(error_buffer, sizeof(error_buffer), + "Address bus overlap at 0x%p expected 0x%x found 0x%x", + &mem_base[i], pattern, mem_base[i]); + __fatal_error(error_buffer); + #endif return false; } } - /* test all ram cells */ - if (!fast) { - for (uint32_t i = 0; i < MICROPY_HW_SDRAM_SIZE; ++i) { + // Test all RAM cells + if (exhaustive) { + // Write all memory first then compare, so even if the cache + // is enabled, it's not just writing and reading from cache. + // Note: This test should also detect refresh rate issues. + for (uint32_t i = 0; i < MICROPY_HW_SDRAM_SIZE; i++) { mem_base[i] = pattern; + } + + for (uint32_t i = 0; i < MICROPY_HW_SDRAM_SIZE; i++) { if (mem_base[i] != pattern) { - printf("address bus test failed! address (%p)\n", &mem_base[i]); + #if MICROPY_HW_SDRAM_TEST_FAIL_ON_ERROR + snprintf(error_buffer, sizeof(error_buffer), + "Address bus slow test failed at 0x%p expected 0x%x found 0x%x", + &mem_base[i], pattern, mem_base[i]); + __fatal_error(error_buffer); + #endif return false; } } - } else { - memset(mem_base, pattern, MICROPY_HW_SDRAM_SIZE); } + #if (__DCACHE_PRESENT == 1) + // Re-enable caches if they were enabled before the test started. + if (i_cache_disabled) { + SCB_EnableICache(); + } + + if (d_cache_disabled) { + SCB_EnableDCache(); + } + #endif + return true; } diff --git a/ports/stm32/sdram.h b/ports/stm32/sdram.h index 773a30802f..f7a124addd 100644 --- a/ports/stm32/sdram.h +++ b/ports/stm32/sdram.h @@ -13,5 +13,5 @@ void *sdram_start(void); void *sdram_end(void); void sdram_enter_low_power(void); void sdram_leave_low_power(void); -bool sdram_test(bool fast); +bool sdram_test(bool exhaustive); #endif // __SDRAM_H__ From a5ac3d5645e99a897f3213149587a43b62779c35 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 22 Jul 2021 16:18:54 +1000 Subject: [PATCH 122/264] samd: Add support for building with user C modules. Fixes issue #7545. Signed-off-by: Damien George --- ports/samd/Makefile | 19 ++++++++++++++++++- ports/samd/main.c | 5 +++++ ports/samd/sections.ld | 16 +++++++++++++--- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/ports/samd/Makefile b/ports/samd/Makefile index d29d9c5b24..a84fc3b12d 100644 --- a/ports/samd/Makefile +++ b/ports/samd/Makefile @@ -33,7 +33,11 @@ CFLAGS_MCU_SAMD21 = -mtune=cortex-m0plus -mcpu=cortex-m0plus -msoft-float CFLAGS_MCU_SAMD51 = -mtune=cortex-m4 -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard CFLAGS = $(INC) -Wall -Werror -std=c99 -nostdlib -mthumb $(CFLAGS_MCU_$(MCU_SERIES)) -fsingle-precision-constant -Wdouble-promotion CFLAGS += -DMCU_$(MCU_SERIES) -D__$(CMSIS_MCU)__ +CFLAGS += $(CFLAGS_MOD) + LDFLAGS = -nostdlib $(addprefix -T,$(LD_FILES)) -Map=$@.map --cref +LDFLAGS += $(LDFLAGS_MOD) + LIBS = $(shell $(CC) $(CFLAGS) -print-libgcc-file-name) # Tune for Debugging or Optimization @@ -45,6 +49,14 @@ LDFLAGS += --gc-sections CFLAGS += -fdata-sections -ffunction-sections endif +# Flags for optional C++ source code +CXXFLAGS += $(filter-out -std=c99,$(CFLAGS)) +CXXFLAGS += $(CXXFLAGS_MOD) +ifneq ($(SRC_CXX)$(SRC_MOD_CXX),) +LIBSTDCPP_FILE_NAME = "$(shell $(CXX) $(CXXFLAGS) -print-file-name=libstdc++.a)" +LDFLAGS += -L"$(shell dirname $(LIBSTDCPP_FILE_NAME))" +endif + SRC_C = \ main.c \ modutime.c \ @@ -70,6 +82,10 @@ SRC_C = \ shared/runtime/pyexec.c \ shared/runtime/stdout_helpers.c \ +SRC_C += $(SRC_MOD) + +SRC_CXX += $(SRC_MOD_CXX) + ifeq ($(MCU_SERIES),SAMD21) SRC_S = shared/runtime/gchelper_m0.s else @@ -77,10 +93,11 @@ SRC_S = shared/runtime/gchelper_m3.s endif # List of sources for qstr extraction -SRC_QSTR += modutime.c modmachine.c +SRC_QSTR += modutime.c modmachine.c $(SRC_MOD) $(SRC_CXX) OBJ += $(PY_O) OBJ += $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(SRC_CXX:.cpp=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_S:.s=.o)) # Workaround for bug in older gcc, warning on "static usbd_device_t _usbd_dev = { 0 };" diff --git a/ports/samd/main.c b/ports/samd/main.c index 1c72f04ab8..5d84a9e882 100644 --- a/ports/samd/main.c +++ b/ports/samd/main.c @@ -87,6 +87,11 @@ void nlr_jump_fail(void *val) { } } +void abort(void) { + for (;;) { + } +} + #ifndef NDEBUG void MP_WEAK __assert_func(const char *file, int line, const char *func, const char *expr) { mp_printf(MP_PYTHON_PRINTER, "Assertion '%s' failed, at file %s:%d\n", expr, file, line); diff --git a/ports/samd/sections.ld b/ports/samd/sections.ld index cbcad463d0..743e70a300 100644 --- a/ports/samd/sections.ld +++ b/ports/samd/sections.ld @@ -11,10 +11,20 @@ SECTIONS *(.rodata*) . = ALIGN(4); _etext = .; - _sidata = _etext; } >FLASH - .data : AT ( _sidata ) + /* For C++ exception handling */ + .ARM : + { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } >FLASH + + /* Used by the start-up code to initialise data */ + _sidata = LOADADDR(.data); + + .data : { . = ALIGN(4); _sdata = .; @@ -22,7 +32,7 @@ SECTIONS *(.data*) . = ALIGN(4); _edata = .; - } >RAM + } >RAM AT> FLASH .bss : { From 7f69246895ed4df78909c3558b91624ad3b3986c Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 22 Jul 2021 17:13:10 +1000 Subject: [PATCH 123/264] docs/library/uasyncio.rst: Document stream readexactly() method. Signed-off-by: Damien George --- docs/library/uasyncio.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/library/uasyncio.rst b/docs/library/uasyncio.rst index 0be3288296..11b9c6aee4 100644 --- a/docs/library/uasyncio.rst +++ b/docs/library/uasyncio.rst @@ -248,6 +248,14 @@ TCP stream connections This is a coroutine, and a MicroPython extension. +.. method:: Stream.readexactly(n) + + Read exactly *n* bytes and return them as a bytes object. + + Raises an ``EOFError`` exception if the stream ends before reading *n* bytes. + + This is a coroutine. + .. method:: Stream.readline() Read a line and return it. From 0256e1ab8b8ca376a9c4da83e375ff01ce1b8af0 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 22 Jul 2021 18:31:49 +1000 Subject: [PATCH 124/264] tools/autobuild: Use separate IDF version to build newer esp32 SoCs. Signed-off-by: Damien George --- tools/autobuild/autobuild.sh | 15 +++++++++++---- tools/autobuild/build-esp32-latest.sh | 12 ++++++++---- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/tools/autobuild/autobuild.sh b/tools/autobuild/autobuild.sh index 0498443811..a14c7890f7 100755 --- a/tools/autobuild/autobuild.sh +++ b/tools/autobuild/autobuild.sh @@ -4,7 +4,8 @@ # # Requirements: # - All toolchains must be in path (arm-none-eabi-gcc, xtensa-lx106-elf) -# - IDF_PATH_V4 must be set +# - IDF_PATH_V42 must be set +# - IDF_PATH_V43 must be set # - MICROPY_AUTOBUILD_MICROPYTHON_REPO must be set to location of micropython repository # - MICROPY_AUTOBUILD_MAKE must be set to the make command to use, eg "make -j2" # @@ -12,8 +13,13 @@ # - MICROPY_AUTOBUILD_REMOTE_MACHINE can be set to a remote ssh machine to copy files to # - MICROPY_AUTOBUILD_REMOTE_DIR can be set to destination directory on remote machine -if [ ! -d "$IDF_PATH_V4" ]; then - echo "must set IDF_PATH_V4" +if [ ! -d "$IDF_PATH_V42" ]; then + echo "must set IDF_PATH_V42" + exit 1 +fi + +if [ ! -d "$IDF_PATH_V43" ]; then + echo "must set IDF_PATH_V43" exit 1 fi @@ -63,7 +69,8 @@ ${AUTODIR}/build-cc3200-latest.sh ${FW_TAG} ${LOCAL_FIRMWARE} cd ../esp8266 ${AUTODIR}/build-esp8266-latest.sh ${FW_TAG} ${LOCAL_FIRMWARE} cd ../esp32 -${AUTODIR}/build-esp32-latest.sh ${IDF_PATH_V4} ${FW_TAG} ${LOCAL_FIRMWARE} +${AUTODIR}/build-esp32-latest.sh ${IDF_PATH_V42} ${FW_TAG} ${LOCAL_FIRMWARE} +${AUTODIR}/build-esp32-latest.sh ${IDF_PATH_V43} ${FW_TAG} ${LOCAL_FIRMWARE} cd ../rp2 ${AUTODIR}/build-rp2-latest.sh ${FW_TAG} ${LOCAL_FIRMWARE} cd ../mimxrt diff --git a/tools/autobuild/build-esp32-latest.sh b/tools/autobuild/build-esp32-latest.sh index e433332c9e..3f94dbb5be 100755 --- a/tools/autobuild/build-esp32-latest.sh +++ b/tools/autobuild/build-esp32-latest.sh @@ -39,7 +39,11 @@ fi source $idf_path/export.sh -# build the versions -do_build esp32 GENERIC FROZEN_MANIFEST=$(pwd)/boards/manifest_release.py -do_build esp32spiram GENERIC_SPIRAM FROZEN_MANIFEST=$(pwd)/boards/manifest_release.py -do_build tinypico UM_TINYPICO +# build the boards, based on the IDF version +if idf.py --version | grep -q v4.2; then + do_build esp32 GENERIC FROZEN_MANIFEST=$(pwd)/boards/manifest_release.py + do_build esp32spiram GENERIC_SPIRAM FROZEN_MANIFEST=$(pwd)/boards/manifest_release.py + do_build tinypico UM_TINYPICO +else + do_build esp32c3 GENERIC_C3 +fi From 25159129dd9e22474d09235ce6d7a4156fd13c6f Mon Sep 17 00:00:00 2001 From: Seon Rozenblum Date: Fri, 16 Jul 2021 12:50:32 +1000 Subject: [PATCH 125/264] tools/autobuild: Add FeatherS2 and TinyS2 to esp32 auto builds. --- tools/autobuild/build-esp32-latest.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/autobuild/build-esp32-latest.sh b/tools/autobuild/build-esp32-latest.sh index 3f94dbb5be..cc04d833e5 100755 --- a/tools/autobuild/build-esp32-latest.sh +++ b/tools/autobuild/build-esp32-latest.sh @@ -46,4 +46,6 @@ if idf.py --version | grep -q v4.2; then do_build tinypico UM_TINYPICO else do_build esp32c3 GENERIC_C3 + do_build tinys2 UM_TINYS2 + do_build featherS2 UM_FEATHERS2 fi From b099db4426c3a47e842c3e8afd5b5bb05cbcdd72 Mon Sep 17 00:00:00 2001 From: Roberto Colistete Jr Date: Fri, 24 Jul 2020 04:13:05 -0300 Subject: [PATCH 126/264] esp8266/Makefile: Add more libm files to build. Allows MICROPY_PY_MATH_SPECIAL_FUNCTIONS to be enabled, and for ulab to be built as a user C module. --- ports/esp8266/Makefile | 8 ++++++++ ports/esp8266/mpconfigport.h | 3 +++ 2 files changed, 11 insertions(+) diff --git a/ports/esp8266/Makefile b/ports/esp8266/Makefile index b54193afef..56c7cc87f6 100644 --- a/ports/esp8266/Makefile +++ b/ports/esp8266/Makefile @@ -123,21 +123,29 @@ LIB_SRC_C = $(addprefix lib/,\ libm/fmodf.c \ libm/nearbyintf.c \ libm/ef_sqrt.c \ + libm/erf_lgamma.c \ libm/kf_rem_pio2.c \ libm/kf_sin.c \ libm/kf_cos.c \ libm/kf_tan.c \ libm/ef_rem_pio2.c \ + libm/sf_erf.c \ libm/sf_sin.c \ libm/sf_cos.c \ libm/sf_tan.c \ libm/sf_frexp.c \ libm/sf_modf.c \ libm/sf_ldexp.c \ + libm/acoshf.c \ libm/asinfacosf.c \ + libm/asinhf.c \ libm/atanf.c \ + libm/atanhf.c \ libm/atan2f.c \ + libm/log1pf.c \ libm/roundf.c \ + libm/wf_lgamma.c \ + libm/wf_tgamma.c \ ) SHARED_SRC_C = $(addprefix shared/,\ diff --git a/ports/esp8266/mpconfigport.h b/ports/esp8266/mpconfigport.h index ac47f0aa29..8bc24e00eb 100644 --- a/ports/esp8266/mpconfigport.h +++ b/ports/esp8266/mpconfigport.h @@ -186,6 +186,9 @@ extern const struct _mp_obj_module_t mp_module_onewire; mp_obj_t pin_irq_handler[16]; \ byte *uart0_rxbuf; \ +// We need an implementation of the log2 function which is not a macro +#define MP_NEED_LOG2 (1) + // We need to provide a declaration/definition of alloca() #include From 753b08cae663b33465d92d3dd145b81f89b1b6ea Mon Sep 17 00:00:00 2001 From: Sashkoiv Date: Wed, 19 Feb 2020 10:30:44 +0200 Subject: [PATCH 127/264] stm32/boards/NUCLEO_F446RE: Enable CAN bus support. --- ports/stm32/boards/NUCLEO_F446RE/mpconfigboard.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ports/stm32/boards/NUCLEO_F446RE/mpconfigboard.h b/ports/stm32/boards/NUCLEO_F446RE/mpconfigboard.h index 30044ef2b4..681231578e 100644 --- a/ports/stm32/boards/NUCLEO_F446RE/mpconfigboard.h +++ b/ports/stm32/boards/NUCLEO_F446RE/mpconfigboard.h @@ -53,6 +53,12 @@ #define MICROPY_HW_SPI4_MISO (pin_A1) // pin 30 on CN7 #define MICROPY_HW_SPI4_MOSI (pin_A11) // pin 14 on CN10 +// CAN buses +#define MICROPY_HW_CAN1_TX (pin_B9) // pin 5 on CN10 +#define MICROPY_HW_CAN1_RX (pin_B8) // pin 3 on CN10 +#define MICROPY_HW_CAN2_TX (pin_B6) // pin 17 on CN10 +#define MICROPY_HW_CAN2_RX (pin_B5) // pin 29 on CN10 + // 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) From 14b853eae052f6d8ef285931336ca1212af76716 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 23 Jul 2021 12:02:03 +1000 Subject: [PATCH 128/264] minimal/Makefile: Add support for building with user C modules. Fixes issue #5750. Signed-off-by: Damien George --- ports/minimal/Makefile | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/ports/minimal/Makefile b/ports/minimal/Makefile index 0e79d581be..21e3fe3f76 100644 --- a/ports/minimal/Makefile +++ b/ports/minimal/Makefile @@ -41,6 +41,14 @@ CFLAGS += -Os -DNDEBUG CFLAGS += -fdata-sections -ffunction-sections endif +# Flags for optional C++ source code +CXXFLAGS += $(filter-out -std=c99,$(CFLAGS)) +CXXFLAGS += $(CXXFLAGS_MOD) + +# Flags for user C modules +CFLAGS += $(CFLAGS_MOD) +LDFLAGS += $(LDFLAGS_MOD) + LIBS = SRC_C = \ @@ -56,7 +64,14 @@ ifeq ($(CROSS), 1) SRC_C += shared/libc/string0.c endif -OBJ = $(PY_CORE_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) +SRC_C += $(SRC_MOD) + +SRC_CXX += $(SRC_MOD_CXX) + +SRC_QSTR += $(SRC_MOD) $(SRC_MOD_CXX) + +OBJ += $(PY_CORE_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(SRC_CXX:.cpp=.o)) ifeq ($(CROSS), 1) all: $(BUILD)/firmware.dfu From 4e39ff221abff9d1d7d7fc654da67a89a7543112 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Thu, 20 Feb 2020 22:30:49 +1100 Subject: [PATCH 129/264] py/runtime: Fix bool unary op for subclasses of native types. Previously a subclass of a type that didn't implement unary_op, or didn't handle MP_UNARY_OP_BOOL, would raise TypeError on bool conversion. Fixes #5677. --- py/runtime.c | 6 ++++++ tests/basics/unary_op.py | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/py/runtime.c b/py/runtime.c index 19686c310b..27e5bc13eb 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -284,6 +284,12 @@ mp_obj_t mp_unary_op(mp_unary_op_t op, mp_obj_t arg) { return result; } } + if (op == MP_UNARY_OP_BOOL) { + // Type doesn't have unary_op (or didn't handle MP_UNARY_OP_BOOL), + // so is implicitly True as this code path is impossible to reach + // if arg==mp_const_none. + return mp_const_true; + } // With MP_UNARY_OP_INT, mp_unary_op() becomes a fallback for mp_obj_get_int(). // In this case provide a more focused error message to not confuse, e.g. chr(1.0) #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE diff --git a/tests/basics/unary_op.py b/tests/basics/unary_op.py index bd78a20d0d..2239e2b206 100644 --- a/tests/basics/unary_op.py +++ b/tests/basics/unary_op.py @@ -23,5 +23,12 @@ print(not A()) # check user instances derived from builtins class B(int): pass print(not B()) +print(True if B() else False) class C(list): pass print(not C()) +print(True if C() else False) +# type doesn't define unary_op +class D(type): pass +d = D("foo", (), {}) +print(not d) +print(True if d else False) From 7870ec03701eb0bac44edf8c98e367a669918658 Mon Sep 17 00:00:00 2001 From: Michel Bouwmans Date: Tue, 13 Jul 2021 10:48:09 +0200 Subject: [PATCH 130/264] tools/mpremote: Add seek whence for mounted files. Fixes issue #7534. Signed-off-by: Michel Bouwmans --- tools/mpremote/mpremote/pyboardextended.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tools/mpremote/mpremote/pyboardextended.py b/tools/mpremote/mpremote/pyboardextended.py index e3f259b395..6f439e2dd0 100644 --- a/tools/mpremote/mpremote/pyboardextended.py +++ b/tools/mpremote/mpremote/pyboardextended.py @@ -26,6 +26,7 @@ fs_hook_cmds = { fs_hook_code = """\ import uos, uio, ustruct, micropython, usys +SEEK_SET = 0 class RemoteCommand: def __init__(self, use_second_port): @@ -213,11 +214,12 @@ class RemoteFile(uio.IOBase): c.end() return n - def seek(self, n): + def seek(self, n, whence=SEEK_SET): c = self.cmd c.begin(CMD_SEEK) c.wr_s8(self.fd) c.wr_s32(n) + c.wr_s8(whence) n = c.rd_s32() c.end() return n @@ -459,8 +461,9 @@ class PyboardCommand: def do_seek(self): fd = self.rd_s8() n = self.rd_s32() + whence = self.rd_s8() # self.log_cmd(f"seek {fd} {n}") - self.data_files[fd][0].seek(n) + n = self.data_files[fd][0].seek(n, whence) self.wr_s32(n) def do_write(self): From 92464f11b0db59456a7cf3f1181f38c200a29210 Mon Sep 17 00:00:00 2001 From: Michel Bouwmans Date: Thu, 22 Jul 2021 23:57:52 +0200 Subject: [PATCH 131/264] tools/mpremote: Raise OSError on unsupported RemoteFile.seek. Signed-off-by: Michel Bouwmans --- tools/mpremote/mpremote/pyboardextended.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tools/mpremote/mpremote/pyboardextended.py b/tools/mpremote/mpremote/pyboardextended.py index 6f439e2dd0..abdffb7532 100644 --- a/tools/mpremote/mpremote/pyboardextended.py +++ b/tools/mpremote/mpremote/pyboardextended.py @@ -1,4 +1,4 @@ -import os, re, serial, struct, time +import io, os, re, serial, struct, time from errno import EPERM from .console import VT_ENABLED @@ -222,6 +222,8 @@ class RemoteFile(uio.IOBase): c.wr_s8(whence) n = c.rd_s32() c.end() + if n < 0: + raise OSError(n) return n @@ -463,7 +465,10 @@ class PyboardCommand: n = self.rd_s32() whence = self.rd_s8() # self.log_cmd(f"seek {fd} {n}") - n = self.data_files[fd][0].seek(n, whence) + try: + n = self.data_files[fd][0].seek(n, whence) + except io.UnsupportedOperation: + n = -1 self.wr_s32(n) def do_write(self): From 53dfb279da4c2502c5040227329b06e3ea79fd63 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Wed, 21 Jul 2021 17:02:01 +1000 Subject: [PATCH 132/264] extmod/modbluetooth: Clamp MTU values to 32->UINT16_MAX. --- extmod/modbluetooth.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extmod/modbluetooth.c b/extmod/modbluetooth.c index e379a8c6a3..bf8d071a94 100644 --- a/extmod/modbluetooth.c +++ b/extmod/modbluetooth.c @@ -842,7 +842,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(bluetooth_ble_gattc_exchange_mtu_obj, bluetooth STATIC mp_obj_t bluetooth_ble_l2cap_listen(mp_obj_t self_in, mp_obj_t psm_in, mp_obj_t mtu_in) { (void)self_in; mp_int_t psm = mp_obj_get_int(psm_in); - mp_int_t mtu = mp_obj_get_int(mtu_in); + mp_int_t mtu = MAX(32, MIN(UINT16_MAX, mp_obj_get_int(mtu_in))); return bluetooth_handle_errno(mp_bluetooth_l2cap_listen(psm, mtu)); } STATIC MP_DEFINE_CONST_FUN_OBJ_3(bluetooth_ble_l2cap_listen_obj, bluetooth_ble_l2cap_listen); @@ -850,7 +850,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_3(bluetooth_ble_l2cap_listen_obj, bluetooth_ble_l STATIC mp_obj_t bluetooth_ble_l2cap_connect(size_t n_args, const mp_obj_t *args) { mp_int_t conn_handle = mp_obj_get_int(args[1]); mp_int_t psm = mp_obj_get_int(args[2]); - mp_int_t mtu = mp_obj_get_int(args[3]); + mp_int_t mtu = MAX(32, MIN(UINT16_MAX, mp_obj_get_int(args[3]))); return bluetooth_handle_errno(mp_bluetooth_l2cap_connect(conn_handle, psm, mtu)); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bluetooth_ble_l2cap_connect_obj, 4, 4, bluetooth_ble_l2cap_connect); From edfb5d56c8392b1c0933abf49e757b90f2c07859 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Wed, 21 Jul 2021 17:04:41 +1000 Subject: [PATCH 133/264] extmod/nimble: Allow modbluetooth binding to hook "sent HCI packet". --- extmod/nimble/hal/hal_uart.c | 6 ++++++ extmod/nimble/modbluetooth_nimble.c | 3 +++ extmod/nimble/modbluetooth_nimble.h | 3 +++ 3 files changed, 12 insertions(+) diff --git a/extmod/nimble/hal/hal_uart.c b/extmod/nimble/hal/hal_uart.c index cd5e49e30b..84a964fde9 100644 --- a/extmod/nimble/hal/hal_uart.c +++ b/extmod/nimble/hal/hal_uart.c @@ -27,6 +27,7 @@ #include "py/runtime.h" #include "py/mphal.h" #include "nimble/ble.h" +#include "extmod/nimble/modbluetooth_nimble.h" #include "extmod/nimble/hal/hal_uart.h" #include "extmod/nimble/nimble/nimble_npl_os.h" #include "extmod/mpbthci.h" @@ -74,6 +75,11 @@ void hal_uart_start_tx(uint32_t port) { #endif mp_bluetooth_hci_uart_write(mp_bluetooth_hci_cmd_buf, len); + + if (len > 0) { + // Allow modbluetooth bindings to hook "sent packet" (e.g. to unstall l2cap channels). + mp_bluetooth_nimble_sent_hci_packet(); + } } int hal_uart_close(uint32_t port) { diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c index 8bbec54881..f3679354f4 100644 --- a/extmod/nimble/modbluetooth_nimble.c +++ b/extmod/nimble/modbluetooth_nimble.c @@ -1392,6 +1392,9 @@ int mp_bluetooth_gattc_exchange_mtu(uint16_t conn_handle) { #endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT +void mp_bluetooth_nimble_sent_hci_packet(void) { +} + #if MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS // Fortunately NimBLE uses mbuf chains correctly with L2CAP COC (rather than diff --git a/extmod/nimble/modbluetooth_nimble.h b/extmod/nimble/modbluetooth_nimble.h index 9ed64368b2..15648a9959 100644 --- a/extmod/nimble/modbluetooth_nimble.h +++ b/extmod/nimble/modbluetooth_nimble.h @@ -71,5 +71,8 @@ void mp_bluetooth_nimble_port_start(void); // Tell the port to stop its background task. void mp_bluetooth_nimble_port_shutdown(void); +// --- Called by the HCI UART layer to let us know when packets have been sent. +void mp_bluetooth_nimble_sent_hci_packet(void); + #endif // MICROPY_INCLUDED_EXTMOD_NIMBLE_MODBLUETOOTH_NIMBLE_H From 341158c251c032fe1116395d60ea69c6dc406898 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Wed, 21 Jul 2021 17:47:13 +1000 Subject: [PATCH 134/264] extmod/nimble: Add "memory stalling" mechanism for l2cap_send. When l2cap_send detects that the sys mempool is running low (used to store the outgoing HCI payloads), it will report stalled back to the application, and then only unstall once these HCI payloads have been sent. This prevents a situation where a remote receiver with very large MTU can cause NimBLE to queue up more than MYNEWT_VAL_MSYS_1_BLOCK_COUNT (i.e. 12) payloads, causing further attempts to send to fail with ENOMEM (even though the channel is not stalled and we have room in the channel mbufs). The regular credit/stall flow control is not effective here because the receiver's MTU is large enough that it will not activate (i.e. there are lots of credits available). Thresholds of 1/2 (stall) and 1/4 (unstall) chosen to allow headroom for other payloads (e.g. notifications) and that when a regular stall occurs it might keep sending (and creating more payloads) in the background. --- extmod/nimble/modbluetooth_nimble.c | 48 +++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c index f3679354f4..d7ad7e17c9 100644 --- a/extmod/nimble/modbluetooth_nimble.c +++ b/extmod/nimble/modbluetooth_nimble.c @@ -1392,7 +1392,16 @@ int mp_bluetooth_gattc_exchange_mtu(uint16_t conn_handle) { #endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT +#if MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS +STATIC void unstall_l2cap_channel(void); +#endif + void mp_bluetooth_nimble_sent_hci_packet(void) { + #if MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS + if (os_msys_num_free() >= os_msys_count() * 3 / 4) { + unstall_l2cap_channel(); + } + #endif } #if MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS @@ -1416,6 +1425,7 @@ typedef struct _mp_bluetooth_nimble_l2cap_channel_t { struct os_mempool sdu_mempool; struct os_mbuf *rx_pending; bool irq_in_progress; + bool mem_stalled; uint16_t mtu; os_membuf_t sdu_mem[]; } mp_bluetooth_nimble_l2cap_channel_t; @@ -1433,6 +1443,19 @@ STATIC void destroy_l2cap_channel() { } } +STATIC void unstall_l2cap_channel(void) { + // Whenever we send an HCI packet and the sys mempool is now less than 1/4 full, + // we can unstall the L2CAP channel if it was marked as "mem_stalled" by + // mp_bluetooth_l2cap_send. (This happens if the pool is half-empty). + mp_bluetooth_nimble_l2cap_channel_t *chan = MP_STATE_PORT(bluetooth_nimble_root_pointers)->l2cap_chan; + if (!chan || !chan->mem_stalled) { + return; + } + DEBUG_printf("unstall_l2cap_channel: count %d, free: %d\n", os_msys_count(), os_msys_num_free()); + chan->mem_stalled = false; + mp_bluetooth_on_l2cap_send_ready(chan->chan->conn_handle, chan->chan->scid, 0); +} + STATIC int l2cap_channel_event(struct ble_l2cap_event *event, void *arg) { DEBUG_printf("l2cap_channel_event: type=%d\n", event->type); mp_bluetooth_nimble_l2cap_channel_t *chan = (mp_bluetooth_nimble_l2cap_channel_t *)arg; @@ -1528,9 +1551,13 @@ STATIC int l2cap_channel_event(struct ble_l2cap_event *event, void *arg) { } case BLE_L2CAP_EVENT_COC_TX_UNSTALLED: { DEBUG_printf("l2cap_channel_event: tx_unstalled: conn_handle=%d status=%d\n", event->tx_unstalled.conn_handle, event->tx_unstalled.status); - ble_l2cap_get_chan_info(event->receive.chan, &info); - // Map status to {0,1} (i.e. "sent everything", or "partial send"). - mp_bluetooth_on_l2cap_send_ready(event->tx_unstalled.conn_handle, info.scid, event->tx_unstalled.status == 0 ? 0 : 1); + assert(event->tx_unstalled.conn_handle == chan->chan->conn_handle); + // Don't unstall if we're still waiting for room in the sys pool. + if (!chan->mem_stalled) { + ble_l2cap_get_chan_info(event->receive.chan, &info); + // Map status to {0,1} (i.e. "sent everything", or "partial send"). + mp_bluetooth_on_l2cap_send_ready(event->tx_unstalled.conn_handle, info.scid, event->tx_unstalled.status == 0 ? 0 : 1); + } break; } case BLE_L2CAP_EVENT_COC_RECONFIG_COMPLETED: { @@ -1678,10 +1705,13 @@ int mp_bluetooth_l2cap_send(uint16_t conn_handle, uint16_t cid, const uint8_t *b return MP_ENOMEM; } + *stalled = false; + err = ble_l2cap_send(chan->chan, sdu_tx); if (err == BLE_HS_ESTALLED) { // Stalled means that this one will still send but any future ones // will fail until we receive an unstalled event. + DEBUG_printf("mp_bluetooth_l2cap_send: credit stall\n"); *stalled = true; err = 0; } else { @@ -1689,15 +1719,21 @@ int mp_bluetooth_l2cap_send(uint16_t conn_handle, uint16_t cid, const uint8_t *b // Anything except stalled means it won't attempt to send, // so free the mbuf (we're failing the op entirely). os_mbuf_free_chain(sdu_tx); - } else { - *stalled = false; } } + if (os_msys_num_free() <= os_msys_count() / 2) { + // If the sys mempool is less than half-full, then back off sending more + // on this channel. + DEBUG_printf("mp_bluetooth_l2cap_send: forcing mem stall: count %d, free: %d\n", os_msys_count(), os_msys_num_free()); + chan->mem_stalled = true; + *stalled = true; + } + // Sometimes we see what looks like BLE_HS_EAGAIN (but it's actually // OS_ENOMEM in disguise). Fixed in NimBLE v1.4. if (err == OS_ENOMEM) { - return MP_ENOMEM; + err = BLE_HS_ENOMEM; } // Other error codes such as BLE_HS_EBUSY (we're stalled) or BLE_HS_EBADDATA (bigger than MTU). From aecb697c72ac1fdb9f549daa5734e2ffbd37a6d3 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sun, 11 Jul 2021 16:39:56 -0700 Subject: [PATCH 135/264] stm32/boards: Add support for SparkFun STM32 MicroMod Processor board. Signed-off-by: Chris Wilson --- .../boards/SPARKFUN_MICROMOD_STM32/bdev.c | 28 +++++ .../SPARKFUN_MICROMOD_STM32/board_init.c | 11 ++ .../SPARKFUN_MICROMOD_STM32/mpconfigboard.h | 100 ++++++++++++++++++ .../SPARKFUN_MICROMOD_STM32/mpconfigboard.mk | 16 +++ .../boards/SPARKFUN_MICROMOD_STM32/pins.csv | 57 ++++++++++ .../stm32f4xx_hal_conf.h | 19 ++++ 6 files changed, 231 insertions(+) create mode 100644 ports/stm32/boards/SPARKFUN_MICROMOD_STM32/bdev.c create mode 100644 ports/stm32/boards/SPARKFUN_MICROMOD_STM32/board_init.c create mode 100644 ports/stm32/boards/SPARKFUN_MICROMOD_STM32/mpconfigboard.h create mode 100644 ports/stm32/boards/SPARKFUN_MICROMOD_STM32/mpconfigboard.mk create mode 100644 ports/stm32/boards/SPARKFUN_MICROMOD_STM32/pins.csv create mode 100644 ports/stm32/boards/SPARKFUN_MICROMOD_STM32/stm32f4xx_hal_conf.h diff --git a/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/bdev.c b/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/bdev.c new file mode 100644 index 0000000000..18b5b85b18 --- /dev/null +++ b/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/bdev.c @@ -0,0 +1,28 @@ +#include "storage.h" + +#if !MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE + +// External SPI flash uses standard SPI interface + +STATIC const mp_soft_spi_obj_t soft_spi_bus = { + .delay_half = MICROPY_HW_SOFTSPI_MIN_DELAY, + .polarity = 0, + .phase = 0, + .sck = MICROPY_HW_SPIFLASH_SCK, + .mosi = MICROPY_HW_SPIFLASH_MOSI, + .miso = MICROPY_HW_SPIFLASH_MISO, +}; + +STATIC mp_spiflash_cache_t spi_bdev_cache; + +const mp_spiflash_config_t spiflash_config = { + .bus_kind = MP_SPIFLASH_BUS_SPI, + .bus.u_spi.cs = MICROPY_HW_SPIFLASH_CS, + .bus.u_spi.data = (void*)&soft_spi_bus, + .bus.u_spi.proto = &mp_soft_spi_proto, + .cache = &spi_bdev_cache, +}; + +spi_bdev_t spi_bdev; + +#endif diff --git a/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/board_init.c b/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/board_init.c new file mode 100644 index 0000000000..0184bc8205 --- /dev/null +++ b/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/board_init.c @@ -0,0 +1,11 @@ +#include "py/mphal.h" + +void board_early_init(void) { + +#if !MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE + // set external SPI flash CS pin high + mp_hal_pin_output(MICROPY_HW_SPIFLASH_CS); + mp_hal_pin_write(MICROPY_HW_SPIFLASH_CS, 1); +#endif + +} diff --git a/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/mpconfigboard.h b/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/mpconfigboard.h new file mode 100644 index 0000000000..0c451a4dbb --- /dev/null +++ b/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/mpconfigboard.h @@ -0,0 +1,100 @@ +// The Sparkfun MicroMod spec uses a zero-based peripheral numbering scheme. +// In cases where the 0th peripheral is the default, the "0" is omitted from +// the name (e.g. "I2C" instead of "I2C0"). +// +// Note: UART (UART0) is not present in the edge connector pinout because the +// primary debug serial port is exposed as a virtual serial port over USB, +// i.e. Serial.print() should print over USB VCP, not UART_TX1. +// +// For more details, see https://www.sparkfun.com/micromod#tech-specs + +#define MICROPY_HW_BOARD_NAME "SparkFun STM32 MicroMod Processor" +#define MICROPY_HW_MCU_NAME "STM32F405RG" + +// 1 = use STM32 internal flash (1 MByte) +// 0 = use onboard external SPI flash (16 MByte) +#define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (0) + +#define MICROPY_HW_HAS_FLASH (1) +#define MICROPY_HW_ENABLE_RNG (1) +#define MICROPY_HW_ENABLE_RTC (1) +#define MICROPY_HW_ENABLE_DAC (1) +#define MICROPY_HW_ENABLE_USB (1) + +// External SPI Flash config +#if !MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE + +// 128 Mbit (16 MByte) external SPI flash +#define MICROPY_HW_SPIFLASH_SIZE_BITS (128 * 1024 * 1024) + +#define MICROPY_HW_SPIFLASH_CS (pin_C3) +#define MICROPY_HW_SPIFLASH_SCK (pin_C10) +#define MICROPY_HW_SPIFLASH_MOSI (pin_C12) +#define MICROPY_HW_SPIFLASH_MISO (pin_C11) + +#define MICROPY_BOARD_EARLY_INIT board_early_init +void board_early_init(void); + +extern const struct _mp_spiflash_config_t spiflash_config; +extern struct _spi_bdev_t spi_bdev; +#define MICROPY_HW_SPIFLASH_ENABLE_CACHE (1) +#define MICROPY_HW_BDEV_IOCTL(op, arg) ( \ + (op) == BDEV_IOCTL_NUM_BLOCKS ? (MICROPY_HW_SPIFLASH_SIZE_BITS / 8 / FLASH_BLOCK_SIZE) : \ + (op) == BDEV_IOCTL_INIT ? spi_bdev_ioctl(&spi_bdev, (op), (uint32_t)&spiflash_config) : \ + spi_bdev_ioctl(&spi_bdev, (op), (arg)) \ +) +#define MICROPY_HW_BDEV_READBLOCKS(dest, bl, n) spi_bdev_readblocks(&spi_bdev, (dest), (bl), (n)) +#define MICROPY_HW_BDEV_WRITEBLOCKS(src, bl, n) spi_bdev_writeblocks(&spi_bdev, (src), (bl), (n)) +#define MICROPY_HW_BDEV_SPIFLASH_EXTENDED (&spi_bdev) // for extended block protocol + +#endif // !MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE + +// STM32 HSE config +// The module has a 12 MHz crystal for the HSE oscillator. +#define MICROPY_HW_CLK_PLLM (12) +#define MICROPY_HW_CLK_PLLN (336) +#define MICROPY_HW_CLK_PLLP (RCC_PLLP_DIV2) +#define MICROPY_HW_CLK_PLLQ (7) +#define MICROPY_HW_CLK_LAST_FREQ (1) + +// STM32 LSE config +// The module has a 32.768 kHz crystal for the LSE (RTC). +#define MICROPY_HW_RTC_USE_LSE (1) +#define MICROPY_HW_RTC_USE_US (0) +#define MICROPY_HW_RTC_USE_CALOUT (1) + +// UART1 config (MicroMod UART1) +#define MICROPY_HW_UART1_NAME "UART1" +#define MICROPY_HW_UART1_TX (pin_A2) +#define MICROPY_HW_UART1_RX (pin_A3) + +// CAN1 config (MicroMod CAN) +#define MICROPY_HW_CAN1_NAME "CAN" +#define MICROPY_HW_CAN1_TX (pin_B9) +#define MICROPY_HW_CAN1_RX (pin_B8) + +// I2C1 config (MicroMod I2C) +#define MICROPY_HW_I2C1_NAME "I2C" +#define MICROPY_HW_I2C1_SCL (pin_B10) +#define MICROPY_HW_I2C1_SDA (pin_B11) + +// I2C2 config (MicroMod I2C1) +#define MICROPY_HW_I2C2_NAME "I2C1" +#define MICROPY_HW_I2C2_SCL (pin_B6) +#define MICROPY_HW_I2C2_SDA (pin_B7) + +// SPI1 config (MicroMod SPI) +#define MICROPY_HW_SPI1_NAME "SPI" +#define MICROPY_HW_SPI1_NSS (pin_C4) +#define MICROPY_HW_SPI1_SCK (pin_A5) +#define MICROPY_HW_SPI1_MISO (pin_A6) +#define MICROPY_HW_SPI1_MOSI (pin_A7) + +// LED1 config +// The module has a single blue status LED. +#define MICROPY_HW_LED1 (pin_A15) +#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_high(pin)) +#define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_low(pin)) + +// USB device config +#define MICROPY_HW_USB_FS (1) diff --git a/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/mpconfigboard.mk b/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/mpconfigboard.mk new file mode 100644 index 0000000000..cb78a7846d --- /dev/null +++ b/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/mpconfigboard.mk @@ -0,0 +1,16 @@ +MCU_SERIES = f4 +CMSIS_MCU = STM32F405xx +AF_FILE = boards/stm32f405_af.csv +ifeq ($(USE_MBOOT),1) +# When using Mboot all the text goes together after the filesystem +LD_FILES = boards/stm32f405.ld boards/common_blifs.ld +TEXT0_ADDR = 0x08020000 +else +# When not using Mboot the ISR text goes first, then the rest after the filesystem +LD_FILES = boards/stm32f405.ld boards/common_ifs.ld +TEXT0_ADDR = 0x08000000 +TEXT1_ADDR = 0x08020000 +endif + +# MicroPython settings +MICROPY_VFS_LFS2 = 1 diff --git a/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/pins.csv b/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/pins.csv new file mode 100644 index 0000000000..5e77de3005 --- /dev/null +++ b/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/pins.csv @@ -0,0 +1,57 @@ +G2,PA0 +BUS2,PA0 +BATT_SENSE,PA1 +UART_TX1,PA2 +UART_RX1,PA3 +AUD_LRCLK,PA4 +SPI_SCK,PA5 +SPI_MISO,PA6 +SPI_CIPO,PA6 +SPI_MOSI,PA7 +SPI_COPI,PA7 +G1,PA8 +BUS1,PA8 +USB_DN,PA11 +USB_DP,PA12 +SWDIO,PA13 +SWDCK,PA14 +STATUS_LED,PA15 +A1,PB0 +I2C_INT,PB1 +AUD_BCLK,PB3 +AUD_OUT,PB4 +AUD_IN,PB5 +I2C_SCL1,PB6 +I2C_SDA1,PB7 +CAN_RX,PB8 +CAN_TX,PB9 +I2C_SCL,PB10 +I2C_SDA,PB11 +USB_HOST_ID,PB12 +G11,PB12 +USB_HOST_VBUS,PB13 +G10,PB13 +USB_HOST_DN,PB14 +USB_HOST_DP,PB15 +D0,PC0 +D1,PC1 +G6,PC2 +BUS6,PC2 +SPI_FLASH_CS,PC3 +SPI_CS,PC4 +A0,PC5 +PWM0,PC6 +PWM1,PC7 +G3,PC8 +BUS3,PC8 +G4,PC9 +BUS4,PC9 +SPI_FLASH_SCK,PC10 +SPI_FLASH_MISO,PC11 +SPI_FLASH_MOSI,PC12 +G5,PC13 +BUS5,PC13 +OSC32_IN,PC14 +OSC32_OUT,PC15 +G0,PD2 +BUS0,PD2 diff --git a/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/stm32f4xx_hal_conf.h b/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/stm32f4xx_hal_conf.h new file mode 100644 index 0000000000..9719157e55 --- /dev/null +++ b/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/stm32f4xx_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_STM32F4XX_HAL_CONF_H +#define MICROPY_INCLUDED_STM32F4XX_HAL_CONF_H + +#include "boards/stm32f4xx_hal_conf_base.h" + +// Oscillator values in Hz +#define HSE_VALUE (12000000) +#define LSE_VALUE (32768) +#define EXTERNAL_CLOCK_VALUE (12288000) + +// Oscillator timeouts in ms +#define HSE_STARTUP_TIMEOUT (100) +#define LSE_STARTUP_TIMEOUT (5000) + +#endif // MICROPY_INCLUDED_STM32F4XX_HAL_CONF_H From 2e62e13455780517047c32279fc8bf2812810602 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Mon, 19 Jul 2021 23:58:26 +0200 Subject: [PATCH 136/264] rp2/machine_uart: Fix poll ioctl to also check hardware FIFO. The RX IRQ does not trigger if the FIFO is less than the trigger level, in which case characters may be available in the FIFO, yet not in the ringbuf, and the ioctl returns false. --- ports/rp2/machine_uart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/rp2/machine_uart.c b/ports/rp2/machine_uart.c index c6206cebe6..2431f496eb 100644 --- a/ports/rp2/machine_uart.c +++ b/ports/rp2/machine_uart.c @@ -477,7 +477,7 @@ STATIC mp_uint_t machine_uart_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint if (request == MP_STREAM_POLL) { uintptr_t flags = arg; ret = 0; - if ((flags & MP_STREAM_POLL_RD) && ringbuf_avail(&self->read_buffer) > 0) { + if ((flags & MP_STREAM_POLL_RD) && (uart_is_readable(self->uart) || ringbuf_avail(&self->read_buffer) > 0)) { ret |= MP_STREAM_POLL_RD; } if ((flags & MP_STREAM_POLL_WR) && ringbuf_free(&self->write_buffer) > 0) { From ee49ae8f82c22b43777aa9e73a1b7a6825304d13 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Fri, 23 Jul 2021 14:04:36 +0200 Subject: [PATCH 137/264] rp2/machine_uart: Fix read when FIFO has chars but ringbuf doesn't. Prior to this fix, if the UART hardware FIFO had a few chars but still below the FIFO trigger threshold, and the ringbuf was empty, the read function would timeout if timeout==0 (the default timeout). This fix follows the suggestion of @iabdalkader. --- ports/rp2/machine_uart.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/ports/rp2/machine_uart.c b/ports/rp2/machine_uart.c index 2431f496eb..a8ec149f4b 100644 --- a/ports/rp2/machine_uart.c +++ b/ports/rp2/machine_uart.c @@ -407,6 +407,13 @@ STATIC mp_uint_t machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t siz for (size_t i = 0; i < size; i++) { // Wait for the first/next character while (ringbuf_avail(&self->read_buffer) == 0) { + if (uart_is_readable(self->uart)) { + // Force a few incoming bytes to the buffer + self->read_lock = true; + uart_drain_rx_fifo(self); + self->read_lock = false; + break; + } if (time_us_64() > t) { // timed out if (i <= 0) { *errcode = MP_EAGAIN; @@ -416,10 +423,6 @@ STATIC mp_uint_t machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t siz } } MICROPY_EVENT_POLL_HOOK - // Force a few incoming bytes to the buffer - self->read_lock = true; - uart_drain_rx_fifo(self); - self->read_lock = false; } *dest++ = ringbuf_get(&(self->read_buffer)); t = time_us_64() + timeout_char_us; From fef21144047201d639b2692e3a55083c7180cb29 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 26 Jul 2021 13:03:42 +1000 Subject: [PATCH 138/264] stm32/uart: Fix LPUART1 baudrate set/get. It needs to use a different function because the formula to compute the baudrate on LPUART1 is different to a normal UART. Fixes issue #7466. Signed-off-by: Damien George --- ports/stm32/boards/stm32h7xx_hal_conf_base.h | 1 + ports/stm32/boards/stm32l0xx_hal_conf_base.h | 1 + ports/stm32/boards/stm32l4xx_hal_conf_base.h | 1 + ports/stm32/boards/stm32wbxx_hal_conf_base.h | 1 + ports/stm32/uart.c | 19 +++++++++++++++++++ 5 files changed, 23 insertions(+) diff --git a/ports/stm32/boards/stm32h7xx_hal_conf_base.h b/ports/stm32/boards/stm32h7xx_hal_conf_base.h index c07ae93e37..08928ed593 100644 --- a/ports/stm32/boards/stm32h7xx_hal_conf_base.h +++ b/ports/stm32/boards/stm32h7xx_hal_conf_base.h @@ -54,6 +54,7 @@ #include "stm32h7xx_hal_usart.h" #include "stm32h7xx_hal_wwdg.h" #include "stm32h7xx_ll_adc.h" +#include "stm32h7xx_ll_lpuart.h" #include "stm32h7xx_ll_pwr.h" #include "stm32h7xx_ll_rtc.h" #include "stm32h7xx_ll_usart.h" diff --git a/ports/stm32/boards/stm32l0xx_hal_conf_base.h b/ports/stm32/boards/stm32l0xx_hal_conf_base.h index cc033666af..7b569907e8 100644 --- a/ports/stm32/boards/stm32l0xx_hal_conf_base.h +++ b/ports/stm32/boards/stm32l0xx_hal_conf_base.h @@ -47,6 +47,7 @@ #include "stm32l0xx_hal_usart.h" #include "stm32l0xx_hal_wwdg.h" #include "stm32l0xx_ll_adc.h" +#include "stm32l0xx_ll_lpuart.h" #include "stm32l0xx_ll_rtc.h" #include "stm32l0xx_ll_usart.h" diff --git a/ports/stm32/boards/stm32l4xx_hal_conf_base.h b/ports/stm32/boards/stm32l4xx_hal_conf_base.h index 8d77a80a70..4f3a78d509 100644 --- a/ports/stm32/boards/stm32l4xx_hal_conf_base.h +++ b/ports/stm32/boards/stm32l4xx_hal_conf_base.h @@ -51,6 +51,7 @@ #include "stm32l4xx_hal_usart.h" #include "stm32l4xx_hal_wwdg.h" #include "stm32l4xx_ll_adc.h" +#include "stm32l4xx_ll_lpuart.h" #include "stm32l4xx_ll_rtc.h" #include "stm32l4xx_ll_usart.h" diff --git a/ports/stm32/boards/stm32wbxx_hal_conf_base.h b/ports/stm32/boards/stm32wbxx_hal_conf_base.h index 72af0262a2..b03ad26864 100644 --- a/ports/stm32/boards/stm32wbxx_hal_conf_base.h +++ b/ports/stm32/boards/stm32wbxx_hal_conf_base.h @@ -42,6 +42,7 @@ #include "stm32wbxx_hal_uart.h" #include "stm32wbxx_hal_usart.h" #include "stm32wbxx_ll_adc.h" +#include "stm32wbxx_ll_lpuart.h" #include "stm32wbxx_ll_rtc.h" #include "stm32wbxx_ll_usart.h" diff --git a/ports/stm32/uart.c b/ports/stm32/uart.c index b15be85336..002b1f168d 100644 --- a/ports/stm32/uart.c +++ b/ports/stm32/uart.c @@ -746,6 +746,15 @@ uint32_t uart_get_source_freq(pyb_uart_obj_t *self) { } 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) + , self->uartx->PRESC + #endif + ); + } + #endif return LL_USART_GetBaudRate(self->uartx, uart_get_source_freq(self), #if defined(STM32H7) || defined(STM32WB) self->uartx->PRESC, @@ -754,6 +763,16 @@ uint32_t uart_get_baudrate(pyb_uart_obj_t *self) { } 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) + LL_LPUART_PRESCALER_DIV1, + #endif + baudrate); + return; + } + #endif LL_USART_SetBaudRate(self->uartx, uart_get_source_freq(self), #if defined(STM32H7) || defined(STM32WB) LL_USART_PRESCALER_DIV1, From aa0cf873bf2c475cf2798461a5e9927e31a53012 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 26 Jul 2021 13:21:30 +1000 Subject: [PATCH 139/264] stm32/uart: Support low baudrates on LPUART1. By selecting a larger prescaler when needed. Signed-off-by: Damien George --- ports/stm32/uart.c | 22 +++++++++++++++++++++- ports/stm32/uart.h | 1 + 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/ports/stm32/uart.c b/ports/stm32/uart.c index 002b1f168d..d2953b2646 100644 --- a/ports/stm32/uart.c +++ b/ports/stm32/uart.c @@ -488,7 +488,7 @@ bool uart_init(pyb_uart_obj_t *uart_obj, uart_obj->uartx = UARTx; - // init UARTx + // Set the initialisation parameters for the UART. UART_HandleTypeDef huart; memset(&huart, 0, sizeof(huart)); huart.Instance = UARTx; @@ -499,6 +499,26 @@ 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(STM32F4) + huart.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE; + #endif + + #if defined(STM32H7) || defined(STM32WB) + // Compute the smallest prescaler that will allow the given baudrate. + uint32_t presc = UART_PRESCALER_DIV1; + if (uart_obj->uart_id == PYB_LPUART_1) { + uint32_t source_clk = uart_get_source_freq(uart_obj); + for (; presc < UART_PRESCALER_DIV256; ++presc) { + uint32_t brr = UART_DIV_LPUART(source_clk, baudrate, presc); + if (brr <= LPUART_BRR_MASK) { + break; + } + } + } + huart.Init.ClockPrescaler = presc; + #endif + + // Initialise the UART hardware. HAL_UART_Init(&huart); // Disable all individual UART IRQs, but enable the global handler diff --git a/ports/stm32/uart.h b/ports/stm32/uart.h index 286b6cdec0..0a268ad465 100644 --- a/ports/stm32/uart.h +++ b/ports/stm32/uart.h @@ -88,6 +88,7 @@ void uart_deinit(pyb_uart_obj_t *uart_obj); void uart_irq_handler(mp_uint_t uart_id); void uart_attach_to_repl(pyb_uart_obj_t *self, bool attached); +uint32_t uart_get_source_freq(pyb_uart_obj_t *self); uint32_t uart_get_baudrate(pyb_uart_obj_t *self); void uart_set_baudrate(pyb_uart_obj_t *self, uint32_t baudrate); From 0f0006f4e15f710b4d2fa6a27cb932f580194729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=BA=C5=A1=20Olek=C5=A1=C3=A1k?= Date: Mon, 4 Jan 2021 20:27:46 +0100 Subject: [PATCH 140/264] stm32/boards/STM32F429DISC: Set correct UART2 pins and add UART3/6. --- ports/stm32/boards/STM32F429DISC/mpconfigboard.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ports/stm32/boards/STM32F429DISC/mpconfigboard.h b/ports/stm32/boards/STM32F429DISC/mpconfigboard.h index 95cab08c8f..cfd9ef9d08 100644 --- a/ports/stm32/boards/STM32F429DISC/mpconfigboard.h +++ b/ports/stm32/boards/STM32F429DISC/mpconfigboard.h @@ -19,8 +19,12 @@ // UART config #define MICROPY_HW_UART1_TX (pin_A9) #define MICROPY_HW_UART1_RX (pin_A10) -#define MICROPY_HW_UART2_TX (pin_D8) -#define MICROPY_HW_UART2_RX (pin_D9) +#define MICROPY_HW_UART2_TX (pin_D5) +#define MICROPY_HW_UART2_RX (pin_D6) +#define MICROPY_HW_UART3_TX (pin_C10) +#define MICROPY_HW_UART3_RX (pin_C11) +#define MICROPY_HW_UART6_TX (pin_C6) +#define MICROPY_HW_UART6_RX (pin_C7) // I2C buses #define MICROPY_HW_I2C3_SCL (pin_A8) From 224ac355cd39eea4a6717692446c4eaf1949be77 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 27 Jul 2021 16:46:10 +1000 Subject: [PATCH 141/264] stm32/boards/NUCLEO_F439ZI: Add board definition for NUCLEO_F439ZI. Signed-off-by: Damien George --- .../boards/NUCLEO_F439ZI/mpconfigboard.h | 81 +++++++++++ .../boards/NUCLEO_F439ZI/mpconfigboard.mk | 11 ++ ports/stm32/boards/NUCLEO_F439ZI/pins.csv | 129 ++++++++++++++++++ .../boards/NUCLEO_F439ZI/stm32f4xx_hal_conf.h | 19 +++ 4 files changed, 240 insertions(+) create mode 100644 ports/stm32/boards/NUCLEO_F439ZI/mpconfigboard.h create mode 100644 ports/stm32/boards/NUCLEO_F439ZI/mpconfigboard.mk create mode 100644 ports/stm32/boards/NUCLEO_F439ZI/pins.csv create mode 100644 ports/stm32/boards/NUCLEO_F439ZI/stm32f4xx_hal_conf.h diff --git a/ports/stm32/boards/NUCLEO_F439ZI/mpconfigboard.h b/ports/stm32/boards/NUCLEO_F439ZI/mpconfigboard.h new file mode 100644 index 0000000000..010e3b1f5f --- /dev/null +++ b/ports/stm32/boards/NUCLEO_F439ZI/mpconfigboard.h @@ -0,0 +1,81 @@ +#define MICROPY_HW_BOARD_NAME "NUCLEO-F439ZI" +#define MICROPY_HW_MCU_NAME "STM32F439ZIT6" + +#define MICROPY_HW_HAS_SWITCH (1) +#define MICROPY_HW_HAS_FLASH (1) +#define MICROPY_HW_ENABLE_RNG (1) +#define MICROPY_HW_ENABLE_RTC (1) +#define MICROPY_HW_ENABLE_USB (1) + +// HSE is 8MHz from ST-LINK, in bypass mode, run SYSCLK at 168MHz +#define MICROPY_HW_CLK_USE_BYPASS (1) +#define MICROPY_HW_CLK_PLLM (8) +#define MICROPY_HW_CLK_PLLN (336) +#define MICROPY_HW_CLK_PLLP (RCC_PLLP_DIV2) +#define MICROPY_HW_CLK_PLLQ (7) +#define MICROPY_HW_FLASH_LATENCY FLASH_LATENCY_5 + +// The board has a 32768Hz crystal for LSE +#define MICROPY_HW_RTC_USE_LSE (1) +#define MICROPY_HW_RTC_USE_US (1) + +// UART config +#define MICROPY_HW_UART2_TX (pin_D5) +#define MICROPY_HW_UART2_RX (pin_D6) +#define MICROPY_HW_UART2_RTS (pin_D4) +#define MICROPY_HW_UART2_CTS (pin_D3) +#define MICROPY_HW_UART3_TX (pin_D8) +#define MICROPY_HW_UART3_RX (pin_D9) +#define MICROPY_HW_UART6_TX (pin_G14) +#define MICROPY_HW_UART6_RX (pin_G9) +#define MICROPY_HW_UART_REPL PYB_UART_3 +#define MICROPY_HW_UART_REPL_BAUD 115200 + +// I2C buses +#define MICROPY_HW_I2C1_SCL (pin_B8) +#define MICROPY_HW_I2C1_SDA (pin_B9) +#define MICROPY_HW_I2C2_SCL (pin_F1) +#define MICROPY_HW_I2C2_SDA (pin_F0) + +// SPI buses +#define MICROPY_HW_SPI1_NSS (pin_D14) +#define MICROPY_HW_SPI1_SCK (pin_A5) +#define MICROPY_HW_SPI1_MISO (pin_A6) +#define MICROPY_HW_SPI1_MOSI (pin_A7) +#define MICROPY_HW_SPI3_NSS (pin_A4) +#define MICROPY_HW_SPI3_SCK (pin_B3) +#define MICROPY_HW_SPI3_MISO (pin_B4) +#define MICROPY_HW_SPI3_MOSI (pin_B5) + +// CAN buses +#define MICROPY_HW_CAN1_TX (pin_D1) +#define MICROPY_HW_CAN1_RX (pin_D0) + +// 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_B0) // green +#define MICROPY_HW_LED2 (pin_B7) // blue +#define MICROPY_HW_LED3 (pin_B14) // red +#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_high(pin)) +#define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_low(pin)) + +// USB config (CN13 - USB OTG FS) +#define MICROPY_HW_USB_FS (1) +#define MICROPY_HW_USB_VBUS_DETECT_PIN (pin_A9) +#define MICROPY_HW_USB_OTG_ID_PIN (pin_A10) + +// Ethernet via RMII +#define MICROPY_HW_ETH_MDC (pin_C1) +#define MICROPY_HW_ETH_MDIO (pin_A2) +#define MICROPY_HW_ETH_RMII_REF_CLK (pin_A1) +#define MICROPY_HW_ETH_RMII_CRS_DV (pin_A7) +#define MICROPY_HW_ETH_RMII_RXD0 (pin_C4) +#define MICROPY_HW_ETH_RMII_RXD1 (pin_C5) +#define MICROPY_HW_ETH_RMII_TX_EN (pin_G11) +#define MICROPY_HW_ETH_RMII_TXD0 (pin_G13) +#define MICROPY_HW_ETH_RMII_TXD1 (pin_B13) diff --git a/ports/stm32/boards/NUCLEO_F439ZI/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_F439ZI/mpconfigboard.mk new file mode 100644 index 0000000000..52702b5112 --- /dev/null +++ b/ports/stm32/boards/NUCLEO_F439ZI/mpconfigboard.mk @@ -0,0 +1,11 @@ +MCU_SERIES = f4 +CMSIS_MCU = STM32F439xx +AF_FILE = boards/stm32f439_af.csv +LD_FILES = boards/stm32f439.ld boards/common_ifs.ld +TEXT0_ADDR = 0x08000000 +TEXT1_ADDR = 0x08020000 + +# MicroPython settings +MICROPY_PY_LWIP = 1 +MICROPY_PY_USSL = 1 +MICROPY_SSL_MBEDTLS = 1 diff --git a/ports/stm32/boards/NUCLEO_F439ZI/pins.csv b/ports/stm32/boards/NUCLEO_F439ZI/pins.csv new file mode 100644 index 0000000000..cea3678ed4 --- /dev/null +++ b/ports/stm32/boards/NUCLEO_F439ZI/pins.csv @@ -0,0 +1,129 @@ +,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 +,PC7 +,PC8 +,PC9 +,PC10 +,PC11 +,PC12 +,PC13 +,PC14 +,PC15 +,PD0 +,PD1 +,PD2 +,PD3 +,PD4 +,PD5 +,PD6 +,PD7 +,PD8 +,PD9 +,PD10 +,PD11 +,PD12 +,PD13 +,PD14 +,PD15 +,PE0 +,PE1 +,PE2 +,PE3 +,PE4 +,PE5 +,PE6 +,PE7 +,PE8 +,PE9 +,PE10 +,PE11 +,PE12 +,PE13 +,PE14 +,PE15 +,PF0 +,PF1 +,PF2 +,PF3 +,PF4 +,PF5 +,PF6 +,PF7 +,PF8 +,PF9 +,PF10 +,PF11 +,PF12 +,PF13 +,PF14 +,PF15 +,PG0 +,PG1 +,PG2 +,PG3 +,PG4 +,PG5 +,PG6 +,PG7 +,PG8 +,PG9 +,PG10 +,PG11 +,PG12 +,PG13 +,PG14 +,PG15 +SW,PC13 +LED_GREEN,PB0 +LED_BLUE,PB7 +LED_RED,PB14 +USB_VBUS,PA9 +USB_ID,PA10 +USB_DM,PA11 +USB_DP,PA12 +ETH_MDC,PC1 +ETH_MDIO,PA2 +ETH_RMII_REF_CLK,PA1 +ETH_RMII_CRS_DV,PA7 +ETH_RMII_RXD0,PC4 +ETH_RMII_RXD1,PC5 +ETH_RMII_TX_EN,PG11 +ETH_RMII_TXD0,PG13 +ETH_RMII_TXD1,PB13 diff --git a/ports/stm32/boards/NUCLEO_F439ZI/stm32f4xx_hal_conf.h b/ports/stm32/boards/NUCLEO_F439ZI/stm32f4xx_hal_conf.h new file mode 100644 index 0000000000..de19251e08 --- /dev/null +++ b/ports/stm32/boards/NUCLEO_F439ZI/stm32f4xx_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_STM32F4XX_HAL_CONF_H +#define MICROPY_INCLUDED_STM32F4XX_HAL_CONF_H + +#include "boards/stm32f4xx_hal_conf_base.h" + +// Oscillator values in Hz +#define HSE_VALUE (8000000) +#define LSE_VALUE (32768) +#define EXTERNAL_CLOCK_VALUE (12288000) + +// Oscillator timeouts in ms +#define HSE_STARTUP_TIMEOUT (100) +#define LSE_STARTUP_TIMEOUT (5000) + +#endif // MICROPY_INCLUDED_STM32F4XX_HAL_CONF_H From 7a1edb91cbd47379c29e46d9c85acd9504a6eb89 Mon Sep 17 00:00:00 2001 From: Tobias Thyrrestrup Date: Wed, 21 Jul 2021 13:45:33 +0200 Subject: [PATCH 142/264] stm32/boards/LEGO_HUB_NO6: Add board definition for LEGO_HUB_NO6. Add basic support for LEGO HUB NO.6 (e.g. LEGO SPIKE Prime, LEGO MINDSTORMS Robot Inventor). See README.md for details. Thanks to @dpgeorge for helping put this together. Signed-off-by: Tobias Thyrrestrup --- ports/stm32/boards/LEGO_HUB_NO6/README.md | 79 +++ ports/stm32/boards/LEGO_HUB_NO6/bdev.c | 63 ++ .../LEGO_HUB_NO6/bluetooth_init_cc2564C_1.5.c | 616 ++++++++++++++++++ ports/stm32/boards/LEGO_HUB_NO6/board_init.c | 170 +++++ ports/stm32/boards/LEGO_HUB_NO6/cc2564.c | 85 +++ ports/stm32/boards/LEGO_HUB_NO6/hub_display.c | 180 +++++ ports/stm32/boards/LEGO_HUB_NO6/hub_display.h | 4 + .../stm32/boards/LEGO_HUB_NO6/mboot_memory.ld | 10 + .../stm32/boards/LEGO_HUB_NO6/mpconfigboard.h | 122 ++++ .../boards/LEGO_HUB_NO6/mpconfigboard.mk | 22 + ports/stm32/boards/LEGO_HUB_NO6/pins.csv | 114 ++++ .../stm32/boards/LEGO_HUB_NO6/stm32f413xg.ld | 27 + .../boards/LEGO_HUB_NO6/stm32f4xx_hal_conf.h | 19 + 13 files changed, 1511 insertions(+) create mode 100644 ports/stm32/boards/LEGO_HUB_NO6/README.md create mode 100644 ports/stm32/boards/LEGO_HUB_NO6/bdev.c create mode 100644 ports/stm32/boards/LEGO_HUB_NO6/bluetooth_init_cc2564C_1.5.c create mode 100644 ports/stm32/boards/LEGO_HUB_NO6/board_init.c create mode 100644 ports/stm32/boards/LEGO_HUB_NO6/cc2564.c create mode 100644 ports/stm32/boards/LEGO_HUB_NO6/hub_display.c create mode 100644 ports/stm32/boards/LEGO_HUB_NO6/hub_display.h create mode 100644 ports/stm32/boards/LEGO_HUB_NO6/mboot_memory.ld create mode 100644 ports/stm32/boards/LEGO_HUB_NO6/mpconfigboard.h create mode 100644 ports/stm32/boards/LEGO_HUB_NO6/mpconfigboard.mk create mode 100644 ports/stm32/boards/LEGO_HUB_NO6/pins.csv create mode 100644 ports/stm32/boards/LEGO_HUB_NO6/stm32f413xg.ld create mode 100644 ports/stm32/boards/LEGO_HUB_NO6/stm32f4xx_hal_conf.h diff --git a/ports/stm32/boards/LEGO_HUB_NO6/README.md b/ports/stm32/boards/LEGO_HUB_NO6/README.md new file mode 100644 index 0000000000..c566ada9d0 --- /dev/null +++ b/ports/stm32/boards/LEGO_HUB_NO6/README.md @@ -0,0 +1,79 @@ +LEGO Hub No.6 +============= + +This board definition is for the LEGO Hub No. 6, a LEGO control unit with a 5x5 +LED display, 6 Powered Up ports, speaker, 6-DOF sensor, Bluetooth, external SPI +flash storage, and a rechargeable battery. The Hub can work without the battery if +it is plugged in to, and powered by, its USB port. But without the battery the LEDs +and power on the Powered Up ports will not function. + +Features that are currently supported: +- standard MicroPython +- machine and bluetooth modules +- filesystem +- USB VCP, MSC and HID + +The Hub has a bootloader preinstalled at 0x08000000 (which is 32kiB in size) which +cannot be erased. This bootloader is entered by holding down the Bluetooth button, +plugging in USB to power it up, then releasing the Bluetooth button after 5 seconds, +at which point the USB DFU device appears. If the battery is installed then the +Bluetooth button's RGB LED will cycle colours. When this bootloader is active, the +flash from 0x08008000 and up can be erased and programmed via USB DFU. + +The built-in bootloader has some drawbacks: it cannot be entered programmatically, +and it does not keep the Hub powered up when running from battery (which requires +keeping BAT_PWR_EN high). As such, this board is configured to work with mboot as +a secondary bootloader: mboot is placed at 0x08008000 and the main application +firmware at 0x08010000. When mboot is installed it can be entered programatically +via machine.bootloader(), or by holding down the left arrow button when powering +on the Hub and waiting until the display says "B" before releasing the button. + +Installing MicroPython +---------------------- + +You first need to build and install mboot, which only needs to be done once. From +the root of this repository run: + + $ cd ports/stm32/mboot + $ make BOARD=LEGO_HUB_NO6 + +Now enter the built-in bootloader by holding down the Bluetooth button for 5 +seconds while powering up the Hub via USB. Then run: + + $ make BOARD=LEGO_HUB_NO6 deploy + +mboot should now be installed. Enter mboot by holding down the left arrow +button when powering up the Hub. The display will cycle the letters: N, S, F, B. +When it gets to "B" release the left arrow and it will start mboot. The Hub then +blinks the centre button red once per second, and appears as a USB DFU device. + +Now build MicroPython (start at the root of this repository): + + $ cd mpy-cross + $ make + $ cd ../ports/stm32 + $ make submodules + $ make BOARD=LEGO_HUB_NO6 + +And deploy to the Hub (making sure mboot is active, the centre button is blinking +red): + + $ make BOARD=LEGO_HUB_NO6 deploy + +If successful, the Hub should now appear as a USB serial and mass storage device. + +Using MicroPython on the Hub +---------------------------- + +Access the MicroPython REPL using mpremote (pip install mpremote), or with any +serial terminal program. + +To scan for BLE devices: + + >>> import bluetooth + >>> ble = bluetooth.BLE() + >>> ble.irq(lambda *x: print(*x)) + >>> ble.active(1) + >>> ble.gap_scan(2000, 625, 625) + +Use help("modules") to see available built-in modules. \ No newline at end of file diff --git a/ports/stm32/boards/LEGO_HUB_NO6/bdev.c b/ports/stm32/boards/LEGO_HUB_NO6/bdev.c new file mode 100644 index 0000000000..44671edb15 --- /dev/null +++ b/ports/stm32/boards/LEGO_HUB_NO6/bdev.c @@ -0,0 +1,63 @@ +/* + * 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. + */ + +#include "py/obj.h" +#include "storage.h" + +#define CMD_EXIT_4_BYTE_ADDRESS_MODE (0xE9) + +STATIC const mp_soft_spi_obj_t soft_spi_bus = { + .delay_half = MICROPY_HW_SOFTSPI_MIN_DELAY, + .polarity = 0, + .phase = 0, + .sck = MICROPY_HW_SPIFLASH_SCK, + .mosi = MICROPY_HW_SPIFLASH_MOSI, + .miso = MICROPY_HW_SPIFLASH_MISO, +}; + +STATIC mp_spiflash_cache_t spi_bdev_cache; + +const mp_spiflash_config_t spiflash_config = { + .bus_kind = MP_SPIFLASH_BUS_SPI, + .bus.u_spi.cs = MICROPY_HW_SPIFLASH_NSS, + .bus.u_spi.data = (void *)&soft_spi_bus, + .bus.u_spi.proto = &mp_soft_spi_proto, + .cache = &spi_bdev_cache, +}; + +spi_bdev_t spi_bdev; + +int32_t board_bdev_ioctl(void) { + int32_t ret = spi_bdev_ioctl(&spi_bdev, BDEV_IOCTL_INIT, (uint32_t)&spiflash_config); + + // Exit 4-byte address mode + uint8_t cmd = CMD_EXIT_4_BYTE_ADDRESS_MODE; + mp_hal_pin_write(MICROPY_HW_SPIFLASH_NSS, 0); + mp_soft_spi_proto.transfer(MP_OBJ_FROM_PTR(&soft_spi_bus), 1, &cmd, NULL); + mp_hal_pin_write(MICROPY_HW_SPIFLASH_NSS, 1); + + return ret; +} diff --git a/ports/stm32/boards/LEGO_HUB_NO6/bluetooth_init_cc2564C_1.5.c b/ports/stm32/boards/LEGO_HUB_NO6/bluetooth_init_cc2564C_1.5.c new file mode 100644 index 0000000000..ec41fdfeef --- /dev/null +++ b/ports/stm32/boards/LEGO_HUB_NO6/bluetooth_init_cc2564C_1.5.c @@ -0,0 +1,616 @@ +// This file contains CC256x initialisation code, and was generated by BTstack via: +// $ cd chipset/cc256x +// $ make -f Makefile.inc BTSTACK_ROOT=../.. bluetooth_init_cc2564C_1.4.c +#if !BUILDING_MBOOT + + // init script created from +// - /Users/dktobthy/Downloads/cc256xc_bt_spv1.5/CC256XC_BT_SP/v1.5/initscripts-TIInit_6.12.26.bts +// - AKA TIInit_6.12.26.bts +// - /Users/dktobthy/Downloads/cc256xc_bt_spv1.5/CC256XC_BT_SP/v1.5/initscripts-TIInit_6.12.26_ble_add-on.bts +#include +#include "lib/btstack/chipset/cc256x/btstack_chipset_cc256x.h" + + +const uint16_t cc256x_init_script_lmp_subversion = 0x9a1a; + +uint16_t btstack_chipset_cc256x_lmp_subversion(void){ + return cc256x_init_script_lmp_subversion; +} + +#if defined(__GNUC__) && defined(__MSP430X__) && (__MSP430X__ > 0) +__attribute__((section (".fartext"))) +#endif +#ifdef __AVR__ +__attribute__((__progmem__)) +#endif +const uint8_t cc256x_init_script[] = { + + // #-------------------------------------------------------------------------------- + // # Description : Orca C ROM Initialization Script + // # + // # Compatibility: Orca, 12.0.26 ROM + // # + // # Last Updated: 17-May-2021 10:31:30.07 + // # + // # Version : 12_26.24 + // # + // # + // # + // # + // # Notes : Use this script on Orca C, 12.0.26 ROM device only (FW v12.0.26) + // #-------------------------------------------------------------------------------- + // ################################################################# + // ## START of CC256x Add-On + // ################################################################# + // + // ## Change UART baudrate + // + // ################################################################# + // ## END of CC256x Add-On + // ################################################################# + // + 0x01, 0x37, 0xfe, 0x02, 0x0c, 0x1a, + + // + // + 0x01, 0x05, 0xff, 0xff, 0xd0, 0x65, 0x08, 0x00, 0xfa, 0x0c, 0x1a, 0x09, 0x18, 0x01, 0x6a, + 0xc8, 0x7b, 0x00, 0x02, 0x89, 0x7b, 0x01, 0x43, 0x09, 0x48, 0x51, 0x30, 0x02, 0x88, 0x06, + 0x48, 0x91, 0x42, 0x03, 0xd1, 0x04, 0x49, 0x09, 0x78, 0x01, 0x29, 0x01, 0xd0, 0x5d, 0x30, + 0xf7, 0x46, 0xff, 0x30, 0xd8, 0x30, 0xf7, 0x46, 0x76, 0x24, 0x08, 0x00, 0xbd, 0x28, 0x02, + 0x00, 0x69, 0x53, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x00, + 0x69, 0xff, 0x21, 0x02, 0x31, 0x09, 0x5c, 0x09, 0x29, 0x05, 0xd1, 0x01, 0x21, 0x00, 0x22, + 0x8e, 0x46, 0x6d, 0x4b, 0xfe, 0x44, 0x18, 0x47, 0x00, 0xbd, 0x6c, 0x4a, 0x11, 0x88, 0x01, + 0x20, 0x40, 0x03, 0x08, 0x43, 0x10, 0x80, 0xf7, 0x46, 0x30, 0xb5, 0x00, 0x69, 0xf4, 0x21, + 0x08, 0x5c, 0x01, 0x28, 0x16, 0xd1, 0xea, 0x48, 0x00, 0x78, 0x03, 0x28, 0x12, 0xd0, 0x00, + 0x25, 0x28, 0x1c, 0xe8, 0x49, 0x01, 0x24, 0xa6, 0x46, 0xe7, 0x4a, 0xfe, 0x44, 0x10, 0x47, + 0x00, 0x28, 0x08, 0xd0, + + 0x01, 0x05, 0xff, 0xff, 0xca, 0x66, 0x08, 0x00, 0xfa, 0xe6, 0x49, 0xe6, 0x4a, 0xbb, 0x32, + 0x20, 0x20, 0x2b, 0x1c, 0xa6, 0x46, 0xe5, 0x4c, 0xfe, 0x44, 0x20, 0x47, 0x30, 0xbd, 0x70, + 0xb5, 0x85, 0x69, 0x00, 0x7d, 0x80, 0x1f, 0x11, 0xd0, 0x47, 0x38, 0x2e, 0xd1, 0xa9, 0x79, + 0x28, 0x20, 0x48, 0x43, 0xdf, 0x4a, 0x10, 0x18, 0x23, 0x22, 0x12, 0x5c, 0x01, 0x2a, 0x25, + 0xd1, 0x80, 0x7b, 0x00, 0x28, 0x22, 0xd0, 0xdb, 0x4a, 0x00, 0x20, 0x50, 0x54, 0x70, 0xbd, + 0x01, 0x24, 0xa6, 0x46, 0xd9, 0x48, 0xfe, 0x44, 0x00, 0x47, 0x01, 0x28, 0x05, 0xd0, 0xa6, + 0x46, 0xd6, 0x48, 0xfe, 0x44, 0x00, 0x47, 0x04, 0x28, 0x11, 0xd1, 0xe8, 0x78, 0x00, 0x28, + 0x0e, 0xd1, 0x0e, 0x26, 0x31, 0x1c, 0xd3, 0x4d, 0x28, 0x1c, 0x14, 0x38, 0xa6, 0x46, 0xd0, + 0x4a, 0xfe, 0x44, 0x10, 0x47, 0x28, 0x1c, 0x31, 0x1c, 0xa6, 0x46, 0xcd, 0x4a, 0xfe, 0x44, + 0x10, 0x47, 0x70, 0xbd, 0x70, 0xb5, 0x01, 0x1c, 0x88, 0x69, 0x89, 0x8a, 0xcb, 0x4a, 0x89, + 0x1a, 0x1c, 0xd0, 0x1c, 0x39, 0x20, 0xd1, 0xc5, 0x7a, 0x01, 0x21, 0x0c, 0x1c, 0x8e, 0x46, + 0xc8, 0x4a, 0xfe, 0x44, 0x10, 0x47, 0x06, 0x1c, 0x10, 0x20, 0xa6, 0x46, 0xc6, 0x49, 0xfe, + 0x44, 0x08, 0x47, 0x00, 0x2d, 0x11, 0xd0, 0x02, 0x2e, 0x0f, 0xd0, 0xc3, 0x49, 0xc4, 0x4a, + 0xd9, 0x32, 0x20, 0x20, 0x00, 0x23, 0xa6, 0x46, 0xb7, 0x4c, 0xfe, 0x44, 0x20, 0x47, 0x70, + 0xbd, 0xc0, 0x7a, 0x00, 0x28, 0x02, 0xd1, 0x00, 0x20, 0xbe, 0x49, 0x08, 0x70, 0x70, 0xbd, + 0x00, 0xb5, 0x00, 0x69, 0xff, 0x21, 0x04, 0x31, 0x09, 0x5c, 0x06, 0x29, 0x06, 0xd1, 0xff, + 0x21, 0x05, 0x31, 0x0a, 0x5c, 0x2c, 0x2a, 0x01, 0xd1, 0x2d, 0x22, 0x0a, 0x54, 0xff, 0x21, + 0x05, 0x31, 0x09, 0x5c, + + 0x01, 0x05, 0xff, 0xff, 0xc4, 0x67, 0x08, 0x00, 0xfa, 0x34, 0x29, 0x11, 0xd1, 0xff, 0x21, + 0x0b, 0x31, 0x09, 0x5c, 0x91, 0x29, 0x0c, 0xd1, 0xf1, 0x21, 0x09, 0x5c, 0x60, 0x22, 0x4a, + 0x43, 0xeb, 0x4b, 0x00, 0x21, 0x99, 0x54, 0x06, 0x21, 0x01, 0x22, 0x96, 0x46, 0xe9, 0x4a, + 0xfe, 0x44, 0x10, 0x47, 0x00, 0xbd, 0xf0, 0xb5, 0x06, 0x1c, 0xf7, 0x69, 0x08, 0x20, 0xc0, + 0x19, 0x01, 0x24, 0xa6, 0x46, 0xe4, 0x49, 0xfe, 0x44, 0x08, 0x47, 0xc1, 0x7b, 0x09, 0x02, + 0x80, 0x7b, 0x08, 0x43, 0x05, 0x04, 0x2d, 0x0c, 0x02, 0x2d, 0x12, 0xd0, 0xef, 0x48, 0x00, + 0x88, 0xa8, 0x42, 0x0e, 0xd1, 0xee, 0x48, 0x00, 0x78, 0x01, 0x28, 0x0a, 0xd1, 0xed, 0x48, + 0x82, 0x8f, 0x81, 0x6b, 0x08, 0x1c, 0x10, 0x43, 0x04, 0xd0, 0x38, 0x1c, 0xa6, 0x46, 0xea, + 0x4b, 0xfe, 0x44, 0x18, 0x47, 0x35, 0x60, 0xe9, 0x48, 0x24, 0x30, 0x30, 0x62, 0xf0, 0xbd, + 0xc0, 0x46, 0xdd, 0x9d, 0x00, 0x00, 0x3e, 0xa6, 0x1b, 0x00, 0xf0, 0xb5, 0x86, 0xb0, 0x04, + 0x90, 0x81, 0x69, 0x05, 0x91, 0x81, 0x8a, 0xe2, 0x48, 0x09, 0x1a, 0xdc, 0x4d, 0x00, 0xd1, + 0xba, 0xe0, 0xe1, 0x4a, 0x89, 0x1a, 0x00, 0xd1, 0xaf, 0xe0, 0x07, 0x38, 0x09, 0x1a, 0x35, + 0xd0, 0xde, 0x48, 0x09, 0x1a, 0x28, 0xd0, 0x05, 0x39, 0x26, 0xd0, 0x13, 0x39, 0x33, 0xd1, + 0xdc, 0x48, 0x05, 0x1c, 0x84, 0x3d, 0x00, 0x78, 0x02, 0x28, 0x2d, 0xd1, 0x01, 0x24, 0xa6, + 0x46, 0xd9, 0x48, 0xfe, 0x44, 0x00, 0x47, 0x00, 0x28, 0x26, 0xd1, 0xa6, 0x46, 0xd7, 0x48, + 0xfe, 0x44, 0x00, 0x47, 0x00, 0x28, 0x20, 0xd1, 0xa6, 0x46, 0xd5, 0x48, 0xfe, 0x44, 0x00, + 0x47, 0x00, 0x28, 0x1a, 0xd1, 0x28, 0x78, 0x00, 0x28, 0x17, 0xd1, 0xa8, 0x78, 0x00, 0x28, + 0x14, 0xd1, 0xa6, 0x20, + + 0x01, 0x05, 0xff, 0xff, 0xbe, 0x68, 0x08, 0x00, 0xfa, 0xa6, 0x46, 0xcf, 0x49, 0xfe, 0x44, + 0x08, 0x47, 0xc4, 0xe0, 0x05, 0x98, 0x81, 0x88, 0x41, 0x18, 0x08, 0x7c, 0x02, 0x28, 0x00, + 0xda, 0xbd, 0xe0, 0x80, 0x1e, 0x08, 0x74, 0xba, 0xe0, 0x51, 0x3d, 0x28, 0x78, 0x2a, 0x28, + 0x02, 0xd0, 0x33, 0x28, 0x00, 0xd0, 0xb3, 0xe0, 0x0e, 0x21, 0x05, 0x98, 0x0f, 0x18, 0x44, + 0x20, 0x0c, 0x21, 0x1a, 0x22, 0x01, 0x24, 0xa6, 0x46, 0xc2, 0x4b, 0xfe, 0x44, 0x18, 0x47, + 0xc1, 0x4e, 0xb1, 0x78, 0xf2, 0x78, 0xc1, 0x48, 0xa6, 0x46, 0xc1, 0x4b, 0xfe, 0x44, 0x18, + 0x47, 0x06, 0x21, 0x05, 0x98, 0x81, 0x80, 0x28, 0x78, 0xbf, 0x4d, 0x2a, 0x28, 0x26, 0xd0, + 0x0a, 0x26, 0x3e, 0x70, 0x01, 0x37, 0x38, 0x1c, 0x00, 0x21, 0xa6, 0x46, 0xba, 0x4a, 0xfe, + 0x44, 0x10, 0x47, 0x07, 0x1c, 0x3e, 0x70, 0x01, 0x37, 0x38, 0x1c, 0x0d, 0x21, 0xa6, 0x46, + 0xb5, 0x4a, 0xfe, 0x44, 0x10, 0x47, 0x07, 0x1c, 0xa6, 0x46, 0xb5, 0x48, 0xfe, 0x44, 0x00, + 0x47, 0x81, 0x02, 0xae, 0x48, 0xc0, 0x78, 0x40, 0x06, 0x40, 0x0e, 0x08, 0x43, 0x05, 0x43, + 0x29, 0x04, 0x09, 0x0c, 0x38, 0x1c, 0xa6, 0x46, 0xac, 0x4a, 0xfe, 0x44, 0x10, 0x47, 0x08, + 0x26, 0x24, 0xe0, 0x08, 0x26, 0x3e, 0x70, 0x01, 0x37, 0x38, 0x1c, 0x00, 0x21, 0xa6, 0x46, + 0xa6, 0x4a, 0xfe, 0x44, 0x10, 0x47, 0x07, 0x1c, 0x3e, 0x70, 0x01, 0x37, 0x38, 0x1c, 0x0d, + 0x21, 0xa6, 0x46, 0xa2, 0x4a, 0xfe, 0x44, 0x10, 0x47, 0x07, 0x1c, 0xa6, 0x46, 0xa1, 0x48, + 0xfe, 0x44, 0x00, 0x47, 0x81, 0x02, 0x9a, 0x48, 0xc0, 0x78, 0x40, 0x06, 0x40, 0x0e, 0x08, + 0x43, 0x05, 0x43, 0x29, 0x04, 0x09, 0x0c, 0x38, 0x1c, 0xa6, 0x46, 0x98, 0x4a, 0xfe, 0x44, + 0x10, 0x47, 0x05, 0x98, + + 0x01, 0x05, 0xff, 0xff, 0xb8, 0x69, 0x08, 0x00, 0xfa, 0x46, 0x80, 0xff, 0x21, 0x02, 0x31, + 0x00, 0x22, 0xa6, 0x46, 0xed, 0x4b, 0xfe, 0x44, 0x18, 0x47, 0x3f, 0xe0, 0xec, 0x49, 0x00, + 0x20, 0x08, 0x70, 0x08, 0x74, 0xeb, 0x49, 0x08, 0x70, 0x3c, 0xe0, 0x05, 0x98, 0x00, 0x21, + 0x6a, 0x46, 0x01, 0x24, 0xa6, 0x46, 0xe8, 0x4b, 0xfe, 0x44, 0x18, 0x47, 0x02, 0xa8, 0x00, + 0x21, 0x06, 0x22, 0xa6, 0x46, 0xe5, 0x4b, 0xfe, 0x44, 0x18, 0x47, 0x00, 0x26, 0x02, 0xe0, + 0x70, 0x1c, 0x06, 0x04, 0x36, 0x0c, 0x28, 0x1c, 0x4f, 0x38, 0x00, 0x78, 0x86, 0x42, 0x23, + 0xda, 0x11, 0x20, 0x40, 0x01, 0x70, 0x43, 0xea, 0x49, 0x0f, 0x18, 0x10, 0x20, 0xc0, 0x19, + 0x69, 0x46, 0xa6, 0x46, 0xe8, 0x4a, 0xfe, 0x44, 0x10, 0x47, 0x00, 0x28, 0xe9, 0xd1, 0x68, + 0x46, 0x02, 0xa9, 0xa6, 0x46, 0xe4, 0x4a, 0xfe, 0x44, 0x10, 0x47, 0x00, 0x28, 0xe1, 0xd0, + 0xb8, 0x78, 0x01, 0x28, 0xde, 0xd0, 0x05, 0x98, 0x69, 0x49, 0x3a, 0x22, 0xa6, 0x46, 0xdf, + 0x4b, 0xfe, 0x44, 0x18, 0x47, 0xec, 0x49, 0xe2, 0x31, 0x04, 0x98, 0x01, 0x62, 0x06, 0xb0, + 0xf0, 0xbd, 0xc0, 0x46, 0x18, 0x32, 0x08, 0x00, 0xce, 0x04, 0x00, 0x00, 0x23, 0xb9, 0x02, + 0x00, 0x85, 0x71, 0x08, 0x00, 0xfd, 0x79, 0x00, 0x00, 0xc9, 0x70, 0x08, 0x00, 0xd4, 0x1d, + 0x08, 0x00, 0x94, 0x54, 0x08, 0x00, 0x8f, 0x8d, 0x01, 0x00, 0xfd, 0x06, 0x05, 0x00, 0x14, + 0x05, 0x1a, 0x00, 0xa2, 0xfd, 0x00, 0x00, 0x65, 0x2d, 0x00, 0x00, 0x7d, 0xca, 0x03, 0x00, + 0xc5, 0x71, 0x08, 0x00, 0x79, 0x47, 0x00, 0x00, 0x78, 0x24, 0x08, 0x00, 0xf0, 0xb5, 0x07, + 0x1c, 0xba, 0x69, 0x50, 0x78, 0xfe, 0x69, 0x25, 0x21, 0x43, 0x1a, 0xe4, 0x49, 0x32, 0xd0, + 0x01, 0x3b, 0x68, 0xd1, + + 0x01, 0x05, 0xff, 0xff, 0xb2, 0x6a, 0x08, 0x00, 0xfa, 0x04, 0x23, 0xb3, 0x80, 0x0c, 0x23, + 0x9d, 0x19, 0x40, 0x00, 0x12, 0x78, 0x02, 0x43, 0x2a, 0x70, 0x01, 0x35, 0x08, 0x78, 0x2a, + 0x28, 0x05, 0xd0, 0x33, 0x28, 0x01, 0xd1, 0x0a, 0x20, 0x02, 0xe0, 0x07, 0x20, 0x00, 0xe0, + 0x08, 0x20, 0x28, 0x70, 0x01, 0x35, 0x28, 0x1c, 0x0d, 0x21, 0x01, 0x24, 0xa6, 0x46, 0x4b, + 0x4a, 0xfe, 0x44, 0x10, 0x47, 0x05, 0x1c, 0xa6, 0x46, 0x4b, 0x48, 0xfe, 0x44, 0x00, 0x47, + 0x82, 0x02, 0xd2, 0x48, 0x00, 0x78, 0x40, 0x06, 0x41, 0x0e, 0x11, 0x43, 0x45, 0x48, 0x08, + 0x43, 0x01, 0x04, 0x09, 0x0c, 0x28, 0x1c, 0xa6, 0x46, 0x41, 0x4a, 0xfe, 0x44, 0x10, 0x47, + 0x2f, 0xe0, 0x04, 0x23, 0xb3, 0x80, 0x0c, 0x23, 0x9d, 0x19, 0x40, 0x00, 0x12, 0x78, 0x02, + 0x43, 0x2a, 0x70, 0x01, 0x35, 0x08, 0x78, 0x2a, 0x28, 0x05, 0xd0, 0x33, 0x28, 0x01, 0xd1, + 0x0a, 0x20, 0x02, 0xe0, 0x07, 0x20, 0x00, 0xe0, 0x08, 0x20, 0x28, 0x70, 0x01, 0x35, 0x28, + 0x1c, 0x0d, 0x21, 0x01, 0x24, 0xa6, 0x46, 0x33, 0x4a, 0xfe, 0x44, 0x10, 0x47, 0x05, 0x1c, + 0xa6, 0x46, 0x32, 0x48, 0xfe, 0x44, 0x00, 0x47, 0x82, 0x02, 0xb9, 0x48, 0x00, 0x78, 0x40, + 0x06, 0x41, 0x0e, 0x11, 0x43, 0x2d, 0x48, 0x08, 0x43, 0x01, 0x04, 0x09, 0x0c, 0x28, 0x1c, + 0xa6, 0x46, 0x29, 0x4a, 0xfe, 0x44, 0x10, 0x47, 0x06, 0x20, 0x70, 0x80, 0x03, 0x20, 0x30, + 0x80, 0xe3, 0x48, 0xe2, 0x49, 0x08, 0x18, 0x38, 0x62, 0xf0, 0xbd, 0xc0, 0x46, 0x76, 0xa0, + 0x1b, 0x00, 0xc5, 0x8e, 0x00, 0x00, 0x5b, 0x19, 0x04, 0x00, 0x10, 0xb5, 0xde, 0x48, 0x00, + 0x21, 0x01, 0x24, 0xa6, 0x46, 0xdd, 0x4a, 0xfe, 0x44, 0x10, 0x47, 0xdc, 0x49, 0x01, 0x28, + 0x01, 0xd0, 0x00, 0x20, + + 0x01, 0x05, 0xff, 0xff, 0xac, 0x6b, 0x08, 0x00, 0xfa, 0x01, 0xe0, 0x08, 0x68, 0x01, 0x30, + 0x08, 0x60, 0xa6, 0x46, 0xd9, 0x48, 0xfe, 0x44, 0x00, 0x47, 0x00, 0x28, 0x06, 0xd0, 0x0b, + 0x48, 0x00, 0x78, 0x02, 0x28, 0x02, 0xd1, 0x02, 0x20, 0xd5, 0x49, 0x08, 0x80, 0x10, 0xbd, + 0xba, 0x53, 0x08, 0x00, 0x90, 0xa1, 0x1b, 0x00, 0xa8, 0x59, 0x08, 0x00, 0x25, 0x6f, 0x04, + 0x00, 0x79, 0x6c, 0x04, 0x00, 0x05, 0x04, 0x00, 0x00, 0xfe, 0x07, 0x00, 0x00, 0x05, 0x10, + 0x00, 0x00, 0x45, 0x10, 0x08, 0x00, 0xc1, 0x72, 0x03, 0x00, 0x1b, 0x5f, 0x03, 0x00, 0x53, + 0x38, 0x02, 0x00, 0x21, 0xf0, 0x04, 0x00, 0xdb, 0x8e, 0x04, 0x00, 0xfc, 0x53, 0x08, 0x00, + 0xc6, 0x02, 0x00, 0x00, 0x8d, 0x8f, 0x04, 0x00, 0xf9, 0x2d, 0x00, 0x00, 0x00, 0x82, 0xff, + 0xff, 0xa9, 0x57, 0x05, 0x00, 0xf8, 0xb5, 0x80, 0x8a, 0xff, 0x21, 0x0b, 0x31, 0x88, 0x42, + 0x5c, 0xd0, 0xff, 0x21, 0x45, 0x31, 0x88, 0x42, 0x16, 0xd1, 0xc1, 0x48, 0x00, 0x78, 0x02, + 0x28, 0x12, 0xd1, 0xb9, 0x49, 0xff, 0x20, 0x41, 0x30, 0x40, 0x18, 0x00, 0x90, 0xb8, 0x48, + 0x40, 0x5c, 0x00, 0x26, 0x86, 0x42, 0x10, 0xd3, 0x51, 0x25, 0xad, 0x00, 0x28, 0x1c, 0x01, + 0x24, 0xa6, 0x46, 0xb4, 0x49, 0xfe, 0x44, 0x08, 0x47, 0x00, 0x28, 0x6d, 0xd1, 0x28, 0x1c, + 0xb2, 0x49, 0xa6, 0x46, 0xb2, 0x4a, 0xfe, 0x44, 0x10, 0x47, 0xf8, 0xbd, 0xb1, 0x00, 0x00, + 0x98, 0x45, 0x58, 0x00, 0x2d, 0x30, 0xd0, 0xfe, 0x20, 0x40, 0x5d, 0x00, 0x28, 0x2c, 0xd0, + 0xf2, 0x20, 0x41, 0x5d, 0x00, 0x29, 0x28, 0xd1, 0xff, 0x20, 0x02, 0x30, 0x40, 0x5d, 0x01, + 0x28, 0x23, 0xd0, 0x24, 0x20, 0x01, 0x24, 0xa6, 0x46, 0xa6, 0x4a, 0xfe, 0x44, 0x10, 0x47, + 0x00, 0x06, 0x00, 0x0e, + + 0x01, 0x05, 0xff, 0xff, 0xa6, 0x6c, 0x08, 0x00, 0xfa, 0xf3, 0x22, 0x51, 0x5d, 0x81, 0x42, + 0x17, 0xd0, 0x50, 0x55, 0x00, 0x28, 0x14, 0xd1, 0x2f, 0x8d, 0x39, 0x1c, 0xe7, 0x48, 0xa6, + 0x46, 0xe7, 0x4a, 0xfe, 0x44, 0x10, 0x47, 0x29, 0x8e, 0x79, 0x18, 0x08, 0x1a, 0x40, 0x1e, + 0x39, 0x1c, 0xa6, 0x46, 0xe3, 0x4a, 0xfe, 0x44, 0x10, 0x47, 0x40, 0x00, 0x28, 0x86, 0x28, + 0x8e, 0x40, 0x08, 0x28, 0x86, 0x01, 0x36, 0xdf, 0x48, 0x00, 0x78, 0xb2, 0xe7, 0x00, 0x20, + 0x01, 0x24, 0xa6, 0x46, 0xdd, 0x49, 0xfe, 0x44, 0x08, 0x47, 0x01, 0x1c, 0x00, 0x90, 0xdc, + 0x4d, 0x28, 0x68, 0x01, 0x30, 0x28, 0x60, 0x0b, 0x27, 0x7f, 0x01, 0xda, 0x4e, 0xda, 0x48, + 0x00, 0x8d, 0x32, 0x68, 0x10, 0x18, 0x80, 0x01, 0x80, 0x09, 0xa6, 0x46, 0xd8, 0x4a, 0xfe, + 0x44, 0x10, 0x47, 0x00, 0x28, 0x11, 0xdd, 0x00, 0x98, 0x30, 0x60, 0x28, 0x68, 0x03, 0x28, + 0x0a, 0xd9, 0x00, 0x20, 0xa6, 0x46, 0xd3, 0x49, 0xfe, 0x44, 0x08, 0x47, 0xcf, 0x48, 0x38, + 0x5c, 0xa6, 0x46, 0xd0, 0x49, 0xfe, 0x44, 0x08, 0x47, 0x00, 0x20, 0x28, 0x60, 0xf8, 0xbd, + 0x30, 0xb5, 0x05, 0x69, 0x24, 0x20, 0x00, 0x21, 0x01, 0x24, 0xa6, 0x46, 0x7a, 0x4a, 0xfe, + 0x44, 0x10, 0x47, 0xf3, 0x21, 0x48, 0x55, 0x51, 0x25, 0xad, 0x00, 0x28, 0x1c, 0xa6, 0x46, + 0x72, 0x49, 0xfe, 0x44, 0x08, 0x47, 0x00, 0x28, 0x05, 0xd1, 0x28, 0x1c, 0x70, 0x49, 0xa6, + 0x46, 0x70, 0x4a, 0xfe, 0x44, 0x10, 0x47, 0x30, 0xbd, 0xed, 0x49, 0x02, 0x00, 0xab, 0x11, + 0x08, 0x00, 0x63, 0x66, 0x08, 0x00, 0x99, 0x2d, 0x00, 0x00, 0xe9, 0x63, 0x05, 0x00, 0x41, + 0x69, 0x00, 0x7e, 0x00, 0x28, 0x11, 0xd1, 0xff, 0x22, 0x04, 0x32, 0x50, 0x5c, 0x01, 0x28, + 0x0c, 0xd1, 0xff, 0x20, + + 0x01, 0x05, 0xff, 0xff, 0xa0, 0x6d, 0x08, 0x00, 0xfa, 0x05, 0x30, 0x43, 0x5c, 0x1b, 0x2b, + 0x07, 0xd1, 0xff, 0x23, 0x0a, 0x33, 0x5b, 0x5c, 0x01, 0x2b, 0x02, 0xd1, 0x00, 0x23, 0x53, + 0x54, 0x43, 0x54, 0xf7, 0x46, 0xc0, 0x46, 0x00, 0x00, 0x08, 0x00, 0x49, 0x8f, 0x03, 0x00, + 0x85, 0x48, 0x02, 0x00, 0x10, 0xb5, 0x80, 0x69, 0x00, 0x88, 0x02, 0x28, 0x13, 0xd1, 0x54, + 0x48, 0x9c, 0x30, 0x01, 0x79, 0xff, 0x29, 0x0e, 0xd0, 0x00, 0x78, 0x03, 0x28, 0x03, 0xd0, + 0x01, 0x28, 0x01, 0xd0, 0x02, 0x28, 0x07, 0xd1, 0x01, 0x20, 0x04, 0x1c, 0x86, 0x46, 0xa2, + 0x49, 0xfe, 0x44, 0x08, 0x47, 0xa2, 0x48, 0x04, 0x70, 0x10, 0xbd, 0x25, 0x00, 0x00, 0x00, + 0x10, 0xb5, 0xc0, 0x69, 0x80, 0x78, 0x80, 0x38, 0x00, 0x06, 0x00, 0x0e, 0x02, 0x28, 0x13, + 0xd1, 0x44, 0x48, 0x9c, 0x30, 0x01, 0x79, 0xff, 0x29, 0x0e, 0xd0, 0x00, 0x78, 0x03, 0x28, + 0x03, 0xd0, 0x01, 0x28, 0x01, 0xd0, 0x02, 0x28, 0x07, 0xd1, 0x01, 0x20, 0x04, 0x1c, 0x86, + 0x46, 0x93, 0x49, 0xfe, 0x44, 0x08, 0x47, 0x92, 0x48, 0x04, 0x70, 0x10, 0xbd, 0xc0, 0x46, + 0x69, 0x53, 0x08, 0x00, 0xff, 0x53, 0x08, 0x00, 0x10, 0xb5, 0x00, 0x69, 0xfe, 0x21, 0x09, + 0x5c, 0x02, 0x29, 0x06, 0xd0, 0xd0, 0x21, 0x09, 0x58, 0x82, 0x8d, 0xd2, 0x00, 0x54, 0x23, + 0x1b, 0x18, 0x05, 0xe0, 0x54, 0x21, 0x0b, 0x18, 0x02, 0x8d, 0x06, 0x20, 0x42, 0x43, 0xd9, + 0x6f, 0x50, 0x18, 0x80, 0x01, 0x81, 0x09, 0xd8, 0x6d, 0x01, 0x24, 0xa6, 0x46, 0x7f, 0x4a, + 0xfe, 0x44, 0x10, 0x47, 0x00, 0x28, 0x12, 0xdc, 0x28, 0x48, 0x9c, 0x30, 0x01, 0x79, 0xff, + 0x29, 0x0d, 0xd0, 0x00, 0x78, 0x03, 0x28, 0x03, 0xd0, 0x01, 0x28, 0x01, 0xd0, 0x02, 0x28, + 0x06, 0xd1, 0x20, 0x1c, + + 0x01, 0x05, 0xff, 0xff, 0x9a, 0x6e, 0x08, 0x00, 0xfa, 0xa6, 0x46, 0x77, 0x49, 0xfe, 0x44, + 0x08, 0x47, 0x77, 0x48, 0x04, 0x70, 0x10, 0xbd, 0x10, 0xb5, 0x1b, 0x48, 0x00, 0x68, 0x50, + 0x28, 0x06, 0xd9, 0x17, 0x48, 0x00, 0x21, 0x01, 0x22, 0x96, 0x46, 0x78, 0x4a, 0xfe, 0x44, + 0x10, 0x47, 0x71, 0x48, 0x00, 0x78, 0xff, 0x21, 0x21, 0x31, 0x41, 0x43, 0x6e, 0x48, 0x41, + 0x18, 0x6f, 0x4c, 0x20, 0x78, 0x14, 0x28, 0x0d, 0xda, 0x00, 0x28, 0x14, 0xd0, 0x6d, 0x49, + 0x09, 0x78, 0xc9, 0x08, 0x05, 0xd3, 0x01, 0x20, 0x86, 0x46, 0x6b, 0x49, 0xfe, 0x44, 0x08, + 0x47, 0x20, 0x78, 0x40, 0x1c, 0x07, 0xe0, 0xff, 0x20, 0x08, 0x70, 0x01, 0x20, 0x86, 0x46, + 0x67, 0x48, 0xfe, 0x44, 0x00, 0x47, 0x00, 0x20, 0x20, 0x70, 0x10, 0xbd, 0xc0, 0x46, 0x4e, + 0x05, 0x00, 0x00, 0xb5, 0xcc, 0x00, 0x00, 0xf8, 0x21, 0x19, 0x00, 0xb3, 0x08, 0x05, 0x00, + 0x5c, 0x66, 0x08, 0x00, 0x71, 0xcd, 0x01, 0x00, 0x4a, 0x24, 0x19, 0x00, 0x54, 0x24, 0x08, + 0x00, 0x07, 0x0e, 0x00, 0x00, 0x21, 0xcb, 0x03, 0x00, 0x00, 0x66, 0xe3, 0x01, 0x0b, 0xc9, + 0x03, 0x00, 0x2b, 0xf0, 0x04, 0x00, 0x45, 0x10, 0x08, 0x00, 0xf0, 0xb5, 0x06, 0x1c, 0x74, + 0x69, 0x30, 0x69, 0x5d, 0x4d, 0x29, 0x78, 0x33, 0x7e, 0x0a, 0x22, 0x9b, 0x1a, 0x6d, 0xd0, + 0x06, 0x3b, 0x53, 0xd0, 0x31, 0x3b, 0x2e, 0xd0, 0x3b, 0x3b, 0x03, 0x2b, 0x78, 0xd8, 0xff, + 0x21, 0x7d, 0x31, 0x09, 0x5d, 0x02, 0x29, 0x73, 0xd1, 0xff, 0x22, 0x81, 0x32, 0x01, 0x78, + 0x11, 0x55, 0xff, 0x21, 0x82, 0x31, 0x09, 0x19, 0x42, 0x78, 0x01, 0x30, 0x0a, 0x70, 0xff, + 0x22, 0x83, 0x32, 0x12, 0x19, 0x43, 0x78, 0x13, 0x70, 0x40, 0x78, 0xff, 0x23, 0x25, 0x33, + 0x18, 0x55, 0x08, 0x78, + + 0x01, 0x05, 0xff, 0xff, 0x94, 0x6f, 0x08, 0x00, 0xfa, 0x19, 0x28, 0x5d, 0xd1, 0x10, 0x78, + 0x37, 0x28, 0x5a, 0xd1, 0x20, 0x1c, 0x05, 0x21, 0x00, 0x22, 0x01, 0x25, 0xae, 0x46, 0x3d, + 0x4b, 0xfe, 0x44, 0x18, 0x47, 0x20, 0x1c, 0xae, 0x46, 0x3c, 0x49, 0xfe, 0x44, 0x08, 0x47, + 0x48, 0xe0, 0x00, 0x29, 0x0a, 0xd1, 0x8a, 0x20, 0x00, 0x19, 0x3b, 0x49, 0x30, 0x22, 0x01, + 0x23, 0x9e, 0x46, 0x3a, 0x4b, 0xfe, 0x44, 0x18, 0x47, 0x00, 0x28, 0x3f, 0xd1, 0xff, 0x20, + 0x5d, 0x30, 0x01, 0x5d, 0x05, 0x20, 0x03, 0x1c, 0x20, 0x1c, 0x41, 0x22, 0x01, 0x27, 0xbe, + 0x46, 0x34, 0x4f, 0xfe, 0x44, 0x38, 0x47, 0x20, 0x1c, 0x05, 0x21, 0x01, 0x22, 0x96, 0x46, + 0x2a, 0x4b, 0xfe, 0x44, 0x18, 0x47, 0x00, 0x20, 0x28, 0x70, 0x25, 0xe0, 0xff, 0x21, 0x7d, + 0x31, 0x00, 0x78, 0x08, 0x55, 0x2c, 0x4a, 0x20, 0x1c, 0x09, 0x5c, 0x53, 0x78, 0x99, 0x42, + 0x02, 0xdb, 0x12, 0x78, 0x91, 0x42, 0x1c, 0xdd, 0xff, 0x21, 0x79, 0x31, 0x09, 0x5c, 0x10, + 0x22, 0x20, 0x23, 0x01, 0x24, 0xa6, 0x46, 0x23, 0x4c, 0xfe, 0x44, 0x20, 0x47, 0x0d, 0xe0, + 0x24, 0x48, 0x00, 0x78, 0x33, 0x28, 0x0d, 0xdb, 0xff, 0x20, 0x79, 0x30, 0x01, 0x5d, 0x20, + 0x1c, 0x29, 0x23, 0x01, 0x24, 0xa6, 0x46, 0x1c, 0x4c, 0xfe, 0x44, 0x20, 0x47, 0x17, 0x48, + 0x16, 0x49, 0x08, 0x18, 0x30, 0x62, 0xf0, 0xbd, 0xc0, 0x46, 0xff, 0xff, 0xff, 0x01, 0x95, + 0x49, 0x05, 0x00, 0x5b, 0x32, 0x08, 0x00, 0xd1, 0xa1, 0x04, 0x00, 0x50, 0x66, 0x08, 0x00, + 0x54, 0x66, 0x08, 0x00, 0x48, 0x10, 0x08, 0x00, 0x3f, 0xa2, 0x04, 0x00, 0xab, 0xea, 0x01, + 0x00, 0x3d, 0x1d, 0x03, 0x00, 0x58, 0x66, 0x08, 0x00, 0xb9, 0x26, 0x08, 0x00, 0x61, 0x66, + 0x08, 0x00, 0x60, 0x66, + + 0x01, 0x05, 0xff, 0xff, 0x8e, 0x70, 0x08, 0x00, 0xfa, 0x08, 0x00, 0x35, 0x57, 0x08, 0x00, + 0x5d, 0x9a, 0x03, 0x00, 0x43, 0x1c, 0x03, 0x00, 0xfd, 0x06, 0x05, 0x00, 0x7f, 0xa6, 0x01, + 0x00, 0xb3, 0x53, 0x04, 0x00, 0xa4, 0x0f, 0x00, 0x00, 0xf9, 0xd2, 0x00, 0x00, 0xdc, 0x0f, + 0x08, 0x00, 0x5f, 0x6a, 0x05, 0x00, 0xed, 0xe7, 0x00, 0x00, 0xb2, 0x11, 0x08, 0x00, 0x63, + 0x66, 0x08, 0x00, 0x69, 0x53, 0x08, 0x00, 0x40, 0x1e, 0x80, 0x00, 0x8c, 0x4b, 0x19, 0x50, + 0x8a, 0x49, 0x0a, 0x50, 0xf7, 0x46, 0xf0, 0xb5, 0x01, 0x1c, 0x88, 0x69, 0x82, 0x88, 0x53, + 0x04, 0x5b, 0x0c, 0x88, 0x4e, 0x88, 0x4f, 0x89, 0x4d, 0x03, 0xd0, 0x60, 0x2b, 0x01, 0xdc, + 0xb2, 0x42, 0x14, 0xd0, 0x82, 0x88, 0x01, 0x23, 0x1b, 0x03, 0x54, 0x04, 0x64, 0x0f, 0x24, + 0x03, 0x9c, 0x42, 0x08, 0xdb, 0x3b, 0x1c, 0x01, 0x33, 0x54, 0x04, 0x24, 0x0d, 0xe4, 0x00, + 0x9c, 0x42, 0x01, 0xda, 0xba, 0x42, 0x03, 0xd0, 0xff, 0x20, 0x88, 0x60, 0xe8, 0x1d, 0xf0, + 0xbd, 0x01, 0x24, 0xa6, 0x46, 0x7b, 0x49, 0xfe, 0x44, 0x08, 0x47, 0x38, 0x1c, 0xa6, 0x46, + 0x7a, 0x49, 0xfe, 0x44, 0x08, 0x47, 0x30, 0x1c, 0xa6, 0x46, 0x77, 0x49, 0xfe, 0x44, 0x08, + 0x47, 0xa6, 0x46, 0x76, 0x48, 0xfe, 0x44, 0x00, 0x47, 0x38, 0x1c, 0xa6, 0x46, 0x73, 0x49, + 0xfe, 0x44, 0x08, 0x47, 0xe8, 0x48, 0x01, 0x68, 0x30, 0x1c, 0xa6, 0x46, 0x71, 0x4a, 0xfe, + 0x44, 0x10, 0x47, 0x71, 0x48, 0x40, 0x19, 0xf0, 0xbd, 0x01, 0x1c, 0x0a, 0x7d, 0x6f, 0x48, + 0x00, 0x2a, 0x02, 0xd0, 0xc9, 0x68, 0x01, 0x29, 0x01, 0xd0, 0x4f, 0x30, 0xf7, 0x46, 0x31, + 0x30, 0xf7, 0x46, 0x41, 0x68, 0x02, 0x39, 0x41, 0x60, 0xe9, 0x48, 0x3f, 0x30, 0xf7, 0x46, + 0x1c, 0xb5, 0x41, 0x68, + + 0x01, 0x05, 0xff, 0xff, 0x88, 0x71, 0x08, 0x00, 0xfa, 0xe8, 0x4c, 0x00, 0x29, 0x17, 0xd0, + 0x41, 0x69, 0xb0, 0x20, 0x40, 0x18, 0xb8, 0x31, 0x6a, 0x46, 0x01, 0x23, 0x9e, 0x46, 0xe2, + 0x4b, 0xfe, 0x44, 0x18, 0x47, 0x00, 0x99, 0xe2, 0x48, 0xe4, 0x38, 0x41, 0x43, 0x68, 0x46, + 0x80, 0x88, 0x41, 0x18, 0xff, 0x20, 0xae, 0x30, 0x81, 0x42, 0x02, 0xd9, 0x20, 0x1c, 0xbf, + 0x30, 0x1c, 0xbd, 0x20, 0x1c, 0xdf, 0x30, 0x1c, 0xbd, 0x10, 0xb5, 0x04, 0x1c, 0xc8, 0x68, + 0x0a, 0x21, 0x01, 0x22, 0x96, 0x46, 0xe0, 0x4a, 0xfe, 0x44, 0x10, 0x47, 0x00, 0x28, 0x00, + 0xd1, 0x8c, 0x20, 0xa0, 0x60, 0xde, 0x48, 0xdb, 0x30, 0x10, 0xbd, 0xde, 0x49, 0x04, 0x39, + 0x09, 0x78, 0x00, 0x29, 0x01, 0xd1, 0x00, 0x21, 0x41, 0x60, 0x41, 0x68, 0x42, 0x69, 0x51, + 0x1a, 0x41, 0x60, 0xd8, 0x48, 0x55, 0x30, 0xf7, 0x46, 0xd8, 0x49, 0x09, 0x78, 0x00, 0x29, + 0x04, 0xd1, 0x01, 0x7d, 0xd5, 0x48, 0x02, 0x30, 0xff, 0x22, 0x42, 0x54, 0xd5, 0x48, 0x4f, + 0x30, 0xf7, 0x46, 0x01, 0x1c, 0x8a, 0x69, 0x4b, 0x68, 0xd3, 0x48, 0x9a, 0x42, 0x01, 0xd9, + 0x3b, 0x30, 0xf7, 0x46, 0xca, 0x60, 0x79, 0x30, 0xf7, 0x46, 0xd1, 0x48, 0xcf, 0x49, 0x08, + 0x80, 0xd0, 0x48, 0xff, 0x30, 0xde, 0x30, 0xf7, 0x46, 0xc2, 0x69, 0xff, 0x21, 0x11, 0x31, + 0x8b, 0x5c, 0xcd, 0x49, 0x5b, 0x08, 0x08, 0xd3, 0xff, 0x23, 0x02, 0x33, 0x9a, 0x5c, 0x02, + 0x2a, 0x03, 0xd0, 0x01, 0x2a, 0x01, 0xd0, 0x03, 0x2a, 0x02, 0xd1, 0x08, 0x1c, 0x5b, 0x30, + 0xf7, 0x46, 0xff, 0x22, 0x42, 0x60, 0x08, 0x1c, 0x39, 0x30, 0xf7, 0x46, 0x00, 0xb5, 0x40, + 0x68, 0x01, 0x21, 0x8e, 0x46, 0xc2, 0x49, 0xfe, 0x44, 0x08, 0x47, 0xc2, 0x48, 0x57, 0x30, + 0x00, 0xbd, 0x02, 0x8a, + + 0x01, 0x05, 0xff, 0xff, 0x82, 0x72, 0x08, 0x00, 0xfa, 0x01, 0x79, 0x0a, 0x29, 0x00, 0xdb, + 0x0a, 0x21, 0xe8, 0x48, 0x8a, 0x42, 0x01, 0xdd, 0x5f, 0x30, 0xf7, 0x46, 0x5b, 0x30, 0xf7, + 0x46, 0xf0, 0xb5, 0x01, 0x24, 0xa6, 0x46, 0xe4, 0x48, 0xfe, 0x44, 0x00, 0x47, 0x01, 0x28, + 0x05, 0xd0, 0xa6, 0x46, 0xe1, 0x48, 0xfe, 0x44, 0x00, 0x47, 0x04, 0x28, 0x19, 0xd1, 0x02, + 0x26, 0xdf, 0x4d, 0x28, 0x79, 0x00, 0x28, 0x11, 0xd0, 0xe8, 0x7a, 0x06, 0x28, 0x0e, 0xd1, + 0x0e, 0x20, 0x01, 0x1c, 0xea, 0x4f, 0x38, 0x1c, 0x14, 0x38, 0xa6, 0x46, 0xe7, 0x4a, 0xfe, + 0x44, 0x10, 0x47, 0x38, 0x1c, 0x0e, 0x21, 0xa6, 0x46, 0xe4, 0x4a, 0xfe, 0x44, 0x10, 0x47, + 0x70, 0x35, 0x01, 0x3e, 0xe7, 0xd1, 0xa6, 0x46, 0xe3, 0x48, 0xfe, 0x44, 0x00, 0x47, 0xe2, + 0x49, 0x0b, 0x48, 0x54, 0x30, 0x40, 0x18, 0xf0, 0xbd, 0xc0, 0x46, 0x04, 0xf3, 0x1a, 0x00, + 0x80, 0x7b, 0x08, 0x00, 0x03, 0x80, 0x00, 0x00, 0x17, 0x10, 0x00, 0x00, 0xc9, 0xfb, 0x04, + 0x00, 0xbb, 0x15, 0x04, 0x00, 0x7d, 0xca, 0x03, 0x00, 0x05, 0x43, 0x02, 0x00, 0x0b, 0xc9, + 0x03, 0x00, 0xab, 0x02, 0x00, 0x00, 0xb1, 0x33, 0x02, 0x00, 0xf0, 0xb5, 0x8d, 0xb0, 0x01, + 0x90, 0x81, 0x69, 0x02, 0x91, 0x01, 0x7d, 0x03, 0x91, 0x42, 0x68, 0x00, 0x92, 0x80, 0x8b, + 0x40, 0x00, 0x04, 0x90, 0x6b, 0x48, 0x83, 0x30, 0x00, 0x78, 0x40, 0x00, 0x05, 0x90, 0x00, + 0x29, 0x01, 0xd1, 0x00, 0x20, 0xe1, 0xe0, 0x04, 0x98, 0x02, 0x04, 0x12, 0x0c, 0x06, 0x92, + 0x02, 0x98, 0x03, 0x99, 0x6b, 0x46, 0x01, 0x24, 0xa6, 0x46, 0xc6, 0x4d, 0xfe, 0x44, 0x28, + 0x47, 0x07, 0x90, 0x00, 0x9e, 0x00, 0x20, 0x08, 0x90, 0x80, 0x48, 0x09, 0x90, 0x0a, 0x90, + 0x7f, 0xe0, 0x09, 0x98, + + 0x01, 0x05, 0xff, 0xff, 0x7c, 0x73, 0x08, 0x00, 0xfa, 0x00, 0x28, 0x2b, 0xd0, 0x0b, 0x98, + 0x09, 0x90, 0x28, 0xe0, 0x05, 0x98, 0x87, 0x42, 0x25, 0xdb, 0x90, 0x79, 0x03, 0x28, 0x01, + 0xd0, 0x01, 0x28, 0x20, 0xd1, 0x79, 0x19, 0x05, 0x98, 0x08, 0x1a, 0x07, 0x99, 0xa6, 0x46, + 0xb8, 0x4a, 0xfe, 0x44, 0x10, 0x47, 0x07, 0x1c, 0x28, 0x1c, 0x07, 0x99, 0xa6, 0x46, 0xb5, + 0x4a, 0xfe, 0x44, 0x10, 0x47, 0x01, 0x04, 0x09, 0x0c, 0x3a, 0x04, 0x12, 0x0c, 0x00, 0x20, + 0xa6, 0x46, 0xb1, 0x4b, 0xfe, 0x44, 0x18, 0x47, 0x00, 0x28, 0x04, 0xd1, 0x0a, 0x98, 0x00, + 0x28, 0x03, 0xd0, 0x0a, 0x97, 0x01, 0xe0, 0x00, 0x20, 0x0a, 0x90, 0xb6, 0x68, 0x00, 0x2e, + 0x03, 0xd0, 0x30, 0x88, 0x07, 0x99, 0x88, 0x42, 0x49, 0xdb, 0x08, 0x98, 0x00, 0x28, 0x46, + 0xd1, 0x61, 0x49, 0x09, 0x98, 0x88, 0x42, 0x42, 0xd1, 0x0a, 0x98, 0x88, 0x42, 0x3f, 0xd1, + 0x03, 0x98, 0x00, 0x28, 0x2c, 0xd0, 0x02, 0x9d, 0x03, 0x98, 0x0c, 0x90, 0x00, 0x27, 0x28, + 0x88, 0x0b, 0x90, 0x04, 0x99, 0xa6, 0x46, 0x9c, 0x4a, 0xfe, 0x44, 0x10, 0x47, 0x00, 0x28, + 0x07, 0xd0, 0x0b, 0x99, 0x04, 0x98, 0xa6, 0x46, 0x98, 0x4a, 0xfe, 0x44, 0x10, 0x47, 0x00, + 0x28, 0x11, 0xd1, 0x08, 0x98, 0xb8, 0x42, 0x09, 0xd0, 0x08, 0x98, 0xc1, 0x00, 0x02, 0x98, + 0x40, 0x18, 0x29, 0x1c, 0x08, 0x22, 0xa6, 0x46, 0xef, 0x4b, 0xfe, 0x44, 0x18, 0x47, 0x08, + 0x98, 0x40, 0x1c, 0x00, 0x06, 0x00, 0x0e, 0x08, 0x90, 0x08, 0x35, 0x01, 0x37, 0x0c, 0x98, + 0x01, 0x38, 0x0c, 0x90, 0xd6, 0xd1, 0x08, 0x98, 0x00, 0x28, 0x0c, 0xd0, 0x02, 0x98, 0x08, + 0x99, 0x06, 0x9a, 0x6b, 0x46, 0xa6, 0x46, 0x85, 0x4d, 0xfe, 0x44, 0x28, 0x47, 0x07, 0x90, + 0x00, 0x9e, 0x3f, 0x48, + + 0x01, 0x05, 0xff, 0xff, 0x76, 0x74, 0x08, 0x00, 0xfa, 0x09, 0x90, 0x0a, 0x90, 0x00, 0x2e, + 0x3a, 0xd0, 0x35, 0x88, 0x07, 0x98, 0x85, 0x42, 0x36, 0xda, 0xb0, 0x68, 0x00, 0x28, 0x05, + 0xd0, 0x00, 0x88, 0x07, 0x99, 0x88, 0x42, 0x01, 0xdc, 0x47, 0x1b, 0x04, 0xe0, 0x07, 0x98, + 0x40, 0x1b, 0x00, 0x99, 0x09, 0x88, 0x0f, 0x18, 0x72, 0x68, 0x91, 0x88, 0x05, 0x98, 0x40, + 0x18, 0x87, 0x42, 0x00, 0xda, 0x6a, 0xe7, 0x48, 0x19, 0x07, 0x99, 0xa6, 0x46, 0x73, 0x4a, + 0xfe, 0x44, 0x10, 0x47, 0x0b, 0x90, 0x79, 0x19, 0x05, 0x98, 0x08, 0x1a, 0x07, 0x99, 0xa6, + 0x46, 0x6e, 0x4a, 0xfe, 0x44, 0x10, 0x47, 0x02, 0x04, 0x12, 0x0c, 0x0b, 0x98, 0x01, 0x04, + 0x09, 0x0c, 0x00, 0x20, 0xa6, 0x46, 0x6a, 0x4b, 0xfe, 0x44, 0x18, 0x47, 0x00, 0x28, 0x00, + 0xd1, 0x48, 0xe7, 0x00, 0x20, 0x09, 0x90, 0x06, 0xe0, 0xc0, 0x46, 0xe0, 0x31, 0x08, 0x00, + 0x1f, 0x48, 0x09, 0x99, 0x81, 0x42, 0x02, 0xd0, 0x09, 0x98, 0x0a, 0x90, 0x02, 0xe0, 0x0a, + 0x99, 0x81, 0x42, 0x01, 0xd0, 0x0a, 0x98, 0x04, 0xe0, 0x06, 0x98, 0xa6, 0x46, 0xe8, 0x49, + 0xfe, 0x44, 0x08, 0x47, 0x01, 0x99, 0x48, 0x60, 0xe5, 0x48, 0xff, 0x30, 0x10, 0x30, 0x0d, + 0xb0, 0xf0, 0xbd, 0xb7, 0x4b, 0x04, 0x00, 0x4f, 0x81, 0x03, 0x00, 0xfd, 0x79, 0x00, 0x00, + 0xc6, 0x05, 0x00, 0x00, 0xe0, 0x49, 0x09, 0x78, 0x2a, 0x29, 0x05, 0xd0, 0x33, 0x29, 0x01, + 0xd1, 0x0a, 0x21, 0x02, 0xe0, 0x06, 0x21, 0x00, 0xe0, 0x08, 0x21, 0x41, 0x60, 0xf1, 0x48, + 0x43, 0x30, 0xf7, 0x46, 0xc0, 0x46, 0x65, 0x2d, 0x00, 0x00, 0x79, 0x47, 0x00, 0x00, 0x0d, + 0x13, 0x02, 0x00, 0x76, 0x24, 0x08, 0x00, 0xe0, 0xa0, 0x1b, 0x00, 0x89, 0x28, 0x05, 0x00, + 0x3d, 0x39, 0x02, 0x00, + + 0x01, 0x05, 0xff, 0xff, 0x70, 0x75, 0x08, 0x00, 0xfa, 0x60, 0x5b, 0x08, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xd9, 0xaa, 0x00, 0x00, 0xd5, 0x75, 0x00, 0x00, 0x23, 0x01, 0x05, 0x00, 0xb3, + 0x09, 0x02, 0x00, 0xfc, 0xb5, 0x00, 0x90, 0xe2, 0x48, 0x00, 0x78, 0x02, 0x28, 0x43, 0xd1, + 0xe1, 0x49, 0xff, 0x20, 0x41, 0x30, 0x40, 0x18, 0x01, 0x90, 0xe0, 0x48, 0x40, 0x5c, 0x00, + 0x26, 0x38, 0xe0, 0xb1, 0x00, 0x01, 0x98, 0x45, 0x58, 0x00, 0x2d, 0x30, 0xd0, 0xfe, 0x20, + 0x40, 0x5d, 0x00, 0x28, 0x2c, 0xd0, 0xf2, 0x20, 0x41, 0x5d, 0x00, 0x29, 0x28, 0xd1, 0xff, + 0x20, 0x02, 0x30, 0x40, 0x5d, 0x01, 0x28, 0x23, 0xd0, 0x24, 0x20, 0x01, 0x24, 0xa6, 0x46, + 0xe7, 0x4a, 0xfe, 0x44, 0x10, 0x47, 0x00, 0x06, 0x00, 0x0e, 0xf3, 0x22, 0x51, 0x5d, 0x81, + 0x42, 0x17, 0xd0, 0x50, 0x55, 0x00, 0x28, 0x14, 0xd1, 0x2f, 0x8d, 0x39, 0x1c, 0xe1, 0x48, + 0xa6, 0x46, 0x24, 0x4a, 0xfe, 0x44, 0x10, 0x47, 0x29, 0x8e, 0x79, 0x18, 0x08, 0x1a, 0x40, + 0x1e, 0x39, 0x1c, 0xa6, 0x46, 0x20, 0x4a, 0xfe, 0x44, 0x10, 0x47, 0x40, 0x00, 0x28, 0x86, + 0x28, 0x8e, 0x40, 0x08, 0x28, 0x86, 0x01, 0x36, 0xd8, 0x48, 0x00, 0x78, 0x86, 0x42, 0xc4, + 0xd3, 0xff, 0x21, 0x41, 0x31, 0x00, 0x98, 0x41, 0x60, 0xd6, 0x48, 0xd5, 0x49, 0x08, 0x18, + 0xfc, 0xbd, 0xf9, 0x97, 0x00, 0x00, 0x8f, 0x8d, 0x01, 0x00, 0xa4, 0x13, 0x08, 0x00, 0x10, + 0xb5, 0x00, 0x79, 0x01, 0x28, 0x06, 0xd1, 0x88, 0x8b, 0x09, 0x7d, 0x01, 0x22, 0x96, 0x46, + 0xce, 0x4a, 0xfe, 0x44, 0x10, 0x47, 0xce, 0x4c, 0x20, 0x78, 0x01, 0x28, 0x04, 0xd1, 0x01, + 0x20, 0x86, 0x46, 0xcc, 0x48, 0xfe, 0x44, 0x00, 0x47, 0x00, 0x20, 0x20, 0x70, 0xca, 0x49, + 0xc5, 0x48, 0x88, 0x38, + + 0x01, 0x05, 0xff, 0xff, 0x6a, 0x76, 0x08, 0x00, 0xfa, 0x40, 0x18, 0x10, 0xbd, 0xc0, 0x46, + 0xe5, 0x06, 0x05, 0x00, 0x14, 0x05, 0x1a, 0x00, 0x77, 0xc5, 0x01, 0x00, 0xb9, 0xc5, 0x01, + 0x00, 0xa1, 0x95, 0x01, 0x00, 0x95, 0x49, 0x05, 0x00, 0x21, 0x96, 0x01, 0x00, 0xf0, 0xb5, + 0x86, 0xb0, 0x01, 0x92, 0x02, 0x91, 0x06, 0x1c, 0x88, 0x68, 0x00, 0x6a, 0x03, 0x90, 0x80, + 0x00, 0x01, 0x99, 0x44, 0x18, 0x04, 0x94, 0x04, 0x19, 0x05, 0x94, 0x04, 0x19, 0x01, 0x98, + 0x03, 0x99, 0x01, 0x25, 0xae, 0x46, 0xb8, 0x4a, 0xfe, 0x44, 0x10, 0x47, 0x04, 0x98, 0x03, + 0x99, 0xae, 0x46, 0xb5, 0x4a, 0xfe, 0x44, 0x10, 0x47, 0x05, 0x98, 0x03, 0x99, 0xae, 0x46, + 0xb2, 0x4a, 0xfe, 0x44, 0x10, 0x47, 0x02, 0x98, 0x80, 0x68, 0xc7, 0x69, 0x70, 0x68, 0x03, + 0x99, 0xae, 0x46, 0xae, 0x4a, 0xfe, 0x44, 0x10, 0x47, 0x00, 0x28, 0x50, 0xd1, 0x30, 0x68, + 0x39, 0x1c, 0x03, 0x9a, 0xae, 0x46, 0xab, 0x4b, 0xfe, 0x44, 0x18, 0x47, 0x00, 0x28, 0x47, + 0xd5, 0x70, 0x68, 0x39, 0x1c, 0x03, 0x9a, 0xae, 0x46, 0xa6, 0x4b, 0xfe, 0x44, 0x18, 0x47, + 0x00, 0x28, 0x3e, 0xd5, 0x30, 0x68, 0x01, 0x99, 0x02, 0x9a, 0x23, 0x1c, 0xae, 0x46, 0xa2, + 0x4f, 0xfe, 0x44, 0x38, 0x47, 0x00, 0x94, 0x30, 0x68, 0x01, 0x99, 0x04, 0x9a, 0x02, 0x9b, + 0xae, 0x46, 0x9f, 0x4f, 0xfe, 0x44, 0x38, 0x47, 0x00, 0x94, 0x30, 0x68, 0x02, 0x99, 0x89, + 0x68, 0xc9, 0x6a, 0x01, 0x9a, 0x02, 0x9b, 0xae, 0x46, 0x99, 0x4f, 0xfe, 0x44, 0x38, 0x47, + 0x01, 0x98, 0x04, 0x99, 0x05, 0x9a, 0x02, 0x9b, 0xae, 0x46, 0x96, 0x4f, 0xfe, 0x44, 0x38, + 0x47, 0x02, 0x98, 0x80, 0x68, 0x01, 0x6b, 0x05, 0x98, 0x01, 0x9a, 0x02, 0x9b, 0xae, 0x46, + 0x91, 0x4f, 0xfe, 0x44, + + 0x01, 0x05, 0xff, 0xff, 0x64, 0x77, 0x08, 0x00, 0xfa, 0x38, 0x47, 0x70, 0x68, 0x04, 0x99, + 0x02, 0x9a, 0x23, 0x1c, 0xae, 0x46, 0x8b, 0x4c, 0xfe, 0x44, 0x20, 0x47, 0x01, 0x98, 0x04, + 0x99, 0x03, 0x9a, 0xae, 0x46, 0x87, 0x4b, 0xfe, 0x44, 0x18, 0x47, 0x00, 0x28, 0x02, 0xd0, + 0x00, 0x20, 0xc0, 0x43, 0x00, 0xe0, 0x00, 0x20, 0x06, 0xb0, 0xf0, 0xbd, 0xf0, 0xb5, 0x85, + 0xb0, 0x45, 0x69, 0x86, 0x69, 0xc0, 0x69, 0x02, 0x90, 0x0f, 0x6a, 0x48, 0x6a, 0x03, 0x90, + 0x88, 0x6b, 0x04, 0x90, 0x28, 0x1c, 0x00, 0x95, 0x31, 0x6a, 0x89, 0x00, 0x4d, 0x19, 0x01, + 0x95, 0x4d, 0x19, 0x31, 0x68, 0x01, 0x29, 0x0d, 0xd1, 0x72, 0x69, 0x39, 0x1c, 0x01, 0x24, + 0xa6, 0x46, 0x0d, 0x4b, 0xfe, 0x44, 0x18, 0x47, 0x72, 0x69, 0x01, 0x98, 0xb9, 0x18, 0xa6, + 0x46, 0x09, 0x4b, 0xfe, 0x44, 0x18, 0x47, 0x68, 0x46, 0x02, 0x99, 0x2a, 0x1c, 0x01, 0x24, + 0xa6, 0x46, 0x71, 0x4b, 0xfe, 0x44, 0x18, 0x47, 0x73, 0x49, 0x70, 0x4d, 0x00, 0x28, 0x06, + 0xd0, 0x0c, 0x70, 0x28, 0x1c, 0x21, 0x30, 0x0d, 0xe0, 0xc0, 0x46, 0x5d, 0x5f, 0x05, 0x00, + 0x00, 0x20, 0x08, 0x70, 0xb2, 0x69, 0x03, 0x98, 0x04, 0x99, 0xa6, 0x46, 0x69, 0x4b, 0xfe, + 0x44, 0x18, 0x47, 0x28, 0x1c, 0xa9, 0x30, 0x05, 0xb0, 0xf0, 0xbd, 0x69, 0x48, 0x00, 0x78, + 0x00, 0x07, 0x40, 0x0e, 0x89, 0x6b, 0x49, 0x06, 0x89, 0x0c, 0x08, 0x18, 0x64, 0x49, 0x08, + 0x80, 0x65, 0x48, 0xc5, 0x30, 0xf7, 0x46, 0x01, 0x1c, 0x0a, 0x79, 0x63, 0x48, 0x06, 0x2a, + 0x03, 0xdb, 0x92, 0x1f, 0x4a, 0x60, 0x2f, 0x30, 0xf7, 0x46, 0x79, 0x30, 0xf7, 0x46, 0x01, + 0x1c, 0x88, 0x69, 0x43, 0x78, 0x3e, 0x22, 0x1a, 0x40, 0x5d, 0x48, 0x06, 0x2a, 0x05, 0xdb, + 0x9a, 0x06, 0x92, 0x0e, + + 0x01, 0x05, 0xff, 0xff, 0x5e, 0x78, 0x08, 0x00, 0xfa, 0x06, 0x3a, 0x4a, 0x60, 0x23, 0x30, + 0xf7, 0x46, 0x49, 0x30, 0xf7, 0x46, 0x30, 0xb5, 0x45, 0x68, 0xff, 0x20, 0x11, 0x30, 0x40, + 0x5d, 0x80, 0x09, 0x0c, 0xd3, 0x11, 0x21, 0x09, 0x01, 0x4a, 0x5d, 0xdf, 0x20, 0x10, 0x40, + 0x48, 0x55, 0x28, 0x1c, 0x00, 0x21, 0x01, 0x22, 0x96, 0x46, 0x50, 0x4a, 0xfe, 0x44, 0x10, + 0x47, 0x01, 0x20, 0x04, 0x1c, 0x86, 0x46, 0x4e, 0x49, 0xfe, 0x44, 0x08, 0x47, 0xa9, 0x8b, + 0x28, 0x1c, 0xa6, 0x46, 0x4c, 0x4a, 0xfe, 0x44, 0x10, 0x47, 0x4c, 0x48, 0x9b, 0x30, 0x30, + 0xbd, 0x6d, 0x95, 0x00, 0x00, 0x4d, 0x96, 0x01, 0x00, 0x69, 0x53, 0x08, 0x00, 0x10, 0xb5, + 0x02, 0x1c, 0x4c, 0x20, 0x43, 0x5a, 0x46, 0x48, 0x06, 0x2b, 0x0d, 0xdb, 0x19, 0x24, 0xe4, + 0x01, 0xa3, 0x42, 0x09, 0xdc, 0x54, 0x23, 0x59, 0x5a, 0x0a, 0x29, 0x05, 0xdb, 0xa1, 0x42, + 0x03, 0xdc, 0x00, 0x21, 0x51, 0x60, 0x99, 0x30, 0x10, 0xbd, 0x3f, 0x4a, 0x00, 0x21, 0xff, + 0x23, 0x21, 0x33, 0x4b, 0x43, 0xd3, 0x5c, 0xff, 0x2b, 0x04, 0xd0, 0x49, 0x1c, 0x09, 0x06, + 0x09, 0x0e, 0x0a, 0x29, 0xf4, 0xdb, 0x49, 0x1e, 0x39, 0x4a, 0x11, 0x70, 0x01, 0x21, 0x38, + 0x4a, 0x11, 0x70, 0xff, 0x30, 0x26, 0x30, 0x10, 0xbd, 0xc0, 0x46, 0x7b, 0x5e, 0x02, 0x00, + 0x45, 0x10, 0x08, 0x00, 0x54, 0x24, 0x08, 0x00, 0x07, 0x0e, 0x00, 0x00, 0x7f, 0xb5, 0x45, + 0x69, 0x10, 0x26, 0x32, 0x1c, 0x68, 0x46, 0x00, 0x21, 0x01, 0x24, 0xa6, 0x46, 0x2e, 0x4b, + 0xfe, 0x44, 0x18, 0x47, 0xff, 0x20, 0x31, 0x30, 0x40, 0x19, 0x69, 0x46, 0x32, 0x1c, 0xa6, + 0x46, 0x2b, 0x4b, 0xfe, 0x44, 0x18, 0x47, 0x00, 0x28, 0x08, 0xd1, 0xff, 0x20, 0x25, 0x30, + 0x05, 0x21, 0x41, 0x55, + + 0x01, 0x05, 0xff, 0xff, 0x58, 0x79, 0x08, 0x00, 0xfa, 0x28, 0x1c, 0xa6, 0x46, 0x26, 0x49, + 0xfe, 0x44, 0x08, 0x47, 0x26, 0x48, 0x6f, 0x30, 0x00, 0x90, 0x7f, 0xbd, 0x25, 0x48, 0xa5, + 0x30, 0xf7, 0x46, 0x2b, 0xf0, 0x04, 0x00, 0xff, 0xff, 0xff, 0x01, 0x5b, 0x32, 0x08, 0x00, + 0x8f, 0x02, 0x00, 0x00, 0xcd, 0x92, 0x01, 0x00, 0x6f, 0x4b, 0x02, 0x00, 0x58, 0x66, 0x08, + 0x00, 0x43, 0x1c, 0x03, 0x00, 0xf5, 0x70, 0x00, 0x00, 0x21, 0x37, 0x05, 0x00, 0xe9, 0x36, + 0x05, 0x00, 0xa9, 0x36, 0x05, 0x00, 0xc3, 0x6c, 0x05, 0x00, 0xa5, 0x6c, 0x05, 0x00, 0xd1, + 0x1c, 0x05, 0x00, 0x8d, 0x76, 0x08, 0x00, 0x91, 0x48, 0x05, 0x00, 0x47, 0x37, 0x05, 0x00, + 0x63, 0x66, 0x08, 0x00, 0x1c, 0x30, 0x19, 0x00, 0x96, 0xa5, 0x1b, 0x00, 0x61, 0x61, 0x03, + 0x00, 0xe1, 0x72, 0x03, 0x00, 0x5f, 0x73, 0x03, 0x00, 0xf5, 0x56, 0x02, 0x00, 0xbb, 0x88, + 0x03, 0x00, 0x3b, 0x3b, 0x02, 0x00, 0x9d, 0xcf, 0x02, 0x00, 0x6d, 0x23, 0x03, 0x00, 0xb9, + 0x26, 0x08, 0x00, 0x61, 0x66, 0x08, 0x00, 0x60, 0x66, 0x08, 0x00, 0xe9, 0x63, 0x05, 0x00, + 0x5f, 0x6a, 0x05, 0x00, 0xd5, 0x12, 0x04, 0x00, 0x5f, 0xde, 0x04, 0x00, 0xbd, 0x65, 0x01, + 0x00, 0xff, 0xb5, 0x68, 0x46, 0xff, 0xf7, 0xc0, 0xf9, 0xff, 0xbd, 0xff, 0xb5, 0x68, 0x46, + 0xff, 0xf7, 0xd9, 0xf9, 0xff, 0xbd, 0xff, 0xb5, 0x68, 0x46, 0xff, 0xf7, 0x00, 0xf9, 0xff, + 0xbd, 0xff, 0xb5, 0x68, 0x46, 0xff, 0xf7, 0xb5, 0xf8, 0xff, 0xbd, 0xff, 0xb5, 0x68, 0x46, + 0xff, 0xf7, 0x3a, 0xfa, 0xff, 0xbd, 0xff, 0xb5, 0x68, 0x46, 0xff, 0xf7, 0x81, 0xfa, 0xff, + 0xbd, 0xff, 0xb5, 0x68, 0x46, 0xff, 0xf7, 0xdc, 0xf9, 0xff, 0xbd, 0xff, 0xb5, 0x68, 0x46, + 0xff, 0xf7, 0xf9, 0xf9, + + 0x01, 0x05, 0xff, 0x6b, 0x52, 0x7a, 0x08, 0x00, 0x66, 0xff, 0xbd, 0xff, 0xb5, 0x68, 0x46, + 0xfe, 0xf7, 0x41, 0xfe, 0xff, 0xbd, 0xff, 0xb5, 0x68, 0x46, 0xfe, 0xf7, 0x73, 0xfe, 0xff, + 0xbd, 0xff, 0xb5, 0x68, 0x46, 0xfe, 0xf7, 0x19, 0xfe, 0xff, 0xbd, 0xff, 0xb5, 0x68, 0x46, + 0xfe, 0xf7, 0xff, 0xfd, 0xff, 0xbd, 0xff, 0xb5, 0x68, 0x46, 0xfe, 0xf7, 0x08, 0xfe, 0xff, + 0xbd, 0xff, 0xb5, 0x68, 0x46, 0xff, 0xf7, 0x07, 0xf8, 0xff, 0xbd, 0xff, 0xb5, 0x68, 0x46, + 0xff, 0xf7, 0x55, 0xf9, 0xff, 0xbd, 0xff, 0xb5, 0x68, 0x46, 0xfe, 0xf7, 0xd5, 0xfe, 0xff, + 0xbd, 0xff, 0xb5, 0x68, 0x46, 0xfe, 0xf7, 0x7b, 0xfe, 0xff, 0xbd, 0xff, 0xb5, 0x68, 0x46, + 0xfe, 0xf7, 0x9c, 0xfe, 0xff, 0xbd, + + 0x01, 0x05, 0xff, 0x8d, 0x78, 0x7b, 0x08, 0x00, 0x88, 0x00, 0xb5, 0xf8, 0xf0, 0xe3, 0xfa, + 0x00, 0xbd, 0xd7, 0x70, 0x08, 0x00, 0x79, 0x71, 0x08, 0x00, 0x3d, 0x72, 0x08, 0x00, 0x6d, + 0x72, 0x08, 0x00, 0x81, 0x72, 0x08, 0x00, 0x99, 0x72, 0x08, 0x00, 0x35, 0x75, 0x08, 0x00, + 0x89, 0x75, 0x08, 0x00, 0x95, 0x77, 0x08, 0x00, 0x61, 0x71, 0x08, 0x00, 0xd5, 0x65, 0x08, + 0x00, 0xe5, 0x71, 0x08, 0x00, 0x01, 0x72, 0x08, 0x00, 0x19, 0x72, 0x08, 0x00, 0x2f, 0x72, + 0x08, 0x00, 0x29, 0x73, 0x08, 0x00, 0x39, 0x76, 0x08, 0x00, 0x1b, 0x78, 0x08, 0x00, 0x35, + 0x78, 0x08, 0x00, 0x4b, 0x78, 0x08, 0x00, 0x6b, 0x78, 0x08, 0x00, 0xc5, 0x71, 0x08, 0x00, + 0xbd, 0x78, 0x08, 0x00, 0x25, 0x79, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x79, 0x08, 0x00, + + 0x01, 0x05, 0xff, 0x85, 0x04, 0xf3, 0x1a, 0x00, 0x80, 0xce, 0xfb, 0x04, 0x00, 0xf4, 0x4b, + 0x04, 0x00, 0x0c, 0x76, 0x00, 0x00, 0x06, 0x0a, 0x02, 0x00, 0x50, 0x98, 0x00, 0x00, 0xb4, + 0xc6, 0x01, 0x00, 0xb8, 0x5e, 0x02, 0x00, 0x58, 0x95, 0x01, 0x00, 0x2e, 0x49, 0x05, 0x00, + 0xde, 0x33, 0x02, 0x00, 0x14, 0x29, 0x02, 0x00, 0x60, 0x13, 0x02, 0x00, 0xd6, 0x28, 0x05, + 0x00, 0x74, 0x39, 0x02, 0x00, 0x88, 0xac, 0x00, 0x00, 0x7a, 0x95, 0x00, 0x00, 0xe8, 0x72, + 0x00, 0x00, 0x1c, 0x62, 0x03, 0x00, 0x0e, 0x73, 0x03, 0x00, 0x7e, 0x73, 0x03, 0x00, 0x34, + 0xd0, 0x02, 0x00, 0x52, 0x48, 0x00, 0x00, 0x04, 0x24, 0x03, 0x00, 0xa0, 0xde, 0x04, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x5a, 0x66, + 0x01, 0x00, + + 0x01, 0x05, 0xff, 0xff, 0x00, 0x00, 0x18, 0x00, 0xfa, 0x70, 0xb5, 0x3a, 0x4d, 0xae, 0x7f, + 0x01, 0x24, 0xa6, 0x46, 0x36, 0x48, 0xfe, 0x44, 0x00, 0x47, 0xb0, 0x42, 0xf8, 0xd1, 0x03, + 0x20, 0x17, 0x21, 0x89, 0x01, 0xa6, 0x46, 0x32, 0x4a, 0xfe, 0x44, 0x10, 0x47, 0xad, 0x7f, + 0xa6, 0x46, 0x2f, 0x48, 0xfe, 0x44, 0x00, 0x47, 0xa8, 0x42, 0xf9, 0xd1, 0xfe, 0xe7, 0x30, + 0xb5, 0x2e, 0x49, 0x08, 0x1f, 0x2e, 0x4a, 0x10, 0x60, 0x31, 0x48, 0x02, 0x1c, 0x71, 0x3a, + 0x93, 0x24, 0x01, 0x23, 0xa3, 0x54, 0x2b, 0x4b, 0x0b, 0x60, 0x02, 0x23, 0x13, 0x71, 0x2a, + 0x4b, 0x4b, 0x60, 0x03, 0x23, 0x53, 0x71, 0x29, 0x4b, 0x8b, 0x60, 0x04, 0x23, 0x03, 0x70, + 0x29, 0x4b, 0xcb, 0x60, 0x05, 0x23, 0x83, 0x73, 0x28, 0x4b, 0x0b, 0x61, 0x06, 0x23, 0x03, + 0x73, 0x27, 0x4b, 0x4b, 0x61, 0x07, 0x23, 0x43, 0x71, 0x26, 0x4b, 0x8b, 0x61, 0x08, 0x24, + 0x03, 0x1c, 0x33, 0x3b, 0x1c, 0x70, 0x24, 0x4b, 0xcb, 0x61, 0x09, 0x23, 0xd3, 0x74, 0x23, + 0x4b, 0x0b, 0x62, 0x0a, 0x23, 0xd3, 0x71, 0xd3, 0x1d, 0x22, 0x4c, 0x4c, 0x62, 0x0b, 0x24, + 0x44, 0x77, 0x21, 0x4c, 0x8c, 0x62, 0x0c, 0x25, 0x04, 0x1c, 0x3d, 0x3c, 0x25, 0x70, 0x1f, + 0x4c, 0xcc, 0x62, 0x0f, 0x38, 0x0d, 0x24, 0x04, 0x70, 0x1d, 0x4c, 0x0c, 0x63, 0x0e, 0x24, + 0x04, 0x73, 0x1c, 0x4c, 0x4c, 0x63, 0x0f, 0x24, 0x44, 0x73, 0x1b, 0x4c, 0x8c, 0x63, 0x10, + 0x24, 0x04, 0x77, 0x1a, 0x48, 0xc8, 0x63, 0x11, 0x20, 0x10, 0x70, 0x19, 0x48, 0x08, 0x64, + 0x12, 0x20, 0x98, 0x76, 0x18, 0x48, 0x48, 0x64, 0x30, 0xbd, 0xc0, 0x46, 0x25, 0x86, 0x04, + 0x00, 0x1b, 0x90, 0x04, 0x00, 0x6c, 0x52, 0x08, 0x00, 0x08, 0x66, 0x08, 0x00, 0x20, 0x55, + 0x08, 0x00, 0x73, 0x7a, + + 0x01, 0x05, 0xff, 0xff, 0xfa, 0x00, 0x18, 0x00, 0xfa, 0x08, 0x00, 0x5f, 0x7a, 0x08, 0x00, + 0x55, 0x7a, 0x08, 0x00, 0x95, 0x55, 0x08, 0x00, 0xa5, 0x7a, 0x08, 0x00, 0x7d, 0x7a, 0x08, + 0x00, 0x69, 0x7a, 0x08, 0x00, 0xaf, 0x7a, 0x08, 0x00, 0x9b, 0x7a, 0x08, 0x00, 0x87, 0x7a, + 0x08, 0x00, 0x19, 0x7a, 0x08, 0x00, 0x91, 0x7a, 0x08, 0x00, 0x23, 0x7a, 0x08, 0x00, 0x05, + 0x7a, 0x08, 0x00, 0x0f, 0x7a, 0x08, 0x00, 0x41, 0x7a, 0x08, 0x00, 0x4b, 0x7a, 0x08, 0x00, + 0x2d, 0x7a, 0x08, 0x00, 0x37, 0x7a, 0x08, 0x00, 0xf0, 0xb5, 0x31, 0x4e, 0x0c, 0x22, 0x32, + 0x70, 0x1a, 0x23, 0x73, 0x70, 0x09, 0x20, 0xb0, 0x70, 0x18, 0x20, 0xf0, 0x70, 0x03, 0x20, + 0x2e, 0x4d, 0x29, 0x1c, 0x01, 0x39, 0x01, 0x24, 0xa6, 0x46, 0x2a, 0x4f, 0xfe, 0x44, 0x38, + 0x47, 0xb2, 0x78, 0xf3, 0x78, 0x03, 0x20, 0x29, 0x1c, 0xa6, 0x46, 0x26, 0x4e, 0xfe, 0x44, + 0x30, 0x47, 0x03, 0x20, 0x29, 0x1c, 0x01, 0x31, 0xa6, 0x46, 0x25, 0x4a, 0xfe, 0x44, 0x10, + 0x47, 0xa6, 0x46, 0x24, 0x48, 0xfe, 0x44, 0x00, 0x47, 0x23, 0x4b, 0x00, 0x21, 0x08, 0x1c, + 0x1a, 0x68, 0x00, 0x2a, 0x04, 0xd0, 0x02, 0x07, 0x15, 0x0f, 0x22, 0x1c, 0xaa, 0x40, 0x11, + 0x43, 0x02, 0x07, 0x12, 0x0f, 0x0f, 0x2a, 0x05, 0xd1, 0xc5, 0x08, 0x06, 0x22, 0x2a, 0x40, + 0x1b, 0x4d, 0xa9, 0x52, 0x00, 0x21, 0x04, 0x33, 0x01, 0x30, 0x20, 0x28, 0xe9, 0xd3, 0x1a, + 0x48, 0x01, 0x1c, 0x50, 0x31, 0x0c, 0x70, 0x0a, 0x21, 0x16, 0x4a, 0x11, 0x70, 0x33, 0x21, + 0x01, 0x70, 0x00, 0x25, 0x16, 0x48, 0x05, 0x60, 0x28, 0x1c, 0xa6, 0x46, 0x16, 0x49, 0xfe, + 0x44, 0x08, 0x47, 0x13, 0x49, 0x08, 0x60, 0x14, 0x48, 0x05, 0x60, 0x07, 0x20, 0x14, 0x49, + 0x08, 0x70, 0x14, 0x4a, + + 0x01, 0x05, 0xff, 0x65, 0xf4, 0x01, 0x18, 0x00, 0x60, 0x91, 0x78, 0x02, 0x20, 0x08, 0x43, + 0x90, 0x70, 0x12, 0x48, 0x05, 0x70, 0x12, 0x48, 0x05, 0x70, 0x12, 0x48, 0x05, 0x70, 0xf0, + 0xbd, 0xc0, 0x46, 0xfc, 0x53, 0x08, 0x00, 0x31, 0x90, 0x04, 0x00, 0xc6, 0x05, 0x00, 0x00, + 0x1b, 0x90, 0x04, 0x00, 0x33, 0x00, 0x18, 0x00, 0x80, 0x7b, 0x08, 0x00, 0x84, 0xf3, 0x1a, + 0x00, 0x6d, 0x22, 0x08, 0x00, 0x69, 0x53, 0x08, 0x00, 0x50, 0x66, 0x08, 0x00, 0x54, 0x66, + 0x08, 0x00, 0xd1, 0xa1, 0x04, 0x00, 0x5c, 0x66, 0x08, 0x00, 0xb3, 0x11, 0x08, 0x00, 0xe4, + 0x15, 0x08, 0x00, 0x63, 0x66, 0x08, 0x00, 0x60, 0x66, 0x08, 0x00, 0x61, 0x66, 0x08, 0x00, + + + 0x01, 0x83, 0xff, 0x14, 0x79, 0x7b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + // + // ###### + // ### Set the BT Core spec to 4.2 for Bluetopia stack compatibility + // ### Remove below VS command for default BT core spec 5.1 + 0x01, 0x09, 0xfd, 0x08, 0x68, 0x53, 0x08, 0x00, 0x00, 0x2a, 0x00, 0xff, + + // ###### + // + 0x01, 0x0c, 0xfd, 0x09, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x64, 0x00, + + 0x01, 0x09, 0xfd, 0x08, 0x58, 0x60, 0x1a, 0x00, 0x00, 0x10, 0x00, 0x10, + + 0x01, 0x09, 0xfd, 0x08, 0x10, 0x60, 0x1a, 0x00, 0x10, 0x00, 0x10, 0x00, + + 0x01, 0x1c, 0xfd, 0x14, 0xff, 0x88, 0x13, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0xfa, 0x00, 0xff, 0xff, 0x00, + + // + // + // ##-------------------------------------------------------------------------------- + // ## Description: ORCA_C Commercial PHY FW Initialization Script + // ##-------------------------------------------------------------------------------- + 0x01, 0x76, 0xfd, 0x31, 0x01, 0x21, 0x54, 0x00, 0x00, 0x61, 0x57, 0x00, 0x00, 0x14, 0x05, + 0x0a, 0x05, 0x00, 0x07, 0x06, 0x0a, 0x04, 0x05, 0x08, 0x09, 0x0b, 0x0c, 0x0d, 0x0e, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, + + // BTstack: added HCI_VS_SET_POWER_VECTOR(GFSK) 0xFD82 template + 0x01, 0x82, 0xfd, 0x14, 0x00, 0x9c, 0x18, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xdc, + 0xe6, 0xf0, 0xfa, 0x04, 0x0e, 0x18, 0xff, 0x00, 0x00, + + // BTstack: added HCI_VS_SET_POWER_VECTOR(EDR2) 0xFD82 template + 0x01, 0x82, 0xfd, 0x14, 0x01, 0x9c, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xd8, + 0xe2, 0xec, 0xf6, 0x00, 0x0a, 0x14, 0xff, 0x00, 0x00, + + // BTstack: added HCI_VS_SET_POWER_VECTOR(EDR3) 0xFD82 for EDR3 template + 0x01, 0x82, 0xfd, 0x14, 0x02, 0x9c, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xd8, + 0xe2, 0xec, 0xf6, 0x00, 0x0a, 0x14, 0xff, 0x00, 0x00, + + // BTstack: added HCI_VS_SET_CLASS2_SINGLE_POWER 0xFD87 template + 0x01, 0x87, 0xfd, 0x03, 0x0d, 0x0d, 0x0d, + + 0x01, 0x80, 0xfd, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + + 0x01, 0x80, 0xfd, 0x06, 0x3c, 0xf0, 0x5f, 0x00, 0x00, 0x00, + + // + // + // + 0x01, 0x38, 0xfe, 0x00, + + // + // ################################################################# + // ## START of CC2564 Adds-On + // ################################################################# + // + // ## Enable fast clock XTAL support + 0x01, 0x1c, 0xfd, 0x14, 0x01, 0x88, 0x13, 0x00, 0x00, 0xd0, 0x07, 0x00, 0x00, 0xff, 0xff, + 0x04, 0xff, 0xff, 0xff, 0xfa, 0x00, 0x00, 0x00, 0x00, + + // + // ## Enable eHCILL + 0x01, 0x2b, 0xfd, 0x05, 0x10, 0x00, 0x50, 0x00, 0x96, + + // + 0x01, 0x0c, 0xfd, 0x09, 0x01, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0x64, 0x00, + + // + // ################################################################# + // ## END of CC2564 Adds-On + // ################################################################# + 0x01, 0x5b, 0xfd, 0x02, 0x01, 0x01, + + // + 0x01, 0xdd, 0xfd, 0x01, 0x01, + +}; + +const uint32_t cc256x_init_script_size = 6771; + +#endif \ No newline at end of file diff --git a/ports/stm32/boards/LEGO_HUB_NO6/board_init.c b/ports/stm32/boards/LEGO_HUB_NO6/board_init.c new file mode 100644 index 0000000000..3f22bfc8fc --- /dev/null +++ b/ports/stm32/boards/LEGO_HUB_NO6/board_init.c @@ -0,0 +1,170 @@ +/* + * 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. + */ + +#include "py/mphal.h" +#include "irq.h" + +void board_init(void) { + if (query_irq() == IRQ_STATE_DISABLED) { + enable_irq(IRQ_STATE_ENABLED); + } + + // Enable 3V3 for all ports + mp_hal_pin_output(pyb_pin_PORT_3V3_EN); + mp_hal_pin_high(pyb_pin_PORT_3V3_EN); + + // Port A + // Enable RX/TX buffer + mp_hal_pin_output(pyb_pin_PORTA_EN); + mp_hal_pin_low(pyb_pin_PORTA_EN); + + // Port B + // Enable RX/TX buffer + mp_hal_pin_output(pyb_pin_PORTB_EN); + mp_hal_pin_low(pyb_pin_PORTB_EN); + + // Port C + // Enable RX/TX buffer + mp_hal_pin_output(pyb_pin_PORTC_EN); + mp_hal_pin_low(pyb_pin_PORTC_EN); + + // Port D + // Enable RX/TX buffer + mp_hal_pin_output(pyb_pin_PORTD_EN); + mp_hal_pin_low(pyb_pin_PORTD_EN); + + // Port E + // Enable RX/TX buffer + mp_hal_pin_output(pyb_pin_PORTE_EN); + mp_hal_pin_low(pyb_pin_PORTE_EN); + // Disable RS485 driver + mp_hal_pin_output(pyb_pin_PORTE_RTS); + mp_hal_pin_low(pyb_pin_PORTE_RTS); + + // Port F + // Enable RX/TX buffer + mp_hal_pin_output(pyb_pin_PORTF_EN); + mp_hal_pin_low(pyb_pin_PORTF_EN); + // Disable RS485 driver + mp_hal_pin_output(pyb_pin_PORTF_RTS); + mp_hal_pin_low(pyb_pin_PORTF_RTS); +} + +#if BUILDING_MBOOT + +#include "mboot/mboot.h" +#include "boardctrl.h" +#include "adc.h" +#include "hub_display.h" + +#define RESET_MODE_NUM_STATES (4) +#define RESET_MODE_TIMEOUT_CYCLES (8) + +#define PATTERN_B (0x00651946) +#define PATTERN_F (0x0021184e) +#define PATTERN_N (0x01296ad2) +#define PATTERN_S (0x0064104c) + +static void board_led_pattern(int reset_mode, uint16_t brightness) { + static const uint32_t pixels[] = { + 0, + PATTERN_N, + PATTERN_S, + PATTERN_F, + PATTERN_B, + }; + uint32_t pixel = pixels[reset_mode]; + for (int i = 0; i < 25; ++i) { + hub_display_set(i, brightness * ((pixel >> i) & 1)); + } + hub_display_update(); +} + +static void board_button_init(void) { + mp_hal_pin_config(pin_A1, MP_HAL_PIN_MODE_ADC, MP_HAL_PIN_PULL_NONE, 0); + adc_config(ADC1, 12); +} + +static int board_button_state(void) { + uint16_t value = adc_config_and_read_u16(ADC1, 1, ADC_SAMPLETIME_15CYCLES); + return value < 44000; +} + +void board_mboot_cleanup(int reset_mode) { + board_led_pattern(0, 0); + hub_display_off(); +} + +void board_mboot_led_init(void) { + hub_display_on(); +} + +void board_mboot_led_state(int led, int state) { + if (state) { + hub_display_set(28, 0x7fff); + hub_display_set(31, 0x7fff); + } else { + hub_display_set(28, 0); + hub_display_set(31, 0); + } + hub_display_update(); +} + +int board_mboot_get_reset_mode(void) { + board_button_init(); + int reset_mode = BOARDCTRL_RESET_MODE_NORMAL; + if (board_button_state()) { + // Cycle through reset modes while USR is held. + // Timeout is roughly 20s, where reset_mode=1. + systick_init(); + hub_display_on(); + reset_mode = 0; + for (int i = 0; i < (RESET_MODE_NUM_STATES * RESET_MODE_TIMEOUT_CYCLES + 1) * 32; i++) { + if (i % 32 == 0) { + if (++reset_mode > RESET_MODE_NUM_STATES) { + reset_mode = BOARDCTRL_RESET_MODE_NORMAL; + } + board_led_pattern(reset_mode, 0x7fff); + } + if (!board_button_state()) { + break; + } + mp_hal_delay_ms(19); + } + // Flash the selected reset mode. + for (int i = 0; i < 6; i++) { + board_led_pattern(reset_mode, 0x0fff); + mp_hal_delay_ms(50); + board_led_pattern(reset_mode, 0x7fff); + mp_hal_delay_ms(50); + } + mp_hal_delay_ms(300); + } + board_led_pattern(0, 0); + return reset_mode; +} + +#endif diff --git a/ports/stm32/boards/LEGO_HUB_NO6/cc2564.c b/ports/stm32/boards/LEGO_HUB_NO6/cc2564.c new file mode 100644 index 0000000000..c54daf3002 --- /dev/null +++ b/ports/stm32/boards/LEGO_HUB_NO6/cc2564.c @@ -0,0 +1,85 @@ +/* + * 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. + */ + +#include + +#include "py/runtime.h" +#include "py/mphal.h" +#include "timer.h" +#include "extmod/mpbthci.h" + +#if !BUILDING_MBOOT + +#define CC2564_PIN_CTS (pyb_pin_BT_CTS) +#define CC2564_PIN_RTS (pyb_pin_BT_RTS) +#define CC2564_PIN_BT_SLOWCLK (pyb_pin_BT_SLOWCLK) +#define CC2564_PIN_BT_ENABLE (pyb_pin_BT_ENABLE) + +STATIC void cc2564_wait_cts_low(mp_hal_pin_obj_t cts, uint32_t timeout_ms) { + for (int i = 0; i < timeout_ms; ++i) { + if (mp_hal_pin_read(cts) == 0) { + break; + } + mp_hal_delay_ms(1); + } +} + +int mp_bluetooth_hci_controller_init(void) { + // Pull BTNSHUTD low to disable chip. + mp_hal_pin_output(CC2564_PIN_BT_ENABLE); + mp_hal_pin_low(CC2564_PIN_BT_ENABLE); + + // Output a 32768Hz signal on BTSLOWCLK. + // tim8 = pyb.Timer(8, freq=32768) + // tim8_ch4 = tim8.channel(4, pyb.Timer.PWM, pin=btclk) + // tim8_ch4.pulse_width_percent(50) + mp_obj_t args[6] = { MP_OBJ_NEW_SMALL_INT(8), MP_OBJ_NEW_QSTR(MP_QSTR_freq), MP_OBJ_NEW_SMALL_INT(32768), MP_OBJ_NULL }; + mp_obj_t tim8 = pyb_timer_type.make_new(&pyb_timer_type, 1, 1, args); + mp_load_method(tim8, MP_QSTR_channel, args); + args[2] = MP_OBJ_NEW_SMALL_INT(4); + args[3] = MP_OBJ_NEW_SMALL_INT(0); // CHANNEL_MODE_PWM_NORMAL + args[4] = MP_OBJ_NEW_QSTR(MP_QSTR_pin); + args[5] = (mp_obj_t)CC2564_PIN_BT_SLOWCLK; + mp_obj_t tim8_ch4 = mp_call_method_n_kw(2, 1, args); + mp_load_method(tim8_ch4, MP_QSTR_pulse_width_percent, args); + args[2] = MP_OBJ_NEW_SMALL_INT(50); + mp_call_method_n_kw(1, 0, args); + + // Pull BTNSHUTD high to enable chip and wait for CTS to go low to indicate ready. + mp_hal_pin_high(CC2564_PIN_BT_ENABLE); + cc2564_wait_cts_low(CC2564_PIN_CTS, 500); + + return 0; +} + +int mp_bluetooth_hci_controller_deinit(void) { + // Pull BTNSHUTD low to disable chip. + mp_hal_pin_low(CC2564_PIN_BT_ENABLE); + + return 0; +} + +#endif diff --git a/ports/stm32/boards/LEGO_HUB_NO6/hub_display.c b/ports/stm32/boards/LEGO_HUB_NO6/hub_display.c new file mode 100644 index 0000000000..1efc388c77 --- /dev/null +++ b/ports/stm32/boards/LEGO_HUB_NO6/hub_display.c @@ -0,0 +1,180 @@ +/* + * 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. + */ + +#include "py/mphal.h" +#include "hub_display.h" + +// Map pixel number 0-24, and 25-36 to TLC bit number. +static const uint8_t hub_display_pixel_map[] = { + // 5x5 display + 9, 11, 6, 1, 14, + 10, 19, 8, 0, 26, + 23, 18, 3, 2, 24, + 21, 20, 15, 13, 25, + 22, 7, 17, 12, 38, + // RGB Bluetooth button + 27, 28, 29, + // RGB middle button (left and right) + 39, 40, 41, 42, 43, 44, + // RGB battery indicator + 45, 46, 47, +}; + +static bool hub_display_init; +static uint8_t hub_display_gs_state[96]; + +static void hub_display_tim_init(void) { + // TLC maximum GSCLK freq: 33MHz + + // tim12, ch2, pwm + + TIM_TypeDef *tim = TIM12; + + __HAL_RCC_TIM12_CLK_ENABLE(); + tim->CR1 = TIM_CR1_ARPE; + + // Configure PWM mode. + uint32_t ch = 1; // ch2 + uint32_t reg = 6 << TIM_CCMR1_OC1M_Pos // PWM1 mode + | 1 << TIM_CCMR1_OC1PE_Pos // preload enabled + | 0 << TIM_CCMR1_CC1S_Pos // output mode + ; + uint32_t shift = 8 * (ch & 1); + tim->CCMR1 = (tim->CCMR1 & ~(0xff << shift)) | reg << shift; + + // Enable output on pin, active high for normal channel. + reg = TIM_CCER_CC1E; + shift = 4 * ch; + tim->CCER = (tim->CCER & ~(0xf << shift)) | reg << shift; + + // Enable the timer if it's not already running. + tim->CR1 |= TIM_CR1_CEN; + + // fast + tim->PSC = 0; + tim->ARR = 2; + + // 50% duty + tim->CCR2 = 2; + tim->EGR = 1; // UG + + mp_hal_pin_config(pin_B15, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, 9); +} + +static void hub_display_spi_init(void) { + // TLC maximum SPI freq: 25MHz + + SPI_TypeDef *spi = SPI1; + + __HAL_RCC_SPI1_CLK_ENABLE(); + spi->CR1 = SPI_CR1_SSM | SPI_CR1_SSI | 0 << SPI_CR1_BR_Pos | SPI_CR1_MSTR; + spi->CR1 |= SPI_CR1_SPE; + + mp_hal_pin_config(pin_A5, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, 5); + mp_hal_pin_config(pin_A6, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, 5); + mp_hal_pin_config(pin_A7, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, 5); +} + +static void hub_display_spi_write(uint8_t value) { + SPI_TypeDef *spi = SPI1; + spi->DR = value; + while (!(spi->SR & SPI_SR_TXE)) { + } +} + +// dc: dot control +// mc: maximum current +// bc: global brightness control +// fc: function control +static void hub_display_latch_ctrl(uint8_t dc, uint32_t mc, uint32_t bc, uint8_t fc) { + hub_display_spi_write(1); // bit 768 + hub_display_spi_write(0x96); // bits 760-767 + for (int i = 0; i < 48; ++i) { + hub_display_spi_write(0); + } + hub_display_spi_write(fc >> 2); // bits 368-375 + hub_display_spi_write(fc << 6 | bc >> 15); // bits 360-367 + hub_display_spi_write(bc >> 7); // bits 352-359 + hub_display_spi_write(bc << 1 | mc >> 8); // bits 344-351 + hub_display_spi_write(mc); // bits 336-343 + for (int i = 0; i < 42; ++i) { + hub_display_spi_write(dc); + } + mp_hal_pin_high(pin_A15); + mp_hal_delay_us(1); + mp_hal_pin_low(pin_A15); +} + +void hub_display_set(uint8_t led, uint16_t value) { + led = hub_display_pixel_map[led]; + hub_display_gs_state[led * 2] = value; + hub_display_gs_state[led * 2 + 1] = value >> 8; +} + +void hub_display_update(void) { + if (!hub_display_init) { + return; + } + hub_display_spi_write(0); + for (int i = 0; i < 96; ++i) { + hub_display_spi_write(hub_display_gs_state[95 - i]); + } + mp_hal_pin_high(pin_A15); + mp_hal_delay_us(1); + mp_hal_pin_low(pin_A15); +} + +void hub_display_on(void) { + if (hub_display_init) { + return; + } + mp_hal_pin_output(pin_A15); + mp_hal_pin_low(pin_A15); + hub_display_spi_init(); + for (int i = 0; i < 2; ++i) { + hub_display_latch_ctrl(0xff, 0, 0x1fffff, 0x11); + } + hub_display_tim_init(); + hub_display_init = true; +} + +void hub_display_off(void) { + if (!hub_display_init) { + return; + } + __HAL_RCC_TIM12_CLK_DISABLE(); + __HAL_RCC_TIM12_FORCE_RESET(); + __HAL_RCC_TIM12_RELEASE_RESET(); + __HAL_RCC_SPI1_CLK_DISABLE(); + __HAL_RCC_SPI1_FORCE_RESET(); + __HAL_RCC_SPI1_RELEASE_RESET(); + mp_hal_pin_config(pin_A5, MP_HAL_PIN_MODE_ANALOG, MP_HAL_PIN_PULL_NONE, 0); + mp_hal_pin_config(pin_A6, MP_HAL_PIN_MODE_ANALOG, MP_HAL_PIN_PULL_NONE, 0); + mp_hal_pin_config(pin_A7, MP_HAL_PIN_MODE_ANALOG, MP_HAL_PIN_PULL_NONE, 0); + mp_hal_pin_config(pin_A15, MP_HAL_PIN_MODE_ANALOG, MP_HAL_PIN_PULL_NONE, 0); + mp_hal_pin_config(pin_B15, MP_HAL_PIN_MODE_ANALOG, MP_HAL_PIN_PULL_NONE, 0); + hub_display_init = false; +} diff --git a/ports/stm32/boards/LEGO_HUB_NO6/hub_display.h b/ports/stm32/boards/LEGO_HUB_NO6/hub_display.h new file mode 100644 index 0000000000..7623e128a8 --- /dev/null +++ b/ports/stm32/boards/LEGO_HUB_NO6/hub_display.h @@ -0,0 +1,4 @@ +void hub_display_on(void); +void hub_display_off(void); +void hub_display_update(void); +void hub_display_set(uint8_t led, uint16_t value); diff --git a/ports/stm32/boards/LEGO_HUB_NO6/mboot_memory.ld b/ports/stm32/boards/LEGO_HUB_NO6/mboot_memory.ld new file mode 100644 index 0000000000..dd914c8e88 --- /dev/null +++ b/ports/stm32/boards/LEGO_HUB_NO6/mboot_memory.ld @@ -0,0 +1,10 @@ +/* + Linker script fragment for mboot on an STM32xxx MCU. + This defines the memory sections for the bootloader to use. +*/ +MEMORY +{ + FLASH_BL (rx) : ORIGIN = 0x08008000, LENGTH = 32K + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 120K +} + diff --git a/ports/stm32/boards/LEGO_HUB_NO6/mpconfigboard.h b/ports/stm32/boards/LEGO_HUB_NO6/mpconfigboard.h new file mode 100644 index 0000000000..a0869fda7a --- /dev/null +++ b/ports/stm32/boards/LEGO_HUB_NO6/mpconfigboard.h @@ -0,0 +1,122 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * The MIT License (MIT) + * Copyright (c) 2021 Damien P. George + */ + +#include + +#define MICROPY_HW_BOARD_NAME "LEGO Technic Hub No.6" +#define MICROPY_HW_MCU_NAME "STM32F413" + +#define MICROPY_HW_HAS_SWITCH (0) +#define MICROPY_HW_HAS_FLASH (0) +#define MICROPY_PY_PYB_LEGACY (0) +#define MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET (0) +#define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (0) +#define MICROPY_HW_ENABLE_RTC (1) +#define MICROPY_HW_ENABLE_RNG (1) +#define MICROPY_HW_ENABLE_DAC (1) +#define MICROPY_HW_ENABLE_USB (1) + +// HSE is 16MHz, CPU freq set to 100MHz, buses at maximum freq +#define MICROPY_HW_CLK_PLLM (16) +#define MICROPY_HW_CLK_PLLN (200) +#define MICROPY_HW_CLK_PLLP (RCC_PLLP_DIV2) +#define MICROPY_HW_CLK_PLLQ (4) +#define MICROPY_HW_CLK_AHB_DIV (RCC_SYSCLK_DIV1) +#define MICROPY_HW_CLK_APB1_DIV (RCC_HCLK_DIV2) +#define MICROPY_HW_CLK_APB2_DIV (RCC_HCLK_DIV1) + +// For 2.7 to 3.6 V, 75 to 100 MHz: 3 wait states. +#define MICROPY_HW_FLASH_LATENCY FLASH_LATENCY_3 + +// UART buses +// Bluetooth HCI +#define MICROPY_HW_UART2_CTS (pin_D3) +#define MICROPY_HW_UART2_RTS (pin_D4) +#define MICROPY_HW_UART2_TX (pin_D5) +#define MICROPY_HW_UART2_RX (pin_D6) +// Port B +#define MICROPY_HW_UART4_TX (pin_D1) +#define MICROPY_HW_UART4_RX (pin_D0) +// Port D +#define MICROPY_HW_UART5_TX (pin_C12) +#define MICROPY_HW_UART5_RX (pin_D2) +// Port A +#define MICROPY_HW_UART7_TX (pin_E8) +#define MICROPY_HW_UART7_RX (pin_E7) +// Port C +#define MICROPY_HW_UART8_TX (pin_E1) +#define MICROPY_HW_UART8_RX (pin_E0) +// Port F +#define MICROPY_HW_UART9_TX (pin_D15) +#define MICROPY_HW_UART9_RX (pin_D14) +// Port E +#define MICROPY_HW_UART10_TX (pin_E3) +#define MICROPY_HW_UART10_RX (pin_E2) + +// SPI buses +#define MICROPY_HW_SPI1_NSS (pin_A4) // shared with DAC +#define MICROPY_HW_SPI1_SCK (pin_A5) // shared with DAC +#define MICROPY_HW_SPI1_MISO (pin_A6) +#define MICROPY_HW_SPI1_MOSI (pin_A7) + +// USB config +#define MICROPY_HW_USB_VBUS_DETECT_PIN (pin_A9) +#define MICROPY_HW_USB_FS (1) +#define MICROPY_HW_USB_MSC (1) + +// Bluetooth config +#define MICROPY_HW_BLE_UART_ID (PYB_UART_2) +#define MICROPY_HW_BLE_UART_BAUDRATE (115200) +#define MICROPY_HW_BLE_UART_BAUDRATE_SECONDARY (921600) +#define MICROPY_HW_BLE_BTSTACK_CHIPSET_INSTANCE btstack_chipset_cc256x_instance() + +// SPI flash, for R/W storage +#define MICROPY_HW_SPIFLASH_ENABLE_CACHE (1) +#define MICROPY_HW_SPIFLASH_SIZE_BITS (256 * 1024 * 1024) +#define MICROPY_HW_SPIFLASH_NSS (pin_B12) +#define MICROPY_HW_SPIFLASH_SCK (pin_B13) +#define MICROPY_HW_SPIFLASH_MISO (pin_C2) +#define MICROPY_HW_SPIFLASH_MOSI (pin_C3) + +// SPI flash, block device config +extern int32_t board_bdev_ioctl(void); +extern struct _spi_bdev_t spi_bdev; +#define MICROPY_HW_BDEV_IOCTL(op, arg) ( \ + (op) == BDEV_IOCTL_NUM_BLOCKS ? (MICROPY_HW_SPIFLASH_SIZE_BITS / 8 / FLASH_BLOCK_SIZE - 1) : \ + (op) == BDEV_IOCTL_INIT ? board_bdev_ioctl() : \ + spi_bdev_ioctl(&spi_bdev, (op), (arg)) \ + ) + +// Jump over first block (bl + 1) as it is cleared by bootloader +#define MICROPY_HW_BDEV_READBLOCKS(dest, bl, n) spi_bdev_readblocks(&spi_bdev, (dest), (bl + 1), (n)) +#define MICROPY_HW_BDEV_WRITEBLOCKS(src, bl, n) spi_bdev_writeblocks(&spi_bdev, (src), (bl + 1), (n)) +#define MICROPY_HW_BDEV_SPIFLASH_EXTENDED (&spi_bdev) // for extended block protocol + +// Board control config +#define MICROPY_BOARD_STARTUP board_init + +/******************************************************************************/ +// Bootloader configuration + +#define MBOOT_LEAVE_BOOTLOADER_VIA_RESET (0) + +#define MBOOT_LED1 0 +#define MBOOT_BOARD_LED_INIT board_mboot_led_init +#define MBOOT_BOARD_LED_STATE board_mboot_led_state + +#define MBOOT_BOARD_EARLY_INIT board_init +#define MBOOT_BOARD_CLEANUP board_mboot_cleanup +#define MBOOT_BOARD_GET_RESET_MODE board_mboot_get_reset_mode + +/******************************************************************************/ +// Function declarations + +void board_init(void); +void board_mboot_cleanup(int reset_mode); +void board_mboot_led_init(void); +void board_mboot_led_state(int led, int state); +int board_mboot_get_reset_mode(void); +void *btstack_chipset_cc256x_instance(void); diff --git a/ports/stm32/boards/LEGO_HUB_NO6/mpconfigboard.mk b/ports/stm32/boards/LEGO_HUB_NO6/mpconfigboard.mk new file mode 100644 index 0000000000..56527af7d3 --- /dev/null +++ b/ports/stm32/boards/LEGO_HUB_NO6/mpconfigboard.mk @@ -0,0 +1,22 @@ +MCU_SERIES = f4 +CMSIS_MCU = STM32F413xx +AF_FILE = boards/stm32f413_af.csv +LD_FILES = boards/LEGO_HUB_NO6/stm32f413xg.ld boards/common_bl.ld +TEXT0_ADDR = 0x08010000 + +BOOTLOADER_DFU_USB_VID ?= 0x0694 +BOOTLOADER_DFU_USB_PID ?= 0x0008 + +# MicroPython settings +MICROPY_PY_BLUETOOTH ?= 1 +MICROPY_BLUETOOTH_NIMBLE ?= 0 +MICROPY_BLUETOOTH_BTSTACK ?= 1 +MICROPY_VFS_LFS2 ?= 1 + +ifneq ($(BUILDING_MBOOT),1) +LIB_SRC_C += lib/btstack/chipset/cc256x/btstack_chipset_cc256x.c +endif + +# Bootloader settings +MBOOT_TEXT0_ADDR = 0x08008000 +MBOOT_LD_FILES = ../boards/LEGO_HUB_NO6/mboot_memory.ld stm32_sections.ld diff --git a/ports/stm32/boards/LEGO_HUB_NO6/pins.csv b/ports/stm32/boards/LEGO_HUB_NO6/pins.csv new file mode 100644 index 0000000000..9e56e2c79d --- /dev/null +++ b/ports/stm32/boards/LEGO_HUB_NO6/pins.csv @@ -0,0 +1,114 @@ +CHG_ISET_PWM,PA0 +BUTTONS_ADC,PA1 +BT_ENABLE,PA2 +CHG_IMON_ADC,PA3 +SND_DAC,PA4 +TLC_SCLK,PA5 +TLC_SOUT,PA6 +TLC_SIN,PA7 +PORTB_EN,PA8 +USB_VBUS,PA9 +PORTA_EN,PA10 +USB_DM,PA11 +USB_DP,PA12 +BAT_PWR_EN,PA13 +PORT_3V3_EN,PA14 +TLC_LAT,PA15 +BAT_NTC,PB0 +PORTF_M2,PB1 +PORTD_EN,PB2 +LSM6_SDA,PB3 +LSM6_INT1,PB4 +PORTE_EN,PB5 +PORTC_M1,PB6 +PORTC_M2,PB7 +PORTD_M1,PB8 +PORTD_M2,PB9 +LSM6_SCL,PB10 +,PB11 +,PB12 +,PB13 +,PB14 +TLC_GS_CLK,PB15 +BAT_IMON_ADC,PC0 +BAT_VMON_ADC,PC1 +,PC2 +,PC3 +CHGOK_CENBTN_3V3OK_ADC,PC4 +PORTF_EN,PC5 +PORTE_M1,PC6 +PORTE_M2,PC7 +PORTF_M1,PC8 +BT_SLOWCLK,PC9 +SND_AMP_EN,PC10 +,PC11 +PORTD_TX,PC12 +,PC13 +,PC14 +,PC15 +PORTB_RX,PD0 +PORTB_TX,PD1 +PORTD_RX,PD2 +BT_CTS,PD3 +BT_RTS,PD4 +BT_TX,PD5 +BT_RX,PD6 +,PD7 +,PD8 +,PD9 +,PD10 +,PD11 +,PD12 +,PD13 +PORTF_RX,PD14 +PORTF_TX,PD15 +PORTC_RX,PE0 +PORTC_TX,PE1 +PORTE_RX,PE2 +PORTE_TX,PE3 +,PE4 +PORTC_EN,PE5 +,PE6 +PORTA_RX,PE7 +PORTA_TX,PE8 +PORTA_M1,PE9 +PORTE_RTS,PE10 +PORTA_M2,PE11 +,PE12 +PORTB_M1,PE13 +PORTB_M2,PE14 +PORTF_RTS,PE15 +,PF0 +,PF1 +,PF2 +,PF3 +,PF4 +,PF5 +,PF6 +,PF7 +,PF8 +,PF9 +,PF10 +,PF11 +,PF12 +,PF13 +,PF14 +,PF15 +,PG0 +,PG1 +,PG2 +,PG3 +,PG4 +,PG5 +,PG6 +,PG7 +,PG8 +,PG9 +,PG10 +,PG11 +,PG12 +,PG13 +,PG14 +,PG15 +,PH0 +,PH1 diff --git a/ports/stm32/boards/LEGO_HUB_NO6/stm32f413xg.ld b/ports/stm32/boards/LEGO_HUB_NO6/stm32f413xg.ld new file mode 100644 index 0000000000..d3c43a6948 --- /dev/null +++ b/ports/stm32/boards/LEGO_HUB_NO6/stm32f413xg.ld @@ -0,0 +1,27 @@ +/* + GNU linker script for STM32F413xg (1MB flash, 320kB RAM) +*/ + +/* Specify the memory areas */ +/* FLASH_FS2 is placed before FLASH_TEXT to support 1MB and 1.5MB FLASH with common code in flashbdev.c */ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K /* entire flash */ + FLASH_APP (rx) : ORIGIN = 0x08010000, LENGTH = 976K + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 320K /* 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; + +/* 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 = ORIGIN(RAM) + LENGTH(RAM) - _estack_reserve; +_sstack = _estack - 12K; /* tunable */ + +/* RAM extents for the garbage collector */ +_ram_start = ORIGIN(RAM); +_ram_end = ORIGIN(RAM) + LENGTH(RAM); +_heap_start = _ebss; /* heap starts just after statically allocated memory */ +_heap_end = _sstack; diff --git a/ports/stm32/boards/LEGO_HUB_NO6/stm32f4xx_hal_conf.h b/ports/stm32/boards/LEGO_HUB_NO6/stm32f4xx_hal_conf.h new file mode 100644 index 0000000000..7d6344f0a2 --- /dev/null +++ b/ports/stm32/boards/LEGO_HUB_NO6/stm32f4xx_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_STM32F4XX_HAL_CONF_H +#define MICROPY_INCLUDED_STM32F4XX_HAL_CONF_H + +#include "boards/stm32f4xx_hal_conf_base.h" + +// Oscillator values in Hz +#define HSE_VALUE (16000000) +#define LSE_VALUE (32768) +#define EXTERNAL_CLOCK_VALUE (12288000) + +// Oscillator timeouts in ms +#define HSE_STARTUP_TIMEOUT (100) +#define LSE_STARTUP_TIMEOUT (5000) + +#endif // MICROPY_INCLUDED_STM32F4XX_HAL_CONF_H From 60e3e51753c99674e27b338809f3b7abdc352929 Mon Sep 17 00:00:00 2001 From: Tobias Thyrrestrup Date: Fri, 23 Jul 2021 10:28:57 +0200 Subject: [PATCH 143/264] stm32/Makefile: Update to only pull in used Bluetooth library. --- ports/stm32/Makefile | 4 +++- tools/ci.sh | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index ebf1466118..d78e2b4851 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -34,7 +34,7 @@ MBOOT_TEXT0_ADDR ?= 0x08000000 # include py core make definitions include $(TOP)/py/py.mk -GIT_SUBMODULES = lib/libhydrogen lib/lwip lib/mbedtls lib/mynewt-nimble lib/stm32lib +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:]') @@ -522,12 +522,14 @@ endif endif ifeq ($(MICROPY_BLUETOOTH_NIMBLE),1) +GIT_SUBMODULES += lib/mynewt-nimble CFLAGS_MOD += -DMICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS=1 include $(TOP)/extmod/nimble/nimble.mk SRC_C += mpnimbleport.c endif ifeq ($(MICROPY_BLUETOOTH_BTSTACK),1) +GIT_SUBMODULES += lib/btstack MICROPY_BLUETOOTH_BTSTACK_H4 ?= 1 include $(TOP)/extmod/btstack/btstack.mk SRC_C += mpbtstackport.c diff --git a/tools/ci.sh b/tools/ci.sh index a6e38b70ea..efc68c03e4 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -267,6 +267,7 @@ function ci_stm32_pyb_build { make ${MAKEOPTS} -C mpy-cross make ${MAKEOPTS} -C ports/stm32 submodules git submodule update --init lib/btstack + git submodule update --init lib/mynewt-nimble make ${MAKEOPTS} -C ports/stm32 BOARD=PYBV11 MICROPY_PY_WIZNET5K=5200 MICROPY_PY_CC3K=1 USER_C_MODULES=../../examples/usercmodule make ${MAKEOPTS} -C ports/stm32 BOARD=PYBD_SF2 make ${MAKEOPTS} -C ports/stm32 BOARD=PYBD_SF6 NANBOX=1 MICROPY_BLUETOOTH_NIMBLE=0 MICROPY_BLUETOOTH_BTSTACK=1 @@ -277,6 +278,7 @@ function ci_stm32_pyb_build { function ci_stm32_nucleo_build { make ${MAKEOPTS} -C mpy-cross make ${MAKEOPTS} -C ports/stm32 submodules + git submodule update --init lib/mynewt-nimble make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_F091RC make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_H743ZI CFLAGS_EXTRA='-DMICROPY_PY_THREAD=1' make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_L073RZ From 40b8ff0a6fd737bdad8e0559994a55b7c2ce0786 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 31 Jul 2021 15:09:16 +1000 Subject: [PATCH 144/264] stm32/README.md: Update supported MCUs, and submodule and mboot use. Also mention mpremote as a way to access the REPL. Signed-off-by: Damien George --- ports/stm32/README.md | 42 +++++++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/ports/stm32/README.md b/ports/stm32/README.md index f2fdb4dd3b..6b5f096d73 100644 --- a/ports/stm32/README.md +++ b/ports/stm32/README.md @@ -2,11 +2,13 @@ MicroPython port to STM32 MCUs ============================== This directory contains the port of MicroPython to ST's line of STM32 -microcontrollers. Supported MCU series are: STM32F0, STM32F4, STM32F7 and -STM32L4. Parts of the code here utilise the STM32Cube HAL library. +microcontrollers. Supported MCU series are: STM32F0, STM32F4, STM32F7, +STM32H7, STM32L0, STM32L4 and STM32WB. Parts of the code here utilise the +STM32Cube HAL library. The officially supported boards are the line of pyboards: PYBv1.0 and PYBv1.1 -(both with STM32F405), and PYBLITEv1.0 (with STM32F411). See +(both with STM32F405), PYBLITEv1.0 (with STM32F411) and PYBD-SFx (with +STM32F7xx MCUs). See [micropython.org/pyboard](http://www.micropython.org/pyboard/) for further details. @@ -40,18 +42,31 @@ see [here](https://launchpad.net/gcc-arm-embedded) for the main GCC ARM Embedded page. The compiler can be changed using the `CROSS_COMPILE` variable when invoking `make`. -First the submodules must be obtained using: +Next, the board to build must be selected. The default board is PYBV10 but any +of the names of the subdirectories in the `boards/` directory is a valid board. +The board name must be passed as the argument to `BOARD=` when invoking `make`. - $ make submodules +All boards require certain submodules to be obtained before they can be built. +The correct set of submodules can be initialised using (with `PYBV11` as an +example of the selected board): -Then to build for a given board, run: + $ make BOARD=PYBV11 submodules + +Then to build the board's firmware run: $ make BOARD=PYBV11 -The default board is PYBV10 but any of the names of the subdirectories in the -`boards/` directory can be passed as the argument to `BOARD=`. The above command -should produce binary images in the `build-PYBV11/` subdirectory (or the -equivalent directory for the board specified). +The above command should produce binary images in the `build-PYBV11/` +subdirectory (or the equivalent directory for the board specified). + +Note that some boards require the mboot bootloader to be built and deployed +before flashing the main firmware. For such boards an information message +about this will be printed at the end of the main firmware build. Mboot +can be built via: + + $ make -C mboot BOARD=STM32F769DISC + +For more information about mboot see mboot/README.md. ### Flashing the Firmware using DFU mode @@ -140,6 +155,11 @@ Accessing the board ------------------- Once built and deployed, access the MicroPython REPL (the Python prompt) via USB -serial or UART, depending on the board. For the pyboard you can try: +serial or UART, depending on the board. There are many ways to do this, one of +which is via `mpremote` (install it using `pip install mpremote`): + + $ mpremote + +Other options are `picocom` and `screen`, for example: $ picocom /dev/ttyACM0 From e29259d171300b9b8de81c736f717ba6de9c8253 Mon Sep 17 00:00:00 2001 From: oclyke Date: Thu, 22 Jul 2021 17:42:30 -0600 Subject: [PATCH 145/264] extmod/uasyncio: In open_connection use address info in socket creation. Rudimentary support for various address families. Signed-off-by: oclyke --- extmod/uasyncio/stream.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extmod/uasyncio/stream.py b/extmod/uasyncio/stream.py index 8de2d2599f..af3b8feab3 100644 --- a/extmod/uasyncio/stream.py +++ b/extmod/uasyncio/stream.py @@ -79,8 +79,8 @@ async def open_connection(host, port): from uerrno import EINPROGRESS import usocket as socket - ai = socket.getaddrinfo(host, port)[0] # TODO this is blocking! - s = socket.socket() + ai = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM)[0] # TODO this is blocking! + s = socket.socket(ai[0], ai[1], ai[2]) s.setblocking(False) ss = Stream(s) try: From 1074c784b0b35c826e0c65b36d2e77ea17970103 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Tue, 20 Jul 2021 08:18:18 +0200 Subject: [PATCH 146/264] mimxrt: Add support for Hyperflash chips. Hyperflash is used by the MIMXRT1050_EVKB, MIMXRT1060_EVK and MIMXRT1064_EVK boards. This commit includes: - add support for Hyperflash - modify MIMXRT1060_EVK and MIMXRT1064_EVK to change from QSPI to hyperflash. - minor incidental changes to other boards so they still build Note: Erasing a sector on the hyperflash is slow. It takes about a second, which seems too long, but matches the data sheet. --- ports/mimxrt/Makefile | 3 +- .../boards/MIMXRT1010_EVK/mpconfigboard.h | 1 + .../boards/MIMXRT1010_EVK/mpconfigboard.mk | 3 + .../boards/MIMXRT1020_EVK/mpconfigboard.h | 1 + .../boards/MIMXRT1020_EVK/mpconfigboard.mk | 3 + .../boards/MIMXRT1050_EVK/mpconfigboard.h | 1 + .../boards/MIMXRT1050_EVK/mpconfigboard.mk | 3 + ports/mimxrt/boards/MIMXRT1052.ld | 4 +- .../boards/MIMXRT1060_EVK/MIMXRT1060_EVK.ld | 2 +- .../evkmimxrt1060_flexspi_nor_config.h | 10 +- .../boards/MIMXRT1060_EVK/flash_config.c | 230 ++++++++++------ .../boards/MIMXRT1060_EVK/mpconfigboard.h | 1 + .../boards/MIMXRT1060_EVK/mpconfigboard.mk | 3 + ports/mimxrt/boards/MIMXRT1064.ld | 6 +- .../boards/MIMXRT1064_EVK/MIMXRT1064_EVK.ld | 2 +- .../evkmimxrt1064_flexspi_nor_config.h | 15 +- .../boards/MIMXRT1064_EVK/flash_config.c | 229 ++++++++++------ .../boards/MIMXRT1064_EVK/mpconfigboard.h | 1 + .../boards/MIMXRT1064_EVK/mpconfigboard.mk | 3 + ports/mimxrt/boards/TEENSY40/mpconfigboard.h | 1 + ports/mimxrt/boards/TEENSY40/mpconfigboard.mk | 3 + ports/mimxrt/boards/TEENSY41/mpconfigboard.h | 1 + ports/mimxrt/boards/TEENSY41/mpconfigboard.mk | 3 + ports/mimxrt/boards/common.ld | 5 + ports/mimxrt/hal/flexspi_hyper_flash.c | 247 ++++++++++++++++++ ports/mimxrt/hal/flexspi_hyper_flash.h | 71 +++++ ports/mimxrt/hal/flexspi_nor_flash.h | 21 ++ ports/mimxrt/mimxrt_flash.c | 25 +- ports/mimxrt/mpconfigport.h | 1 + 29 files changed, 713 insertions(+), 186 deletions(-) create mode 100644 ports/mimxrt/hal/flexspi_hyper_flash.c create mode 100644 ports/mimxrt/hal/flexspi_hyper_flash.h diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index 580dc32c7e..818a2a3c84 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -133,7 +133,7 @@ SRC_HAL_IMX_C += \ $(MCU_DIR)/drivers/fsl_snvs_lp.c \ $(MCU_DIR)/drivers/fsl_trng.c \ -SRC_C = \ +SRC_C += \ main.c \ led.c \ pin.c \ @@ -156,7 +156,6 @@ SRC_C = \ modmimxrt.c \ moduos.c \ mphalport.c \ - hal/flexspi_nor_flash.c \ shared/libc/printf.c \ shared/libc/string0.c \ shared/readline/readline.c \ diff --git a/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.h index 756eaf85eb..2cdb433e18 100644 --- a/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.h @@ -8,6 +8,7 @@ #define MICROPY_HW_LED_ON(pin) (mp_hal_pin_high(pin)) #define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_low(pin)) #define BOARD_FLASH_CONFIG_HEADER_H "evkmimxrt1010_flexspi_nor_config.h" +#define BOARD_FLASH_OPS_HEADER_H "hal/flexspi_nor_flash.h" #define MICROPY_HW_NUM_PIN_IRQS (2 * 32) diff --git a/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.mk index ca8efcd5bf..ccc8ffeb41 100644 --- a/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.mk @@ -3,6 +3,9 @@ MCU_VARIANT = MIMXRT1011DAE5A MICROPY_FLOAT_IMPL = single +SRC_C += \ + hal/flexspi_nor_flash.c \ + JLINK_PATH = /media/RT1010-EVK/ JLINK_COMMANDER_SCRIPT = $(BUILD)/script.jlink diff --git a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h index b0574fad0f..d2a2cbbdbd 100644 --- a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h @@ -9,6 +9,7 @@ #define MICROPY_HW_LED_ON(pin) (mp_hal_pin_low(pin)) #define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_high(pin)) #define BOARD_FLASH_CONFIG_HEADER_H "evkmimxrt1020_flexspi_nor_config.h" +#define BOARD_FLASH_OPS_HEADER_H "hal/flexspi_nor_flash.h" #define MICROPY_HW_NUM_PIN_IRQS (3 * 32) diff --git a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk index b0bbd705cb..f8f66b0dff 100644 --- a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk @@ -3,6 +3,9 @@ MCU_VARIANT = MIMXRT1021DAG5A MICROPY_FLOAT_IMPL = double +SRC_C += \ + hal/flexspi_nor_flash.c \ + JLINK_PATH ?= /media/RT1020-EVK/ JLINK_COMMANDER_SCRIPT = $(BUILD)/script.jlink diff --git a/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h index 5d920d5286..af0975bc17 100644 --- a/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h @@ -8,6 +8,7 @@ #define MICROPY_HW_LED_ON(pin) (mp_hal_pin_low(pin)) #define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_high(pin)) #define BOARD_FLASH_CONFIG_HEADER_H "evkmimxrt1050_flexspi_nor_config.h" +#define BOARD_FLASH_OPS_HEADER_H "hal/flexspi_nor_flash.h" #define MICROPY_HW_NUM_PIN_IRQS (4 * 32 + 3) diff --git a/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.mk index e338ec6cf8..61826b6f60 100644 --- a/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.mk @@ -3,6 +3,9 @@ MCU_VARIANT = MIMXRT1052DVL6B MICROPY_FLOAT_IMPL = double +SRC_C += \ + hal/flexspi_nor_flash.c \ + JLINK_PATH ?= /media/RT1050-EVK/ deploy: $(BUILD)/firmware.bin diff --git a/ports/mimxrt/boards/MIMXRT1052.ld b/ports/mimxrt/boards/MIMXRT1052.ld index f78773531a..7519297940 100644 --- a/ports/mimxrt/boards/MIMXRT1052.ld +++ b/ports/mimxrt/boards/MIMXRT1052.ld @@ -8,8 +8,8 @@ ivt_size = 0x00001000; interrupts_start = 0x60002000; interrupts_size = 0x00000400; text_start = 0x60002400; -text_size = ((((text_start) + 1M) + (4k - 1)) & ~(4k - 1)) - (text_start); /* reserve 1M for code but align on 4k boundary */ -vfs_start = (text_start) + (text_size); +vfs_start = 0x60100000; +text_size = ((vfs_start) - (text_start)); vfs_size = ((flash_end) - (vfs_start)); itcm_start = 0x00000000; itcm_size = 0x00020000; diff --git a/ports/mimxrt/boards/MIMXRT1060_EVK/MIMXRT1060_EVK.ld b/ports/mimxrt/boards/MIMXRT1060_EVK/MIMXRT1060_EVK.ld index fd1bf32ede..f616178a9b 100644 --- a/ports/mimxrt/boards/MIMXRT1060_EVK/MIMXRT1060_EVK.ld +++ b/ports/mimxrt/boards/MIMXRT1060_EVK/MIMXRT1060_EVK.ld @@ -1 +1 @@ -flash_size = 8M; \ No newline at end of file +flash_size = 64M; \ No newline at end of file diff --git a/ports/mimxrt/boards/MIMXRT1060_EVK/evkmimxrt1060_flexspi_nor_config.h b/ports/mimxrt/boards/MIMXRT1060_EVK/evkmimxrt1060_flexspi_nor_config.h index 03bdac1f1c..cff520e0e2 100644 --- a/ports/mimxrt/boards/MIMXRT1060_EVK/evkmimxrt1060_flexspi_nor_config.h +++ b/ports/mimxrt/boards/MIMXRT1060_EVK/evkmimxrt1060_flexspi_nor_config.h @@ -10,7 +10,7 @@ #include #include -#include "fsl_common.h" +#include "fsl_flexspi.h" /*! @name Driver version */ /*@{*/ @@ -224,6 +224,14 @@ typedef struct _FlexSPIConfig #define NOR_CMD_LUT_SEQ_IDX_CHIPERASE 11 #define NOR_CMD_LUT_SEQ_IDX_EXITQPI 12 +#define HYPERFLASH_CMD_LUT_SEQ_IDX_READDATA 0 +#define HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEDATA 1 +#define HYPERFLASH_CMD_LUT_SEQ_IDX_READSTATUS 2 +#define HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEENABLE 4 +#define HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR 6 +#define HYPERFLASH_CMD_LUT_SEQ_IDX_PAGEPROGRAM 10 +#define HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP 12 + /* * Serial NOR configuration block */ diff --git a/ports/mimxrt/boards/MIMXRT1060_EVK/flash_config.c b/ports/mimxrt/boards/MIMXRT1060_EVK/flash_config.c index 3fa584b73a..e28f581547 100644 --- a/ports/mimxrt/boards/MIMXRT1060_EVK/flash_config.c +++ b/ports/mimxrt/boards/MIMXRT1060_EVK/flash_config.c @@ -25,98 +25,162 @@ __attribute__((section(".boot_hdr.conf"))) 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, - .sflashPadType = kSerialFlash_4Pads, - .serialClkFreq = kFlexSpiSerialClk_100MHz, - .sflashA1Size = 8u * 1024u * 1024u, + .tag = FLEXSPI_CFG_BLK_TAG, + .version = FLEXSPI_CFG_BLK_VERSION, + .readSampleClkSrc = kFlexSPIReadSampleClk_ExternalInputFromDqsPad, + .csHoldTime = 3u, + .csSetupTime = 3u, + .columnAddressWidth = 3u, + // Enable DDR mode, Wordaddressable, Safe configuration, Differential clock + .controllerMiscOption = + (1u << kFlexSpiMiscOffset_DdrModeEnable) | (1u << kFlexSpiMiscOffset_WordAddressableEnable) | + (1u << kFlexSpiMiscOffset_SafeConfigFreqEnable) | (1u << kFlexSpiMiscOffset_DiffClkEnable), + .sflashPadType = kSerialFlash_8Pads, + .serialClkFreq = kFlexSpiSerialClk_133MHz, + .sflashA1Size = 64u * 1024u * 1024u, + .dataValidTime = {16u, 16u}, .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 + /* 0 Read Data */ + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_READDATA] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xA0, kFLEXSPI_Command_RADDR_DDR, kFLEXSPI_8PAD, 0x18), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_READDATA + 1] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_CADDR_DDR, kFLEXSPI_8PAD, 0x10, kFLEXSPI_Command_READ_DDR, kFLEXSPI_8PAD, 0x04), - // 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 + /* 1 Write Data */ + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEDATA] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x20, kFLEXSPI_Command_RADDR_DDR, kFLEXSPI_8PAD, 0x18), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEDATA + 1] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_CADDR_DDR, kFLEXSPI_8PAD, 0x10, kFLEXSPI_Command_WRITE_DDR, kFLEXSPI_8PAD, 0x02), - // 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 + /* 2 Read Status */ + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_READSTATUS] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_READSTATUS + 1] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA), // ADDR 0x555 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_READSTATUS + 2] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x05), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_READSTATUS + 3] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x70), // DATA 0x70 + // +1 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_READSTATUS + 4] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xA0, kFLEXSPI_Command_RADDR_DDR, kFLEXSPI_8PAD, 0x18), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_READSTATUS + 5] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_CADDR_DDR, kFLEXSPI_8PAD, 0x10, kFLEXSPI_Command_DUMMY_RWDS_DDR, kFLEXSPI_8PAD, 0x0B), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_READSTATUS + 6] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_READ_DDR, kFLEXSPI_8PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0x0), - // 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 Write Enable */ + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEENABLE] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x20, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEENABLE + 1] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA), // ADDR 0x555 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEENABLE + 2] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x05), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEENABLE + 3] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA), // DATA 0xAA + // +1 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEENABLE + 4] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x20, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEENABLE + 5] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x55), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEENABLE + 6] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x02), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEENABLE + 7] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x55), - // 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 + /* 6 Erase Sector */ + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 1] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA), // ADDR 0x555 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 2] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x05), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 3] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x80), // DATA 0x80 + // +1 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 4] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 5] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 6] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x05), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 7] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA), // ADDR 0x555 + // +2 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 8] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 9] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x55), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 10] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x02), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 11] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x55), + // +3 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 12] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_RADDR_DDR, kFLEXSPI_8PAD, 0x18), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 13] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_CADDR_DDR, kFLEXSPI_8PAD, 0x10, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 14] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x30, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0x00), - // 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 + /* 10 program page with word program command sequence */ + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_PAGEPROGRAM] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x20, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_PAGEPROGRAM + 1] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA), // ADDR 0x555 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_PAGEPROGRAM + 2] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x05), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_PAGEPROGRAM + 3] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xA0), // DATA 0xA0 + // +1 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_PAGEPROGRAM + 4] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x20, kFLEXSPI_Command_RADDR_DDR, kFLEXSPI_8PAD, 0x18), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_PAGEPROGRAM + 5] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_CADDR_DDR, kFLEXSPI_8PAD, 0x10, kFLEXSPI_Command_WRITE_DDR, kFLEXSPI_8PAD, 0x80), - // 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 + /* 12 Erase chip */ + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 1] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 2] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x05), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 3] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x80), + // +1 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 4] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 5] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 6] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x05), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 7] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA), + // +2 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 8] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 9] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x55), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 10] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x02), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 11] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x55), + // +3 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 12] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 13] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 14] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x05), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 15] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x10), }, }, - .pageSize = 256u, - .sectorSize = 4u * 1024u, - .blockSize = 64u * 1024u, - .isUniformBlockSize = false, + .pageSize = 512u, + .sectorSize = 256u * 1024u, + .blockSize = 256u * 1024u, + .isUniformBlockSize = true, }; + #endif /* XIP_BOOT_HEADER_ENABLE */ diff --git a/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h index f849cba966..c207da832e 100644 --- a/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h @@ -8,6 +8,7 @@ #define MICROPY_HW_LED_ON(pin) (mp_hal_pin_low(pin)) #define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_high(pin)) #define BOARD_FLASH_CONFIG_HEADER_H "evkmimxrt1060_flexspi_nor_config.h" +#define BOARD_FLASH_OPS_HEADER_H "hal/flexspi_hyper_flash.h" #define MICROPY_HW_NUM_PIN_IRQS (4 * 32 + 3) diff --git a/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk index b7ea5fcbe1..f5eaf3eab5 100644 --- a/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk @@ -3,6 +3,9 @@ MCU_VARIANT = MIMXRT1062DVJ6A MICROPY_FLOAT_IMPL = double +SRC_C += \ + hal/flexspi_hyper_flash.c \ + JLINK_PATH ?= /media/RT1060-EVK/ JLINK_COMMANDER_SCRIPT = $(BUILD)/script.jlink diff --git a/ports/mimxrt/boards/MIMXRT1064.ld b/ports/mimxrt/boards/MIMXRT1064.ld index 236d984293..8203930017 100644 --- a/ports/mimxrt/boards/MIMXRT1064.ld +++ b/ports/mimxrt/boards/MIMXRT1064.ld @@ -8,8 +8,8 @@ ivt_size = 0x00001000; interrupts_start = 0x60002000; interrupts_size = 0x00000400; text_start = 0x60002400; -text_size = ((((text_start) + 1M) + (4k - 1)) & ~(4k - 1)) - (text_start); /* reserve 1M for code but align on 4k boundary */ -vfs_start = (text_start) + (text_size); +vfs_start = 0x60100000; +text_size = ((vfs_start) - (text_start)); vfs_size = ((flash_end) - (vfs_start)); itcm_start = 0x00000000; itcm_size = 0x00020000; @@ -25,4 +25,4 @@ _sstack = __StackLimit; /* Use second OCRAM bank for GC heap. */ _gc_heap_start = ORIGIN(m_ocrm); -_gc_heap_end = ORIGIN(m_ocrm) + LENGTH(m_ocrm); \ No newline at end of file +_gc_heap_end = ORIGIN(m_ocrm) + LENGTH(m_ocrm); diff --git a/ports/mimxrt/boards/MIMXRT1064_EVK/MIMXRT1064_EVK.ld b/ports/mimxrt/boards/MIMXRT1064_EVK/MIMXRT1064_EVK.ld index fd1bf32ede..f616178a9b 100644 --- a/ports/mimxrt/boards/MIMXRT1064_EVK/MIMXRT1064_EVK.ld +++ b/ports/mimxrt/boards/MIMXRT1064_EVK/MIMXRT1064_EVK.ld @@ -1 +1 @@ -flash_size = 8M; \ No newline at end of file +flash_size = 64M; \ No newline at end of file diff --git a/ports/mimxrt/boards/MIMXRT1064_EVK/evkmimxrt1064_flexspi_nor_config.h b/ports/mimxrt/boards/MIMXRT1064_EVK/evkmimxrt1064_flexspi_nor_config.h index 3c433f3be9..7560f24836 100644 --- a/ports/mimxrt/boards/MIMXRT1064_EVK/evkmimxrt1064_flexspi_nor_config.h +++ b/ports/mimxrt/boards/MIMXRT1064_EVK/evkmimxrt1064_flexspi_nor_config.h @@ -10,7 +10,7 @@ #include #include -#include "fsl_common.h" +#include "fsl_flexspi.h" /*! @name Driver version */ /*@{*/ @@ -19,9 +19,9 @@ /*@}*/ /* FLEXSPI memory config block related defintions */ -#define FLEXSPI_CFG_BLK_TAG (0x42464346UL) // ascii "FCFB" Big Endian +#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) +#define FLEXSPI_CFG_BLK_SIZE (512) /* FLEXSPI Feature related definitions */ #define FLEXSPI_FEATURE_HAS_PARALLEL_MODE 1 @@ -223,6 +223,15 @@ typedef struct _FlexSPIConfig #define NOR_CMD_LUT_SEQ_IDX_ENTERQPI 10 #define NOR_CMD_LUT_SEQ_IDX_CHIPERASE 11 #define NOR_CMD_LUT_SEQ_IDX_EXITQPI 12 + +#define HYPERFLASH_CMD_LUT_SEQ_IDX_READDATA 0 +#define HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEDATA 1 +#define HYPERFLASH_CMD_LUT_SEQ_IDX_READSTATUS 2 +#define HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEENABLE 4 +#define HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR 6 +#define HYPERFLASH_CMD_LUT_SEQ_IDX_PAGEPROGRAM 10 +#define HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP 12 + /* * Serial NOR configuration block */ diff --git a/ports/mimxrt/boards/MIMXRT1064_EVK/flash_config.c b/ports/mimxrt/boards/MIMXRT1064_EVK/flash_config.c index 9338c6948a..f308b7f8ad 100644 --- a/ports/mimxrt/boards/MIMXRT1064_EVK/flash_config.c +++ b/ports/mimxrt/boards/MIMXRT1064_EVK/flash_config.c @@ -25,99 +25,162 @@ __attribute__((section(".boot_hdr.conf"))) 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, - // Enable DDR mode, Wordaddassable, Safe configuration, Differential clock - .sflashPadType = kSerialFlash_4Pads, - .serialClkFreq = kFlexSpiSerialClk_100MHz, - .sflashA1Size = 8u * 1024u * 1024u, + .tag = FLEXSPI_CFG_BLK_TAG, + .version = FLEXSPI_CFG_BLK_VERSION, + .readSampleClkSrc = kFlexSPIReadSampleClk_ExternalInputFromDqsPad, + .csHoldTime = 3u, + .csSetupTime = 3u, + .columnAddressWidth = 3u, + // Enable DDR mode, Wordaddressable, Safe configuration, Differential clock + .controllerMiscOption = + (1u << kFlexSpiMiscOffset_DdrModeEnable) | (1u << kFlexSpiMiscOffset_WordAddressableEnable) | + (1u << kFlexSpiMiscOffset_SafeConfigFreqEnable) | (1u << kFlexSpiMiscOffset_DiffClkEnable), + .sflashPadType = kSerialFlash_8Pads, + .serialClkFreq = kFlexSpiSerialClk_133MHz, + .sflashA1Size = 64u * 1024u * 1024u, + .dataValidTime = {16u, 16u}, .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 + /* 0 Read Data */ + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_READDATA] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xA0, kFLEXSPI_Command_RADDR_DDR, kFLEXSPI_8PAD, 0x18), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_READDATA + 1] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_CADDR_DDR, kFLEXSPI_8PAD, 0x10, kFLEXSPI_Command_READ_DDR, kFLEXSPI_8PAD, 0x04), - // 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 + /* 1 Write Data */ + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEDATA] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x20, kFLEXSPI_Command_RADDR_DDR, kFLEXSPI_8PAD, 0x18), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEDATA + 1] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_CADDR_DDR, kFLEXSPI_8PAD, 0x10, kFLEXSPI_Command_WRITE_DDR, kFLEXSPI_8PAD, 0x02), - // 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 + /* 2 Read Status */ + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_READSTATUS] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_READSTATUS + 1] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA), // ADDR 0x555 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_READSTATUS + 2] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x05), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_READSTATUS + 3] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x70), // DATA 0x70 + // +1 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_READSTATUS + 4] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xA0, kFLEXSPI_Command_RADDR_DDR, kFLEXSPI_8PAD, 0x18), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_READSTATUS + 5] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_CADDR_DDR, kFLEXSPI_8PAD, 0x10, kFLEXSPI_Command_DUMMY_RWDS_DDR, kFLEXSPI_8PAD, 0x0B), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_READSTATUS + 6] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_READ_DDR, kFLEXSPI_8PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0x0), - // 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 Write Enable */ + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEENABLE] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x20, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEENABLE + 1] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA), // ADDR 0x555 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEENABLE + 2] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x05), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEENABLE + 3] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA), // DATA 0xAA + // +1 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEENABLE + 4] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x20, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEENABLE + 5] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x55), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEENABLE + 6] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x02), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEENABLE + 7] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x55), - // 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 + /* 6 Erase Sector */ + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 1] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA), // ADDR 0x555 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 2] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x05), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 3] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x80), // DATA 0x80 + // +1 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 4] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 5] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 6] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x05), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 7] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA), // ADDR 0x555 + // +2 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 8] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 9] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x55), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 10] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x02), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 11] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x55), + // +3 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 12] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_RADDR_DDR, kFLEXSPI_8PAD, 0x18), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 13] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_CADDR_DDR, kFLEXSPI_8PAD, 0x10, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 14] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x30, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0x00), - // 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 + /* 10 program page with word program command sequence */ + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_PAGEPROGRAM] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x20, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_PAGEPROGRAM + 1] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA), // ADDR 0x555 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_PAGEPROGRAM + 2] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x05), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_PAGEPROGRAM + 3] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xA0), // DATA 0xA0 + // +1 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_PAGEPROGRAM + 4] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x20, kFLEXSPI_Command_RADDR_DDR, kFLEXSPI_8PAD, 0x18), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_PAGEPROGRAM + 5] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_CADDR_DDR, kFLEXSPI_8PAD, 0x10, kFLEXSPI_Command_WRITE_DDR, kFLEXSPI_8PAD, 0x80), - // 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 + /* 12 Erase chip */ + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 1] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 2] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x05), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 3] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x80), + // +1 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 4] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 5] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 6] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x05), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 7] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA), + // +2 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 8] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 9] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x55), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 10] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x02), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 11] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x55), + // +3 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 12] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 13] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 14] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x05), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 15] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x10), }, }, - .pageSize = 256u, - .sectorSize = 4u * 1024u, + .pageSize = 512u, + .sectorSize = 256u * 1024u, .blockSize = 256u * 1024u, - .isUniformBlockSize = false, + .isUniformBlockSize = true, }; + #endif /* XIP_BOOT_HEADER_ENABLE */ diff --git a/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h index 2301f34131..59f28b0e2a 100644 --- a/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h @@ -6,6 +6,7 @@ #define MICROPY_HW_LED_ON(pin) (mp_hal_pin_low(pin)) #define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_high(pin)) #define BOARD_FLASH_CONFIG_HEADER_H "evkmimxrt1064_flexspi_nor_config.h" +#define BOARD_FLASH_OPS_HEADER_H "hal/flexspi_hyper_flash.h" #define MICROPY_HW_NUM_PIN_IRQS (4 * 32 + 3) diff --git a/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk index bd4c496416..444fe99672 100644 --- a/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk @@ -3,6 +3,9 @@ MCU_VARIANT = MIMXRT1064DVL6A MICROPY_FLOAT_IMPL = double +SRC_C += \ + hal/flexspi_hyper_flash.c \ + JLINK_PATH ?= /media/RT1064-EVK/ CFLAGS += -DBOARD_FLASH_SIZE=0x400000 diff --git a/ports/mimxrt/boards/TEENSY40/mpconfigboard.h b/ports/mimxrt/boards/TEENSY40/mpconfigboard.h index b6c81a4229..aacee4623c 100644 --- a/ports/mimxrt/boards/TEENSY40/mpconfigboard.h +++ b/ports/mimxrt/boards/TEENSY40/mpconfigboard.h @@ -8,6 +8,7 @@ #define MICROPY_HW_LED_ON(pin) (mp_hal_pin_high(pin)) #define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_low(pin)) #define BOARD_FLASH_CONFIG_HEADER_H "teensy40_flexspi_nor_config.h" +#define BOARD_FLASH_OPS_HEADER_H "hal/flexspi_nor_flash.h" #define MICROPY_HW_NUM_PIN_IRQS (4 * 32 + 3) diff --git a/ports/mimxrt/boards/TEENSY40/mpconfigboard.mk b/ports/mimxrt/boards/TEENSY40/mpconfigboard.mk index beab16c086..a15dd78128 100644 --- a/ports/mimxrt/boards/TEENSY40/mpconfigboard.mk +++ b/ports/mimxrt/boards/TEENSY40/mpconfigboard.mk @@ -3,5 +3,8 @@ MCU_VARIANT = MIMXRT1062DVJ6A MICROPY_FLOAT_IMPL = double +SRC_C += \ + hal/flexspi_nor_flash.c \ + deploy: $(BUILD)/firmware.hex teensy_loader_cli --mcu=imxrt1062 -v -w $< diff --git a/ports/mimxrt/boards/TEENSY41/mpconfigboard.h b/ports/mimxrt/boards/TEENSY41/mpconfigboard.h index 17d2751682..a72bd127b6 100644 --- a/ports/mimxrt/boards/TEENSY41/mpconfigboard.h +++ b/ports/mimxrt/boards/TEENSY41/mpconfigboard.h @@ -8,6 +8,7 @@ #define MICROPY_HW_LED_ON(pin) (mp_hal_pin_high(pin)) #define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_low(pin)) #define BOARD_FLASH_CONFIG_HEADER_H "teensy41_flexspi_nor_config.h" +#define BOARD_FLASH_OPS_HEADER_H "hal/flexspi_nor_flash.h" #define MICROPY_HW_NUM_PIN_IRQS (4 * 32 + 3) diff --git a/ports/mimxrt/boards/TEENSY41/mpconfigboard.mk b/ports/mimxrt/boards/TEENSY41/mpconfigboard.mk index beab16c086..a15dd78128 100755 --- a/ports/mimxrt/boards/TEENSY41/mpconfigboard.mk +++ b/ports/mimxrt/boards/TEENSY41/mpconfigboard.mk @@ -3,5 +3,8 @@ MCU_VARIANT = MIMXRT1062DVJ6A MICROPY_FLOAT_IMPL = double +SRC_C += \ + hal/flexspi_nor_flash.c \ + deploy: $(BUILD)/firmware.hex teensy_loader_cli --mcu=imxrt1062 -v -w $< diff --git a/ports/mimxrt/boards/common.ld b/ports/mimxrt/boards/common.ld index e5d91f1191..6f45da24d1 100644 --- a/ports/mimxrt/boards/common.ld +++ b/ports/mimxrt/boards/common.ld @@ -178,12 +178,14 @@ SECTIONS . = ALIGN(4); __DATA_RAM = .; __data_start__ = .; /* create a global symbol at data start */ + __data_section_table = .; *(m_usb_dma_init_data) *(.data) /* .data sections */ *(.data*) /* .data* sections */ KEEP(*(.jcr*)) . = ALIGN(4); __data_end__ = .; /* define a global symbol at data end */ + __data_section_table_end = .; } > m_dtcm __RAM_FUNCTIONS_ROM = __DATA_ROM + (__data_end__ - __data_start__); @@ -224,12 +226,14 @@ SECTIONS . = ALIGN(4); __START_BSS = .; __bss_start__ = .; + __bss_section_table = .; *(m_usb_dma_noninit_data) *(.bss) *(.bss*) *(COMMON) . = ALIGN(4); __bss_end__ = .; + __bss_section_table_end = .; __END_BSS = .; } > m_dtcm @@ -253,6 +257,7 @@ SECTIONS /* Initializes stack on the end of block */ __StackTop = ORIGIN(m_dtcm) + LENGTH(m_dtcm); __StackLimit = __StackTop - STACK_SIZE; + _vStackTop = __StackTop; PROVIDE(__stack = __StackTop); .ARM.attributes 0 : { *(.ARM.attributes) } diff --git a/ports/mimxrt/hal/flexspi_hyper_flash.c b/ports/mimxrt/hal/flexspi_hyper_flash.c new file mode 100644 index 0000000000..fb71d4d941 --- /dev/null +++ b/ports/mimxrt/hal/flexspi_hyper_flash.c @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2016, Freescale Semiconductor, Inc. + * Copyright 2016-2021 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "fsl_clock.h" +#include "flexspi_hyper_flash.h" + +// Copy of a few (pseudo-)functions from fsl_clock.h, which were nor reliably +// inlined when they should be. That caused DEBUG mode to fail. +// It does not increase the code size, since they were supposed to be inline. + +__attribute__((always_inline)) static inline void clock_set_div(clock_div_t divider, uint32_t value) { + uint32_t busyShift; + + busyShift = CCM_TUPLE_BUSY_SHIFT(divider); + CCM_TUPLE_REG(CCM, divider) = (CCM_TUPLE_REG(CCM, divider) & (~CCM_TUPLE_MASK(divider))) | + (((uint32_t)((value) << CCM_TUPLE_SHIFT(divider))) & CCM_TUPLE_MASK(divider)); + + /* Clock switch need Handshake? */ + if (CCM_NO_BUSY_WAIT != busyShift) { + /* Wait until CCM internal handshake finish. */ + while (CCM->CDHIPR & (1U << busyShift)) {} + } +} + +__attribute__((always_inline)) static inline void clock_control_gate(clock_ip_name_t name, clock_gate_value_t value) { + uint32_t index = ((uint32_t)name) >> 8U; + uint32_t shift = ((uint32_t)name) & 0x1FU; + volatile uint32_t *reg; + + reg = ((volatile uint32_t *)&CCM->CCGR0) + index; + *reg = ((*reg) & ~(3U << shift)) | (((uint32_t)value) << shift); +} + +__attribute__((always_inline)) static inline void clock_enable_clock(clock_ip_name_t name) { + clock_control_gate(name, kCLOCK_ClockNeededRunWait); +} + +__attribute__((always_inline)) static inline void clock_disable_clock(clock_ip_name_t name) { + clock_control_gate(name, kCLOCK_ClockNotNeeded); +} + +#define DIV_PAGE_PGM 4 +#define DIV_ERASE_PGM 4 +#define DIV_READ 0 + +static void SetFlexSPIDiv(uint32_t div) __attribute__((section(".ram_functions"))); +static void SetFlexSPIDiv(uint32_t div) { + FLEXSPI_Enable(FLEXSPI, false); + clock_disable_clock(kCLOCK_FlexSpi); + clock_set_div(kCLOCK_FlexspiDiv, div); /* flexspi clock 332M, DDR mode, internal clock 166M. */ + clock_enable_clock(kCLOCK_FlexSpi); + FLEXSPI_Enable(FLEXSPI, true); +} + +status_t flexspi_nor_hyperbus_read(FLEXSPI_Type *base, uint32_t addr, uint32_t *buffer, uint32_t bytes) __attribute__((section(".ram_functions"))); +status_t flexspi_nor_hyperbus_read(FLEXSPI_Type *base, uint32_t addr, uint32_t *buffer, uint32_t bytes) { + flexspi_transfer_t flashXfer; + status_t status; + + flashXfer.deviceAddress = addr * 2; + flashXfer.port = kFLEXSPI_PortA1; + flashXfer.cmdType = kFLEXSPI_Read; + flashXfer.SeqNumber = 1; + flashXfer.seqIndex = HYPERFLASH_CMD_LUT_SEQ_IDX_READDATA; + flashXfer.data = buffer; + flashXfer.dataSize = bytes; + status = FLEXSPI_TransferBlocking(base, &flashXfer); + + return status; +} + +status_t flexspi_nor_hyperbus_write(FLEXSPI_Type *base, uint32_t addr, uint32_t *buffer, uint32_t bytes) __attribute__((section(".ram_functions"))); +status_t flexspi_nor_hyperbus_write(FLEXSPI_Type *base, uint32_t addr, uint32_t *buffer, uint32_t bytes) { + flexspi_transfer_t flashXfer; + status_t status; + + flashXfer.deviceAddress = addr * 2; + flashXfer.port = kFLEXSPI_PortA1; + flashXfer.cmdType = kFLEXSPI_Write; + flashXfer.SeqNumber = 1; + flashXfer.seqIndex = HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEDATA; + flashXfer.data = buffer; + flashXfer.dataSize = bytes; + status = FLEXSPI_TransferBlocking(base, &flashXfer); + + return status; +} + +status_t flexspi_nor_write_enable(FLEXSPI_Type *base, uint32_t baseAddr) __attribute__((section(".ram_functions"))); +status_t flexspi_nor_write_enable(FLEXSPI_Type *base, uint32_t baseAddr) { + flexspi_transfer_t flashXfer; + status_t status; + + /* Write enable */ + flashXfer.deviceAddress = baseAddr; + flashXfer.port = kFLEXSPI_PortA1; + flashXfer.cmdType = kFLEXSPI_Command; + flashXfer.SeqNumber = 2; + flashXfer.seqIndex = HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEENABLE; + + status = FLEXSPI_TransferBlocking(base, &flashXfer); + + return status; +} + +status_t flexspi_nor_wait_bus_busy(FLEXSPI_Type *base) __attribute__((section(".ram_functions"))); +status_t flexspi_nor_wait_bus_busy(FLEXSPI_Type *base) { + /* Wait status ready. */ + bool isBusy; + uint32_t readValue; + status_t status; + flexspi_transfer_t flashXfer; + + flashXfer.deviceAddress = 0; + flashXfer.port = kFLEXSPI_PortA1; + flashXfer.cmdType = kFLEXSPI_Read; + flashXfer.SeqNumber = 2; + flashXfer.seqIndex = HYPERFLASH_CMD_LUT_SEQ_IDX_READSTATUS; + flashXfer.data = &readValue; + flashXfer.dataSize = 2; + + do { + status = FLEXSPI_TransferBlocking(base, &flashXfer); + + if (status != kStatus_Success) { + return status; + } + if (readValue & 0x8000) { + isBusy = false; + } else { + isBusy = true; + } + + if (readValue & 0x3200) { + status = kStatus_Fail; + break; + } + + } while (isBusy); + + return status; +} + +status_t flexspi_nor_flash_erase_sector(FLEXSPI_Type *base, uint32_t address) __attribute__((section(".ram_functions"))); +status_t flexspi_nor_flash_erase_sector(FLEXSPI_Type *base, uint32_t address) { + status_t status; + flexspi_transfer_t flashXfer; + + /* Write enable */ + status = flexspi_nor_write_enable(base, address); + + if (status != kStatus_Success) { + return status; + } + + flashXfer.deviceAddress = address; + flashXfer.port = kFLEXSPI_PortA1; + flashXfer.cmdType = kFLEXSPI_Command; + flashXfer.SeqNumber = 4; + flashXfer.seqIndex = HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR; + status = FLEXSPI_TransferBlocking(base, &flashXfer); + + if (status != kStatus_Success) { + return status; + } + + status = flexspi_nor_wait_bus_busy(base); + + return status; +} + +status_t flexspi_nor_flash_page_program(FLEXSPI_Type *base, uint32_t address, const uint32_t *src, uint32_t size ) __attribute__((section(".ram_functions"))); +status_t flexspi_nor_flash_page_program(FLEXSPI_Type *base, uint32_t address, const uint32_t *src, uint32_t size) { + status_t status; + flexspi_transfer_t flashXfer; + + /* Speed down flexspi clock */ + SetFlexSPIDiv(DIV_PAGE_PGM); + + /* Write enable */ + status = flexspi_nor_write_enable(base, address); + + if (status != kStatus_Success) { + return status; + } + + /* Prepare page program command */ + flashXfer.deviceAddress = address; + flashXfer.port = kFLEXSPI_PortA1; + flashXfer.cmdType = kFLEXSPI_Write; + flashXfer.SeqNumber = 2; + flashXfer.seqIndex = HYPERFLASH_CMD_LUT_SEQ_IDX_PAGEPROGRAM; + flashXfer.data = (uint32_t *)src; + flashXfer.dataSize = size; + status = FLEXSPI_TransferBlocking(base, &flashXfer); + + if (status != kStatus_Success) { + return status; + } + + status = flexspi_nor_wait_bus_busy(base); + + SetFlexSPIDiv(DIV_READ); + + return status; +} + +status_t flexspi_nor_hyperflash_cfi(FLEXSPI_Type *base) __attribute__((section(".ram_functions"))); +status_t flexspi_nor_hyperflash_cfi(FLEXSPI_Type *base) { + /* + * Read ID-CFI Parameters + */ + // CFI Entry + status_t status; + uint32_t buffer[2]; + uint8_t data[4] = {0x00, 0x98}; + status = flexspi_nor_hyperbus_write(base, 0x555, (uint32_t *)data, 2); + if (status != kStatus_Success) { + return status; + } + + // ID-CFI Read + // Read Query Unique ASCII String + status = flexspi_nor_hyperbus_read(base, 0x10, &buffer[0], sizeof(buffer)); + if (status != kStatus_Success) { + return status; + } + buffer[1] &= 0xFFFF; + // Check that the data read out is unicode "QRY" in big-endian order + if ((buffer[0] != 0x52005100) || (buffer[1] != 0x5900)) { + status = kStatus_Fail; + return status; + } + // ASO Exit 0xF000 + data[1] = 0xF0; + status = flexspi_nor_hyperbus_write(base, 0x0, (uint32_t *)data, 2); + if (status != kStatus_Success) { + return status; + } + + return status; +} diff --git a/ports/mimxrt/hal/flexspi_hyper_flash.h b/ports/mimxrt/hal/flexspi_hyper_flash.h new file mode 100644 index 0000000000..bba76c1324 --- /dev/null +++ b/ports/mimxrt/hal/flexspi_hyper_flash.h @@ -0,0 +1,71 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 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. + */ +#ifndef MICROPY_INCLUDED_MIMXRT_HAL_FLEXSPI_HYPER_FLASH_H +#define MICROPY_INCLUDED_MIMXRT_HAL_FLEXSPI_HYPER_FLASH_H + +#include "mpconfigboard.h" +#include "fsl_flexspi.h" +#include BOARD_FLASH_CONFIG_HEADER_H + +// Defined in boards flash_config.c +extern flexspi_nor_config_t qspiflash_config; + +status_t flexspi_nor_hyperflash_cfi(FLEXSPI_Type *base); +void flexspi_hyper_flash_init(void); +void flexspi_nor_update_lut(void); +status_t flexspi_nor_flash_erase_sector(FLEXSPI_Type *base, uint32_t address); +status_t flexspi_nor_flash_page_program(FLEXSPI_Type *base, uint32_t address, const uint32_t *src, uint32_t size); + +static inline uint32_t flexspi_get_frequency(void) { + uint32_t div; + uint32_t fre; + + /* Clock divider: + 000 divided by 1 + 001 divided by 2 + 010 divided by 3 + 011 divided by 4 + 100 divided by 5 + 101 divided by 6 + 110 divided by 7 + 111 divided by 8 + */ + div = CLOCK_GetDiv(kCLOCK_FlexspiDiv); + /* Get frequency */ + fre = CLOCK_GetFreq(kCLOCK_Usb1PllPfd0Clk) / (div + 0x01U); + + return fre; +} + +#define HYPERFLASH_CMD_LUT_SEQ_IDX_READDATA 0 +#define HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEDATA 1 +#define HYPERFLASH_CMD_LUT_SEQ_IDX_READSTATUS 2 +#define HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEENABLE 4 +#define HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR 6 +#define HYPERFLASH_CMD_LUT_SEQ_IDX_PAGEPROGRAM 10 +#define HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP 12 + +#endif // MICROPY_INCLUDED_MIMXRT_HAL_FLEXSPI_HYPER_FLASH_H diff --git a/ports/mimxrt/hal/flexspi_nor_flash.h b/ports/mimxrt/hal/flexspi_nor_flash.h index a9a9bbe700..c34df74168 100644 --- a/ports/mimxrt/hal/flexspi_nor_flash.h +++ b/ports/mimxrt/hal/flexspi_nor_flash.h @@ -39,4 +39,25 @@ void flexspi_nor_update_lut(void); status_t flexspi_nor_flash_erase_sector(FLEXSPI_Type *base, uint32_t address); status_t flexspi_nor_flash_page_program(FLEXSPI_Type *base, uint32_t address, const uint32_t *src, uint32_t size); +static inline uint32_t flexspi_get_frequency(void) { + uint32_t div; + uint32_t fre; + + /* Clock divider: + 000 divided by 1 + 001 divided by 2 + 010 divided by 3 + 011 divided by 4 + 100 divided by 5 + 101 divided by 6 + 110 divided by 7 + 111 divided by 8 + */ + div = CLOCK_GetDiv(kCLOCK_FlexspiDiv); + /* Get frequency */ + fre = CLOCK_GetFreq(kCLOCK_Usb1PllPfd0Clk) / (div + 0x01U); + + return fre; +} + #endif // MICROPY_INCLUDED_MIMXRT_HAL_FLEXSPI_NOR_FLASH_H diff --git a/ports/mimxrt/mimxrt_flash.c b/ports/mimxrt/mimxrt_flash.c index bf054c87f4..79c4e676dc 100644 --- a/ports/mimxrt/mimxrt_flash.c +++ b/ports/mimxrt/mimxrt_flash.c @@ -30,9 +30,10 @@ #include "py/runtime.h" #include "extmod/vfs.h" #include "modmimxrt.h" -#include "hal/flexspi_nor_flash.h" +#include BOARD_FLASH_OPS_HEADER_H // BOARD_FLASH_SIZE is defined in mpconfigport.h + #define SECTOR_SIZE_BYTES (qspiflash_config.sectorSize) #define PAGE_SIZE_BYTES (qspiflash_config.pageSize) @@ -62,7 +63,7 @@ STATIC mimxrt_flash_obj_t mimxrt_flash_obj = { }; // flash_erase_block(erase_addr_bytes) -// erases the 4k sector starting at adddr +// erases the sector starting at addr. Sector size according to the flash properties. status_t flash_erase_block(uint32_t erase_addr) __attribute__((section(".ram_functions"))); status_t flash_erase_block(uint32_t erase_addr) { status_t status; @@ -77,21 +78,31 @@ status_t flash_erase_block(uint32_t erase_addr) { // flash_write_block(flash_dest_addr_bytes, data_source, length_bytes) // writes length_byte data to the destination address -// length is a multiple of the page size = 256 // the vfs driver takes care for erasing the sector if required status_t flash_write_block(uint32_t dest_addr, const uint8_t *src, uint32_t length) __attribute__((section(".ram_functions"))); status_t flash_write_block(uint32_t dest_addr, const uint8_t *src, uint32_t length) { status_t status; + uint32_t size; + uint32_t next_addr; + SCB_CleanInvalidateDCache(); SCB_DisableDCache(); - // write sector in page size chunks - for (int i = 0; i < length; i += PAGE_SIZE_BYTES) { + // write data in chunks not crossing a page boundary + while (length > 0) { + next_addr = dest_addr - (dest_addr % PAGE_SIZE_BYTES) + PAGE_SIZE_BYTES; // next page boundary + size = next_addr - dest_addr; // maximal chunk length + if (size > length) { // compare against remaining data size + size = length; + } __disable_irq(); - status = flexspi_nor_flash_page_program(FLEXSPI, dest_addr + i, (uint32_t *)(src + i), PAGE_SIZE_BYTES); + status = flexspi_nor_flash_page_program(FLEXSPI, dest_addr, (uint32_t *)src, size); __enable_irq(); if (status != kStatus_Success) { break; } + length -= size; + src += size; + dest_addr += size; } SCB_EnableDCache(); return status; @@ -105,7 +116,7 @@ STATIC mp_obj_t mimxrt_flash_make_new(const mp_obj_type_t *type, size_t n_args, // This should be performed by the boot ROM but for some reason it is not. FLEXSPI_UpdateLUT(FLEXSPI, 0, qspiflash_config.memConfig.lookupTable, - sizeof(qspiflash_config.memConfig.lookupTable) / sizeof(qspiflash_config.memConfig.lookupTable[0])); + ARRAY_SIZE(qspiflash_config.memConfig.lookupTable)); // Configure FLEXSPI IP FIFO access. FLEXSPI->MCR0 &= ~(FLEXSPI_MCR0_ARDFEN_MASK); diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index e1e102797a..1066b93e16 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -145,6 +145,7 @@ __attribute__((always_inline)) static inline void enable_irq(uint32_t state) { __attribute__((always_inline)) static inline uint32_t disable_irq(void) { uint32_t state = __get_PRIMASK(); + __disable_irq(); return state; } From b2533fe47930b12e695e2afbef2019c664261790 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Thu, 29 Jul 2021 17:48:03 +0200 Subject: [PATCH 147/264] mimxrt/boards: Add support for the MIMXRT1050_EVKB board. --- .../boards/MIMXRT1050_EVKB/MIMXRT1050_EVKB.ld | 1 + .../evkbmimxrt1050_flexspi_nor_config.h | 263 ++++++++++++++++++ .../boards/MIMXRT1050_EVKB/flash_config.c | 186 +++++++++++++ .../boards/MIMXRT1050_EVKB/mpconfigboard.h | 69 +++++ .../boards/MIMXRT1050_EVKB/mpconfigboard.mk | 12 + ports/mimxrt/boards/MIMXRT1050_EVKB/pins.csv | 31 +++ 6 files changed, 562 insertions(+) create mode 100644 ports/mimxrt/boards/MIMXRT1050_EVKB/MIMXRT1050_EVKB.ld create mode 100644 ports/mimxrt/boards/MIMXRT1050_EVKB/evkbmimxrt1050_flexspi_nor_config.h create mode 100644 ports/mimxrt/boards/MIMXRT1050_EVKB/flash_config.c create mode 100644 ports/mimxrt/boards/MIMXRT1050_EVKB/mpconfigboard.h create mode 100644 ports/mimxrt/boards/MIMXRT1050_EVKB/mpconfigboard.mk create mode 100644 ports/mimxrt/boards/MIMXRT1050_EVKB/pins.csv diff --git a/ports/mimxrt/boards/MIMXRT1050_EVKB/MIMXRT1050_EVKB.ld b/ports/mimxrt/boards/MIMXRT1050_EVKB/MIMXRT1050_EVKB.ld new file mode 100644 index 0000000000..f616178a9b --- /dev/null +++ b/ports/mimxrt/boards/MIMXRT1050_EVKB/MIMXRT1050_EVKB.ld @@ -0,0 +1 @@ +flash_size = 64M; \ No newline at end of file diff --git a/ports/mimxrt/boards/MIMXRT1050_EVKB/evkbmimxrt1050_flexspi_nor_config.h b/ports/mimxrt/boards/MIMXRT1050_EVKB/evkbmimxrt1050_flexspi_nor_config.h new file mode 100644 index 0000000000..02ac394e95 --- /dev/null +++ b/ports/mimxrt/boards/MIMXRT1050_EVKB/evkbmimxrt1050_flexspi_nor_config.h @@ -0,0 +1,263 @@ +/* + * Copyright 2018 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __EVKMIMXRT1050_FLEXSPI_NOR_CONFIG__ +#define __EVKMIMXRT1050_FLEXSPI_NOR_CONFIG__ + +#include +#include +#include "fsl_flexspi.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, +} 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 + +#define HYPERFLASH_CMD_LUT_SEQ_IDX_READDATA 0 +#define HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEDATA 1 +#define HYPERFLASH_CMD_LUT_SEQ_IDX_READSTATUS 2 +#define HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEENABLE 4 +#define HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR 6 +#define HYPERFLASH_CMD_LUT_SEQ_IDX_PAGEPROGRAM 10 +#define HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP 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 /* __EVKMIMXRT1050_FLEXSPI_NOR_CONFIG__ */ diff --git a/ports/mimxrt/boards/MIMXRT1050_EVKB/flash_config.c b/ports/mimxrt/boards/MIMXRT1050_EVKB/flash_config.c new file mode 100644 index 0000000000..e8190bcb6c --- /dev/null +++ b/ports/mimxrt/boards/MIMXRT1050_EVKB/flash_config.c @@ -0,0 +1,186 @@ +/* + * Copyright 2018 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "evkbmimxrt1050_flexspi_nor_config.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(__CC_ARM) || 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_ExternalInputFromDqsPad, + .csHoldTime = 3u, + .csSetupTime = 3u, + .columnAddressWidth = 3u, + // Enable DDR mode, Wordaddressable, Safe configuration, Differential clock + .controllerMiscOption = + (1u << kFlexSpiMiscOffset_DdrModeEnable) | (1u << kFlexSpiMiscOffset_WordAddressableEnable) | + (1u << kFlexSpiMiscOffset_SafeConfigFreqEnable) | (1u << kFlexSpiMiscOffset_DiffClkEnable), + .sflashPadType = kSerialFlash_8Pads, + .serialClkFreq = kFlexSpiSerialClk_133MHz, + .sflashA1Size = 64u * 1024u * 1024u, + .dataValidTime = {16u, 16u}, + .lookupTable = + { + /* 0 Read Data */ + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_READDATA] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xA0, kFLEXSPI_Command_RADDR_DDR, kFLEXSPI_8PAD, 0x18), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_READDATA + 1] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_CADDR_DDR, kFLEXSPI_8PAD, 0x10, kFLEXSPI_Command_READ_DDR, kFLEXSPI_8PAD, 0x04), + + /* 1 Write Data */ + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEDATA] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x20, kFLEXSPI_Command_RADDR_DDR, kFLEXSPI_8PAD, 0x18), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEDATA + 1] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_CADDR_DDR, kFLEXSPI_8PAD, 0x10, kFLEXSPI_Command_WRITE_DDR, kFLEXSPI_8PAD, 0x02), + + /* 2 Read Status */ + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_READSTATUS] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_READSTATUS + 1] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA), // ADDR 0x555 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_READSTATUS + 2] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x05), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_READSTATUS + 3] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x70), // DATA 0x70 + // +1 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_READSTATUS + 4] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xA0, kFLEXSPI_Command_RADDR_DDR, kFLEXSPI_8PAD, 0x18), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_READSTATUS + 5] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_CADDR_DDR, kFLEXSPI_8PAD, 0x10, kFLEXSPI_Command_DUMMY_RWDS_DDR, kFLEXSPI_8PAD, 0x0B), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_READSTATUS + 6] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_READ_DDR, kFLEXSPI_8PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0x0), + + /* 4 Write Enable */ + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEENABLE] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x20, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEENABLE + 1] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA), // ADDR 0x555 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEENABLE + 2] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x05), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEENABLE + 3] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA), // DATA 0xAA + // +1 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEENABLE + 4] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x20, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEENABLE + 5] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x55), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEENABLE + 6] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x02), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEENABLE + 7] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x55), + + /* 6 Erase Sector */ + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 1] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA), // ADDR 0x555 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 2] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x05), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 3] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x80), // DATA 0x80 + // +1 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 4] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 5] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 6] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x05), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 7] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA), // ADDR 0x555 + // +2 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 8] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 9] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x55), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 10] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x02), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 11] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x55), + // +3 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 12] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_RADDR_DDR, kFLEXSPI_8PAD, 0x18), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 13] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_CADDR_DDR, kFLEXSPI_8PAD, 0x10, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASESECTOR + 14] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x30, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0x00), + + /* 10 program page with word program command sequence */ + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_PAGEPROGRAM] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x20, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_PAGEPROGRAM + 1] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA), // ADDR 0x555 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_PAGEPROGRAM + 2] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x05), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_PAGEPROGRAM + 3] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xA0), // DATA 0xA0 + // +1 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_PAGEPROGRAM + 4] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x20, kFLEXSPI_Command_RADDR_DDR, kFLEXSPI_8PAD, 0x18), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_PAGEPROGRAM + 5] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_CADDR_DDR, kFLEXSPI_8PAD, 0x10, kFLEXSPI_Command_WRITE_DDR, kFLEXSPI_8PAD, 0x80), + + /* 12 Erase chip */ + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 1] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 2] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x05), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 3] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x80), + // +1 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 4] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 5] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 6] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x05), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 7] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA), + // +2 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 8] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 9] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x55), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 10] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x02), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 11] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x55), + // +3 + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 12] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 13] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0xAA), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 14] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x05), + [4 * HYPERFLASH_CMD_LUT_SEQ_IDX_ERASECHIP + 15] = + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x00, kFLEXSPI_Command_DDR, kFLEXSPI_8PAD, 0x10), + }, + }, + .pageSize = 512u, + .sectorSize = 256u * 1024u, + .blockSize = 256u * 1024u, + .isUniformBlockSize = true, +}; + +#endif /* XIP_BOOT_HEADER_ENABLE */ diff --git a/ports/mimxrt/boards/MIMXRT1050_EVKB/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1050_EVKB/mpconfigboard.h new file mode 100644 index 0000000000..518240b03f --- /dev/null +++ b/ports/mimxrt/boards/MIMXRT1050_EVKB/mpconfigboard.h @@ -0,0 +1,69 @@ +#define MICROPY_HW_BOARD_NAME "i.MX RT1050 EVKB" +#define MICROPY_HW_MCU_NAME "MIMXRT1052DVL6B" + +#define BOARD_FLASH_SIZE (64 * 1024 * 1024) + +// MIMXRT1050_EVKB has 1 user LED +#define MICROPY_HW_LED1_PIN (pin_GPIO_AD_B0_09) +#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_low(pin)) +#define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_high(pin)) +#define BOARD_FLASH_CONFIG_HEADER_H "evkbmimxrt1050_flexspi_nor_config.h" +#define BOARD_FLASH_OPS_HEADER_H "hal/flexspi_hyper_flash.h" + +#define MICROPY_HW_NUM_PIN_IRQS (4 * 32 + 3) + +// Define mapping logical UART # to hardware UART # +// LPUART3 on D0/D1 -> 1 +// LPUART2 on D7/D6 -> 2 +// LPUART6 on D8/D9 -> 3 +// LPUART8 on A1/A0 -> 4 + +#define MICROPY_HW_UART_NUM (sizeof(uart_index_table) / sizeof(uart_index_table)[0]) +#define MICROPY_HW_UART_INDEX { 0, 3, 2, 6, 8 } + +#define IOMUX_TABLE_UART \ + { 0 }, { 0 }, \ + { IOMUXC_GPIO_AD_B1_02_LPUART2_TX }, { IOMUXC_GPIO_AD_B1_03_LPUART2_RX }, \ + { IOMUXC_GPIO_AD_B1_06_LPUART3_TX }, { IOMUXC_GPIO_AD_B1_07_LPUART3_RX }, \ + { 0 }, { 0 }, \ + { 0 }, { 0 }, \ + { IOMUXC_GPIO_AD_B0_02_LPUART6_TX }, { IOMUXC_GPIO_AD_B0_03_LPUART6_RX }, \ + { 0 }, { 0 }, \ + { IOMUXC_GPIO_AD_B1_10_LPUART8_TX }, { IOMUXC_GPIO_AD_B1_11_LPUART8_RX }, + +#define MICROPY_HW_SPI_INDEX { 1 } + +#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 }, + +#define DMA_REQ_SRC_RX { 0, kDmaRequestMuxLPSPI1Rx, kDmaRequestMuxLPSPI2Rx, \ + kDmaRequestMuxLPSPI3Rx, kDmaRequestMuxLPSPI4Rx } + +#define DMA_REQ_SRC_TX { 0, kDmaRequestMuxLPSPI1Tx, kDmaRequestMuxLPSPI2Tx, \ + kDmaRequestMuxLPSPI3Tx, kDmaRequestMuxLPSPI4Tx } + +// Define the mapping hardware I2C # to logical I2C # +// SDA/SCL HW-I2C Logical I2C +// D14/D15 LPI2C1 -> 0 +// D1/D0 LPI2C3 -> 1 + +#define MICROPY_HW_I2C_INDEX { 1, 3 } + +#define IOMUX_TABLE_I2C \ + { IOMUXC_GPIO_AD_B1_00_LPI2C1_SCL }, { IOMUXC_GPIO_AD_B1_01_LPI2C1_SDA }, \ + { 0 }, { 0 }, \ + { IOMUXC_GPIO_AD_B1_07_LPI2C3_SCL }, { IOMUXC_GPIO_AD_B1_06_LPI2C3_SDA }, + +#define USDHC_DUMMY_PIN NULL , 0 + +#define MICROPY_USDHC1 \ + { \ + .cmd = {GPIO_SD_B0_00_USDHC1_CMD}, \ + .clk = { GPIO_SD_B0_01_USDHC1_CLK }, \ + .cd_b = { GPIO_B1_12_USDHC1_CD_B },\ + .data0 = { GPIO_SD_B0_02_USDHC1_DATA0 },\ + .data1 = { GPIO_SD_B0_03_USDHC1_DATA1 },\ + .data2 = { GPIO_SD_B0_04_USDHC1_DATA2 },\ + .data3 = { GPIO_SD_B0_05_USDHC1_DATA3 },\ + } diff --git a/ports/mimxrt/boards/MIMXRT1050_EVKB/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1050_EVKB/mpconfigboard.mk new file mode 100644 index 0000000000..c9e32612ca --- /dev/null +++ b/ports/mimxrt/boards/MIMXRT1050_EVKB/mpconfigboard.mk @@ -0,0 +1,12 @@ +MCU_SERIES = MIMXRT1052 +MCU_VARIANT = MIMXRT1052DVL6B + +MICROPY_FLOAT_IMPL = double + +SRC_C += \ + hal/flexspi_hyper_flash.c \ + +JLINK_PATH ?= /media/RT1050-EVKB/ + +deploy: $(BUILD)/firmware.bin + cp $< $(JLINK_PATH) diff --git a/ports/mimxrt/boards/MIMXRT1050_EVKB/pins.csv b/ports/mimxrt/boards/MIMXRT1050_EVKB/pins.csv new file mode 100644 index 0000000000..366a141ff8 --- /dev/null +++ b/ports/mimxrt/boards/MIMXRT1050_EVKB/pins.csv @@ -0,0 +1,31 @@ +D0,GPIO_AD_B1_07 +D1,GPIO_AD_B1_06 +D2,GPIO_AD_B0_11 +D3,GPIO_AD_B1_08 +D4,GPIO_AD_B0_09 +D5,GPIO_AD_B0_10 +D6,GPIO_AD_B1_02 +D7,GPIO_AD_B1_03 +D8,GPIO_AD_B0_03 +D9,GPIO_AD_B0_02 +D10,GPIO_SD_B0_01 +D11,GPIO_SD_B0_02 +D12,GPIO_SD_B0_03 +D13,GPIO_SD_B0_00 +D14,GPIO_AD_B1_01 +D15,GPIO_AD_B1_00 +A0,GPIO_AD_B1_10 +A1,GPIO_AD_B1_11 +A2,GPIO_AD_B1_04 +A3,GPIO_AD_B1_05 +A4,GPIO_AD_B1_01 +A5,GPIO_AD_B1_00 +RX,GPIO_AD_B1_07 +TX,GPIO_AD_B1_06 +SCL,GPIO_AD_B1_00 +SDA,GPIO_AD_B1_01 +SCK,GPIO_SD_B0_00 +SDI,GPIO_SD_B0_03 +SDO,GPIO_SD_B0_02 +CS,GPIO_SD_B0_01 +LED_GREEN,GPIO_AD_B0_09 From 4445c73b113d8a35506ae5467559a7302dc79c7a Mon Sep 17 00:00:00 2001 From: robert-hh Date: Thu, 29 Jul 2021 17:52:19 +0200 Subject: [PATCH 148/264] tools/autobuild: Add the MIMXRT1050_EVKB board to the daily builds. --- tools/autobuild/build-mimxrt-latest.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/autobuild/build-mimxrt-latest.sh b/tools/autobuild/build-mimxrt-latest.sh index e36b294aca..4db65c0916 100755 --- a/tools/autobuild/build-mimxrt-latest.sh +++ b/tools/autobuild/build-mimxrt-latest.sh @@ -34,3 +34,4 @@ fi do_build TEENSY40 TEENSY40 hex do_build TEENSY41 TEENSY41 hex do_build MIMXRT1020_EVK MIMXRT1020_EVK bin +do_build MIMXRT1050_EVKB MIMXRT1050_EVKB bin From afcc77cebc38020d838e0af5ef4c9b7d3028a6c3 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 30 Jul 2021 10:26:17 -0500 Subject: [PATCH 149/264] py/builtinimport: Fix condition for including do_execute_raw_code(). Commit e33bc597 ("py: Remove calls to file reader functions when these are disabled.") changed the condition for one caller of do_execute_raw_code() from MICROPY_PERSISTENT_CODE_LOAD to MICROPY_HAS_FILE_READER && MICROPY_PERSISTENT_CODE_LOAD The condition that enables compiling the function itself needs to be changed to match. Signed-off-by: David Lechner --- py/builtinimport.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/builtinimport.c b/py/builtinimport.c index abbeefced5..cdee5e4070 100644 --- a/py/builtinimport.c +++ b/py/builtinimport.c @@ -142,7 +142,7 @@ STATIC void do_load_from_lexer(mp_obj_t module_obj, mp_lexer_t *lex) { } #endif -#if MICROPY_PERSISTENT_CODE_LOAD || MICROPY_MODULE_FROZEN_MPY +#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) { (void)source_name; From 7ae9e6ef6900ad2d386bf5453b47a89b026b51f1 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Tue, 27 Jul 2021 18:32:38 +0200 Subject: [PATCH 150/264] rp2/tusb_port: Allow boards to configure USB VID and PID. By defining MICROPY_HW_USB_VID and MICROPY_HW_USB_PID. --- ports/rp2/tusb_port.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/ports/rp2/tusb_port.c b/ports/rp2/tusb_port.c index 874d837b9b..8896be9077 100644 --- a/ports/rp2/tusb_port.c +++ b/ports/rp2/tusb_port.c @@ -27,8 +27,12 @@ #include "tusb.h" #include "pico/unique_id.h" -#define USBD_VID (0x2E8A) // Raspberry Pi -#define USBD_PID (0x0005) // RP2 MicroPython +#ifndef MICROPY_HW_USB_VID +#define MICROPY_HW_USB_VID (0x2E8A) // Raspberry Pi +#endif +#ifndef MICROPY_HW_USB_PID +#define MICROPY_HW_USB_PID (0x0005) // RP2 MicroPython +#endif #define USBD_DESC_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN) #define USBD_MAX_POWER_MA (250) @@ -58,8 +62,8 @@ static const tusb_desc_device_t usbd_desc_device = { .bDeviceSubClass = MISC_SUBCLASS_COMMON, .bDeviceProtocol = MISC_PROTOCOL_IAD, .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, - .idVendor = USBD_VID, - .idProduct = USBD_PID, + .idVendor = MICROPY_HW_USB_VID, + .idProduct = MICROPY_HW_USB_PID, .bcdDevice = 0x0100, .iManufacturer = USBD_STR_MANUF, .iProduct = USBD_STR_PRODUCT, From 23e2e0077910a7a47c0c0fc752b9056bdccfd5bb Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Fri, 30 Jul 2021 03:42:08 +0200 Subject: [PATCH 151/264] rp2/boards/ADAFRUIT_FEATHER_RP2040: Configure custom VID/PID. --- ports/rp2/boards/ADAFRUIT_FEATHER_RP2040/mpconfigboard.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ports/rp2/boards/ADAFRUIT_FEATHER_RP2040/mpconfigboard.h b/ports/rp2/boards/ADAFRUIT_FEATHER_RP2040/mpconfigboard.h index 5068d35541..3d7b2e714a 100644 --- a/ports/rp2/boards/ADAFRUIT_FEATHER_RP2040/mpconfigboard.h +++ b/ports/rp2/boards/ADAFRUIT_FEATHER_RP2040/mpconfigboard.h @@ -1,3 +1,6 @@ // Board and hardware specific configuration #define MICROPY_HW_BOARD_NAME "Adafruit Feather RP2040" #define MICROPY_HW_FLASH_STORAGE_BYTES (3072 * 1024) + +#define MICROPY_HW_USB_VID (0x239A) +#define MICROPY_HW_USB_PID (0x80F2) From 49497084f18fe05248ee35e474f5f3d559671fd1 Mon Sep 17 00:00:00 2001 From: Mike Causer Date: Sat, 24 Jul 2021 02:02:35 +1000 Subject: [PATCH 152/264] rp2/boards/ADAFRUIT_FEATHER_RP2040: Configure I2C/SPI default pins. --- .../ADAFRUIT_FEATHER_RP2040/mpconfigboard.h | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/ports/rp2/boards/ADAFRUIT_FEATHER_RP2040/mpconfigboard.h b/ports/rp2/boards/ADAFRUIT_FEATHER_RP2040/mpconfigboard.h index 3d7b2e714a..84d2bf20db 100644 --- a/ports/rp2/boards/ADAFRUIT_FEATHER_RP2040/mpconfigboard.h +++ b/ports/rp2/boards/ADAFRUIT_FEATHER_RP2040/mpconfigboard.h @@ -1,6 +1,20 @@ -// Board and hardware specific configuration -#define MICROPY_HW_BOARD_NAME "Adafruit Feather RP2040" -#define MICROPY_HW_FLASH_STORAGE_BYTES (3072 * 1024) +// https://www.adafruit.com/product/4884 +// https://learn.adafruit.com/adafruit-feather-rp2040-pico/pinouts + +#define MICROPY_HW_BOARD_NAME "Adafruit Feather RP2040" +#define MICROPY_HW_FLASH_STORAGE_BYTES (7 * 1024 * 1024) #define MICROPY_HW_USB_VID (0x239A) #define MICROPY_HW_USB_PID (0x80F2) + +// STEMMA QT / Qwiic on I2C1 +#define MICROPY_HW_I2C1_SCL (3) +#define MICROPY_HW_I2C1_SDA (2) + +#define MICROPY_HW_SPI0_SCK (18) +#define MICROPY_HW_SPI0_MOSI (19) +#define MICROPY_HW_SPI0_MISO (20) + +// NeoPixel GPIO16, power not toggleable + +// Red user LED GPIO13 From 6986a8d680421aafd4141962f1684cc4c693ddb3 Mon Sep 17 00:00:00 2001 From: Mike Causer Date: Sat, 24 Jul 2021 02:06:38 +1000 Subject: [PATCH 153/264] rp2/boards/SPARKFUN_PROMICRO: Configure UART/I2C/SPI default pins. --- .../boards/SPARKFUN_PROMICRO/mpconfigboard.h | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/ports/rp2/boards/SPARKFUN_PROMICRO/mpconfigboard.h b/ports/rp2/boards/SPARKFUN_PROMICRO/mpconfigboard.h index d6c8007ba0..65b29eecd1 100644 --- a/ports/rp2/boards/SPARKFUN_PROMICRO/mpconfigboard.h +++ b/ports/rp2/boards/SPARKFUN_PROMICRO/mpconfigboard.h @@ -1,3 +1,22 @@ -// Board and hardware specific configuration -#define MICROPY_HW_BOARD_NAME "SparkFun Pro Micro RP2040" -#define MICROPY_HW_FLASH_STORAGE_BYTES (15 * 1024 * 1024) +// https://www.sparkfun.com/products/17717 + +#define MICROPY_HW_BOARD_NAME "SparkFun Pro Micro RP2040" +#define MICROPY_HW_FLASH_STORAGE_BYTES (15 * 1024 * 1024) + +#define MICROPY_HW_USB_VID (0x1B4F) +#define MICROPY_HW_USB_PID (0x0026) + +#define MICROPY_HW_UART1_TX (8) +#define MICROPY_HW_UART1_RX (9) +#define MICROPY_HW_UART1_CTS (10) +#define MICROPY_HW_UART1_RTS (11) + +// Qwiic on I2C0 +#define MICROPY_HW_I2C0_SCL (17) +#define MICROPY_HW_I2C0_SDA (16) + +#define MICROPY_HW_SPI0_SCK (22) +#define MICROPY_HW_SPI0_MOSI (23) +#define MICROPY_HW_SPI0_MISO (20) + +// NeoPixel data GPIO25, power not toggleable From e7a3e6ee6a1d5073dc0590e386f86152d5744539 Mon Sep 17 00:00:00 2001 From: Mike Causer Date: Sat, 24 Jul 2021 02:08:01 +1000 Subject: [PATCH 154/264] rp2/boards/SPARKFUN_THINGPLUS: Configure I2C/SPI default pins. --- .../boards/SPARKFUN_THINGPLUS/mpconfigboard.h | 34 +++++++++++++++++-- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/ports/rp2/boards/SPARKFUN_THINGPLUS/mpconfigboard.h b/ports/rp2/boards/SPARKFUN_THINGPLUS/mpconfigboard.h index 9749acd25a..f88ba5dd63 100644 --- a/ports/rp2/boards/SPARKFUN_THINGPLUS/mpconfigboard.h +++ b/ports/rp2/boards/SPARKFUN_THINGPLUS/mpconfigboard.h @@ -1,3 +1,31 @@ -// Board and hardware specific configuration -#define MICROPY_HW_BOARD_NAME "SparkFun Thing Plus RP2040" -#define MICROPY_HW_FLASH_STORAGE_BYTES (15 * 1024 * 1024) +// https://www.sparkfun.com/products/17745 + +#define MICROPY_HW_BOARD_NAME "SparkFun Thing Plus RP2040" +#define MICROPY_HW_FLASH_STORAGE_BYTES (15 * 1024 * 1024) + +#define MICROPY_HW_USB_VID (0x1B4F) +#define MICROPY_HW_USB_PID (0x0025) + +#define MICROPY_HW_I2C0_SCL (17) +#define MICROPY_HW_I2C0_SDA (16) + +// Qwiic on I2C1 +#define MICROPY_HW_I2C1_SCL (7) +#define MICROPY_HW_I2C1_SDA (6) + +#define MICROPY_HW_SPI0_SCK (2) +#define MICROPY_HW_SPI0_MOSI (3) +#define MICROPY_HW_SPI0_MISO (4) + +// MicroSD on SPI1 +#define MICROPY_HW_SPI1_SCK (14) +#define MICROPY_HW_SPI1_MOSI (15) +#define MICROPY_HW_SPI1_MISO (12) + +// Battery fuel guage MAX17048 on I2C1 +// BATT_ALERT GPIO24 + +// NeoPixel data GPIO8, power not toggleable +// data out is broken out to a pin + +// Blue user LED GPIO25 From 043848158df7d211f30d83b2565a7d3039b0592e Mon Sep 17 00:00:00 2001 From: Mike Causer Date: Sat, 24 Jul 2021 02:11:56 +1000 Subject: [PATCH 155/264] rp2/boards: Add Adafruit ItsyBitsy RP2040. --- .../mpconfigboard.cmake | 1 + .../ADAFRUIT_ITSYBITSY_RP2040/mpconfigboard.h | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 ports/rp2/boards/ADAFRUIT_ITSYBITSY_RP2040/mpconfigboard.cmake create mode 100644 ports/rp2/boards/ADAFRUIT_ITSYBITSY_RP2040/mpconfigboard.h diff --git a/ports/rp2/boards/ADAFRUIT_ITSYBITSY_RP2040/mpconfigboard.cmake b/ports/rp2/boards/ADAFRUIT_ITSYBITSY_RP2040/mpconfigboard.cmake new file mode 100644 index 0000000000..711e24ed59 --- /dev/null +++ b/ports/rp2/boards/ADAFRUIT_ITSYBITSY_RP2040/mpconfigboard.cmake @@ -0,0 +1 @@ +# cmake file for Adafruit ItsyBitsy RP2040 diff --git a/ports/rp2/boards/ADAFRUIT_ITSYBITSY_RP2040/mpconfigboard.h b/ports/rp2/boards/ADAFRUIT_ITSYBITSY_RP2040/mpconfigboard.h new file mode 100644 index 0000000000..8f5551172b --- /dev/null +++ b/ports/rp2/boards/ADAFRUIT_ITSYBITSY_RP2040/mpconfigboard.h @@ -0,0 +1,21 @@ +// https://www.adafruit.com/product/4888 +// https://learn.adafruit.com/adafruit-itsybitsy-rp2040/pinouts + +#define MICROPY_HW_BOARD_NAME "Adafruit ItsyBitsy RP2040" +#define MICROPY_HW_FLASH_STORAGE_BYTES (7 * 1024 * 1024) + +#define MICROPY_HW_USB_VID (0x239A) +#define MICROPY_HW_USB_PID (0x80FE) + +#define MICROPY_HW_I2C0_SCL (3) +#define MICROPY_HW_I2C0_SDA (2) + +#define MICROPY_HW_SPI0_SCK (18) +#define MICROPY_HW_SPI0_MOSI (19) +#define MICROPY_HW_SPI0_MISO (20) + +// NeoPixel data GPIO17, power GPIO16 + +// Red user LED GPIO11 + +// Boot button GPIO13 From 02cbe018a523c46a0c835bc7c77dfe491e90a5c0 Mon Sep 17 00:00:00 2001 From: Mike Causer Date: Sat, 24 Jul 2021 02:12:52 +1000 Subject: [PATCH 156/264] rp2/boards: Add Adafruit QT Py RP2040. --- .../ADAFRUIT_QTPY_RP2040/mpconfigboard.cmake | 1 + .../ADAFRUIT_QTPY_RP2040/mpconfigboard.h | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 ports/rp2/boards/ADAFRUIT_QTPY_RP2040/mpconfigboard.cmake create mode 100644 ports/rp2/boards/ADAFRUIT_QTPY_RP2040/mpconfigboard.h diff --git a/ports/rp2/boards/ADAFRUIT_QTPY_RP2040/mpconfigboard.cmake b/ports/rp2/boards/ADAFRUIT_QTPY_RP2040/mpconfigboard.cmake new file mode 100644 index 0000000000..799ced5cd8 --- /dev/null +++ b/ports/rp2/boards/ADAFRUIT_QTPY_RP2040/mpconfigboard.cmake @@ -0,0 +1 @@ +# cmake file for Adafruit QT Py RP2040 diff --git a/ports/rp2/boards/ADAFRUIT_QTPY_RP2040/mpconfigboard.h b/ports/rp2/boards/ADAFRUIT_QTPY_RP2040/mpconfigboard.h new file mode 100644 index 0000000000..ca341cedd8 --- /dev/null +++ b/ports/rp2/boards/ADAFRUIT_QTPY_RP2040/mpconfigboard.h @@ -0,0 +1,28 @@ +// https://www.adafruit.com/product/4900 +// https://learn.adafruit.com/adafruit-qt-py-2040/pinouts + +#define MICROPY_HW_BOARD_NAME "Adafruit QT Py RP2040" +#define MICROPY_HW_FLASH_STORAGE_BYTES (7 * 1024 * 1024) + +#define MICROPY_HW_USB_VID (0x239A) +#define MICROPY_HW_USB_PID (0x80F8) + +#define MICROPY_HW_UART1_TX (20) +#define MICROPY_HW_UART1_RX (5) +#define MICROPY_HW_UART1_CTS (10) +#define MICROPY_HW_UART1_RTS (7) + +#define MICROPY_HW_I2C0_SCL (25) +#define MICROPY_HW_I2C0_SDA (24) + +// STEMMA QT / Qwiic on I2C1 +#define MICROPY_HW_I2C1_SCL (23) +#define MICROPY_HW_I2C1_SDA (22) + +#define MICROPY_HW_SPI0_SCK (6) +#define MICROPY_HW_SPI0_MOSI (3) +#define MICROPY_HW_SPI0_MISO (4) + +// NeoPixel data GPIO12, power GPIO11 + +// Boot button GPIO21 From 95eff8d96ab6a347d2aa5987110467760c703dfd Mon Sep 17 00:00:00 2001 From: Mike Causer Date: Sat, 24 Jul 2021 02:14:10 +1000 Subject: [PATCH 157/264] rp2/boards: Add Pimoroni Pico LiPo 4MB. --- .../PIMORONI_PICOLIPO_4MB/mpconfigboard.cmake | 1 + .../PIMORONI_PICOLIPO_4MB/mpconfigboard.h | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 ports/rp2/boards/PIMORONI_PICOLIPO_4MB/mpconfigboard.cmake create mode 100644 ports/rp2/boards/PIMORONI_PICOLIPO_4MB/mpconfigboard.h diff --git a/ports/rp2/boards/PIMORONI_PICOLIPO_4MB/mpconfigboard.cmake b/ports/rp2/boards/PIMORONI_PICOLIPO_4MB/mpconfigboard.cmake new file mode 100644 index 0000000000..8da827a17d --- /dev/null +++ b/ports/rp2/boards/PIMORONI_PICOLIPO_4MB/mpconfigboard.cmake @@ -0,0 +1 @@ +# cmake file for Pimoroni Pico LiPo 4MB diff --git a/ports/rp2/boards/PIMORONI_PICOLIPO_4MB/mpconfigboard.h b/ports/rp2/boards/PIMORONI_PICOLIPO_4MB/mpconfigboard.h new file mode 100644 index 0000000000..fd45547e1b --- /dev/null +++ b/ports/rp2/boards/PIMORONI_PICOLIPO_4MB/mpconfigboard.h @@ -0,0 +1,28 @@ +// https://shop.pimoroni.com/products/pimoroni-pico-lipo?variant=39386149093459 + +#define MICROPY_HW_BOARD_NAME "Pimoroni Pico LiPo 4MB" +#define MICROPY_HW_FLASH_STORAGE_BYTES (3 * 1024 * 1024) + +#define MICROPY_HW_USB_VID (0x2E8A) +#define MICROPY_HW_USB_PID (0x1002) + +#define MICROPY_HW_UART1_TX (8) +#define MICROPY_HW_UART1_RX (9) +#define MICROPY_HW_UART1_CTS (10) +#define MICROPY_HW_UART1_RTS (11) + +// Qwiic on I2C0 +#define MICROPY_HW_I2C0_SCL (4) +#define MICROPY_HW_I2C0_SDA (5) + +#define MICROPY_HW_SPI0_SCK (18) +#define MICROPY_HW_SPI0_MOSI (19) +#define MICROPY_HW_SPI0_MISO (16) + +// User LED GPIO25 + +// VBUS_SENSE GPIO24 + +// BAT_SENSE GPIO29 + +// Boot button GPIO23 From 042a4bebc3c84ae3ae81bcc32d9d8eed71c1a0b9 Mon Sep 17 00:00:00 2001 From: Mike Causer Date: Sat, 24 Jul 2021 02:15:00 +1000 Subject: [PATCH 158/264] rp2/boards: Add Pimoroni Pico LiPo 16MB. --- .../mpconfigboard.cmake | 1 + .../PIMORONI_PICOLIPO_16MB/mpconfigboard.h | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 ports/rp2/boards/PIMORONI_PICOLIPO_16MB/mpconfigboard.cmake create mode 100644 ports/rp2/boards/PIMORONI_PICOLIPO_16MB/mpconfigboard.h diff --git a/ports/rp2/boards/PIMORONI_PICOLIPO_16MB/mpconfigboard.cmake b/ports/rp2/boards/PIMORONI_PICOLIPO_16MB/mpconfigboard.cmake new file mode 100644 index 0000000000..b98ff44956 --- /dev/null +++ b/ports/rp2/boards/PIMORONI_PICOLIPO_16MB/mpconfigboard.cmake @@ -0,0 +1 @@ +# cmake file for Pimoroni Pico LiPo 16MB diff --git a/ports/rp2/boards/PIMORONI_PICOLIPO_16MB/mpconfigboard.h b/ports/rp2/boards/PIMORONI_PICOLIPO_16MB/mpconfigboard.h new file mode 100644 index 0000000000..68478f7613 --- /dev/null +++ b/ports/rp2/boards/PIMORONI_PICOLIPO_16MB/mpconfigboard.h @@ -0,0 +1,28 @@ +// https://shop.pimoroni.com/products/pimoroni-pico-lipo?variant=39335427080275 + +#define MICROPY_HW_BOARD_NAME "Pimoroni Pico LiPo 16MB" +#define MICROPY_HW_FLASH_STORAGE_BYTES (7 * 1024 * 1024) + +#define MICROPY_HW_USB_VID (0x2E8A) +#define MICROPY_HW_USB_PID (0x1003) + +#define MICROPY_HW_UART1_TX (8) +#define MICROPY_HW_UART1_RX (9) +#define MICROPY_HW_UART1_CTS (10) +#define MICROPY_HW_UART1_RTS (11) + +// Qwiic on I2C0 +#define MICROPY_HW_I2C0_SCL (4) +#define MICROPY_HW_I2C0_SDA (5) + +#define MICROPY_HW_SPI0_SCK (18) +#define MICROPY_HW_SPI0_MOSI (19) +#define MICROPY_HW_SPI0_MISO (16) + +// User LED GPIO25 + +// VBUS_SENSE GPIO24 + +// BAT_SENSE GPIO29 + +// Boot button GPIO23 From 71722c84ca43c0f80db74d3f89e036982083a163 Mon Sep 17 00:00:00 2001 From: Mike Causer Date: Sat, 24 Jul 2021 02:15:58 +1000 Subject: [PATCH 159/264] rp2/boards: Add Pimoroni Tiny 2040. --- .../PIMORONI_TINY2040/mpconfigboard.cmake | 1 + .../boards/PIMORONI_TINY2040/mpconfigboard.h | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 ports/rp2/boards/PIMORONI_TINY2040/mpconfigboard.cmake create mode 100644 ports/rp2/boards/PIMORONI_TINY2040/mpconfigboard.h diff --git a/ports/rp2/boards/PIMORONI_TINY2040/mpconfigboard.cmake b/ports/rp2/boards/PIMORONI_TINY2040/mpconfigboard.cmake new file mode 100644 index 0000000000..b6c4b3efc3 --- /dev/null +++ b/ports/rp2/boards/PIMORONI_TINY2040/mpconfigboard.cmake @@ -0,0 +1 @@ +# cmake file for Pimoroni Tiny 2040 diff --git a/ports/rp2/boards/PIMORONI_TINY2040/mpconfigboard.h b/ports/rp2/boards/PIMORONI_TINY2040/mpconfigboard.h new file mode 100644 index 0000000000..50cb2bd599 --- /dev/null +++ b/ports/rp2/boards/PIMORONI_TINY2040/mpconfigboard.h @@ -0,0 +1,17 @@ +// https://shop.pimoroni.com/products/tiny-2040 + +#define MICROPY_HW_BOARD_NAME "Pimoroni Tiny 2040" +#define MICROPY_HW_FLASH_STORAGE_BYTES (7 * 1024 * 1024) + +#define MICROPY_HW_USB_VID (0x16D0) +#define MICROPY_HW_USB_PID (0x08C7) + +#define MICROPY_HW_I2C0_SCL (4) +#define MICROPY_HW_I2C0_SDA (5) + +// RGB LED, active low +// Red LED 18 +// Green LED 19 +// Blue LED 20 + +// Boot button GPIO23 From a3675294ae39a724aec6959238c45af0dea92f21 Mon Sep 17 00:00:00 2001 From: Will Sowerbutts Date: Sun, 14 Feb 2021 16:34:26 +0000 Subject: [PATCH 160/264] esp32/machine_uart: Add flow kw-arg to enable hardware flow control. This enables optional support for the hardware UART to use the RTS and/or CTS pins for flow control. The new "flow" constructor keyword specifies a bitmask of RTS and/or CTS. This matches the interface used by machine.UART on stm32 and rp2. Previously on ESP32 it was possible to specify which pins to use for the RTS and CTS signals, but hardware flow control was never functional: CTS was not checked before transmitting bytes, and RTS was always driven high (signalling no buffer space available). With this patch, CTS and RTS both operate as expected. This also includes an update to the machine.UART documentation. Signed-off-by: Will Sowerbutts --- docs/library/machine.UART.rst | 11 +++++++++++ ports/esp32/machine_uart.c | 28 +++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/docs/library/machine.UART.rst b/docs/library/machine.UART.rst index 957d58ca18..2eb2e20cb3 100644 --- a/docs/library/machine.UART.rst +++ b/docs/library/machine.UART.rst @@ -56,11 +56,22 @@ Methods - *tx* specifies the TX pin to use. - *rx* specifies the RX pin to use. + - *rts* specifies the RTS (output) pin to use for hardware receive flow control. + - *cts* specifies the CTS (input) pin to use for hardware transmit flow control. - *txbuf* specifies the length in characters of the TX buffer. - *rxbuf* specifies the length in characters of the RX buffer. - *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. + - *flow* specifies which hardware flow control signals to use. The value + is a bitmask. + + - ``0`` will ignore hardware flow control signals. + - ``UART.RTS`` will enable receive flow control by using the RTS output pin to + signal if the receive FIFO has sufficient space to accept more data. + - ``UART.CTS`` will enable transmit flow control by pausing transmission when the + CTS input pin signals that the receiver is running low on buffer space. + - ``UART.RTS | UART.CTS`` will enable both, for full hardware flow control. On the WiPy only the following keyword-only parameter is supported: diff --git a/ports/esp32/machine_uart.c b/ports/esp32/machine_uart.c index 2953ac171c..3c90a72150 100644 --- a/ports/esp32/machine_uart.c +++ b/ports/esp32/machine_uart.c @@ -53,6 +53,7 @@ typedef struct _machine_uart_obj_t { mp_obj_base_t base; uart_port_t uart_num; + uart_hw_flowcontrol_t flowcontrol; uint8_t bits; uint8_t parity; uint8_t stop; @@ -107,11 +108,25 @@ STATIC void machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_pri mp_printf(print, "INV_CTS"); } } + if (self->flowcontrol) { + mp_printf(print, ", flow="); + uint32_t flow_mask = self->flowcontrol; + if (flow_mask & UART_HW_FLOWCTRL_RTS) { + mp_printf(print, "RTS"); + flow_mask &= ~UART_HW_FLOWCTRL_RTS; + if (flow_mask) { + mp_printf(print, "|"); + } + } + if (flow_mask & UART_HW_FLOWCTRL_CTS) { + mp_printf(print, "CTS"); + } + } mp_printf(print, ")"); } 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_rts, ARG_cts, ARG_txbuf, ARG_rxbuf, ARG_timeout, ARG_timeout_char, ARG_invert }; + enum { ARG_baudrate, ARG_bits, ARG_parity, ARG_stop, ARG_tx, ARG_rx, ARG_rts, ARG_cts, ARG_txbuf, ARG_rxbuf, ARG_timeout, ARG_timeout_char, ARG_invert, ARG_flow }; static const mp_arg_t allowed_args[] = { { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_bits, MP_ARG_INT, {.u_int = 0} }, @@ -126,6 +141,7 @@ STATIC void machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, co { 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_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); @@ -257,6 +273,13 @@ STATIC void machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, co } 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")); + } + self->flowcontrol = args[ARG_flow].u_int; + uart_set_hw_flow_ctrl(self->uart_num, self->flowcontrol, UART_FIFO_LEN - UART_FIFO_LEN / 4); } 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) { @@ -400,6 +423,9 @@ STATIC const mp_rom_map_elem_t machine_uart_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_INV_RX), MP_ROM_INT(UART_INV_RX) }, { MP_ROM_QSTR(MP_QSTR_INV_RTS), MP_ROM_INT(UART_INV_RTS) }, { MP_ROM_QSTR(MP_QSTR_INV_CTS), MP_ROM_INT(UART_INV_CTS) }, + + { MP_ROM_QSTR(MP_QSTR_RTS), MP_ROM_INT(UART_HW_FLOWCTRL_RTS) }, + { MP_ROM_QSTR(MP_QSTR_CTS), MP_ROM_INT(UART_HW_FLOWCTRL_CTS) }, }; STATIC MP_DEFINE_CONST_DICT(machine_uart_locals_dict, machine_uart_locals_dict_table); From 028fc815cdb63b2ea50bc1f377177b19b6a3f9cc Mon Sep 17 00:00:00 2001 From: Patrick Van Oosterwijck Date: Fri, 23 Jul 2021 19:31:51 -0600 Subject: [PATCH 161/264] esp32/boards: Add Silicognition wESP32 board configuration. For rev 7+ boards with 16MB of flash. Partition table allocates 2.4 MiB for MicroPython, 11 MiB for the filesystem. --- .../boards/SIL_WESP32/mpconfigboard.cmake | 10 +++++++++ ports/esp32/boards/SIL_WESP32/mpconfigboard.h | 2 ++ ports/esp32/boards/SIL_WESP32/sdkconfig.board | 22 +++++++++++++++++++ ports/esp32/partitions-16MiB-ota.csv | 10 +++++++++ 4 files changed, 44 insertions(+) create mode 100644 ports/esp32/boards/SIL_WESP32/mpconfigboard.cmake create mode 100644 ports/esp32/boards/SIL_WESP32/mpconfigboard.h create mode 100644 ports/esp32/boards/SIL_WESP32/sdkconfig.board create mode 100644 ports/esp32/partitions-16MiB-ota.csv diff --git a/ports/esp32/boards/SIL_WESP32/mpconfigboard.cmake b/ports/esp32/boards/SIL_WESP32/mpconfigboard.cmake new file mode 100644 index 0000000000..885d4eaa26 --- /dev/null +++ b/ports/esp32/boards/SIL_WESP32/mpconfigboard.cmake @@ -0,0 +1,10 @@ +set(SDKCONFIG_DEFAULTS + boards/sdkconfig.base + boards/sdkconfig.ble + boards/sdkconfig.240mhz + boards/SIL_WESP32/sdkconfig.board +) + +if(NOT MICROPY_FROZEN_MANIFEST) + set(MICROPY_FROZEN_MANIFEST ${MICROPY_PORT_DIR}/boards/manifest.py) +endif() diff --git a/ports/esp32/boards/SIL_WESP32/mpconfigboard.h b/ports/esp32/boards/SIL_WESP32/mpconfigboard.h new file mode 100644 index 0000000000..fc66aac64c --- /dev/null +++ b/ports/esp32/boards/SIL_WESP32/mpconfigboard.h @@ -0,0 +1,2 @@ +#define MICROPY_HW_BOARD_NAME "Silicognition wESP32" +#define MICROPY_HW_MCU_NAME "ESP32" diff --git a/ports/esp32/boards/SIL_WESP32/sdkconfig.board b/ports/esp32/boards/SIL_WESP32/sdkconfig.board new file mode 100644 index 0000000000..98eef1d109 --- /dev/null +++ b/ports/esp32/boards/SIL_WESP32/sdkconfig.board @@ -0,0 +1,22 @@ +# 16 MB flash + +CONFIG_ESPTOOLPY_FLASHSIZE_4MB= +CONFIG_ESPTOOLPY_FLASHSIZE_8MB= +CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y +CONFIG_ESPTOOLPY_FLASHSIZE="16MB" + +# Fast flash + +CONFIG_FLASHMODE_QIO=y +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_ESP32_REV_MIN_1=y + +# OTA + +CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=y +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-16MiB-ota.csv" + +# Network name + +CONFIG_LWIP_LOCAL_HOSTNAME="wESP32" diff --git a/ports/esp32/partitions-16MiB-ota.csv b/ports/esp32/partitions-16MiB-ota.csv new file mode 100644 index 0000000000..a6f83bc46b --- /dev/null +++ b/ports/esp32/partitions-16MiB-ota.csv @@ -0,0 +1,10 @@ +# Partition table for MicroPython with OTA support using 16MB flash +# Notes: the offset of the partition table itself is set in +# $IDF_PATH/components/partition_table/Kconfig.projbuild. +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x4000, +otadata, data, ota, 0xd000, 0x2000, +phy_init, data, phy, 0xf000, 0x1000, +ota_0, app, ota_0, 0x10000, 0x270000, +ota_1, app, ota_1, 0x280000, 0x270000, +vfs, data, fat, 0x4f0000, 0xb10000, From bfc8e88ce165fc3d25dd12e6dc6853d3c8cd4a5e Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Wed, 4 Aug 2021 15:35:00 +1000 Subject: [PATCH 162/264] esp32/mpconfigport.h: Enable reverse and inplace special methods. Reverse operations are supported on stm32 and rp2, and esp32 has enough space to also enable inplace operations, to make it complete. Signed-off-by: Jim Mussared --- ports/esp32/mpconfigport.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index 46d8a38af1..d60e2823df 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -82,6 +82,8 @@ #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) From 8616129f2e382310802950db066178648a9429a3 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 3 Aug 2021 22:46:24 +1000 Subject: [PATCH 163/264] esp8266,esp32: Include hidden networks in WLAN.scan results. Addresses issues #2697 and #5329. Signed-off-by: Damien George --- docs/library/network.WLAN.rst | 2 ++ ports/esp32/modnetwork.c | 2 +- ports/esp8266/modnetwork.c | 4 +++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/library/network.WLAN.rst b/docs/library/network.WLAN.rst index 88bd3a3707..35e4b798ae 100644 --- a/docs/library/network.WLAN.rst +++ b/docs/library/network.WLAN.rst @@ -46,6 +46,8 @@ Methods .. method:: WLAN.scan() Scan for the available wireless networks. + Hidden networks -- where the SSID is not broadcast -- will also be scanned + if the WLAN interface allows it. Scanning is only possible on STA interface. Returns list of tuples with the information about WiFi access points: diff --git a/ports/esp32/modnetwork.c b/ports/esp32/modnetwork.c index 3977256166..2b64105e79 100644 --- a/ports/esp32/modnetwork.c +++ b/ports/esp32/modnetwork.c @@ -466,7 +466,7 @@ STATIC mp_obj_t esp_scan(mp_obj_t self_in) { mp_obj_t list = mp_obj_new_list(0, NULL); wifi_scan_config_t config = { 0 }; - // XXX how do we scan hidden APs (and if we can scan them, are they really hidden?) + config.show_hidden = true; MP_THREAD_GIL_EXIT(); esp_err_t status = esp_wifi_scan_start(&config, 1); MP_THREAD_GIL_ENTER(); diff --git a/ports/esp8266/modnetwork.c b/ports/esp8266/modnetwork.c index 949aeba106..f0cd83aeec 100644 --- a/ports/esp8266/modnetwork.c +++ b/ports/esp8266/modnetwork.c @@ -233,7 +233,9 @@ STATIC mp_obj_t esp_scan(mp_obj_t self_in) { } mp_obj_t list = mp_obj_new_list(0, NULL); esp_scan_list = &list; - wifi_station_scan(NULL, (scan_done_cb_t)esp_scan_cb); + struct scan_config config = {0}; + config.show_hidden = 1; + wifi_station_scan(&config, (scan_done_cb_t)esp_scan_cb); while (esp_scan_list != NULL) { // our esp_scan_cb is called via ets_loop_iter so it's safe to set the // esp_scan_list variable to NULL without disabling interrupts From ffc854f17f1c4a3a9904fa8909f0b6ab5385206f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Z=C3=BCger?= Date: Wed, 3 Feb 2021 09:24:25 +0100 Subject: [PATCH 164/264] extmod/modujson: Add support for dump/dumps separators keyword-argument. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Optionally enabled via MICROPY_PY_UJSON_SEPARATORS. Enabled by default. For dump, make sure mp_get_stream_raise is called after mod_ujson_separators since CPython does it in this order (if both separators and stream are invalid, separators will raise an exception first). Add separators argument in the docs as well. Signed-off-by: Peter Züger Signed-off-by: Damien George --- docs/library/ujson.rst | 10 ++++++-- extmod/modujson.c | 58 ++++++++++++++++++++++++++++++++++++++++++ py/mpconfig.h | 5 ++++ py/mpprint.h | 8 ++++++ py/objdict.c | 11 ++++++-- py/objlist.c | 7 ++++- py/objtuple.c | 6 ++++- 7 files changed, 99 insertions(+), 6 deletions(-) diff --git a/docs/library/ujson.rst b/docs/library/ujson.rst index 5668eb21a8..65ed1867e6 100644 --- a/docs/library/ujson.rst +++ b/docs/library/ujson.rst @@ -12,14 +12,20 @@ data format. Functions --------- -.. function:: dump(obj, stream) +.. function:: dump(obj, stream, separators=None) Serialise *obj* to a JSON string, writing it to the given *stream*. -.. function:: dumps(obj) + If specified, separators should be an ``(item_separator, key_separator)`` + tuple. The default is ``(', ', ': ')``. To get the most compact JSON + representation, you should specify ``(',', ':')`` to eliminate whitespace. + +.. function:: dumps(obj, separators=None) Return *obj* represented as a JSON string. + The arguments have the same meaning as in `dump`. + .. function:: load(stream) Parse the given *stream*, interpreting it as a JSON string and diff --git a/extmod/modujson.c b/extmod/modujson.c index 8dff673580..e5bfb0d966 100644 --- a/extmod/modujson.c +++ b/extmod/modujson.c @@ -34,6 +34,62 @@ #if MICROPY_PY_UJSON +#if MICROPY_PY_UJSON_SEPARATORS + +enum { + DUMP_MODE_TO_STRING = 1, + DUMP_MODE_TO_STREAM = 2, +}; + +STATIC mp_obj_t mod_ujson_dump_helper(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args, unsigned int mode) { + enum { ARG_separators }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_separators, MP_ARG_KW_ONLY | 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 - mode, pos_args + mode, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_print_ext_t print_ext; + + if (args[ARG_separators].u_obj == mp_const_none) { + print_ext.item_separator = ", "; + print_ext.key_separator = ": "; + } else { + mp_obj_t *items; + mp_obj_get_array_fixed_n(args[ARG_separators].u_obj, 2, &items); + print_ext.item_separator = mp_obj_str_get_str(items[0]); + print_ext.key_separator = mp_obj_str_get_str(items[1]); + } + + if (mode == DUMP_MODE_TO_STRING) { + // dumps(obj) + vstr_t vstr; + vstr_init_print(&vstr, 8, &print_ext.base); + mp_obj_print_helper(&print_ext.base, pos_args[0], PRINT_JSON); + return mp_obj_new_str_from_vstr(&mp_type_str, &vstr); + } else { + // dump(obj, stream) + print_ext.base.data = MP_OBJ_TO_PTR(pos_args[1]); + print_ext.base.print_strn = mp_stream_write_adaptor; + mp_get_stream_raise(pos_args[1], MP_STREAM_OP_WRITE); + mp_obj_print_helper(&print_ext.base, pos_args[0], PRINT_JSON); + return mp_const_none; + } +} + +STATIC mp_obj_t mod_ujson_dump(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + return mod_ujson_dump_helper(n_args, pos_args, kw_args, DUMP_MODE_TO_STREAM); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_ujson_dump_obj, 2, mod_ujson_dump); + +STATIC mp_obj_t mod_ujson_dumps(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + return mod_ujson_dump_helper(n_args, pos_args, kw_args, DUMP_MODE_TO_STRING); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_ujson_dumps_obj, 1, mod_ujson_dumps); + +#else + STATIC mp_obj_t mod_ujson_dump(mp_obj_t obj, mp_obj_t stream) { mp_get_stream_raise(stream, MP_STREAM_OP_WRITE); mp_print_t print = {MP_OBJ_TO_PTR(stream), mp_stream_write_adaptor}; @@ -51,6 +107,8 @@ STATIC mp_obj_t mod_ujson_dumps(mp_obj_t obj) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_ujson_dumps_obj, mod_ujson_dumps); +#endif + // The function below implements a simple non-recursive JSON parser. // // The JSON specification is at http://www.ietf.org/rfc/rfc4627.txt diff --git a/py/mpconfig.h b/py/mpconfig.h index f67e11cd4b..a91c39b018 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -1380,6 +1380,11 @@ typedef double mp_float_t; #define MICROPY_PY_UJSON (0) #endif +// Whether to support the "separators" argument to dump, dumps +#ifndef MICROPY_PY_UJSON_SEPARATORS +#define MICROPY_PY_UJSON_SEPARATORS (1) +#endif + #ifndef MICROPY_PY_URE #define MICROPY_PY_URE (0) #endif diff --git a/py/mpprint.h b/py/mpprint.h index aef9015deb..0dff9a770a 100644 --- a/py/mpprint.h +++ b/py/mpprint.h @@ -52,6 +52,14 @@ typedef struct _mp_print_t { mp_print_strn_t print_strn; } mp_print_t; +typedef struct _mp_print_ext_t { + mp_print_t base; + const char *item_separator; + const char *key_separator; +}mp_print_ext_t; + +#define MP_PRINT_GET_EXT(print) ((mp_print_ext_t *)print) + // All (non-debug) prints go through one of the two interfaces below. // 1) Wrapper for platform print function, which wraps MP_PLAT_PRINT_STRN. extern const mp_print_t mp_plat_print; diff --git a/py/objdict.c b/py/objdict.c index 63e5381c66..ed4376aa4f 100644 --- a/py/objdict.c +++ b/py/objdict.c @@ -69,8 +69,15 @@ STATIC mp_map_elem_t *dict_iter_next(mp_obj_dict_t *dict, size_t *cur) { STATIC void dict_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { mp_obj_dict_t *self = MP_OBJ_TO_PTR(self_in); bool first = true; + const char *item_separator = ", "; + const char *key_separator = ": "; if (!(MICROPY_PY_UJSON && kind == PRINT_JSON)) { kind = PRINT_REPR; + } else { + #if MICROPY_PY_UJSON_SEPARATORS + item_separator = MP_PRINT_GET_EXT(print)->item_separator; + key_separator = MP_PRINT_GET_EXT(print)->key_separator; + #endif } if (MICROPY_PY_COLLECTIONS_ORDEREDDICT && self->base.type != &mp_type_dict && kind != PRINT_JSON) { mp_printf(print, "%q(", self->base.type->name); @@ -80,7 +87,7 @@ STATIC void dict_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_ mp_map_elem_t *next = NULL; while ((next = dict_iter_next(self, &cur)) != NULL) { if (!first) { - mp_print_str(print, ", "); + mp_print_str(print, item_separator); } first = false; bool add_quote = MICROPY_PY_UJSON && kind == PRINT_JSON && !mp_obj_is_str_or_bytes(next->key); @@ -91,7 +98,7 @@ STATIC void dict_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_ if (add_quote) { mp_print_str(print, "\""); } - mp_print_str(print, ": "); + mp_print_str(print, key_separator); mp_obj_print_helper(print, next->value, kind); } mp_print_str(print, "}"); diff --git a/py/objlist.c b/py/objlist.c index 8c989facc0..f431e273df 100644 --- a/py/objlist.c +++ b/py/objlist.c @@ -44,13 +44,18 @@ STATIC mp_obj_t list_pop(size_t n_args, const mp_obj_t *args); STATIC void list_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { mp_obj_list_t *o = MP_OBJ_TO_PTR(o_in); + const char *item_separator = ", "; if (!(MICROPY_PY_UJSON && kind == PRINT_JSON)) { kind = PRINT_REPR; + } else { + #if MICROPY_PY_UJSON_SEPARATORS + item_separator = MP_PRINT_GET_EXT(print)->item_separator; + #endif } mp_print_str(print, "["); for (size_t i = 0; i < o->len; i++) { if (i > 0) { - mp_print_str(print, ", "); + mp_print_str(print, item_separator); } mp_obj_print_helper(print, o->items[i], kind); } diff --git a/py/objtuple.c b/py/objtuple.c index 07a560ac08..67d7bc356f 100644 --- a/py/objtuple.c +++ b/py/objtuple.c @@ -39,15 +39,19 @@ void mp_obj_tuple_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { mp_obj_tuple_t *o = MP_OBJ_TO_PTR(o_in); + const char *item_separator = ", "; if (MICROPY_PY_UJSON && kind == PRINT_JSON) { mp_print_str(print, "["); + #if MICROPY_PY_UJSON_SEPARATORS + item_separator = MP_PRINT_GET_EXT(print)->item_separator; + #endif } else { mp_print_str(print, "("); kind = PRINT_REPR; } for (size_t i = 0; i < o->len; i++) { if (i > 0) { - mp_print_str(print, ", "); + mp_print_str(print, item_separator); } mp_obj_print_helper(print, o->items[i], kind); } From d290f369d08990cba4b5c2e0d207f8603c5166b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Z=C3=BCger?= Date: Sun, 30 May 2021 14:28:13 +0200 Subject: [PATCH 165/264] tests/extmod/ujson: Add tests for dump/dumps separators argument. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Basically just copied ujson_dump(s).py and added various valid/invalid separator tests. Signed-off-by: Peter Züger --- tests/extmod/ujson_dump_separators.py | 62 ++++++++++++++++++++++++++ tests/extmod/ujson_dumps_separators.py | 60 +++++++++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 tests/extmod/ujson_dump_separators.py create mode 100644 tests/extmod/ujson_dumps_separators.py diff --git a/tests/extmod/ujson_dump_separators.py b/tests/extmod/ujson_dump_separators.py new file mode 100644 index 0000000000..e19b99a26d --- /dev/null +++ b/tests/extmod/ujson_dump_separators.py @@ -0,0 +1,62 @@ +try: + from uio import StringIO + import ujson as json +except: + try: + from io import StringIO + import json + except ImportError: + print("SKIP") + raise SystemExit + +for sep in [ + None, + (", ", ": "), + (",", ": "), + (",", ":"), + [", ", ": "], + [",", ": "], + [",", ":"], +]: + s = StringIO() + json.dump(False, s, separators=sep) + print(s.getvalue()) + + s = StringIO() + json.dump({"a": (2, [3, None])}, s, separators=sep) + print(s.getvalue()) + + # dump to a small-int not allowed + try: + json.dump(123, 1, separators=sep) + except (AttributeError, OSError): # CPython and uPy have different errors + print("Exception") + + # dump to an object not allowed + try: + json.dump(123, {}, separators=sep) + except (AttributeError, OSError): # CPython and uPy have different errors + print("Exception") + + +try: + s = StringIO() + json.dump(False, s, separators={"a": 1}) +except (TypeError, ValueError): # CPython and uPy have different errors + print("Exception") + +# invalid separator types +for sep in [1, object()]: + try: + s = StringIO() + json.dump(False, s, separators=sep) + except TypeError: + print("Exception") + +# too many/ not enough separators +for sep in [(), (",", ":", "?"), (",",), []]: + try: + s = StringIO() + json.dump(False, s, separators=sep) + except ValueError: + print("Exception") diff --git a/tests/extmod/ujson_dumps_separators.py b/tests/extmod/ujson_dumps_separators.py new file mode 100644 index 0000000000..efb541fe8b --- /dev/null +++ b/tests/extmod/ujson_dumps_separators.py @@ -0,0 +1,60 @@ +try: + import ujson as json +except ImportError: + try: + import json + except ImportError: + print("SKIP") + raise SystemExit + +for sep in [ + None, + (", ", ": "), + (",", ": "), + (",", ":"), + [", ", ": "], + [",", ": "], + [",", ":"], +]: + print(json.dumps(False, separators=sep)) + print(json.dumps(True, separators=sep)) + print(json.dumps(None, separators=sep)) + print(json.dumps(1, separators=sep)) + print(json.dumps("abc", separators=sep)) + print(json.dumps("\x00\x01\x7e", separators=sep)) + print(json.dumps([], separators=sep)) + print(json.dumps([1], separators=sep)) + print(json.dumps([1, 2], separators=sep)) + print(json.dumps([1, True], separators=sep)) + print(json.dumps((), separators=sep)) + print(json.dumps((1,), separators=sep)) + print(json.dumps((1, 2), separators=sep)) + print(json.dumps((1, (2, 3)), separators=sep)) + print(json.dumps({}, separators=sep)) + print(json.dumps({"a": 1}, separators=sep)) + print(json.dumps({"a": (2, [3, None])}, separators=sep)) + print(json.dumps('"quoted"', separators=sep)) + print(json.dumps("space\n\r\tspace", separators=sep)) + print(json.dumps({None: -1}, separators=sep)) + print(json.dumps({False: 0}, separators=sep)) + print(json.dumps({True: 1}, separators=sep)) + print(json.dumps({1: 2}, separators=sep)) + +try: + json.dumps(False, separators={"a": 1}) +except (TypeError, ValueError): # CPython and uPy have different errors + print("Exception") + +# invalid separator types +for sep in [1, object()]: + try: + json.dumps(False, separators=sep) + except TypeError: + print("Exception") + +# too many/ not enough separators +for sep in [(), (",", ":", "?"), (",",), []]: + try: + json.dumps(False, separators=sep) + except ValueError: + print("Exception") From 78718fffb1f3010c7a40bb4c29c6ddf5b8dadaa3 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 3 Aug 2021 22:30:15 +1000 Subject: [PATCH 166/264] py/mkrules: Automatically build mpy-cross if it doesn't exist. Commit 41739506589ec8397613c86d8f682fb7f86c0a9f removed automatic building of mpy-cross, which rebuilt it whenever any of its dependent source files changed. But needing to build mpy-cross, and not knowing how, is a frequent issue. This commit aims to help by automatically building mpy-cross only if it doesn't exist. For Makefiles it uses an order-only prerequisite, while for CMake it uses a custom command. If MICROPY_MPYCROSS (which is what makemanifest.py uses to locate the mpy-cross executable) is defined in the environment then automatic build will not be attempted, allowing a way to prevent this auto-build if needed. Thanks to Trammell Hudson aka @osresearch for the original idea; see #5760. Signed-off-by: Damien George --- py/mkenv.mk | 6 +++++- py/mkrules.cmake | 16 ++++++++++++++++ py/mkrules.mk | 12 +++++++++--- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/py/mkenv.mk b/py/mkenv.mk index d54f0a0d31..2b247974b6 100644 --- a/py/mkenv.mk +++ b/py/mkenv.mk @@ -55,11 +55,15 @@ AR = $(CROSS_COMPILE)ar MAKE_MANIFEST = $(PYTHON) $(TOP)/tools/makemanifest.py MAKE_FROZEN = $(PYTHON) $(TOP)/tools/make-frozen.py -MPY_CROSS = $(TOP)/mpy-cross/mpy-cross MPY_TOOL = $(PYTHON) $(TOP)/tools/mpy-tool.py MPY_LIB_DIR = $(TOP)/../micropython-lib +ifeq ($(MICROPY_MPYCROSS),) +MICROPY_MPYCROSS = $(TOP)/mpy-cross/mpy-cross +MICROPY_MPYCROSS_DEPENDENCY = $(MICROPY_MPYCROSS) +endif + all: .PHONY: all diff --git a/py/mkrules.cmake b/py/mkrules.cmake index 7589255b20..9d08017931 100644 --- a/py/mkrules.cmake +++ b/py/mkrules.cmake @@ -129,11 +129,27 @@ if(MICROPY_FROZEN_MANIFEST) set(MICROPY_LIB_DIR ${MICROPY_DIR}/../micropython-lib) endif() + # If MICROPY_MPYCROSS is not explicitly defined in the environment (which + # is what makemanifest.py will use) then create an mpy-cross dependency + # to automatically build mpy-cross if needed. + set(MICROPY_MPYCROSS $ENV{MICROPY_MPYCROSS}) + if(NOT MICROPY_MPYCROSS) + set(MICROPY_MPYCROSS_DEPENDENCY ${MICROPY_DIR}/mpy-cross/mpy-cross) + if(NOT MICROPY_MAKE_EXECUTABLE) + set(MICROPY_MAKE_EXECUTABLE make) + endif() + add_custom_command( + OUTPUT ${MICROPY_MPYCROSS_DEPENDENCY} + COMMAND ${MICROPY_MAKE_EXECUTABLE} -C ${MICROPY_DIR}/mpy-cross + ) + endif() + add_custom_command( OUTPUT ${MICROPY_FROZEN_CONTENT} COMMAND ${Python3_EXECUTABLE} ${MICROPY_DIR}/tools/makemanifest.py -o ${MICROPY_FROZEN_CONTENT} -v "MPY_DIR=${MICROPY_DIR}" -v "MPY_LIB_DIR=${MICROPY_LIB_DIR}" -v "PORT_DIR=${MICROPY_PORT_DIR}" -v "BOARD_DIR=${MICROPY_BOARD_DIR}" -b "${CMAKE_BINARY_DIR}" -f${MICROPY_CROSS_FLAGS} ${MICROPY_FROZEN_MANIFEST} DEPENDS MICROPY_FORCE_BUILD ${MICROPY_QSTRDEFS_GENERATED} + ${MICROPY_MPYCROSS_DEPENDENCY} VERBATIM ) endif() diff --git a/py/mkrules.mk b/py/mkrules.mk index 63f704967f..730ad45890 100644 --- a/py/mkrules.mk +++ b/py/mkrules.mk @@ -136,9 +136,15 @@ $(OBJ_DIRS): $(HEADER_BUILD): $(MKDIR) -p $@ +ifneq ($(MICROPY_MPYCROSS_DEPENDENCY),) +# to automatically build mpy-cross, if needed +$(MICROPY_MPYCROSS_DEPENDENCY): + $(MAKE) -C $(dir $@) +endif + ifneq ($(FROZEN_MANIFEST),) # to build frozen_content.c from a manifest -$(BUILD)/frozen_content.c: FORCE $(BUILD)/genhdr/qstrdefs.generated.h +$(BUILD)/frozen_content.c: FORCE $(BUILD)/genhdr/qstrdefs.generated.h | $(MICROPY_MPYCROSS_DEPENDENCY) $(Q)$(MAKE_MANIFEST) -o $@ -v "MPY_DIR=$(TOP)" -v "MPY_LIB_DIR=$(MPY_LIB_DIR)" -v "PORT_DIR=$(shell pwd)" -v "BOARD_DIR=$(BOARD_DIR)" -b "$(BUILD)" $(if $(MPY_CROSS_FLAGS),-f"$(MPY_CROSS_FLAGS)",) --mpy-tool-flags="$(MPY_TOOL_FLAGS)" $(FROZEN_MANIFEST) ifneq ($(FROZEN_DIR),) @@ -164,10 +170,10 @@ FROZEN_MPY_PY_FILES := $(shell find -L $(FROZEN_MPY_DIR) -type f -name '*.py' | FROZEN_MPY_MPY_FILES := $(addprefix $(BUILD)/frozen_mpy/,$(FROZEN_MPY_PY_FILES:.py=.mpy)) # to build .mpy files from .py files -$(BUILD)/frozen_mpy/%.mpy: $(FROZEN_MPY_DIR)/%.py +$(BUILD)/frozen_mpy/%.mpy: $(FROZEN_MPY_DIR)/%.py | $(MICROPY_MPYCROSS_DEPENDENCY) @$(ECHO) "MPY $<" $(Q)$(MKDIR) -p $(dir $@) - $(Q)$(MPY_CROSS) -o $@ -s $(<:$(FROZEN_MPY_DIR)/%=%) $(MPY_CROSS_FLAGS) $< + $(Q)$(MICROPY_MPYCROSS) -o $@ -s $(<:$(FROZEN_MPY_DIR)/%=%) $(MPY_CROSS_FLAGS) $< # to build frozen_mpy.c from all .mpy files $(BUILD)/frozen_mpy.c: $(FROZEN_MPY_MPY_FILES) $(BUILD)/genhdr/qstrdefs.generated.h From 45f9a38451ce7419c9a97f8f2b471746a383c7b7 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 5 Aug 2021 13:00:35 +1000 Subject: [PATCH 167/264] docs/library/machine.I2S.rst: Fix use of sd pin in examples. Signed-off-by: Damien George --- docs/library/machine.I2S.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/library/machine.I2S.rst b/docs/library/machine.I2S.rst index e1c6af990c..45877a5ae0 100644 --- a/docs/library/machine.I2S.rst +++ b/docs/library/machine.I2S.rst @@ -19,17 +19,17 @@ I2S objects can be created and initialized using:: # ESP32 sck_pin = Pin(14) # Serial clock output ws_pin = Pin(13) # Word clock output - sdout_pin = Pin(12) # Serial data output + sd_pin = Pin(12) # Serial data output or # PyBoards sck_pin = Pin("Y6") # Serial clock output ws_pin = Pin("Y5") # Word clock output - sdout_pin = Pin("Y8") # Serial data output + sd_pin = Pin("Y8") # Serial data output audio_out = I2S(2, - sck=sck_pin, ws=ws_pin, sdin=sdin_pin, + sck=sck_pin, ws=ws_pin, sd=sd_pin, mode=I2S.TX, bits=16, format=I2S.MONO, @@ -37,7 +37,7 @@ I2S objects can be created and initialized using:: ibuf=20000) audio_in = I2S(2, - sck=sck_pin, ws=ws_pin, sdin=sdin_pin, + sck=sck_pin, ws=ws_pin, sd=sd_pin, mode=I2S.RX, bits=32, format=I2S.STEREO, From 96c6b8cae3ad5037c41f4e029974fb17d727d012 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 1 Aug 2021 11:04:15 +1000 Subject: [PATCH 168/264] ports: Rename USBD_VID/PID config macros to MICROPY_HW_USB_VID/PID. For consistency with other board-level config macros that begin with MICROPY_HW_USB. Also allow boards in the mimxrt, nrf and samd ports to configure these values. Signed-off-by: Damien George --- ports/mimxrt/tusb_port.c | 10 +++++---- ports/nrf/drivers/usb/usb_descriptors.c | 10 +++++---- ports/samd/tusb_port.c | 10 +++++---- ports/stm32/Makefile | 2 +- ports/stm32/main.c | 6 +++--- ports/stm32/mpconfigboard_common.h | 27 +++++++++++++++++++++++++ ports/stm32/usb.c | 24 +++++++++++----------- ports/stm32/usb.h | 16 --------------- tools/insert-usb-ids.py | 7 ++++--- 9 files changed, 65 insertions(+), 47 deletions(-) diff --git a/ports/mimxrt/tusb_port.c b/ports/mimxrt/tusb_port.c index 2b1ccabbe7..128861a49a 100644 --- a/ports/mimxrt/tusb_port.c +++ b/ports/mimxrt/tusb_port.c @@ -26,8 +26,10 @@ #include "tusb.h" -#define USBD_VID (0xf055) -#define USBD_PID (0x9802) +#ifndef MICROPY_HW_USB_VID +#define MICROPY_HW_USB_VID (0xf055) +#define MICROPY_HW_USB_PID (0x9802) +#endif #define USBD_DESC_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN) #define USBD_MAX_POWER_MA (250) @@ -57,8 +59,8 @@ static const tusb_desc_device_t usbd_desc_device = { .bDeviceSubClass = MISC_SUBCLASS_COMMON, .bDeviceProtocol = MISC_PROTOCOL_IAD, .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, - .idVendor = USBD_VID, - .idProduct = USBD_PID, + .idVendor = MICROPY_HW_USB_VID, + .idProduct = MICROPY_HW_USB_PID, .bcdDevice = 0x0100, .iManufacturer = USBD_STR_MANUF, .iProduct = USBD_STR_PRODUCT, diff --git a/ports/nrf/drivers/usb/usb_descriptors.c b/ports/nrf/drivers/usb/usb_descriptors.c index e9436ded54..b1edfcc18f 100644 --- a/ports/nrf/drivers/usb/usb_descriptors.c +++ b/ports/nrf/drivers/usb/usb_descriptors.c @@ -26,8 +26,10 @@ #include "tusb.h" -#define USBD_VID (0xf055) -#define USBD_PID (0x9802) +#ifndef MICROPY_HW_USB_VID +#define MICROPY_HW_USB_VID (0xf055) +#define MICROPY_HW_USB_PID (0x9802) +#endif #define USBD_DESC_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN) #define USBD_MAX_POWER_MA (250) @@ -57,8 +59,8 @@ static const tusb_desc_device_t usbd_desc_device = { .bDeviceSubClass = MISC_SUBCLASS_COMMON, .bDeviceProtocol = MISC_PROTOCOL_IAD, .bMaxPacketSize0 = CFG_TUD_ENDOINT0_SIZE, - .idVendor = USBD_VID, - .idProduct = USBD_PID, + .idVendor = MICROPY_HW_USB_VID, + .idProduct = MICROPY_HW_USB_PID, .bcdDevice = 0x0100, .iManufacturer = USBD_STR_MANUF, .iProduct = USBD_STR_PRODUCT, diff --git a/ports/samd/tusb_port.c b/ports/samd/tusb_port.c index e96f332464..2098334fba 100644 --- a/ports/samd/tusb_port.c +++ b/ports/samd/tusb_port.c @@ -27,8 +27,10 @@ #include "samd_soc.h" #include "tusb.h" -#define USBD_VID (0xf055) -#define USBD_PID (0x9802) +#ifndef MICROPY_HW_USB_VID +#define MICROPY_HW_USB_VID (0xf055) +#define MICROPY_HW_USB_PID (0x9802) +#endif #define USBD_DESC_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN) #define USBD_MAX_POWER_MA (250) @@ -58,8 +60,8 @@ static const tusb_desc_device_t usbd_desc_device = { .bDeviceSubClass = MISC_SUBCLASS_COMMON, .bDeviceProtocol = MISC_PROTOCOL_IAD, .bMaxPacketSize0 = CFG_TUD_ENDOINT0_SIZE, - .idVendor = USBD_VID, - .idProduct = USBD_PID, + .idVendor = MICROPY_HW_USB_VID, + .idProduct = MICROPY_HW_USB_PID, .bcdDevice = 0x0100, .iManufacturer = USBD_STR_MANUF, .iProduct = USBD_STR_PRODUCT, diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index d78e2b4851..fd76f20a6d 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -725,7 +725,7 @@ GEN_PINS_AF_PY = $(BUILD)/pins_af.py INSERT_USB_IDS = $(TOP)/tools/insert-usb-ids.py FILE2H = $(TOP)/tools/file2h.py -USB_IDS_FILE = usb.h +USB_IDS_FILE = mpconfigboard_common.h CDCINF_TEMPLATE = pybcdc.inf_template GEN_CDCINF_FILE = $(HEADER_BUILD)/pybcdc.inf GEN_CDCINF_HEADER = $(HEADER_BUILD)/pybcdc_inf.h diff --git a/ports/stm32/main.c b/ports/stm32/main.c index 3ff7077ac2..d55f1a2c30 100644 --- a/ports/stm32/main.c +++ b/ports/stm32/main.c @@ -582,13 +582,13 @@ soft_reset: // init USB device to default setting if it was not already configured if (!(pyb_usb_flags & PYB_USB_FLAG_USB_MODE_CALLED)) { #if MICROPY_HW_USB_MSC - const uint16_t pid = USBD_PID_CDC_MSC; + const uint16_t pid = MICROPY_HW_USB_PID_CDC_MSC; const uint8_t mode = USBD_MODE_CDC_MSC; #else - const uint16_t pid = USBD_PID_CDC; + const uint16_t pid = MICROPY_HW_USB_PID_CDC; const uint8_t mode = USBD_MODE_CDC; #endif - pyb_usb_dev_init(pyb_usb_dev_detect(), USBD_VID, pid, mode, 0, NULL, NULL); + pyb_usb_dev_init(pyb_usb_dev_detect(), MICROPY_HW_USB_VID, pid, mode, 0, NULL, NULL); } #endif diff --git a/ports/stm32/mpconfigboard_common.h b/ports/stm32/mpconfigboard_common.h index ce9dbdb0af..46456420da 100644 --- a/ports/stm32/mpconfigboard_common.h +++ b/ports/stm32/mpconfigboard_common.h @@ -177,6 +177,33 @@ #define MICROPY_HW_UART_IS_RESERVED(uart_id) (false) #endif +/*****************************************************************************/ +// USB configuration + +// The USBD_xxx VID/PID macros have been renamed to MICROPY_HW_USB_xxx. +#ifdef USBD_VID +#error "Old USBD_VID configuration option used" +#endif + +// Default VID and PID values to use for the USB device. If MICROPY_HW_USB_VID +// is defined by a board then all needed PID options must also be defined. The +// VID and PID can also be set dynamically in pyb.usb_mode(). +// Windows needs a different PID to distinguish different device configurations. +#ifndef MICROPY_HW_USB_VID +#define MICROPY_HW_USB_VID (0xf055) +#define MICROPY_HW_USB_PID_CDC_MSC (0x9800) +#define MICROPY_HW_USB_PID_CDC_HID (0x9801) +#define MICROPY_HW_USB_PID_CDC (0x9802) +#define MICROPY_HW_USB_PID_MSC (0x9803) +#define MICROPY_HW_USB_PID_CDC2_MSC (0x9804) +#define MICROPY_HW_USB_PID_CDC2 (0x9805) +#define MICROPY_HW_USB_PID_CDC3 (0x9806) +#define MICROPY_HW_USB_PID_CDC3_MSC (0x9807) +#define MICROPY_HW_USB_PID_CDC_MSC_HID (0x9808) +#define MICROPY_HW_USB_PID_CDC2_MSC_HID (0x9809) +#define MICROPY_HW_USB_PID_CDC3_MSC_HID (0x980a) +#endif + /*****************************************************************************/ // General configuration diff --git a/ports/stm32/usb.c b/ports/stm32/usb.c index 838613f283..1ed45592e9 100644 --- a/ports/stm32/usb.c +++ b/ports/stm32/usb.c @@ -412,7 +412,7 @@ STATIC mp_obj_t pyb_usb_mode(size_t n_args, const mp_obj_t *pos_args, mp_map_t * static const mp_arg_t allowed_args[] = { { MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, { MP_QSTR_port, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, - { MP_QSTR_vid, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = USBD_VID} }, + { MP_QSTR_vid, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = MICROPY_HW_USB_VID} }, { MP_QSTR_pid, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, #if MICROPY_HW_USB_MSC { MP_QSTR_msc, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_empty_tuple_obj)} }, @@ -489,61 +489,61 @@ STATIC mp_obj_t pyb_usb_mode(size_t n_args, const mp_obj_t *pos_args, mp_map_t * uint8_t mode; if (strcmp(mode_str, "CDC+MSC") == 0 || strcmp(mode_str, "VCP+MSC") == 0) { if (pid == -1) { - pid = USBD_PID_CDC_MSC; + pid = MICROPY_HW_USB_PID_CDC_MSC; } mode = USBD_MODE_CDC_MSC; } else if (strcmp(mode_str, "VCP+MSC+HID") == 0) { if (pid == -1) { - pid = USBD_PID_CDC_MSC_HID; + pid = MICROPY_HW_USB_PID_CDC_MSC_HID; } mode = USBD_MODE_CDC_MSC_HID; #if MICROPY_HW_USB_CDC_NUM >= 2 } else if (strcmp(mode_str, "VCP+VCP") == 0) { if (pid == -1) { - pid = USBD_PID_CDC2; + pid = MICROPY_HW_USB_PID_CDC2; } mode = USBD_MODE_CDC2; } else if (strcmp(mode_str, "VCP+VCP+MSC") == 0) { if (pid == -1) { - pid = USBD_PID_CDC2_MSC; + pid = MICROPY_HW_USB_PID_CDC2_MSC; } mode = USBD_MODE_CDC2_MSC; } else if (strcmp(mode_str, "2xVCP+MSC+HID") == 0) { if (pid == -1) { - pid = USBD_PID_CDC2_MSC_HID; + pid = MICROPY_HW_USB_PID_CDC2_MSC_HID; } mode = USBD_MODE_CDC2_MSC_HID; #endif #if MICROPY_HW_USB_CDC_NUM >= 3 } else if (strcmp(mode_str, "3xVCP") == 0) { if (pid == -1) { - pid = USBD_PID_CDC3; + pid = MICROPY_HW_USB_PID_CDC3; } mode = USBD_MODE_CDC3; } else if (strcmp(mode_str, "3xVCP+MSC") == 0) { if (pid == -1) { - pid = USBD_PID_CDC3_MSC; + pid = MICROPY_HW_USB_PID_CDC3_MSC; } mode = USBD_MODE_CDC3_MSC; } else if (strcmp(mode_str, "3xVCP+MSC+HID") == 0) { if (pid == -1) { - pid = USBD_PID_CDC3_MSC_HID; + pid = MICROPY_HW_USB_PID_CDC3_MSC_HID; } mode = USBD_MODE_CDC3_MSC_HID; #endif } else if (strcmp(mode_str, "CDC+HID") == 0 || strcmp(mode_str, "VCP+HID") == 0) { if (pid == -1) { - pid = USBD_PID_CDC_HID; + pid = MICROPY_HW_USB_PID_CDC_HID; } mode = USBD_MODE_CDC_HID; } else if (strcmp(mode_str, "CDC") == 0 || strcmp(mode_str, "VCP") == 0) { if (pid == -1) { - pid = USBD_PID_CDC; + pid = MICROPY_HW_USB_PID_CDC; } mode = USBD_MODE_CDC; } else if (strcmp(mode_str, "MSC") == 0) { if (pid == -1) { - pid = USBD_PID_MSC; + pid = MICROPY_HW_USB_PID_MSC; } mode = USBD_MODE_MSC; } else { diff --git a/ports/stm32/usb.h b/ports/stm32/usb.h index 295038ebd4..3c382887ad 100644 --- a/ports/stm32/usb.h +++ b/ports/stm32/usb.h @@ -30,22 +30,6 @@ #define PYB_USB_FLAG_USB_MODE_CALLED (0x0002) -#ifndef USBD_VID -// Windows needs a different PID to distinguish different device configurations -#define USBD_VID (0xf055) -#define USBD_PID_CDC_MSC (0x9800) -#define USBD_PID_CDC_HID (0x9801) -#define USBD_PID_CDC (0x9802) -#define USBD_PID_MSC (0x9803) -#define USBD_PID_CDC2_MSC (0x9804) -#define USBD_PID_CDC2 (0x9805) -#define USBD_PID_CDC3 (0x9806) -#define USBD_PID_CDC3_MSC (0x9807) -#define USBD_PID_CDC_MSC_HID (0x9808) -#define USBD_PID_CDC2_MSC_HID (0x9809) -#define USBD_PID_CDC3_MSC_HID (0x980a) -#endif - typedef enum { PYB_USB_STORAGE_MEDIUM_NONE = 0, PYB_USB_STORAGE_MEDIUM_FLASH, diff --git a/tools/insert-usb-ids.py b/tools/insert-usb-ids.py index b1d848ad4e..9b53f3f68a 100644 --- a/tools/insert-usb-ids.py +++ b/tools/insert-usb-ids.py @@ -8,6 +8,7 @@ import sys import re import string +config_prefix = "MICROPY_HW_USB_" needed_keys = ("USB_PID_CDC_MSC", "USB_PID_CDC_HID", "USB_PID_CDC", "USB_VID") @@ -16,10 +17,10 @@ def parse_usb_ids(filename): for line in open(filename).readlines(): line = line.rstrip("\r\n") match = re.match("^#define\s+(\w+)\s+\(0x([0-9A-Fa-f]+)\)$", line) - if match and match.group(1).startswith("USBD_"): - key = match.group(1).replace("USBD", "USB") + if match and match.group(1).startswith(config_prefix): + key = match.group(1).replace(config_prefix, "USB_") val = match.group(2) - print("key =", key, "val =", val) + # print("key =", key, "val =", val) if key in needed_keys: rv[key] = val for k in needed_keys: From f28e8b90730a3af5dc4149dd2d4e25468944aff6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 5 Aug 2021 12:01:01 +1000 Subject: [PATCH 169/264] stm32/usbd_desc: Rename USBD_xxx descriptor opts to MICROPY_HW_USB_xxx. Signed-off-by: Damien George --- ports/stm32/mpconfigboard_common.h | 46 ++++++++++++++++++++++++-- ports/stm32/usbd_desc.c | 52 ++++++------------------------ 2 files changed, 53 insertions(+), 45 deletions(-) diff --git a/ports/stm32/mpconfigboard_common.h b/ports/stm32/mpconfigboard_common.h index 46456420da..24c5e1a3cd 100644 --- a/ports/stm32/mpconfigboard_common.h +++ b/ports/stm32/mpconfigboard_common.h @@ -180,9 +180,17 @@ /*****************************************************************************/ // USB configuration -// The USBD_xxx VID/PID macros have been renamed to MICROPY_HW_USB_xxx. -#ifdef USBD_VID -#error "Old USBD_VID configuration option used" +// The USBD_xxx macros have been renamed to MICROPY_HW_USB_xxx. +#if defined(USBD_VID) \ + || defined(USBD_LANGID_STRING) \ + || defined(USBD_MANUFACTURER_STRING) \ + || defined(USBD_PRODUCT_HS_STRING) \ + || defined(USBD_PRODUCT_FS_STRING) \ + || defined(USBD_CONFIGURATION_HS_STRING) \ + || defined(USBD_INTERFACE_HS_STRING) \ + || defined(USBD_CONFIGURATION_FS_STRING) \ + || defined(USBD_INTERFACE_FS_STRING) +#error "Old USBD_xxx configuration option used, renamed to MICROPY_HW_USB_xxx" #endif // Default VID and PID values to use for the USB device. If MICROPY_HW_USB_VID @@ -204,6 +212,38 @@ #define MICROPY_HW_USB_PID_CDC3_MSC_HID (0x980a) #endif +#ifndef MICROPY_HW_USB_LANGID_STRING +#define MICROPY_HW_USB_LANGID_STRING 0x409 +#endif + +#ifndef MICROPY_HW_USB_MANUFACTURER_STRING +#define MICROPY_HW_USB_MANUFACTURER_STRING "MicroPython" +#endif + +#ifndef MICROPY_HW_USB_PRODUCT_HS_STRING +#define MICROPY_HW_USB_PRODUCT_HS_STRING "Pyboard Virtual Comm Port in HS Mode" +#endif + +#ifndef MICROPY_HW_USB_PRODUCT_FS_STRING +#define MICROPY_HW_USB_PRODUCT_FS_STRING "Pyboard Virtual Comm Port in FS Mode" +#endif + +#ifndef MICROPY_HW_USB_CONFIGURATION_HS_STRING +#define MICROPY_HW_USB_CONFIGURATION_HS_STRING "Pyboard Config" +#endif + +#ifndef MICROPY_HW_USB_INTERFACE_HS_STRING +#define MICROPY_HW_USB_INTERFACE_HS_STRING "Pyboard Interface" +#endif + +#ifndef MICROPY_HW_USB_CONFIGURATION_FS_STRING +#define MICROPY_HW_USB_CONFIGURATION_FS_STRING "Pyboard Config" +#endif + +#ifndef MICROPY_HW_USB_INTERFACE_FS_STRING +#define MICROPY_HW_USB_INTERFACE_FS_STRING "Pyboard Interface" +#endif + /*****************************************************************************/ // General configuration diff --git a/ports/stm32/usbd_desc.c b/ports/stm32/usbd_desc.c index aa6837163d..fd50029fc5 100644 --- a/ports/stm32/usbd_desc.c +++ b/ports/stm32/usbd_desc.c @@ -36,46 +36,14 @@ // need this header just for MP_HAL_UNIQUE_ID_ADDRESS #include "py/mphal.h" -// need this header for any overrides to the below constants +// Need this header for MICROPY_HW_USB_xxx configuration values. #include "py/mpconfig.h" -#ifndef USBD_LANGID_STRING -#define USBD_LANGID_STRING 0x409 -#endif - -#ifndef USBD_MANUFACTURER_STRING -#define USBD_MANUFACTURER_STRING "MicroPython" -#endif - -#ifndef USBD_PRODUCT_HS_STRING -#define USBD_PRODUCT_HS_STRING "Pyboard Virtual Comm Port in HS Mode" -#endif - -#ifndef USBD_PRODUCT_FS_STRING -#define USBD_PRODUCT_FS_STRING "Pyboard Virtual Comm Port in FS Mode" -#endif - -#ifndef USBD_CONFIGURATION_HS_STRING -#define USBD_CONFIGURATION_HS_STRING "Pyboard Config" -#endif - -#ifndef USBD_INTERFACE_HS_STRING -#define USBD_INTERFACE_HS_STRING "Pyboard Interface" -#endif - -#ifndef USBD_CONFIGURATION_FS_STRING -#define USBD_CONFIGURATION_FS_STRING "Pyboard Config" -#endif - -#ifndef USBD_INTERFACE_FS_STRING -#define USBD_INTERFACE_FS_STRING "Pyboard Interface" -#endif - __ALIGN_BEGIN static const uint8_t USBD_LangIDDesc[USB_LEN_LANGID_STR_DESC] __ALIGN_END = { USB_LEN_LANGID_STR_DESC, USB_DESC_TYPE_STRING, - LOBYTE(USBD_LANGID_STRING), - HIBYTE(USBD_LANGID_STRING), + LOBYTE(MICROPY_HW_USB_LANGID_STRING), + HIBYTE(MICROPY_HW_USB_LANGID_STRING), }; // set the VID, PID and device release number @@ -140,14 +108,14 @@ STATIC uint8_t *USBD_StrDescriptor(USBD_HandleTypeDef *pdev, uint8_t idx, uint16 return (uint8_t *)USBD_LangIDDesc; // the data should only be read from this buf case USBD_IDX_MFC_STR: - str = USBD_MANUFACTURER_STRING; + str = MICROPY_HW_USB_MANUFACTURER_STRING; break; case USBD_IDX_PRODUCT_STR: if (pdev->dev_speed == USBD_SPEED_HIGH) { - str = USBD_PRODUCT_HS_STRING; + str = MICROPY_HW_USB_PRODUCT_HS_STRING; } else { - str = USBD_PRODUCT_FS_STRING; + str = MICROPY_HW_USB_PRODUCT_FS_STRING; } break; @@ -174,17 +142,17 @@ STATIC uint8_t *USBD_StrDescriptor(USBD_HandleTypeDef *pdev, uint8_t idx, uint16 case USBD_IDX_CONFIG_STR: if (pdev->dev_speed == USBD_SPEED_HIGH) { - str = USBD_CONFIGURATION_HS_STRING; + str = MICROPY_HW_USB_CONFIGURATION_HS_STRING; } else { - str = USBD_CONFIGURATION_FS_STRING; + str = MICROPY_HW_USB_CONFIGURATION_FS_STRING; } break; case USBD_IDX_INTERFACE_STR: if (pdev->dev_speed == USBD_SPEED_HIGH) { - str = USBD_INTERFACE_HS_STRING; + str = MICROPY_HW_USB_INTERFACE_HS_STRING; } else { - str = USBD_INTERFACE_FS_STRING; + str = MICROPY_HW_USB_INTERFACE_FS_STRING; } break; From 3b32b3d1b31f957d2ed57d69bc0c503bbdf2a4cf Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 5 Aug 2021 12:21:58 +1000 Subject: [PATCH 170/264] stm32/usbd_cdc_interface: Rename USBD_CDC_xx opts to MICROPY_HW_USB_xx. Signed-off-by: Damien George --- .../boards/B_L072Z_LRWAN1/mpconfigboard.h | 4 ++-- .../stm32/boards/NUCLEO_L432KC/mpconfigboard.h | 4 ++-- ports/stm32/boards/NUCLEO_WB55/mpconfigboard.h | 4 ++-- .../boards/USBDONGLE_WB55/mpconfigboard.h | 4 ++-- ports/stm32/mpconfigboard_common.h | 16 +++++++++++++++- ports/stm32/usbd_cdc_interface.c | 18 +++++++++--------- ports/stm32/usbd_cdc_interface.h | 15 +++++---------- 7 files changed, 37 insertions(+), 28 deletions(-) diff --git a/ports/stm32/boards/B_L072Z_LRWAN1/mpconfigboard.h b/ports/stm32/boards/B_L072Z_LRWAN1/mpconfigboard.h index 0cded1bc86..08ded0e03e 100644 --- a/ports/stm32/boards/B_L072Z_LRWAN1/mpconfigboard.h +++ b/ports/stm32/boards/B_L072Z_LRWAN1/mpconfigboard.h @@ -67,5 +67,5 @@ #define MICROPY_HW_USB_FS (1) #define MICROPY_HW_USB_MSC (0) #define MICROPY_HW_USB_HID (0) -#define USBD_CDC_RX_DATA_SIZE (256) -#define USBD_CDC_TX_DATA_SIZE (256) +#define MICROPY_HW_USB_CDC_RX_DATA_SIZE (256) +#define MICROPY_HW_USB_CDC_TX_DATA_SIZE (256) diff --git a/ports/stm32/boards/NUCLEO_L432KC/mpconfigboard.h b/ports/stm32/boards/NUCLEO_L432KC/mpconfigboard.h index 0f1134491f..8b3a8f38b1 100644 --- a/ports/stm32/boards/NUCLEO_L432KC/mpconfigboard.h +++ b/ports/stm32/boards/NUCLEO_L432KC/mpconfigboard.h @@ -67,5 +67,5 @@ #define MICROPY_HW_USB_FS (MICROPY_HW_ENABLE_USB) #define MICROPY_HW_USB_MSC (0) #define MICROPY_HW_USB_HID (0) -#define USBD_CDC_RX_DATA_SIZE (256) -#define USBD_CDC_TX_DATA_SIZE (256) +#define MICROPY_HW_USB_CDC_RX_DATA_SIZE (256) +#define MICROPY_HW_USB_CDC_TX_DATA_SIZE (256) diff --git a/ports/stm32/boards/NUCLEO_WB55/mpconfigboard.h b/ports/stm32/boards/NUCLEO_WB55/mpconfigboard.h index 179369d943..a7473b9d6a 100644 --- a/ports/stm32/boards/NUCLEO_WB55/mpconfigboard.h +++ b/ports/stm32/boards/NUCLEO_WB55/mpconfigboard.h @@ -59,8 +59,8 @@ // USB config #define MICROPY_HW_USB_FS (1) -#define USBD_CDC_RX_DATA_SIZE (512) -#define USBD_CDC_TX_DATA_SIZE (512) +#define MICROPY_HW_USB_CDC_RX_DATA_SIZE (512) +#define MICROPY_HW_USB_CDC_TX_DATA_SIZE (512) // Bluetooth config #define MICROPY_HW_BLE_UART_ID (0) diff --git a/ports/stm32/boards/USBDONGLE_WB55/mpconfigboard.h b/ports/stm32/boards/USBDONGLE_WB55/mpconfigboard.h index fdb061900b..d4b12ae5bf 100644 --- a/ports/stm32/boards/USBDONGLE_WB55/mpconfigboard.h +++ b/ports/stm32/boards/USBDONGLE_WB55/mpconfigboard.h @@ -43,8 +43,8 @@ // USB config #define MICROPY_HW_USB_FS (1) -#define USBD_CDC_RX_DATA_SIZE (512) -#define USBD_CDC_TX_DATA_SIZE (512) +#define MICROPY_HW_USB_CDC_RX_DATA_SIZE (512) +#define MICROPY_HW_USB_CDC_TX_DATA_SIZE (512) // Bluetooth config #define MICROPY_HW_BLE_UART_ID (0) diff --git a/ports/stm32/mpconfigboard_common.h b/ports/stm32/mpconfigboard_common.h index 24c5e1a3cd..0cf6adce33 100644 --- a/ports/stm32/mpconfigboard_common.h +++ b/ports/stm32/mpconfigboard_common.h @@ -189,7 +189,9 @@ || defined(USBD_CONFIGURATION_HS_STRING) \ || defined(USBD_INTERFACE_HS_STRING) \ || defined(USBD_CONFIGURATION_FS_STRING) \ - || defined(USBD_INTERFACE_FS_STRING) + || defined(USBD_INTERFACE_FS_STRING) \ + || defined(USBD_CDC_RX_DATA_SIZE) \ + || defined(USBD_CDC_TX_DATA_SIZE) #error "Old USBD_xxx configuration option used, renamed to MICROPY_HW_USB_xxx" #endif @@ -244,6 +246,18 @@ #define MICROPY_HW_USB_INTERFACE_FS_STRING "Pyboard Interface" #endif +// Amount of incoming buffer space for each CDC instance. +// This must be 2 or greater, and a power of 2. +#ifndef MICROPY_HW_USB_CDC_RX_DATA_SIZE +#define MICROPY_HW_USB_CDC_RX_DATA_SIZE (1024) +#endif + +// Amount of outgoing buffer space for each CDC instance. +// This must be a power of 2 and no greater than 16384. +#ifndef MICROPY_HW_USB_CDC_TX_DATA_SIZE +#define MICROPY_HW_USB_CDC_TX_DATA_SIZE (1024) +#endif + /*****************************************************************************/ // General configuration diff --git a/ports/stm32/usbd_cdc_interface.c b/ports/stm32/usbd_cdc_interface.c index 5f27bbe29a..61a7c82486 100644 --- a/ports/stm32/usbd_cdc_interface.c +++ b/ports/stm32/usbd_cdc_interface.c @@ -172,7 +172,7 @@ int8_t usbd_cdc_control(usbd_cdc_state_t *cdc_in, uint8_t cmd, uint8_t *pbuf, ui } static inline uint16_t usbd_cdc_tx_buffer_mask(uint16_t val) { - return val & (USBD_CDC_TX_DATA_SIZE - 1); + return val & (MICROPY_HW_USB_CDC_TX_DATA_SIZE - 1); } static inline uint16_t usbd_cdc_tx_buffer_size(usbd_cdc_itf_t *cdc) { @@ -188,18 +188,18 @@ static inline bool usbd_cdc_tx_buffer_will_be_empty(usbd_cdc_itf_t *cdc) { } static inline bool usbd_cdc_tx_buffer_full(usbd_cdc_itf_t *cdc) { - return usbd_cdc_tx_buffer_size(cdc) == USBD_CDC_TX_DATA_SIZE; + return usbd_cdc_tx_buffer_size(cdc) == MICROPY_HW_USB_CDC_TX_DATA_SIZE; } static uint16_t usbd_cdc_tx_send_length(usbd_cdc_itf_t *cdc) { - uint16_t to_end = USBD_CDC_TX_DATA_SIZE - usbd_cdc_tx_buffer_mask(cdc->tx_buf_ptr_out); + uint16_t to_end = MICROPY_HW_USB_CDC_TX_DATA_SIZE - usbd_cdc_tx_buffer_mask(cdc->tx_buf_ptr_out); return MIN(usbd_cdc_tx_buffer_size(cdc), to_end); } static void usbd_cdc_tx_buffer_put(usbd_cdc_itf_t *cdc, uint8_t data, bool check_overflow) { cdc->tx_buf[usbd_cdc_tx_buffer_mask(cdc->tx_buf_ptr_in)] = data; cdc->tx_buf_ptr_in++; - if (check_overflow && usbd_cdc_tx_buffer_size(cdc) > USBD_CDC_TX_DATA_SIZE) { + if (check_overflow && usbd_cdc_tx_buffer_size(cdc) > MICROPY_HW_USB_CDC_TX_DATA_SIZE) { cdc->tx_buf_ptr_out++; cdc->tx_buf_ptr_out_next = cdc->tx_buf_ptr_out; } @@ -270,7 +270,7 @@ void HAL_PCD_SOFCallback(PCD_HandleTypeDef *hpcd) { bool usbd_cdc_rx_buffer_full(usbd_cdc_itf_t *cdc) { int get = cdc->rx_buf_get, put = cdc->rx_buf_put; - int remaining = (get - put) + (-((int)(get <= put)) & USBD_CDC_RX_DATA_SIZE); + int remaining = (get - put) + (-((int)(get <= put)) & MICROPY_HW_USB_CDC_RX_DATA_SIZE); return remaining < CDC_DATA_MAX_PACKET_SIZE + 1; } @@ -298,7 +298,7 @@ int8_t usbd_cdc_receive(usbd_cdc_state_t *cdc_in, size_t len) { if (cdc->attached_to_repl && *src == mp_interrupt_char) { pendsv_kbd_intr(); } else { - uint16_t next_put = (cdc->rx_buf_put + 1) & (USBD_CDC_RX_DATA_SIZE - 1); + uint16_t next_put = (cdc->rx_buf_put + 1) & (MICROPY_HW_USB_CDC_RX_DATA_SIZE - 1); if (next_put == cdc->rx_buf_get) { // overflow, we just discard the rest of the chars break; @@ -322,7 +322,7 @@ int8_t usbd_cdc_receive(usbd_cdc_state_t *cdc_in, size_t len) { int usbd_cdc_tx_half_empty(usbd_cdc_itf_t *cdc) { int32_t tx_waiting = usbd_cdc_tx_buffer_size(cdc); - return tx_waiting <= USBD_CDC_TX_DATA_SIZE / 2; + return tx_waiting <= MICROPY_HW_USB_CDC_TX_DATA_SIZE / 2; } // Writes only the data that fits if flow & CTS, else writes all data @@ -405,7 +405,7 @@ void usbd_cdc_tx_always(usbd_cdc_itf_t *cdc, const uint8_t *buf, uint32_t len) { int usbd_cdc_rx_num(usbd_cdc_itf_t *cdc) { int32_t rx_waiting = (int32_t)cdc->rx_buf_put - (int32_t)cdc->rx_buf_get; if (rx_waiting < 0) { - rx_waiting += USBD_CDC_RX_DATA_SIZE; + rx_waiting += MICROPY_HW_USB_CDC_RX_DATA_SIZE; } usbd_cdc_rx_check_resume(cdc); return rx_waiting; @@ -434,7 +434,7 @@ int usbd_cdc_rx(usbd_cdc_itf_t *cdc, uint8_t *buf, uint32_t len, uint32_t timeou // Copy byte from device to user buffer buf[i] = cdc->rx_user_buf[cdc->rx_buf_get]; - cdc->rx_buf_get = (cdc->rx_buf_get + 1) & (USBD_CDC_RX_DATA_SIZE - 1); + cdc->rx_buf_get = (cdc->rx_buf_get + 1) & (MICROPY_HW_USB_CDC_RX_DATA_SIZE - 1); } usbd_cdc_rx_check_resume(cdc); diff --git a/ports/stm32/usbd_cdc_interface.h b/ports/stm32/usbd_cdc_interface.h index 6b510f2399..76b5c9e79b 100644 --- a/ports/stm32/usbd_cdc_interface.h +++ b/ports/stm32/usbd_cdc_interface.h @@ -31,12 +31,7 @@ ****************************************************************************** */ -#ifndef USBD_CDC_RX_DATA_SIZE -#define USBD_CDC_RX_DATA_SIZE (1024) // this must be 2 or greater, and a power of 2 -#endif -#ifndef USBD_CDC_TX_DATA_SIZE -#define USBD_CDC_TX_DATA_SIZE (1024) // This must be a power of 2 and no greater than 16384 -#endif +#include "py/mpconfig.h" // Values for connect_state #define USBD_CDC_CONNECT_STATE_DISCONNECTED (0) @@ -52,14 +47,14 @@ typedef struct _usbd_cdc_itf_t { usbd_cdc_state_t base; // state for the base CDC layer uint8_t rx_packet_buf[CDC_DATA_MAX_PACKET_SIZE]; // received data from USB OUT endpoint is stored in this buffer - uint8_t rx_user_buf[USBD_CDC_RX_DATA_SIZE]; // received data is buffered here until the user reads it + uint8_t rx_user_buf[MICROPY_HW_USB_CDC_RX_DATA_SIZE]; // received data is buffered here until the user reads it volatile uint16_t rx_buf_put; // circular buffer index uint16_t rx_buf_get; // circular buffer index uint8_t rx_buf_full; // rx from host will be blocked while this is true - uint8_t tx_buf[USBD_CDC_TX_DATA_SIZE]; // data for USB IN endpoind is stored in this buffer - uint16_t tx_buf_ptr_in; // increment this pointer modulo USBD_CDC_TX_DATA_SIZE when new data is available - volatile uint16_t tx_buf_ptr_out; // increment this pointer modulo USBD_CDC_TX_DATA_SIZE when data is drained + uint8_t tx_buf[MICROPY_HW_USB_CDC_TX_DATA_SIZE]; // data for USB IN endpoind is stored in this buffer + uint16_t tx_buf_ptr_in; // increment this pointer modulo MICROPY_HW_USB_CDC_TX_DATA_SIZE when new data is available + volatile uint16_t tx_buf_ptr_out; // increment this pointer modulo MICROPY_HW_USB_CDC_TX_DATA_SIZE when data is drained uint16_t tx_buf_ptr_out_next; // next position of above once transmission finished uint8_t tx_need_empty_packet; // used to flush the USB IN endpoint if the last packet was exactly the endpoint packet size From f834fef6bbd28e31d8e5c04dc2092886b162fe89 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 22 Jul 2021 15:29:14 +1000 Subject: [PATCH 171/264] stm32/powerctrl: Support changing frequency on WB MCUs. This allows changing the frequency to: 100kHz, 200kHz, 400kHz, 800kHz, 1MHz, 2MHz, 4MHz, 8MHz, 16MHz, 32MHz, 64MHz. For frequencies 2MHz and below, low power run (LPR) mode is enabled automatically. Signed-off-by: Damien George --- ports/stm32/Makefile | 3 +- ports/stm32/powerctrl.c | 99 +++++++++++++++++++++++++++++++++++-- ports/stm32/powerctrl.h | 1 + ports/stm32/powerctrlboot.c | 2 +- 4 files changed, 100 insertions(+), 5 deletions(-) diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index fd76f20a6d..cdccfddcfd 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -100,7 +100,7 @@ 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) +CFLAGS += -D$(CMSIS_MCU) -DUSE_FULL_LL_DRIVER CFLAGS += $(CFLAGS_MCU_$(MCU_SERIES)) CFLAGS += $(COPT) CFLAGS += -I$(BOARD_DIR) @@ -396,6 +396,7 @@ HAL_SRC_C += $(addprefix $(HAL_DIR)/Src/stm32$(MCU_SERIES)xx_,\ hal_tim.c \ hal_tim_ex.c \ hal_uart.c \ + ll_utils.c \ ) ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f4 f7 h7 l0 l4 wb)) diff --git a/ports/stm32/powerctrl.c b/ports/stm32/powerctrl.c index 253f1056ca..137312ade8 100644 --- a/ports/stm32/powerctrl.c +++ b/ports/stm32/powerctrl.c @@ -519,10 +519,103 @@ set_clk: #elif defined(STM32WB) +#include "stm32wbxx_ll_utils.h" + +#define LPR_THRESHOLD (2000000) +#define VOS2_THRESHOLD (16000000) + +enum { + SYSCLK_MODE_NONE, + SYSCLK_MODE_MSI, + SYSCLK_MODE_HSE_64M, +}; + int powerctrl_set_sysclk(uint32_t sysclk, uint32_t ahb, uint32_t apb1, uint32_t apb2) { - // For now it's not supported to change SYSCLK (only bus dividers). - if (sysclk != HAL_RCC_GetSysClockFreq()) { - return -MP_EINVAL; + int sysclk_mode = SYSCLK_MODE_NONE; + uint32_t msirange = 0; + uint32_t sysclk_cur = HAL_RCC_GetSysClockFreq(); + + if (sysclk == sysclk_cur) { + // SYSCLK does not need changing. + } else if (sysclk == 64000000) { + sysclk_mode = SYSCLK_MODE_HSE_64M; + } else { + for (msirange = 0; msirange < MP_ARRAY_SIZE(MSIRangeTable); ++msirange) { + if (MSIRangeTable[msirange] != 0 && sysclk == MSIRangeTable[msirange]) { + sysclk_mode = SYSCLK_MODE_MSI; + break; + } + } + + if (sysclk_mode == SYSCLK_MODE_NONE) { + // Unsupported SYSCLK value. + return -MP_EINVAL; + } + } + + // Exit LPR if SYSCLK will increase beyond threshold. + if (LL_PWR_IsEnabledLowPowerRunMode()) { + if (sysclk > LPR_THRESHOLD) { + if (sysclk_cur < LPR_THRESHOLD) { + // Must select MSI=LPR_THRESHOLD=2MHz to exit LPR. + LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_5); + } + + // Exit LPR and wait for the regulator to be ready. + LL_PWR_ExitLowPowerRunMode(); + while (!LL_PWR_IsActiveFlag_REGLPF()) { + } + } + } + + // Select VOS1 if SYSCLK will increase beyond threshold. + if (sysclk > VOS2_THRESHOLD) { + LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1); + while (LL_PWR_IsActiveFlag_VOS()) { + } + } + + if (sysclk_mode == SYSCLK_MODE_HSE_64M) { + SystemClock_Config(); + } 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) { + } + + // Before changing the MSIRANGE value, if MSI is on then it must also be ready. + while ((RCC->CR & (RCC_CR_MSIRDY | RCC_CR_MSION)) == RCC_CR_MSION) { + } + LL_RCC_MSI_SetRange(msirange << RCC_CR_MSIRANGE_Pos); + + // Clock SYSCLK from MSI. + LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_MSI); + while (LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_MSI) { + } + + // Disable PLL to decrease power consumption. + LL_RCC_PLL_Disable(); + while (LL_RCC_PLL_IsReady() != 0) { + } + LL_RCC_PLL_DisableDomain_SYS(); + + // Select VOS2 if possible. + if (sysclk <= VOS2_THRESHOLD) { + LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE2); + } + + // Enter LPR if possible. + if (sysclk <= LPR_THRESHOLD) { + LL_PWR_EnterLowPowerRunMode(); + } + + // Configure flash latency for the new SYSCLK. + LL_SetFlashLatency(sysclk); + + // Update HAL state and SysTick. + SystemCoreClockUpdate(); + powerctrl_config_systick(); } // Return straightaway if the clocks are already at the desired frequency. diff --git a/ports/stm32/powerctrl.h b/ports/stm32/powerctrl.h index 9f223e794a..eedc448b2b 100644 --- a/ports/stm32/powerctrl.h +++ b/ports/stm32/powerctrl.h @@ -35,6 +35,7 @@ NORETURN void powerctrl_mcu_reset(void); NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr); void powerctrl_check_enter_bootloader(void); +void powerctrl_config_systick(void); int powerctrl_rcc_clock_config_pll(RCC_ClkInitTypeDef *rcc_init, uint32_t sysclk_mhz, bool need_pllsai); int powerctrl_set_sysclk(uint32_t sysclk, uint32_t ahb, uint32_t apb1, uint32_t apb2); void powerctrl_enter_stop_mode(void); diff --git a/ports/stm32/powerctrlboot.c b/ports/stm32/powerctrlboot.c index 880e43e04c..caa5632926 100644 --- a/ports/stm32/powerctrlboot.c +++ b/ports/stm32/powerctrlboot.c @@ -28,7 +28,7 @@ #include "irq.h" #include "powerctrl.h" -static inline void powerctrl_config_systick(void) { +void powerctrl_config_systick(void) { // Configure SYSTICK to run at 1kHz (1ms interval) SysTick->CTRL |= SYSTICK_CLKSOURCE_HCLK; SysTick_Config(HAL_RCC_GetHCLKFreq() / 1000); From 3b594f7b27881818645a52d7aa9469c97ef10ecf Mon Sep 17 00:00:00 2001 From: Glenn Ruben Bakke Date: Sun, 13 Dec 2020 22:03:18 +0100 Subject: [PATCH 172/264] nrf/boards/common.ld: Calculate unused flash region. Calculate the unused flash area on the target device. The values will be exposed by _unused_flash_start and _unused_flash_length. The start address and the length are not aligned to either word or pages. --- ports/nrf/boards/common.ld | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ports/nrf/boards/common.ld b/ports/nrf/boards/common.ld index d2e5eef762..866336fa02 100644 --- a/ports/nrf/boards/common.ld +++ b/ports/nrf/boards/common.ld @@ -81,3 +81,5 @@ SECTIONS /* Define heap and stack areas */ _ram_end = ORIGIN(RAM) + LENGTH(RAM); _estack = ORIGIN(RAM) + LENGTH(RAM); +_unused_flash_start = (_sidata + (_edata - _sdata)); +_unused_flash_len = (ORIGIN(FLASH_TEXT) + LENGTH(FLASH_TEXT)) - _unused_flash_start; From 127cec8cae3ae486acefb4dbf71b37181bd9ed5d Mon Sep 17 00:00:00 2001 From: Glenn Ruben Bakke Date: Wed, 22 Jul 2020 13:58:32 +0200 Subject: [PATCH 173/264] nrf/modules/nrf: Add new nrf module with flash block device. This commit adds the "nrf" module for port specific modules and objects. Included in it is the "Flash" object which exposes a block device implementation to access internal SoC flash. Thanks to @aykevl aka Ayke van Laethem for the initial implementation. --- ports/nrf/Makefile | 3 + ports/nrf/modules/nrf/flashbdev.c | 205 ++++++++++++++++++++++++++++++ ports/nrf/modules/nrf/flashbdev.h | 39 ++++++ ports/nrf/modules/nrf/modnrf.c | 70 ++++++++++ 4 files changed, 317 insertions(+) create mode 100644 ports/nrf/modules/nrf/flashbdev.c create mode 100644 ports/nrf/modules/nrf/flashbdev.h create mode 100644 ports/nrf/modules/nrf/modnrf.c diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index 7428ab82c0..ad099e1105 100644 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -70,6 +70,7 @@ INC += -I./modules/ubluepy INC += -I./modules/music INC += -I./modules/ble INC += -I./modules/board +INC += -I./modules/nrf INC += -I../../shared/readline INC += -I./drivers/bluetooth INC += -I./drivers @@ -322,6 +323,8 @@ DRIVERS_SRC_C += $(addprefix modules/,\ music/modmusic.c \ music/musictunes.c \ ble/modble.c \ + nrf/modnrf.c \ + nrf/flashbdev.c \ ) # Custom micropython startup file with smaller interrupt vector table diff --git a/ports/nrf/modules/nrf/flashbdev.c b/ports/nrf/modules/nrf/flashbdev.c new file mode 100644 index 0000000000..7a1296c5d1 --- /dev/null +++ b/ports/nrf/modules/nrf/flashbdev.c @@ -0,0 +1,205 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Ayke van Laethem + * Copyright (c) 2019 Glenn Ruben Bakke + * + * 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 "py/runtime.h" +#include "py/mperrno.h" + +#if MICROPY_PY_NRF && MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE + +#include "flashbdev.h" +#include "flash.h" +#include "extmod/vfs.h" + +extern byte _fs_start[]; +extern byte _fs_end[]; + +#define FLASH_BLOCK_START (((uint32_t)_fs_start + (FLASH_PAGESIZE - 1)) / FLASH_PAGESIZE) + +typedef struct _nrf_flash_obj_t { + mp_obj_base_t base; + uint32_t start; // in bytes + uint32_t len; // in bytes +} nrf_flash_obj_t; + +// This flash object represents the flash region defined by _fs_start and _fs_end +// in memory.ld. The start/len entries will be filled in by flashbdev_init(). +nrf_flash_obj_t nrf_flash_obj = { + { &nrf_flashbdev_type }, + 0, + 0, +}; + +mp_obj_t nrf_flashbdev_readblocks(size_t n_args, const mp_obj_t *args) { + nrf_flash_obj_t *self = MP_OBJ_TO_PTR(args[0]); + uint32_t block_num = mp_obj_get_int(args[1]); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_WRITE); + + mp_int_t address = self->start + (block_num * FLASH_PAGESIZE); + + if (n_args == 4) { + uint32_t offset = mp_obj_get_int(args[3]); + address += offset; + } + + byte *buf = bufinfo.buf; + byte *p = (byte *)address; + memcpy(buf, p, bufinfo.len); + + return MP_OBJ_NEW_SMALL_INT(0); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(nrf_flashbdev_readblocks_obj, 3, 4, nrf_flashbdev_readblocks); + +mp_obj_t nrf_flashbdev_writeblocks(size_t n_args, const mp_obj_t *args) { + nrf_flash_obj_t *self = MP_OBJ_TO_PTR(args[0]); + uint32_t block_num = mp_obj_get_int(args[1]); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_WRITE); + + mp_int_t address = self->start + (block_num * FLASH_PAGESIZE); + + if (n_args == 4) { + uint32_t offset = mp_obj_get_int(args[3]); + address += offset; + } else { + flash_page_erase(address); + } + + flash_write_bytes(address, bufinfo.buf, bufinfo.len); + + return MP_OBJ_NEW_SMALL_INT(0); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(nrf_flashbdev_writeblocks_obj, 3, 4, nrf_flashbdev_writeblocks); + +mp_obj_t nrf_flashbdev_ioctl(mp_obj_t self_in, mp_obj_t op_in, mp_obj_t arg_in) { + nrf_flash_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_int_t op = mp_obj_get_int(op_in); + switch (op) { + case MP_BLOCKDEV_IOCTL_INIT: { + return MP_OBJ_NEW_SMALL_INT(0); + } + + case MP_BLOCKDEV_IOCTL_DEINIT: { + return MP_OBJ_NEW_SMALL_INT(0); + } + + 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->len / FLASH_PAGESIZE); + } + + case MP_BLOCKDEV_IOCTL_BLOCK_SIZE: { + return MP_OBJ_NEW_SMALL_INT(FLASH_PAGESIZE); + } + + case MP_BLOCKDEV_IOCTL_BLOCK_ERASE: { + mp_int_t block_num = mp_obj_get_int(arg_in); + mp_int_t address = self->start + (block_num * FLASH_PAGESIZE); + + if ((address & 0x3) || (address % FLASH_PAGESIZE != 0)) { + return MP_OBJ_NEW_SMALL_INT(-MP_EIO); + } + + flash_page_erase(address); + return MP_OBJ_NEW_SMALL_INT(0); + } + + default: + return mp_const_none; + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(nrf_flashbdev_ioctl_obj, nrf_flashbdev_ioctl); + +STATIC const mp_rom_map_elem_t nrf_flashbdev_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&nrf_flashbdev_readblocks_obj) }, + { MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&nrf_flashbdev_writeblocks_obj) }, + { MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&nrf_flashbdev_ioctl_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(nrf_flashbdev_locals_dict, nrf_flashbdev_locals_dict_table); + +STATIC void nrf_flashbdev_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + nrf_flash_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "Flash(start=0x%08x, len=%u)", self->start, self->len); +} + +STATIC mp_obj_t nrf_flashbdev_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + // Parse arguments + enum { ARG_start, ARG_len }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_len, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + }; + 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); + + if (args[ARG_start].u_int == -1 && args[ARG_len].u_int == -1) { + return MP_OBJ_FROM_PTR(&nrf_flash_obj); + } + + nrf_flash_obj_t *self = m_new_obj(nrf_flash_obj_t); + self->base.type = &nrf_flashbdev_type; + + mp_int_t start = args[ARG_start].u_int; + mp_int_t len = args[ARG_len].u_int; + + if ((start == -1) || (start % FLASH_PAGESIZE != 0)) { + mp_raise_ValueError(NULL); + } + if ((len == -1) || (len % FLASH_PAGESIZE != 0)) { + mp_raise_ValueError(NULL); + } + + self->start = start; + self->len = len; + + return MP_OBJ_FROM_PTR(self); +} + +const mp_obj_type_t nrf_flashbdev_type = { + { &mp_type_type }, + .name = MP_QSTR_Flash, + .print = nrf_flashbdev_print, + .make_new = nrf_flashbdev_make_new, + .locals_dict = (mp_obj_dict_t *)&nrf_flashbdev_locals_dict, +}; + +void flashbdev_init(void) { + // Set start to first aligned page from _fs_start. + mp_int_t page_start = FLASH_BLOCK_START; + nrf_flash_obj.start = page_start * FLASH_PAGESIZE; + // Trim len to only count whole pages. + mp_int_t page_end = (uint32_t)_fs_end / FLASH_PAGESIZE; + mp_int_t num_pages = page_end - page_start; + nrf_flash_obj.len = num_pages * FLASH_PAGESIZE; +} + +#endif // MICROPY_PY_NRF && MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE diff --git a/ports/nrf/modules/nrf/flashbdev.h b/ports/nrf/modules/nrf/flashbdev.h new file mode 100644 index 0000000000..29d7a12edd --- /dev/null +++ b/ports/nrf/modules/nrf/flashbdev.h @@ -0,0 +1,39 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Ayke van Laethem + * Copyright (c) 2019 Glenn Ruben Bakke + * + * 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_NRF_FLASHBDEV_H +#define MICROPY_INCLUDED_NRF_FLASHBDEV_H + +#include "py/obj.h" +#include "extmod/vfs_fat.h" + +extern const struct _mp_obj_type_t nrf_flashbdev_type; +extern struct _nrf_flash_obj_t nrf_flash_obj; + +void flashbdev_init(void); + +#endif // MICROPY_INCLUDED_NRF_FLASHBDEV_H diff --git a/ports/nrf/modules/nrf/modnrf.c b/ports/nrf/modules/nrf/modnrf.c new file mode 100644 index 0000000000..fd955dc62f --- /dev/null +++ b/ports/nrf/modules/nrf/modnrf.c @@ -0,0 +1,70 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Ayke van Laethem + * Copyright (c) 2019 Glenn Ruben Bakke + * + * 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" + +#if MICROPY_PY_NRF + +#if MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE +#include "flashbdev.h" +#include "flash.h" + +#define FLASH_PAGE_ALIGN_UP(addr) (((addr) - 1) + (FLASH_PAGESIZE)-(((addr) - 1) % (FLASH_PAGESIZE))) + +extern uint32_t _unused_flash_start; +extern uint32_t _unused_flash_len; + +mp_obj_t nrf_modnrf_freeflash_start_aligned(void) { + return mp_obj_new_int_from_uint(FLASH_PAGE_ALIGN_UP((uint32_t)&_unused_flash_start)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(nrf_modnrf_freeflash_start_aligned_obj, nrf_modnrf_freeflash_start_aligned); + +mp_obj_t nrf_modnrf_freeflash_length_aligned(void) { + uint32_t align_diff = FLASH_PAGE_ALIGN_UP((uint32_t)&_unused_flash_start) - ((uint32_t)&_unused_flash_start); + uint32_t temp_len = ((uint32_t)&_unused_flash_len) - align_diff; + uint32_t len_page_aligned = (temp_len / FLASH_PAGESIZE) * FLASH_PAGESIZE; + return mp_obj_new_int_from_uint(len_page_aligned); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(nrf_modnrf_freeflash_length_aligned_obj, nrf_modnrf_freeflash_length_aligned); +#endif + +STATIC const mp_rom_map_elem_t nrf_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_nrf) }, + #if MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE + { MP_ROM_QSTR(MP_QSTR_Flash), MP_ROM_PTR(&nrf_flashbdev_type) }, + { MP_ROM_QSTR(MP_QSTR_unused_flash_start), MP_ROM_PTR(&nrf_modnrf_freeflash_start_aligned_obj) }, + { MP_ROM_QSTR(MP_QSTR_unused_flash_length), MP_ROM_PTR(&nrf_modnrf_freeflash_length_aligned_obj) }, + #endif +}; +STATIC MP_DEFINE_CONST_DICT(nrf_module_globals, nrf_module_globals_table); + +const mp_obj_module_t nrf_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&nrf_module_globals, +}; + +#endif // MICROPY_PY_NRF From 5a873e27eb5d03f6d5cfedab2dbf3532ac8ac849 Mon Sep 17 00:00:00 2001 From: Glenn Ruben Bakke Date: Thu, 23 Jul 2020 10:56:00 +0200 Subject: [PATCH 174/264] nrf/drivers: Add support for using flash block device with SoftDevice. Update flash.c to also be compiled in when MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE is enabled and SoftDevice is present. Update bluetooth/ble_drv.c to forward flash events to flash.c when MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE is enabled. --- ports/nrf/drivers/bluetooth/ble_drv.c | 2 +- ports/nrf/drivers/flash.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ports/nrf/drivers/bluetooth/ble_drv.c b/ports/nrf/drivers/bluetooth/ble_drv.c index 3a15025cb4..0040580933 100644 --- a/ports/nrf/drivers/bluetooth/ble_drv.c +++ b/ports/nrf/drivers/bluetooth/ble_drv.c @@ -929,7 +929,7 @@ void ble_drv_discover_descriptors(void) { static void sd_evt_handler(uint32_t evt_id) { switch (evt_id) { -#if MICROPY_MBFS +#if MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE || MICROPY_MBFS case NRF_EVT_FLASH_OPERATION_SUCCESS: flash_operation_finished(FLASH_STATE_SUCCESS); break; diff --git a/ports/nrf/drivers/flash.c b/ports/nrf/drivers/flash.c index 5a7256a0c6..85e5f71d63 100644 --- a/ports/nrf/drivers/flash.c +++ b/ports/nrf/drivers/flash.c @@ -26,7 +26,7 @@ #include "py/mpconfig.h" -#if MICROPY_MBFS && BLUETOOTH_SD +#if (MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE || MICROPY_MBFS) && BLUETOOTH_SD #include "drivers/flash.h" #include "drivers/bluetooth/ble_drv.h" @@ -129,4 +129,4 @@ void flash_write_bytes(uint32_t dst, const uint8_t *src, uint32_t num_bytes) { } } -#endif // MICROPY_MBFS +#endif // (MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE || MICROPY_MBFS) && BLUETOOTH_SD From 85cad502660e8d1359f175402aaea98695b3521f Mon Sep 17 00:00:00 2001 From: Glenn Ruben Bakke Date: Thu, 23 Jul 2020 12:12:24 +0200 Subject: [PATCH 175/264] nrf/mpconfigport.h: Expose nrf module when MICROPY_PY_NRF is set. --- ports/nrf/mpconfigport.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/ports/nrf/mpconfigport.h b/ports/nrf/mpconfigport.h index 183fdf6818..d1e9665dee 100644 --- a/ports/nrf/mpconfigport.h +++ b/ports/nrf/mpconfigport.h @@ -179,6 +179,10 @@ #define MICROPY_PY_TIME_TICKS (1) #endif +#ifndef MICROPY_PY_NRF +#define MICROPY_PY_NRF (0) +#endif + #define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1) #define MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE (0) @@ -212,11 +216,18 @@ 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; extern const struct _mp_obj_module_t mp_module_ubluepy; extern const struct _mp_obj_module_t music_module; +#if MICROPY_PY_NRF +#define NRF_MODULE { MP_ROM_QSTR(MP_QSTR_nrf), MP_ROM_PTR(&nrf_module) }, +#else +#define NRF_MODULE +#endif + #if MICROPY_PY_UBLUEPY #define UBLUEPY_MODULE { MP_ROM_QSTR(MP_QSTR_ubluepy), MP_ROM_PTR(&mp_module_ubluepy) }, #else @@ -255,6 +266,7 @@ extern const struct _mp_obj_module_t ble_module; MUSIC_MODULE \ UBLUEPY_MODULE \ MICROPY_BOARD_BUILTINS \ + NRF_MODULE \ #else @@ -266,6 +278,7 @@ extern const struct _mp_obj_module_t ble_module; { MP_ROM_QSTR(MP_QSTR_uos), MP_ROM_PTR(&mp_module_uos) }, \ MUSIC_MODULE \ MICROPY_BOARD_BUILTINS \ + NRF_MODULE \ #endif // BLUETOOTH_SD From b40dfa961dcd9c3504f742c212248444b68413b3 Mon Sep 17 00:00:00 2001 From: Glenn Ruben Bakke Date: Thu, 23 Jul 2020 12:29:10 +0200 Subject: [PATCH 176/264] nrf/README: Update README.md to reflect internal file systems. This documents parameters that can be passed to make to enable a specific file system to included in the build. Also, document the Makefile override parameter "FS_SIZE" that can be used to tune the size of the flash region to use as internal flash file system. --- ports/nrf/README.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/ports/nrf/README.md b/ports/nrf/README.md index 0341cd81cd..a4509cc6e1 100644 --- a/ports/nrf/README.md +++ b/ports/nrf/README.md @@ -117,6 +117,34 @@ For example: make BOARD=pca10040 MICROPY_VFS_FAT=1 +## Enable MICROPY_VFS_LFS1 or MICROPY_VFS_LFS2 + +In order to enable `littlefs` as device flash filesystem, `MICROPY_VFS_LFS1` +or `MICROPY_VFS_LFS2` can be set. This will be in addition of setting +`MICROPY_VFS` in mpconfigport.h or mpconfigboard.h. + +For example: + + make BOARD=pca10056 MICROPY_VFS_LFS2=1 + +## Set file system size + +The size of the file system on the internal flash is configured by the linker +script parameter `_fs_size`. This can either be overriden by the linker script +or dynamically through the makefile. By seting a value to the `FS_SIZE`. +The number will be passed directly to the linker scripts in order to calculate +the start and end of the file system. Note that the parameter value must be in +linker script syntax as it is passed directly. + +For example, if we want to override the default file system size set by the +linker scripts to use 256K: + + make BOARD=pca10056 MICROPY_VFS_LFS2=1 FS_SIZE=256K + +Also note that changing this size between builds might cause loss of files +present from a previous firmware as it will format the file system due to a new +location. + ## Target Boards and Make Flags Target Board (BOARD) | Bluetooth Stack (SD) | Bluetooth Support | Bootloader | Default Flash Util From c9b72ba69418eb91595698182022f64c24d64556 Mon Sep 17 00:00:00 2001 From: Glenn Ruben Bakke Date: Thu, 23 Jul 2020 19:34:21 +0200 Subject: [PATCH 177/264] nrf/mpconfigport.h: Tune FAT FS configuration. Disable MICROPY_FATFS_MULTI_PARTITION configuration because there is no partition table in the flash for FATFS to read. Also, set MICROPY_FATFS_MAX_SS to the size of a flash page. For nrf51 the value 1024 is set. For nrf52/nrf91 the value 4096 is set. --- ports/nrf/mpconfigport.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ports/nrf/mpconfigport.h b/ports/nrf/mpconfigport.h index d1e9665dee..92b87a036b 100644 --- a/ports/nrf/mpconfigport.h +++ b/ports/nrf/mpconfigport.h @@ -75,7 +75,13 @@ #define MICROPY_FATFS_LFN_CODE_PAGE 437 /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */ #define MICROPY_FATFS_USE_LABEL (1) #define MICROPY_FATFS_RPATH (2) -#define MICROPY_FATFS_MULTI_PARTITION (1) +#define MICROPY_FATFS_MULTI_PARTITION (0) + +#if NRF51 + #define MICROPY_FATFS_MAX_SS (1024) +#else + #define MICROPY_FATFS_MAX_SS (4096) +#endif // TODO these should be generic, not bound to fatfs #define mp_type_fileio fatfs_type_fileio From 0bde907a8b010e0b7a6436f3840f7350455bcd01 Mon Sep 17 00:00:00 2001 From: Glenn Ruben Bakke Date: Thu, 23 Jul 2020 20:57:29 +0200 Subject: [PATCH 178/264] nrf/Makefile: Add _fs_size linker script override from make. Add posibility to override linker script "_fs_size" from make by adding the FS_SIZE parameter. The syntax of value is linker script syntax. For example, the value of 131072 bytes can be written as 128K like this: FS_SIZE=128K. If not set, default value for "_fs_size" from linker script will be used. --- ports/nrf/Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index ad099e1105..fb7eb5c591 100644 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -130,6 +130,10 @@ LDFLAGS = $(CFLAGS) LDFLAGS += -Xlinker -Map=$(@:.elf=.map) LDFLAGS += -mthumb -mabi=aapcs $(addprefix -T,$(LD_FILES)) -L boards/ +ifneq ($(FS_SIZE),) +LDFLAGS += -Wl,'--defsym=_fs_size=$(FS_SIZE)' +endif + #Debugging/Optimization ifeq ($(DEBUG), 1) #ASMFLAGS += -g -gtabs+ From 7a833edf37ed9714abdf5b7cf96d615fe877f3b9 Mon Sep 17 00:00:00 2001 From: Glenn Ruben Bakke Date: Thu, 23 Jul 2020 21:08:25 +0200 Subject: [PATCH 179/264] nrf/modules/uos: Allow a board to configure MICROPY_VFS_FAT/LFS1/LFS2. --- ports/nrf/modules/uos/moduos.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ports/nrf/modules/uos/moduos.c b/ports/nrf/modules/uos/moduos.c index 98a5661f4f..fd5334d004 100644 --- a/ports/nrf/modules/uos/moduos.c +++ b/ports/nrf/modules/uos/moduos.c @@ -36,6 +36,7 @@ #include "modules/uos/microbitfs.h" #include "extmod/vfs.h" #include "extmod/vfs_fat.h" +#include "extmod/vfs_lfs.h" #include "genhdr/mpversion.h" #include "uart.h" @@ -85,10 +86,12 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(os_uname_obj, os_uname); /// \function sync() /// Sync all filesystems. STATIC mp_obj_t os_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 disk_ioctl(MP_OBJ_TO_PTR(vfs->obj), CTRL_SYNC, NULL); } + #endif return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_0(mod_os_sync_obj, os_sync); @@ -174,7 +177,15 @@ STATIC const mp_rom_map_elem_t os_module_globals_table[] = { #if MICROPY_VFS { 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 }; From f99aa82e850a32caee2ef864da673e9c5c529054 Mon Sep 17 00:00:00 2001 From: Glenn Ruben Bakke Date: Thu, 23 Jul 2020 21:12:34 +0200 Subject: [PATCH 180/264] nrf/mpconfigport.h: Enable MICROPY_PY_IO_FILEIO when an FS is enabled. Enable MICROPY_PY_IO_FILEIO if MICROPY_VFS_FAT, MICROPY_VFS_LFS1 or MICROPY_VFS2 has been enabled. --- ports/nrf/mpconfigport.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/nrf/mpconfigport.h b/ports/nrf/mpconfigport.h index 92b87a036b..480368aaa1 100644 --- a/ports/nrf/mpconfigport.h +++ b/ports/nrf/mpconfigport.h @@ -126,7 +126,7 @@ #define MICROPY_PY_MATH_SPECIAL_FUNCTIONS (0) #define MICROPY_PY_CMATH (0) #define MICROPY_PY_IO (0) -#define MICROPY_PY_IO_FILEIO (0) +#define MICROPY_PY_IO_FILEIO (MICROPY_VFS_FAT || MICROPY_VFS_LFS1 || MICROPY_VFS_LFS2) #define MICROPY_PY_URANDOM (1) #define MICROPY_PY_URANDOM_EXTRA_FUNCS (1) #define MICROPY_PY_UCTYPES (0) From 6ff3a2afef3263153d50c73ea5c0d941dc701a9d Mon Sep 17 00:00:00 2001 From: Glenn Ruben Bakke Date: Thu, 23 Jul 2020 21:15:05 +0200 Subject: [PATCH 181/264] nrf/qstrdefsport.h: Add entries for in-built FS mount points. --- ports/nrf/qstrdefsport.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ports/nrf/qstrdefsport.h b/ports/nrf/qstrdefsport.h index 9608dbb247..3cc2f1e646 100644 --- a/ports/nrf/qstrdefsport.h +++ b/ports/nrf/qstrdefsport.h @@ -27,6 +27,12 @@ // qstrs specific to this port // *FORMAT-OFF* +// Entries for sys.path +Q(/flash) + +// For uos.sep +Q(/) + Q(a) Q(a#) Q(a#:1) @@ -138,4 +144,3 @@ Q(r4:2) Q(r:1) Q(r:2) Q(r:3) - From b0fd4372c4cb8fce22b6e90eb4deb5c7e34b9c21 Mon Sep 17 00:00:00 2001 From: Glenn Ruben Bakke Date: Thu, 23 Jul 2020 21:21:58 +0200 Subject: [PATCH 182/264] nrf/main: Add auto mount and auto format hook for internal flash FS. --- ports/nrf/main.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/ports/nrf/main.c b/ports/nrf/main.c index 3de5a310f0..254d9491c0 100644 --- a/ports/nrf/main.c +++ b/ports/nrf/main.c @@ -76,6 +76,13 @@ #include "usb_cdc.h" #endif +#if MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE +#include "extmod/vfs_fat.h" +#include "lib/oofatfs/ff.h" +#include "extmod/vfs.h" +#include "flashbdev.h" +#endif + void do_str(const char *src, mp_parse_input_kind_t input_kind) { mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0); if (lex == NULL) { @@ -99,6 +106,28 @@ void do_str(const char *src, mp_parse_input_kind_t input_kind) { extern uint32_t _heap_start; extern uint32_t _heap_end; +#if MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE +STATIC int vfs_mount_and_chdir(mp_obj_t bdev, mp_obj_t mount_point) { + nlr_buf_t nlr; + mp_int_t ret = -MP_EIO; + if (nlr_push(&nlr) == 0) { + mp_obj_t args[] = { bdev, mount_point }; + mp_vfs_mount(2, args, (mp_map_t *)&mp_const_empty_map); + mp_vfs_chdir(mount_point); + ret = 0; // success + nlr_pop(); + } else { + mp_obj_base_t *exc = nlr.ret_val; + if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(exc->type), MP_OBJ_FROM_PTR(&mp_type_OSError))) { + mp_obj_t v = mp_obj_exception_get_value(MP_OBJ_FROM_PTR(exc)); + mp_obj_get_int_maybe(v, &ret); // get errno value + ret = -ret; + } + } + return ret; +} +#endif + int main(int argc, char **argv) { @@ -169,6 +198,23 @@ soft_reset: pin_init0(); + #if MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE + flashbdev_init(); + + // Try to mount the flash on "/flash" and chdir to it for the boot-up directory. + mp_obj_t mount_point = MP_OBJ_NEW_QSTR(MP_QSTR__slash_flash); + int ret = vfs_mount_and_chdir((mp_obj_t)&nrf_flash_obj, mount_point); + + if ((ret == -MP_ENODEV) || (ret == -MP_EIO)) { + pyexec_frozen_module("_mkfs.py"); // Frozen script for formatting flash filesystem. + ret = vfs_mount_and_chdir((mp_obj_t)&nrf_flash_obj, mount_point); + } + + if (ret != 0) { + printf("MPY: can't mount flash\n"); + } + #endif + #if MICROPY_MBFS microbit_filesystem_init(); #endif From 4326e0880273431d5ee8fcc0b9b081e848fdc736 Mon Sep 17 00:00:00 2001 From: Glenn Ruben Bakke Date: Thu, 23 Jul 2020 21:25:50 +0200 Subject: [PATCH 183/264] nrf/boards: Enable needed features for FAT/LFS1/LFS2. Enable the following features for all boards except nrf51 boards with SoftDevice present: - MICROPY_VFS - MICROPY_PY_NRF - MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE --- ports/nrf/mpconfigdevice_nrf51822.h | 28 ++++++++++++++++++++++++++++ ports/nrf/mpconfigdevice_nrf52832.h | 16 ++++++++++++++-- ports/nrf/mpconfigdevice_nrf52840.h | 16 ++++++++++++++-- ports/nrf/mpconfigdevice_nrf9160.h | 16 ++++++++++++++-- 4 files changed, 70 insertions(+), 6 deletions(-) diff --git a/ports/nrf/mpconfigdevice_nrf51822.h b/ports/nrf/mpconfigdevice_nrf51822.h index 2f85c9f4c5..d10e91a79e 100644 --- a/ports/nrf/mpconfigdevice_nrf51822.h +++ b/ports/nrf/mpconfigdevice_nrf51822.h @@ -27,11 +27,19 @@ // Board overridable build configuration. #ifndef MICROPY_MBFS +#if defined(BLUETOOTH_SD) #define MICROPY_MBFS (1) +#else +#define MICROPY_MBFS (0) +#endif #endif #ifndef MICROPY_VFS +#if defined(BLUETOOTH_SD) #define MICROPY_VFS (0) +#else +#define MICROPY_VFS (1) +#endif #endif // Board overridable feature configuration. @@ -59,3 +67,23 @@ #define MICROPY_PY_UBINASCII (1) #endif #endif + +// Board overridable port specific feature configuration. + +#ifndef MICROPY_PY_NRF +#if defined(BLUETOOTH_SD) +#define MICROPY_PY_NRF (0) +#else +#define MICROPY_PY_NRF (1) +#endif +#endif + +// Board overridable hardware configuration. + +#ifndef MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE +#if defined(BLUETOOTH_SD) +#define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (0) +#else +#define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (1) +#endif +#endif diff --git a/ports/nrf/mpconfigdevice_nrf52832.h b/ports/nrf/mpconfigdevice_nrf52832.h index 2bfd047ca1..cc7bcbe9b4 100644 --- a/ports/nrf/mpconfigdevice_nrf52832.h +++ b/ports/nrf/mpconfigdevice_nrf52832.h @@ -27,11 +27,11 @@ // Board overridable build configuration. #ifndef MICROPY_MBFS -#define MICROPY_MBFS (1) +#define MICROPY_MBFS (0) #endif #ifndef MICROPY_VFS -#define MICROPY_VFS (0) +#define MICROPY_VFS (1) #endif // Board overridable feature configuration. @@ -47,3 +47,15 @@ #ifndef MICROPY_PY_UBINASCII #define MICROPY_PY_UBINASCII (1) #endif + +// Board overridable port specific feature configuration. + +#ifndef MICROPY_PY_NRF +#define MICROPY_PY_NRF (1) +#endif + +// Board overridable hardware configuration. + +#ifndef MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE +#define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (1) +#endif diff --git a/ports/nrf/mpconfigdevice_nrf52840.h b/ports/nrf/mpconfigdevice_nrf52840.h index 2bfd047ca1..cc7bcbe9b4 100644 --- a/ports/nrf/mpconfigdevice_nrf52840.h +++ b/ports/nrf/mpconfigdevice_nrf52840.h @@ -27,11 +27,11 @@ // Board overridable build configuration. #ifndef MICROPY_MBFS -#define MICROPY_MBFS (1) +#define MICROPY_MBFS (0) #endif #ifndef MICROPY_VFS -#define MICROPY_VFS (0) +#define MICROPY_VFS (1) #endif // Board overridable feature configuration. @@ -47,3 +47,15 @@ #ifndef MICROPY_PY_UBINASCII #define MICROPY_PY_UBINASCII (1) #endif + +// Board overridable port specific feature configuration. + +#ifndef MICROPY_PY_NRF +#define MICROPY_PY_NRF (1) +#endif + +// Board overridable hardware configuration. + +#ifndef MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE +#define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (1) +#endif diff --git a/ports/nrf/mpconfigdevice_nrf9160.h b/ports/nrf/mpconfigdevice_nrf9160.h index 2bfd047ca1..cc7bcbe9b4 100644 --- a/ports/nrf/mpconfigdevice_nrf9160.h +++ b/ports/nrf/mpconfigdevice_nrf9160.h @@ -27,11 +27,11 @@ // Board overridable build configuration. #ifndef MICROPY_MBFS -#define MICROPY_MBFS (1) +#define MICROPY_MBFS (0) #endif #ifndef MICROPY_VFS -#define MICROPY_VFS (0) +#define MICROPY_VFS (1) #endif // Board overridable feature configuration. @@ -47,3 +47,15 @@ #ifndef MICROPY_PY_UBINASCII #define MICROPY_PY_UBINASCII (1) #endif + +// Board overridable port specific feature configuration. + +#ifndef MICROPY_PY_NRF +#define MICROPY_PY_NRF (1) +#endif + +// Board overridable hardware configuration. + +#ifndef MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE +#define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (1) +#endif From 990341d18e72636c300e1880dd11500c07bd9f46 Mon Sep 17 00:00:00 2001 From: Glenn Ruben Bakke Date: Wed, 4 Dec 2019 07:37:19 +0100 Subject: [PATCH 184/264] nrf: Facilitate use of freeze manifest. Update the Makefile to handle FROZEN_MANIFEST, and the README with some small samples on how to use freeze manifests. And add BOARD_DIR to the Makefile which can be referenced in boards//mpconfigboard.mk to include a board specific manifest. --- ports/nrf/Makefile | 6 ++++-- ports/nrf/README.md | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index fb7eb5c591..0e46885ea0 100644 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -5,6 +5,8 @@ ifeq ($(wildcard boards/$(BOARD)/.),) $(error Invalid BOARD specified) endif +BOARD_DIR ?= boards/$(BOARD) + # If SoftDevice is selected, try to use that one. SD ?= SD_LOWER = $(shell echo $(SD) | tr '[:upper:]' '[:lower:]') @@ -537,13 +539,13 @@ GEN_PINS_QSTR = $(BUILD)/pins_qstr.h GEN_PINS_AF_CONST = $(HEADER_BUILD)/pins_af_const.h GEN_PINS_AF_PY = $(BUILD)/pins_af.py -ifneq ($(FROZEN_DIR),) +ifneq ($(FROZEN_MANIFEST)$(FROZEN_DIR),) # To use frozen source modules, put your .py files in a subdirectory (eg scripts/) # and then invoke make with FROZEN_DIR=scripts (be sure to build from scratch). CFLAGS += -DMICROPY_MODULE_FROZEN_STR endif -ifneq ($(FROZEN_MPY_DIR),) +ifneq ($(FROZEN_MANIFEST)$(FROZEN_MPY_DIR),) # To use frozen bytecode, put your .py files in a subdirectory (eg frozen/) and # then invoke make with FROZEN_MPY_DIR=frozen (be sure to build from scratch). CFLAGS += -DMICROPY_QSTR_EXTRA_POOL=mp_qstr_frozen_const_pool diff --git a/ports/nrf/README.md b/ports/nrf/README.md index a4509cc6e1..aa8968ff92 100644 --- a/ports/nrf/README.md +++ b/ports/nrf/README.md @@ -110,6 +110,22 @@ To use frozen modules, put them in a directory (e.g. `freeze/`) and supply make BOARD=pca10040 FROZEN_MPY_DIR=freeze +## Compile with freeze manifest + +Freeze manifests can be used by definining `FROZEN_MANIFEST` pointing to a +`manifest.py`. This can either be done by a `make` invocation or by defining +it in the specific target board's `mpconfigboard.mk`. + +For example: + + make BOARD=pca10040 FROZEN_MANIFEST=path/to/manifest.py + +In case of using the target board's makefile, add a line similar to this: + + FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py + +In these two examples, the manual `make` invocation will have precedence. + ## Enable MICROPY_VFS_FAT As the `oofatfs` module is not having header guards that can exclude the implementation compile time, this port provides a flag to enable it explicitly. The MICROPY_VFS_FAT is by default set to 0 and has to be set to 1 if `oofatfs` files should be compiled. This will be in addition of setting `MICROPY_VFS` in mpconfigport.h. From 4f76f6618507c01a067d69cc5b9934fafe261e18 Mon Sep 17 00:00:00 2001 From: Glenn Ruben Bakke Date: Wed, 9 Dec 2020 21:00:34 +0100 Subject: [PATCH 185/264] tools/ci.sh: Add mpy-cross build to nrf port. --- tools/ci.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/ci.sh b/tools/ci.sh index efc68c03e4..4c216257d2 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -189,6 +189,7 @@ function ci_nrf_setup { function ci_nrf_build { ports/nrf/drivers/bluetooth/download_ble_stack.sh s140_nrf52_6_1_1 + make ${MAKEOPTS} -C mpy-cross make ${MAKEOPTS} -C ports/nrf submodules make ${MAKEOPTS} -C ports/nrf BOARD=pca10040 make ${MAKEOPTS} -C ports/nrf BOARD=microbit From ffc636de2f2e91e1fecfca1b4b2691a59d83a8eb Mon Sep 17 00:00:00 2001 From: Glenn Ruben Bakke Date: Thu, 23 Jul 2020 23:11:14 +0200 Subject: [PATCH 186/264] nrf/boards: Set FROZEN_MANIFEST blank when SD present on nrf51 targets. --- ports/nrf/boards/microbit/mpconfigboard.mk | 3 +++ ports/nrf/boards/pca10000/mpconfigboard.mk | 4 ++++ ports/nrf/boards/pca10001/mpconfigboard.mk | 4 ++++ ports/nrf/boards/pca10028/mpconfigboard.mk | 4 ++++ ports/nrf/boards/pca10031/mpconfigboard.mk | 4 ++++ ports/nrf/boards/wt51822_s4at/mpconfigboard.mk | 4 ++++ 6 files changed, 23 insertions(+) diff --git a/ports/nrf/boards/microbit/mpconfigboard.mk b/ports/nrf/boards/microbit/mpconfigboard.mk index 96f430071b..d96044195e 100644 --- a/ports/nrf/boards/microbit/mpconfigboard.mk +++ b/ports/nrf/boards/microbit/mpconfigboard.mk @@ -2,9 +2,12 @@ MCU_SERIES = m0 MCU_VARIANT = nrf51 MCU_SUB_VARIANT = nrf51822 SOFTDEV_VERSION = 8.0.0 + ifneq ($(SD),) LD_FILES += boards/microbit/custom_nrf51822_s110_microbit.ld +FROZEN_MANIFEST ?= endif + LD_FILES += boards/nrf51x22_256k_16k.ld FLASHER = pyocd diff --git a/ports/nrf/boards/pca10000/mpconfigboard.mk b/ports/nrf/boards/pca10000/mpconfigboard.mk index c0cef5f3af..52b8df88b7 100644 --- a/ports/nrf/boards/pca10000/mpconfigboard.mk +++ b/ports/nrf/boards/pca10000/mpconfigboard.mk @@ -3,3 +3,7 @@ MCU_VARIANT = nrf51 MCU_SUB_VARIANT = nrf51822 SOFTDEV_VERSION = 8.0.0 LD_FILES += boards/nrf51x22_256k_16k.ld + +ifneq ($(SD),) +FROZEN_MANIFEST ?= +endif diff --git a/ports/nrf/boards/pca10001/mpconfigboard.mk b/ports/nrf/boards/pca10001/mpconfigboard.mk index c0cef5f3af..52b8df88b7 100644 --- a/ports/nrf/boards/pca10001/mpconfigboard.mk +++ b/ports/nrf/boards/pca10001/mpconfigboard.mk @@ -3,3 +3,7 @@ MCU_VARIANT = nrf51 MCU_SUB_VARIANT = nrf51822 SOFTDEV_VERSION = 8.0.0 LD_FILES += boards/nrf51x22_256k_16k.ld + +ifneq ($(SD),) +FROZEN_MANIFEST ?= +endif diff --git a/ports/nrf/boards/pca10028/mpconfigboard.mk b/ports/nrf/boards/pca10028/mpconfigboard.mk index b3c8f21ea9..779276ca1c 100644 --- a/ports/nrf/boards/pca10028/mpconfigboard.mk +++ b/ports/nrf/boards/pca10028/mpconfigboard.mk @@ -3,3 +3,7 @@ MCU_VARIANT = nrf51 MCU_SUB_VARIANT = nrf51822 SOFTDEV_VERSION = 8.0.0 LD_FILES += boards/nrf51x22_256k_32k.ld + +ifneq ($(SD),) +FROZEN_MANIFEST ?= +endif diff --git a/ports/nrf/boards/pca10031/mpconfigboard.mk b/ports/nrf/boards/pca10031/mpconfigboard.mk index b3c8f21ea9..779276ca1c 100644 --- a/ports/nrf/boards/pca10031/mpconfigboard.mk +++ b/ports/nrf/boards/pca10031/mpconfigboard.mk @@ -3,3 +3,7 @@ MCU_VARIANT = nrf51 MCU_SUB_VARIANT = nrf51822 SOFTDEV_VERSION = 8.0.0 LD_FILES += boards/nrf51x22_256k_32k.ld + +ifneq ($(SD),) +FROZEN_MANIFEST ?= +endif diff --git a/ports/nrf/boards/wt51822_s4at/mpconfigboard.mk b/ports/nrf/boards/wt51822_s4at/mpconfigboard.mk index 515de07f5b..61c36f21d9 100644 --- a/ports/nrf/boards/wt51822_s4at/mpconfigboard.mk +++ b/ports/nrf/boards/wt51822_s4at/mpconfigboard.mk @@ -5,3 +5,7 @@ SOFTDEV_VERSION = 8.0.0 LD_FILES += boards/nrf51x22_256k_16k.ld CFLAGS += -DBLUETOOTH_LFCLK_RC + +ifneq ($(SD),) +FROZEN_MANIFEST ?= +endif From 7b6ad0ce2e6c3c623966c5568989ed5042fb9b1d Mon Sep 17 00:00:00 2001 From: Glenn Ruben Bakke Date: Fri, 24 Jul 2020 00:38:33 +0200 Subject: [PATCH 187/264] nrf/modules/scripts: Add file system formatting script. Add a helper script _mkfs.py which automatically formats the file system if nrf.Flash() is located and a VFS file system has been included in the compilation. The precedence is: first LFS1, LFS2 then FAT. --- ports/nrf/modules/scripts/_mkfs.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 ports/nrf/modules/scripts/_mkfs.py diff --git a/ports/nrf/modules/scripts/_mkfs.py b/ports/nrf/modules/scripts/_mkfs.py new file mode 100644 index 0000000000..00522ffbb9 --- /dev/null +++ b/ports/nrf/modules/scripts/_mkfs.py @@ -0,0 +1,23 @@ +import uos, nrf + +try: + from uos import VfsLfs1 + + uos.VfsLfs1.mkfs(nrf.Flash()) +except ImportError: + try: + from uos import VfsLfs2 + + uos.VfsLfs2.mkfs(nrf.Flash()) + except ImportError: + try: + from uos import VfsFat + + uos.VfsFat.mkfs(nrf.Flash()) + except ImportError: + pass + except OSError as e: + if e.args[0] == 5: # I/O Error + flashbdev_size = (nrf.Flash.ioctl(4, 0) * nrf.Flash.ioctl(5, 0)) // 1024 + print() + print("Is `FS_SIZE=%iK` enough for FAT filesystem?" % flashbdev_size) From aa857eb65e57a935ec3757abc35b007996433298 Mon Sep 17 00:00:00 2001 From: Glenn Ruben Bakke Date: Thu, 23 Jul 2020 23:12:51 +0200 Subject: [PATCH 188/264] nrf/Makefile: Set default manifest file for all targets. Set the default manifest to "modules/manifest.py". This includes files from the folder "modules/scripts". The manifest default value is overriden by all nrf51 boards that have SoftDevice present (SD=s110) to save flash. Also add "modules/manifest.py" which is set to freeze "modules/scripts/_mkfs.py". --- ports/nrf/Makefile | 2 ++ ports/nrf/modules/manifest.py | 1 + 2 files changed, 3 insertions(+) create mode 100644 ports/nrf/modules/manifest.py diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index 0e46885ea0..a8a5228270 100644 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -52,6 +52,8 @@ ifeq ($(DEBUG), 0) MICROPY_ROM_TEXT_COMPRESSION ?= 1 endif +FROZEN_MANIFEST ?= modules/manifest.py + # include py core make definitions include ../../py/py.mk diff --git a/ports/nrf/modules/manifest.py b/ports/nrf/modules/manifest.py new file mode 100644 index 0000000000..4e8482226b --- /dev/null +++ b/ports/nrf/modules/manifest.py @@ -0,0 +1 @@ +freeze("$(PORT_DIR)/modules/scripts", "_mkfs.py") From 23e8729d3e256a38ae4bbe68c8aa66a40e31e2e8 Mon Sep 17 00:00:00 2001 From: Glenn Ruben Bakke Date: Wed, 9 Dec 2020 21:01:28 +0100 Subject: [PATCH 189/264] nrf/mphalport: Add dummy function for mp_hal_time_ns(). extmod/vfs_lfs.c needs to resolve `mp_hal_time_ns()` in order to calculate a timestamp from 1970 epoch. A wall clock is not available in the nrf port, hence the function is implemented to resolve compilation linkage error. The function always return 0. --- ports/nrf/mphalport.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ports/nrf/mphalport.c b/ports/nrf/mphalport.c index b8e4c2e4d8..8ffb256018 100644 --- a/ports/nrf/mphalport.c +++ b/ports/nrf/mphalport.c @@ -150,6 +150,10 @@ mp_uint_t mp_hal_ticks_ms(void) { #endif +uint64_t mp_hal_time_ns(void) { + return 0; +} + // this table converts from HAL_StatusTypeDef to POSIX errno const byte mp_hal_status_to_errno_table[4] = { [HAL_OK] = 0, From 55d4321c3eb1a99b7f135ca930878d0ce97620e6 Mon Sep 17 00:00:00 2001 From: Glenn Ruben Bakke Date: Thu, 23 Jul 2020 23:37:25 +0200 Subject: [PATCH 190/264] nrf/boards: Enable MICROPY_VFS_LFS2 for all target boards. Enable LittleFS v2 for all targets, except nrf51 targets when SoftDevice is present. --- ports/nrf/boards/actinius_icarus/mpconfigboard.mk | 2 ++ ports/nrf/boards/arduino_primo/mpconfigboard.mk | 2 ++ ports/nrf/boards/blueio_tag_evim/mpconfigboard.mk | 2 ++ ports/nrf/boards/dvk_bl652/mpconfigboard.mk | 2 ++ ports/nrf/boards/evk_nina_b1/mpconfigboard.mk | 2 ++ ports/nrf/boards/feather52/mpconfigboard.mk | 1 + ports/nrf/boards/ibk_blyst_nano/mpconfigboard.mk | 2 ++ ports/nrf/boards/idk_blyst_nano/mpconfigboard.mk | 2 ++ ports/nrf/boards/microbit/mpconfigboard.mk | 2 ++ ports/nrf/boards/nrf52840-mdk-usb-dongle/mpconfigboard.mk | 2 ++ ports/nrf/boards/particle_xenon/mpconfigboard.mk | 2 ++ ports/nrf/boards/pca10000/mpconfigboard.mk | 2 ++ ports/nrf/boards/pca10001/mpconfigboard.mk | 2 ++ ports/nrf/boards/pca10028/mpconfigboard.mk | 2 ++ ports/nrf/boards/pca10031/mpconfigboard.mk | 2 ++ ports/nrf/boards/pca10040/mpconfigboard.mk | 2 ++ ports/nrf/boards/pca10056/mpconfigboard.mk | 2 ++ ports/nrf/boards/pca10059/mpconfigboard.mk | 2 ++ ports/nrf/boards/pca10090/mpconfigboard.mk | 2 ++ ports/nrf/boards/wt51822_s4at/mpconfigboard.mk | 2 ++ 20 files changed, 39 insertions(+) diff --git a/ports/nrf/boards/actinius_icarus/mpconfigboard.mk b/ports/nrf/boards/actinius_icarus/mpconfigboard.mk index c1e05fd306..8e6f75b36c 100644 --- a/ports/nrf/boards/actinius_icarus/mpconfigboard.mk +++ b/ports/nrf/boards/actinius_icarus/mpconfigboard.mk @@ -4,3 +4,5 @@ MCU_SUB_VARIANT = nrf9160 LD_FILES += boards/nrf9160_1M_256k.ld NRF_DEFINES += -DNRF9160_XXAA -DNRF_TRUSTZONE_NONSECURE + +MICROPY_VFS_LFS2 = 1 diff --git a/ports/nrf/boards/arduino_primo/mpconfigboard.mk b/ports/nrf/boards/arduino_primo/mpconfigboard.mk index 84685236bd..e814d3d550 100644 --- a/ports/nrf/boards/arduino_primo/mpconfigboard.mk +++ b/ports/nrf/boards/arduino_primo/mpconfigboard.mk @@ -6,3 +6,5 @@ LD_FILES += boards/nrf52832_512k_64k.ld FLASHER = pyocd NRF_DEFINES += -DNRF52832_XXAA + +MICROPY_VFS_LFS2 = 1 diff --git a/ports/nrf/boards/blueio_tag_evim/mpconfigboard.mk b/ports/nrf/boards/blueio_tag_evim/mpconfigboard.mk index 02a3177446..c3164c8f08 100644 --- a/ports/nrf/boards/blueio_tag_evim/mpconfigboard.mk +++ b/ports/nrf/boards/blueio_tag_evim/mpconfigboard.mk @@ -6,3 +6,5 @@ LD_FILES += boards/nrf52832_512k_64k.ld FLASHER = idap NRF_DEFINES += -DNRF52832_XXAA + +MICROPY_VFS_LFS2 = 1 diff --git a/ports/nrf/boards/dvk_bl652/mpconfigboard.mk b/ports/nrf/boards/dvk_bl652/mpconfigboard.mk index 8730d02978..30767d766a 100644 --- a/ports/nrf/boards/dvk_bl652/mpconfigboard.mk +++ b/ports/nrf/boards/dvk_bl652/mpconfigboard.mk @@ -6,3 +6,5 @@ LD_FILES += boards/nrf52832_512k_64k.ld NRF_DEFINES += -DNRF52832_XXAA CFLAGS += -DBLUETOOTH_LFCLK_RC + +MICROPY_VFS_LFS2 = 1 diff --git a/ports/nrf/boards/evk_nina_b1/mpconfigboard.mk b/ports/nrf/boards/evk_nina_b1/mpconfigboard.mk index db64a60dd3..96245e3e45 100644 --- a/ports/nrf/boards/evk_nina_b1/mpconfigboard.mk +++ b/ports/nrf/boards/evk_nina_b1/mpconfigboard.mk @@ -5,3 +5,5 @@ SOFTDEV_VERSION = 6.1.1 LD_FILES += boards/nrf52832_512k_64k.ld NRF_DEFINES += -DNRF52832_XXAA + +MICROPY_VFS_LFS2 = 1 diff --git a/ports/nrf/boards/feather52/mpconfigboard.mk b/ports/nrf/boards/feather52/mpconfigboard.mk index 5e0f336da4..96245e3e45 100644 --- a/ports/nrf/boards/feather52/mpconfigboard.mk +++ b/ports/nrf/boards/feather52/mpconfigboard.mk @@ -6,3 +6,4 @@ LD_FILES += boards/nrf52832_512k_64k.ld NRF_DEFINES += -DNRF52832_XXAA +MICROPY_VFS_LFS2 = 1 diff --git a/ports/nrf/boards/ibk_blyst_nano/mpconfigboard.mk b/ports/nrf/boards/ibk_blyst_nano/mpconfigboard.mk index 02a3177446..c3164c8f08 100644 --- a/ports/nrf/boards/ibk_blyst_nano/mpconfigboard.mk +++ b/ports/nrf/boards/ibk_blyst_nano/mpconfigboard.mk @@ -6,3 +6,5 @@ LD_FILES += boards/nrf52832_512k_64k.ld FLASHER = idap NRF_DEFINES += -DNRF52832_XXAA + +MICROPY_VFS_LFS2 = 1 diff --git a/ports/nrf/boards/idk_blyst_nano/mpconfigboard.mk b/ports/nrf/boards/idk_blyst_nano/mpconfigboard.mk index 02a3177446..c3164c8f08 100644 --- a/ports/nrf/boards/idk_blyst_nano/mpconfigboard.mk +++ b/ports/nrf/boards/idk_blyst_nano/mpconfigboard.mk @@ -6,3 +6,5 @@ LD_FILES += boards/nrf52832_512k_64k.ld FLASHER = idap NRF_DEFINES += -DNRF52832_XXAA + +MICROPY_VFS_LFS2 = 1 diff --git a/ports/nrf/boards/microbit/mpconfigboard.mk b/ports/nrf/boards/microbit/mpconfigboard.mk index d96044195e..6fdd2bc500 100644 --- a/ports/nrf/boards/microbit/mpconfigboard.mk +++ b/ports/nrf/boards/microbit/mpconfigboard.mk @@ -6,6 +6,8 @@ SOFTDEV_VERSION = 8.0.0 ifneq ($(SD),) LD_FILES += boards/microbit/custom_nrf51822_s110_microbit.ld FROZEN_MANIFEST ?= +else +MICROPY_VFS_LFS2 = 1 endif LD_FILES += boards/nrf51x22_256k_16k.ld diff --git a/ports/nrf/boards/nrf52840-mdk-usb-dongle/mpconfigboard.mk b/ports/nrf/boards/nrf52840-mdk-usb-dongle/mpconfigboard.mk index ca437418d5..3a26a92b7d 100644 --- a/ports/nrf/boards/nrf52840-mdk-usb-dongle/mpconfigboard.mk +++ b/ports/nrf/boards/nrf52840-mdk-usb-dongle/mpconfigboard.mk @@ -17,3 +17,5 @@ endif LD_FILES += boards/nrf52840_1M_256k.ld NRF_DEFINES += -DNRF52840_XXAA + +MICROPY_VFS_LFS2 = 1 diff --git a/ports/nrf/boards/particle_xenon/mpconfigboard.mk b/ports/nrf/boards/particle_xenon/mpconfigboard.mk index 34bead59ff..d28324f76e 100644 --- a/ports/nrf/boards/particle_xenon/mpconfigboard.mk +++ b/ports/nrf/boards/particle_xenon/mpconfigboard.mk @@ -9,3 +9,5 @@ NRF_DEFINES += -DNRF52840_XXAA # The nrf52-particle.cfg is not included here, it can be found in the Particle Workbench # Note: This requires openocd >0.10 OPENOCD_TARGET ?= boards/$(BOARD)/nrf52-particle.cfg + +MICROPY_VFS_LFS2 = 1 diff --git a/ports/nrf/boards/pca10000/mpconfigboard.mk b/ports/nrf/boards/pca10000/mpconfigboard.mk index 52b8df88b7..64cf513c04 100644 --- a/ports/nrf/boards/pca10000/mpconfigboard.mk +++ b/ports/nrf/boards/pca10000/mpconfigboard.mk @@ -6,4 +6,6 @@ LD_FILES += boards/nrf51x22_256k_16k.ld ifneq ($(SD),) FROZEN_MANIFEST ?= +else +MICROPY_VFS_LFS2 = 1 endif diff --git a/ports/nrf/boards/pca10001/mpconfigboard.mk b/ports/nrf/boards/pca10001/mpconfigboard.mk index 52b8df88b7..64cf513c04 100644 --- a/ports/nrf/boards/pca10001/mpconfigboard.mk +++ b/ports/nrf/boards/pca10001/mpconfigboard.mk @@ -6,4 +6,6 @@ LD_FILES += boards/nrf51x22_256k_16k.ld ifneq ($(SD),) FROZEN_MANIFEST ?= +else +MICROPY_VFS_LFS2 = 1 endif diff --git a/ports/nrf/boards/pca10028/mpconfigboard.mk b/ports/nrf/boards/pca10028/mpconfigboard.mk index 779276ca1c..83b26a1dea 100644 --- a/ports/nrf/boards/pca10028/mpconfigboard.mk +++ b/ports/nrf/boards/pca10028/mpconfigboard.mk @@ -6,4 +6,6 @@ LD_FILES += boards/nrf51x22_256k_32k.ld ifneq ($(SD),) FROZEN_MANIFEST ?= +else +MICROPY_VFS_LFS2 = 1 endif diff --git a/ports/nrf/boards/pca10031/mpconfigboard.mk b/ports/nrf/boards/pca10031/mpconfigboard.mk index 779276ca1c..83b26a1dea 100644 --- a/ports/nrf/boards/pca10031/mpconfigboard.mk +++ b/ports/nrf/boards/pca10031/mpconfigboard.mk @@ -6,4 +6,6 @@ LD_FILES += boards/nrf51x22_256k_32k.ld ifneq ($(SD),) FROZEN_MANIFEST ?= +else +MICROPY_VFS_LFS2 = 1 endif diff --git a/ports/nrf/boards/pca10040/mpconfigboard.mk b/ports/nrf/boards/pca10040/mpconfigboard.mk index db64a60dd3..96245e3e45 100644 --- a/ports/nrf/boards/pca10040/mpconfigboard.mk +++ b/ports/nrf/boards/pca10040/mpconfigboard.mk @@ -5,3 +5,5 @@ SOFTDEV_VERSION = 6.1.1 LD_FILES += boards/nrf52832_512k_64k.ld NRF_DEFINES += -DNRF52832_XXAA + +MICROPY_VFS_LFS2 = 1 diff --git a/ports/nrf/boards/pca10056/mpconfigboard.mk b/ports/nrf/boards/pca10056/mpconfigboard.mk index ca555d3932..d8ee0f8a21 100644 --- a/ports/nrf/boards/pca10056/mpconfigboard.mk +++ b/ports/nrf/boards/pca10056/mpconfigboard.mk @@ -5,3 +5,5 @@ SOFTDEV_VERSION = 6.1.1 LD_FILES += boards/nrf52840_1M_256k.ld NRF_DEFINES += -DNRF52840_XXAA + +MICROPY_VFS_LFS2 = 1 diff --git a/ports/nrf/boards/pca10059/mpconfigboard.mk b/ports/nrf/boards/pca10059/mpconfigboard.mk index ca437418d5..3a26a92b7d 100644 --- a/ports/nrf/boards/pca10059/mpconfigboard.mk +++ b/ports/nrf/boards/pca10059/mpconfigboard.mk @@ -17,3 +17,5 @@ endif LD_FILES += boards/nrf52840_1M_256k.ld NRF_DEFINES += -DNRF52840_XXAA + +MICROPY_VFS_LFS2 = 1 diff --git a/ports/nrf/boards/pca10090/mpconfigboard.mk b/ports/nrf/boards/pca10090/mpconfigboard.mk index 9363900e2b..cc7e7c3683 100644 --- a/ports/nrf/boards/pca10090/mpconfigboard.mk +++ b/ports/nrf/boards/pca10090/mpconfigboard.mk @@ -4,3 +4,5 @@ MCU_SUB_VARIANT = nrf9160 LD_FILES += boards/nrf9160_1M_256k.ld NRF_DEFINES += -DNRF9160_XXAA -DNRF_TRUSTZONE_NONSECURE + +MICROPY_VFS_LFS2 = 1 diff --git a/ports/nrf/boards/wt51822_s4at/mpconfigboard.mk b/ports/nrf/boards/wt51822_s4at/mpconfigboard.mk index 61c36f21d9..d0de24ba40 100644 --- a/ports/nrf/boards/wt51822_s4at/mpconfigboard.mk +++ b/ports/nrf/boards/wt51822_s4at/mpconfigboard.mk @@ -8,4 +8,6 @@ CFLAGS += -DBLUETOOTH_LFCLK_RC ifneq ($(SD),) FROZEN_MANIFEST ?= +else +MICROPY_VFS_LFS2 = 1 endif From 85f0ce214e42e0d8f36602ae1b084a97ef40c462 Mon Sep 17 00:00:00 2001 From: Glenn Ruben Bakke Date: Sat, 25 Jul 2020 11:32:56 +0200 Subject: [PATCH 191/264] tools/codeformat.py: Include ports/nrf/modules/nrf in code formatting. --- tools/codeformat.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/codeformat.py b/tools/codeformat.py index f8202c66b9..a5d64739aa 100755 --- a/tools/codeformat.py +++ b/tools/codeformat.py @@ -45,6 +45,7 @@ PATHS = [ "mpy-cross/*.[ch]", "ports/*/*.[ch]", "ports/windows/msvc/**/*.[ch]", + "ports/nrf/modules/nrf/*.[ch]", "py/*.[ch]", # Python "drivers/**/*.py", From e5e05532240e464da90b7b941542a06b36baa766 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 8 Aug 2021 00:47:34 +1000 Subject: [PATCH 192/264] nrf/modules/uos: Add ilistdir to uos module. This was missed in the initial implementation of the uos module. Signed-off-by: Damien George --- ports/nrf/modules/uos/moduos.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/nrf/modules/uos/moduos.c b/ports/nrf/modules/uos/moduos.c index fd5334d004..e8a3790dac 100644 --- a/ports/nrf/modules/uos/moduos.c +++ b/ports/nrf/modules/uos/moduos.c @@ -145,6 +145,7 @@ STATIC const mp_rom_map_elem_t os_module_globals_table[] = { #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) }, From 77b4cfcbc9d2f46cb275fb6027999a1bfc0feb77 Mon Sep 17 00:00:00 2001 From: Glenn Ruben Bakke Date: Sun, 11 Nov 2018 21:37:05 +0100 Subject: [PATCH 193/264] nrf/modules/nrf: Add function to enable/disable DCDC. This function can be used to enable and disable the DC/DC converter with or without the Bluetooth stack enabled. It can also be used to query the current state of the DC/DC. This commit also adds a definition of ARRAY_SIZE needed by nrfx HAL-layer. --- ports/nrf/modules/nrf/modnrf.c | 30 +++++++++++++++++++++++++++++- ports/nrf/nrfx_glue.h | 7 +++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/ports/nrf/modules/nrf/modnrf.c b/ports/nrf/modules/nrf/modnrf.c index fd955dc62f..fc217b1d7e 100644 --- a/ports/nrf/modules/nrf/modnrf.c +++ b/ports/nrf/modules/nrf/modnrf.c @@ -29,15 +29,40 @@ #if MICROPY_PY_NRF -#if MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE #include "flashbdev.h" #include "flash.h" +#include "nrf_power.h" + +#if BLUETOOTH_SD +#include "nrf_soc.h" +#include "ble_drv.h" +#define BLUETOOTH_STACK_ENABLED() (ble_drv_stack_enabled()) +#endif #define FLASH_PAGE_ALIGN_UP(addr) (((addr) - 1) + (FLASH_PAGESIZE)-(((addr) - 1) % (FLASH_PAGESIZE))) extern uint32_t _unused_flash_start; extern uint32_t _unused_flash_len; +#if NRF_POWER_HAS_DCDCEN +STATIC mp_obj_t dcdc(size_t n_args, const mp_obj_t *args) { + if (n_args > 0) { + bool dcdc_state = mp_obj_is_true(args[0]); + #if BLUETOOTH_SD + if (BLUETOOTH_STACK_ENABLED()) { + sd_power_dcdc_mode_set(dcdc_state); + } else + #endif + { + nrf_power_dcdcen_set(NRF_POWER, dcdc_state); + } + } + return mp_obj_new_bool(nrf_power_dcdcen_get(NRF_POWER)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dcdc_obj, 0, 1, dcdc); +#endif + +#if MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE mp_obj_t nrf_modnrf_freeflash_start_aligned(void) { return mp_obj_new_int_from_uint(FLASH_PAGE_ALIGN_UP((uint32_t)&_unused_flash_start)); } @@ -54,6 +79,9 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(nrf_modnrf_freeflash_length_aligned_obj, nrf_mo STATIC const mp_rom_map_elem_t nrf_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_nrf) }, + #if NRF_POWER_HAS_DCDCEN + { MP_ROM_QSTR(MP_QSTR_dcdc), MP_ROM_PTR(&dcdc_obj) }, + #endif #if MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE { MP_ROM_QSTR(MP_QSTR_Flash), MP_ROM_PTR(&nrf_flashbdev_type) }, { MP_ROM_QSTR(MP_QSTR_unused_flash_start), MP_ROM_PTR(&nrf_modnrf_freeflash_start_aligned_obj) }, diff --git a/ports/nrf/nrfx_glue.h b/ports/nrf/nrfx_glue.h index b4a60257a8..56e1f719da 100644 --- a/ports/nrf/nrfx_glue.h +++ b/ports/nrf/nrfx_glue.h @@ -27,8 +27,15 @@ #ifndef NRFX_GLUE_H #define NRFX_GLUE_H +#include "py/mpconfig.h" +#include "py/misc.h" + #include +#ifndef ARRAY_SIZE +#define ARRAY_SIZE MP_ARRAY_SIZE +#endif + #define NRFX_STATIC_ASSERT(expression) #define NRFX_ASSERT(expression) do { bool res = expression; (void)res; } while (0) From 8645b7c23b9d55ddc4e5927f52de5aa3da05a915 Mon Sep 17 00:00:00 2001 From: Daniel Mizyrycki Date: Sun, 8 Aug 2021 14:15:56 -0700 Subject: [PATCH 194/264] nrf: Enable source line on tracebacks. --- ports/nrf/mpconfigdevice_nrf51822.h | 8 ++++++++ ports/nrf/mpconfigdevice_nrf52832.h | 4 ++++ ports/nrf/mpconfigdevice_nrf52840.h | 4 ++++ ports/nrf/mpconfigdevice_nrf9160.h | 4 ++++ ports/nrf/mpconfigport.h | 1 - 5 files changed, 20 insertions(+), 1 deletion(-) diff --git a/ports/nrf/mpconfigdevice_nrf51822.h b/ports/nrf/mpconfigdevice_nrf51822.h index d10e91a79e..67a81d2509 100644 --- a/ports/nrf/mpconfigdevice_nrf51822.h +++ b/ports/nrf/mpconfigdevice_nrf51822.h @@ -44,6 +44,14 @@ // Board overridable feature configuration. +#ifndef MICROPY_ENABLE_SOURCE_LINE +#if defined(BLUETOOTH_SD) +#define MICROPY_ENABLE_SOURCE_LINE (0) +#else +#define MICROPY_ENABLE_SOURCE_LINE (1) +#endif +#endif + #ifndef MICROPY_PY_ARRAY_SLICE_ASSIGN #if defined(BLUETOOTH_SD) #define MICROPY_PY_ARRAY_SLICE_ASSIGN (0) diff --git a/ports/nrf/mpconfigdevice_nrf52832.h b/ports/nrf/mpconfigdevice_nrf52832.h index cc7bcbe9b4..fa9258f2ac 100644 --- a/ports/nrf/mpconfigdevice_nrf52832.h +++ b/ports/nrf/mpconfigdevice_nrf52832.h @@ -36,6 +36,10 @@ // Board overridable feature configuration. +#ifndef MICROPY_ENABLE_SOURCE_LINE +#define MICROPY_ENABLE_SOURCE_LINE (1) +#endif + #ifndef MICROPY_PY_ARRAY_SLICE_ASSIGN #define MICROPY_PY_ARRAY_SLICE_ASSIGN (1) #endif diff --git a/ports/nrf/mpconfigdevice_nrf52840.h b/ports/nrf/mpconfigdevice_nrf52840.h index cc7bcbe9b4..fa9258f2ac 100644 --- a/ports/nrf/mpconfigdevice_nrf52840.h +++ b/ports/nrf/mpconfigdevice_nrf52840.h @@ -36,6 +36,10 @@ // Board overridable feature configuration. +#ifndef MICROPY_ENABLE_SOURCE_LINE +#define MICROPY_ENABLE_SOURCE_LINE (1) +#endif + #ifndef MICROPY_PY_ARRAY_SLICE_ASSIGN #define MICROPY_PY_ARRAY_SLICE_ASSIGN (1) #endif diff --git a/ports/nrf/mpconfigdevice_nrf9160.h b/ports/nrf/mpconfigdevice_nrf9160.h index cc7bcbe9b4..fa9258f2ac 100644 --- a/ports/nrf/mpconfigdevice_nrf9160.h +++ b/ports/nrf/mpconfigdevice_nrf9160.h @@ -36,6 +36,10 @@ // Board overridable feature configuration. +#ifndef MICROPY_ENABLE_SOURCE_LINE +#define MICROPY_ENABLE_SOURCE_LINE (1) +#endif + #ifndef MICROPY_PY_ARRAY_SLICE_ASSIGN #define MICROPY_PY_ARRAY_SLICE_ASSIGN (1) #endif diff --git a/ports/nrf/mpconfigport.h b/ports/nrf/mpconfigport.h index 480368aaa1..af77ef69ca 100644 --- a/ports/nrf/mpconfigport.h +++ b/ports/nrf/mpconfigport.h @@ -55,7 +55,6 @@ #define MICROPY_REPL_EMACS_KEYS (0) #define MICROPY_REPL_AUTO_INDENT (1) #define MICROPY_KBD_EXCEPTION (1) -#define MICROPY_ENABLE_SOURCE_LINE (0) #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) #if NRF51 #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_NONE) From 3835f5f597a002149d85423dcc72371e8d8bbcc5 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 10 Aug 2021 00:18:46 +1000 Subject: [PATCH 195/264] esp32/makeimg.py: Get bootloader and partition offset from sdkconfig. So that it works on ESP32C3, which has the bootloader at 0x0. Fixes issue #7565. Signed-off-by: Damien George --- ports/esp32/Makefile | 1 + ports/esp32/makeimg.py | 48 +++++++++++++++++++++++++++++++++--------- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/ports/esp32/Makefile b/ports/esp32/Makefile index 5065221635..403b698a32 100644 --- a/ports/esp32/Makefile +++ b/ports/esp32/Makefile @@ -33,6 +33,7 @@ endif all: idf.py $(IDFPY_FLAGS) build @$(PYTHON) makeimg.py \ + $(BUILD)/sdkconfig \ $(BUILD)/bootloader/bootloader.bin \ $(BUILD)/partition_table/partition-table.bin \ $(BUILD)/micropython.bin \ diff --git a/ports/esp32/makeimg.py b/ports/esp32/makeimg.py index fd04583188..0646ce372c 100644 --- a/ports/esp32/makeimg.py +++ b/ports/esp32/makeimg.py @@ -6,8 +6,17 @@ sys.path.append(os.getenv("IDF_PATH") + "/components/partition_table") import gen_esp32part -OFFSET_BOOTLOADER = 0x1000 -OFFSET_PARTITIONS = 0x8000 +OFFSET_BOOTLOADER_DEFAULT = 0x1000 +OFFSET_PARTITIONS_DEFAULT = 0x8000 + + +def load_sdkconfig_hex_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 default def load_partition_table(filename): @@ -15,28 +24,47 @@ def load_partition_table(filename): return gen_esp32part.PartitionTable.from_binary(f.read()) -partition_table = load_partition_table(sys.argv[2]) +# Extract command-line arguments. +arg_sdkconfig = sys.argv[1] +arg_bootloader_bin = sys.argv[2] +arg_partitions_bin = sys.argv[3] +arg_application_bin = sys.argv[4] +arg_output_bin = sys.argv[5] -max_size_bootloader = OFFSET_PARTITIONS - OFFSET_BOOTLOADER +# Load required sdkconfig values. +offset_bootloader = load_sdkconfig_hex_value( + arg_sdkconfig, "BOOTLOADER_OFFSET_IN_FLASH", OFFSET_BOOTLOADER_DEFAULT +) +offset_partitions = load_sdkconfig_hex_value( + arg_sdkconfig, "PARTITION_TABLE_OFFSET", OFFSET_PARTITIONS_DEFAULT +) + +# Load the partition table. +partition_table = load_partition_table(arg_partitions_bin) + +max_size_bootloader = offset_partitions - offset_bootloader max_size_partitions = 0 offset_application = 0 max_size_application = 0 +# Inspect the partition table to find offsets and maximum sizes. for part in partition_table: if part.name == "nvs": - max_size_partitions = part.offset - OFFSET_PARTITIONS + max_size_partitions = part.offset - offset_partitions elif part.type == gen_esp32part.APP_TYPE and offset_application == 0: offset_application = part.offset max_size_application = part.size +# Define the input files, their location and maximum size. files_in = [ - ("bootloader", OFFSET_BOOTLOADER, max_size_bootloader, sys.argv[1]), - ("partitions", OFFSET_PARTITIONS, max_size_partitions, sys.argv[2]), - ("application", offset_application, max_size_application, sys.argv[3]), + ("bootloader", offset_bootloader, max_size_bootloader, arg_bootloader_bin), + ("partitions", offset_partitions, max_size_partitions, arg_partitions_bin), + ("application", offset_application, max_size_application, arg_application_bin), ] -file_out = sys.argv[4] +file_out = arg_output_bin -cur_offset = OFFSET_BOOTLOADER +# Write output file with combined firmware. +cur_offset = offset_bootloader with open(file_out, "wb") as fout: for name, offset, max_size, file_in in files_in: assert offset >= cur_offset From 23531bca74b593890e0a3049eb08f8db043f8dc1 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sat, 7 Aug 2021 13:24:14 +0200 Subject: [PATCH 196/264] rp2/CMakeLists.txt: Allow a board's cmake to set the manifest path. This allows boards to add frozen modules, or bypass the port manifest entirely. --- ports/rp2/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt index 3dea793ef9..34fabbc8bd 100644 --- a/ports/rp2/CMakeLists.txt +++ b/ports/rp2/CMakeLists.txt @@ -163,7 +163,9 @@ set(PICO_SDK_COMPONENTS # Define mpy-cross flags and frozen manifest set(MICROPY_CROSS_FLAGS -march=armv7m) -set(MICROPY_FROZEN_MANIFEST ${PROJECT_SOURCE_DIR}/boards/manifest.py) +if (NOT MICROPY_FROZEN_MANIFEST) + set(MICROPY_FROZEN_MANIFEST ${PROJECT_SOURCE_DIR}/boards/manifest.py) +endif() target_sources(${MICROPY_TARGET} PRIVATE ${MICROPY_SOURCE_PY} From a0cd18c1a5cdd9031c8a6dec27fd03419aac0096 Mon Sep 17 00:00:00 2001 From: Ned Konz Date: Fri, 6 Aug 2021 13:13:20 -0700 Subject: [PATCH 197/264] stm32/boards/NUCLEO_H743ZI2: Add modified version of NUCLEO_H743ZI. This commit creates a new stm32 board for the NUCLEO_H743ZI2, which is the current version of this from ST. This is a modified copy of the NUCLEO_H743ZI board, and the ZI2 board differs in a few minor ways: - LED2 has moved from PB7 to PE1 and is now yellow rather than blue - the USB power enable has moved from PG6 to PG10 - the USER button is now pulled down --- ports/stm32/boards/NUCLEO_H743ZI/board_init.c | 5 +- .../stm32/boards/NUCLEO_H743ZI2/board_init.c | 1 + .../boards/NUCLEO_H743ZI2/mpconfigboard.h | 25 ++++ .../boards/NUCLEO_H743ZI2/mpconfigboard.mk | 1 + ports/stm32/boards/NUCLEO_H743ZI2/pins.csv | 130 ++++++++++++++++++ .../NUCLEO_H743ZI2/stm32h7xx_hal_conf.h | 1 + 6 files changed, 160 insertions(+), 3 deletions(-) create mode 100644 ports/stm32/boards/NUCLEO_H743ZI2/board_init.c create mode 100644 ports/stm32/boards/NUCLEO_H743ZI2/mpconfigboard.h create mode 100644 ports/stm32/boards/NUCLEO_H743ZI2/mpconfigboard.mk create mode 100644 ports/stm32/boards/NUCLEO_H743ZI2/pins.csv create mode 100644 ports/stm32/boards/NUCLEO_H743ZI2/stm32h7xx_hal_conf.h diff --git a/ports/stm32/boards/NUCLEO_H743ZI/board_init.c b/ports/stm32/boards/NUCLEO_H743ZI/board_init.c index 04149c37b8..40f3730cc2 100644 --- a/ports/stm32/boards/NUCLEO_H743ZI/board_init.c +++ b/ports/stm32/boards/NUCLEO_H743ZI/board_init.c @@ -2,7 +2,6 @@ void NUCLEO_H743ZI_board_early_init(void) { // Turn off the USB switch - #define USB_PowerSwitchOn pin_G6 - mp_hal_pin_output(USB_PowerSwitchOn); - mp_hal_pin_low(USB_PowerSwitchOn); + mp_hal_pin_output(pyb_pin_OTG_FS_POWER); + mp_hal_pin_low(pyb_pin_OTG_FS_POWER); } diff --git a/ports/stm32/boards/NUCLEO_H743ZI2/board_init.c b/ports/stm32/boards/NUCLEO_H743ZI2/board_init.c new file mode 100644 index 0000000000..04caaaca9e --- /dev/null +++ b/ports/stm32/boards/NUCLEO_H743ZI2/board_init.c @@ -0,0 +1 @@ +#include "boards/NUCLEO_H743ZI/board_init.c" diff --git a/ports/stm32/boards/NUCLEO_H743ZI2/mpconfigboard.h b/ports/stm32/boards/NUCLEO_H743ZI2/mpconfigboard.h new file mode 100644 index 0000000000..22060277de --- /dev/null +++ b/ports/stm32/boards/NUCLEO_H743ZI2/mpconfigboard.h @@ -0,0 +1,25 @@ +#include "boards/NUCLEO_H743ZI/mpconfigboard.h" + +#undef MICROPY_HW_BOARD_NAME +#define MICROPY_HW_BOARD_NAME "NUCLEO_H743ZI2" + +// The board has an external 32kHz crystal attached +#undef MICROPY_HW_RTC_USE_LSE +#define MICROPY_HW_RTC_USE_LSE (1) + +// There is no external HS crystal. +// JP1 STLNK_RST will disable the incoming 8MHz clock +// since it is derived from the STLINK's MCO output +#undef MICROPY_HW_CLK_USE_BYPASS +#define MICROPY_HW_CLK_USE_BYPASS (1) + +#undef MICROPY_HW_LED2 +#define MICROPY_HW_LED2 (pin_E1) // yellow + +// only when mboot is used +// Define the user button for entering mboot +#if defined(USE_MBOOT) +#define MBOOT_BOOTPIN_PIN (pin_C13) +#define MBOOT_BOOTPIN_PULL (MP_HAL_PIN_PULL_DOWN) +#define MBOOT_BOOTPIN_ACTIVE (1) +#endif diff --git a/ports/stm32/boards/NUCLEO_H743ZI2/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_H743ZI2/mpconfigboard.mk new file mode 100644 index 0000000000..e49a4d0d61 --- /dev/null +++ b/ports/stm32/boards/NUCLEO_H743ZI2/mpconfigboard.mk @@ -0,0 +1 @@ +include boards/NUCLEO_H743ZI/mpconfigboard.mk diff --git a/ports/stm32/boards/NUCLEO_H743ZI2/pins.csv b/ports/stm32/boards/NUCLEO_H743ZI2/pins.csv new file mode 100644 index 0000000000..450d6e432a --- /dev/null +++ b/ports/stm32/boards/NUCLEO_H743ZI2/pins.csv @@ -0,0 +1,130 @@ +A0,PA3 +A1,PC0 +A2,PC3 +A3,PB1 +A4,PC2 +A5,PF10 +A6,PF4 +A7,PF5 +A8,PF6 +D0,PB7 +D1,PB6 +D2,PG14 +D3,PE13 +D4,PE14 +D5,PE11 +D6,PE9 +D7,PG12 +D8,PF3 +D9,PD15 +D10,PD14 +D11,PB5 +D12,PA6 +D13,PA7 +D14,PB9 +D15,PB8 +D16,PC6 +D17,PB15 +D18,PB13 +D19,PB12 +D20,PA15 +D21,PC7 +D22,PB5 +D23,PB3 +D24,PA4 +D25,PB4 +D26,PG6 +D27,PB2 +D28,PD13 +D29,PD12 +D30,PD11 +D31,PE2 +D32,PA0 +D33,PB0 +D34,PE0 +D35,PB11 +D36,PB10 +D37,PE15 +D38,PE6 +D39,PE12 +D40,PE10 +D41,PE7 +D42,PE8 +D43,PC8 +D44,PC9 +D45,PC10 +D46,PC11 +D47,PC12 +D48,PD2 +D49,PG2 +D50,PG3 +D51,PD7 +D52,PD6 +D53,PD5 +D54,PD4 +D55,PD3 +D56,PE2 +D57,PE4 +D58,PE5 +D59,PE6 +D60,PE3 +D61,PF8 +D62,PF7 +D63,PF9 +D64,PG1 +D65,PG0 +D66,PD1 +D67,PD0 +D68,PF0 +D69,PF1 +D70,PF2 +D71,PE9 +D72,PB2 +DAC1,PA4 +DAC2,PA5 +LED1,PB0 +LED2,PE1 +LED3,PB14 +SW,PC13 +I2C1_SDA,PB9 +I2C1_SCL,PB8 +I2C2_SDA,PF0 +I2C2_SCL,PF1 +I2C4_SCL,PF14 +I2C4_SDA,PF15 +SD_D0,PC8 +SD_D1,PC9 +SD_D2,PC10 +SD_D3,PC11 +SD_CMD,PD2 +SD_CK,PC12 +SD_SW,PG2 +OTG_FS_POWER,PD10 +OTG_FS_OVER_CURRENT,PG7 +USB_VBUS,PA9 +USB_ID,PA10 +USB_DM,PA11 +USB_DP,PA12 +UART2_TX,PD5 +UART2_RX,PD6 +UART2_RTS,PD4 +UART2_CTS,PD3 +UART3_TX,PD8 +UART3_RX,PD9 +UART5_TX,PB6 +UART5_RX,PB12 +UART6_TX,PC6 +UART6_RX,PC7 +UART7_TX,PF7 +UART7_RX,PF6 +UART8_TX,PE1 +UART8_RX,PE0 +ETH_MDC,PC1 +ETH_MDIO,PA2 +ETH_RMII_REF_CLK,PA1 +ETH_RMII_CRS_DV,PA7 +ETH_RMII_RXD0,PC4 +ETH_RMII_RXD1,PC5 +ETH_RMII_TX_EN,PG11 +ETH_RMII_TXD0,PG13 +ETH_RMII_TXD1,PB13 diff --git a/ports/stm32/boards/NUCLEO_H743ZI2/stm32h7xx_hal_conf.h b/ports/stm32/boards/NUCLEO_H743ZI2/stm32h7xx_hal_conf.h new file mode 100644 index 0000000000..61f202e629 --- /dev/null +++ b/ports/stm32/boards/NUCLEO_H743ZI2/stm32h7xx_hal_conf.h @@ -0,0 +1 @@ +#include "boards/NUCLEO_H743ZI/stm32h7xx_hal_conf.h" From 42d1a1635cc35fd3a9ae28b19685184fc3c23f1e Mon Sep 17 00:00:00 2001 From: Ned Konz Date: Fri, 6 Aug 2021 07:28:38 -0700 Subject: [PATCH 198/264] stm32/mbedtls: Fix compile warning about uninitialized val. --- ports/stm32/mbedtls/mbedtls_port.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/stm32/mbedtls/mbedtls_port.c b/ports/stm32/mbedtls/mbedtls_port.c index efb8d2c2a4..c31eb744ae 100644 --- a/ports/stm32/mbedtls/mbedtls_port.c +++ b/ports/stm32/mbedtls/mbedtls_port.c @@ -80,7 +80,7 @@ void m_free_mbedtls(void *ptr_in) { } int mbedtls_hardware_poll(void *data, unsigned char *output, size_t len, size_t *olen) { - uint32_t val; + uint32_t val = 0; int n = 0; *olen = len; while (len--) { From 333e16521bc8af8a55f362c1bad2f21c4704d20b Mon Sep 17 00:00:00 2001 From: Julia Hathaway Date: Mon, 2 Aug 2021 17:01:57 -0500 Subject: [PATCH 199/264] docs/zephyr: Add quick reference for the Zephyr port. Includes an introduction to using the Zephyr port on MicroPython. The quickref details examples of how to use each module the port currently supports. The tutorial provides additional details for Zephyr specific modules. Signed-off-by: Julia Hathaway --- docs/index.rst | 1 + docs/zephyr/general.rst | 22 +++++ docs/zephyr/quickref.rst | 157 +++++++++++++++++++++++++++++++ docs/zephyr/tutorial/index.rst | 16 ++++ docs/zephyr/tutorial/intro.rst | 30 ++++++ docs/zephyr/tutorial/pins.rst | 46 +++++++++ docs/zephyr/tutorial/repl.rst | 75 +++++++++++++++ docs/zephyr/tutorial/storage.rst | 56 +++++++++++ 8 files changed, 403 insertions(+) create mode 100644 docs/zephyr/general.rst create mode 100644 docs/zephyr/quickref.rst create mode 100644 docs/zephyr/tutorial/index.rst create mode 100644 docs/zephyr/tutorial/intro.rst create mode 100644 docs/zephyr/tutorial/pins.rst create mode 100644 docs/zephyr/tutorial/repl.rst create mode 100644 docs/zephyr/tutorial/storage.rst diff --git a/docs/index.rst b/docs/index.rst index 58552daf51..a97bff1c84 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -14,3 +14,4 @@ MicroPython documentation and references rp2/quickref.rst wipy/quickref.rst unix/quickref.rst + zephyr/quickref.rst diff --git a/docs/zephyr/general.rst b/docs/zephyr/general.rst new file mode 100644 index 0000000000..362f03ada0 --- /dev/null +++ b/docs/zephyr/general.rst @@ -0,0 +1,22 @@ +.. _zephyr_general: + +General information about the Zephyr port +========================================= + +The Zephyr Project is a Linux Foundation hosted Collaboration Project. It’s an open +source collaborative effort uniting developers and users in building a +small, scalable, real-time operating system (RTOS) optimized for resource-constrained +devices, across multiple architectures. + +Multitude of boards +------------------- + +There is a multitude of modules and boards from different sources that are supported +by the Zephyr OS. All boards supported by Zephyr (with standard level of features +support, like UART console) should work with MicroPython (but not all were tested). +The FRDM-K64f board is taken as a reference board for the port for this documentation. +If you have another board, please make sure you have a datasheet, schematics and other +reference materials for your board handy to look up various aspects of your board +functioning. + +For a full list of Zephyr supported boards click `here (external link) `_ diff --git a/docs/zephyr/quickref.rst b/docs/zephyr/quickref.rst new file mode 100644 index 0000000000..783621316c --- /dev/null +++ b/docs/zephyr/quickref.rst @@ -0,0 +1,157 @@ +.. _zephyr_quickref: + +Quick reference for the Zephyr port +=================================== + +Below is a quick reference for the Zephyr port. If it is your first time working with this port please consider reading the following sections first: + +.. toctree:: + :maxdepth: 1 + + general.rst + tutorial/index.rst + +Running MicroPython +------------------- + +See the corresponding section of the tutorial: :ref:`intro`. + +Delay and timing +---------------- + +Use the :mod:`time ` module:: + + import time + + time.sleep(1) # sleep for 1 second + time.sleep_ms(500) # sleep for 500 milliseconds + time.sleep_us(10) # sleep for 10 microseconds + start = time.ticks_ms() # get millisecond counter + delta = time.ticks_diff(time.ticks_ms(), start) # compute time difference + +Pins and GPIO +------------- + +Use the :ref:`machine.Pin ` class:: + + from machine import Pin + + pin = Pin(("GPIO_1", 21), Pin.IN) # create input pin on GPIO1 + print(pin) # print pin port and number + + pin.init(Pin.OUT, Pin.PULL_UP, value=1) # reinitialize pin + + pin.value(1) # set pin to high + pin.value(0) # set pin to low + + pin.on() # set pin to high + pin.off() # set pin to low + + pin = Pin(("GPIO_1", 21), Pin.IN) # create input pin on GPIO1 + + pin = Pin(("GPIO_1", 21), Pin.OUT, value=1) # set pin high on creation + + pin = Pin(("GPIO_1", 21), Pin.IN, Pin.PULL_UP) # enable internal pull-up resistor + + switch = Pin(("GPIO_2", 6), Pin.IN) # create input pin for a switch + switch.irq(lambda t: print("SW2 changed")) # enable an interrupt when switch state is changed + +Hardware I2C bus +---------------- + +Hardware I2C is accessed via the :ref:`machine.I2C ` class:: + + from machine import I2C + + i2c = I2C("I2C_0") # construct an i2c bus + print(i2c) # print device name + + i2c.scan() # scan the device for available I2C slaves + + i2c.readfrom(0x1D, 4) # read 4 bytes from slave 0x1D + i2c.readfrom_mem(0x1D, 0x0D, 1) # read 1 byte from slave 0x1D at slave memory 0x0D + + i2c.writeto(0x1D, b'abcd') # write to slave with address 0x1D + i2c.writeto_mem(0x1D, 0x0D, b'ab') # write to slave 0x1D at slave memory 0x0D + + buf = bytearray(8) # create buffer of size 8 + i2c.writeto(0x1D, b'abcd') # write buf to slave 0x1D + +Hardware SPI bus +---------------- + +Hardware SPI is accessed via the :ref:`machine.SPI ` class:: + + from machine import SPI + + spi = SPI("SPI_0") # construct a spi bus with default configuration + spi.init(baudrate=100000, polarity=0, phase=0, bits=8, firstbit=SPI.MSB) # set configuration + + # equivalently, construct spi bus and set configuration at the same time + spi = SPI("SPI_0", baudrate=100000, polarity=0, phase=0, bits=8, firstbit=SPI.MSB) + print(spi) # print device name and bus configuration + + spi.read(4) # read 4 bytes on MISO + spi.read(4, write=0xF) # read 4 bytes while writing 0xF on MOSI + + buf = bytearray(8) # create a buffer of size 8 + spi.readinto(buf) # read into the buffer (reads number of bytes equal to the buffer size) + spi.readinto(buf, 0xF) # read into the buffer while writing 0xF on MOSI + + spi.write(b'abcd') # write 4 bytes on MOSI + + buf = bytearray(4) # create buffer of size 8 + spi.write_readinto(b'abcd', buf) # write to MOSI and read from MISO into the buffer + spi.write_readinto(buf, buf) # write buf to MOSI and read back into the buf + +Disk Access +----------- + +Use the :ref:`zephyr.DiskAccess ` class to support filesystem:: + + import os + from zephyr import DiskAccess + + block_dev = DiskAccess('SDHC') # create a block device object for an SD card + os.VfsFat.mkfs(block_dev) # create FAT filesystem object using the disk storage block + os.mount(block_dev, '/sd') # mount the filesystem at the SD card subdirectory + + # with the filesystem mounted, files can be manipulated as normal + with open('/sd/hello.txt','w') as f: # open a new file in the directory + f.write('Hello world') # write to the file + print(open('/sd/hello.txt').read()) # print contents of the file + +Flash Area +---------- + +Use the :ref:`zephyr.FlashArea ` class to support filesystem:: + + import os + from zephyr import FlashArea + + block_dev = FlashArea(4, 4096) # creates a block device object in the frdm-k64f flash scratch partition + os.VfsLfs2.mkfs(block_dev) # create filesystem in lfs2 format using the flash block device + os.mount(block_dev, '/flash') # mount the filesystem at the flash subdirectory + + # with the filesystem mounted, files can be manipulated as normal + with open('/flash/hello.txt','w') as f: # open a new file in the directory + f.write('Hello world') # write to the file + print(open('/flash/hello.txt').read()) # print contents of the file + +Sensor +------ + +Use the :ref:`zsensor.Sensor ` class to access sensor data:: + + import zsensor + from zsensor import Sensor + + accel = Sensor("FXOX8700") # create sensor object for the accelerometer + + accel.measure() # obtain a measurement reading from the accelerometer + + # each of these prints the value taken by measure() + accel.float(zsensor.ACCEL_X) # print measurement value for accelerometer X-axis sensor channel as float + accel.millis(zsensor.ACCEL_Y) # print measurement value for accelerometer Y-axis sensor channel in millionths + accel.micro(zsensor.ACCEL_Z) # print measurement value for accelerometer Z-axis sensor channel in thousandths + accel.int(zsensor.ACCEL_X) # print measurement integer value only for accelerometer X-axis sensor channel diff --git a/docs/zephyr/tutorial/index.rst b/docs/zephyr/tutorial/index.rst new file mode 100644 index 0000000000..218156b3b0 --- /dev/null +++ b/docs/zephyr/tutorial/index.rst @@ -0,0 +1,16 @@ +.. _zephyr_tutorial: + +MicroPython tutorial for the Zephyr port +======================================== + +This tutorial is intended to get you started with the Zephyr port. + +.. toctree:: + :maxdepth: 1 + :numbered: + + intro.rst + repl.rst + storage.rst + pins.rst + diff --git a/docs/zephyr/tutorial/intro.rst b/docs/zephyr/tutorial/intro.rst new file mode 100644 index 0000000000..ffdbea8b39 --- /dev/null +++ b/docs/zephyr/tutorial/intro.rst @@ -0,0 +1,30 @@ +.. _intro_zephyr: + +Getting started with MicroPython on the Zephyr port +=================================================== + +Let’s get started! + +Requirements +------------ + +To use the MicroPython Zephyr port, you will need a Zephyr supported board (for a list of acceptable +boards see :ref:`zephyr_general`). + +Powering up +----------- + +If your board has a USB connector on it then most likely it is powered +through this when connected to your PC. Otherwise you will need to power +it directly. Please refer to the documentation for your board for +further details. + +Getting and deploying the firmware +---------------------------------- + +The first step you will need to do is either clone the `MicroPython repository `_ +or download it from the `MicroPython downloads page `_. If you are an end user of MicroPython, +it is recommended to start with the stable firmware builds. If you would like to work on development, you may follow the daily +builds on git. + +Next, follow the Zephyr port readme document (``ports/zephyr/README.md``) to build and run the application on your board. diff --git a/docs/zephyr/tutorial/pins.rst b/docs/zephyr/tutorial/pins.rst new file mode 100644 index 0000000000..8e1d6602af --- /dev/null +++ b/docs/zephyr/tutorial/pins.rst @@ -0,0 +1,46 @@ +.. _pins_zephyr: + +GPIO Pins +========= + +Use :ref:`machine.Pin ` to control I/O pins. + +For Zephyr, pins are initialized using a tuple of port and pin number ``(\"GPIO_x\", pin#)`` +for the ``id`` value. For example to initialize a pin for the red LED on a FRDM-k64 board:: + + LED = Pin(("GPIO_1", 22), Pin.OUT) + +Reference your board's datasheet or Zephyr documentation for pin numbers, see below for more examples. + +.. list-table:: Pin Formatting + :header-rows: 1 + + * - Board + - Pin + - Format + * - frdm_k64f + - Red LED = PTB22 + - ("GPIO_1", 22) + * - 96b_carbon + - LED1 = PD2 + - ("GPIOD", 2) + * - mimxrt685_evk_cm33 + - Green LED = PIO0_14 + - ("GPIO0", 14) + +Interrupts +---------- + +The Zephyr port also supports interrupt handling for Pins using `machine.Pin.irq() `. +To respond to Pin change IRQs run:: + + from machine import Pin + + SW2 = Pin(("GPIO_2", 6), Pin.IN) # create Pin object for switch 2 + SW3 = Pin(("GPIO_0", 4), Pin.IN) # create Pin object for switch 3 + + SW2.irq(lambda t: print("SW2 changed")) # print message when SW2 state is changed (triggers change IRQ) + SW3.irq(lambda t: print("SW3 changed")) # print message when SW3 state is changed (triggers change IRQ) + + while True: # wait + pass diff --git a/docs/zephyr/tutorial/repl.rst b/docs/zephyr/tutorial/repl.rst new file mode 100644 index 0000000000..a7e8955d0b --- /dev/null +++ b/docs/zephyr/tutorial/repl.rst @@ -0,0 +1,75 @@ +Getting a MicroPython REPL prompt +================================= + +REPL stands for Read Evaluate Print Loop, and is the name given to the +interactive MicroPython prompt that you can access on your board through +Zephyr. It is recommended to use REPL to test out your code and run commands. + +REPL over the serial port +------------------------- + +The REPL is available on a UART serial peripheral specified for the board by +the ``zephyr,console`` devicetree node. The baudrate of the REPL is 115200. +If your board has a USB-serial convertor on it then you should be able to access +the REPL directly from your PC. + +To access the prompt over USB-serial you will need to use a terminal emulator +program. For a Linux or Mac machine, open a terminal and run:: + + screen /dev/ttyACM0 115200 + +You can also try ``picocom`` or ``minicom`` instead of screen. You may have to use +``/dev/ttyACM1`` or a higher number for ``ttyACM``. Additional permissions +may be necessary to access this device (eg group ``uucp`` or ``dialout``, or use sudo). +For Windows, get a terminal software, such as puTTY and connect via a serial session +using the proper COM port. + +Using the REPL +-------------- + +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 v2.6.0-rc1-416-g3056c5ec30ad *** + MicroPython v2.6.0-rc1-416-g3056c5ec30 on 2021-06-24; zephyr-frdm_k64f with mk64f12 + Type "help()" for more information. + >>> + +Now you can try running MicroPython code directly on your board. + +Anything you type at the prompt, indicated by ``>>>``, will be executed after you press +the Enter key. If there is an error with the text that you enter then an error +message is printed. + +Start by typing the following at the prompt to make sure it is working:: + + >>> print("hello world!") + hello world! + +If you already know some python you can now try some basic commands here. For +example:: + + >>> 1 + 2 + 3 + >>> 1 / 2 + 0.5 + >>> 3 * 'Zephyr' + ZephyrZephyrZephyr + +If your board has an LED, you can blink it using the following code:: + + >>>import time + >>>from machine import Pin + + >>>LED = Pin(("GPIO_1", 21), Pin.OUT) + >>>while True: + ... LED.value(1) + ... time.sleep(0.5) + ... LED.value(0) + ... time.sleep(0.5) + +The above code uses an LED location for a FRDM-K64F board (port B, pin 21; +following Zephyr conventions ports are identified by "GPIO_x", where *x* +starts from 0). You will need to adjust it for another board using the board's +reference materials. diff --git a/docs/zephyr/tutorial/storage.rst b/docs/zephyr/tutorial/storage.rst new file mode 100644 index 0000000000..f57a08fc40 --- /dev/null +++ b/docs/zephyr/tutorial/storage.rst @@ -0,0 +1,56 @@ +.. _storage_zephyr: + +Filesystems and Storage +======================= + +Storage modules support virtual filesystem with FAT and littlefs formats, backed by either +Zephyr DiskAccess or FlashArea (flash map) APIs depending on which the board supports. + +See `uos Filesystem Mounting `_. + +Disk Access +----------- + +The :ref:`zephyr.DiskAccess ` class can be used to access storage devices, such as SD cards. +This class uses `Zephyr Disk Access API `_ and +implements the `uos.AbstractBlockDev` protocol. + +For use with SD card controllers, SD cards must be present at boot & not removed; they will +be auto detected and initialized by filesystem at boot. Use the disk driver interface and a +file system to access SD cards via disk access (see below). + +Example usage of FatFS with an SD card on the mimxrt1050_evk board:: + + import os + from zephyr import DiskAccess + bdev = zephyr.DiskAccess('SDHC') # create block device object using DiskAccess + os.VfsFat.mkfs(bdev) # create FAT filesystem object using the disk storage block + os.mount(bdev, '/sd') # mount the filesystem at the SD card subdirectory + with open('/sd/hello.txt','w') as f: # open a new file in the directory + f.write('Hello world') # write to the file + print(open('/sd/hello.txt').read()) # print contents of the file + + +Flash Area +---------- + +The :ref:`zephyr.FlashArea ` class can be used to implement a low-level storage system or +customize filesystem configurations. To store persistent data on the device, using a higher-level filesystem +API is recommended (see below). + +This class uses `Zephyr Flash map API `_ and +implements the `uos.AbstractBlockDev` protocol. + +Example usage with the internal flash on the reel_board or the rv32m1_vega_ri5cy board:: + + import os + from zephyr import FlashArea + bdev = FlashArea(FlashArea.STORAGE, 4096) # create block device object using FlashArea + os.VfsLfs2.mkfs(bdev) # create Little filesystem object using the flash area block + os.mount(bdev, '/flash') # mount the filesystem at the flash storage subdirectory + with open('/flash/hello.txt','w') as f: # open a new file in the directory + f.write('Hello world') # write to the file + print(open('/flash/hello.txt').read()) # print contents of the file + +For boards such as the frdm_k64f in which the MicroPython application spills into the default flash storage +partition, use the scratch partition by replacing ``FlashArea.STORAGE`` with the integer value 4. From 6ed69906257ee59d6533dc06ddfa02db5f4fbce3 Mon Sep 17 00:00:00 2001 From: Julia Hathaway Date: Mon, 2 Aug 2021 17:08:00 -0500 Subject: [PATCH 200/264] docs/library/zephyr: Add libraries specific to the Zephyr port. Includes documentation for Zephyr specific modules (zephyr and zsensor), classes (DiskAccess and FlashArea), and functions. Signed-off-by: Julia Hathaway --- docs/library/index.rst | 10 +++ docs/library/zephyr.DiskAccess.rst | 38 +++++++++ docs/library/zephyr.FlashArea.rst | 40 ++++++++++ docs/library/zephyr.rst | 60 ++++++++++++++ docs/library/zephyr.zsensor.rst | 123 +++++++++++++++++++++++++++++ 5 files changed, 271 insertions(+) create mode 100644 docs/library/zephyr.DiskAccess.rst create mode 100644 docs/library/zephyr.FlashArea.rst create mode 100644 docs/library/zephyr.rst create mode 100644 docs/library/zephyr.zsensor.rst diff --git a/docs/library/index.rst b/docs/library/index.rst index 2536e8dc96..5fd4a8f77e 100644 --- a/docs/library/index.rst +++ b/docs/library/index.rst @@ -176,3 +176,13 @@ The following libraries are specific to the RP2040, as used in the Raspberry Pi :maxdepth: 2 rp2.rst + +Libraries specific to Zephyr +---------------------------- + +The following libraries are specific to the Zephyr port. + +.. toctree:: + :maxdepth: 2 + + zephyr.rst diff --git a/docs/library/zephyr.DiskAccess.rst b/docs/library/zephyr.DiskAccess.rst new file mode 100644 index 0000000000..d19d81a962 --- /dev/null +++ b/docs/library/zephyr.DiskAccess.rst @@ -0,0 +1,38 @@ +.. currentmodule:: zephyr +.. _zephyr.DiskAccess: + +class DiskAccess -- access to disk storage +========================================== + +Uses `Zephyr Disk Access API `_. + +This class allows access to storage devices on the board, such as support for SD card controllers and +interfacing with SD cards via SPI. Disk devices are automatically detected and initialized on boot using +Zephyr devicetree data. + +The Zephyr disk access class enables the transfer of data between a disk device and an accessible memory buffer given a disk name, +buffer, starting disk block, and number of sectors to read. MicroPython reads as many blocks as necessary to fill the buffer, so +the number of sectors to read is found by dividing the buffer length by block size of the disk. + +Constructors +------------ + +.. class:: DiskAccess(disk_name) + + Gets an object for accessing disk memory of the specific disk. + For accessing an SD card on the mimxrt1050_evk, ``disk_name`` would be ``SDHC``. See board documentation and + devicetree for usable disk names for your board (ex. RT boards use style USDHC#). + +Methods +------- + +.. method:: DiskAccess.readblocks(block_num, buf) + DiskAccess.readblocks(block_num, buf, offset) +.. method:: DiskAccess.writeblocks(block_num, buf) + DiskAccess.writeblocks(block_num, buf, offset) +.. method:: DiskAccess.ioctl(cmd, arg) + + These methods implement the simple and extended + :ref:`block protocol ` defined by + :class:`uos.AbstractBlockDev`. + diff --git a/docs/library/zephyr.FlashArea.rst b/docs/library/zephyr.FlashArea.rst new file mode 100644 index 0000000000..306347d449 --- /dev/null +++ b/docs/library/zephyr.FlashArea.rst @@ -0,0 +1,40 @@ +.. currentmodule:: zephyr +.. _zephyr.FlashArea: + +class FlashArea -- access to built-in flash storage +=================================================== + +Uses `Zephyr flash map API `_. + +This class allows access to device flash partition data. +Flash area structs consist of a globally unique ID number, the name of the flash device the partition is in, +the start offset (expressed in relation to the flash memory beginning address per partition), +and the size of the partition that the device represents. For fixed flash partitions, data from the device +tree is used; however, fixed flash partitioning is not enforced in MicroPython because MCUBoot is not enabled. + +Constructors +------------ + +.. class:: FlashArea(id, block_size) + + Gets an object for accessing flash memory at partition specified by ``id`` and with block size of ``block_size``. + + ``id`` values are integers correlating to fixed flash partitions defined in the devicetree. + A commonly used partition is the designated flash storage area defined as ``FlashArea.STORAGE`` if + ``FLASH_AREA_LABEL_EXISTS(storage)`` returns true at boot. + Zephyr devicetree fixed flash partitions are ``boot_partition``, ``slot0_partition``, ``slot1_partition``, and + ``scratch_partition``. Because MCUBoot is not enabled by default for MicroPython, these fixed partitions can be accessed by + ID integer values 1, 2, 3, and 4, respectively. + +Methods +------- + +.. method:: FlashArea.readblocks(block_num, buf) + FlashArea.readblocks(block_num, buf, offset) +.. method:: FlashArea.writeblocks(block_num, buf) + FlashArea.writeblocks(block_num, buf, offset) +.. method:: FlashArea.ioctl(cmd, arg) + + These methods implement the simple and extended + :ref:`block protocol ` defined by + :class:`uos.AbstractBlockDev`. diff --git a/docs/library/zephyr.rst b/docs/library/zephyr.rst new file mode 100644 index 0000000000..da3d14a093 --- /dev/null +++ b/docs/library/zephyr.rst @@ -0,0 +1,60 @@ +.. currentmodule:: zephyr + +:mod:`zephyr` --- functionality specific to the Zephyr port +=========================================================== + +.. module:: zephyr + :synopsis: functionality specific to Zephyr + +The ``zephyr`` module contains functions and classes specific to the Zephyr port. + +Functions +--------- + +.. function:: is_preempt_thread() + + Returns true if the current thread is a preemptible thread. + + Zephyr preemptible threads are those with non-negative priority values (low priority levels), which therefore, + can be supplanted as soon as a higher or equal priority thread becomes ready. + +.. function:: current_tid() + + Returns the thread id of the current thread, which is used to reference the thread. + +.. function:: thread_analyze() + + Runs the Zephyr debug thread analyzer on the current thread and prints stack size statistics in the format: + + "``thread_name``-20s: STACK: unused ``available_stack_space`` usage ``stack_space_used`` + / ``stack_size`` (``percent_stack_space_used`` %); CPU: ``cpu_utilization`` %" + + * *CPU utilization is only printed if runtime statistics are configured via the ``CONFIG_THREAD_RUNTIME_STATS`` kconfig* + + This function can only be accessed if ``CONFIG_THREAD_ANALYZER`` is configured for the port in ``zephyr/prj.conf``. + For more infomation, see documentation for Zephyr `thread analyzer + `_. + +.. function:: shell_exec(cmd_in) + + Executes the given command on an UART backend. This function can only be accessed if ``CONFIG_SHELL_BACKEND_SERIAL`` + is configured for the port in ``zephyr/prj.conf``. + + A list of possible commands can be found in the documentation for Zephyr `shell commands `_. + +Classes +------- + +.. toctree:: + :maxdepth: 1 + + zephyr.DiskAccess.rst + zephyr.FlashArea.rst + +Additional Modules +------------------ + +.. toctree:: + :maxdepth: 1 + + zephyr.zsensor.rst diff --git a/docs/library/zephyr.zsensor.rst b/docs/library/zephyr.zsensor.rst new file mode 100644 index 0000000000..4eadc926dc --- /dev/null +++ b/docs/library/zephyr.zsensor.rst @@ -0,0 +1,123 @@ +.. currentmodule:: zsensor + +:mod:`zsensor` --- Zephyr sensor bindings +========================================= + +.. module:: zsensor + :synopsis: zephyr sensor bindings + +The ``zsensor`` module contains a class for using sensors with Zephyr. + +.. _zsensor.Sensor: + +class Sensor --- sensor control for the Zephyr port +--------------------------------------------------- + +Use this class to access data from sensors on your board. +See Zephyr documentation for sensor usage here: `Sensors +`_. + +Sensors are defined in the Zephyr devicetree for each board. The quantities that a given sensor can +measure are called a sensor channels. Sensors can have multiple channels to represent different axes +of one property or different properties a sensor can measure. See `Channels`_ below for defined sensor +channels. + +Constructor +~~~~~~~~~~~ + +.. class:: Sensor(device_name) + + Device names are defined in the devicetree for your board. + For example, the device name for the accelerometer in the FRDM-k64f board is "FXOS8700". + +Methods +~~~~~~~ + +.. method:: Sensor.measure() + + Obtains a measurement sample from the sensor device using Zephyr sensor_sample_fetch and + stores it in an internal driver buffer as a useful value, a pair of (integer part of value, + fractional part of value in 1-millionths). + Returns none if successful or OSError value if failure. + +.. method:: Sensor.get_float(sensor_channel) + + Returns the value of the sensor measurement sample as a float. + +.. method:: Sensor.get_micros(sensor_channel) + + Returns the value of the sensor measurement sample in millionths. + (Ex. value of ``(1, 500000)`` returns as ``1500000``) + +.. method:: Sensor.get_millis(sensor_channel) + + Returns the value of sensor measurement sample in thousandths. + (Ex. value of ``(1, 500000)`` returns as ``1500``) + +.. method:: Sensor.get_int(sensor_channel) + + Returns only the integer value of the measurement sample. + (Ex. value of ``(1, 500000)`` returns as ``1``) + +Channels +~~~~~~~~ + +.. data:: ACCEL_X + + Acceleration on the X axis, in m/s^2. + +.. data:: ACCEL_Y + + Acceleration on the Y axis, in m/s^2. + +.. data:: ACCEL_Z + + Acceleration on the Z axis, in m/s^2. + +.. data:: GYRO_X + + Angular velocity around the X axis, in radians/s. + +.. data:: GYRO_Y + + Angular velocity around the Y axis, in radians/s. + +.. data:: GYRO_Z + + Angular velocity around the Z axis, in radians/s. + +.. data:: MAGN_X + + Magnetic field on the X axis, in Gauss. + +.. data:: MAGN_Y + + Magnetic field on the Y axis, in Gauss. + +.. data:: MAGN_Z + + Magnetic field on the Z axis, in Gauss. + +.. data:: DIE_TEMP + + Device die temperature in degrees Celsius. + +.. data:: PRESS + + Pressure in kilopascal. + +.. data:: PROX + + Proximity. Dimensionless. A value of 1 indicates that an object is close. + +.. data:: HUMIDITY + + Humidity, in percent. + +.. data:: LIGHT + + Illuminance in visible spectrum, in lux. + +.. data:: ALTITUDE + + Altitude, in meters. From 671f012306e0942cbe59fcd43e25e67c289da351 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 13 Aug 2021 20:12:07 +1000 Subject: [PATCH 201/264] docs/templates: Add unix and zephyr quickref links to top-index. Signed-off-by: Damien George --- docs/templates/topindex.html | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/templates/topindex.html b/docs/templates/topindex.html index 42233942bc..c14f840ceb 100644 --- a/docs/templates/topindex.html +++ b/docs/templates/topindex.html @@ -66,6 +66,14 @@ Quick reference for the WiPy/CC3200
pinout for the WiPy/CC3200, snippets of useful code, and a tutorial