How do I create a “spacer” in a C++ class memory structure?

  • A+
Category:Languages

The issue

In a low level bare-metal embedded context, I would like to create a blank space in the memory, within a C++ structure and without any name, to forbid the user to access such memory location.

Right now, I have achieved to do so by putting an ugly uint32_t :96; bitfield which will conveniently take place of three words, but it will raise a warning from GCC (Bitfield too large to fit in uint32_t, which is pretty legitimate).

While it works fine, it is not very clean when you want to distribute a library with several hundreds of those warnings...

How do I do that properly?

Why is there an issue in the first place?

The project I'm working on consist in defining the memory structure of different peripherals of a whole microcontroller line (STMicroelectronics STM32). To do so, the result is a class which contains an union of several structures which defines all registers, depending on the targeted microcontroller.

One simple example is the following, for a pretty simple peripheral: a General Purpose Input/Output (GPIO)

union {      struct     {         GPIO_MAP0_MODER;         GPIO_MAP0_OTYPER;         GPIO_MAP0_OSPEEDR;         GPIO_MAP0_PUPDR;         GPIO_MAP0_IDR;         GPIO_MAP0_ODR;         GPIO_MAP0_BSRR;         GPIO_MAP0_LCKR;         GPIO_MAP0_AFR;         GPIO_MAP0_BRR;         GPIO_MAP0_ASCR;     };     struct     {         GPIO_MAP1_CRL;         GPIO_MAP1_CRH;         GPIO_MAP1_IDR;         GPIO_MAP1_ODR;         GPIO_MAP1_BSRR;         GPIO_MAP1_BRR;         GPIO_MAP1_LCKR;         uint32_t :32;         GPIO_MAP1_AFRL;         GPIO_MAP1_AFRH;         uint32_t :64;     };     struct     {         uint32_t :192;         GPIO_MAP2_BSRRL;         GPIO_MAP2_BSRRH;         uint32_t :160;     }; }; 

Where all GPIO_MAPx_YYY is a macro, defined either as uint32_t :32 or the register type (a dedicated structure).

Here you see the uint32_t :192; which works well, but trigger a warning.

I might have replaced it by several uint32_t :32; (here 6), but I have some extreme cases where I have uint32_t :1344; (42) (among others). So I would rather not add about one hundred lines on top of 8k others, even though the structure generation is scripted.

By the way, all posted code is working absolutely (and surprisingly) fine.

The exact warning message is something like this: width of 'sool::ll::GPIO::<anonymous union>::<anonymous struct>::<anonymous>' exceeds its type (I just love how shady it is).

I would rather not solve this by simply removing the warning, but the use of

#pragma GCC diagnostic push #pragma GCC diagnostic ignored "-WTheRightFlag" /* My code */ #pragma GCC diagnostic pop 

May be a solution... If I find TheRightFlag

In the same way as pointed out in this thread, gcc/cp/class.c hold this sad code part:

warning_at (DECL_SOURCE_LOCATION (field), 0,         "width of %qD exceeds its type", field); 

Which tells us that there is no -Wxxx flag to remove this warning...

 


In the embedded systems arena, you can model hardware either by using a structure or by defining pointers to the register addresses.

Modeling by structure is not recommended because the compiler is allowed to add padding between members for alignment purposes (although many compilers for embedded systems have a pragma for packing the structure).

Example:

uint16_t * const UART1 = (uint16_t *)(0x40000); const unsigned int UART_STATUS_OFFSET = 1U; const unsigned int UART_TRANSMIT_REGISTER = 2U; uint16_t * const UART1_STATUS_REGISTER = (UART1 + UART_STATUS_OFFSET); uint16_t * const UART1_TRANSMIT_REGISTER = (UART1 + UART_TRANSMIT_REGISTER); 

You could also use the array notation:

uint16_t status = UART1[UART_STATUS_OFFSET];   

If you must use the structure, IMHO, the best method to skip addresses would be to define a member and not access it:

struct UART1 {   uint16_t status;   uint16_t reserved1; // Transmit register   uint16_t receive_register; }; 

In one of our projects we have both constants and structs from different vendors (vendor 1 uses constants while vendor 2 uses structures).

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: