Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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 it by putting an ugly uint32_t :96; bitfield which will conveniently take the 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 consists of defining the memory structure of different peripherals of a whole microcontroller line (STMicroelectronics STM32). To do so, the result is a class which contains a union of several structures which define all registers, depending on the targeted microcontroller.

One simple example for a pretty simple peripheral is the following: 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 it triggers a warning.

What I've considered so far:

I might have replaced it by several uint32_t :32; (6 here), 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.

The exact warning message is something like: 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. However, as pointed out in this thread, gcc/cp/class.c with 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...

like image 378
J Faucher Avatar asked Nov 01 '18 21:11

J Faucher


5 Answers

How about a C++-ish way?

namespace GPIO {

static volatile uint32_t &MAP0_MODER = *reinterpret_cast<uint32_t*>(0x4000);
static volatile uint32_t &MAP0_OTYPER = *reinterpret_cast<uint32_t*>(0x4004);

}

int main() {
    GPIO::MAP0_MODER = 42;
}

You get autocompletion because of the GPIO namespace, and there is no need for dummy padding. Even, it is more clear what's going on, as you can see the address of each register, you don't have to rely on the compiler's padding behavior at all.

like image 173
geza Avatar answered Nov 06 '22 03:11

geza


Use multiple adjacent anonymous bitfields. So instead of:

    uint32_t :160;

for example, you'd have:

    uint32_t :32;
    uint32_t :32;
    uint32_t :32;
    uint32_t :32;
    uint32_t :32;

One for each register you want to be anonymous.

If you have large spaces to fill it may be clearer and less error prone to use macros to repeat the single 32 bit space. For example, given:

#define REPEAT_2(a) a a
#define REPEAT_4(a) REPEAT_2(a) REPEAT_2(a)
#define REPEAT_8(a) REPEAT_4(a) REPEAT_4(a)
#define REPEAT_16(a) REPEAT_8(a) REPEAT_8(a)
#define REPEAT_32(a) REPEAT_16(a) REPEAT_16(a)

Then a 1344 (42 * 32 bit) space can be added thus:

struct
{
    ...
    REPEAT_32(uint32_t :32;) 
    REPEAT_8(uint32_t :32;) 
    REPEAT_2(uint32_t :32;)
    ...
};
like image 33
Clifford Avatar answered Nov 06 '22 02:11

Clifford


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).

like image 20
Thomas Matthews Avatar answered Nov 06 '22 03:11

Thomas Matthews


geza's right that you really don't want to be using classes for this.

But, if you were to insist, the best way to add an unused member of n bytes' width, is simply to do so:

char unused[n];

If you add an implementation-specific pragma to prevent the addition of arbitrary padding to the class's members, this can work.


For GNU C/C++ (gcc, clang, and others that support the same extensions), one of the valid places to put the attribute is:

#include <stddef.h>
#include <stdint.h>
#include <assert.h>  // for C11 static_assert, so this is valid C as well as C++

struct __attribute__((packed)) GPIO {
    volatile uint32_t a;
    char unused[3];
    volatile uint32_t b;
};

static_assert(offsetof(struct GPIO, b) == 7, "wrong GPIO struct layout");

(example on the Godbolt compiler explorer showing offsetof(GPIO, b) = 7 bytes.)

like image 13
Lightness Races in Orbit Avatar answered Nov 06 '22 01:11

Lightness Races in Orbit


To expand on @Clifford's and @Adam Kotwasinski's answers:

#define REP10(a)        a a a a a a a a a a
#define REP1034(a)      REP10(REP10(REP10(a))) REP10(a a a) a a a a

struct foo {
        int before;
        REP1034(unsigned int :32;)
        int after;
};
int main(void){
        struct foo bar;
        return 0;
}
like image 9
Placeholder Name Avatar answered Nov 06 '22 03:11

Placeholder Name