Page 1 of 1

PIO jmp not_x behaviour

Posted: Sun Apr 04, 2021 3:49 am
by Leonti
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?

Re: PIO jmp not_x behaviour

Posted: Sun Apr 04, 2021 2:43 pm
by Leonti
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!

Re: PIO jmp not_x behaviour

Posted: Sun Apr 04, 2021 3:11 pm
by Roberthh
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
.

Re: PIO jmp not_x behaviour

Posted: Sun Apr 04, 2021 7:23 pm
by Roberthh
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.

Re: PIO jmp not_x behaviour

Posted: Mon Apr 05, 2021 12:50 am
by Leonti
Wow, thanks Robert!

How do you look at the generated code?

Re: PIO jmp not_x behaviour

Posted: Mon Apr 05, 2021 6:39 am
by Roberthh
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.

Re: PIO jmp not_x behaviour

Posted: Mon Apr 05, 2021 7:33 am
by Leonti
That is very useful, thanks a lot!