Zilch cooperative multi-tasking for Teensy 3.x/LC

Status
Not open for further replies.
For the KINETISL, you call task_swap(), and the ASM code assumes that the two arguments (current frame and next frame pointers) are in registers r0 and r1. For KINETISK, you do the swap entirely in ASM, including statements to move the current frame and next frame pointers into r0 and r1. Is it guaranteed that two pointer-type arguments will be in r0 and r1 when you call a function? Is that true regardless of whether or not the function defines the arguments as volatile?
Yes, ARM holds the first and second arguments in r0, r1 for function calls for the task_swap function, volatile would not change this.

Can you explain your choice to use PSP rather than MSP for tasks?
Arm has two stack pointers (PSP, MSP). The MSP 'main stack pointer' is normally used in 'Big Loop' programs like arduino. The MSP also is only used when an isr 'Handler Mode' is called. The PSP 'process stack pointer' is only used in 'Thread Mode' which would just be non-isr functions. This allows the tasks to have a separate stack pointer from any isr code. This is still experimental on my part though you could easily just use the MSP for everything (isr and non-isr). The reason would be that the system could still run "using the MSP" to safely restart or shutdown the Teensy if a stack overflow happens in one of the users tasks. This is not implemented yet though!

For LC, is there any reason you don't save the current frame in two steps rather than three? I tried the code below and it seems to be equivalent to the 3-step process in beta 2.0

"STMIA r0!,{r3-r7}" "\n\t" // Save r3,r4-r7 to r0! (currentframe->sp)
"MOV r2,r8" "\n\t" // move r8 into r2
"MOV r3,r9" "\n\t" // move r9 into r3
"MOV r4,sl" "\n\t" // move sl (r10) into r4
"MOV r5,fp" "\n\t" // move fp (r11) into r5
"MOV r6,ip" "\n\t" // move ip (r12) into r6
"MOV r7,lr" "\n\t" // move lr into r7
"STMIA r0!,{r2-r7}" "\n\t" // Save r8-r12,lr to r0! (currentframe->r8)
The current version does this in two chunks that I uploaded last night, just like you proposed above. The Teensy LC cannot directly load or store the 'high registers' so thats what you see in the context switch code. This could be done a bunch of different ways but this is the best I came up with so far that keeps the same 'stack_frame' structures as the Teensy 3.x. I'm trying to keep the Teensy LC and Teensy 3.x code as much as possible the same.

One thing that I'm interested in is you said you have code for mutexs, semaphores and such would you be willing to merge that into this library? If not its cool just those would be nice to have also.
 
One thing that I'm interested in is you said you have code for mutexs, semaphores and such would you be willing to merge that into this library? If not its cool just those would be nice to have also.

I think so. I'm using a minimum subset based on your pre-2.0 beta. All that remains is an array of tasks, two integers (ntasks, curtask), and functions os_init(), task_create(), task_start(), task_swap(), and yield(). All tasks use MSP only, task[0]'s stack is the system stack, and I've adapted your new KINETISL context switch. To that I have added timers and mailboxes for inter-task communication with timeouts. Let me review your latest code and see what it would take to adapt them for your new linked-list of tasks. Do you actually use the functions to pause/resume tasks, or are those for "future enhancements"?
 
All tasks use MSP only, task[0]'s stack is the system stack, and I've adapted your new KINETISL context switch.
I'm developing version now that the library spawns an internal 'kernel' task that uses MSP while all user tasks spawn using the PSP. There will be some defines to enable this kernel task. It will handle basic stack integrity checking and other things os related. This required new context switch code that looks terrible but the actual context switch performance is still pretty good.

Let me review your latest code and see what it would take to adapt them for your new linked-list of tasks.
I would like it to be a separate class that can be added in by the user.

Do you actually use the functions to pause/resume tasks, or are those for "future enhancements"?
These are still in development but will be used by the 'kernel' for errors on user tasks or the user can use them to pause or resume another user task.
 
I'm developing version now that the library spawns an internal 'kernel' task that uses MSP while all user tasks spawn using the PSP. There will be some defines to enable this kernel task. It will handle basic stack integrity checking and other things os related. This required new context switch code that looks terrible but the actual context switch performance is still pretty good.

Can you explain why you want to go in that direction? I'm looking for the simplest possible method of creating cooperative fibers. If I wanted more features, I would go to preemptive OS like FreeRTOS.

I would like it to be a separate class that can be added in by the user.

There will have to be some coupling via yield(), ntasks and curtask.
 
Can you explain why you want to go in that direction? I'm looking for the simplest possible method of creating cooperative fibers. If I wanted more features, I would go to preemptive OS like FreeRTOS.
It will have #defines to enable the 'kernel' task. If not defined (default) it will just be the bare bones scheduler without any bells or whistles.

There will have to be some coupling via yield(), ntasks and curtask.
Ya, it could inherit those from Zilch?
 
I created a github account and a repository called minios (https://github.com/joepasquariello/minios).

This version is just the timing and mailbox functions. To try it, create a sketch that uses the minios.cpp/h and INO files from the repository, plus your Zilch Beta 2.1 and TimerOne. You have to make 3 changes to Zilch Beta 2.1:

In Zilch.cpp:
- define variables: uint32_t ntasks=0, curtask=0;
- add to task_create(): ntasks = os.num_task;
- add to yield(): if (++curtask >= ntasks) curtask=0;
In Zilch.h
- declare variables: extern uint32_t ntasks, curtask;
 
I created a github account and a repository called minios (https://github.com/joepasquariello/minios).

This version is just the timing and mailbox functions. To try it, create a sketch that uses the minios.cpp/h and INO files from the repository, plus your Zilch Beta 2.1 and TimerOne. You have to make 3 changes to Zilch Beta 2.1:

In Zilch.cpp:
- define variables: uint32_t ntasks=0, curtask=0;
- add to task_create(): ntasks = os.num_task;
- add to yield(): if (++curtask >= ntasks) curtask=0;
In Zilch.h
- declare variables: extern uint32_t ntasks, curtask;
Sorry got on a house project yesterday I'll take a look at this tonight, still need to finalize some code for v2.2 then I'll try to add this in, thanks!
 
Sorry got on a house project yesterday I'll take a look at this tonight, still need to finalize some code for v2.2 then I'll try to add this in, thanks!

I'm wondering what to do with my "minimum subset" of Zilch. I think it's useful. Do you mind if I put it on my github as an example cooperative scheduler to go with minios?
 
Status
Not open for further replies.
Back
Top