Course contentsShow
← Embedded Systems & Computer Architecture
Lesson 182 of 9363. Microcontroller Fundamentals and Bare-Metal CFree lesson

Handling Memory Alignment and Padding in Registers

Learners will manage memory alignment and padding requirements to ensure that peripheral structures map correctly to the underlying hardware architecture.

In this lesson, you will learn how to align C structures with hardware peripheral addresses to prevent data misalignment and unintended memory gaps.

When we map C structs to hardware peripherals, we rely on the compiler to layout members exactly as we define them. However, compilers are designed for general-purpose computing. To optimize access speed, they often insert invisible "padding bytes" between struct members to ensure data falls on natural memory boundaries (e.g., placing a 32-bit integer only at addresses divisible by 4).

In the world of embedded systems, this is dangerous. If your peripheral hardware expects a 16-bit status register at a specific offset, but the compiler injects a hidden padding byte before it, your code will write data to the wrong hardware register!

Why Padding Occurs

Consider this typical peripheral block:

struct Timer_Peripheral {
    uint8_t  control;    // Offset 0x00
    uint32_t counter;    // Offset 0x01? No, compiler adds 3 bytes padding!
};

Because the uint32_t requires 4-byte alignment, the compiler creates a hole at offset 0x01 through 0x03 to push the counter to address 0x04. Your hardware, however, likely expected that counter to start immediately at 0x01.

Managing Alignment

To force the compiler to honor your specific hardware layout, we must use attributes to disable this automatic padding. In most embedded GCC environments, we use __attribute__((packed)) to tell the compiler: "Do exactly as I say, byte for byte."

struct __attribute__((packed)) Timer_Peripheral {
    uint8_t  control;    // Offset 0x00
    uint32_t counter;    // Offset 0x01 (No padding inserted)
};

By packing the structure, you guarantee that the C struct maps perfectly to the memory-mapped I/O (MMIO) region defined in your microcontroller's datasheet. Always check your datasheet to see if the peripheral registers are strictly packed or if they leave gaps intentionally. If they leave gaps, you may need to insert "dummy" padding members yourself to ensure your pointer offsets remain synchronized with the hardware.

Key Takeaway: Use the packed attribute to prevent the compiler from inserting hidden padding, ensuring your C structures perfectly match the rigid, byte-sensitive layout of peripheral hardware registers.