Pages: [1]   Go Down
Author Topic: DUE interrupt latency  (Read 2108 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Jr. Member
**
Karma: 9
Posts: 79
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Bill Grundmann's Blog (http://billgrundmann.wordpress.com/2009/03/02/the-overhead-of-arduino-interrupts/) has a nice discussion of interrupt overhead on AVR 168. I ran a simple DUE sketch with pin 13 tied to pin 2 to measure number of cycles (systick) to enter and exit an interrupt ISR using standard attachInterrupt() ISR and a version where I modified the core ISR handler. I ran similar sketches on maple and teensy3. The results are in isrperf.txt and the DUE sketch is isrperf.ino at the following github site

https://github.com/manitou48/DUEZoo
Logged

nr Bundaberg, Australia
Offline Offline
Tesla Member
***
Karma: 129
Posts: 8592
Scattered showers my arse -- Noah, 2348BC.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

So the standard latency is 355+128 and modified 53+28.

At what rate dies the systick counter tick so we can calc the actual time?

______
Rob
Logged

Rob Gray aka the GRAYnomad www.robgray.com

Earth
Offline Offline
Sr. Member
****
Karma: 14
Posts: 331
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

You sir are a gentleman and a scholar. I really like your git repo and the work you're doing. The core library for Due needs a lot of work to make it what it should be and your work is a big help in that.
Logged

nr Bundaberg, Australia
Offline Offline
Tesla Member
***
Karma: 129
Posts: 8592
Scattered showers my arse -- Noah, 2348BC.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Why thanks Adder, that's very kind of y...Oh, you mean mantoui smiley

_____
Rob
Logged

Rob Gray aka the GRAYnomad www.robgray.com

Offline Offline
Newbie
*
Karma: 1
Posts: 42
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

When measuring interrupt latency for the DUE one has to be a bit careful. If you attach an interrupt to a pin using attachInterrupt(), the latency depends on the pin that you choose. The reason is the following:

The ARM detects interrupts only for a whole PORT and sends a 32bit word with the information on which pin/pins on this PORT an interrupt was triggered. The code that handles this word and decides which ISR should be called, is in the "WInterrupts.c" file (shown for port A only):

Code:
void PIOA_Handler(void) {
uint32_t isr = PIOA->PIO_ISR;
uint32_t i;
for (i=0; i<32; i++, isr>>=1) {
if ((isr & 0x1) == 0)
continue;
if (callbacksPioA[i])
callbacksPioA[i]();
}
}

As you can see, it scans through each bit of the 32bit word "isr" that corresponds to the 32 pins on port A and checks if an interrupt was triggered on this pin or not (pin corresponding bit either 1 or 0). Then it calls the interrupt service routine that was previously stored for this pin in "callbacksPioA".

The problem with this code is, that running through the loop without calling any service routines already takes about 300 clock cycles (by measuring I find something like 4µs). This means that the first pin on each port has a latency on the order of 120ns and the last one of about 4µs. What I don't like about this code is also the fact that it scans through all the 32 pins (meaning 4µs dead time for the processor) even if you have only one interrupt attached.

I think this problem can be quite easily circumvent by storing the bitmasks for the pins that you attached interrupts to and just let the sum running over the number of interrupts that were attached so far. Then using the stored bitmasks it would be quite easy to see where an interrupt was triggered by looking only at the pins for which an interrupt was already defined.
This would massively reduce both the average interrupt latency as well as the caused processor dead time for most applications where you only use a couple of interrupts. (Of course if you use interrupts for all 32pins on a port, then you have to live with these delays and deadtimes)

I might do some coding later on and see if I can implement something like this.
Logged

Offline Offline
Newbie
*
Karma: 1
Posts: 42
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Just as a comment to your data:
This is actually why you see 355ticks "in to ISR" and 128 "out of ISR" for your pin 2 (which is pin 25 on PORT B):
The ratio of "in to ISR" to the total time 355/(355+128) = 0.73 is almost the same as your "pin on port" number to total pin numbers 25/32 =  0.78
Logged

Offline Offline
Jr. Member
**
Karma: 0
Posts: 60
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

How can I define my own PIOA_Handler function without modifying the IDE code. I see that the function pointer is placed in the exception table (which is a const) which goes to .vectors section. How do I modify this table and replace the PIOA_Handler with myPIOA_Handler.
Logged

Offline Offline
Jr. Member
**
Karma: 0
Posts: 60
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

tried this code  -

Code:
static void PIOA_attach_handler()
{
Serial.println("attaching interrupt handler");
DeviceVectors * ptr = (DeviceVectors *) &(exception_table);

Serial.print("pioa handler address original - ");
Serial.println((unsigned int )exception_table.pfnPIOA_Handler, HEX);
Serial.print("new pioa handler address - ");
Serial.println((unsigned int )&myPIOA_Handler, HEX);

ptr->pfnPIOA_Handler = (void *) &myPIOA_Handler;
Serial.print("pioa handler address after change, source - ");
Serial.println((unsigned int )ptr->pfnPIOA_Handler, HEX);

Serial.print("pioa handler address after change - ");
Serial.println((unsigned int )exception_table.pfnPIOA_Handler, HEX);
}

i got this output, new handler doesnt goto the table.

Code:
attaching interrupt handler
pioa handler address original - 845E9
new pioa handler address - 801DD
pioa handler address after change, source - 845E9
pioa handler address after change - 845E9
Logged

Offline Offline
Jr. Member
**
Karma: 0
Posts: 60
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I tried to modify the sources (startup_sam3xa.c) in the IDE, hoping I could get it to work that way. But the exception table is unfortunately buried inside an already built lib - hardware/arduino/sam//variants/arduino_due_x/libsam_sam3x8e_gcc_rel.a(startup_sam3xa.o). So just changing the sources wont help, need to compile those too. Anyone tried building this library, makefile included in that area didnt work out of the box.
Logged

Earth
Offline Offline
Sr. Member
****
Karma: 14
Posts: 331
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The actual interrupt handler is defined in WInterrupts.c as:
Code:
void PIOA_Handler(void) {
uint32_t isr = PIOA->PIO_ISR;
uint32_t i;
for (i=0; i<32; i++, isr>>=1) {
if ((isr & 0x1) == 0)
continue;
if (callbacksPioA[i])
callbacksPioA[i]();
}
}

What you want to do is override this function. You can do that but you have to define it as weak. Add this line before that function:
void PIOA_Handler(void) __attribute__((weak));

Now, you are free to create a function that matches the prototype in your sketch and it will actually override the Arduino core version properly at link time. This requires changing the arduino core source just a bit so it doesn't quite fit the bill for what you originally wanted but it is close.
Logged

Offline Offline
Jr. Member
**
Karma: 0
Posts: 60
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

thanks a lot.. that worked fine... i can live with this for the time being:)
Logged

Pages: [1]   Go Up
Jump to: