【发布时间】:2014-08-26 20:19:07
【问题描述】:
我目前正在努力处理一段非常简单的代码,它表明 ARM GCC 的 1 级优化器以某种方式破坏了一个简单的公式。
它使用标准编译器设置 (O1) 在最新的 Atmel 6.2 Studio 上运行。
Atmel Toolchain\ARM GCC\Native\4.8.1426\arm-gnu-toolchain
代码非常少:
volatile uint32_t g_timing_tick_ms=0;
void SysTick_Handler(void)
{
g_timing_tick_ms++;
}
inline uint32_t get_millis()
{
return g_timing_tick_ms;
}
uint32_t get_micros()
{
return (g_timing_tick_ms * 1000 + (1000 - SysTick->VAL/84));
}
uint8_t timer_expired(timer_ *t)
{
uint32_t cur_us = get_micros();
uint32_t dt = cur_us - t->last_systick_us;
t->last_systick_us = cur_us;
if (t->elapsed <= dt)
{
// <--------- dt is regularly a huge value (around 0xfffffe00)
// this happens because t->last_systick_us sometimes is bigger than cur_us (overflow)
// however get_micros() is without such an error, cur_us ALWAYS increases and the
// variables are not modified outside this function which is called every 500us.
t->elapsed = t->interval;
return 1;
}
t->elapsed -= dt;
return 0;
};
get_millis 从每毫秒调用一次的 Systick 计时器返回毫秒数。
systick 计时器是 24 位的,以 84mhz 的速率倒计时。get_micros() 使用这个 systick 值并计算自上次重置以来经过的微秒,然后加上毫秒*1000。
这很好用,我找不到更快的方法来获取当前微秒作为时间戳。
第三个函数显示了一个零星的问题,有时存储在 t->last_systick_us 中的值(直接来自 get_micros() )大于应有的值。
确切地说,最后三位十进制值始终为 986 (20065986,1000986)。
该值太高了大约 1000us,总是在十进制数的末尾加上 986。
每次调用都会发生这种情况。
解决方案:
1) 改变:
uint32_t dt = cur_us - t->last_systick_us; --->
volatile uint32_t dt = cur_us - t->last_systick_us;
将此变量更改为 volatile 解决了这个问题,这导致编译器以一种糟糕的方式处理它的想法。 该变量不是静态的,它是本地变量,没有什么是从外部修改它,易失性是一种浪费,但可以解决数学问题。 2) 改变
uint32_t get_micros() -----> 内联 uint32_t get_micros() 这也解决了这个问题,但这也不是一个好的解决方法,因为编译器不必将其内联。所以这可能会在未来的某个时候适得其反。
3) 在值更改之前将任何调试写入或类似内容添加到计时器函数中也会修复它,具体取决于代码。
这似乎是 gcc-ARM 核心编译器中的一个错误,优化器以某种方式破坏了数学。
我可以提供 asm,我不知道 ARM ASM,但我注意到它在接近 get_micro() 公式的部分删除了一个“子”。
我不认为我在这里有代码错误,它太简单了(而且效果很好)。
此外,解决方案表明这不是编码错误,在函数中添加或删除内联应该不会有任何区别,除了优化。
也许有人知道该怎么做,经历过/解决过类似的行为。 我正处于完全删除优化器的边缘,但这可能会花费很多性能。
更新
当我意识到可能的原因时,我正要准备 asm 差异(并通读它),我认为情况就是如此。
我认为这是一个竞争条件,Systick 的中断尚未触发但 systick 计时器溢出。
结果是大约 1000us 的错误(随着计时器每 84ns 滴答一次而小一点。
这将导致我的错误,不可预测,并且通过更改代码,周期会通过更改周期而更改,它可能会以导致竞争条件稍后出现的方式对齐代码。
我进行了调试,可以验证问题在 Systick 重新加载后不久发生。
很抱歉在编译器错误中做出过快的猜测。
【问题讨论】:
-
你为什么不发布你的函数的反汇编?这将使包括我自己在内的许多人的事情变得容易得多。
-
@Jake'Alquimista'LEE:感谢您的回复。我想我必须后退两步:/我更新了我的问题,问题似乎在我的尽头。
-
我试图通过详细说明问题来帮助人们,但我收到了负票。我想最好的办法是删除问题和相关的解决方案?
-
我知道这个问题比较老,但我对 SAM3N systick 也有过类似的令人沮丧的经历。这个问题和你的回答很有用。请不要删除。
标签: c gcc arm compiler-optimization