Designing a small preemptive kernel – part1
First of all a caveat! I have never been trained as a software engineer. I say this to avoid abuse from those who have and know things that I don’t. My training is in electrical engineering. I have designed many things that have needed embedded intelligence, and over the years I have acquired a few skills in writing software for those systems.
One of the needs I quickly discovered was the requirement for systems to have definable real-time response. Way back I designed a small kernel for the Motorola HC11 to provide preemptive task switching. It was extremely simple, but has proved to be so useful over the years, I thought I would share the concepts with other designers working with microprocessors that lack memory management infrastructure.
Microprocessors are designed to respond to their environment either via interrupts or via polling. In the case of interrupts, processor hardware redirects the flow of execution when a certain event occurs like, for example, a processor pin being toggled. At the time of the interrupt, the current state of execution, the context, has to be saved so that it can be returned to later; and execution of an interrupt service routine, ISR is commenced.
With the polling method, software continually checks the status of various processor subsystems looking for new events. When an event discovery is made, then the code branches to a routine that services it.
Both these methods make the processor “aware” of external (and internal) events. However, the issue often encountered is: what is the response time of the system to an event, and what is the repeatability of this response.
With the polling method, checking for the event has to wait until the code has executed to the point where the actual checking is done. The execution may then go on to do other things before it gets back to checking again. With such systems there is often large variations in response time, depending on when the external event occurs relative to the software cycle.
The interrupt system is so named, because the present context of the processor is interrupted to attend an event. The interrupt system has much more promise for repeatable and fast response times.
Interrupts – the basis for Real-time response
So the concept of the interrupt is ideal as a basis for a real-time system. The problems arise when system has to handle many events. Some of those events are considered high priority to the user, whereas others are just house-keeping. Our simple single core processor can only do one thing at a time! If the processor is interrupted for the low priority task which takes 10 ms to service, what do we do if a high priority event occurs microseconds after the low priority one? We have a choice of letting it interrupt the ISR for the low priority task (nested ISRs) or it has to wait 10 ms while the processor does relatively low priority work. If you allow ISRs to be interruptable, what do you do in the case of low, medium and high priority events. Say you allow low and medium ISRs to be interruptable, so they can both be interrupted by the high priority event, but you don’t allow the high priority one to be interruptable. Then we have have problem whereby the medium priority event can then be interrupted by the low priority event.
Some microprocessors solve this by having interrupts whose priority can be assigned. On some, this priority is only effective in resolving which of two simultaneous events is handled first. With others, an interruptable ISR can only be interrupted by a higher priority ISR. This latter case can be very helpful for designing a system with good real-time response. However, your microprocessor doesn’t have to have this capability to give it good and repeatable real-time response.
For the sake of this discussion, let us use the term background processing to mean “behind the scenes work” like fetching or sending data, and foreground to mean the main software algorithms we are trying to execute to realize our intelligent functionality.
The idea with this approach is to let your ISRs do the background work and to keep them really really short. This is the interrupt or background context. The foreground context, where the real work is done, is handled by a scheduler, whose job is to start a new task if it is higher in priority than current foreground work. The scheduler is in general called by ISRs, but can also be called by foreground tasks using a software interrupt. As a result, the scheduler runs a lot and an efficient design is therefore very important. In addition to the scheduler, we can also run foreground work in the idle loop, where attention flags are used to initiate the task. However multiple tasks assigned to idle loop processing can only be done in a round-robin fashion with no regard to priority.
In the next post I will describe the scheduler and its operation in more detail…see you then!
© 2005-2015 Precision Technology Ltd All rights reserved