SPIN Task-Switching for us Prop Noobies
eldonb46
Posts: 70
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:
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:
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>
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:
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 - <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
--
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.
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 ForeverTo 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 // 10Unless 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 / 5For 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
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