为什么 NOP/few 额外的 code/optimization 行指针别名有帮助? [富士通MB90F543单片机C代码]
Why NOP/few extra lines of code/optimization of pointer aliasing helps? [Fujitsu MB90F543 MCU C code]
我正在尝试修复在 Fujitsu MB90F543 成熟程序中发现的错误。该程序运行至今将近10年,但人们发现,在某些特殊情况下,它一开始就没有做两件事。其中一项至关重要。
在低电平和高电平初始化(端口、引脚、外围设备、IRQ 处理程序)之后,配置数据通过 SPI 从 EEPROM 读取,状态 LED 亮起片刻(为了打开它们,数据通过 SPI 发送到LED驱动器)。
当那些特殊情况首先发生并且只有第一个函数调用时,只有几个 EEPROM 读取失败,另外一些应该不打开的 LED。
该程序是用 C 编写的,使用 Softune v30L32 编译的。
令人惊讶的是,在低级硬件初始化中添加单个 __asm(" NOP ") 足以使程序在上述情况下按预期工作。在优化设置中关闭 'Control optimization of pointer aliasing' 就足够了。在不同的地方添加几行代码也有帮助。
我比较了(DIFFed)编译程序的 ASM 列表,其中包含和不包含 __asm(" NOP ") 以及上述两种优化器设置,它们看起来都很好。
Softune编译器在编译过程中多年来一直打印的唯一警告如下:
*** W1372L: The section is placed outside the RAM area or the I/O area (IOXTND)
我确实意识到这是一个相当笼统的问题,但也许有更全面了解的人能够指出可能的原因。
你知道是什么导致了这种奇怪的行为吗?如何定位bug并修复?
在初始化过程中使用了几个长(约 20ms)的延迟循环。尽管它们从大约 2ms 开始增加,但它们没有帮助,但是在硬件初始化函数的任何行中甚至在函数之前或之后的单个 NOP 都有帮助。
两个等待循环都有效。我用示波器检查过它。 (我在之前和之后添加了 LED 打开)。
我通过将 SPI 时钟从 1MHz 减慢到 500kHz 来检查时序假设。它不会改变任何东西。减慢到 250kHz 会使看门狗重置,因为代码的某些部分执行时间过长 (>25ms)。
还有一件事。我观察到在任何源文件中添加局部变量有时会使问题消失或重新出现。同样涉及初始化未初始化的局部变量。在任何文件中添加几行额外的代码有助于或揭示问题。
void main(void)
{
watchdog_init();
// waiting for power supply to stabilize
wait; // about 45ms
hardware_init();
clear_watchdog();
application_init();
clear_watchdog();
wait; // about 20ms
test_LED();
{...}
}
void hardware_init (void)
{
__asm("NOP"); // how it comes it helps? - it may be in any line of the function
io_init(); // ports initialization
clk_init();
timer_init();
adc_init();
spi_init();
LED_init();
spi_start();
key_driver_init();
can_init();
irq_init(); // set IRQ priorities and global IRQ enable
}
可能是很多事情中的一件,但有两个 spring 需要注意。
时机。
也许等待的时间不足以让电源稳定下来,而且并非所有内容都与时钟同步。 NOP 让一切恢复同步。
对齐。
也许 NOP 使您的指令在硬件期望的 32 或 64 位边界上对齐。 (我们过去常常在大型机汇编器上这样做,因为 IO 操作通常期望事情在双字边界上)。
问题已解决。这是由一个小错误引起的。
EEPROM 的nHOLD 和nCS 信号不是在MCU 复位后立即初始化的,而是在第一次使用EEPROM 之前初始化的。结果他们都是0,所以很活跃。
这意味着选择了 EEPROM,但正在等待。与此同时,其他使用 SPI 的传输开始了。在 8 个 CLK 脉冲中的 6 个之后,EEPROM 的 nHOLD I/O 引脚被初始化并变为高电平。 EEPROM 不再处于暂停状态,因此它为另一个外设输入数据的最后两位。后续对EEPROM的每一次操作都发现CLK和MOSI没有同步
当我添加 NOP 或其他任何东西时,nHOLD 0->1 边沿的时刻被转移到最后一个 CLK 脉冲之后发生。现在 CLK-MOSI 同步了。
我所要做的就是初始化所有 EEPROM 的 SPI 线,在
特定的 nHOLD 和 nCS 在 MCU 复位之后。
我正在尝试修复在 Fujitsu MB90F543 成熟程序中发现的错误。该程序运行至今将近10年,但人们发现,在某些特殊情况下,它一开始就没有做两件事。其中一项至关重要。
在低电平和高电平初始化(端口、引脚、外围设备、IRQ 处理程序)之后,配置数据通过 SPI 从 EEPROM 读取,状态 LED 亮起片刻(为了打开它们,数据通过 SPI 发送到LED驱动器)。 当那些特殊情况首先发生并且只有第一个函数调用时,只有几个 EEPROM 读取失败,另外一些应该不打开的 LED。
该程序是用 C 编写的,使用 Softune v30L32 编译的。 令人惊讶的是,在低级硬件初始化中添加单个 __asm(" NOP ") 足以使程序在上述情况下按预期工作。在优化设置中关闭 'Control optimization of pointer aliasing' 就足够了。在不同的地方添加几行代码也有帮助。
我比较了(DIFFed)编译程序的 ASM 列表,其中包含和不包含 __asm(" NOP ") 以及上述两种优化器设置,它们看起来都很好。
Softune编译器在编译过程中多年来一直打印的唯一警告如下:
*** W1372L: The section is placed outside the RAM area or the I/O area (IOXTND)
我确实意识到这是一个相当笼统的问题,但也许有更全面了解的人能够指出可能的原因。
你知道是什么导致了这种奇怪的行为吗?如何定位bug并修复?
在初始化过程中使用了几个长(约 20ms)的延迟循环。尽管它们从大约 2ms 开始增加,但它们没有帮助,但是在硬件初始化函数的任何行中甚至在函数之前或之后的单个 NOP 都有帮助。
两个等待循环都有效。我用示波器检查过它。 (我在之前和之后添加了 LED 打开)。
我通过将 SPI 时钟从 1MHz 减慢到 500kHz 来检查时序假设。它不会改变任何东西。减慢到 250kHz 会使看门狗重置,因为代码的某些部分执行时间过长 (>25ms)。
还有一件事。我观察到在任何源文件中添加局部变量有时会使问题消失或重新出现。同样涉及初始化未初始化的局部变量。在任何文件中添加几行额外的代码有助于或揭示问题。
void main(void)
{
watchdog_init();
// waiting for power supply to stabilize
wait; // about 45ms
hardware_init();
clear_watchdog();
application_init();
clear_watchdog();
wait; // about 20ms
test_LED();
{...}
}
void hardware_init (void)
{
__asm("NOP"); // how it comes it helps? - it may be in any line of the function
io_init(); // ports initialization
clk_init();
timer_init();
adc_init();
spi_init();
LED_init();
spi_start();
key_driver_init();
can_init();
irq_init(); // set IRQ priorities and global IRQ enable
}
可能是很多事情中的一件,但有两个 spring 需要注意。
时机。
也许等待的时间不足以让电源稳定下来,而且并非所有内容都与时钟同步。 NOP 让一切恢复同步。
对齐。
也许 NOP 使您的指令在硬件期望的 32 或 64 位边界上对齐。 (我们过去常常在大型机汇编器上这样做,因为 IO 操作通常期望事情在双字边界上)。
问题已解决。这是由一个小错误引起的。
EEPROM 的nHOLD 和nCS 信号不是在MCU 复位后立即初始化的,而是在第一次使用EEPROM 之前初始化的。结果他们都是0,所以很活跃。 这意味着选择了 EEPROM,但正在等待。与此同时,其他使用 SPI 的传输开始了。在 8 个 CLK 脉冲中的 6 个之后,EEPROM 的 nHOLD I/O 引脚被初始化并变为高电平。 EEPROM 不再处于暂停状态,因此它为另一个外设输入数据的最后两位。后续对EEPROM的每一次操作都发现CLK和MOSI没有同步
当我添加 NOP 或其他任何东西时,nHOLD 0->1 边沿的时刻被转移到最后一个 CLK 脉冲之后发生。现在 CLK-MOSI 同步了。
我所要做的就是初始化所有 EEPROM 的 SPI 线,在 特定的 nHOLD 和 nCS 在 MCU 复位之后。