Application Notes
Holon 11 Monitors
Handling Interrupts
Using the Real Time Kernel
Memory Banks

Holon 11  Multi Tasking

Tasks

A task (or thread) is a part of the program that is running in parallel with other parts of the program. At any moment in time only one part is in control, but control passes rapidly from one task to the next and this gives the illusion that the tasks are all executing at the same time.The price for this magic is a little more complexity in the application. The real time kernel of Holon 11 has been carefully designed to provide the important features of a multi tasking system, yet to make the mechanism clear and easy to use. The real time kernel is included with the developer's version of Holon 11. The following examples are provided as an introduction and an illustration of using tasks in Holon.

A simple task

Here is a simple task routine, which keeps incrementing the memory location $100. You can use the code view of Holon 11 to see the task running. 

: TaskRoutine
       begin   1 $100 +!  Pause  again  ;

The word Pause is the task switcher, which passes control to the next task. Tasks pass control deliberately. In practice this simple scheduling is highly effective. 

 20 30 Task myTask  TaskRoutine

This defines the task myTask, gives it 20 byte of return stack and 30 byte of data stack area, and makes myTask control the program word TaskRoutine.A task only makes sense if there is at least one more task in the program. Otherwise the task is the program. Holon 11 also treats the main word as a task. The main word is the word that you call from the monitor or activate at cold start in the EPROM. The main word uses the main data and return stacks, and if it is the only "task" in the program, there is no task switching and you can forget about the tasking mechanism. But now we have a task and therefore we implement the main word as a task routine too. 

: MainRoutine
      begin   1 $102 +!  Pause  again  ;

We are ready to start the tasking machine.

: RunProgram
      InitTasking  init myTask  MainRoutine  ;

When you execute RunProgram and let the tasks run, the host shows a small square at the lower right corner and waits for the program to return control to the monitor. But the program remains in the task loops. Therefore, press the <Esc> key to regain control of the host. The host is still online because the monitor (PMON or EMON) is controlled by interrupt, and the interrupt routine runs in parallel with the program tasks. 

Now you can look into the running program, press <F6>. You should see heavy traffic in $100 and $102.


Channels

Tasks need to exchange data and to signal events. For example, one task may wait for a signal from another task. The simple solution is to define a global variable and set and read the variable from the tasks. One task could read the variable and call Pause as long as the variable contains zero. The other task would write a non-zero value to the variable in order to signal the event. Holon 11 packs this mechanism in the object type Channel and offers you a clearer syntax. The following example describes the use of channels.

Channel Message

: Receiver
     begin  waitfor Message  $100  !  again  ;

: Sender   
     begin  $100  @  send Message   1  $100  +!  again  ;

20 20 Task RTask  Receiver

: RunCTasks
     InitTasking  init RTask   Sender  ;

Notes

20 20 Task STask  Sender

: Main
      begin Pause again  ;

: RunCTasks
     InitTasking  init RTask  init STask   Main   ;


Timing

Tasks are fine for creating timed delays in a program. Without tasking the program is blocked for the delay time. Task delays let you create pulses, say for PWM, controlling step motors and much more. Only the delayed task is blocked while waiting. I assume here that there is a LED attached to PORTA bit 0 in the target. 

: LED-on
      1 PORTA c!   ;

: LED-off
       0 PORTA  c!   ;

: InitLED
      1 DDRA c!  ;

The following example makes the LED blink.

: Blinking
     
InitLED
      begin   LED-on   400 msec  
                 LED-off   800 msec  
      again  ;

20 20 Task BTask  Blinking

: BMain
     begin   1 $102 +!   Pause  again   ;

: RunBlinker
     InitTasking  init BTask  StartTimer  BMain   ;

Execute RunBlinker and let the tasks run. Watch location $102 to see the main task running while the word Blinking is delayed.

Timing mechanism

The timing is based on the real time interrupt (RTI) of the 68HC11. The RTI generates an interrupt every 4.1 ms, which is the granularity (tick) of our task timing. The word StartTimer enables the RTI.  The timer keeps running until you call StopTimer or reset the target system.The timing mechanism is activated by the word msec. The task is delayed by the given number of milliseconds. 

msec calculates the number of ticks and calls wait, which makes the current task sleep for this amount of ticks. The timer interrupt routine decrements the tick number in all waiting tasks. When the tick number is zero,  the task is active and runs as soon as the scheduler calls it again.

The task sleeps for the delay period. The word Pause is not needed. The word wait calls Pause and the scheduler skips the sleeping task. 


Task routines
A task routine is an infinite loop with one or more Pause statements. Pause is not needed, if the task uses channels or delays, the channel operations and the delay perform Pause.
Reusing task areas
Tasks are created at compile time, the task areas are persistent. A task can be reused with other task routines. If you plan to reuse a task, assign the task to a DOER (execution vector). Then set the DOER to the current task routine. To select a different task routine, stop the task, change the execution vector, start the task. See the example in Holon 11.
Tuning the stack areas
You can easily tune the size of the stack areas. The areas are initially filled with the pattern $77. Run the tasks for a while, then end the program and note the actually used amount of stack space. Don't look into the memory while the tasks are running. The stacks are then "polluted" with the CPU data that are saved by the SCI interrupt request (used in the monitor). 
End a running task in the monitor
In order to end the running task program in the target, execute any command, say, SWAP or NOOP. The command word replaces the previous program word, is executed and then returns control to the host. 
Task switch time

A task switch takes 40 µs (2 MHz system clock).


Debugging

You have everything under control. The real time kernel is fully defined in the target. Step through the task routines and also through the sequence of the task switch. The steps are displayed in the source text.