Skip to content

Commit 60e9ab9

Browse files
author
Memfault Inc
committed
Memfault Firmware SDK 1.28.0 (Build 14860)
1 parent e59fd95 commit 60e9ab9

17 files changed

+365
-226
lines changed

CHANGELOG.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,71 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to
77
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
88

9+
## [1.28.0] - 2025-08-04
10+
11+
This is a minor update release. Highlights:
12+
13+
- added a new test command for simulating hangs in ISRs
14+
- improved NMI exception capture
15+
- fixed a build issue in the nRF-Connect SDK port
16+
17+
### 📈 Added
18+
19+
- Zephyr:
20+
21+
- Add a new test command, `mflt test isr_hang`, which will cause a busy loop
22+
hang inside a `k_timer`, which normally is executing from an ISR context.
23+
The system will only exit this condition if there is a watchdog or other
24+
higher-priority interrupt capable of preempting the `k_timer` ISR. The
25+
[`qemu` sample app](examples/zephyr/qemu) is updated to enable a watchdog
26+
which generates an NMI exception, caught by Memfault.
27+
28+
- Added a debug print when uploading data using
29+
`CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD` that shows the bytes sent:
30+
31+
```bash
32+
[00:09:45.821,000] <dbg> mflt: memfault_platform_log: Uploaded 118 bytes of Memfault data
33+
```
34+
35+
Only enabled when debug level prints are enabled for Memfault
36+
(`CONFIG_MEMFAULT_LOG_LEVEL_DBG=y`).
37+
38+
### 🛠️ Changed
39+
40+
- Zephyr:
41+
42+
- NMI exceptions are now properly handled. Prior to this change, a coredump
43+
was captured on NMI exceptions but the MSP context was not fully unwound and
44+
the NVIC state was not included in the trace data.
45+
46+
- Add a new Kconfig option,
47+
`CONFIG_MEMFAULT_COREDUMP_NVIC_INTERRUPTS_TO_COLLECT`, which controls the
48+
existing `memfault_platform_config.h` setting
49+
`MEMFAULT_NVIC_INTERRUPTS_TO_COLLECT` for Cortex-M targets. The default now
50+
is to collect all `NUM_IRQS` as defined by Zephyr, which on many platforms
51+
will increase from the previous default of `32`. This improves the
52+
out-of-the-box information at the cost of 44 bytes in coredump storage
53+
consumed per additional 32 interrupts. Some example deltas shown below,
54+
including the byte delta in the stored coredump:
55+
56+
| Platform | Previous Default | New Default | Byte Delta |
57+
| --------- | ---------------- | ----------------------------------------------- | ---------- |
58+
| nRF52840 | 32 | 64 (\*48, rounded up to nearest multiple of 32) | +44 |
59+
| nRF91 | 32 | 96 (\*65 rounded up) | +88 |
60+
| nRF53 | 32 | 96 (\*69 rounded up) | +88 |
61+
| STM32F407 | 32 | 96 (\*82 rounded up) | +88 |
62+
| STM32H7B0 | 32 | 160 (\*155 rounded up) | +176 |
63+
| STM32H563 | 32 | 160 (\*155 rounded up) | +176 |
64+
65+
To restore the previous default, set
66+
`CONFIG_MEMFAULT_COREDUMP_NVIC_INTERRUPTS_TO_COLLECT=32`.
67+
68+
- nRF-Connect SDK:
69+
70+
- Fix a build issue impacting some nRF54 series chips related to reboot reason
71+
decoding. Thanks to [@grochu](https://github.com/grochu) for providing the
72+
fix in [#94](https://github.com/memfault/memfault-firmware-sdk/pull/94) 🎉!
73+
974
## [1.27.0] - 2025-07-21
1075

1176
### 📈 Added

VERSION

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
BUILD ID: 14706
2-
GIT COMMIT: d002984f03
3-
VERSION: 1.27.0
1+
BUILD ID: 14860
2+
GIT COMMIT: 4678716f67
3+
VERSION: 1.28.0

components/include/memfault/default_config.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,7 @@ extern "C" {
432432
// the "ISR Analysis" tab for the Issue analyzed in the Memfault UI about the
433433
// appropriate setting needed.
434434
//
435-
// NB: For each additional 32 NVIC interrupts analyzed, 40 extra bytes are
435+
// NB: For each additional 32 NVIC interrupts analyzed, 44 extra bytes are
436436
// needed for coredump storage.
437437
#ifndef MEMFAULT_NVIC_INTERRUPTS_TO_COLLECT
438438
#define MEMFAULT_NVIC_INTERRUPTS_TO_COLLECT 32

components/include/memfault/version.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ typedef struct {
2020
} sMfltSdkVersion;
2121

2222
#define MEMFAULT_SDK_VERSION \
23-
{ .major = 1, .minor = 27, .patch = 0 }
24-
#define MEMFAULT_SDK_VERSION_STR "1.27.0"
23+
{ .major = 1, .minor = 28, .patch = 0 }
24+
#define MEMFAULT_SDK_VERSION_STR "1.28.0"
2525

2626
#ifdef __cplusplus
2727
}

components/panics/src/memfault_coredump_regions_armv7.c

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,16 @@
2020
#include "memfault/panics/coredump_impl.h"
2121
#include "memfault/panics/platform/coredump.h"
2222

23-
MEMFAULT_STATIC_ASSERT(((MEMFAULT_NVIC_INTERRUPTS_TO_COLLECT) % 32 == 0) ||
24-
((MEMFAULT_NVIC_INTERRUPTS_TO_COLLECT) == 496),
23+
//! Round up to the nearest multiple of 32. Interrupt state is collected in
24+
//! groups of 32 due to register layout.
25+
#define MEMFAULT_NVIC_INTERRUPTS_TO_COLLECT_ROUNDED_UP \
26+
((MEMFAULT_NVIC_INTERRUPTS_TO_COLLECT + 31) / 32 * 32)
27+
28+
MEMFAULT_STATIC_ASSERT(((MEMFAULT_NVIC_INTERRUPTS_TO_COLLECT_ROUNDED_UP) % 32 == 0) ||
29+
((MEMFAULT_NVIC_INTERRUPTS_TO_COLLECT_ROUNDED_UP) == 496),
2530
"Must be a multiple of 32 or 496");
26-
MEMFAULT_STATIC_ASSERT((MEMFAULT_NVIC_INTERRUPTS_TO_COLLECT) <= 512, "Exceeded max possible size");
31+
MEMFAULT_STATIC_ASSERT((MEMFAULT_NVIC_INTERRUPTS_TO_COLLECT_ROUNDED_UP) <= 512,
32+
"Exceeded max possible size");
2733

2834
MEMFAULT_STATIC_ASSERT((MEMFAULT_MPU_REGIONS_TO_COLLECT) <= 16, "Exceeded max possible size");
2935

@@ -74,14 +80,14 @@ sMfltDebugExcMonCtrlReg;
7480
typedef MEMFAULT_PACKED_STRUCT {
7581
// representation for NVIC ISER, ISPR, and IABR ...
7682
// A single bit encodes whether or not the interrupt is enabled, pending, active, respectively.
77-
uint32_t IxxR[(MEMFAULT_NVIC_INTERRUPTS_TO_COLLECT + 31) / 32];
83+
uint32_t IxxR[MEMFAULT_NVIC_INTERRUPTS_TO_COLLECT_ROUNDED_UP / 32];
7884
}
7985
sMfltNvicIserIsprIabr;
8086

8187
typedef MEMFAULT_PACKED_STRUCT {
8288
// 8 bits are used to encode the priority so 4 interrupts are covered by each register
8389
// NB: unimplemented priority levels read back as 0
84-
uint32_t IPR[MEMFAULT_NVIC_INTERRUPTS_TO_COLLECT / 4];
90+
uint32_t IPR[MEMFAULT_NVIC_INTERRUPTS_TO_COLLECT_ROUNDED_UP / 4];
8591
}
8692
sMfltNvicIpr;
8793

@@ -168,6 +174,14 @@ const sMfltCoredumpRegion *memfault_coredump_get_arch_regions(size_t *num_region
168174
}
169175
#endif /* MEMFAULT_COLLECT_MPU_STATE */
170176

177+
#if MEMFAULT_CACHE_FAULT_REGS
178+
// Cache the faults registers if it hasn't happened already
179+
sMfltCachedBlock *fault_regs = (sMfltCachedBlock *)&s_cached_fault_regs[0];
180+
if (!fault_regs->valid_cache) {
181+
memfault_coredump_cache_fault_regs();
182+
}
183+
#endif
184+
171185
static const sMfltCoredumpRegion s_coredump_regions[] = {
172186
{ .type = FAULT_REG_REGION_TYPE,
173187
.region_start = (void *)FAULT_REG_REGION_START,
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,8 @@
11
CONFIG_MPU_STACK_GUARD=y
2+
CONFIG_WATCHDOG=y
3+
CONFIG_WDOG_CMSDK_APB_START_AT_BOOT=y
4+
5+
# This isn't actually needed- it just adds a reboot in the driver, and we wrap
6+
# the NMI handler anyway, but there is a Kconfig dependency in the CMSDK APB
7+
# watchdog driver so we are forced to include it.
8+
CONFIG_RUNTIME_NMI=y

examples/zephyr/qemu/qemu-app/src/main.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,26 @@ uint64_t memfault_platform_get_time_since_boot_ms(void) {
366366
}
367367
#endif
368368

369+
#if defined(CONFIG_WDOG_CMSDK_APB_START_AT_BOOT)
370+
#include MEMFAULT_ZEPHYR_INCLUDE(drivers/watchdog.h)
371+
// Watchdog feed timer
372+
static void prv_watchdog_feed_timer_handler(struct k_timer *dummy) {
373+
ARG_UNUSED(dummy);
374+
const struct device *const wdt = DEVICE_DT_GET(DT_ALIAS(watchdog0));
375+
// the CMSDK WDOG doesn't use a channel, pass 0
376+
wdt_feed(wdt, 0);
377+
}
378+
K_TIMER_DEFINE(s_watchdog_feed_timer, prv_watchdog_feed_timer_handler, NULL);
379+
380+
static int prv_start_watchdog_feed_timer() {
381+
// Start the watchdog feed timer to prevent the watchdog from resetting the system
382+
k_timer_start(&s_watchdog_feed_timer, K_MSEC(100), K_MSEC(100));
383+
return 0;
384+
}
385+
386+
SYS_INIT(prv_start_watchdog_feed_timer, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
387+
#endif // CONFIG_WDOG_CMSDK_APB_START_AT_BOOT
388+
369389
int main(void) {
370390
LOG_INF("👋 Memfault Demo App! Board %s\n", CONFIG_BOARD);
371391
memfault_device_info_dump();

ports/zephyr/Kconfig

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ config MEMFAULT
22
bool "Memfault Support"
33
default n
44
depends on CPU_CORTEX_M || RISCV || SOC_SERIES_ESP32 || SOC_SERIES_ESP32S3 || ARCH_POSIX
5-
select RUNTIME_NMI if CPU_CORTEX_M
65
select EXTRA_EXCEPTION_INFO if CPU_CORTEX_M
76
select DEBUG_THREAD_INFO
87
select REBOOT if ARCH_POSIX
@@ -182,6 +181,17 @@ config MEMFAULT_COREDUMP_MPU_REGIONS_TO_COLLECT
182181
The maximum amount of MPU regions Memfault will store state for in a
183182
coredump.
184183

184+
config MEMFAULT_COREDUMP_NVIC_INTERRUPTS_TO_COLLECT
185+
int "Maximum amount of NVIC interrupts to collect in a coredump"
186+
default NUM_IRQS
187+
range 32 512
188+
depends on CPU_CORTEX_M
189+
help
190+
The maximum amount of NVIC interrupts Memfault will store state for in
191+
a coredump. This is useful for debugging interrupt state at the time
192+
of the fault. Each additional 32 interrupts collected uses 44 bytes of
193+
coredump storage.
194+
185195
endmenu # Memfault Coredump Settings
186196

187197
config MEMFAULT_HEAP_STATS

ports/zephyr/common/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ zephyr_library_sources(memfault_platform_system_time.c)
99
zephyr_library_sources_ifdef(CONFIG_MEMFAULT_HTTP_ENABLE memfault_platform_http.c)
1010
zephyr_library_sources_ifdef(CONFIG_MEMFAULT_HTTP_ENABLE memfault_platform_fota.c)
1111
zephyr_library_sources_ifdef(CONFIG_MEMFAULT_METRICS_THREADS memfault_platform_thread_metrics.c)
12+
zephyr_library_sources_ifdef(CONFIG_MEMFAULT_METRICS_WIFI memfault_platform_metrics_wifi.c)
1213
zephyr_library_sources_ifdef(CONFIG_MEMFAULT_RAM_BACKED_COREDUMP memfault_platform_ram_backed_coredump.c)
1314
zephyr_library_sources_ifdef(CONFIG_MEMFAULT_ROOT_CERT_STORAGE_TLS_CREDENTIAL_STORAGE memfault_tls_root_cert_storage.c)
1415
zephyr_library_sources_ifdef(CONFIG_MEMFAULT_SHELL memfault_demo_cli.c)

ports/zephyr/common/memfault_demo_cli.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,24 @@ static int prv_timer_isr_crash_example(const struct shell *shell, size_t argc, c
327327
return 0;
328328
}
329329

330+
static void prv_hang_timer_handler(struct k_timer *dummy) {
331+
while (1) {
332+
k_busy_wait(1000);
333+
}
334+
}
335+
336+
K_TIMER_DEFINE(s_isr_hang_timer, prv_hang_timer_handler, NULL);
337+
338+
static int prv_timer_isr_hang_example(const struct shell *shell, size_t argc, char **argv) {
339+
#if !defined(CONFIG_WATCHDOG)
340+
shell_print(shell, "Hanging in ISR. Warning, no watchdog configured, this may hang forever!");
341+
#else
342+
shell_print(shell, "Hanging in ISR, waiting for watchdog to trigger");
343+
#endif
344+
k_timer_start(&s_isr_hang_timer, K_MSEC(10), K_MSEC(10));
345+
return 0;
346+
}
347+
330348
#if defined(CONFIG_MEMFAULT_SHELL_SELF_TEST)
331349
static int prv_self_test(MEMFAULT_UNUSED const struct shell *shell, size_t argc, char **argv) {
332350
return memfault_demo_cli_cmd_self_test(argc, argv);
@@ -374,6 +392,7 @@ SHELL_STATIC_SUBCMD_SET_CREATE(
374392
SHELL_CMD(badptr, NULL, "trigger fault via store to a bad address", prv_bad_ptr_deref_example),
375393
SHELL_CMD(isr_badptr, NULL, "trigger fault via store to a bad address from an ISR",
376394
prv_timer_isr_crash_example),
395+
SHELL_CMD(isr_hang, NULL, "trigger a hang in an ISR", prv_timer_isr_hang_example),
377396

378397
//! user initiated reboot
379398
SHELL_CMD(reboot, NULL, "trigger a reboot and record it using memfault", prv_test_reboot),

0 commit comments

Comments
 (0)