Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 77

Register Read-Back Testing for Hardware Fault Diagnosis

Recommended resources

Hello! Welcome to your next lesson on functional safety.

In our last lesson, we explored how to use a Memory Protection Unit (MPU) to create hardware-enforced firewalls between software components. This technique, known as spatial separation, is fundamental to ensuring Freedom From Interference (FFI) and preventing software faults in one component from corrupting another.

Today, we continue our focus on hardware integrity, shifting from memory to the peripherals themselves. The learning outcome for this lesson is to implement a register read-back test to diagnose hardware peripheral faults. This is a critical diagnostic measure required by ISO 26262 to ensure that your peripheral hardware is configured correctly and hasn't been compromised by a permanent or transient fault. Given your background in ISO 26262 and driver development, you'll recognize this as a key technique for achieving the required hardware diagnostic coverage (DC) for a specific ASIL.

1. The "Why": The Silent Threat of Corrupted Registers

After you initialize a peripheral like a CAN controller or a timer, you assume its configuration registers hold the values you wrote to them. However, this assumption can be violated by several hardware faults:

  • Permanent Faults: A memory cell within a register could be "stuck" at 0 or 1 due to a manufacturing defect or wear.
  • Bus Faults: The internal bus connecting the CPU core to the peripheral might have a fault, causing data to be corrupted during a write or read operation.
  • Transient Faults: A Single Event Upset (SEU), often caused by cosmic radiation, can flip a bit in a register at any time during operation. An SEU in a CAN bit-timing register, for example, could silently take a node off the bus.

To guard against these threats, ISO 26262 recommends implementing diagnostic tests. One of the most effective and widely used is the periodic read-back of static configuration registers.

Functional Safety Manual for MSPM0G3x0x-Q1

This excerpt from a Texas Instruments Functional Safety Manual provides the official context for this safety mechanism within the ISO 26262 framework.

Please read section 6.3.1, "Periodic Read of Static Configuration Registers." Focus on how it describes the mechanism: software verifying the values of static configuration registers against a known reference.

As the document highlights, this test targets static configuration registers—those that are typically set once at initialization and are not expected to change during normal operation.


2. The "How": Implementing a Register Test

There are two primary methods for implementing this test, each with its own trade-offs.

Method 1: Direct Write/Read-Back Test (POST and On-Demand)

This is the most thorough method and is ideal for running as part of a Power-On Self-Test (POST) or in a safe state where the peripheral can be temporarily disturbed. The goal is to check each read-write bit for stuck-at-0 and stuck-at-1 faults.

The algorithm is as follows:

  1. Isolate: The test is highly intrusive. You must ensure the peripheral is disabled and that no other part of the system (e.g., an ISR or a DMA channel) will try to access it. This entire sequence must be a critical section, typically by disabling interrupts.
  2. Save State: Read and save the original value of the register being tested.
  3. Define a Mask: From the microcontroller datasheet, determine the bitmask of all readable and writable bits (rw_mask). This is crucial, as you must not attempt to write to read-only, reserved, or special-purpose bits (like write-1-to-clear flags).
  4. Test for Stuck-at-0: Write a pattern of all ones (0xFFFFFFFF) to the RW bits.
    • *reg_addr = (original_val & ~rw_mask) | (0xFFFFFFFF & rw_mask);
  5. Verify: Read the value back. The bits corresponding to the rw_mask must be all ones.
    • if ((*reg_addr & rw_mask) != (0xFFFFFFFF & rw_mask)) { /* Fault detected */ }
  6. Test for Stuck-at-1: Write a pattern of all zeros (0x00000000) to the RW bits.
    • *reg_addr = (original_val & ~rw_mask) | (0x00000000 & rw_mask);
  7. Verify: Read the value back. The bits corresponding to the rw_mask must be all zeros.
    • if ((*reg_addr & rw_mask) != (0x00000000 & rw_mask)) { /* Fault detected */ }
  8. Restore State: Write the original value back to the register.
  9. Re-enable: Exit the critical section (re-enable interrupts).

This can be encapsulated in a generic C function:

#include <stdbool.h>
#include <stdint.h>

/**
 * @brief Performs a read/write test on a single hardware register.
 * @param reg_addr Volatile pointer to the register.
 * @param rw_mask A mask indicating which bits are read-write.
 * @return true if the test passes, false if a fault is detected.
 *
 * @note This function is intrusive and should only be run when the
 *       peripheral is in a safe state (e.g., disabled). The caller
 *       is responsible for managing critical sections (disabling interrupts).
 */
bool test_register_integrity(volatile uint32_t* reg_addr, uint32_t rw_mask) {
    uint32_t original_val = *reg_addr;
    
    // Use alternating bit patterns for a thorough test.
    const uint32_t pattern1 = 0xAAAAAAAA;
    const uint32_t pattern2 = 0x55555555;

    // Test with pattern 1
    *reg_addr = (original_val & ~rw_mask) | (pattern1 & rw_mask);
    if ((*reg_addr & rw_mask) != (pattern1 & rw_mask)) {
        *reg_addr = original_val; // Attempt to restore before failing
        return false;
    }

    // Test with pattern 2
    *reg_addr = (original_val & ~rw_mask) | (pattern2 & rw_mask);
    if ((*reg_addr & rw_mask) != (pattern2 & rw_mask)) {
        *reg_addr = original_val; // Attempt to restore before failing
        return false;
    }

    // Success, restore the original configuration
    *reg_addr = original_val;
    return true;
}
Test your understanding! (Interview Question)

You are testing a timer's control register (TIMER_CTL) at address 0x40010000. The datasheet specifies the 32-bit register as follows:

  • Bits 31:16: Reserved, read as 0.
  • Bits 15:8: Prescaler value (Read/Write).
  • Bit 7: Update Event Flag (Read-Only, Write-1-to-clear).
  • Bits 6:1: Reserved, read as 0.
  • Bit 0: Counter Enable (Read/Write).

What rw_mask would you use for the test_register_integrity function, and why?

Show answer

The correct mask would be 0x0000FF01.

Here's the breakdown:

  • Prescaler (Bits 15:8): These are standard R/W bits, so 0x0000FF00 is included.
  • Counter Enable (Bit 0): This is a standard R/W bit, so 0x00000001 is included.
  • Reserved Bits: These must never be written to, so they are excluded from the mask.
  • Update Event Flag (Bit 7): This is a W1C (Write-1-to-Clear) flag. Writing a 1 has a side effect (clearing the flag), and writing a 0 has no effect. It is not a standard R/W bit and should be excluded from a generic integrity test to avoid unintended side effects. The test is meant to verify storage, not trigger actions.

Therefore, the final mask combines the Prescaler and Counter Enable bits: 0x0000FF00 | 0x00000001 = 0x0000FF01.

To know which registers to test, you would consult the safety manual or datasheet.

Timer Peripheral Block Diagram with Capture/Compare Functionality
This block diagram shows the internal structure of a timer peripheral. A register read-back test would target the static configuration registers like the prescaler, auto-reload register, and various control registers (e.g., CCCTL, CTRCTL). It would NOT test dynamic registers like the counter itself.

The following reading provides a list of typical static registers for various peripherals, giving you a concrete idea of the scope of this test.

Functional Safety Manual for MSPM0G3x0x-Q1

Let's return to the TI Safety Manual. This table lists the specific configuration registers associated with safety mechanisms for a wide range of peripherals.

Review Table 6-1. You don't need to memorize it, but observe the types of registers listed for peripherals you are familiar with, like UART, SPI, I2C, and CAN (MCAN). Notice they are primarily control, clocking, and configuration registers, reinforcing the concept of testing the static setup.

Method 2: CRC Check (Periodic Runtime)

The write/read-back test is too disruptive for continuous runtime monitoring. A less intrusive method, mentioned in the first TI document you read, is to use a CRC.

The algorithm is:

  1. Calculate Golden CRC: After system initialization, when all peripherals are configured, perform a one-time calculation. Read the values of all static configuration registers for a peripheral (or a group of them) in a fixed order and compute a CRC checksum. Store this "golden CRC" value in a safe RAM location.
  2. Periodic Check: In a low-priority background task, periodically re-read the same set of registers in the exact same order and recalculate the CRC.
  3. Compare: Compare the newly calculated CRC with the stored golden CRC. If they do not match, it indicates that at least one bit in the configuration space has been corrupted. A fault should be reported.

Trade-offs:

  • Pro (CRC): Very low overhead and non-intrusive, making it perfect for runtime checks.
  • Con (CRC): It only detects if a register's value has changed from its initial state. It cannot detect if a bit was already stuck when the initial "golden CRC" was calculated.
  • Pro (Write/Read-Back): Actively tests every R/W bit for both stuck-at-0 and stuck-at-1 faults. More comprehensive fault detection.
  • Con (Write/Read-Back): Highly intrusive, requiring the peripheral to be in a safe state.

A robust safety design often uses both: the comprehensive write/read-back test at startup (POST) and the efficient CRC check periodically during runtime.

Finally, let's look at a real-world library function that implements one of these tests.

DIAG_ADC_SFRWriteRead - Microchip Online Docs

This documentation from Microchip describes a diagnostic library function for checking ADC registers. It provides a practical example of how this functionality is exposed to the application developer.

Read the function description and look at its C prototype. This function implements the direct write/read-back test ("checks that values written... can be read back"). Note its parameters, which allow it to return detailed results about which registers failed, and that it's intended to be run at POST or on-demand.


Conclusion

You have now learned how to implement a register read-back test, a crucial safety mechanism for validating the integrity of your hardware peripherals. This technique directly addresses the risk of configuration corruption from both permanent and transient hardware faults.

Key Takeaways:

  • Purpose: Register read-back tests are a software-based diagnostic for detecting hardware faults (stuck-at, bus, SEU) in peripheral configuration registers, as recommended by ISO 26262.
  • Target: The test focuses on static configuration registers, whose values should not change during operation.
  • Direct Test Method: A comprehensive test involves saving the register, writing test patterns (e.g., 0xAAAAAAAA, 0x55555555), reading back, verifying against a rw_mask, and restoring. This is best for POST.
  • CRC Method: A less intrusive runtime method involves calculating a CRC of all static registers and periodically comparing it against a "golden" value computed at startup.
  • Critical Implementation Detail: You must carefully identify the R/W bits from the datasheet and create a rw_mask. Tests must be performed atomically within a critical section to prevent race conditions.

Next Lesson Preview

Detecting a fault is only the first step. The next is to report it in a standardized way so the system can take appropriate action. In our final lesson for this module, you will learn how to integrate a driver with the AUTOSAR Development Error Tracer (DET) module for runtime fault reporting. This will bridge the gap between low-level fault detection, like the register test you learned today, and system-level error handling in an AUTOSAR architecture.