Skip to main content
Indian firmware engineer debugging a Cortex-M HardFault with SEGGER J-Link and Ozone

Debugging Cortex-M HardFaults in Indian Embedded Projects: A SEGGER J-Link + RTT Workflow

GSAS Editorial · · 8 min read

Cortex-M HardFault is the single most common crash category in Indian firmware support tickets. Across STM32, Nordic nRF52/nRF53, NXP i.MX RT, Renesas RA, and every other Arm Cortex-M silicon family Indian product teams are building on, the same exception shows up at roughly 02:00 the week before a customer demo, and the same question gets asked in every engineering WhatsApp group in Bengaluru, Pune, Chennai, and Hyderabad: “The CPU jumped to 0xFFFFFFFE. What happened?”

This post walks through exactly what happened and, more importantly, how to find out in minutes instead of hours. It is based on SEGGER’s public application note AN00016, Analyzing HardFaults on Cortex-M CPUs, localized for the Indian embedded engineering context and the specific silicon families Indian teams are running in production today. GSAS Micro Systems is India’s authorized SEGGER partner, J-Link, Ozone, SystemView, and RTT-based debugging are available with local support across six India offices.

What a HardFault actually is

Arm Cortex-M has four fault categories: UsageFault (illegal instruction, divide by zero, unaligned access on strict cores), BusFault (memory access the bus could not satisfy), MemManage (MPU violation), and HardFault (the escalation target when any of the above is raised but its own handler is disabled, or when a fault occurs inside another fault handler).

On most Indian production firmware, the three configurable fault handlers are left disabled at reset and every fault escalates to HardFault. This is fine in steady state but it means every fault arrives at the same handler and loses the context about which specific condition triggered it. The first step in any HardFault debug session is therefore decoding the fault back to its underlying cause, which is the information the Cortex-M hardware captures for you in the System Control Block (SCB) fault-status registers.

The four registers every Indian firmware engineer should read

  • CFSR (Configurable Fault Status Register): the primary decoder. Split into three 8-bit fields: UFSR (upper), BFSR (middle), MMFSR (lower). Each bit identifies one specific cause. UFSR’s UNDEFINSTR bit fires on illegal instructions. BFSR’s PRECISERR fires on synchronous bus faults with a valid address in BFAR. MMFSR’s DACCVIOL fires on MPU data-access violations.
  • HFSR (HardFault Status Register): tells you whether the HardFault was escalated from a lower-priority fault (FORCED bit) or was a direct vector-table fault (VECTTBL bit). Almost all production firmware HardFaults have FORCED = 1, which means the real information is in CFSR.
  • BFAR (BusFault Address Register): the address the bus tried to access when the fault occurred, valid only when BFSR.BFARVALID = 1. On a NULL dereference this typically reads 0x00000000 or a small offset from it.
  • MMFAR (MemManage Fault Address Register): the address of the MPU violation, valid only when MMFSR.MMARVALID = 1. Essential when your MPU-protected RTOS task crossed its stack boundary.

SEGGER’s AN00016 includes a compact generic HardFault handler that reads all four registers, unpacks the fault cause, and prints the result in human-readable form over RTT so the running firmware can report its own crash without a live debugger attach. This is the workflow most Indian professional teams standardize on.

Installing the SEGGER generic HardFault handler

AN00016 ships a single C file with one HardFault handler implementation that you drop into your project’s startup code. It is written to be vendor-agnostic, no STM32 HAL dependency, no Nordic SoftDevice dependency, no NXP MCUXpresso assumption. It calls SEGGER’s RTT printf to emit the decoded fault state over the debug pin without halting the CPU.

Integration steps on the common Indian silicon families:

  • STM32 (any Cube-generated project): replace the default HardFault_Handler in stm32xx_it.c with the SEGGER version. No CubeMX regeneration concerns, the handler is stateless.
  • Nordic nRF52/nRF53 (SDK or Nordic Connect SDK / Zephyr): for bare-SDK projects, replace HardFault_Handler in gcc_startup_nrf5*.s. For Zephyr-based Nordic Connect SDK projects, use Zephyr’s CONFIG_ARM_UNWIND path to integrate the decoder into Zephyr’s own fault handler, Zephyr already has excellent fault decoding built in, so the RTT printout is additive rather than replacement.
  • NXP i.MX RT (MCUXpresso or command-line): replace HardFault_Handler in the MCUXpresso startup file or the generic Cortex-M startup that ships with the SDK. On dual-core RT1170 (M7 + M4), install the handler on both cores, each core has its own vector table and its own HardFault handler.
  • Raspberry Pi RP2040 / RP2350: install on both cores via the pico_sdk’s irq_set_exclusive_handler path.

Capturing the fault state over RTT without a live debug attach

Here is the thing AN00016 makes possible that changes Indian firmware debug practice. Once the SEGGER HardFault handler is installed, any crash the unit experiences, in the lab, on the test bench, in a customer’s facility, at a trade show, is streamed over RTT and captured by JLinkRTTViewer or JLinkRTTLogger on the laptop connected via J-Link.

The key: RTT does not need a debug session to be active. The J-Link can be attached to the running system in “no halt” mode, and as long as the target CPU is physically connected to the debug pins and JLinkRTTLogger is running on the host, any RTT output the firmware emits is captured, including the fault decoder output at the moment of a crash. No breakpoints, no stepping, no interference with the running system’s real-time behavior.

Practically, the Indian debug workflow is:

  1. Install the SEGGER HardFault handler and the RTT init code in your startup.
  2. On every bench running the unit, leave a J-Link attached (a cheap J-Link BASE or J-Link PLUS Compact is enough for this role).
  3. Start JLinkRTTLogger pointing at a dated log file on the bench PC.
  4. Run the unit normally, customer demo, stress test, field trial, whatever.
  5. When a HardFault happens (and it will, this is firmware engineering), the decoder output is in the log file, timestamped.

Hours of “step through the code and hope” replaced by a timestamped log line like:

[12:43:17] *** HardFault ***
           CFSR  = 0x00000200 (PRECISERR)
           HFSR  = 0x40000000 (FORCED)
           BFAR  = 0x00000024 (bus fault at 0x24)
           LR    = 0xFFFFFFED (thread mode, PSP)
           Stack frame @ 0x20001FC0:
             R0=0x00000024  R1=0x00000000  R2=0x00000000  R3=0x00000000
             R12=0x00000000 LR=0x08001234  PC=0x08002A40 xPSR=0x61000000

You can go straight from that log entry to objdump -d firmware.elf | grep -A 5 '8002a40:' to find the exact line that dereferenced the bus address 0x24.

The three most frequent causes of HardFaults in Indian production firmware

From GSAS’s field observations across several dozen customer engagements, the root causes of Indian production HardFaults cluster into three categories:

  1. NULL pointer dereference: typically a callback pointer that was never registered, or a structure field that was not initialized at boot before an interrupt tried to dispatch through it. Signature: BFAR reads a small value (0-64), CFSR = 0x00000200 (PRECISERR). Fix: initialize aggressively, use MPU to catch NULL dereferences, add assertion macros that RTT-log their failures.
  2. Stack overflow into a peripheral region: a task stack or ISR stack grows past its allocated bound and starts trampling on SFR space. Signature: BFAR reads something in the peripheral address range (0x40000000 and up on STM32), CFSR = 0x00000200. Fix: turn on MPU-based stack protection (RTOS-managed for embOS, FreeRTOS, and Zephyr), increase stack sizes, use SystemView to catch stack high-watermarks.
  3. Unaligned memory access on cores that enforce alignment: typically a struct packed differently than the compiler expected, accessed via a cast. Signature: UFSR.UNALIGNED = 1, CFSR = 0x01000000. Fix: respect alignment attributes, use __packed carefully, turn off “treat unaligned accesses as OK” in the memory controller only as a last resort.

Every one of these root causes shows up clearly in the RTT log the SEGGER handler emits. The hard part is never “what was the fault”, it is “which line of code caused it”, and the PC + LR in the stack frame point you at that exact line.

You can in principle do this same thing with ST-LINK, Nordic’s on-board debugger, the NXP LPC-Link, or a CMSIS-DAP clone. The practical reality is that RTT throughput on non-SEGGER probes is either very slow (most ST-LINK clones), unreliable (CMSIS-DAP implementations without proper flow control), or requires additional workarounds through OpenOCD that don’t fit into a standing “always-on” logging role.

J-Link was designed around RTT as a first-class feature. Every J-Link model, from the entry J-Link BASE to the flagship J-Link PRO and the Ethernet-isolated J-Link ULTRA, runs RTT at hardware speed with no special configuration. The JLinkRTTLogger utility ships with the SEGGER software pack. The Ozone GUI debugger integrates the RTT viewer natively. This is the combination that makes the “leave the probe attached, capture everything” workflow actually practical on Indian benches.

For CI racks that flash and test Indian firmware at volume, the J-Link PRO is worth the step up, Ethernet interface removes USB cable contention, isolated JTAG protects the host PC when testing high-current targets, and the probe sits cleanly in a rack alongside other test equipment.

GSAS is SEGGER’s authorised engineering partner in India and supplies the J-Link family on quote. Whether you are a startup team in Bengaluru debugging your first STM32 product, an automotive Tier-1 in Pune building a dual-core i.MX RT1170 ECU, or a medical device OEM in Hyderabad running 24/7 stress tests on an nRF52 wearable, contact us for model selection guidance and a hands-on session on installing the SEGGER HardFault handler in your codebase.

Further reading

Interested in SEGGER tools?

Talk to our application engineers for personalized tool recommendations.

Stay in the Loop

Get monthly compliance updates, product insights, and engineering best practices delivered to your inbox.