sMulTi
  EmbLin
  xvTouch
  sMultiTA
  Rabbit Dlr
  Java Fun
  sMultiTA     by Luc Hermans
smultita at gmail.com

Advertisement:     Touch your DVD

Worlds smallest full functioning Multi Tasking system

by Luc Hermans,   smultita at gmail.com

     

where I work
Last update 15 Nov 2002
This document describes the worlds smallest, but full functioning, software extension for MultiTasking: sMultiTa

Content

1. Introduction

In the early days we build embedded systems based on a Z80 compatible processor. Because the software got more and more complex I wrote a simple multi tasking enhancement in C.
Later on the Z80 board was replaced by an embedded ix86 and I ported this stuff to DOS.
These days DOS is replaced by Linux and the multi tasking soft is also ported to Linux. (which may seem ridicule, because Linux has a perfect multiprocessing system, but this was the easiest way to get my application running on Linux )

The Z80 compiler did have an option (for debugging purpose) which inserts a RST instruction between every C source line. This RST instruction pointed to the task switcher cs(), and did switch to another task every 32 calls. In the DOS and Linux version I didn't find such options, so here the cs() call is put in every loop statement.

I did not choose for a timer interrupt switcher, like most others do, because then a switch can occur everywhere in the program. Also in the middle of a C statement where a variable is written. Then this variable contains partial old, partial new data. It may not be used/read by another task. So this requires, mostly complex inter tasking communication/synchronization.

This task switcher does not use, nor disable any interrupts. They are free for your hard realtime application. Task switching is done by software which makes inter tasking communication very easy. Even non reentrant system calls and global functions can be shared between tasks.

2. Advantages

  • Very very small; about 200 lines of source code.
  • Includes many multi tasking features; see Function list.
  • Multi platform; should be easy to reconfigure to others systems
    Only csdef.h to change and following requirements:
    • A C-compiler which uses a stack to store return addresses (are these not all ?).
    • An instruction or two to read/write the stack pointer.
    • Some knowledge of stack frames.
  • No use of interrupts; no interrupt blocking, interrupts free for your hard realtime things.
  • Includes Timers, Hardware and Software watchdog.
  • Easy inter tasking communications; all global variables are common.
  • Full control of task switching gives you efficient task managing functions.

3. Task switching technique

Every task has it's own stack area defined in a task structure something like:

  • Task stack pointer: pointing to tasks stack area
  • Task status, which can be:
    • ACTIVE: task currently running
    • SUSPEND: task in background, ready to run
    • WAIT: task delayed for some time
    • HALT: temporary stopped
    • CANCEL: not running at all
  • Task delay counter
  • Task priority
  • ...
Let's go switching now... Suppose task1 and task2 are already running and each have a proper stack frame stack1 resp stack2.
When task1 calls the switcher cs(), the return address of task1 is pushed on stack1. The task switcher additionally pushes all necessary registers on stack1, saves task1 stack pointer, sets task1 to SUSPEND, finds another task to run, which is task2 here, and sets task2 to ACTIVE.
Then the switcher loads task2's stack pointer and pops all registers from stack2. Next a return instruction will pop the return address of task2 into the program counter and task2 continues running where it has left off, until task2 calls the switcher ...
That's all for switching.

It's a bit more complicated to get a task running, or how to get a proper stack frame for this new task ?
This is done by the cs_run() function. Let's say task1 calls cs_run() to start task2. Then cs_run() pushes the same registers as cs() on stack1, saves task1's stack pointer and set it to SUSPEND.
This way task1 has a stack frame exactly the same as cs() should have done. Next task2's stack pointer is set to a new stack area (stack2) and set ACTIVE.
Instead of popping everything from the stack, cs_run() just calls the task2's start address. When task2 now calls cs(), cs() itself will build a proper stack frame for it and switch to the next task, which is task1.

The initialization of the task switcher is cs_init(). This function just sets all tasks suspended except the main task (task0) is set active. This main task calls cs_run() to start task1, task2 ... to start the new tasks. Except from this, all tasks have the same rights, there is no leading or controlling task.

This code is written for 99.97% in C (only 3 assembly instructions used). This is important because that's why this task switcher is not depending on the stack frame used by the compiler, processor or memory model, and can be used (with 00.03% modification) on many platforms.

4. Timers and Watchdogs

This software supports timers and watchdogs.
If a watchdog is set for a task, the task must call wd_set() before the watchdog timer has elapsed. Otherwise the external function cs_reboot() is called.
A hardware watchdog is also implemented. The switcher cs() will call the external cs_wdog() function regularly to re-trigger the hardware watchdog.

5. Task Priority

A simple task priority system is implemented.

6. Multi Tasking Function List

Initialize task switchervoid cs_init( )
Start a new taskvoid cs_run( task_nr, &task_function(void), ...)
Put a task in waitvoid cs_wait( nr_of_ticks )
Change tasks priorityvoid cs_priority( priority )
Cancel a task permanent     void cs_cancel( task_nr )
Halt a task temporaryvoid cs_halt( )
Continue a halted taskvoid cs_cont( )
Read a task statusint cs_status( task_nr )
Force a task switchcs_switch( )
Disable task switchingcs_disable( )
Enable task switchingcs_enable( )
Enable watchdogvoid wd_run( )
Re-trigger watchdogvoid wd_set( nr_of_ticks )
Disable watchdogvoid wd_stop( )
Start a timervoid tm_set( &timer )
Check timer elapsedint tm_chk( &timer, delay )

7. Programming Tips

  • A task is defined like any other functions void task( void ). It should never return from this function if you don't like crashes. Only the main task can return (to the OS).
  • Put a cs() instruction into every loop statement. (do, while, for ..)
  • Use a cs_wait() function if your task is waiting for user or device input (or output).
  • Protect your variables not shared with other tasks, by declaring them in the function (auto) or by using the keyword static.
  • Put every task in a different source file and link them together. Also your global variables (for inter tasking communication) and functions are best put in a separate source and header file.
  • If you call a library function, shared by more tasks, don't put a cs() call into this function unless these functions are reentrant.
  • Do not use blocking system calls, otherwise switching will block. Use non blocking system calls in a cs_wait() loop.

8. Download Demo

CSdemoR003.ZIP (Ver 0.03) is a port for Rabbit tested with Dynamic C Version 7.21SE
Optional preemptive multitasking is included (timerB interrupt switch). (Rabbit users: see also Download & Run)

Just click on csdemo.zip to download. This zip file is only 28 Kb but contains:

  • a stupid but full functioning demonstration
  • csdemo.exe executable for DOS users
  • csdemo binary for LINUX users
  • source files: cs.cpp, cs.h csdef.h and csdemo.cpp
  • GNU GPL Licence
  • this html page
Demo screen dump:
------- s M u l t i T a -----------
 software multitasking simple demo 
----------------------------------
Task0 start  STACK=0000FFCC
Task1 start  1
Task2 start  20120120120120120120120120120120120120120120120
Task0 wait   1212121212121212
Task2 wait   11111111
Task1 halt   
Task2 wakeup 2
Task0 wakeup 02020202020202020202020202020202020202020202020
Task1 cont   120120120120120120120120120120120120120120120
Task3 start  30123012301230123012301230123012
Task3 prioty 3012301201230120123012012301201230120123012012
Task3 cancel 0120120120120120120
TimerElapsed 120120120120120120120120120120120120120120120120
Task0 all done
-------------------------------

9. License

Copyright (C) 2002 Luc Hermans working at DoBiT nv

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.

If you make changes to this, or build an OS or a VJM based on this
please let me know smultita at gmail.com



Previous Next Contents