The PMA memory for the USB peripheral on some STM32s is structured as 256 16-bit words that are 32-bit aligned. This format is demonstrated in the following table:
| Address + offset | 0x0 | 0x1 | 0x2 | 0x3 |
|---|---|---|---|---|
| 0x40006000 | 0x000 | 0x001 | ----- | ----- |
| 0x40006004 | 0x002 | 0x003 | ----- | ----- |
| 0x40006008 | 0x004 | 0x005 | ----- | ----- |
| 0x4000600C | 0x006 | 0x007 | ----- | ----- |
| 0x40006010 | 0x008 | 0x009 | ----- | ----- |
| .... | .... | .... | ----- | ----- |
| 0x400063F8 | 0x1FC | 0x1FD | ----- | ----- |
| 0x400063FC | 0x1FE | 0x1FF | ----- | ----- |
For example, the PMA address 0x006 is accessed by the CPU at address 0x4000600C. Access to any address with an offset of 0x2 and 0x3 is invalid.
Ideally, data in the PMA could be accessed by defining structs such as the following, but count would occupy offsets 0x3 and 0x4, assuming the struct points to an address with an offset of 0x0.
struct buf_desc_entry {
volatile uint16_t addr;
volatile uint16_t count;
} __attribute__((packed));
One solution would be to pad the struct so that count occupies offsets 0x0 and 0x1. This would be tedious and insecure since it would have to be done manually for all structs.
struct buf_desc_entry {
volatile uint16_t addr;
volatile uint16_t _1;
volatile uint16_t count;
volatile uint16_t _2;
} __attribute__((packed));
Another solution for this example would be to 32-bit align all variables either using __attribute__((aligned (4))) or using a linker script, however, this solution would not work for structs that contain 8-bit variables, since they can occupy offsets of 0x0 or 0x1.
Is there a more elegant way of doing this?