Presentation is loading. Please wait.

Presentation is loading. Please wait.

uC/OS-II Real-Time Systems Concepts Kernel Structure Task Management

Similar presentations


Presentation on theme: "uC/OS-II Real-Time Systems Concepts Kernel Structure Task Management"— Presentation transcript:

0 Introduction to uC/OS-II

1 uC/OS-II Real-Time Systems Concepts Kernel Structure Task Management
Time Management Intertask Communication & Synchronization Memory Management 15:57 /35

2 Real-Time Systems Concepts
Multitasking Kernel Scheduling Mutual Exclusion Message Passing Interrupt 15:57 /35

3 Foreground/Background Systems
Interrupt Level Task Level 15:57 /35

4 Multitasking Multitasking Related issues
A process of scheduling and switching CPU between several tasks. Related issues Context Switch (Task Switch) Resource Sharing Critical Section 15:57 /35

5 Multitasking 15:57 /35

6 Task A task, or called a thread, a simple program that thinks it has the CPU all to itself. Each task is assigned a priority, its own set of CPU registers, and its own stack area . Each task typically is an infinite loop that can be in any one of five states. Ready, Running, Waiting, ISR, Dormant 15:57 /35

7 Task States 15:57 /35

8 Non-Preemptive Kernel
Kernel is the part of a multitasking system responsible for the management of tasks. Context switching is the fundamental service of a kernel. Non-Preemptive Kernel v.s. Preemptive Kernel 15:57 /35

9 Non-Preemptive Kernel
Cooperative multitasking A non-preemptive kernel allows each task to run until it voluntarily gives up control of the CPU. An ISR can make a higher priority task ready to run, but the ISR always returns to the interrupted task. Linux 2.4 is non-preemptive. Linux 2.6 is preemptive. 15:57 /35

10 Non-Preemptive Kernel
15:57 /35

11 Preemptive Kernel A preemptive kernel always executes the highest priority task that is ready to run. µC/OS-II and most commercial real-time kernels are preemptive. Much better response time. Should not use non-reentrant functions, unless the functions are mutual exclusive. 15:57 /35

12 Preemptive Kernel 15:57 /35

13 Function Reentrancy A reentrant function is a function that can be used by more than one task without fear of data corruption. Reentrant functions either use local variables or protected global variables. OS_ENTER_CRITICAL() OS_EXIT_CRITICAL() 15:57 /35

14 Function Reentrancy Non-Reentrant Function Reentrant Function
static int Temp; void swap(int *x, int *y) { Temp = *x; *x = *y; *y = Temp; } Reentrant Function void strcpy(char *dest, char *src) { while (*dest++ = *src++) { ; } *dest = NULL; } 15:57 /35

15 Scheduling Round-Robin Scheduling Task Priority Assignment
Tasks executed sequentially Task Priority Assignment Static priority Rate Monotonic (RM) Dynamic priority Earliest-Deadline First (EDF) Time-Derived Scheduling Pinwheel 15:57 /35

16 Round-Robin Kernel gives control to next task if the current task
has no work to do completes reaches the end of time-slice Not supported in uC/OS-II O(1) priority preemptive scheduling 15:57 /35

17 Priority Inversion Problem
15:57 /35

18 Priority Inheritance 15:57 /35

19 Mutual Exclusion Protected shared data of processes.
Exclusive access implementation Disabling and enabling interrupts Test-and-Set Disabling and enabling scheduler Semaphores Simple Semaphore Counting Semaphore Deadlock – set timeout for a semaphore 15:57 /35

20 Using Semaphore 15:57 /35

21 Synchronization Synchronization mechanism is used between tasks or task to ISR. Unilateral rendezvous Bilateral rendezvous 15:57 /35

22 Unilateral rendezvous
15:57 /35

23 Bilateral rendezvous 15:57 /35

24 Event Flags 15:57 /35

25 Message Passing Intertask Communication
Using global variables or sending messages Only communicate to ISR through global variables Tasks are not aware when the global variables is changed unless task polls the content. 15:57 /35

26 Message Mailboxes A Message Mailbox, also called a message exchange, is typically a pointer size variable. Operations Initial POST PEND necessary to wait for the message being deposited ACCEPT Acknowledgement 15:57 /35

27 Message Queues A message queue is used to send one or more messages to a task. A message queue is an array of message mailboxes. Generally, FIFO is used. µC/OS-II allows a task to get messages Last-In-First-Out (LIFO). 15:57 /35

28 Interrupt An interrupt is a hardware mechanism used to inform the CPU that an asynchronous event has occurred. Interrupt Latency Interrupt Response Interrupt Recovery 15:57 /35

29 Interrupt Nesting 15:57 /35

30 Interrupt Latency Disabling interrupts affects interrupt latency.
All real-time systems disable interrupts to manipulate critical sections of code. Re-enable interrupts when the critical section has executed. Interrupt latency = Maximum time to disable interrupts + Time to start the first instruction in ISR. 15:57 /35

31 Interrupt Response Interrupt Response means time between the reception of the interrupt and the start of the user code which will handle the interrupt. Interrupt response = Interrupt latency + Time to save CPU context The worst case for interrupt response is adopted. 15:57 /35

32 Interrupt Recovery Interrupt recovery is defined as the time required for the processor to return to the interrupted code. Interrupt recovery = Time to determine if a high priority task is ready + Time to restore the CPU context of the highest priority task + time of executing return-from-interrupt. 15:57 /35

33 Non-preemptive Kernel
15:57 /35

34 Preemptive Kernel 15:57 /35

35 Non-Maskable Interrupts (NMIs)
Service the most important time-critical ISR Can not be disabled Interrupt latency = Time to execution the longest instruction + Time to start execution the NMI ISR Interrupt response = Interrupt latency + Time to save CPU context Interrupt recovery = Time to restore CPU context + time of executing return-from-interrupt 15:57 /35

36 Clock Tick A periodical interrupt Be viewed as heartbeat
Application specific and is generally between 10ms and 200ms Faster timer causes higher overhead Delay problem should be considered in real-time kernel. 15:57 /35

37 Delaying a Task for 1 Tick
Tick Interrupt Tick ISR All higher priority tasks Delayed Task t1 t2 t3 20 mS (6 mS) (19 mS) (27 mS) Call to delay 1 tick (20 mS) 15:57 /35

38 Clock Tick Solve the problem
Increase the clock rate of your microprocessor. Increase the time between tick interrupts. Rearrange task priorities. Avoid using floating-point math. Get a compiler that performs better code optimization. Write time-critical code in assembly language. If possible, upgrade to a faster microprocessor in the same family. 15:57 /35

39 Memory Requirement Be careful to avoid large RAM requirement
Large local arrays Nested/Recursive function Interrupt nesting Stack used by libraries Too many function arguments 15:57 /35

40 Real-Time Kernels Real-time OS allows real-time application to be designed and expanded easily. The use of an RTOS simplifies the design process by splitting the application into separate tasks. Real-time kernel requires more ROM/RAM and 2 to 4 percent overhead. 15:57 /35

41 uC/OS-II Real-Time Systems Concepts Kernel Structure Task Management
Time Management Intertask Communication & Synchronization Memory Management 15:57 /35

42 Kernel Structure Task Control Blocks Ready List Task Scheduling
Interrupt under uC/OS-II 15:57 /35

43 Critical Sections Archive this by disabling interrupt OS_CPU.H
OS_ENTER_CRITICAL() OS_EXIT_CRITICAL() 15:57 /35

44 Tasks Up to 64 tasks Two tasks for system use(idle and statistic)
Priorities 0, 1, 2, 3, OS_LOWEST_PRIO-3, OS_LOWEST_PRIO-2, OS_LOWEST_PRIO-1, OS_LOWEST_PRIO for future use The lower the priority number, the higher the priority of the task. The task priority is also the task identifier. 15:57 /35

45 Task States 15:57 /35

46 Task Control Blocks 15:57 /35 typedef struct os_tcb {
OS_STK *OSTCBStkPtr;  #if OS_TASK_CREATE_EXT_EN void *OSTCBExtPtr; OS_STK *OSTCBStkBottom; INT32U OSTCBStkSize; INT16U OSTCBOpt; INT16U OSTCBId; #endif   struct os_tcb *OSTCBNext; struct os_tcb *OSTCBPrev; #if (OS_Q_EN && (OS_MAX_QS >= 2)) || OS_MBOX_EN || OS_SEM_EN OS_EVENT *OSTCBEventPtr;  #if (OS_Q_EN && (OS_MAX_QS >= 2)) || OS_MBOX_EN void *OSTCBMsg; INT16U OSTCBDly; INT8U OSTCBStat; INT8U OSTCBPrio; INT8U OSTCBX; INT8U OSTCBY; INT8U OSTCBBitX; INT8U OSTCBBitY; #if OS_TASK_DEL_EN BOOLEAN OSTCBDelReq; } OS_TCB; 15:57 /35

47 OS_TCB Lists TCBs store in OSTCBTbl[]
All TCBs are initialized and linked when uC/OS-II is initialized 15:57 /35

48 Ready List 15:57 /35

49 OSRdyGrp and OSRdyTbl[]
Bit 0 in OSRdyGrp is 1 when any bit in OSRdyTbl[0] is 1. Bit 1 in OSRdyGrp is 1 when any bit in OSRdyTbl[1] is 1. Bit 2 in OSRdyGrp is 1 when any bit in OSRdyTbl[2] is 1. Bit 3 in OSRdyGrp is 1 when any bit in OSRdyTbl[3] is 1. Bit 4 in OSRdyGrp is 1 when any bit in OSRdyTbl[4] is 1. Bit 5 in OSRdyGrp is 1 when any bit in OSRdyTbl[5] is 1. Bit 6 in OSRdyGrp is 1 when any bit in OSRdyTbl[6] is 1. Bit 7 in OSRdyGrp is 1 when any bit in OSRdyTbl[7] is 1. 15:57 /35

50 Making a Task Ready to Run
OSRdyGrp |= OSMapTbl[prio >> 3]; OSRdyTbl[prio >> 3] |= OSMapTbl[prio & 0x07]; Index Bit mask (Binary) 1 2 3 4 5 6 7 Task’s Priority Y X 15:57 /35

51 Removing a Task from the Ready List
if ((OSRdyTbl[prio >> 3] &= ~OSMapTbl[prio & 0x07]) == 0) OSRdyGrp &= ~OSMapTbl[prio >> 3]; 15:57 /35

52 Finding the Highest Priority Task
OSUnMapTbl[] y = OSUnMapTbl[OSRdyGrp]; x = OSUnMapTbl[OSRdyTbl[y]]; prio = (y << 3) + x; INT8U const OSUnMapTbl[] = { 0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 }; 15:57 /35

53 Task Scheduler 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(); (6) } OS_EXIT_CRITICAL(); 15:57 /35

54 Locking The Scheduler void OSSchedLock (void) { if (OSRunning == TRUE) { OS_ENTER_CRITICAL(); OSLockNesting++; OS_EXIT_CRITICAL(); } } 15:57 /35

55 Unlocking The Scheduler
void OSSchedUnlock (void) { if (OSRunning == TRUE) { OS_ENTER_CRITICAL(); if (OSLockNesting > 0) { OSLockNesting--; if ((OSLockNesting | OSIntNesting) == 0) { (1) OS_EXIT_CRITICAL(); OSSched(); (2) } else { OS_EXIT_CRITICAL(); } } else { OS_EXIT_CRITICAL(); } } } 15:57 /35

56 Idle Task OSTaskIdle() Lowest priority, OS_LOWEST_PRIO
void OSTaskIdle (void *pdata) { pdata = pdata; for (;;) { OS_ENTER_CRITICAL(); OSIdleCtr++; OS_EXIT_CRITICAL(); } } 15:57 /35

57 Statistics Task 15:57 /35

58 Initializing the Statistic Task
void main (void) { OSInit(); /* Initialize uC/OS-II (1)*/ /* Install uC/OS-II's context switch vector */ /* Create your startup task (for sake of discussion, TaskStart()) (2)*/ OSStart(); /* Start multitasking (3)*/ } void TaskStart (void *pdata) { /* Install and initialize µC/OS-II’s ticker (4)*/ OSStatInit(); /* Initialize statistics task (5)*/ /* Create your application task(s) */ for (;;) { /* Code for TaskStart() goes here! */ } } 15:57 /35

59 Initializing the Statistic Task
void OSStatInit (void) { OSTimeDly(2); OS_ENTER_CRITICAL(); OSIdleCtr = 0L; OS_EXIT_CRITICAL(); OSTimeDly(OS_TICKS_PER_SEC); OSIdleCtrMax = OSIdleCtr; OSStatRdy = TRUE; } 15:57 /35

60 Statistics Task 15:57 /35 void OSTaskStat (void *pdata) { INT32U run;
INT8S usage; pdata = pdata; while (OSStatRdy == FALSE) { (1) OSTimeDly(2 * OS_TICKS_PER_SEC); } for (;;) { OS_ENTER_CRITICAL(); OSIdleCtrRun = OSIdleCtr; run = OSIdleCtr; OSIdleCtr = 0L; OS_EXIT_CRITICAL(); if (OSIdleCtrMax > 0L) { usage = (INT8S)(100L - 100L * run / OSIdleCtrMax); (2) if (usage > 100) { OSCPUUsage = 100; } else if (usage < 0) { OSCPUUsage = 0; } else { OSCPUUsage = usage; OSTaskStatHook(); (3) OSTimeDly(OS_TICKS_PER_SEC); 15:57 /35

61 Interrupts under uC/OS-II
Be written in assembly language or C code with in-line assembly. YourISR: Save all CPU registers; (1) Call OSIntEnter() or, increment OSIntNesting directly; (2) Execute user code to service ISR; (3) Call OSIntExit(); (4) Restore all CPU registers; (5) Execute a return from interrupt instruction; 15:57 /35

62 Servicing an Interrupt
15:57 /35

63 Beginning an ISR void OSIntEnter (void) { OS_ENTER_CRITICAL(); OSIntNesting++; OS_EXIT_CRITICAL(); } 15:57 /35

64 Leaving an ISR 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(); (4) } OS_EXIT_CRITICAL(); 15:57 /35

65 Pseudo Code for Tick ISR
void OSTickISR(void) { Save processor registers; Call OSIntEnter() or increment OSIntNesting; Call OSTimeTick(); Call OSIntExit(); Restore processor registers; Execute a return from interrupt instruction; } 15:57 /35

66 Service a Tick void OSTimeTick (void) { OS_TCB *ptcb; OSTimeTickHook(); (1) ptcb = OSTCBList; (2) while (ptcb->OSTCBPrio != OS_IDLE_PRIO) { (3) OS_ENTER_CRITICAL(); if (ptcb->OSTCBDly != 0) { if (--ptcb->OSTCBDly == 0) { if (!(ptcb->OSTCBStat & OS_STAT_SUSPEND)) { (5) OSRdyGrp |= ptcb->OSTCBBitY; (4) OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX; } else { ptcb->OSTCBDly = 1; } } } ptcb = ptcb->OSTCBNext; OS_EXIT_CRITICAL(); } OS_ENTER_CRITICAL(); (7) OSTime++; (6) OS_EXIT_CRITICAL(); } 15:57 /35

67 uC/OS-II Initialization
Initialize variables and data structures Create the idle task OSTaskIdle() Create the statistic task OSTaskStat() 15:57 /35

68 After calling OSInit()
15:57 /35

69 Starting uC/OS-II void main (void) { OSInit(); /* Initialize uC/OS-II */ Create at least 1 task using either OSTaskCreate() or OSTaskCreateExt(); OSStart(); /* Start multitasking! OSStart() will not return */ } void OSStart (void) { INT8U y; INT8U x;   if (OSRunning == FALSE) { y = OSUnMapTbl[OSRdyGrp]; x = OSUnMapTbl[OSRdyTbl[y]]; OSPrioHighRdy = (INT8U)((y << 3) + x); OSPrioCur = OSPrioHighRdy; OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; (1) OSTCBCur = OSTCBHighRdy; OSStartHighRdy(); (2) } } 15:57 /35

70 uC/OS-II Real-Time Systems Concepts Kernel Structure Task Management
Time Management Intertask Communication & Synchronization Memory Management 15:57 /35

71 Task Management Task prototype Create a task Task stacks
Stack checking Delete a task Change a task’s priority Suspend a task Resume a task Query task information 15:57 /35

72 Task Prototype void YourTask (void *pdata) { for (;;) { /* USER CODE */ Call one of uC/OS-II’s services: /* USER CODE */ } } void YourTask (void *pdata) { /* USER CODE */ OSTaskDel(OS_PRIO_SELF); } 15:57 /35

73 OSTaskCreate() Check that the task priority is valid
Check that the task is unique Set up the task stack OSTaskStkInit() Obtain and initialize OS_TCB from TCB pool OSTCBInit() Call OSTaskCreateHook() to extend the functionality Call OSSched() when OSTaskCreate() is called from a task 15:57 /35

74 INT8U OSTaskCreate (void (. task)(void. pd), void. pdata, OS_STK
INT8U OSTaskCreate (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT8U prio) { void *psp; INT8U err; if (prio > OS_LOWEST_PRIO) { (1) return (OS_PRIO_INVALID); } OS_ENTER_CRITICAL(); if (OSTCBPrioTbl[prio] == (OS_TCB *)0) { (2) OSTCBPrioTbl[prio] = (OS_TCB *)1; (3) OS_EXIT_CRITICAL(); (4) psp = (void *)OSTaskStkInit(task, pdata, ptos, 0); (5) err = OSTCBInit(prio, psp, (void *)0, 0, 0, (void *)0, 0); (6) if (err == OS_NO_ERR) { (7) OSTaskCtr++; (8) OSTaskCreateHook(OSTCBPrioTbl[prio]); (9) OS_EXIT_CRITICAL(); if (OSRunning) { (10) OSSched(); (11) } else { OSTCBPrioTbl[prio] = (OS_TCB *)0; (12) return (err); return (OS_PRIO_EXIST); OSTaskCreate() 15:57 /35

75 OSTaskCreateExt() More flexibility, at the expense of overhead
The first four arguments are the same as OSTaskCreate() id, assign a unique identifier for the task pbos, a pointer that can perform stack checking stk_size, specifies the size of the stack pext, a pointer to extend the OS_TCB opt, specifies whether stack checking is performed 15:57 /35

76 OSTaskCreateExt() INT8U OSTaskCreateExt (void (*task)(void *pd),
void *pdata, OS_STK *ptos, INT8U prio, INT16U id, OS_STK *pbos, INT32U stk_size, void *pext, INT16U opt) { void *psp; INT8U err; INT16U i; OS_STK *pfill; if (prio > OS_LOWEST_PRIO) { (1) return (OS_PRIO_INVALID); } OS_ENTER_CRITICAL(); if (OSTCBPrioTbl[prio] == (OS_TCB *)0) { (2) OSTCBPrioTbl[prio] = (OS_TCB *)1; (3) OS_EXIT_CRITICAL(); (4) return (err); } else { OS_EXIT_CRITICAL(); return (OS_PRIO_EXIST); OSTaskCreateExt() 15:57 /35

77 Task Stacks or Using malloc() to allocate stack space
Static OS_STK MyTaskStack[stack_size]; or OS_STK MyTaskStack[stack_size]; Using malloc() to allocate stack space OS_STK *pstk; pstk = (OS_STK *)malloc(stack_size); if (pstk != (OS_STK *)0) { /* Make sure malloc() had enough space */ Create the task; } 15:57 /35

78 Fragmentation 15:57 /35

79 OSTaskStkChk() Computes the amount of free stack space by “walking” from the bottom of the stack until a nonzero value is found 15:57 /35

80 Stack Checking 15:57 /35

81 OSTaskDel() Return to the DORMANT state
The code for the task will not be deleted Procedures Prevent from deleting and idle task Prevent from deleting a task from within an ISR Verify that the task to be deleted does exist Remove the OS_TCB Remove the task from ready list or other lists Set .OSTCBStat to OS_STAT_RDY Call OSDummy() Call OSTaskDelHook() Remove the OS_TCB from priority table Call OSSched() 15:57 /35

82 OSTaskDelReq() Tell the task that owns memory buffers or semaphore to delete itself Procedures Check the task’s priority If the task’s priority is OS_PRIO_SELF Return the flag of OSTCBDelReq Otherwise Set OSTCBDelReq of the task to OS_TASK_DEL_REQ 15:57 /35

83 OSTaskDelReq() Example
void RequestorTask (void *pdata) { for (;;) { /* Application code */ if (‘TaskToBeDeleted()’ needs to be deleted) { while (OSTaskDelReq(TASK_TO_DEL_PRIO) != OS_TASK_NOT_EXIST) { OSTimeDly(1); } } /* Application code */ } } void TaskToBeDeleted (void *pdata) { for (;;) { /* Application code */ if (OSTaskDelReq(OS_PRIO_SELF) == OS_TASK_DEL_REQ) { Release any owned resources; De-allocate any dynamic memory; OSTaskDel(OS_PRIO_SELF); } else { /* Application code */ } } } 15:57 /35

84 OSTaskChangePrio() Cannot change the priority of any idle task
Procedures Reserve the new priority by OSTCBPrioTbl[newprio] = (OS_TCB *) 1; Remove the task from the priority table Insert the task into new location of the priority table Change the OS_TCB of the task Call OSSched() 15:57 /35

85 OSTaskSuspend() Procedures Check the input priority
Remove the task from the ready list Set the OS_STAT_SUSPEND flag in OS_TCB Call OSSched() 15:57 /35

86 OSTaskResume() Procedures Check the input priority
Clear the OS_STAT_SUSPEND bit in the OSTCBStat field Set OSTCBDly to 0 Call OSSched() 15:57 /35

87 OSTaskQuery() INT8U OSTaskQuery (INT8U prio, OS_TCB *pdata) {
OS_TCB *ptcb; if (prio > OS_LOWEST_PRIO && prio != OS_PRIO_SELF) { (1) return (OS_PRIO_INVALID); } OS_ENTER_CRITICAL(); if (prio == OS_PRIO_SELF) { (2) prio = OSTCBCur->OSTCBPrio; if ((ptcb = OSTCBPrioTbl[prio]) == (OS_TCB *)0) { (3) OS_EXIT_CRITICAL(); return (OS_PRIO_ERR); *pdata = *ptcb; (4) return (OS_NO_ERR); 15:57 /35

88 uC/OS-II Real-Time Systems Concepts Kernel Structure Task Management
Time Management Intertask Communication & Synchronization Memory Management 15:57 /35

89 Time Management OSTimeDly() OSTimeDlyHMSM() OSTimeDlyResume()
OSTimeGet() OSTimeSet() 15:57 /35

90 OSTimeDly() void OSTimeDly (INT16U ticks) { if (ticks > 0) {
if ((OSRdyTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0) { OSRdyGrp &= ~OSTCBCur->OSTCBBitY; } OSTCBCur->OSTCBDly = ticks; OSSched(); } } 15:57 /35

91 OSTimeDlyHMSM() INT8U OSTimeDlyHMSM (INT8U hours, INT8U minutes, INT8U seconds, INT16U milli) { { INT32U ticks; INT16U loops; if (hours > 0 || minutes > 0 || seconds > 0 || milli > 0) { if (minutes > 59) { return (OS_TIME_INVALID_MINUTES); } if (seconds > 59) { return (OS_TIME_INVALID_SECONDS); if (milli > 999) { return (OS_TIME_INVALID_MILLI); ticks = (INT32U)hours * 3600L * OS_TICKS_PER_SEC + (INT32U)minutes * 60L * OS_TICKS_PER_SEC + (INT32U)seconds) * OS_TICKS_PER_SEC + OS_TICKS_PER_SEC * ((INT32U)milli + 500L / OS_TICKS_PER_SEC) / 1000L; loops = ticks / 65536L; ticks = ticks % 65536L; OSTimeDly(ticks); while (loops > 0) { OSTimeDly(32768); loops--; } return (OS_NO_ERR); } else { return (OS_TIME_ZERO_DLY); } 15:57 /35

92 OSTimeDlyResume() INT8U OSTimeDlyResume (INT8U prio) {
ptcb = (OS_TCB *)OSTCBPrioTbl[prio]; if (ptcb != (OS_TCB *)0) { if (ptcb->OSTCBDly != 0) { ptcb->OSTCBDly = 0; if (!(ptcb->OSTCBStat & OS_STAT_SUSPEND)) { OSRdyGrp |= ptcb->OSTCBBitY; OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX; OSSched(); } return (OS_NO_ERR); } else { return (OS_TIME_NOT_DLY); } } else { return (OS_TASK_NOT_EXIST); } } 15:57 /35

93 OSTimeGet() & OSTimeSet()
INT32U OSTimeGet (void) { ticks = OSTime; return (ticks); } void OSTimeSet (INT32U ticks) { OSTime = ticks; 15:57 /35

94 uC/OS-II Real-Time Systems Concepts Kernel Structure Task Management
Time Management Intertask Communication & Synchronization Memory Management 15:57 /35

95 Intertask Communication & Synchronization
OS_ENTER_CRITICAL() OS_EXIT_CRITICAL() OSSchedLock() OSSchedUnlock() Semaphore Message mailbox Message queue 15:57 /35

96 OS_ENTER_CRITICAL() & OS_EXIT_CRITICAL()
#define OS_CRITICAL_METHOD 2 #if OS_CRITICAL_METHOD == 1 #define OS_ENTER_CRITICAL() asm CLI #define OS_EXIT_CRITICAL() asm STI #endif #if OS_CRITICAL_METHOD == 2 #define OS_ENTER_CRITICAL() asm {PUSHF; CLI} #define OS_EXIT_CRITICAL() asm POPF CLI STI 15:57 /35

97 Linux Example /* include/asm-i386/system.h */
/* interrupt control.. */ #define __save_flags(x) __asm__ __volatile__("pushfl ; popl %0":"=g" (x):) #define __restore_flags(x) __asm__ __volatile__("pushl %0; popfl": /* no output */ :"g" (x):"memory", "cc") #define __cli() __asm__ __volatile__("cli": : :"memory") #define __sti() __asm__ __volatile__("sti": : :"memory") /* For spinlocks etc */ #define local_irq_save(x) __asm__ __volatile__("pushfl; popl %0; cli":"=g" (x): :"memory") #define local_irq_restore(x) __restore_flags(x) #define local_irq_disable() __cli() #define local_irq_enable() __sti() 15:57 /35

98 Locking and Unlocking the Scheduler
void OSSchedLock (void) { if (OSRunning == TRUE) { OSLockNesting++; } } void OSSchedUnlock (void) { if (OSLockNesting > 0) { OSLockNesting--; if ((OSLockNesting | OSIntNesting) == 0) { OSSched(); } 15:57 /35

99 ECB (Event Control Block)
15:57 /35

100 Event Control Block typedef struct {
void *OSEventPtr; /* Ptr to message or queue structure */ INT8U OSEventTbl[OS_EVENT_TBL_SIZE]; /* Wait list for event to occur */ INT16U OSEventCnt; /* Count (when event is a semaphore) */ INT8U OSEventType; /* Event type */ INT8U OSEventGrp; /* Group for wait list */ } OS_EVENT; 15:57 /35

101 List of Free ECBs 15:57 /35

102 Wait Queue Functions void OSEventTaskRdy (OS_EVENT *pevent, void *msg, INT8U msk) { y = OSUnMapTbl[pevent->OSEventGrp]; bity = OSMapTbl[y]; x = OSUnMapTbl[pevent->OSEventTbl[y]]; bitx = OSMapTbl[x]; prio = (INT8U)((y << 3) + x); if ((pevent->OSEventTbl[y] &= ~bitx) == 0) { pevent->OSEventGrp &= ~bity; } ptcb = OSTCBPrioTbl[prio]; ptcb->OSTCBDly = 0; ptcb->OSTCBEventPtr = (OS_EVENT *)0; ptcb->OSTCBMsg = msg; ptcb->OSTCBStat &= ~msk; if (ptcb->OSTCBStat == OS_STAT_RDY) { OSRdyGrp |= bity; OSRdyTbl[y] |= bitx; } } 15:57 /35

103 Wait Queue Functions void OSEventTaskWait (OS_EVENT *pevent) {
OSTCBCur->OSTCBEventPtr = pevent; if ((OSRdyTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0) { OSRdyGrp &= ~OSTCBCur->OSTCBBitY; } pevent->OSEventTbl[OSTCBCur->OSTCBY] |= OSTCBCur->OSTCBBitX; pevent->OSEventGrp |= OSTCBCur->OSTCBBitY; } void OSEventTO (OS_EVENT *pevent) { if ((pevent->OSEventTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0) { pevent->OSEventGrp &= ~OSTCBCur->OSTCBBitY; } OSTCBCur->OSTCBStat = OS_STAT_RDY; OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; 15:57 /35

104 Semaphore 15:57 /35

105 Creating a Semaphore OS_EVENT *OSSemCreate (INT16U cnt) {
pevent = OSEventFreeList; if (OSEventFreeList != (OS_EVENT *)0) { OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr; } if (pevent != (OS_EVENT *)0) { pevent->OSEventType = OS_EVENT_TYPE_SEM; pevent->OSEventCnt = cnt; OSEventWaitListInit(pevent); } return (pevent); } 15:57 /35

106 Waiting for a Semaphore
void OSSemPend (OS_EVENT *pevent, INT16U timeout, INT8U *err) { if (pevent->OSEventCnt > 0) { pevent->OSEventCnt--; } else if (OSIntNesting > 0) { *err = OS_ERR_PEND_ISR; } else { OSTCBCur->OSTCBStat |= OS_STAT_SEM; OSTCBCur->OSTCBDly = timeout; OSEventTaskWait(pevent); OSSched(); if (OSTCBCur->OSTCBStat & OS_STAT_SEM) { OSEventTO(pevent); OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; } } 15:57 /35

107 Signaling a Semaphore INT8U OSSemPost (OS_EVENT *pevent) {
if (pevent->OSEventGrp) { OSEventTaskRdy(pevent, (void *)0, OS_STAT_SEM); OSSched(); return (OS_NO_ERR); } else { if (pevent->OSEventCnt < 65535) { pevent->OSEventCnt++; return (OS_SEM_OVF); } } 15:57 /35

108 Getting a Semaphore without Waiting
INT16U OSSemAccept (OS_EVENT *pevent) { cnt = pevent->OSEventCnt; if (cnt > 0) { pevent->OSEventCnt--; } return (cnt); 15:57 /35

109 Obtaining the Status of a Semaphore
INT8U OSSemQuery (OS_EVENT *pevent, OS_SEM_DATA *pdata) { pdata->OSEventGrp = pevent->OSEventGrp; psrc = &pevent->OSEventTbl[0]; pdest = &pdata->OSEventTbl[0]; for (i = 0; i < OS_EVENT_TBL_SIZE; i++) { *pdest++ = *psrc++; } pdata->OSCnt = pevent->OSEventCnt; return (OS_NO_ERR); } 15:57 /35

110 Message Mailbox 15:57 /35

111 Creating a Mailbox OS_EVENT *OSMboxCreate (void *msg) {
pevent = OSEventFreeList; if (OSEventFreeList != (OS_EVENT *)0) { OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr; } if (pevent != (OS_EVENT *)0) { pevent->OSEventType = OS_EVENT_TYPE_MBOX; pevent->OSEventPtr = msg; OSEventWaitListInit(pevent); return (pevent); 15:57 /35

112 Waiting for a Message to Arrive at a Mailbox
void *OSMboxPend (OS_EVENT *pevent, INT16U timeout, INT8U *err) { msg = pevent->OSEventPtr; if (msg != (void *)0) { pevent->OSEventPtr = (void *)0; } else if (OSIntNesting > 0) { *err = OS_ERR_PEND_ISR; } else { OSTCBCur->OSTCBStat |= OS_STAT_MBOX; OSTCBCur->OSTCBDly = timeout; OSEventTaskWait(pevent); OSSched(); if ((msg = OSTCBCur->OSTCBMsg) != (void *)0) { OSTCBCur->OSTCBMsg = (void *)0; OSTCBCur->OSTCBStat = OS_STAT_RDY; OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; } else if (OSTCBCur->OSTCBStat & OS_STAT_MBOX) { OSEventTO(pevent); } return (msg); 15:57 /35

113 Depositing a Message in a Mailbox
INT8U OSMboxPost (OS_EVENT *pevent, void *msg) { if (pevent->OSEventGrp) { OSEventTaskRdy(pevent, msg, OS_STAT_MBOX); OSSched(); return (OS_NO_ERR); } else { if (pevent->OSEventPtr != (void *)0) { return (OS_MBOX_FULL); pevent->OSEventPtr = msg; return (OS_NO_ERR); } } 15:57 /35

114 Getting a Message without Waiting
void *OSMboxAccept (OS_EVENT *pevent) { msg = pevent->OSEventPtr; if (msg != (void *)0) { pevent->OSEventPtr = (void *)0; } return (msg); } 15:57 /35

115 Obtaining the Status of a Mailbox
INT8U OSMboxQuery (OS_EVENT *pevent, OS_MBOX_DATA *pdata) { pdata->OSEventGrp = pevent->OSEventGrp; psrc = &pevent->OSEventTbl[0]; pdest = &pdata->OSEventTbl[0]; for (i = 0; i < OS_EVENT_TBL_SIZE; i++) { *pdest++ = *psrc++; } pdata->OSMsg = pevent->OSEventPtr; return (OS_NO_ERR); } 15:57 /35

116 Using a Mailbox as a Binary Semaphore
void Task1 (void *pdata) { for (;;) { OSMboxPend(MboxSem, 0, &err); /* Obtain access to resource(s) */ . . /* Task has semaphore, access resource(s) */ OSMboxPost(MboxSem, (void )1); /* Release access to resource(s) */ } 15:57 /35

117 Using a Mailbox as a Time Delay
void Task1 (void *pdata) { for (;;) { OSMboxPend(MboxTimeDly, TIMEOUT, &err); /* Delay task */ . . /* Code executed after time delay */ } void Task2 (void *pdata) { OSMboxPost(MboxTimeDly, (void *)1); /* Cancel delay for Task1 */ 15:57 /35

118 Message Queues OSQCreate() OSQPend() OSQPost() OSQPostFront()
OSQAccept() OSQFlush() OSQQuery() 15:57 /35

119 Message Queues A message queue
Allows a task or an ISR to send pointer size variables to another task. Each pointer points a specific data structure containing a ‘message’. Looks like a mailbox with multiple entries. Is like an array of mailboxes except that there is only one wait list. 15:57 /35

120 Message Queues (Cont.) 15:57 /35

121 Data Structures in a Message Queue
15:57 /35

122 Queue Control Block A queue control block contains following fields
OSQPtr OSQStart OSQEnd OSQIn OSQOut OSQSize OSQEntries 15:57 /35

123 List of Free Queue Control Blocks
15:57 /35

124 Message Queue Is a Circular Buffer
15:57 /35

125 Creating a Queue Specify the number of entries.
OSQCreate() requires that you allocate an array of pointers that hold the message. The array must be declared as an array of pointers to void. Once a message queue has been created, it cannot be deleted. 15:57 /35

126 OSQCreate() OS_EVENT *OSQCreate (void **start, INT16U size) {
pevent = OSEventFreeList; if (OSEventFreeList != (OS_EVENT *)0) { OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr; } if (pevent != (OS_EVENT *)0) { pq = OSQFreeList; if (OSQFreeList != (OS_Q *)0) { OSQFreeList = OSQFreeList->OSQPtr; if (pq != (OS_Q *)0) { pevent->OSEventType = OS_EVENT_TYPE_Q; pevent->OSEventPtr = pq; OSEventWaitListInit(pevent); } else { pevent->OSEventPtr = (void *)OSEventFreeList; OSEventFreeList = pevent; pevent = (OS_EVENT *)0; return (pevent); 15:57 /35

127 OSQPend() (1) void *OSQPend (OS_EVENT *pevent, INT16U timeout, INT8U *err) { pq = pevent->OSEventPtr; if (pq->OSQEntries != 0) { msg = *pq->OSQOut++; pq->OSQEntries--; if (pq->OSQOut == pq->OSQEnd) { pq->OSQOut = pq->OSQStart; } *err = OS_NO_ERR; } else if (OSIntNesting > 0) { *err = OS_ERR_PEND_ISR; } else { OSTCBCur->OSTCBStat |= OS_STAT_Q; OSTCBCur->OSTCBDly = timeout; OSEventTaskWait(pevent); OSSched(); if ((msg = OSTCBCur->OSTCBMsg) != (void *)0) { OSTCBCur->OSTCBMsg = (void *)0; OSTCBCur->OSTCBStat = OS_STAT_RDY; OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; *err = OS_NO_ERR; 15:57 /35

128 OSQPend() (2) } else if (OSTCBCur->OSTCBStat & OS_STAT_Q) {
OSEventTO(pevent); msg = (void *)0; *err = OS_TIMEOUT; } else { msg = *pq->OSQOut++; pq->OSQEntries--; if (pq->OSQOut == pq->OSQEnd) { pq->OSQOut = pq->OSQStart; } OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; *err = OS_NO_ERR; return (msg); 15:57 /35

129 OSQPost() INT8U OSQPost (OS_EVENT *pevent, void *msg) {
if (pevent->OSEventGrp) { OSEventTaskRdy(pevent, msg, OS_STAT_Q); OSSched(); return (OS_NO_ERR); } else { pq = pevent->OSEventPtr; if (pq->OSQEntries >= pq->OSQSize) return (OS_Q_FULL); else { *pq->OSQIn++ = msg; pq->OSQEntries++; if (pq->OSQIn == pq->OSQEnd) { pq->OSQIn = pq->OSQStart; } 15:57 /35

130 OSQPostFront() OSQPostFront() Is basically identical to OSQPost().
Uses OSQOut instead of OSQIn as the pointer to the next entry. 15:57 /35

131 OSQPostFront() (Cont.)
INT8U OSQPostFront (OS_EVENT *pevent, void *msg){ if (pevent->OSEventGrp) { OSEventTaskRdy(pevent, msg, OS_STAT_Q); OSSched(); return (OS_NO_ERR); } else { pq = pevent->OSEventPtr; if (pq->OSQEntries >= pq->OSQSize) return (OS_Q_FULL); else { if (pq->OSQOut == pq->OSQStart) { pq->OSQOut = pq->OSQEnd; } pq->OSQOut--; *pq->OSQOut = msg; pq->OSQEntries++; 15:57 /35

132 OSQAccept() void *OSQAccept (OS_EVENT *pevent) {
pq = pevent->OSEventPtr; if (pq->OSQEntries != 0) { msg = *pq->OSQOut++; pq->OSQEntries--; if (pq->OSQOut == pq->OSQEnd) { pq->OSQOut = pq->OSQStart; } } else { msg = (void *)0; return (msg); 15:57 /35

133 OSQFlush() INT8U OSQFlush (OS_EVENT *pevent) {
pq = pevent->OSEventPtr; pq->OSQIn = pq->OSQStart; pq->OSQOut = pq->OSQStart; pq->OSQEntries = 0; return (OS_NO_ERR); } 15:57 /35

134 OSQQuery() Pass a OS_Q_DATA structure to query.
OS_Q_DATA contains following fields OSMsg OSNMsgs OSQSize OSEventTbl[] OSEventGrp 15:57 /35

135 OSQQuery() (Cont.) INT8U OSQQuery (OS_EVENT *pevent, OS_Q_DATA *pdata){ pdata->OSEventGrp = pevent->OSEventGrp; psrc = &pevent->OSEventTbl[0]; pdest = &pdata->OSEventTbl[0]; for (i = 0; i < OS_EVENT_TBL_SIZE; i++) { *pdest++ = *psrc++; } pq = (OS_Q *)pevent->OSEventPtr; if (pq->OSQEntries > 0) { pdata->OSMsg = pq->OSQOut; } else { pdata->OSMsg = (void *)0; pdata->OSNMsgs = pq->OSQEntries; pdata->OSQSize = pq->OSQSize; return (OS_NO_ERR); 15:57 /35

136 Using a Message Queue When Reading Analog Inputs
15:57 /35

137 Using a Queue as a Counting Semaphore
void main (void){ OSInit(); QSem = OSQCreate(&QMsgTbl[0], N_RESOURCES); for (i = 0; i < N_RESOURCES; i++) { OSQPost(Qsem, (void *)1); } OSTaskCreate(Task1, .., .., ..); OSStart(); void Task1 (void *pdata) { for (;;) { OSQPend(&QSem, 0, &err); /* Obtain access to resource(s) */ Task has semaphore, access resource(s) OSMQPost(QSem, (void )1); /* Release access to resource(s) */ 15:57 /35

138 uC/OS-II Real-Time Systems Concepts Kernel Structure Task Management
Time Management Intertask Communication & Synchronization Memory Management 15:57 /35

139 Memory Management Overview Memory Control Blocks OSMemCreate()
OSMemGet() OSMemPut() OSMemQuery() Examples 15:57 /35

140 ANSI C malloc() and free()
Dangerous in an embedded real-time system. Fragmentation. Non-deterministic. 15:57 /35

141 µC/OS-II malloc() and free()
Obtain a contiguous memory area. Memory blocks are the same size. Partition contains an integral number of blocks. Allocation and de-allocation is deterministic. More than one memory partition can exist. Application can obtain memory blocks of different sizes. Memory block must always be returned to the partition from which it came from. 15:57 /35

142 Multiple Memory Partitions
15:57 /35

143 Memory Control Blocks Keeps track of memory partitions through MCB.
Each memory partition requires its own memory control block. Initialization is done by OSMemInit(). Number of memory partitions must be set to at least 2. 15:57 /35

144 Memory Control Block Data Structure
typedef struct { void *OSMemAddr; void *OSMemFreeList; INT32U OSMemBlkSize; INT32U OSMemNBlks; INT32U OSMemNFree; } OS_MEM; 15:57 /35

145 List of Free Memory Control Blocks
15:57 /35

146 OSMemCreate() (1) 15:57 /35

147 OSMemCreate() (2) OS_MEM *OSMemCreate (void *addr, INT32U nblks, INT32U blksize, INT8U *err) { pmem = OSMemFreeList; if (OSMemFreeList != (OS_MEM *)0) { OSMemFreeList = (OS_MEM *)OSMemFreeList>OSMemFreeList; } if (pmem == (OS_MEM *)0) { *err = OS_MEM_INVALID_PART; return ((OS_MEM *)0); plink = (void **)addr; pblk = (INT8U *)addr + blksize; for (i = 0; i < (nblks - 1); i++) { *plink = (void *)pblk; plink = (void **)pblk; pblk = pblk + blksize; *plink = (void *)0; pmem->OSMemAddr = addr; pmem->OSMemFreeList = addr; pmem->OSMemNFree = nblks; pmem->OSMemNBlks = nblks; pmem->OSMemBlkSize = blksize; *err = OS_NO_ERR; return (pmem); >2 entry, entry size>a pointer 15:57 /35

148 OSMemGet() void *OSMemGet (OS_MEM *pmem, INT8U *err) {
if (pmem->OSMemNFree > 0) { pblk = pmem->OSMemFreeList; pmem->OSMemFreeList = *(void **)pblk; pmem->OSMemNFree--; *err = OS_NO_ERR; return (pblk); } else { *err = OS_MEM_NO_FREE_BLKS; return ((void *)0); } 15:57 /35

149 Returning a Memory Block
INT8U OSMemPut (OS_MEM *pmem, void *pblk) { if (pmem->OSMemNFree >= pmem->OSMemNBlks) return (OS_MEM_FULL); *(void **)pblk = pmem->OSMemFreeList; pmem->OSMemFreeList = pblk; pmem->OSMemNFree++; return (OS_NO_ERR); } 15:57 /35

150 Obtaining Status about Memory Partition
Pass a OS_MEM_DATA structure to query. OS_MEM_DATA contains following fields OSAddr OSFreeList OSBlkSize OSNBlks OSNFree OSNUsed 15:57 /35

151 OSMemQuery() INT8U OSMemQuery (OS_MEM *pmem, OS_MEM_DATA *pdata) {
pdata->OSAddr = pmem->OSMemAddr; (1) pdata->OSFreeList = pmem->OSMemFreeList; pdata->OSBlkSize = pmem->OSMemBlkSize; pdata->OSNBlks = pmem->OSMemNBlks; pdata->OSNFree = pmem->OSMemNFree; pdata->OSNUsed = pdata->OSNBlks - pdata->OSNFree; (2) return (OS_NO_ERR); } 15:57 /35

152 Using Dynamic Memory Allocation
15:57 /35

153 Waiting for Memory Blocks (1)
Sometimes it’s useful to have a task wait for a memory block in case a partition runs out of blocks. µC/OS-II doesn’t support ‘pending’ on a partitions. Can support this requirement by adding a counting semaphore. 15:57 /35

154 Waiting for Memory Blocks (2)
void main (void) { SemaphorePtr = OSSemCreate(100); PartitionPtr = OSMemCreate(Partition, 100, 32, &err); OSTaskCreate(Task, (void *)0, &TaskStk[999], &err); OSStart(); } 15:57 /35

155 Waiting for Memory Blocks (3)
void Task (void *pdata) { INT8U err; INT8U *pblock; for (;;) { OSSemPend(SemaphorePtr, 0, &err); pblock = OSMemGet(PartitionPtr, &err); /* Use the memory block */ OSMemPut(PartitionPtr, pblock); OSSemPost(SemaphorePtr); } 15:57 /35


Download ppt "uC/OS-II Real-Time Systems Concepts Kernel Structure Task Management"

Similar presentations


Ads by Google