How to improve interrupt performance

I am looking for some ideas about how to improve interrupt performance, namely by making interrupt service routines much quicker.  I wrote a small ISR for the CAN controller’s receive routine:     void can2_rx_irq()     {         portENTER_SWITCHING_ISR();         portBASE_TYPE ret;         ret = xQueueSendFromISR(rxQ2, (CanMsg *) &CAN2_RFS, pdFALSE);         CAN2_CMR.word = 0x04;         portEXIT_SWITCHING_ISR(ret);     } This generates a whopping 256 instructions.  I haven’t gone through and counted cycles but these interrupts could potentially occur at a rate of 128 microseconds, if a burst of packets arrives in both CAN controllers at once. I could instead create a circular buffer of incoming frames and have an assembly language ISR stuff packets into that buffer in a handful of assembly language instructions.  The problem is, that wouldn’t wake up any tasks waiting on incoming messages.  Using a semaphore is exactly the same as using a queue, less a little copying (these frames are only 4 words of copying). Does anybody have a suggested architecture that might work better?  One idea I had was to make the ISR be an FIQ that just stores the packet then triggers a software interrupt through the VICSoftInt.  The FIQ would stuff the packets, the IRQ would pull them from the first level buffer then do as above.  In order to help, ordinary IRQs would have to be interruptible by the FIQ.  Normally I try to avoid interrupt nesting at all expense, but in this case I think that this will be a problem because the FIQ handler wouldn’t even need a stack. It almost seems like what I need is a REALLY lightweight asynchronous event / signal mechanism.  Or, if I had some way for my ISR to easily notify the schedule to wake up a certain task next time the scheduler happens to run. I could fake this through the idle task and a global pretty easily but that sounds like poor design, and there’s no guarantee the idle task will ever get to run.  I could make a high level task wake up on a timer and check to see whether or not any new packets have arrived but that’s either going to have high latency or waste a lot of cycles polling the queue.

How to improve interrupt performance

> I am looking for some ideas about how to improve interrupt > performance, namely > by making interrupt service routines much quicker.  I wrote a > small ISR for > the CAN controller’s receive routine: > >     void can2_rx_irq() >     { >         portENTER_SWITCHING_ISR(); >         portBASE_TYPE ret; >         ret = xQueueSendFromISR(rxQ2, (CanMsg *) &CAN2_RFS, pdFALSE); >         CAN2_CMR.word = 0x04; >         portEXIT_SWITCHING_ISR(ret); >     } Is this done per message, or per byte?  It takes the form of the demo serial drivers which are known (according to the comments in the docs) to be inefficient. Can you – use a DMA to grab all the message in one go?  Use a fifo to only generate interrupts when there are (say) 8 bytes of data available at the same time?  Obtain more than one set of data within a single ISR?  Use the ISR to simply place data in a ring buffer, then use a semaphore to wake the task when there is an entire message available? > > This generates a whopping 256 instructions.  I haven’t gone > through and counted > cycles but these interrupts could potentially occur at a rate > of 128 microseconds, > if a burst of packets arrives in both CAN controllers at once. > > I could instead create a circular buffer of incoming frames > and have an assembly > language ISR stuff packets into that buffer in a handful of > assembly language > instructions.  The problem is, that wouldn’t wake up any > tasks waiting on incoming > messages.  Using a semaphore is exactly the same as using a > queue, less a little > copying (these frames are only 4 words of copying). That answers some of my previous questions. > > Does anybody have a suggested architecture that might work > better?  One idea > I had was to make the ISR be an FIQ that just stores the > packet then triggers > a software interrupt through the VICSoftInt.  The FIQ would > stuff the packets, > the IRQ would pull them from the first level buffer then do > as above.  It is a common technique for the portENTER_CRITICAL and portEXIT_CRITICAL macros to only disable interrupts at the kernel level – in this case only disable the IRQ interrupt.  Then have higher priority interrupts that are never held up by the kernel.  Note that the higher priority interrupts cannot use the API though otherwise you could get crashes due to race conditions. You only need to switch to the task within the interrupt if it is a really high priority task.  Otherwise, simply waking the task in the interrupt is sufficient.  It will then get processing time in accordance with its priority with respect to the other tasks in the application.  If you do this you need not save and restore the context of the task each time. Dave.

How to improve interrupt performance

>Is this done per message, or per byte? No, it’s per CAN frame, but the bus rate in my case is 250kbps.  (Many CAN networks use 1Mbps).  The minimum CAN frame is about 63 bits, so back-to-back they could occur at a 256 microsecond rate. >Can you – use a DMA to grab all the message in one go? Unfortunately it doesn’t appear so.  There is a mode in the LPC2129 CAN implementation that lets it write packets to a dedicated 2KB block of SRAM but it does it in a "mailbox" rather than sequential fashion. >You only need to switch to the task within the interrupt if it is a really high priority task. Ahh hah!  So, how would I do that… is this acceptable?     void my_irq()     {         portENTER_CRITICAL();         (void) xQueueSendFromISR(rxQ2, (Canmsg *) &CAN2_RFS, pdFALSE);         CAN2_CMR.word = 0x04;         VICVectAddr = portCLEAR_VIC_INTERRUPT;         portEXIT_CRITICAL();     } Is the ENTER_CRITICAL() and EXIT_CRITICAL() even necessary if no interrupt nesting is allowed?

How to improve interrupt performance

Sounds like the 2K mailbox thingy mechanism might be your best bet. In your my_irq() implementation you don’t need the enter and exit critical – in fact, definitely don’t use them.  If you are using GCC then you have to declare the interrupt using __attribute__(("IRQ")) rather than __attribute__(("naked")) – I might have the syntax slightly wrong.  In any case, just make the function a normal interrupt handler function. Dave.

How to improve interrupt performance

>Sounds like the 2K mailbox thingy mechanism might be your best bet. The problem is that if you receive a packet with the same ID twice you’ll only get one, then the second copy will overwrite the first because it has the same ID and thus maps to the same mailbox.  This is very useful if you have a get/set paradigm typical of many CAN protocols, but less useful if you have a streaming protocol like the SAE J1939 multi-packet transport layer.  What would have been GREAT would have been if NXP had designed a mode where you could use the 2K as a circular buffer into which it stuffs packets.  It doesn’t do that. >If you are using GCC then you have to declare the interrupt >using __attribute__(("IRQ")) rather than __attribute__(("naked")) I’ll try that and eyeball the generated code.  Thanks!