Last month, in 3 Tips for Speeding Up Framework Interrupt Handlers, we looked at several methods that could be used to speed up interrupts. In today’s post, we will explore three more tips on how to speed-up interrupt handlers. Let’s get to it!
Tip #1 – Move Interrupt Handlers from Flash to RAM
Depending on the toolchain that you are using, you might find that your interrupt vector table by default is placed into flash. This can be a good idea if you are working on an application where security is important. With the interrupt vector table in flash, no one can rewrite the table; However, this can affect run-time performance and there are other ways to protect the vector tables.
Reading and writing to flash is far slower than reading or writing to SRAM. On some parts, the difference in speeds can approach 10x or so. A great way to speed-up the interrupt handling is to place the interrupt vector table and the interrupt service routine into SRAM. This won’t be a huge savings, but I’d expect there to be upwards of 1 microsecond worth of savings available depending on the microcontroller clock speed and architecture.
If you were worried about security, you can use the memory protection unit (MPU) to set up a protection scheme for the interrupt vector table and the interrupts.
Tip #2 – Compile for Speed
A good compiler will allow a developer to control the optimizations that are applied to each module and even to individual functions. When concerned with execution speed, an interrupt service routine can be optimized by the compiler for speed. Depending on the code, it’s possible for this to yield improvements anywhere from 10 – 20%! Again, this may only improve things by a few microseconds, but every little bit can help!
It’s also important to note that by default, most compilers will try to balance execution speed and code size. The optimizer is always at war with balancing these two. If you want fast executing code, the code size tends to go up. Since interrupt routines should be short, optimizing these functions should have a minimal impact on the overall code size.
Tip #3 – Limit the Interrupts Function
There is a temptation to add more functionality to an interrupt service routine than is necessary to get the job done. For example, if an interrupt will be used to gather timestamps that are used to calculate an input signals frequency and duty cycle, a developer will often run the duty cycle and frequency calculations in the interrupt service routine! This seems logical since the data is already there and it’s just a few calculations to get and store the result, but those quick calculations can dramatically increase the time that the interrupt has the CPU!
Perhaps the best tip for making interrupts fast is to just limit what they do. If a developer wants to calculate frequency and duty cycle, they should capture the timestamps for the edges in the interrupt service routine and then use application code to calculate the values when they are needed by the application. By doing this, it’s possible to decrease interrupt execution times by more than 50%!
In the interrupt, do only the minimum that is necessary, the highest priority activity, and then get back to the application as quickly as possible.
Interrupts are a core tool that developers use in nearly every application. Unfortunately, today’s example code and software frameworks come with bloated interrupt service routines. They are not optimized to run quickly or efficiently. In fact, it’s not uncommon to come across code that is running 300 – 400 % longer than is necessary! The result is that interrupt service routines become the dominant activity that consumes the most CPU time and cycles. We’ve explored in total six tips that can be used to ensure that interrupt service routines run quickly and efficiently, leaving more processing power to the application and not supporting infrastructure code.