6

I have a register definition provided by the microcontroller manufacturer that can be handled as a bit field. The register is defined as follows:

#define SCU_WDTSCON0 (*( SCU_WDTSCON0_type *) 0xf00360f0u)

The bit field definition looks like this:

typedef volatile union {
unsigned U;
int I;
struct {
    unsigned    ENDINIT  :1;    // [0:0] End-of-Initialization Control Bit
    unsigned    LCK  :1;    // [1:1] Lock Bit to Control Access to WDTxCON0
    unsigned    HPW0     :2;    // [3:2] Hardware Password 0
    unsigned    HPW1     :4;    // [7:4] Hardware Password 1
    unsigned    PW   :8;    // [15:8] User-Definable Password Field for Access to WDTxCON0
    unsigned    REL  :16;   // [31:16] Reload Value for the WDT
  } B;
} SCU_WDTSCON0_type;

Instead of directly writing to the register, I want to use a uint32 buffer variable first, but still be able to edit it in the manner of the register bit field definition. This implementation seems to be working, as the address is just replaced with &buffer_variable:

volatile uint32 buffer_variable;
SCU_WDTSCON0_type register_buffer = (*( SCU_WDTSCON0_type *) &buffer_variable);

Could this lead to undefined behavior?

Mentulatus
  • 63
  • 4
  • This is a good question. In my mind the way to test it is to try the same code on two architectures, one big endian, one little endian. I happen to have such systems here and if there's no definitive answer I'll compile some code when I have time. – David Hoelzer Apr 07 '16 at 09:57
  • 1
    @DavidHoelzer This doesn't have a lot to do with endianess. You can't know how a bit-field stores its bits regardless of endianess. The behavior of bit-fields is not well specified to begin with. [See this](http://stackoverflow.com/questions/6043483/why-bit-endianness-is-an-issue-in-bitfields/6044223#6044223). – Lundin Apr 07 '16 at 11:07
  • Honestly, I rarely use them. I can just imagine that the endianness could have an impact if you cast them to something else. – David Hoelzer Apr 07 '16 at 12:32
  • it is a bad practice for the library to do that, fortunately for them the probably only support specific compilers and versions of those compilers so it only has to work for those situations. generically there is no reason for it to work per the standard. likewise then trying to use a union to have two ways to access those bits is also not supported (spec doesnt mentioned bitfields), almost always you get lucky but, it is just that luck. these things work often enough they become a common practice, but look at the library and how much chaos there would be when it does break. – old_timer Apr 07 '16 at 17:02
  • been there, done that when it fails it fails massively and the repair work is costly. it doesnt gain anything, so not worth the risk. – old_timer Apr 07 '16 at 17:03
  • Standard type is `uint32_t`. Don't use homebrew types if there is already a standard type avaliable. And I'd rather change the manufacturer. Whoever uses `unsigned` etc. in a register declaration header has been sleeping the last 17 years. – too honest for this site Apr 07 '16 at 19:41
  • Care to elaborate why using `unsigned` in register declarations should be avoided? – Mentulatus Apr 18 '16 at 09:15

1 Answers1

3

Your buffer variable needs to be exactly the same type as one of the union members, in this case unsigned. In case the compiler treats uint32 and unsigned as different types, it will lead to undefined behavior (violates strict aliasing rule). Otherwise, if they are the same type, the code is fine.

(As a side note, most bugs related to strict aliasing violations are caused by the compiler's optimizer. In case of volatile variables, this is less of an issue, since the compiler isn't allowed to optimize them anyway. So in practice, I doubt you'll ever encounter any UB for this scenario, even though it could be UB in theory.)

Lundin
  • 195,001
  • 40
  • 254
  • 396