Zephyr + ESP32-C3 support, RC1
Based on:
f4c4de30c20ca2511dbff725dd678cb03e6c1bc1
diff --git a/components/demo/src/panics/memfault_demo_panics.c b/components/demo/src/panics/memfault_demo_panics.c
index 7b59880..e2e253f 100644
--- a/components/demo/src/panics/memfault_demo_panics.c
+++ b/components/demo/src/panics/memfault_demo_panics.c
@@ -195,18 +195,6 @@
return -1;
}
-int memfault_demo_cli_loadaddr(int argc, char *argv[]) {
- if (argc < 2) {
- MEMFAULT_LOG_ERROR("Usage: loadaddr <addr>");
- return -1;
- }
- uint32_t addr = (uint32_t)strtoul(argv[1], NULL, 0);
- uint32_t val = *(uint32_t *)addr;
-
- MEMFAULT_LOG_INFO("Read 0x%08" PRIx32 " from 0x%08" PRIx32, val, (uint32_t)(uintptr_t)addr);
- return 0;
-}
-
#endif // MEMFAULT_COMPILER_ARM_CORTEX_M
#if MEMFAULT_COMPILER_ARM_V7_A_R
@@ -229,3 +217,15 @@
}
#endif // MEMFAULT_COMPILER_ARM_V7_A_R
+
+int memfault_demo_cli_loadaddr(int argc, char *argv[]) {
+ if (argc < 2) {
+ MEMFAULT_LOG_ERROR("Usage: loadaddr <addr>");
+ return -1;
+ }
+ uint32_t addr = (uint32_t)strtoul(argv[1], NULL, 0);
+ uint32_t val = *(uint32_t *)addr;
+
+ MEMFAULT_LOG_INFO("Read 0x%08" PRIx32 " from 0x%08" PRIx32, val, (uint32_t)(uintptr_t)addr);
+ return 0;
+}
diff --git a/components/include/memfault/core/compiler_gcc.h b/components/include/memfault/core/compiler_gcc.h
index aa92ef8..a7be7d5 100644
--- a/components/include/memfault/core/compiler_gcc.h
+++ b/components/include/memfault/core/compiler_gcc.h
@@ -55,6 +55,13 @@
# define MEMFAULT_GET_LR(_a) _a = __builtin_return_address(0)
# define MEMFAULT_GET_PC(_a) _a = ({ __label__ _l; _l: &&_l;});
# define MEMFAULT_BREAKPOINT(val) __asm__ ("break 0,0")
+#elif defined(__riscv)
+# define MEMFAULT_GET_LR(_a) _a = __builtin_return_address(0)
+// Take advantage of "Locally Declared Labels" to get a PC
+// https://gcc.gnu.org/onlinedocs/gcc/Local-Labels.html#Local-Labels
+# define MEMFAULT_GET_PC(_a) _a = ({ __label__ _l; _l: &&_l;});
+// Trigger a breakpoint exception (similar to bkpt instruction in ARM)
+#define MEMFAULT_BREAKPOINT(val) __asm volatile("ebreak")
#elif defined(MEMFAULT_UNITTEST) || defined(__APPLE__) // Memfault iOS SDK also #includes this header
# define MEMFAULT_GET_LR(_a) _a = 0
# define MEMFAULT_GET_PC(_a) _a = 0
diff --git a/components/include/memfault/demo/cli.h b/components/include/memfault/demo/cli.h
index 0be1b9d..b51d7f0 100644
--- a/components/include/memfault/demo/cli.h
+++ b/components/include/memfault/demo/cli.h
@@ -39,12 +39,12 @@
//! Command which will generate a UsageFault on Cortex-M hardware
int memfault_demo_cli_cmd_usagefault(int argc, char *argv[]);
+#endif // MEMFAULT_COMPILER_ARM_CORTEX_M
+
//! Read a 32-bit memory address and print the value. Can be used to test
//! specific faults due to protected regions
int memfault_demo_cli_loadaddr(int argc, char *argv[]);
-#endif // MEMFAULT_COMPILER_ARM_CORTEX_M
-
#if MEMFAULT_COMPILER_ARM_V7_A_R
//! Trigger a data abort on an ARMv7-A/R chip
int memfault_demo_cli_cmd_dataabort(int argc, char *argv[]);
diff --git a/components/panics/src/memfault_fault_handling_riscv.c b/components/panics/src/memfault_fault_handling_riscv.c
index 7dc26ec..065f9f8 100644
--- a/components/panics/src/memfault_fault_handling_riscv.c
+++ b/components/panics/src/memfault_fault_handling_riscv.c
@@ -17,6 +17,7 @@
#include "memfault/panics/arch/riscv/riscv.h"
#include "memfault/panics/coredump.h"
#include "memfault/panics/coredump_impl.h"
+ #include "memfault/panics/fault_handling.h"
const sMfltCoredumpRegion *memfault_coredump_get_arch_regions(size_t *num_regions) {
*num_regions = 0;
@@ -42,6 +43,66 @@
prv_fault_handling_assert(pc, lr, reason);
}
+// For non-esp-idf riscv implementations, provide a full assert handler and
+// other utilities.
+ #if defined(__ZEPHYR__) && defined(CONFIG_SOC_FAMILY_ESP32)
+
+ #include "hal/cpu_hal.h"
+
+void memfault_platform_halt_if_debugging(void) {
+ if (cpu_ll_is_debugger_attached()) {
+ MEMFAULT_BREAKPOINT();
+ }
+}
+
+static inline uint32_t prv_read_mstatus(void) {
+ uint32_t mstatus;
+ __asm volatile("csrr %0, mstatus" : "=r"(mstatus));
+ return mstatus;
+}
+
+bool memfault_arch_is_inside_isr(void) {
+ // Read the value of mstatus CSR
+ uint32_t mstatus = prv_read_mstatus();
+
+ // Check the MPIE (Machine Previous Interrupt Enable) bit
+ // If MPIE is set, then the processor is inside an ISR
+ return (mstatus & (1U << 7)) != 0;
+}
+
+static void prv_fault_handling_assert_native(void *pc, void *lr, eMemfaultRebootReason reason) {
+ prv_fault_handling_assert(pc, lr, reason);
+
+ #if MEMFAULT_ASSERT_HALT_IF_DEBUGGING_ENABLED
+ memfault_platform_halt_if_debugging();
+ #endif
+
+ // dereference a null pointer to trigger fault
+ *(uint32_t *)0 = 0x90;
+
+ // We just trap'd into the fault handler logic so it should never be possible to get here but if
+ // we do the best thing that can be done is rebooting the system to recover it.
+ memfault_platform_reboot();
+}
+
+MEMFAULT_NO_OPT
+void memfault_fault_handling_assert_extra(void *pc, void *lr, sMemfaultAssertInfo *extra_info) {
+ prv_fault_handling_assert_native(pc, lr, extra_info->assert_reason);
+
+ MEMFAULT_UNREACHABLE;
+}
+
+MEMFAULT_NO_OPT
+void memfault_fault_handling_assert(void *pc, void *lr) {
+ prv_fault_handling_assert_native(pc, lr, kMfltRebootReason_Assert);
+
+ MEMFAULT_UNREACHABLE;
+}
+
+ #elif !defined(ESP_PLATFORM)
+ #error "Unsupported RISC-V platform, please contact [email protected]"
+ #endif // !defined(ESP_PLATFORM) && defined(__ZEPHYR__)
+
void memfault_fault_handler(const sMfltRegState *regs, eMemfaultRebootReason reason) {
if (s_crash_reason == kMfltRebootReason_Unknown) {
// TODO confirm this works correctly- we should have the correct
diff --git a/examples/esp32/apps/memfault_demo_app/main/Kconfig.projbuild b/examples/esp32/apps/memfault_demo_app/main/Kconfig.projbuild
index c05b5a9..e134029 100644
--- a/examples/esp32/apps/memfault_demo_app/main/Kconfig.projbuild
+++ b/examples/esp32/apps/memfault_demo_app/main/Kconfig.projbuild
@@ -2,11 +2,12 @@
config STORE_HISTORY
bool "Store command history in flash"
- default y
+ default n
help
Linenoise line editing library provides functions to save and load
command history. If this option is enabled, initalizes a FAT filesystem
and uses it to store command history.
+ Note that this adds a ~22kB heap allocation on system boot.
config MEMFAULT_APP_OTA
bool "Enable automatic periodic check+update for OTA"
diff --git a/examples/esp32/apps/memfault_demo_app/main/cmd_app.c b/examples/esp32/apps/memfault_demo_app/main/cmd_app.c
index 2ecc23f..d5e01a4 100644
--- a/examples/esp32/apps/memfault_demo_app/main/cmd_app.c
+++ b/examples/esp32/apps/memfault_demo_app/main/cmd_app.c
@@ -11,6 +11,7 @@
#include "cmd_decl.h"
#include "esp_console.h"
+#include "esp_heap_task_info.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
@@ -110,6 +111,47 @@
// !defined(CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE)
#endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0)
+#if defined(CONFIG_HEAP_TASK_TRACKING)
+// Print out per-task heap allocations. This is lifted from the example here:
+// https://github.com/espressif/esp-idf/blob/v5.1.2/examples/system/heap_task_tracking/main/heap_task_tracking_main.c
+static int prv_heap_task_stats(MEMFAULT_UNUSED int argc, MEMFAULT_UNUSED char **argv) {
+ #define MAX_TASK_NUM 10 // Max number of per tasks info that it can store
+ #define MAX_BLOCK_NUM 10 // Max number of per block info that it can store
+
+ static size_t s_prepopulated_num = 0;
+ static heap_task_totals_t s_totals_arr[MAX_TASK_NUM];
+ static heap_task_block_t s_block_arr[MAX_BLOCK_NUM];
+
+ heap_task_info_params_t heap_info = {0};
+ heap_info.caps[0] = MALLOC_CAP_8BIT; // Gets heap with CAP_8BIT capabilities
+ heap_info.mask[0] = MALLOC_CAP_8BIT;
+ heap_info.caps[1] = MALLOC_CAP_32BIT; // Gets heap info with CAP_32BIT capabilities
+ heap_info.mask[1] = MALLOC_CAP_32BIT;
+ heap_info.tasks = NULL; // Passing NULL captures heap info for all tasks
+ heap_info.num_tasks = 0;
+ heap_info.totals = s_totals_arr; // Gets task wise allocation details
+ heap_info.num_totals = &s_prepopulated_num;
+ heap_info.max_totals = MAX_TASK_NUM; // Maximum length of "s_totals_arr"
+ heap_info.blocks = s_block_arr; // Gets block wise allocation details. For each block, gets owner
+ // task, address and size
+ heap_info.max_blocks = MAX_BLOCK_NUM; // Maximum length of "s_block_arr"
+
+ heap_caps_get_per_task_info(&heap_info);
+
+ for (int i = 0; i < *heap_info.num_totals; i++) {
+ printf(
+ "Task: %s -> CAP_8BIT: %d CAP_32BIT: %d\n",
+ heap_info.totals[i].task ? pcTaskGetName(heap_info.totals[i].task) : "Pre-Scheduler allocs",
+ heap_info.totals[i].size[0], // Heap size with CAP_8BIT capabilities
+ heap_info.totals[i].size[1]); // Heap size with CAP32_BIT capabilities
+ }
+
+ printf("\n\n");
+
+ return 0;
+}
+#endif // CONFIG_HEAP_TASK_TRACKING
+
void register_app(void) {
#if MEMFAULT_TASK_WATCHDOG_ENABLE
const esp_console_cmd_t test_watchdog_cmd = {
@@ -121,6 +163,16 @@
ESP_ERROR_CHECK(esp_console_cmd_register(&test_watchdog_cmd));
#endif
+#if defined(CONFIG_HEAP_TASK_TRACKING)
+ const esp_console_cmd_t heap_task_stats_cmd = {
+ .command = "heap_task_stats",
+ .help = "Prints heap usage per task",
+ .hint = NULL,
+ .func = &prv_heap_task_stats,
+ };
+ ESP_ERROR_CHECK(esp_console_cmd_register(&heap_task_stats_cmd));
+#endif
+
// Only support the stack overflow test on esp-idf >= 4.3.0
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0)
#if defined(CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK) || \
diff --git a/examples/esp32/apps/memfault_demo_app/main/console_example_main.c b/examples/esp32/apps/memfault_demo_app/main/console_example_main.c
index 9ab2416..0cdc351 100644
--- a/examples/esp32/apps/memfault_demo_app/main/console_example_main.c
+++ b/examples/esp32/apps/memfault_demo_app/main/console_example_main.c
@@ -35,7 +35,10 @@
#include "nvs_flash.h"
#include "settings.h"
-static const char *TAG = "example";
+// Conditionally enable the logging tag variable only when it's used
+#if defined(CONFIG_STORE_HISTORY) || defined(CONFIG_HEAP_USE_HOOKS)
+static const char *TAG = "main";
+#endif
/* Console command history can be stored to and loaded from a file.
* The easiest way to do this is to use FATFS filesystem on top of
@@ -121,7 +124,7 @@
linenoiseSetHintsCallback((linenoiseHintsCallback *)&esp_console_get_hint);
/* Set command history size */
- linenoiseHistorySetMaxLen(100);
+ linenoiseHistorySetMaxLen(10);
#if CONFIG_STORE_HISTORY
/* Load command history from filesystem */
@@ -333,11 +336,11 @@
// In our app, there's a periodic 1696 byte alloc. Filter out anything that
// size or smaller from this log, otherwise it's quite spammy
if (size > 1696) {
- ESP_LOGI(TAG, "Large alloc: %p, size: %d, caps: %lu", ptr, size, caps);
+ ESP_LOGI("main", "Large alloc: %p, size: %d, caps: %lu", ptr, size, caps);
multi_heap_info_t heap_info = {0};
heap_caps_get_info(&heap_info, MALLOC_CAP_DEFAULT);
- ESP_LOGI(TAG, "Total free bytes: %d", heap_info.total_free_bytes);
+ ESP_LOGI("main", "Total free bytes: %d", heap_info.total_free_bytes);
}
}
#endif
diff --git a/examples/esp32/apps/memfault_demo_app/sdkconfig.defaults b/examples/esp32/apps/memfault_demo_app/sdkconfig.defaults
index 582898c..495fa7d 100644
--- a/examples/esp32/apps/memfault_demo_app/sdkconfig.defaults
+++ b/examples/esp32/apps/memfault_demo_app/sdkconfig.defaults
@@ -7,6 +7,9 @@
CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=3072
CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=3072
+# Enable watchpoint stack overflow guard
+CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y
+
# Enable filesystem
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv"
diff --git a/examples/esp32/apps/memfault_demo_app/sdkconfig.heaptrace b/examples/esp32/apps/memfault_demo_app/sdkconfig.heaptrace
new file mode 100644
index 0000000..8c2445b
--- /dev/null
+++ b/examples/esp32/apps/memfault_demo_app/sdkconfig.heaptrace
@@ -0,0 +1,4 @@
+# Heap tracing configs
+CONFIG_HEAP_POISONING_LIGHT=y
+CONFIG_HEAP_TASK_TRACKING=y
+CONFIG_HEAP_USE_HOOKS=y
diff --git a/examples/zephyr/qemu/qemu-app/config/memfault_platform_config.h b/examples/zephyr/qemu/qemu-app/config/memfault_platform_config.h
new file mode 100644
index 0000000..32b508a
--- /dev/null
+++ b/examples/zephyr/qemu/qemu-app/config/memfault_platform_config.h
@@ -0,0 +1,20 @@
+#pragma once
+
+//! @file
+//!
+//! @brief
+//! Platform overrides for the default configuration settings in the memfault-firmware-sdk.
+//! Default configuration settings can be found in "memfault/config.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(CONFIG_SOC_FAMILY_ESP32)
+ #define ZEPHYR_DATA_REGION_START _data_start
+ #define ZEPHYR_DATA_REGION_END _data_end
+#endif
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/examples/zephyr/qemu/qemu-app/src/main.c b/examples/zephyr/qemu/qemu-app/src/main.c
index 67a0884..0c5cb82 100644
--- a/examples/zephyr/qemu/qemu-app/src/main.c
+++ b/examples/zephyr/qemu/qemu-app/src/main.c
@@ -38,9 +38,7 @@
// Blink code taken from the zephyr/samples/basic/blinky example.
static void blink_forever(void) {
-#if CONFIG_QEMU_TARGET
- k_sleep(K_FOREVER);
-#else
+#if DT_NODE_HAS_PROP(DT_ALIAS(led0), gpios)
/* 1000 msec = 1 sec */
#define SLEEP_TIME_MS 1000
@@ -58,7 +56,10 @@
gpio_pin_toggle_dt(&led);
k_msleep(SLEEP_TIME_MS);
}
-#endif // CONFIG_QEMU_TARGET
+#else
+ // no led on this board, just sleep forever
+ k_sleep(K_FOREVER);
+#endif
}
void memfault_platform_get_device_info(sMemfaultDeviceInfo *info) {
diff --git a/examples/zephyr/qemu/qemu-app/west.yml b/examples/zephyr/qemu/qemu-app/west.yml
index 32fe7d0..5a135eb 100644
--- a/examples/zephyr/qemu/qemu-app/west.yml
+++ b/examples/zephyr/qemu/qemu-app/west.yml
@@ -18,6 +18,7 @@
# Limit the Zephyr modules to the required set
name-allowlist:
- cmsis
+ - hal_espressif
- name: memfault-firmware-sdk
path: modules/lib/memfault-firmware-sdk
diff --git a/ports/esp_idf/memfault/Kconfig b/ports/esp_idf/memfault/Kconfig
index 6b00b28..dbeff8b 100644
--- a/ports/esp_idf/memfault/Kconfig
+++ b/ports/esp_idf/memfault/Kconfig
@@ -150,8 +150,12 @@
config MEMFAULT_ASSERT_ON_ALLOC_FAILURE
bool "Assert on allocation failure"
default n
+ depends on !HEAP_ABORT_WHEN_ALLOCATION_FAILS
help
When enabled, the Memfault SDK will assert if any allocation fails.
- This can be useful for tracking down heap memory issues.
+ This can be useful for tracking down heap memory issues. Note that
+ this operates similarly to HEAP_ABORT_WHEN_ALLOCATION_FAILS, but the
+ Memfault Issue created will be tagged as "Out of Memory" instead of
+ a generic "Assert".
endmenu
diff --git a/ports/zephyr/CMakeLists.txt b/ports/zephyr/CMakeLists.txt
index b0d5405..139ccd9 100644
--- a/ports/zephyr/CMakeLists.txt
+++ b/ports/zephyr/CMakeLists.txt
@@ -18,9 +18,15 @@
list(APPEND MEMFAULT_COMPONENTS metrics)
endif()
+ if(CONFIG_RISCV)
+ set(MEMFAULT_ARCH "ARCH_RISCV")
+ else()
+ set(MEMFAULT_ARCH "ARCH_ARM_CORTEX_M")
+ endif()
+
include(${MEMFAULT_SDK_ROOT}/cmake/Memfault.cmake)
memfault_library(${MEMFAULT_SDK_ROOT} MEMFAULT_COMPONENTS
- MEMFAULT_COMPONENTS_SRCS MEMFAULT_COMPONENTS_INC_FOLDERS)
+ MEMFAULT_COMPONENTS_SRCS MEMFAULT_COMPONENTS_INC_FOLDERS ${MEMFAULT_ARCH})
# Add Memfault SDK sources to memfault library
zephyr_interface_library_named(memfault)
@@ -70,4 +76,30 @@
zephyr_include_directories(${ZEPHYR_BASE}/include/zephyr)
endif()
+ # If enabled, apply a post-build step to generate a Memfault build id
+ if(CONFIG_MEMFAULT_USE_MEMFAULT_BUILD_ID)
+ get_filename_component(
+ memfault_fw_build_id_script
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../scripts/fw_build_id.py
+ ABSOLUTE
+ )
+ # force our command to be at the front of the list so it runs before any
+ # other operations on the .elf file, including binary/OTA image generation
+ get_property(
+ PROPERTY_EXTRA_POST_BUILD_COMMANDS
+ GLOBAL
+ PROPERTY extra_post_build_commands
+ )
+ set_property(
+ GLOBAL
+ PROPERTY extra_post_build_commands
+ COMMAND
+ ${PYTHON_EXECUTABLE}
+ ${memfault_fw_build_id_script}
+ ${CMAKE_BINARY_DIR}/zephyr/${CONFIG_KERNEL_BIN_NAME}.elf
+ # now append the rest of the commands
+ ${PROPERTY_EXTRA_POST_BUILD_COMMANDS}
+ )
+ endif()
+
endif()
diff --git a/ports/zephyr/Kconfig b/ports/zephyr/Kconfig
index 325ec1c..2673e83 100644
--- a/ports/zephyr/Kconfig
+++ b/ports/zephyr/Kconfig
@@ -1,9 +1,9 @@
config MEMFAULT
bool "Memfault Support"
default n
- depends on CPU_CORTEX_M
+ depends on CPU_CORTEX_M || RISCV
select RUNTIME_NMI
- select EXTRA_EXCEPTION_INFO
+ select EXTRA_EXCEPTION_INFO if CPU_CORTEX_M
select DEBUG_THREAD_INFO
help
Enable Zephyr Integration with the Memfault SDK
@@ -463,8 +463,21 @@
memfault_zephyr_date_time_evt_handler() function should be called from
the application's date_time event handler.
+config MEMFAULT_USE_MEMFAULT_BUILD_ID
+ bool "Use a Memfault build ID instead of a GNU build ID"
+ default y if SOC_FAMILY_ESP32
+ depends on !MEMFAULT_USE_GNU_BUILD_ID
+ help
+ Use a Memfault build ID generated by scripts/fw_build_id.py instead
+ of a GNU build id. This is useful if the board is being flashed with
+ the esptool.py script, which doesn't correctly load the
+ .note.gnu.build-id section.
+
menuconfig MEMFAULT_USE_GNU_BUILD_ID
- default y
+ # esptool.py, the flashing tool for esp32 devices, does not flash the
+ # .note.gnu.build-id section correctly. only enable by default for
+ # non-esp32 devices
+ default y if !SOC_FAMILY_ESP32
bool "Use a GNU build ID in an image"
if MEMFAULT_USE_GNU_BUILD_ID
diff --git a/ports/zephyr/common/memfault-build-id.ld b/ports/zephyr/common/memfault-build-id.ld
index 6490730..ecb3af6 100644
--- a/ports/zephyr/common/memfault-build-id.ld
+++ b/ports/zephyr/common/memfault-build-id.ld
@@ -2,4 +2,4 @@
{
__start_gnu_build_id_start = .;
KEEP(*(.note.gnu.build-id))
-} GROUP_LINK_IN(ROMABLE_REGION)
+} GROUP_ROM_LINK_IN(RODATA_REGION, ROMABLE_REGION)
diff --git a/ports/zephyr/common/memfault_demo_cli.c b/ports/zephyr/common/memfault_demo_cli.c
index 4c260cb..d875e96 100644
--- a/ports/zephyr/common/memfault_demo_cli.c
+++ b/ports/zephyr/common/memfault_demo_cli.c
@@ -188,6 +188,8 @@
return -1;
}
+#if CONFIG_ARM
+
static int prv_busfault_example(const struct shell *shell, size_t argc, char **argv) {
//! Note: The Zephyr fault handler dereferences the pc which triggers a fault
//! if the pc itself is from a bad pointer:
@@ -217,6 +219,8 @@
return -1;
}
+#endif // CONFIG_ARM
+
static int prv_zephyr_assert_example(const struct shell *shell, size_t argc, char **argv) {
#if !CONFIG_ASSERT
shell_print(shell, "CONFIG_ASSERT was disabled in the build, this command will have no effect");
@@ -273,13 +277,17 @@
SHELL_STATIC_SUBCMD_SET_CREATE(
sub_memfault_crash_cmds,
//! different crash types that should result in a coredump being collected
- SHELL_CMD(assert, NULL, "trigger memfault assert", prv_memfault_assert_example),
+
+ #if CONFIG_ARM
SHELL_CMD(busfault, NULL, "trigger a busfault", prv_busfault_example),
- SHELL_CMD(hang, NULL, "trigger a hang", prv_hang_example),
SHELL_CMD(hardfault, NULL, "trigger a hardfault", prv_hardfault_example),
SHELL_CMD(memmanage, NULL, "trigger a memory management fault", prv_memmanage_example),
SHELL_CMD(usagefault, NULL, "trigger a usage fault", prv_usagefault_example),
+ #endif // CONFIG_ARM
+
+ SHELL_CMD(hang, NULL, "trigger a hang", prv_hang_example),
SHELL_CMD(zassert, NULL, "trigger a zephyr assert", prv_zephyr_assert_example),
+ SHELL_CMD(assert, NULL, "trigger memfault assert", prv_memfault_assert_example),
SHELL_CMD(loadaddr, NULL, "test a 32 bit load from an address", prv_zephyr_load_32bit_address),
SHELL_CMD(double_free, NULL, "trigger a double free error", prv_cli_cmd_double_free),
SHELL_CMD(badptr, NULL, "trigger fault via store to a bad address", prv_bad_ptr_deref_example),
diff --git a/ports/zephyr/common/memfault_platform_coredump_regions.c b/ports/zephyr/common/memfault_platform_coredump_regions.c
index b1a64eb..503977e 100644
--- a/ports/zephyr/common/memfault_platform_coredump_regions.c
+++ b/ports/zephyr/common/memfault_platform_coredump_regions.c
@@ -15,6 +15,7 @@
#include "memfault/panics/platform/coredump.h"
#include "memfault/ports/zephyr/version.h"
+#if CONFIG_ARM
#if MEMFAULT_ZEPHYR_VERSION_GT(3, 4)
#include <cmsis_core.h>
#elif MEMFAULT_ZEPHYR_VERSION_GT(2, 1)
@@ -22,6 +23,7 @@
#else
#include MEMFAULT_ZEPHYR_INCLUDE(arch/arm/cortex_m/cmsis.h)
#endif
+#endif
#include "memfault/core/compiler.h"
#include "memfault/core/math.h"
@@ -38,8 +40,6 @@
size_t region_idx = 0;
#if CONFIG_MEMFAULT_COREDUMP_COLLECT_STACK_REGIONS
- const bool msp_was_active = (crash_info->exception_reg_state->exc_return & (1 << 2)) == 0;
-
size_t stack_size_to_collect = memfault_platform_sanitize_address_range(
crash_info->stack_address, CONFIG_MEMFAULT_COREDUMP_STACK_SIZE_TO_COLLECT);
@@ -47,6 +47,9 @@
MEMFAULT_COREDUMP_MEMORY_REGION_INIT(crash_info->stack_address, stack_size_to_collect);
region_idx++;
+ #if CONFIG_ARM
+ const bool msp_was_active = (crash_info->exception_reg_state->exc_return & (1 << 2)) == 0;
+
if (msp_was_active) {
// System crashed in an ISR but the running task state is on PSP so grab that too
void *psp = (void *)(uintptr_t)__get_PSP();
@@ -59,6 +62,7 @@
regions[region_idx] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(psp, stack_size_to_collect);
region_idx++;
}
+ #endif // CONFIG_ARM
#endif
#if CONFIG_MEMFAULT_COREDUMP_COLLECT_KERNEL_REGION
diff --git a/ports/zephyr/common/memfault_zephyr_ram_regions.c b/ports/zephyr/common/memfault_zephyr_ram_regions.c
index c8a35c4..b340c5b 100644
--- a/ports/zephyr/common/memfault_zephyr_ram_regions.c
+++ b/ports/zephyr/common/memfault_zephyr_ram_regions.c
@@ -93,6 +93,11 @@
MEMFAULT_WEAK
size_t memfault_platform_sanitize_address_range(void *start_addr, size_t desired_size) {
+ #if CONFIG_RISCV
+ // Linker script does not define _image_ram_start/end for RISC-V
+ const uint32_t ram_start = 0;
+ const uint32_t ram_end = 0xffffffff;
+ #else
// NB: This only works for MCUs which have a contiguous RAM address range. (i.e Any MCU in the
// nRF53, nRF52, and nRF91 family). All of these MCUs have a contigous RAM address range so it is
// sufficient to just look at _image_ram_start/end from the Zephyr linker script
@@ -101,6 +106,7 @@
const uint32_t ram_start = (uint32_t)_image_ram_start;
const uint32_t ram_end = (uint32_t)_image_ram_end;
+ #endif
if ((uint32_t)start_addr >= ram_start && (uint32_t)start_addr < ram_end) {
return MEMFAULT_MIN(desired_size, ram_end - (uint32_t)start_addr);
@@ -199,7 +205,13 @@
}
#endif
- void *sp = (void *)thread->callee_saved.psp;
+ void *sp =
+ #if CONFIG_ARM
+ (void *)thread->callee_saved.psp
+ #else
+ (void *)thread->callee_saved.sp
+ #endif
+ ;
#if defined(CONFIG_THREAD_STACK_INFO)
// We know where the top of the stack is. Use that information to shrink
@@ -259,13 +271,15 @@
// with a Memfault SDK version >=0.27.3, because that NCS release used an
// intermediate Zephyr release, so the version number checking is not
// possible.
-#if !MEMFAULT_ZEPHYR_USE_OLD_DATA_REGION_NAMES && MEMFAULT_ZEPHYR_VERSION_GT(2, 6)
-#define ZEPHYR_DATA_REGION_START __data_region_start
-#define ZEPHYR_DATA_REGION_END __data_region_end
-#else
- // The old names are used in previous Zephyr versions (<=2.6)
-#define ZEPHYR_DATA_REGION_START __data_ram_start
-#define ZEPHYR_DATA_REGION_END __data_ram_end
+#if !defined(ZEPHYR_DATA_REGION_START) && !defined(ZEPHYR_DATA_REGION_END)
+ #if !MEMFAULT_ZEPHYR_USE_OLD_DATA_REGION_NAMES && MEMFAULT_ZEPHYR_VERSION_GT(2, 6)
+ #define ZEPHYR_DATA_REGION_START __data_region_start
+ #define ZEPHYR_DATA_REGION_END __data_region_end
+ #else
+ // The old names are used in previous Zephyr versions (<=2.6)
+ #define ZEPHYR_DATA_REGION_START __data_ram_start
+ #define ZEPHYR_DATA_REGION_END __data_ram_end
+ #endif
#endif
extern uint32_t ZEPHYR_DATA_REGION_START[];
diff --git a/ports/zephyr/v2.4/CMakeLists.txt b/ports/zephyr/v2.4/CMakeLists.txt
index 0e428b2..263e0f6 100644
--- a/ports/zephyr/v2.4/CMakeLists.txt
+++ b/ports/zephyr/v2.4/CMakeLists.txt
@@ -1,4 +1,12 @@
-zephyr_library_sources(memfault_fault_handler.c)
+if (CONFIG_RISCV)
+ zephyr_library_sources(memfault_fault_handler_riscv.c)
+elseif(CONFIG_ARM)
+ zephyr_library_sources(memfault_fault_handler.c)
+else()
+ # Unsupported configuration
+ message(FATAL_ERROR "Unsupported chip architecture")
+endif()
+
zephyr_include_directories(.)
# Zephyr fatals for ARM Cortex-M's take the following path:
diff --git a/ports/zephyr/v2.4/memfault_fault_handler_riscv.c b/ports/zephyr/v2.4/memfault_fault_handler_riscv.c
new file mode 100644
index 0000000..0218163
--- /dev/null
+++ b/ports/zephyr/v2.4/memfault_fault_handler_riscv.c
@@ -0,0 +1,84 @@
+//! @file
+//!
+//!
+
+#include <arch/cpu.h>
+#include <fatal.h>
+#include <init.h>
+#include <logging/log.h>
+#include <logging/log_ctrl.h>
+#include <soc.h>
+
+#include "memfault/core/compiler.h"
+#include "memfault/core/platform/core.h"
+#include "memfault/core/reboot_reason_types.h"
+#include "memfault/panics/arch/riscv/riscv.h"
+#include "memfault/panics/coredump.h"
+#include "memfault/panics/fault_handling.h"
+#include "memfault/ports/zephyr/version.h"
+
+// Note: There is no header exposed for this zephyr function
+extern void sys_arch_reboot(int type);
+
+// Intercept zephyr/kernel/fatal.c:z_fatal_error()
+void __wrap_z_fatal_error(unsigned int reason, const z_arch_esf_t *esf);
+
+void __wrap_z_fatal_error(unsigned int reason, const z_arch_esf_t *esf) {
+ // flush logs prior to capturing coredump & rebooting
+ LOG_PANIC();
+
+ sMfltRegState reg = {
+ .mepc = esf->mepc, /* machine exception program counter */
+ .ra = esf->ra,
+#ifdef CONFIG_USERSPACE
+ .sp = esf->sp, /* preserved (user or kernel) stack pointer */
+#endif
+ // .gp = ?,
+ // .tp = ?,
+ .t =
+ {
+ esf->t0, /* Caller-saved temporary register */
+ esf->t1, /* Caller-saved temporary register */
+ esf->t2, /* Caller-saved temporary register */
+#if !defined(CONFIG_RISCV_ISA_RV32E)
+ esf->t3, /* Caller-saved temporary register */
+ esf->t4, /* Caller-saved temporary register */
+ esf->t5, /* Caller-saved temporary register */
+ esf->t6, /* Caller-saved temporary register */
+#endif
+ },
+ .s =
+ {
+ esf->s0, /* callee-saved s0 */
+ },
+ .a =
+ {
+ esf->a0, /* function argument/return value */
+ esf->a1, /* function argument */
+ esf->a2, /* function argument */
+ esf->a3, /* function argument */
+ esf->a4, /* function argument */
+ esf->a5, /* function argument */
+#if !defined(CONFIG_RISCV_ISA_RV32E)
+ esf->a6, /* function argument */
+ esf->a7, /* function argument */
+#endif
+ },
+ .mstatus = esf->mstatus,
+ // .mtvec = ?,
+ // .mcause = ?,
+ // .mtval = ?,
+ // .mhartid = ?,
+ };
+
+ memfault_fault_handler(®, kMfltRebootReason_HardFault);
+}
+
+MEMFAULT_WEAK
+MEMFAULT_NORETURN
+void memfault_platform_reboot(void) {
+ memfault_platform_halt_if_debugging();
+
+ sys_arch_reboot(0);
+ CODE_UNREACHABLE;
+}
diff --git a/scripts/memfault_gdb.py b/scripts/memfault_gdb.py
index 7ff249f..e12ddef 100644
--- a/scripts/memfault_gdb.py
+++ b/scripts/memfault_gdb.py
@@ -1350,7 +1350,7 @@
"""Captures a coredump from the target and uploads it to Memfault for analysis"""
ALPHANUM_SLUG_DOTS_COLON_REGEX = r"^[-a-zA-Z0-9_\.\+:]+$"
- ALPHANUM_SLUG_DOTS_COLON_SPACES_PARENS_SLASH_REGEX = r"^[-a-zA-Z0-9_\.\+: \(\)\[\]/]+$"
+ ALPHANUM_SLUG_DOTS_COLON_SPACES_PARENS_SLASH_COMMA_REGEX = r"^[-a-zA-Z0-9_\.\+: \(\)\[\]/,]+$"
DEFAULT_CORE_DUMP_HARDWARE_REVISION = "DEVBOARD"
DEFAULT_CORE_DUMP_SERIAL_NUMBER = "DEMOSERIALNUMBER"
DEFAULT_CORE_DUMP_SOFTWARE_TYPE = "main"
@@ -1541,7 +1541,7 @@
parser.add_argument(
"--software-version",
type=_character_check(
- self.ALPHANUM_SLUG_DOTS_COLON_SPACES_PARENS_SLASH_REGEX, "software version"
+ self.ALPHANUM_SLUG_DOTS_COLON_SPACES_PARENS_SLASH_COMMA_REGEX, "software version"
),
help=(
"Overrides the software version that will be reported in the core dump."