I am writing register maps in C for a TI ARM based Micro-controller board. Here is the link to the datasheet.
I am using the following guidelines on how Register maps should be written in C: Register Maps. These guidelines are similar to ARMs CMSIS (Cortex Microcontroller Software Interface Standard) for writing C code.
I am facing problem with writing System Control Register maps(refer section 5.5 page 237 onwards in datasheet) using C struct. All registers are of size 32-bits
- The base address is 0x400F.E000.
- Register 1: Device Identification 0(DID0), offset 0x000
- Register 2: Device Identification 1 (DID1), offset 0x004
- Register 3: Brown-Out Reset Control (PBORCTL), offset 0x030
- Register 4: Raw Interrupt Status (RIS), offset 0x050
- etc..
If I now write a structure like this:
typedef struct
{
uint32_t DID0; // offset 0x000. distance: 0
uint32_t DID1; // offset 0x004. distance: 4
uint32_t PBORCTL; // **HOW to place this at offset 0x030 ?**
uint32_t RIS; // **HOW to place this at offset 0x050 ?**
// ...and so on
}PER_REG_MAP_T;
#define PERIPH_BASE ((uint32_t)0x400FE000)
#define MY_PERIPH_A ((PER_REG_MAP_T*)PERIPH_BASE)
void Reset(PER_REG_MAP_T* PERIPH_A)
{
PERIPH_A->DID0 = 0;
PERIPH_A->DID1= 0;
PERIPH_A->PBORCTL= 1;
PERIPH_A->RIS= 0;
// .. and so on
}
The main issue I am facing is how to place PBORCTL and RIS inside the structure as they are at offset 0x030 and 0x050 with respect to the base address of the structure. I have not done too much bit-level C-programming before so this question might be too simple, but I donot know how to do it.
Best Answer
Structures are unsuitable for writing register maps, because a struct can have padding bytes added anywhere inside it, for alignment purposes. This depends on the compiler - you have to ensure that no padding is used (for example through a static assert).
Furthermore, it is easy to make mistakes when writing large structures that are supposed to reflect a register map, you have to get every single byte right or you'll break everything. This makes the struct vulnerable during maintenance.
I would strongly suggest you to write register maps through macros, such as:
This also has the advantage of being 100% portable to any C compiler.
Alternatively, you could do something more intricate such as this:
Then when writing your driver, you can do like this:
This is very useful when you have many identical peripherals on the MCU, with the same registers, but only want one code to control them.