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:
    inactiveThis task does not want to have a computing time.
    sleepingThis task is waiting for something (may be for a delay or an event) it wants computing-time sometime in the future.
    bereitThis task wants to have processor time.
    Each task has to return the processor after execution automatically, and its return-value indicates the desired future state for this task (see below).

    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 addressThe entry point address of the task
    StateA flag that indicates whether the task is ready or inactive.
    Wake up timeThis holds the time when this task would like to be assigned the processor again. It is counted in system ticks as a signed-integer.
    Since normally, in an embedded environment, all possible tasks are known, the CORTOS task-list is created statically at compile-time. Thus the code overhead for handling adding and removing elements from the list is eliminated. A drawback of this approach is that the complete list must be scanned on each task change. But, since in a embedded environment this list is typically short, this is acceptable.

    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:
  • 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.

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.
    This takes place in the following way:
  • 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.

Application