a Cooperative Scheduler Arduino style

XCore Project reviews, ideas, videos and proposals.
User avatar
fabriceo
Experienced Member
Posts: 119
Joined: Mon Jan 08, 2018 4:14 pm

a Cooperative Scheduler Arduino style

Post by fabriceo »

Hi

I’ve developed the SCoop library for the Arduino platform some years ago here:
https://forum.arduino.cc/index.php?topic=137801.0
and I was missing an easy Cooperative scheduler to execute multiple cpp tasks on XMOS.
The intend is for me to manage some sort of front-panel and I/O within the USB application, in an Arduino style.

Here is a xc demo application using XCScheduler.cpp library file providing a Cooperative scheduler solution, in addition and compatible with the XCore multitasking inherent capability of the XMOS architecture; it uses Setjump/longjump and is 80% based on the original code ad smart approach of from Mikael Patel here: https://github.com/mikaelpatel/Arduino-Scheduler
All credits to him for this very smart approach.

A main.xc program declares 2 tasks in a par statement for 2 cores.
One core is just printing a message every 5 seconds using a blocking delay function;
The other core is
- initializing the XCScheduler with a certain amount of stack space for all the cpp tasks
- launching the initialization of multiple tasks written in cpp
- allocating a specific stack space for each of them
- orchestrating and monitoring a simple context switch between the task with a round robin approach.
- This is done as a default branch of a select statement.
Each cpp task is blocking and just yielding to the other tasks when cpu is not needed.
A sleepMs function provides a simple delay(ms) with embedded yield call.

A stronger and clean library will most probably be provided on my github in a next post here but wanted to share this interesting outcome as of now, hope it worth your reading.

Remark:
- A #pragma stackfunction is needed and the amount of memory reserved should be greater than the sum of all stack task (2500 words in this demo)
- With this implementation spread between xc and cpp, there is no any linker error due to stack information missing or due to name-mangling. All seems ok 
- The xcore task1 can just yield or execute a loop() in the cpp code, depending if we want a circularly or sequential approach. The second one gives the possibility to measure the time of a complete circularly cycle, which is of 2us in this example. Any firm blocking timer within a task will increase the time for a cycle.
- the demo uses only printf to the stdio rerouted with xscope so need a xtag

Don’t forget to change the platform .xn file to suit your dev board. This app is running on a DXIO board bought at diyinhk.com
app_demoScheduler.zip
You do not have the required permissions to view the files attached to this post.


User avatar
fabriceo
Experienced Member
Posts: 119
Joined: Mon Jan 08, 2018 4:14 pm

Post by fabriceo »

Hi,
there was 19 downloads as of october 8th, any feedback or suggestions ?
cheers
User avatar
fabriceo
Experienced Member
Posts: 119
Joined: Mon Jan 08, 2018 4:14 pm

Post by fabriceo »

Hi,
the scheduler is hosted on my xmos GitHub project but havent had time to make examples, your welcome to fork and propose merge
https://github.com/fabriceo/XMOS
User avatar
fabriceo
Experienced Member
Posts: 119
Joined: Mon Jan 08, 2018 4:14 pm

Post by fabriceo »

Hello,
I ve proposed a cooperative scheduler some years ago based on a cpp library made by a pear.
But I was not fully satisfied with it so I ve used some spare time to elaborate another one. (it is like sudoku, sometime you just need that).
Very simple (2 functions) and very easy to use.

just include "xcscheduler.h"
and then create a task with a macro, and then just call yield() as mush as possible from the main XC task and from the created tasks.

The example given is creating 100 tasks, each calls for a random number and increments an index in an array if the number correspond to the task number. Process stops after 5000 counts per task so around 50millions random number.

in this version, tasks are allocated with malloc() and will be free()) if they end/return, but the preferred behavior is that a task should be an infinite loop to avoid heap fragmentation of course. Stack size needs is automatically extracted from compiler output (nstackwords).

The yield() code is written in assembly and very easy to read if you want to look at it.
There are 2 options, either the task switch always come back to the main XC thread before moving to next one, ore it goes through the whole list (round robin style) (default, can be changed by commenting line 49 in .s file)

the library maintains a list of cooperative tasks per logical core, so each core can yield and spread its time to its own tasks without impacting other tasks;

The pro & cons of a cooperative scheduler is that the program switch/yield to the other task under the program control and not on a timer slice priority based like in any preemptive RTOS. This is an easy way to create multiple independent task in a program to treat multiple context like for example a serial protocol, a rotary encoder and front panel switched, an IR remote input or a menu logic on a display, instead of having multiple re-entrant code launched from a single select. might make things easier than a complex state machine. Also parallel usage is not anymore a problem and you almost never need to "lock" a piece of code as no other task will enter in it unless a yield is present.

some helpers also provided to manage timers and test channel data presence.

essentially for fun and experimentation with an augmented XMOS :)

fabriceo
You do not have the required permissions to view the files attached to this post.
User avatar
Ross
XCore Expert
Posts: 944
Joined: Thu Dec 10, 2009 9:20 pm
Location: Bristol, UK

Post by Ross »

Thanks for sharing! Is this on GitHub somewhere?
User avatar
fabriceo
Experienced Member
Posts: 119
Joined: Mon Jan 08, 2018 4:14 pm

Post by fabriceo »