gcc 是否优化了我的等待代码,尽管将其标记为易失性?

问题描述 投票:0回答:1

在 ST32 ARM Cortex M4 上,我有一个简单的裸机闪烁灯,可以使 LED 闪烁。当我使用旋转等待时效果很好:

typedef volatile uint32_t vuint32_t;
#define SET(addr, bits) (*((vuint32_t*) (addr)) |= (bits))
#define CLR(addr, bits) (*((vuint32_t*) (addr)) &= ~(bits))

for (;;) {
  SET(GPIOA+_ODR, BIT(5));
  // spin(100);
  for (volatile int i = 100 * 1000; i; --i) asm("nop");
  CLR(GPIOA+_ODR, BIT(5));
  //spin(1000);
  for (volatile int i = 1000 * 1000; i; --i) asm("nop");
}

上面的代码工作得很好——它周期性地打开 LED。而且,如果我取消注释 spin() 调用中的

one
,并注释掉相应的
for
调用,它也能正常工作。但如果我两者都使用
spin()
,则灯会反转:主要是,闪烁

使用

gdb
,看起来它正在切换调用的顺序(或者以与预期不同的方式执行它们)。不过,我不明白为什么它会这样做,因为我将所有内容都标记为易失性:

void spin(uint32_t ms) {
  uint32_ticks = (CPU_HZ / 1000) * ms;
  (*(vuint32_t*) (STK+_LOAD)) = ticks - 1;
  SET((STK+_CTRL), (BIT(2) | BIT(0)));
  while (! ((*(vuint32_t*) STK+_CTRL) & COUNTFLAG)) asm volatile("nop");
}

GPIOA
STK
_LOAD
这样的宏是因为我正在做这个裸机,没有外部库。但我已经对它们进行了测试,它们在隔离状态下都能正常工作。原因似乎是编译器正在更改顺序(或省略?)。我至少在某些时候能够使用
objdump
确认这一点。

我正在使用

arm-none-eabi-gcc 10.3.1
。另请参阅:gcc 11.1 中 volatile 的不合格优化


更新:

仅对

spin
进行一次调用,
spin(100)
按预期运行,
spin(1000)
按预期运行,但
spin(5000)
似乎只旋转 1 秒
(据我所知)。

我正在使用

-Os
。切换到
-O0
并不能解决问题。

c gcc embedded inline-assembly volatile
1个回答
0
投票

不要责怪编译器!

spin
功能错误。您需要在 while 循环之前重置 COUNTFLAG。

void spin(uint32_t ms) 
{
  uint32_ticks = (CPU_HZ / 1000) * ms;
  (*(vuint32_t*) (STK+_LOAD)) = ticks + 1;
  SET((STK+_CTRL), (BIT(2) | BIT(0)));
  CLR((STK+_CTRL), BIT(16));
  while (! ((*(vuint32_t*) STK+_CTRL) & COUNTFLAG));
}
© www.soinside.com 2019 - 2024. All rights reserved.