CORTOS: a mini real time core for the AVR
Introduction
- In order to be able to settle several things quasi parallel on a processor,
there must be some technique to share the processor among those tasks.
There are, in principle, two different techniques:
- Time slot dispatching:
The processor is assigned to each task, in turn, for a set period of time. - Cooperative multitasking:
Each task returns the control of the processor deliberately, so another task can gain control over the processor.
Both methods have pro and cons, but I do not want to go into more detail here.
As described in the following, CORTOS is a quite simple system,
based on the cooperative approach, and is suitable for the needs of embedded applications.
Overview of CORTOS
-
CORTOS administers cooperative tasks. These can have one of the following states:
- set_task_ready(TASK_ID): This task changes to the ready-state.
- isr_set_task_ready(TASK_ID): This task changes to ready-state. Use this call inside ISR (as long as global interrupts are disabled).
- set_task_blocked(TASK_ID): This task changes to the inactive-state.
Note: do not call this from the task to be blocked, since it would be superceded by the normal return value handling of this task. - A task can assign itself (by means of the return value) its desired future state:
O: the task is immediately ready again (i.e. it returned actually only for the sake of fair play.)
-1: the task becomes inactive
1 .. 20000: the task wants to sleep for the given time (in ticks).
Notes on the sleeping time:
This time is not counted starting from return time, but added to the last wake up time.
This ensures a certain rate for this task,
even if in the meantime other tasks were perhaps blocking the processor for some time.
inactive | This task does not want to have a computing time. |
sleeping | This task is waiting for something (may be for a delay or an event) it wants computing-time sometime in the future. |
bereit | This task wants to have processor time. |
The tasks are administered by CORTOS by using a list which contains the following parameters. The list index is the so-called TASK_ID.
Code address | The entry point address of the task |
State | A flag that indicates whether the task is ready or inactive. |
Wake up time | This holds the time when this task would like to be assigned the processor again. It is counted in system ticks as a signed-integer. |
For the state transfer between tasks, there are the following calls. Note that these are differentiated as to whether a call is used inside an interrupt routine (ISR), or is called by another task:
Details on CORTOS
Idle-Task
-
If no other task is ready, IDLE_TASK (is always ready) is called. This task tyically
call the sleep for the processor. This reduces power consumption. The system will wake up again at the next time slot.
Tick-Hook
- Routines, which are to be activated with each run of the core, are hooked
to the tick. This is suitable i.e. for keyboard scans.
Tick-Rate
- The major loop of CORTOS is typically run every few ms.
For this a timer interrupt is needed, which sends to the CORTOS core an event (the so called tick).
The timer interrupt also increments the system time,
in accordance with which it is decided whether a sleeping task is woken.
Producer Consumer, Fifos
-
To transfer messages between tasks fifos are used.
The task, which waits for a message, is called consumer.
If a task (consumer) should wait for an external event,
then it should wait at the appropriate fifo. The external event generates amn interrupt an this will send
a message to the fifo.
- The task examines whether there is something in the fifo: this is performed by a call cr_fifo_filled(fifo_id)
- If there is data present in the fifo, it is fetched with cr_fifo_get_nowait(fifo_id) and gets processed.
- If there is no data present in the fifo, the the task returns to CORTOS with -1 (=not ready)
- If a new event is entered into the fifo (with cr_fifo_put(fifo_id)), then the task is set ready again by fifo control. To designate the fifo to wake up the correct task, during init a call to cr_fifo_set_comsumer(fifo_id, TASK_ID) must be performed. This sets the consumertask of the fifo.
This takes place in the following way: