微处理器经常需要被读取寄存器来清除某些状态条件。 有没有在C移植的方式,以确保在不使用的数据进行读取时不优化掉? 是不是足够的指针到内存映射寄存器被声明为volatile吗? 换句话说,将始终下面就符合标准的编译器的工作?
void func(void)
{
volatile unsigned int *REGISTER = (volatile unsigned int *) 0x12345678;
*REGISTER;
}
据我所知,对付这样的功能,运行到编译器相关的问题。 所以,我的便携式的定义是有点在这种情况下松动。 我只是说,这将与最流行的工具链,尽可能广泛地工作。
人们争论颇为吃力究竟什么volatile
手段。 我想大多数人都认为你展示构建打算做你想做的,但在标准C语言实际上保证了它作为C99的没有一致的。 (这种情况可能在C2011已经有所好转,我没有看过那呢。)
非标准,但还算嵌入式编译器的广泛支持,替代方案,可能是更容易的工作
void func(void)
{
asm volatile ("" : : "r" (*(unsigned int *)0x12345678));
}
(在“挥发”这里appies到“ASM”,并表示“即使它没有输出操作数,这可能不会被删除。这是没有必要把它的指针也。)
这个结构的主要剩余的缺点是,你还有没有保证,编译器将生成读入一个指令存储器。 随着C2011,使用_Atomic unsigned int
可能就足够了,但在没有该功能,你几乎必须写一个真实(非空的)组件插入自己,如果你需要这样的保证。
编辑:另一种皱纹今天早上发生在我身上。 如果从存储位置读取具有相应位置内存更改值的副作用,你需要
void func(void)
{
unsigned int *ptr = (unsigned int *)0x12345678;
asm volatile ("" : "=m" (*ptr) : "r" (*ptr));
}
以防止错优化等从该位置读取。 (100%清楚,这种变化不会改变用于生成汇编语言func
本身,但可能会影响周围的代码,尤其是当优化func
内联)。
是的,C标准保证代码访问的易失性变量将不会被优化。
C11 5.1.2.3/2
“访问一个volatile对象,” ...“都是副作用”
C11 5.1.2.3/4
“实际上,在实现无法计算表达式的一部分,如果它可以推断其值未被使用且没有需要的副作用产生(包括任何引起调用一个函数或访问的易失性对象)”。
C11 5.1.2.3/6
“对符合标准的实现最低要求是:
- 访问根据所述抽象机的规则评价严格挥发性物“。
IIRC,C标准是在使用中的定义有点松,所以*REGISTER
不一定解释为做一读 。
但下面应该做的:
int x = *REGISTER;
即,存储器参考的结果在某处被使用。 该x
不需要挥发,但是。
更新 :为了避免_unused变量你可以用无操作功能做的警告。 静态和/或内联函数应该被优化掉,而不增加运行时间:
static /*inline*/ void no_op(int x)
{ }
no_op(*REGISTER);
更新2:我刚刚想出了一个更好的功能:
static unsigned int read(volatile unsigned int *addr)
{
return *addr;
}
read(REGISTER);
现在,这个功能既可以用于读取和使用以及读取和放弃使用。 8-)
编译器通常不优化组合内联(这是很难正确地分析它们)。 此外,这似乎是一个妥善的解决办法:你要在寄存器更明确的控制,它是自然的组装。
既然你正在编写一个微控制器,我认为有一些组件已经在你的代码,所以有点内嵌汇编的将不会是一个问题。
也许GNU C特定扩展名不被认为是非常便携,但这里是另一种选择。
#define read1(x) \
({ \
__typeof(x) * _addr = (volatile __typeof(x) *) &(x); \
*_addr; \
})
这将转化为以下汇编行(用gcc编译x86和优化,-02): movl SOME_REGISTER(%rip), %eax?
我从同一汇编:
inline read2(volatile uint32_t *addr)
{
return *addr;
}`
...所建议的另一个答案,但read1()
将处理不同的寄存器大小。 虽然我不知道,如果使用read2()
与8位或16位寄存器将永远是一个问题,至少有上参数类型没有任何警告。