Real-time kernels normally use a timer or a similar source of periodic interrupts to implement delays and other helpful services for multi-task applications. Although the code needed to take advantage of such services often involves little more than calls to kernel API functions, it seems that the periodic interrupts – or ticks, as they are commonly known – have become a major source of confusion, and even debate, amongst kernel users.
A common misperception amongst new kernel users is that the tick handler is the kernel’s task scheduler. Tick interrupts, in other words, are thought to be the only mechanism through which tasks can be made to run. The reality is that in preemptive, priority-based kernels, the code that services ticks is one of many pieces of code that can cause CPU control to pass from one task to another. In such kernels, it’s usually possible for any interrupt to cause a new task to run, as shown in Figure 1, involving a UART interrupt. Additionally, tasks themselves may have a number of ways to give up the CPU and enter a pending, or waiting, state.
The tick interrupt becomes necessary when a task requires the ability to control the amount of time that it’ll spend in the waiting state. The µC/OS-II and µC/OS-III operating systems, for example, provide a means to control through timeout parameters that specify a maximum wait time for non-tick events (such as the reception of a UART character), and through time-delay functions, like OSTimeDly().
Figure 2, based on µC/OS-II, highlights the tick interrupts’ role in implementing of OSTimeDly(). On the left side of the diagram, a relatively high-priority task invokes OSTimeDly() to produce a five-tick delay, causing the kernel to initialize a delay field in a data structure associated with the task, and to move the task out of the ready state to allow another task to run. The delay field is initialized to a value of 5, and, on each tick interrupt following the call to OSTimeDly(), the field is decremented. On the fifth interrupt after the call, the field reaches 0, and the kernel’s tick handler (which is part of the ISR in µC/OS-II but has its own task in µC/OS-III) makes the high-priority task ready to run again. The task is then given CPU control because its priority exceeds that of the task that was running when the fifth tick occurred.
Technically, it’s possible to write a multi-task application without timeouts and delay functions like OSTimeDly(). However, most multi-task systems encompass at least one task that could benefit from tick-based services. In upcoming posts, I’ll consider the ramifications of two important tick parameters – frequency and priority – on such systems.
Matt Gordon is a senior applications engineer at Micrium. He began his career developing device drivers, kernel ports, and example applications for Micrium’s customers. Drawing on this experience, Matt has written multiple articles on embedded software. He also heads Micrium’s training program and regularly teaches classes on real-time kernels and other embedded software components. Matt holds a bachelor’s degree in computer engineering from Georgia Institute of Technology, Georgia.
Read part 2: Frequency