Reputation: 2133
I have some code on an embedded system. The code can be seen here link, particularly this snippet:
uint32_t raw_fusebits[2];
....
/* Read the fuse settings in the user row, 64 bit */
((uint16_t*)&raw_fusebits)[0] = (uint16_t)NVM_MEMORY[NVMCTRL_USER / 2];
((uint16_t*)&raw_fusebits)[1] = (uint16_t)NVM_MEMORY[(NVMCTRL_USER / 2) + 1];
((uint16_t*)&raw_fusebits)[2] = (uint16_t)NVM_MEMORY[(NVMCTRL_USER / 2) + 2];
((uint16_t*)&raw_fusebits)[3] = (uint16_t)NVM_MEMORY[(NVMCTRL_USER / 2) + 3];
This causes a compiler warning under C99 related to strict-aliasing. Is there a better way to write this? At first I thought (using some simpler variable names for readability):
uint32_t x[2];
uint16_t a, b, c, d;
x[0] = ((uint32_t)a << 16) | ((uint32_t)b);
x[1] = ((uint32_t)c << 16) | ((uint32_t)d);
And then I switched to,
x[0] = ((uint32_t)b << 16) | ((uint32_t)a);
x[1] = ((uint32_t)d << 16) | ((uint32_t)c);
Which is correct but on my little-endian machine. And then I confused myself by wondering why endianness didn't apply when I tested these with certain values.
Is there a compiler-friendly way to rewrite the library code to be endian-agnostic?
edit: On line 73, you can see the definition for NVM_MEMORY, so the size is consistent there at least.
#define NVM_MEMORY ((volatile uint16_t *)FLASH_ADDR)
Upvotes: 1
Views: 190
Reputation: 154916
In this case you can just use memcpy
as suggested by John. But if the values you need to assign are calculated, memcpy
won't be sufficient. In that case, a compiler-friendly way to alias values supported by C is using a union
:
union {
struct {
uint32_t val[2];
} u32;
struct {
uint16_t val[4];
} u16;
} raw;
...
raw.u16.val[0] = NVM_MEMORY[NVMCTRL_USER / 2];
raw.u16.val[1] = NVM_MEMORY[(NVMCTRL_USER / 2) + 1];
raw.u16.val[2] = NVM_MEMORY[(NVMCTRL_USER / 2) + 2];
raw.u16.val[3] = NVM_MEMORY[(NVMCTRL_USER / 2) + 3];
/* obtain 32-bit values from raw.u32.val[0] and raw.u32.val[1] */
Upvotes: 1
Reputation: 249153
How about this?
memcpy(raw_fusebits, NVM_MEMORY + (NVMCTRL_USER / 2), sizeof(raw_fusebits));
Given that the fields are copied as-is, 16 bits at a time, it doesn't seem like there's any endianness handling in the original code. And using memcpy()
avoids the strict-aliasing problem.
Upvotes: 1