Quality RTOS & Embedded Software

 Real time embedded FreeRTOS RSS feed 
Real time embedded FreeRTOS mailing list 
Quick Start Supported MCUs PDF Books Trace Tools Ecosystem TCP & FAT Training




Loading

How to improve interrupt performance

Posted by Christopher Piggott on May 30, 2007
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.


RE: How to improve interrupt performance

Posted by Dave on May 30, 2007
> 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.

RE: How to improve interrupt performance

Posted by Christopher Piggott on May 30, 2007
>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?


RE: How to improve interrupt performance

Posted by Dave on May 30, 2007
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.

RE: How to improve interrupt performance

Posted by Christopher Piggott on May 30, 2007
>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!


[ Back to the top ]    [ About FreeRTOS ]    [ Sitemap ]    [ ]




Copyright (C) 2004-2010 Richard Barry. Copyright (C) 2010-2016 Real Time Engineers Ltd.
Any and all data, files, source code, html content and documentation included in the FreeRTOSTM distribution or available on this site are the exclusive property of Real Time Engineers Ltd.. See the files license.txt (included in the distribution) and this copyright notice for more information. FreeRTOSTM and FreeRTOS.orgTM are trade marks of Real Time Engineers Ltd.

Latest News:

FreeRTOS V9.0.0 is now available for download.


Free TCP/IP and file system demos for the RTOS


Sponsored Links

⇓ Now With No Code Size Limit! ⇓
⇑ Free Download Without Registering ⇑


FreeRTOS Partners

ARM Connected RTOS partner for all ARM microcontroller cores

Renesas Electronics Gold Alliance RTOS Partner.jpg

Microchip Premier RTOS Partner

RTOS partner of NXP for all NXP ARM microcontrollers

Atmel RTOS partner supporting ARM Cortex-M3 and AVR32 microcontrollers

STMicro RTOS partner supporting ARM7, ARM Cortex-M3, ARM Cortex-M4 and ARM Cortex-M0

Xilinx Microblaze and Zynq partner

Silicon Labs low power RTOS partner

Altera RTOS partner for Nios II and Cortex-A9 SoC

Freescale Alliance RTOS Member supporting ARM and ColdFire microcontrollers

Infineon ARM Cortex-M microcontrollers

Texas Instruments MCU Developer Network RTOS partner for ARM and MSP430 microcontrollers

Cypress RTOS partner supporting ARM Cortex-M3

Fujitsu RTOS partner supporting ARM Cortex-M3 and FM3

Microsemi (previously Actel) RTOS partner supporting ARM Cortex-M3

Atollic Partner

IAR Partner

Keil ARM Partner

Embedded Artists