Search code examples
microcontrollervolatilememory-address

Casting a struct with many members to a memory address


I am reading (self learning) through some microcontroller code and want to verify that I've understood this correctly.

#define PERIPH_BASE           ((u32)0x40000000)
#define AHBPERIPH_BASE        (PERIPH_BASE + 0x20000)
#define RCC_BASE              (AHBPERIPH_BASE + 0x1000)

/*------------------------ Reset and Clock Control ---------------------------*/
typedef struct
{
  vu32 CR;
  vu32 CFGR;
  vu32 CIR;
  vu32 APB2RSTR;
  vu32 APB1RSTR;
  vu32 AHBENR;
  vu32 APB2ENR;
  vu32 APB1ENR;
  vu32 BDCR;
  vu32 CSR;
} RCC_TypeDef;

#define RCC                 ((RCC_TypeDef *) RCC_BASE)

Line 3 above is assigning the address 0x40000000 + 0x20000 + 0x1000 to RCC_BASE.

That would be the address defined by the chip designers; which I can find in this datasheet, and it tells the CPU where to find the beginning of the register set for the rest and control clock.

Obviously u32 and vu32 are typdefs, vu32 being a volatile to tell the compiler not to do any optimization on it. I pulled it and it's:

typedef volatile unsigned long  vu32;

So then the last line sets the variable RCC to RCC_BASE thereby assigning its first member CR to the value of RCC_BASE, and therefore CFGR becomes RCC_BASE + 0x04 ( 4 bytes i.e 32 bits), and CIR = RCC_BASE + 0x08 (64 bits). And this is because of the volatile.

Is that all accurate? Somewhat accurate? Outright false?


Solution

  • The code segment looks very ARM-ish and CMSIS-ish so I'll do my best in those terms.

    These types of declarations are common when defining memory mapped peripheral registers. The last line that defines RCC is not a variable declaration. It is a pre-processor macro definition. The intent is to allow you to have statements such as:

    vu32 cr = RCC->CR ;
    

    in your code, which gives you the value of the CR register in the RCC peripheral into a local variable which I have named similarly. The compiler computes the correct offset to the CR structure member and generates the instructions necessary to fetch the value from memory at that address. Clearly, the compiler has no idea what resides at the address. Since it is macro expanded by the pre-processor implying a direct text substitution, ultimately you have a hard physical address cast to point to a particular layout of hardware peripheral registers that you have "cleverly" arranged into a "C" structure. Volatility doesn't have a lot to do with this other than the compiler will not assume that just because it performs no writes to the structure member that the value has not changed, i.e. the value can change without the program performing a write.