Search code examples
carmgpiocortex-m

ARM Cortex M4 - C Programming and Memory Access Optimization


The following three lines of code are optimized ways to modify bits with 1 MOV instruction instead of using a less interrupt safe read-modify-write idiom. They are identical to each other, and set the LED_RED bit in the GPIO Port's Data Register:

  1. *((volatile unsigned long*)(0x40025000 + (LED_RED << 2))) = LED_RED;
  2. *(GPIO_PORTF_DATA_BITS_R + LED_RED) = LED_RED;
  3. GPIO_PORTF_DATA_BITS_R[LED_RED] = LED_RED;

LED_RED is simply (volatile unsigned long) 0x02. In the memory map of this microcontroller, the first 2 LSBs of this register (and others) are unused, so the left shift in the first example makes sense.

The definition for GPIO_PORTF_DATA_BITS_R is:

#define GPIO_PORTF_DATA_BITS_R ((volatile unsigned long *)0x40025000)

My question is: How come I do not need to left shift twice when using pointer arithmetic or array indexing (2nd method and 3rd method, respectively)? I'm having a hard time understanding. Thank you in advance.


Solution

  • Remember how C pointer arithmetic works: adding an offset to a pointer operates in units of the type pointed to. Since GPIO_PORTF_DATA_BITS_R has type unisgned long *, and sizeof(unsigned long) == 4, then GPIO_PORTF_DATA_BITS_R + LED_RED effectively adds 2 * 4 = 8 bytes.

    Note that (1) does arithmetic on 0x40025000, which is an integer, not a pointer, so we need to add 8 to get the same result. Left shift by 2 is the same as multiplication by 4, so LED_RED << 2 again equals 8.

    (3) is exactly equivalent to (2) by definition of the [] operator.