Project 7.01 Section 7 ⚡ Embedded Relevance: Critical HardFault MemManage Stack Smashing Defensive Assertions static_assert MISRA C++

7.01 Syntax, Runtime, & Logic Bugs vs Microcontroller Hardware Faults (HardFault & MemManage)

Executive Summary: Exploring the taxonomy of bugs: syntax errors, runtime faults, and subtle logic errors. In bare-metal systems, logic errors often manifest as fatal ARM Cortex-M HardFaults, MemManage violations, or stack-smashing crashes. We explore hardware fault triggers and defensive compile-time/runtime assertion architectures.

💻 1. Annotated Source Code

#include <iostream>
using namespace std;

int imBroken(int num1, int num2);

int main() {
	int a;

	try {
		a = imBroken(10, 5);
		cout << a << endl;

		a = imBroken(10, 0);   //will cause exception
		cout << a << endl;
	}
	catch (invalid_argument& err) {
		cout << "Exception caught: " << err.what() << endl;
	}

	return 0;
}

int imBroken(int num1, int num2) {
	int result = 0;

	if (num2 != 0) {
		result = num1 / num2;
	}
	else {
		throw invalid_argument("Cannot divide by 0");
	}

	return result;
}

📐 2. Architecture & UML Class Model

📐 Runtime Bug Tracing & Assert Verification Model
+ Public - Private # Protected
<<compilation-unit>> BugFunDebugEngine Debug & Diagnostics
-testBuffer[4] : int32_t
+triggerOutOfBounds(idx: int) : void
+safeAccess(idx: size_t) : int32_t

📚 3. Core C++ Concepts Deep-Dive

1. Bug Classification Taxonomy

  • Syntax Errors: Violations of the C++ grammar detected at compile-time (e.g. missing semicolons, type mismatches).
  • Runtime Errors: Program halts unexpectedly during execution (e.g. division by zero, null pointer dereferencing, uncaught exceptions).
  • Logic Errors: The program compiles and runs without crashing, but computes incorrect results (e.g. off-by-one loops, inverted boolean conditions).

2. Compiler Warning Diagnostics as First Line of Defense

Modern compilers can catch 90%+ of common logic bugs when configured with strict flags: -Wall -Wextra -Wpedantic -Wshadow -Wconversion -Werror. In embedded safety-critical systems, treating warnings as errors is mandatory.

⚡ 4. Embedded Systems & Hardware Reality

1. How Bugs Manifest as Hardware Faults on ARM Cortex-M

In microcontrollers without an operating system, runtime bugs trigger hardware interrupt exceptions:

  • HardFault: Triggered by unaligned memory access (if configured), executing undefined instructions, or bus errors during vector fetch.
  • MemManage Fault: Triggered by Memory Protection Unit (MPU) rule violations (e.g. writing to read-only Flash or executing code from SRAM).
  • BusFault: Triggered by attempting to access non-existent memory addresses or unpowered peripheral registers over the AHB/APB bus.
  • UsageFault: Triggered by division by zero (when DIV_0_TRP is set in NVIC) or unaligned memory access.

2. Watchdog Timers (WDT) and Fail-Safe Recovery

Logic bugs like infinite loops or deadlock cause watchdog timer expiration. The hardware watchdog forcibly resets the microcontroller into a known safe state (e.g., de-energizing motor PWMs and logging the Program Counter register to non-volatile backup registers).

⚠️ MISRA C++:2008 Rule 0-1-1 & Rule 0-1-9

The code shall not contain unreachable code or dead execution paths. Defensive assertions must be implemented without introducing non-terminating loops in release builds.

💡 5. Production-Ready Embedded Refactoring

Production firmware replaces generic runtime crashes with compile-time static_assert and custom hardware fault capture handlers:

💡 Production-Ready Refactor
#include <cstdint>
#include <type_traits>

// 1. Compile-Time Invariant Verification
template <typename T, size_t BufferSize>
struct DmaRingBuffer {
    static_assert(BufferSize > 0, "Buffer size must be non-zero");
    static_assert((BufferSize & (BufferSize - 1)) == 0, "Buffer size must be a power of 2 for fast bitmask indexing");
    static_assert(std::is_trivially_copyable<T>::value, "DMA elements must be trivially copyable");
    
    T buffer[BufferSize];
    uint32_t head{0};
    uint32_t tail{0};
};

// 2. Hardware Fault Capture Handler (ARM Cortex-M Example)
struct FaultFrame {
    uint32_t r0, r1, r2, r3, r12, lr, pc, psr;
};

extern "C" void HardFault_Handler_C(FaultFrame* frame) {
    // Read NVIC Configurable Fault Status Register (CFSR)
    volatile uint32_t* const CFSR = reinterpret_cast<volatile uint32_t*>(0xE000ED28);
    uint32_t fault_reason = *CFSR;
    
    // Log crash telemetry (PC and LR) to persistent battery-backed backup SRAM
    // Safe shutdown: disable all actuators
    while (1) {
        // Halt or trigger controlled watchdog reset
    }
}

📝 Knowledge Verification Quiz

Test your understanding of the C++ concepts and embedded microcontroller trade-offs covered in this guide. Click any option for instant feedback.

Q1. Which ARM Cortex-M hardware exception is triggered when firmware attempts to write to an unmapped peripheral memory address?
A MemManage Fault or BusFault
B Syntax Error Handler
C Segmentation Fault (SIGSEGV)
D Page Fault (Interrupt 14)
Detailed Explanation: Attempting to access non-existent memory addresses across the AHB/APB system bus triggers a BusFault or MemManage Fault (if an MPU is configured). Microcontrollers lack MMU-based OS page fault tables.
Q2. What is the primary advantage of static_assert over runtime assert() in embedded firmware?
A It evaluates conditions at compile time, incurring zero ROM/RAM footprint and preventing buggy binaries from flashing
B It generates larger binary files with more debug symbols
C It dynamically catches hardware brown-out resets
D It allows strings to be dynamically formatted at runtime
Detailed Explanation: static_assert runs during compilation. If the condition is false, compilation fails immediately, ensuring zero runtime CPU cycle overhead and zero Flash memory bloat.
Q3. Why are logic errors that cause infinite loops particularly hazardous in battery-powered IoT devices?
A They prevent CPU low-power sleep modes (WFI/WFE), causing rapid battery depletion and triggering Watchdog resets
B They automatically corrupt flash bootloader sectors
C They cause C++ templates to instantiate infinitely
D They double the microcontroller clock frequency
Detailed Explanation: Infinite loops prevent the microcontroller from entering low-power sleep modes (Wait For Interrupt - WFI), causing current draw to stay at peak (e.g. 20mA vs 2uA), draining batteries in hours.
Q4. Which compiler flag configuration converts all warnings into fatal compilation errors in safety-critical builds?
A -Wall -Wextra -Werror
B -O3 -fno-rtti
C -g -nostdlib
D -std=c++98 -fexceptions
Detailed Explanation: -Wall -Wextra enables comprehensive compiler warnings, and -Werror forces the compiler to treat all warnings as errors, preventing problematic code from compiling.