给出C中的表达式:
foo = (<an expression>);
编译器通常会设法多次分配foo
值。当foo
是控制硬件的寄存器时,可能会导致意外结果。例如,我发现表达式喜欢
foo = (struct Bar){.field1=13, .field2=42};
foo = Field1Value(13) | Field2Value(42);
将生成更新foo的不同序列。第二个通常非常适合完成rvalue然后分配它。但第一个经常喜欢用多个作业更新foo。我尝试了括号放置,但优化器似乎不这么认为。
更新
我想了解的是为什么这3个陈述有不同的结果:
// 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;
第一个是有问题的。第二个2工作。我想了解原因。显然,执行此类操作的替代方法是使用以下代码:
GCLK->CLKCTRL.reg = (GCLK_CLKCTRL_ID_TCC0_TCC1 | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_CLKEN);
GCLK全局是指向此结构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
其中__IO
是volatile
的宏。
答案 0 :(得分:2)
如何强制C编译器在赋值之前完成计算(?)
确保foo
属于volatile
类型应该足够了。 @ Eugene Sh.
volatile type_of_foo foo = Field1Value(13) | Field2Value(42);
替代方案将使用2个步骤:
将结果分配给可以多次设置的安全volatile
对象,然后分配给寄存器。
volatile type_of_foo foo_tmp = Field1Value(13) | Field2Value(42);
volatile type_of_foo foo = foo_tmp;
编译器可以在汇编代码级别多次foo_tmp
分配foo
,但在foo_tmp
分配完成之前,不能使用它来分配foo = foo_tmp;
。
迂腐地说,_Atomic
本身在理论上可能涉及多个任务,但不太可能。考虑"1245114.00"
,选项C功能可获得更多创意。
答案 1 :(得分:2)
要访问内存映射寄存器,请始终使用指针到volatile
类型,并始终确保指向类型是可在硬件体系结构上作为单个单元访问的基本类型。特别是不使用整个struct-type对象的赋值而不使用bitfields,因为C语言没有严格指定它们与volatile
的交互方式。但是,您可以使用一个包含volatile的限定结构,其中各个成员的类型对于作为一个单元进行访问是有意义的。例如:
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;
这样做的原因是C语言要求通过易失性对象执行的访问次数等于将在语言定义的“抽象机器”上执行的次数,以及它们相对于每个语言的顺序其他尊重他们在抽象机器上的顺序。