Developed in C by Jean Labrosse in 1992
Currently, maintained by Micrium Inc. (
http://www.micrium.com/products/rtos/kernel/
rtos.html
)
The current version is C/OS-II v3.10(v2.52 for
textbook)
The new version C/OS-III provides more
features
◦Round-robin scheduling
Allow multiple tasks to run at the same priority level
◦Near zero interrupt disable time
◦Unlimited number of application tasks
◦Error checking and more.
Licensing
◦Free distribution of C/OS-II source and object code to
accredited Colleges and Universities w/o requiring licens
e
◦‘Object Code Distribution License’ is required for commer
cial products
C/OS-II
◦A highly portable, ROMable, scalable, preemptive,
real-time, deterministic multitasking kernel for
microprocessors, microcontrollers and DSP-based
devices
◦Actual target: embedded system
◦Easily portable to many processor (
http://www.micrium.com/products/rtos/kernel/ports.
html
)
Open source code
Portable
◦Written in ANSI C + target-specific code written in
assembly language
◦Run on most 8-, 16-, 32- or 64-bit platforms
◦Portable data types
typedef unsigned char INT8U
typedef unsigned intINT16U
typedef unsigned shortINT16U (for 32-bit)
typedef unsigned long INT32U
ROMable
◦Designed for embedded applications
◦Embedded as part of products
Scalability using conditional compilation
◦May contain only needed features for a small
footprint
◦Depending on the processor, the size can be
reduced as small as between 5KB to 24KB
Fully preemptible real-time, deterministic,
multitasking kernel for microprocessors,
microcontrollers and DSPs
Multitasking
◦Manage up to 64 tasks
8 system tasks and 56 application tasks
Up to 63 app tasks allowed
◦No round-robin allowed
Each task has a unique 64 priority levels
Deterministic
◦The execution time for most of the functions and
services is both constant and known in advance
◦The execution time is independent of the number of
tasks currently running
◦Exception
OSTimeTick()
Some event flag services(e.g., OSFlagCreate(),
OSFlagPost(), etc.)
Task stacks
◦Each tasks requires its own stack
◦Different stack sizes for different tasks
◦Exact size can be determined by stack-checking
feature
Reduce the amount of RAM needed by an each
application code
Use OSTaskStkChk()
Interrupt management: 255 levels
Robust and Reliable
◦Used in 100s of commercial apps since 1992
◦Suitable for Safety Critical Systems common to
Aviation and Medical products
◦Certifiable for use in Safety Critical Systems
A Validation Suite provides all of the documentation
necessary to deliver µC/OS-II as a pre-certifiable
software component for safety critical systems
Include avionics RTCA DO-178B and EUROCAE/ ED-
12B, medical FDA 510(k), and IEC 61508 standard for
transportation and nuclear systems
Robust and Reliable
◦Revised
The source code for µC/OS-II is now 99% compliant
with the Motor Industry Software Reliability
Association (MISRA) C Coding Standards.
Improve the safety, reliability and predictability of C
programs in critical automotive systems.
Members of the MISRA consortium : Delco Electronics,
Ford Motor Company, Jaguar Cars Ltd., Lotus
Engineering, Lucas Electronics, Rolls-Royce, Rover
Group Ltd., etc.
18
A task is a simple program that thinks it has the
CPU all to itself
Each Task has
◦Its own stack space
◦A priority based on its importance
A task contains YOUR application code
19
A task is an infinite loop
void Task (void *p_arg)
{
Do something with ‘argument’ p_arg;
Task initialization;
for (;;) {
/* Processing (Your Code) */
Wait for event; /* Time to expire ... */
/* Signal from ISR ... */
/* Signal from task ... */
/* Processing (Your Code) */
}
}
•A task can be deleted by call OSTaskDel()
–The task code is not actually deleted (also in memory)
–The OS doesn’t schedule it
Example
void YourTask (void *pdata) (1)
{
for (;;) { (2)
/* USER CODE */
Call one of uC/OS-II’s services:
OSMboxPend();
OSQPend();
OSSemPend();
OSTaskDel(OS_PRIO_SELF);
OSTaskSuspend(OS_PRIO_SELF);
OSTimeDly();
OSTimeDlyHMSM();
/* USER CODE */
}
}
•The argument is a
pointer to a void
–Allow pass any kind
of data to your task
•It is possible to create
many identical tasks
–EX: create 4 serial
ports task
–Each task has a
same code
–Pass different data
structure that
defines the serial
port’s parameters
(baud rate , I/O
port addresses,
interrupt vector,
etc)
uC/OS-II can manage up to 64 tasks
Each task needs to assign a priority
◦The lower the priority number, the higher the priority of the
task.
◦uC/OS-II reserves 8 priority (0, 1, 2, 3, OS_LOWEST_PRIO-3,
OS_LOWEST_PRIO-2, OS_LOWEST_PRIO-1, OS_LOWEST_PRIO )
◦Two system tasks:Idle Task, Statistics Task.
◦Only up to 56 application tasks
◦Task priority number is as the task identifier number
Always executes the highest priority task ready to run
Priority number service:OSTaskChangePrio() ,
OSTaskDel().
Create a task:OSTaskCreate(), OSTaskCreateExt().
22
High Priority Task
Low Priority Task
Task
Task
Task
Task
Task
Task
Event Event
Each Task
Infinite Loop
Importance
23
Tasks
Reside
In
ROM
Task Waiting For Event
Task
Ready-To-Run
24
To make it ready for multitasking
The kernel needs to have information about
your task
◦Its starting address
◦Its top-of-stack (TOS)
◦Its priority
◦Arguments passed to the task
◦Other information about your task
task is a pointer to the task's code.
parg is a pointer to an optional data area which
can be used to pass parameters to the task when
it is created.
pstk is a pointer to the task's top of stack.
prio is the task priority. A unique priority number
must be assigned to each task and the lower the
number, the higher the priority (i.e. the
importance) of the task. id is the task’s ID number.
At this time , the ID is not currently used in any
other function and has simply been added in
OSTaskCreateExt() for future expansion. You
should set the id to the same value as the task’s
priority.
pbos is a pointer to the task's bottom of stack.
pext is a pointer to a user supplied memory
location (typically a data structure) which is used
as a TCB extension. For example, this user
memory can hold the contents of floating-point
registers during a context switch, the time each
task takes to execute, the number of times the
task is switched-in, etc.
opt contains task specific options. The lower 8
bits are reserved by μC/OS-II but you can use the
upper 8 bits for application specific options. Each
option consist of a bit. The option is selected when
the bit is set. The current version of μC/OS-II
supports the following options:
OS_TASK_OPT_STK_CHK specifies whether
stack checking is allowed for the task.
OS_TASK_OPT_STK_CLR specifies whether the
stack needs to be cleared.
OS_TASK_OPT_SAVE_FP specifies whether
floating-point registers will be saved and the stack
needs to be cleared.
29
main
Disable Interrupts
Start the execution of the RTOS
Create the First Application Task (AppTaskStart)
Initialize internal OS structures
Disable
Interrupts
Initialize:
interrupt nesting counter
task counter
context switch counter
statistics
TCB and Events lists
To disable interrupts in order to access
critical sections of code and to reenable
interrupts when down.
◦The interrupt disable time affects the
responsiveness of system or real-time events
◦uC/OS-II defines two macros to disable/enable
interrupts
OS_ENTER_CRITICAL( )
OS_ENTER_CRITICAL asm CLI
OS_EXIT_CRITICAL( )
OS_EXIT_CRITICAL asm STI
When a task is created, it is assigned a Task
Control block, OS-TCB
◦To maintain the state of a task when it is
preempted
◦OS-TCB resides in RAMtypedef 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;
#endif
OSTCBStkPtr
Point to the current top-of-stack for the task
Each task has its own stack
Each stack can be any size
Placing it at the first entry of structure
Make access this field easier from assembly language code
OSTCBExtPtr
Pointer to user definable data for TCB extension
Allow user to extend TCB without changing the
source code for uc/os-II
OS_TASK_CREATE_EXT_EN set to 1 to enable this
field
Place this pointer immediately after the stack
pointer
OSTCBStkBottom
Point to the bottom the the stack
OSTCBStkSize
The field is used by OSTaskStkChk()
The field is valid only if you set OS_TASK_CREATE_EXT_EN
to 1
OSTCBOpt
It holds options that can be passed to OSTaskCreateExt()
This field is valid only if you set OS_TASK_CREATE_EXT_EN
to 1
OS_TASK_OPT_STK_CHK 0x0001
OS_TASK_OPT_STK_CLR 0x0002
OS_TASK_OPT_SAVE_FP 0X0004
The stack only needs to be cleared if you intend to do
stack checking
OSTCBNext
OSTCBPrev
Used to double link OS_TCBs
This chain OS_TCBs is used by OSTimeTick() to
update the OSTCBDly field for each task
When the task is created ,its OS_TCB is linked in
this chain
When the task is deleted, its OS_TCB is removed
OSTCBDly
Used when a task needs to be delayed for a certain
number of clock ticks
A Task needs to pend for an event to occur with a
timeout
When this variable is 0,the task is not delayed or has no
timeout when waiting for an event
OSTCBEventPtr
It is a pointer to an event control block (ECB)
When waiting for an event (semaphore ,mbox,or
message) to occur ,it points to the ECB
OSTCBMsg
It is a pointer to a message that is sent to a task
It is modified while OSMboxPost() OSQPost()
OSQFrontPost() call OSEventTaskRdy()
OSTCBId
It is used to hold an identifier for the task
This field is currently not used
OSTCBPrio
Task Priority
A high-priority task has a low value
OSTCBStat: contain the state of the task
OS_STAT_RDY0x00/*Ready to run
OS_STAT_SEM0x01 /*Pending on semaphore
OS_STAT_MBOX 0x02 /*Pending on mailbox
OS_STAT_Q 0x04 /*Pending on queue
OS_STAT_SUSPEND 0x08 /*Task is suspend
OSTCBDelReq
To indicate whether or not a task that current task
be deleted
OS_MAX_TASKS (in OS_CFG.H) specifies the maximum
number of tasks
◦We can reduce the amount of RAM needed by setting this variable
to the actual number of tasks needed in our application
All OS-TCBs are placed in OSTCBTbl[ ]
◦When uC/OS-II is initiialized, all OS-TCBs in the table are linked in
a singly linked list of free OS_TCBs
0OSTCBNextOSTCBNextOSTCBNextOSTCBNext
OSTCBTbl[0] OSTCBTbl[1] OSTCBTbl[2]
OSTCBFreeList
OSTCBTbl[OS_MAX_TASKS+OS_N_SYS_TASKS-1]
Each task that is ready to run is placed in a
ready list consisting of two variables
◦OSRdyGrp and OSRdyTbl[ ]
◦Eight task priorities are grouped in OSRdyGrp
◦Each bit in OSRdyGrp indicates when a task in a
group is ready to run
◦Which priority tasks that are ready to run in a group
of OSRdyGrp is required to look up 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
EX: if OSRdyGrp contains 01101000 OSUnMapTbl[OSRdyGrp] =3, it
means 3th group is the highest priority
If OSRdyTbl[3] = 11100100, then OSUnMapTbl[OSRdyTbl[3]]=2, the means
is that the task priority 3*8+2=26 is the highest priority ready task.
Getting a pointer to the OS-TCB for the corresponding task
OSRdyGrp |= OSMapTbl[prio >> 3];
OSRdyTbl[prio >> 3] |= OSMapTbl[prio & 0x07];
Removing a task from the ready list
if ((OSRdyTbl[prio >> 3] &= ~OSMapTbl[prio & 0x07]) == 0)
OSRdyGrp &= ~OSMapTbl[prio >> 3];
Finding the highest priority task ready to
run
y = OSUnMapTbl[OSRdyGrp];
x = OSUnMapTbl[OSRdyTbl[y]];
prio = (y << 3) + x;
Task-level scheduling is performed by OSSched()
ISR-level scheduling is handled by OSIntExit()
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();
}
Task Scheduler
The stack frame for a ready task always looks as if an interrupt just occurred and
all processor registers were saved onto it the uC/OS-II has to do context switch
is restore all processor registers from the task’s stack and execute a return from
interrupt
Locking the scheduler
void OSSchedLock (void)
{ if (OSRunning == TRUE) {
OS_ENTER_CRITICAL();
OSLockNesting++;
OS_EXIT_CRITICAL();
}
}
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();
}
}
}
uC/OS-II provides run-time statistics
◦OSTaskState() executes every second and computes the percent
CPU usage
◦OSStateInit() is called before you create your other application tasks
◦The startup code in Main() must create only one task before calling
OSStart().
◦From this one task, you must call OSStatInit() before you create
your other application tasks
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! */
}
}
uC/OS-II has only three tasks to manage when main() calls
OSStart()
◦TaskStart(), OSTaskIdle(), and OSTaskStat()
◦TaskStart() requires call OSStartInit() before creating other AP tasks
main()
{
OSInit(); (1)
Install context switch vector; (2)
Create TaskStart(); (3)
OSStart();
}
TaskStart()
{
Init uC/OS-II's ticker; (5)
OSStatInit(): (6)
OSTimeDly(2); (7)
OSIdleCtr = 0; (12)
OSTimeDly(1 second); (13)
OSIdleCtrMax = OSIdleCtr; (15)
OSStatRdy = TRUE; (16)
for (;;) {
Task code;
}
}
OSTaskStat()
{
while (OSStatRdy == FALSE) { (8)
OSTimeDly(2 seconds); (9)
}
for (;;) {
Compute Statistics; (17)
}
}
OSTaskIdle()
{
for (;;) {
OSIdleCtr++; (10)
}
for (;;) {
OSIdleCtr++; (14)
}
Scheduler
Scheduler
Scheduler
After 2 ticks
After 1 second
Scheduler
Highest Priority OS_LOWEST_PRIOOS_LOWEST_PRIO - 1
2 ticks
1 second
2 seconds
(4)
(11)
Figure 3.4 Statistic task initialization
Variable OSIdleCtr records how much ticks are consumed
in the idle task
First, the OSStatInit() measures the maximum value of
OSIdleCtr when the system doesn’t do anything
OSTaskStat() calculates the following equation every
second, which represent the OS CPU usage
void OSStatInit (void)
{
OSTimeDly(2);
OS_ENTER_CRITICAL();
OSIdleCtr = 0L;
OS_EXIT_CRITICAL();
OSTimeDly(OS_TICKS_PER_SEC);
OS_ENTER_CRITICAL();
OSIdleCtrMax = OSIdleCtr;
OSStatRdy = TRUE;
OS_EXIT_CRITICAL();
}
axOSIdleCtrM
OSIdleCtr
UsageOSCPU 1100
(%)
OSIntEnter() : increase the global variable OSIntNesting
◦uC/OS-II allows nesting interrupt
OSIntExit() : decide whether requires to do the context
switch or not
◦If OSIntNexting > 0, the ISR direct return to the Interrupted
task or ISR
◦If scheduling has been disabled (OSLockNesting > 0), the
uC/OS-II will return to the interrupted task
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; (6)
ISRs under uC/OS-II
Interrupt Request
TASK TASK
Vectoring
Saving Context
Notify kernel:
OSIntEnter() or,
OSIntNesting++
User ISR code
Notify kernel: OSIntExit()
Restore context
Notify kernel: OSIntExit()
Restore context
Return from interrupt
Return from interrupt
TASK
Interrupt Response
Interrupt Recovery
Interrupt Recovery
µC/OS-IIor your application
has interrupts disabled.
Time
ISR signals a task
No New HPT or,
OSLockNesting > 0
New HPT
Task Response
Task Response
(1)
(2)
(3)
(4)
(5)
(6)
(7)
(8)
(9)
(10)
(11)
(12)
Figure 3.5 Servicing an interrupt
There are two functions to do the context switch
◦OSIntCtxSw() called by OSIntExit() : interrupt level context switch
◦OS_TASK_SW() called by OSSched() : Task level context switch
◦The context switch is done by the iret
Processor Status Word
Interrupt Return Address
LOW MEMORY
HIGH MEMORY
Stack Growth
SP must be adjusted
to point here.
This new SP is saved into
the preempted task's OS_TCB.
Saved Processor Registers
Return address to caller of OSIntExit()
Return address to caller of OSIntCtxSw()
Processor Status Word
SP Points Here!
(1)
(2)
(3)
(4)
(5)
(6)
Provide a periodic time source to keep track of
time delays and timeout
The ticker interrupts must be enable after
multitaking has started
◦After calling OSStart()
void OSTickISR(void)
{
Save processor registers;
Call OSIntEnter() or increment OSIntNesting;
Call OSTimeTick();
Call OSIntExit();
Restore processor registers;
Execute a return from interrupt instruction;
}
OSTimeTick() can be called at the task level
to reduce the tick ISR service time
void TickTask (void *pdata)
{
pdata = pdata;
for (;;) {
OSMboxPend(...); /* Wait for signal from Tick ISR */
OSTimeTick();
}
}
void OSTickISR(void)
{
Save processor registers;
Call OSIntEnter() or increment OSIntNesting;
Post a ‘dummy’ message (e.g. (void *)1) to the tick mailbox;
Call OSIntExit();
Restore processor registers;
Execute a return from interrupt instruction;
}
OSInit() initializes all uC/OS-II variables and
data structures
OSInit() creates the idle task OSTaskIdle()
OSInit() may create OSTaskStat(), if
OS_TASK_STAT_EN=1
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 */
}
PSW = 0x0202
SEG task
OFF task
AX = 0xAAAA
BX = 0xBBBB
CX = 0xCCCC
DX = 0xDDDD
SI = 0x2222
DI = 0x3333
BP = 0x1111
SP = 0x0000
ES = 0x4444
DS = Current DS
LOW MEMORY
HIGH MEMORY
Simulate PUSH ES
Simulate PUSH DS
Simulate PUSHA
Simulate Interrupt
Stack Growth
SEG pdata
OFF pdata
SEG task
OFF task
Simulate call to task
OSTCBHighRdy->OSTCBStkPtr
SS:SP points here after executing:
POP DS
POP ES
POPA
IRET
Figure 9.3 80x86 Stack frame when task is created
_OSStartHighRdy PROC FAR
MOV AX, SEG _OSTCBHighRdy ; Reload DS
MOV DS, AX ;
LES BX, DWORD PTR DS:_OSTCBHighRdy ; SS:SP = OSTCBHighRdy->OSTCBStkPtr (1)
MOV SS, ES:[BX+2] ;
MOV SP, ES:[BX+0] ;
;
POP DS ; Load task's context (2)
POP ES ; (3)
POPA ; (4)
;
IRET ; Run task (5)
_OSStartHighRdy ENDP