Embedded Integration Guide
The following guide will walk you through step-by-step how to integrate and test the Memfault SDK for a Cortex-M device using the GNU GCC, Clang, IAR, ARM MDK, or TI ARM Compiler.
Adding the Memfault Firmware SDK to your device will provide rich diagnostics, including:
(Optionally) Collect a coredump capture using GDB
- Without GDB
- Using GDB
If you do not use GDB, skip ahead to the next step.
If you use GDB, a full coredump can be captured by just using the debugger! This can be useful during development to take advantage of Memfault's analyzers when a device crashes or hangs.
To perform the capture, navigate to http://app.memfault.com/ and select the "Issues" page in the Memfault UI, click on the "Manual Upload" button, and click on "walk-through on how to upload your first coredump". From there you can follow the guided steps to perform a capture. At the end you will see an analysis of your system state.
1. Create a Project
Create a Project and get a Project Key
Go to app.memfault.com and from the "Select A Project" dropdown, click on "Create Project" to setup your first project. Choose a name that reflects your product, such as "smart-sink-dev".
Once you've created your project, you'll be automatically taken to an page that includes your project key. Copy the key and follow the rest of this guide.
2. Set up the SDK
Prepare folder for memfault-firmware-sdk & port
The memfault-firmware-sdk
is a self-contained C SDK you will need to include
into your project.
Create a folder to hold memfault-firmware-sdk
as well as the configuration and
porting files to your platform.
cd ${YOUR_PROJECT_DIR}
mkdir -p third_party/memfault
cd third_party/memfault
# Add memfault repo as submodule, subtree, or copy in as source directly
git submodule add https://github.com/memfault/memfault-firmware-sdk.git memfault-firmware-sdk
cp memfault-firmware-sdk/ports/templates/* .
When you are done, you should have the following directory structure in your project:
memfault/
//...
├── memfault-firmware-sdk (submodule)
| # Files where port / glue layer to your platform will be implemented
├── memfault_platform_port.c
|
| # Configuration Headers
├── memfault_metrics_heartbeat_config.def
└── memfault_trace_reason_user_config.def
└── memfault_platform_log_config.h
└── memfault_platform_config.h
Add Sources to Build System
Based on the build system you are using, expand the appropriate tab below and follow the steps to add the sources to your target
- Make
- CMake
- Other
MEMFAULT_PORT_ROOT := <YOUR_PROJECT_ROOT>/third_party/memfault
MEMFAULT_SDK_ROOT := $(MEMFAULT_PORT_ROOT)/memfault-firmware-sdk
MEMFAULT_COMPONENTS := core util panics metrics
include $(MEMFAULT_SDK_ROOT)/makefiles/MemfaultWorker.mk
<YOUR_SRC_FILES> += \
$(MEMFAULT_COMPONENTS_SRCS) \
$(MEMFAULT_PORT_ROOT)/memfault_platform_port.c
<YOUR_INCLUDE_PATHS> += \
$(MEMFAULT_COMPONENTS_INC_FOLDERS) \
$(MEMFAULT_SDK_ROOT)/ports/include \
$(MEMFAULT_PORT_ROOT)
Be sure to update YOUR_SRC_FILES
, YOUR_INCLUDE_PATHS
, and
YOUR_PROJECT_ROOT
accordingly for your system above!
set(MEMFAULT_PORT_ROOT <YOUR_PROJECT_ROOT>/third_party/memfault)
set(MEMFAULT_SDK_ROOT ${MEMFAULT_PORT_ROOT}/memfault-firmware-sdk)
list(APPEND MEMFAULT_COMPONENTS core util panics metrics)
include(${MEMFAULT_SDK_ROOT}/cmake/Memfault.cmake)
memfault_library(${MEMFAULT_SDK_ROOT} MEMFAULT_COMPONENTS
MEMFAULT_COMPONENTS_SRCS MEMFAULT_COMPONENTS_INC_FOLDERS)
# Add the following to your target sources:
# ${MEMFAULT_COMPONENTS_SRCS}
# ${MEMFAULT_PORT_ROOT}/memfault_platform_port.c
#
# Add the following to your target includes
# ${MEMFAULT_COMPONENTS_INC_FOLDERS}
# ${MEMFAULT_SDK_ROOT}/ports/include
# ${MEMFAULT_PORT_ROOT}
Be sure to update YOUR_PROJECT_ROOT
, add ${MEMFAULT_COMPONENTS_SRCS}
to your
target sources and ${MEMFAULT_COMPONENTS_INC_FOLDERS}
to your target includes
accordingly
- add
$MEMFAULT_FIRMWARE_SDK/components/include
to the include paths for your project - add
$MEMFAULT_FIRMWARE_SDK/ports/include
to the include paths for your project - add the sources under
$MEMFAULT_FIRMWARE_SDK/components/[core, util, panics, metrics]/src/**/*.c
to your project - add
third_party/memfault/memfault_platform_port.c
sources to project - add
third_party/memfault/
to the include paths for your project
When using Eclipse as your build system, folders can be opted out from a build by right clicking on the folder and selecting "Resource Configuration" -> "Exclude from Build".
Core Dependencies
Open the
memfault_platform_port.c
copied into your third_party/memfault
folder and fill out the stub
implementations accordingly.
Initialize Memfault Subsystem On Bootup
From your main routine, add a call to memfault_platform_boot()
prior to the
startup of an RTOS or baremetal while loop.
#include "memfault/components.h"
// ...
int main(void) {
// ...
memfault_platform_boot();
// ...
}
Reboot Tracking Dependencies
Memfault has a module for tracking and reporting what resets are taking place on your platform.
- nRF5 SDK
- STM32
- NXP S32 SDK
- EFM/EFR (Gecko SDK)
- Other
Add the following file to your build system:
${MEMFAULT_SDK_ROOT}/ports/nrf5_sdk/resetreas_reboot_tracking.c
STM32CubeF4
Add the following file to your build system:
${MEMFAULT_SDK_ROOT}/ports/stm32cube/f4/rcc_reboot_tracking.c
STM32CubeL4
Add the following file to your build system:
${MEMFAULT_SDK_ROOT}/ports/stm32cube/l4/rcc_reboot_tracking.c
STM32CubeWB
Add the following file to your build system:
${MEMFAULT_SDK_ROOT}/ports/stm32cube/wb/rcc_reboot_tracking.c
Allocate noinit region & Collect Reboot Info
Add the following to your memfault_platform_port.c
//! @file memfault_platform_port.c
MEMFAULT_PUT_IN_SECTION(".noinit.mflt_reboot_tracking")
static uint8_t s_reboot_tracking[MEMFAULT_REBOOT_TRACKING_REGION_SIZE];
void memfault_platform_reboot_tracking_boot(void) {
sResetBootupInfo reset_info = { 0 };
memfault_reboot_reason_get(&reset_info);
memfault_reboot_tracking_boot(s_reboot_tracking, &reset_info);
}
It's expected that s_reboot_tracking
is placed in "noinit" RAM. That is, a
region of RAM not initialized on bootup or used by your bootloaders. This can be
achieved by adding a "noinit" section to your linker script.
GNU GCC Example
Add the following to your .ld
file:
.noinit (NOLOAD): { KEEP(*(*.noinit.mflt*)) } >RAM
ARM MDK Example
Add the following to your .sct
file:
; Within previously defined Load Region, modify NOINIT_RAM address and length as necessary
NOINIT_RAM 0x20010000 UNINIT 0x00000200 { ;no init section
*(.noinit.mflt*)
}
IAR Example
Add the following to your .icf
file:
do not initialize
{
section .noinit,
section .stack,
section .heap,
/* Add line to a do not initialize directive */
rw section .noinit.mflt*,
};
Add the following file to your build system:
${MEMFAULT_SDK_ROOT}/ports/s32sdk/rcm_reboot_tracking.c
Allocate noinit region & Collect Reboot Info
Add the following to your memfault_platform_port.c
//! @file memfault_platform_port.c
MEMFAULT_PUT_IN_SECTION(".noinit.mflt_reboot_tracking")
static uint8_t s_reboot_tracking[MEMFAULT_REBOOT_TRACKING_REGION_SIZE];
void memfault_platform_reboot_tracking_boot(void) {
sResetBootupInfo reset_info = { 0 };
memfault_reboot_reason_get(&reset_info);
memfault_reboot_tracking_boot(s_reboot_tracking, &reset_info);
}
It's expected that s_reboot_tracking
is placed in "noinit" RAM. That is, a
region of RAM not initialized on bootup or used by your bootloaders. This can be
achieved by adding a "noinit" section to your linker script.
GNU GCC Example
Add the following to your .ld
file:
.noinit (NOLOAD): { KEEP(*(*.noinit.mflt*)) } >RAM
ARM MDK Example
Add the following to your .sct
file:
; Within previously defined Load Region, modify NOINIT_RAM address and length as necessary
NOINIT_RAM 0x20010000 UNINIT 0x00000200 { ;no init section
*(.noinit.mflt*)
}
IAR Example
Add the following to your .icf
file:
do not initialize
{
section .noinit,
section .stack,
section .heap,
/* Add line to a do not initialize directive */
rw section .noinit.mflt*,
};
Add the following file to your build system:
${MEMFAULT_SDK_ROOT}/ports/emlib/rmu_reboot_tracking.c
Allocate noinit region & Collect Reboot Info
Add the following to your memfault_platform_port.c
//! @file memfault_platform_port.c
MEMFAULT_PUT_IN_SECTION(".noinit.mflt_reboot_tracking")
static uint8_t s_reboot_tracking[MEMFAULT_REBOOT_TRACKING_REGION_SIZE];
void memfault_platform_reboot_tracking_boot(void) {
sResetBootupInfo reset_info = { 0 };
memfault_reboot_reason_get(&reset_info);
memfault_reboot_tracking_boot(s_reboot_tracking, &reset_info);
}
It's expected that s_reboot_tracking
is placed in "noinit" RAM. That is, a
region of RAM not initialized on bootup or used by your bootloaders. This can be
achieved by adding a "noinit" section to your linker script.
GNU GCC Example
Add the following to your .ld
file:
.noinit (NOLOAD): { KEEP(*(*.noinit.mflt*)) } >RAM
ARM MDK Example
Add the following to your .sct
file:
; Within previously defined Load Region, modify NOINIT_RAM address and length as necessary
NOINIT_RAM 0x20010000 UNINIT 0x00000200 { ;no init section
*(.noinit.mflt*)
}
IAR Example
Add the following to your .icf
file:
do not initialize
{
section .noinit,
section .stack,
section .heap,
/* Add line to a do not initialize directive */
rw section .noinit.mflt*,
};
Implement the following function for your MCU. The ports listed in other tabs serve as a good reference.
//! @file memfault_platform_port.c
void memfault_reboot_reason_get(sResetBootupInfo *info) {
const uint32_t reset_cause = 0; // TODO: Populate with MCU reset reason
eMemfaultRebootReason reset_reason = kMfltRebootReason_Unknown;
// TODO: Convert MCU specific reboot reason to memfault enum
*info = (sResetBootupInfo) {
.reset_reason_reg = reset_cause,
.reset_reason = reset_reason,
};
}
Allocate noinit region & Collect Reboot Info
Add the following to your memfault_platform_port.c
//! @file memfault_platform_port.c
MEMFAULT_PUT_IN_SECTION(".noinit.mflt_reboot_tracking")
static uint8_t s_reboot_tracking[MEMFAULT_REBOOT_TRACKING_REGION_SIZE];
void memfault_platform_reboot_tracking_boot(void) {
sResetBootupInfo reset_info = { 0 };
memfault_reboot_reason_get(&reset_info);
memfault_reboot_tracking_boot(s_reboot_tracking, &reset_info);
}
It's expected that s_reboot_tracking
is placed in "noinit" RAM. That is, a
region of RAM not initialized on bootup or used by your bootloaders. This can be
achieved by adding a "noinit" section to your linker script.
GNU GCC Example
Add the following to your .ld
file:
.noinit (NOLOAD): { KEEP(*(*.noinit.mflt*)) } >RAM
ARM MDK Example
Add the following to your .sct
file:
; Within previously defined Load Region, modify NOINIT_RAM address and length as necessary
NOINIT_RAM 0x20010000 UNINIT 0x00000200 { ;no init section
*(.noinit.mflt*)
}
IAR Example
Add the following to your .icf
file:
do not initialize
{
section .noinit,
section .stack,
section .heap,
/* Add line to a do not initialize directive */
rw section .noinit.mflt*,
};
Implement Coredump Storage Dependency
When the system faults or asserts crash information can be recorded and sent to Memfault after the device reboots. In order for this information to be saved, it must be persisted to a storage area that persists across a device reset.
Implement Coredump Storage Area
Coredumps can be saved in a .noinit
region of SRAM, internal flash, external
flash, or even streamed out over a peripheral to another MCU when a coredump
takes place.
- RAM-backed
- EFM/EFR (Gecko SDK)
- STM32
- nRF5 SDK
- NXP S32 SDK
- Other
1. Add source code
Add the following file to your build system:
${MEMFAULT_SDK_ROOT}/ports/panics/src/memfault_platform_ram_backed_coredump.c
2. Configure Coredump Region
The RAM region is placed in a special section .noinit.mflt_coredump
which
needs to be placed in a noinit memory region. This can be achieved by adding a
noinit
section to your linker script. Check out the following examples for
your compiler/linker:
GNU GCC Example
Add the following to your .ld
file:
.noinit (NOLOAD): { KEEP(*(*.noinit.mflt*)) } >RAM
ARM MDK Example
Add the following to your .sct
file:
; Within previously defined Load Region, modify NOINIT_RAM address and length as necessary
NOINIT_RAM 0x20010000 UNINIT 0x00000200 { ;no init section
*(.noinit.mflt*)
}
IAR Example
Add the following to your .icf
file:
do not initialize
{
section .noinit,
section .stack,
section .heap,
/* Add line to a do not initialize directive */
rw section .noinit.mflt*,
};
Add the following file to your build system:
${MEMFAULT_SDK_ROOT}/ports/emlib/msc_coredump_storage.c
Follow instructions in msc_coredump_storage.c to configure.