timer callback performance help

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
Post Reply
Egon
Posts: 4
Joined: Mon Mar 07, 2016 2:52 am
Location: Australia

timer callback performance help

Post by Egon » Mon Mar 07, 2016 4:00 am

Hi, I have a PyBoard v1.1 that I want to use for a high speed application, callback response < 6uS.

First thing to note is that my callback is to a asm_thumb function which itself has parameter passing restrictions.

If I use a callback directly to a function {i.e. tim.callback(somefunction) } I get a 3.2uS setup time which is acceptable as the code that I will be executing will only take an additional 1 to 1.5uS. Using this though I don's seem to be able to pass any additional parameters?

If I use { tim.callback(lambda t: somefunction(arrayAddress)) } I can pass the necessary Array Address parameter which contains my global variables, but this produces a 11.5uS setup time (9.2uS if I don't pass any parameters).

What I would like to know is if there is a way of defining a const() that contains the address of my global Array?

Regards, Egon

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: timer callback performance help

Post by dhylands » Mon Mar 07, 2016 6:22 am

Timer callbacks need to take one, and exactly one argument, which is the timer object associated with the interrupt.

You can use bound functions to get additional information, but I've only done this in python, so I'm not sure it will meet your timing constraints.

Here's an example that uses a bound method: https://github.com/dhylands/upy-example ... eat_irq.py

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

Re: timer callback performance help

Post by pythoncoder » Mon Mar 07, 2016 7:23 am

Assembler functions can be bound methods but the means of accessing class or instance data is undocumented and certainly unknown to me. I think you might have to be a little devious to provide runtime access to the address of your global data. const() won't work as its argument must be known at compile time. However the Pyboard does have some places where data can be put. The RTC has a set of backup registers and also backup RAM. At initialisation the Python code could store the address of the array (uctypes.addressof()) in RTC backup register 0, and the assembler code could retrieve it.

There is some Python code for accessing the registers here https://github.com/peterhinch/micropyth ... /upower.py - you clearly don't need the entire module.
Peter Hinch
Index to my micropython libraries.

Egon
Posts: 4
Joined: Mon Mar 07, 2016 2:52 am
Location: Australia

Re: timer callback performance help

Post by Egon » Tue Mar 08, 2016 12:26 am

Thank you Dave and pythoncoder for your prompt and informative response.

It's taken me a bit of time this morning to verify all of the RTC and Backup NVRAM addresses. I don't like the finger-poking in register addresses unless I understand what is going on :)

I implemented backup RAM to hold only the address of my transfer array so I only have to access one 32bit value. Using this it works! as a method of passing additional parameters to a asm_thumb ISR module and does it with no additional overhead.

< 80nS to change the state of a GIO pin, load the address of the backup NVRAM location, load a register from NVRAM with the address of the transfer Array and change the state of the GIO pin again.

Unfortunately as wonderful as the pyboard and micro python are it's only acceptable 98% of the time for my application. 2% of the time I am getting spurious delays in servicing the interrupt which doubles my service time to approximately 6.6uS. Next option is to use a Cortex M4 IDE to write it in C.

I am very impressed that the pyBoard can reliably handle ISR down to 7uS. Under 7uS though if you can tolerate only a ~ 95% response you can go down to 3.8us and still execute a number of floating point instructions, including a sort(), and still return to normal processing.

Thank you again and best regards,
Egon

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: timer callback performance help

Post by dhylands » Tue Mar 08, 2016 7:40 am

Which ISR are you using?

You may need to tweak the ISR levels, so that your timer has higher priority than the USB/flash stuff, which looks like using a value of 1.

This file controls the current ISR levels:
https://github.com/micropython/micropyt ... /irq.h#L78

You should be able to tweak the ISR levels by a register write from asm code. This is the C code:
https://github.com/micropython/micropyt ... m4.h#L1555

The NVIC->IP is at address 0xE000E000 + 0x100 + 0x300 + IRQ-number
You can find the IRQ numbers for the various timers by looking here:
https://github.com/micropython/micropyt ... 05xx.h#L96

You'll need to take the IRQ level (number from 0 to 15) and shift it left by 4 bits and store that at the address calculated above. You can do this in python using the stm module. For example, I know that just initializing the UART will set its IRQ entry. USART6_IRQn has a value of 71.

Code: Select all

>>> import stm
>>> hex(stm.mem8[0xe000E000 + 0x100 + 0x300 + 71])
'0x0'
>>> pyb.UART(6,9600)
UART(6, baudrate=9600, bits=8, parity=None, stop=1, timeout=1000, timeout_char=2, read_buf_len=64)
>>> hex(stm.mem8[0xe000E000 + 0x100 + 0x300 + 71])
'0x10'

Egon
Posts: 4
Joined: Mon Mar 07, 2016 2:52 am
Location: Australia

Re: timer callback performance help

Post by Egon » Wed Mar 09, 2016 5:10 am

Hello Dave,

I was using timer 2 but I have now changed that to time5 which from your post has an inherent higher priority. This helped and improved the stability to >99% i would guess. Hard to tell how bright a skewed trigger is when so infrequent.

I have now implement a partial priority change. I've scanned the interrupt priority list and changed time5 to be second highest. I now can't seen any triggers deviating more than about 500nS. This is good :)

The only other services I will have running in the background will be UART comms and I can accept an additional .5uS per byte during UART reads.

Any ideas into ISR service and return times? No longer critical but I am intrigued by the almost 3uS in and 3uS out this seems long for such a fast processor and is by far the limiting factor is ISR service times. I assume that there isn't a option in callback that minimises the context being saved and relies on the developer to push/pull only those registers being used :D .

Looking good though, thank you.
Best regards,
Egon

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: timer callback performance help

Post by dhylands » Wed Mar 09, 2016 7:18 am

Currently, we only have one callback function (in C for all of the timers):
https://github.com/micropython/micropyt ... er.c#L1322
I did some analysis of timings here:
http://forum.micropython.org/viewtopic. ... t=10#p4781

The timer code deals with both timer and timer channel interrupts.

In some cases, multiple timers are mapped onto the same interrpt handler (for example timers 1 & 10 and also 8 & 13).

chuckbook
Posts: 135
Joined: Fri Oct 30, 2015 11:55 pm

Re: timer callback performance help

Post by chuckbook » Wed Mar 09, 2016 11:11 am

It might be a good idea to lift timer int to highest priority as systick currently uses top priority.
Also timer 5 has slightly more overhead than timer 2.
Further improvement would require a mod of MPY to circumvent timer int dispatching.

Damien
Site Admin
Posts: 647
Joined: Mon Dec 09, 2013 5:02 pm

Re: timer callback performance help

Post by Damien » Wed Mar 09, 2016 10:25 pm

Egon wrote: Any ideas into ISR service and return times? No longer critical but I am intrigued by the almost 3uS in and 3uS out this seems long for such a fast processor and is by far the limiting factor is ISR service times. I assume that there isn't a option in callback that minimises the context being saved and relies on the developer to push/pull only those registers being used :D .
The 3us will be due to the overhead to dispatch to the callback. There is quite a lot being done behind the scenes: the GC is locked so you can't allocate on the heap; an exception handler context is pushed to trap Python exceptions; the callback method is dynamically looked up, arguments are checked and then the dispatch is made.

This is one area that could definitely be optimised, but so far no work has been done in that direction. Ideas for optimisation are:

1. Check the callback type signature upon registering it so that it doesn't need to be checked every interrupt (and it'll never change so checking it once should be enough).

2. Inline the function dispatch code to reduce overhead during the ISR. Because there is always exactly 1 arg being passed this would lead to quite a big simplification in the dispatch.

3. Have a special path for assembler functions that doesn't push the exception handler context or lock the heap (since assembler can't allocate on the heap or raise an exception).

Egon
Posts: 4
Joined: Mon Mar 07, 2016 2:52 am
Location: Australia

Re: timer callback performance help

Post by Egon » Thu Mar 10, 2016 3:24 am

Hello All,

Thank you,

I have just had a look at the service link provided by Dave and also had a look at the STM32F programming documentation for interrupt servicing and well please forgive me I am an old dude that has also been out of it for a decade.

I'm still use to individual interrupt vectors with some inherent priority control for small batches of like types but where you still have a number of interrupt service routine.

I can now see why there is a delay in servicing interrupts and it doesn't help having so many potential sources of interrupts. I am surprised it is being done this way but I can certainly understand why it's done that way.

Thanks and best regards,
Egon

Post Reply