PIO jmp not_x behaviour

RP2040 based microcontroller boards running MicroPython.
Target audience: MicroPython users with an RP2040 boards.
This does not include conventional Linux-based Raspberry Pi boards.
Post Reply
Leonti
Posts: 14
Joined: Sun Mar 28, 2021 12:29 pm

PIO jmp not_x behaviour

Post by Leonti » Sun Apr 04, 2021 3:49 am

Hi!

I'm working on a simple stepper motor driver with a very basic behaviour:
1. Send pulse to A4988
2. Wait for a specified delay
3. Repeat

Here is my code:

Code: Select all

import time
import rp2
from machine import Pin

@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def stepper():
  pull(noblock) # pull new data or recycle x value
  mov(x, osr)
  mov(y, x) # copy x into y to be able to reset later

  # jmp(not_x, "end") # this always jumps to the end
  irq(0)

  set(pins, 1)
  set(pins, 0)

  label("delay")
  jmp(x_dec, "delay")
  
  mov(x, y) # x is zero now, so restore its value from y

  label("end")

def turn(sm):
  print("irq")

sm = rp2.StateMachine(0, stepper, freq=10000, set_base=Pin(25))
sm.irq(turn)

sm.put(10000)
sm.active(1)
print("moving")
time.sleep(5)
print("faster")
sm.put(5000)
time.sleep(5)
print("even faster")
sm.put(1000)
time.sleep(5)

sm.active(0)
The code runs as expected - when I put 10000 it does pulses at 1s interval, 5000 is 0.5s, 1000 is even faster.

Now I want to implement stop functionality - just send 0 with sm.put(0) and jump straight to the end if value is zero, skipping the pin triggering and waiting.
But if I put:

Code: Select all

jmp(not_x, "end")
It always skips to the end and I don't see any "irq" output in the console.

Why is that? I thought this would only jump if x is set to 0, but it always jumps to the end, even if it's not.
Am I misunderstanding what jmp(not_x, "end") is supposed to do?

Leonti
Posts: 14
Joined: Sun Mar 28, 2021 12:29 pm

Re: PIO jmp not_x behaviour

Post by Leonti » Sun Apr 04, 2021 2:43 pm

Thanks Robert!

I thought that it might have been the case briefly, but discarded the idea because I saw this is JMP conditions in RP2040 datasheet (section 3.4.2):

Code: Select all

!X: scratch X zero
and also

Code: Select all

Is an optional condition listed above (e.g. !x for scratch X zero). If a condition code is not specified,the branch is always taken
So I misunderstood it as "x is zero" condition.

Thanks for the clarification, it was super confusing for me as a beginner.

Cheers!

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: PIO jmp not_x behaviour

Post by Roberthh » Sun Apr 04, 2021 3:11 pm

Forget it. I stumbled over the same thing again. not_x means x == 0.

You have to move the label "end" up before the mov(x, y)

Code: Select all

  label("end")
  mov(x, y) # x is zero now, so restore its value from y
Edit: But I do not understand why
Edit2: The reason: there was no statement after the label("end"), and therefore the jmp had not target. It seems that in this case just no code is generated, or a jmp to an endless one-instruction loop. Both moving the label and adding a nop() after the label works
.

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: PIO jmp not_x behaviour

Post by Roberthh » Sun Apr 04, 2021 7:23 pm

Looking at the generated code, this is what happens if a label at the end of the code is the target for a jmp.
The jmp target offset will point one instruction beyond the end of the PIO program. At this address, there is either the first instruction of the next program, or default-initialized program code. As far as I recall, that default is just a jmp to itself, an endless loop.

That does not explain why in this special case the irq is not at least executed once, because the first value was pushed before the state machine was started.

P.S.: I made that error once myself in a PIO script, and it took me a while to find the bug. Obviously I did not learn too much from that. Maybe this time it sticks better.

Leonti
Posts: 14
Joined: Sun Mar 28, 2021 12:29 pm

Re: PIO jmp not_x behaviour

Post by Leonti » Mon Apr 05, 2021 12:50 am

Wow, thanks Robert!

How do you look at the generated code?

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: PIO jmp not_x behaviour

Post by Roberthh » Mon Apr 05, 2021 6:39 am

The pio program object, in you code 'stepper', is a list. The first element of that list is an array of 16 bit numbers, which is the code. So the code snipped below after instantiating the state machine will print the binary code:

Code: Select all

for i,j in enumerate(stepper[0]):
    print("%2d: %04x" % (i,j))
which results in:

Code: Select all

 0: 8080
 1: a027
 2: a041
 3: 0029
 4: c000
 5: e001
 6: e000
 7: 0047
 8: a022
At index 3 is the 'jmp(not_x, "end")' instruction. The target address is 9, which is beyond the end of the code.

Leonti
Posts: 14
Joined: Sun Mar 28, 2021 12:29 pm

Re: PIO jmp not_x behaviour

Post by Leonti » Mon Apr 05, 2021 7:33 am

That is very useful, thanks a lot!

Post Reply