220 likes | 458 Views
Chapter 13 Porting uC/OS-II. A Processor can run uC/OS-II if it satisfies the following general requirement You must have a C compiler for the processor and the C compiler must be able to produce reentrant code. You must be able to disable and enable interrupts from C.
E N D
A Processor can run uC/OS-II if it satisfies the following general requirement • You must have a C compiler for the processor and the C compiler must be able to produce reentrant code. • You must be able to disable and enable interrupts from C. • The processor must support interrupts and you need to provide an interrupt that occurs at regular intervals (typically between 10 to 100 Hz). • The processor must support a hardware stack, and the processor must be able to store a fair amount of data on the stack (possibly many Kbytes). • The processor must have instructions to load and store the stack pointer and other CPU registers either on the stack or in memory.
uC/OS-II Hardware/Software Architecture u u Setting the value of 2 #define constants (OS_CPU.H) Declaring 11 data types (OS_CPU.H) Declaring 2 #define macros (OS_CPU.H) Writing 10 simple functions in C (OS_CPU_C.C) Writing 4 assembly language functions (OS_CPU_A.ASM) u
OS_CPU.H #ifdef OS_CPU_GLOBALS #define OS_CPU_EXT #else #define OS_CPU_EXT extern #endif /* ********************************************************************************************************* * DATA TYPES * (Compiler Specific) ********************************************************************************************************* */ typedef unsigned char BOOLEAN; typedef unsigned char INT8U; /* Unsigned 8 bit quantity */ (1) typedef signed char INT8S; /* Signed 8 bit quantity */ typedef unsigned int INT16U; /* Unsigned 16 bit quantity */ typedef signed int INT16S; /* Signed 16 bit quantity */ typedef unsigned long INT32U; /* Unsigned 32 bit quantity */ typedef signed long INT32S; /* Signed 32 bit quantity */ typedef float FP32; /* Single precision floating point */ (2) typedef double FP64; /* Double precision floating point */ typedef unsigned int OS_STK; /* Each stack entry is 16-bit wide */ /* ********************************************************************************************************* * Processor Specifics ********************************************************************************************************* */ #define OS_ENTER_CRITICAL() ??? /* Disable interrupts */ (3) #define OS_EXIT_CRITICAL() ??? /* Enable interrupts */ #define OS_STK_GROWTH 1 /* Define stack growth: 1 = Down, 0 = Up */ (4) 1 #define #define OS_TASK_SW() ??? (5) 10 data types
OS_Enter_Critical() and OS_Exit_Critical() • Be careful to use these two function • Your application will crash if you disable interrupts before calling a uC/OS-II service, such as OSTimeDly. • The task is suspended until time expires, but because interrupts are disabled • Method 1 • If you call the uC/OS-II function with interrupts disabled, on return from uC/OS-II, interrupts would be enabled • Total required four cycles #define OS_ENTER_CRITICAL() asm CLI #define OS_EXIT_CRITICAL() asm STI • Method 2 • OS_enter_Critical() consumes 12 clock cycles • OS_Exit_Critical() use up to 8 clock cycles #define OS_ENTER_CRITICAL() asm PUSHF; CLI #define OS_EXIT_CRITICAL() asm POPF
13.04 OS_CPU_C.C A uC/OS-II port requires that you write 10 fairly simple C functions • OSTaskStkInit() • OSTaskCreateHook(): called by OS_TCBInit() whenever a task is created • OSTaskDelHook(): called by OSTaskDel() after removing the task from either the ready list or a wait list. • OSTaskSwHook(): called whenever a task switch occurs. • OSTaskIdleHook(): called by OS_TaskIdel() for bring the CPU into a low-power mode. The CPU exits low-power mode when it receives an interrupt. • OSTaskStatHook(): called once every second by OSTaskStat() • OSTimeTickHook(): called by OSTimetick() at every system tick. • OSInitHookBegin(): called immediately upon entering OSInit() • OSinitHookEnd(): called at the end of OSInit(). • OSTCBinitHook(): called by OS_TCBInit() immediately before it calls OSTaskCreateHook(). • The only necessary functions is OSTaskStkInit()
OSTaskStkInit() • This function is called by OSTaskCreate() and OSTaskCreateExt() to initialize the stack frame of a task • The stack looks as if an interrupt has just occurred and all the processor registers have been pushed onto that stack void MyTask (void *pdata) { /* Do something with argument ‘pdata’ */ for (;;) { /* Task code */ } } • OS_STK *OSTaskStkInit() (void (*task)(void *pd), • void *pdata, • OS_STK *ptos, • INIT16U opt); • { • simulate call to function with an argument (i.e. pdata); • Simulate ISR vector; • Setup stack frame to contain desired initial values of all registers; • Return new top-of-stack pointer to caller; • } • If I were to call MyTask() from another function, the C compiler might push the argument onto the stack followed by the return address of the function calling MyTask()
Context Switch • In uC/OS-II, the stack frame for a ready task always looks as if an interrupt has just occurred and all processor registers were saved onto to it • Make a ready task to run is to restore all processor registers from the task’s stack and execute a return from interrupt • OS_TASk_SW() is a macro which is always called from task-level code. The OS_TASK_SW() must to simulate an interrupt It makes a software interrupt (asm INT uCOS) and the interrupt vector points to the OSCtxSw() which is an assembly program. uCOS=80h • OSIntExit() is used to perform a context switch when an ISR makes a higher priority task ready for execution • OS_CPU_A.ASM • OSStartHighRdy() • OSCtxSw() • OSIntCtxSw() • OSTickISR() Four assembly language functions
OSStartHighRdy() • OSStartHighRdy() is called by OSStart() to start the highest priority task ready to run • Before you can call OSStart(), you must have created at least one of you tasks. Void OSStartHighRdy(void) { call user definable OSTaskSwHook() Get the stack pointer of the task to resume: stack pointer = OSTCBHighRdy->OSTCBStkPtr; OSRunning = True; Restore all processor registers from the new task’s stack; Execute a return from interrupt instruction: }
A task-level context switch is accomplished by issuing a software-interrupt instruction or, depending on the processor, executing a TRAP instruction. • The interrupt service routine, trap, or exception handler must vector to OSCtxSw(). • When the current task calls a service provided by uC/OS-II, which causes a higher priority task to be ready to run • At the end of the service call, uC/OS-II calls OS_Sched() • OS_Sched() loads the address of the highest priority task into OSTCBHighRdy and then calls OS_TASK_SW() to executes the software interrupt. void OSSched (void) { INT8U y; OS_ENTER_CRITICAL(); if ((OSLockNesting | OSIntNesting) == 0) { (1) y = OSUnMapTbl[OSRdyGrp]; (2) OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]); (2) if (OSPrioHighRdy != OSPrioCur) { (3) OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; (4) OSCtxSwCtr++; (5) OS_TASK_SW(); #define OS_TASK_SW() asm INT uCOS (6) } } OS_EXIT_CRITICAL(); } The ISR is OSCtxSw()
The interrupt vector of INT uCOS points to the OSCtxSw() written by assembly void OSCtxSw(void) { Save processor registers; Save the current task’s stack pointer into the current task’s OS_TCB: OSTCBCur->OSTCBStkPtr = Stack pointer; Call user definable OSTaskSwHook(); OSTCBCur = OSTCBHighRdy; OSPrioCur = OSPrioHighRdy; Get the stack pointer of the task to resume: Stack pointer = OSTCBHighRdy->OSTCBStkPtr; Restore all processor registers from the new task’s stack; Execute a return from interrupt instruction; }
OSTickISR() • uC/OS-II requires you to provide a periodic time source to keep track of time delays and timeouts • You must enable ticker interrupts after multitasking has started, that is after calling OSStart(). • if the ticker interrupts are enable before calling OSStart(), the system may crash • You can and should initialize and tick interrupts in the first task that executes following a call to OSStart() • Incorrect place to start the tick interrupt void main(void) { . . OSInit(); /* Initialize µC/OS-II */ . . /* Application initialization code ... */ /* ... Create at least on task by calling OSTaskCreate() */ . . Enable TICKER interrupts; /* DO NOT DO THIS HERE!!! */ . . OSStart(); /* Start multitasking */ }
Pseudocode for tick ISR void OSTickISR(void) { Save processor registers; Call OSIntEnter() or increment OSIntNesting; if (OSIntNexting == 1){ OSTCBCur->OSTCBStkPtr = Stack Pointer; } clear interrupting device; Call OSTimeTick(); Call OSIntExit(); Restore processor registers; Execute a return from interrupt instruction; } Record the SP to avoid SP adjusting in OSIntCtxSw()
void OSIntExit (void) { OS_ENTER_CRITICAL(); (1) if ((--OSIntNesting | OSLockNesting) == 0) { (2) OSIntExitY = OSUnMapTbl[OSRdyGrp]; (3) OSPrioHighRdy = (INT8U)((OSIntExitY << 3) + OSUnMapTbl[OSRdyTbl[OSIntExitY]]); if (OSPrioHighRdy != OSPrioCur) { OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; OSCtxSwCtr++; OSIntCtxSw(); //this is an assembly function (4) } } OS_EXIT_CRITICAL(); }
OSIntCtxSw() • Called by OSIntExit() to perform a context switch from an ISR • Because OSIntCtxSw() is called from an ISR, we assume that all the processor registers are properly saved onto the interrupted task’s stack. void OSIntCtxSw(void) { Adjust the stack pointer to remove calls to: OSIntExit(), OSIntCtxSw() and possibly the push of the processor status word; Save the current task’s stack pointer into the current task’s OS_TCB: OSTCBCur->OSTCBStkPtr = Stack pointer; Call user definable OSTaskSwHook(); OSTCBCur = OSTCBHighRdy; OSPrioCur = OSPrioHighRdy; Get the stack pointer of the task to resume: Stack pointer = OSTCBHighRdy->OSTCBStkPtr; Restore all processor registers from the new task’s stack; Execute a return from interrupt instruction; } These code is not required after v2.51
OSIntCtxSw() Prior to v2.51 needs to adjust the SP. Stack contents during an ISR
Testing a Port • You should test your port without application code • Four testing steps: • Ensure that the code compiles, assembles, and links • Verify OSTaskStkInit(0 and OSStartHighRdy() • Verify OSCtxSw() • Verify OSIntCtxSw() and OSTickISR()