Functions | |
| POSFROMEXT UVAR_t | p_pos_findbit (const UVAR_t bitfield) |
| POSFROMEXT UVAR_t | p_pos_findbit (const UVAR_t bitfield, UVAR_t rrOffset) |
| POSFROMEXT void | p_pos_initArch (void) |
| POSFROMEXT void | p_pos_initTask (POSTASK_t task, void *stackstart, POSTASKFUNC_t funcptr, void *funcarg) |
| POSFROMEXT VAR_t | p_pos_initTask (POSTASK_t task, UINT_t stacksize, POSTASKFUNC_t funcptr, void *funcarg) |
| POSFROMEXT void | p_pos_freeStack (POSTASK_t task) |
| POSFROMEXT VAR_t | p_pos_initTask (POSTASK_t task, POSTASKFUNC_t funcptr, void *funcarg) |
| POSFROMEXT void | p_pos_startFirstContext (void) |
| POSFROMEXT void | p_pos_softContextSwitch (void) |
| POSFROMEXT void | p_pos_intContextSwitch (void) |
| POSEXTERN void | c_pos_intEnter (void) |
| POSEXTERN void | c_pos_intExit (void) |
| POSEXTERN void | 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.
|
|
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.
|
|
|
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.
|
|
|
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:
PUSH ALL; // push all registers to stack@n if (posInInterrupt_g == 0) saveStackptrToCurrentTaskEnv(); c_pos_intEnter(); c_pos_timerInterrupt(); c_pos_intExit(); PULL ALL; // pull all registers from stack@n RETI; // return from interrupt@n
|
|
||||||||||||
|
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.
|
|
|
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.
|
|
|
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.
|
|
|
Architecture port initialization. This function is called from the posInit function to initialize the architecture specific part of the operating system.
|
|
||||||||||||||||
|
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 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 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.
|
|
|
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.
|
|
|
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.
|
|
|
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.
|
1.4.6-NO