Main menu
IT Visions
Microprocessor
Task schedule
Architecture
Instruction set

Task Switching

The processor contains hardware support (instructions) for a simple, but very efficient operating system like the one in the ST20. In this way, it can perform task switching much faster and much more reliable than any software based solutions. The task scheduler may be disabled if the user wants to use another operating system.

There are two task priorities with each one timer queue - microsecond timers for high priority tasks and millisecond timers for low priority tasks. At any time, a task may be

  • active and executed,
  • active, but interrupted by a higher priority task,
  • in the high priority queue waiting to be executed,
  • in the low priority queue waiting to be executed,
  • inactive, waiting for an input on a channel,
  • inactive, waiting for a free output channel,
  • inactive high priority task, waiting in the usec. timer queue until time-out,
  • inactive low priority task, waiting in the msec. timer queue until time-out,
  • disabled because of an error like e.g. a newer empty data stack.

Only the active task consume processor time.

The task switching is based on instructions (system calls) rather than a time schedule, because this by far gives the best utilization of the system resources, but a supervisory system is build in, which switch the task if a given maximum execution time is exceeded. Task switching can only take place if the data stack is empty, so that it is not necessary to save it. This is the case no matter if the switch condition is coursed by an instruction or by the task scheduler (time out). If there is an error, so that the data stack do not get empty within a given amound of time after the switch condition, the task is disabled until next reset. The data stack pointer is common to all tasks and need not to be saved.

At task switching, the PC and the status register (SR) are saved on the general purpose stack which belongs to the active task so far, as if it had been an interrupt. The accumulator (A) and the address register stack (R0-R7) are also pushed on the stack. For the address registers this is done by means of 8 dummy pushes to R0.

Each task has its own workspace i memory, which holds two pointers to the workspace of the following tasks in the various queues, any timer value and the general purpose stack of that task. The stack grows in negative direction so that the positive addresses are free for any data area. If the task is not the active one, the workspace also contains a copy of the internal part of the general purpose stack and the displacement part of the stack pointer. Because the base address of the stack pointer is the same as the base address of the workspace it need not to be saved.

 
 
 
20
 
 
 
Data area
16 Timer value
12 Pointer to next timed task
8 Pointer to next task
4 Displacement part of the pointer to the general purpose stack.

 
 
 
Space for the internal part of the general purpose stack.
The size depends on the CPU type.
 
 
 
 
External part of general purpose stack

The task scheduler itself has a pointer to the workspace of the first and the last task in each queue and a pointer to the first task in each timer queue. These pointers are stored somewhere in the general purpose register area (R8 - R255). Because a call for task switching is always performed by an active task and the active task is the first one in the active queue, the task scheduler has a pointer to the present workspace and therefore knows where to save the various parts. In the same way, an active task always knows where the workspace is because it is addressed by the base address part of the pointer to the general purpose stack.


Task communication

Communication between tasks takes place over channels. Each channel can hold one message. A task reads a message by means of the In instruction. If the message is not ready, the task is descheduled until the message is present. A task writes a message to a channel by means of the Out instruction. If the channel is not empty, the task is descheduled until the channel is ready. Note that unlike most software based operating systems it is not possible to hold more than one message in each channel, but in most systems there is actually no need for more buffering. It has no sense to supply the data faster than the receiving task can keep up with. The task may just receive old obsolete data if more data are buffered. Besides, buffer overflow or lack of message-descriptions for a new message is one of the most common error sources in most operating systems.

The one-message channels may also be used for allocating system resources. When a task wants access to such a resource, it executes an Out instruction to a channel for that resource. Because a channel can only hold one message the next task trying to execute an Out instruction to the same channel will be put to sleep until the channel is ready. When the job is done, the task executes an In instruction to the channel and in this way frees the resource for the next task in the queue. To avoid that a high priority task interrupts a low priority task and uses its resource, the same resource must not be shared between high and low priority tasks.


Task timing

When a task wants to wait for a given amound of time it first reads the present value of the system timer, which is a 32 bit wrap-around counter. Then it calculates the timeout time by adding the wanted delay time and writes the value to the timer position in its workspace. After that it executes the Wait instruction. This instruction puts the task in the timer queue if the specified time is in the future. The task is put into the queue in such a position, that the first task in the queue always has the shortest time before timeout. In this way, the task scheduler only has to compare the first level with the actual time. This comparision is done contineously in hardware. When the specified time becomes less than the count of the system timer, the task is applied to the back of the active queue for that task priority.