pico]OS
1.0.4
|
Functions | |
POSFROMEXT UVAR_t POSCALL | p_pos_findbit (const UVAR_t bitfield) |
POSFROMEXT UVAR_t POSCALL | p_pos_findbit (const UVAR_t bitfield, UVAR_t rrOffset) |
POSFROMEXT void POSCALL | p_pos_initArch (void) |
POSFROMEXT void POSCALL | p_pos_initTask (POSTASK_t task, void *stackstart, POSTASKFUNC_t funcptr, void *funcarg) |
POSFROMEXT VAR_t POSCALL | p_pos_initTask (POSTASK_t task, UINT_t stacksize, POSTASKFUNC_t funcptr, void *funcarg) |
POSFROMEXT void POSCALL | p_pos_freeStack (POSTASK_t task) |
POSFROMEXT void POSCALL | p_pos_lock (void) |
POSFROMEXT void POSCALL | p_pos_unlock (void) |
POSFROMEXT VAR_t POSCALL | p_pos_initTask (POSTASK_t task, POSTASKFUNC_t funcptr, void *funcarg) |
POSFROMEXT void POSCALL | p_pos_startFirstContext (void) |
POSFROMEXT void POSCALL | p_pos_softContextSwitch (void) |
POSFROMEXT void POSCALL | p_pos_intContextSwitch (void) |
POSFROMEXT void POSCALL | p_pos_intContextSwitchPending (void) |
POSEXTERN void POSCALL | c_pos_intEnter (void) |
POSEXTERN void POSCALL | c_pos_intExit (void) |
POSEXTERN void POSCALL | c_pos_intExitQuick (void) |
POSEXTERN void POSCALL | c_pos_timerInterrupt (void) |
The operating system can be easily ported to other architectures, it can be ported to very small 8 bit architectures with low memory and to 32 bit architectures with lots of memory. To keep the porting as simple as possible, there are only a couple of functions that must be adapted to the architecute. Before you start porting the operating system to your architecture, you must choose a stack management type. You have the choice between:
POSCFG_TASKSTACKTYPE = 0
The stack memory is provided by the user. This is the best choice for very small architectures with low memory.
POSCFG_TASKSTACKTYPE = 1
The stack memory is dynamically allocated by the architecture dependent code of the operating system. The size of the stack frame is variable and can be choosen by the user who creates the task. This is the best choice for big architectures with lots of memory.
POSCFG_TASKSTACKTYPE = 2
The stack memory is dynamically allocated by the architecture dependent code of the operating system. The size of the stack frame is fixed and can not be changed by the user. This may be an alternative to type 0, it is a little bit more user friendly.
Here is a list of the functions that are architecture specific and must be ported:
p_pos_initTask, p_pos_startFirstContext, p_pos_softContextSwitch, p_pos_intContextSwitch.
If you choose POSCFG_TASKSTACKTYPE = 2 or 3, you must also provide the function p_pos_freeStack.
If your application is critical in performance, you may also provide an assembler version of the function "findbit". There are two different function prototypes possible. The simple prototype for the standard scheduling scheme (POSCFG_ROUNDROBIN = 0) is
UVAR_t p_pos_findbit(const UVAR_t bitfield);
The prototype for a findbit function that supports round robin scheduling (POSCFG_ROUNDROBIN = 1) is
UVAR_t p_pos_findbit(const UVAR_t bitfield, UVAR_t rrOffset);
The function gets a bitfield as input, and returns the number of the right most set bit (that is the number of the first lsb that is set). If round robin is enabled, the function takes an offset as second parameter. The offset is the position where the function starts to search the first set bit. The function scans the bitfield always from right to left, starting with the bit denoted by the offset. The bitfield is seen as circle, when the rightmost bit is not set the function must continue scanning the leftmost bit (wrap around), so all bits of the field are scanned.
It is possible to implement the findbit mechanism as look up table. For this purpose you can define the macro FINDBIT. Please see the header file picoos.h (search for the word POSCFG_FBIT_USE_LUTABLE) and the source file fbit_gen.c for details.
Unfortunately, not the whole operating system can be written in C. The platform port must be written in assembly language. I tried to keep the assembly part of the RTOS as small as possible. But there are three assembly functions left, that are needed for doing the context switching:
The operating system requires also a timer interrupt that is used to cut the task execution time into slices. Hardware interrupts must comply with some conventions to be compatible to pico]OS. So the fourth thing you need to write in assember is a framework for hardware interrupts.
The diagram shows the assembler functions in logical structure. At the left side I have drawn a normal interrupt service routine for reference.
The context switching (multitasking) is done by simply swaping the stack frame when an interrupt service routine (eg. the timer interrupt) is left. But it must also be possible for a task to give of processing time without the need of an interrupt. This is done by the function p_pos_softContextSwitch at the right side in the diagram. Since this function is not called by a processor interrupt, it must build up an ISR compatible stack frame by itself. Note that the second part of this function is equal to the function p_pos_intContextSwitch, so the function must be terminated by an return-from-interrupt instruction, even if the function was called from a C-routine.
For completeness, the next diagram shows at its left side how the function p_pos_startFirstContext works. Again, this function looks like the lower part of the funtion p_pos_intContextSwitch in the diagram above. In the middle you can see how the timer interrupt routine must look like.
There is a special interrupt handling needed when interrupts are interruptable on your system. To prevent a deadlock situation (that is, an ISR would be called again and again until the stack flows over), a counting flag variable is exported by pico]OS: posInInterrupt_g. This variable contains the value zero if no interrupt is running yet. And only if no other interrupt is running, the ISR must save the stack pointer to the task environment structure where posCurrentTask_g points to. This behaviour is shown at the right side in the diagram above.
Note that interrupt service routines need some stack space to be able to do their work - in the discussed configuration every ISR would take some stack memory from the stack frame of the currently active task. But this may be a problem at platforms that are low on memory - it would be to expensive to increase every tasks stack frame by the count of bytes an ISR would need. In this case, you can set up a special stackframe that is only used by interrupt service routines. The diagram below shows the small changes to the ISRs discussed above. But attention - this method is only applicable on platforms where interrupts can not interrupt each other.
POSEXTERN void POSCALL c_pos_intEnter | ( | void | ) |
Interrupt control function. This function must be called from an interrupt service routine to show the operating system that an ISR is currently running. This function must be called first before other operating system functions can be called from within the ISR.
POSEXTERN void POSCALL c_pos_intExit | ( | void | ) |
Interrupt control function. This function must be called from an interrupt service routine to show the operating system that the ISR is going to complete its work and no operating system functions will be called any more from within the ISR.
POSEXTERN void POSCALL c_pos_intExitQuick | ( | void | ) |
Interrupt control function. Similar to c_pos_intExit, except that context switch is not performed. Instead, a call to p_pos_intContextSwitchQueue is performed if it is necessary to perform task scheduling. p_pos_intContextSwitchQueue should "queue" context switch to occur at later time.
This was needed for Arm Cortex-M cpus, which need to use PendSV exception for context switching.
POSEXTERN void POSCALL c_pos_timerInterrupt | ( | void | ) |
Timer interrupt control function. This function must be called periodically from within a timer interrupt service routine. The whole system timing is derived from this timer interrupt.
A timer ISR could look like this:
Bit finding function. This function is called by the operating system to find the first set bit in a bitfield. See the file fbit_gen.c for an example.
bitfield | This is the bitfield that shall be scanned. |
Bit finding function. This function is called by the operating system to find the first set bit in a bitfield. See the file fbit_gen.c for an example.
bitfield | This is the bitfield that shall be scanned. |
rrOffset | Offset into the bitfield. Scanning begins here. |
POSFROMEXT void POSCALL p_pos_freeStack | ( | POSTASK_t | task | ) |
Stack free function. This function is called by the operating system to free a stack frame that was set up by the function p_pos_initTask. See the available port source files for an example on how to write this function.
task | pointer to the task environment structure. |
Stack free function. This function is called by the operating system to free a stack frame that was set up by the function p_pos_initTask. See the available port source files for an example on how to write this function.
task | pointer to the task environment structure. |
POSFROMEXT void POSCALL p_pos_initArch | ( | void | ) |
Architecture port initialization. This function is called from the posInit function to initialize the architecture specific part of the operating system.
POSFROMEXT void POSCALL p_pos_initTask | ( | POSTASK_t | task, |
void * | stackstart, | ||
POSTASKFUNC_t | funcptr, | ||
void * | funcarg | ||
) |
Task initialization function. This function is called by the operating system to initialize the stack frame of a new task. See the available port source files for an example on how to write this function.
task | pointer to the task environment structure. |
stackstart | pointer to the start of the stack memory. |
funcptr | pointer to the first function that shall be executed by the new task. |
funcarg | argument that should be passed to the first function. |
POSFROMEXT VAR_t POSCALL p_pos_initTask | ( | POSTASK_t | task, |
UINT_t | stacksize, | ||
POSTASKFUNC_t | funcptr, | ||
void * | funcarg | ||
) |
Task initialization function. This function is called by the operating system to initialize the stack frame of a new task. See the available port source files for an example on how to write this function.
task | pointer to the task environment structure. |
stacksize | size of the stack memory for the new task. The stack memory may be allocated dynamically from within this function. |
funcptr | pointer to the first function that shall be executed by the new task. |
funcarg | argument that should be passed to the first function. |
POSFROMEXT VAR_t POSCALL p_pos_initTask | ( | POSTASK_t | task, |
POSTASKFUNC_t | funcptr, | ||
void * | funcarg | ||
) |
Task initialization function. This function is called by the operating system to initialize the stack frame of a new task. This function is responsible to allocate the stack memory and to store the pointer of the stack frame into the task environment. See the available port source files for an example on how to write this function.
task | pointer to the task environment structure. |
funcptr | pointer to the first function that shall be executed by the new task. |
funcarg | argument that should be passed to the first function. |
POSFROMEXT void POSCALL p_pos_intContextSwitch | ( | void | ) |
Context switch function. This function is called by the operating system to initiate a context switch from interrupt level. This function has then to switch the context variable and restore the new context from stack memory. See the available port source files for an example on how to write this function.
POSFROMEXT void POSCALL p_pos_intContextSwitchPending | ( | void | ) |
Context switch function. Called by c_pos_intExitQuick if task scheduling is needed. This function should queue context switch somehow, for example in Arm Cortex-M CPU:s a PendSV exception can be set pending.
POSFROMEXT void POSCALL p_pos_lock | ( | void | ) |
Port lock. In some really special cases it may be required that Pico]OS must acquire a mutex before it can call the functions p_pos_initTask and p_pos_freeStack. If you need such a mutex for your port, please define POSCFG_PORTMUTEX to 1. Then implement the both functions p_pos_lock and p_pos_unlock in your arch_c.c file.
POSFROMEXT void POSCALL p_pos_softContextSwitch | ( | void | ) |
Context switch function. This function is called by the operating system to initiate a software context switch. This function has then to save all volatile processor registers to stack memory, switch the context variable and restore the new context from stack memory. See the available port source files for an example on how to write this function.
POSFROMEXT void POSCALL p_pos_startFirstContext | ( | void | ) |
Context switch function. This function is called by the operating system to start the multitasking. The function has to restore the first context from stack memory. See the available port source files for an example on how to write this function.
POSFROMEXT void POSCALL p_pos_unlock | ( | void | ) |
Port unlock. Counterpart of function p_pos_lock.