Time management available in a traditional RTOS includes delaying a calling thread by timed blocking (delay()
) or blocking with a timeout on various kernel objects (e.g., semaphores or event flags). These blocking mechanisms are not useful in Active Object-based systems where blocking is not allowed. Instead, to be compatible with the Active Object model of computation, time management must be based on the event-driven paradigm in which every relevant for the system occurrence manifests itself as an event instance.
An event-driven framework like QP/C needs an event-driven time management mechanism based on Time Events (sometimes called timers). A Time Event is a UML term and denotes a point in time (at the specified time, the event occurs [UML 2.5]). A Time Event is said to be armed when it is actively timing out towards the specified time in the future. When the specified time arrives, the Time Event expires and QP/C Framework directly posts the Time Event to the event queue of the recipient Active Object. An expired Time Event armed originally for one-shot gets automatically disarmed. A Time Event armed for periodic expiration gets automatically re-armed to expire again in the specified time interval in the future. A disarmed Time Event is dormant and must be explicitly armed to time out in the future.
Most real-time systems, including traditional RTOSes and Active Object Frameworks like QP, require a periodic time source called the System Clock Tick to keep track of time delays, timeouts, and armed Time Events in case of the event-driven QP/C Framework. The System Clock Tick must call a special service in the QP/C Framework to periodically update all armed Time Events.
The System Clock Tick is typically a periodic interrupt (asynchronous with respect to the baseline code execution) that occurs at a predetermined rate, typically between 10Hz and 1000Hz. This rate establishes the basic time units as "clock ticks" and the resolution of Time Events as integer number of "clock ticks". The faster the tick rate, the finer the granularity of "clock ticks" and higher resolution of Time Events, but also the more overhead the time management implies.
Even though the resolution of Time Events is one "clock tick", it does not mean that the accuracy is also one "clock tick". Figure SRS-TE-JIT shows in a somewhat exaggerated manner that the Time Event delivery and eventual handling is always subject to jitter. The main sources of this jitter are the following variable delays:
[1]
Delay caused by the varying length of processing inside the System Clock Tick ISR
[2]
Delay caused by the varying length of processing inside the higher-priority Active Objects or other "naked" threads in the system (such processing might also be triggered by the system Clock Tick ISR)
[3]
Delay caused by the varying length of processing inside the recipient Active Object.
Generally the jitter in Time Event processing gets worse as the priority of the recipient Active Object gets lower. In heavily loaded systems, the jitter might even exceed one "clock tick" period. In particular, a Time Event armed for just one tick might expire almost immediately because the System Clock Tick is asynchronous with respect to Active Object execution. To guarantee at least one "clock tick" timeout, you need to arm a Time Event for two clock ticks. Note too that Time Events are generally not lost due to event queuing. This is in contrast to clock ticks of a traditional RTOS, which can be lost during periods of heavy loading.
Time management has a big impact on the power efficiency of the system. Specifically the need to periodically service Time Events (or various timeouts in a traditional RTOS kernel) has the unfortunate side effect of constantly interrupting the CPU, which means that the CPU can spend less time in the low-power sleep mode.
While a fixed System Clock Tick is very useful, it often interrupts the CPU regardless if real work needs to be done or not. To mitigate that problem, some real-time kernels use the low-power optimization called the "Tickless Mode" (a.k.a. "tick suppression" or "dynamic tick"). In this mode, instead of indiscriminately making the System Clock Tick expire with a fixed period, the kernel adjusts the clock ticks dynamically, as needed. Specifically, after each clock tick the kernel re-calculates the time for the next clock tick and then sets the clock tick interrupt for the earliest timeout it has to wait for. For example, if the shortest timeout the kernel has to attend to is 300 milliseconds into the future, then the clock interrupt will be set for 300 milliseconds.
This approach maximizes the amount of time the CPU can remain asleep, but requires the kernel to perform the additional work to calculate the dynamic tick intervals and to program them into the hardware. This additional bookkeeping adds complexity to the kernel, might be an additional source of jitter and, most importantly, extends the time CPU spends in the high-power active mode and thus eliminates some of the power gains the "Tickless Mode" was supposed to bring.
Also, the "Tickless Mode" requires a more capable hardware timer that must be able of being reprogrammed for every interrupt in a wide dynamic range and also must accurately keep track of the elapsed time to correct for the irregular (dynamic) tick intervals. Still, even with various precautions and corrections, "Tickless Mode" often causes a drift in the timing of the clock tick.
For the reasons just described, QP/C Framework does not support "Tickless Mode". Instead, QP/C Framework supports multiple clock tick rates as an alternative way of achieving similar goals (to avoid interrupting the CPU at higher rate than necessary.)
Support for multiple tick rates means that each Time Event instance in QP/C is associated with a particular clock tick rate. For example, TimeEvt0 instance might be associated with rate #0 of only 10Hz, while TimeEvt1 instance might be associated with rate #1 of 1000Hz. The higher tick rate #1 is needed only occasionally (only in certain modes of the system), which means that TimeEvt1 is armed only during these periods and otherwise is disarmed. This provides an opportunity to shut down the high tick rate #1 of 1000Hz when it is not needed, and QP/C Framework provides a method for finding out when there are no armed Time Events for any given rate. The high tick rate #1 can be re-started again only when some Time Events (like TimeEvt1) get armed again. In an extreme case, a system can shut down all clock tick rates until some external event (typically interrupt) wakes the system up.
"Shutting down" a clock rate might be implemented in various ways. For example, a hardware timer that generates an interrupt at that rate can be disabled. But it is also possible to generate multiple tick rates from one hardware timer. In that case, shutting down a clock rate might mean reprogramming that timer to generate interrupts at a different period. All of this is under control of QP/C Application.
From the point of view of QP/C Framework, the support for multiple static clock tick rates is significantly simpler than the "Tickless Mode", and essentially does not increase the complexity of the framework because the same code for the single tick rate can handle other tick rates the same way. Also, multiple static tick rates require much simpler hardware timers, which can be clocked specifically to the desired tick rate and don't need particularly wide dynamic range. For example, 16-bit timers or even 8-bit timers with or without prescalers are completely adequate. Yet the multiple clock rates can deliver similar low-power performance for the system, while keeping QP/C Framework much simpler and easier to certify than "Tickless" Kernels.
SRS_QP_TM_00 : QP/C Framework shall support Time Events. |
---|
Description Support for Time Events means that QP/C Framework shall provide a Time Event abstraction that behaves like other events in QP, but is additionally equipped with the notion of time passage. The time passage consists of discrete steps (clock ticks). |
Use Case The basic usage model of the Time Events is as follows. An Active Object allocates and initializes one or more Time Event instances. When the Active Object needs to arrange for a timeout, it arms one of its time events to expire either just once (one-shot) or periodically. Each Time Event times out independently from the others, so a QP/C Application can make multiple parallel timeout requests (from the same or different Active Objects). When QP/C Framework service associated with the armed Time Events detects that the appropriate moment has arrived, it inserts the expiring Time Event instance directly into the recipient's event queue using the default FIFO policy with event delivery guarantee (SRS_QP_EDM_00). The recipient Active Object then processes the Time Event instance just like any other event. |
Forward Traceability
|
SRS_QP_TM_10 : QP/C Framework shall support up to 15 clock tick rates. |
---|
Description Support for multiple clock tick rates means that QP/C Framework shall provide that many different ways of grouping Time Events and associating them with a given clock tick rate. The maximum number of clock tick rates supported by QP/C Framework shall be compile-time configurable with the maximum of 15. |
Forward Traceability |
SRS_QP_TM_11 : For every clock rate QP/C Framework shall provide a clock-tick processing operation that QP/C Application must call periodically to service the armed Time Events. |
---|
Description The clock-tick processing operation shall update all armed Time Events associated with a given tick rate. The clock-tick processing operation must be callable from the interrupt level and also from the thread level (e.g., from an Active Object). Also, clock-tick processing operations for different tick rates must be allowed to preempt each other (e.g., higher clock tick rates might be serviced from interrupts while others from threads). |
Use Case QP/C Framework can provide the clock-tick processing operation parameterized by the tick rate. For each tick rate, QP/C Application must then invoke the corresponding clock-tick processing operation at the right period either from a time-tick ISR or from the thread level. |
Forward Traceability
|
SRS_QP_TM_20 : QP/C Framework shall provide Time Event initialization. |
---|
Description The association between a Time Event and the event signal, recipient Active Object, and the tick rate shall be made only during initialization (e.g., via a constructor) and shall not be changed later during the lifetime of the Time Event. |
SRS_QP_TM_21 : QP/C Framework shall allow a Time Event to be armed both for one-shot and periodic expiry. |
---|
Description Arming an already armed Time Event shall be considered as a programming error and the QP/C Framework shall enter a safe state. When the Timeout Expires, it gets directly posted (using the default FIFO policy with event delivery guarantee) into the event queue of the recipient Active Object. After posting, a one-shot time event gets automatically disarmed while a periodic time event (interval != 0) is automatically re-armed. |
Forward Traceability
|
SRS_QP_TM_22 : QP/C Framework shall allow a Time Event to be explicitly disarmed. |
---|
Description A Time Event shall provide a service to disarm it. This service shall be available for Time Events armed for one-shot or periodic expiration. Disarming an already disarmed Time Event is fine and not considered an error. |
Use Case Due to the asynchronous nature of System Clock Tick, an Active Object sometimes cannot know whether a one-shot Time Event was automatically disarmed. Therefore the disarm operation shall return the status of the Time Event. The return of 'true' shall mean that the Time Event was still armed. The return of 'false' means that the Time Event was not truly disarmed because it has already expired. The 'false' return is only possible for one-shot Time Events that have been automatically disarmed upon expiration. In this case the 'false' return means that the Time Event has already been posted and should be expected in the recipient Active Object's event queue. |
Forward Traceability
|
SRS_QP_TM_23 : QP/C Framework shall allow a Time Event to be rearmed. |
---|
Description A Time Event shall provide a service to rearm it to expire in a specified number of clock ticks (of the associated tick rate). This service shall be available for Time Events armed for one-shot or periodic expiration. Rearming a Time Event that is disarmed is fine and not considered an error. |
Use Case Due to the asynchronous nature of System Clock Tick, an Active Object sometimes cannot know whether a one-shot Time Event was automatically rearmed. Therefore the rearm operation shall return the status of the Time Event. Return 'true' shall mean that the Time Event was running as it was re-armed. The 'false' return means that the Time Event was not truly rearmed because it has already expired. The 'false' return is only possible for one-shot Time Events that have been automatically disarmed upon expiration. In this case the 'false' return means that the Time Event has already been posted and should be expected in the Active Object's event queue. |
Forward Traceability
|
SRS_QP_TM_30 : QP/C Framework shall provide operation to check whether any Time Events are armed for a given tick rate. |
---|
Description The operation shall return 'true' if no Time Events are armed at the given tick rate and 'false' otherwise. Also, due to the asynchronous nature of system clock tick, the operation shall be designed to run inside a critical section thus preventing preemption. |
Forward Traceability
|
SRS_QP_TM_40 : QP/C Framework shall support low-power sleep modes. |
---|
Description QP/C Framework shall detect the idle condition of the system and provide a mechanism for QP/C Application to enter the desired sleep mode safely. "Entering a sleep mode safely" means that there should be no race conditions and situations, where the system would enter a sleep mode with some events present in the Active Object event queues and thus requiring processing. |
Forward Traceability |