μC/OS-II
Mike Holenderski
Department of Mathematics and Computer ScienceSystem Architecture and NetworkingEindhoven University of Technology
07 April 2014
Outline
• Introduction to operating systems
• Introduction to μC/OS-II
• Tasks
• Scheduling
• Interrupts
• Exercises
2
Why operating systems?
3
Operating system
There are standards (or specifications) which operating systems should satisfy, in order to provide the desired portability, e.g. POSIX.
Operating system
4
• Provide applications with a hardware abstraction layer- Generic interfaces (APIs) hiding hardware details- Convenient abstractions, addressing common problems
• File system, unix pipes, tasks, ...
- Multiplex application tasks on the shared resources• Processor, bus, network, memory, timers, ...
• Monolithic vs. microkernel based operating system- Microkernel:
• Kernel implements only basic services (task and memory management, and task communication)
• Higher level services are implemented on top• Increased maintainability, security and stability
The microkernel design allows for easier management of code due to its division into user space services. This also allows for increased security and stability resulting from the reduced amount of code running in kernel mode. For example, if a networking service crashed due to buffer overflow, only the networking service's memory would be corrupted, leaving the rest of the system still functional.
Real-time operating system
• Manage tasks and communication between tasks- Interrupt handling- Task scheduling- Context switching between tasks- Task synchronization and communication:
• Semaphores, mutexes, timers, ...
• Known and high performance- low and bounded latencies and jitter for API calls and ISRs
• Small memory foot print (for embedded use)- Configurable (no cost for unused functionality)
5
Our focus:Tasks (with suspension)Timer management (activating tasks)
Example: OSEK/VDX
• Joint project in German/French automotive industry
• Motivation:- High, recurring expenses in the development of control
software (i.e. non-application)- Incompatibility of control units made by different
manufactures due to different interfaces and protocols
• Goal: “create an industry standard for an open-ended architecture for distributed control units in vehicles”- Support for portability and reusability, through:
• Specification of abstract interfaces (i.e. independent of applications and hardware)
• Configurable and efficient architecture
6
http://portal.osek-vdx.org/index.php?option=com_content&task=view&id=4&Itemid=4
OSEK (1993): BMW, Bosch, DaimlerChrysler, Opel, Siemens, VW, University of KarlsruheOSEK/VDX (1994): PSA, Renault
Example: OSEK/VDX
• Several OSEK specifications:- Operating System
• Real-time execution of ECU software and base for the other OSEK/VDX modules
- Communication• Data exchange within and between ECUs
- Network Management• Configuration and monitoring
7
Example: OSEK OS
• Task management- Basic and Extended tasks- Activation, suspension and termination of tasks- Task switching- Note: tasks, semaphores, ... must be statically allocated!
• Synchronization- Priority Ceiling
• Interrupt management
• Alarms (i.e. Timers)
• Error treatment
8
Extended tasks are like basic tasks, but can react to external events.
OSEK OS: task states
9
OSEK/VDX Operating System
Specification 2.2.3
18 © by OSEK OSEK OS 2.2.3
4.2.2 Basic tasks
The state model of basic tasks is nearly identical to the extended tasks state model. The only exception is that basic tasks do not have a waiting state. running In the running state, the CPU is assigned to the task, so that its instructions
can be executed. Only one task can be in this state at any point in time, while all the other states can be adopted simultaneously by several tasks.
ready All functional prerequisites for a transition into the running state exist, and the task only waits for allocation of the processor. The scheduler decides which ready task is executed next.
suspended In the suspended state the task is passive and can be activated.
running
suspendedstart
activate
terminate
preempt
ready
Figure 4-3 Basic task state model
Transition Former state
New state
Description
activate suspended ready4 A new task is set into the ready state by a system service. The OSEK operating system ensures that the execution of the task will start with the first instruction.
start ready running A ready task selected by the scheduler is executed. preempt running ready The scheduler decides to start another task. The
running task is put into the ready state. terminate running suspended The running task causes its transition into the
suspended state by a system service.
Figure 4-4 States and status transitions for basic tasks
4.2.3 Comparison of the task types Basic tasks have no waiting state, and thus only comprise synchronisation points at the beginning and the end of the task. Application parts with internal synchronisation points shall be implemented by more than one basic task. An advantage of basic tasks is their moderate requirement regarding run time context (RAM). 4 Task activation will not immediately change the state of the task in case of multiple activation requests. If the task is not suspended, the activation will only be recorded and performed later.
OSEK/VDX Operating System
Specification 2.2.3
OSEK OS 2.2.3 © by OSEK 17
ready All functional prerequisites for a transition into the running state exist, and the task only waits for allocation of the processor. The scheduler decides which ready task is executed next.
waiting A task cannot continue execution because it shall wait for at least one event (see chapter 7, Event mechanism).
suspended In the suspended state the task is passive and can be activated.
running
suspendedstart
wait
activate
terminate
release
preempt
ready
waiting
Figure 4-1 Extended task state model
Transition Former state
New state
Description
activate suspended ready A new task is set into the ready state by a system service. The OSEK operating system ensures that the execution of the task will start with the first instruction.
start ready running A ready task selected by the scheduler is executed. wait running waiting The transition into the waiting state is caused by a
system service. To be able to continue operation, the waiting task requires an event.
release waiting ready At least one event has occurred which a task has waited for.
preempt running ready The scheduler decides to start another task. The run-ning task is put into the ready state.
terminate running suspended The running task causes its transition into the suspended state by a system service.
Figure 4-2 States and status transitions for extended tasks
Termination of a task is only possible if the task terminates itself ("self-termination"). This restriction reduces complexity of an operating system. There is no provision for a direct transition from the suspended state into the waiting state. This transition is redundant and would add to the complexity of the scheduler.
Extended task Basic task
[OSEK/VDX OS 2.2.3]
Example: OSEK OS
• Task management- Basic and Extended tasks- Activation, suspension and termination of tasks- Task switching
• Synchronization- Priority Ceiling
• Interrupt management
• Alarms (i.e. Timers)
• Error treatment
10
What is μC/OS-II?
• Real-time operating system
• Used in medical, military, aerospace, automotive, consumer electronics, ...
• Commercial, but “open source”
11
Properties of μC/OS-II
• Fixed-priority preemptive multitasking- Up to 64 or 256 tasks (configurable)
• Small and deterministic overheads- Short and predictable interrupt path
• Scalable- Many services: semaphores, mutexes, flags, mailboxes, ...- Enable services with conditional compilation directives
• Nested interrupts- Up to 255 levels deep
12
μC/OS-II architecture
13
CPU Timer
Hardware
Software
μC/OS-II Configuration(application specific)
μC/OS-II(processor independent code)
μC/OS-II Port(processor specific code)
Application Software
“uC/OS-II Port” contains platform specific code, such as context switching and interrupt initialization. Porting uC/OS-II to a new platform requires to change only the “uC/OS-II Port”.
This presentation is about “Application Software” and “uC/OS-II”
Application structure
14
void main(void) { OSInit(); OSTaskCreate(Task1, (void*)100, (void*)&Task1Stack[TASK_STACK_SIZE-1], 4); OSTaskCreate(Task2, ...); RandomSem = OSSemCreate(1); OSStart();}
main():- initializes the system with OSInit(), e.g. internal data structures, interrupt handlers, ...- creates the tasks with OSTaskCreate() (akin to RegisterTask() in Johan’s kernel)- starts multitasking with OSStart()
NOTE: uC/OS-II allows dynamic creation of tasks, semaphores. In this example they are created statically (similar to OSEK).
15
OSTaskCreate()
INT8U OSTaskCreate(void (*task)(void* pd), void* pdata, OS_STK* ptos, INT8U prio);
task function
argument totask function
pointer to the stack
task priority
Each task has its own state (to support blocking).
Task example
16
OS_STK Task1Stack[TASK1_STACK_SIZE];
void Task1 (void* data) { INT8U err; INT16U delay = data; INT16U x; for (x = 0;; x++) { OSSemPend(RandomSem, 0, &err); if (x % 3 == 0) ToggleLeds(GREEN); OSTimeDly(delay); }}
void main(void) { ... OSTaskCreate(Task1, 50, &Task1Stack[TASK1_STACK_SIZE-1], 5); RandomSem = OSSemCreate(0); ...}
Tasks are implemented as repetitive loops.
NOTE: the stack can grow up or down, depending on the underlying architecture, i.e. how the push() and pop() methods work.
Task: one shot vs. thread
• Both are dispatched by the scheduler within an interrupt handler
• One shot- Cannot block or self suspend- Executed within the context of the interrupt handler
• Thread- Can block or self suspend- Preserves its context
• Has its own stack
17
One shot tasks are executed from scratch upon every interrupt handler invocation (just like the tasks in Johan’s kernel).
Task: one shot vs. thread
• Both are dispatched by the scheduler within an interrupt handler
• One shot- Cannot block or self suspend- Executed within the context of the interrupt handler
✓Thread- Can block or self suspend- Preserves its context
• Has its own stack
18
Johan’s kernel supports one shot tasks.
uC/OS-II implements threads.
Task as a thread
• Task Control Block (TCB)- Stack Pointer, unique priority, ...
• Task context- Local task variables- Special registers (Program Counter, Status Register (PSW))- Preserving context:
• Store and load the task context on the task’s stack
• Task switch- Store the task’s context on its stack- Store the Stack Pointer inside the TCB
19
The state of a task is described by two kinds of variables:Task Control Block stores task variables which are common in all tasks.Task context contains the rest, e.g. local variables used during task execution, special registers.
Task state storage
20
f1()φ1T1SP1
TCB for τ0
f2()φ2T2SP2
TCB for τ1void Task2(void) { p = ReadPressure(); if (p > P) InflateAirbag();}
void Task1(void) { a = ReadRotation(); if (a < A) ActivateABS();}
Stack for τ1
Stack for τ2
datadata
datadatadatadata
Scheduler
• Fixed-Priority Preemptive Scheduler• Unique priorities (lower number means higher priority)• Triggered synchronously and asynchronously (w.r.t. control flow)
- Synchronously: called from e.g. OSTimeDly() or OSSemPend()- Asynchronously: called from OSIntExit()
• Scheduler:1. Select highest priority ready task2. Switch if it has higher priority than current task
21
Task
SchedulerSuspend
Task
SchedulerTimerexpired
Preemptive kernels are used when responsiveness is important (the highest ready task is always given control of the CPU, as long as data consistency is preserved).
Unique priorities avoid round robin and simplify resource sharing.
OSSched() is called from synchronization primitives. OSIntExit() is called at the end of an interrupt handler, in particular the tick interrupt handler.
Scheduler
• How does the scheduler select the highest priority task?- Maintain a ready queue keeping track which tasks are ready
and which are not- Maintain the queue when tasks become ready or not ready- When scheduler is invoked, consult the queue to select the
highest priority task
22
Ready queue implementation
• A list ordered by priority- Task becomes ready: insert task into the list- Task becomes not ready: remove task from the list- Select highest priority task: take the task from the head
• A bitmap, with one bit per task- Bit is 1 means task is ready, bit is 0 means task is not ready- Task becomes ready: set the corresponding bit to 1- Task becomes not ready: set the corresponding bit to 0- Select highest priority task: select the “right most” set bit
23
01234567
89101112131415
1617181920212223
2425262728293031
3233343536373839
4041424344454647
4849505152535455
5657585960616263
Task Priority #
Lowest Priority Task(Idle Task)
Highest Priority Task
X
Y
OSRdyTbl[OS_LOWEST_PRIO / 8 + 1]01234567
OSRdyGrp
[7]
[6]
[5]
[4]
[3]
[2]
[1]
[0]
0 0 Y Y Y X X X
Bit position in OSRdyTbl[OS_LOWEST_PRIO / 8 + 1]
Bit position in OSRdyGrp andIndex into OSRdyTbl[OS_LOWEST_PRIO / 8 + 1]
Task's Priority
Figure 3-3, µC/OS-II1s Ready List The following piece of code is used to place a task in the ready list: OSRdyGrp |= OSMapTbl[prio >> 3]; OSRdyTbl[prio >> 3] |= OSMapTbl[prio & 0x07];
Listing 3.5, Making a task ready-to-run. where prio is the task's priority. As you can see from Figure 3-3, the lower 3 bits of the task's priority are used to determine the bit position in OSRdyTbl[], while the next three most significant bits are used to determine the index into OSRdyTbl[]. Note that OSMapTbl[] (see OS_CORE.C) is a table in ROM, used to equate an index from 0 to 7 to a bit mask as shown in the table below:
Index Bit mask (Binary)
[Labrosse, 2002]
25
Scheduler
• The ready state of all tasks is managed with global variables- OSRdyTbl[] and OSRdyGrp
• Making a task ready
• Checking if a task is ready
• Both can be done in constant time!
- At the memory cost of lookup tables OSMapTbl and OSUnMapTbl
OSRdyGrp |= OSMapTbl[prio >> 3];OSRdyTbl[prio >> 3] |= OSMapTbl[prio & 0x07];
y = OSUnMapTbl[OSRdyGrp];x = OSUnMapTbl[OSRdyTbl[y]];prio = (y << 3) + x;
OSMapTbl[] and OSUnMapTbl[] are arrays in ROM mapping between numbers in the range 0..7 and the corresponding bit mask, containing a 1 at the position indicated by the number.
The thing to take away from this slide is that the ready table can be managed in constant time.
Interrupts
• Interrupt handler dispatched by an incoming interrupt
• Has higher priority than any task (interfere with tasks)- Needs to have a short and predictable execution time- Tasks can disable interrupts
• OS_ENTER_CRITICAL() and OS_EXIT_CRITICAL()• Can lead to increased interrupt latency or missed interrupts• Keep interrupts disabled for as little time as possible!• Use only for short critical sections
• Represents high priority events (need to be handled)- Sometimes sufficient to only disable the scheduler
• Task maintains control of the CPU, but incoming interrupts are handled
26
There is a tradeoff: interrupts should be handled with little latency, but can lead to high interference of other tasks.
Interrupt Service Routine
27
• General structure of an ISR in μC/OS-II:
• ISR executes within the context of the currently running task- It uses the stack space of the current task
void OSTickISR(void) { Save processor registers; Call OSIntEnter(); Call OSTimeTick(); Call OSIntExit(); Restore processor registers; Execute a return from interrupt instruction;}
Interrupts
28
Task A
save context A
ISR
restore context A
Task A
Task B
restore context B
time
interrupt latency
interrupt response
interrupt overhead
interrupt recovery
ASK: how would you measure interrupt latency?
interrupt latency = longest critical section
Response time defined slightly different that in Johan’s slides (S05.14) and Reinder’s: the response is the time until the beginning of the User ISR, rather than its end. (Also their task response time in the book is until the beginning of the task, rather than its completion).
Scenario A is selected when the same task is resumed after the interrupt.Scenario B is selected when a higher priority task is ready to execute. Includes a context switch to the higher priority task.
Interrupt timing
• Interrupt latency- Max time interrupts are disabled- Time to start executing first instruction
of the ISR
• Interrupt response- Interrupt latency- Time to save CPU context (registers
PC, PWD)
- Time to enter the ISR ( OSIntEnter())
• Interrupt recovery- Time to exit the ISR ( OSIntExit())
- Time to restore CPU context
29
void SomeISR(void) { Save processor registers; Call OSIntEnter(); Call SomeISRBody(); Call OSIntExit(); Restore processor registers; Return from interrupt; }
Relate to Slide 29 , 14Time to Start executing the first instruction of ISR includes waiting for last instruction to complete
OSIntExit() includes time to determine if higher priority task is ready and switching to it.
Timer interrupt
30
• Timer interrupts are especially important- Task delays in control applications, network packet
timeouts, ...- Keep track of time by incrementing the global OSTime
variable
Timer interrupt
• System clock is connected to the MCU via a pin- Clock operates independently of the MCU- Clock sets the pin high periodically with a specified
frequency- Setting a pin high triggers an interrupt on the MCU- Interrupt is handled by an ISR, which sets the pin low
31
MCU
Timer interrupt(interrupts enabled)
32
OSTickISR
OSTime2 3 4
Task1
0 1
Time2 3 41
Clock pin
1 20
2 3 41
OSTickISR
OSTime
Task1
Time
Clock pin
Timer interrupt(interrupts disabled)
33
Timer interrupts
34
• Timer interrupts are especially important- Task delays in control applications, network packet
timeouts, ...
• Timed evens in uC/OS-II- No periodic tasks!- Single primitive:
• Delay a task for number of ticks: OSTimeDly() - Relative to the current time
Timer interrupts
35
Task 1
Task 2
Task 3
Here is a logical view on the timer interrupts: each task wants to be triggered by its own timer.
The different sizes represent the work that needs to be done by the task.
Task 1
Task 2
Task 3
Timer interrupts
36
Most platforms provide a single hardware timer, so we need to multiplex the logical (or software) timers onto the single timer.
Task 1
Task 2
Task 3
Multiplexer
Timer interrupts
37
The Multiplexer box represents the overhead for multiplexing several software timers on a single hardware timer.
Note that the multiplexer delays the time when a task can start responding to the expiration of its timer.
Task 1
Task 2
Task 3
OSTimeTick()
Timer interrupts
38
Timer interrupts
39
• Timer interrupts are especially important- Task delays in control applications, network packet
timeouts, ...
• Timed evens in uC/OS-II- No periodic tasks!- Single primitive:
• Delay a task for number of ticks: OSTimeDly() - Relative to the current time
• Handled by the tick handler OSTimeTick()- Called within the timer interrupt handler- Loops through all tasks, decrementing OSTimeDly and
checking if it is 0
OSTimeTick()
40
void OSTimeTick (void) {OS_TCB *task;
for all tasks {OS_ENTER_CRITICAL();if (task->OSTCBDly > 0) {
if (--task->OSTCBDly == 0) {OSRdyGrp |= task->OSTCBBitY;OSRdyTbl[task->OSTCBY] |= task->OSTCBBitX;
}}OS_EXIT_CRITICAL();
}OS_ENTER_CRITICAL();OSTime++;OS_EXIT_CRITICAL();
}
Each task is equipped with a OSTCBDly counter: if it is larger than 0 then it represents the number of ticks when the task should become ready.
Most of the work done by OSTimeTick() basically consist of decrementing the OSTCBDly field for each OS_TCB (if it’s nonzero). When the OSTCBDly field of a task's OS_TCB is decremented to zero, the task is made ready to run.
Note that this is pseudo code with the key parts. The OSTimeTick() function in uC/OS-II also deals with suspended tasks.
OSTimeTick()
41
void OSTimeTick (void) {OS_TCB *task;
for all tasks {OS_ENTER_CRITICAL();if (task->OSTCBDly > 0) {
if (--task->OSTCBDly == 0) {OSRdyGrp |= task->OSTCBBitY;OSRdyTbl[task->OSTCBY] |= task->OSTCBBitX;
}}OS_EXIT_CRITICAL();
}OS_ENTER_CRITICAL();OSTime++;OS_EXIT_CRITICAL();
}
Looping through all the tasks is costly.
Within SAN we have developed a really neat timed event management component, called RELTEQ, and integrated it with uC/OS-II. It avoids looping through all the tasks upon every tick.
Worst-Case Response Time analysis
42
• Smallest x satisfying:
• WCRT of the timer interrupt handler:- B0 : interrupt latency- WC0 : (interrupt response - latency) + OSTimeTick() +
interrupt recovery- AJ0 : clock jitter- WT0 : timer interrupt frequency
x = Bi + WCi +X
j<i
⇠x + AJj
WTj
⇡WCj
1
Note: this analysis holds for deadlines smaller or equal to periods (i.e. task’s response time needs to be smaller than its period).
Clock jitter: due to hardware inaccuracies and the divider (1024 works).
Worst-Case Response Time analysis
43
• Smallest x satisfying:
• WCRT of the highest priority task:- B1 : longest time a lower priority task disabled interrupts- WC1 : worst-case path through Task()
- AJ1 : AJ0 + WR0 - BR0 = AJ0 + B0 + WC0 - BC0
- WT1 : task period
x = Bi + WCi +X
j<i
⇠x + AJj
WTj
⇡WCj
1
Need to consider other resources as well: accessing memory, network, ... . In our case we do not allow those.
Activation jitter: assuming no nested interrupts. Clock is never interrupted.
Exercises
• Measure- Tick interrupt latency (suggest how to measure)- Tick interrupt overhead
• OSTimeTick() (best and worst case)
• Compare μC/OS-II with Johan’s kernel- Measure the above for Johan’s kernel
• Instantiate the WCRT analysis for Johan’s kernel and task set from Exercise C1
44
Exercises
• Implement periodic tasks on top of uC/OS-II- Given the following task structure:
What are the key issues here? Propose an improvement. Hint: use semaphores, and either timers in os_tmr.c or the timer hook OSTimeTickHook() to handle the arrival of periodic events.
45
void Task1 (void* data) { INT16U period = data; INT16U k = 0; INT32U offset = OSTimeGet(); for (;;) { ... OS_ENTER_CRITICAL(); delay = ++k*period - (OSTimeGet() - offset); OS_EXIT_CRITICAL(); OSTimeDly(delay); }}
Exercise
• Optional (for the very ambitious): implement one-shot tasks in uC/OS-II
46
References
• “MicroC/OS-II, The Real-time Kernel”, J. Labrosse, 2002, Second Endition
• “μC/OS-II Reference Manual”- Chapter 16 from the book- Available online
• μC/OS-II source code- Download from http://micrium.com
47