Cortex M4:cmp 指令损坏?
Cortex M4: cmp instruction broken?
这个太棒了,我挠头了。
设置:
- ARM Cortex M4 微控制器 (PAC5532)。
- Segger J-Link加调试器。
- GCC 7.2.0 编译器
- GDB 8.0.1
- 使用 -O0 编译(无优化)
这是代码。它是 GPIO 输入的去抖动逻辑的一部分。通过 pac5xxx_tile_register_read
函数读取 GPIO。该引脚是 dinsig1
中的位 0。如果 dinsig
是 0x01
,则表示 GPIO 为高电平。如果dinsig
为0x00
,则GPIO为低
static uint32_t triggerDebounce = 0;
volatile uint8_t dinsig1 = pac5xxx_tile_register_read(ADDR_DINSIG1);
if ((dinsig1 & 0x01) != 0) //This is the problem line.
triggerDebounce = (triggerDebounce << 1);
else
triggerDebounce = (triggerDebounce << 1) | 1;
if ((dinsig1 & 0x01) != 0)
指令是引起问题的指令。代码将正确 运行 直到 GPIO 从高电平变为低电平,然后从低电平变为高电平(dinsig
从 0x01
变为 0x00
再到 0x01
)。 dinsig
始终准确读取,但 if ((dinsig1 & 0x01) != 0)
计算结果为真。
这是 if ((dinsig1 & 0x01) != 0)
语句的反汇编。
0x00004268 ldrb r3, [r7, #7] ;Loads dinsig into r3.
0x0000426a uxtb r3, r3 ;Expands dinsig 3 into a 32 bit word.
0x0000426c and.w r3, r3, #1 ;ANDs dinsig 3 with 0x01 and stores result in r3
0x00004270 cmp r3, #0 ;Compares r3 with 0
0x00004272 beq.n 0x4280 <IsTriggerPressed+40> ; Jumps to address 0x4280 if the ZERO flag is set.
我正在查看 ASPR register 寄存器,同时逐步执行反汇编。 cmp r3, #0
指令明确设置了零标志,这是不应该的。因为r3是0x01
,不等于0。
我在这里不知所措。这是一个流氓分支预测器吗?工具坏了?我知道最好不要责怪这些工具,因为这实际上总是我的错,但我很难相信 CPU 行为不端。
.thumb_func
.globl TEST0
TEST0:
mov r0,#1
mov r3,#1
uxtb r3,r3
and.w r3,r3,#1
cmp r3,#0
beq.n skip
mov r0,#0
skip:
bx lr
returns 预期为零。在 cortex-m4 上。 r3 不包含您认为它包含的内容。在没有调试器的情况下尝试。
或者您正在以某种方式自我修改代码。如果您认为它是分支预测(您是 sram 中的 运行 代码,则更改该代码和 运行 同一地址 space 中的一些其他代码)。添加一个 BPIALL 写
ldr r0,=0xE000EF78
str r0,[r0]
分支预测不做分支它只是提前几个时钟开始预取,如果分支没有发生它不使用那些获取的指令。
如果您的内核支持分支预测(CCR 寄存器第 18 位),您可以禁用它。我现在 运行 上的那个你现在不能设置那个位,所以似乎不受支持。
感谢所有建议。我通过将 GCC 更新到 9.3.1 并将 GDB 更新到 9.2
解决了这个问题
这个太棒了,我挠头了。
设置:
- ARM Cortex M4 微控制器 (PAC5532)。
- Segger J-Link加调试器。
- GCC 7.2.0 编译器
- GDB 8.0.1
- 使用 -O0 编译(无优化)
这是代码。它是 GPIO 输入的去抖动逻辑的一部分。通过 pac5xxx_tile_register_read
函数读取 GPIO。该引脚是 dinsig1
中的位 0。如果 dinsig
是 0x01
,则表示 GPIO 为高电平。如果dinsig
为0x00
,则GPIO为低
static uint32_t triggerDebounce = 0;
volatile uint8_t dinsig1 = pac5xxx_tile_register_read(ADDR_DINSIG1);
if ((dinsig1 & 0x01) != 0) //This is the problem line.
triggerDebounce = (triggerDebounce << 1);
else
triggerDebounce = (triggerDebounce << 1) | 1;
if ((dinsig1 & 0x01) != 0)
指令是引起问题的指令。代码将正确 运行 直到 GPIO 从高电平变为低电平,然后从低电平变为高电平(dinsig
从 0x01
变为 0x00
再到 0x01
)。 dinsig
始终准确读取,但 if ((dinsig1 & 0x01) != 0)
计算结果为真。
这是 if ((dinsig1 & 0x01) != 0)
语句的反汇编。
0x00004268 ldrb r3, [r7, #7] ;Loads dinsig into r3.
0x0000426a uxtb r3, r3 ;Expands dinsig 3 into a 32 bit word.
0x0000426c and.w r3, r3, #1 ;ANDs dinsig 3 with 0x01 and stores result in r3
0x00004270 cmp r3, #0 ;Compares r3 with 0
0x00004272 beq.n 0x4280 <IsTriggerPressed+40> ; Jumps to address 0x4280 if the ZERO flag is set.
我正在查看 ASPR register 寄存器,同时逐步执行反汇编。 cmp r3, #0
指令明确设置了零标志,这是不应该的。因为r3是0x01
,不等于0。
我在这里不知所措。这是一个流氓分支预测器吗?工具坏了?我知道最好不要责怪这些工具,因为这实际上总是我的错,但我很难相信 CPU 行为不端。
.thumb_func
.globl TEST0
TEST0:
mov r0,#1
mov r3,#1
uxtb r3,r3
and.w r3,r3,#1
cmp r3,#0
beq.n skip
mov r0,#0
skip:
bx lr
returns 预期为零。在 cortex-m4 上。 r3 不包含您认为它包含的内容。在没有调试器的情况下尝试。
或者您正在以某种方式自我修改代码。如果您认为它是分支预测(您是 sram 中的 运行 代码,则更改该代码和 运行 同一地址 space 中的一些其他代码)。添加一个 BPIALL 写
ldr r0,=0xE000EF78
str r0,[r0]
分支预测不做分支它只是提前几个时钟开始预取,如果分支没有发生它不使用那些获取的指令。
如果您的内核支持分支预测(CCR 寄存器第 18 位),您可以禁用它。我现在 运行 上的那个你现在不能设置那个位,所以似乎不受支持。
感谢所有建议。我通过将 GCC 更新到 9.3.1 并将 GDB 更新到 9.2
解决了这个问题