Page 1 of 1

@rp2.asm_pio()

Posted: Wed Aug 24, 2022 12:54 pm
by superace
Hi,
Either I am doing something illegal or I just do not get it.
MicroPython v1.19.1 on 2022-06-18; Raspberry Pi Pico with RP2040
Thonny

How come this code prints in REPL
>>>
inside
inside
>>>

Code: Select all

from machine import Pin
import rp2

@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def blink_1hz():
    print ("inside")
    # Cycles: 1 + 7 + 32 * (30 + 1) = 1000
    set(pins, 1)
    set(x, 31)                  [6]
    label("delay_high")
    nop()                       [29]
    jmp(x_dec, "delay_high")

    # Cycles: 1 + 7 + 32 * (30 + 1) = 1000
    set(pins, 0)
    set(x, 31)                  [6]
    label("delay_low")
    nop()                       [29]
    jmp(x_dec, "delay_low")


# Create and start a StateMachine with blink_1hz, outputting on Pin(25)

#sm = rp2.StateMachine(0, blink_1hz, freq=2000, set_base=Pin(25))
#sm.active(1)
As you see the last 2 rows are commented out so I do not get why the blink_1hz function is run... So I would expect no printed output at all, and why do I get 2 rows?

Re: @rp2.asm_pio()

Posted: Wed Aug 24, 2022 1:32 pm
by TheSilverBullet

Code: Select all

import rp2
@rp2.asm_pio()
def whatever():
    print('Cough … decorator.')
    print('Cough … closure.')

Re: @rp2.asm_pio()

Posted: Wed Aug 24, 2022 1:56 pm
by jimmo
superace wrote:
Wed Aug 24, 2022 12:54 pm
How come this code prints in REPL
There's quite a lot of magic in how the rp2.asm_pio decorator works. The summary is that it executes your function, providing it with a scope that includes functions like "set", "label", etc. And when your code calls those functions it emits the corresponding machine code (which can then be loaded into a given state machine).

(In other words -- the PIO doesn't execute your Python function. Rather the asm_pio decorator executes your function immediately as teh decorator is applied, and that's why the print statements run straight away).

See https://github.com/micropython/micropyt ... p2.py#L235 for the implementation.
TheSilverBullet wrote:
Wed Aug 24, 2022 1:32 pm
print('Cough … decorator.')
print('Cough … closure.')
TheSilverBullet: I think you're alluding to the same thing... can't quite tell though.

Re: @rp2.asm_pio()

Posted: Wed Aug 24, 2022 1:59 pm
by TheSilverBullet
jimmo wrote:
Wed Aug 24, 2022 1:56 pm
TheSilverBullet: I think you're alluding to the same thing... can't quite tell though.
Yeah, I have to admit, it was a bit cryptic.
Food for thought, helps the discovery and learning process …

Re: @rp2.asm_pio()

Posted: Tue Aug 30, 2022 5:44 pm
by hippy
jimmo wrote:
Wed Aug 24, 2022 1:56 pm
(In other words -- the PIO doesn't execute your Python function. Rather the asm_pio decorator executes your function immediately as teh decorator is applied, and that's why the print statements run straight away).
And, because it's a two pass assembler, executes it twice, causing the 'print' within it to be executed twice.

This is a simplified CPython program equivalent of the RP2 assembler which can be run at a command line to illustrate what's going on ...

Code: Select all

emit = []

def nop   ()     : emit.append("nop")
def label (name) : emit.append(name + ":")
def jmp   (name) : emit.append("jmp " + name)

def asm_pio(**kw):
  def inner(func):
    global emit
    emit = []; func()
    emit = []; func()
    return emit
  return inner

@asm_pio()
def MyAsm():
  print("--Inside MyAsm--")
  label("loop")
  nop()
  jmp("loop")

# print("Executable PIO code is : {}".format(MyAsm))
Note that the two 'func()' calls within 'inner' are what executes your 'MyAsm' routine twice, causing the "--Inside MyAsm--" to be printed twice, even with the 'print' commented out.

Re: @rp2.asm_pio()

Posted: Tue Aug 30, 2022 11:37 pm
by jimmo
hippy wrote:
Tue Aug 30, 2022 5:44 pm
And, because it's a two pass assembler, executes it twice, causing the 'print' within it to be executed twice.
Yes good point!

Good example with the demo version... when we get around to writing more docs about it, then that would probably be a worthwhile thing to add!

Re: @rp2.asm_pio()

Posted: Wed Aug 31, 2022 12:52 pm
by hippy
My view is that RP2.py needs to be nuked from orbit and completely rewritten to allow specification of free-form PIO assembler, viz -

Code: Select all

@rp2.asm_pio()
def MyAsm():
  """
  start: nop
         jmp  start
  """
I'd be even more happier with having StateMachine do the assembly -

Code: Select all

MyAsm = """
  start: nop
         jmp  start
  """

sm = StateMachine(0, MyAsm)
I appreciate backwards compatibility is an issue but it is possible to support all of the above and retain backwards compatibility.

Even if we simply stick with what we have I believe it still needs nuking, or at least a very significant rewrite, to allow error checking to prevent things like 'set(x, 255)' and 'mov(x, 255)' mistakes and many others.

Re: @rp2.asm_pio()

Posted: Thu Sep 01, 2022 2:29 am
by jimmo
hippy wrote:
Wed Aug 31, 2022 12:52 pm
I appreciate backwards compatibility is an issue but it is possible to support all of the above and retain backwards compatibility.
I think partly this was to make the PIO look similar tot he existing @asm_thumb feature in MicroPython. At the end of the day, much of this is about using the existing lexer+parser that we have for Python.
hippy wrote:
Wed Aug 31, 2022 12:52 pm
or at least a very significant rewrite, to allow error checking to prevent things like 'set(x, 255)' and 'mov(x, 255)' mistakes and many others.
Isn't this mostly just a matter of adding more argument checking to the set() and mov() functions? Why does that require a significant rewrite? (I didn't write rp2.py and haven't had much experience with it, so this may be a silly question).

Re: @rp2.asm_pio()

Posted: Thu Sep 01, 2022 10:35 am
by hippy
jimmo wrote:
Thu Sep 01, 2022 2:29 am
hippy wrote:
Wed Aug 31, 2022 12:52 pm
or at least a very significant rewrite, to allow error checking to prevent things like 'set(x, 255)' and 'mov(x, 255)' mistakes and many others.
Isn't this mostly just a matter of adding more argument checking to the set() and mov() functions? Why does that require a significant rewrite? (I didn't write rp2.py and haven't had much experience with it, so this may be a silly question).
Better argument checking is what's needed, but that requires being able to determine that named constants were used where they should have been used and not where they shouldn't be used. That's hard because, by the time the arguments pass to the code generating functions, they have all been converted to integers so those routines can't tell if they were named or not.

For example 'set(x, null)' and 'mov(x, 3)' are both accepted but neither do what the person writing those presumably expected.

My view is that error checking should catch all mistakes to protect the coder from themselves. To achieve that myself I defined named constants as a class type rather then integer so I could check they were names of registers and then check if valid for the specific command. That wasn't overly complicated but did require quite a number of changes.

Other reasons for rewriting would be to make it single pass with fixups, and to support things like 'dec(x)' and 'inc(x)' which currently require 'jmp' to a following label to be specified. What the SDK assembler accepts has been tightened up but the MicroPython RP2 hasn't kept up so now accepts things the SDK won't.

I got most of this working as proof of concept but it needs completing and a rewrite to make it production quality, and also needs extensive testing to prove correctness and backwards compatibility. I haven't found the time to complete that.