0

This one's a doozy and I'm scratching my head.

Setup:

  • ARM Cortex M4 microcontroller (PAC5532).
  • Segger J-Link Plus debugger.
  • GCC 7.2.0 compiler
  • GDB 8.0.1
  • Compiled with -O0 (no optimizations)

Here's the code. It's part of the debounce logic for a GPIO input. The GPIO is read via the pac5xxx_tile_register_read function. The pin is bit 0 in dinsig1. If dinsig is 0x01, that means the GPIO is high. If dinsig is 0x00, the GPIO is low.

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;

The if ((dinsig1 & 0x01) != 0) instruction is the one causing problems. The code will correctly run until the GPIO goes from high to low, and then low to high (dinsig goes from 0x01 to 0x00 to 0x01). dinsig always reads accurately, but if ((dinsig1 & 0x01) != 0) evaluates to true.

Here's the disassembly for the if ((dinsig1 & 0x01) != 0) statement.

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.

I'm watching the ASPR register register while stepping through the disassembly. The cmp r3, #0 instruction clearly sets the Zero flag, which it should not. Because r3 is 0x01, and that is not equal to zero.

I'm at a loss here. Is this a branch predictor gone rogue? Tools broken? I know better than to blame the tools because it's virtually always my fault, but I find it hard to believe the CPU is misbehaving.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
CurtisHx
  • 746
  • 3
  • 11
  • 30
  • 1
    Very strange. Make sure the code on the microcontroller is the same as your test code. It is very unlikely that an instruction is defective outside of what the errata sheet says. – fuz Nov 25 '20 at 19:48
  • 1
    you have confirmed that r3 is non-zero? – old_timer Nov 26 '20 at 01:07
  • if you dont use a debugger and simply run the code what happens? – old_timer Nov 26 '20 at 01:08
  • 1
    Branch prediction can't affect the results of a `cmp`, that would be value-prediction for ALU instructions. And branch prediction or other speculation isn't *architecturally* visible - single-stepping should always take you to the correct branch target, never a speculative guess. – Peter Cordes Nov 26 '20 at 10:35

2 Answers2

1

Thanks for all the suggestions. I fixed the issue by updating GCC to 9.3.1 and GDB to 9.2

CurtisHx
  • 746
  • 3
  • 11
  • 30
0
.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 a zero as expected. On a cortex-m4. r3 does not contain what you think it contains. Try it without a debugger.

That or you are doing self modifying code in some way. If you think it is branch prediction (you are running code in sram, then changing that code and running some other code in the same address space). Add a BPIALL write

ldr r0,=0xE000EF78
str r0,[r0]

branch prediction does not do a branch it simply starts a prefetch a few clocks early, if the branch does not happen it does not use those fetched instructions.

You can disable branch prediction if your core even supports it (CCR register bit 18). The one I am running on right now you cannot set that bit, so appears to be not supported.

old_timer
  • 69,149
  • 8
  • 89
  • 168