wiringPi and GPIO interrupt handling


19 posts
by devegied » Tue Jan 22, 2013 8:19 pm
Hi all,
I am trying to drive some RFM70 transceivers (nice overview here) using SPI. For accessing GPIO functions I am using great library wiringPi. To test all functionality of RFM70 I connected its IRQ line to signal (on falling edge) about received packets on incoming pipe or empty transmission pipe. The problem I bump in is that time to handle interrupt state in user function is longer than time between to consecutive interrupts (or new interrupt occurs somewhere between d. and a. in following scenario). wiringPi is handling interrupts in such steps:
  1. Initialization (call to wiringPiISR): setups GPIO, opens "value" file on sysfs, creates interrupt handling thread;
  2. Interrupt handling thread waits in the loop on the "value" file for events and waiting consist of:
    1. clearing all existing events state (by reading from "value")
    2. seeking to "value" file beginning
    3. starts poll´ing "value" file (actual waiting between this and next step)
    4. returns from poll call
    5. calls user provided handling function
With such scenario (by clearing all existing events) wiringPi guarantees that the user code will be executed only if event occurs somewhere between a. and d. and discards everything if event occurs between d. and e. or d. and a. Is it such practice considered a norm or this depends on library? And how this problem is solved in other systems? I found one IRQ handling example where step a. is done after d., and b. is entirely missing. In such handling scenario interrupts occurring in time of user handling function will not be missed (of course if there will be more than one interrupt user function will be called only once).
User avatar
Posts: 4
Joined: Tue Jan 22, 2013 7:11 pm
by gordon@drogon.net » Tue Jan 22, 2013 10:28 pm
You've looked at the source - and that's it, basically. poll() deschedules your program until the interrupt fires, then resumes it....

But what we're doing here is really a bit outside what Linux is good at - vectoring interrupts into userland was probably never envisaged way back... Tests showed that we can handle some 10K interrupts/sec though - this isn't a lot, but I think Linux has a lot of work to do here - take the interrupt, schedulle the program to be resumed, then jump back to userlant to resume it at the appropriate point...

I do have a "plan b" interrupt mechanism though - which works like a traditional "ISR" type thing, however under the bonnet it uses exactly the same poll(), etc. it just runs a user function as a thread concurrently with the main program - fetch the latest wiringPi for that, but as it's relying on poll() - actually, it calls the existing waitForInterrupt itself, then the mechanism is the same.

I've had a look at that other link you've provided and it's certianly worthy of some testing though, and if I have time later this week, I'll persue it.

From what I gather, the only way to get higher speed interrupts vectored into code is to write it as a kernel module.

-Gordon
--
Gordons projects: https://projects.drogon.net/
User avatar
Posts: 1526
Joined: Tue Feb 07, 2012 2:14 pm
Location: Devon, UK
by devegied » Tue Jan 22, 2013 11:16 pm
I see a problem not in speed of interrupts but in the way what library sees as a condition for interrupt after user function returned. Lets see example with RFM70: in user interrupt handling function I read some data and clear RFM70 interrupt state by sending status register value through SPI. RFM70 reacts quickly and pulls up interrupt line. When it receives wireless data it pulls down interrupt line. If these events occurs before wiringPi clears existing state by reading from "value" file (before step a.), interrupt line stays down, RFM70 waits indefinitely because it already signaled interrupt state, user function will never be called second time. This is real situation I got with RPi, RFM70 and wiringPi library. I tried the same wireless data rate with rewritten waitForInterrupt function where I am doing steps c. d. a. e. and it handles the load perfectly (there is needed one initial read on "value" file in wiringPiISR to discard initial event).
User avatar
Posts: 4
Joined: Tue Jan 22, 2013 7:11 pm
by devegied » Wed Jan 23, 2013 7:16 am
I draw some timing diagram to better understand situation
Image
I think my situation is like signal INTSIG2: falling edge signals interrupt, GPIO driver pushes event to "value" file, but wiringPi discards it by reading "value" before calling poll. But depending on situation different user programs will need different behavior. For example RFM70 with INTSIG2 needs that user function would be called second time, but with INTSIG3 it needs only one additional call (regardless two interrupt signal falls in that time). So maybe wiringPiISR needs additional parameter to configure how it should react to interrupt conditions occurred in time of user function execution?
User avatar
Posts: 4
Joined: Tue Jan 22, 2013 7:11 pm
by gordon@drogon.net » Wed Jan 23, 2013 7:44 pm
devegied wrote: So maybe wiringPiISR needs additional parameter to configure how it should react to interrupt conditions occurred in time of user function execution?


So.. I've spent a little time on this today... run some experiments and learned a lot more about these gpio interrupts...

The seek I was using is not needed (serves me right for copying someone elses example!) and the read() is neccessary as it resets the interrupt. (which I was aware of)

The issue is what happens then when a 2nd interrupt comes in while your servicing the 1st one. (And what happens if a 3rd or 4th comes in ...)

Arguably, if a 2nd interrupt comes in while still processing the first one, then you've lost - interrupts are coming in too fast for you to handle. However may be a case for 2 interrupt to be sent in very quick succession followed by a longer time interval and it's catching that 2nd interrupt which is desirable. (And if I've read it correctly, this is what's happening in your case)

The current wiringPi code throws away all pending interrupts before it waits for a new one. (so it misses/throws away a 2nd (or more) interrupt while you are processing the current one.

I've now changed the behaviour somewhat. This applies to both waitForInterrupt and the new ISR code (as it's waitForInterrupt () that I've changed). The behaviour now is to clear the interrupt as soon as it's detected, then do whatever it was you were doing. This will allow a 2nd interrupt to be latched and dispatched then next time was waitForInterrupt(), but only a 2nd - any more will be ignored and lost.

I've also noticed there is often a spurious interrupt when the system is setup - I can clear that using the ISR code, but not the 'raw' waitForInterrupt() - not sure if this is going to be an issue for anyone though.

If you want to try the new code, drop me an email - but I'll be doing a new more tests before I push a new released of wiringPi out.

-Gordon
--
Gordons projects: https://projects.drogon.net/
User avatar
Posts: 1526
Joined: Tue Feb 07, 2012 2:14 pm
Location: Devon, UK
by devegied » Wed Jan 23, 2013 9:47 pm
To keep existing functionality for existing users of wiringPi and introduce this new functionality you can add some new defines next to existing INT_EDGE_XXX and let the user by bit-or'ing mode parameter of wiringPiISR choose between two behaviors: old one where interrupt condition should occur when user function is not active and new one when wiringPi also reacts to interrupt condition at the time of user function execution.
gordon@drogon.net wrote:The seek I was using is not needed (serves me right for copying someone elses example!)
As I understand what RasperryPi/Linux developers say there are five interrupt conditions: raising edge, falling edges, both edges, low and high. With seek you could read last two conditions (without seek read after poll always returns 0 bytes) but for now I can not imagine what it exactly means interrupt on low or interrupt on high - does it means that poll without waiting will always return POLLPRI event while GPIO pin stays low or high?
Arguably, if a 2nd interrupt comes in while still processing the first one, then you've lost - interrupts are coming in too fast for you to handle.
Or the hardware/software setup has a flaw: with RFM70 to clear interrupt state I need to send some SPI data and RFM70 clears interrupt by returning INT line to high level, but after moment it pulls it down again and my user handler function at this time is still active (I tried to put call to wiringPiSPIDataRW as last instruction in user handler and there still was moments when new interrupt signal was discarded by read in waitForInterrupt).
This will allow a 2nd interrupt to be latched and dispatched then next time was waitForInterrupt(), but only a 2nd - any more will be ignored and lost.
With one additional thread per pin and semaphores it is possible to implement interrupt counting and call user function as many times as the interrupt condition occurs. But I can not imagine real world situation that would need it.
I've also noticed there is often a spurious interrupt when the system is setup
I saw the same behavior and think that it needs to be cleared in wiringPiISR (you can use the same function waitForInterrupt with 0 timeout).
If you want to try the new code, drop me an email - but I'll be doing a new more tests before I push a new released of wiringPi out.
I will send you PM. And it would be nice to hear some more opinions from forum readers - maybe someone have some practical experience with interrupt handling in userland programs (for me it is very first project - with RPi, interrupts, wiringPi, SPI and a lot more things - feels like I'm learning very well).
User avatar
Posts: 4
Joined: Tue Jan 22, 2013 7:11 pm
by gordon@drogon.net » Wed Jan 23, 2013 10:57 pm
FWIW: I have built up a little test and connected 2 GPIO pins together - one output, one input and wired the input to an "ISR" type function. The ISR is triggered on a falling edge. It raises the gpio pin, increments a global counter then lowers the pin (thus triggering another interrupt).

It's running at about 65K interrupts/sec.

Which I actually think is quite respectable, although there is no CPU left over to do anything useful at all...

-Gordon
--
Gordons projects: https://projects.drogon.net/
User avatar
Posts: 1526
Joined: Tue Feb 07, 2012 2:14 pm
Location: Devon, UK
by Glutton » Thu Jan 24, 2013 5:49 pm
I'm a relative newbie to all things "C" and "C++", but you *might* find my code for a kernel module for working with interrupts useful. It is buggy as the post mentions, but it works well reading 6 remote control rx lines while playing an mp3.
http://www.raspberrypi.org/phpBB3/viewtopic.php?f=33&t=28654
Bring on the punishment, Bring on the pain, For it is in the fight that we truly gain.
Knowledge is power, The fight for it fraught, To be without it is to truly know nought.
Posts: 9
Joined: Mon Jan 07, 2013 1:00 am
by Glutton » Fri Jan 25, 2013 3:36 am
Hi, I have investigated a little further and have created a simpler if a little "hackier" tester for interrupts in kernel modules.

The code in the archive is a quick re-write of my previous code, but bing simpler I hope is easier to follow. On "insmod" it will hog the cpu, "loading" the module. What it is in fact doing is the "flip flopping" of an input that is wired to an output to generate the falling edge interrupts and "logging" via kernel error messages. It will "report" every 1000000 interrupts, reporting for 10 loops, giving the time for each 1000000 to complete.

My math may be off, but running in this mode I see circa 200K interrupts/sec.

- Gordon, you were right - bitbanging a usb device interface is never going to fly directly by interrupts.

The code can be found here : http://www.zen90294.zen.co.uk/kgpioitest.tar.gz
Bring on the punishment, Bring on the pain, For it is in the fight that we truly gain.
Knowledge is power, The fight for it fraught, To be without it is to truly know nought.
Posts: 9
Joined: Mon Jan 07, 2013 1:00 am
by Glutton » Fri Jan 25, 2013 5:45 am
Someone stop me if I've lost the plot or not being helpful but, it *might* be possible to remap the irq for certain gpio lines (or banks thereof) to fiq.

From what I am reading here http://www.gnudd.com/sw/fiq-engine.html MHz speeds should be possible....
Bring on the punishment, Bring on the pain, For it is in the fight that we truly gain.
Knowledge is power, The fight for it fraught, To be without it is to truly know nought.
Posts: 9
Joined: Mon Jan 07, 2013 1:00 am
by gordon@drogon.net » Fri Jan 25, 2013 7:13 am
Glutton wrote:Someone stop me if I've lost the plot or not being helpful but, it *might* be possible to remap the irq for certain gpio lines (or banks thereof) to fiq.

From what I am reading here http://www.gnudd.com/sw/fiq-engine.html MHz speeds should be possible....


Maybe... But is the Pi running Linux the right platform that that sort of application?

-Gordon
--
Gordons projects: https://projects.drogon.net/
User avatar
Posts: 1526
Joined: Tue Feb 07, 2012 2:14 pm
Location: Devon, UK
by Glutton » Fri Jan 25, 2013 11:08 am
I don't know if it's a good idea or not.

Who knows what performance we can get until we try?

At present I'd say that using irq's is ok up to 25K interrupts/sec. More than that and you quickly run out of cpu do do any sensible processing with. With the variability in the timings I think that a mostly accurate 5K with "sloppy" timer is possible.

As for FIQ - well it's there to be (ab?)used, and if a puny SAM9 running linux can get into the 100's of MHz.......

It's going to be fun finding out :D
Bring on the punishment, Bring on the pain, For it is in the fight that we truly gain.
Knowledge is power, The fight for it fraught, To be without it is to truly know nought.
Posts: 9
Joined: Mon Jan 07, 2013 1:00 am
by trouch » Fri Jan 25, 2013 1:18 pm
from my understanding of BCM 2835 Datasheet, there is NO hardware interrupt triggered by GPIOs.
the only hardware interrupts are a timer and a watch dog one.
software interrupts in Pi linux use a workaround by polling the "edge detection" registers on the SoC.

WebIOPi - Raspberry Pi REST Framework to control your Pi from the web
http://store.raspberrypi.com/projects/webiopi
http://code.google.com/p/webiopi/
http://trouch.com
Posts: 308
Joined: Fri Aug 03, 2012 7:24 pm
Location: France
by Glutton » Fri Jan 25, 2013 9:16 pm
trouch wrote:from my understanding of BCM 2835 Datasheet, there is NO hardware interrupt triggered by GPIOs.
the only hardware interrupts are a timer and a watch dog one.
software interrupts in Pi linux use a workaround by polling the "edge detection" registers on the SoC.


This is not how I understand it. On page 113 of the BCM2835-ARM-Peripherals.pdf You can see that an irq is allocated to each "bank" of gpio pins. ( irq numbers 49-52 inclusive )

I *think* you can also select *ONE* irq to be FIQ.

I am still investigating, though I *think* the FIQ has already been used to solve some of the usb/networking issues....
Bring on the punishment, Bring on the pain, For it is in the fight that we truly gain.
Knowledge is power, The fight for it fraught, To be without it is to truly know nought.
Posts: 9
Joined: Mon Jan 07, 2013 1:00 am
by jojopi » Fri Jan 25, 2013 10:00 pm
trouch wrote:software interrupts in Pi linux use a workaround by polling the "edge detection" registers on the SoC.
Absolutely not. Edge detection uses hardware interrupts. The latency in switching context to the user task waiting for the event is rather high, but the jitter is much lower than could be produced by periodic polling. Anyway, this thread has already advanced to discuss servicing the interrupts entirely in kernel.
User avatar
Posts: 2056
Joined: Tue Oct 11, 2011 8:38 pm
by Glutton » Fri Jan 25, 2013 10:07 pm
I have read some more, this time the fiq changes for the usb fixes. I am reading here https://github.com/raspberrypi/linux/commit/f010d94155524454e2d5a9463ad6ca2b4fb81a2e

It looks more like every irq has an fiq counterpart at least it does if I'm reading the #defines correctly.
+#define FIQ_GPIO0 (FIQ_START+INTERRUPT_GPIO0)
+#define FIQ_GPIO1 (FIQ_START+INTERRUPT_GPIO1)
+#define FIQ_GPIO2 (FIQ_START+INTERRUPT_GPIO2)
+#define FIQ_GPIO3 (FIQ_START+INTERRUPT_GPIO3)


So now I'm thinking that the BCM datasheet is wrong, and that like a lot of other ARM cores out there you can mix and match your irq / fiq choices..

I've only been seriously trying to use "c" and "c++" for about a month or two and this usb code is looking like a *scary monster*. Not even sure the kernel I'm running on my pi has the fixes in it for the usb, might need to compile one to make sure I have as much fiq support code in place as I can....
Bring on the punishment, Bring on the pain, For it is in the fight that we truly gain.
Knowledge is power, The fight for it fraught, To be without it is to truly know nought.
Posts: 9
Joined: Mon Jan 07, 2013 1:00 am
by trouch » Fri Jan 25, 2013 10:37 pm
trouch wrote:software interrupts in Pi linux use a workaround by polling the "edge detection" registers on the SoC.


Glutton wrote:This is not how I understand it. On page 113 of the BCM2835-ARM-Peripherals.pdf You can see that an irq is allocated to each "bank" of gpio pins. ( irq numbers 49-52 inclusive )


jojopi wrote:Absolutely not. Edge detection uses hardware interrupts. The latency in switching context to the user task waiting for the event is rather high, but the jitter is much lower than could be produced by periodic polling. Anyway, this thread has already advanced to discuss servicing the interrupts entirely in kernel


Thanks for pointing me this part I miss ! still happy to learn something
I made bad assumptions reading GPIO registers pages and interrupt introduction :(
I already tried to find in the sources where the kernel write to /sys/class/gpio/gpioX/value to check
Do you know where ?

WebIOPi - Raspberry Pi REST Framework to control your Pi from the web
http://store.raspberrypi.com/projects/webiopi
http://code.google.com/p/webiopi/
http://trouch.com
Posts: 308
Joined: Fri Aug 03, 2012 7:24 pm
Location: France
by Glutton » Fri Jan 25, 2013 10:40 pm
trouch wrote:Thanks for pointing me this part I miss ! still happy to learn something
I made bad assumptions reading GPIO registers pages and interrupt introduction :(
I already tried to find in the sources where the kernel write to /sys/class/gpio/gpioX/value to check
Do you know where ?


I'm learning as well. You might find my kernel interrupt test code useful. You can find it here http://www.zen90294.zen.co.uk/kgpioitest.tar.gz
Bring on the punishment, Bring on the pain, For it is in the fight that we truly gain.
Knowledge is power, The fight for it fraught, To be without it is to truly know nought.
Posts: 9
Joined: Mon Jan 07, 2013 1:00 am
by trouch » Fri Jan 25, 2013 10:50 pm
Glutton wrote:I'm learning as well. You might find my kernel interrupt test code useful. You can find it here http://www.zen90294.zen.co.uk/kgpioitest.tar.gz


For sure, thanks for sharing !
WebIOPi will definitely need interrupts when I'll replace HTTP repetitive polling with WebSockets and Long-Polling
But I don't want to use linux poll() on /sys/class/gpio/gpioX/value as I already use SoC registers.

WebIOPi - Raspberry Pi REST Framework to control your Pi from the web
http://store.raspberrypi.com/projects/webiopi
http://code.google.com/p/webiopi/
http://trouch.com
Posts: 308
Joined: Fri Aug 03, 2012 7:24 pm
Location: France