From 038c92a2f59b9be98ba49bfeddd1126a0fe0bff8 Mon Sep 17 00:00:00 2001 From: eightycc Date: Wed, 15 Jan 2025 18:02:28 -0800 Subject: [PATCH 1/4] Serial UART console bug fixes and enhancements. --- locale/circuitpython.pot | 4 + ports/raspberrypi/supervisor/port.c | 13 ++- py/circuitpy_mpconfig.h | 11 ++ supervisor/shared/serial.c | 174 +++++++++++++++++++++++----- supervisor/shared/serial.h | 4 +- 5 files changed, 175 insertions(+), 31 deletions(-) diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 93f332adcfd1d..6ab347c078c91 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -975,6 +975,10 @@ msgstr "" msgid "Failed to allocate %q buffer" msgstr "" +#: shared-bindings/ramlog/ramlog.c +msgid "Failed to allocate RAM log buffer" +msgstr "" + #: ports/espressif/common-hal/wifi/__init__.c msgid "Failed to allocate Wifi memory" msgstr "" diff --git a/ports/raspberrypi/supervisor/port.c b/ports/raspberrypi/supervisor/port.c index bdfb9bc8e73f0..3b7b6d6f6d4df 100644 --- a/ports/raspberrypi/supervisor/port.c +++ b/ports/raspberrypi/supervisor/port.c @@ -326,10 +326,6 @@ safe_mode_t port_init(void) { // Reset everything into a known state before board_init. reset_port(); - #ifdef CIRCUITPY_PSRAM_CHIP_SELECT - setup_psram(); - #endif - // Initialize RTC #if CIRCUITPY_RTC common_hal_rtc_init(); @@ -339,6 +335,15 @@ safe_mode_t port_init(void) { hardware_alarm_claim(0); hardware_alarm_set_callback(0, _tick_callback); + // RP2 port-specific early serial initialization for psram debug. + // The RTC must already be initialized, otherwise the serial UART + // will hang. + serial_early_init(); + + #ifdef CIRCUITPY_PSRAM_CHIP_SELECT + setup_psram(); + #endif + // Check brownout. #if CIRCUITPY_CYW43 diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index eacfbfbeb77c2..7e28f9f1697c6 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -328,9 +328,20 @@ typedef long mp_off_t; #define CIRCUITPY_CONSOLE_UART (1) #ifndef CIRCUITPY_CONSOLE_UART_BAUDRATE #define CIRCUITPY_CONSOLE_UART_BAUDRATE (115200) +#if !defined(CIRCUITPY_CONSOLE_UART_PRINTF) +#define CIRCUITPY_CONSOLE_UART_PRINTF(...) mp_printf(&console_uart_print, __VA_ARGS__) +#endif +#if !defined(CIRCUITPY_CONSOLE_UART_HEXDUMP) +#define CIRCUITPY_CONSOLE_UART_HEXDUMP(pfx, buf, len) print_hexdump(&console_uart_print, pfx, (const uint8_t *)buf, len) +#endif +#if !defined(CIRCUITPY_CONSOLE_UART_TIMESTAMP) +#define CIRCUITPY_CONSOLE_UART_TIMESTAMP (0) +#endif #endif #else #define CIRCUITPY_CONSOLE_UART (0) +#define CIRCUITPY_CONSOLE_UART_PRINTF(...) (void)0 +#define CIRCUITPY_CONSOLE_UART_HEXDUMP(...) (void)0 #endif // These CIRCUITPY_xxx values should all be defined in the *.mk files as being on or off. diff --git a/supervisor/shared/serial.c b/supervisor/shared/serial.c index fce066f70de7f..783450a61ac6e 100644 --- a/supervisor/shared/serial.c +++ b/supervisor/shared/serial.c @@ -63,33 +63,94 @@ static bool _serial_console_write_disabled; // Set to true to temporarily discard writes to the display terminal only. static bool _serial_display_write_disabled; +// Indicates that serial console has been early initialized. +static bool _serial_console_early_inited = false; + #if CIRCUITPY_CONSOLE_UART -static void console_uart_print_strn(void *env, const char *str, size_t len) { + +// All output to the console uart comes through this inner write function. It ensures that all +// lines are terminated with a "cooked" '\r\n' sequence. Lines that are already cooked are sent +// on unchanged. +static void inner_console_uart_write_cb(void *env, const char *str, size_t len) { (void)env; int uart_errcode; - common_hal_busio_uart_write(&console_uart, (const uint8_t *)str, len, &uart_errcode); + bool last_cr = false; + while (len > 0) { + size_t i = 0; + if (str[0] == '\n' && !last_cr) { + common_hal_busio_uart_write(&console_uart, (const uint8_t *)"\r", 1, &uart_errcode); + i = 1; + } + // Lump all characters on the next line together. + while ((last_cr || str[i] != '\n') && i < len) { + last_cr = str[i] == '\r'; + i++; + } + common_hal_busio_uart_write(&console_uart, (const uint8_t *)str, i, &uart_errcode); + str = &str[i]; + len -= i; + } } -const mp_print_t console_uart_print = {NULL, console_uart_print_strn}; +#if CIRCUITPY_CONSOLE_UART_TIMESTAMP +static const mp_print_t inner_console_uart_write = {NULL, inner_console_uart_write_cb}; +static uint32_t console_uart_write_prev_time = 0; +static bool console_uart_write_prev_nl = true; + +static inline void console_uart_write_timestamp(void) { + uint32_t now = supervisor_ticks_ms32(); + uint32_t delta = now - console_uart_write_prev_time; + console_uart_write_prev_time = now; + mp_printf(&inner_console_uart_write, + "%01lu.%03lu(%01lu.%03lu): ", + now / 1000, now % 1000, delta / 1000, delta % 1000); +} #endif -int console_uart_printf(const char *fmt, ...) { - #if CIRCUITPY_CONSOLE_UART - // Skip prints that occur before console serial is started. It's better than - // crashing. - if (common_hal_busio_uart_deinited(&console_uart)) { - return 0; +static size_t console_uart_write(const char *str, size_t len) { + // Ignore writes if console uart is not yet initialized. + if (!_serial_console_early_inited) { + return len; } - va_list ap; - va_start(ap, fmt); - int ret = mp_vprintf(&console_uart_print, fmt, ap); - va_end(ap); - return ret; - #else - return 0; - #endif + + if (!_first_write_done) { + mp_hal_delay_ms(50); + _first_write_done = true; + } + + // There may be multiple newlines in the string, split at newlines. + int remaining_len = len; + while (remaining_len > 0) { + #if CIRCUITPY_CONSOLE_UART_TIMESTAMP + if (console_uart_write_prev_nl) { + console_uart_write_timestamp(); + console_uart_write_prev_nl = false; + } + #endif + int print_len = 0; + while (print_len < remaining_len) { + if (str[print_len++] == '\n') { + #if CIRCUITPY_CONSOLE_UART_TIMESTAMP + console_uart_write_prev_nl = true; + #endif + break; + } + } + inner_console_uart_write_cb(NULL, str, print_len); + str += print_len; + remaining_len -= print_len; + } + return len; +} + +static void console_uart_write_cb(void *env, const char *str, size_t len) { + (void)env; + console_uart_write(str, len); } +const mp_print_t console_uart_print = {NULL, console_uart_write_cb}; +#endif + MP_WEAK void board_serial_early_init(void) { } @@ -137,9 +198,14 @@ MP_WEAK void port_serial_write_substring(const char *text, uint32_t length) { } void serial_early_init(void) { - // Set up console UART, if enabled. + // Ignore duplicate calls to initialize allowing port-specific code to + // call this function early. + if (_serial_console_early_inited) { + return; + } #if CIRCUITPY_CONSOLE_UART + // Set up console UART, if enabled. console_uart.base.type = &busio_uart_type; const mcu_pin_obj_t *console_rx = MP_OBJ_TO_PTR(CIRCUITPY_CONSOLE_UART_RX); @@ -150,12 +216,15 @@ void serial_early_init(void) { console_uart_rx_buf, true); common_hal_busio_uart_never_reset(&console_uart); - // Do an initial print so that we can confirm the serial output is working. - console_uart_printf("Serial console setup\r\n"); #endif board_serial_early_init(); port_serial_early_init(); + + _serial_console_early_inited = true; + + // Do an initial print so that we can confirm the serial output is working. + CIRCUITPY_CONSOLE_UART_PRINTF("Serial console setup\n"); } void serial_init(void) { @@ -347,12 +416,7 @@ uint32_t serial_write_substring(const char *text, uint32_t length) { #endif #if CIRCUITPY_CONSOLE_UART - if (!_first_write_done) { - mp_hal_delay_ms(50); - _first_write_done = true; - } - int uart_errcode; - length_sent = common_hal_busio_uart_write(&console_uart, (const uint8_t *)text, length, &uart_errcode); + length_sent = console_uart_write(text, length); #endif #if CIRCUITPY_SERIAL_BLE @@ -409,3 +473,61 @@ bool serial_display_write_disable(bool disabled) { _serial_display_write_disabled = disabled; return now; } + +// A general purpose hex/ascii dump function for arbitrary area of memory. +void print_hexdump(const mp_print_t *printer, const char *prefix, const uint8_t *buf, size_t len) { + size_t i; + for (i = 0; i < len; ++i) { + // print hex digit + if (i % 32 == 0) { + mp_printf(printer, "%s0x%04x:", prefix, i); + } + if (i % 32 == 16) { + mp_printf(printer, " : "); + } else if (i % 4 == 0) { + mp_printf(printer, " "); + } + mp_printf(printer, "%02x", buf[i]); + // print ascii chars for this line + if (i % 32 == 31) { + size_t k = i - 31; + mp_printf(printer, " : "); + for (size_t j = 0; j < 32; ++j) { + if (j == 16) { + mp_printf(printer, " "); + } + if (buf[k + j] >= 32 && buf[k + j] < 127) { + mp_printf(printer, "%c", buf[k + j]); + } else { + mp_printf(printer, "."); + } + } + mp_printf(printer, "\n"); + } + } + if (i % 32 != 0) { + // For a final line of less than 32 bytes, pad with spaces + i -= i % 32; + for (size_t j = len % 32; j < 32; ++j) { + if (j % 32 == 16) { + mp_printf(printer, " "); + } else if (j % 4 == 0) { + mp_printf(printer, " "); + } + mp_printf(printer, " "); + } + // Print ascii chars for the last line fragment + mp_printf(printer, " : "); + for (size_t j = 0; j < len % 32; ++j) { + if (j == 16) { + mp_printf(printer, " "); + } + if (buf[i + j] >= 32 && buf[i + j] < 127) { + mp_printf(printer, "%c", buf[i + j]); + } else { + mp_printf(printer, "."); + } + } + mp_printf(printer, "\n"); + } +} diff --git a/supervisor/shared/serial.h b/supervisor/shared/serial.h index ea0f147cc49ee..56d15d83907e0 100644 --- a/supervisor/shared/serial.h +++ b/supervisor/shared/serial.h @@ -47,4 +47,6 @@ char board_serial_read(void); uint32_t board_serial_bytes_available(void); void board_serial_write_substring(const char *text, uint32_t length); -int console_uart_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); +extern const mp_print_t console_uart_print; + +void print_hexdump(const mp_print_t *printer, const char *prefix, const uint8_t *buf, size_t len); From 6262311a08c960954814a84530a08174e30e2213 Mon Sep 17 00:00:00 2001 From: eightycc Date: Wed, 15 Jan 2025 18:09:11 -0800 Subject: [PATCH 2/4] Back out change to circuitpython.pot. --- locale/circuitpython.pot | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 6ab347c078c91..28712b4f1cfa3 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -975,10 +975,6 @@ msgstr "" msgid "Failed to allocate %q buffer" msgstr "" -#: shared-bindings/ramlog/ramlog.c -msgid "Failed to allocate RAM log buffer" -msgstr "" - #: ports/espressif/common-hal/wifi/__init__.c msgid "Failed to allocate Wifi memory" msgstr "" @@ -2319,6 +2315,7 @@ msgstr "" #: ports/espressif/boards/m5stack_core_fire/mpconfigboard.h #: ports/espressif/boards/m5stack_stick_c/mpconfigboard.h #: ports/espressif/boards/m5stack_stick_c_plus/mpconfigboard.h +#: ports/espressif/boards/m5stack_stick_c_plus2/mpconfigboard.h msgid "You pressed button A at start up." msgstr "" From f5ddfa11b45de16e5c108fff7b8350a49a44fb8f Mon Sep 17 00:00:00 2001 From: eightycc Date: Thu, 16 Jan 2025 08:18:37 -0800 Subject: [PATCH 3/4] Fix missing py/mpprint.h include in serial.c. --- supervisor/shared/serial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/supervisor/shared/serial.c b/supervisor/shared/serial.c index 783450a61ac6e..9e53e12b4f882 100644 --- a/supervisor/shared/serial.c +++ b/supervisor/shared/serial.c @@ -9,6 +9,7 @@ #include "py/mpconfig.h" #include "py/mphal.h" +#include "py/mpprint.h" #include "supervisor/shared/cpu.h" #include "supervisor/shared/display.h" @@ -34,7 +35,6 @@ #endif #if CIRCUITPY_CONSOLE_UART -#include "py/mpprint.h" #include "shared-bindings/busio/UART.h" busio_uart_obj_t console_uart; From 687e439299fe741785274deaeb18bbcf97b28f8a Mon Sep 17 00:00:00 2001 From: eightycc Date: Thu, 16 Jan 2025 08:49:55 -0800 Subject: [PATCH 4/4] Try py/mpprint.h include again. --- supervisor/shared/serial.h | 1 + 1 file changed, 1 insertion(+) diff --git a/supervisor/shared/serial.h b/supervisor/shared/serial.h index 56d15d83907e0..982f4fb3d7bfb 100644 --- a/supervisor/shared/serial.h +++ b/supervisor/shared/serial.h @@ -11,6 +11,7 @@ #include #include "py/mpconfig.h" +#include "py/mpprint.h" #ifdef CIRCUITPY_BOOT_OUTPUT_FILE #include "py/misc.h"