Accessing memory-mapped peripherals with Arm DS

Tutorial about accessing memory-mapped peripherals with Arm DS

Introduction Arm recommendations Alignment of registers Mapping of variables to specific addresses Code efficiency

Code efficiency

The Arm compiler normally uses a base register plus the immediate offset field to compile struct members or specific array element accesses. The immediate offset is available in the load or store instructions. In the Arm instruction set, LDR and STR word and byte instructions have a 4KB range, but LDRH and STRH instructions have a smaller immediate offset of 256 bytes.

Equivalent 16-bit Thumb instructions are much more restricted. LDR and STR have a range of 32 words, LDRH and STRH have a range of 32 halfwords, and LDRB and STRB have a range of 32 bytes. However, 32-bit Thumb instructions offer a significant range improvement. Because of the range restrictions, it is important to group related peripheral registers near to each other if possible. The compiler is generally good at minimizing the number of instructions required to access array elements or structure members. To perform these accesses, the compiler uses base registers.

You can choose between using one large C struct or array for the whole I/O space and smaller per-peripheral structs. For the two methods, there is little difference in terms of code efficiency. Using a large struct might help if:

  • For word and byte accesses, your code works with a base pointer with a 4KB range.
  • Entire I/O space is <4KB.

But arguably it is more elegant to have one struct per peripheral. Smaller, per-peripheral structs are more maintainable.