From b1823d00728d8ab1bcc858b05dd8a68763fc13d4 Mon Sep 17 00:00:00 2001 From: Dirk Van Haerenborgh Date: Tue, 3 Nov 2026 15:54:01 +0100 Subject: [PATCH] Add SEGGER RTT debug output support Add RTT as an alternative output backend for the existing minimal UART printf implementation, sharing the formatter rather than introducing a separate one. RTT output uses up-channel 0; UART takes precedence when both backends are enabled. To enable RTT, check out https://github.com/SEGGERMicro/RTT and build with DEBUG_RTT=/path/to/segger_rtt and DEBUG_UART=0. Supply the configuration header as SEGGER_RTT_Conf.h and add its directory using CFLAGS_EXTRA=-I/path/to/rtt_config/. The build links the RTT implementation and enables PRINTF_ENABLED automatically. --- arch.mk | 5 ++ docs/compile.md | 13 +++++ include/printf.h | 6 +-- options.mk | 8 +++ src/string.c | 92 ++++++++++++++++++---------------- test-app/Makefile | 8 ++- test-app/syscalls.c | 4 +- tools/unit-tests/unit-string.c | 74 +++++++++++++-------------- 8 files changed, 124 insertions(+), 86 deletions(-) diff --git a/arch.mk b/arch.mk index 0da7cdd03f..99ac7480cb 100644 --- a/arch.mk +++ b/arch.mk @@ -3345,6 +3345,11 @@ ifeq ($(DEBUG_UART),1) endif endif +# Debug RTT +ifeq ($(DEBUG_RTT),1) + CFLAGS+=-DDEBUG_RTT +endif + # UART QSPI programmer (PolarFire SoC MPFS): receive binary over UART and # write it directly to QSPI flash. Requires EXT_FLASH=1 and DEBUG_UART=1. # Use tools/scripts/mpfs_qspi_prog.py on the host side. diff --git a/docs/compile.md b/docs/compile.md index 6c6415f4e0..b1c94138dd 100644 --- a/docs/compile.md +++ b/docs/compile.md @@ -212,6 +212,19 @@ To debug the bootloader, simply compile with `DEBUG=1`. The size of the bootload consistently, so ensure that you have enough space at the beginning of the flash before `WOLFBOOT_PARTITION_BOOT_ADDRESS`. +### Enable SEGGER RTT logging + +With Make, set `DEBUG_RTT` to the root of a checkout of [SEGGER RTT](https://github.com/SEGGERMicro/RTT), not `1`. +Provide the RTT configuration as `SEGGER_RTT_Conf.h` and add its directory to the include path: + +```sh +make DEBUG_RTT=/path/to/segger_rtt DEBUG_UART=0 \ + CFLAGS_EXTRA=-I/path/to/rtt_config/ +``` + +This automatically enables `PRINTF_ENABLED` and sends `wolfBoot_printf` output to RTT up-channel 0. +`DEBUG_UART=1` takes precedence over RTT, so disable it when using RTT. Read the output with an RTT-capable debugger or viewer. + ### Disable interrupt vector relocation On some platforms, it might be convenient to avoid the interrupt vector relocation before boot-up. diff --git a/include/printf.h b/include/printf.h index 6a5661edf9..89aa8b28a5 100644 --- a/include/printf.h +++ b/include/printf.h @@ -62,10 +62,10 @@ # include "efi/efilib.h" /* NOTE: %s arguments will not work as EFI uses widechar string */ # define wolfBoot_printf(_f_, ...) Print(L##_f_, ##__VA_ARGS__) -# elif defined(DEBUG_UART) +# elif defined(DEBUG_UART) || defined(DEBUG_RTT) /* use minimal printf support in string.h */ - void uart_printf(const char* fmt, ...); -# define wolfBoot_printf(_f_, ...) uart_printf(_f_, ##__VA_ARGS__) + void tiny_printf(const char* fmt, ...); +# define wolfBoot_printf(_f_, ...) tiny_printf(_f_, ##__VA_ARGS__) # elif defined(__CCRX__) # define wolfBoot_printf printf # elif defined(WOLFBOOT_LOG_PRINTF) diff --git a/options.mk b/options.mk index 0592944472..c5d294dc0e 100644 --- a/options.mk +++ b/options.mk @@ -1179,6 +1179,14 @@ ifeq ($(DEBUG_UART),1) endif endif endif + +ifneq ($(strip $(DEBUG_RTT)),) + CFLAGS+=-DDEBUG_RTT + CFLAGS+=-I$(DEBUG_RTT) + CFLAGS+=-DPRINTF_ENABLED + OBJS+=$(DEBUG_RTT)/RTT/SEGGER_RTT.o +endif + # Flash erase/write/read test at WOLFBOOT_PARTITION_UPDATE_ADDRESS. ifeq ($(TEST_FLASH),1) CFLAGS+=-DTEST_FLASH diff --git a/src/string.c b/src/string.c index e5bd12ef34..60b78a5f13 100644 --- a/src/string.c +++ b/src/string.c @@ -51,7 +51,7 @@ size_t strlen(const char *s); /* forward declaration */ #endif -#ifdef DEBUG_UART +#if defined(DEBUG_UART) || defined(DEBUG_RTT) #include "printf.h" #ifdef PRINTF_ENABLED #include @@ -67,7 +67,7 @@ size_t strlen(const char *s); /* forward declaration */ /* allow using built-in libc if WOLFBOOT_USE_STDLIBC is defined */ #ifndef WOLFBOOT_USE_STDLIBC #if !(defined(BUILD_LOADER_STAGE1) && defined(ARCH_PPC)) || \ - (defined(PRINTF_ENABLED) && defined(DEBUG_UART)) \ + (defined(PRINTF_ENABLED) && (defined(DEBUG_UART) || defined(DEBUG_RTT))) \ || defined(TARGET_same51) int islower(int c) @@ -268,9 +268,9 @@ void* memchr(void const *s, int c_in, size_t n) } return NULL; } -#endif /* !BUILD_LOADER_STAGE1 || (PRINTF_ENABLED && DEBUG_UART) */ +#endif /* !BUILD_LOADER_STAGE1 || (PRINTF_ENABLED && (DEBUG_UART || DEBUG_RTT)) */ -#if !(defined(BUILD_LOADER_STAGE1) && defined(ARCH_PPC)) || defined(DEBUG_UART) +#if !(defined(BUILD_LOADER_STAGE1) && defined(ARCH_PPC)) || defined(DEBUG_UART) || defined(DEBUG_RTT) size_t strlen(const char *s) { size_t i = 0; @@ -411,26 +411,36 @@ void __aeabi_memclr8(void *dst, size_t n) #endif /* __ARM_EABI__ && !IAR */ #endif /* WOLFBOOT_USE_STDLIBC */ -#if defined(PRINTF_ENABLED) && defined(DEBUG_UART) +#ifdef PRINTF_ENABLED +# if defined(DEBUG_UART) +# define write_str(buf, sz) uart_write(buf, sz) +# elif defined(DEBUG_RTT) +# include "RTT/SEGGER_RTT.h" +# define write_str(buf, sz) SEGGER_RTT_Write(0, buf, sz) +# endif +/* fail build when neither uart or rtt are defined */ +#endif + +#if defined(PRINTF_ENABLED) && ( defined(DEBUG_UART) || defined(DEBUG_RTT) ) /* Shared digit table -- avoids duplicating the literal in each width's * formatting loop. */ -static const char uart_writenum_digits[] = "0123456789ABCDEF"; +static const char write_num_digits[] = "0123456789ABCDEF"; /* Shared tail for both widths: applies zeropad spacing, slides the digit * run into place, and emits to UART. Caller has filled digits at the * tail of `buf` and counted them in `sz`; `i` is the prefix length * (sign character only). */ -static void uart_writenum_emit(char *buf, int bufsize, int i, int sz, +static void write_num_emit(char *buf, int bufsize, int i, int sz, int zeropad, int maxdigits) { if (zeropad && sz < maxdigits) { i += maxdigits - sz; } memmove(&buf[i], &buf[bufsize - sz], sz); - uart_write(buf, i + sz); + write_str(buf, i + sz); } -void uart_writenum(int num, int base, int zeropad, int maxdigits, +void write_num(int num, int base, int zeropad, int maxdigits, int is_signed) { int i = 0, sz = 0; @@ -454,20 +464,19 @@ void uart_writenum(int num, int base, int zeropad, int maxdigits, /* 32-bit divide: stays out of libgcc 64-bit helpers, which aren't * linked into freestanding stage1 / Cortex-M builds. */ do { - buf[sizeof(buf) - sz - 1] = - uart_writenum_digits[(val % (unsigned)base)]; + buf[sizeof(buf) - sz - 1] = write_num_digits[(val % (unsigned)base)]; sz++; val /= (unsigned)base; } while (val > 0U); - uart_writenum_emit(buf, sizeof(buf), i, sz, zeropad, maxdigits); + write_num_emit(buf, sizeof(buf), i, sz, zeropad, maxdigits); } #ifdef PRINTF_LONG_LONG /* 64-bit core for %llu/%lld/%llx. Pulls in libgcc 64-bit divide * (__udivmoddi4 / __aeabi_uldivmod) so it's only compiled when needed * and only called from the long-long printf paths -- never from the - * 32-bit uart_writenum() fast path above. */ -static void uart_writenum_ll(unsigned long long val, int is_negative, + * 32-bit write_num() fast path above. */ +static void write_num_ll(unsigned long long val, int is_negative, int base, int zeropad, int maxdigits) { int i = 0, sz = 0; @@ -486,23 +495,22 @@ static void uart_writenum_ll(unsigned long long val, int is_negative, memset(&buf[i], '0', maxdigits); } do { - buf[sizeof(buf) - sz - 1] = - uart_writenum_digits[(val % (unsigned)base)]; + buf[sizeof(buf) - sz - 1] = write_num_digits[(val % (unsigned)base)]; sz++; val /= (unsigned)base; } while (val > 0ULL); - uart_writenum_emit(buf, sizeof(buf), i, sz, zeropad, maxdigits); + write_num_emit(buf, sizeof(buf), i, sz, zeropad, maxdigits); } #endif /* PRINTF_LONG_LONG */ -void uart_vprintf(const char* fmt, va_list argp) +void tiny_vprintf(const char* fmt, va_list argp) { char* fmtp = (char*)fmt; int zeropad, maxdigits, precision, leftjust, islong, iszl; while (fmtp != NULL && *fmtp != '\0') { /* print non formatting characters */ if (*fmtp != '%') { - uart_write(fmtp++, 1); + write_str(fmtp++, 1); continue; } fmtp++; /* skip % */ @@ -565,7 +573,7 @@ void uart_vprintf(const char* fmt, va_list argp) switch (*fmtp) { case '%': - uart_write(fmtp, 1); + write_str(fmtp, 1); break; case 'u': case 'i': @@ -591,7 +599,7 @@ void uart_vprintf(const char* fmt, va_list argp) else { val = va_arg(argp, unsigned long long); } - uart_writenum_ll(val, is_neg, 10, zeropad, maxdigits); + write_num_ll(val, is_neg, 10, zeropad, maxdigits); } else if (islong == 1 || iszl) { /* %ld / %lu / %zd / %zu: long is 64-bit here */ @@ -611,7 +619,7 @@ void uart_vprintf(const char* fmt, va_list argp) val = (unsigned long long)va_arg(argp, unsigned long); } - uart_writenum_ll(val, is_neg, 10, zeropad, maxdigits); + write_num_ll(val, is_neg, 10, zeropad, maxdigits); } else #endif @@ -619,17 +627,17 @@ void uart_vprintf(const char* fmt, va_list argp) int n; if (*fmtp == 'u') { n = (int)va_arg(argp, unsigned int); - uart_writenum(n, 10, zeropad, maxdigits, 0); + write_num(n, 10, zeropad, maxdigits, 0); } else { n = (int)va_arg(argp, int); - uart_writenum(n, 10, zeropad, maxdigits, 1); + write_num(n, 10, zeropad, maxdigits, 1); } } break; } case 'p': - uart_write("0x", 2); + write_str("0x", 2); /* fall through */ case 'x': case 'X': @@ -639,7 +647,7 @@ void uart_vprintf(const char* fmt, va_list argp) /* %llx: full 64-bit value */ unsigned long long val = va_arg(argp, unsigned long long); - uart_writenum_ll(val, 0, 16, zeropad, maxdigits); + write_num_ll(val, 0, 16, zeropad, maxdigits); } else if (islong == 1 || iszl || *fmtp == 'p') { /* %lx / %zx / %p: consume at the type's natural width @@ -653,7 +661,7 @@ void uart_vprintf(const char* fmt, va_list argp) val = (unsigned long long)va_arg(argp, unsigned long); } - uart_writenum_ll(val, 0, 16, zeropad, maxdigits); + write_num_ll(val, 0, 16, zeropad, maxdigits); } else #endif @@ -665,7 +673,7 @@ void uart_vprintf(const char* fmt, va_list argp) else { n = (int)va_arg(argp, int); } - uart_writenum(n, 16, zeropad, maxdigits, 0); + write_num(n, 16, zeropad, maxdigits, 0); } break; } @@ -674,27 +682,27 @@ void uart_vprintf(const char* fmt, va_list argp) char* str = (char*)va_arg(argp, char*); int slen = (int)strlen(str); if (leftjust) { - uart_write(str, slen); + write_str(str, slen); while (slen < maxdigits) { - uart_write(" ", 1); + write_str(" ", 1); slen++; } } else { while (slen < maxdigits) { - uart_write(" ", 1); + write_str(" ", 1); slen++; } - uart_write(str, (uint32_t)strlen(str)); + write_str(str, (uint32_t)strlen(str)); } break; } case 'c': { char c = (char)va_arg(argp, int); - uart_write(&c, 1); + write_str(&c, 1); break; } -#ifdef UART_PRINTF_FLOAT +#ifdef PRINTF_FLOAT case 'f': case 'e': case 'g': @@ -706,43 +714,43 @@ void uart_vprintf(const char* fmt, va_list argp) /* handle negative */ if (val < 0.0) { - uart_write("-", 1); + write_str("-", 1); val = -val; } /* integer part */ ipart = (unsigned int)val; - uart_writenum((int)ipart, 10, 0, 0, 0); + write_num((int)ipart, 10, 0, 0, 0); /* fractional part */ if (prec > 0) { double frac = val - (double)ipart; char c; int i; - uart_write(".", 1); + write_str(".", 1); for (i = 0; i < prec; i++) { frac *= 10.0; digit = (int)frac; if (digit > 9) digit = 9; c = '0' + digit; - uart_write(&c, 1); + write_str(&c, 1); frac -= (double)digit; } } break; } -#endif /* UART_PRINTF_FLOAT */ +#endif /* PRINTF_FLOAT */ default: break; } fmtp++; }; } -void uart_printf(const char* fmt, ...) +void tiny_printf(const char* fmt, ...) { va_list argp; va_start(argp, fmt); - uart_vprintf(fmt, argp); + tiny_vprintf(fmt, argp); va_end(argp); } -#endif /* PRINTF_ENABLED && DEBUG_UART */ +#endif /* PRINTF_ENABLED && ( DEBUG_UART || DEBUG_RTT ) */ diff --git a/test-app/Makefile b/test-app/Makefile index 1da5f5429e..f57d56dc82 100644 --- a/test-app/Makefile +++ b/test-app/Makefile @@ -271,12 +271,16 @@ endif ifeq ($(WOLFCRYPT_BENCHMARK),1) CFLAGS+=-DWOLFCRYPT_BENCHMARK WOLFCRYPT_SUPPORT=1 - UART_PRINTF_FLOAT?=1 + PRINTF_FLOAT?=1 APP_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/benchmark/benchmark.o endif ifeq ($(UART_PRINTF_FLOAT),1) - CFLAGS+=-DUART_PRINTF_FLOAT + CFLAGS+=-DPRINTF_FLOAT +endif + +ifeq ($(RTT_PRINTF_FLOAT),1) + CFLAGS+=-DPRINTF_FLOAT endif ifeq ($(WOLFCRYPT_SUPPORT),1) diff --git a/test-app/syscalls.c b/test-app/syscalls.c index 0a1559d764..cd7cb5f7b2 100644 --- a/test-app/syscalls.c +++ b/test-app/syscalls.c @@ -340,7 +340,7 @@ static int ___vsnprintf(char *buf, unsigned int size, const char *fmt, va_list a if (pos < (int)size - 1) buf[pos++] = (char)va_arg(argp, int); break; -#ifdef UART_PRINTF_FLOAT +#ifdef PRINTF_FLOAT case 'f': case 'e': case 'g': { double val = va_arg(argp, double); @@ -368,7 +368,7 @@ static int ___vsnprintf(char *buf, unsigned int size, const char *fmt, va_list a } break; } -#endif /* UART_PRINTF_FLOAT */ +#endif /* PRINTF_FLOAT */ default: break; } diff --git a/tools/unit-tests/unit-string.c b/tools/unit-tests/unit-string.c index 5f70de7414..449297d747 100644 --- a/tools/unit-tests/unit-string.c +++ b/tools/unit-tests/unit-string.c @@ -382,141 +382,141 @@ START_TEST(test_memcpy_aligned_buffers) } END_TEST -START_TEST(test_uart_writenum_basic) +START_TEST(test_write_num_basic) { reset_uart_buf(); - uart_writenum(0, 10, 0, 0, 1); + write_num(0, 10, 0, 0, 1); ck_assert_str_eq(uart_buf, "0"); reset_uart_buf(); - uart_writenum(255, 16, 0, 0, 0); + write_num(255, 16, 0, 0, 0); ck_assert_str_eq(uart_buf, "FF"); reset_uart_buf(); - uart_writenum(-5, 10, 0, 0, 1); + write_num(-5, 10, 0, 0, 1); ck_assert_str_eq(uart_buf, "-5"); reset_uart_buf(); - uart_writenum(7, 10, 1, 4, 1); + write_num(7, 10, 1, 4, 1); ck_assert_str_eq(uart_buf, "0007"); reset_uart_buf(); - uart_writenum(1, 10, 1, 2, 1); + write_num(1, 10, 1, 2, 1); ck_assert_str_eq(uart_buf, "01"); reset_uart_buf(); - uart_writenum(0x1234, 16, 1, 6, 0); + write_num(0x1234, 16, 1, 6, 0); ck_assert_str_eq(uart_buf, "001234"); reset_uart_buf(); - uart_writenum(1, 10, 1, 64, 1); + write_num(1, 10, 1, 64, 1); ck_assert_int_eq(uart_buf[0], '0'); } END_TEST -START_TEST(test_uart_printf_formats) +START_TEST(test_tiny_printf_formats) { reset_uart_buf(); - uart_printf("X=%u Y=%d Z=%x W=%X %% %s %c", 5U, -3, 0x2a, 0x2a, "ok", '!'); + tiny_printf("X=%u Y=%d Z=%x W=%X %% %s %c", 5U, -3, 0x2a, 0x2a, "ok", '!'); ck_assert_str_eq(uart_buf, "X=5 Y=-3 Z=2A W=2A % ok !"); reset_uart_buf(); - uart_printf("%04u", 12U); + tiny_printf("%04u", 12U); ck_assert_str_eq(uart_buf, "0012"); reset_uart_buf(); - uart_printf("%p", (void*)(uintptr_t)0x10); + tiny_printf("%p", (void*)(uintptr_t)0x10); ck_assert_str_eq(uart_buf, "0x10"); reset_uart_buf(); - uart_printf("A%08uB", 12U); + tiny_printf("A%08uB", 12U); ck_assert_str_eq(uart_buf, "A00000012B"); reset_uart_buf(); - uart_printf("%12u", 3U); + tiny_printf("%12u", 3U); ck_assert_str_eq(uart_buf, "3"); reset_uart_buf(); - uart_printf("%lu", (unsigned long)7); + tiny_printf("%lu", (unsigned long)7); ck_assert_str_eq(uart_buf, "7"); reset_uart_buf(); - uart_printf("%zu", (size_t)9); + tiny_printf("%zu", (size_t)9); ck_assert_str_eq(uart_buf, "9"); reset_uart_buf(); - uart_printf("bad%qend"); + tiny_printf("bad%qend"); ck_assert_str_eq(uart_buf, "badend"); reset_uart_buf(); - uart_printf("%i", -1); + tiny_printf("%i", -1); ck_assert_str_eq(uart_buf, "-1"); /* %u must print the full unsigned range, not the signed interpretation: * values >= 2^31 used to come out negative. */ reset_uart_buf(); - uart_printf("%u", 0x80000000u); + tiny_printf("%u", 0x80000000u); ck_assert_str_eq(uart_buf, "2147483648"); reset_uart_buf(); - uart_printf("%u", 0xFFFFFFFFu); + tiny_printf("%u", 0xFFFFFFFFu); ck_assert_str_eq(uart_buf, "4294967295"); reset_uart_buf(); - uart_printf("%010u", 0xFFFFFFFFu); + tiny_printf("%010u", 0xFFFFFFFFu); ck_assert_str_eq(uart_buf, "4294967295"); } END_TEST /* F-11030: %ld/%lu/%zd/%zu/%lx/%p must consume the full-width * argument, not int (truncation on 64-bit hosts). */ -START_TEST(test_uart_printf_64bit_args) +START_TEST(test_tiny_printf_64bit_args) { reset_uart_buf(); - uart_printf("%ld", (long)1234567890123LL); + tiny_printf("%ld", (long)1234567890123LL); ck_assert_str_eq(uart_buf, "1234567890123"); reset_uart_buf(); - uart_printf("%lu", (unsigned long)18446744073709551615ULL); + tiny_printf("%lu", (unsigned long)18446744073709551615ULL); ck_assert_str_eq(uart_buf, "18446744073709551615"); reset_uart_buf(); - uart_printf("%zu", (size_t)4294967296ULL); + tiny_printf("%zu", (size_t)4294967296ULL); ck_assert_str_eq(uart_buf, "4294967296"); reset_uart_buf(); - uart_printf("%p", (void*)(uintptr_t)0x1234567890ULL); + tiny_printf("%p", (void*)(uintptr_t)0x1234567890ULL); ck_assert_str_eq(uart_buf, "0x1234567890"); reset_uart_buf(); - uart_printf("%lx", (unsigned long)0xABCDEF0123ULL); + tiny_printf("%lx", (unsigned long)0xABCDEF0123ULL); ck_assert_str_eq(uart_buf, "ABCDEF0123"); reset_uart_buf(); - uart_printf("%ld", (long)-1234567890123LL); + tiny_printf("%ld", (long)-1234567890123LL); ck_assert_str_eq(uart_buf, "-1234567890123"); reset_uart_buf(); - uart_printf("%lld", (long long)-1234567890123LL); + tiny_printf("%lld", (long long)-1234567890123LL); ck_assert_str_eq(uart_buf, "-1234567890123"); reset_uart_buf(); - uart_printf("%d", 42); + tiny_printf("%d", 42); ck_assert_str_eq(uart_buf, "42"); } END_TEST /* F-11048: a negative '*' width must not reach the zero-pad memset * as a huge size_t. */ -START_TEST(test_uart_printf_negative_star_width) +START_TEST(test_tiny_printf_negative_star_width) { reset_uart_buf(); - uart_printf("%0*x", -3, 0x2a); + tiny_printf("%0*x", -3, 0x2a); /* clamped to 0 -> default 8-digit zero pad */ ck_assert_str_eq(uart_buf, "0000002A"); reset_uart_buf(); - uart_printf("%0*llu", -1, 0x123ULL); + tiny_printf("%0*llu", -1, 0x123ULL); ck_assert_str_eq(uart_buf, "00000291"); } END_TEST @@ -553,10 +553,10 @@ Suite *string_suite(void) #endif #endif tcase_add_test(tcase_misc, test_memcpy_aligned_buffers); - tcase_add_test(tcase_misc, test_uart_writenum_basic); - tcase_add_test(tcase_misc, test_uart_printf_formats); - tcase_add_test(tcase_misc, test_uart_printf_64bit_args); - tcase_add_test(tcase_misc, test_uart_printf_negative_star_width); + tcase_add_test(tcase_misc, test_write_num_basic); + tcase_add_test(tcase_misc, test_tiny_printf_formats); + tcase_add_test(tcase_misc, test_tiny_printf_64bit_args); + tcase_add_test(tcase_misc, test_tiny_printf_negative_star_width); suite_add_tcase(s, tcase_strncasecmp); suite_add_tcase(s, tcase_misc);