RP2040 PIO Pin change to HIGH do not works

RP2040 based microcontroller boards running MicroPython.
Target audience: MicroPython users with an RP2040 boards.
This does not include conventional Linux-based Raspberry Pi boards.
User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: RP2040 PIO Pin change to HIGH do not works

Post by pythoncoder » Thu Mar 31, 2022 8:13 am

In applications where I care about GC latency I explicitly call .collect() at points in the code where the latency is tolerable. Calling it regularly reduces the time it takes: the less garbage has accumulated, the quicker it runs. However as you've discovered 1-2ms is as good as it gets. This confirms my measurements on platforms I've tested.
Peter Hinch
Index to my micropython libraries.

beyonlo
Posts: 58
Joined: Thu Nov 26, 2015 8:03 pm

Re: RP2040 PIO Pin change to HIGH do not works

Post by beyonlo » Thu Mar 31, 2022 12:48 pm

pythoncoder wrote:
Thu Mar 31, 2022 8:13 am
In applications where I care about GC latency I explicitly call .collect() at points in the code where the latency is tolerable. Calling it regularly reduces the time it takes: the less garbage has accumulated, the quicker it runs. However as you've discovered 1-2ms is as good as it gets. This confirms my measurements on platforms I've tested.
Great @pythoncoder

I'm doing exactly that in my app: "I explicitly call .collect() at points in the code where the latency is tolerable" and I calling regularly (in each loop on main loop), but I would like ~500us faster, something like 1.5ms. In this doc https://docs.micropython.org/en/latest/ ... collection say "typically on the order of 1ms if done frequently" and would be happy if that can be done :), but if is possible to improve to 1.5ms will be great, so I can have a better safety margin :). Is possible to change something to have the gc.collect() to be a bit faster? I don't know if you noticed, but in that simple example gc.py I configured the RP2040 frequency clock to the max that MicroPython accept (273MHz), and that modification made the gc.collect() very faster, otherwise, with default 125Mhz the gc.collect() is ~4ms. So maybe do you know if there are something else to turn that faster, like as change something in the GC module, or maybe possible more than 273Mz, and so on, I do not know - just thinking.

And I would like just to be sure: in a PWM running, when gc.collect() start, it will not affect/delay the PWM right? In my knowledge, will not affect, because the PWM is done in hardware, and it do not need to execute any python code after started. So, I can use a PWM instead a Timer to turn on/off a PIN OUT in deterministic time.

Thank you in advance!

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: RP2040 PIO Pin change to HIGH do not works

Post by pythoncoder » Fri Apr 01, 2022 9:05 am

I don't know a way to improve on what you are doing.

My understanding is:
  1. PWM is unaffected by GC.
  2. Timer callbacks on the RP2 are hard interrupts and should also be unaffected.
Peter Hinch
Index to my micropython libraries.

beyonlo
Posts: 58
Joined: Thu Nov 26, 2015 8:03 pm

Re: RP2040 PIO Pin change to HIGH do not works

Post by beyonlo » Fri Apr 01, 2022 12:47 pm

pythoncoder wrote:
Fri Apr 01, 2022 9:05 am
I don't know a way to improve on what you are doing.

My understanding is:
  1. PWM is unaffected by GC.
  2. Timer callbacks on the RP2 are hard interrupts and should also be unaffected.
Hello @pythoncoder

Just to understand better about the item 2: if the GC is running when Timer IRQ happen, will the GC to stop to answer (without delay - as if the GC is NOT running) the Timer and run a Python code callback for that Timer? And after that, will the GC to continue to run? - from the point it stopped? The Timer is hard on RP2040 as you say, but differently of PWM, the Timer needs to run a callback Python code, so, how that, even a hard interrupt, works simultaneously with the GC?

Example:

Code: Select all

# 800Hz = 1250 µs
freq_ = 800
tim = Timer()

@micropython.native
def start_pin():
    global pin
    pin.value(1)

tim.init(freq=freq_, mode=Timer.ONE_SHOT, callback=start_pin)
In this scenario, the interrupt happen in the exactly time of 1250us, where that time is just when GC is running.

Thank you so much!

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: RP2040 PIO Pin change to HIGH do not works

Post by pythoncoder » Fri Apr 01, 2022 1:20 pm

A hard IRQ will interrupt a GC. Once the Python ISR has completed, the GC will resume from the point where it was interrupted. The ISR cannot allocate and any attempt to do so will raise an exception.
Peter Hinch
Index to my micropython libraries.

beyonlo
Posts: 58
Joined: Thu Nov 26, 2015 8:03 pm

Re: RP2040 PIO Pin change to HIGH do not works

Post by beyonlo » Fri Apr 01, 2022 2:14 pm

pythoncoder wrote:
Fri Apr 01, 2022 1:20 pm
A hard IRQ will interrupt a GC. Once the Python ISR has completed, the GC will resume from the point where it was interrupted. The ISR cannot allocate and any attempt to do so will raise an exception.
Understood! Thank you very much @pythoncoder for your help.

I will add a "test.append('ok')" inside that callback function (to allocate memory) and adjust to the Timer interruption happen just when the GC are running to check it raising an exception :)

Code: Select all

@micropython.native
def start_pin():
    global pin
    pin.value(1)
    test = []
    test.append('ok')

beyonlo
Posts: 58
Joined: Thu Nov 26, 2015 8:03 pm

Re: RP2040 PIO Pin change to HIGH do not works

Post by beyonlo » Mon Apr 04, 2022 1:19 pm

beyonlo wrote:
Fri Apr 01, 2022 2:14 pm
pythoncoder wrote:
Fri Apr 01, 2022 1:20 pm
A hard IRQ will interrupt a GC. Once the Python ISR has completed, the GC will resume from the point where it was interrupted. The ISR cannot allocate and any attempt to do so will raise an exception.
Understood! Thank you very much @pythoncoder for your help.

I will add a "test.append('ok')" inside that callback function (to allocate memory) and adjust to the Timer interruption happen just when the GC are running to check it raising an exception :)

Code: Select all

@micropython.native
def start_pin():
    global pin
    pin.value(1)
    test = []
    test.append('ok')
Hey @pythoncoder

The ISR allocate memory successfully:
I did that test, to allocate memory (a.append()), but do not raise an exception, it just works - why? Please, could you to check if my example test is right?

Code: Select all

from machine import Pin, Timer, freq
import gc, time, array
freq(273_000_000)

gc.disable()

# 800Hz = 1250 µs
# 200Hz = 5000 µs
freq_ = 800

tim = Timer()
pin = Pin(25, Pin.OUT)
pin.value(0)

a = []

@micropython.native
def start_pin(timer):
    pin.value(1)
    a.append(time.ticks_us()) ####### allocate memory ######

start_pin_ref = start_pin

time_list = array.array('i', (0 for _ in range(10)))
count = 0


gc.collect()
while count < 10:
    tim.init(freq=freq_, mode=Timer.ONE_SHOT, callback=start_pin_ref)
    start_time = time.ticks_us()
    gc.collect()
    end_time = time.ticks_us()
    time_list[count] = time.ticks_diff(end_time, start_time)
    count += 1
    time.sleep_ms(30)
    pin.value(0)
    time.sleep_ms(30)

print(time_list)
print(freq())
print(a)
Output:

Code: Select all

>>> import os
>>> os.uname()
(sysname='rp2', nodename='rp2', release='1.18.0', version='v1.18 on 2022-01-17 (GNU 11.2.0 MinSizeRel)', machine='Raspberry Pi Pico with RP2040')
>>> import gc2
array('i', [1975, 1948, 1945, 1945, 1943, 1945, 1943, 1943, 1939, 1939])
273000000
[616240306, 616302298, 616364299, 616426298, 616488300, 616550305, 616612305, 616674306, 616736304, 616798304]
>>> 
MPY: soft reboot
MicroPython v1.18 on 2022-01-17; Raspberry Pi Pico with RP2040
Type "help()" for more information.
>>> import gc2
array('i', [1969, 1944, 1939, 1939, 1940, 1940, 1939, 1940, 1938, 1940])
273000000
[621036843, 621098841, 621160835, 621222829, 621284821, 621346812, 621408806, 621470801, 621532794, 621594785]
>>> 
In the output, you can notice, that I repeated the test after a soft reset (CTRL+D), because the first one is a bit different of others repeated tests with soft reset.


Test 2:
And, in this same code, are included another test, where, I think, that confirm what you say: that GC stop to answer the Timer callback, and after GC resume, adding ~4us on the GC total time. Look the var freq_ = 800, where the 800Hz is 1250us, and if GC time is around ~2000us, so that Timer callback will happen exactly when GC are running, right? This test I used 800Hz and do not allocate memory on the callback Timer:

Code: Select all

freq_ = 800

@micropython.native
def start_pin(timer):
    pin.value(1)
The output:

Code: Select all

>>> import gc2
array('i', [1971, 1944, 1940, 1939, 1937, 1938, 1937, 1938, 1938, 1937])
273000000
[]
>>>
This output is just test after soft reset.

Test 3:
To be sure (is a good way?) that really the Timer is happening when GC are running, I just changed var to the freq_ = 200, where the 200Hz is 5000us, that is of course after that ~2000us used by GC.

Code: Select all

freq_ = 200

@micropython.native
def start_pin(timer):
    pin.value(1)

Code: Select all

>>> 
>>> import gc2
array('i', [1937, 1937, 1934, 1934, 1934, 1934, 1934, 1933, 1933, 1933])
273000000
[]
>>> 
This output is just test after soft reset.

So, you can see the output difference between Test 2 and Test 3 in us is the around 4us. Is that a proof that GC is stopping to answer the callback and resume after?

Thank you in advance!

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: RP2040 PIO Pin change to HIGH do not works

Post by pythoncoder » Tue Apr 05, 2022 9:44 am

This is an area I haven't tested on the RP2, however the fact that you can allocate in a timer ISR implies that the RP2 timer IRQ is soft. Your ISR code would definitely fail on a Pyboard's hardware timer. The docs for RP2 indicate that timers are soft. Pin IRQ's on RP2 may be hard or soft.
Peter Hinch
Index to my micropython libraries.

beyonlo
Posts: 58
Joined: Thu Nov 26, 2015 8:03 pm

Re: RP2040 PIO Pin change to HIGH do not works

Post by beyonlo » Tue Apr 05, 2022 6:31 pm

pythoncoder wrote:
Tue Apr 05, 2022 9:44 am
This is an area I haven't tested on the RP2, however the fact that you can allocate in a timer ISR implies that the RP2 timer IRQ is soft. Your ISR code would definitely fail on a Pyboard's hardware timer. The docs for RP2 indicate that timers are soft. Pin IRQ's on RP2 may be hard or soft.
Hi @pythoncoder

But if the Timer IRQ is soft, how is possible the GC do not delaying that timer (1250us) answer? The Timer is answered while the GC are running (apparently answer and resume), and not after that GC is complete - the proof are the Test 2 and Test 3 above, right?

1. As you wrote in this thread viewtopic.php?f=21&t=12166#p66154 : "Soft ISR's are delayed until any running GC is complete. This can imply significant latency, but allows the ISR to run any Python code."

2. And, As you wrote in this thread viewtopic.php?f=21&t=12166&start=10#p66223 "A hard IRQ will interrupt a GC. Once the Python ISR has completed, the GC will resume from the point where it was interrupted."

I'm mentioning the 1. and 2. comments because I would like to figure out what I have in hands. Apparently, that behaviour in the tests above are the hard IRQ with capable allocate memory feature - but the docs for RP2 indicate that timers are soft, yes. Well, that behaviour (in the tests) do not fit as soft IRQ as well not hard IRQ, right? Maybe is possible that there are a new feature in the MicroPython where a soft IRQ is capable to interrupt the GC, answer and resume, like as on the hard IRQ?

Thank you in advance!

beyonlo
Posts: 58
Joined: Thu Nov 26, 2015 8:03 pm

Re: RP2040 PIO Pin change to HIGH do not works

Post by beyonlo » Thu Apr 07, 2022 11:42 pm

beyonlo wrote:
Tue Apr 05, 2022 6:31 pm
pythoncoder wrote:
Tue Apr 05, 2022 9:44 am
This is an area I haven't tested on the RP2, however the fact that you can allocate in a timer ISR implies that the RP2 timer IRQ is soft. Your ISR code would definitely fail on a Pyboard's hardware timer. The docs for RP2 indicate that timers are soft. Pin IRQ's on RP2 may be hard or soft.
Hi @pythoncoder

But if the Timer IRQ is soft, how is possible the GC do not delaying that timer (1250us) answer? The Timer is answered while the GC are running (apparently answer and resume), and not after that GC is complete - the proof are the Test 2 and Test 3 above, right?

1. As you wrote in this thread viewtopic.php?f=21&t=12166#p66154 : "Soft ISR's are delayed until any running GC is complete. This can imply significant latency, but allows the ISR to run any Python code."

2. And, As you wrote in this thread viewtopic.php?f=21&t=12166&start=10#p66223 "A hard IRQ will interrupt a GC. Once the Python ISR has completed, the GC will resume from the point where it was interrupted."

I'm mentioning the 1. and 2. comments because I would like to figure out what I have in hands. Apparently, that behaviour in the tests above are the hard IRQ with capable allocate memory feature - but the docs for RP2 indicate that timers are soft, yes. Well, that behaviour (in the tests) do not fit as soft IRQ as well not hard IRQ, right? Maybe is possible that there are a new feature in the MicroPython where a soft IRQ is capable to interrupt the GC, answer and resume, like as on the hard IRQ?

Thank you in advance!

Hey @pythoncoder

Sorry for my mistake, it was my fault by do not test in the oscilloscope. The interrupt by Timer is really delaying, I tested on the scope, confirming of course that Timers is soft. On the scope, when the timer interrupt should be happen, while GC is running, that interrupt is answer only after GC is complete.

I tested the PWM too on the scope, and the PWM is not affected by GC. Well, I will try to use PWM on the RP2040 instead interrupt by Timers :). Problem with PWM is that do not accept a float value on frequency argument. Is the reason for that why the RP2040 do not have a float point on the processor?

Thank you!

Post Reply