Skip to main content

NXP MCUXpresso Getting Started Guide for ARM

This tutorial will go over integrating the Memfault Firmware SDK into an NXP i.MX RT1060 project using NXP's MCUXpresso IDE.

note

Note: these instructions were written using mcuxpressoide-11.4.0_6224.x86_64 on ubuntu 22.04, using the MIMXRT1060-EVKB

The full example project can be found here: https://github.com/memfault/mcuxpresso-rt1060-example

These instructions should also apply to other NXP RT family boards (ex: MIMXRT1020-EVK), though there may be adjustments needed to the reset logic or linker script.

Overview

This document describes how to add the Memfault SDK to an MCUXpresso project. The strategy documented here follows these high-level steps:

Part 1: generate a sample project

  1. Download MCUXpresso SDK from NXP
  2. Generate a sample project from that SDK

Part 2: add the Memfault SDK

  1. Add the Memfault SDK sources to the project using the eclipse_patch.py script from the Memfault SDK
  2. Configure a few build settings (gnu build id, debug level)
  3. (Optional, for i.MX RT1060 network demo) Add the example HTTPS client port and associated application changes for the i.MX RT1060 LwIP project, and configure Memfault Project Key for uploading Memfault data from the EVK

Part 1: Setup and Verify LwIP Sample Application

note

👉 This section describes setting up the lwip_httpscli_mbedTLS_freertos sample project provided by the NXP SDK.

The integration steps in Part 2 can be applied to other projects (including projects not based on an NXP sample), so skip to that section if necessary.

  1. Install mcuxpresso: https://www.nxp.com/design/software/development-software/mcuxpresso-software-and-tools-/mcuxpresso-integrated-development-environment-ide:MCUXpresso-IDE

  2. Download the SDK for the MIMXRT1060-EVKB (requires login to nxp.com): select all to download all components, and select “mcuxpresso” as the target toolchain. This example used the specific SDK version: SDK_2.10.1_MIMXRT1060-EVKB (NOTE: v2.11.1 was not compatible with MCUXpresso v11.4.0)

    Untitled
  3. Import the SDK into MCUXpresso:

    Untitled
  4. To create the sample project, click “Import SDK example(s)...”, then select our board, and select lwip_httpscli_mbedTLS_freertos:

    Untitled
  5. Default settings in the next screen are all OK, except for:

    1. Set library type (and hosting variant): change it to “NewlibNano (none)”
    2. Include semihost HardFault handler: uncheck
  6. Build the sample with “Project→Build all”

  7. Flash the sample with “Run→Debug as...→MCUXpresso IDE LinkServer (inc. CMSIS-DAP) probes”, assuming the EVK has the default CMSIS-DAP debug firmware

  8. Open a serial terminal (example below is using pyserial):

    pyserial-miniterm --raw /dev/ttyACM0 115200
  9. Pressing “Run” in MCUXpresso, the following output should appear in the serial terminal:

    Initializing PHY...
  10. Connect an ethernet cable to a router to see the demo proceed

Part 2: Integrate the Memfault SDK

note

👉 This section describes how to add the Memfault SDK sources. In this example, the Memfault Demo CLI is connected to the debug UART in order to test the Memfault functionality, and an HTTPS client is enabled over the Ethernet connection for uploading Memfault data.

Note that the provided HTTPS client example is specific to the i.MX RT1060 EVK sample app; other boards will potentially require a different connectivity implementation.

Add the Memfault Sources and Build Flags

  1. Download the Memfault SDK. either copy the folder into the project, or preferably, add it as a Git submodule:

    git submodule add https://github.com/memfault/memfault-firmware-sdk.git memfault-firmware-sdk
  2. Run the eclipse patch script to add the Memfault sources (including the "demo" component, which is the test CLI):

    python memfault-firmware-sdk/scripts/eclipse_patch.py --project-dir . --memfault-sdk-dir memfault-firmware-sdk --components core,util,metrics,panics,demo --target-port freertos
  3. Copy in the platform template files:

    cp memfault-firmware-sdk/ports/templates/* source/
  4. Refresh the project (right-click on the Project, “Refresh”)

  5. To enable the Memfault SDK sources in the build, uncheck “Exclude from build” for the “memfault_components” virtual folder:

    Untitled
  6. Add the memfault_platform_ram_backed_coredump.c source file to a new virtual folder

    1. Create a new virtual folder. this is to not conflict with the sources the eclipse_patch.py script operates on, so that script can be re-run in the future (eg if the Memfault SDK is updated) without affecting these port-specific sources, so name it mfltport_components

    2. Add the source file, which is located at memfault-firmware-sdk/ports/panics/src/memfault_platform_ram_backed_coredump.c, by dragging from a file explorer window into the virtual folder:

      Untitled
      Untitled
    3. Right-click on the new virtual folder, select “Properties”, and uncheck “Exclude from build”

  7. Add the following stubs to source/memfault_platform_port.c:

    #include <stdio.h>

    #include "fsl_debug_console.h"

    void memfault_platform_log(eMemfaultPlatformLogLevel level, const char *fmt,
    ...) {
    const char *lvl_str;
    switch (level) {
    case kMemfaultPlatformLogLevel_Debug:
    lvl_str = "D";
    break;

    case kMemfaultPlatformLogLevel_Info:
    lvl_str = "I";
    break;

    case kMemfaultPlatformLogLevel_Warning:
    lvl_str = "W";
    break;

    case kMemfaultPlatformLogLevel_Error:
    lvl_str = "E";
    break;

    default:
    return;
    break;
    }

    va_list args;
    va_start(args, fmt);

    char log_buf[128];
    vsnprintf(log_buf, sizeof(log_buf), fmt, args);

    PRINTF("[%s] MFLT: %s\n", lvl_str, log_buf);

    va_end(args);
    }

    bool memfault_platform_metrics_timer_boot(
    uint32_t period_sec, MemfaultPlatformTimerCallback callback) {
    (void)period_sec, (void)callback;
    // Schedule a timer to invoke callback() repeatedly after period_sec
    return true;
    }

    uint64_t memfault_platform_get_time_since_boot_ms(void) {
    // Return time since boot in ms, this is used for relative timings.
    return 0;
    }

    MEMFAULT_PUT_IN_SECTION(".noinit.mflt_reboot_tracking")
    static uint8_t s_reboot_tracking[MEMFAULT_REBOOT_TRACKING_REGION_SIZE];

    MEMFAULT_WEAK 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);
    }

    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,
    };
    }
  8. Add memfault_platform_boot(); to main() (located in source/lwip_httpscli_mbedTLS_freertos.c.

  9. Confirm the project now builds.

Add GNU Build ID

  1. Add GNU build id:

    1. Add this flag to source/memfault_platform_config.h:

      #define MEMFAULT_USE_GNU_BUILD_ID 1
    2. Add the following file at source/gnu_build_id.ld:

      /*
      Custom section to be included in the linker
      command file, to support GNU build id.
      */

      SECTIONS
      {
      .note.gnu.build-id :
      {
      __start_gnu_build_id_start = .;
      KEEP(*(.note.gnu.build-id))
      }
      }
      INSERT AFTER .text;
    3. Add the necessary linker flags: right-click project→Properties. in the left pane, open “C/C++ Build”→”Settings”, then select MCU Linker→Miscellaneous, and add the --build-id build flag:

      Untitled
    4. Add a second flag to add the linker script fragment file to the build: -T "${workspace_loc:/${ProjName}/source/gnu_build_id.ld}"

  2. Set Debug Level to -g3 in “MCU C Compiler”→Settings:

    Untitled
  3. Clean and build. flash the project to the board and confirm the startup text appears in the serial console:

    [I] MFLT: GNU Build ID: 63a58c3567c22fd6786adbc4cc186d258e66efe7
    [I] MFLT: S/N: DEMOSERIAL
    [I] MFLT: SW type: app-fw
    [I] MFLT: SW version: 1.0.0
    [I] MFLT: HW version: dvt1
    [I] MFLT: Memfault Initialized!

Enable HTTPS Client and Test

  1. The last step is to pick up the necessary port + https client changes, which can be found under the source/directory in this repo: https://github.com/memfault/mcuxpresso-rt1060-example

    1. Copy the necessary files:

      1. Everything under source/
      2. lwip_httpscli_mbedTLS/httpsclient.c
    2. Configure a Memfault project key by replacing the placeholder text in lwip_httpscli_mbedTLS/httpsclient.c. The project key is located under Settings in the Memfault project, see https://docs.memfault.com/docs/mcu/export-chunks-over-console#grab-project-key-from-memfault-ui

      // Memfault project key
      const char *memfault_project_key = "<YOUR PROJECT KEY HERE>";
    3. Flashing this should show a new post_chunks console command when running help:

      [I] MFLT: GNU Build ID: 63a58c3567c22fd6786adbc4cc186d258e66efe7
      [I] MFLT: S/N: DEMOSERIAL
      [I] MFLT: SW type: app-fw
      [I] MFLT: SW version: 1.0.0
      [I] MFLT: HW version: dvt1
      [I] MFLT: Memfault Initialized!

      mflt> help
      get_core: Get coredump info
      clear_core: Clear an existing coredump
      crash: Trigger a crash
      trigger_logs: Trigger capture of current log buffer contents
      drain_chunks: Flushes queued Memfault data. To upload data see https://mflt.io/posting-chunks-with-gdb
      trace: Capture an example trace event
      get_device_info: Get device info
      reboot: Reboot system and tracks it with a trace event
      export: Export base64-encoded chunks. To upload data see https://mflt.io/chunk-data-export
      post_chunks: Upload chunks to Memfault
      help: Lists all commands
  2. Test out the demo commands, eg crash, reboot, post_chunks. The post_chunks command requires an ethernet cable to be connected, with network access to the internet.

    note

    Note: be sure to detach the debugger before running the crash or reboot commands. Depending on configuration, the debugger may prevent the board from resetting correctly!

  3. To see decoded data on app.memfault.com, the Symbol File will need to be uploaded. typically the symbol file will be a .axf file named from the project, for example Debug/mcuxpresso-rt1060-example.axf. symbol files can be uploaded through the Memfault web ui or using the Memfault CLI tool.

Troubleshooting Data Transfer

If you encounter any issues in your data transfer implementation, Memfault has tools to help debug!

  • To troubleshoot data not getting uploaded or processed correctly by the Memfault cloud, take a look at the Integration Hub → Processing Log view. This provides a filterable, chronological view of recent errors that have occurred while processing received data. See this documentation page for more information on the Integration Hub.
  • A view you can use toview the raw "Chunk" data payloadsthat have arrived for your project.
  • Precanned Data Payloads you can pass through your `user_transport_send_chunk_data()` implementation to test data transfer in isolation.
  • Server-side rate limiting will apply to the device you're using to work on the integration process. Once you can see the device on the Memfault Web App, consider enabling Server-Side Developer Mode for it on the Memfault Web App to temporarily bypass these limits.

Additional Notes

Data Cache

The Cortex-M7 chip on the i.MX RT10xx series chips has an instruction and data cache. The Memfault SDK relies on a minimum of 1 variable stored in non-initialized (.noinit) RAM when the system encounters a crash. If the RAM-backed coredump storage Memfault component is used, that data also should be located in a non-initialized section of RAM.

In order to ensure data integrity of the non-initialized data when the chip reboots after a crash, it's important to flush the D-cache. The CMSIS API to perform that flush is:

SCB_CleanDCache();

This call should be inserted before the chip is reset, typically in memfault_platform_reboot() just before NVIC_SystemReset():

void memfault_platform_reboot(void) {
// flush the data cache before issuing the reboot, to ensure the saved reboot
// reason data or RAM-backed coredump data is written out
SCB_CleanDCache();

NVIC_SystemReset();
while (1) { } // unreachable
}

For reference, see this white paper from Microchip:

http://ww1.microchip.com/downloads/en/DeviceDoc/Managing-Cache-Coherency-on-Cortex-M7-Based-MCUs-DS90003195A.pdf

And the CMSIS API documentation:

https://www.keil.com/pack/doc/CMSIS/Core/html/group__Dcache__functions__m7.html