Reputation: 22252
Given an expression in C like:
foo = (<an expression>);
The compiler will often deign to assign the foo
value multiple times. When foo
is a register that controls hardware, that can lead to unexpected results. For example, I'm finding that expressions likes
foo = (struct Bar){.field1=13, .field2=42};
foo = Field1Value(13) | Field2Value(42);
will generate different sequences of updating foo. The second is generally pretty good about completing the rvalue and then assigning it. But the first often likes to update foo with multiple assignments. I have tried parentheses placement, but the optimizer seems to think otherwise.
UPDATE
What I wanted to understand is why these 3 statements had different results:
// 1
GCLK->CLKCTRL = (GCLK_CLKCTRL_Type){{.ID=GCLK_CLKCTRL_ID_TCC0_TCC1_Val, .GEN=GCLK_CLKCTRL_GEN_GCLK0_Val, .CLKEN=true}};
// 2
GCLK->CLKCTRL.reg = (GCLK_CLKCTRL_Type){{.ID=GCLK_CLKCTRL_ID_TCC0_TCC1_Val, .GEN=GCLK_CLKCTRL_GEN_GCLK0_Val, .CLKEN=true}}.reg;
// 3
GCLK_CLKCTRL_Type tmp = {{.ID=GCLK_CLKCTRL_ID_TCC0_TCC1_Val, .GEN=GCLK_CLKCTRL_GEN_GCLK0_Val, .CLKEN=true}};
GCLK->CLKCTRL = tmp;
The first was problematic. The second 2 work. I wanted to understand why. Obviously, the alternative way to do this kind of thing is with code like:
GCLK->CLKCTRL.reg = (GCLK_CLKCTRL_ID_TCC0_TCC1 | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_CLKEN);
The GCLK global is a pointer to this structure def:
typedef struct {
__IO GCLK_CTRL_Type CTRL; /**< \brief Offset: 0x0 (R/W 8) Control */
__I GCLK_STATUS_Type STATUS; /**< \brief Offset: 0x1 (R/ 8) Status */
__IO GCLK_CLKCTRL_Type CLKCTRL; /**< \brief Offset: 0x2 (R/W 16) Generic Clock Control */
__IO GCLK_GENCTRL_Type GENCTRL; /**< \brief Offset: 0x4 (R/W 32) Generic Clock Generator Control */
__IO GCLK_GENDIV_Type GENDIV; /**< \brief Offset: 0x8 (R/W 32) Generic Clock Generator Division */
} Gclk
where the __IO
is a macro for volatile
among other things.
Upvotes: 1
Views: 112
Reputation: 153478
How to force C compiler to complete computation before assignment (?)
It should be sufficient to insure foo
is of type volatile
. @ Eugene Sh.
volatile type_of_foo foo = Field1Value(13) | Field2Value(42);
An alternative would use 2 steps:
Assign the result to a safe, volatile
, object that can be set many times, then assign to the register.
volatile type_of_foo foo_tmp = Field1Value(13) | Field2Value(42);
volatile type_of_foo foo = foo_tmp;
The compiler can assign, at the assembly code level, foo_tmp
many times, yet cannot use that to assign foo
until foo_tmp
assignment is complete.
Pedantically, the foo = foo_tmp;
itself, could in theory involve multiple assignments, but not likely. Consider _Atomic
, an option C feature for more ideas.
Upvotes: 2
Reputation: 215259
For accessing memory-mapped registers, always use pointer-to-volatile
types, and always ensure that the pointed-to type is a basic type that can be accessed as a single unit on your hardware architecture. In particular do not use assignment of whole struct-type objects and do not use bitfields, since the C language does not strictly specify how they interact with volatile
. You can use a volatle-qualified struct whose individual members have types that make sense to access as a unit, though. For example:
struct regs {
uint32_t r1;
uint16_t r2, r3;
};
volatile struct regs *myregs = (volatile void *)0xadd13000;
myregs->r1 = x<<24 | y;
myregs->r2 = z;
myregs->r3 = w;
The reason this works is that the C language requires that the number of accesses performed via volatile objects be equal to the number that would be performed on the "abstract machine" defined by the language, and that their order with respect to each other honor the order they have on the abstract machine.
Upvotes: 2