Shop OBEX P1 Docs P2 Docs Learn Events
SPIN Task-Switching for us Prop Noobies — Parallax Forums

SPIN Task-Switching for us Prop Noobies

eldonb46eldonb46 Posts: 70
edited 2012-09-12 03:42 in Propeller 1
This is for Prop Noobies like myself, that want to enjoy Multi-Tasking Spin Programming.

There may be nothing NEW here, but it is all in one place.

One of the programming techniques that I have used in other programming environments, provide Low Resolution Multi-Tasking for an application. The combination of "negative true logic" and the "modulus function" makes the Task Switching Simple.

Low Resolution Multi-Tasking can be used to:
  • Update Displays
  • Set LED Indicators
  • Read Knobs, Push Buttons
  • Read or Set the Real Time Clock (RTC)
  • etc, etc.
The technique is "Low Resolution" because it is implemented in application program space, where programmer written subroutines control the granularity.

Low Resolution Multi-Tasking is not appropriate for real-time programming or other high speed event driven program tasks, use another COG for that effort.

This example is written in SPIN, but the strategy is applicable to any programming language. For this example generic tasks are called as; Task1, Task2, etc:
VAR
Long LoopCnt, CSync

PUB main 
  Initialize
  repeat
    LoopCnt+
    ifnot LoopCnt // 10
      Task1
    ifnot LoopCnt // 100
      Task2a
    ifnot LoopCnt // 100 - 12
      Task2b
    ifnot LoopCnt // 100 - 24
      Task2c
    ifnot LoopCnt // 15
      Task3
    ifnot LoopCnt // 125
      Task4
    if (cnt - CSync > CLKFREQ)
      CSync += CLKFREQ
      ClockTask
{repeate_end} 'Loop Forever
To be useful, the Tasks must be short and fast, without programmed "delays". Longer tasks need to be divided into sections (as explained below).

The main program's "repeat loop" spends its time looking for a Task to run.

The "modulus value" on the "ifnot" statements controls how often a Task is scheduled, a value of 10 will run the Task once for each 10 trips through the repeat loop. Adjust this "modulus value" as necessary for your application needs. The "ifnot" expression will evaluate to Zero (negative true logic) once for each "modulus value" loop trips, and therefore it will run the associated Task.

Example Syntax:
ifnot LoopCnt // <modulus value>
ifnot LoopCnt // 10
Unless there is a very good reason, it is better form, to stagger the modulus values, using non-multiple values (e.g., 13, 29, or 71, prime numbers are good). Task2 which is a multiple section task, should used the same base modulus value for each section (see next paragraph).

Task2 is divided into 3 sections (a, b and c), the decrement (i.e., -12 and -24) ensure that each will run in succession, but yet allow other Tasks to run between as desired. If a non-critical delay in needed within a Task, it can be implemented as a split task of two sections. Critical timing delays will require other arrangements.

The last "ClockTask" only runs once per second, and is typical used to display a Clock value, or run other time specific commands.

This Multi-Tasking technique is easy to program and fun to use.

Some Follow on, Load Average:

One of my interests, is to know what Percentage of time is the COG is running my Tasks, that is, I want to know the "COG Load Average". There are many opinions and methods to measure the load and task CPU usage. I prefer this simple and indirect method, which also takes into account all Task Switching overhead.

Adding a just few lines to the above program provides a simple, yet effective means to measure of COG Load Average.

The strategy is to monitor the LoopCnt for a known time interval, while running just a single standard Task with a known execution time (and low overhead). And then, count the times that tasks runs while other tasks are running. With that, you can calculate the effective COG Load Average.

Here is the implementation as an augmented SPIN program from above:
VAR 
Long LoopCnt
Long CSync
Long LoadAvg, FreeAvg, AvgWindow, PrevCnt

PUB main 
Initialize
repeat
  LoopCnt+
  pauseMS(2)      'A Standard known Tasks, used to calculate COG LoadAvg
  ifnot LoopCnt // 10
      Task1
  ifnot LoopCnt // 100
      Task2a
  ifnot LoopCnt // 100 - 12
      Task2b
  ifnot LoopCnt // 100 - 24
      Task2c
  ifnot LoopCnt // 15
      Task3
  ifnot LoopCnt // 125
      Task4
  if (cnt - CSync > CLKFREQ)
         CSync += CLKFREQ
         ClockTask

         'Compute and Display COG LoadAvg
          AvgWindow := ++AvgCnt <# 6        'Average over 6 Seconds
          FreeAvg := (FreeAvg * (AvgWindow-1) + (LoopCnt - PrevCnt)) / AvgWindow
          LoadAvg := 100 - FreeAvg / 5
          PrevCnt := LoopCnt
          DSPL.Str(string("COG Load Avg:"))
          DSPL.RjDec(LoadAvg, 4, " ")
          DSPL.Str(string("%"))
We know if only the "pauseMS(2)" task was running, the LoopCnt would be (approximately) 500 count for each second. That is, there are 500, 2ms time periods within 1 second. The COG LoadAvg would be 0%.

And if the loop and the "pauseMS(2)" task only had time to run once (or less) each second, because of other tasks consuming ALL of the available time within a Second, the LoadAvg would be 100%.

FreeAvg is really LoopCnt/Second averaged over a AvgWindow (in this case, a window of 6).

To calculate COG Load Average:
LoadAvg := 100 - FreeAvg / 5
For Example:
LoadAvg := 100 - <500> / 5 . . . . . => 0%
LoadAvg := 100 - <250> / 5 . . . . . =>50%
LoadAvg := 100 - <0> / 5 . . . . . =>100%

With the COG LoadAvg known, additional Tasks can be added, or the originals adjusted, to provide additional functionality with confidence that it will work as expected.

I use the above strategy to provide Low Resolution Multi-Tasking and Load Insight of my SPIN programming efforts.

I hope this is useful to other Prop Noobies, like myself.

Eldon

--

Comments

  • Christof Eb.Christof Eb. Posts: 1,237
    edited 2012-09-12 03:42
    Hi Eldon,
    this kind of simple multitasking is very useful, in my opinion. I have used similar things often. Therefore I would recommend that you might put it into OBEX, where it would be better available for longer access. In the forum it will be "buried" soon....
    Christof
Sign In or Register to comment.